Skip to content

Commit 5ef99ca

Browse files
authored
Merge pull request github#11003 from smowton/smowton/fix/reintroduce-pointless-wildcards
Kotlin: reintroduce pointless wildcards when a Java declaration explicitly uses them
2 parents 6533e2e + 28b6e26 commit 5ef99ca

File tree

7 files changed

+38
-3
lines changed

7 files changed

+38
-3
lines changed

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -960,11 +960,13 @@ open class KotlinUsesExtractor(
960960
((t as? IrSimpleType)?.classOrNull?.owner?.isFinalClass) != true
961961
}
962962

963-
private fun wildcardAdditionAllowed(v: Variance, t: IrType, addByDefault: Boolean) =
963+
private fun wildcardAdditionAllowed(v: Variance, t: IrType, addByDefault: Boolean, javaVariance: Variance?) =
964964
when {
965965
t.hasAnnotation(jvmWildcardAnnotation) -> true
966966
!addByDefault -> false
967967
t.hasAnnotation(jvmWildcardSuppressionAnnotation) -> false
968+
// If a Java declaration specifies a variance, introduce it even if it's pointless (e.g. ? extends FinalClass, or ? super Object)
969+
javaVariance == v -> true
968970
v == Variance.IN_VARIANCE -> !(t.isNullableAny() || t.isAny())
969971
v == Variance.OUT_VARIANCE -> extendsAdditionAllowed(t)
970972
else -> false
@@ -973,14 +975,21 @@ open class KotlinUsesExtractor(
973975
private fun addJavaLoweringArgumentWildcards(p: IrTypeParameter, t: IrTypeArgument, addByDefault: Boolean, javaType: JavaType?): IrTypeArgument =
974976
(t as? IrTypeProjection)?.let {
975977
val newBase = addJavaLoweringWildcards(it.type, addByDefault, javaType)
978+
// Note javaVariance == null means we don't have a Java type to conform to -- for example if this is a Kotlin source definition.
979+
val javaVariance = javaType?.let { jType ->
980+
when (jType) {
981+
is JavaWildcardType -> if (jType.isExtends) Variance.OUT_VARIANCE else Variance.IN_VARIANCE
982+
else -> Variance.INVARIANT
983+
}
984+
}
976985
val newVariance =
977986
if (it.variance == Variance.INVARIANT &&
978987
p.variance != Variance.INVARIANT &&
979988
// The next line forbids inferring a wildcard type when we have a corresponding Java type with conflicting variance.
980989
// For example, Java might declare f(Comparable<CharSequence> cs), in which case we shouldn't add a `? super ...`
981990
// wildcard. Note if javaType is unknown (e.g. this is a Kotlin source element), we assume wildcards should be added.
982-
(javaType?.let { jt -> jt is JavaWildcardType && jt.isExtends == (p.variance == Variance.OUT_VARIANCE) } != false) &&
983-
wildcardAdditionAllowed(p.variance, it.type, addByDefault))
991+
(javaVariance == null || javaVariance == p.variance) &&
992+
wildcardAdditionAllowed(p.variance, it.type, addByDefault, javaVariance))
984993
p.variance
985994
else
986995
it.variance
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
public class Test {
2+
3+
// This gets mapped to kotlin.Iterable<out T>, meaning we must reintroduce the use-site extends variance to get a type consistent with Java.
4+
public static void needlessExtends(Iterable<? extends String> l) { }
5+
6+
// This type is defined KotlinConsumer<in T>, meaning we must reintroduce the use-site extends variance to get a type consistent with Java.
7+
public static void needlessSuper(KotlinConsumer<? super Object> l) { }
8+
9+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public class KotlinConsumer<in T> { }
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| Test.java:4:22:4:36 | needlessExtends | file:///modules/java.base/java/lang/Iterable.class:0:0:0:0 | Iterable<? extends String> |
2+
| Test.java:7:22:7:34 | needlessSuper | build1/KotlinConsumer.class:0:0:0:0 | KotlinConsumer<? super Object> |
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from create_database_utils import *
2+
3+
os.mkdir('build1')
4+
os.mkdir('build2')
5+
run_codeql_database_create(["kotlinc kConsumer.kt -d build1", "javac Test.java -cp build1 -d build2", "kotlinc user.kt -cp build1:build2"], lang="java")
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import java
2+
3+
from Method m
4+
where m.fromSource()
5+
select m, m.getAParamType()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fun f() {
2+
Test.needlessExtends(null)
3+
Test.needlessSuper(null)
4+
}

0 commit comments

Comments
 (0)