commit 077025f552fbed9b0cc283b260e136b56f001347
parent c6a82a3a2bc1166329ee461d9c9d152ee5709ea6
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 29 Apr 2026 23:22:56 -0700
cc: fix for-loop continue to execute the step expression
The previous for-loop lowering used cg-loop which targets a single
back-edge label. A `continue` inside `for (init; cond; step) body`
jumped directly to the condition check, skipping the step expression.
Re-lower for-loops explicitly:
jump test; top: step; test: cond; if-false break; body; jump top
continue now targets ::TAG_top (step), break targets ::TAG_end.
The step token list is collected before the body is parsed and replayed
via parse-saved-expr-stmt after the top label is emitted.
Refactor collect-til-rparen into collect-til-top-punct (parameterised
on delimiter and error message) so the for-cond tokens can also be
collected without special-casing semicolons. Bracket pairs
([]) are now tracked alongside parens when scanning for the delimiter.
Regression: tests/cc/133-for-continue.c.
Diffstat:
3 files changed, 60 insertions(+), 30 deletions(-)
diff --git a/cc/cc.scm b/cc/cc.scm
@@ -6056,49 +6056,66 @@
((stmt-starts-decl? ps) (parse-local-decl ps))
(else (parse-expr ps) (cg-pop (ps-cg ps))
(expect-punct ps 'semi)))
- (cg-loop (ps-cg ps)
- (lambda ()
- (cond ((at-punct? ps 'semi)
- (cg-push-imm (ps-cg ps) %t-i32 1))
- (else (parse-expr ps) (rval! ps)))
- (expect-punct ps 'semi))
- (lambda (tag)
- (let ((stk (collect-til-rparen ps)))
- (expect-punct ps 'rparen)
- (push-loop-ctx! ps 'for tag #t)
- (parse-stmt ps)
- (pop-loop-ctx! ps)
- (cond
- ((null? stk) #t)
- (else
- ;; Step expr is parsed AFTER the body. Swap the
- ;; parser's iter for a list-iter wrapping the saved
- ;; tokens (followed by EOF so parse-expr stops);
- ;; restore on exit.
- (let ((sv (ps-iter ps)))
- (ps-iter-set! ps
- (make-list-iter
- (append stk (list (make-tok 'EOF #f #f)))))
- (parse-expr ps) (cg-pop (ps-cg ps))
- (ps-iter-set! ps sv)))))))
+ (let* ((cg (ps-cg ps))
+ (cond-toks (cond
+ ((at-punct? ps 'semi) '())
+ (else (collect-til-top-punct ps 'semi "EOF in for-cond"))))
+ (_ (expect-punct ps 'semi))
+ (step-toks (collect-til-rparen ps))
+ (_ (expect-punct ps 'rparen))
+ (tag (%cg-fresh-loop-tag cg)))
+ ;; A C `continue` in a for-loop must run the step expression before
+ ;; retesting the condition. Arrange the loop as:
+ ;; jump test; top: step; test: condition; body; jump top
+ (%cg-emit-many cg (list "%b(&::" tag "_test)\n"
+ "::" tag "_top\n"))
+ (parse-saved-expr-stmt ps step-toks)
+ (%cg-emit-many cg (list "::" tag "_test\n"))
+ (cond
+ ((null? cond-toks) (cg-push-imm cg %t-i32 1))
+ (else (parse-saved-expr ps cond-toks) (rval! ps)))
+ (let ((c (cg-pop cg)))
+ (%cg-load-opnd-into cg c 't0)
+ (%cg-emit-many cg (list "%if_eqz(t0, { %break(" tag ") })\n")))
+ (push-loop-ctx! ps 'for tag #t)
+ (parse-stmt ps)
+ (pop-loop-ctx! ps)
+ (%cg-emit-many cg (list "%b(&::" tag "_top)\n"
+ "::" tag "_end\n")))
(scope-leave! ps) #t)
-(define (collect-til-rparen ps)
+(define (parse-saved-expr ps toks)
+ (let ((sv (ps-iter ps)))
+ (ps-iter-set! ps (make-list-iter (append toks (list (make-tok 'EOF #f #f)))))
+ (parse-expr ps)
+ (ps-iter-set! ps sv)))
+
+(define (parse-saved-expr-stmt ps toks)
+ (cond
+ ((null? toks) #t)
+ (else (parse-saved-expr ps toks) (cg-pop (ps-cg ps)))))
+
+(define (collect-til-top-punct ps punct err)
(let loop ((acc '()) (d 0))
(let ((t (peek ps)))
(cond
((eq? (tok-kind t) 'EOF)
- (die (tok-loc t) "EOF in for-step"))
+ (die (tok-loc t) err))
((and (zero? d) (eq? (tok-kind t) 'PUNCT)
- (eq? (tok-value t) 'rparen)) (reverse acc))
+ (eq? (tok-value t) punct)) (reverse acc))
(else
(let ((nt (advance ps)))
(loop (cons nt acc)
(cond ((not (eq? (tok-kind nt) 'PUNCT)) d)
- ((eq? (tok-value nt) 'lparen) (+ d 1))
- ((eq? (tok-value nt) 'rparen) (- d 1))
+ ((or (eq? (tok-value nt) 'lparen)
+ (eq? (tok-value nt) 'lbrack)) (+ d 1))
+ ((or (eq? (tok-value nt) 'rparen)
+ (eq? (tok-value nt) 'rbrack)) (- d 1))
(else d)))))))))
+(define (collect-til-rparen ps)
+ (collect-til-top-punct ps 'rparen "EOF in for-step"))
+
(define (parse-switch-stmt ps)
(expect-kw ps 'switch) (expect-punct ps 'lparen)
(parse-expr ps) (rval! ps)
diff --git a/tests/cc/133-for-continue.c b/tests/cc/133-for-continue.c
@@ -0,0 +1,12 @@
+int main(void) {
+ int i;
+ int sum = 0;
+
+ for (i = 0; i < 5; i = i + 1) {
+ if (i < 3)
+ continue;
+ sum = sum + i;
+ }
+
+ return sum == 7 ? 0 : 1;
+}
diff --git a/tests/cc/133-for-continue.expected-exit b/tests/cc/133-for-continue.expected-exit
@@ -0,0 +1 @@
+0