commit 0941b72ae2a2fe737f7a9affa37553da2bb78287
parent a835d6b3630add6a1700ddcb6103208e5bf9d9bf
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sun, 26 Apr 2026 22:34:21 -0700
cc/parse: dot/arrow use cg-push-field (§D.1)
The dot and arrow arms in parse-postfix-rest previously dropped the
field-name token, pushed imm 0, added (no scaling — wrong type),
and called cg-push-deref — producing offset 0 for every field. This
worked accidentally for .x where x is the first field and lhs was
already a pointer, but blew up in all other cases.
Replace both arms with cg-push-field. The arrow arm first does
rval! (decays/loads the pointer) and cg-push-deref to reach the
struct lval through the pointer, then cg-push-field on the struct.
Lock with cc-parse/36-struct-load.c: s.a=1; s.b=2; return s.a + s.b*10;
exits 21.
Diffstat:
4 files changed, 42 insertions(+), 18 deletions(-)
diff --git a/cc/parse.scm b/cc/parse.scm
@@ -909,15 +909,25 @@
(cg-call (ps-cg ps) n #t)
(lp)))
((eq? v 'dot)
- (advance ps) (advance ps)
- (cg-push-imm (ps-cg ps) %t-i64 0)
- (cg-binop (ps-cg ps) 'add)
- (cg-push-deref (ps-cg ps)) (lp))
+ (advance ps)
+ (let ((nt (advance ps)))
+ (cond
+ ((not (eq? (tok-kind nt) 'IDENT))
+ (die (tok-loc nt) "expected field name"))
+ (else
+ (cg-push-field (ps-cg ps) (tok-value nt)) (lp)))))
((eq? v 'arrow)
- (advance ps) (advance ps) (rval! ps)
- (cg-push-imm (ps-cg ps) %t-i64 0)
- (cg-binop (ps-cg ps) 'add)
- (cg-push-deref (ps-cg ps)) (lp))
+ (advance ps)
+ (let ((nt (advance ps)))
+ (cond
+ ((not (eq? (tok-kind nt) 'IDENT))
+ (die (tok-loc nt) "expected field name"))
+ (else
+ ;; ptr -> field: load the pointer to rval, deref to
+ ;; reach the struct lval, then push the field.
+ (rval! ps)
+ (cg-push-deref (ps-cg ps))
+ (cg-push-field (ps-cg ps) (tok-value nt)) (lp)))))
((eq? v 'inc)
(advance ps)
(cg-take-addr (ps-cg ps)) (cg-push-deref (ps-cg ps))
diff --git a/docs/CC-PUNCHLIST.md b/docs/CC-PUNCHLIST.md
@@ -144,16 +144,15 @@ broken. Pick one fix and document it in
### D. Aggregates
-- [ ] **Struct member load**
- - cg: `cc-cg/NN-struct-load.scm` — pushes a struct frame lval at
- offset, loads field-typed value.
- - parse: `cc-parse/NN-struct-load.c` — `struct S {int a; int b;}; struct S s;
- s.a=1; s.b=2; return s.a + s.b*10;` → exit 21.
- - Needs: `cg-push-field cg fname` — pop struct/union lval, look up
- `fname` in `ctype-ext`'s `(tag complete? fields)`, push frame
- lval at the right offset with the field's ctype. Replaces the
- parser stub at `parse.scm` lines 947–960 that ignores the field
- name and uses offset 0.
+- [x] **Struct member load**
+ - cg: `cc-cg/36-struct-load.scm` — two-int struct, fields at 0 and 4.
+ - parse: `cc-parse/36-struct-load.c`
+ - Done: added `cg-push-field cg fname` (cg.scm). Pops struct/union
+ lval, looks up `fname` in `ctype-ext`'s `(tag complete? fields)`.
+ Three input cases: direct frame lval shifts the slot offset;
+ indirect frame lval loads addr+fo into a new indirect slot;
+ global lval `la`'s the label, adds `fo`, stashes via indirect
+ slot. Parser `dot` arm replaced.
- [ ] **Struct member store**
- cg: `cc-cg/NN-struct-store.scm`
diff --git a/tests/cc-parse/36-struct-load.c b/tests/cc-parse/36-struct-load.c
@@ -0,0 +1,14 @@
+// tests/cc-parse/36-struct-load.c — struct member load via real C
+// (§D.1 of docs/CC-PUNCHLIST.md). Two int fields at distinct offsets;
+// reading both back in distinct positions of the result confirms the
+// parser uses the field offset, not 0 for both.
+//
+// s.a=1; s.b=2; return s.a + s.b*10; => exit 21.
+
+int main() {
+ struct S { int a; int b; };
+ struct S s;
+ s.a = 1;
+ s.b = 2;
+ return s.a + s.b * 10;
+}
diff --git a/tests/cc-parse/36-struct-load.expected-exit b/tests/cc-parse/36-struct-load.expected-exit
@@ -0,0 +1 @@
+21