K2: Resolve non-exhaustive when expressions independently

^KT-55169 Fixed
^KT-55175 Related
This commit is contained in:
Denis.Zharkov
2022-11-28 18:02:19 +01:00
committed by Space Team
parent 9fa0f51a61
commit 04d3996e68
8 changed files with 78 additions and 5 deletions
@@ -35152,6 +35152,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/tests/when/NonExhaustiveBooleanNullable.kt");
}
@Test
@TestMetadata("nonExhaustiveDependentContext.kt")
public void testNonExhaustiveDependentContext() throws Exception {
runTest("compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.kt");
}
@Test
@TestMetadata("NonExhaustivePlatformEnum.kt")
public void testNonExhaustivePlatformEnum() throws Exception {
@@ -35248,6 +35248,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/when/NonExhaustiveBooleanNullable.kt");
}
@Test
@TestMetadata("nonExhaustiveDependentContext.kt")
public void testNonExhaustiveDependentContext() throws Exception {
runTest("compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.kt");
}
@Test
@TestMetadata("NonExhaustivePlatformEnum.kt")
public void testNonExhaustivePlatformEnum() throws Exception {
@@ -35152,6 +35152,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/when/NonExhaustiveBooleanNullable.kt");
}
@Test
@TestMetadata("nonExhaustiveDependentContext.kt")
public void testNonExhaustiveDependentContext() throws Exception {
runTest("compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.kt");
}
@Test
@TestMetadata("NonExhaustivePlatformEnum.kt")
public void testNonExhaustivePlatformEnum() throws Exception {
@@ -91,6 +91,9 @@ class FirWhenExhaustivenessTransformer(private val bodyResolveComponents: BodyRe
throw IllegalArgumentException("Should not be there")
}
/**
* The synthetic call for the whole [whenExpression] might be not completed yet
*/
override fun transformWhenExpression(whenExpression: FirWhenExpression, data: Any?): FirStatement {
processExhaustivenessCheck(whenExpression)
bodyResolveComponents.session.enumWhenTracker?.reportEnumUsageInWhen(
@@ -64,7 +64,7 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirAbstractBodyRes
@Suppress("NAME_SHADOWING")
var whenExpression = whenExpression.transformSubject(transformer, ResolutionMode.ContextIndependent)
val subjectType = whenExpression.subject?.typeRef?.coneType?.fullyExpandedType(session)
var callCompleted = false
var completionNeeded = false
context.withWhenSubjectType(subjectType, components) {
when {
whenExpression.branches.isEmpty() -> {}
@@ -78,19 +78,39 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirAbstractBodyRes
whenExpression = syntheticCallGenerator.generateCalleeForWhenExpression(whenExpression, resolutionContext) ?: run {
whenExpression = whenExpression.transformSingle(whenExhaustivenessTransformer, null)
dataFlowAnalyzer.exitWhenExpression(whenExpression, callCompleted)
dataFlowAnalyzer.exitWhenExpression(whenExpression, callCompleted = false)
whenExpression.resultType = buildErrorTypeRef {
diagnostic = ConeSimpleDiagnostic("Can't resolve when expression", DiagnosticKind.InferenceError)
}
return@withWhenSubjectType whenExpression
}
val completionResult = callCompleter.completeCall(whenExpression, data)
whenExpression = completionResult.result
callCompleted = completionResult.callCompleted
completionNeeded = true
}
}
whenExpression = whenExpression.transformSingle(whenExhaustivenessTransformer, null)
// This is necessary to perform outside the place where the synthetic call is created because
// exhaustiveness is not yet computed there, but at the same time to compute it properly
// we need having branches condition bes analyzed that is why we can't have call
// `whenExpression.transformSingle(whenExhaustivenessTransformer, null)` at the beginning
val callCompleted = when {
completionNeeded -> {
val completionResult = callCompleter.completeCall(
whenExpression,
// For non-exhaustive when expressions, we should complete then as independent because below
// their type is artificially replaced with Unit, while candidate symbol's return type remains the same
// So when combining two when's the inner one was erroneously resolved as a normal dependent exhaustive sub-expression
// At the same time, it all looks suspicious and inconsistent, so we hope it would be investigated at KT-55175
if (whenExpression.isProperlyExhaustive) data else ResolutionMode.ContextIndependent,
)
whenExpression = completionResult.result
completionResult.callCompleted
}
else -> false
}
dataFlowAnalyzer.exitWhenExpression(whenExpression, callCompleted)
whenExpression = whenExpression.replaceReturnTypeIfNotExhaustive()
whenExpression
@@ -0,0 +1,13 @@
// SKIP_TXT
fun bar(a: String): String {
return <!RETURN_TYPE_MISMATCH!>when {
a.length == 1 -> {
when { // Error in K1, no error in K2
a == "a" -> ""
a == "b" -> ""
}
}
else -> ""
}<!>
}
@@ -0,0 +1,13 @@
// SKIP_TXT
fun bar(a: String): String {
return when {
a.length == 1 -> {
<!NO_ELSE_IN_WHEN!>when<!> { // Error in K1, no error in K2
a == "a" -> ""
a == "b" -> ""
}
}
else -> ""
}
}
@@ -35248,6 +35248,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/when/NonExhaustiveBooleanNullable.kt");
}
@Test
@TestMetadata("nonExhaustiveDependentContext.kt")
public void testNonExhaustiveDependentContext() throws Exception {
runTest("compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.kt");
}
@Test
@TestMetadata("NonExhaustivePlatformEnum.kt")
public void testNonExhaustivePlatformEnum() throws Exception {