Fix class type parameter erasure within $default methods

This commit is contained in:
Chris Smowton
2022-10-05 14:45:09 +01:00
parent be655432d6
commit 3452dcbced
6 changed files with 200 additions and 5 deletions

View File

@@ -859,7 +859,10 @@ open class KotlinFileExtractor(
// n + o'th parameter, where `o` is the parameter offset caused by adding any dispatch receiver to the parameter list.
// Note we don't need to add the extension receiver here because `useValueParameter` always assumes an extension receiver
// will be prepended if one exists.
DeclarationStackAdjuster(f, OverriddenFunctionAttributes(id, id, locId, nonSyntheticParams, typeParameters = listOf())).use {
// Note we have to get the real function ID here before entering this block, because otherwise we'll misrepresent the signature of a generic
// function without its type variables -- for example, trying to address `f(T, List<T>)` as `f(Object, List)`.
val realFunctionId = useFunction<DbCallable>(f)
DeclarationStackAdjuster(f, OverriddenFunctionAttributes(id, id, locId, nonSyntheticParams, typeParameters = listOf(), isStatic = true)).use {
val realParamsVarId = getValueParameterLabel(id, parameterTypes.size - 2)
val intType = pluginContext.irBuiltIns.intType
val paramIdxOffset = listOf(dispatchReceiver, f.extensionReceiverParameter).count { it != null }
@@ -889,7 +892,6 @@ open class KotlinFileExtractor(
}
}
// Now call the real function:
val realFunctionId = useFunction<DbCallable>(f)
if (f is IrConstructor) {
tw.getFreshIdLabel<DbConstructorinvocationstmt>().also { thisCallId ->
tw.writeStmts_constructorinvocationstmt(thisCallId, blockId, nextStmt++, id)
@@ -5275,10 +5277,19 @@ open class KotlinFileExtractor(
fun peek() = stack.peek()
fun findOverriddenAttributes(f: IrFunction) =
stack.firstOrNull { it.first == f } ?.second
stack.lastOrNull { it.first == f } ?.second
fun findFirst(f: (Pair<IrDeclaration, OverriddenFunctionAttributes?>) -> Boolean) =
stack.findLast(f)
}
data class OverriddenFunctionAttributes(val id: Label<out DbCallable>? = null, val sourceDeclarationId: Label<out DbCallable>? = null, val sourceLoc: Label<DbLocation>? = null, val valueParameters: List<IrValueParameter>? = null, val typeParameters: List<IrTypeParameter>? = null)
data class OverriddenFunctionAttributes(
val id: Label<out DbCallable>? = null,
val sourceDeclarationId: Label<out DbCallable>? = null,
val sourceLoc: Label<DbLocation>? = null,
val valueParameters: List<IrValueParameter>? = null,
val typeParameters: List<IrTypeParameter>? = null,
val isStatic: Boolean? = null)
private fun peekDeclStackAsDeclarationParent(elementToReportOn: IrElement): IrDeclarationParent? {
val trapWriter = tw

View File

@@ -661,10 +661,17 @@ open class KotlinUsesExtractor(
private fun isOnDeclarationStackWithoutTypeParameters(f: IrFunction) =
this is KotlinFileExtractor && this.declarationStack.findOverriddenAttributes(f)?.typeParameters?.isEmpty() == true
private fun isStaticFunctionOnStackBeforeClass(c: IrClass) =
this is KotlinFileExtractor && (this.declarationStack.findFirst { it.first == c || it.second?.isStatic == true })?.second?.isStatic == true
private fun isUnavailableTypeParameter(t: IrType) =
t is IrSimpleType && t.classifier.owner.let { owner ->
owner is IrTypeParameter && owner.parent.let { parent ->
parent is IrFunction && isOnDeclarationStackWithoutTypeParameters(parent)
when (parent) {
is IrFunction -> isOnDeclarationStackWithoutTypeParameters(parent)
is IrClass -> isStaticFunctionOnStackBeforeClass(parent)
else -> false
}
}
}

View File

@@ -847,3 +847,154 @@ test.kt:
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 1
# 1| 4: [NullLiteral] null
# 143| 12: [Class,GenericType,ParameterizedType] TestGenericFunction
#-----| -2: (Generic Parameters)
# 143| 0: [TypeVariable] T
# 143| 1: [Constructor] TestGenericFunction
# 143| 5: [BlockStmt] { ... }
# 143| 0: [SuperConstructorInvocationStmt] super(...)
# 143| 1: [BlockStmt] { ... }
# 145| 2: [Method] f
#-----| 2: (Generic Parameters)
# 145| 0: [TypeVariable] S
# 145| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 145| 0: [Parameter] x
# 145| 0: [TypeAccess] S
# 145| 1: [Parameter] y
# 145| 0: [TypeAccess] T
# 145| 2: [Parameter] def1
# 145| 0: [TypeAccess] T
# 145| 3: [Parameter] def2
# 145| 0: [TypeAccess] List<? extends T>
# 145| 0: [WildcardTypeAccess] ? ...
# 145| 0: [TypeAccess] T
# 145| 4: [Parameter] def3
# 145| 0: [TypeAccess] S
# 145| 5: [Parameter] def4
# 145| 0: [TypeAccess] List<? extends S>
# 145| 0: [WildcardTypeAccess] ? ...
# 145| 0: [TypeAccess] S
# 145| 5: [BlockStmt] { ... }
# 146| 0: [ExprStmt] <Expr>;
# 146| 0: [MethodAccess] sink(...)
# 146| -1: [TypeAccess] TestKt
# 146| 0: [VarAccess] y
# 145| 3: [Method] f$default
# 145| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 145| 0: [Parameter] p0
# 145| 0: [TypeAccess] TestGenericFunction<>
# 145| 1: [Parameter] p1
# 145| 0: [TypeAccess] Object
# 145| 2: [Parameter] p2
# 145| 0: [TypeAccess] Object
# 145| 3: [Parameter] p3
# 145| 0: [TypeAccess] Object
# 145| 4: [Parameter] p4
# 145| 0: [TypeAccess] List<>
# 145| 5: [Parameter] p5
# 145| 0: [TypeAccess] Object
# 145| 6: [Parameter] p6
# 145| 0: [TypeAccess] List<>
# 145| 7: [Parameter] p7
# 145| 0: [TypeAccess] int
# 145| 8: [Parameter] p8
# 145| 0: [TypeAccess] Object
# 145| 5: [BlockStmt] { ... }
# 145| 0: [IfStmt] if (...)
# 145| 0: [EQExpr] ... == ...
# 145| 0: [AndBitwiseExpr] ... & ...
# 145| 0: [IntegerLiteral] 2
# 145| 1: [VarAccess] p7
# 145| 1: [IntegerLiteral] 0
# 145| 1: [ExprStmt] <Expr>;
# 145| 0: [AssignExpr] ...=...
# 145| 0: [VarAccess] p2
# 145| 1: [VarAccess] p1
# 145| 1: [IfStmt] if (...)
# 145| 0: [EQExpr] ... == ...
# 145| 0: [AndBitwiseExpr] ... & ...
# 145| 0: [IntegerLiteral] 4
# 145| 1: [VarAccess] p7
# 145| 1: [IntegerLiteral] 0
# 145| 1: [ExprStmt] <Expr>;
# 145| 0: [AssignExpr] ...=...
# 145| 0: [VarAccess] p3
# 145| 1: [NullLiteral] null
# 145| 2: [IfStmt] if (...)
# 145| 0: [EQExpr] ... == ...
# 145| 0: [AndBitwiseExpr] ... & ...
# 145| 0: [IntegerLiteral] 8
# 145| 1: [VarAccess] p7
# 145| 1: [IntegerLiteral] 0
# 145| 1: [ExprStmt] <Expr>;
# 145| 0: [AssignExpr] ...=...
# 145| 0: [VarAccess] p4
# 145| 1: [MethodAccess] listOf(...)
# 145| -2: [TypeAccess] Object
# 145| -1: [TypeAccess] CollectionsKt
# 145| 0: [VarAccess] p2
# 145| 3: [IfStmt] if (...)
# 145| 0: [EQExpr] ... == ...
# 145| 0: [AndBitwiseExpr] ... & ...
# 145| 0: [IntegerLiteral] 16
# 145| 1: [VarAccess] p7
# 145| 1: [IntegerLiteral] 0
# 145| 1: [ExprStmt] <Expr>;
# 145| 0: [AssignExpr] ...=...
# 145| 0: [VarAccess] p5
# 145| 1: [NullLiteral] null
# 145| 4: [IfStmt] if (...)
# 145| 0: [EQExpr] ... == ...
# 145| 0: [AndBitwiseExpr] ... & ...
# 145| 0: [IntegerLiteral] 32
# 145| 1: [VarAccess] p7
# 145| 1: [IntegerLiteral] 0
# 145| 1: [ExprStmt] <Expr>;
# 145| 0: [AssignExpr] ...=...
# 145| 0: [VarAccess] p6
# 145| 1: [MethodAccess] listOf(...)
# 145| -2: [TypeAccess] Object
# 145| -1: [TypeAccess] CollectionsKt
# 145| 0: [VarAccess] p1
# 145| 5: [ReturnStmt] return ...
# 145| 0: [MethodAccess] f(...)
# 145| -1: [VarAccess] p0
# 145| 0: [VarAccess] p1
# 145| 1: [VarAccess] p2
# 145| 2: [VarAccess] p3
# 145| 3: [VarAccess] p4
# 145| 4: [VarAccess] p5
# 145| 5: [VarAccess] p6
# 149| 4: [Method] user
# 149| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 149| 0: [Parameter] inst
# 149| 0: [TypeAccess] TestGenericFunction<String>
# 149| 0: [TypeAccess] String
# 149| 5: [BlockStmt] { ... }
# 150| 0: [ExprStmt] <Expr>;
# 150| 0: [MethodAccess] f$default(...)
# 150| -1: [TypeAccess] TestGenericFunction<>
# 150| 0: [VarAccess] inst
# 150| 1: [StringLiteral] generic function sunk
# 1| 2: [NullLiteral] null
# 1| 3: [NullLiteral] null
# 1| 4: [NullLiteral] null
# 1| 5: [NullLiteral] null
# 1| 6: [NullLiteral] null
# 1| 7: [IntegerLiteral] 1
# 1| 8: [NullLiteral] null
# 151| 1: [ExprStmt] <Expr>;
# 151| 0: [MethodAccess] f$default(...)
# 151| -1: [TypeAccess] TestGenericFunction<>
# 151| 0: [VarAccess] inst
# 151| 1: [StringLiteral] generic function sunk fp
# 151| 2: [StringLiteral] generic function sunk 2
# 1| 3: [NullLiteral] null
# 1| 4: [NullLiteral] null
# 1| 5: [NullLiteral] null
# 1| 6: [NullLiteral] null
# 1| 7: [IntegerLiteral] 3
# 1| 8: [NullLiteral] null

View File

@@ -0,0 +1,13 @@
import java
// 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
}
from Expr e
where
e.getEnclosingCallable().getName().matches("%$default") and
containsTypeVariables(e.getType())
select e, e.getType()

View File

@@ -139,3 +139,16 @@ class TestGeneric<T> {
}
}
class TestGenericFunction<T> {
fun <S : T> f(x: S, y: T = x, def1: T? = null, def2: List<T> = listOf(y), def3: S? = null, def4: List<S>? = listOf(x)) {
sink(y)
}
fun user(inst: TestGenericFunction<String>) {
inst.f<String>("generic function sunk")
inst.f("generic function sunk fp", "generic function sunk 2")
}
}