diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index c448d77a1a3..4b39a799977 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -745,7 +745,7 @@ public class ExpressionCodegen extends KtVisitor impleme public StackValue visitConstantExpression(@NotNull KtConstantExpression expression, StackValue receiver) { ConstantValue compileTimeValue = getPrimitiveOrStringCompileTimeConstant(expression); assert compileTimeValue != null; - return StackValue.constant(compileTimeValue.getValue(), expressionType(expression)); + return StackValue.constant(compileTimeValue.getValue(), expressionType(expression), kotlinType(expression)); } @Nullable @@ -2093,7 +2093,16 @@ public class ExpressionCodegen extends KtVisitor impleme KtValueArgument valueArgument = CollectionsKt.firstOrNull(expression.getValueArguments()); if (valueArgument == null) return null; - return gen(valueArgument.getArgumentExpression()); + SimpleType inlineClassType = ((ClassDescriptor) descriptor.getContainingDeclaration()).getDefaultType(); + + Type underlyingType = typeMapper.mapType(inlineClassType); + KotlinType underlyingKotlinType = InlineClassesUtilsKt.unsubstitutedUnderlyingType(inlineClassType); + + StackValue argumentValue = gen(valueArgument.getArgumentExpression()); + + return StackValue.coercionValueForArgumentOfInlineClassConstructor( + argumentValue, underlyingType, inlineClassType, underlyingKotlinType + ); } return generateNewCall(expression, resolvedCall); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index 19c8b6edeed..2e2ebee63ff 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -204,12 +204,17 @@ public abstract class StackValue { @NotNull public static StackValue constant(@Nullable Object value, @NotNull Type type) { + return constant(value, type, null); + } + + @NotNull + public static StackValue constant(@Nullable Object value, @NotNull Type type, @Nullable KotlinType kotlinType) { if (type == Type.BOOLEAN_TYPE) { assert value instanceof Boolean : "Value for boolean constant should have boolean type: " + value; return BranchedValue.Companion.booleanConstant((Boolean) value); } else { - return new Constant(value, type); + return new Constant(value, type, kotlinType); } } @@ -567,10 +572,21 @@ public abstract class StackValue { } public static StackValue coercion(@NotNull StackValue value, @NotNull Type castType, @Nullable KotlinType castKotlinType) { - if (value.type.equals(castType)) { + return coercionValueForArgumentOfInlineClassConstructor(value, castType, castKotlinType, null); + } + + public static StackValue coercionValueForArgumentOfInlineClassConstructor( + @NotNull StackValue value, + @NotNull Type castType, + @Nullable KotlinType castKotlinType, + @Nullable KotlinType underlyingKotlinType + ) { + boolean kotlinTypesAreEqual = value.kotlinType == null && castKotlinType == null || + value.kotlinType != null && castKotlinType != null && castKotlinType.equals(value.kotlinType); + if (value.type.equals(castType) && kotlinTypesAreEqual) { return value; } - return new CoercionValue(value, castType, castKotlinType); + return new CoercionValue(value, castType, castKotlinType, underlyingKotlinType); } @NotNull @@ -925,8 +941,8 @@ public abstract class StackValue { @Nullable public final Object value; - public Constant(@Nullable Object value, Type type) { - super(type, false); + public Constant(@Nullable Object value, Type type, KotlinType kotlinType) { + super(type, kotlinType, false); assert !Type.BOOLEAN_TYPE.equals(type) : "Boolean constants should be created via 'StackValue.constant'"; this.value = value; } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.kt index 056774fa3b2..ba6671fff7c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.kt @@ -23,12 +23,20 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter class CoercionValue( val value: StackValue, private val castType: Type, - private val castKotlinType: KotlinType? -) : StackValue(castType, value.canHaveSideEffects()) { + private val castKotlinType: KotlinType?, + private val underlyingKotlinType: KotlinType? // type of the underlying parameter for inline class +) : StackValue(castType, castKotlinType, value.canHaveSideEffects()) { override fun putSelector(type: Type, kotlinType: KotlinType?, v: InstructionAdapter) { value.putSelector(value.type, value.kotlinType, v) - StackValue.coerce(value.type, value.kotlinType, castType, castKotlinType, v) + + // consider the following example: + + // inline class AsAny(val a: Any) + // val a = AsAny(1) + // + // Here we should coerce `Int` (1) to `Any` and remember that resulting type is inline class type `AsAny` (not `Any`) + StackValue.coerce(value.type, value.kotlinType, castType, underlyingKotlinType ?: castKotlinType, v) StackValue.coerce(castType, castKotlinType, type, kotlinType, v) } diff --git a/compiler/testData/codegen/box/inlineClasses/createInlineClassInArgumentPosition.kt b/compiler/testData/codegen/box/inlineClasses/createInlineClassInArgumentPosition.kt new file mode 100644 index 00000000000..16d40a585fb --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/createInlineClassInArgumentPosition.kt @@ -0,0 +1,30 @@ +// !LANGUAGE: +InlineClasses + +inline class AsInt(val value: Int) { + override fun toString(): String { + return "asInt: ${value.toString()}" + } +} + +inline class AsAny(val value: Any) { + override fun toString(): String { + return "asAny: ${value.toString()}" + } +} + +fun takeAny(a: Any): String = a.toString() + +fun getInt(): Int = 10 +fun id(x: T) = x + +fun box(): String { + if (takeAny(AsInt(123)) != "asInt: 123") return "fail" + if (takeAny(AsAny(321)) != "asAny: 321") return "fail" + + if (takeAny(AsInt(getInt())) != "asInt: 10") return "fail" + if (takeAny(AsInt(id(20))) != "asInt: 20") return "fail" + + if (takeAny(AsAny(id(30))) != "asAny: 30") return "fail" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/boxResultAfterConstructorCall.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/boxResultAfterConstructorCall.kt new file mode 100644 index 00000000000..34ac5df74a6 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/boxResultAfterConstructorCall.kt @@ -0,0 +1,20 @@ +// !LANGUAGE: +InlineClasses + +inline class AsInt(val value: Int) +inline class AsAny(val value: Any) + +fun takeAny(a: Any) {} + +fun test() { + takeAny(AsInt(123)) // box + takeAny(AsAny(123)) // box int, box inline class +} + +// 1 INVOKESTATIC AsInt\$Erased.box +// 0 INVOKEVIRTUAL AsInt.unbox + +// 1 INVOKESTATIC AsAny\$Erased.box +// 0 INVOKEVIRTUAL AsAny.unbox + +// 1 valueOf +// 0 intValue \ No newline at end of file diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 13a035f0b26..a0d72fc9946 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -10431,6 +10431,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("createInlineClassInArgumentPosition.kt") + public void testCreateInlineClassInArgumentPosition() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/createInlineClassInArgumentPosition.kt"); + doTest(fileName); + } + @TestMetadata("emptyConstructorForInlineClass.kt") public void testEmptyConstructorForInlineClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/emptyConstructorForInlineClass.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 353e6c701ce..e7ada07ffe9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -10431,6 +10431,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("createInlineClassInArgumentPosition.kt") + public void testCreateInlineClassInArgumentPosition() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/createInlineClassInArgumentPosition.kt"); + doTest(fileName); + } + @TestMetadata("emptyConstructorForInlineClass.kt") public void testEmptyConstructorForInlineClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/emptyConstructorForInlineClass.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 111f9ada27f..f01dd56fb92 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1914,6 +1914,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("boxResultAfterConstructorCall.kt") + public void testBoxResultAfterConstructorCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/boxResultAfterConstructorCall.kt"); + doTest(fileName); + } + @TestMetadata("boxUnboxInlineClassFromMethodReturnType.kt") public void testBoxUnboxInlineClassFromMethodReturnType() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxInlineClassFromMethodReturnType.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index c41ef4bc80b..6d94de36155 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -10431,6 +10431,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("createInlineClassInArgumentPosition.kt") + public void testCreateInlineClassInArgumentPosition() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/createInlineClassInArgumentPosition.kt"); + doTest(fileName); + } + @TestMetadata("emptyConstructorForInlineClass.kt") public void testEmptyConstructorForInlineClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/emptyConstructorForInlineClass.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 7c5a3735781..8d82a701ca1 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -11415,6 +11415,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("createInlineClassInArgumentPosition.kt") + public void testCreateInlineClassInArgumentPosition() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/createInlineClassInArgumentPosition.kt"); + doTest(fileName); + } + @TestMetadata("emptyConstructorForInlineClass.kt") public void testEmptyConstructorForInlineClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/emptyConstructorForInlineClass.kt");