cmd+selfhost+test: predeclare nomem in universe scope
Per Hare convention, `nomem` is a language-level error type — no
import required, in scope alongside void/done/rune/str. ref/hare uses
it bare at errors/string.ha:14, types/c/strings.ha:89, net/uri/parse.ha:17
with no `use`. Precondition for graduating the `alloc` builtin to
`(*T | nomem)` returns.
cstage: ty_nomem is NAMED{under=ty_void, iserror=1}, installed by
typesinit and surfaced via lookup_builtin. wwstage seeds the same
shape in both check.ww (scope) and cgen.ww (aliases) — separate
tables, both consulted; without the cgen seed wwstage drops the
zero-init for `let e: nomem;` locals and breaks byte-identity.
Tests: tagged_ptr_ret.ww and trypromote.ww drop their local
`type nomem = !void;` aliases. 990_selfhost.c adds a regression that
a value named `nomem` does not collide with the predeclared type.
This commit is contained in:
@@ -6859,6 +6859,23 @@ fn seedprimitives(c: *checker) void = {
|
||||
scopedefine(c.top, "f64", skind.SK_TYPE, c.tc.tyf64, nil);
|
||||
scopedefine(c.top, "str", skind.SK_TYPE, c.tc.tystr, nil);
|
||||
scopedefine(c.top, "never", skind.SK_TYPE, c.tc.tynever, nil);
|
||||
// #29: predeclare `type nomem = !void;` so user code needn't
|
||||
// declare it locally. Synthesize an nkind.N_TYPEDECL whose lhs is
|
||||
// nkind.N_TBANG{nkind.N_TNAME("void")} so varianterr and other
|
||||
// iserror-aware paths treat `nomem` identically to a user-written
|
||||
// alias. Mirrors cmd/wcc/check.c lookup_builtin returning
|
||||
// ty_nomem (NAMED, under=ty_void, iserror=1). Note: cgen owns a
|
||||
// separate alias chain — see collectaliases in cgen.ww for the
|
||||
// companion seed.
|
||||
let empty: str;
|
||||
let tnvoid: *node = newnode(c.a, nkind.N_TNAME, empty, 0, 0);
|
||||
tnvoid.str = "void";
|
||||
let bang: *node = newnode(c.a, nkind.N_TBANG, empty, 0, 0);
|
||||
bang.lhs = tnvoid;
|
||||
let nomemdecl: *node = newnode(c.a, nkind.N_TYPEDECL, empty, 0, 0);
|
||||
nomemdecl.str = "nomem";
|
||||
nomemdecl.lhs = bang;
|
||||
scopedefine(c.top, "nomem", skind.SK_TYPE, nil, nomemdecl);
|
||||
// `nil`, `true`, `false` are keywords — handled at the lex/parser
|
||||
// level, no symbol needed.
|
||||
// `len`, `alloc`, `free`, `append` are pseudo-builtins; scopedefine
|
||||
@@ -19634,6 +19651,27 @@ type aliasent = struct {
|
||||
|
||||
fn collectaliases(c: *cgen, file: *node) void = {
|
||||
c.aliases = nil;
|
||||
// #29: seed `type nomem = !void;` here AS WELL AS in check.ww's
|
||||
// seedprimitives. The two seeds aren't redundant: wwstage's check
|
||||
// owns c.top (used by name resolution); cgen owns its own
|
||||
// c.aliases chain (used by resolvetype / slotsize / TBANG checks).
|
||||
// Without this seed, resolvetype("nomem") returns the raw N_TNAME
|
||||
// — slotsize falls through to 8B without zero-init, diverging from
|
||||
// cstage's `let e: nomem;` MOVQ $0 emit on the slot (rule 10).
|
||||
// Inserted at the head so the user-decl loop below prepends; the
|
||||
// same-module / any-match passes in aliaslookup then let a local
|
||||
// `type nomem = !void;` shadow this fallback within its module.
|
||||
let empty: str;
|
||||
let tnvoid: *node = newnode(c.a, nkind.N_TNAME, empty, 0, 0);
|
||||
tnvoid.str = "void";
|
||||
let bang: *node = newnode(c.a, nkind.N_TBANG, empty, 0, 0);
|
||||
bang.lhs = tnvoid;
|
||||
let nomemal: *aliasent = amalloc(c.a, 64u64): *aliasent;
|
||||
nomemal.aname = "nomem";
|
||||
nomemal.amod = empty;
|
||||
nomemal.target = bang;
|
||||
nomemal.aanext = nil;
|
||||
c.aliases = nomemal;
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == nkind.N_TYPEDECL) {
|
||||
|
||||
@@ -55,6 +55,27 @@ type aliasent = struct {
|
||||
|
||||
fn collectaliases(c: *cgen, file: *node) void = {
|
||||
c.aliases = nil;
|
||||
// #29: seed `type nomem = !void;` here AS WELL AS in check.ww's
|
||||
// seedprimitives. The two seeds aren't redundant: wwstage's check
|
||||
// owns c.top (used by name resolution); cgen owns its own
|
||||
// c.aliases chain (used by resolvetype / slotsize / TBANG checks).
|
||||
// Without this seed, resolvetype("nomem") returns the raw N_TNAME
|
||||
// — slotsize falls through to 8B without zero-init, diverging from
|
||||
// cstage's `let e: nomem;` MOVQ $0 emit on the slot (rule 10).
|
||||
// Inserted at the head so the user-decl loop below prepends; the
|
||||
// same-module / any-match passes in aliaslookup then let a local
|
||||
// `type nomem = !void;` shadow this fallback within its module.
|
||||
let empty: str;
|
||||
let tnvoid: *node = newnode(c.a, nkind.N_TNAME, empty, 0, 0);
|
||||
tnvoid.str = "void";
|
||||
let bang: *node = newnode(c.a, nkind.N_TBANG, empty, 0, 0);
|
||||
bang.lhs = tnvoid;
|
||||
let nomemal: *aliasent = amalloc(c.a, 64u64): *aliasent;
|
||||
nomemal.aname = "nomem";
|
||||
nomemal.amod = empty;
|
||||
nomemal.target = bang;
|
||||
nomemal.aanext = nil;
|
||||
c.aliases = nomemal;
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == nkind.N_TYPEDECL) {
|
||||
|
||||
@@ -63,6 +63,23 @@ fn seedprimitives(c: *checker) void = {
|
||||
scopedefine(c.top, "f64", skind.SK_TYPE, c.tc.tyf64, nil);
|
||||
scopedefine(c.top, "str", skind.SK_TYPE, c.tc.tystr, nil);
|
||||
scopedefine(c.top, "never", skind.SK_TYPE, c.tc.tynever, nil);
|
||||
// #29: predeclare `type nomem = !void;` so user code needn't
|
||||
// declare it locally. Synthesize an nkind.N_TYPEDECL whose lhs is
|
||||
// nkind.N_TBANG{nkind.N_TNAME("void")} so varianterr and other
|
||||
// iserror-aware paths treat `nomem` identically to a user-written
|
||||
// alias. Mirrors cmd/wcc/check.c lookup_builtin returning
|
||||
// ty_nomem (NAMED, under=ty_void, iserror=1). Note: cgen owns a
|
||||
// separate alias chain — see collectaliases in cgen.ww for the
|
||||
// companion seed.
|
||||
let empty: str;
|
||||
let tnvoid: *node = newnode(c.a, nkind.N_TNAME, empty, 0, 0);
|
||||
tnvoid.str = "void";
|
||||
let bang: *node = newnode(c.a, nkind.N_TBANG, empty, 0, 0);
|
||||
bang.lhs = tnvoid;
|
||||
let nomemdecl: *node = newnode(c.a, nkind.N_TYPEDECL, empty, 0, 0);
|
||||
nomemdecl.str = "nomem";
|
||||
nomemdecl.lhs = bang;
|
||||
scopedefine(c.top, "nomem", skind.SK_TYPE, nil, nomemdecl);
|
||||
// `nil`, `true`, `false` are keywords — handled at the lex/parser
|
||||
// level, no symbol needed.
|
||||
// `len`, `alloc`, `free`, `append` are pseudo-builtins; scopedefine
|
||||
|
||||
@@ -6859,6 +6859,23 @@ fn seedprimitives(c: *checker) void = {
|
||||
scopedefine(c.top, "f64", skind.SK_TYPE, c.tc.tyf64, nil);
|
||||
scopedefine(c.top, "str", skind.SK_TYPE, c.tc.tystr, nil);
|
||||
scopedefine(c.top, "never", skind.SK_TYPE, c.tc.tynever, nil);
|
||||
// #29: predeclare `type nomem = !void;` so user code needn't
|
||||
// declare it locally. Synthesize an nkind.N_TYPEDECL whose lhs is
|
||||
// nkind.N_TBANG{nkind.N_TNAME("void")} so varianterr and other
|
||||
// iserror-aware paths treat `nomem` identically to a user-written
|
||||
// alias. Mirrors cmd/wcc/check.c lookup_builtin returning
|
||||
// ty_nomem (NAMED, under=ty_void, iserror=1). Note: cgen owns a
|
||||
// separate alias chain — see collectaliases in cgen.ww for the
|
||||
// companion seed.
|
||||
let empty: str;
|
||||
let tnvoid: *node = newnode(c.a, nkind.N_TNAME, empty, 0, 0);
|
||||
tnvoid.str = "void";
|
||||
let bang: *node = newnode(c.a, nkind.N_TBANG, empty, 0, 0);
|
||||
bang.lhs = tnvoid;
|
||||
let nomemdecl: *node = newnode(c.a, nkind.N_TYPEDECL, empty, 0, 0);
|
||||
nomemdecl.str = "nomem";
|
||||
nomemdecl.lhs = bang;
|
||||
scopedefine(c.top, "nomem", skind.SK_TYPE, nil, nomemdecl);
|
||||
// `nil`, `true`, `false` are keywords — handled at the lex/parser
|
||||
// level, no symbol needed.
|
||||
// `len`, `alloc`, `free`, `append` are pseudo-builtins; scopedefine
|
||||
@@ -19634,6 +19651,27 @@ type aliasent = struct {
|
||||
|
||||
fn collectaliases(c: *cgen, file: *node) void = {
|
||||
c.aliases = nil;
|
||||
// #29: seed `type nomem = !void;` here AS WELL AS in check.ww's
|
||||
// seedprimitives. The two seeds aren't redundant: wwstage's check
|
||||
// owns c.top (used by name resolution); cgen owns its own
|
||||
// c.aliases chain (used by resolvetype / slotsize / TBANG checks).
|
||||
// Without this seed, resolvetype("nomem") returns the raw N_TNAME
|
||||
// — slotsize falls through to 8B without zero-init, diverging from
|
||||
// cstage's `let e: nomem;` MOVQ $0 emit on the slot (rule 10).
|
||||
// Inserted at the head so the user-decl loop below prepends; the
|
||||
// same-module / any-match passes in aliaslookup then let a local
|
||||
// `type nomem = !void;` shadow this fallback within its module.
|
||||
let empty: str;
|
||||
let tnvoid: *node = newnode(c.a, nkind.N_TNAME, empty, 0, 0);
|
||||
tnvoid.str = "void";
|
||||
let bang: *node = newnode(c.a, nkind.N_TBANG, empty, 0, 0);
|
||||
bang.lhs = tnvoid;
|
||||
let nomemal: *aliasent = amalloc(c.a, 64u64): *aliasent;
|
||||
nomemal.aname = "nomem";
|
||||
nomemal.amod = empty;
|
||||
nomemal.target = bang;
|
||||
nomemal.aanext = nil;
|
||||
c.aliases = nomemal;
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == nkind.N_TYPEDECL) {
|
||||
|
||||
Reference in New Issue
Block a user