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) {
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<StackValue, StackValue> 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);
@@ -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;
}
@@ -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)
}
@@ -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);
}
@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");
@@ -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");
@@ -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");
@@ -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");
@@ -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");