diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ClosureExpressionsTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ClosureExpressionsTypingVisitor.java index 6e5cc2a970e..9ac687dd994 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ClosureExpressionsTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ClosureExpressionsTypingVisitor.java @@ -303,54 +303,65 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor { : (expectedReturnType != null ? expectedReturnType : NO_EXPECTED_TYPE); ExpressionTypingContext newContext = context.replaceScope(functionInnerScope).replaceExpectedType(expectedType); context.trace.record(EXPECTED_RETURN_TYPE, functionLiteral, expectedType); + JetType typeOfBodyExpression = // needed for error reporting + components.expressionTypingServices.getBlockReturnedType(bodyExpression, COERCION_TO_UNIT, newContext).getType(); - JetType typeOfBodyExpression = components.expressionTypingServices.getBlockReturnedType(bodyExpression, COERCION_TO_UNIT, newContext).getType(); + if (declaredReturnType != null) { + return declaredReturnType; + } + else { + return computeReturnTypeBasedOnReturnExpressions(functionLiteral, context, typeOfBodyExpression); + } + } + + @Nullable + private JetType computeReturnTypeBasedOnReturnExpressions( + @NotNull JetFunctionLiteral functionLiteral, + @NotNull ExpressionTypingContext context, + @Nullable JetType typeOfBodyExpression + ) { + List returnedExpressionTypes = Lists.newArrayList(); + + boolean hasEmptyReturn = false; + Collection returnExpressions = collectReturns(functionLiteral, context.trace); + for (JetReturnExpression returnExpression : returnExpressions) { + JetExpression returnedExpression = returnExpression.getReturnedExpression(); + if (returnedExpression == null) { + hasEmptyReturn = true; + } + else { + // the type should have been computed by getBlockReturnedType() above, but can be null, if returnExpression contains some error + ContainerUtil.addIfNotNull(returnedExpressionTypes, context.trace.get(EXPRESSION_TYPE, returnedExpression)); + } + } + + if (hasEmptyReturn) { + for (JetReturnExpression returnExpression : returnExpressions) { + JetExpression returnedExpression = returnExpression.getReturnedExpression(); + if (returnedExpression != null) { + JetType type = context.trace.get(EXPRESSION_TYPE, returnedExpression); + if (type == null || !KotlinBuiltIns.isUnit(type)) { + context.trace.report(RETURN_TYPE_MISMATCH.on(returnedExpression, components.builtIns.getUnitType())); + } + } + } + return components.builtIns.getUnitType(); + } - List returnedExpressionTypes = Lists.newArrayList(getTypesOfLocallyReturnedExpressions( - functionLiteral, context.trace, collectReturns(bodyExpression))); ContainerUtil.addIfNotNull(returnedExpressionTypes, typeOfBodyExpression); - if (declaredReturnType != null) return declaredReturnType; if (returnedExpressionTypes.isEmpty()) return null; return CommonSupertypes.commonSupertype(returnedExpressionTypes); } - private List getTypesOfLocallyReturnedExpressions( - final JetFunctionLiteral functionLiteral, - final BindingTrace trace, - Collection returnExpressions + private static Collection collectReturns( + @NotNull final JetFunctionLiteral functionLiteral, + @NotNull final BindingTrace trace ) { - return ContainerUtil.mapNotNull(returnExpressions, new Function() { - @Override - public JetType fun(JetReturnExpression returnExpression) { - JetSimpleNameExpression label = returnExpression.getTargetLabel(); - if (label == null) { - // No label => non-local return - return null; - } - - PsiElement labelTarget = trace.get(BindingContext.LABEL_TARGET, label); - if (labelTarget != functionLiteral) { - // Either a local return of inner lambda/function or a non-local return - return null; - } - - JetExpression returnedExpression = returnExpression.getReturnedExpression(); - if (returnedExpression == null) { - return components.builtIns.getUnitType(); - } - JetType returnedType = trace.get(EXPRESSION_TYPE, returnedExpression); - assert returnedType != null : "No type for returned expression: " + returnedExpression + ",\n" + - "the type should have been computed by getBlockReturnedType() above\n" + - JetPsiUtil.getElementTextWithContext(returnedExpression); - return returnedType; - } - }); - } - - public static Collection collectReturns(@NotNull JetExpression expression) { Collection result = Lists.newArrayList(); - expression.accept( + JetBlockExpression bodyExpression = functionLiteral.getBodyExpression(); + assert bodyExpression != null; + bodyExpression.accept( new JetTreeVisitor>() { @Override public Void visitReturnExpression( @@ -362,6 +373,22 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor { }, result ); - return result; + return ContainerUtil.mapNotNull(result, new Function() { + @Override + public JetReturnExpression fun(@NotNull JetReturnExpression returnExpression) { + JetSimpleNameExpression label = returnExpression.getTargetLabel(); + if (label == null) { + // No label => non-local return + return null; + } + + PsiElement labelTarget = trace.get(BindingContext.LABEL_TARGET, label); + if (labelTarget != functionLiteral) { + // Either a local return of inner lambda/function or a non-local return + return null; + } + return returnExpression; + } + }); } } 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 4f2c1530131..7c17bfbc858 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java @@ -47,6 +47,7 @@ import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory; import static org.jetbrains.kotlin.resolve.BindingContext.*; import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT; import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE; +import static org.jetbrains.kotlin.types.TypeUtils.isDontCarePlaceholder; import static org.jetbrains.kotlin.types.TypeUtils.noExpectedType; import static org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.createCallForSpecialConstruction; import static org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.createDataFlowInfoForArgumentsForIfCall; @@ -492,7 +493,11 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { .replaceContextDependency(INDEPENDENT)); } else { - if (expectedType != null && !noExpectedType(expectedType) && !KotlinBuiltIns.isUnit(expectedType)) { + if (expectedType != null && + !noExpectedType(expectedType) && + !KotlinBuiltIns.isUnit(expectedType) && + !isDontCarePlaceholder(expectedType)) // for lambda with implicit return type Unit + { context.trace.report(RETURN_TYPE_MISMATCH.on(expression, expectedType)); } } diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnNoCoercionToUnit.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnNoCoercionToUnit.kt index 8d2c916db69..59a328d0f4f 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnNoCoercionToUnit.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnNoCoercionToUnit.kt @@ -1,7 +1,7 @@ fun test(a: Int) { run @f{ - if (a > 0) return@f - else return@f 1 + if (a > 0) return@f + else return@f 1 } } diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnSecondUnit.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnSecondUnit.kt new file mode 100644 index 00000000000..c3e99267d8c --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnSecondUnit.kt @@ -0,0 +1,6 @@ +val flag = true + +val a = @b { + if (flag) return@b 4 + return@b +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnSecondUnit.txt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnSecondUnit.txt new file mode 100644 index 00000000000..45e3b0af842 --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnSecondUnit.txt @@ -0,0 +1,4 @@ +package + +internal val a: () -> kotlin.Unit +internal val flag: kotlin.Boolean = true diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnit.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnit.kt index 6b1b30c2162..2cdd68581dd 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnit.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnit.kt @@ -1,6 +1,6 @@ fun test(a: Int) { val x = run @f{ - if (a > 0) return@f + if (a > 0) return@f else return@f Unit } x: Unit diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.kt new file mode 100644 index 00000000000..8785f484a42 --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.kt @@ -0,0 +1,20 @@ +val flag = true +fun run(f: () -> T): T { return f() } + +// type of a was checked by txt +val a = run { // () -> Unit + return@run +} + +// Unit +val b = run { + if (flag) return@run + 5 +} + +// Unit +val c = run { + if (flag) return@run + + return@run 4 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.txt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.txt new file mode 100644 index 00000000000..910ad2c7159 --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.txt @@ -0,0 +1,7 @@ +package + +internal val a: kotlin.Unit +internal val b: kotlin.Unit +internal val c: kotlin.Unit +internal val flag: kotlin.Boolean = true +internal fun run(/*0*/ f: () -> T): T diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitWithBodyExpression.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitWithBodyExpression.kt new file mode 100644 index 00000000000..ddb57af7c65 --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitWithBodyExpression.kt @@ -0,0 +1,7 @@ +val flag = true + +// type of lambda was checked by txt +val a = @b { // () -> Unit + if (flag) return@b + else 54 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitWithBodyExpression.txt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitWithBodyExpression.txt new file mode 100644 index 00000000000..45e3b0af842 --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitWithBodyExpression.txt @@ -0,0 +1,4 @@ +package + +internal val a: () -> kotlin.Unit +internal val flag: kotlin.Boolean = true diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnWithExplicitUnit.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnWithExplicitUnit.kt new file mode 100644 index 00000000000..069ca643919 --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnWithExplicitUnit.kt @@ -0,0 +1,16 @@ +val flag = true + +// type of lambda was checked by txt +val a = @l { // () -> Any + if (flag) return@l 4 + return@l Unit +} + +val b = @l { // () -> Any + if (flag) return@l Unit + 5 +} + +val c = @l { // () -> Unit + if (flag) return@l Unit +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnWithExplicitUnit.txt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnWithExplicitUnit.txt new file mode 100644 index 00000000000..ff8ba09b62f --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnWithExplicitUnit.txt @@ -0,0 +1,6 @@ +package + +internal val a: () -> kotlin.Any +internal val b: () -> kotlin.Any +internal val c: () -> kotlin.Unit +internal val flag: kotlin.Boolean = true diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/unresolvedReferenceInReturnBlock.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/unresolvedReferenceInReturnBlock.kt new file mode 100644 index 00000000000..fd0595e1932 --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/unresolvedReferenceInReturnBlock.kt @@ -0,0 +1,9 @@ +val a = @l { + return@l r +} + +val b = @l { + if ("" == "OK") return@l + + return@l r +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/unresolvedReferenceInReturnBlock.txt b/compiler/testData/diagnostics/tests/functionLiterals/return/unresolvedReferenceInReturnBlock.txt new file mode 100644 index 00000000000..82b93afa25e --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/unresolvedReferenceInReturnBlock.txt @@ -0,0 +1,4 @@ +package + +internal val a: () -> kotlin.Nothing +internal val b: () -> kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 17884fd2185..ad1591b8a61 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -4697,18 +4697,42 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("LocalReturnSecondUnit.kt") + public void testLocalReturnSecondUnit() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnSecondUnit.kt"); + doTest(fileName); + } + @TestMetadata("LocalReturnUnit.kt") public void testLocalReturnUnit() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnit.kt"); doTest(fileName); } + @TestMetadata("LocalReturnUnitAndDontCareType.kt") + public void testLocalReturnUnitAndDontCareType() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.kt"); + doTest(fileName); + } + + @TestMetadata("LocalReturnUnitWithBodyExpression.kt") + public void testLocalReturnUnitWithBodyExpression() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitWithBodyExpression.kt"); + doTest(fileName); + } + @TestMetadata("LocalReturnWithExpectedType.kt") public void testLocalReturnWithExpectedType() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnWithExpectedType.kt"); doTest(fileName); } + @TestMetadata("LocalReturnWithExplicitUnit.kt") + public void testLocalReturnWithExplicitUnit() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnWithExplicitUnit.kt"); + doTest(fileName); + } + @TestMetadata("LocalReturnsWithExplicitReturnType.kt") public void testLocalReturnsWithExplicitReturnType() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnsWithExplicitReturnType.kt"); @@ -4738,6 +4762,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/SmartCastWithExplicitType.kt"); doTest(fileName); } + + @TestMetadata("unresolvedReferenceInReturnBlock.kt") + public void testUnresolvedReferenceInReturnBlock() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/unresolvedReferenceInReturnBlock.kt"); + doTest(fileName); + } } }