diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 43a608abda7..64760e040f6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -1175,7 +1175,10 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { DataFlowInfo dataFlowInfo = resolvedCall.getDataFlowInfoForArguments().getInfo(call.getValueArguments().get(1)); KotlinType type = resolvedCall.getResultingDescriptor().getReturnType(); - if (type == null || rightType == null) return TypeInfoFactoryKt.noTypeInfo(dataFlowInfo); + if (type == null || + rightType == null || + leftType == null && KotlinBuiltIns.isNothing(rightType)) return TypeInfoFactoryKt.noTypeInfo(dataFlowInfo); + if (leftType != null) { DataFlowValue leftValue = createDataFlowValue(left, leftType, context); DataFlowInfo rightDataFlowInfo = resolvedCall.getDataFlowInfoForArguments().getResultInfo(); @@ -1197,7 +1200,6 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } } - // Sometimes return type for special call for elvis operator might be nullable, // but result is not nullable if the right type is not nullable if (!TypeUtils.isNullableType(rightType) && TypeUtils.isNullableType(type)) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java index 09883aa2655..2ccf238c34e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java @@ -179,6 +179,10 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { else { resultDataFlowInfo = thenDataFlowInfo.or(elseDataFlowInfo); } + if (thenType == null && jumpInElse || + elseType == null && jumpInThen) { + return TypeInfoFactoryKt.noTypeInfo(resultDataFlowInfo); + } } // If break or continue was possible, take condition check info as the jump info return TypeInfoFactoryKt.createTypeInfo( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt index 87ecf3c022a..d5f2479d1e0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.* import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo +import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo import java.util.* class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTypingInternals) : ExpressionTypingVisitor(facade) { @@ -87,29 +88,29 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping val whenReturnType = inferTypeForWhenExpression(expression, contextWithExpectedType, contextAfterSubject, dataFlowInfoForEntries) val whenResultValue = whenReturnType?.let { DataFlowValueFactory.createDataFlowValue(expression, it, contextAfterSubject) } - val (outputDataFlowInfo, jumpOutPossible) = - joinWhenExpressionBranches(expression, contextAfterSubject, jumpOutPossibleInSubject, whenResultValue) + val branchesTypeInfo = + joinWhenExpressionBranches(expression, contextAfterSubject, whenReturnType, jumpOutPossibleInSubject, whenResultValue) val isExhaustive = WhenChecker.isWhenExhaustive(expression, trace) + val branchesDataFlowInfo = branchesTypeInfo.dataFlowInfo val resultDataFlowInfo = if (expression.elseExpression == null && !isExhaustive) { // Without else expression in non-exhaustive when, we *must* take initial data flow info into account, // because data flow can bypass all when branches in this case - outputDataFlowInfo.or(contextAfterSubject.dataFlowInfo) + branchesDataFlowInfo.or(contextAfterSubject.dataFlowInfo) } else { - outputDataFlowInfo + branchesDataFlowInfo } if (whenReturnType != null && isExhaustive && expression.elseExpression == null && KotlinBuiltIns.isNothing(whenReturnType)) { trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression) } - val resultType = whenReturnType?.let { - components.dataFlowAnalyzer.checkType(it, expression, contextWithExpectedType) - } + val branchesType = branchesTypeInfo.type ?: return noTypeInfo(resultDataFlowInfo) + val resultType = components.dataFlowAnalyzer.checkType(branchesType, expression, contextWithExpectedType) - return createTypeInfo(resultType, resultDataFlowInfo, jumpOutPossible, contextWithExpectedType.dataFlowInfo) + return createTypeInfo(resultType, resultDataFlowInfo, branchesTypeInfo.jumpOutPossible, contextWithExpectedType.dataFlowInfo) } private fun inferTypeForWhenExpression( @@ -172,19 +173,24 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping private fun joinWhenExpressionBranches( expression: KtWhenExpression, contextAfterSubject: ExpressionTypingContext, + resultType: KotlinType?, jumpOutPossibleInSubject: Boolean, whenResultValue: DataFlowValue? - ): Pair { + ): KotlinTypeInfo { val bindingContext = contextAfterSubject.trace.bindingContext var currentDataFlowInfo: DataFlowInfo? = null var jumpOutPossible = jumpOutPossibleInSubject + var errorTypeExistInBranch = false for (whenEntry in expression.entries) { val entryExpression = whenEntry.expression ?: continue val entryTypeInfo = BindingContextUtils.getRecordedTypeInfo(entryExpression, bindingContext) ?: continue val entryType = entryTypeInfo.type + if (entryType == null) { + errorTypeExistInBranch = true + } val entryDataFlowInfo = if (whenResultValue != null && entryType != null) { @@ -206,7 +212,11 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping jumpOutPossible = jumpOutPossible or entryTypeInfo.jumpOutPossible } - return Pair(currentDataFlowInfo ?: contextAfterSubject.dataFlowInfo, jumpOutPossible) + val resultDataFlowInfo = currentDataFlowInfo ?: contextAfterSubject.dataFlowInfo + return if (resultType == null || errorTypeExistInBranch && KotlinBuiltIns.isNothing(resultType)) + noTypeInfo(resultDataFlowInfo) + else + createTypeInfo(resultType, resultDataFlowInfo, jumpOutPossible, resultDataFlowInfo) } private fun checkSmartCastsInSubjectIfRequired( diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt index 15d0335e02e..0928e1713c1 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt @@ -9,7 +9,7 @@ else { { true } ?: null!! } -val ww = if (true) { +val ww = if (true) { { true } ?: null!! } else if (true) { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.txt index bd614b3d9bd..a3fe50a1be6 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.txt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.txt @@ -7,6 +7,6 @@ public val bbbb: (() -> kotlin.Boolean)? public val n: kotlin.Nothing public val v: () -> kotlin.Boolean public val w: () -> kotlin.Boolean -public val ww: kotlin.Nothing +public val ww: ??? public fun f(/*0*/ x: kotlin.Long?): kotlin.Long public fun l(): (() -> kotlin.Boolean)? diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/unresolvedReference.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/unresolvedReference.kt new file mode 100644 index 00000000000..13ce6a2d403 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/unresolvedReference.kt @@ -0,0 +1,48 @@ +// See KT-6665: unresolved reference (v.bar) should not produce "unreachable code" after it +fun foo(): Int { + val v = 1 + val c = v.bar ?: return 0 + return 42 +} + +fun foo2(): Int { + val v = 1 + val c = if (true) v.bar else return 3 + val b = c + return 42 +} + +fun foo3(): Int { + val v = 1 + val c = when { + true -> v.bar + else -> return 3 + } + val b = c + return 42 +} + +// Type + ErrorType should give Type, unless Type is Nothing + +fun bar(): Int { + val v = 1 + val c = v.bar ?: 42 + return c +} + +fun bar2(): Int { + val v = 1 + val c = if (true) v.bar else 3 + val b = c + return b +} + +fun bar3(): Int { + val v = 1 + val c = when { + true -> v.bar + else -> 3 + } + val b = c + return b +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/unresolvedReference.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/unresolvedReference.txt new file mode 100644 index 00000000000..24703fc597a --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/unresolvedReference.txt @@ -0,0 +1,8 @@ +package + +public fun bar(): kotlin.Int +public fun bar2(): kotlin.Int +public fun bar3(): kotlin.Int +public fun foo(): kotlin.Int +public fun foo2(): kotlin.Int +public fun foo3(): kotlin.Int diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 6290e11ce23..8cf0899931f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3453,6 +3453,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("unresolvedReference.kt") + public void testUnresolvedReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unresolvedReference.kt"); + doTest(fileName); + } + @TestMetadata("useUninitializedInLambda.kt") public void testUseUninitializedInLambda() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/useUninitializedInLambda.kt");