Generate is check for inline classes same as for primitives

Should be improved when inline class is based on non-null reference
This commit is contained in:
Mikhail Zarechenskiy
2018-02-12 03:59:38 +03:00
parent d606e5bc89
commit a88dbefcd9
9 changed files with 134 additions and 15 deletions
@@ -4369,20 +4369,20 @@ The "returned" value of try expression with no finally is either the last expres
}
private StackValue generateIsCheck(StackValue expressionToMatch, KtTypeReference typeReference, boolean negated) {
KotlinType jetType = bindingContext.get(TYPE, typeReference);
KotlinType kotlinType = bindingContext.get(TYPE, typeReference);
markStartLineNumber(typeReference);
StackValue value = generateIsCheck(expressionToMatch, jetType, false);
StackValue value = generateIsCheck(expressionToMatch, kotlinType, false);
return negated ? StackValue.not(value) : value;
}
private StackValue generateIsCheck(StackValue expressionToGen, KotlinType kotlinType, boolean leaveExpressionOnStack) {
return StackValue.operation(Type.BOOLEAN_TYPE, v -> {
expressionToGen.put(OBJECT_TYPE, null, v);
expressionToGen.put(OBJECT_TYPE, kotlinType, v);
if (leaveExpressionOnStack) {
v.dup();
}
Type type = boxType(asmType(kotlinType));
Type type = boxType(typeMapper.mapTypeAsDeclaration(kotlinType));
if (TypeUtils.isReifiedTypeParameter(kotlinType)) {
putReifiedOperationMarkerIfTypeIsReifiedParameter(kotlinType, ReifiedTypeInliner.OperationKind.IS);
v.instanceOf(type);
@@ -399,12 +399,12 @@ public abstract class StackValue {
);
}
private static void unboxInlineClass(@NotNull Type type, @NotNull KotlinType kotlinType, @NotNull InstructionAdapter v) {
Type owner = KotlinTypeMapper.mapInlineClassTypeAsDeclaration(kotlinType);
private static void unboxInlineClass(@NotNull Type type, @NotNull KotlinType targetInlineClassType, @NotNull InstructionAdapter v) {
Type owner = KotlinTypeMapper.mapInlineClassTypeAsDeclaration(targetInlineClassType);
coerce(type, owner, v);
Type resultType = KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType(kotlinType);
Type resultType = KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType(targetInlineClassType);
v.invokevirtual(
owner.getInternalName(),
InlineClassDescriptorResolver.UNBOX_METHOD_NAME.asString(),
@@ -440,25 +440,34 @@ public abstract class StackValue {
@NotNull InstructionAdapter v
) {
if (fromKotlinType == null || toKotlinType == null) return false;
if (!InlineClassesUtilsKt.isInlineClassType(fromKotlinType)) return false;
boolean isFromTypeInlineClass = InlineClassesUtilsKt.isInlineClassType(fromKotlinType);
boolean isToTypeInlineClass = InlineClassesUtilsKt.isInlineClassType(toKotlinType);
if (!isFromTypeInlineClass && !isToTypeInlineClass) return false;
if (fromKotlinType.equals(toKotlinType) && fromType.equals(toType)) return true;
boolean isFromTypeUnboxed = isUnboxedInlineClass(fromKotlinType, fromType);
if (InlineClassesUtilsKt.isInlineClassType(toKotlinType)) {
if (isFromTypeInlineClass && isToTypeInlineClass) {
boolean isFromTypeUnboxed = isUnboxedInlineClass(fromKotlinType, fromType);
boolean isToTypeUnboxed = isUnboxedInlineClass(toKotlinType, toType);
if (isFromTypeUnboxed && !isToTypeUnboxed) {
boxInlineClass(fromKotlinType, v);
}
else if (!isFromTypeUnboxed && isToTypeUnboxed) {
unboxInlineClass(fromType, fromKotlinType, v);
unboxInlineClass(fromType, toKotlinType, v);
}
}
else {
if (isFromTypeUnboxed) {
else if (isFromTypeInlineClass) {
if (isUnboxedInlineClass(fromKotlinType, fromType)) {
boxInlineClass(fromKotlinType, v);
}
}
else { // isToTypeInlineClass is `true`
if (isUnboxedInlineClass(toKotlinType, toType)) {
unboxInlineClass(fromType, toKotlinType, v);
}
}
return true;
}
@@ -1549,14 +1558,14 @@ public abstract class StackValue {
private final ExpressionCodegen generator;
public Expression(Type type, KtExpression expression, ExpressionCodegen generator) {
super(type);
super(type, generator.kotlinType(expression));
this.expression = expression;
this.generator = generator;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
generator.gen(expression, type);
generator.gen(expression, type, kotlinType);
}
}
@@ -0,0 +1,55 @@
// !LANGUAGE: +InlineClasses
inline class UInt(val u: Int) {
override fun toString(): String {
return "UInt: $u"
}
}
fun Any.isUInt(): Boolean = this is UInt
fun Any.notIsUInt(): Boolean = this !is UInt
inline fun <reified T> Any?.instanceOf(): Boolean = this is T
fun UInt.extension(): String = "OK:"
fun foo(x: UInt?): String {
if (x is UInt) {
return x.extension() + x.toString()
}
return "fail"
}
fun bar(x: UInt?): String {
if (x is Any) {
return x.extension()
}
return "fail"
}
fun box(): String {
val u = UInt(12)
if (!u.isUInt()) return "fail"
if (u.notIsUInt()) return "fail"
if (1.isUInt()) return "fail"
if (!1.notIsUInt()) return "fail"
if (!u.instanceOf<UInt>()) return "fail"
if (1.instanceOf<UInt>()) return "fail"
val nullableUInt: UInt? = UInt(10)
if (!nullableUInt.instanceOf<UInt>()) return "fail"
val nullAsUInt: UInt? = null
if (nullAsUInt.instanceOf<UInt>()) return "fail"
if (!nullAsUInt.instanceOf<UInt?>()) return "fail"
if (foo(u) != "OK:UInt: 12") return "fail"
if (bar(u) != "OK:") return "fail"
return "OK"
}
@@ -0,0 +1,25 @@
// !LANGUAGE: +InlineClasses
inline class UInt(val u: Int) {
fun member() {}
}
fun UInt?.extension() {}
fun test(a: Any, b: Any?) {
if (a is UInt) {
a.member()
}
if (b is UInt?) {
b.extension()
}
}
// 2 INSTANCEOF UInt
// 1 CHECKCAST UInt
// 1 INVOKEVIRTUAL UInt.unbox
// 2 INVOKESTATIC UInt\$Erased.member
// 0 intValue
@@ -10449,6 +10449,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("checkForInstanceOfInlineClass.kt")
public void testCheckForInstanceOfInlineClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/checkForInstanceOfInlineClass.kt");
doTest(fileName);
}
@TestMetadata("checkLambdaWithInlineClassesInFunctionalType.kt")
public void testCheckLambdaWithInlineClassesInFunctionalType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/checkLambdaWithInlineClassesInFunctionalType.kt");
@@ -10449,6 +10449,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("checkForInstanceOfInlineClass.kt")
public void testCheckForInstanceOfInlineClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/checkForInstanceOfInlineClass.kt");
doTest(fileName);
}
@TestMetadata("checkLambdaWithInlineClassesInFunctionalType.kt")
public void testCheckLambdaWithInlineClassesInFunctionalType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/checkLambdaWithInlineClassesInFunctionalType.kt");
@@ -1998,6 +1998,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
doTest(fileName);
}
@TestMetadata("isCheckForInlineClass.kt")
public void testIsCheckForInlineClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/isCheckForInlineClass.kt");
doTest(fileName);
}
@TestMetadata("skipCallToUnderlyingValueOfInlineClass.kt")
public void testSkipCallToUnderlyingValueOfInlineClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/skipCallToUnderlyingValueOfInlineClass.kt");
@@ -10449,6 +10449,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
@TestMetadata("checkForInstanceOfInlineClass.kt")
public void testCheckForInstanceOfInlineClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/checkForInstanceOfInlineClass.kt");
doTest(fileName);
}
@TestMetadata("checkLambdaWithInlineClassesInFunctionalType.kt")
public void testCheckLambdaWithInlineClassesInFunctionalType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/checkLambdaWithInlineClassesInFunctionalType.kt");
@@ -11433,6 +11433,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName);
}
@TestMetadata("checkForInstanceOfInlineClass.kt")
public void testCheckForInstanceOfInlineClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/checkForInstanceOfInlineClass.kt");
doTest(fileName);
}
@TestMetadata("checkLambdaWithInlineClassesInFunctionalType.kt")
public void testCheckLambdaWithInlineClassesInFunctionalType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/checkLambdaWithInlineClassesInFunctionalType.kt");