Kotlin: fix extraction of Java nested wildcards; wildcards in return types

This fixes two mistakes: return-type extraction not imposing a wildcard where a Java prototype explicitly uses one, and nested wildcard detection quietly failing due to not looking through a `JavaWildcardType` correctly.

I add a variant of the `kotlin_java_lowering_wildcards` test where Java prototypes are only seen from Kotlin, to be sure extraction is working as expected.
This commit is contained in:
Chris Smowton
2022-11-04 11:37:26 +00:00
parent 2bec4479e7
commit ca04779dfc
6 changed files with 36 additions and 3 deletions

View File

@@ -963,9 +963,9 @@ open class KotlinUsesExtractor(
private fun wildcardAdditionAllowed(v: Variance, t: IrType, addByDefault: Boolean, javaVariance: Variance?) =
when {
t.hasAnnotation(jvmWildcardAnnotation) -> true
!addByDefault -> false
// If a Java declaration specifies a variance, introduce it even if it's pointless (e.g. ? extends FinalClass, or ? super Object)
javaVariance == v -> true
!addByDefault -> false
v == Variance.IN_VARIANCE -> !(t.isNullableAny() || t.isAny())
v == Variance.OUT_VARIANCE -> extendsAdditionAllowed(t)
else -> false
@@ -1006,8 +1006,9 @@ open class KotlinUsesExtractor(
null
} ?: t
private fun getJavaTypeArgument(jt: JavaType, idx: Int) =
private fun getJavaTypeArgument(jt: JavaType, idx: Int): JavaType? =
when(jt) {
is JavaWildcardType -> jt.bound?.let { getJavaTypeArgument(it, idx) }
is JavaClassifierType -> jt.typeArguments.getOrNull(idx)
is JavaArrayType -> if (idx == 0) jt.componentType else null
else -> null

View File

@@ -0,0 +1,18 @@
public class JavaDefns2 {
// Currently known not to work: the Comparable<? extends X> case, which Kotlin sees as Comparable<*> because the
// wildcard goes the opposite direction to the variance declared on Comparable's type parameter.
public static void takesComparable(Comparable<CharSequence> invar, Comparable<? super CharSequence> contravar) { }
public static void takesNestedComparable(Comparable<Comparable<? super CharSequence>> innerContravar, Comparable<? super Comparable<CharSequence>> outerContravar) { }
public static void takesArrayOfComparable(Comparable<CharSequence>[] invar, Comparable<? super CharSequence>[] contravar) { }
public static Comparable<? super CharSequence> returnsWildcard() { return null; }
public static Comparable<CharSequence> returnsInvariant() { return null; }
public JavaDefns2(Comparable<CharSequence> invar, Comparable<? super CharSequence> contravar) { }
}

View File

@@ -7,4 +7,6 @@ fun user() {
JavaDefns.takesArrayOfComparable(acs, acs)
val constructed = JavaDefns(cs, cs)
JavaDefns2.takesComparable(cs, cs)
}

View File

@@ -8,6 +8,16 @@
| JavaDefns | takesComparable | invar | Comparable<CharSequence> |
| JavaDefns | takesNestedComparable | innerContravar | Comparable<Comparable<? super CharSequence>> |
| JavaDefns | takesNestedComparable | outerContravar | Comparable<? super Comparable<CharSequence>> |
| JavaDefns2 | JavaDefns2 | p0 | Comparable<CharSequence> |
| JavaDefns2 | JavaDefns2 | p1 | Comparable<? super CharSequence> |
| JavaDefns2 | returnsInvariant | return | Comparable<CharSequence> |
| JavaDefns2 | returnsWildcard | return | Comparable<? super CharSequence> |
| JavaDefns2 | takesArrayOfComparable | p0 | Comparable<CharSequence>[] |
| JavaDefns2 | takesArrayOfComparable | p1 | Comparable<? super CharSequence>[] |
| JavaDefns2 | takesComparable | p0 | Comparable<CharSequence> |
| JavaDefns2 | takesComparable | p1 | Comparable<? super CharSequence> |
| JavaDefns2 | takesNestedComparable | p0 | Comparable<Comparable<? super CharSequence>> |
| JavaDefns2 | takesNestedComparable | p1 | Comparable<? super Comparable<CharSequence>> |
| KotlinDefns | returnsContravar | return | Comparable<CharSequence> |
| KotlinDefns | returnsContravarForced | return | Comparable<? super CharSequence> |
| KotlinDefns | returnsCovar | return | List<CharSequence> |

View File

@@ -1,3 +1,5 @@
from create_database_utils import *
# Compile the JavaDefns2 copy outside tracing, to make sure the Kotlin view of it matches the Java view seen by the traced javac compilation of JavaDefns.java below.
runSuccessfully(["javac", "JavaDefns2.java"])
run_codeql_database_create(["kotlinc kotlindefns.kt", "javac JavaUser.java JavaDefns.java -cp .", "kotlinc -cp . kotlinuser.kt"], lang="java")

View File

@@ -1,7 +1,7 @@
import java
predicate isInterestingClass(Class c) {
[c, c.(NestedType).getEnclosingType()].getName().matches(["KotlinDefns%", "JavaDefns"])
[c, c.(NestedType).getEnclosingType()].getName().matches(["KotlinDefns%", "JavaDefns%"])
}
from Callable c, string paramOrReturnName, Type paramOrReturnType