diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index e6fbe1bed71..68a2811ea1e 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -20898,6 +20898,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("inferenceFrom.kt") + public void testInferenceFrom() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/inferenceFrom.kt"); + } + @Test @TestMetadata("listSuperType.kt") public void testListSuperType() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 156f0caa93b..03c74ac6ea0 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -20898,6 +20898,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("inferenceFrom.kt") + public void testInferenceFrom() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/inferenceFrom.kt"); + } + @Test @TestMetadata("listSuperType.kt") public void testListSuperType() throws Exception { 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 5ef31686182..ca57447ba56 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 @@ -409,7 +409,7 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound( JavaToKotlinClassMap.mapJavaToKotlin(classifier.fqName!!) } ?: classifier.classId!! - if (isLowerBound) { + if (isLowerBound || argumentsMakeSenseOnlyForMutableContainer(classId, session)) { classId = classId.readOnlyToMutable() ?: classId } @@ -460,6 +460,26 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound( } } +// Returns true for covariant read-only container that has mutable pair with invariant parameter +// List does not make sense, but MutableList does +// Same for Map +// But both Iterable, MutableIterable don't make sense as they are covariant, so return false +private fun JavaClassifierType.argumentsMakeSenseOnlyForMutableContainer( + classId: ClassId, + session: FirSession, +): Boolean { + if (!JavaToKotlinClassMap.isReadOnly(classId.asSingleFqName().toUnsafe())) return false + val mutableClassId = classId.readOnlyToMutable() ?: return false + + if (!typeArguments.lastOrNull().isSuperWildcard()) return false + val mutableLastParameterVariance = + (mutableClassId.toLookupTag().toSymbol(session)?.fir as? FirRegularClass)?.typeParameters?.lastOrNull()?.symbol?.fir?.variance + ?: return false + + return mutableLastParameterVariance != OUT_VARIANCE +} + + private fun FirRegularClass.createRawArguments( session: FirSession, defaultArgs: List, diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/inferenceFrom.kt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/inferenceFrom.kt new file mode 100644 index 00000000000..e0d8ee0ce00 --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/inferenceFrom.kt @@ -0,0 +1,13 @@ +// FIR_IDENTICAL +// SKIP_TXT +// FILE: JavaClass.java +import org.jetbrains.annotations.NotNull; +public class JavaClass { + public static void add(final @NotNull java.util.List addedLibraries) {} +} + +// FILE: main.kt + +fun bar() { + JavaClass.add(ArrayList()) +} diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/strangeVariance.fir.kt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/strangeVariance.fir.kt index f4610b66e4e..c5e7906c3db 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/strangeVariance.fir.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/strangeVariance.fir.kt @@ -11,8 +11,8 @@ public class A { fun main(a: A, ml: MutableList, l: List) { a.foo(ml) - a.foo(l) + a.foo(l) - a.bar(ml) - a.bar(l) + a.bar(ml) + a.bar(l) } diff --git a/compiler/testData/ir/irText/firProblems/V8ArrayToList.fir.txt b/compiler/testData/ir/irText/firProblems/V8ArrayToList.fir.txt index 75a166442a5..30133ccae28 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: @[FlexibleNullability] .V8Array?): @[FlexibleNullability] kotlin.collections.List<*>? declared in .Utils' type=@[FlexibleNullability] kotlin.collections.List<*>? origin=null + CALL 'public open fun toList (array: @[FlexibleNullability] .V8Array?): @[FlexibleNullability] kotlin.collections.MutableList? declared in .Utils' type=@[FlexibleNullability] kotlin.collections.MutableList? 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 42f503e7b62..eeac0e9fdfc 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/MutableList>?>|, src: R|ft>, kotlin/collections/List>?>|): 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 open fun |> copyMap(dest: R|ft, in ft>, kotlin/collections/MutableMap, in ft>?>|, 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 247f95ae1c6..c94f9821e52 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/MutableList>?>|): R|kotlin/Unit| public constructor(): R|test/MethodWithTypeParameters| diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 30992682298..ba45e67e885 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -20904,6 +20904,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("inferenceFrom.kt") + public void testInferenceFrom() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/inferenceFrom.kt"); + } + @Test @TestMetadata("listSuperType.kt") public void testListSuperType() throws Exception { diff --git a/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/structure/javaTypes.kt b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/structure/javaTypes.kt index 4c48535ba3a..7cee56194a6 100644 --- a/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/structure/javaTypes.kt +++ b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/structure/javaTypes.kt @@ -43,3 +43,5 @@ interface JavaWildcardType : JavaType { val bound: JavaType? val isExtends: Boolean } + +fun JavaType?.isSuperWildcard(): Boolean = (this as? JavaWildcardType)?.let { it.bound != null && !it.isExtends } ?: false diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt index df9534f6c72..5662c4729c2 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt @@ -184,8 +184,6 @@ class JavaTypeResolver( private fun JavaClassifierType.argumentsMakeSenseOnlyForMutableContainer( readOnlyContainer: ClassDescriptor ): Boolean { - fun JavaType?.isSuperWildcard(): Boolean = (this as? JavaWildcardType)?.let { it.bound != null && !it.isExtends } ?: false - if (!typeArguments.lastOrNull().isSuperWildcard()) return false val mutableLastParameterVariance = JavaToKotlinClassMapper.convertReadOnlyToMutable(readOnlyContainer) .typeConstructor.parameters.lastOrNull()?.variance ?: return false diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index c85580bb64f..979462e1402 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -18320,6 +18320,11 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @TestMetadata("inferenceFrom.kt") + public void testInferenceFrom() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/inferenceFrom.kt"); + } + @TestMetadata("listSuperType.kt") public void testListSuperType() throws Exception { runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/listSuperType.kt");