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 c8b89d6c9bd..4b147faa937 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 @@ -16973,6 +16973,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/inference/substitutions/delegationAndInference.kt"); } + @Test + @TestMetadata("hideFlexibleLocalTypeInPublicPosition.kt") + public void testHideFlexibleLocalTypeInPublicPosition() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition.kt"); + } + + @Test + @TestMetadata("hideFlexibleLocalTypeInPublicPosition_before.kt") + public void testHideFlexibleLocalTypeInPublicPosition_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition_before.kt"); + } + @Test @TestMetadata("hideLocalTypeForReturnTypeOfSingleExpressionFunction.kt") public void testHideLocalTypeForReturnTypeOfSingleExpressionFunction() throws Exception { @@ -16985,6 +16997,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition.kt"); } + @Test + @TestMetadata("hideNullableLocalTypeInPublicPosition_before.kt") + public void testHideNullableLocalTypeInPublicPosition_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.kt"); + } + @Test @TestMetadata("kt32189returnTypeWithTypealiasSubtitution.kt") public void testKt32189returnTypeWithTypealiasSubtitution() 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 1300366e942..3a82f486943 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 @@ -16973,6 +16973,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/inference/substitutions/delegationAndInference.kt"); } + @Test + @TestMetadata("hideFlexibleLocalTypeInPublicPosition.kt") + public void testHideFlexibleLocalTypeInPublicPosition() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition.kt"); + } + + @Test + @TestMetadata("hideFlexibleLocalTypeInPublicPosition_before.kt") + public void testHideFlexibleLocalTypeInPublicPosition_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition_before.kt"); + } + @Test @TestMetadata("hideLocalTypeForReturnTypeOfSingleExpressionFunction.kt") public void testHideLocalTypeForReturnTypeOfSingleExpressionFunction() throws Exception { @@ -16985,6 +16997,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition.kt"); } + @Test + @TestMetadata("hideNullableLocalTypeInPublicPosition_before.kt") + public void testHideNullableLocalTypeInPublicPosition_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.kt"); + } + @Test @TestMetadata("kt32189returnTypeWithTypealiasSubtitution.kt") public void testKt32189returnTypeWithTypealiasSubtitution() throws Exception { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt index 2b47145cb41..db5f96a8c09 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt @@ -12,7 +12,6 @@ import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fakeElement import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.copyWithNewSourceKind import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass import org.jetbrains.kotlin.fir.declarations.utils.isExpect @@ -381,39 +380,50 @@ private fun FirTypeRef.hideLocalTypeIfNeeded( session: FirSession, isInlineFunction: Boolean = false ): FirTypeRef { - if (!shouldHideLocalType(containingCallableVisibility, isInlineFunction)) return this + if (this !is FirResolvedTypeRef || !shouldHideLocalType(containingCallableVisibility, isInlineFunction)) return this + return withReplacedConeType(type.approximateToOnlySupertype(session)) +} - val coneType = coneTypeSafe() ?: return this - val firClass = (coneType.lookupTag as? ConeClassLookupTagWithFixedSymbol)?.symbol?.fir +private fun ConeKotlinType.approximateToOnlySupertype(session: FirSession): ConeKotlinType? { + if (this is ConeFlexibleType) { + val lower = lowerBound.approximateToOnlySupertype(session)?.coneLowerBoundIfFlexible() + val upper = upperBound.approximateToOnlySupertype(session)?.coneUpperBoundIfFlexible() + if (lower == null && upper == null) { + return null + } + return coneFlexibleOrSimpleType(session.typeContext, lower ?: lowerBound, upper ?: upperBound) + } + + if (this !is ConeClassLikeType) { + return null + } + val firClass = (lookupTag as? ConeClassLookupTagWithFixedSymbol)?.symbol?.fir if (firClass !is FirAnonymousObject) { // NB: local classes are acceptable here, but reported by EXPOSED_* checkers as errors - return this + return null } if (firClass.superTypeRefs.size > 1) { // NB: don't approximate so members can be resolved. The error is reported by FirAmbiguousAnonymousTypeChecker. - return this + return null } val superType = firClass.superTypeRefs.single() - if (superType is FirResolvedTypeRef) { - val newKind = source?.kind - var result = superType - val resultTypeArguments = result.type.typeArguments - - result = result.withReplacedConeType(result.type.withNullability(coneType.nullability, session.typeContext)) - if (resultTypeArguments.isNotEmpty() && resultTypeArguments.size == coneType.typeArguments.size) { - val substitution = mutableMapOf() - for (index in resultTypeArguments.indices) { - val key = resultTypeArguments[index] - val value = coneType.typeArguments[index] - val symbol = (key as? ConeTypeParameterType)?.lookupTag?.typeParameterSymbol ?: continue - substitution[symbol] = value.type!! - } - result = result.withReplacedConeType(ConeSubstitutorByMap(substitution, session).substituteOrSelf(result.type)) - } - - return if (newKind is KtFakeSourceElementKind) result.copyWithNewSourceKind(newKind) else result + if (superType !is FirResolvedTypeRef) { + return null } - return this + val result = superType.type.withNullability(nullability, session.typeContext) + val resultTypeArguments = result.typeArguments + if (resultTypeArguments.isNotEmpty() && resultTypeArguments.size == typeArguments.size) { + val substitution = mutableMapOf() + for (index in resultTypeArguments.indices) { + // TODO: this is not correct (should use firClass' arguments as keys); see comment in KT-51418 + val key = resultTypeArguments[index] + val value = typeArguments[index] + val symbol = (key as? ConeTypeParameterType)?.lookupTag?.typeParameterSymbol ?: continue + substitution[symbol] = value.type!! + } + return ConeSubstitutorByMap(substitution, session).substituteOrSelf(result) + } + return result } fun shouldHideLocalType(containingCallableVisibility: Visibility?, isInlineFunction: Boolean): Boolean { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index e6c8b36c008..f54fde652ce 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -621,6 +621,8 @@ public interface Errors { DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory1 APPROXIMATED_LOCAL_TYPE_WILL_BECOME_NULLABLE = DiagnosticFactory1.create(WARNING, DECLARATION_SIGNATURE); + DiagnosticFactory1 APPROXIMATED_LOCAL_TYPE_WILL_BECOME_FLEXIBLE = + DiagnosticFactory1.create(WARNING, DECLARATION_SIGNATURE); DiagnosticFactory1 KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE = DiagnosticFactory1.create(ERROR, PositioningStrategies.DECLARATION_NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 56591abea99..ae27297710f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -1069,6 +1069,8 @@ public class DefaultErrorMessages { MAP.put(AMBIGUOUS_ANONYMOUS_TYPE_INFERRED, "Right-hand side has anonymous type. Please specify type explicitly", TO_STRING); MAP.put(APPROXIMATED_LOCAL_TYPE_WILL_BECOME_NULLABLE, "Declaration return type inferred as ''{0}'' instead of ''{0}?''. This will be fixed in Kotlin 1.9. Please specify the type explicitly to avoid future errors or unexpected changes in behavior. See https://youtrack.jetbrains.com/issue/KT-53982 for details.", TO_STRING); + MAP.put(APPROXIMATED_LOCAL_TYPE_WILL_BECOME_FLEXIBLE, + "Declaration return type inferred as ''{0}'' but it may be nullable. This will be fixed in Kotlin 1.9. Please specify the type explicitly to avoid unexpected changes in behavior. See https://youtrack.jetbrains.com/issue/KT-53982 for details.", TO_STRING); MAP.put(KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE, "Declaration has an inconsistent return type. Please add upper bound Any for type parameter ''{0}'' or specify return type explicitly", NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java index bdd58fbd489..c2e341f274e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java @@ -1094,10 +1094,22 @@ public class DescriptorResolver { substitutedSuperType = approximatingSuperType; } + UnwrappedType unwrapped = type.unwrap(); + boolean lowerNullable = FlexibleTypesKt.lowerIfFlexible(unwrapped).isMarkedNullable(); + boolean upperNullable = FlexibleTypesKt.upperIfFlexible(unwrapped).isMarkedNullable(); if (languageVersionSettings.supportsFeature(LanguageFeature.KeepNullabilityWhenApproximatingLocalType)) { - return TypeUtils.makeNullableIfNeeded(substitutedSuperType, type.isMarkedNullable()); - } else if (type.isMarkedNullable()) { - trace.report(APPROXIMATED_LOCAL_TYPE_WILL_BECOME_NULLABLE.on(declaration, substitutedSuperType)); + if (lowerNullable != upperNullable) { + return KotlinTypeFactory.flexibleType( + FlexibleTypesKt.lowerIfFlexible(substitutedSuperType), + FlexibleTypesKt.upperIfFlexible(substitutedSuperType).makeNullableAsSpecified(true)); + } + return TypeUtils.makeNullableIfNeeded(substitutedSuperType, upperNullable); + } else if (upperNullable) { + if (lowerNullable) { + trace.report(APPROXIMATED_LOCAL_TYPE_WILL_BECOME_NULLABLE.on(declaration, substitutedSuperType)); + } else { + trace.report(APPROXIMATED_LOCAL_TYPE_WILL_BECOME_FLEXIBLE.on(declaration, substitutedSuperType)); + } } return substitutedSuperType; } diff --git a/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition.kt b/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition.kt new file mode 100644 index 00000000000..5b8038c1244 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition.kt @@ -0,0 +1,24 @@ +// FIR_IDENTICAL +// ISSUE: KT-30054 +// !LANGUAGE: +KeepNullabilityWhenApproximatingLocalType +// FILE: J.java +public class J { + public static T flexibleId(T x) { return x; } +} + +// FILE: main.kt +interface I { + fun foo(): String +} + +fun bar(condition: Boolean) /*: I! */ = + J.flexibleId(object : I { + override fun foo() = "may or may not check for null first" + fun baz() = "invisible" + }) + +fun main() { + bar(false).baz() + bar(false).foo() + bar(false)?.foo() +} diff --git a/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition.txt b/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition.txt new file mode 100644 index 00000000000..bd4feb085b2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition.txt @@ -0,0 +1,21 @@ +package + +public fun bar(/*0*/ condition: kotlin.Boolean): I! +public fun main(): kotlin.Unit + +public interface I { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo(): kotlin.String + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class J { + public constructor J() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public open fun flexibleId(/*0*/ x: T!): T! +} diff --git a/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition_before.fir.kt b/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition_before.fir.kt new file mode 100644 index 00000000000..d71966419f4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition_before.fir.kt @@ -0,0 +1,23 @@ +// ISSUE: KT-30054 +// !LANGUAGE: -KeepNullabilityWhenApproximatingLocalType +// FILE: J.java +public class J { + public static T flexibleId(T x) { return x; } +} + +// FILE: main.kt +interface I { + fun foo(): String +} + +fun bar(condition: Boolean) /*: I! */ = + J.flexibleId(object : I { + override fun foo() = "may or may not check for null first" + fun baz() = "invisible" + }) + +fun main() { + bar(false).baz() + bar(false).foo() + bar(false)?.foo() +} diff --git a/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition_before.kt b/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition_before.kt new file mode 100644 index 00000000000..5e1dfde631e --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition_before.kt @@ -0,0 +1,23 @@ +// ISSUE: KT-30054 +// !LANGUAGE: -KeepNullabilityWhenApproximatingLocalType +// FILE: J.java +public class J { + public static T flexibleId(T x) { return x; } +} + +// FILE: main.kt +interface I { + fun foo(): String +} + +fun bar(condition: Boolean) /*: I! */ = + J.flexibleId(object : I { + override fun foo() = "may or may not check for null first" + fun baz() = "invisible" + }) + +fun main() { + bar(false).baz() + bar(false).foo() + bar(false)?.foo() +} diff --git a/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition_before.txt b/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition_before.txt new file mode 100644 index 00000000000..af538db0f12 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition_before.txt @@ -0,0 +1,21 @@ +package + +public fun bar(/*0*/ condition: kotlin.Boolean): I +public fun main(): kotlin.Unit + +public interface I { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo(): kotlin.String + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class J { + public constructor J() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public open fun flexibleId(/*0*/ x: T!): T! +} diff --git a/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.fir.kt b/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.fir.kt index fbc2c17e3db..9451557840d 100644 --- a/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.fir.kt @@ -1,4 +1,5 @@ // ISSUE: KT-30054 +// !LANGUAGE: -KeepNullabilityWhenApproximatingLocalType interface I { fun foo(): String } diff --git a/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.kt b/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.kt index 66022c12587..be533067537 100644 --- a/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.kt +++ b/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.kt @@ -1,4 +1,5 @@ // ISSUE: KT-30054 +// !LANGUAGE: -KeepNullabilityWhenApproximatingLocalType interface I { fun foo(): String } 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 46969ac3e13..45cff0dd534 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 @@ -16979,6 +16979,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/inference/substitutions/delegationAndInference.kt"); } + @Test + @TestMetadata("hideFlexibleLocalTypeInPublicPosition.kt") + public void testHideFlexibleLocalTypeInPublicPosition() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition.kt"); + } + + @Test + @TestMetadata("hideFlexibleLocalTypeInPublicPosition_before.kt") + public void testHideFlexibleLocalTypeInPublicPosition_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition_before.kt"); + } + @Test @TestMetadata("hideLocalTypeForReturnTypeOfSingleExpressionFunction.kt") public void testHideLocalTypeForReturnTypeOfSingleExpressionFunction() throws Exception { @@ -16991,6 +17003,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition.kt"); } + @Test + @TestMetadata("hideNullableLocalTypeInPublicPosition_before.kt") + public void testHideNullableLocalTypeInPublicPosition_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.kt"); + } + @Test @TestMetadata("kt32189returnTypeWithTypealiasSubtitution.kt") public void testKt32189returnTypeWithTypealiasSubtitution() throws Exception {