Skip to content

Commit 3452dcb

Browse files
committed
Fix class type parameter erasure within $default methods
1 parent be65543 commit 3452dcb

File tree

6 files changed

+200
-5
lines changed

6 files changed

+200
-5
lines changed

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,10 @@ open class KotlinFileExtractor(
859859
// n + o'th parameter, where `o` is the parameter offset caused by adding any dispatch receiver to the parameter list.
860860
// Note we don't need to add the extension receiver here because `useValueParameter` always assumes an extension receiver
861861
// will be prepended if one exists.
862-
DeclarationStackAdjuster(f, OverriddenFunctionAttributes(id, id, locId, nonSyntheticParams, typeParameters = listOf())).use {
862+
// Note we have to get the real function ID here before entering this block, because otherwise we'll misrepresent the signature of a generic
863+
// function without its type variables -- for example, trying to address `f(T, List<T>)` as `f(Object, List)`.
864+
val realFunctionId = useFunction<DbCallable>(f)
865+
DeclarationStackAdjuster(f, OverriddenFunctionAttributes(id, id, locId, nonSyntheticParams, typeParameters = listOf(), isStatic = true)).use {
863866
val realParamsVarId = getValueParameterLabel(id, parameterTypes.size - 2)
864867
val intType = pluginContext.irBuiltIns.intType
865868
val paramIdxOffset = listOf(dispatchReceiver, f.extensionReceiverParameter).count { it != null }
@@ -889,7 +892,6 @@ open class KotlinFileExtractor(
889892
}
890893
}
891894
// Now call the real function:
892-
val realFunctionId = useFunction<DbCallable>(f)
893895
if (f is IrConstructor) {
894896
tw.getFreshIdLabel<DbConstructorinvocationstmt>().also { thisCallId ->
895897
tw.writeStmts_constructorinvocationstmt(thisCallId, blockId, nextStmt++, id)
@@ -5275,10 +5277,19 @@ open class KotlinFileExtractor(
52755277
fun peek() = stack.peek()
52765278

52775279
fun findOverriddenAttributes(f: IrFunction) =
5278-
stack.firstOrNull { it.first == f } ?.second
5280+
stack.lastOrNull { it.first == f } ?.second
5281+
5282+
fun findFirst(f: (Pair<IrDeclaration, OverriddenFunctionAttributes?>) -> Boolean) =
5283+
stack.findLast(f)
52795284
}
52805285

5281-
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)
5286+
data class OverriddenFunctionAttributes(
5287+
val id: Label<out DbCallable>? = null,
5288+
val sourceDeclarationId: Label<out DbCallable>? = null,
5289+
val sourceLoc: Label<DbLocation>? = null,
5290+
val valueParameters: List<IrValueParameter>? = null,
5291+
val typeParameters: List<IrTypeParameter>? = null,
5292+
val isStatic: Boolean? = null)
52825293

52835294
private fun peekDeclStackAsDeclarationParent(elementToReportOn: IrElement): IrDeclarationParent? {
52845295
val trapWriter = tw

java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,10 +661,17 @@ open class KotlinUsesExtractor(
661661
private fun isOnDeclarationStackWithoutTypeParameters(f: IrFunction) =
662662
this is KotlinFileExtractor && this.declarationStack.findOverriddenAttributes(f)?.typeParameters?.isEmpty() == true
663663

664+
private fun isStaticFunctionOnStackBeforeClass(c: IrClass) =
665+
this is KotlinFileExtractor && (this.declarationStack.findFirst { it.first == c || it.second?.isStatic == true })?.second?.isStatic == true
666+
664667
private fun isUnavailableTypeParameter(t: IrType) =
665668
t is IrSimpleType && t.classifier.owner.let { owner ->
666669
owner is IrTypeParameter && owner.parent.let { parent ->
667-
parent is IrFunction && isOnDeclarationStackWithoutTypeParameters(parent)
670+
when (parent) {
671+
is IrFunction -> isOnDeclarationStackWithoutTypeParameters(parent)
672+
is IrClass -> isStaticFunctionOnStackBeforeClass(parent)
673+
else -> false
674+
}
668675
}
669676
}
670677

java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.expected

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,3 +847,154 @@ test.kt:
847847
# 1| 2: [NullLiteral] null
848848
# 1| 3: [IntegerLiteral] 1
849849
# 1| 4: [NullLiteral] null
850+
# 143| 12: [Class,GenericType,ParameterizedType] TestGenericFunction
851+
#-----| -2: (Generic Parameters)
852+
# 143| 0: [TypeVariable] T
853+
# 143| 1: [Constructor] TestGenericFunction
854+
# 143| 5: [BlockStmt] { ... }
855+
# 143| 0: [SuperConstructorInvocationStmt] super(...)
856+
# 143| 1: [BlockStmt] { ... }
857+
# 145| 2: [Method] f
858+
#-----| 2: (Generic Parameters)
859+
# 145| 0: [TypeVariable] S
860+
# 145| 3: [TypeAccess] Unit
861+
#-----| 4: (Parameters)
862+
# 145| 0: [Parameter] x
863+
# 145| 0: [TypeAccess] S
864+
# 145| 1: [Parameter] y
865+
# 145| 0: [TypeAccess] T
866+
# 145| 2: [Parameter] def1
867+
# 145| 0: [TypeAccess] T
868+
# 145| 3: [Parameter] def2
869+
# 145| 0: [TypeAccess] List<? extends T>
870+
# 145| 0: [WildcardTypeAccess] ? ...
871+
# 145| 0: [TypeAccess] T
872+
# 145| 4: [Parameter] def3
873+
# 145| 0: [TypeAccess] S
874+
# 145| 5: [Parameter] def4
875+
# 145| 0: [TypeAccess] List<? extends S>
876+
# 145| 0: [WildcardTypeAccess] ? ...
877+
# 145| 0: [TypeAccess] S
878+
# 145| 5: [BlockStmt] { ... }
879+
# 146| 0: [ExprStmt] <Expr>;
880+
# 146| 0: [MethodAccess] sink(...)
881+
# 146| -1: [TypeAccess] TestKt
882+
# 146| 0: [VarAccess] y
883+
# 145| 3: [Method] f$default
884+
# 145| 3: [TypeAccess] Unit
885+
#-----| 4: (Parameters)
886+
# 145| 0: [Parameter] p0
887+
# 145| 0: [TypeAccess] TestGenericFunction<>
888+
# 145| 1: [Parameter] p1
889+
# 145| 0: [TypeAccess] Object
890+
# 145| 2: [Parameter] p2
891+
# 145| 0: [TypeAccess] Object
892+
# 145| 3: [Parameter] p3
893+
# 145| 0: [TypeAccess] Object
894+
# 145| 4: [Parameter] p4
895+
# 145| 0: [TypeAccess] List<>
896+
# 145| 5: [Parameter] p5
897+
# 145| 0: [TypeAccess] Object
898+
# 145| 6: [Parameter] p6
899+
# 145| 0: [TypeAccess] List<>
900+
# 145| 7: [Parameter] p7
901+
# 145| 0: [TypeAccess] int
902+
# 145| 8: [Parameter] p8
903+
# 145| 0: [TypeAccess] Object
904+
# 145| 5: [BlockStmt] { ... }
905+
# 145| 0: [IfStmt] if (...)
906+
# 145| 0: [EQExpr] ... == ...
907+
# 145| 0: [AndBitwiseExpr] ... & ...
908+
# 145| 0: [IntegerLiteral] 2
909+
# 145| 1: [VarAccess] p7
910+
# 145| 1: [IntegerLiteral] 0
911+
# 145| 1: [ExprStmt] <Expr>;
912+
# 145| 0: [AssignExpr] ...=...
913+
# 145| 0: [VarAccess] p2
914+
# 145| 1: [VarAccess] p1
915+
# 145| 1: [IfStmt] if (...)
916+
# 145| 0: [EQExpr] ... == ...
917+
# 145| 0: [AndBitwiseExpr] ... & ...
918+
# 145| 0: [IntegerLiteral] 4
919+
# 145| 1: [VarAccess] p7
920+
# 145| 1: [IntegerLiteral] 0
921+
# 145| 1: [ExprStmt] <Expr>;
922+
# 145| 0: [AssignExpr] ...=...
923+
# 145| 0: [VarAccess] p3
924+
# 145| 1: [NullLiteral] null
925+
# 145| 2: [IfStmt] if (...)
926+
# 145| 0: [EQExpr] ... == ...
927+
# 145| 0: [AndBitwiseExpr] ... & ...
928+
# 145| 0: [IntegerLiteral] 8
929+
# 145| 1: [VarAccess] p7
930+
# 145| 1: [IntegerLiteral] 0
931+
# 145| 1: [ExprStmt] <Expr>;
932+
# 145| 0: [AssignExpr] ...=...
933+
# 145| 0: [VarAccess] p4
934+
# 145| 1: [MethodAccess] listOf(...)
935+
# 145| -2: [TypeAccess] Object
936+
# 145| -1: [TypeAccess] CollectionsKt
937+
# 145| 0: [VarAccess] p2
938+
# 145| 3: [IfStmt] if (...)
939+
# 145| 0: [EQExpr] ... == ...
940+
# 145| 0: [AndBitwiseExpr] ... & ...
941+
# 145| 0: [IntegerLiteral] 16
942+
# 145| 1: [VarAccess] p7
943+
# 145| 1: [IntegerLiteral] 0
944+
# 145| 1: [ExprStmt] <Expr>;
945+
# 145| 0: [AssignExpr] ...=...
946+
# 145| 0: [VarAccess] p5
947+
# 145| 1: [NullLiteral] null
948+
# 145| 4: [IfStmt] if (...)
949+
# 145| 0: [EQExpr] ... == ...
950+
# 145| 0: [AndBitwiseExpr] ... & ...
951+
# 145| 0: [IntegerLiteral] 32
952+
# 145| 1: [VarAccess] p7
953+
# 145| 1: [IntegerLiteral] 0
954+
# 145| 1: [ExprStmt] <Expr>;
955+
# 145| 0: [AssignExpr] ...=...
956+
# 145| 0: [VarAccess] p6
957+
# 145| 1: [MethodAccess] listOf(...)
958+
# 145| -2: [TypeAccess] Object
959+
# 145| -1: [TypeAccess] CollectionsKt
960+
# 145| 0: [VarAccess] p1
961+
# 145| 5: [ReturnStmt] return ...
962+
# 145| 0: [MethodAccess] f(...)
963+
# 145| -1: [VarAccess] p0
964+
# 145| 0: [VarAccess] p1
965+
# 145| 1: [VarAccess] p2
966+
# 145| 2: [VarAccess] p3
967+
# 145| 3: [VarAccess] p4
968+
# 145| 4: [VarAccess] p5
969+
# 145| 5: [VarAccess] p6
970+
# 149| 4: [Method] user
971+
# 149| 3: [TypeAccess] Unit
972+
#-----| 4: (Parameters)
973+
# 149| 0: [Parameter] inst
974+
# 149| 0: [TypeAccess] TestGenericFunction<String>
975+
# 149| 0: [TypeAccess] String
976+
# 149| 5: [BlockStmt] { ... }
977+
# 150| 0: [ExprStmt] <Expr>;
978+
# 150| 0: [MethodAccess] f$default(...)
979+
# 150| -1: [TypeAccess] TestGenericFunction<>
980+
# 150| 0: [VarAccess] inst
981+
# 150| 1: [StringLiteral] generic function sunk
982+
# 1| 2: [NullLiteral] null
983+
# 1| 3: [NullLiteral] null
984+
# 1| 4: [NullLiteral] null
985+
# 1| 5: [NullLiteral] null
986+
# 1| 6: [NullLiteral] null
987+
# 1| 7: [IntegerLiteral] 1
988+
# 1| 8: [NullLiteral] null
989+
# 151| 1: [ExprStmt] <Expr>;
990+
# 151| 0: [MethodAccess] f$default(...)
991+
# 151| -1: [TypeAccess] TestGenericFunction<>
992+
# 151| 0: [VarAccess] inst
993+
# 151| 1: [StringLiteral] generic function sunk fp
994+
# 151| 2: [StringLiteral] generic function sunk 2
995+
# 1| 3: [NullLiteral] null
996+
# 1| 4: [NullLiteral] null
997+
# 1| 5: [NullLiteral] null
998+
# 1| 6: [NullLiteral] null
999+
# 1| 7: [IntegerLiteral] 3
1000+
# 1| 8: [NullLiteral] null

java/ql/test/kotlin/library-tests/parameter-defaults/erasure.expected

Whitespace-only changes.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import java
2+
3+
// This checks that all type parameter references are erased in the context of a $default function.
4+
predicate containsTypeVariables(Type t) {
5+
t != t.getErasure() and
6+
not t.getErasure().(GenericType).getRawType() = t
7+
}
8+
9+
from Expr e
10+
where
11+
e.getEnclosingCallable().getName().matches("%$default") and
12+
containsTypeVariables(e.getType())
13+
select e, e.getType()

java/ql/test/kotlin/library-tests/parameter-defaults/test.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,16 @@ class TestGeneric<T> {
139139
}
140140

141141
}
142+
143+
class TestGenericFunction<T> {
144+
145+
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)) {
146+
sink(y)
147+
}
148+
149+
fun user(inst: TestGenericFunction<String>) {
150+
inst.f<String>("generic function sunk")
151+
inst.f("generic function sunk fp", "generic function sunk 2")
152+
}
153+
154+
}

0 commit comments

Comments
 (0)