JVM_IR: use nullability when boxing/unboxing inline class types
Given inline class V(Any?), a coercion from (Object, V) to (Object, V?) is boxing. In theory, the same issue in the old backend can be fixed by making `KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType` use `computeExpandedTypeForInlineClass`, but for some reason this breaks a lot of stuff. #KT-48430 Fixed
This commit is contained in:
@@ -458,14 +458,19 @@ public abstract class StackValue {
|
|||||||
public static void boxInlineClass(
|
public static void boxInlineClass(
|
||||||
@NotNull KotlinTypeMarker kotlinType, @NotNull InstructionAdapter v, @NotNull KotlinTypeMapperBase typeMapper
|
@NotNull KotlinTypeMarker kotlinType, @NotNull InstructionAdapter v, @NotNull KotlinTypeMapperBase typeMapper
|
||||||
) {
|
) {
|
||||||
Type boxedType = typeMapper.mapTypeCommon(kotlinType, TypeMappingMode.CLASS_DECLARATION);
|
Type boxed = typeMapper.mapTypeCommon(kotlinType, TypeMappingMode.CLASS_DECLARATION);
|
||||||
Type underlyingType = KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType(kotlinType, typeMapper);
|
Type unboxed = KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType(kotlinType, typeMapper);
|
||||||
|
boolean isNullable = typeMapper.getTypeSystem().isNullableType(kotlinType) && !isPrimitive(unboxed);
|
||||||
|
boxInlineClass(unboxed, boxed, isNullable, v);
|
||||||
|
}
|
||||||
|
|
||||||
if (typeMapper.getTypeSystem().isNullableType(kotlinType) && !isPrimitive(underlyingType)) {
|
public static void boxInlineClass(
|
||||||
boxOrUnboxWithNullCheck(v, vv -> invokeBoxMethod(vv, boxedType, underlyingType));
|
@NotNull Type unboxed, @NotNull Type boxed, boolean isNullable, @NotNull InstructionAdapter v
|
||||||
}
|
) {
|
||||||
else {
|
if (isNullable) {
|
||||||
invokeBoxMethod(v, boxedType, underlyingType);
|
boxOrUnboxWithNullCheck(v, vv -> invokeBoxMethod(vv, boxed, unboxed));
|
||||||
|
} else {
|
||||||
|
invokeBoxMethod(v, boxed, unboxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,17 +493,20 @@ public abstract class StackValue {
|
|||||||
@NotNull InstructionAdapter v,
|
@NotNull InstructionAdapter v,
|
||||||
@NotNull KotlinTypeMapperBase typeMapper
|
@NotNull KotlinTypeMapperBase typeMapper
|
||||||
) {
|
) {
|
||||||
Type owner = typeMapper.mapTypeCommon(targetInlineClassType, TypeMappingMode.CLASS_DECLARATION);
|
Type boxed = typeMapper.mapTypeCommon(targetInlineClassType, TypeMappingMode.CLASS_DECLARATION);
|
||||||
|
Type unboxed = KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType(targetInlineClassType, typeMapper);
|
||||||
|
boolean isNullable = typeMapper.getTypeSystem().isNullableType(targetInlineClassType) && !isPrimitive(unboxed);
|
||||||
|
unboxInlineClass(type, boxed, unboxed, isNullable, v);
|
||||||
|
}
|
||||||
|
|
||||||
coerce(type, owner, v);
|
public static void unboxInlineClass(
|
||||||
|
@NotNull Type type, @NotNull Type boxed, @NotNull Type unboxed, boolean isNullable, @NotNull InstructionAdapter v
|
||||||
Type resultType = KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType(targetInlineClassType, typeMapper);
|
) {
|
||||||
|
coerce(type, boxed, v);
|
||||||
if (typeMapper.getTypeSystem().isNullableType(targetInlineClassType) && !isPrimitive(resultType)) {
|
if (isNullable) {
|
||||||
boxOrUnboxWithNullCheck(v, vv -> invokeUnboxMethod(vv, owner, resultType));
|
boxOrUnboxWithNullCheck(v, vv -> invokeUnboxMethod(vv, boxed, unboxed));
|
||||||
}
|
} else {
|
||||||
else {
|
invokeUnboxMethod(v, boxed, unboxed);
|
||||||
invokeUnboxMethod(v, owner, resultType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
@@ -18370,6 +18370,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("boxNullableForFakeOverride.kt")
|
||||||
|
public void testBoxNullableForFakeOverride() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableForFakeOverride.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
||||||
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
||||||
|
|||||||
+17
-26
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
|||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.isTypeParameter
|
import org.jetbrains.kotlin.ir.util.isTypeParameter
|
||||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.org.objectweb.asm.Label
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
@@ -27,35 +28,25 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val
|
|||||||
open fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
|
open fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
|
||||||
val erasedSourceType = irType.eraseTypeParameters()
|
val erasedSourceType = irType.eraseTypeParameters()
|
||||||
val erasedTargetType = irTarget.eraseTypeParameters()
|
val erasedTargetType = irTarget.eraseTypeParameters()
|
||||||
val fromTypeRepresentation = erasedSourceType.getClass()!!.inlineClassRepresentation
|
|
||||||
val toTypeRepresentation = erasedTargetType.getClass()!!.inlineClassRepresentation
|
|
||||||
|
|
||||||
// Coerce inline classes
|
// Coerce inline classes
|
||||||
if (fromTypeRepresentation != null || toTypeRepresentation != null) {
|
val isFromTypeUnboxed = InlineClassAbi.unboxType(erasedSourceType)?.let(typeMapper::mapType) == type
|
||||||
val isFromTypeUnboxed = fromTypeRepresentation?.underlyingType?.let(typeMapper::mapType) == type
|
val isToTypeUnboxed = InlineClassAbi.unboxType(erasedTargetType)?.let(typeMapper::mapType) == target
|
||||||
val isToTypeUnboxed = toTypeRepresentation?.underlyingType?.let(typeMapper::mapType) == target
|
if (isFromTypeUnboxed && !isToTypeUnboxed) {
|
||||||
|
val boxed = typeMapper.mapType(erasedSourceType, TypeMappingMode.CLASS_DECLARATION)
|
||||||
when {
|
StackValue.boxInlineClass(type, boxed, erasedSourceType.isNullable(), mv)
|
||||||
isFromTypeUnboxed && !isToTypeUnboxed -> {
|
return
|
||||||
StackValue.boxInlineClass(erasedSourceType, mv, typeMapper)
|
}
|
||||||
return
|
if (!isFromTypeUnboxed && isToTypeUnboxed) {
|
||||||
}
|
val boxed = typeMapper.mapType(erasedTargetType, TypeMappingMode.CLASS_DECLARATION)
|
||||||
|
val irClass = codegen.irFunction.parentAsClass
|
||||||
!isFromTypeUnboxed && isToTypeUnboxed -> {
|
if (irClass.isInline && irClass.symbol == irType.classifierOrNull && !irType.isNullable()) {
|
||||||
val irClass = codegen.irFunction.parentAsClass
|
// Use getfield instead of unbox-impl inside inline classes
|
||||||
if (irClass.isInline && irClass.symbol == irType.classifierOrNull && !irType.isNullable()) {
|
codegen.mv.getfield(boxed.internalName, irClass.inlineClassFieldName.asString(), target.descriptor)
|
||||||
// Use getfield instead of unbox-impl inside inline classes
|
} else {
|
||||||
codegen.mv.getfield(
|
StackValue.unboxInlineClass(type, boxed, target, erasedTargetType.isNullable(), mv)
|
||||||
typeMapper.classInternalName(irClass),
|
|
||||||
irClass.inlineClassFieldName.asString(),
|
|
||||||
typeMapper.mapType(irType).descriptor
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
StackValue.unboxInlineClass(type, erasedTargetType, mv, typeMapper)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type != target || (castForReified && irType.anyTypeArgument { it.isReified })) {
|
if (type != target || (castForReified && irType.anyTypeArgument { it.isReified })) {
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// IGNORE_BACKEND: JVM
|
||||||
|
abstract class C<T> {
|
||||||
|
fun foo(v: T?, x: (T) -> Any?) = v?.let { x(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
inline class V(val value: Any?)
|
||||||
|
|
||||||
|
class D : C<V>()
|
||||||
|
|
||||||
|
fun box() = D().foo(V("OK")) { it.value } as String
|
||||||
+6
@@ -18220,6 +18220,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("boxNullableForFakeOverride.kt")
|
||||||
|
public void testBoxNullableForFakeOverride() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableForFakeOverride.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
||||||
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
||||||
|
|||||||
+6
@@ -18370,6 +18370,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("boxNullableForFakeOverride.kt")
|
||||||
|
public void testBoxNullableForFakeOverride() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableForFakeOverride.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
||||||
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
||||||
|
|||||||
+5
@@ -15162,6 +15162,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("boxNullableForFakeOverride.kt")
|
||||||
|
public void testBoxNullableForFakeOverride() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableForFakeOverride.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
||||||
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt");
|
||||||
|
|||||||
js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java
Generated
+5
@@ -13176,6 +13176,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("boxNullableForFakeOverride.kt")
|
||||||
|
public void testBoxNullableForFakeOverride() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableForFakeOverride.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
||||||
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt");
|
||||||
|
|||||||
Generated
+5
@@ -12582,6 +12582,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("boxNullableForFakeOverride.kt")
|
||||||
|
public void testBoxNullableForFakeOverride() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableForFakeOverride.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
||||||
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt");
|
||||||
|
|||||||
Generated
+5
@@ -12647,6 +12647,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("boxNullableForFakeOverride.kt")
|
||||||
|
public void testBoxNullableForFakeOverride() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableForFakeOverride.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
||||||
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt");
|
||||||
|
|||||||
js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java
Generated
+5
@@ -6727,6 +6727,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("boxNullableForFakeOverride.kt")
|
||||||
|
public void testBoxNullableForFakeOverride() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableForFakeOverride.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
@TestMetadata("boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt")
|
||||||
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
public void testBoxNullableValueOfInlineClassWithNonNullUnderlyingType() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/boxNullableValueOfInlineClassWithNonNullUnderlyingType.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user