w6c+wwstage: reject untyped empty-[] alloc — require context, loud cannot-infer (#3 B', subsumes #5)
An empty `[]` carries no element type; ww gets it only from a let annotation (the #45 retype). Both stages used to silently default the element to u8, and in value-form positions (return / call-arg) the lowering miscompiled — malloc(8) ignoring n, a 16B *u8|nomem where a 24B slice was expected (#5). Now every empty alloc that isn't a let-annotated binding fails to infer with a loud error, aligning ww DOWN to harec (ref/harec/src/check.c:1801-1802). Mechanism: clet / checkletassign flags the single alloc call node that a `let x: []T =` rescues (save/restore around the init walk); the alloc branch errors on any empty alloc that isn't that node. The #45 wide-T retype path is kept. wwstage needs an extra not-yet-stamped guard because resolvewalk re-types value nodes context-free after checkletassign. Tests: negative cstage-driver 729 (table-driven: bare-let, return, call-arg, assignment) + positive @test in attest_pass.ww exercising the u8 and the wide-i32 (#45) paths at runtime. Both stages reject symmetrically; byte-id verified on []u8 and []i32.
This commit is contained in:
@@ -1450,6 +1450,22 @@ cexpr(Checker *c, Node *n)
|
||||
!(c->cur_mod &&
|
||||
scope_lookup_in_module(c->cur, c->cur_mod, "alloc"))) {
|
||||
(void)cexpr(c, n->list->next);
|
||||
/* B' (#3): an empty `[]` carries no element type. ww
|
||||
* gets that type only from a let annotation (the
|
||||
* #45 retype below). Any other empty alloc — return,
|
||||
* call-arg, bare `let b = alloc([],n)` — has no hint,
|
||||
* so refuse to guess instead of defaulting to u8 (was
|
||||
* a silent u8-default + #5 value-form miscompile).
|
||||
* Aligns ww DOWN to harec, which errors the same way:
|
||||
* ref/harec/src/check.c:1801-1802. */
|
||||
if (n != c->alloc_octx) {
|
||||
err(c, n->pos, "cannot infer slice element "
|
||||
"type for alloc([], n) without a type "
|
||||
"hint; annotate the binding, e.g. "
|
||||
"`let x: []T = alloc([], n)`");
|
||||
n->lhs->type = ty_err;
|
||||
return n->type = ty_err;
|
||||
}
|
||||
Type *st = type_slice(c->a, ty_u8);
|
||||
/* Task #30 — slice form graduates the same way:
|
||||
* `alloc([], n)` now returns `([]T | nomem)`. Slot is
|
||||
@@ -1862,7 +1878,28 @@ clet(Checker *c, Node *n)
|
||||
{
|
||||
Type *declared = n->lhs ? resolve_type(c, n->lhs) : NULL;
|
||||
Type *initt = NULL;
|
||||
/* B' (#3): a `let x: []T = alloc([], n)` is the one context that
|
||||
* lets the empty alloc infer its element type (the #45 retype runs
|
||||
* AFTER cexpr, so flag the exact call node up front; cexpr errors on
|
||||
* any empty alloc that isn't this one). Peel the same ?/! wrapper
|
||||
* #45 peels so the flagged node matches. */
|
||||
Node *octx = NULL;
|
||||
if (declared && declared->kind == TY_SLICE && n->rhs) {
|
||||
Node *call = n->rhs;
|
||||
if (call->kind == N_TRYPROP || call->kind == N_TRYUNW)
|
||||
call = call->lhs;
|
||||
if (call && call->kind == N_CALL && call->lhs
|
||||
&& call->lhs->kind == N_IDENT && call->lhs->str
|
||||
&& strcmp(call->lhs->str, "alloc") == 0
|
||||
&& call->list && call->list->kind == N_ARRLIT
|
||||
&& call->list->list == NULL
|
||||
&& call->list->next && call->list->next->next == NULL)
|
||||
octx = call;
|
||||
}
|
||||
Node *saved_octx = c->alloc_octx;
|
||||
c->alloc_octx = octx;
|
||||
if (n->rhs) initt = cexpr(c, n->rhs);
|
||||
c->alloc_octx = saved_octx;
|
||||
/* `let xs: [_]T = arrlit;` — fill in the inferred length from the
|
||||
* initialiser. `resolve_type` left alen=0 as a sentinel. */
|
||||
if (declared && declared->kind == TY_ARRAY && declared->alen == 0 &&
|
||||
|
||||
@@ -547,6 +547,11 @@ struct Checker {
|
||||
* would shadow an imported module bareword. */
|
||||
int loops; /* nesting count for break/continue */
|
||||
int errs;
|
||||
Node *alloc_octx; /* #3/B': the one empty `alloc([], n)` call node
|
||||
* that has let-declared slice context this walk;
|
||||
* any OTHER empty alloc has no element-type hint
|
||||
* and must fail to infer (harec check.c:1801).
|
||||
* Set by clet around its cexpr, NULL elsewhere. */
|
||||
};
|
||||
|
||||
void check_init(Checker*, Arena*);
|
||||
|
||||
Reference in New Issue
Block a user