commit f18b740fb0d49db51a671bf3ab0dac51646bdcac
parent db4d877362e9b952d72f15507e5e5fa481cd7b5d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sun, 26 Apr 2026 22:39:41 -0700
cc/parse: arrow + nested struct access fixtures (§D.3 §D.4)
§D.3: cc-parse/38-arrow.c locks in p->a / p->b via the §D.1 arrow
arm rewrite. p = &s; p->a = 4; p->b = 9; return p->b - p->a; -> 5.
§D.4: cc-parse/39-struct-nested.c chains s.inner.x and p->inner.x.
The outer struct has a leading int padding before the inner, so
each field-access step's offset must be summed correctly across
the chain. Distinct *1/*10/*100/*1000 multipliers in the readback
isolate every component, compared to a known constant for exit 1.
Diffstat:
5 files changed, 50 insertions(+), 7 deletions(-)
diff --git a/docs/CC-PUNCHLIST.md b/docs/CC-PUNCHLIST.md
@@ -161,13 +161,20 @@ broken. Pick one fix and document it in
- Done: cg-push-field from §D.1 plus the width-aware store path
from §A.1. No new primitive.
-- [ ] **Pointer-to-struct (`p->x`)**
- - cg: `cc-cg/NN-arrow.scm`
- - parse: `cc-parse/NN-arrow.c`
- - Needs: parser does ptr → deref → field via `cg-push-field`.
-
-- [ ] **Nested struct access (`s.inner.x`, `s->inner.x`)**
- - parse: `cc-parse/NN-struct-nested.c`
+- [x] **Pointer-to-struct (`p->x`)**
+ - cg: `cc-cg/38-arrow.scm`
+ - parse: `cc-parse/38-arrow.c`
+ - Done: arrow arm in `parse-postfix-rest` calls rval! (loads ptr),
+ cg-push-deref (struct lval through ptr), then cg-push-field.
+ Indirect-frame branch of cg-push-field (added in §D.1) handles
+ the deref-result struct lval correctly.
+
+- [x] **Nested struct access (`s.inner.x`, `s->inner.x`)**
+ - parse: `cc-parse/39-struct-nested.c`
+ - Done: cg-push-field pushes a new lval whose ctype is the field's
+ type; if that's a struct, a subsequent `.x` chains naturally.
+ The fixture exercises both s.inner.x (direct frame) and
+ p->inner.x (indirect frame, via the §D.1 indirect path).
- [ ] **Array element access at non-zero index**
- cg: `cc-cg/NN-array-index.scm` — `int a[3]; a[0]=1; a[1]=2; a[2]=4;
diff --git a/tests/cc-parse/38-arrow.c b/tests/cc-parse/38-arrow.c
@@ -0,0 +1,14 @@
+// tests/cc-parse/38-arrow.c — pointer-to-struct field access via real C
+// (§D.3 of docs/CC-PUNCHLIST.md). Validates the arrow arm: rval the
+// pointer, deref to reach the struct, push-field at the right offset.
+//
+// p->a=4; p->b=9; return p->b - p->a; => exit 5.
+
+int main() {
+ struct S { int a; int b; };
+ struct S s;
+ struct S *p = &s;
+ p->a = 4;
+ p->b = 9;
+ return p->b - p->a;
+}
diff --git a/tests/cc-parse/38-arrow.expected-exit b/tests/cc-parse/38-arrow.expected-exit
@@ -0,0 +1 @@
+5
diff --git a/tests/cc-parse/39-struct-nested.c b/tests/cc-parse/39-struct-nested.c
@@ -0,0 +1,20 @@
+// tests/cc-parse/39-struct-nested.c — nested struct field access via
+// real C (§D.4 of docs/CC-PUNCHLIST.md). Tests both `s.inner.x` and
+// `p->inner.x` chains. Padding ensures the inner struct is at a
+// non-zero offset, so each step's offset must be summed correctly.
+//
+// outer.lead = 0; outer.inner.x=2; outer.inner.y=3;
+// return p->inner.x + p->inner.y*10 + s.inner.x*100 + s.inner.y*1000;
+// = 2 + 30 + 200 + 3000 = 3232. We compare to 3232 -> exit 1.
+
+int main() {
+ struct Inner { int x; int y; };
+ struct Outer { int lead; struct Inner inner; };
+ struct Outer s;
+ struct Outer *p = &s;
+ s.lead = 99;
+ s.inner.x = 2;
+ s.inner.y = 3;
+ return (p->inner.x + p->inner.y * 10 + s.inner.x * 100 + s.inner.y * 1000)
+ == 3232;
+}
diff --git a/tests/cc-parse/39-struct-nested.expected-exit b/tests/cc-parse/39-struct-nested.expected-exit
@@ -0,0 +1 @@
+1