From faba8b70dd6f0bd0a42e60fc17ab2383009e8cd0 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Fri, 22 May 2026 03:40:52 +0900 Subject: [PATCH] selfhost/cmd/wcc: stamp inferred-let decl.lhs (#15 precursor, #43) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit checkletassign previously early-returned on `n.lhs == nil`, so an inferred binding (`let r = expr;`) left decl.lhs unset; exprtype's N_IDENT branch at check.ww:1550 reads `s.decl.lhs` and so the use sites lost the inferred type. Mirrors cstage cmd/wcc/check.c:1477 clet `if (t == NULL && initt) t = type_default(initt);` and ref/harec/src/check.c:1422 check_expr_binding. cstage carries the let type on Sym.type; wwstage carries it on decl.lhs — same observable result, byte-id (rule 10) intact. Defaulting (untyped_int → i32) stays at use sites in exprtype, not the binding site. Mutation layer matches #41's installparams parser-decl-mutation precedent at check.ww:2747. Table-driven test deferred to #44 per the speed-first cadence; existing 990–997 byte-id corpus implicitly exercises inferred lets. Context for the capture sweep: #36. --- selfhost/cmd/w6c/main.combined.ww | 13 ++++++++++++- selfhost/cmd/wcc/check.ww | 13 ++++++++++++- selfhost/cmd/wwdump/main.combined.ww | 13 ++++++++++++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index c82f079a..d80022b2 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -9465,11 +9465,22 @@ fn errnotassign(c: *checker, dst: *node, src: *node, where: str) void = { fn checkletassign(c: *checker, n: *node) void = { if (n == nil) { return; }; - if (n.lhs == nil) { return; }; // no declared type, nothing to check if (n.rhs == nil) { return; }; // no init // hint = nil for A.6.0; A.6.1 will pass n.lhs once STRUCTLIT/ARRLIT // arms consume it. Plumbing-only at this point. let src: *node = exprtype(c, n.rhs, nil); + // Inferred binding (`let r = expr;`, no type annotation). Mirror + // cstage cmd/wcc/check.c:1477 clet `if (t == NULL && initt) t = + // type_default(initt);` and ref/harec/src/check.c:1422 + // check_expr_binding. wwstage carries the let's type on decl.lhs + // (exprtype N_IDENT at L1546 reads s.decl.lhs); cstage carries it + // on Sym.type — same observable result, rule-10 byte-id holds. + // Defaulting (untyped_int → i32, etc.) is exprtype's job at use + // sites, not the binding site. + if (n.lhs == nil) { + if (src != nil) { n.lhs = src; }; + return; + }; if (src == nil) { return; }; // can't infer // #45: alloc([], n) defers element type to the let-init context // (Hare-style). exprtype's alloc-slice branch synthesizes diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index 5dd6085f..efd7f631 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -2501,11 +2501,22 @@ fn errnotassign(c: *checker, dst: *node, src: *node, where: str) void = { fn checkletassign(c: *checker, n: *node) void = { if (n == nil) { return; }; - if (n.lhs == nil) { return; }; // no declared type, nothing to check if (n.rhs == nil) { return; }; // no init // hint = nil for A.6.0; A.6.1 will pass n.lhs once STRUCTLIT/ARRLIT // arms consume it. Plumbing-only at this point. let src: *node = exprtype(c, n.rhs, nil); + // Inferred binding (`let r = expr;`, no type annotation). Mirror + // cstage cmd/wcc/check.c:1477 clet `if (t == NULL && initt) t = + // type_default(initt);` and ref/harec/src/check.c:1422 + // check_expr_binding. wwstage carries the let's type on decl.lhs + // (exprtype N_IDENT at L1546 reads s.decl.lhs); cstage carries it + // on Sym.type — same observable result, rule-10 byte-id holds. + // Defaulting (untyped_int → i32, etc.) is exprtype's job at use + // sites, not the binding site. + if (n.lhs == nil) { + if (src != nil) { n.lhs = src; }; + return; + }; if (src == nil) { return; }; // can't infer // #45: alloc([], n) defers element type to the let-init context // (Hare-style). exprtype's alloc-slice branch synthesizes diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 62e5c2b1..356f8d80 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -9465,11 +9465,22 @@ fn errnotassign(c: *checker, dst: *node, src: *node, where: str) void = { fn checkletassign(c: *checker, n: *node) void = { if (n == nil) { return; }; - if (n.lhs == nil) { return; }; // no declared type, nothing to check if (n.rhs == nil) { return; }; // no init // hint = nil for A.6.0; A.6.1 will pass n.lhs once STRUCTLIT/ARRLIT // arms consume it. Plumbing-only at this point. let src: *node = exprtype(c, n.rhs, nil); + // Inferred binding (`let r = expr;`, no type annotation). Mirror + // cstage cmd/wcc/check.c:1477 clet `if (t == NULL && initt) t = + // type_default(initt);` and ref/harec/src/check.c:1422 + // check_expr_binding. wwstage carries the let's type on decl.lhs + // (exprtype N_IDENT at L1546 reads s.decl.lhs); cstage carries it + // on Sym.type — same observable result, rule-10 byte-id holds. + // Defaulting (untyped_int → i32, etc.) is exprtype's job at use + // sites, not the binding site. + if (n.lhs == nil) { + if (src != nil) { n.lhs = src; }; + return; + }; if (src == nil) { return; }; // can't infer // #45: alloc([], n) defers element type to the let-init context // (Hare-style). exprtype's alloc-slice branch synthesizes