KT-30419 Box inline classes in return types of covariant overrides

Use boxed version of an inline class in return type position for
covariant and generic-specialized overrides.

Also fixes KT-35234 and KT-31585.
This commit is contained in:
Dmitry Petrov
2020-03-30 12:41:48 +03:00
parent e44f12d7b0
commit 24b8495e00
31 changed files with 1296 additions and 68 deletions
@@ -68,6 +68,7 @@ import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import java.util.stream.Collectors;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableAny;
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
@@ -1021,7 +1022,7 @@ public class FunctionCodegen {
boolean isSpecial = SpecialBuiltinMembers.getOverriddenBuiltinReflectingJvmDescriptor(descriptor) != null;
Set<Bridge<Method>> bridgesToGenerate;
Set<Bridge<Method, DescriptorBasedFunctionHandleForJvm>> bridgesToGenerate;
if (!isSpecial) {
bridgesToGenerate =
JvmBridgesImplKt.generateBridgesForFunctionDescriptorForJvm(descriptor, typeMapper::mapAsmMethod, state);
@@ -1030,22 +1031,23 @@ public class FunctionCodegen {
boolean isSpecialBridge =
BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(descriptor) != null;
for (Bridge<Method> bridge : bridgesToGenerate) {
generateBridge(origin, descriptor, bridge.getFrom(), bridge.getTo(), isSpecialBridge, false);
for (Bridge<Method, DescriptorBasedFunctionHandleForJvm> bridge : bridgesToGenerate) {
generateBridge(origin, descriptor, bridge.getFrom(), bridge.getTo(), getBridgeReturnType(bridge), isSpecialBridge, false);
}
}
}
else {
Set<BridgeForBuiltinSpecial<Method>> specials = BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial(
descriptor, typeMapper::mapAsmMethod, state
);
Set<BridgeForBuiltinSpecial<Method>> specials =
BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial(descriptor, typeMapper::mapAsmMethod, state);
if (!specials.isEmpty()) {
PsiElement origin = descriptor.getKind() == DECLARATION ? getSourceFromDescriptor(descriptor) : null;
for (BridgeForBuiltinSpecial<Method> bridge : specials) {
generateBridge(
origin, descriptor, bridge.getFrom(), bridge.getTo(),
bridge.isSpecial(), bridge.isDelegateToSuper());
descriptor.getReturnType(), // TODO
bridge.isSpecial(), bridge.isDelegateToSuper()
);
}
}
@@ -1062,6 +1064,35 @@ public class FunctionCodegen {
}
}
private KotlinType getBridgeReturnType(Bridge<Method, DescriptorBasedFunctionHandleForJvm> bridge) {
// Return type for the bridge affects inline class values boxing/unboxing in bridge.
// Here we take 1st available return type for the bridge.
// In correct cases it doesn't matter what particular return type to use,
// since either all return types are inline class itself,
// or all return types are supertypes of inline class (and can't be inline classes).
for (DescriptorBasedFunctionHandleForJvm handle : bridge.getOriginalFunctions()) {
KotlinType returnType = handle.getDescriptor().getReturnType();
if (returnType != null) {
return returnType;
}
}
if (state.getClassBuilderMode().mightBeIncorrectCode) {
// Don't care, 'Any?' would suffice.
return state.getModule().getBuiltIns().getNullableAnyType();
} else if (bridge.getOriginalFunctions().isEmpty()) {
throw new AssertionError("No overridden functions for the bridge method '" + bridge.getTo() + "'");
} else {
throw new AssertionError(
"No return type for the bridge method '" + bridge.getTo() + "' found in the following overridden functions:\n" +
bridge.getOriginalFunctions().stream()
.map(handle -> handle.getDescriptor().toString())
.collect(Collectors.joining("\n"))
);
}
}
public static boolean isThereOverriddenInKotlinClass(@NotNull CallableMemberDescriptor descriptor) {
return CollectionsKt.any(
getAllOverriddenDescriptors(descriptor),
@@ -1376,6 +1407,7 @@ public class FunctionCodegen {
@NotNull FunctionDescriptor descriptor,
@NotNull Method bridge,
@NotNull Method delegateTo,
@NotNull KotlinType bridgeReturnType,
boolean isSpecialBridge,
boolean isStubDeclarationWithDelegationToSuper
) {
@@ -1452,7 +1484,7 @@ public class FunctionCodegen {
}
KotlinType returnType = descriptor.getReturnType();
StackValue.coerce(delegateTo.getReturnType(), returnType, bridge.getReturnType(), returnType, iv);
StackValue.coerce(delegateTo.getReturnType(), returnType, bridge.getReturnType(), bridgeReturnType, iv);
iv.areturn(bridge.getReturnType());
endVisit(mv, "bridge method", origin);
@@ -200,7 +200,7 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
}
InstructionAdapter v = codegen.v;
result.put(returnType, v);
result.put(returnType, functionDescriptor.getReturnType(), v);
v.areturn(returnType);
}
@@ -88,6 +88,6 @@ fun <Signature> generateBridgesForFunctionDescriptorForJvm(
descriptor: FunctionDescriptor,
signature: (FunctionDescriptor) -> Signature,
state: GenerationState
): Set<Bridge<Signature>> {
): Set<Bridge<Signature, DescriptorBasedFunctionHandleForJvm>> {
return generateBridges(DescriptorBasedFunctionHandleForJvm(descriptor, state)) { signature(it.descriptor) }
}
@@ -936,10 +936,13 @@ class KotlinTypeMapper @JvmOverloads constructor(
private fun forceBoxedReturnType(descriptor: FunctionDescriptor): Boolean {
if (isBoxMethodForInlineClass(descriptor)) return true
return isJvmPrimitive(descriptor.returnType!!) &&
getAllOverriddenDescriptors(descriptor).any { !isJvmPrimitive(it.returnType!!) }
return isJvmPrimitiveOrInlineClass(descriptor.returnType!!) &&
getAllOverriddenDescriptors(descriptor).any { !isJvmPrimitiveOrInlineClass(it.returnType!!) }
}
private fun isJvmPrimitiveOrInlineClass(kotlinType: KotlinType) =
KotlinBuiltIns.isPrimitiveType(kotlinType) || kotlinType.isInlineClassType()
private fun isBoxMethodForInlineClass(descriptor: FunctionDescriptor): Boolean {
val containingDeclaration = descriptor.containingDeclaration
return containingDeclaration.isInlineClass() &&
@@ -947,12 +950,6 @@ class KotlinTypeMapper @JvmOverloads constructor(
descriptor.name == InlineClassDescriptorResolver.BOX_METHOD_NAME
}
private fun isJvmPrimitive(kotlinType: KotlinType): Boolean {
if (KotlinBuiltIns.isPrimitiveType(kotlinType)) return true
return kotlinType.isInlineClassType() && !kotlinType.isError && AsmUtil.isPrimitive(mapInlineClassType(kotlinType))
}
fun mapFieldSignature(backingFieldType: KotlinType, propertyDescriptor: PropertyDescriptor): String? {
val sw = BothSignatureWriter(BothSignatureWriter.Mode.TYPE)