FIR: use expected type for lambda return statements if possible
This commit is contained in:
+6
@@ -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 {
|
||||||
|
|||||||
+6
@@ -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 {
|
||||||
|
|||||||
+6
@@ -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 {
|
||||||
|
|||||||
+10
-11
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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()
|
||||||
|
|||||||
+1
-1
@@ -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() }
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+2
-2
@@ -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) {})<!>)
|
||||||
|
|||||||
+2
-2
@@ -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>")!>{}<!>)
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+6
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user