Fix runtime CCE in case of out-projected SAM
Probably, when NI is there this fix will become unnecessary because there will be no approximation applied, thus the value parameter will remain Hello<#Captured> instead of Nothing #KT-17171 Fixed
This commit is contained in:
@@ -17,13 +17,34 @@
|
|||||||
package org.jetbrains.kotlin.codegen;
|
package org.jetbrains.kotlin.codegen;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||||
|
import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
|
||||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
|
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor;
|
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor;
|
||||||
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
|
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
|
||||||
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils;
|
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils;
|
||||||
import org.jetbrains.kotlin.types.KotlinType;
|
import org.jetbrains.kotlin.types.KotlinType;
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
|
||||||
|
|
||||||
public class SamType {
|
public class SamType {
|
||||||
|
@Nullable
|
||||||
|
public static SamType createByValueParameter(@NotNull ValueParameterDescriptor valueParameter) {
|
||||||
|
KotlinType originalTypeToUse =
|
||||||
|
// This can be true in case when the value parameter is in the method of a generic type with out-projection.
|
||||||
|
// We approximate Inv<Captured#1> to Nothing, while Inv itself can be a SAM interface safe to call here
|
||||||
|
// (see testData genericSamProjectedOut.kt for details)
|
||||||
|
KotlinBuiltIns.isNothing(valueParameter.getType())
|
||||||
|
// In such a case we can't have a proper supertype since wildcards are not allowed there,
|
||||||
|
// so we use Nothing arguments instead that leads to a raw type used for a SAM wrapper.
|
||||||
|
// See org.jetbrains.kotlin.codegen.state.KotlinTypeMapper#writeGenericType to understand how
|
||||||
|
// raw types and Nothing arguments relate.
|
||||||
|
? TypeUtilsKt.replaceArgumentsWithNothing(valueParameter.getOriginal().getType())
|
||||||
|
: valueParameter.getType();
|
||||||
|
|
||||||
|
return create(TypeMapperUtilsKt.removeExternalProjections(originalTypeToUse));
|
||||||
|
}
|
||||||
public static SamType create(@NotNull KotlinType originalType) {
|
public static SamType create(@NotNull KotlinType originalType) {
|
||||||
if (!SingleAbstractMethodUtils.isSamType(originalType)) return null;
|
if (!SingleAbstractMethodUtils.isSamType(originalType)) return null;
|
||||||
return new SamType(originalType);
|
return new SamType(originalType);
|
||||||
|
|||||||
+3
-4
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.cfg.WhenChecker;
|
|||||||
import org.jetbrains.kotlin.codegen.*;
|
import org.jetbrains.kotlin.codegen.*;
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
|
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||||
import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
|
|
||||||
import org.jetbrains.kotlin.codegen.when.SwitchCodegenProvider;
|
import org.jetbrains.kotlin.codegen.when.SwitchCodegenProvider;
|
||||||
import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
|
import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
||||||
@@ -608,7 +607,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
|||||||
ValueParameterDescriptor adaptedParameter = descriptor.getValueParameters().get(valueParameter.getIndex());
|
ValueParameterDescriptor adaptedParameter = descriptor.getValueParameters().get(valueParameter.getIndex());
|
||||||
if (KotlinTypeChecker.DEFAULT.equalTypes(adaptedParameter.getType(), valueParameter.getType())) continue;
|
if (KotlinTypeChecker.DEFAULT.equalTypes(adaptedParameter.getType(), valueParameter.getType())) continue;
|
||||||
|
|
||||||
SamType samType = SamType.create(TypeMapperUtilsKt.removeExternalProjections(valueParameter.getType()));
|
SamType samType = SamType.createByValueParameter(valueParameter);
|
||||||
if (samType == null) continue;
|
if (samType == null) continue;
|
||||||
|
|
||||||
ResolvedValueArgument resolvedValueArgument = valueArguments.get(valueParameter.getIndex());
|
ResolvedValueArgument resolvedValueArgument = valueArguments.get(valueParameter.getIndex());
|
||||||
@@ -692,7 +691,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
|||||||
FunctionDescriptor original = SamCodegenUtil.getOriginalIfSamAdapter((FunctionDescriptor) operationDescriptor);
|
FunctionDescriptor original = SamCodegenUtil.getOriginalIfSamAdapter((FunctionDescriptor) operationDescriptor);
|
||||||
if (original == null) return;
|
if (original == null) return;
|
||||||
|
|
||||||
SamType samType = SamType.create(original.getValueParameters().get(0).getType());
|
SamType samType = SamType.createByValueParameter(original.getValueParameters().get(0));
|
||||||
if (samType == null) return;
|
if (samType == null) return;
|
||||||
|
|
||||||
IElementType token = expression.getOperationToken();
|
IElementType token = expression.getOperationToken();
|
||||||
@@ -718,7 +717,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
|||||||
List<KtExpression> indexExpressions = expression.getIndexExpressions();
|
List<KtExpression> indexExpressions = expression.getIndexExpressions();
|
||||||
List<ValueParameterDescriptor> parameters = original.getValueParameters();
|
List<ValueParameterDescriptor> parameters = original.getValueParameters();
|
||||||
for (ValueParameterDescriptor valueParameter : parameters) {
|
for (ValueParameterDescriptor valueParameter : parameters) {
|
||||||
SamType samType = SamType.create(valueParameter.getType());
|
SamType samType = SamType.createByValueParameter(valueParameter);
|
||||||
if (samType == null) continue;
|
if (samType == null) continue;
|
||||||
|
|
||||||
if (isSetter && valueParameter.getIndex() == parameters.size() - 1) {
|
if (isSetter && valueParameter.getIndex() == parameters.size() - 1) {
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// FILE: example/Hello.java
|
||||||
|
package example;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Hello<A> {
|
||||||
|
void invoke(A a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: example/SomeJavaClass.java
|
||||||
|
package example;
|
||||||
|
|
||||||
|
public class SomeJavaClass<A> {
|
||||||
|
public void someFunction(Hello<A> hello) {
|
||||||
|
((Hello)hello).invoke("OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void plus(Hello<A> hello) {
|
||||||
|
((Hello)hello).invoke("OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void get(Hello<A> hello) {
|
||||||
|
((Hello)hello).invoke("OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
import example.SomeJavaClass
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a: SomeJavaClass<out String> = SomeJavaClass()
|
||||||
|
|
||||||
|
var result = "fail"
|
||||||
|
|
||||||
|
// a::someFunction parameter has type of Nothing
|
||||||
|
// while it's completely safe to pass a lambda for a SAM
|
||||||
|
// since Hello is effectively contravariant by its parameter
|
||||||
|
a.someFunction {
|
||||||
|
result = it
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != "OK") return "fail 1: $result"
|
||||||
|
result = "fail"
|
||||||
|
|
||||||
|
a + {
|
||||||
|
result = it
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != "OK") return "fail 2: $result"
|
||||||
|
result = "fail"
|
||||||
|
|
||||||
|
a[{
|
||||||
|
result = it
|
||||||
|
}]
|
||||||
|
|
||||||
|
if (result != "OK") return "fail 3: $result"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Generated
+6
@@ -10995,6 +10995,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/javaInterop"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/javaInterop"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("genericSamProjectedOut.kt")
|
||||||
|
public void testGenericSamProjectedOut() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/javaInterop/genericSamProjectedOut.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("lambdaInstanceOf.kt")
|
@TestMetadata("lambdaInstanceOf.kt")
|
||||||
public void testLambdaInstanceOf() throws Exception {
|
public void testLambdaInstanceOf() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/javaInterop/lambdaInstanceOf.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/javaInterop/lambdaInstanceOf.kt");
|
||||||
|
|||||||
+6
@@ -10995,6 +10995,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/javaInterop"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/javaInterop"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("genericSamProjectedOut.kt")
|
||||||
|
public void testGenericSamProjectedOut() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/javaInterop/genericSamProjectedOut.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("lambdaInstanceOf.kt")
|
@TestMetadata("lambdaInstanceOf.kt")
|
||||||
public void testLambdaInstanceOf() throws Exception {
|
public void testLambdaInstanceOf() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/javaInterop/lambdaInstanceOf.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/javaInterop/lambdaInstanceOf.kt");
|
||||||
|
|||||||
+6
@@ -10995,6 +10995,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/javaInterop"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/javaInterop"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("genericSamProjectedOut.kt")
|
||||||
|
public void testGenericSamProjectedOut() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/javaInterop/genericSamProjectedOut.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("lambdaInstanceOf.kt")
|
@TestMetadata("lambdaInstanceOf.kt")
|
||||||
public void testLambdaInstanceOf() throws Exception {
|
public void testLambdaInstanceOf() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/javaInterop/lambdaInstanceOf.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/javaInterop/lambdaInstanceOf.kt");
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.descriptors.*
|
|||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.isCaptured
|
import org.jetbrains.kotlin.resolve.calls.inference.isCaptured
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import org.jetbrains.kotlin.types.checker.NewCapturedType
|
import org.jetbrains.kotlin.types.checker.NewCapturedType
|
||||||
@@ -170,21 +171,24 @@ fun KotlinType.getImmediateSuperclassNotAny(): KotlinType? {
|
|||||||
fun KotlinType.asTypeProjection(): TypeProjection = TypeProjectionImpl(this)
|
fun KotlinType.asTypeProjection(): TypeProjection = TypeProjectionImpl(this)
|
||||||
fun KotlinType.contains(predicate: (UnwrappedType) -> Boolean) = TypeUtils.contains(this, predicate)
|
fun KotlinType.contains(predicate: (UnwrappedType) -> Boolean) = TypeUtils.contains(this, predicate)
|
||||||
|
|
||||||
fun KotlinType.replaceArgumentsWithStarProjections(): KotlinType {
|
fun KotlinType.replaceArgumentsWithStarProjections() = replaceArgumentsWith(::StarProjectionImpl)
|
||||||
|
fun KotlinType.replaceArgumentsWithNothing() = replaceArgumentsWith { it.builtIns.nothingType.asTypeProjection() }
|
||||||
|
|
||||||
|
private inline fun KotlinType.replaceArgumentsWith(replacement: (TypeParameterDescriptor) -> TypeProjection): KotlinType {
|
||||||
val unwrapped = unwrap()
|
val unwrapped = unwrap()
|
||||||
return when (unwrapped) {
|
return when (unwrapped) {
|
||||||
is FlexibleType -> KotlinTypeFactory.flexibleType(
|
is FlexibleType -> KotlinTypeFactory.flexibleType(
|
||||||
unwrapped.lowerBound.replaceArgumentsWithStarProjections(),
|
unwrapped.lowerBound.replaceArgumentsWith(replacement),
|
||||||
unwrapped.upperBound.replaceArgumentsWithStarProjections()
|
unwrapped.upperBound.replaceArgumentsWith(replacement)
|
||||||
)
|
)
|
||||||
is SimpleType -> unwrapped.replaceArgumentsWithStarProjections()
|
is SimpleType -> unwrapped.replaceArgumentsWith(replacement)
|
||||||
}.inheritEnhancement(unwrapped)
|
}.inheritEnhancement(unwrapped)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SimpleType.replaceArgumentsWithStarProjections(): SimpleType {
|
private inline fun SimpleType.replaceArgumentsWith(replacement: (TypeParameterDescriptor) -> TypeProjection): SimpleType {
|
||||||
if (constructor.parameters.isEmpty() || constructor.declarationDescriptor == null) return this
|
if (constructor.parameters.isEmpty() || constructor.declarationDescriptor == null) return this
|
||||||
|
|
||||||
val newArguments = constructor.parameters.map(::StarProjectionImpl)
|
val newArguments = constructor.parameters.map(replacement)
|
||||||
|
|
||||||
return replace(newArguments)
|
return replace(newArguments)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user