Kotlin: fix type variable erasure inside default function values

Previously because extractClassInstance didn't use the declaration stack, we wouldn't notice that it was legal to refer to its type variable in the context of extracting a specialised method <-> method source-decl edge. This led to erasing the types of the source-decl, so that e.g. Map.put(...) would have signature (Object, Object) not (K, V)
as it should.
This commit is contained in:
Chris Smowton
2022-10-07 17:31:38 +01:00
parent 0d98eba604
commit bef4011947
5 changed files with 94 additions and 13 deletions

View File

@@ -1165,3 +1165,59 @@ test.kt:
# 161| -1: [VarAccess] p0
# 161| 0: [VarAccess] p1
# 161| 1: [VarAccess] p2
# 165| 15: [Class,GenericType,ParameterizedType] TestGenericUsedWithinDefaultValue
#-----| -2: (Generic Parameters)
# 165| 0: [TypeVariable] T
# 165| 1: [Constructor] TestGenericUsedWithinDefaultValue
# 165| 5: [BlockStmt] { ... }
# 165| 0: [SuperConstructorInvocationStmt] super(...)
# 165| 1: [BlockStmt] { ... }
# 171| 2: [Method] f
# 171| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 171| 0: [Parameter] x
# 171| 0: [TypeAccess] int
# 171| 1: [Parameter] y
# 171| 0: [TypeAccess] String
# 171| 5: [BlockStmt] { ... }
# 171| 3: [Method] f$default
# 171| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 171| 0: [Parameter] p0
# 171| 0: [TypeAccess] TestGenericUsedWithinDefaultValue<>
# 171| 1: [Parameter] p1
# 171| 0: [TypeAccess] int
# 171| 2: [Parameter] p2
# 171| 0: [TypeAccess] String
# 171| 3: [Parameter] p3
# 171| 0: [TypeAccess] int
# 171| 4: [Parameter] p4
# 171| 0: [TypeAccess] Object
# 171| 5: [BlockStmt] { ... }
# 171| 0: [IfStmt] if (...)
# 171| 0: [EQExpr] ... == ...
# 171| 0: [AndBitwiseExpr] ... & ...
# 171| 0: [IntegerLiteral] 2
# 171| 1: [VarAccess] p3
# 171| 1: [IntegerLiteral] 0
# 171| 1: [ExprStmt] <Expr>;
# 171| 0: [AssignExpr] ...=...
# 171| 0: [VarAccess] p2
# 171| 1: [MethodAccess] ident(...)
# 171| -1: [ClassInstanceExpr] new TestGenericUsedWithinDefaultValue<String>(...)
# 171| -3: [TypeAccess] TestGenericUsedWithinDefaultValue<String>
# 171| 0: [TypeAccess] String
# 171| 0: [StringLiteral] Hello world
# 171| 1: [ReturnStmt] return ...
# 171| 0: [MethodAccess] f(...)
# 171| -1: [VarAccess] p0
# 171| 0: [VarAccess] p1
# 171| 1: [VarAccess] p2
# 173| 4: [Method] ident
# 173| 3: [TypeAccess] T
#-----| 4: (Parameters)
# 173| 0: [Parameter] t
# 173| 0: [TypeAccess] T
# 173| 5: [BlockStmt] { ... }
# 173| 0: [ReturnStmt] return ...
# 173| 0: [VarAccess] t

View File

@@ -1,9 +1,15 @@
import java
class InstantiatedType extends ParameterizedType {
InstantiatedType() { typeArgs(_, _, this) }
}
// This checks that all type parameter references are erased in the context of a $default function.
predicate containsTypeVariables(Type t) {
t != t.getErasure() and
not t.getErasure().(GenericType).getRawType() = t
t instanceof TypeVariable or
containsTypeVariables(t.(InstantiatedType).getATypeArgument()) or
containsTypeVariables(t.(NestedType).getEnclosingType()) or
containsTypeVariables(t.(Wildcard).getATypeBound().getType())
}
from Expr e

View File

@@ -161,3 +161,15 @@ class VisibilityTests {
private fun i(x: Int, y: Int = 0) = x + y
}
class TestGenericUsedWithinDefaultValue<T> {
// This tests parameter erasure works properly: we should notice that here the type variable T
// isn't used in the specialisation TestGenericUsedWithinDefaultValue<String>, but it can be
// cited in contexts like "the signature of the source declaration of 'TestGenericUsedWithinDefaultValue<String>.f(String)' is 'f(T)'",
// not 'f(Object)' as we might mistakenly conclude if we're inappropriately erasing 'T'.
fun f(x: Int, y: String = TestGenericUsedWithinDefaultValue<String>().ident("Hello world")) { }
fun ident(t: T) = t
}