Desugar constructor call of inline class to the underlying value

Also add information about KotlinType to the constant stack values
This commit is contained in:
Mikhail Zarechenskiy
2018-02-09 04:10:58 +03:00
parent 6320368609
commit 8015295059
10 changed files with 123 additions and 10 deletions
@@ -745,7 +745,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
public StackValue visitConstantExpression(@NotNull KtConstantExpression expression, StackValue receiver) { public StackValue visitConstantExpression(@NotNull KtConstantExpression expression, StackValue receiver) {
ConstantValue<?> compileTimeValue = getPrimitiveOrStringCompileTimeConstant(expression); ConstantValue<?> compileTimeValue = getPrimitiveOrStringCompileTimeConstant(expression);
assert compileTimeValue != null; assert compileTimeValue != null;
return StackValue.constant(compileTimeValue.getValue(), expressionType(expression)); return StackValue.constant(compileTimeValue.getValue(), expressionType(expression), kotlinType(expression));
} }
@Nullable @Nullable
@@ -2093,7 +2093,16 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
KtValueArgument valueArgument = CollectionsKt.firstOrNull(expression.getValueArguments()); KtValueArgument valueArgument = CollectionsKt.firstOrNull(expression.getValueArguments());
if (valueArgument == null) return null; 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); return generateNewCall(expression, resolvedCall);
@@ -204,12 +204,17 @@ public abstract class StackValue {
@NotNull @NotNull
public static StackValue constant(@Nullable Object value, @NotNull Type type) { 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) { if (type == Type.BOOLEAN_TYPE) {
assert value instanceof Boolean : "Value for boolean constant should have boolean type: " + value; assert value instanceof Boolean : "Value for boolean constant should have boolean type: " + value;
return BranchedValue.Companion.booleanConstant((Boolean) value); return BranchedValue.Companion.booleanConstant((Boolean) value);
} }
else { 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) { 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 value;
} }
return new CoercionValue(value, castType, castKotlinType); return new CoercionValue(value, castType, castKotlinType, underlyingKotlinType);
} }
@NotNull @NotNull
@@ -925,8 +941,8 @@ public abstract class StackValue {
@Nullable @Nullable
public final Object value; public final Object value;
public Constant(@Nullable Object value, Type type) { public Constant(@Nullable Object value, Type type, KotlinType kotlinType) {
super(type, false); super(type, kotlinType, false);
assert !Type.BOOLEAN_TYPE.equals(type) : "Boolean constants should be created via 'StackValue.constant'"; assert !Type.BOOLEAN_TYPE.equals(type) : "Boolean constants should be created via 'StackValue.constant'";
this.value = value; this.value = value;
} }
@@ -23,12 +23,20 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class CoercionValue( class CoercionValue(
val value: StackValue, val value: StackValue,
private val castType: Type, private val castType: Type,
private val castKotlinType: KotlinType? private val castKotlinType: KotlinType?,
) : StackValue(castType, value.canHaveSideEffects()) { 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) { override fun putSelector(type: Type, kotlinType: KotlinType?, v: InstructionAdapter) {
value.putSelector(value.type, value.kotlinType, v) 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) StackValue.coerce(castType, castKotlinType, type, kotlinType, v)
} }
@@ -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 <T> 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"
}
@@ -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
@@ -10431,6 +10431,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName); 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") @TestMetadata("emptyConstructorForInlineClass.kt")
public void testEmptyConstructorForInlineClass() throws Exception { public void testEmptyConstructorForInlineClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/emptyConstructorForInlineClass.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/emptyConstructorForInlineClass.kt");
@@ -10431,6 +10431,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName); 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") @TestMetadata("emptyConstructorForInlineClass.kt")
public void testEmptyConstructorForInlineClass() throws Exception { public void testEmptyConstructorForInlineClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/emptyConstructorForInlineClass.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/emptyConstructorForInlineClass.kt");
@@ -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); 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") @TestMetadata("boxUnboxInlineClassFromMethodReturnType.kt")
public void testBoxUnboxInlineClassFromMethodReturnType() throws Exception { public void testBoxUnboxInlineClassFromMethodReturnType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxInlineClassFromMethodReturnType.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxInlineClassFromMethodReturnType.kt");
@@ -10431,6 +10431,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName); 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") @TestMetadata("emptyConstructorForInlineClass.kt")
public void testEmptyConstructorForInlineClass() throws Exception { public void testEmptyConstructorForInlineClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/emptyConstructorForInlineClass.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/emptyConstructorForInlineClass.kt");
@@ -11415,6 +11415,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName); 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") @TestMetadata("emptyConstructorForInlineClass.kt")
public void testEmptyConstructorForInlineClass() throws Exception { public void testEmptyConstructorForInlineClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/emptyConstructorForInlineClass.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/emptyConstructorForInlineClass.kt");