FIR: use expected type for lambda return statements if possible

This commit is contained in:
pyos
2023-01-02 11:37:38 +01:00
committed by Dmitriy Novozhilov
parent 1eccb9aea1
commit 5e8591d61d
10 changed files with 60 additions and 17 deletions
@@ -15229,6 +15229,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/lambdaWithNullableUnitInElvis.kt"); runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/lambdaWithNullableUnitInElvis.kt");
} }
@Test
@TestMetadata("nestedLambda.kt")
public void testNestedLambda() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/nestedLambda.kt");
}
@Test @Test
@TestMetadata("noCoercion.kt") @TestMetadata("noCoercion.kt")
public void testNoCoercion() throws Exception { public void testNoCoercion() throws Exception {
@@ -15235,6 +15235,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/lambdaWithNullableUnitInElvis.kt"); runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/lambdaWithNullableUnitInElvis.kt");
} }
@Test
@TestMetadata("nestedLambda.kt")
public void testNestedLambda() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/nestedLambda.kt");
}
@Test @Test
@TestMetadata("noCoercion.kt") @TestMetadata("noCoercion.kt")
public void testNoCoercion() throws Exception { public void testNoCoercion() throws Exception {
@@ -15229,6 +15229,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/lambdaWithNullableUnitInElvis.kt"); runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/lambdaWithNullableUnitInElvis.kt");
} }
@Test
@TestMetadata("nestedLambda.kt")
public void testNestedLambda() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/nestedLambda.kt");
}
@Test @Test
@TestMetadata("noCoercion.kt") @TestMetadata("noCoercion.kt")
public void testNoCoercion() throws Exception { public void testNoCoercion() throws Exception {
@@ -15,11 +15,10 @@ import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedReferenceError
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeLambdaArgumentConstraintPosition import org.jetbrains.kotlin.fir.resolve.inference.model.ConeLambdaArgumentConstraintPosition
import org.jetbrains.kotlin.fir.resolve.shouldReturnUnit import org.jetbrains.kotlin.fir.resolve.shouldReturnUnit
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
import org.jetbrains.kotlin.fir.types.ConeTypeVariable import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.isMarkedNullable
import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerContext import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerContext
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
@@ -152,7 +151,6 @@ class PostponedArgumentsAnalyzer(
candidate, candidate,
results, results,
completionMode, completionMode,
expectedTypeForReturnArguments,
::substitute ::substitute
) )
return results return results
@@ -164,7 +162,6 @@ class PostponedArgumentsAnalyzer(
candidate: Candidate, candidate: Candidate,
results: ReturnArgumentsAnalysisResult, results: ReturnArgumentsAnalysisResult,
completionMode: ConstraintSystemCompletionMode, completionMode: ConstraintSystemCompletionMode,
expectedReturnType: ConeKotlinType? = null,
substitute: (ConeKotlinType) -> ConeKotlinType = c.createSubstituteFunctorForLambdaAnalysis() substitute: (ConeKotlinType) -> ConeKotlinType = c.createSubstituteFunctorForLambdaAnalysis()
) { ) {
val (returnArguments, inferenceSession) = results val (returnArguments, inferenceSession) = results
@@ -174,7 +171,9 @@ class PostponedArgumentsAnalyzer(
val lastExpression = lambda.atom.body?.statements?.lastOrNull() as? FirExpression val lastExpression = lambda.atom.body?.statements?.lastOrNull() as? FirExpression
var hasExpressionInReturnArguments = false var hasExpressionInReturnArguments = false
val lambdaReturnType = lambda.returnType.let(substitute) val returnTypeRef = lambda.atom.returnTypeRef.let {
it as? FirResolvedTypeRef ?: it.resolvedTypeFromPrototype(substitute(lambda.returnType))
}
returnArguments.forEach { returnArguments.forEach {
val haveSubsystem = c.addSubsystemFromExpression(it) val haveSubsystem = c.addSubsystemFromExpression(it)
// If the lambda returns Unit, the last expression is not returned and should not be constrained. // If the lambda returns Unit, the last expression is not returned and should not be constrained.
@@ -186,7 +185,7 @@ class PostponedArgumentsAnalyzer(
// } // }
// Things get even weirder if T has an upper bound incompatible with Unit. // Things get even weirder if T has an upper bound incompatible with Unit.
if (it == lastExpression && !haveSubsystem && if (it == lastExpression && !haveSubsystem &&
(expectedReturnType?.isUnitOrFlexibleUnit == true || lambda.atom.shouldReturnUnit(returnArguments)) (returnTypeRef.type.isUnitOrFlexibleUnit || lambda.atom.shouldReturnUnit(returnArguments))
) return@forEach ) return@forEach
hasExpressionInReturnArguments = true hasExpressionInReturnArguments = true
@@ -194,8 +193,8 @@ class PostponedArgumentsAnalyzer(
candidate.resolveArgumentExpression( candidate.resolveArgumentExpression(
builder, builder,
it, it,
lambdaReturnType, returnTypeRef.type,
lambda.atom.returnTypeRef, // TODO: proper ref returnTypeRef,
checkerSink, checkerSink,
context = resolutionContext, context = resolutionContext,
isReceiver = false, isReceiver = false,
@@ -204,10 +203,10 @@ class PostponedArgumentsAnalyzer(
} }
} }
if (!hasExpressionInReturnArguments && !lambdaReturnType.isUnitOrFlexibleUnit) { if (!hasExpressionInReturnArguments && !returnTypeRef.type.isUnitOrFlexibleUnit) {
builder.addSubtypeConstraint( builder.addSubtypeConstraint(
components.session.builtinTypes.unitType.type, components.session.builtinTypes.unitType.type,
lambdaReturnType, returnTypeRef.type,
ConeLambdaArgumentConstraintPosition(lambda.atom) ConeLambdaArgumentConstraintPosition(lambda.atom)
) )
} }
@@ -661,7 +661,7 @@ class BodyResolveContext(
val labelName = anonymousFunction.label?.name?.let { Name.identifier(it) } val labelName = anonymousFunction.label?.name?.let { Name.identifier(it) }
withContainer(anonymousFunction) { withContainer(anonymousFunction) {
withLabelAndReceiverType(labelName, anonymousFunction, receiverTypeRef?.coneType, holder) { withLabelAndReceiverType(labelName, anonymousFunction, receiverTypeRef?.coneType, holder) {
if (mode is ResolutionMode.LambdaResolution) { if (mode is ResolutionMode.LambdaResolution && mode.expectedReturnTypeRef == null) {
withLambdaBeingAnalyzedInDependentContext(anonymousFunction.symbol, f) withLambdaBeingAnalyzedInDependentContext(anonymousFunction.symbol, f)
} else { } else {
f() f()
@@ -27,7 +27,7 @@ fun testResultOfAnonFun2() =
fun testReturnFromAnonFun() = fun testReturnFromAnonFun() =
run(fun () { run(fun () {
return <!ARGUMENT_TYPE_MISMATCH, RETURN_TYPE_MISMATCH!>if (true) 42 else println()<!> return if (true) 42 else println()
}) })
fun <!IMPLICIT_NOTHING_RETURN_TYPE!>testReturn1<!>() = fun <!IMPLICIT_NOTHING_RETURN_TYPE!>testReturn1<!>() =
@@ -0,0 +1,20 @@
// FIR_IDENTICAL
// !SKIP_TXT
interface Context<T> {
fun proceed(): T
}
interface A<T : Any, K : Any> {
fun process(block: Context<T>.(T) -> Unit)
}
fun <T> processNested(body: () -> T): T {
return body()
}
fun test(pipeline: A<Any, String>) {
pipeline.process {
// OK: processNested<Unit> { /* no return */ proceed() /*: Any */ /* implicit coercion to Unit */ }
return@process processNested { proceed() }
}
}
@@ -139,8 +139,8 @@ fun main() {
* K <: (C) -> Unit -> TypeVariable(_RP1) >: C * K <: (C) -> Unit -> TypeVariable(_RP1) >: C
* K == (B) -> Unit -> TypeVariable(_RP1) == B * K == (B) -> Unit -> TypeVariable(_RP1) == B
*/ */
val x17: (C) -> Unit = <!INITIALIZER_TYPE_MISMATCH, TYPE_MISMATCH!>selectB(id <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("B")!>it<!> }<!>, id <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH!>{ it }<!>, id<(B) -> Unit> { x -> x })<!> val x17: (C) -> Unit = <!INITIALIZER_TYPE_MISMATCH, TYPE_MISMATCH!>selectB(id <!ARGUMENT_TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("B")!>it<!> }<!>, id <!ARGUMENT_TYPE_MISMATCH!>{ it }<!>, id<(B) -> Unit> { x -> x })<!>
val x18: (C) -> Unit = <!TYPE_MISMATCH!>select(id <!ARGUMENT_TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("C")!>it<!> }<!>, <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("C")!>it<!> }<!>, id<(B) -> Unit> { x -> x })<!> val x18: (C) -> Unit = <!TYPE_MISMATCH!>select(id <!ARGUMENT_TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("C")!>it<!> }<!>, <!ARGUMENT_TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("C")!>it<!> }<!>, id<(B) -> Unit> { x -> x })<!>
// Resolution of extension/non-extension functions combination // Resolution of extension/non-extension functions combination
val x19: String.() -> Unit = select(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.String, kotlin.Unit>")!>id { <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>this<!> }<!>, <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.String, kotlin.Unit>")!>id(fun(x: String) {})<!>) val x19: String.() -> Unit = select(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.String, kotlin.Unit>")!>id { <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>this<!> }<!>, <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.String, kotlin.Unit>")!>id(fun(x: String) {})<!>)
@@ -19,7 +19,7 @@ fun main() {
val x1: suspend (Int) -> Unit = takeSuspend(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>id { it }<!>, <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>{ x -> x }<!>) val x1: suspend (Int) -> Unit = takeSuspend(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>id { it }<!>, <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>{ x -> x }<!>)
// Here, the error should be // Here, the error should be
val x2: (Int) -> Unit = <!INITIALIZER_TYPE_MISMATCH!>takeSuspend(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>id <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH!>{ it }<!><!>, <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH, DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>{ x -> x }<!>)<!> val x2: (Int) -> Unit = <!INITIALIZER_TYPE_MISMATCH!>takeSuspend(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>id <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH!>{ it }<!><!>, <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH, DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>{ x -> x }<!>)<!>
val x3: suspend (Int) -> Unit = takeSimpleFunction(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>id <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH!>{ it }<!><!>, <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH, DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>{ x -> x }<!>) val x3: suspend (Int) -> Unit = takeSimpleFunction(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>id <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH!>{ it }<!><!>, <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH, DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>{ x -> x }<!>)
val x4: (Int) -> Unit = takeSimpleFunction(<!ARGUMENT_TYPE_MISMATCH!>id<suspend (Int) -> Unit> {}<!>, <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.Int, kotlin.Unit>")!>{}<!>) val x4: (Int) -> Unit = takeSimpleFunction(<!ARGUMENT_TYPE_MISMATCH!>id<suspend (Int) -> Unit> {}<!>, <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.Int, kotlin.Unit>")!>{}<!>)
} }
@@ -15235,6 +15235,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/lambdaWithNullableUnitInElvis.kt"); runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/lambdaWithNullableUnitInElvis.kt");
} }
@Test
@TestMetadata("nestedLambda.kt")
public void testNestedLambda() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/nestedLambda.kt");
}
@Test @Test
@TestMetadata("noCoercion.kt") @TestMetadata("noCoercion.kt")
public void testNoCoercion() throws Exception { public void testNoCoercion() throws Exception {