From 71cbe9768869db76b6ee54f44faca7d75de7be6f Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Tue, 2 Jun 2020 15:04:39 +0300 Subject: [PATCH] Introduce `Unit`-conversions for simple arguments --- ...irOldFrontendDiagnosticsTestGenerated.java | 43 ++++++++++ .../calls/components/UnitTypeConversions.kt | 83 +++++++++++++++++++ .../calls/components/typeConversions.kt | 24 ++++-- .../chainedFunSuspendUnitConversion.fir.kt | 18 ++++ .../chainedFunSuspendUnitConversion.kt | 18 ++++ .../chainedFunSuspendUnitConversion.txt | 20 +++++ .../chainedFunUnitConversion.fir.kt | 15 ++++ .../chainedFunUnitConversion.kt | 15 ++++ .../chainedFunUnitConversion.txt | 19 +++++ .../chainedUnitSuspendConversion.fir.kt | 14 ++++ .../chainedUnitSuspendConversion.kt | 14 ++++ .../chainedUnitSuspendConversion.txt | 13 +++ .../unitConversionCompatibility.kt | 0 .../unitConversionForAllKinds.fir.kt | 16 ++++ .../unitConversionForAllKinds.kt | 16 ++++ .../unitConversionForAllKinds.txt | 7 ++ .../unitConversionForSubtypes.fir.kt | 22 +++++ .../unitConversionForSubtypes.kt | 22 +++++ .../unitConversionForSubtypes.txt | 21 +++++ .../checkers/DiagnosticsTestGenerated.java | 43 ++++++++++ .../DiagnosticsUsingJavacTestGenerated.java | 43 ++++++++++ .../kotlin/config/LanguageVersionSettings.kt | 1 + .../kotlin/builtins/functionTypes.kt | 3 + 23 files changed, 484 insertions(+), 6 deletions(-) create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/UnitTypeConversions.kt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.fir.kt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.kt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.txt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.fir.kt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.kt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.txt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.fir.kt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.kt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.txt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.kt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.fir.kt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.kt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.txt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.fir.kt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.kt create mode 100644 compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.txt diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index bb6693383e1..5f0d932387c 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -24444,6 +24444,49 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte } } + @TestMetadata("compiler/testData/diagnostics/tests/unitConversion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class UnitConversion extends AbstractFirOldFrontendDiagnosticsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInUnitConversion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/unitConversion"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("chainedFunSuspendUnitConversion.kt") + public void testChainedFunSuspendUnitConversion() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.kt"); + } + + @TestMetadata("chainedFunUnitConversion.kt") + public void testChainedFunUnitConversion() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.kt"); + } + + @TestMetadata("chainedUnitSuspendConversion.kt") + public void testChainedUnitSuspendConversion() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.kt"); + } + + @TestMetadata("unitConversionCompatibility.kt") + public void testUnitConversionCompatibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.kt"); + } + + @TestMetadata("unitConversionForAllKinds.kt") + public void testUnitConversionForAllKinds() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.kt"); + } + + @TestMetadata("unitConversionForSubtypes.kt") + public void testUnitConversionForSubtypes() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.kt"); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/varargs") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/UnitTypeConversions.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/UnitTypeConversions.kt new file mode 100644 index 00000000000..62580abda64 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/UnitTypeConversions.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.calls.components + +import org.jetbrains.kotlin.builtins.* +import org.jetbrains.kotlin.descriptors.ParameterDescriptor +import org.jetbrains.kotlin.resolve.calls.inference.model.LowerPriorityToPreserveCompatibility +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallArgument +import org.jetbrains.kotlin.resolve.calls.model.KotlinResolutionCandidate +import org.jetbrains.kotlin.resolve.calls.model.SimpleKotlinCallArgument +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.isDynamic +import org.jetbrains.kotlin.types.typeUtil.isUnit + +object UnitTypeConversions : ParameterTypeConversion { + override fun conversionDefinitelyNotNeeded( + candidate: KotlinResolutionCandidate, + argument: KotlinCallArgument, + expectedParameterType: UnwrappedType + ): Boolean { + // for callable references and lambdas it already works + if (argument !is SimpleKotlinCallArgument) return true + + val receiver = argument.receiver + if (receiver.receiverValue.type.hasUnitOrDynamicReturnType()) return true + if (receiver.typesFromSmartCasts.any { it.hasUnitOrDynamicReturnType() }) return true + + if ( + !expectedParameterType.isBuiltinFunctionalType || + !expectedParameterType.getReturnTypeFromFunctionType().isUnit() + ) return true + + return false + } + + private fun KotlinType.hasUnitOrDynamicReturnType(): Boolean = + isFunctionOrKFunctionTypeWithAnySuspendability && arguments.last().type.isUnitOrDynamic() + + private fun KotlinType.isUnitOrDynamic(): Boolean = isUnit() || isDynamic() + + override fun conversionIsNeededBeforeSubtypingCheck(argument: KotlinCallArgument): Boolean = + argument is SimpleKotlinCallArgument && argument.receiver.stableType.isFunctionType + + override fun conversionIsNeededAfterSubtypingCheck(argument: KotlinCallArgument): Boolean { + if (argument !is SimpleKotlinCallArgument) return false + + var isFunctionTypeOrSubtype = false + val hasReturnTypeInSubtypes = argument.receiver.stableType.isFunctionTypeOrSubtype { + isFunctionTypeOrSubtype = true + it.getReturnTypeFromFunctionType().isUnitOrDynamic() + } + + if (!isFunctionTypeOrSubtype) return false + + return !hasReturnTypeInSubtypes + } + + override fun convertParameterType( + candidate: KotlinResolutionCandidate, + argument: KotlinCallArgument, + parameter: ParameterDescriptor, + expectedParameterType: UnwrappedType + ): UnwrappedType? { + val nonUnitReturnedParameterType = createFunctionType( + candidate.callComponents.builtIns, + expectedParameterType.annotations, + expectedParameterType.getReceiverTypeFromFunctionType(), + expectedParameterType.getValueParameterTypesFromFunctionType().map { it.type }, + parameterNames = null, + candidate.callComponents.builtIns.nullableAnyType, + suspendFunction = expectedParameterType.isSuspendFunctionType + ) + + candidate.addDiagnostic(LowerPriorityToPreserveCompatibility) + + return nonUnitReturnedParameterType + } + +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/typeConversions.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/typeConversions.kt index 0bd5d6be55a..1236a68e5a0 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/typeConversions.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/typeConversions.kt @@ -40,14 +40,23 @@ object TypeConversions { ) val suspendConversionData = performConversionBeforeSubtyping( - candidate, argument, candidateParameter, samConversionData.convertedType ?: candidateExpectedType, SuspendTypeConversions + candidate, argument, candidateParameter, + candidateExpectedType = samConversionData.convertedType ?: candidateExpectedType, + SuspendTypeConversions + ) + + val unitConversionData = performConversionBeforeSubtyping( + candidate, argument, candidateParameter, + candidateExpectedType = suspendConversionData.convertedType ?: samConversionData.convertedType ?: candidateExpectedType, + UnitTypeConversions ) return ConversionData( - convertedType = suspendConversionData.convertedType ?: samConversionData.convertedType, - wasConversion = samConversionData.wasConversion || suspendConversionData.wasConversion, + convertedType = unitConversionData.convertedType ?: suspendConversionData.convertedType ?: samConversionData.convertedType, + wasConversion = samConversionData.wasConversion || suspendConversionData.wasConversion || unitConversionData.wasConversion, conversionDefinitelyNotNeeded = samConversionData.conversionDefinitelyNotNeeded && - suspendConversionData.conversionDefinitelyNotNeeded + suspendConversionData.conversionDefinitelyNotNeeded && + unitConversionData.conversionDefinitelyNotNeeded ) } @@ -60,10 +69,13 @@ object TypeConversions { val samConvertedType = performConversionAfterSubtyping( candidate, argument, candidateParameter, candidateExpectedType, SamTypeConversions ) - if (samConvertedType != null) return samConvertedType - return performConversionAfterSubtyping(candidate, argument, candidateParameter, candidateExpectedType, SuspendTypeConversions) + val suspendConvertedType = + performConversionAfterSubtyping(candidate, argument, candidateParameter, candidateExpectedType, SuspendTypeConversions) + if (suspendConvertedType != null) return suspendConvertedType + + return performConversionAfterSubtyping(candidate, argument, candidateParameter, candidateExpectedType, UnitTypeConversions) } private fun performConversionAfterSubtyping( diff --git a/compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.fir.kt b/compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.fir.kt new file mode 100644 index 00000000000..8872067d293 --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.fir.kt @@ -0,0 +1,18 @@ +// !LANGUAGE: +UnitConversion +SuspendConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +fun interface SuspendRunnable { + suspend fun run() +} + +fun foo(r: SuspendRunnable) {} + +fun bar(): String = "" + +abstract class SubInt : () -> Int + +fun test(f: () -> String, s: SubInt) { + foo(f) + foo(s) + foo(::bar) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.kt b/compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.kt new file mode 100644 index 00000000000..1bc346b8bd6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.kt @@ -0,0 +1,18 @@ +// !LANGUAGE: +UnitConversion +SuspendConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +fun interface SuspendRunnable { + suspend fun run() +} + +fun foo(r: SuspendRunnable) {} + +fun bar(): String = "" + +abstract class SubInt : () -> Int + +fun test(f: () -> String, s: SubInt) { + foo(f) + foo(s) + foo(::bar) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.txt b/compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.txt new file mode 100644 index 00000000000..5437122c88d --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.txt @@ -0,0 +1,20 @@ +package + +public fun bar(): kotlin.String +public fun foo(/*0*/ r: SuspendRunnable): kotlin.Unit +public fun test(/*0*/ f: () -> kotlin.String, /*1*/ s: SubInt): kotlin.Unit + +public abstract class SubInt : () -> kotlin.Int { + public constructor SubInt() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun invoke(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public fun interface SuspendRunnable { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract suspend fun run(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.fir.kt b/compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.fir.kt new file mode 100644 index 00000000000..663d9bc3718 --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.fir.kt @@ -0,0 +1,15 @@ +// !LANGUAGE: +UnitConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +fun interface KRunnable { + fun run() +} + +fun foo(r: KRunnable) {} + +abstract class SubInt : () -> Int + +fun test(f: () -> Int, s: SubInt) { + foo(f) + foo(s) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.kt b/compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.kt new file mode 100644 index 00000000000..d2cb49ee966 --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.kt @@ -0,0 +1,15 @@ +// !LANGUAGE: +UnitConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +fun interface KRunnable { + fun run() +} + +fun foo(r: KRunnable) {} + +abstract class SubInt : () -> Int + +fun test(f: () -> Int, s: SubInt) { + foo(f) + foo(s) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.txt b/compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.txt new file mode 100644 index 00000000000..9834b5ec45e --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.txt @@ -0,0 +1,19 @@ +package + +public fun foo(/*0*/ r: KRunnable): kotlin.Unit +public fun test(/*0*/ f: () -> kotlin.Int, /*1*/ s: SubInt): kotlin.Unit + +public fun interface KRunnable { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract fun run(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public abstract class SubInt : () -> kotlin.Int { + public constructor SubInt() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun invoke(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.fir.kt b/compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.fir.kt new file mode 100644 index 00000000000..0166b5e4859 --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.fir.kt @@ -0,0 +1,14 @@ +// !LANGUAGE: +UnitConversion +SuspendConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +fun foo(f: suspend () -> Unit) {} + +fun bar(): String = "" + +abstract class SubInt : () -> Int + +fun test(g: () -> Double, s: SubInt) { + foo(::bar) + foo(g) + foo(s) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.kt b/compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.kt new file mode 100644 index 00000000000..ad5611f7a73 --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.kt @@ -0,0 +1,14 @@ +// !LANGUAGE: +UnitConversion +SuspendConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +fun foo(f: suspend () -> Unit) {} + +fun bar(): String = "" + +abstract class SubInt : () -> Int + +fun test(g: () -> Double, s: SubInt) { + foo(::bar) + foo(g) + foo(s) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.txt b/compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.txt new file mode 100644 index 00000000000..b40df966942 --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.txt @@ -0,0 +1,13 @@ +package + +public fun bar(): kotlin.String +public fun foo(/*0*/ f: suspend () -> kotlin.Unit): kotlin.Unit +public fun test(/*0*/ g: () -> kotlin.Double, /*1*/ s: SubInt): kotlin.Unit + +public abstract class SubInt : () -> kotlin.Int { + public constructor SubInt() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun invoke(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.kt b/compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.kt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.fir.kt b/compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.fir.kt new file mode 100644 index 00000000000..b7549eda2ea --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.fir.kt @@ -0,0 +1,16 @@ +// !LANGUAGE: +UnitConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +fun foo(f: () -> Unit) {} +fun fooGeneric(f: (T) -> Unit): T = TODO() + +fun bar(): String = "" +fun createCall(): () -> Int = TODO() + +fun test(g: () -> String, h: (Float) -> String) { + foo(::bar) + foo { "something" } + foo(g) + + fooGeneric(h) +} diff --git a/compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.kt b/compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.kt new file mode 100644 index 00000000000..38681e94c2d --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.kt @@ -0,0 +1,16 @@ +// !LANGUAGE: +UnitConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +fun foo(f: () -> Unit) {} +fun fooGeneric(f: (T) -> Unit): T = TODO() + +fun bar(): String = "" +fun createCall(): () -> Int = TODO() + +fun test(g: () -> String, h: (Float) -> String) { + foo(::bar) + foo { "something" } + foo(g) + + fooGeneric(h) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.txt b/compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.txt new file mode 100644 index 00000000000..d04d936106e --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.txt @@ -0,0 +1,7 @@ +package + +public fun bar(): kotlin.String +public fun createCall(): () -> kotlin.Int +public fun foo(/*0*/ f: () -> kotlin.Unit): kotlin.Unit +public fun fooGeneric(/*0*/ f: (T) -> kotlin.Unit): T +public fun test(/*0*/ g: () -> kotlin.String, /*1*/ h: (kotlin.Float) -> kotlin.String): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.fir.kt b/compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.fir.kt new file mode 100644 index 00000000000..237022dddfe --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.fir.kt @@ -0,0 +1,22 @@ +// !LANGUAGE: +UnitConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +fun foo(f: (Int, String) -> Unit) {} + +abstract class SubInt : (Int, String) -> Int +abstract class SubIntWrong : (String, String) -> Int + +fun test1(s: SubInt, sWrong: SubIntWrong) { + foo(s) + foo(sWrong) + + val a = "foo" + foo(a) + + a as (Int, String) -> String + foo(a) +} + +fun test2(x: T) where T : (Int, String) -> Int, T : (Double) -> Int { + foo(x) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.kt b/compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.kt new file mode 100644 index 00000000000..c8995b7d9e6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.kt @@ -0,0 +1,22 @@ +// !LANGUAGE: +UnitConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +fun foo(f: (Int, String) -> Unit) {} + +abstract class SubInt : (Int, String) -> Int +abstract class SubIntWrong : (String, String) -> Int + +fun test1(s: SubInt, sWrong: SubIntWrong) { + foo(s) + foo(sWrong) + + val a = "foo" + foo(a) + + a as (Int, String) -> String + foo(a) +} + +fun test2(x: T) where T : (Int, String) -> Int, T : (Double) -> Int { + foo(x) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.txt b/compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.txt new file mode 100644 index 00000000000..49b8ff4644e --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.txt @@ -0,0 +1,21 @@ +package + +public fun foo(/*0*/ f: (kotlin.Int, kotlin.String) -> kotlin.Unit): kotlin.Unit +public fun test1(/*0*/ s: SubInt, /*1*/ sWrong: SubIntWrong): kotlin.Unit +public fun kotlin.Int> test2(/*0*/ x: T): kotlin.Unit where T : (kotlin.Double) -> kotlin.Int + +public abstract class SubInt : (kotlin.Int, kotlin.String) -> kotlin.Int { + public constructor SubInt() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun invoke(/*0*/ p1: kotlin.Int, /*1*/ p2: kotlin.String): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public abstract class SubIntWrong : (kotlin.String, kotlin.String) -> kotlin.Int { + public constructor SubIntWrong() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun invoke(/*0*/ p1: kotlin.String, /*1*/ p2: kotlin.String): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 94eb1933f65..e020359378f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -24526,6 +24526,49 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali } } + @TestMetadata("compiler/testData/diagnostics/tests/unitConversion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class UnitConversion extends AbstractDiagnosticsTestWithFirValidation { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInUnitConversion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/unitConversion"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("chainedFunSuspendUnitConversion.kt") + public void testChainedFunSuspendUnitConversion() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.kt"); + } + + @TestMetadata("chainedFunUnitConversion.kt") + public void testChainedFunUnitConversion() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.kt"); + } + + @TestMetadata("chainedUnitSuspendConversion.kt") + public void testChainedUnitSuspendConversion() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.kt"); + } + + @TestMetadata("unitConversionCompatibility.kt") + public void testUnitConversionCompatibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.kt"); + } + + @TestMetadata("unitConversionForAllKinds.kt") + public void testUnitConversionForAllKinds() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.kt"); + } + + @TestMetadata("unitConversionForSubtypes.kt") + public void testUnitConversionForSubtypes() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.kt"); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/varargs") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index f0239f0d3f8..a76fe38530c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -24446,6 +24446,49 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing } } + @TestMetadata("compiler/testData/diagnostics/tests/unitConversion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class UnitConversion extends AbstractDiagnosticsUsingJavacTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInUnitConversion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/unitConversion"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("chainedFunSuspendUnitConversion.kt") + public void testChainedFunSuspendUnitConversion() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.kt"); + } + + @TestMetadata("chainedFunUnitConversion.kt") + public void testChainedFunUnitConversion() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/chainedFunUnitConversion.kt"); + } + + @TestMetadata("chainedUnitSuspendConversion.kt") + public void testChainedUnitSuspendConversion() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/chainedUnitSuspendConversion.kt"); + } + + @TestMetadata("unitConversionCompatibility.kt") + public void testUnitConversionCompatibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.kt"); + } + + @TestMetadata("unitConversionForAllKinds.kt") + public void testUnitConversionForAllKinds() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.kt"); + } + + @TestMetadata("unitConversionForSubtypes.kt") + public void testUnitConversionForSubtypes() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionForSubtypes.kt"); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/varargs") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 35a3d393527..7007f131a75 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -153,6 +153,7 @@ enum class LanguageFeature( FunctionReferenceWithDefaultValueAsOtherType(sinceVersion = KOTLIN_1_4), NonStrictOnlyInputTypesChecks(sinceVersion = KOTLIN_1_4), SuspendConversion(sinceVersion = KOTLIN_1_4, defaultState = State.DISABLED), + UnitConversion(sinceVersion = KOTLIN_1_4, defaultState = State.DISABLED), OverloadResolutionByLambdaReturnType(sinceVersion = KOTLIN_1_4), BooleanElvisBoundSmartCasts(sinceVersion = KOTLIN_1_3, defaultState = State.DISABLED), // see KT-26357 for details diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt index fa6e67519b0..ddd0c03cae8 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt @@ -54,6 +54,9 @@ val KotlinType.isSuspendFunctionTypeOrSubtype: Boolean val KotlinType.isBuiltinFunctionalTypeOrSubtype: Boolean get() = isTypeOrSubtypeOf { it.isBuiltinFunctionalType } +fun KotlinType.isFunctionTypeOrSubtype(predicate: (KotlinType) -> Boolean): Boolean = + isTypeOrSubtypeOf { it.isFunctionType && predicate(it) } + val KotlinType.isFunctionType: Boolean get() = constructor.declarationDescriptor?.getFunctionalClassKind() == FunctionClassDescriptor.Kind.Function