cmd+selfhost+test: gate alloc builtin behind same-module fn alloc
Mirrors the existing abort/assert gates in cstage check.c (strict same-module lookup rather than scope_lookup_prefer, since lib/os.alloc under a `use os;` import must not suppress the bare-alloc builtin in client code). cgen.c shadows the resolution: only fire the rt_alloc path when the typer left N_CALL.lhs->type == ty_err. wwstage gets a new samemodfn helper for the matching gate. Test fixtures: package-main repair for the 3 alloc rows in 700_e2e.c that the parser was inheriting curmod="os" from the concat'd os.ww; new shadow-test row asserts a same-module `fn alloc(n: i64) i64` beats the builtin in cgen.
This commit is contained in:
@@ -4122,10 +4122,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (n->lhs && n->lhs->kind == N_IDENT && n->lhs->str &&
|
||||
strcmp(n->lhs->str, "alloc") == 0 && n->list) {
|
||||
if (n->lhs && n->lhs->kind == N_IDENT &&
|
||||
n->lhs->type == ty_err &&
|
||||
n->lhs->str && strcmp(n->lhs->str, "alloc") == 0 &&
|
||||
n->list) {
|
||||
/* alloc(value): heap-init a fresh *T with the value's
|
||||
* bytes. Size comes from the value's static type. */
|
||||
* bytes. Size comes from the value's static type.
|
||||
* `n->lhs->type == ty_err` gate (mirrors assert above)
|
||||
* — check.c only stamps ty_err when no user-scoped
|
||||
* `alloc` shadows the builtin (task #23). */
|
||||
Node *v = n->list;
|
||||
Type *t = v->type;
|
||||
Type *u = (t && t->kind == TY_NAMED) ? t->under : t;
|
||||
|
||||
@@ -963,9 +963,22 @@ cexpr(Checker *c, Node *n)
|
||||
n->lhs->type = ty_err;
|
||||
return n->type;
|
||||
}
|
||||
/* `alloc(value)` Hare-style builtin — suppressed when the
|
||||
* current module declares its own `alloc` (lib/os/os.ww,
|
||||
* rt/ensure.ww). Without the gate, the bare same-module call
|
||||
* lands in the typed-builtin path and silently allocates
|
||||
* sizeof(arg-type) bytes against rt_alloc, shadowing the
|
||||
* user decl. Strict same-module check (not scope_lookup_prefer):
|
||||
* `use os;` in a primary brings os.alloc into the flat scope
|
||||
* as a fallback match — that's what the `abort` precedent
|
||||
* sidesteps by leaving os.abort un-exported, but os.alloc IS
|
||||
* exported. c->cur_mod==NULL is the primary unit; gate only
|
||||
* fires when a same-module decl is registered. Task #23. */
|
||||
if (n->lhs && n->lhs->kind == N_IDENT &&
|
||||
n->lhs->str && strcmp(n->lhs->str, "alloc") == 0 &&
|
||||
n->list != NULL && n->list->next == NULL) {
|
||||
n->list != NULL && n->list->next == NULL &&
|
||||
!(c->cur_mod &&
|
||||
scope_lookup_in_module(c->cur, c->cur_mod, "alloc"))) {
|
||||
Type *t = cexpr(c, n->list);
|
||||
Type *def = type_default(t);
|
||||
n->type = type_ptr(c->a, def ? def : ty_void);
|
||||
@@ -1019,12 +1032,15 @@ cexpr(Checker *c, Node *n)
|
||||
}
|
||||
/* alloc([], n) — Hare-style fresh slice with cap n. We pin
|
||||
* the element type to u8 by default; the caller's declared
|
||||
* slice type drives the actual element size at codegen. */
|
||||
* slice type drives the actual element size at codegen.
|
||||
* Same scope_lookup_in_module gate as the value-form (#23). */
|
||||
if (n->lhs && n->lhs->kind == N_IDENT &&
|
||||
n->lhs->str && strcmp(n->lhs->str, "alloc") == 0 &&
|
||||
n->list && n->list->kind == N_ARRLIT &&
|
||||
n->list->list == NULL &&
|
||||
n->list->next && n->list->next->next == NULL) {
|
||||
n->list->next && n->list->next->next == NULL &&
|
||||
!(c->cur_mod &&
|
||||
scope_lookup_in_module(c->cur, c->cur_mod, "alloc"))) {
|
||||
(void)cexpr(c, n->list->next);
|
||||
n->type = type_slice(c->a, ty_u8);
|
||||
n->lhs->type = ty_err;
|
||||
|
||||
Reference in New Issue
Block a user