KT-31908 Handle SAM conversion on vararg elements

This commit is contained in:
Dmitry Petrov
2020-06-02 15:54:50 +03:00
parent 343af60cb4
commit 16f175612e
11 changed files with 152 additions and 31 deletions
@@ -32,17 +32,31 @@ import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
public class SamType {
@Nullable
public static SamType createByValueParameter(@NotNull ValueParameterDescriptor valueParameter) {
KotlinType singleArgumentType;
KotlinType originalSingleArgumentType;
KotlinType varargElementType = valueParameter.getVarargElementType();
if (varargElementType != null) {
singleArgumentType = varargElementType;
originalSingleArgumentType = valueParameter.getOriginal().getVarargElementType();
assert originalSingleArgumentType != null :
"Value parameter and original value parameter have inconsistent varargs: " +
valueParameter + "; " + valueParameter.getOriginal();
} else {
singleArgumentType = valueParameter.getType();
originalSingleArgumentType = valueParameter.getOriginal().getType();
}
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())
KotlinBuiltIns.isNothing(singleArgumentType)
// 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();
? TypeUtilsKt.replaceArgumentsWithNothing(originalSingleArgumentType)
: singleArgumentType;
return create(TypeMapperUtilsKt.removeExternalProjections(originalTypeToUse));
}
@@ -42,10 +42,7 @@ import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.*;
import org.jetbrains.kotlin.resolve.calls.tower.NewResolvedCallImpl;
import org.jetbrains.kotlin.resolve.calls.tower.NewVariableAsFunctionResolvedCallImpl;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
@@ -799,12 +796,16 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
CallableDescriptor descriptor = call.getResultingDescriptor();
if (!(descriptor instanceof FunctionDescriptor)) return;
recordSamValueForNewInference(call);
recordSamValuesForNewInference(call);
recordSamConstructorIfNeeded(expression, call);
recordSamValuesForOldInference(call, descriptor);
}
private void recordSamValuesForOldInference(ResolvedCall<?> call, CallableDescriptor descriptor) {
FunctionDescriptor original = SamCodegenUtil.getOriginalIfSamAdapter((FunctionDescriptor) descriptor);
if (original == null) return;
// TODO we can just record SAM_VALUE on relevant value arguments as we do in recordSamValuesForNewInference
List<ValueParameterDescriptor> valueParametersWithSAMConversion = new SmartList<>();
for (ValueParameterDescriptor valueParameter : original.getValueParameters()) {
ValueParameterDescriptor adaptedParameter = descriptor.getValueParameters().get(valueParameter.getIndex());
@@ -814,30 +815,43 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
writeSamValueForValueParameters(valueParametersWithSAMConversion, call.getValueArgumentsByIndex());
}
private void recordSamValueForNewInference(@NotNull ResolvedCall<?> call) {
NewResolvedCallImpl<?> newResolvedCall = null;
if (call instanceof NewVariableAsFunctionResolvedCallImpl) {
newResolvedCall = ((NewVariableAsFunctionResolvedCallImpl) call).getFunctionCall();
}
else if(call instanceof NewResolvedCallImpl) {
newResolvedCall = (NewResolvedCallImpl<?>) call;
}
private void recordSamValuesForNewInference(@NotNull ResolvedCall<?> call) {
NewResolvedCallImpl<?> newResolvedCall = getNewResolvedCallForCallWithPossibleSamConversions(call);
if (newResolvedCall == null) return;
List<ValueParameterDescriptor> valueParametersWithSAMConversion = new SmartList<>();
Map<ValueParameterDescriptor, ResolvedValueArgument> arguments = newResolvedCall.getValueArguments();
for (ValueParameterDescriptor valueParameter : arguments.keySet()) {
SamType samType = SamType.createByValueParameter(valueParameter);
if (samType == null) continue;
ResolvedValueArgument argument = arguments.get(valueParameter);
if (!(argument instanceof ExpressionValueArgument)) continue;
ValueArgument valueArgument = ((ExpressionValueArgument) argument).getValueArgument();
if (valueArgument == null || newResolvedCall.getExpectedTypeForSamConvertedArgument(valueArgument) == null) continue;
valueParametersWithSAMConversion.add(valueParameter);
if (argument instanceof ExpressionValueArgument) {
ValueArgument valueArgument = ((ExpressionValueArgument) argument).getValueArgument();
if (valueArgument != null && newResolvedCall.getExpectedTypeForSamConvertedArgument(valueArgument) != null) {
recordSamTypeOnArgumentExpression(samType, valueArgument);
}
} else if (argument instanceof VarargValueArgument) {
VarargValueArgument varargValueArgument = (VarargValueArgument) argument;
for (ValueArgument valueArgument : varargValueArgument.getArguments()) {
if (valueArgument != null && newResolvedCall.getExpectedTypeForSamConvertedArgument(valueArgument) != null) {
recordSamTypeOnArgumentExpression(samType, valueArgument);
}
}
}
}
writeSamValueForValueParameters(valueParametersWithSAMConversion, newResolvedCall.getValueArgumentsByIndex());
}
@Nullable
private static NewResolvedCallImpl<?> getNewResolvedCallForCallWithPossibleSamConversions(@NotNull ResolvedCall<?> call) {
if (call instanceof NewVariableAsFunctionResolvedCallImpl) {
return ((NewVariableAsFunctionResolvedCallImpl) call).getFunctionCall();
}
else if (call instanceof NewResolvedCallImpl) {
return (NewResolvedCallImpl<?>) call;
}
else {
return null;
}
}
private void writeSamValueForValueParameters(
@@ -854,13 +868,17 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
assert resolvedValueArgument instanceof ExpressionValueArgument : resolvedValueArgument;
ValueArgument valueArgument = ((ExpressionValueArgument) resolvedValueArgument).getValueArgument();
assert valueArgument != null;
KtExpression argumentExpression = valueArgument.getArgumentExpression();
assert argumentExpression != null : valueArgument.asElement().getText();
bindingTrace.record(CodegenBinding.SAM_VALUE, argumentExpression, samType);
recordSamTypeOnArgumentExpression(samType, valueArgument);
}
}
private void recordSamTypeOnArgumentExpression(SamType samType, ValueArgument valueArgument) {
KtExpression argumentExpression = valueArgument.getArgumentExpression();
assert argumentExpression != null : valueArgument.asElement().getText();
bindingTrace.record(CodegenBinding.SAM_VALUE, argumentExpression, samType);
}
@Override
public void visitSuperTypeCallEntry(@NotNull KtSuperTypeCallEntry call) {
// Closures in super type constructor calls for anonymous objects are created in outer context
@@ -942,7 +960,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
if (!(operationDescriptor instanceof FunctionDescriptor)) return;
ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(expression, bindingContext);
if (resolvedCall != null) recordSamValueForNewInference(resolvedCall);
if (resolvedCall != null) recordSamValuesForNewInference(resolvedCall);
FunctionDescriptor original = SamCodegenUtil.getOriginalIfSamAdapter((FunctionDescriptor) operationDescriptor);
if (original == null) return;
@@ -967,7 +985,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
if (!(operationDescriptor instanceof FunctionDescriptor)) return;
ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(expression, bindingContext);
if (resolvedCall != null) recordSamValueForNewInference(resolvedCall);
if (resolvedCall != null) recordSamValuesForNewInference(resolvedCall);
boolean isSetter = operationDescriptor.getName().asString().equals("set");
FunctionDescriptor original = SamCodegenUtil.getOriginalIfSamAdapter((FunctionDescriptor) operationDescriptor);
@@ -11205,6 +11205,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/funInterface/castFromAny.kt");
}
@TestMetadata("funConversionInVararg.kt")
public void testFunConversionInVararg() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funConversionInVararg.kt");
}
@TestMetadata("funInterfaceInheritance.kt")
public void testFunInterfaceInheritance() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funInterfaceInheritance.kt");
@@ -28143,6 +28148,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/sam/kt24825.kt");
}
@TestMetadata("kt31908.kt")
public void testKt31908() throws Exception {
runTest("compiler/testData/codegen/box/sam/kt31908.kt");
}
@TestMetadata("nonInlinedSamWrapper.kt")
public void testNonInlinedSamWrapper() throws Exception {
runTest("compiler/testData/codegen/box/sam/nonInlinedSamWrapper.kt");
@@ -0,0 +1,17 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument
fun interface MyRunnable {
fun run()
}
fun box(): String {
var result = "failed"
val r = MyRunnable { result += "K" }
foo({ result = "O" }, r)
return result
}
fun foo(vararg rs: MyRunnable) {
for (r in rs) {
r.run()
}
}
+17
View File
@@ -0,0 +1,17 @@
// TARGET_BACKEND: JVM
// FILE: kt31908.kt
fun box(): String {
var result = "failed"
val r = java.lang.Runnable { result += "K" }
J().foo({ result = "O" }, r)
return result
}
// FILE: J.java
public class J {
public void foo(Runnable... rs) {
for (Runnable r : rs) {
r.run();
}
}
}
@@ -12420,6 +12420,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/funInterface/castFromAny.kt");
}
@TestMetadata("funConversionInVararg.kt")
public void testFunConversionInVararg() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funConversionInVararg.kt");
}
@TestMetadata("funInterfaceInheritance.kt")
public void testFunInterfaceInheritance() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funInterfaceInheritance.kt");
@@ -29729,6 +29734,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/sam/kt24825.kt");
}
@TestMetadata("kt31908.kt")
public void testKt31908() throws Exception {
runTest("compiler/testData/codegen/box/sam/kt31908.kt");
}
@TestMetadata("nonInlinedSamWrapper.kt")
public void testNonInlinedSamWrapper() throws Exception {
runTest("compiler/testData/codegen/box/sam/nonInlinedSamWrapper.kt");
@@ -12425,6 +12425,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/funInterface/castFromAny.kt");
}
@TestMetadata("funConversionInVararg.kt")
public void testFunConversionInVararg() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funConversionInVararg.kt");
}
@TestMetadata("funInterfaceWithReceiver.kt")
public void testFunInterfaceWithReceiver() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funInterfaceWithReceiver.kt");
@@ -27363,6 +27368,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/sam/kt24825.kt");
}
@TestMetadata("kt31908.kt")
public void testKt31908() throws Exception {
runTest("compiler/testData/codegen/box/sam/kt31908.kt");
}
@TestMetadata("nonInlinedSamWrapper.kt")
public void testNonInlinedSamWrapper() throws Exception {
runTest("compiler/testData/codegen/box/sam/nonInlinedSamWrapper.kt");
@@ -11205,6 +11205,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/funInterface/castFromAny.kt");
}
@TestMetadata("funConversionInVararg.kt")
public void testFunConversionInVararg() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funConversionInVararg.kt");
}
@TestMetadata("funInterfaceInheritance.kt")
public void testFunInterfaceInheritance() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funInterfaceInheritance.kt");
@@ -28143,6 +28148,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/sam/kt24825.kt");
}
@TestMetadata("kt31908.kt")
public void testKt31908() throws Exception {
runTest("compiler/testData/codegen/box/sam/kt31908.kt");
}
@TestMetadata("nonInlinedSamWrapper.kt")
public void testNonInlinedSamWrapper() throws Exception {
runTest("compiler/testData/codegen/box/sam/nonInlinedSamWrapper.kt");
@@ -9605,6 +9605,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
runTest("compiler/testData/codegen/box/funInterface/castFromAny.kt");
}
@TestMetadata("funConversionInVararg.kt")
public void testFunConversionInVararg() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funConversionInVararg.kt");
}
@TestMetadata("funInterfaceInheritance.kt")
public void testFunInterfaceInheritance() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funInterfaceInheritance.kt");
@@ -9615,6 +9615,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/funInterface/castFromAny.kt");
}
@TestMetadata("funConversionInVararg.kt")
public void testFunConversionInVararg() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funConversionInVararg.kt");
}
@TestMetadata("funInterfaceInheritance.kt")
public void testFunInterfaceInheritance() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funInterfaceInheritance.kt");
@@ -9615,6 +9615,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/funInterface/castFromAny.kt");
}
@TestMetadata("funConversionInVararg.kt")
public void testFunConversionInVararg() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funConversionInVararg.kt");
}
@TestMetadata("funInterfaceInheritance.kt")
public void testFunInterfaceInheritance() throws Exception {
runTest("compiler/testData/codegen/box/funInterface/funInterfaceInheritance.kt");