From 4ef57c120f9244ffb6994db8e3933270f48779b8 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Thu, 24 Sep 2020 12:46:47 +0300 Subject: [PATCH] [FIR] Consider variance of type parameters during java type enhancement #KT-41940 Fixed --- .../testData/resolve/arguments/kt41940.kt | 23 +++++++ .../testData/resolve/arguments/kt41940.txt | 4 ++ .../fir/FirDiagnosticsTestGenerated.java | 5 ++ ...DiagnosticsWithLightTreeTestGenerated.java | 5 ++ .../kotlin/fir/java/JavaSymbolProvider.kt | 2 +- .../jetbrains/kotlin/fir/java/JavaUtils.kt | 61 +++++++++++++------ .../tests/j+k/wrongVarianceInJava.fir.kt | 4 +- .../irText/firProblems/v8arrayToList.fir.txt | 2 +- .../MethodWithMappedClasses.fir.txt | 4 +- .../MethodWithTypeParameters.fir.txt | 2 +- 10 files changed, 88 insertions(+), 24 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/arguments/kt41940.kt create mode 100644 compiler/fir/analysis-tests/testData/resolve/arguments/kt41940.txt diff --git a/compiler/fir/analysis-tests/testData/resolve/arguments/kt41940.kt b/compiler/fir/analysis-tests/testData/resolve/arguments/kt41940.kt new file mode 100644 index 00000000000..4e905829852 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/arguments/kt41940.kt @@ -0,0 +1,23 @@ +// ISSUE: KT-41940 + +// FILE: Matcher.java + +public interface Matcher {} + +// FILE: Matchers.java + +public class Matchers { + public static Matcher> hasItem(T item) { + return null; + } + + public static void assertThat(T actual, Matcher matcher) {} +} + +// FILE: main.kt + +import Matchers.* + +fun test(list: List, string: String) { + assertThat(list, ..kotlin.collections.Iterable<*>?!>..Matcher..kotlin.collections.Iterable<*>?!>?!")!>hasItem(string)) +} diff --git a/compiler/fir/analysis-tests/testData/resolve/arguments/kt41940.txt b/compiler/fir/analysis-tests/testData/resolve/arguments/kt41940.txt new file mode 100644 index 00000000000..8ce9ec7d6fc --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/arguments/kt41940.txt @@ -0,0 +1,4 @@ +FILE: main.kt + public final fun test(list: R|kotlin/collections/List|, string: R|kotlin/String|): R|kotlin/Unit| { + R|/Matchers.assertThat|, kotlin/collections/List?>!|>(R|/list|, R|/Matchers.hasItem|!|>(R|/string|)) + } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index d8edca83afc..4bc91f0ef2a 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -520,6 +520,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { runTest("compiler/fir/analysis-tests/testData/resolve/arguments/javaArrayVariance.kt"); } + @TestMetadata("kt41940.kt") + public void testKt41940() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/arguments/kt41940.kt"); + } + @TestMetadata("lambda.kt") public void testLambda() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/arguments/lambda.kt"); diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java index 3e15e033e89..742bb71f633 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java @@ -520,6 +520,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/arguments/javaArrayVariance.kt"); } + @TestMetadata("kt41940.kt") + public void testKt41940() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/arguments/kt41940.kt"); + } + @TestMetadata("lambda.kt") public void testLambda() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/arguments/lambda.kt"); diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt index b893a392ceb..30eab63b8cc 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt @@ -196,7 +196,6 @@ class JavaSymbolProvider( isInline = false isFun = classKind == ClassKind.INTERFACE } - addAnnotationsFrom(this@JavaSymbolProvider.session, javaClass, javaTypeParameterStack) // TODO: may be we can process fields & methods later. // However, they should be built up to override resolve stage for (javaField in javaClass.fields) { @@ -257,6 +256,7 @@ class JavaSymbolProvider( ) } ) + firJavaClass.addAnnotationsFrom(this@JavaSymbolProvider.session, javaClass, javaTypeParameterStack) return firJavaClass } diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt index 0a4c3cd5577..3510d170105 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.diagnostics.* import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.* +import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass import org.jetbrains.kotlin.fir.java.declarations.buildJavaValueParameter import org.jetbrains.kotlin.fir.java.enhancement.readOnlyToMutable import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference @@ -40,6 +41,7 @@ import org.jetbrains.kotlin.load.java.typeEnhancement.TypeComponentPosition import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.Variance.* +import org.jetbrains.kotlin.utils.addToStdlib.runIf internal val JavaModifierListOwner.modality: Modality get() = when { @@ -70,7 +72,7 @@ internal fun FirTypeRef.toConeKotlinTypeProbablyFlexible( when (this) { is FirResolvedTypeRef -> type is FirJavaTypeRef -> { - type.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack) + type.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes = false) } else -> ConeKotlinErrorType( ConeSimpleDiagnostic("Unexpected type reference in JavaClassUseSiteMemberScope: ${this::class.java}", DiagnosticKind.Java) @@ -93,9 +95,9 @@ internal fun JavaClassifierType.toFirResolvedTypeRef( ): FirResolvedTypeRef { val coneType = if (isForSupertypes) - toConeKotlinTypeForFlexibleBound(session, javaTypeParameterStack, isLowerBound = true, forTypeParameterBounds) + toConeKotlinTypeForFlexibleBound(session, javaTypeParameterStack, isLowerBound = true, forTypeParameterBounds, isForSupertypes) else - toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, forTypeParameterBounds) + toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, forTypeParameterBounds, isForSupertypes) return buildResolvedTypeRef { type = coneType @@ -106,7 +108,8 @@ internal fun JavaClassifierType.toFirResolvedTypeRef( internal fun JavaType?.toConeKotlinTypeWithoutEnhancement( session: FirSession, javaTypeParameterStack: JavaTypeParameterStack, - forAnnotationValueParameter: Boolean = false + forAnnotationValueParameter: Boolean = false, + isForSupertypes: Boolean = false ): ConeKotlinType { return when (this) { is JavaClassifierType -> { @@ -123,9 +126,9 @@ internal fun JavaType?.toConeKotlinTypeWithoutEnhancement( classId.toConeKotlinType(emptyArray(), isNullable = false) } is JavaArrayType -> { - toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, forAnnotationValueParameter) + toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, forAnnotationValueParameter, isForSupertypes) } - is JavaWildcardType -> bound?.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack) ?: run { + is JavaWildcardType -> bound?.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes = isForSupertypes) ?: run { StandardClassIds.Any.toConeFlexibleType(emptyArray()) } null -> { @@ -138,12 +141,13 @@ internal fun JavaType?.toConeKotlinTypeWithoutEnhancement( private fun JavaArrayType.toConeKotlinTypeWithoutEnhancement( session: FirSession, javaTypeParameterStack: JavaTypeParameterStack, - forAnnotationValueParameter: Boolean = false + forAnnotationValueParameter: Boolean = false, + isForSupertypes: Boolean ): ConeFlexibleType { val componentType = componentType return if (componentType !is JavaPrimitiveType) { val classId = StandardClassIds.Array - val argumentType = componentType.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, forAnnotationValueParameter) + val argumentType = componentType.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, forAnnotationValueParameter, isForSupertypes) classId.toConeFlexibleType( arrayOf(argumentType), typeArgumentsForUpper = arrayOf(ConeKotlinTypeProjectionOut(argumentType)) @@ -168,6 +172,7 @@ private fun JavaClassifierType.toConeKotlinTypeWithoutEnhancement( session: FirSession, javaTypeParameterStack: JavaTypeParameterStack, forTypeParameterBounds: Boolean = false, + isForSupertypes: Boolean = false, forAnnotationValueParameter: Boolean = false ): ConeKotlinType { val lowerBound = toConeKotlinTypeForFlexibleBound( @@ -175,6 +180,7 @@ private fun JavaClassifierType.toConeKotlinTypeWithoutEnhancement( javaTypeParameterStack, isLowerBound = true, forTypeParameterBounds, + isForSupertypes, forAnnotationValueParameter = forAnnotationValueParameter ) val upperBound = @@ -183,6 +189,7 @@ private fun JavaClassifierType.toConeKotlinTypeWithoutEnhancement( javaTypeParameterStack, isLowerBound = false, forTypeParameterBounds, + isForSupertypes, lowerBound, forAnnotationValueParameter = forAnnotationValueParameter ) @@ -285,6 +292,7 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound( javaTypeParameterStack: JavaTypeParameterStack, isLowerBound: Boolean, forTypeParameterBounds: Boolean, + isForSupertypes: Boolean, lowerBound: ConeLookupTagBasedType? = null, forAnnotationValueParameter: Boolean = false ): ConeLookupTagBasedType { @@ -308,8 +316,9 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound( ) } - val mappedTypeArguments = if (isRaw) { + val classSymbol = session.firSymbolProvider.getClassLikeSymbolByFqName(classId) as? FirRegularClassSymbol + val mappedTypeArguments = if (isRaw) { val defaultArgs = (1..classifier.typeParameters.size).map { ConeStarProjection } if (forTypeParameterBounds) { @@ -317,15 +326,18 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound( // to create a proper raw type arguments, we should take class parameters some time defaultArgs } else { - val classSymbol = session.firSymbolProvider.getClassLikeSymbolByFqName(classId) as? FirRegularClassSymbol val position = if (isLowerBound) TypeComponentPosition.FLEXIBLE_LOWER else TypeComponentPosition.FLEXIBLE_UPPER classSymbol?.fir?.createRawArguments(defaultArgs, position) ?: defaultArgs } } else { - typeArguments.map { argument -> + val typeParameters = runIf(!forTypeParameterBounds && !isForSupertypes) { classSymbol?.fir?.typeParameters } ?: emptyList() + + typeArguments.indices.map { index -> + val argument = typeArguments[index] + val parameter = typeParameters.getOrNull(index)?.symbol?.fir argument.toConeProjectionWithoutEnhancement( - session, javaTypeParameterStack, boundTypeParameter = null + session, javaTypeParameterStack, boundTypeParameter = parameter, isForSupertypes = isForSupertypes ) } } @@ -371,9 +383,23 @@ internal fun JavaAnnotation.toFirAnnotationCall( @FirBuilderDsl internal fun FirAnnotationContainerBuilder.addAnnotationsFrom( session: FirSession, javaAnnotationOwner: JavaAnnotationOwner, javaTypeParameterStack: JavaTypeParameterStack +) { + annotations.addAnnotationsFrom(session, javaAnnotationOwner, javaTypeParameterStack) +} + +internal fun FirJavaClass.addAnnotationsFrom( + session: FirSession, javaAnnotationOwner: JavaAnnotationOwner, javaTypeParameterStack: JavaTypeParameterStack +) { + annotations.addAnnotationsFrom(session, javaAnnotationOwner, javaTypeParameterStack) +} + +private fun MutableList.addAnnotationsFrom( + session: FirSession, + javaAnnotationOwner: JavaAnnotationOwner, + javaTypeParameterStack: JavaTypeParameterStack ) { for (annotation in javaAnnotationOwner.annotations) { - annotations += annotation.toFirAnnotationCall(session, javaTypeParameterStack) + this += annotation.toFirAnnotationCall(session, javaTypeParameterStack) } } @@ -393,7 +419,8 @@ internal fun JavaValueParameter.toFirValueParameter( private fun JavaType?.toConeProjectionWithoutEnhancement( session: FirSession, javaTypeParameterStack: JavaTypeParameterStack, - boundTypeParameter: FirTypeParameter? + boundTypeParameter: FirTypeParameter?, + isForSupertypes: Boolean = false ): ConeTypeProjection { return when (this) { null -> ConeStarProjection @@ -404,7 +431,7 @@ private fun JavaType?.toConeProjectionWithoutEnhancement( if (bound == null || parameterVariance != INVARIANT && parameterVariance != argumentVariance) { ConeStarProjection } else { - val boundType = bound.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack) + val boundType = bound.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes = isForSupertypes) if (argumentVariance == OUT_VARIANCE) { ConeKotlinTypeProjectionOut(boundType) } else { @@ -412,8 +439,8 @@ private fun JavaType?.toConeProjectionWithoutEnhancement( } } } - is JavaClassifierType -> toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack) - is JavaArrayType -> toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack) + is JavaClassifierType -> toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes) + is JavaArrayType -> toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes = isForSupertypes) else -> ConeClassErrorType(ConeSimpleDiagnostic("Unexpected type argument: $this", DiagnosticKind.Java)) } } diff --git a/compiler/testData/diagnostics/tests/j+k/wrongVarianceInJava.fir.kt b/compiler/testData/diagnostics/tests/j+k/wrongVarianceInJava.fir.kt index 29911c2d792..0c3a1fd26da 100644 --- a/compiler/testData/diagnostics/tests/j+k/wrongVarianceInJava.fir.kt +++ b/compiler/testData/diagnostics/tests/j+k/wrongVarianceInJava.fir.kt @@ -19,6 +19,6 @@ class In { } fun test() { - A.foo().x() checkType { _() } - A.bar().y(null) + A.foo().x() checkType { _() } + A.bar().y(null) } diff --git a/compiler/testData/ir/irText/firProblems/v8arrayToList.fir.txt b/compiler/testData/ir/irText/firProblems/v8arrayToList.fir.txt index d195f2578d9..7d10168abdb 100644 --- a/compiler/testData/ir/irText/firProblems/v8arrayToList.fir.txt +++ b/compiler/testData/ir/irText/firProblems/v8arrayToList.fir.txt @@ -24,7 +24,7 @@ FILE fqName: fileName:/v8arrayToList.kt CONSTRUCTOR_CALL 'public constructor () [primary] declared in .V8Array' type=.V8Array origin=null VAR name:list type:kotlin.collections.List [val] TYPE_OP type=kotlin.collections.List origin=CAST typeOperand=kotlin.collections.List - CALL 'public open fun toList (array: .V8Array?): kotlin.collections.List? declared in .Utils' type=IrErrorType origin=null + CALL 'public open fun toList (array: .V8Array?): kotlin.collections.List<*>? declared in .Utils' type=kotlin.collections.List<*>? origin=null array: GET_VAR 'val array: .V8Array [val] declared in .box' type=.V8Array origin=null RETURN type=kotlin.Nothing from='public final fun box (): kotlin.String declared in ' CALL 'public abstract fun get (index: kotlin.Int): E of kotlin.collections.List [operator] declared in kotlin.collections.List' type=kotlin.String origin=null diff --git a/compiler/testData/loadJava/compiledJava/kotlinSignature/MethodWithMappedClasses.fir.txt b/compiler/testData/loadJava/compiledJava/kotlinSignature/MethodWithMappedClasses.fir.txt index 28c7ec5e4fa..cd191791590 100644 --- a/compiler/testData/loadJava/compiledJava/kotlinSignature/MethodWithMappedClasses.fir.txt +++ b/compiler/testData/loadJava/compiledJava/kotlinSignature/MethodWithMappedClasses.fir.txt @@ -1,7 +1,7 @@ public open class MethodWithMappedClasses : R|kotlin/Any| { - public open fun !|> copy(dest: R|ft!>, kotlin/collections/List!>?>!|, src: R|ft!>, kotlin/collections/List!>?>!|): R|kotlin/Unit| + public open fun !|> copy(dest: R|ft!>, kotlin/collections/List<*>?>!|, src: R|ft!>, kotlin/collections/List!>?>!|): R|kotlin/Unit| - public open fun !|> copyMap(dest: R|ft!, in ft!>, kotlin/collections/Map!, in ft!>?>!|, src: R|ft!, ft!>, kotlin/collections/Map!, ft!>?>!|): R|kotlin/Unit| + public open fun !|> copyMap(dest: R|ft!, in ft!>, kotlin/collections/Map!, *>?>!|, src: R|ft!, ft!>, kotlin/collections/Map!, ft!>?>!|): R|kotlin/Unit| public constructor(): R|test/MethodWithMappedClasses| diff --git a/compiler/testData/loadJava/compiledJava/kotlinSignature/MethodWithTypeParameters.fir.txt b/compiler/testData/loadJava/compiledJava/kotlinSignature/MethodWithTypeParameters.fir.txt index dccc0ba9c57..bd4975c1c2f 100644 --- a/compiler/testData/loadJava/compiledJava/kotlinSignature/MethodWithTypeParameters.fir.txt +++ b/compiler/testData/loadJava/compiledJava/kotlinSignature/MethodWithTypeParameters.fir.txt @@ -1,5 +1,5 @@ public open class MethodWithTypeParameters : R|kotlin/Any| { - public open fun !|, B : R|ft!|, R|ft!>, kotlin/collections/List!>?>!|> foo(a: R|ft!|, b: R|ft!>, kotlin/collections/List!>?>!|, list: R|ft!>, kotlin/collections/List!>?>!|): R|kotlin/Unit| + public open fun !|, B : R|ft!|, R|ft!>, kotlin/collections/List!>?>!|> foo(a: R|ft!|, b: R|ft!>, kotlin/collections/List!>?>!|, list: R|ft!>, kotlin/collections/List<*>?>!|): R|kotlin/Unit| public constructor(): R|test/MethodWithTypeParameters|