diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java index 18bf40d9649..98416b61fea 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.cfg; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -701,10 +702,17 @@ public class ControlFlowInformationProvider { for (KtElement element : instruction.getOwner().getValueElements(value)) { if (!(element instanceof KtIfExpression)) continue; KtIfExpression ifExpression = (KtIfExpression) element; - if (ifExpression.getThen() != null && ifExpression.getElse() != null) continue; if (BindingContextUtilsKt.isUsedAsExpression(ifExpression, trace.getBindingContext())) { - trace.report(INVALID_IF_AS_EXPRESSION.on(ifExpression)); + KtExpression thenExpression = ifExpression.getThen(); + KtExpression elseExpression = ifExpression.getElse(); + + if (thenExpression == null || elseExpression == null) { + trace.report(INVALID_IF_AS_EXPRESSION.on(ifExpression)); + } + else { + checkImplicitCastOnConditionalExpression(ifExpression, ImmutableList.of(thenExpression, elseExpression)); + } } } } @@ -712,6 +720,31 @@ public class ControlFlowInformationProvider { ); } + private void checkImplicitCastOnConditionalExpression( + @NotNull KtExpression expression, + @NotNull Collection branchExpressions + ) { + KotlinType expectedExpressionType = trace.get(EXPECTED_EXPRESSION_TYPE, expression); + if (expectedExpressionType != null) return; + + KotlinType expressionType = trace.getType(expression); + if (expressionType == null) { + return; + } + if (KotlinBuiltIns.isAnyOrNullableAny(expressionType)) { + for (KtExpression branchExpression : branchExpressions) { + KotlinType branchType = trace.getType(branchExpression); + if (branchType == null || KotlinBuiltIns.isAnyOrNullableAny(branchType)) { + return; + } + } + trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType)); + } + else if (KotlinBuiltIns.isUnit(expressionType)) { + trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType)); + } + } + public void markWhenWithoutElse() { final Map> initializers = pseudocodeVariablesData.getVariableInitializers(); PseudocodeTraverserKt.traverse( @@ -741,6 +774,18 @@ public class ControlFlowInformationProvider { for (KtElement element : instruction.getOwner().getValueElements(value)) { if (!(element instanceof KtWhenExpression)) continue; KtWhenExpression whenExpression = (KtWhenExpression) element; + + if (BindingContextUtilsKt.isUsedAsExpression(whenExpression, trace.getBindingContext())) { + List branchExpressions = new ArrayList(whenExpression.getEntries().size() + 1); + for (KtWhenEntry whenEntry : whenExpression.getEntries()) { + branchExpressions.add(whenEntry.getExpression()); + } + if (whenExpression.getElseExpression() != null) { + branchExpressions.add(whenExpression.getElseExpression()); + } + checkImplicitCastOnConditionalExpression(whenExpression, branchExpressions); + } + if (whenExpression.getElseExpression() != null) continue; BindingContext context = trace.getBindingContext(); @@ -761,6 +806,8 @@ public class ControlFlowInformationProvider { } } } + + } } ); 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 4c7fb59f2d6..ffaa81a03f3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java @@ -91,6 +91,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { } public KotlinTypeInfo visitIfExpression(KtIfExpression ifExpression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) { + components.dataFlowAnalyzer.recordExpectedType(contextWithExpectedType.trace, ifExpression, contextWithExpectedType.expectedType); + ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE); KtExpression condition = ifExpression.getCondition(); DataFlowInfo conditionDataFlowInfo = checkCondition(context.scope, condition, context); @@ -112,12 +114,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { ? result.replaceJumpOutPossible(true).replaceJumpFlowInfo(conditionDataFlowInfo) : result; } - return TypeInfoFactoryKt.createTypeInfo(components.dataFlowAnalyzer.checkImplicitCast( - components.builtIns.getUnitType(), ifExpression, - contextWithExpectedType, isStatement - ), - thenInfo.or(elseInfo) - ); + return TypeInfoFactoryKt.createTypeInfo(components.builtIns.getUnitType(), thenInfo.or(elseInfo)); } if (thenBranch == null) { return getTypeInfoWhenOnlyOneBranchIsPresent( @@ -173,9 +170,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { } // If break or continue was possible, take condition check info as the jump info - return TypeInfoFactoryKt - .createTypeInfo(components.dataFlowAnalyzer.checkImplicitCast(resultType, ifExpression, contextWithExpectedType, isStatement), - resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo); + return TypeInfoFactoryKt.createTypeInfo(resultType, resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo); } @NotNull @@ -199,15 +194,10 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { } else { dataFlowInfo = typeInfo.getDataFlowInfo().or(otherInfo); } - return components.dataFlowAnalyzer.checkImplicitCast( - components.dataFlowAnalyzer.checkType( - typeInfo.replaceType(components.builtIns.getUnitType()), - ifExpression, - context - ), + return components.dataFlowAnalyzer.checkType( + typeInfo.replaceType(components.builtIns.getUnitType()), ifExpression, - context, - isStatement + context ).replaceDataFlowInfo(dataFlowInfo); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java index 9f416c970c5..4a37a625873 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java @@ -271,29 +271,6 @@ public class DataFlowAnalyzer { return builtIns.getUnitType(); } - @Nullable - public KotlinType checkImplicitCast(@Nullable KotlinType expressionType, @NotNull KtExpression expression, @NotNull ResolutionContext context, boolean isStatement) { - boolean isIfExpression = expression instanceof KtIfExpression; - if (expressionType != null - && (context.expectedType == NO_EXPECTED_TYPE || isIfExpression) - && context.contextDependency == INDEPENDENT && !isStatement - && (KotlinBuiltIns.isUnit(expressionType) || KotlinBuiltIns.isAnyOrNullableAny(expressionType)) - && !DynamicTypesKt.isDynamic(expressionType)) { - if (isIfExpression && KotlinBuiltIns.isUnit(expressionType) || isIfExpression && context.expectedType != NO_EXPECTED_TYPE) { - return expressionType; - } - else { - context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType)); - } - } - return expressionType; - } - - @NotNull - public KotlinTypeInfo checkImplicitCast(@NotNull KotlinTypeInfo typeInfo, @NotNull KtExpression expression, @NotNull ResolutionContext context, boolean isStatement) { - return typeInfo.replaceType(checkImplicitCast(typeInfo.getType(), expression, context, isStatement)); - } - @NotNull public KotlinTypeInfo illegalStatementType(@NotNull KtExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) { facade.checkStatementType( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java index ac142972e8b..1093eca5938 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java @@ -162,12 +162,9 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { if (isExhaustive && expression.getElseExpression() == null && KotlinBuiltIns.isNothing(resultType)) { context.trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression); } + resultType = components.dataFlowAnalyzer.checkType(resultType, expression, contextWithExpectedType); } - return TypeInfoFactoryKt.createTypeInfo(expressionTypes.isEmpty() ? null : components.dataFlowAnalyzer.checkType( - components.dataFlowAnalyzer.checkImplicitCast( - resultType, expression, - contextWithExpectedType, isStatement), - expression, contextWithExpectedType), + return TypeInfoFactoryKt.createTypeInfo(resultType, commonDataFlowInfo, loopBreakContinuePossible, contextWithExpectedType.dataFlowInfo); diff --git a/compiler/testData/diagnostics/tests/controlStructures/ifInResultOfLambda.kt b/compiler/testData/diagnostics/tests/controlStructures/ifInResultOfLambda.kt new file mode 100644 index 00000000000..021014c1597 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlStructures/ifInResultOfLambda.kt @@ -0,0 +1,7 @@ +val test1 = { if (true) 1 else "" } + +val test2 = { { if (true) 1 else "" } } + +val test3: (Boolean) -> Any = { if (it) 1 else "" } + +val test4: (Boolean) -> Any? = { if (it) 1 else "" } diff --git a/compiler/testData/diagnostics/tests/controlStructures/ifInResultOfLambda.txt b/compiler/testData/diagnostics/tests/controlStructures/ifInResultOfLambda.txt new file mode 100644 index 00000000000..8c178e5b829 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlStructures/ifInResultOfLambda.txt @@ -0,0 +1,6 @@ +package + +public val test1: () -> kotlin.Any +public val test2: () -> () -> kotlin.Any +public val test3: (kotlin.Boolean) -> kotlin.Any +public val test4: (kotlin.Boolean) -> kotlin.Any? diff --git a/compiler/testData/diagnostics/tests/controlStructures/improperElseInExpression.kt b/compiler/testData/diagnostics/tests/controlStructures/improperElseInExpression.kt index f50df7b02d7..90fc5ecfccf 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/improperElseInExpression.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/improperElseInExpression.kt @@ -17,12 +17,12 @@ fun example() { }(); { - if (true) {} else false + if (true) {} else false }(); { - if (true) true else {} + if (true) true else {} }() fun t(): Boolean { diff --git a/compiler/testData/diagnostics/tests/controlStructures/kt770.kt351.kt735_StatementType.kt b/compiler/testData/diagnostics/tests/controlStructures/kt770.kt351.kt735_StatementType.kt index a0c559f9690..a5d25e3c119 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/kt770.kt351.kt735_StatementType.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/kt770.kt351.kt735_StatementType.kt @@ -18,13 +18,13 @@ val w = while (true) {} fun foo() { var z = 2 - val r = { // type fun(): Int is inferred - if (true) { + val r = { // type fun(): Any is inferred + if (true) { 2 } else { z = 34 - } + } } val f: ()-> Int = r val g: ()-> Any = r @@ -73,11 +73,11 @@ fun testCoercionToUnit() { var x = 43 val checkType = { - if (true) { + if (true) { x = 4 } else { 45 - } + } } val f : () -> String = checkType } diff --git a/compiler/testData/diagnostics/tests/controlStructures/whenInResultOfLambda.kt b/compiler/testData/diagnostics/tests/controlStructures/whenInResultOfLambda.kt new file mode 100644 index 00000000000..92fbc2e2069 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlStructures/whenInResultOfLambda.kt @@ -0,0 +1,7 @@ +val test1 = { when (true) { true -> 1; else -> "" } } + +val test2 = { { when (true) { true -> 1; else -> "" } } } + +val test3: (Boolean) -> Any = { when (true) { true -> 1; else -> "" } } + +val test4: (Boolean) -> Any? = { when (true) { true -> 1; else -> "" } } diff --git a/compiler/testData/diagnostics/tests/controlStructures/whenInResultOfLambda.txt b/compiler/testData/diagnostics/tests/controlStructures/whenInResultOfLambda.txt new file mode 100644 index 00000000000..8c178e5b829 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlStructures/whenInResultOfLambda.txt @@ -0,0 +1,6 @@ +package + +public val test1: () -> kotlin.Any +public val test2: () -> () -> kotlin.Any +public val test3: (kotlin.Boolean) -> kotlin.Any +public val test4: (kotlin.Boolean) -> kotlin.Any? diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index b6bc17bf0a5..2bc30b4490c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3318,6 +3318,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("ifInResultOfLambda.kt") + public void testIfInResultOfLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/ifInResultOfLambda.kt"); + doTest(fileName); + } + @TestMetadata("ifWhenWithoutElse.kt") public void testIfWhenWithoutElse() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/ifWhenWithoutElse.kt"); @@ -3402,6 +3408,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("whenInResultOfLambda.kt") + public void testWhenInResultOfLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/whenInResultOfLambda.kt"); + doTest(fileName); + } + @TestMetadata("when.kt234.kt973.kt") public void testWhen_kt234_kt973() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/when.kt234.kt973.kt");