Skip to content

Commit 44cf260

Browse files
authored
Merge pull request #9571 from smowton/smowton/fix/array-variance-lowering
Kotlin: Implement array type variance lowering
2 parents cc354ca + 2d57d3a commit 44cf260

File tree

5 files changed

+265
-6
lines changed

5 files changed

+265
-6
lines changed

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

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -637,14 +637,31 @@ open class KotlinUsesExtractor(
637637
)
638638

639639
(s.isBoxedArray && s.arguments.isNotEmpty()) || s.isPrimitiveArray() -> {
640-
var dimensions = 1
641-
var isPrimitiveArray = s.isPrimitiveArray()
642-
val componentType = s.getArrayElementType(pluginContext.irBuiltIns)
643-
var elementType = componentType
640+
641+
fun replaceComponentTypeWithAny(t: IrSimpleType, dimensions: Int): IrSimpleType =
642+
if (dimensions == 0)
643+
pluginContext.irBuiltIns.anyType as IrSimpleType
644+
else
645+
t.toBuilder().also { it.arguments = (it.arguments[0] as IrTypeProjection)
646+
.let { oldArg ->
647+
listOf(makeTypeProjection(replaceComponentTypeWithAny(oldArg.type as IrSimpleType, dimensions - 1), oldArg.variance))
648+
}
649+
}.buildSimpleType()
650+
651+
var componentType = s.getArrayElementType(pluginContext.irBuiltIns)
652+
var isPrimitiveArray = false
653+
var dimensions = 0
654+
var elementType: IrType = s
644655
while (elementType.isBoxedArray || elementType.isPrimitiveArray()) {
645656
dimensions++
646-
if(elementType.isPrimitiveArray())
657+
if (elementType.isPrimitiveArray())
647658
isPrimitiveArray = true
659+
if (((elementType as IrSimpleType).arguments.singleOrNull() as? IrTypeProjection)?.variance == Variance.IN_VARIANCE) {
660+
// Because Java's arrays are covariant, Kotlin will render Array<in X> as Object[], Array<Array<in X>> as Object[][] etc.
661+
componentType = replaceComponentTypeWithAny(s, dimensions - 1)
662+
elementType = pluginContext.irBuiltIns.anyType as IrSimpleType
663+
break
664+
}
648665
elementType = elementType.getArrayElementType(pluginContext.irBuiltIns)
649666
}
650667

@@ -857,13 +874,33 @@ open class KotlinUsesExtractor(
857874
private val jvmWildcardAnnotation = FqName("kotlin.jvm.JvmWildcard")
858875
private val jvmWildcardSuppressionAnnotaton = FqName("kotlin.jvm.JvmSuppressWildcards")
859876

877+
private fun arrayExtendsAdditionAllowed(t: IrSimpleType): Boolean =
878+
// Note the array special case includes Array<*>, which does permit adding `? extends ...` (making `? extends Object[]` in that case)
879+
// Surprisingly Array<in X> does permit this as well, though the contravariant array lowers to Object[] so this ends up `? extends Object[]` as well.
880+
t.arguments[0].let {
881+
when (it) {
882+
is IrTypeProjection -> when (it.variance) {
883+
Variance.INVARIANT -> false
884+
Variance.IN_VARIANCE -> !(it.type.isAny() || it.type.isNullableAny())
885+
Variance.OUT_VARIANCE -> extendsAdditionAllowed(it.type)
886+
}
887+
else -> true
888+
}
889+
}
890+
891+
private fun extendsAdditionAllowed(t: IrType) =
892+
if (t.isBoxedArray)
893+
arrayExtendsAdditionAllowed(t as IrSimpleType)
894+
else
895+
((t as? IrSimpleType)?.classOrNull?.owner?.isFinalClass) != true
896+
860897
private fun wildcardAdditionAllowed(v: Variance, t: IrType, addByDefault: Boolean) =
861898
when {
862899
t.hasAnnotation(jvmWildcardAnnotation) -> true
863900
!addByDefault -> false
864901
t.hasAnnotation(jvmWildcardSuppressionAnnotaton) -> false
865902
v == Variance.IN_VARIANCE -> !(t.isNullableAny() || t.isAny())
866-
v == Variance.OUT_VARIANCE -> ((t as? IrSimpleType)?.classOrNull?.owner?.isFinalClass) != true
903+
v == Variance.OUT_VARIANCE -> extendsAdditionAllowed(t)
867904
else -> false
868905
}
869906

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
public class User {
2+
3+
public static void test() {
4+
5+
TakesArrayList tal = new TakesArrayList();
6+
tal.invarArray(null);
7+
// Using one method suffices to get the extractor to describe all the methods defined on takesArrayList.
8+
9+
}
10+
11+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
public class TakesArrayList {
2+
3+
// There is a Java user to flag up any problems, as if the .class file differs from my
4+
// claimed types above we'll see two overloads and two parameter types instead of one.
5+
6+
// Test how Array types with a variance should be lowered:
7+
fun invarArray(a: Array<CharSequence>) { }
8+
fun outArray(a: Array<out CharSequence>) { }
9+
fun inArray(a: Array<in CharSequence>) { }
10+
11+
fun invarInvarArray(a: Array<Array<CharSequence>>) { }
12+
fun invarOutArray(a: Array<Array<out CharSequence>>) { }
13+
fun invarInArray(a: Array<Array<in CharSequence>>) { }
14+
15+
fun outInvarArray(a: Array<out Array<CharSequence>>) { }
16+
fun outOutArray(a: Array<out Array<out CharSequence>>) { }
17+
fun outInArray(a: Array<out Array<in CharSequence>>) { }
18+
19+
fun inInvarArray(a: Array<in Array<CharSequence>>) { }
20+
fun inOutArray(a: Array<in Array<out CharSequence>>) { }
21+
fun inInArray(a: Array<in Array<in CharSequence>>) { }
22+
23+
// Test how Array type arguments with a variance should acquire implicit wildcards:
24+
fun invarArrayList(l: List<Array<CharSequence>>) { }
25+
fun outArrayList(l: List<Array<out CharSequence>>) { }
26+
fun inArrayList(l: List<Array<in CharSequence>>) { }
27+
28+
// Check the cases of nested arrays:
29+
fun invarInvarArrayList(l: List<Array<Array<CharSequence>>>) { }
30+
fun invarOutArrayList(l: List<Array<Array<out CharSequence>>>) { }
31+
fun invarInArrayList(l: List<Array<Array<in CharSequence>>>) { }
32+
fun outInvarArrayList(l: List<Array<out Array<CharSequence>>>) { }
33+
fun outOutArrayList(l: List<Array<out Array<out CharSequence>>>) { }
34+
fun outInArrayList(l: List<Array<out Array<in CharSequence>>>) { }
35+
fun inInvarArrayList(l: List<Array<in Array<CharSequence>>>) { }
36+
fun inOutArrayList(l: List<Array<in Array<out CharSequence>>>) { }
37+
fun inInArrayList(l: List<Array<in Array<in CharSequence>>>) { }
38+
39+
// Now check all of that again for Comparable, whose type parameter is contravariant:
40+
fun invarArrayComparable(c: Comparable<Array<CharSequence>>) { }
41+
fun outArrayComparable(c: Comparable<Array<out CharSequence>>) { }
42+
fun inArrayComparable(c: Comparable<Array<in CharSequence>>) { }
43+
44+
fun invarInvarArrayComparable(c: Comparable<Array<Array<CharSequence>>>) { }
45+
fun invarOutArrayComparable(c: Comparable<Array<Array<out CharSequence>>>) { }
46+
fun invarInArrayComparable(c: Comparable<Array<Array<in CharSequence>>>) { }
47+
fun outInvarArrayComparable(c: Comparable<Array<out Array<CharSequence>>>) { }
48+
fun outOutArrayComparable(c: Comparable<Array<out Array<out CharSequence>>>) { }
49+
fun outInArrayComparable(c: Comparable<Array<out Array<in CharSequence>>>) { }
50+
fun inInvarArrayComparable(c: Comparable<Array<in Array<CharSequence>>>) { }
51+
fun inOutArrayComparable(c: Comparable<Array<in Array<out CharSequence>>>) { }
52+
fun inInArrayComparable(c: Comparable<Array<in Array<in CharSequence>>>) { }
53+
54+
// ... duplicate all of that for a final array element (I choose String as a final type), which sometimes suppresses addition of `? extends ...`
55+
fun invarArrayListFinal(l: List<Array<String>>) { }
56+
fun outArrayListFinal(l: List<Array<out String>>) { }
57+
fun inArrayListFinal(l: List<Array<in String>>) { }
58+
59+
fun invarInvarArrayListFinal(l: List<Array<Array<String>>>) { }
60+
fun invarOutArrayListFinal(l: List<Array<Array<out String>>>) { }
61+
fun invarInArrayListFinal(l: List<Array<Array<in String>>>) { }
62+
fun outInvarArrayListFinal(l: List<Array<out Array<String>>>) { }
63+
fun outOutArrayListFinal(l: List<Array<out Array<out String>>>) { }
64+
fun outInArrayListFinal(l: List<Array<out Array<in String>>>) { }
65+
fun inInvarArrayListFinal(l: List<Array<in Array<String>>>) { }
66+
fun inOutArrayListFinal(l: List<Array<in Array<out String>>>) { }
67+
fun inInArrayListFinal(l: List<Array<in Array<in String>>>) { }
68+
69+
fun invarArrayComparableFinal(c: Comparable<Array<String>>) { }
70+
fun outArrayComparableFinal(c: Comparable<Array<out String>>) { }
71+
fun inArrayComparableFinal(c: Comparable<Array<in String>>) { }
72+
73+
fun invarInvarArrayComparableFinal(c: Comparable<Array<Array<String>>>) { }
74+
fun invarOutArrayComparableFinal(c: Comparable<Array<Array<out String>>>) { }
75+
fun invarInArrayComparableFinal(c: Comparable<Array<Array<in String>>>) { }
76+
fun outInvarArrayComparableFinal(c: Comparable<Array<out Array<String>>>) { }
77+
fun outOutArrayComparableFinal(c: Comparable<Array<out Array<out String>>>) { }
78+
fun outInArrayComparableFinal(c: Comparable<Array<out Array<in String>>>) { }
79+
fun inInvarArrayComparableFinal(c: Comparable<Array<in Array<String>>>) { }
80+
fun inOutArrayComparableFinal(c: Comparable<Array<in Array<out String>>>) { }
81+
fun inInArrayComparableFinal(c: Comparable<Array<in Array<in String>>>) { }
82+
83+
// ... and duplicate it once more with Any as the array element, which can suppress adding `? super ...`
84+
fun invarArrayListAny(l: List<Array<Any>>) { }
85+
fun outArrayListAny(l: List<Array<out Any>>) { }
86+
fun inArrayListAny(l: List<Array<in Any>>) { }
87+
88+
fun invarInvarArrayListAny(l: List<Array<Array<Any>>>) { }
89+
fun invarOutArrayListAny(l: List<Array<Array<out Any>>>) { }
90+
fun invarInArrayListAny(l: List<Array<Array<in Any>>>) { }
91+
fun outInvarArrayListAny(l: List<Array<out Array<Any>>>) { }
92+
fun outOutArrayListAny(l: List<Array<out Array<out Any>>>) { }
93+
fun outInArrayListAny(l: List<Array<out Array<in Any>>>) { }
94+
fun inInvarArrayListAny(l: List<Array<in Array<Any>>>) { }
95+
fun inOutArrayListAny(l: List<Array<in Array<out Any>>>) { }
96+
fun inInArrayListAny(l: List<Array<in Array<in Any>>>) { }
97+
98+
fun invarArrayComparableAny(c: Comparable<Array<Any>>) { }
99+
fun outArrayComparableAny(c: Comparable<Array<out Any>>) { }
100+
fun inArrayComparableAny(c: Comparable<Array<in Any>>) { }
101+
102+
fun invarInvarArrayComparableAny(c: Comparable<Array<Array<Any>>>) { }
103+
fun invarOutArrayComparableAny(c: Comparable<Array<Array<out Any>>>) { }
104+
fun invarInArrayComparableAny(c: Comparable<Array<Array<in Any>>>) { }
105+
fun outInvarArrayComparableAny(c: Comparable<Array<out Array<Any>>>) { }
106+
fun outOutArrayComparableAny(c: Comparable<Array<out Array<out Any>>>) { }
107+
fun outInArrayComparableAny(c: Comparable<Array<out Array<in Any>>>) { }
108+
fun inInvarArrayComparableAny(c: Comparable<Array<in Array<Any>>>) { }
109+
fun inOutArrayComparableAny(c: Comparable<Array<in Array<out Any>>>) { }
110+
fun inInArrayComparableAny(c: Comparable<Array<in Array<in Any>>>) { }
111+
112+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
broken
2+
#select
3+
| inArray | Object[] |
4+
| inArrayComparable | Comparable<? super Object[]> |
5+
| inArrayComparableAny | Comparable<? super Object[]> |
6+
| inArrayComparableFinal | Comparable<? super Object[]> |
7+
| inArrayList | List<? extends Object[]> |
8+
| inArrayListAny | List<Object[]> |
9+
| inArrayListFinal | List<? extends Object[]> |
10+
| inInArray | Object[] |
11+
| inInArrayComparable | Comparable<? super Object[]> |
12+
| inInArrayComparableAny | Comparable<? super Object[]> |
13+
| inInArrayComparableFinal | Comparable<? super Object[]> |
14+
| inInArrayList | List<? extends Object[]> |
15+
| inInArrayListAny | List<? extends Object[]> |
16+
| inInArrayListFinal | List<? extends Object[]> |
17+
| inInvarArray | Object[] |
18+
| inInvarArrayComparable | Comparable<? super Object[]> |
19+
| inInvarArrayComparableAny | Comparable<? super Object[]> |
20+
| inInvarArrayComparableFinal | Comparable<? super Object[]> |
21+
| inInvarArrayList | List<? extends Object[]> |
22+
| inInvarArrayListAny | List<? extends Object[]> |
23+
| inInvarArrayListFinal | List<? extends Object[]> |
24+
| inOutArray | Object[] |
25+
| inOutArrayComparable | Comparable<? super Object[]> |
26+
| inOutArrayComparableAny | Comparable<? super Object[]> |
27+
| inOutArrayComparableFinal | Comparable<? super Object[]> |
28+
| inOutArrayList | List<? extends Object[]> |
29+
| inOutArrayListAny | List<? extends Object[]> |
30+
| inOutArrayListFinal | List<? extends Object[]> |
31+
| invarArray | CharSequence[] |
32+
| invarArrayComparable | Comparable<? super CharSequence[]> |
33+
| invarArrayComparableAny | Comparable<? super Object[]> |
34+
| invarArrayComparableFinal | Comparable<? super String[]> |
35+
| invarArrayList | List<CharSequence[]> |
36+
| invarArrayListAny | List<Object[]> |
37+
| invarArrayListFinal | List<String[]> |
38+
| invarInArray | Object[][] |
39+
| invarInArrayComparable | Comparable<? super Object[][]> |
40+
| invarInArrayComparableAny | Comparable<? super Object[][]> |
41+
| invarInArrayComparableFinal | Comparable<? super Object[][]> |
42+
| invarInArrayList | List<Object[][]> |
43+
| invarInArrayListAny | List<Object[][]> |
44+
| invarInArrayListFinal | List<Object[][]> |
45+
| invarInvarArray | CharSequence[][] |
46+
| invarInvarArrayComparable | Comparable<? super CharSequence[][]> |
47+
| invarInvarArrayComparableAny | Comparable<? super Object[][]> |
48+
| invarInvarArrayComparableFinal | Comparable<? super String[][]> |
49+
| invarInvarArrayList | List<CharSequence[][]> |
50+
| invarInvarArrayListAny | List<Object[][]> |
51+
| invarInvarArrayListFinal | List<String[][]> |
52+
| invarOutArray | CharSequence[][] |
53+
| invarOutArrayComparable | Comparable<? super CharSequence[][]> |
54+
| invarOutArrayComparableAny | Comparable<? super Object[][]> |
55+
| invarOutArrayComparableFinal | Comparable<? super String[][]> |
56+
| invarOutArrayList | List<CharSequence[][]> |
57+
| invarOutArrayListAny | List<Object[][]> |
58+
| invarOutArrayListFinal | List<String[][]> |
59+
| outArray | CharSequence[] |
60+
| outArrayComparable | Comparable<? super CharSequence[]> |
61+
| outArrayComparableAny | Comparable<? super Object[]> |
62+
| outArrayComparableFinal | Comparable<? super String[]> |
63+
| outArrayList | List<? extends CharSequence[]> |
64+
| outArrayListAny | List<? extends Object[]> |
65+
| outArrayListFinal | List<String[]> |
66+
| outInArray | Object[][] |
67+
| outInArrayComparable | Comparable<? super Object[][]> |
68+
| outInArrayComparableAny | Comparable<? super Object[][]> |
69+
| outInArrayComparableFinal | Comparable<? super Object[][]> |
70+
| outInArrayList | List<? extends Object[][]> |
71+
| outInArrayListAny | List<Object[][]> |
72+
| outInArrayListFinal | List<? extends Object[][]> |
73+
| outInvarArray | CharSequence[][] |
74+
| outInvarArrayComparable | Comparable<? super CharSequence[][]> |
75+
| outInvarArrayComparableAny | Comparable<? super Object[][]> |
76+
| outInvarArrayComparableFinal | Comparable<? super String[][]> |
77+
| outInvarArrayList | List<CharSequence[][]> |
78+
| outInvarArrayListAny | List<Object[][]> |
79+
| outInvarArrayListFinal | List<String[][]> |
80+
| outOutArray | CharSequence[][] |
81+
| outOutArrayComparable | Comparable<? super CharSequence[][]> |
82+
| outOutArrayComparableAny | Comparable<? super Object[][]> |
83+
| outOutArrayComparableFinal | Comparable<? super String[][]> |
84+
| outOutArrayList | List<? extends CharSequence[][]> |
85+
| outOutArrayListAny | List<? extends Object[][]> |
86+
| outOutArrayListFinal | List<String[][]> |
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import java
2+
3+
class InterestingMethod extends Method {
4+
InterestingMethod() { this.getDeclaringType().getName() = "TakesArrayList" }
5+
}
6+
7+
query predicate broken(string methodName) {
8+
methodName = any(InterestingMethod m).getName() and
9+
count(Type t, InterestingMethod m | methodName = m.getName() and t = m.getAParamType() | t) != 1
10+
}
11+
12+
from InterestingMethod m
13+
select m.getName(), m.getAParamType().toString()

0 commit comments

Comments
 (0)