Fix == for inline classes with boxes

TODO generalize code generating object vs primitive equality

 #KT-25914 Fixed
 #KT-25981 Fixed
 #KT-25983 Fixed
This commit is contained in:
Dmitry Petrov
2018-08-09 18:30:09 +03:00
parent cd12772bc5
commit 4f6aa50417
10 changed files with 225 additions and 29 deletions
@@ -3210,9 +3210,32 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@Nullable StackValue pregeneratedSubject,
@Nullable PrimitiveNumericComparisonInfo primitiveNumericComparisonInfo
) {
if (left == null || right == null) {
return StackValue.operation(Type.VOID_TYPE, v -> {
v.aconst(null);
v.athrow();
return Unit.INSTANCE;
});
}
KotlinType leftKotlinType = kotlinType(left);
KotlinType rightKotlinType = kotlinType(right);
boolean leftIsInlineClass = leftKotlinType != null && InlineClassesUtilsKt.isInlineClassType(leftKotlinType);
boolean rightIsInlineClass = rightKotlinType != null && InlineClassesUtilsKt.isInlineClassType(rightKotlinType);
Type leftType = pregeneratedSubject != null ? pregeneratedSubject.type : expressionType(left);
Type rightType = expressionType(right);
boolean leftIsInlineClassWithPrimitiveEquality = leftIsInlineClass && isInlineClassTypeWithPrimitiveEquality(leftKotlinType);
boolean rightIsInlineClassWithPrimitiveEquality = rightIsInlineClass && isInlineClassTypeWithPrimitiveEquality(rightKotlinType);
boolean leftHasPrimitiveEquality = isPrimitive(leftType) && (!leftIsInlineClass || leftIsInlineClassWithPrimitiveEquality);
boolean rightHasPrimitiveEquality = isPrimitive(rightType) && (!rightIsInlineClass || rightIsInlineClassWithPrimitiveEquality);
if (leftIsInlineClass && !leftIsInlineClassWithPrimitiveEquality || rightIsInlineClass && !rightIsInlineClassWithPrimitiveEquality) {
return generateEqualityWithInlineClassesBoxing(left, leftType, right, rightType, opToken, pregeneratedSubject);
}
if (KtPsiUtil.isNullConstant(left)) {
return genCmpWithNull(right, opToken, null);
}
@@ -3221,51 +3244,55 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return genCmpWithNull(left, opToken, pregeneratedSubject);
}
if (isIntZero(left, leftType) && isIntPrimitive(rightType)) {
if (isIntZero(left, leftType) && rightHasPrimitiveEquality && isIntPrimitive(rightType)) {
return genCmpWithZero(right, opToken, null);
}
if (isIntZero(right, rightType) && isIntPrimitive(leftType)) {
if (isIntZero(right, rightType) && leftHasPrimitiveEquality && isIntPrimitive(leftType)) {
return genCmpWithZero(left, opToken, pregeneratedSubject);
}
if (pregeneratedSubject == null && left instanceof KtSafeQualifiedExpression &&
isSelectorPureNonNullType((KtSafeQualifiedExpression) left) && isPrimitive(rightType)) {
isSelectorPureNonNullType((KtSafeQualifiedExpression) left) &&
isPrimitive(rightType) && rightHasPrimitiveEquality) {
return genCmpSafeCallToPrimitive((KtSafeQualifiedExpression) left, right, rightType, opToken);
}
if (isPrimitive(leftType) && right instanceof KtSafeQualifiedExpression &&
if (isPrimitive(leftType) && leftHasPrimitiveEquality &&
right instanceof KtSafeQualifiedExpression &&
isSelectorPureNonNullType(((KtSafeQualifiedExpression) right))) {
return genCmpPrimitiveToSafeCall(left, leftType, (KtSafeQualifiedExpression) right, opToken, pregeneratedSubject);
}
if (BoxedToPrimitiveEquality.isApplicable(opToken, leftType, rightType)) {
return BoxedToPrimitiveEquality.create(
opToken,
genLazyUnlessProvided(pregeneratedSubject, left, leftType), leftType,
genLazy(right, rightType), rightType,
myFrameMap
);
}
if (!leftIsInlineClass && !rightIsInlineClass) {
if (BoxedToPrimitiveEquality.isApplicable(opToken, leftType, rightType)) {
return BoxedToPrimitiveEquality.create(
opToken,
genLazyUnlessProvided(pregeneratedSubject, left, leftType), leftType,
genLazy(right, rightType), rightType,
myFrameMap
);
}
if (PrimitiveToBoxedEquality.isApplicable(opToken, leftType, rightType)) {
return PrimitiveToBoxedEquality.create(
opToken,
genLazyUnlessProvided(pregeneratedSubject, left, leftType), leftType,
genLazy(right, rightType), rightType
);
}
if (PrimitiveToBoxedEquality.isApplicable(opToken, leftType, rightType)) {
return PrimitiveToBoxedEquality.create(
opToken,
genLazyUnlessProvided(pregeneratedSubject, left, leftType), leftType,
genLazy(right, rightType), rightType
);
}
if (PrimitiveToObjectEquality.isApplicable(opToken, leftType, rightType)) {
return PrimitiveToObjectEquality.create(
opToken,
genLazyUnlessProvided(pregeneratedSubject, left, leftType), leftType,
genLazy(right, rightType), rightType
);
if (PrimitiveToObjectEquality.isApplicable(opToken, leftType, rightType)) {
return PrimitiveToObjectEquality.create(
opToken,
genLazyUnlessProvided(pregeneratedSubject, left, leftType), leftType,
genLazy(right, rightType), rightType
);
}
}
if (isPrimitive(leftType) != isPrimitive(rightType)) {
if (!leftIsInlineClass && !rightIsInlineClass && isPrimitive(leftType) != isPrimitive(rightType)) {
leftType = boxType(leftType);
rightType = boxType(rightType);
}
@@ -3297,6 +3324,38 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
);
}
private StackValue generateEqualityWithInlineClassesBoxing(
@NotNull KtExpression left,
@NotNull Type leftType,
@NotNull KtExpression right,
@NotNull Type rightType,
@NotNull IElementType opToken,
@Nullable StackValue pregeneratedSubject
) {
KotlinType leftKotlinType = kotlinType(left);
KotlinType rightKotlinType = kotlinType(right);
StackValue leftValue = genLazyUnlessProvided(pregeneratedSubject, left, leftType);
StackValue rightValue = genLazy(right, rightType);
return StackValue.operation(Type.BOOLEAN_TYPE, v -> {
KotlinType nullableAnyType = state.getModule().getBuiltIns().getNullableAnyType();
leftValue.put(leftType, leftKotlinType, v);
StackValue.coerce(leftType, leftKotlinType, AsmTypes.OBJECT_TYPE, nullableAnyType, v);
rightValue.put(rightType, rightKotlinType, v);
StackValue.coerce(rightType, rightKotlinType, AsmTypes.OBJECT_TYPE, nullableAnyType, v);
genAreEqualCall(v);
if (opToken == KtTokens.EXCLEQ || opToken == KtTokens.EXCLEQEQEQ) {
genInvertBoolean(v);
}
return Unit.INSTANCE;
});
}
private boolean isEnumExpression(@Nullable KtExpression expression) {
KotlinType expressionType = bindingContext.getType(expression);
if (expressionType == null) return false;
@@ -3362,8 +3421,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
/*tries to use IEEE 754 arithmetic*/
private StackValue genEqualsForExpressionsPreferIeee754Arithmetic(
@Nullable KtExpression left,
@Nullable KtExpression right,
@NotNull KtExpression left,
@NotNull KtExpression right,
@NotNull IElementType opToken,
@NotNull Type leftType,
@NotNull Type rightType,
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.codegen
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.UnsignedTypes
import org.jetbrains.kotlin.codegen.context.CodegenContext
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
import org.jetbrains.kotlin.codegen.context.PackageContext
@@ -36,6 +37,7 @@ import org.jetbrains.kotlin.resolve.annotations.hasJvmStaticAnnotation
import org.jetbrains.kotlin.resolve.calls.callUtil.getFirstArgumentExpression
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.isInlineClassType
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
@@ -322,7 +324,7 @@ fun initializeVariablesForDestructuredLambdaParameters(codegen: ExpressionCodege
val destructuringDeclaration =
(DescriptorToSourceUtils.descriptorToDeclaration(parameterDescriptor) as? KtParameter)?.destructuringDeclaration
?: error("Destructuring declaration for descriptor $parameterDescriptor not found")
?: error("Destructuring declaration for descriptor $parameterDescriptor not found")
codegen.initializeDestructuringDeclarationVariables(
destructuringDeclaration,
@@ -418,4 +420,14 @@ fun MethodNode.textifyMethodNode(): String {
val sw = StringWriter()
text.print(PrintWriter(sw))
return "$sw"
}
fun KotlinType.isInlineClassTypeWithPrimitiveEquality(): Boolean {
if (!isInlineClassType()) return false
// Always treat unsigned types as inline classes with primitive equality
if (UnsignedTypes.isUnsignedType(this)) return true
// TODO support other inline classes that can be compared as underlying primitives
return false
}