Local returns are only allowed with explicitly specified return types

This is a temporary limitation: otherwise type inference is having trouble to account for all the returned expressions.
 We will fix the inference and remove the limitation
This commit is contained in:
Andrey Breslav
2013-08-21 18:05:16 +04:00
parent d97506476f
commit 08625a6b2f
16 changed files with 52 additions and 23 deletions
@@ -535,6 +535,7 @@ public interface Errors {
DiagnosticFactory0<JetSimpleNameExpression> EXPRESSION_EXPECTED_NAMESPACE_FOUND = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<JetReturnExpression> RETURN_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<JetReturnExpression> RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<JetReturnExpression> RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<JetDeclarationWithBody>
NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY = DiagnosticFactory0.create(ERROR, DECLARATION_WITH_BODY);
@@ -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");
@@ -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));
@@ -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 {
@@ -6,10 +6,10 @@ fun unitEmptyReturn() : Unit {return}
fun unitIntReturn() : Unit {return <!TYPE_MISMATCH!>1<!>}
fun unitUnitReturn() : Unit {return Unit.VALUE}
fun test1() : Any = {<!RETURN_NOT_ALLOWED, RETURN_TYPE_MISMATCH!>return<!>}
fun test2() : Any = @a {return@a 1}
fun test2() : Any = @a {<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@a 1<!>}
fun test3() : Any { <!RETURN_TYPE_MISMATCH!>return<!> }
fun test4(): ()-> Unit = { <!RETURN_NOT_ALLOWED, RETURN_TYPE_MISMATCH!>return@test4<!> }
fun test5(): Any = @{ return@ }
fun test5(): Any = @{ <!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@<!> }
fun test6(): Any = {<!RETURN_NOT_ALLOWED!>return 1<!>}
fun bbb() {
@@ -1,7 +1,7 @@
fun test2(a: Int) {
(run @f{
if (a > 0) <!RETURN_NOT_ALLOWED!>return<!>
return@f 1
<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@f 1<!>
}): Int
}
@@ -1,5 +1,5 @@
fun test2() {
(run @f{return@f 1}): Int
(run @f{<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@f 1<!>}): Int
}
fun run<T>(f: () -> T): T { return f() }
@@ -1,10 +1,10 @@
fun test() {
run(@f{return@f 1}): Int
run(@f{<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@f 1<!>}): Int
}
fun test1() {
run(@{return@ 1}): Int
run(@{<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@ 1<!>}): Int
}
fun run<T>(f: () -> T): T { return f() }
@@ -4,7 +4,7 @@ fun test() {
if (a > 0) return "2"
return@local "3"
}
return@f 1
<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@f 1<!>
}): Int
}
@@ -1,9 +1,9 @@
fun test() {
(run @f{
run @ff {
return@ff "2"
<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@ff "2"<!>
}
return@f 1
<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@f 1<!>
}): Int
}
@@ -1,7 +1,7 @@
fun test(a: Int) {
run @f{
if (a > 0) return@f
else return@f 1
if (a > 0) <!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@f<!>
else <!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@f 1<!>
}
}
@@ -1,7 +1,7 @@
fun test(a: Int) {
(run @f{
if (a > 0) return@f
else return@f Unit.VALUE
if (a > 0) <!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@f<!>
else <!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@f Unit.VALUE<!>
}): Unit
}
@@ -0,0 +1,11 @@
fun test(a: Int) {
run @f{ (): Int ->
if (a > 0) return@f <!TYPE_MISMATCH!>""<!>
return@f 1
}
run { (): Int -> <!TYPE_MISMATCH!>""<!> }
run { (): Int -> 1 }
}
fun run<T>(f: () -> T): T { return f() }
@@ -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_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@f a<!>
b
}): A
}
@@ -5,7 +5,7 @@ package kt411
fun f() {
invoker(
@{
return@ 11 // expects Function, but should expect Int
<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@ 11<!> // expects Function, but should expect Int
}
)
}
@@ -21,7 +21,7 @@ fun t1() {
fun t2() : String {
val <!UNUSED_VARIABLE!>g<!> : ()-> Int = @{
if (true) {
return@ 1
<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@ 1<!>
}
<!RETURN_NOT_ALLOWED!>return "s"<!>
}
@@ -37,7 +37,7 @@ fun t3() : String {
else {
<!RETURN_NOT_ALLOWED!>return <!ERROR_COMPILE_TIME_VALUE!>2<!><!>
}
return@ 0
<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@ 0<!>
}
)
invoker(
@@ -55,7 +55,7 @@ fun t3() : String {
fun t4() : Int {
val <!UNUSED_VARIABLE!>h<!> : ()-> String = @l{
return@l "a"
<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@l "a"<!>
}
val <!UNUSED_VARIABLE!>g<!> : ()-> String = @{ () : String ->
return@ "a"
@@ -66,4 +66,4 @@ fun t4() : Int {
}
return 12
}
}
@@ -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");