cmd+rt+selfhost+test: graduate alloc to (*T | nomem) / ([]T | nomem)
Per Hare convention, alloc is a typed builtin that returns a tagged
union carrying nomem as the OOM variant. Callers spell their policy:
`alloc(T)!` aborts on OOM (the old behavior), `alloc(T)?` propagates
when the enclosing fn already returns nomem.
cstage: check builds TY_TAGGED{*T | nomem} (or {[]T | nomem}); cgen
emits AX=tag, DX=ptr per the general tagged-return ABI (the (*T|!void)
nullable-ptr fold gated in ea76ee4 keeps this clean). wwstage cgalloc
mirrors. rt/alloc.s zeroes AX on syscall error so the builtin's null
check sees a clean 0 instead of mmap's -errno leaking through as a
poisoned pointer.
Migration: 3 `!` sites in test/wcc/700_e2e.c, 1 `!` site in
rt/ensure.ww (preserves the pre-existing sizeof bug tracked by #27),
1 `?` site in selfhost/test/tagged_ptr_ret.ww (allocbox exercises
real `?` propagation against a (*T | nomem) return).
130/130 tests green, 994_w6c_ww + 995_self_rebuild stage byte-identity
preserved. Follow-ups #31 (wwstage checkletassign leniency), #32
(wwstage slice-form gap), #33 (tagged_ptr_ret.ww make-test wiring).
This commit is contained in:
@@ -4130,16 +4130,30 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* 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). */
|
||||
* `alloc` shadows the builtin (task #23).
|
||||
*
|
||||
* Result is the graduated `(*T | nomem)` tagged-pointer
|
||||
* ABI (AX=tag, DX=ptr) — task #30. rt_alloc returns 0
|
||||
* on OOM (rt/alloc.s); we branch on AX, building tag=1
|
||||
* (nomem, DX=0) on null and tag=0 (success, DX=ptr)
|
||||
* after the value-init stores complete. Callers wrap
|
||||
* with `!` / `?` to consume the union. */
|
||||
Node *v = n->list;
|
||||
Type *t = v->type;
|
||||
Type *u = (t && t->kind == TY_NAMED) ? t->under : t;
|
||||
Type *def = type_default(t);
|
||||
int sz = def ? (int)def->size : 8;
|
||||
if (sz == 0) sz = 8;
|
||||
/* call os.alloc(sz) */
|
||||
char *alloc_ok = mklabel(c, "alloc_ok");
|
||||
char *alloc_done = mklabel(c, "alloc_done");
|
||||
ins2(c, A_MOVQ, aimm(sz), areg(D_DI));
|
||||
ins1(c, A_CALL, asym(ffi_resolve("alloc")));
|
||||
ins2(c, A_CMPQ, aimm(0), areg(D_AX));
|
||||
ins1(c, A_JNE, abranch(alloc_ok));
|
||||
ins2(c, A_MOVQ, aimm(1), areg(D_AX));
|
||||
ins2(c, A_MOVQ, aimm(0), areg(D_DX));
|
||||
ins1(c, A_JMP, abranch(alloc_done));
|
||||
label(c, alloc_ok);
|
||||
ins1(c, A_PUSHQ, areg(D_AX)); /* save ptr */
|
||||
if (v->kind == N_STRUCTLIT && u && u->kind == TY_STRUCT) {
|
||||
for (Node *f = v->list; f; f = f->next) {
|
||||
@@ -4190,7 +4204,9 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
else if (sz == 4) op = A_MOVL;
|
||||
ins2(c, op, areg(D_AX), amem(D_BX, 0));
|
||||
}
|
||||
ins1(c, A_POPQ, areg(D_AX)); /* return the ptr */
|
||||
ins1(c, A_POPQ, areg(D_DX)); /* DX = success ptr */
|
||||
ins2(c, A_MOVQ, aimm(0), areg(D_AX));
|
||||
label(c, alloc_done);
|
||||
break;
|
||||
}
|
||||
if (n->lhs && n->lhs->kind == N_IDENT && n->lhs->str &&
|
||||
@@ -6346,29 +6362,52 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
int isf32 = type_isf32(lt);
|
||||
/* alloc([], n) initialiser for a slice local: allocate
|
||||
* n*esize bytes, build the {ptr, 0, n} header in the slot.
|
||||
* Element size comes from the declared slice type. */
|
||||
if (n->rhs && lu && lu->kind == TY_SLICE && sz == 24
|
||||
&& n->rhs->kind == N_CALL && n->rhs->lhs
|
||||
&& n->rhs->lhs->kind == N_IDENT
|
||||
&& strcmp(n->rhs->lhs->str, "alloc") == 0
|
||||
&& n->rhs->list && n->rhs->list->kind == N_ARRLIT
|
||||
&& n->rhs->list->list == NULL
|
||||
&& n->rhs->list->next && n->rhs->list->next->next == NULL) {
|
||||
Node *count = n->rhs->list->next;
|
||||
int esz = (lu->sub) ? (int)lu->sub->size : 1;
|
||||
cgexpr(c, count, *locals); /* AX = n */
|
||||
ins1(c, A_PUSHQ, areg(D_AX)); /* save count */
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_BX));
|
||||
ins2(c, A_IMULQ, areg(D_BX), areg(D_AX));
|
||||
* Element size comes from the declared slice type.
|
||||
*
|
||||
* Task #30 graduated the builtin to `([]T | nomem)`. The let
|
||||
* declares a bare `[]T`, so the canonical idiom wraps in `!`
|
||||
* to abort on OOM; walk into the N_TRYUNW to keep the
|
||||
* direct-store fast path. */
|
||||
{
|
||||
Node *call = NULL;
|
||||
int via_tryunw = 0;
|
||||
if (n->rhs && n->rhs->kind == N_TRYUNW && n->rhs->lhs
|
||||
&& n->rhs->lhs->kind == N_CALL) {
|
||||
call = n->rhs->lhs;
|
||||
via_tryunw = 1;
|
||||
}
|
||||
if (call && lu && lu->kind == TY_SLICE && sz == 24
|
||||
&& call->lhs && call->lhs->kind == N_IDENT
|
||||
&& 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) {
|
||||
Node *count = call->list->next;
|
||||
int esz = (lu->sub) ? (int)lu->sub->size : 1;
|
||||
cgexpr(c, count, *locals); /* AX = n */
|
||||
ins1(c, A_PUSHQ, areg(D_AX)); /* save count */
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_BX));
|
||||
ins2(c, A_IMULQ, areg(D_BX), areg(D_AX));
|
||||
}
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_DI));
|
||||
ins1(c, A_CALL, asym(ffi_resolve("alloc")));
|
||||
if (via_tryunw) {
|
||||
char *ok = mklabel(c, "tryunw_ok");
|
||||
ins2(c, A_CMPQ, aimm(0), areg(D_AX));
|
||||
ins1(c, A_JNE, abranch(ok));
|
||||
ins2(c, A_MOVQ, aimm(1), areg(D_DI));
|
||||
ins2(c, A_MOVQ, aimm(60), areg(D_AX));
|
||||
ins0(c, A_SYSCALL);
|
||||
label(c, ok);
|
||||
}
|
||||
ins1(c, A_POPQ, areg(D_BX)); /* count */
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + 0));
|
||||
ins2(c, A_MOVQ, aimm(0), amem(D_BP, off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 16));
|
||||
break;
|
||||
}
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_DI));
|
||||
ins1(c, A_CALL, asym(ffi_resolve("alloc")));
|
||||
ins1(c, A_POPQ, areg(D_BX)); /* count */
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + 0));
|
||||
ins2(c, A_MOVQ, aimm(0), amem(D_BP, off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 16));
|
||||
break;
|
||||
}
|
||||
/* str initialiser: cgexpr produces (AX=ptr, BX=len). */
|
||||
if (n->rhs && type_isstr(lt) && sz == 16) {
|
||||
|
||||
@@ -985,7 +985,22 @@ cexpr(Checker *c, Node *n)
|
||||
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);
|
||||
Type *pt = type_ptr(c->a, def ? def : ty_void);
|
||||
/* Task #30 — graduate to Hare's `(*T | nomem)` shape;
|
||||
* the cgen branches on rt_alloc's null return to emit
|
||||
* the nomem variant. Two-variant union with TY_PTR +
|
||||
* TY_NAMED(nomem) does not trip the nullable-pointer
|
||||
* fold (#25), so the result rides the general AX=tag,
|
||||
* DX=ptr ABI both stages already share. */
|
||||
Type *tt = newtype(c->a, TY_TAGGED);
|
||||
Tparam *vp = amalloc(c->a, sizeof *vp);
|
||||
Tparam *ve = amalloc(c->a, sizeof *ve);
|
||||
vp->type = pt; vp->next = ve;
|
||||
ve->type = ty_nomem; ve->next = NULL;
|
||||
tt->params = vp;
|
||||
tt->size = 16;
|
||||
tt->align = 8;
|
||||
n->type = tt;
|
||||
n->lhs->type = ty_err;
|
||||
return n->type;
|
||||
}
|
||||
@@ -1046,7 +1061,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);
|
||||
n->type = type_slice(c->a, ty_u8);
|
||||
Type *st = type_slice(c->a, ty_u8);
|
||||
/* Task #30 — slice form graduates the same way:
|
||||
* `alloc([], n)` now returns `([]T | nomem)`. Slot is
|
||||
* 8 (tag) + 24 (slice payload) = 32B. The element type
|
||||
* defaults to u8 here; the let-init shortcut in
|
||||
* cmd/w6c/cgen.c N_LET drives the real element size
|
||||
* from the declared slice type. */
|
||||
Type *tt = newtype(c->a, TY_TAGGED);
|
||||
Tparam *vs = amalloc(c->a, sizeof *vs);
|
||||
Tparam *ve = amalloc(c->a, sizeof *ve);
|
||||
vs->type = st; vs->next = ve;
|
||||
ve->type = ty_nomem; ve->next = NULL;
|
||||
tt->params = vs;
|
||||
tt->size = 32;
|
||||
tt->align = 8;
|
||||
n->type = tt;
|
||||
n->lhs->type = ty_err;
|
||||
return n->type;
|
||||
}
|
||||
|
||||
10
rt/alloc.s
10
rt/alloc.s
@@ -5,6 +5,12 @@
|
||||
//
|
||||
// We pin to PROT_READ|PROT_WRITE and MAP_PRIVATE|MAP_ANONYMOUS so
|
||||
// callers never have to plumb file descriptors through.
|
||||
//
|
||||
// On mmap failure the raw syscall returns -errno (negative). Task #30
|
||||
// graduated the `alloc` builtin to a fallible `(*T | nomem)` /
|
||||
// `([]T | nomem)` signature whose cgen branches on a null return, so
|
||||
// the failure path here returns 0 instead of a poisoned pointer. The
|
||||
// builtin's caller is expected to `!`/`?` the result.
|
||||
|
||||
TEXT rt_alloc,$0
|
||||
MOVQ DI, SI // arg 1: length = caller's n
|
||||
@@ -15,6 +21,10 @@ TEXT rt_alloc,$0
|
||||
MOVQ $0, R9 // arg 5: offset = 0
|
||||
MOVQ $9, AX // syscall: mmap
|
||||
SYSCALL
|
||||
CMPQ $0, AX
|
||||
JGE rt_alloc_ok
|
||||
XORQ AX, AX
|
||||
rt_alloc_ok:
|
||||
RET
|
||||
|
||||
TEXT rt_free,$0
|
||||
|
||||
@@ -37,7 +37,12 @@ export fn rt_ensure(s: *slice, membsz: u64) void = {
|
||||
let nc: i64 = s.cap * 2i64;
|
||||
if (nc < 8i64) { nc = 8i64; };
|
||||
for (nc < s.len) { nc *= 2i64; };
|
||||
let np: *u8 = alloc((nc: u64) * membsz): *u8;
|
||||
// Task #30: the alloc builtin returns `(*T | nomem)`; `!` aborts
|
||||
// on OOM. The longstanding sizeof-only allocation bug (task #27)
|
||||
// stays unfixed here — rt_ensure presumes a same-module `fn
|
||||
// alloc(n)` resolution that the bare cur_mod=NULL gate at
|
||||
// cmd/wcc/check.c:984 doesn't honour.
|
||||
let np: *u8 = alloc((nc: u64) * membsz)!: *u8;
|
||||
let n: u64 = (s.cap: u64) * membsz;
|
||||
let i: u64 = 0u64;
|
||||
for (i < n) {
|
||||
|
||||
@@ -14581,7 +14581,14 @@ fn cgbin(c: *cgen, n: *node) void = {
|
||||
// region. For an N_STRUCTLIT arg, allocate the struct's totsize and
|
||||
// emit per-field stores at each field's offset. For a scalar/ptr,
|
||||
// allocate 8 bytes and store one word. Mirrors cmd/w6c/cgen.c's
|
||||
// alloc-special branch in N_CALL. Returns the heap ptr in AX.
|
||||
// alloc-special branch in N_CALL.
|
||||
//
|
||||
// Task #30: result is the graduated `(*T | nomem)` tagged-pointer
|
||||
// pair (AX=tag, DX=ptr). rt_alloc now returns 0 on OOM
|
||||
// (rt/alloc.s); branch on AX to emit the nomem variant (tag=1,
|
||||
// DX=0) or the success variant (tag=0, DX=ptr) after the
|
||||
// value-init stores complete. Callers wrap with `!` / `?` to
|
||||
// consume the union.
|
||||
fn cgalloc(c: *cgen, n: *node) void = {
|
||||
let v: *node = n.list;
|
||||
let sz: i32 = 8;
|
||||
@@ -14597,10 +14604,18 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
si = structlookup(c, sname);
|
||||
if (si != nil) { sz = si.totsize; };
|
||||
};
|
||||
let okl: str = mklabel(c, "alloc_ok");
|
||||
let donel: str = mklabel(c, "alloc_done");
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(sz: i64);
|
||||
emitline(", DI\n");
|
||||
emitline("\tCALL\trt_alloc(SB)\n");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJNE\t"); emitline(okl); emitline("\n");
|
||||
emitline("\tMOVQ\t$1, AX\n");
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tJMP\t"); emitline(donel); emitline("\n");
|
||||
emitlabel(okl);
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
if (v.kind == nkind.N_STRUCTLIT) {
|
||||
if (si != nil) {
|
||||
@@ -14653,7 +14668,9 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
emitline(sop);
|
||||
emitline("\tAX, (BX)\n");
|
||||
};
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tPOPQ\tDX\n");
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
emitlabel(donel);
|
||||
};
|
||||
|
||||
// cgappend — Hare-style `append(s, v)` / `append(s, items...)` lowering.
|
||||
|
||||
@@ -2711,7 +2711,14 @@ fn cgbin(c: *cgen, n: *node) void = {
|
||||
// region. For an N_STRUCTLIT arg, allocate the struct's totsize and
|
||||
// emit per-field stores at each field's offset. For a scalar/ptr,
|
||||
// allocate 8 bytes and store one word. Mirrors cmd/w6c/cgen.c's
|
||||
// alloc-special branch in N_CALL. Returns the heap ptr in AX.
|
||||
// alloc-special branch in N_CALL.
|
||||
//
|
||||
// Task #30: result is the graduated `(*T | nomem)` tagged-pointer
|
||||
// pair (AX=tag, DX=ptr). rt_alloc now returns 0 on OOM
|
||||
// (rt/alloc.s); branch on AX to emit the nomem variant (tag=1,
|
||||
// DX=0) or the success variant (tag=0, DX=ptr) after the
|
||||
// value-init stores complete. Callers wrap with `!` / `?` to
|
||||
// consume the union.
|
||||
fn cgalloc(c: *cgen, n: *node) void = {
|
||||
let v: *node = n.list;
|
||||
let sz: i32 = 8;
|
||||
@@ -2727,10 +2734,18 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
si = structlookup(c, sname);
|
||||
if (si != nil) { sz = si.totsize; };
|
||||
};
|
||||
let okl: str = mklabel(c, "alloc_ok");
|
||||
let donel: str = mklabel(c, "alloc_done");
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(sz: i64);
|
||||
emitline(", DI\n");
|
||||
emitline("\tCALL\trt_alloc(SB)\n");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJNE\t"); emitline(okl); emitline("\n");
|
||||
emitline("\tMOVQ\t$1, AX\n");
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tJMP\t"); emitline(donel); emitline("\n");
|
||||
emitlabel(okl);
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
if (v.kind == nkind.N_STRUCTLIT) {
|
||||
if (si != nil) {
|
||||
@@ -2783,7 +2798,9 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
emitline(sop);
|
||||
emitline("\tAX, (BX)\n");
|
||||
};
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tPOPQ\tDX\n");
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
emitlabel(donel);
|
||||
};
|
||||
|
||||
// cgappend — Hare-style `append(s, v)` / `append(s, items...)` lowering.
|
||||
|
||||
@@ -14581,7 +14581,14 @@ fn cgbin(c: *cgen, n: *node) void = {
|
||||
// region. For an N_STRUCTLIT arg, allocate the struct's totsize and
|
||||
// emit per-field stores at each field's offset. For a scalar/ptr,
|
||||
// allocate 8 bytes and store one word. Mirrors cmd/w6c/cgen.c's
|
||||
// alloc-special branch in N_CALL. Returns the heap ptr in AX.
|
||||
// alloc-special branch in N_CALL.
|
||||
//
|
||||
// Task #30: result is the graduated `(*T | nomem)` tagged-pointer
|
||||
// pair (AX=tag, DX=ptr). rt_alloc now returns 0 on OOM
|
||||
// (rt/alloc.s); branch on AX to emit the nomem variant (tag=1,
|
||||
// DX=0) or the success variant (tag=0, DX=ptr) after the
|
||||
// value-init stores complete. Callers wrap with `!` / `?` to
|
||||
// consume the union.
|
||||
fn cgalloc(c: *cgen, n: *node) void = {
|
||||
let v: *node = n.list;
|
||||
let sz: i32 = 8;
|
||||
@@ -14597,10 +14604,18 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
si = structlookup(c, sname);
|
||||
if (si != nil) { sz = si.totsize; };
|
||||
};
|
||||
let okl: str = mklabel(c, "alloc_ok");
|
||||
let donel: str = mklabel(c, "alloc_done");
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(sz: i64);
|
||||
emitline(", DI\n");
|
||||
emitline("\tCALL\trt_alloc(SB)\n");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJNE\t"); emitline(okl); emitline("\n");
|
||||
emitline("\tMOVQ\t$1, AX\n");
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tJMP\t"); emitline(donel); emitline("\n");
|
||||
emitlabel(okl);
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
if (v.kind == nkind.N_STRUCTLIT) {
|
||||
if (si != nil) {
|
||||
@@ -14653,7 +14668,9 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
emitline(sop);
|
||||
emitline("\tAX, (BX)\n");
|
||||
};
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tPOPQ\tDX\n");
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
emitlabel(donel);
|
||||
};
|
||||
|
||||
// cgappend — Hare-style `append(s, v)` / `append(s, items...)` lowering.
|
||||
|
||||
@@ -13,10 +13,19 @@
|
||||
// Two match arms cover both runtime outcomes — success unwrap (tag=0,
|
||||
// ptr payload in DX) and error propagation (tag=1) — exercising the
|
||||
// same AX/DX ABI both stages must agree on.
|
||||
//
|
||||
// Task #30: the `alloc` builtin itself now returns `(*T | nomem)`. The
|
||||
// allocbox arm below propagates the builtin's tagged return through
|
||||
// the enclosing fn via `?` against a same-shape `(*point | nomem)`
|
||||
// — matching the team-lead spec's "exercise `alloc(T)?` against a
|
||||
// real function returning `(T | nomem)`".
|
||||
|
||||
package test;
|
||||
package main;
|
||||
|
||||
import fmt;
|
||||
import os;
|
||||
|
||||
type point = struct { x: i32, y: i32 };
|
||||
|
||||
fn alloc1(fail: i64) (*u8 | nomem) = {
|
||||
if (fail != 0i64) { let e: nomem; return e; };
|
||||
@@ -29,6 +38,11 @@ fn caller(fail: i64) (*u8 | nomem) = {
|
||||
return p;
|
||||
};
|
||||
|
||||
fn allocbox() (*point | nomem) = {
|
||||
let p: *point = alloc(point { x = 3, y = 4 })?;
|
||||
return p;
|
||||
};
|
||||
|
||||
export fn main() i32 = {
|
||||
let rc: i32 = 0;
|
||||
match (caller(0i64)) {
|
||||
@@ -50,5 +64,15 @@ export fn main() i32 = {
|
||||
fmt.println("nomem as expected");
|
||||
};
|
||||
};
|
||||
match (allocbox()) {
|
||||
case let p: *point => {
|
||||
fmt.println("allocbox ok");
|
||||
if (p.x * p.x + p.y * p.y != 25) { rc = 4; };
|
||||
};
|
||||
case nomem => {
|
||||
fmt.println("allocbox unexpected nomem");
|
||||
rc = 5;
|
||||
};
|
||||
};
|
||||
return rc;
|
||||
};
|
||||
|
||||
@@ -277,12 +277,13 @@ static const struct row rows[] = {
|
||||
/* alloc() builtin: heap-allocate a struct, init from struct-lit.
|
||||
* `package main;` is required so the bare-alloc-builtin gate
|
||||
* (task #23) sees c->cur_mod=="main" and doesn't suppress the
|
||||
* builtin via the inherited os.alloc decl. */
|
||||
* builtin via the inherited os.alloc decl. Task #30 graduated
|
||||
* the builtin to `(*T | nomem)`; the `!` aborts on OOM. */
|
||||
{ "package main;\n"
|
||||
"import os;\n"
|
||||
"type point = struct { x: i32, y: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let p: *point = alloc(point { x = 3, y = 4 });\n"
|
||||
" let p: *point = alloc(point { x = 3, y = 4 })!;\n"
|
||||
" return p.x * p.x + p.y * p.y;\n"
|
||||
"};", 25 },
|
||||
/* Hare-style range loop: for (let x .. slice) iterates elements */
|
||||
@@ -296,11 +297,13 @@ static const struct row rows[] = {
|
||||
" return total;\n"
|
||||
"};", 100 },
|
||||
/* alloc([], n): fresh empty slice with cap n. `package main;` for
|
||||
* the same reason as the value-form test above (task #23 gate). */
|
||||
* the same reason as the value-form test above (task #23 gate).
|
||||
* Task #30 graduated the slice form to `([]T | nomem)`; the `!`
|
||||
* aborts on OOM. */
|
||||
{ "package main;\n"
|
||||
"import os;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let s: []u8 = alloc([], 16);\n"
|
||||
" let s: []u8 = alloc([], 16)!;\n"
|
||||
" append(s, 72u8, 105u8);\n"
|
||||
" return s.cap;\n"
|
||||
"};", 16 },
|
||||
@@ -358,12 +361,13 @@ static const struct row rows[] = {
|
||||
" return (t.0 + t.1): i32;\n"
|
||||
"};", 42 },
|
||||
/* Hare-style abort/assert + free() builtin. `package main;` for
|
||||
* the alloc-builtin gate (task #23). */
|
||||
* the alloc-builtin gate (task #23). Task #30 graduated the
|
||||
* builtin to a fallible signature; the `!` aborts on OOM. */
|
||||
{ "package main;\n"
|
||||
"import os;\n"
|
||||
"type point = struct { x: i64, y: i64 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let p: *point = alloc(point { x = 7, y = 35 });\n"
|
||||
" let p: *point = alloc(point { x = 7, y = 35 })!;\n"
|
||||
" let r: i64 = p.x + p.y;\n"
|
||||
" free(p);\n"
|
||||
" os.assert(r == 42, \"sum mismatch\\n\");\n"
|
||||
|
||||
Reference in New Issue
Block a user