wcc/cgen: #66(b-i) wwstage inferred-literal scalar global — default annotation to int, emit DATA+load (align up)
An inferred-literal scalar module-global (`let s = 42;`) was wwstage
silent-wrong: the checker stamps the annotation N_TNAME("untyped_int"),
which letscalarprim does not recognise, so letemitsize returns 0 — the
global is dropped from collectlets (no DATA emitted) AND cgident falls to
the silent module-leaf (no load), running garbage. cstage defaults
untyped_int to an 8B int before emit (DATAW + MOVQ), which is correct.
Fix (wwstage-only, align up to cstage): defaultinferredlets in cgen.ww,
called from cgfile (cgendecl.ww) before collectlets, rewrites the
annotation "untyped_int" -> "int" (8B machine word, NOT i32 — the #108
truncation trap is the opposite polarity) for a module-level N_LET whose
rhs is N_INTLIT. All three consumers (letemitsize, emitletdataw, cgident
global-read) then resolve a concrete int. cstage is untouched.
Scope: N_INTLIT only. A const-expr inferred global (`let s = 7*6;`, N_BIN)
stays on its existing path — that is a separate live cs!=ww silent
miscompile tracked as #133, out of scope here.
Pin: 947_inferred_scalar_global_run — inferred `let s=42` (42, base
wwstage garbage) + typed control, cs==ww byte-id.
This commit is contained in:
@@ -38056,6 +38056,7 @@ export fn cgfile(c: *cgen, file: *node) void = {
|
||||
collectfnrets(c, file);
|
||||
fficollect(c, file);
|
||||
collectmods(c, file);
|
||||
defaultinferredlets(c, file);
|
||||
collectlets(c, file);
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
@@ -39114,6 +39115,40 @@ fn letemitsize(c: *cgen, d: *node) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// defaultinferredlets — #66(b-i): an inferred module-global int-literal
|
||||
// `let s = 42;` is stamped by the checker with an N_TNAME("untyped_int")
|
||||
// annotation (d.lhs). letemitsize / emitletdataw / the cgident global-read arm
|
||||
// key on that annotation's name, which letscalarprim doesn't recognise → the
|
||||
// global is dropped from collectlets (no DATAW) and the read falls to the
|
||||
// silent module-leaf (no MOVQ, MOVSXD on stale AX → wrong). cstage instead
|
||||
// type_default's the untyped int to the 8B machine word BEFORE emit. Mirror
|
||||
// that here at the single global-decl pass: rewrite the annotation to the
|
||||
// concrete machine word `int` so all three consumers resolve it as an 8B int
|
||||
// global (DATAW + MOVQ, byte-identical to cstage). int (not i32) per
|
||||
// [[project_int_machine_word_derived_limits]] — i32 is the #108 truncation
|
||||
// trap, opposite polarity.
|
||||
// Guarded to N_INTLIT ONLY: a const-EXPR rhs (`let s = 7*6`, N_BIN) is also
|
||||
// stamped untyped_int but is #66(b-ii) — a SEPARATE loud both-stage gap (no
|
||||
// DATA → link-fail) — and must stay on its loud route, not be defaulted to a
|
||||
// silent value. Runs before collectlets in cgfile so the mutated d.lhs is
|
||||
// visible to letpreintern + emitletdataw too.
|
||||
fn defaultinferredlets(c: *cgen, file: *node) void = {
|
||||
if (file == nil) { return; };
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == nkind.N_LET) {
|
||||
if (d.lhs != nil && d.rhs != nil) {
|
||||
if (d.lhs.kind == nkind.N_TNAME
|
||||
&& streq(d.lhs.str, "untyped_int")
|
||||
&& d.rhs.kind == nkind.N_INTLIT) {
|
||||
d.lhs.str = "int";
|
||||
};
|
||||
};
|
||||
};
|
||||
d = d.next;
|
||||
};
|
||||
};
|
||||
|
||||
fn collectlets(c: *cgen, file: *node) void = {
|
||||
c.lets = nil;
|
||||
if (file == nil) { return; };
|
||||
|
||||
@@ -1040,6 +1040,40 @@ fn letemitsize(c: *cgen, d: *node) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// defaultinferredlets — #66(b-i): an inferred module-global int-literal
|
||||
// `let s = 42;` is stamped by the checker with an N_TNAME("untyped_int")
|
||||
// annotation (d.lhs). letemitsize / emitletdataw / the cgident global-read arm
|
||||
// key on that annotation's name, which letscalarprim doesn't recognise → the
|
||||
// global is dropped from collectlets (no DATAW) and the read falls to the
|
||||
// silent module-leaf (no MOVQ, MOVSXD on stale AX → wrong). cstage instead
|
||||
// type_default's the untyped int to the 8B machine word BEFORE emit. Mirror
|
||||
// that here at the single global-decl pass: rewrite the annotation to the
|
||||
// concrete machine word `int` so all three consumers resolve it as an 8B int
|
||||
// global (DATAW + MOVQ, byte-identical to cstage). int (not i32) per
|
||||
// [[project_int_machine_word_derived_limits]] — i32 is the #108 truncation
|
||||
// trap, opposite polarity.
|
||||
// Guarded to N_INTLIT ONLY: a const-EXPR rhs (`let s = 7*6`, N_BIN) is also
|
||||
// stamped untyped_int but is #66(b-ii) — a SEPARATE loud both-stage gap (no
|
||||
// DATA → link-fail) — and must stay on its loud route, not be defaulted to a
|
||||
// silent value. Runs before collectlets in cgfile so the mutated d.lhs is
|
||||
// visible to letpreintern + emitletdataw too.
|
||||
fn defaultinferredlets(c: *cgen, file: *node) void = {
|
||||
if (file == nil) { return; };
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == nkind.N_LET) {
|
||||
if (d.lhs != nil && d.rhs != nil) {
|
||||
if (d.lhs.kind == nkind.N_TNAME
|
||||
&& streq(d.lhs.str, "untyped_int")
|
||||
&& d.rhs.kind == nkind.N_INTLIT) {
|
||||
d.lhs.str = "int";
|
||||
};
|
||||
};
|
||||
};
|
||||
d = d.next;
|
||||
};
|
||||
};
|
||||
|
||||
fn collectlets(c: *cgen, file: *node) void = {
|
||||
c.lets = nil;
|
||||
if (file == nil) { return; };
|
||||
|
||||
@@ -619,6 +619,7 @@ export fn cgfile(c: *cgen, file: *node) void = {
|
||||
collectfnrets(c, file);
|
||||
fficollect(c, file);
|
||||
collectmods(c, file);
|
||||
defaultinferredlets(c, file);
|
||||
collectlets(c, file);
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
|
||||
@@ -38056,6 +38056,7 @@ export fn cgfile(c: *cgen, file: *node) void = {
|
||||
collectfnrets(c, file);
|
||||
fficollect(c, file);
|
||||
collectmods(c, file);
|
||||
defaultinferredlets(c, file);
|
||||
collectlets(c, file);
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
@@ -39114,6 +39115,40 @@ fn letemitsize(c: *cgen, d: *node) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// defaultinferredlets — #66(b-i): an inferred module-global int-literal
|
||||
// `let s = 42;` is stamped by the checker with an N_TNAME("untyped_int")
|
||||
// annotation (d.lhs). letemitsize / emitletdataw / the cgident global-read arm
|
||||
// key on that annotation's name, which letscalarprim doesn't recognise → the
|
||||
// global is dropped from collectlets (no DATAW) and the read falls to the
|
||||
// silent module-leaf (no MOVQ, MOVSXD on stale AX → wrong). cstage instead
|
||||
// type_default's the untyped int to the 8B machine word BEFORE emit. Mirror
|
||||
// that here at the single global-decl pass: rewrite the annotation to the
|
||||
// concrete machine word `int` so all three consumers resolve it as an 8B int
|
||||
// global (DATAW + MOVQ, byte-identical to cstage). int (not i32) per
|
||||
// [[project_int_machine_word_derived_limits]] — i32 is the #108 truncation
|
||||
// trap, opposite polarity.
|
||||
// Guarded to N_INTLIT ONLY: a const-EXPR rhs (`let s = 7*6`, N_BIN) is also
|
||||
// stamped untyped_int but is #66(b-ii) — a SEPARATE loud both-stage gap (no
|
||||
// DATA → link-fail) — and must stay on its loud route, not be defaulted to a
|
||||
// silent value. Runs before collectlets in cgfile so the mutated d.lhs is
|
||||
// visible to letpreintern + emitletdataw too.
|
||||
fn defaultinferredlets(c: *cgen, file: *node) void = {
|
||||
if (file == nil) { return; };
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == nkind.N_LET) {
|
||||
if (d.lhs != nil && d.rhs != nil) {
|
||||
if (d.lhs.kind == nkind.N_TNAME
|
||||
&& streq(d.lhs.str, "untyped_int")
|
||||
&& d.rhs.kind == nkind.N_INTLIT) {
|
||||
d.lhs.str = "int";
|
||||
};
|
||||
};
|
||||
};
|
||||
d = d.next;
|
||||
};
|
||||
};
|
||||
|
||||
fn collectlets(c: *cgen, file: *node) void = {
|
||||
c.lets = nil;
|
||||
if (file == nil) { return; };
|
||||
|
||||
Reference in New Issue
Block a user