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:
@@ -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);
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user