From adcbc5ec99c4b0aa69728d042e38217a380368c3 Mon Sep 17 00:00:00 2001 From: pyos Date: Mon, 12 Sep 2022 12:59:42 +0200 Subject: [PATCH] FE1.0: keep nullability when approximating local types (in 1.9) Report an error that inference will change and type has to be provided manually in other language versions, since the current behavior is an unsoundness that can cause runtime NPEs while the new behavior may silently change overload resolution. ^KT-30054 Fixed --- .../jetbrains/kotlin/diagnostics/Errors.java | 2 ++ .../rendering/DefaultErrorMessages.java | 2 ++ .../kotlin/resolve/DescriptorResolver.java | 5 +++++ .../hideNullableLocalTypeInPublicPosition.kt | 6 ++++-- .../hideNullableLocalTypeInPublicPosition.txt | 2 +- ...bleLocalTypeInPublicPosition_before.fir.kt} | 0 ...NullableLocalTypeInPublicPosition_before.kt | 18 ++++++++++++++++++ ...ullableLocalTypeInPublicPosition_before.txt | 11 +++++++++++ .../kotlin/config/LanguageVersionSettings.kt | 1 + 9 files changed, 44 insertions(+), 3 deletions(-) rename compiler/testData/diagnostics/tests/inference/substitutions/{hideNullableLocalTypeInPublicPosition.fir.kt => hideNullableLocalTypeInPublicPosition_before.fir.kt} (100%) create mode 100644 compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.kt create mode 100644 compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 7db77b8fd78..e6c8b36c008 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -619,6 +619,8 @@ public interface Errors { DiagnosticFactory1> AMBIGUOUS_ANONYMOUS_TYPE_INFERRED = DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE); + DiagnosticFactory1 APPROXIMATED_LOCAL_TYPE_WILL_BECOME_NULLABLE = + 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 20aa1409583..56591abea99 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -1067,6 +1067,8 @@ public class DefaultErrorMessages { MAP.put(CATCH_PARAMETER_WITH_DEFAULT_VALUE, "Catch clause parameter may not have a default value"); 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(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 76e08ed6d0b..bdd58fbd489 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java @@ -1094,6 +1094,11 @@ public class DescriptorResolver { substitutedSuperType = approximatingSuperType; } + 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)); + } return substitutedSuperType; } else { diff --git a/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition.kt b/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition.kt index 2e4e5e03f3a..ea668fa02d8 100644 --- a/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition.kt +++ b/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition.kt @@ -1,4 +1,6 @@ // ISSUE: KT-30054 +// FIR_IDENTICAL +// !LANGUAGE: +KeepNullabilityWhenApproximatingLocalType interface I { fun foo(): String } @@ -13,6 +15,6 @@ fun bar(condition: Boolean) /*: I? */ = fun main() { bar(false).baz() - bar(false).foo() - bar(false)?.foo() + bar(false).foo() + bar(false)?.foo() } diff --git a/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition.txt b/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition.txt index e619714080d..cc60679abe1 100644 --- a/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition.txt +++ b/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition.txt @@ -1,6 +1,6 @@ package -public fun bar(/*0*/ condition: kotlin.Boolean): I +public fun bar(/*0*/ condition: kotlin.Boolean): I? public fun main(): kotlin.Unit public interface I { diff --git a/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition.fir.kt b/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.fir.kt similarity index 100% rename from compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition.fir.kt rename to compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.fir.kt diff --git a/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.kt b/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.kt new file mode 100644 index 00000000000..66022c12587 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.kt @@ -0,0 +1,18 @@ +// ISSUE: KT-30054 +interface I { + fun foo(): String +} + +fun bar(condition: Boolean) /*: I? */ = + if (condition) + object : I { + override fun foo() = "should check for null first" + fun baz() = "invisible" + } + else null + +fun main() { + bar(false).baz() + bar(false).foo() + bar(false)?.foo() +} diff --git a/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.txt b/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.txt new file mode 100644 index 00000000000..e619714080d --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/substitutions/hideNullableLocalTypeInPublicPosition_before.txt @@ -0,0 +1,11 @@ +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 +} diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 46cfd364a9d..15338044908 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -278,6 +278,7 @@ enum class LanguageFeature( ForbidInferringTypeVariablesIntoEmptyIntersection(KOTLIN_1_9, kind = BUG_FIX), // KT-51221 ForbidExtensionCallsOnInlineFunctionalParameters(KOTLIN_1_9, kind = BUG_FIX), // KT-52502 ForbidInferringPostponedTypeVariableIntoDeclaredUpperBound(KOTLIN_1_9, kind = BUG_FIX), // KT-47986 + KeepNullabilityWhenApproximatingLocalType(KOTLIN_1_9, kind = BUG_FIX), // KT-53982 SkipStandaloneScriptsInSourceRoots(KOTLIN_1_9, kind = OTHER), // KT-52525 ModifierNonBuiltinSuspendFunError(KOTLIN_1_9), BreakContinueInInlineLambdas(KOTLIN_1_9, defaultState = State.ENABLED), // KT-1436