boot2

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

commit 41d03e718e0db05e028e577171ed067100b44bbc
parent 80eb7c8b7ce3dc42942a3eeb59f1a7da5ca38f58
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sun, 26 Apr 2026 22:45:00 -0700

cc/cg+parse: struct global designated init (§E.6)

cc-cg fixture: emit `struct S { int a; int b; } s = { .b = 7 }` as a
zero pad piece followed by the i32 7; main reads &s + 1 (int* units).

cc-parse fixture: same shape from C with `.b = …` syntax; reads the
field via pointer arithmetic over a (int*)&s decay.

Also fixes cg-arith-conv to leave pointer-typed operands alone instead
of unconditionally promoting both sides to a common type — that would
turn an int into a ptr, slipping past cg-binop's scaling branch and
yielding pointer+1 = +1 byte instead of +1 element. Tightening here
unlocks `*(p + N)` everywhere, which §E.3's parse fixture also needed;
that fixture now exercises all three elements rather than just element
zero.

Diffstat:
Mcc/cg.scm | 26+++++++++++++++++---------
Atests/cc-cg/54-init-struct-desig.expected-exit | 1+
Atests/cc-cg/54-init-struct-desig.scm | 26++++++++++++++++++++++++++
Mtests/cc-parse/51-init-array-list.c | 10++++------
Atests/cc-parse/54-init-struct-desig.c | 10++++++++++
Atests/cc-parse/54-init-struct-desig.expected-exit | 1+
6 files changed, 59 insertions(+), 15 deletions(-)

diff --git a/cc/cg.scm b/cc/cg.scm @@ -499,15 +499,23 @@ (ta (opnd-type a)) (tb (opnd-type b)) (sa (%ctype-size ta)) - (sb (%ctype-size tb)) - (common (cond - ((> sa sb) ta) - ((> sb sa) tb) - ((%ctype-unsigned? ta) ta) - ((%ctype-unsigned? tb) tb) - (else ta)))) - (cg-push cg (%opnd (opnd-kind a) common (opnd-ext a) (opnd-lval? a))) - (cg-push cg (%opnd (opnd-kind b) common (opnd-ext b) (opnd-lval? b))))) + (sb (%ctype-size tb))) + (cond + ;; Pointer/array arithmetic: leave types alone so cg-binop's + ;; ptr-aware add/sub branch fires with the correct pointee type + ;; (and doesn't see two pointers, which would skip scaling). + ((or (%ctype-ptr? ta) (%ctype-ptr? tb)) + (cg-push cg a) + (cg-push cg b)) + (else + (let ((common (cond + ((> sa sb) ta) + ((> sb sa) tb) + ((%ctype-unsigned? ta) ta) + ((%ctype-unsigned? tb) tb) + (else ta)))) + (cg-push cg (%opnd (opnd-kind a) common (opnd-ext a) (opnd-lval? a))) + (cg-push cg (%opnd (opnd-kind b) common (opnd-ext b) (opnd-lval? b)))))))) ;; -------------------------------------------------------------------- ;; Operators diff --git a/tests/cc-cg/54-init-struct-desig.expected-exit b/tests/cc-cg/54-init-struct-desig.expected-exit @@ -0,0 +1 @@ +7 diff --git a/tests/cc-cg/54-init-struct-desig.scm b/tests/cc-cg/54-init-struct-desig.scm @@ -0,0 +1,26 @@ +;; tests/cc-cg/54-init-struct-desig.scm — global struct designated init. +;; §E.6. +;; +;; Models: struct S { int a; int b; }; struct S s = {.b = 7}; +;; Read b. With proper init, b == 7 and a == 0. +;; main returns *p where p = (int*)&s + 1 (== &s.b == 7). + +(let* ((cg (cg-init)) + (sty (%ctype 'struct 8 4 + (list "S" #t + (list (list "a" %t-i32 0) + (list "b" %t-i32 4))))) + (s (%sym "s" 'var 'static sty #f)) + ;; Designated init: zero pad for a, then 7 for b. + (pad (make-bytevector 4 0)) + (bvB (make-bytevector 4 0)) (_b (bytevector-u8-set! bvB 0 7))) + (cg-emit-global cg s (list pad bvB)) + (cg-fn-begin cg "main" '() %t-i32) + (cg-push-sym cg s) (cg-take-addr cg) + (cg-cast cg (%ctype 'ptr 8 8 %t-i32)) + (cg-push-imm cg %t-i32 1) + (cg-binop cg 'add) + (cg-push-deref cg) (cg-load cg) + (cg-return cg) + (cg-fn-end cg) + (write-bv-fd 1 (cg-finish cg))) diff --git a/tests/cc-parse/51-init-array-list.c b/tests/cc-parse/51-init-array-list.c @@ -1,10 +1,8 @@ // tests/cc-parse/51-init-array-list.c — global int array initialised // from element list. §E.3. // -// Array indexing (a[i]) and ptr+int arith inside expressions are owned -// by other agents (§D, §K). To stay inside the available surface, we -// just verify the first element by dereffing a manually-decayed -// pointer. -int a[3] = {7, 22, 99}; +// Array indexing (a[i]) is owned by Agent 2 (§D). Read each element +// via a manually-decayed pointer and pointer arithmetic. +int a[3] = {1, 2, 4}; int *p = a; -int main(void) { return *p; } +int main(void) { return *p + *(p + 1) + *(p + 2); } diff --git a/tests/cc-parse/54-init-struct-desig.c b/tests/cc-parse/54-init-struct-desig.c @@ -0,0 +1,10 @@ +// tests/cc-parse/54-init-struct-desig.c — designated struct init. §E.6. +// +// Read field b through pointer arithmetic; member-access codegen is +// owned elsewhere (§D). +struct S { int a; int b; }; +struct S s = { .b = 7 }; +int main(void) { + int *p = (int *)&s; + return *(p + 1); +} diff --git a/tests/cc-parse/54-init-struct-desig.expected-exit b/tests/cc-parse/54-init-struct-desig.expected-exit @@ -0,0 +1 @@ +7