[FIR] Infer Unit type for generic lambda function if implicit return exists
Make behavior more consistent with K1 ^KT-63563 Fixed
This commit is contained in:
committed by
Space Team
parent
b19c496c44
commit
a2cd2200d6
+6
@@ -14357,6 +14357,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
|
||||
runTest("compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("LambdaReturnTypeIsUnitIfImplicitReturnExists.kt")
|
||||
public void testLambdaReturnTypeIsUnitIfImplicitReturnExists() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/functionLiterals/return/LambdaReturnTypeIsUnitIfImplicitReturnExists.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("LambdaWithParameter.kt")
|
||||
public void testLambdaWithParameter() throws Exception {
|
||||
|
||||
+6
@@ -14357,6 +14357,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
|
||||
runTest("compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("LambdaReturnTypeIsUnitIfImplicitReturnExists.kt")
|
||||
public void testLambdaReturnTypeIsUnitIfImplicitReturnExists() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/functionLiterals/return/LambdaReturnTypeIsUnitIfImplicitReturnExists.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("LambdaWithParameter.kt")
|
||||
public void testLambdaWithParameter() throws Exception {
|
||||
|
||||
+6
@@ -14351,6 +14351,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
||||
runTest("compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("LambdaReturnTypeIsUnitIfImplicitReturnExists.kt")
|
||||
public void testLambdaReturnTypeIsUnitIfImplicitReturnExists() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/functionLiterals/return/LambdaReturnTypeIsUnitIfImplicitReturnExists.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("LambdaWithParameter.kt")
|
||||
public void testLambdaWithParameter() throws Exception {
|
||||
|
||||
+6
@@ -14357,6 +14357,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
||||
runTest("compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("LambdaReturnTypeIsUnitIfImplicitReturnExists.kt")
|
||||
public void testLambdaReturnTypeIsUnitIfImplicitReturnExists() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/functionLiterals/return/LambdaReturnTypeIsUnitIfImplicitReturnExists.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("LambdaWithParameter.kt")
|
||||
public void testLambdaWithParameter() throws Exception {
|
||||
|
||||
+1
-19
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.KtRealSourceElementKind
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.cfa.util.previousCfgNodes
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.hasExplicitReturnType
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isSubtypeForTypeMismatch
|
||||
@@ -21,7 +20,6 @@ import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.isExhaustive
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
|
||||
object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() {
|
||||
@@ -91,19 +89,13 @@ object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() {
|
||||
}
|
||||
}
|
||||
} else if (resultExpression.source?.kind is KtFakeSourceElementKind.ImplicitUnit &&
|
||||
!functionReturnType.lowerBoundIfFlexible().isUnit &&
|
||||
shouldCheckMismatchForAnonymousFunction(targetElement, expression)
|
||||
!functionReturnType.lowerBoundIfFlexible().isUnit
|
||||
) {
|
||||
// Disallow cases like
|
||||
// fun foo(): Any { return }
|
||||
// Allow cases like
|
||||
// fun foo(): Unit { return }
|
||||
// fun foo() { return Unit }
|
||||
// But ignore anonymous functions without explicit returns, the following code is valid (where type of parameter R is being inferenced):
|
||||
// run {
|
||||
// if (flag) return@run
|
||||
// "str"
|
||||
// }
|
||||
// If type parameter is specified explicitly, checking is performed in the branch above and RETURN_TYPE_MISMATCH is reported
|
||||
reporter.reportOn(
|
||||
resultExpression.source,
|
||||
@@ -116,15 +108,5 @@ object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldCheckMismatchForAnonymousFunction(targetElement: FirFunction, expression: FirReturnExpression): Boolean {
|
||||
if (targetElement !is FirAnonymousFunction || !targetElement.isLambda) return true
|
||||
val cfgNodes = targetElement.controlFlowGraphReference?.controlFlowGraph?.exitNode?.previousCfgNodes ?: return true
|
||||
// Check if any return expression other than the current is explicit and not Unit
|
||||
return cfgNodes.any {
|
||||
val fir = it.fir
|
||||
if (fir === expression) false else fir is FirReturnExpression && !fir.result.resolvedType.isUnit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildSamConversionExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirPropertyAccessExpressionImpl
|
||||
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildResolvedCallableReference
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
|
||||
@@ -43,7 +42,6 @@ import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
|
||||
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.InferredEmptyIntersection
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
|
||||
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
@@ -707,6 +705,8 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
|
||||
val initialReturnType = anonymousFunction.returnTypeRef.coneTypeSafe<ConeKotlinType>()
|
||||
val expectedReturnType = initialReturnType?.let { finallySubstituteOrSelf(it) }
|
||||
?: runIf(returnExpressions.any { it.source?.kind is KtFakeSourceElementKind.ImplicitUnit.Return })
|
||||
{ session.builtinTypes.unitType.coneType }
|
||||
?: expectedType?.returnType(session) as? ConeClassLikeType
|
||||
?: (data as? ExpectedArgumentType.ArgumentsMap)?.lambdasReturnTypes?.get(anonymousFunction)
|
||||
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// FIR_IDENTICAL
|
||||
// ISSUE: KT-63563
|
||||
|
||||
fun foo(x: () -> Any) = x()
|
||||
fun foo2(x: () -> Unit) = x()
|
||||
fun <T> foo3(x: () -> T): T = x()
|
||||
|
||||
fun main2() {
|
||||
foo {
|
||||
if ("0".hashCode() == 42) <!RETURN_TYPE_MISMATCH!>return@foo<!>
|
||||
""
|
||||
}
|
||||
foo2 {
|
||||
if ("1".hashCode() == 42) return@foo2
|
||||
""
|
||||
}
|
||||
foo3 { // Infer T to Unit -> no error
|
||||
if ("2".hashCode() == 42) return@foo3
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
fun test(a: Int) {
|
||||
run f@{
|
||||
if (a > 0) <!RETURN_TYPE_MISMATCH!>return@f<!>
|
||||
else return@f 1
|
||||
}
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
fun test(a: Int) {
|
||||
run f@{
|
||||
if (a > 0) return@f
|
||||
|
||||
Vendored
-20
@@ -1,20 +0,0 @@
|
||||
// NI_EXPECTED_FILE
|
||||
val flag = true
|
||||
|
||||
// type of a was checked by txt
|
||||
val a = run { // () -> Unit
|
||||
return@run
|
||||
}
|
||||
|
||||
// Unit
|
||||
val b = run {
|
||||
if (flag) return@run
|
||||
5
|
||||
}
|
||||
|
||||
// Unit
|
||||
val c = run {
|
||||
if (flag) <!RETURN_TYPE_MISMATCH!>return@run<!>
|
||||
|
||||
return@run 4
|
||||
}
|
||||
Vendored
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// NI_EXPECTED_FILE
|
||||
val flag = true
|
||||
|
||||
|
||||
Generated
+6
@@ -14357,6 +14357,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("LambdaReturnTypeIsUnitIfImplicitReturnExists.kt")
|
||||
public void testLambdaReturnTypeIsUnitIfImplicitReturnExists() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/functionLiterals/return/LambdaReturnTypeIsUnitIfImplicitReturnExists.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("LambdaWithParameter.kt")
|
||||
public void testLambdaWithParameter() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user