commit ff130e4a25e02fe7231bf399779b0bcf38d2903b
parent 5e19b52523881a930b8d0464a769727677be950e
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sun, 26 Apr 2026 22:46:28 -0700
cc/parse: multi-dim arrays via right-to-left suffix wrap (§D.6)
parse-decl-suf-cont was applying [N]/(...) suffixes left-to-right
(outermost first), so `int a[2][3]` produced `arr (arr int 2) 3`
(outer dim 3, inner 2) — the opposite of C's spiral rule. Same
issue with chained fn suffixes.
Fix: in the recursive form, build the inner result first then wrap
this level — `(%mk-arr (r b) ln)` rather than `(r (%mk-arr b ln))`.
Same shape change in the fn arm. Now `int a[2][3]` is `arr (arr int 3) 2`
as expected, and §D.5's decay+pointer-arithmetic path produces the
right element address for `a[i][j]`.
Lock with cc-parse/41-array-2d.c: int a[2][3] with distinct values
0..2/10..12 in each row sums to 36; a stride bug yields a different
sum.
Diffstat:
4 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/cc/parse.scm b/cc/parse.scm
@@ -263,6 +263,11 @@
(cons #f (lambda (b k) (k #f (s b))))))))
(define (parse-decl-suf-cont ps)
+ ;; C declarator suffixes apply RIGHT-TO-LEFT (innermost first):
+ ;; int a[2][3] ⇒ arr (arr int 3) 2 (outer dim 2)
+ ;; not arr (arr int 2) 3 (which would treat the leftmost suffix as
+ ;; outermost). The recursive structure builds the inner suffix's
+ ;; result first, then this level wraps.
(cond
((at-punct? ps 'lbrack)
(advance ps)
@@ -270,14 +275,14 @@
(else (parse-const-int ps))))
(_ (expect-punct ps 'rbrack))
(r (parse-decl-suf-cont ps)))
- (lambda (b) (r (%mk-arr b ln)))))
+ (lambda (b) (%mk-arr (r b) ln))))
((at-punct? ps 'lparen)
(advance ps)
(let* ((res (parse-fn-params ps))
(p (car res)) (v (cdr res)))
(expect-punct ps 'rparen)
(let ((r (parse-decl-suf-cont ps)))
- (lambda (b) (r (%mk-fn b p v))))))
+ (lambda (b) (%mk-fn (r b) p v)))))
(else (lambda (b) b))))
(define (paren-is-group? ps)
diff --git a/docs/CC-PUNCHLIST.md b/docs/CC-PUNCHLIST.md
@@ -186,9 +186,13 @@ broken. Pick one fix and document it in
lval. cg-take-addr on an arr lval was also adjusted to yield
T* (not (T[N])*) so `&a[0]` stays consistent.
-- [ ] **Multi-dim arrays**
- - parse: `cc-parse/NN-array-2d.c`
- - Needs: derived `arr (arr T N) M`; verify size/align/decay.
+- [x] **Multi-dim arrays**
+ - parse: `cc-parse/41-array-2d.c`
+ - Done: fixed `parse-decl-suf-cont` to apply suffixes
+ right-to-left (innermost first) so `int a[2][3]` produces
+ `arr (arr int 3) 2`, not `arr (arr int 2) 3`. Same fix in the
+ fn-suffix arm so `T (...)(...)` chains compose correctly. Decay
+ + ptr arithmetic from §D.5 then handles the rest.
- [ ] **Struct passed by pointer to a function**
- parse: `cc-parse/NN-struct-fn-arg.c` — passes `&s`.
diff --git a/tests/cc-parse/41-array-2d.c b/tests/cc-parse/41-array-2d.c
@@ -0,0 +1,17 @@
+// tests/cc-parse/41-array-2d.c — multi-dim array indexing (§D.6 of
+// docs/CC-PUNCHLIST.md). int a[2][3]; row-major, so &a[1][2] is at
+// byte offset (1*3 + 2)*4 = 20 from a's base. We write distinct
+// values into a[0][0]..a[1][2] and read them back via a known sum.
+//
+// Sum is 0 + 1 + 2 + 10 + 11 + 12 = 36.
+
+int main() {
+ int a[2][3];
+ a[0][0] = 0;
+ a[0][1] = 1;
+ a[0][2] = 2;
+ a[1][0] = 10;
+ a[1][1] = 11;
+ a[1][2] = 12;
+ return a[0][0] + a[0][1] + a[0][2] + a[1][0] + a[1][1] + a[1][2];
+}
diff --git a/tests/cc-parse/41-array-2d.expected-exit b/tests/cc-parse/41-array-2d.expected-exit
@@ -0,0 +1 @@
+36