From 2e6dbbe02046966a63349cbbd7acd164ab716eab Mon Sep 17 00:00:00 2001 From: Brian Norman Date: Wed, 4 Oct 2023 16:48:49 -0500 Subject: [PATCH] [FIR] Completely unwrap original argument when computing type arguments When computing the conversion type for a type operator call, the argument needs to be fully unwrapped before getting the resolved type. This avoids the situation when the argument is a when-subject and the assumed original type is not correct. Before, only smart-casts were unwrapped, and this change will also unwrap when-subjects (as well as a few other FirExpressions). ^KT-62114 Fixed --- .../FirExpressionsResolveTransformer.kt | 2 +- .../fir/expressions/FirExpressionUtil.kt | 10 +++++++ .../tests/smartCasts/implicitReceiver.fir.kt | 25 +++++++++++++++++ .../tests/smartCasts/implicitReceiver.kt | 25 +++++++++++++++++ .../tests/smartCasts/implicitReceiver.txt | 27 +++++++++++++++++++ 5 files changed, 88 insertions(+), 1 deletion(-) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index e983ff210dc..7dcc5136e30 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -839,7 +839,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT val firClass = type.lookupTag.toSymbol(session)?.fir ?: return this if (firClass.typeParameters.isEmpty()) return this - val originalType = argument.unwrapSmartcastExpression().resolvedType + val originalType = argument.unwrapExpression().resolvedType val newType = components.computeRepresentativeTypeForBareType(type, originalType) ?: if (firClass.isLocal && (operation == FirOperation.AS || operation == FirOperation.SAFE_AS)) { (firClass as FirClass).defaultType() diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirExpressionUtil.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirExpressionUtil.kt index 1acf49822ef..17a8d1c0621 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirExpressionUtil.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirExpressionUtil.kt @@ -151,6 +151,16 @@ fun FirVariableAssignment.unwrapLValue(): FirQualifiedAccessExpression? { val FirElement.calleeReference: FirReference? get() = (this as? FirResolvable)?.calleeReference ?: (this as? FirVariableAssignment)?.calleeReference +fun FirExpression.unwrapExpression(): FirExpression = + when (this) { + is FirWhenSubjectExpression -> whenRef.value.subject?.unwrapExpression() ?: this + is FirSmartCastExpression -> originalExpression.unwrapExpression() + is FirCheckedSafeCallSubject -> originalReceiverRef.value.unwrapExpression() + is FirCheckNotNullCall -> argument.unwrapExpression() + is FirDesugaredAssignmentValueReferenceExpression -> expressionRef.value.unwrapExpression() + else -> this + } + fun FirExpression.unwrapSmartcastExpression(): FirExpression = when (this) { is FirSmartCastExpression -> originalExpression diff --git a/compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.fir.kt index 62d0141a968..3e7d4c91190 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.fir.kt @@ -1,3 +1,5 @@ +// ISSUE: KT-62114 + open class A { class B : A() { val a = "FAIL" @@ -27,3 +29,26 @@ class C { else return "" } } + +sealed class Received { + sealed class Error : Received() { + data class SomeError(val details: T?) : Error() + } +} + +val Received.thisRaisesUnresolvedReference: Boolean + get() = if (this is Received.Error<*>) { + when (this) { + is Received.Error.SomeError -> details?.length == 0 + } + } else { + false + } + +val Received.thisIsFine: Boolean + get() = if (this is Received.Error<*>) { + if (this is Received.Error.SomeError) { details?.length == 0 } + else false + } else { + false + } diff --git a/compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.kt b/compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.kt index 7f99e077f24..9813a738c71 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.kt @@ -1,3 +1,5 @@ +// ISSUE: KT-62114 + open class A { class B : A() { val a = "FAIL" @@ -27,3 +29,26 @@ class C { else return "" } } + +sealed class Received { + sealed class Error : Received() { + data class SomeError(val details: T?) : Error() + } +} + +val Received.thisRaisesUnresolvedReference: Boolean + get() = if (this is Received.Error<*>) { + when (this) { + is Received.Error.SomeError -> details?.length == 0 + } + } else { + false + } + +val Received.thisIsFine: Boolean + get() = if (this is Received.Error<*>) { + if (this is Received.Error.SomeError) { details?.length == 0 } + else false + } else { + false + } diff --git a/compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.txt b/compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.txt index d8641e4f161..14856322689 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.txt +++ b/compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.txt @@ -1,5 +1,7 @@ package +public val Received.thisIsFine: kotlin.Boolean +public val Received.thisRaisesUnresolvedReference: kotlin.Boolean public fun A?.bar(): kotlin.Unit public fun A.gav(): kotlin.String @@ -36,3 +38,28 @@ public final class C { public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public final fun A?.complex(): kotlin.String } + +public sealed class Received { + protected constructor Received() + 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 + + public sealed class Error : Received { + protected constructor Error() + 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 + + public final data class SomeError : Received.Error { + public constructor SomeError(/*0*/ details: T?) + public final val details: T? + public final operator /*synthesized*/ fun component1(): T? + public final /*synthesized*/ fun copy(/*0*/ details: T? = ...): Received.Error.SomeError + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + } +} +