diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt index 23df5748263..1fe126f7a6d 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt @@ -7,17 +7,23 @@ package org.jetbrains.kotlin.fir.backend import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.backend.generators.ClassMemberGenerator import org.jetbrains.kotlin.fir.backend.generators.OperatorExpressionGenerator import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.builder.buildProperty +import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.expressions.builder.* import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition import org.jetbrains.kotlin.fir.expressions.impl.FirStubStatement import org.jetbrains.kotlin.fir.expressions.impl.FirUnitExpression +import org.jetbrains.kotlin.fir.expressions.impl.buildSingleExpressionBlock import org.jetbrains.kotlin.fir.references.FirReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference +import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference import org.jetbrains.kotlin.fir.resolve.firSymbolProvider import org.jetbrains.kotlin.fir.resolve.isIteratorNext import org.jetbrains.kotlin.fir.resolve.scope @@ -27,6 +33,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirIntegerOperator import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.builder.buildImplicitTypeRef import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor import org.jetbrains.kotlin.fir.visitors.transformSingle import org.jetbrains.kotlin.ir.IrElement @@ -777,6 +784,72 @@ class Fir2IrVisitor( } } + override fun visitElvisCall(elvisCall: FirElvisCall, data: Any?): IrElement { + val subjectName = Name.special("") + val subjectVariable = buildProperty { + source = elvisCall.source + session = this@Fir2IrVisitor.session + origin = FirDeclarationOrigin.Source + returnTypeRef = elvisCall.lhs.typeRef + name = subjectName + initializer = elvisCall.lhs + symbol = FirPropertySymbol(name) + isVar = false + isLocal = true + status = FirDeclarationStatusImpl(Visibilities.LOCAL, Modality.FINAL) + } + + @OptIn(FirContractViolation::class) + val ref = FirExpressionRef() + val subjectExpression = buildWhenSubjectExpression { + source = elvisCall.source + whenRef = ref + } + + val whenExpression = buildWhenExpression { + source = elvisCall.source + this.subject = elvisCall.lhs + this.subjectVariable = subjectVariable + branches += buildWhenBranch { + condition = buildOperatorCall { + operation = FirOperation.EQ + argumentList = buildBinaryArgumentList( + subjectExpression, buildConstExpression(null, FirConstKind.Null, null).apply { + replaceTypeRef(session.builtinTypes.nullableNothingType) + } + ) + } + result = buildSingleExpressionBlock(elvisCall.rhs) + } + branches += buildWhenBranch { + condition = buildElseIfTrueCondition {} + var resultExpression = buildQualifiedAccessExpression { + calleeReference = buildResolvedNamedReference { + name = subjectVariable.name + resolvedSymbol = subjectVariable.symbol + } + typeRef = subjectVariable.returnTypeRef + } + val originalType = resultExpression.typeRef.coneTypeUnsafe() + val notNullType = originalType.withNullability(ConeNullability.NOT_NULL) + if (notNullType != originalType) { + resultExpression = buildExpressionWithSmartcast { + originalExpression = resultExpression + typeRef = resultExpression.typeRef.resolvedTypeFromPrototype(notNullType) + typesFromSmartCast = setOf(notNullType) + } + } + result = buildSingleExpressionBlock(resultExpression) + } + typeRef = elvisCall.typeRef + isExhaustive = true + }.also { + ref.bind(it) + } + val result = whenExpression.accept(this, data) + return result + } + override fun visitGetClassCall(getClassCall: FirGetClassCall, data: Any?): IrElement { val argument = getClassCall.argument val irType = getClassCall.typeRef.toIrType() diff --git a/compiler/testData/ir/irText/expressions/kt30796.fir.txt b/compiler/testData/ir/irText/expressions/kt30796.fir.txt index 69f20437930..bd65fde6b86 100644 --- a/compiler/testData/ir/irText/expressions/kt30796.fir.txt +++ b/compiler/testData/ir/irText/expressions/kt30796.fir.txt @@ -97,18 +97,19 @@ FILE fqName: fileName:/kt30796.kt then: GET_VAR 'val tmp_5: T of .test [val] declared in .test' type=T of .test origin=null VAR name:x5 type:kotlin.Any [val] BLOCK type=kotlin.Int origin=ELVIS - VAR IR_TEMPORARY_VARIABLE name:tmp_7 type:kotlin.Nothing [val] - CALL 'public final fun magic (): T of .magic declared in ' type=kotlin.Nothing origin=null - : kotlin.Nothing + VAR IR_TEMPORARY_VARIABLE name:tmp_7 type:kotlin.Int? [val] + CALL 'public final fun magic (): T of .magic declared in ' type=kotlin.Int? origin=null + : kotlin.Int? WHEN type=kotlin.Int origin=ELVIS BRANCH if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ - arg0: GET_VAR 'val tmp_7: kotlin.Nothing [val] declared in .test' type=kotlin.Nothing origin=null + arg0: GET_VAR 'val tmp_7: kotlin.Int? [val] declared in .test' type=kotlin.Int? origin=null arg1: CONST Null type=kotlin.Nothing? value=null then: CONST Int type=kotlin.Int value=42 BRANCH if: CONST Boolean type=kotlin.Boolean value=true - then: GET_VAR 'val tmp_7: kotlin.Nothing [val] declared in .test' type=kotlin.Nothing origin=null + then: TYPE_OP type=kotlin.Int origin=IMPLICIT_CAST typeOperand=kotlin.Int + GET_VAR 'val tmp_7: kotlin.Int? [val] declared in .test' type=kotlin.Int? origin=null VAR name:x6 type:kotlin.Any [val] BLOCK type=T of .test origin=ELVIS VAR IR_TEMPORARY_VARIABLE name:tmp_8 type:T of .test [val] @@ -136,26 +137,28 @@ FILE fqName: fileName:/kt30796.kt then: GET_VAR 'val tmp_8: T of .test [val] declared in .test' type=T of .test origin=null VAR name:x7 type:kotlin.Any [val] BLOCK type=T of .test origin=ELVIS - VAR IR_TEMPORARY_VARIABLE name:tmp_10 type:T of .test [val] - BLOCK type=T of .test origin=ELVIS - VAR IR_TEMPORARY_VARIABLE name:tmp_11 type:kotlin.Nothing [val] - CALL 'public final fun magic (): T of .magic declared in ' type=kotlin.Nothing origin=null - : kotlin.Nothing - WHEN type=T of .test origin=ELVIS + VAR IR_TEMPORARY_VARIABLE name:tmp_10 type:T of .test? [val] + BLOCK type=T of .test? origin=ELVIS + VAR IR_TEMPORARY_VARIABLE name:tmp_11 type:T of .test? [val] + CALL 'public final fun magic (): T of .magic declared in ' type=T of .test? origin=null + : T of .test? + WHEN type=T of .test? origin=ELVIS BRANCH if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ - arg0: GET_VAR 'val tmp_11: kotlin.Nothing [val] declared in .test' type=kotlin.Nothing origin=null + arg0: GET_VAR 'val tmp_11: T of .test? [val] declared in .test' type=T of .test? origin=null arg1: CONST Null type=kotlin.Nothing? value=null then: GET_VAR 'value: T of .test declared in .test' type=T of .test origin=null BRANCH if: CONST Boolean type=kotlin.Boolean value=true - then: GET_VAR 'val tmp_11: kotlin.Nothing [val] declared in .test' type=kotlin.Nothing origin=null + then: TYPE_OP type=T of .test origin=IMPLICIT_CAST typeOperand=T of .test + GET_VAR 'val tmp_11: T of .test? [val] declared in .test' type=T of .test? origin=null WHEN type=T of .test origin=ELVIS BRANCH if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ - arg0: GET_VAR 'val tmp_10: T of .test [val] declared in .test' type=T of .test origin=null + arg0: GET_VAR 'val tmp_10: T of .test? [val] declared in .test' type=T of .test? origin=null arg1: CONST Null type=kotlin.Nothing? value=null then: CONST Int type=kotlin.Int value=42 BRANCH if: CONST Boolean type=kotlin.Boolean value=true - then: GET_VAR 'val tmp_10: T of .test [val] declared in .test' type=T of .test origin=null + then: TYPE_OP type=T of .test origin=IMPLICIT_CAST typeOperand=T of .test + GET_VAR 'val tmp_10: T of .test? [val] declared in .test' type=T of .test? origin=null