FIR/FE1.0: keep flexibility when approximating local types
This commit is contained in:
+18
@@ -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 {
|
||||
|
||||
+18
@@ -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 {
|
||||
|
||||
@@ -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<ConeClassLikeType>() ?: 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<FirTypeParameterSymbol, ConeKotlinType>()
|
||||
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<FirTypeParameterSymbol, ConeKotlinType>()
|
||||
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 {
|
||||
|
||||
@@ -621,6 +621,8 @@ public interface Errors {
|
||||
DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE);
|
||||
DiagnosticFactory1<KtDeclaration, KotlinType> APPROXIMATED_LOCAL_TYPE_WILL_BECOME_NULLABLE =
|
||||
DiagnosticFactory1.create(WARNING, DECLARATION_SIGNATURE);
|
||||
DiagnosticFactory1<KtDeclaration, KotlinType> APPROXIMATED_LOCAL_TYPE_WILL_BECOME_FLEXIBLE =
|
||||
DiagnosticFactory1.create(WARNING, DECLARATION_SIGNATURE);
|
||||
|
||||
DiagnosticFactory1<KtNamedDeclaration, TypeParameterDescriptor>
|
||||
KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE = DiagnosticFactory1.create(ERROR, PositioningStrategies.DECLARATION_NAME);
|
||||
|
||||
+2
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
compiler/testData/diagnostics/tests/inference/substitutions/hideFlexibleLocalTypeInPublicPosition.kt
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
// FIR_IDENTICAL
|
||||
// ISSUE: KT-30054
|
||||
// !LANGUAGE: +KeepNullabilityWhenApproximatingLocalType
|
||||
// FILE: J.java
|
||||
public class J {
|
||||
public static <T> 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).<!UNRESOLVED_REFERENCE!>baz<!>()
|
||||
bar(false).foo()
|
||||
bar(false)?.foo()
|
||||
}
|
||||
+21
@@ -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 </*0*/ T : kotlin.Any!> flexibleId(/*0*/ x: T!): T!
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// ISSUE: KT-30054
|
||||
// !LANGUAGE: -KeepNullabilityWhenApproximatingLocalType
|
||||
// FILE: J.java
|
||||
public class J {
|
||||
public static <T> 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).<!UNRESOLVED_REFERENCE!>baz<!>()
|
||||
bar(false).foo()
|
||||
bar(false)?.foo()
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// ISSUE: KT-30054
|
||||
// !LANGUAGE: -KeepNullabilityWhenApproximatingLocalType
|
||||
// FILE: J.java
|
||||
public class J {
|
||||
public static <T> T flexibleId(T x) { return x; }
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
interface I {
|
||||
fun foo(): String
|
||||
}
|
||||
|
||||
<!APPROXIMATED_LOCAL_TYPE_WILL_BECOME_FLEXIBLE!>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).<!UNRESOLVED_REFERENCE!>baz<!>()
|
||||
bar(false).foo()
|
||||
bar(false)<!UNNECESSARY_SAFE_CALL!>?.<!>foo()
|
||||
}
|
||||
+21
@@ -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 </*0*/ T : kotlin.Any!> flexibleId(/*0*/ x: T!): T!
|
||||
}
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// ISSUE: KT-30054
|
||||
// !LANGUAGE: -KeepNullabilityWhenApproximatingLocalType
|
||||
interface I {
|
||||
fun foo(): String
|
||||
}
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// ISSUE: KT-30054
|
||||
// !LANGUAGE: -KeepNullabilityWhenApproximatingLocalType
|
||||
interface I {
|
||||
fun foo(): String
|
||||
}
|
||||
|
||||
Generated
+18
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user