boot2

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

commit e43969a17afb5202cc1ec0f5752c34350a6f4475
parent ca3b28606cf90cb1a43bfc82ba92858c587971d2
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 28 Apr 2026 08:53:56 -0700

cc: CC-SCRATCH Phase 1 pre-refactors

Six semantically-null refactors that shrink the cross-decl boundary
contract ahead of Phase 3's promote walkers:

- 1.1 Drop ps-typedefs; typedef? derives from scope-lookup.
- 1.2 Drop cg-globals; cg-emit-global/extern still emit bytes.
- 1.3 Eliminate ctype mutation in parse-enum-spec and the array-init
  fixup path. Only mutation left is complete-agg! for forward
  struct/union completion. parse-init-global now returns
  (values pieces final-ty); handle-decl defers sm construction for
  inferred-length arrays so sym-type captures the resolved length.
- 1.4 Introduce shared world record (scope/tags/str-pool); pstate
  and cg both reference one. Shim accessors keep callers unchanged.
- 1.5 Drop the bytevector-append "cc__" precompute in handle-decl
  and parse-fn-body. sym-slot becomes #f for fn / global var;
  %const-init-piece mangles on demand via %cg-mangle-global.
- 1.6 Document sym immutability on the record definition.

Tests on aarch64: cc-util 14/14, cc-lex 16/16, cc-pp 31/31,
cc-cg 52/52, cc 143/143, scheme1 111/111.

Diffstat:
Mcc/cc.scm | 275++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
1 file changed, 161 insertions(+), 114 deletions(-)

diff --git a/cc/cc.scm b/cc/cc.scm @@ -319,11 +319,9 @@ ;; -------------------------------------------------------------------- ;; ctype — C type. ;; -;; Fields that mutate over a ctype's lifetime: -;; size and align — set to -1/-1 on forward struct/union decl, -;; fixed when the type is completed. -;; ext — same; struct/union ext changes shape when -;; the body is parsed. +;; size/align/ext mutate only on forward struct/union completion (see +;; complete-agg!). Every other ctype is constructed in its final shape +;; and treated as immutable thereafter. ;; -------------------------------------------------------------------- (define-record-type ctype (%ctype kind size align ext) @@ -357,6 +355,12 @@ ;; var) from a definition (fn body, var with initializer, tentative def ;; without `extern`). scope-bind! merges compatible decls; only two ;; defined? syms with the same name fire a redefinition error. +;; +;; sym is immutable — no `sym-*-set!` accessor exists. scope-bind!'s +;; merge logic constructs a fresh sym rather than mutating in place. +;; Promotion (Phase 3 of CC-SCRATCH) relies on this: a deep-copied +;; sym in main heap is guaranteed structurally identical to its +;; scratch original. ;; -------------------------------------------------------------------- (define-record-type sym (%sym name kind storage type slot defined?) @@ -365,7 +369,8 @@ (kind sym-kind) ; symbol from §1.7 (storage sym-storage) ; symbol from §1.8 or #f (type sym-type) ; ctype - (slot sym-slot) ; fixnum | bv | #f, per kind + (slot sym-slot) ; fixnum (auto local / param / enum-const value) + ; | #f (fn / global var / typedef) (defined? sym-defined?)) ; #t = definition, #f = decl-only ;; -------------------------------------------------------------------- @@ -402,6 +407,23 @@ (labels fn-ctx-labels fn-ctx-labels-set!)) ;; -------------------------------------------------------------------- +;; world — cross-decl persistent parser/cg state. The same world record +;; is shared by pstate and cg so its three slots — scope (var/typedef +;; bindings), tags (struct/union/enum tags), str-pool (interned string +;; literals) — can be reasoned about as one boundary contract. +;; Phase 3's promote walkers will deep-copy from this single root. +;; -------------------------------------------------------------------- +(define-record-type world + (%world scope tags str-pool) + world? + (scope world-scope world-scope-set!) + (tags world-tags world-tags-set!) + (str-pool world-str-pool world-str-pool-set!)) + +(define (make-world) + (%world (list '()) (list '()) '())) + +;; -------------------------------------------------------------------- ;; pstate — parser state. Owned by parse.scm; read-only to cg. ;; -------------------------------------------------------------------- ;; iter holds a tok-iter (typically a pp-iter chained over a lex-iter). @@ -409,15 +431,18 @@ ;; so the parser pulls one token at a time, with no full materialized ;; token list. (define-record-type pstate - (%pstate iter scope tags loops fn-ctx typedefs cg) + (%pstate iter world loops fn-ctx cg) pstate? - (iter ps-iter ps-iter-set!) - (scope ps-scope ps-scope-set!) - (tags ps-tags ps-tags-set!) - (loops ps-loops ps-loops-set!) - (fn-ctx ps-fn-ctx ps-fn-ctx-set!) - (typedefs ps-typedefs ps-typedefs-set!) - (cg ps-cg)) + (iter ps-iter ps-iter-set!) + (world ps-world) + (loops ps-loops ps-loops-set!) + (fn-ctx ps-fn-ctx ps-fn-ctx-set!) + (cg ps-cg)) + +(define (ps-scope ps) (world-scope (ps-world ps))) +(define (ps-scope-set! ps v) (world-scope-set! (ps-world ps) v)) +(define (ps-tags ps) (world-tags (ps-world ps))) +(define (ps-tags-set! ps v) (world-tags-set! (ps-world ps) v)) ;; -------------------------------------------------------------------- ;; cg — codegen state. Owned by cg.scm. @@ -432,17 +457,12 @@ ;; in-fn? discriminates "currently inside a function body" so ;; %cg-emit-buf can route emits to fn-buf during the body and cg-text ;; outside it (entry stub, etc.). -;; cg-globals: user-visible globals only (cg-emit-global / cg-emit-extern). -;; Stable except when user code adds a global — which is exactly what the -;; parse-fn-body rewind-safety check probes. ;; ;; cg-fn-meta: transient per-function state (fn-name, ret-slot, ret-type, ;; vararg-first-slot, indirect-slots, switch-case lists, ...). Reset on -;; cg-fn-begin/v; reads via %cg-fn-get / writes via %cg-fn-set!. Kept -;; out of cg-globals so rewind-safety checks on cg-globals aren't -;; tripped by every fn-begin. +;; cg-fn-begin/v; reads via %cg-fn-get / writes via %cg-fn-set!. (define-record-type cg - (%cg text data bss vstack frame-hi label-ctr str-pool globals fn-meta fn-buf prologue-buf max-outgoing in-fn?) + (%cg text data bss vstack frame-hi label-ctr world fn-meta fn-buf prologue-buf max-outgoing in-fn?) cg? (text cg-text) (data cg-data) @@ -450,14 +470,16 @@ (vstack cg-vstack cg-vstack-set!) (frame-hi cg-frame-hi cg-frame-hi-set!) (label-ctr cg-label-ctr cg-label-ctr-set!) - (str-pool cg-str-pool cg-str-pool-set!) - (globals cg-globals cg-globals-set!) + (world cg-world) (fn-meta cg-fn-meta cg-fn-meta-set!) (fn-buf cg-fn-buf) (prologue-buf cg-prologue-buf) (max-outgoing cg-max-outgoing cg-max-outgoing-set!) (in-fn? cg-in-fn? cg-in-fn?-set!)) +(define (cg-str-pool cg) (world-str-pool (cg-world cg))) +(define (cg-str-pool-set! cg v) (world-str-pool-set! (cg-world cg) v)) + ;; -------------------------------------------------------------------- ;; Symbol alphabets — canonical alists. ;; -------------------------------------------------------------------- @@ -2557,9 +2579,7 @@ (define (%n n) (number->string n 10)) ;; Per-fn metadata (name, ret-slot, ret-type, switch-case lists, ...) -;; lives on cg-fn-meta, reset at every cg-fn-begin/v. Keeping it off -;; cg-globals means cg-globals only mutates when the user emits a real -;; global, which is what parse-fn-body's rewind-safety check needs. +;; lives on cg-fn-meta, reset at every cg-fn-begin/v. (define (%cg-fn-set! cg key val) (cg-fn-meta-set! cg (alist-update key (lambda (_) val) (cg-fn-meta cg)))) @@ -2788,8 +2808,7 @@ '() ; vstack 0 ; frame-hi 0 ; label-ctr - '() ; str-pool - '() ; globals + (make-world) ; world (shared with pstate) '() ; fn-meta (make-buf/cap %BUF-CAP-FN) ; fn-buf (reused per fn) (make-buf/cap %BUF-CAP-PROLOGUE) ; prologue-buf (reused per fn) @@ -3916,12 +3935,9 @@ (zero-loop (- rem 8) (cons "$(0)\n" acc))) (else (zero-loop (- rem 1) (cons "!(0)\n" acc)))))))))) - (cg-globals-set! cg (alist-set (sym-name sym) sym (cg-globals cg))) 0)) -(define (cg-emit-extern cg sym) - (cg-globals-set! cg (alist-set (sym-name sym) sym (cg-globals cg))) - 0) +(define (cg-emit-extern cg sym) 0) (define (cg-intern-string cg bv-content) (let ((p (alist-ref bv-content (cg-str-pool cg)))) @@ -3949,7 +3965,7 @@ ;; cc/parse.scm — recursive-descent + Pratt parser. Minimal scheme1. (define (make-pstate iter cg) - (%pstate iter (list '()) (list '()) '() #f '() cg)) + (%pstate iter (cg-world cg) '() #f cg)) (define (peek ps) (iter-peek (ps-iter ps))) (define (peek2 ps) (iter-peek2 (ps-iter ps))) @@ -4050,10 +4066,9 @@ (cond ((null? f) #f) (else (let ((v (alist-ref n (car f)))) (if v v (loop (cdr f)))))))) -(define (typedef-add! ps n) - (ps-typedefs-set! ps (alist-set n #t (ps-typedefs ps)))) (define (typedef? ps n) - (if (alist-ref n (ps-typedefs ps)) #t #f)) + (let ((sm (scope-lookup ps n))) + (and sm (eq? (sym-kind sm) 'typedef)))) (define (%mk-ptr p) (%ctype 'ptr 8 8 p)) (define (%mk-arr e n) @@ -4110,11 +4125,10 @@ ((at-kw? ps 'enum) (loop sto sn lg (parse-enum-spec ps) #t)) ((and (not b) (eq? (tok-kind t) 'IDENT) - (typedef? ps (tok-value t))) + (let ((sm (scope-lookup ps (tok-value t)))) + (and sm (eq? (sym-kind sm) 'typedef)))) (let* ((tk (advance ps)) (sm (scope-lookup ps (tok-value tk)))) - (if (and sm (eq? (sym-kind sm) 'typedef)) - (loop sto sn lg (sym-type sm) #t) - (die (tok-loc tk) "typedef no sym" (tok-value tk))))) + (loop sto sn lg (sym-type sm) #t))) (else (cond ((not saw) (die (tok-loc t) "expected decl-spec" (tok-value t))) @@ -4217,24 +4231,28 @@ (cond ((at-punct? ps 'lbrace) (advance ps) - (let ((ct (%ctype 'enum 4 4 (list tag '())))) - (if tag (tag-bind! ps tag ct)) - (let loop ((vs '()) (nv 0)) - (cond - ((at-punct? ps 'rbrace) - (advance ps) - (ctype-ext-set! ct (list tag (reverse vs))) ct) - (else - (let* ((nt (advance ps)) (nm (tok-value nt)) - (val (cond ((at-punct? ps 'assign) - (advance ps) (parse-const-int ps)) - (else nv)))) - (scope-bind! ps nm - (%sym nm 'enum-const #f %t-i32 val #t)) - (cond ((at-punct? ps 'comma) (advance ps)) - ((at-punct? ps 'rbrace) #t) - (else (die (tok-loc (peek ps)) "enum"))) - (loop (cons (cons nm val) vs) (+ val 1)))))))) + ;; Parse all members first, then construct the enum ctype with + ;; the final members list and tag-bind it. Members reference + ;; earlier enum-consts via scope-lookup (not via the enum tag), + ;; so deferring tag-bind! is safe. + (let loop ((vs '()) (nv 0)) + (cond + ((at-punct? ps 'rbrace) + (advance ps) + (let ((ct (%ctype 'enum 4 4 (list tag (reverse vs))))) + (if tag (tag-bind! ps tag ct)) + ct)) + (else + (let* ((nt (advance ps)) (nm (tok-value nt)) + (val (cond ((at-punct? ps 'assign) + (advance ps) (parse-const-int ps)) + (else nv)))) + (scope-bind! ps nm + (%sym nm 'enum-const #f %t-i32 val #t)) + (cond ((at-punct? ps 'comma) (advance ps)) + ((at-punct? ps 'rbrace) #t) + (else (die (tok-loc (peek ps)) "enum"))) + (loop (cons (cons nm val) vs) (+ val 1))))))) (tag (let ((e (tag-lookup ps tag))) (cond (e e) (else (let ((c (%ctype 'enum 4 4 (list tag '())))) @@ -4719,12 +4737,10 @@ (cond ((not n) (die #f "no name")) ((eq? sto 'typedef) - (typedef-add! ps n) (scope-bind! ps n (%sym n 'typedef #f ty #f #t))) ((ctype-is-fn? ty) (scope-bind! ps n - (%sym n 'fn (or sto 'extern) ty - (bytevector-append "cc__" n) #f))) + (%sym n 'fn (or sto 'extern) ty #f #f))) ;; §I: block-scope `static` routes to a global with a name mangled ;; on the enclosing function so two functions can each have their ;; own `static int n;` without colliding. The sym's NAME holds the @@ -4733,15 +4749,20 @@ ;; remains the original identifier for source-level lookup. ((and (eq? sto 'static) (ps-fn-ctx ps)) (let* ((fname (fn-ctx-name (ps-fn-ctx ps))) - (mangled (bytevector-append fname "__" n)) - (sm (%sym mangled 'var 'static ty - (bytevector-append "cc__" mangled) #t))) - (scope-bind! ps n sm) + (mangled (bytevector-append fname "__" n))) (cond ((at-punct? ps 'assign) (advance ps) - (cg-emit-global (ps-cg ps) sm (parse-init-global ps ty))) - (else (cg-emit-global (ps-cg ps) sm #f))))) + ;; Parse init first so an inferred-length array picks up its + ;; resolved type before sm is constructed (sym is immutable). + (let-values (((pieces ty2) (parse-init-global ps ty))) + (let ((sm (%sym mangled 'var 'static ty2 #f #t))) + (scope-bind! ps n sm) + (cg-emit-global (ps-cg ps) sm pieces)))) + (else + (let ((sm (%sym mangled 'var 'static ty #f #t))) + (scope-bind! ps n sm) + (cg-emit-global (ps-cg ps) sm #f)))))) (else (cond ((not (ps-fn-ctx ps)) @@ -4749,18 +4770,20 @@ ;; (initializer present, or no `extern` keyword) is a definition ;; or tentative def, so the merge logic in scope-bind! treats it ;; as the authoritative binding. - (let* ((init? (at-punct? ps 'assign)) - (def? (or init? (not (eq? sto 'extern)))) - (sm (%sym n 'var (or sto 'extern) ty - (bytevector-append "cc__" n) def?))) - (scope-bind! ps n sm) - (cond - (init? - (advance ps) - (cg-emit-global (ps-cg ps) sm - (parse-init-global ps ty))) - ((eq? sto 'extern) (cg-emit-extern (ps-cg ps) sm)) - (else (cg-emit-global (ps-cg ps) sm #f))))) + (cond + ((at-punct? ps 'assign) + (advance ps) + (let-values (((pieces ty2) (parse-init-global ps ty))) + (let ((sm (%sym n 'var (or sto 'extern) ty2 #f #t))) + (scope-bind! ps n sm) + (cg-emit-global (ps-cg ps) sm pieces)))) + (else + (let* ((def? (not (eq? sto 'extern))) + (sm (%sym n 'var (or sto 'extern) ty #f def?))) + (scope-bind! ps n sm) + (cond + ((eq? sto 'extern) (cg-emit-extern (ps-cg ps) sm)) + (else (cg-emit-global (ps-cg ps) sm #f))))))) (else (let* ((sz (max (ctype-size ty) 1)) (al (max (ctype-align ty) 1)) @@ -4843,7 +4866,7 @@ (and (eq? (sym-kind sm) 'var) (or (eq? (sym-storage sm) 'static) (eq? (sym-storage sm) 'extern)))) - (cons 'label-ref (sym-slot sm))) + (cons 'label-ref (%cg-mangle-global (sym-name sm)))) (else (die (tok-loc it) "init: &x must reference a global" (tok-value it)))))) @@ -4860,7 +4883,7 @@ (eq? (sym-storage sm) 'extern))))))) (advance ps) (let ((sm (scope-lookup ps (tok-value t)))) - (cons 'label-ref (sym-slot sm)))) + (cons 'label-ref (%cg-mangle-global (sym-name sm))))) ;; Plain string literal as char* initializer. ((eq? (tok-kind t) 'STR) (advance ps) @@ -4879,11 +4902,12 @@ ;; Declared array length (-1 = inferred). (cond ((eq? (ctype-kind ty) 'arr) (cdr (ctype-ext ty))) (else -1))) -(define (%init-fix-array-size! ty count) - ;; Patch an inferred-length array to `count`. - (let ((elem (car (ctype-ext ty)))) - (ctype-ext-set! ty (cons elem count)) - (ctype-size-set! ty (* count (ctype-size elem))))) +(define (%init-fixed-arr-type ty count) + ;; Construct a fresh array ctype with the inferred length resolved + ;; to `count`. Pure — does not mutate `ty`. For non-inferred or + ;; non-array `ty`, callers should detect this themselves and just + ;; pass `ty` through. + (%mk-arr (car (ctype-ext ty)) count)) (define (%init-struct-fields ty) ;; Return ((name-bv ctype offset) ...) for a struct/union ctype. @@ -4900,6 +4924,9 @@ (make-bytevector nbytes 0)) ;; ----- Global initializers --------------------------------------------- +;; Returns (values pieces final-ty). For inferred-length array `ty`, +;; final-ty is a freshly-built array ctype with the resolved length; +;; otherwise final-ty is `ty` unchanged. (define (parse-init-global ps ty) (pmatch (peek ps) ;; String literal initializer for char[] @@ -4910,12 +4937,14 @@ (advance ps) (let* ((slen (bytevector-length s)) (decl (cdr (ctype-ext ty))) - (final (cond ((< decl 0) (+ slen 1)) (else decl)))) - (cond ((< decl 0) (%init-fix-array-size! ty final))) + (final (cond ((< decl 0) (+ slen 1)) (else decl))) + (final-ty (cond ((< decl 0) (%init-fixed-arr-type ty final)) + (else ty)))) (let ((bv (make-bytevector final 0))) (let loop ((i 0)) (cond - ((or (= i slen) (>= i final)) (list bv)) + ((or (= i slen) (>= i final)) + (values (list bv) final-ty)) (else (bytevector-u8-set! bv i (bytevector-u8-ref s i)) (loop (+ i 1)))))))) @@ -4924,18 +4953,27 @@ (advance ps) (cond ((eq? (ctype-kind ty) 'arr) - (%parse-init-array-list ps ty)) + (let-values (((pieces count) (%parse-init-array-list ps ty))) + (let* ((decl (%init-array-decl-len ty)) + (final-ty (cond ((< decl 0) + (%init-fixed-arr-type ty count)) + (else ty)))) + (values pieces final-ty)))) ((or (eq? (ctype-kind ty) 'struct) (eq? (ctype-kind ty) 'union)) - (%parse-init-struct-list ps ty)) + (values (%parse-init-struct-list ps ty) ty)) (else ;; Brace-wrapped scalar: { expr } (let ((piece (%const-init-piece ps ty))) (cond ((at-punct? ps 'comma) (advance ps))) (expect-punct ps 'rbrace) - (list piece))))) + (values (list piece) ty))))) ;; Bare scalar initializer - (else (list (%const-init-piece ps ty))))) + (else (values (list (%const-init-piece ps ty)) ty)))) +;; Returns (values pieces count). `count` is the number of element +;; initializers actually consumed (used by parse-init-global to resolve +;; an inferred top-level length). C99 forbids inferred length in +;; nested array elements, so recursive callers ignore `count`. (define (%parse-init-array-list ps ty) ;; Element-list array initializer; assumes `{` already consumed. (let* ((elem (%init-array-elem-type ty)) @@ -4945,14 +4983,15 @@ (cond ((at-punct? ps 'rbrace) (advance ps) - (cond ((< decl 0) (%init-fix-array-size! ty count))) ;; Pad to declared length if longer than count. (let* ((final (cond ((< decl 0) count) (else decl))) (pad (- final count))) - (cond - ((> pad 0) - (reverse (cons (%pad-piece (* pad esize)) acc))) - (else (reverse acc))))) + (values + (cond + ((> pad 0) + (reverse (cons (%pad-piece (* pad esize)) acc))) + (else (reverse acc))) + count))) (else (let ((piece (cond @@ -4962,7 +5001,8 @@ ;; element is itself struct/array (cond ((eq? (ctype-kind elem) 'arr) - (%parse-init-array-list ps elem)) + (let-values (((p _c) (%parse-init-array-list ps elem))) + p)) ((or (eq? (ctype-kind elem) 'struct) (eq? (ctype-kind elem) 'union)) (%parse-init-struct-list ps elem)) @@ -5075,7 +5115,8 @@ (advance ps) (cond ((eq? (ctype-kind fty) 'arr) - (%parse-init-array-list ps fty)) + (let-values (((p _c) (%parse-init-array-list ps fty))) + p)) ((or (eq? (ctype-kind fty) 'struct) (eq? (ctype-kind fty) 'union)) (%parse-init-struct-list ps fty)) @@ -5110,10 +5151,15 @@ (let ((et (car (ctype-ext ty)))) (or (eq? et %t-i8) (eq? et %t-u8))))) (advance ps) + ;; Note: for inferred-length (`int x[] = "..."`) auto arrays the + ;; sm-type still records the original (size=-1) ctype — `sizeof(x)` + ;; in the body would not see the resolved length. The slot is also + ;; sized off the original (= 1 byte), so the path is pre-existing + ;; broken; we don't paper over it here. Real C bootstrap code uses + ;; statics/globals for inferred-length arrays. (let* ((slen (bytevector-length s)) (decl (cdr (ctype-ext ty))) (final (cond ((< decl 0) (+ slen 1)) (else decl)))) - (cond ((< decl 0) (%init-fix-array-size! ty final))) ;; Emit byte stores for each char in s, plus NUL for the ;; trailing slot if final > slen. (let loop ((i 0)) @@ -5156,7 +5202,9 @@ (cond ((at-punct? ps 'rbrace) (advance ps) - (cond ((< decl 0) (%init-fix-array-size! ty i))) + ;; Inferred-length auto path is pre-existing broken (slot + ;; allocated off size=-1, sm-type unfixed). See note in + ;; parse-init-local-aggregate STR branch. ;; Zero out remaining slots if any (declared length > i). (let ((final (cond ((< decl 0) i) (else decl)))) (let zlp ((k i)) @@ -5320,30 +5368,29 @@ ;; so it survives. Inner scope frames are popped via scope-leave! before ;; the rewind, so their cells become unreachable; rewind reclaims them. ;; -;; Rewind-safety guard: the body might add user-visible globals -;; (block-statics), strings (literals), tags, or typedefs. Those entries -;; are post-mark and would dangle on rewind. We snapshot the relevant -;; alists before parsing and skip the rewind if any changed — paying -;; full heap cost only for functions that genuinely mutate global state. +;; Rewind-safety guard: the body might add string-pool entries (string +;; literals) or tags. Those entries are post-mark and would dangle on +;; rewind. We snapshot the relevant alists before parsing and skip the +;; rewind if any changed — paying full heap cost only for functions +;; that genuinely mutate global state. +;; (Block-scope typedefs and block-static syms are popped with +;; scope-leave! so their cells become unreachable cleanly; emitted +;; .data/.bss bytes for block-statics live in pre-mark fixed-storage +;; bufs and are unaffected by rewind.) (define (parse-fn-body ps name dt) ;; Hoist the recursive-binding scope-bind! out of the marked region ;; so the fn-sym cons survives rewind. defined?=#t marks this as a ;; definition so a prior forward decl gets overwritten and a second ;; definition with the same name fires sym-merge's redefinition error. (scope-bind! ps name - (%sym name 'fn 'extern dt - (bytevector-append "cc__" name) #t)) + (%sym name 'fn 'extern dt #f #t)) (let* ((cg (ps-cg ps)) (mark (heap-mark)) - (globals-before (cg-globals cg)) (str-pool-before (cg-str-pool cg)) - (typedefs-before (ps-typedefs ps)) (tags-before (ps-tags ps))) (%parse-fn-body-inner ps name dt) (cond - ((and (eq? globals-before (cg-globals cg)) - (eq? str-pool-before (cg-str-pool cg)) - (eq? typedefs-before (ps-typedefs ps)) + ((and (eq? str-pool-before (cg-str-pool cg)) (eq? tags-before (ps-tags ps))) ;; cg-fn-meta points at post-mark alist conses (fn metadata, ;; switch-case lists, indirect-slots). Drop the reference before