boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

commit 17ffef6431c02dd6b487a62ef7d21d1365cceb3f
parent 41ffdc2a3fa3b9492c0a8d82497602187411babe
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri,  1 May 2026 17:37:35 -0700

cc/pp: track #else seen on cond-stack; reject #elif/#else after #else

The cond-stack frame was a (active? . taken?) pair, so the dispatcher
couldn't tell whether the current #if-group had already passed
through an #else. Sequences like #if 0 / #else / #elif 1 / #endif
silently fell through with no diagnostic — and worse, an #elif clause
in that position re-evaluated its expression and could turn back on
content that the spec says is dead.

Promote the frame to a triple (active? taken? else?). %pp-do-else
sets else? = #t; %pp-do-elif and %pp-do-else die when they see a
frame with else? = #t already.

Test: tests/cc-pp/54-elif-after-else.c expects exit 1 (was passing
through, exit 0).

Diffstat:
Mcc/cc.scm | 40+++++++++++++++++++++++++++-------------
Atests/cc-pp/54-elif-after-else.c | 7+++++++
Atests/cc-pp/54-elif-after-else.expected-exit | 1+
3 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/cc/cc.scm b/cc/cc.scm @@ -2056,32 +2056,42 @@ (else (cons (car al) (%pp-alist-drop key (cdr al)))))) ;; --- #if / #ifdef / #ifndef / #elif / #else / #endif --- +;; cond-stack frame: (active? taken? else?). active? gates the body +;; until the next #elif/#else/#endif; taken? records whether ANY arm +;; (the original #if branch or any #elif) has matched, so later arms +;; stay inactive; else? records that we have already passed an #else +;; in this frame, so a subsequent #elif/#else is rejected. +(define (%pp-frame a? t? e?) (list a? t? e?)) +(define (%pp-frame-active? f) (car f)) +(define (%pp-frame-taken? f) (car (cdr f))) +(define (%pp-frame-else? f) (car (cdr (cdr f)))) + (define (%pp-do-if line state) (cond ((not (%pp-active? state)) - (pps-cond-stack-set! state (cons (cons #f #f) (pps-cond-stack state)))) + (pps-cond-stack-set! state (cons (%pp-frame #f #f #f) (pps-cond-stack state)))) (else (let* ((v (pp-eval-cexpr line (pps-macros state))) (a? (not (= v 0)))) - (pps-cond-stack-set! state (cons (cons a? a?) (pps-cond-stack state))))))) + (pps-cond-stack-set! state (cons (%pp-frame a? a? #f) (pps-cond-stack state))))))) (define (%pp-do-ifdef line state) (cond ((not (%pp-active? state)) - (pps-cond-stack-set! state (cons (cons #f #f) (pps-cond-stack state)))) + (pps-cond-stack-set! state (cons (%pp-frame #f #f #f) (pps-cond-stack state)))) (else (let ((d? (%pp-defined? (%pp-name-of-single line) state))) (pps-cond-stack-set! state - (cons (cons d? d?) (pps-cond-stack state))))))) + (cons (%pp-frame d? d? #f) (pps-cond-stack state))))))) (define (%pp-do-ifndef line state) (cond ((not (%pp-active? state)) - (pps-cond-stack-set! state (cons (cons #f #f) (pps-cond-stack state)))) + (pps-cond-stack-set! state (cons (%pp-frame #f #f #f) (pps-cond-stack state)))) (else (let ((a? (not (%pp-defined? (%pp-name-of-single line) state)))) (pps-cond-stack-set! state - (cons (cons a? a?) (pps-cond-stack state))))))) + (cons (%pp-frame a? a? #f) (pps-cond-stack state))))))) (define (%pp-name-of-single line) (cond @@ -2101,16 +2111,18 @@ ((null? cs) (die #f "#elif outside #if")) (else (let* ((top (car cs)) (rest (cdr cs)) - (taken? (cdr top)) + (taken? (%pp-frame-taken? top)) + (else? (%pp-frame-else? top)) (par? (%pp-parent-active? state))) (cond + (else? (die #f "#elif after #else")) ((or (not par?) taken?) - (pps-cond-stack-set! state (cons (cons #f taken?) rest))) + (pps-cond-stack-set! state (cons (%pp-frame #f taken? #f) rest))) (else (let* ((v (pp-eval-cexpr line (pps-macros state))) (a? (not (= v 0)))) (pps-cond-stack-set! state - (cons (cons a? (or a? taken?)) rest)))))))))) + (cons (%pp-frame a? (or a? taken?) #f) rest)))))))))) (define (%pp-do-else line state) (let ((cs (pps-cond-stack state))) @@ -2118,15 +2130,17 @@ ((null? cs) (die #f "#else outside #if")) (else (let* ((top (car cs)) (rest (cdr cs)) - (taken? (cdr top)) + (taken? (%pp-frame-taken? top)) + (else? (%pp-frame-else? top)) (par? (%pp-parent-active? state))) (cond + (else? (die #f "#else after #else")) ((not par?) - (pps-cond-stack-set! state (cons (cons #f taken?) rest))) + (pps-cond-stack-set! state (cons (%pp-frame #f taken? #t) rest))) (taken? - (pps-cond-stack-set! state (cons (cons #f #t) rest))) + (pps-cond-stack-set! state (cons (%pp-frame #f #t #t) rest))) (else - (pps-cond-stack-set! state (cons (cons #t #t) rest))))))))) + (pps-cond-stack-set! state (cons (%pp-frame #t #t #t) rest))))))))) (define (%pp-do-endif line state) (let ((cs (pps-cond-stack state))) diff --git a/tests/cc-pp/54-elif-after-else.c b/tests/cc-pp/54-elif-after-else.c @@ -0,0 +1,7 @@ +#if 0 +1 +#else +2 +#elif 1 +3 +#endif diff --git a/tests/cc-pp/54-elif-after-else.expected-exit b/tests/cc-pp/54-elif-after-else.expected-exit @@ -0,0 +1 @@ +1