diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index a5df7e16a79..0fe79f6db61 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -13881,6 +13881,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/inference/underscoredTypeInForbiddenPositions.kt"); } + @Test + @TestMetadata("unsoundness1.kt") + public void testUnsoundness1() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/unsoundness1.kt"); + } + + @Test + @TestMetadata("unsoundness2.kt") + public void testUnsoundness2() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/unsoundness2.kt"); + } + @Test @TestMetadata("useFunctionLiteralsToInferType.kt") public void testUseFunctionLiteralsToInferType() throws Exception { @@ -31083,6 +31095,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/typeParameters/repeatedBound.kt"); } + @Test + @TestMetadata("starProjectionInsteadOutCaptured.kt") + public void testStarProjectionInsteadOutCaptured() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/starProjectionInsteadOutCaptured.kt"); + } + @Test @TestMetadata("upperBoundCannotBeArray.kt") public void testUpperBoundCannotBeArray() throws Exception { diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.fir.txt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.fir.txt index 42f7b68fa54..fbca37f9aaa 100644 --- a/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.fir.txt @@ -140,3 +140,21 @@ FILE: upperBoundViolated.kt } public final typealias Alias = R|(Class) -> kotlin/Boolean| + public abstract class Base|> : R|kotlin/Any| { + public constructor|>(): R|Base| { + super() + } + + } + public final class DerivedOut|> : R|kotlin/Any| { + public constructor|>(): R|DerivedOut| { + super() + } + + } + public final class DerivedIn|> : R|kotlin/Any| { + public constructor|>(): R|DerivedIn| { + super() + } + + } diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.kt index 1b0dd99fb2c..035a6e1d562 100644 --- a/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.kt +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.kt @@ -55,7 +55,7 @@ class Test1, K : Any> class Test2> class Test3, K : Any> -class Test4S4, out Any>> +class Test4> class Test5, K : Any> class Test6> @@ -66,9 +66,8 @@ class Test8in Any>> class Class typealias Alias = (Class) -> Boolean -/* TODO: Should not be errors. Uncomment after fixing of https://youtrack.jetbrains.com/issue/KT-48044 abstract class Base> {} class DerivedOut> {} -class DerivedIn> {}*/ +class DerivedIn> {} 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 832e0bef8da..2d509aef2db 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 @@ -13881,6 +13881,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/inference/underscoredTypeInForbiddenPositions.kt"); } + @Test + @TestMetadata("unsoundness1.kt") + public void testUnsoundness1() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/unsoundness1.kt"); + } + + @Test + @TestMetadata("unsoundness2.kt") + public void testUnsoundness2() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/unsoundness2.kt"); + } + @Test @TestMetadata("useFunctionLiteralsToInferType.kt") public void testUseFunctionLiteralsToInferType() throws Exception { @@ -31083,6 +31095,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/typeParameters/repeatedBound.kt"); } + @Test + @TestMetadata("starProjectionInsteadOutCaptured.kt") + public void testStarProjectionInsteadOutCaptured() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/starProjectionInsteadOutCaptured.kt"); + } + @Test @TestMetadata("upperBoundCannotBeArray.kt") public void testUpperBoundCannotBeArray() 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 7948ce33211..890f136ca1c 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 @@ -13881,6 +13881,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/inference/underscoredTypeInForbiddenPositions.kt"); } + @Test + @TestMetadata("unsoundness1.kt") + public void testUnsoundness1() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/unsoundness1.kt"); + } + + @Test + @TestMetadata("unsoundness2.kt") + public void testUnsoundness2() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/unsoundness2.kt"); + } + @Test @TestMetadata("useFunctionLiteralsToInferType.kt") public void testUseFunctionLiteralsToInferType() throws Exception { @@ -31083,6 +31095,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/typeParameters/repeatedBound.kt"); } + @Test + @TestMetadata("starProjectionInsteadOutCaptured.kt") + public void testStarProjectionInsteadOutCaptured() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/starProjectionInsteadOutCaptured.kt"); + } + @Test @TestMetadata("upperBoundCannotBeArray.kt") public void testUpperBoundCannotBeArray() throws Exception { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirInconsistentTypeParameterHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirInconsistentTypeParameterHelpers.kt index 8a6437e9343..a6211b32d17 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirInconsistentTypeParameterHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirInconsistentTypeParameterHelpers.kt @@ -6,10 +6,10 @@ package org.jetbrains.kotlin.fir.analysis.checkers import org.jetbrains.kotlin.KtSourceElement -import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.diagnostics.DiagnosticReporter -import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap @@ -70,7 +70,11 @@ private fun buildDeepSubstitutionMultimap( for (index in 0 until count) { val typeArgument = typeArguments[index] - val substitutedArgument = ConeSubstitutorByMap(substitution, session).substituteArgument(typeArgument) ?: typeArgument + val substitutedArgument = ConeSubstitutorByMap(substitution, session).substituteArgument( + typeArgument, + classSymbol.toLookupTag(), + index + ) ?: typeArgument val substitutedType = substitutedArgument.type ?: continue val typeParameterSymbol = typeParameterSymbols[index] diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirUpperBoundViolatedHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirUpperBoundViolatedHelpers.kt index c5e8457ef07..b187c4e1542 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirUpperBoundViolatedHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirUpperBoundViolatedHelpers.kt @@ -8,16 +8,23 @@ package org.jetbrains.kotlin.fir.analysis.checkers import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor -import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.resolve.withCombinedAttributesFrom +import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.types.AbstractTypeChecker +import org.jetbrains.kotlin.types.TypeApproximatorConfiguration +import org.jetbrains.kotlin.types.Variance +import kotlin.reflect.KClass /** * Recursively analyzes type parameters and reports the diagnostic on the given source calculated using typeRef @@ -44,23 +51,9 @@ fun checkUpperBoundViolated( return } - val count = minOf(typeParameterSymbols.size, type.typeArguments.size) - val substitution = mutableMapOf() + val substitution = typeParameterSymbols.zip(type.typeArguments).toMap() + val substitutor = FE10LikeConeSubstitutor(substitution, context.session) - for (index in 0 until count) { - val typeArgument = type.typeArguments[index] - val typeParameterSymbol = typeParameterSymbols[index] - - val typeArgumentType = typeArgument.type - if (typeArgumentType != null) { - substitution[typeParameterSymbol] = typeArgumentType - } else { - substitution[typeParameterSymbol] = - ConeStubTypeForTypeVariableInSubtyping(ConeTypeVariable("", typeParameterSymbol.toLookupTag()), ConeNullability.NOT_NULL) - } - } - - val substitutor = substitutorByMap(substitution, context.session) val typeRefAndSourcesForArguments = extractArgumentsTypeRefAndSource(typeRef) ?: return val typeArgumentsWithSourceInfo = type.typeArguments.withIndex().map { (index, projection) -> val (argTypeRef, source) = @@ -80,6 +73,87 @@ fun checkUpperBoundViolated( ) } +private class FE10LikeConeSubstitutor( + private val substitution: Map, + private val useSiteSession: FirSession +) : AbstractConeSubstitutor(useSiteSession.typeContext) { + override fun substituteType(type: ConeKotlinType): ConeKotlinType? { + if (type !is ConeTypeParameterType) return null + val projection = substitution[type.lookupTag.symbol] ?: return null + + if (projection.isStarProjection) { + return StandardClassIds.Any.constructClassLikeType(emptyArray(), isNullable = true).withProjection(projection) + } + + val result = + projection.type!!.updateNullabilityIfNeeded(type) + ?.withCombinedAttributesFrom(type, useSiteSession.typeContext) + ?: return null + + if (type.isUnsafeVarianceType(useSiteSession)) { + useSiteSession.typeApproximator.approximateToSuperType( + result, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference + )?.let { + return it.withProjection(projection) + } + } + + return result.withProjection(projection) + } + + private fun ConeKotlinType.withProjection(projection: ConeTypeProjection): ConeKotlinType { + if (projection.kind == ProjectionKind.INVARIANT) return this + return withAttributes(ConeAttributes.create(listOf(OriginalProjectionTypeAttribute(projection))), useSiteSession.typeContext) + } + + override fun substituteArgument(projection: ConeTypeProjection, lookupTag: ConeClassLikeLookupTag, index: Int): ConeTypeProjection? { + val substitutedProjection = super.substituteArgument(projection, lookupTag, index) ?: return null + if (substitutedProjection.isStarProjection) return null + + val type = substitutedProjection.type!! + + val projectionFromType = type.attributes.originalProjection?.data ?: type + val projectionKindFromType = projectionFromType.kind + + if (projectionKindFromType == ProjectionKind.STAR) return ConeStarProjection + + if (projectionKindFromType == ProjectionKind.INVARIANT || projectionKindFromType == projection.kind) { + return substitutedProjection + } + + if (projection.kind == ProjectionKind.INVARIANT) { + return wrapProjection(projectionFromType, type) + } + + return ConeStarProjection + } +} + +private class OriginalProjectionTypeAttribute(val data: ConeTypeProjection) : ConeAttribute() { + override fun union(other: OriginalProjectionTypeAttribute?): OriginalProjectionTypeAttribute? { + return other + } + + override fun intersect(other: OriginalProjectionTypeAttribute?): OriginalProjectionTypeAttribute? { + return other + } + + override fun add(other: OriginalProjectionTypeAttribute?): OriginalProjectionTypeAttribute? { + return other + } + + override fun isSubtypeOf(other: OriginalProjectionTypeAttribute?): Boolean { + return true + } + + override fun toString() = "OriginalProjectionTypeAttribute: $data" + + override val key: KClass + get() = OriginalProjectionTypeAttribute::class +} + +private val ConeAttributes.originalProjection: OriginalProjectionTypeAttribute? by ConeAttributes.attributeAccessor() + class TypeArgumentWithSourceInfo( val coneTypeProjection: ConeTypeProjection, val typeRef: FirTypeRef?, diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/TypeExpansionUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/TypeExpansionUtils.kt index e490d283d7a..8434fd83d14 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/TypeExpansionUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/TypeExpansionUtils.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirTypeAlias import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor +import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.ensureResolved import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol import org.jetbrains.kotlin.fir.types.* @@ -115,10 +116,18 @@ private fun mapTypeAliasArguments( return null } - override fun substituteArgument(projection: ConeTypeProjection): ConeTypeProjection? { + override fun substituteArgument( + projection: ConeTypeProjection, + lookupTag: ConeClassLikeLookupTag, + index: Int + ): ConeTypeProjection? { val type = (projection as? ConeKotlinTypeProjection)?.type ?: return null - val symbol = (type as? ConeTypeParameterType)?.lookupTag?.symbol ?: return super.substituteArgument(projection) - val mappedProjection = typeAliasMap[symbol] ?: return super.substituteArgument(projection) + val symbol = (type as? ConeTypeParameterType)?.lookupTag?.symbol ?: return super.substituteArgument( + projection, + lookupTag, + index + ) + val mappedProjection = typeAliasMap[symbol] ?: return super.substituteArgument(projection, lookupTag, index) var mappedType = (mappedProjection as? ConeKotlinTypeProjection)?.type.updateNullabilityIfNeeded(type) mappedType = when (mappedType) { is ConeClassErrorType, diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt index 4d79446dcb8..5aed2c60f60 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.resolve.substitution import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.resolve.withCombinedAttributesFrom +import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl @@ -17,7 +18,7 @@ import org.jetbrains.kotlin.types.model.TypeVariableMarker import org.jetbrains.kotlin.types.model.typeConstructor abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContext) : ConeSubstitutor() { - private fun wrapProjection(old: ConeTypeProjection, newType: ConeKotlinType): ConeTypeProjection { + protected fun wrapProjection(old: ConeTypeProjection, newType: ConeKotlinType): ConeTypeProjection { return when (old) { is ConeStarProjection -> old is ConeKotlinTypeProjectionIn -> ConeKotlinTypeProjectionIn(newType) @@ -29,7 +30,7 @@ abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContex } abstract fun substituteType(type: ConeKotlinType): ConeKotlinType? - open fun substituteArgument(projection: ConeTypeProjection): ConeTypeProjection? { + open fun substituteArgument(projection: ConeTypeProjection, lookupTag: ConeClassLikeLookupTag, index: Int): ConeTypeProjection? { val type = (projection as? ConeKotlinTypeProjection)?.type ?: return null val newType = substituteOrNull(type) ?: return null return wrapProjection(projection, newType) @@ -124,8 +125,11 @@ abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContex private fun ConeKotlinType.substituteArguments(): ConeKotlinType? { val newArguments by lazy { arrayOfNulls(typeArguments.size) } var initialized = false + + require(this is ConeClassLikeType) { "Unknown type to substitute: $this, ${this::class}" } + for ((index, typeArgument) in this.typeArguments.withIndex()) { - newArguments[index] = substituteArgument(typeArgument)?.also { + newArguments[index] = substituteArgument(typeArgument, lookupTag, index)?.also { initialized = true } } @@ -144,8 +148,7 @@ abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContex nullability.isNullable, attributes ) - is ConeClassLikeType -> error("Unknown class-like type to substitute: $this, ${this::class}") - else -> error("Unknown type to substitute: $this, ${this::class}") + else -> error("Unknown class-like type to substitute: $this, ${this::class}") } } return null diff --git a/compiler/testData/codegen/box/inference/kt49838.kt b/compiler/testData/codegen/box/inference/kt49838.kt index f39d45e4486..22b0e6e817a 100644 --- a/compiler/testData/codegen/box/inference/kt49838.kt +++ b/compiler/testData/codegen/box/inference/kt49838.kt @@ -1,6 +1,4 @@ // IGNORE_BACKEND: JVM -// IGNORE_BACKEND_FIR: JVM_IR -// FIR status: UPPER_BOUND_VIOLATED at Service inline fun < reified TService : Service, @@ -25,4 +23,4 @@ class SomeService : Service { fun box(): String { event { someEvent: SomeService.SomeEvent -> } // REIFIED_TYPE_FORBIDDEN_SUBSTITUTION return "OK" -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/inference/unsoundness1.fir.kt b/compiler/testData/diagnostics/tests/inference/unsoundness1.fir.kt new file mode 100644 index 00000000000..f1303ae99e5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/unsoundness1.fir.kt @@ -0,0 +1,23 @@ +// SKIP_TXT +// !LANGUAGE: +ProperTypeInferenceConstraintsProcessing + +class A +fun foo(a: A<*, in CharSequence>) {} +fun coerce(t: T): U { + val constrain: Constrain? = null + val bind = Bind(constrain) + return bind.upcast(t) +} + +class Constrain + +class Bind(val constrain: Constrain?) { + fun upcast(c: C): A = c +} + +fun coerce2(t: T): U { + // We might report an error on unsound type reference Constrain?, too + val constrain: Constrain? = null + val bind = Bind(constrain) // WARNING: Type mismatch: inferred type is T but U was expected + return bind.upcast(t) +} diff --git a/compiler/testData/diagnostics/tests/inference/unsoundness1.kt b/compiler/testData/diagnostics/tests/inference/unsoundness1.kt new file mode 100644 index 00000000000..c64912ef54d --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/unsoundness1.kt @@ -0,0 +1,23 @@ +// SKIP_TXT +// !LANGUAGE: +ProperTypeInferenceConstraintsProcessing + +class A +fun foo(a: A<*, in CharSequence>) {} +fun coerce(t: T): U { + val constrain: Constrain? = null + val bind = Bind(constrain) + return bind.upcast(t) +} + +class Constrain + +class Bind(val constrain: Constrain?) { + fun upcast(c: C): A = c +} + +fun coerce2(t: T): U { + // We might report an error on unsound type reference Constrain?, too + val constrain: Constrain? = null + val bind = Bind(constrain) // WARNING: Type mismatch: inferred type is T but U was expected + return bind.upcast(t) +} diff --git a/compiler/testData/diagnostics/tests/inference/unsoundness2.kt b/compiler/testData/diagnostics/tests/inference/unsoundness2.kt new file mode 100644 index 00000000000..15b14cb4e17 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/unsoundness2.kt @@ -0,0 +1,22 @@ +// FIR_IDENTICAL +// SKIP_TXT +// !LANGUAGE: +ProperTypeInferenceConstraintsProcessing + +fun main(args: Array) { + val zero = coerce(0) +} + +fun coerce(t: T): U { + // Should be an error somewhere because this code leads to unsoundness + // We may report that `Constrain?` type definition is unsound or the call `Bind(constrain)` + // See KT-50230 + val constrain: Constrain? = null + val bind = Bind(constrain) + return bind.upcast(t) +} + +class Constrain + +class Bind(val constrain: Constrain?) { + fun upcast(c: C): A = c +} diff --git a/compiler/testData/diagnostics/tests/typeParameters/starProjectionInsteadOutCaptured.kt b/compiler/testData/diagnostics/tests/typeParameters/starProjectionInsteadOutCaptured.kt new file mode 100644 index 00000000000..c02a9b4d315 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/starProjectionInsteadOutCaptured.kt @@ -0,0 +1,11 @@ +// FIR_IDENTICAL +// SKIP_TXT +interface A +interface B +interface C> + +fun foo1(c: C>) {} + +fun foo2(c: C<*, B<*>>) {} +fun > foo3(c: C<*, T>) {} +fun > foo4(c: C) {} 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 387f5d0b544..399dfe9f974 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 @@ -13887,6 +13887,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/inference/underscoredTypeInForbiddenPositions.kt"); } + @Test + @TestMetadata("unsoundness1.kt") + public void testUnsoundness1() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/unsoundness1.kt"); + } + + @Test + @TestMetadata("unsoundness2.kt") + public void testUnsoundness2() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/unsoundness2.kt"); + } + @Test @TestMetadata("useFunctionLiteralsToInferType.kt") public void testUseFunctionLiteralsToInferType() throws Exception { @@ -31173,6 +31185,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/typeParameters/repeatedBound.kt"); } + @Test + @TestMetadata("starProjectionInsteadOutCaptured.kt") + public void testStarProjectionInsteadOutCaptured() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/starProjectionInsteadOutCaptured.kt"); + } + @Test @TestMetadata("upperBoundCannotBeArray.kt") public void testUpperBoundCannotBeArray() throws Exception {