From 4bf1b568727194948d0fd96fd7137239e98d3abc Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Tue, 12 May 2026 12:00:18 +0900 Subject: [PATCH] selfhost: cgident read path for top-level lets Mirror the C cgen's N_IDENT load fallback. Reads of a top-level scalar `let` now emit `MOVQ name(SB), AX` (RIP-relative) instead of silently dropping. Truly undefined names still fall through to the silent return, matching the pre-existing defensive behaviour; the C side's broader unconditional fallback is intentionally not mirrored here so typos surface as nothing-emitted rather than a link-time stub. --- selfhost/cmd/w6c/main.combined.ww | 11 +++++++++++ selfhost/cmd/wcc/cgenexpr.ww | 11 +++++++++++ selfhost/cmd/wwdump/main.combined.ww | 11 +++++++++++ 3 files changed, 33 insertions(+) diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 30bac62c..76b4daa2 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -6128,6 +6128,17 @@ fn cgident(c: *cgen, n: *node) void = { emitline("(SB), AX\n"); return; }; + // Top-level mutable `let` — RIP-relative load from its DATAW + // slot. Mirrors C cgen's catch-all `MOVQ masym(s), AX`. Names + // that aren't lets either (typos, never-defined) flow through + // here too in C; the divergence today is bounded to scalar lets, + // which is what `isletvar` gates on. + if (isletvar(c, nm)) { + emitline("\tMOVQ\t"); + emitsymname(c, nm); + emitline("(SB), AX\n"); + return; + }; return; }; diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index 62ece917..b1e03a9b 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -400,6 +400,17 @@ fn cgident(c: *cgen, n: *node) void = { emitline("(SB), AX\n"); return; }; + // Top-level mutable `let` — RIP-relative load from its DATAW + // slot. Mirrors C cgen's catch-all `MOVQ masym(s), AX`. Names + // that aren't lets either (typos, never-defined) flow through + // here too in C; the divergence today is bounded to scalar lets, + // which is what `isletvar` gates on. + if (isletvar(c, nm)) { + emitline("\tMOVQ\t"); + emitsymname(c, nm); + emitline("(SB), AX\n"); + return; + }; return; }; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index f823a591..218b5589 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -6128,6 +6128,17 @@ fn cgident(c: *cgen, n: *node) void = { emitline("(SB), AX\n"); return; }; + // Top-level mutable `let` — RIP-relative load from its DATAW + // slot. Mirrors C cgen's catch-all `MOVQ masym(s), AX`. Names + // that aren't lets either (typos, never-defined) flow through + // here too in C; the divergence today is bounded to scalar lets, + // which is what `isletvar` gates on. + if (isletvar(c, nm)) { + emitline("\tMOVQ\t"); + emitsymname(c, nm); + emitline("(SB), AX\n"); + return; + }; return; };