diff --git a/NEWS.md b/NEWS.md index 2cec3a6092db9..232f7614f2316 100644 --- a/NEWS.md +++ b/NEWS.md @@ -55,6 +55,8 @@ New language features * Improved reporting of syntax errors ([#6179]) + * `break` inside a `for` loop with multiple ranges now exits the entire loop nest ([#5154]) + REPL improvements ----------------- diff --git a/doc/manual/control-flow.rst b/doc/manual/control-flow.rst index b1f1a24d49e66..93b24cab61635 100644 --- a/doc/manual/control-flow.rst +++ b/doc/manual/control-flow.rst @@ -514,6 +514,9 @@ forming the cartesian product of its iterables: (2,3) (2,4) +A ``break`` statement inside such a loop exits the entire nest of loops, +not just the inner one. + .. _man-exception-handling: Exception Handling diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 549b02f035bb7..06872168082ba 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -1016,10 +1016,9 @@ (let* ((ranges (parse-comma-separated-iters s)) (body (parse-block s))) (expect-end s) - (let nest ((r ranges)) - (if (null? r) - body - `(for ,(car r) ,(nest (cdr r))))))) + `(for ,(if (length= ranges 1) (car ranges) (cons 'block ranges)) + ,body))) + ((if) (let* ((test (parse-cond s)) (then (if (memq (require-token s) '(else elseif)) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 60e34df9f8a45..f55eb22cae7f2 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1494,6 +1494,24 @@ (cadr e) (caddr e)))) +(define (expand-for while lhs X body) + ;; (for (= lhs X) body) + (let ((coll (gensy)) + (state (gensy))) + `(scope-block + (block (= ,coll ,(expand-forms X)) + (= ,state (call (top start) ,coll)) + ,(expand-forms + `(,while + (call (top !) (call (top done) ,coll ,state)) + (scope-block + (block + ;; NOTE: enable this to force loop-local var + #;,@(map (lambda (v) `(local ,v)) (lhs-vars lhs)) + ,(lower-tuple-assignment (list lhs state) + `(call (top next) ,coll ,state)) + ,body)))))))) + (define (map-expand-forms e) (map expand-forms e)) (define (expand-forms e) @@ -1793,6 +1811,13 @@ (break-block loop-cont ,(expand-forms (caddr e))))))) + 'inner-while + (lambda (e) + `(scope-block + (_while ,(expand-forms (cadr e)) + (break-block loop-cont + ,(expand-forms (caddr e)))))) + 'break (lambda (e) (if (pair? (cdr e)) @@ -1803,25 +1828,16 @@ 'for (lambda (e) - (let ((X (caddr (cadr e))) - (lhs (cadr (cadr e))) - (body (caddr e))) - ;; (for (= lhs X) body) - (let ((coll (gensy)) - (state (gensy))) - `(scope-block - (block (= ,coll ,(expand-forms X)) - (= ,state (call (top start) ,coll)) - ,(expand-forms - `(while - (call (top !) (call (top done) ,coll ,state)) - (scope-block - (block - ;; NOTE: enable this to force loop-local var - #;,@(map (lambda (v) `(local ,v)) (lhs-vars lhs)) - ,(lower-tuple-assignment (list lhs state) - `(call (top next) ,coll ,state)) - ,body))))))))) + (let nest ((ranges (if (eq? (car (cadr e)) 'block) + (cdr (cadr e)) + (list (cadr e)))) + (first #t)) + (expand-for (if first 'while 'inner-while) + (cadr (car ranges)) + (caddr (car ranges)) + (if (null? (cdr ranges)) + (caddr e) ;; body + (nest (cdr ranges) #f))))) '+= lower-update-op '-= lower-update-op diff --git a/test/core.jl b/test/core.jl index 716a914834793..531448afd8c10 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1772,3 +1772,13 @@ macro let_with_uninit() end @test @let_with_uninit() == 2 + +# issue #5154 +let + v = {} + for i=1:3, j=1:3 + push!(v, (i, j)) + i == 1 && j == 2 && break + end + @test v == {(1,1), (1,2)} +end