Initial boxing for inline classes: local variable assignments
This commit is contained in:
@@ -3750,7 +3750,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
Type varType = getVariableTypeNoSharing(variableDescriptor);
|
||||
|
||||
StackValue storeTo = sharedVarType == null ? StackValue.local(index, varType) : StackValue.shared(index, varType);
|
||||
StackValue storeTo = sharedVarType == null ?
|
||||
StackValue.local(index, varType, variableDescriptor) :
|
||||
StackValue.shared(index, varType, variableDescriptor);
|
||||
|
||||
storeTo.putReceiver(v, false);
|
||||
if (variableDescriptor.isLateInit()) {
|
||||
@@ -3765,6 +3767,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
markLineNumber(variableDeclaration, false);
|
||||
Type resultType = initializer.type;
|
||||
KotlinType resultKotlinType = initializer.kotlinType;
|
||||
|
||||
if (isDelegatedLocalVariable(variableDescriptor)) {
|
||||
StackValue metadataValue = getVariableMetadataValue(variableDescriptor);
|
||||
@@ -3772,15 +3775,17 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
ResolvedCall<FunctionDescriptor> provideDelegateCall = bindingContext.get(PROVIDE_DELEGATE_RESOLVED_CALL, variableDescriptor);
|
||||
if (provideDelegateCall != null) {
|
||||
resultType = generateProvideDelegateCallForLocalVariable(initializer, metadataValue, provideDelegateCall);
|
||||
StackValue provideDelegateValue = generateProvideDelegateCallForLocalVariable(initializer, metadataValue, provideDelegateCall);
|
||||
resultType = provideDelegateValue.type;
|
||||
resultKotlinType = provideDelegateValue.kotlinType;
|
||||
}
|
||||
}
|
||||
|
||||
storeTo.storeSelector(resultType, null, v);
|
||||
storeTo.storeSelector(resultType, resultKotlinType, v);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Type generateProvideDelegateCallForLocalVariable(
|
||||
private StackValue generateProvideDelegateCallForLocalVariable(
|
||||
@NotNull StackValue initializer,
|
||||
StackValue metadataValue,
|
||||
ResolvedCall<FunctionDescriptor> provideDelegateResolvedCall
|
||||
@@ -3808,7 +3813,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
result.put(result.type, result.kotlinType, v);
|
||||
tempVariables.remove(arguments.get(0).asElement());
|
||||
tempVariables.remove(arguments.get(1).asElement());
|
||||
return result.type;
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -23,9 +23,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
import org.jetbrains.kotlin.psi.ValueArgument;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor;
|
||||
import org.jetbrains.kotlin.resolve.*;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
|
||||
@@ -153,7 +151,7 @@ public abstract class StackValue {
|
||||
|
||||
@NotNull
|
||||
public static Local local(int index, @NotNull Type type, @NotNull VariableDescriptor descriptor) {
|
||||
return new Local(index, type, descriptor.isLateInit(), descriptor.getName());
|
||||
return new Local(index, type, descriptor.getType(), descriptor.isLateInit(), descriptor.getName());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -174,7 +172,7 @@ public abstract class StackValue {
|
||||
|
||||
@NotNull
|
||||
public static StackValue shared(int index, @NotNull Type type, @NotNull VariableDescriptor descriptor) {
|
||||
return new Shared(index, type, descriptor.isLateInit(), descriptor.getName());
|
||||
return new Shared(index, type, descriptor.getType(), descriptor.isLateInit(), descriptor.getName());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -363,12 +361,81 @@ public abstract class StackValue {
|
||||
v.invokevirtual(methodOwner.getInternalName(), type.getClassName() + "Value", "()" + type.getDescriptor(), false);
|
||||
}
|
||||
|
||||
private static void boxInlineClass(@NotNull KotlinType kotlinType, @NotNull InstructionAdapter v) {
|
||||
Type boxedType = KotlinTypeMapper.mapInlineClassTypeAsDeclaration(kotlinType);
|
||||
Type owner = KotlinTypeMapper.mapToErasedInlineClassType(kotlinType);
|
||||
Type underlyingType = KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType(kotlinType);
|
||||
v.invokestatic(
|
||||
owner.getInternalName(),
|
||||
InlineClassDescriptorResolver.BOX_METHOD_NAME.asString(),
|
||||
Type.getMethodDescriptor(boxedType, underlyingType),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
private static void unboxInlineClass(@NotNull KotlinType kotlinType, @NotNull InstructionAdapter v) {
|
||||
Type owner = KotlinTypeMapper.mapInlineClassTypeAsDeclaration(kotlinType);
|
||||
Type resultType = KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType(kotlinType);
|
||||
v.invokevirtual(
|
||||
owner.getInternalName(),
|
||||
InlineClassDescriptorResolver.UNBOX_METHOD_NAME.asString(),
|
||||
"()" + resultType.getDescriptor(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
protected void coerceTo(@NotNull Type toType, @Nullable KotlinType toKotlinType, @NotNull InstructionAdapter v) {
|
||||
coerce(this.type, toType, v);
|
||||
coerce(this.type, this.kotlinType, toType, toKotlinType, v);
|
||||
}
|
||||
|
||||
protected void coerceFrom(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) {
|
||||
coerce(topOfStackType, this.type, v);
|
||||
coerce(topOfStackType, topOfStackKotlinType, this.type, this.kotlinType, v);
|
||||
}
|
||||
|
||||
public static void coerce(
|
||||
@NotNull Type fromType,
|
||||
@Nullable KotlinType fromKotlinType,
|
||||
@NotNull Type toType,
|
||||
@Nullable KotlinType toKotlinType,
|
||||
@NotNull InstructionAdapter v
|
||||
) {
|
||||
if (coerceInlineClasses(fromType, fromKotlinType, toType, toKotlinType, v)) return;
|
||||
coerce(fromType, toType, v);
|
||||
}
|
||||
|
||||
private static boolean coerceInlineClasses(
|
||||
@NotNull Type fromType,
|
||||
@Nullable KotlinType fromKotlinType,
|
||||
@NotNull Type toType,
|
||||
@Nullable KotlinType toKotlinType,
|
||||
@NotNull InstructionAdapter v
|
||||
) {
|
||||
if (fromKotlinType == null || toKotlinType == null) return false;
|
||||
if (!InlineClassesUtilsKt.isInlineClassType(fromKotlinType)) return false;
|
||||
|
||||
if (fromKotlinType.equals(toKotlinType) && fromType.equals(toType)) return true;
|
||||
|
||||
boolean isFromTypeUnboxed = isUnboxedInlineClass(fromKotlinType, fromType);
|
||||
if (InlineClassesUtilsKt.isInlineClassType(toKotlinType)) {
|
||||
boolean isToTypeUnboxed = isUnboxedInlineClass(toKotlinType, toType);
|
||||
if (isFromTypeUnboxed && !isToTypeUnboxed) {
|
||||
boxInlineClass(fromKotlinType, v);
|
||||
}
|
||||
else if (!isFromTypeUnboxed) {
|
||||
unboxInlineClass(fromKotlinType, v);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isFromTypeUnboxed) {
|
||||
boxInlineClass(fromKotlinType, v);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isUnboxedInlineClass(@NotNull KotlinType kotlinType, @NotNull Type actualType) {
|
||||
return KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType(kotlinType).equals(actualType);
|
||||
}
|
||||
|
||||
public static void coerce(@NotNull Type fromType, @NotNull Type toType, @NotNull InstructionAdapter v) {
|
||||
@@ -674,8 +741,8 @@ public abstract class StackValue {
|
||||
private final boolean isLateinit;
|
||||
private final Name name;
|
||||
|
||||
private Local(int index, Type type, boolean isLateinit, Name name) {
|
||||
super(type, false);
|
||||
private Local(int index, Type type, KotlinType kotlinType, boolean isLateinit, Name name) {
|
||||
super(type, kotlinType, false);
|
||||
|
||||
if (index < 0) {
|
||||
throw new IllegalStateException("local variable index must be non-negative");
|
||||
@@ -691,7 +758,7 @@ public abstract class StackValue {
|
||||
}
|
||||
|
||||
private Local(int index, Type type) {
|
||||
this(index, type, false, null);
|
||||
this(index, type, null, false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -876,7 +943,7 @@ public abstract class StackValue {
|
||||
private final Type type;
|
||||
|
||||
public ArrayElement(Type type, StackValue array, StackValue index) {
|
||||
super(type, false, false, new Receiver(Type.LONG_TYPE, array, index), true);
|
||||
super(type, null, false, false, new Receiver(Type.LONG_TYPE, array, index), true);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@@ -1069,7 +1136,7 @@ public abstract class StackValue {
|
||||
@Nullable ResolvedCall<FunctionDescriptor> resolvedSetCall,
|
||||
@NotNull ExpressionCodegen codegen
|
||||
) {
|
||||
super(type, false, false, collectionElementReceiver, true);
|
||||
super(type, null, false, false, collectionElementReceiver, true);
|
||||
this.resolvedGetCall = resolvedGetCall;
|
||||
this.resolvedSetCall = resolvedSetCall;
|
||||
this.setter = resolvedSetCall == null ? null :
|
||||
@@ -1193,7 +1260,7 @@ public abstract class StackValue {
|
||||
@NotNull StackValue receiver,
|
||||
@Nullable DeclarationDescriptor descriptor
|
||||
) {
|
||||
super(type, isStatic, isStatic, receiver, receiver.canHaveSideEffects());
|
||||
super(type, null, isStatic, isStatic, receiver, receiver.canHaveSideEffects());
|
||||
this.owner = owner;
|
||||
this.name = name;
|
||||
this.descriptor = descriptor;
|
||||
@@ -1233,7 +1300,7 @@ public abstract class StackValue {
|
||||
@NotNull StackValue receiver, @NotNull ExpressionCodegen codegen, @Nullable ResolvedCall resolvedCall,
|
||||
boolean skipLateinitAssertion
|
||||
) {
|
||||
super(type, isStatic(isStaticBackingField, getter), isStatic(isStaticBackingField, setter), receiver, true);
|
||||
super(type, descriptor.getType(), isStatic(isStaticBackingField, getter), isStatic(isStaticBackingField, setter), receiver, true);
|
||||
this.backingFieldOwner = backingFieldOwner;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
@@ -1419,8 +1486,8 @@ public abstract class StackValue {
|
||||
private final boolean isLateinit;
|
||||
private final Name name;
|
||||
|
||||
public Shared(int index, Type type, boolean isLateinit, Name name) {
|
||||
super(type, false, false, local(index, OBJECT_TYPE), false);
|
||||
public Shared(int index, Type type, KotlinType kotlinType, boolean isLateinit, Name name) {
|
||||
super(type, kotlinType, false, false, local(index, OBJECT_TYPE), false);
|
||||
this.index = index;
|
||||
|
||||
if (isLateinit && name == null) {
|
||||
@@ -1432,7 +1499,7 @@ public abstract class StackValue {
|
||||
}
|
||||
|
||||
public Shared(int index, Type type) {
|
||||
this(index, type, false, null);
|
||||
this(index, type, null, false, null);
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
@@ -1491,7 +1558,7 @@ public abstract class StackValue {
|
||||
Type type, Type owner, String name, StackValue.Field receiver,
|
||||
boolean isLateinit, Name variableName
|
||||
) {
|
||||
super(type, false, false, receiver, receiver.canHaveSideEffects());
|
||||
super(type, null, false, false, receiver, receiver.canHaveSideEffects());
|
||||
|
||||
if (isLateinit && variableName == null) {
|
||||
throw new IllegalArgumentException("variableName should be non-null for captured lateinit variable " + name);
|
||||
@@ -1631,12 +1698,13 @@ public abstract class StackValue {
|
||||
|
||||
public StackValueWithSimpleReceiver(
|
||||
@NotNull Type type,
|
||||
@Nullable KotlinType kotlinType,
|
||||
boolean isStaticPut,
|
||||
boolean isStaticStore,
|
||||
@NotNull StackValue receiver,
|
||||
boolean canHaveSideEffects
|
||||
) {
|
||||
super(type, canHaveSideEffects);
|
||||
super(type, kotlinType, canHaveSideEffects);
|
||||
this.receiver = receiver;
|
||||
this.isStaticPut = isStaticPut;
|
||||
this.isStaticStore = isStaticStore;
|
||||
@@ -1783,7 +1851,7 @@ public abstract class StackValue {
|
||||
@NotNull StackValueWithSimpleReceiver originalValue,
|
||||
@NotNull ComplexReceiver receiver
|
||||
) {
|
||||
super(type, bothReceiverStatic(originalValue), bothReceiverStatic(originalValue), receiver, originalValue.canHaveSideEffects());
|
||||
super(type, null, bothReceiverStatic(originalValue), bothReceiverStatic(originalValue), receiver, originalValue.canHaveSideEffects());
|
||||
this.originalValue = originalValue;
|
||||
}
|
||||
|
||||
@@ -1868,7 +1936,7 @@ public abstract class StackValue {
|
||||
@Nullable private final Label ifNull;
|
||||
|
||||
public SafeFallback(@NotNull Type type, @Nullable Label ifNull, StackValue receiver) {
|
||||
super(type, false, false, receiver, true);
|
||||
super(type, null, false, false, receiver, true);
|
||||
this.ifNull = ifNull;
|
||||
}
|
||||
|
||||
|
||||
@@ -470,11 +470,33 @@ public class KotlinTypeMapper {
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Type mapInlineClassTypeAsDeclaration(@NotNull KotlinType kotlinType) {
|
||||
return mapInlineClassType(kotlinType, TypeMappingMode.CLASS_DECLARATION);
|
||||
}
|
||||
|
||||
public static Type mapInlineClassType(
|
||||
@NotNull
|
||||
public static Type mapUnderlyingTypeOfInlineClassType(@NotNull KotlinType kotlinType) {
|
||||
KotlinType underlyingType = InlineClassesUtilsKt.underlyingTypeOfInlineClassType(kotlinType);
|
||||
if (underlyingType == null) {
|
||||
throw new IllegalStateException("There should be underlying type for inline class type: " + kotlinType);
|
||||
}
|
||||
return mapInlineClassType(underlyingType, TypeMappingMode.DEFAULT);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Type mapToErasedInlineClassType(@NotNull KotlinType kotlinType) {
|
||||
return Type.getObjectType(
|
||||
mapInlineClassTypeAsDeclaration(kotlinType).getInternalName() + JvmAbi.ERASED_INLINE_CLASS_SUFFIX
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Type mapInlineClassType(@NotNull KotlinType kotlinType) {
|
||||
return mapInlineClassType(kotlinType, TypeMappingMode.DEFAULT);
|
||||
}
|
||||
|
||||
private static Type mapInlineClassType(
|
||||
@NotNull KotlinType kotlinType,
|
||||
@NotNull TypeMappingMode mode
|
||||
) {
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
|
||||
inline class InlineNotNullPrimitive(val x: Int)
|
||||
inline class InlineNullablePrimitive(val x: Int?)
|
||||
inline class InlineNotNullReference(val a: Any)
|
||||
inline class InlineNullableReference(val a: Any?)
|
||||
|
||||
fun test1(a: InlineNotNullPrimitive) {
|
||||
val a0 = a
|
||||
val a1: Any = a // box
|
||||
val a2: Any? = a // box
|
||||
val a3: InlineNotNullPrimitive = a
|
||||
val a4: InlineNotNullPrimitive? = a // box
|
||||
}
|
||||
|
||||
fun test2(b: InlineNullablePrimitive) {
|
||||
val b0 = b
|
||||
val b1: Any = b // box
|
||||
val b2: Any? = b // box
|
||||
val b3: InlineNullablePrimitive = b
|
||||
val b4: InlineNullablePrimitive? = b // box
|
||||
}
|
||||
|
||||
fun test3(c: InlineNotNullReference) {
|
||||
val c0 = c
|
||||
val c1: Any = c // box
|
||||
val c2: Any? = c // box
|
||||
val c3: InlineNotNullReference = c
|
||||
val c4: InlineNotNullReference? = c
|
||||
}
|
||||
|
||||
fun test4(d: InlineNullableReference) {
|
||||
val d0 = d
|
||||
val d1: Any = d // box
|
||||
val d2: Any? = d // box
|
||||
val d3: InlineNullableReference = d
|
||||
val d4: InlineNullableReference? = d // box
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val a = InlineNotNullPrimitive(1)
|
||||
val b = InlineNullablePrimitive(1)
|
||||
val c = InlineNotNullReference("some")
|
||||
val d = InlineNullableReference("other")
|
||||
|
||||
test1(a)
|
||||
test2(b)
|
||||
test3(c)
|
||||
test4(d)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
|
||||
inline class InlineNotNullPrimitive(val x: Int)
|
||||
inline class InlineNullablePrimitive(val x: Int?)
|
||||
inline class InlineNotNullReference(val a: Any)
|
||||
inline class InlineNullableReference(val a: Any?)
|
||||
|
||||
fun test1(a: InlineNotNullPrimitive) {
|
||||
val a0 = a
|
||||
val a1: Any = a // box
|
||||
val a2: Any? = a // box
|
||||
val a3: InlineNotNullPrimitive = a
|
||||
val a4: InlineNotNullPrimitive? = a // box
|
||||
}
|
||||
|
||||
fun test2(b: InlineNullablePrimitive) {
|
||||
val b0 = b
|
||||
val b1: Any = b // box
|
||||
val b2: Any? = b // box
|
||||
val b3: InlineNullablePrimitive = b
|
||||
val b4: InlineNullablePrimitive? = b // box
|
||||
}
|
||||
|
||||
fun test3(c: InlineNotNullReference) {
|
||||
val c0 = c
|
||||
val c1: Any = c // box
|
||||
val c2: Any? = c // box
|
||||
val c3: InlineNotNullReference = c
|
||||
val c4: InlineNotNullReference? = c
|
||||
}
|
||||
|
||||
fun test4(d: InlineNullableReference) {
|
||||
val d0 = d
|
||||
val d1: Any = d // box
|
||||
val d2: Any? = d // box
|
||||
val d3: InlineNullableReference = d
|
||||
val d4: InlineNullableReference? = d // box
|
||||
}
|
||||
|
||||
// 3 INVOKESTATIC InlineNotNullPrimitive\$Erased.box
|
||||
// 3 INVOKESTATIC InlineNullablePrimitive\$Erased.box
|
||||
// 2 INVOKESTATIC InlineNotNullReference\$Erased.box
|
||||
// 3 INVOKESTATIC InlineNullableReference\$Erased.box
|
||||
|
||||
// 0 valueOf
|
||||
@@ -1925,6 +1925,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/checkOuterInlineFunctionCall.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineClassBoxingOnAssignment.kt")
|
||||
public void testInlineClassBoxingOnAssignment() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassBoxingOnAssignment.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/interfaces")
|
||||
|
||||
@@ -8,10 +8,18 @@ package org.jetbrains.kotlin.resolve
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
fun ClassDescriptor.underlyingRepresentation(): ValueParameterDescriptor? {
|
||||
if (!isInline) return null
|
||||
return unsubstitutedPrimaryConstructor?.valueParameters?.singleOrNull()
|
||||
}
|
||||
|
||||
fun DeclarationDescriptor.isInlineClass() = this is ClassDescriptor && this.isInline
|
||||
fun DeclarationDescriptor.isInlineClass() = this is ClassDescriptor && this.isInline
|
||||
|
||||
fun KotlinType.underlyingTypeOfInlineClassType(): KotlinType? {
|
||||
return constructor.declarationDescriptor.safeAs<ClassDescriptor>()?.underlyingRepresentation()?.type
|
||||
}
|
||||
|
||||
fun KotlinType.isInlineClassType(): Boolean = constructor.declarationDescriptor?.isInlineClass() ?: false
|
||||
Reference in New Issue
Block a user