From f80a71517f1e58ae8335b9219180f56758185816 Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Tue, 19 Nov 2019 19:14:45 +0300 Subject: [PATCH] [NI] Handle vararg parameter in reflection type wrt array types Vararg parameter in reflection type is interpreted as covariant array type against array in expected functional type and as vararg element type otherwise. For instance having function fun foo(vararg args: Int): Unit { /*...*/ } reference ::foo can be passed against expected (Int) -> Unit, (Int, Int) -> Unit, etc. In none of such cases type for parameter in foo's reflection type should be changed to array. However, against expected type (IntArray) -> Unit args' type must become IntArray. ^KT-25514 Fixed --- .../fir/FirDiagnosticsSmokeTestGenerated.java | 5 ++ .../components/CallableReferenceResolution.kt | 67 +++++++++++++++++-- .../varargViewedAsPrimitiveArray.kt | 28 ++++++++ .../callableReferenceOnExpectFun.kt | 3 +- .../tests/varargs/varargViewedAsArray.kt | 29 ++++++++ .../tests/varargs/varargViewedAsArray.txt | 14 ++++ .../checkers/DiagnosticsTestGenerated.java | 5 ++ .../DiagnosticsUsingJavacTestGenerated.java | 5 ++ .../codegen/BlackBoxCodegenTestGenerated.java | 5 ++ .../LightAnalysisModeTestGenerated.java | 5 ++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 ++ .../IrJsCodegenBoxTestGenerated.java | 5 ++ .../semantics/JsCodegenBoxTestGenerated.java | 5 ++ 13 files changed, 173 insertions(+), 8 deletions(-) create mode 100644 compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsPrimitiveArray.kt create mode 100644 compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.kt create mode 100644 compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.txt diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java index fe3b9bffc04..8f6e9ade851 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java @@ -23463,6 +23463,11 @@ public class FirDiagnosticsSmokeTestGenerated extends AbstractFirDiagnosticsSmok runTest("compiler/testData/diagnostics/tests/varargs/varargOfNothing.kt"); } + @TestMetadata("varargViewedAsArray.kt") + public void testVarargViewedAsArray() throws Exception { + runTest("compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.kt"); + } + @TestMetadata("varargsAndFunctionLiterals.kt") public void testVarargsAndFunctionLiterals() throws Exception { runTest("compiler/testData/diagnostics/tests/varargs/varargsAndFunctionLiterals.kt"); diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt index 6c6055669aa..867413af213 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastI import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.checker.captureFromExpression import org.jetbrains.kotlin.types.expressions.CoercionStrategy import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes @@ -178,7 +179,8 @@ class CallableReferencesCandidateFactory( candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver, - expectedType + expectedType, + callComponents.builtIns ) if (defaults != 0 && @@ -224,10 +226,15 @@ class CallableReferencesCandidateFactory( ) } + private enum class VarargMappingState { + UNMAPPED, MAPPED_WITH_PLAIN_ARGS, MAPPED_WITH_ARRAY + } + private fun getArgumentAndReturnTypeUseMappingByExpectedType( descriptor: FunctionDescriptor, expectedType: UnwrappedType?, - unboundReceiverCount: Int + unboundReceiverCount: Int, + builtins: KotlinBuiltIns ): Triple, CoercionStrategy, Int>? { val inputOutputTypes = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType) ?: return null @@ -244,13 +251,29 @@ class CallableReferencesCandidateFactory( * fun foo(a: A, b: B = B(), vararg c: C) */ var defaults = 0 + var varargMappingState = VarargMappingState.UNMAPPED val mappedArguments = arrayOfNulls(fakeArguments.size) + for ((valueParameter, resolvedArgument) in argumentMapping.parameterToCallArgumentMap) { for (fakeArgument in resolvedArgument.arguments) { val index = (fakeArgument as FakeKotlinCallArgumentForCallableReference).index val substitutedParameter = descriptor.valueParameters.getOrNull(valueParameter.index) ?: continue - mappedArguments[index] = substitutedParameter.varargElementType ?: substitutedParameter.type + val mappedArgument: KotlinType? + if (substitutedParameter.isVararg) { + val (varargType, newVarargMappingState) = varargParameterTypeByExpectedParameter( + inputOutputTypes.inputTypes[index], + substitutedParameter, + varargMappingState, + builtins + ) + varargMappingState = newVarargMappingState + mappedArgument = varargType + } else { + mappedArgument = substitutedParameter.type + } + + mappedArguments[index] = mappedArgument } if (resolvedArgument == ResolvedCallArgument.DefaultArgument) defaults++ } @@ -265,11 +288,42 @@ class CallableReferencesCandidateFactory( return Triple(mappedArguments as Array, coercion, defaults) } + private fun varargParameterTypeByExpectedParameter( + expectedParameterType: KotlinType, + substitutedParameter: ValueParameterDescriptor, + varargMappingState: VarargMappingState, + builtins: KotlinBuiltIns + ): Pair { + val elementType = substitutedParameter.varargElementType + ?: error("Vararg parameter $substitutedParameter does not have vararg type") + + return when (varargMappingState) { + VarargMappingState.UNMAPPED -> { + if (KotlinBuiltIns.isArrayOrPrimitiveArray(expectedParameterType)) { + val arrayType = builtins.getPrimitiveArrayKotlinTypeByPrimitiveKotlinType(elementType) + ?: builtins.getArrayType(Variance.OUT_VARIANCE, elementType) + arrayType to VarargMappingState.MAPPED_WITH_ARRAY + } else { + elementType to VarargMappingState.MAPPED_WITH_PLAIN_ARGS + } + } + VarargMappingState.MAPPED_WITH_PLAIN_ARGS -> { + if (KotlinBuiltIns.isArrayOrPrimitiveArray(expectedParameterType)) + null to VarargMappingState.MAPPED_WITH_PLAIN_ARGS + else + elementType to VarargMappingState.MAPPED_WITH_PLAIN_ARGS + } + VarargMappingState.MAPPED_WITH_ARRAY -> + null to VarargMappingState.MAPPED_WITH_ARRAY + } + } + private fun buildReflectionType( descriptor: CallableDescriptor, dispatchReceiver: CallableReceiver?, extensionReceiver: CallableReceiver?, - expectedType: UnwrappedType? + expectedType: UnwrappedType?, + builtins: KotlinBuiltIns ): Pair { val argumentsAndReceivers = ArrayList(descriptor.valueParameters.size + 2) @@ -281,7 +335,7 @@ class CallableReferencesCandidateFactory( } val descriptorReturnType = descriptor.returnType - ?: ErrorUtils.createErrorType("Error return type for descriptor: $descriptor") + ?: ErrorUtils.createErrorType("Error return type for descriptor: $descriptor") when (descriptor) { is PropertyDescriptor -> { @@ -305,7 +359,8 @@ class CallableReferencesCandidateFactory( val defaults: Int val argumentsAndExpectedTypeCoercion = getArgumentAndReturnTypeUseMappingByExpectedType( descriptor, expectedType, - unboundReceiverCount = argumentsAndReceivers.size + unboundReceiverCount = argumentsAndReceivers.size, + builtins = builtins ) if (argumentsAndExpectedTypeCoercion == null) { diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsPrimitiveArray.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsPrimitiveArray.kt new file mode 100644 index 00000000000..8c9839b753c --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsPrimitiveArray.kt @@ -0,0 +1,28 @@ +// !LANGUAGE: +NewInference +// DONT_RUN_GENERATED_CODE: JS + +fun sum(vararg args: Int): Int { + var result = 0 + for (arg in args) + result += arg + return result +} + +fun nsum(vararg args: Number) = sum(*IntArray(args.size) { args[it].toInt() }) + +fun usePlainArgs(fn: (Int, Int) -> Int) = fn(1, 1) +fun usePrimitiveArray(fn: (IntArray) -> Int) = fn(intArrayOf(1, 1, 1)) +fun useArray(fn: (Array) -> Int) = fn(arrayOf(1, 1, 1, 1)) + +fun box(): String { + var result = usePlainArgs(::sum) + if (result != 2) + return "Fail: plain args $result != 2" + result = usePrimitiveArray(::sum) + if (result != 3) + return "Fail: primitive array $result != 3" + result = useArray(::nsum) + if (result != 4) + return "Fail: reference array $result != 4" + return "OK" +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/callableReferenceOnExpectFun.kt b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/callableReferenceOnExpectFun.kt index 0acf67cc457..0c22e4752ef 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/callableReferenceOnExpectFun.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/callableReferenceOnExpectFun.kt @@ -1,4 +1,3 @@ -// !WITH_NEW_INFERENCE // !LANGUAGE: +MultiPlatformProjects // MODULE: m1-common // FILE: common.kt @@ -10,7 +9,7 @@ expect fun foo(): String fun g(f: () -> String): String = f() fun test() { - g(::foo) + g(::foo) } // MODULE: m2-jvm(m1-common) diff --git a/compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.kt b/compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.kt new file mode 100644 index 00000000000..b9d02d090bf --- /dev/null +++ b/compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.kt @@ -0,0 +1,29 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun stringVararg(vararg args: String) {} +fun intVararg(vararg args: Int) {} +fun numberVararg(vararg args: Number) {} + +fun useStrings(fn: (String, String, String) -> Unit) = fn("foo", "bar", "boo") +fun useStringArray(fn: (Array) -> Unit) = fn(arrayOf("foo", "bar", "boo")) + +fun useIntArray(fn: (Array) -> Unit) = fn(arrayOf(1, 2, 3)) +fun usePrimitiveIntArray(fn: (IntArray) -> Unit) = fn(intArrayOf(4, 5, 6)) + +fun useMixedStringArgs1(fn: (String, Array) -> Unit) = fn("foo", arrayOf("bar", "baz")) +fun useMixedStringArgs2(fn: (Array, String) -> Unit) = fn(arrayOf("foo", "bar"), "baz") +fun useMixedStringArgs3(fn: (String, Array, String) -> Unit) = fn("foo", arrayOf("bar", "baz"), "boo") +fun useTwoStringArrays(fn: (Array, Array) -> Unit) = fn(arrayOf("foo", "bar"), arrayOf("baz", "boo")) + +fun test() { + useStrings(::stringVararg) + useStringArray(::stringVararg) + useIntArray(::numberVararg) + usePrimitiveIntArray(::intVararg) + useIntArray() -> Unit", "KFunction1")!>::intVararg) + useMixedStringArgs1() -> Unit", "KFunction1, Unit>")!>::stringVararg) + useMixedStringArgs2(, String) -> Unit", "KFunction1, Unit>")!>::stringVararg) + useMixedStringArgs3(, String) -> Unit", "KFunction1, Unit>")!>::stringVararg) + useTwoStringArrays(, Array) -> Unit", "KFunction1, Unit>")!>::stringVararg) +} diff --git a/compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.txt b/compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.txt new file mode 100644 index 00000000000..19bfbb853c3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.txt @@ -0,0 +1,14 @@ +package + +public fun intVararg(/*0*/ vararg args: kotlin.Int /*kotlin.IntArray*/): kotlin.Unit +public fun numberVararg(/*0*/ vararg args: kotlin.Number /*kotlin.Array*/): kotlin.Unit +public fun stringVararg(/*0*/ vararg args: kotlin.String /*kotlin.Array*/): kotlin.Unit +public fun test(): kotlin.Unit +public fun useIntArray(/*0*/ fn: (kotlin.Array) -> kotlin.Unit): kotlin.Unit +public fun useMixedStringArgs1(/*0*/ fn: (kotlin.String, kotlin.Array) -> kotlin.Unit): kotlin.Unit +public fun useMixedStringArgs2(/*0*/ fn: (kotlin.Array, kotlin.String) -> kotlin.Unit): kotlin.Unit +public fun useMixedStringArgs3(/*0*/ fn: (kotlin.String, kotlin.Array, kotlin.String) -> kotlin.Unit): kotlin.Unit +public fun usePrimitiveIntArray(/*0*/ fn: (kotlin.IntArray) -> kotlin.Unit): kotlin.Unit +public fun useStringArray(/*0*/ fn: (kotlin.Array) -> kotlin.Unit): kotlin.Unit +public fun useStrings(/*0*/ fn: (kotlin.String, kotlin.String, kotlin.String) -> kotlin.Unit): kotlin.Unit +public fun useTwoStringArrays(/*0*/ fn: (kotlin.Array, kotlin.Array) -> kotlin.Unit): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 712c4c266d8..fb4d44f1f72 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -23545,6 +23545,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/varargs/varargOfNothing.kt"); } + @TestMetadata("varargViewedAsArray.kt") + public void testVarargViewedAsArray() throws Exception { + runTest("compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.kt"); + } + @TestMetadata("varargsAndFunctionLiterals.kt") public void testVarargsAndFunctionLiterals() throws Exception { runTest("compiler/testData/diagnostics/tests/varargs/varargsAndFunctionLiterals.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 51c13a242ea..0d47b33225e 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -23465,6 +23465,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/varargs/varargOfNothing.kt"); } + @TestMetadata("varargViewedAsArray.kt") + public void testVarargViewedAsArray() throws Exception { + runTest("compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.kt"); + } + @TestMetadata("varargsAndFunctionLiterals.kt") public void testVarargsAndFunctionLiterals() throws Exception { runTest("compiler/testData/diagnostics/tests/varargs/varargsAndFunctionLiterals.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index b45888afd45..e58979ea11f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -2697,6 +2697,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsArray.kt"); } + @TestMetadata("varargViewedAsPrimitiveArray.kt") + public void testVarargViewedAsPrimitiveArray() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsPrimitiveArray.kt"); + } + @TestMetadata("varargWithDefaultValue.kt") public void testVarargWithDefaultValue() throws Exception { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargWithDefaultValue.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index a455ea6d3a4..7f9bda635f5 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -2697,6 +2697,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsArray.kt"); } + @TestMetadata("varargViewedAsPrimitiveArray.kt") + public void testVarargViewedAsPrimitiveArray() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsPrimitiveArray.kt"); + } + @TestMetadata("varargWithDefaultValue.kt") public void testVarargWithDefaultValue() throws Exception { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargWithDefaultValue.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 32bd54b6234..5320ace7ca8 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -2677,6 +2677,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsArray.kt"); } + @TestMetadata("varargViewedAsPrimitiveArray.kt") + public void testVarargViewedAsPrimitiveArray() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsPrimitiveArray.kt"); + } + @TestMetadata("varargWithDefaultValue.kt") public void testVarargWithDefaultValue() throws Exception { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargWithDefaultValue.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 6369d92f292..cb935863069 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -2127,6 +2127,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsArray.kt"); } + @TestMetadata("varargViewedAsPrimitiveArray.kt") + public void testVarargViewedAsPrimitiveArray() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsPrimitiveArray.kt"); + } + @TestMetadata("varargWithDefaultValue.kt") public void testVarargWithDefaultValue() throws Exception { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargWithDefaultValue.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 872ad68ec0d..9330cc95bc2 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -2127,6 +2127,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsArray.kt"); } + @TestMetadata("varargViewedAsPrimitiveArray.kt") + public void testVarargViewedAsPrimitiveArray() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsPrimitiveArray.kt"); + } + @TestMetadata("varargWithDefaultValue.kt") public void testVarargWithDefaultValue() throws Exception { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargWithDefaultValue.kt");