Skip to content

Commit 0d98eba

Browse files
authored
Merge pull request #10683 from smowton/smowton/feature/kotlin-function-overloads
Kotlin: implement $default function synthesis
2 parents 75cb0ef + 289843e commit 0d98eba

File tree

28 files changed

+2615
-534
lines changed

28 files changed

+2615
-534
lines changed

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

Lines changed: 574 additions & 298 deletions
Large diffs are not rendered by default.

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

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,26 @@ open class KotlinUsesExtractor(
658658
RETURN, GENERIC_ARGUMENT, OTHER
659659
}
660660

661+
private fun isOnDeclarationStackWithoutTypeParameters(f: IrFunction) =
662+
this is KotlinFileExtractor && this.declarationStack.findOverriddenAttributes(f)?.typeParameters?.isEmpty() == true
663+
664+
private fun isStaticFunctionOnStackBeforeClass(c: IrClass) =
665+
this is KotlinFileExtractor && (this.declarationStack.findFirst { it.first == c || it.second?.isStatic == true })?.second?.isStatic == true
666+
667+
private fun isUnavailableTypeParameter(t: IrType) =
668+
t is IrSimpleType && t.classifier.owner.let { owner ->
669+
owner is IrTypeParameter && owner.parent.let { parent ->
670+
when (parent) {
671+
is IrFunction -> isOnDeclarationStackWithoutTypeParameters(parent)
672+
is IrClass -> isStaticFunctionOnStackBeforeClass(parent)
673+
else -> false
674+
}
675+
}
676+
}
677+
678+
private fun argIsUnavailableTypeParameter(t: IrTypeArgument) =
679+
t is IrTypeProjection && isUnavailableTypeParameter(t.type)
680+
661681
private fun useSimpleType(s: IrSimpleType, context: TypeContext): TypeResults {
662682
if (s.abbreviation != null) {
663683
// TODO: Extract this information
@@ -729,11 +749,13 @@ open class KotlinUsesExtractor(
729749
}
730750

731751
owner is IrClass -> {
732-
val args = if (s.isRawType()) null else s.arguments
752+
val args = if (s.isRawType() || s.arguments.any { argIsUnavailableTypeParameter(it) }) null else s.arguments
733753

734754
return useSimpleTypeClass(owner, args, s.isNullable())
735755
}
736756
owner is IrTypeParameter -> {
757+
if (isUnavailableTypeParameter(s))
758+
return useType(erase(s), context)
737759
val javaResult = useTypeParameter(owner)
738760
val aClassId = makeClass("kotlin", "TypeParam") // TODO: Wrong
739761
val kotlinResult = if (true) TypeResult(fakeKotlinType(), "TODO", "TODO") else
@@ -1043,9 +1065,9 @@ open class KotlinUsesExtractor(
10431065
f.parent,
10441066
maybeParentId,
10451067
getFunctionShortName(f).nameInDB,
1046-
maybeParameterList ?: f.valueParameters,
1068+
(maybeParameterList ?: f.valueParameters).map { it.type },
10471069
getAdjustedReturnType(f),
1048-
f.extensionReceiverParameter,
1070+
f.extensionReceiverParameter?.type,
10491071
getFunctionTypeParameters(f),
10501072
classTypeArgsIncludingOuterClasses,
10511073
overridesCollectionsMethodWithAlteredParameterTypes(f),
@@ -1067,12 +1089,12 @@ open class KotlinUsesExtractor(
10671089
maybeParentId: Label<out DbElement>?,
10681090
// The name of the function; normally f.name.asString().
10691091
name: String,
1070-
// The value parameters that the functions takes; normally f.valueParameters.
1071-
parameters: List<IrValueParameter>,
1092+
// The types of the value parameters that the functions takes; normally f.valueParameters.map { it.type }.
1093+
parameterTypes: List<IrType>,
10721094
// The return type of the function; normally f.returnType.
10731095
returnType: IrType,
1074-
// The extension receiver of the function, if any; normally f.extensionReceiverParameter.
1075-
extensionReceiverParameter: IrValueParameter?,
1096+
// The extension receiver of the function, if any; normally f.extensionReceiverParameter?.type.
1097+
extensionParamType: IrType?,
10761098
// The type parameters of the function. This does not include type parameters of enclosing classes.
10771099
functionTypeParameters: List<IrTypeParameter>,
10781100
// The type arguments of enclosing classes of the function.
@@ -1089,11 +1111,7 @@ open class KotlinUsesExtractor(
10891111
prefix: String = "callable"
10901112
): String {
10911113
val parentId = maybeParentId ?: useDeclarationParent(parent, false, classTypeArgsIncludingOuterClasses, true)
1092-
val allParams = if (extensionReceiverParameter == null) {
1093-
parameters
1094-
} else {
1095-
listOf(extensionReceiverParameter) + parameters
1096-
}
1114+
val allParamTypes = if (extensionParamType == null) parameterTypes else listOf(extensionParamType) + parameterTypes
10971115

10981116
val substitutionMap = classTypeArgsIncludingOuterClasses?.let { notNullArgs ->
10991117
if (notNullArgs.isEmpty()) {
@@ -1103,11 +1121,11 @@ open class KotlinUsesExtractor(
11031121
enclosingClass?.let { notNullClass -> makeTypeGenericSubstitutionMap(notNullClass, notNullArgs) }
11041122
}
11051123
}
1106-
val getIdForFunctionLabel = { it: IndexedValue<IrValueParameter> ->
1124+
val getIdForFunctionLabel = { it: IndexedValue<IrType> ->
11071125
// Kotlin rewrites certain Java collections types adding additional generic constraints-- for example,
11081126
// Collection.remove(Object) because Collection.remove(Collection::E) in the Kotlin universe.
11091127
// If this has happened, erase the type again to get the correct Java signature.
1110-
val maybeAmendedForCollections = if (overridesCollectionsMethod) eraseCollectionsMethodParameterType(it.value.type, name, it.index) else it.value.type
1128+
val maybeAmendedForCollections = if (overridesCollectionsMethod) eraseCollectionsMethodParameterType(it.value, name, it.index) else it.value
11111129
// Add any wildcard types that the Kotlin compiler would add in the Java lowering of this function:
11121130
val withAddedWildcards = addJavaLoweringWildcards(maybeAmendedForCollections, addParameterWildcardsByDefault, javaSignature?.let { sig -> getJavaValueParameterType(sig, it.index) })
11131131
// Now substitute any class type parameters in:
@@ -1117,7 +1135,7 @@ open class KotlinUsesExtractor(
11171135
val maybeErased = if (functionTypeParameters.isEmpty()) maybeSubbed else erase(maybeSubbed)
11181136
"{${useType(maybeErased).javaResult.id}}"
11191137
}
1120-
val paramTypeIds = allParams.withIndex().joinToString(separator = ",", transform = getIdForFunctionLabel)
1138+
val paramTypeIds = allParamTypes.withIndex().joinToString(separator = ",", transform = getIdForFunctionLabel)
11211139
val labelReturnType =
11221140
if (name == "<init>")
11231141
pluginContext.irBuiltIns.unitType
@@ -1551,7 +1569,7 @@ open class KotlinUsesExtractor(
15511569
* Note that `Array<T>` is retained (with `T` itself erased) because these are expected to be lowered to Java
15521570
* arrays, which are not generic.
15531571
*/
1554-
private fun erase (t: IrType): IrType {
1572+
fun erase (t: IrType): IrType {
15551573
if (t is IrSimpleType) {
15561574
val classifier = t.classifier
15571575
val owner = classifier.owner
@@ -1578,6 +1596,8 @@ open class KotlinUsesExtractor(
15781596
private fun eraseTypeParameter(t: IrTypeParameter) =
15791597
erase(t.superTypes[0])
15801598

1599+
fun getValueParameterLabel(parentId: Label<out DbElement>?, idx: Int) = "@\"params;{$parentId};$idx\""
1600+
15811601
/**
15821602
* Gets the label for `vp` in the context of function instance `parent`, or in that of its declaring function if
15831603
* `parent` is null.
@@ -1607,7 +1627,7 @@ open class KotlinUsesExtractor(
16071627
logger.error("Unexpected negative index for parameter")
16081628
}
16091629

1610-
return "@\"params;{$parentId};$idx\""
1630+
return getValueParameterLabel(parentId, idx)
16111631
}
16121632

16131633

@@ -1669,7 +1689,7 @@ open class KotlinUsesExtractor(
16691689
val returnType = getter?.returnType ?: setter?.valueParameters?.singleOrNull()?.type ?: pluginContext.irBuiltIns.unitType
16701690
val typeParams = getFunctionTypeParameters(func)
16711691

1672-
getFunctionLabel(p.parent, parentId, p.name.asString(), listOf(), returnType, ext, typeParams, classTypeArgsIncludingOuterClasses, overridesCollectionsMethod = false, javaSignature = null, addParameterWildcardsByDefault = false, prefix = "property")
1692+
getFunctionLabel(p.parent, parentId, p.name.asString(), listOf(), returnType, ext.type, typeParams, classTypeArgsIncludingOuterClasses, overridesCollectionsMethod = false, javaSignature = null, addParameterWildcardsByDefault = false, prefix = "property")
16731693
}
16741694
}
16751695

java/ql/consistency-queries/visibility.ql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ string visibility(Method m) {
1010
result = "internal" and m.isInternal()
1111
}
1212

13+
predicate hasPackagePrivateVisibility(Method m) { not exists(visibility(m)) }
14+
1315
// TODO: This ought to check more than just methods
1416
from Method m
1517
where
@@ -19,5 +21,6 @@ where
1921
// TODO: This ought to have visibility information
2022
not m.getName() = "<clinit>" and
2123
count(visibility(m)) != 1 and
22-
not (count(visibility(m)) = 2 and visibility(m) = "public" and visibility(m) = "internal") // This is a reasonable result, since the JVM symbol is declared public, but Kotlin metadata flags it as internal
24+
not (count(visibility(m)) = 2 and visibility(m) = "public" and visibility(m) = "internal") and // This is a reasonable result, since the JVM symbol is declared public, but Kotlin metadata flags it as internal
25+
not (hasPackagePrivateVisibility(m) and m.getName().matches("%$default")) // This is a reasonable result because the $default forwarder methods corresponding to private methods are package-private.
2326
select m, concat(visibility(m), ", ")

java/ql/integration-tests/posix-only/kotlin/gradle_kotlinx_serialization/PrintAst.expected

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,48 @@ app/src/main/kotlin/testProject/App.kt:
6565
# 0| -3: [TypeAccess] Project
6666
# 0| 0: [VarAccess] name
6767
# 0| 1: [VarAccess] language
68-
# 0| 5: [Method] equals
68+
# 0| 5: [Method] copy$default
69+
# 0| 3: [TypeAccess] Project
70+
#-----| 4: (Parameters)
71+
# 0| 0: [Parameter] p0
72+
# 0| 0: [TypeAccess] Project
73+
# 0| 1: [Parameter] p1
74+
# 0| 0: [TypeAccess] String
75+
# 0| 2: [Parameter] p2
76+
# 0| 0: [TypeAccess] int
77+
# 0| 3: [Parameter] p3
78+
# 0| 0: [TypeAccess] int
79+
# 0| 4: [Parameter] p4
80+
# 0| 0: [TypeAccess] Object
81+
# 0| 5: [BlockStmt] { ... }
82+
# 0| 0: [IfStmt] if (...)
83+
# 0| 0: [EQExpr] ... == ...
84+
# 0| 0: [AndBitwiseExpr] ... & ...
85+
# 0| 0: [IntegerLiteral] 1
86+
# 0| 1: [VarAccess] p3
87+
# 0| 1: [IntegerLiteral] 0
88+
# 0| 1: [ExprStmt] <Expr>;
89+
# 0| 0: [AssignExpr] ...=...
90+
# 0| 0: [VarAccess] p1
91+
# 0| 1: [VarAccess] p0.name
92+
# 0| -1: [VarAccess] p0
93+
# 0| 1: [IfStmt] if (...)
94+
# 0| 0: [EQExpr] ... == ...
95+
# 0| 0: [AndBitwiseExpr] ... & ...
96+
# 0| 0: [IntegerLiteral] 2
97+
# 0| 1: [VarAccess] p3
98+
# 0| 1: [IntegerLiteral] 0
99+
# 0| 1: [ExprStmt] <Expr>;
100+
# 0| 0: [AssignExpr] ...=...
101+
# 0| 0: [VarAccess] p2
102+
# 0| 1: [VarAccess] p0.language
103+
# 0| -1: [VarAccess] p0
104+
# 0| 2: [ReturnStmt] return ...
105+
# 0| 0: [MethodAccess] copy(...)
106+
# 0| -1: [VarAccess] p0
107+
# 0| 0: [VarAccess] p1
108+
# 0| 1: [VarAccess] p2
109+
# 0| 6: [Method] equals
69110
# 0| 3: [TypeAccess] boolean
70111
#-----| 4: (Parameters)
71112
# 0| 0: [Parameter] other
@@ -114,7 +155,7 @@ app/src/main/kotlin/testProject/App.kt:
114155
# 0| 0: [BooleanLiteral] false
115156
# 0| 5: [ReturnStmt] return ...
116157
# 0| 0: [BooleanLiteral] true
117-
# 0| 6: [Method] hashCode
158+
# 0| 7: [Method] hashCode
118159
# 0| 3: [TypeAccess] int
119160
# 0| 5: [BlockStmt] { ... }
120161
# 0| 0: [LocalVariableDeclStmt] var ...;
@@ -134,7 +175,7 @@ app/src/main/kotlin/testProject/App.kt:
134175
# 0| -1: [ThisAccess] this
135176
# 0| 2: [ReturnStmt] return ...
136177
# 0| 0: [VarAccess] result
137-
# 0| 7: [Method] toString
178+
# 0| 8: [Method] toString
138179
# 0| 3: [TypeAccess] String
139180
# 0| 5: [BlockStmt] { ... }
140181
# 0| 0: [ReturnStmt] return ...
@@ -148,7 +189,7 @@ app/src/main/kotlin/testProject/App.kt:
148189
# 0| 5: [VarAccess] this.language
149190
# 0| -1: [ThisAccess] this
150191
# 0| 6: [StringLiteral] )
151-
# 0| 8: [Method] write$Self
192+
# 0| 9: [Method] write$Self
152193
# 0| 3: [TypeAccess] Unit
153194
#-----| 4: (Parameters)
154195
# 0| 0: [Parameter] self
@@ -172,7 +213,7 @@ app/src/main/kotlin/testProject/App.kt:
172213
# 7| 1: [IntegerLiteral] 1
173214
# 7| 2: [MethodAccess] getLanguage(...)
174215
# 7| -1: [VarAccess] self
175-
# 7| 9: [Class] $serializer
216+
# 7| 10: [Class] $serializer
176217
# 0| 1: [FieldDeclaration] SerialDescriptor descriptor;
177218
# 0| -1: [TypeAccess] SerialDescriptor
178219
# 0| 2: [Method] childSerializers
@@ -384,7 +425,7 @@ app/src/main/kotlin/testProject/App.kt:
384425
# 7| -1: [ThisAccess] $serializer.this
385426
# 7| 0: [TypeAccess] $serializer
386427
# 7| 1: [VarAccess] tmp0_serialDesc
387-
# 7| 10: [Class] Companion
428+
# 7| 11: [Class] Companion
388429
# 0| 1: [Method] serializer
389430
# 0| 3: [TypeAccess] KSerializer<Project>
390431
# 0| 0: [TypeAccess] Project
@@ -395,7 +436,7 @@ app/src/main/kotlin/testProject/App.kt:
395436
# 7| 5: [BlockStmt] { ... }
396437
# 7| 0: [SuperConstructorInvocationStmt] super(...)
397438
# 7| 1: [BlockStmt] { ... }
398-
# 8| 11: [Constructor] Project
439+
# 8| 12: [Constructor] Project
399440
#-----| 4: (Parameters)
400441
# 8| 0: [Parameter] name
401442
# 8| 0: [TypeAccess] String
@@ -410,21 +451,21 @@ app/src/main/kotlin/testProject/App.kt:
410451
# 8| 1: [ExprStmt] <Expr>;
411452
# 8| 0: [KtInitializerAssignExpr] ...=...
412453
# 8| 0: [VarAccess] language
413-
# 8| 12: [FieldDeclaration] String name;
454+
# 8| 13: [FieldDeclaration] String name;
414455
# 8| -1: [TypeAccess] String
415456
# 8| 0: [VarAccess] name
416-
# 8| 13: [Method] getName
457+
# 8| 14: [Method] getName
417458
# 8| 3: [TypeAccess] String
418459
# 8| 5: [BlockStmt] { ... }
419460
# 8| 0: [ReturnStmt] return ...
420461
# 8| 0: [VarAccess] this.name
421462
# 8| -1: [ThisAccess] this
422-
# 8| 14: [Method] getLanguage
463+
# 8| 15: [Method] getLanguage
423464
# 8| 3: [TypeAccess] int
424465
# 8| 5: [BlockStmt] { ... }
425466
# 8| 0: [ReturnStmt] return ...
426467
# 8| 0: [VarAccess] this.language
427468
# 8| -1: [ThisAccess] this
428-
# 8| 15: [FieldDeclaration] int language;
469+
# 8| 16: [FieldDeclaration] int language;
429470
# 8| -1: [TypeAccess] int
430471
# 8| 0: [VarAccess] language

java/ql/integration-tests/posix-only/kotlin/jvmoverloads_flow/User.java

Lines changed: 0 additions & 39 deletions
This file was deleted.

java/ql/integration-tests/posix-only/kotlin/jvmoverloads_flow/test.expected

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)