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 5e9d38e5579..c3388af3b79 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 @@ -292,14 +292,22 @@ class Fir2IrVisitor( ) if (initializer != null) { irVariable.initializer = - with(implicitCastInserter) { - convertToIrExpression(initializer).cast(initializer, initializer.typeRef, variable.returnTypeRef) - } + convertToIrExpression(initializer) + .insertImplicitCast(initializer, initializer.typeRef, variable.returnTypeRef) } annotationGenerator.generate(irVariable, variable) return irVariable } + private fun IrExpression.insertImplicitCast( + baseExpression: FirExpression, + valueType: FirTypeRef, + expectedType: FirTypeRef + ) = + with(implicitCastInserter) { + this@insertImplicitCast.cast(baseExpression, valueType, expectedType) + } + override fun visitProperty(property: FirProperty, data: Any?): IrElement { if (property.isLocal) return visitLocalVariable(property) val irProperty = declarationStorage.getCachedIrProperty(property)!! @@ -707,13 +715,15 @@ class Fir2IrVisitor( val notNullType = originalType.withNullability(ConeNullability.NOT_NULL, session.typeContext) val irBranches = listOf( IrBranchImpl( - startOffset, endOffset, primitiveOp2( + startOffset, endOffset, + primitiveOp2( startOffset, endOffset, irBuiltIns.eqeqSymbol, irBuiltIns.booleanType, IrStatementOrigin.EQEQ, irGetLhsValue(), IrConstImpl.constNull(startOffset, endOffset, irBuiltIns.nothingNType) ), convertToIrExpression(elvisExpression.rhs) + .insertImplicitCast(elvisExpression, elvisExpression.rhs.typeRef, elvisExpression.typeRef) ), IrElseBranchImpl( IrConstImpl.boolean(startOffset, endOffset, irBuiltIns.booleanType, true), @@ -815,9 +825,7 @@ class Fir2IrVisitor( private fun FirWhenBranch.toIrWhenBranch(whenExpressionType: FirTypeRef): IrBranch { return convertWithOffsets { startOffset, endOffset -> val condition = condition - val irResult = with(implicitCastInserter) { - convertToIrExpression(result).cast(result, result.typeRef, whenExpressionType) - } + val irResult = convertToIrExpression(result).insertImplicitCast(result, result.typeRef, whenExpressionType) if (condition is FirElseIfTrueCondition) { IrElseBranchImpl(IrConstImpl.boolean(irResult.startOffset, irResult.endOffset, irBuiltIns.booleanType, true), irResult) } else { diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/Fir2IrTextTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/Fir2IrTextTestGenerated.java index f0f4d5e21aa..93c605c1cbd 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/Fir2IrTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/Fir2IrTextTestGenerated.java @@ -2901,6 +2901,12 @@ public class Fir2IrTextTestGenerated extends AbstractFir2IrTextTest { runTest("compiler/testData/ir/irText/types/nullChecks/implicitNotNullOnPlatformType.kt"); } + @Test + @TestMetadata("nullCheckInElvisRhs.kt") + public void testNullCheckInElvisRhs() throws Exception { + runTest("compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.kt"); + } + @Test @TestMetadata("nullCheckOnInterfaceDelegation.kt") public void testNullCheckOnInterfaceDelegation() throws Exception { diff --git a/compiler/testData/codegen/box/notNullAssertions/rightElvisOperand.kt b/compiler/testData/codegen/box/notNullAssertions/rightElvisOperand.kt index d9b947af205..06eeefd8137 100644 --- a/compiler/testData/codegen/box/notNullAssertions/rightElvisOperand.kt +++ b/compiler/testData/codegen/box/notNullAssertions/rightElvisOperand.kt @@ -1,6 +1,4 @@ // TARGET_BACKEND: JVM -// IGNORE_BACKEND_FIR: JVM_IR -// FIR status: Fail: should have been an exception in 'bar()' // MODULE: lib // FILE: RightElvisOperand.java diff --git a/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.fir.ir.txt b/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.fir.ir.txt new file mode 100644 index 00000000000..9114a8de79d --- /dev/null +++ b/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.fir.ir.txt @@ -0,0 +1,23 @@ +Module: lib +Module: main +FILE fqName: fileName:/nullCheckInElvisRhs.kt + FUN name:baz visibility:public modality:FINAL <> () returnType:kotlin.String? + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun baz (): kotlin.String? declared in ' + CONST Null type=kotlin.Nothing? value=null + FUN name:test visibility:public modality:FINAL <> () returnType:kotlin.String + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun test (): kotlin.String declared in ' + BLOCK type=kotlin.String origin=ELVIS + VAR IR_TEMPORARY_VARIABLE name:tmp_0 type:kotlin.String? [val] + CALL 'public final fun baz (): kotlin.String? declared in ' type=kotlin.String? origin=null + WHEN type=kotlin.String 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_0: kotlin.String? [val] declared in .test' type=kotlin.String? origin=null + arg1: CONST Null type=kotlin.Nothing? value=null + then: TYPE_OP type=kotlin.String origin=IMPLICIT_NOTNULL typeOperand=kotlin.String + CALL 'public/*package*/ open fun foo (): @[FlexibleNullability] kotlin.String? declared in .RightElvisOperand' type=@[FlexibleNullability] kotlin.String? origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: GET_VAR 'val tmp_0: kotlin.String? [val] declared in .test' type=kotlin.String? origin=null diff --git a/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.fir.kt.txt b/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.fir.kt.txt new file mode 100644 index 00000000000..8851a704d19 --- /dev/null +++ b/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.fir.kt.txt @@ -0,0 +1,17 @@ +// MODULE: lib +// MODULE: main +// FILE: nullCheckInElvisRhs.kt + +fun baz(): String? { + return null +} + +fun test(): String { + return { // BLOCK + val : String? = baz() + when { + EQEQ(arg0 = , arg1 = null) -> foo() /*!! String */ + else -> + } + } +} diff --git a/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.ir.txt b/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.ir.txt new file mode 100644 index 00000000000..52e741de578 --- /dev/null +++ b/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.ir.txt @@ -0,0 +1,23 @@ +Module: lib +Module: main +FILE fqName: fileName:/nullCheckInElvisRhs.kt + FUN name:baz visibility:public modality:FINAL <> () returnType:kotlin.String? + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun baz (): kotlin.String? declared in ' + CONST Null type=kotlin.Nothing? value=null + FUN name:test visibility:public modality:FINAL <> () returnType:kotlin.String + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun test (): kotlin.String declared in ' + BLOCK type=kotlin.String origin=ELVIS + VAR IR_TEMPORARY_VARIABLE name:tmp_0 type:kotlin.String? [val] + CALL 'public final fun baz (): kotlin.String? declared in ' type=kotlin.String? origin=null + WHEN type=kotlin.String origin=null + 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_0: kotlin.String? [val] declared in .test' type=kotlin.String? origin=null + arg1: CONST Null type=kotlin.Nothing? value=null + then: TYPE_OP type=kotlin.String origin=IMPLICIT_NOTNULL typeOperand=kotlin.String + CALL 'public/*package*/ open fun foo (): @[FlexibleNullability] kotlin.String? declared in .RightElvisOperand' type=@[FlexibleNullability] kotlin.String? origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: GET_VAR 'val tmp_0: kotlin.String? [val] declared in .test' type=kotlin.String? origin=null diff --git a/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.kt b/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.kt new file mode 100644 index 00000000000..f35e9d4c3fe --- /dev/null +++ b/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.kt @@ -0,0 +1,17 @@ +// TARGET_BACKEND: JVM + +// MODULE: lib +// FILE: RightElvisOperand.java + +class RightElvisOperand { + static String foo() { + return null; + } +} + +// MODULE: main(lib) +// FILE: nullCheckInElvisRhs.kt + +fun baz(): String? = null + +fun test(): String = baz() ?: RightElvisOperand.foo() \ No newline at end of file diff --git a/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.kt.txt b/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.kt.txt new file mode 100644 index 00000000000..cb2bd5e8af2 --- /dev/null +++ b/compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.kt.txt @@ -0,0 +1,18 @@ +// MODULE: lib +// MODULE: main +// FILE: nullCheckInElvisRhs.kt + +fun baz(): String? { + return null +} + +fun test(): String { + return { // BLOCK + val tmp0_elvis_lhs: String? = baz() + when { + EQEQ(arg0 = tmp0_elvis_lhs, arg1 = null) -> foo() /*!! String */ + else -> tmp0_elvis_lhs + } + } +} + diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ir/IrTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ir/IrTextTestGenerated.java index 17e2ec26d5b..21172c979ff 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ir/IrTextTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/ir/IrTextTestGenerated.java @@ -2901,6 +2901,12 @@ public class IrTextTestGenerated extends AbstractIrTextTest { runTest("compiler/testData/ir/irText/types/nullChecks/implicitNotNullOnPlatformType.kt"); } + @Test + @TestMetadata("nullCheckInElvisRhs.kt") + public void testNullCheckInElvisRhs() throws Exception { + runTest("compiler/testData/ir/irText/types/nullChecks/nullCheckInElvisRhs.kt"); + } + @Test @TestMetadata("nullCheckOnInterfaceDelegation.kt") public void testNullCheckOnInterfaceDelegation() throws Exception {