boot2

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

commit 3d092914a70e99f5e3707e25f641e221b1695354
parent 82d992202c949f29f917edf6bc55ef99a684a8f6
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 27 Apr 2026 01:08:06 -0700

cc/parse: lvalue-conversion runs for every cast (§L.1)

parse-cast-or-unary skipped rval! when the target type was a pointer,
which missed array-to-pointer decay. (struct s *)buf relabeled buf's
global lval as struct s* but left it pointing at the array storage,
so subsequent dereferences read array bytes as if they were a pointer.

Fix: always rval! before cg-cast. Matches C lvalue-conversion
semantics — arrays decay, lvals become rvals, and the cast bit-casts
the resulting rval to the target type.

§L.1 (flexible array member) falls out for free: the parser already
accepted T name[] via the existing [] suffix arm, complete-agg!
already excluded flex extent via (max sz 0), and cg-push-field +
cg-decay-array already composed correctly.

Lock-in fixture: tests/cc-parse/77-flex-array.c — backs struct s {
int n; int data[]; } with an int[] buffer, casts to struct s *, indexes
through p->data[i]; expects 63.

All suites green on aarch64/amd64/riscv64 (897 tests, 0 failures).

Diffstat:
Mcc/parse.scm | 10++++++++--
Mdocs/CC-PUNCHLIST.md | 22++++++++++++++--------
Atests/cc-parse/77-flex-array.c | 21+++++++++++++++++++++
Atests/cc-parse/77-flex-array.expected-exit | 1+
4 files changed, 44 insertions(+), 10 deletions(-)

diff --git a/cc/parse.scm b/cc/parse.scm @@ -1343,7 +1343,10 @@ (ty (cdr p))) (expect-punct ps 'rparen) (parse-unary ps) - (cond ((not (ctype-is-ptr? ty)) (rval! ps)) (else #t)) + ;; Cast operand undergoes lvalue conversion first (C semantics): + ;; arrays decay to pointers, lvals become rvals. cg-cast then + ;; bit-casts the resulting rval to the target type. + (rval! ps) (cg-cast (ps-cg ps) ty))) ((and (eq? (tok-kind t) 'IDENT) (typedef? ps (tok-value t))) (advance ps) @@ -1352,7 +1355,10 @@ (ty (cdr p))) (expect-punct ps 'rparen) (parse-unary ps) - (cond ((not (ctype-is-ptr? ty)) (rval! ps)) (else #t)) + ;; Cast operand undergoes lvalue conversion first (C semantics): + ;; arrays decay to pointers, lvals become rvals. cg-cast then + ;; bit-casts the resulting rval to the target type. + (rval! ps) (cg-cast (ps-cg ps) ty))) (else (advance ps) (parse-expr ps) (expect-punct ps 'rparen) diff --git a/docs/CC-PUNCHLIST.md b/docs/CC-PUNCHLIST.md @@ -417,14 +417,20 @@ responsible for arranging compatible types in the two branches. ### L. Aggregates round 2 -- [ ] **Flexible array member as last struct field** - - parse: `cc-parse/NN-flex-array.c` — `struct s { int n; int data[]; };` - indexed via a global instance plus malloc-extra padding. - tcc.c's `Sym` / `TokenSym` rely on this. - - Needs: parser accepts `T name[]` only as last field; `complete-agg!` - sets `ctype-size` to the offset of the flex member (excludes its - extent); `cg-push-field` for the flex member returns an `arr`- - typed lval that decays to `ptr` on use. +- [x] **Flexible array member as last struct field** + - parse: `cc-parse/77-flex-array.c` — backs `struct s { int n; int data[]; };` + with an `int[]` buffer, casts to `struct s *`, indexes through + `p->data[i]`. tcc.c's `Sym` / `TokenSym` rely on this. + - Done: parser already accepted `T name[]` (no size) via the existing + `parse-decl-suf-cont` `[]` arm; `complete-agg!` already excluded + flex extent because `(max sz 0)` collapses `-1` to 0; `cg-push-field` + + the indirect-frame path + `cg-decay-array` already composed + correctly. The actual fix was a latent cast-conversion bug in + `parse-cast-or-unary` — it skipped `rval!` when the target type + was a pointer, so `(struct s *)buf` relabeled the array's lval + rather than decaying it to a ptr-rval. Now `rval!` runs for every + cast (matches C lvalue-conversion semantics: arrays decay, lvals + become rvals before the bit-cast). - [x] **`T[]` in parameter position decays to `T *`** - parse: `cc-parse/43-array-param-decay.c` — `int sum(int a[], int n) diff --git a/tests/cc-parse/77-flex-array.c b/tests/cc-parse/77-flex-array.c @@ -0,0 +1,21 @@ +/* §L.1 flexible array member. + * + * `int data[]` as the last field of a struct: sizeof excludes the + * trailing array, but accessing `s->data[i]` yields the right element + * by treating data[] as a pointer to memory immediately after the + * named fields. + * + * We back the struct with a plain int[] buffer and reinterpret-cast + * to avoid depending on flex-aware initializer syntax. + * + * Expected: 3 + 10 + 20 + 30 = 63. + */ + +struct s { int n; int data[]; }; + +int buf[] = { 3, 10, 20, 30 }; + +int main(void) { + struct s *p = (struct s *)buf; + return p->n + p->data[0] + p->data[1] + p->data[2]; +} diff --git a/tests/cc-parse/77-flex-array.expected-exit b/tests/cc-parse/77-flex-array.expected-exit @@ -0,0 +1 @@ +63