diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index 170bdb50f36..39a90981068 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -535,6 +535,7 @@ public interface Errors { DiagnosticFactory0 EXPRESSION_EXPECTED_NAMESPACE_FOUND = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 RETURN_NOT_ALLOWED = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY = DiagnosticFactory0.create(ERROR, DECLARATION_WITH_BODY); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java index babddf27d93..532fb717c1d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java @@ -83,6 +83,7 @@ public class DefaultErrorMessages { MAP.put(TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM, "Type checking has run into a recursive problem. Easiest workaround: specify types of your declarations explicitly"); // TODO: message MAP.put(RETURN_NOT_ALLOWED, "'return' is not allowed here"); + MAP.put(RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED, "'return' is only allowed in function literals that have return types specified explicitly"); MAP.put(PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE, "Projections are not allowed for immediate arguments of a supertype"); MAP.put(LABEL_NAME_CLASH, "There is more than one label with such a name in this scope"); MAP.put(EXPRESSION_EXPECTED_NAMESPACE_FOUND, "Expression expected, but a namespace name found"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java index 770728bd5f4..40808f2a796 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java @@ -282,6 +282,8 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor { JetType declaredReturnType = null; if (returnTypeRef != null) { declaredReturnType = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, returnTypeRef, context.trace, true); + // This is needed for ControlStructureTypingVisitor#visitReturnExpression() to properly type-check returned expressions + functionDescriptor.setReturnType(declaredReturnType); if (expectedReturnType != null) { if (!JetTypeChecker.INSTANCE.isSubtypeOf(declaredReturnType, expectedReturnType)) { temporaryTrace.report(EXPECTED_RETURN_TYPE_MISMATCH.on(returnTypeRef, expectedReturnType)); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java index 78ab6045277..5f3c9d447db 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java @@ -58,6 +58,8 @@ import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.*; public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { + public static final String RETURN_NOT_ALLOWED_MESSAGE = "Return not allowed"; + protected ControlStructureTypingVisitor(@NotNull ExpressionTypingInternals facade) { super(facade); } @@ -504,7 +506,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { } while (containingFunction instanceof JetFunctionLiteral); // Unqualified, in a function literal context.trace.report(RETURN_NOT_ALLOWED.on(expression)); - resultType = ErrorUtils.createErrorType("Return not allowed"); + resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE); } if (containingFunctionDescriptor != null) { expectedType = DescriptorUtils.getFunctionExpectedReturnType(containingFunctionDescriptor, (JetElement) containingFunction); @@ -513,17 +515,24 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { else { // Outside a function context.trace.report(RETURN_NOT_ALLOWED.on(expression)); - resultType = ErrorUtils.createErrorType("Return not allowed"); + resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE); } } else if (labelTargetElement != null) { SimpleFunctionDescriptor functionDescriptor = context.trace.get(FUNCTION, labelTargetElement); if (functionDescriptor != null) { expectedType = DescriptorUtils.getFunctionExpectedReturnType(functionDescriptor, labelTargetElement); - if (functionDescriptor != containingFunctionDescriptor) { + boolean inLambdaWithNoExplicitType = expectedType == TypeUtils.NO_EXPECTED_TYPE; + if (inLambdaWithNoExplicitType) { + // expectedType is NO_EXPECTED_TYPE iff the return type of the corresponding function descriptor is not computed yet + // our temporary policy is to prohibit returns in this case. It mostly applies to local returns in lambdas + context.trace.report(RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED.on(expression)); + resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE); + } + else if (functionDescriptor != containingFunctionDescriptor) { // Qualified, non-local context.trace.report(RETURN_NOT_ALLOWED.on(expression)); - resultType = ErrorUtils.createErrorType("Return not allowed"); + resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE); } } else { diff --git a/compiler/testData/diagnostics/tests/FunctionReturnTypes.kt b/compiler/testData/diagnostics/tests/FunctionReturnTypes.kt index 1054a4828e3..a93cc6b6063 100644 --- a/compiler/testData/diagnostics/tests/FunctionReturnTypes.kt +++ b/compiler/testData/diagnostics/tests/FunctionReturnTypes.kt @@ -6,10 +6,10 @@ fun unitEmptyReturn() : Unit {return} fun unitIntReturn() : Unit {return 1} fun unitUnitReturn() : Unit {return Unit.VALUE} fun test1() : Any = {return} -fun test2() : Any = @a {return@a 1} +fun test2() : Any = @a {return@a 1} fun test3() : Any { return } fun test4(): ()-> Unit = { return@test4 } -fun test5(): Any = @{ return@ } +fun test5(): Any = @{ return@ } fun test6(): Any = {return 1} fun bbb() { diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalAndNonLocalReturnInLambda.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalAndNonLocalReturnInLambda.kt index bce3c7a961a..2141d0cdbc4 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalAndNonLocalReturnInLambda.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalAndNonLocalReturnInLambda.kt @@ -1,7 +1,7 @@ fun test2(a: Int) { (run @f{ if (a > 0) return - return@f 1 + return@f 1 }): Int } diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnExplicitLabelNoParens.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnExplicitLabelNoParens.kt index ec6760299be..ff517b875f1 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnExplicitLabelNoParens.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnExplicitLabelNoParens.kt @@ -1,5 +1,5 @@ fun test2() { - (run @f{return@f 1}): Int + (run @f{return@f 1}): Int } fun run(f: () -> T): T { return f() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnExplicitLabelParens.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnExplicitLabelParens.kt index edc92bb1971..e9fbb39b72a 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnExplicitLabelParens.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnExplicitLabelParens.kt @@ -1,10 +1,10 @@ fun test() { - run(@f{return@f 1}): Int + run(@f{return@f 1}): Int } fun test1() { - run(@{return@ 1}): Int + run(@{return@ 1}): Int } fun run(f: () -> T): T { return f() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnInNestedFunction.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnInNestedFunction.kt index 8a4bba7bb27..20b4c2f540c 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnInNestedFunction.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnInNestedFunction.kt @@ -4,7 +4,7 @@ fun test() { if (a > 0) return "2" return@local "3" } - return@f 1 + return@f 1 }): Int } diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnInNestedLambda.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnInNestedLambda.kt index 2fa1a8975d3..e435d501e08 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnInNestedLambda.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnInNestedLambda.kt @@ -1,9 +1,9 @@ fun test() { (run @f{ run @ff { - return@ff "2" + return@ff "2" } - return@f 1 + return@f 1 }): Int } diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnNoCoercionToUnit.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnNoCoercionToUnit.kt index f7e24127b6a..510f53196f6 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/LocalReturnUnit.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnit.kt index ddc0329d4b8..ac427a12443 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnit.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnit.kt @@ -1,7 +1,7 @@ fun test(a: Int) { (run @f{ - if (a > 0) return@f - else return@f Unit.VALUE + if (a > 0) return@f + else return@f Unit.VALUE }): Unit } diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnsWithExplicitReturnType.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnsWithExplicitReturnType.kt new file mode 100644 index 00000000000..1ba557e941e --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnsWithExplicitReturnType.kt @@ -0,0 +1,11 @@ +fun test(a: Int) { + run @f{ (): Int -> + if (a > 0) return@f "" + return@f 1 + } + + run { (): Int -> "" } + run { (): Int -> 1 } +} + +fun run(f: () -> T): T { return f() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/MixedReturnsFromLambda.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/MixedReturnsFromLambda.kt index 397dee4263d..c677309489a 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/MixedReturnsFromLambda.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/MixedReturnsFromLambda.kt @@ -5,7 +5,7 @@ trait C: A fun test(a: C, b: B) { (run @f{ - if (a != b) return@f a + if (a != b) return@f a b }): A } diff --git a/compiler/testData/diagnostics/tests/regressions/kt411.kt b/compiler/testData/diagnostics/tests/regressions/kt411.kt index 20764624fe8..7c0fd28b775 100644 --- a/compiler/testData/diagnostics/tests/regressions/kt411.kt +++ b/compiler/testData/diagnostics/tests/regressions/kt411.kt @@ -5,7 +5,7 @@ package kt411 fun f() { invoker( @{ - return@ 11 // expects Function, but should expect Int + return@ 11 // expects Function, but should expect Int } ) } @@ -21,7 +21,7 @@ fun t1() { fun t2() : String { val g : ()-> Int = @{ if (true) { - return@ 1 + return@ 1 } return "s" } @@ -37,7 +37,7 @@ fun t3() : String { else { return 2 } - return@ 0 + return@ 0 } ) invoker( @@ -55,7 +55,7 @@ fun t3() : String { fun t4() : Int { val h : ()-> String = @l{ - return@l "a" + return@l "a" } val g : ()-> String = @{ () : String -> return@ "a" @@ -66,4 +66,4 @@ fun t4() : Int { } return 12 -} +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index d455b0d805a..6c60028a515 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -2472,6 +2472,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage doTest("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnit.kt"); } + @TestMetadata("LocalReturnsWithExplicitReturnType.kt") + public void testLocalReturnsWithExplicitReturnType() throws Exception { + doTest("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnsWithExplicitReturnType.kt"); + } + @TestMetadata("MixedReturnsFromLambda.kt") public void testMixedReturnsFromLambda() throws Exception { doTest("compiler/testData/diagnostics/tests/functionLiterals/return/MixedReturnsFromLambda.kt");