From 3e451c58110096942e29ebd1917aae2650ad5c95 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Mon, 24 Feb 2014 20:31:19 +0200 Subject: [PATCH] Disallow break or continue across a function boundary Code containing breaks or continues that attempt to jump across a function boundary weren't detected during analysis but would crash the compiler during code generation. Add diagnostics for these kinds of errors. Example: fun f() { while (true) { fun inner() { continue } } } #KT-4334 Fixed --- .../jet/lang/cfg/JetControlFlowProcessor.java | 12 ++++++++++++ .../jetbrains/jet/lang/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../jet/lang/resolve/BindingContextUtils.java | 5 +++++ .../jumpAcrossFunctionBoundary.kt | 17 +++++++++++++++++ .../pseudocodeTraverseNextInstructions.kt | 2 +- .../checkers/JetDiagnosticsTestGenerated.java | 5 +++++ 7 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.kt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java index 3656470ebca..481e14cee3f 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java @@ -636,6 +636,7 @@ public class JetControlFlowProcessor { public void visitBreakExpressionVoid(@NotNull JetBreakExpression expression, CFPContext context) { JetElement loop = getCorrespondingLoop(expression); if (loop != null) { + checkJumpDoesNotCrossFunctionBoundary(expression, loop); builder.jump(builder.getExitPoint(loop)); } } @@ -644,6 +645,7 @@ public class JetControlFlowProcessor { public void visitContinueExpressionVoid(@NotNull JetContinueExpression expression, CFPContext context) { JetElement loop = getCorrespondingLoop(expression); if (loop != null) { + checkJumpDoesNotCrossFunctionBoundary(expression, loop); builder.jump(builder.getEntryPoint(loop)); } } @@ -672,6 +674,16 @@ public class JetControlFlowProcessor { return loop; } + private void checkJumpDoesNotCrossFunctionBoundary(@NotNull JetLabelQualifiedExpression jumpExpression, @NotNull JetElement jumpTarget) { + BindingContext bindingContext = trace.getBindingContext(); + + FunctionDescriptor labelExprEnclosingFunc = BindingContextUtils.getEnclosingFunctionDescriptor(bindingContext, jumpExpression); + FunctionDescriptor labelTargetEnclosingFunc = BindingContextUtils.getEnclosingFunctionDescriptor(bindingContext, jumpTarget); + if (labelExprEnclosingFunc != labelTargetEnclosingFunc) { + trace.report(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY.on(jumpExpression)); + } + } + @Override public void visitReturnExpressionVoid(@NotNull JetReturnExpression expression, CFPContext context) { JetExpression returnedExpression = expression.getReturnedExpression(); 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 a9165da3e7d..45656172c98 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -431,6 +431,7 @@ public interface Errors { DiagnosticFactory0 AMBIGUOUS_LABEL = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 BREAK_OR_CONTINUE_OUTSIDE_A_LOOP = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 NOT_A_LOOP_LABEL = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 NOT_A_RETURN_LABEL = DiagnosticFactory1.create(ERROR); 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 e211addda96..2091e0fd379 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 @@ -327,6 +327,7 @@ public class DefaultErrorMessages { MAP.put(NO_TAIL_CALLS_FOUND, "A function is marked as tail-recursive but no tail calls are found"); MAP.put(VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION, "A type annotation is required on a value parameter"); MAP.put(BREAK_OR_CONTINUE_OUTSIDE_A_LOOP, "'break' and 'continue' are only allowed inside a loop"); + MAP.put(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY, "'break' or 'continue' jumps across a function boundary"); MAP.put(NOT_A_LOOP_LABEL, "The label ''{0}'' does not denote a loop", TO_STRING); MAP.put(NOT_A_RETURN_LABEL, "The label ''{0}'' does not reference to a context from which we can return", TO_STRING); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java index 1dedc0dee6f..ef98c328fe0 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java @@ -236,6 +236,11 @@ public class BindingContextUtils { return descriptor; } + public static FunctionDescriptor getEnclosingFunctionDescriptor(@NotNull BindingContext context, @NotNull JetElement element) { + JetFunction function = PsiTreeUtil.getParentOfType(element, JetFunction.class); + return (FunctionDescriptor)context.get(DECLARATION_TO_DESCRIPTOR, function); + } + public static void reportAmbiguousLabel( @NotNull BindingTrace trace, @NotNull JetSimpleNameExpression targetLabel, diff --git a/compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.kt b/compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.kt new file mode 100644 index 00000000000..b46f894c48c --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.kt @@ -0,0 +1,17 @@ +fun call(f: () -> Unit) = f() + +fun f1() { + @outer while (true) { + call { + break@outer + } + } +} + +fun f2() { + do { + fun inner() { + continue + } + } while (true) +} diff --git a/compiler/testData/diagnostics/tests/incompleteCode/pseudocodeTraverseNextInstructions.kt b/compiler/testData/diagnostics/tests/incompleteCode/pseudocodeTraverseNextInstructions.kt index 52db12e29b4..d83399b474a 100644 --- a/compiler/testData/diagnostics/tests/incompleteCode/pseudocodeTraverseNextInstructions.kt +++ b/compiler/testData/diagnostics/tests/incompleteCode/pseudocodeTraverseNextInstructions.kt @@ -3,6 +3,6 @@ package b fun foo() { for (i in collection) { { - break + break } } \ 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 4e1d965816b..b94c92e5ae0 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -1745,6 +1745,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest("compiler/testData/diagnostics/tests/controlStructures/ForbidStatementAsDirectFunctionBody.kt"); } + @TestMetadata("jumpAcrossFunctionBoundary.kt") + public void testJumpAcrossFunctionBoundary() throws Exception { + doTest("compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.kt"); + } + @TestMetadata("kt1075.kt") public void testKt1075() throws Exception { doTest("compiler/testData/diagnostics/tests/controlStructures/kt1075.kt");