wcc-ww: assert/abort builtins — checker tag + cgen rt_abort lowering (#58)
wwstage had no EXPR_ASSERT-family intercept: the checker left bare assert/abort calls untyped (asserttyped gate 4 skipped them by design) and cgcall fell through to the regular call path, emitting CALL assert(SB) for a symbol that exists nowhere — link-fail. cstage was already correct (tag ty_err at check.c:1536-1572, lower inline via rt_abort at cgen.c:6618-6663). Mirror the same tag-then-lower pair: exprtype N_CALL stamps the call void and the callee TY_ERR behind the scopelookupprefer no-shadow gate (the isassertfam predicate), with the cstage arg diagnostics (cond must be bool, msg must be str, arity caps); cgcall keys on the TY_ERR tag and emits the identical CMPQ/JNE/rt_abort sequence. A user-shadowed assert/abort (same-module or cross-module, the #45 shape, task #14) stays untagged on the regular call path — byte-id for all existing lib code preserved. The cond check does NOT alias-peel: cstage compares ty_bool by identity (check.c:1560), so `type myb = bool` is rejected there; wwstage aligns down per rule 10 (a resolvealias here was accepting it — cs/ww accept-reject divergence). Widening both stages together belongs to the alias-peel choke-point arc (task #5, #47/#68). The resolvewalk N_IDENT resolution counter learns the builtin shape: an unshadowed abort/assert ident binds no sym BY DESIGN, so wwdump -r's zero-unresolved gate (990 probe 4) counts it resolved instead of failing builtin-using units. test 957: 13 rows — pass/fail/msg/bare abort (run exit + rt_abort stderr content; no-msg rows pin EMPTY stderr = the (NULL,0) shape), assert in an imported module, same-module + cross-module shadow controls, 5 checker rejects pinned on diagnostic CONTENT (shared substring; cstage prefixes pos, wwstage cerr is bare) incl. the alias-of-bool cond row pinning the rule-10 down-alignment; each positive row pins cstage run exit + cs==ww byte-id. On pre-fix master 11/13 rows trip (survivors = the two shadow controls). Residual (separate root, deferred diagnostic class): zero-arg assert() is not intercepted by either stage; cstage rejects via the generic undefined-ident path, wwstage's undefined-callee diagnostic is the class deferred behind wiring checkfile into w6c_ww.
This commit is contained in:
@@ -5634,6 +5634,46 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
let callee: *node = n.lhs;
|
||||
if (callee != nil) {
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
// abort([msg]) / assert(cond[, msg]) — checker-tagged
|
||||
// builtins (callee TY_ERR is the routing key, cstage's
|
||||
// `lhs->type == ty_err` at cmd/w6c/cgen.c:6618,6645);
|
||||
// lower to rt_abort. A user-shadowed abort/assert is
|
||||
// untagged and stays on the regular call path (#58).
|
||||
let bti: *tinfo = callee.type_: *tinfo;
|
||||
if (bti != nil && bti.kind == tykind.TY_ERR) {
|
||||
if (streq(callee.str, "abort")) {
|
||||
if (n.list != nil) {
|
||||
cgexpr(c, n.list);
|
||||
emitline("\tMOVQ\tAX, DI\n");
|
||||
emitline("\tMOVQ\tBX, SI\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t$0, DI\n");
|
||||
emitline("\tMOVQ\t$0, SI\n");
|
||||
};
|
||||
emitline("\tCALL\trt_abort(SB)\n");
|
||||
return;
|
||||
};
|
||||
if (streq(callee.str, "assert") && n.list != nil) {
|
||||
cgexpr(c, n.list);
|
||||
let skip: str = mklabel(c, "as");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJNE\t");
|
||||
emitline(skip);
|
||||
emitline("\n");
|
||||
let msg: *node = n.list.next;
|
||||
if (msg != nil) {
|
||||
cgexpr(c, msg);
|
||||
emitline("\tMOVQ\tAX, DI\n");
|
||||
emitline("\tMOVQ\tBX, SI\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t$0, DI\n");
|
||||
emitline("\tMOVQ\t$0, SI\n");
|
||||
};
|
||||
emitline("\tCALL\trt_abort(SB)\n");
|
||||
emitlabel(skip);
|
||||
return;
|
||||
};
|
||||
};
|
||||
if (streq(callee.str, "append")) {
|
||||
if (n.list != nil) {
|
||||
if (n.list.next != nil) {
|
||||
|
||||
Reference in New Issue
Block a user