Final closer for the A.6.2 sequence. Mirror of harec's `assert(expr->result)` at ref/harec/src/check.c:3810: every value- producing nkind dispatched by resolvewalk (L475-488 + N_DOT at L391) reaches a stamping arm in exprtype that sets e.type_ before returning. asserttyped is the post-checker invariant gate; it walks checkfile's decls in pass 3 (same curmod context exprtype saw in pass 2) and writes a one-line stderr diagnostic for any dispatched node whose type_ remained nil. Three residual gates encode bails that aren't true gaps until #19 (Drew's δ: dedicated AST kinds for alloc/size/etc.) retires the seeded-SK_FN-with-nil-decl + SK_USE-as-value shapes: 1. N_IDENT resolving to SK_USE (module ref like `os` in `os.write`) 2. N_IDENT whose sym.decl == nil (pseudo-builtin callee — len, append, free, alloc, size, align, offset seeded at L86-98) 3. N_IDENT in LHS-of-N_DOT syntactic position (member-access lookup target, not value-producing) — tracked via `indot` param ZERO fires across all 5 selfhost combined.ww corpora (wcc, w6c, w6a, w6l, wwdump). asserttyped IS the regression catch — future commits that drop a type_ stamp will fire it during the 990_selfhost probes; no standalone table-driven test is bundled. Accreted folds: - 5-lite-a (#33): dispatcher-invariant docstring at exprtype L1535. - 5-lite-b (#34): WHY comments at 9 helper bail sites (unifyarith/binoptype/unoptype/indexresult) classifying each as unreachable-for-valid-input, propagation-from-callee, or invalid-input (cstage errors at the matching cite). Cites ref/harec/src/types.c type_promote on the function-doc updates. - A.6.2.1c (#24): three propagation pointers re-cite the inherent-IDENT bail at exprtype N_IDENT arm L1596-1599. - unoptype TK_STAR dead `if (u == nil) { return nil; }` removed — resolvealias(unwrapbang(non-nil)) is non-nil by parser invariant (parsetype L148 always sets N_TBANG.lhs; resolvealias L514-569 every exit returns non-nil for non-nil input). lib/ww/ast.ww: nkname becomes export so asserttyped's diagnostic can format the offending node's kind without duplicating the table. Closes #4 (A.6.2 umbrella) and #15 (A.6.2.1e).
This commit is contained in:
@@ -149,7 +149,7 @@ export fn newnode(k: nkind, file: str, line: i32, col: i32) *node = {
|
||||
|
||||
// ---- printer ----------------------------------------------------------
|
||||
|
||||
fn nkname(k: nkind) str = {
|
||||
export fn nkname(k: nkind) str = {
|
||||
if (k == nkind.N_NONE) { return "none"; };
|
||||
if (k == nkind.N_INTLIT) { return "int"; };
|
||||
if (k == nkind.N_FLOATLIT) { return "float"; };
|
||||
|
||||
@@ -4569,7 +4569,7 @@ export fn newnode(k: nkind, file: str, line: i32, col: i32) *node = {
|
||||
|
||||
// ---- printer ----------------------------------------------------------
|
||||
|
||||
fn nkname(k: nkind) str = {
|
||||
export fn nkname(k: nkind) str = {
|
||||
if (k == nkind.N_NONE) { return "none"; };
|
||||
if (k == nkind.N_INTLIT) { return "int"; };
|
||||
if (k == nkind.N_FLOATLIT) { return "float"; };
|
||||
@@ -6985,6 +6985,7 @@ package wcc;
|
||||
|
||||
import os;
|
||||
import tok;
|
||||
import strconv;
|
||||
|
||||
type checker = struct {
|
||||
tc: *tctx,
|
||||
@@ -8310,10 +8311,17 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
|
||||
};
|
||||
|
||||
// unifyarith — usual-arithmetic-conversion analogue at the AST-tnode
|
||||
// layer. Mirrors cstage cmd/wcc/check.c:580-596 `unify_arith`. The
|
||||
// trailing typeeq fallback covers same-typed operands; mismatched typed
|
||||
// pairs silently return ltn (cstage flags the same shape — wwstage's
|
||||
// checker stays silent here per existing discipline).
|
||||
// layer. Mirrors cstage cmd/wcc/check.c:580-596 `unify_arith` and harec
|
||||
// ref/harec/src/types.c type_promote. Trailing `return ltn` covers
|
||||
// mismatched typed pairs; cstage flags the same shape — wwstage's
|
||||
// checker stays silent here per existing discipline.
|
||||
//
|
||||
// Nil-on-valid classification (5-lite-b #34, A.6.2.1c #24): both ltn
|
||||
// and rtn can be nil when an operand was an inherent-IDENT bail
|
||||
// (exprtype N_IDENT arm L1596-1599 — SK_USE module ref or pseudo-
|
||||
// builtin callee with sym.decl == nil; #19 retires these as
|
||||
// dedicated AST kinds). Propagation, not silent gap — asserttyped
|
||||
// gates those idents at the consumer layer.
|
||||
fn unifyarith(c: *checker, ltn: *node, rtn: *node) *node = {
|
||||
let lu: bool = isuntypedint(ltn) || isuntypedfloat(ltn);
|
||||
let ru: bool = isuntypedint(rtn) || isuntypedfloat(rtn);
|
||||
@@ -8331,6 +8339,9 @@ fn unifyarith(c: *checker, ltn: *node, rtn: *node) *node = {
|
||||
if (isassignable(c, ltn, rtn, &conf)) { return ltn; };
|
||||
};
|
||||
if (typeeqast(ltn, rtn)) { return ltn; };
|
||||
// Mismatched typed pair — return ltn so the binop stamps something;
|
||||
// 5-lite-b: the trailing nil-on-mismatch shape was eliminated when
|
||||
// the helper was split out of binoptype.
|
||||
return ltn;
|
||||
};
|
||||
|
||||
@@ -8372,13 +8383,18 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
op == tkind.TK_AND || op == tkind.TK_OR) {
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
// Unreachable for valid input: op is one of TK_PLUS/MINUS/STAR/
|
||||
// SLASH/PERCENT/AMP/PIPE/CARET/LSHIFT/RSHIFT/EQ/NEQ/LT/LE/GT/GE/
|
||||
// AND/OR per parser invariant (lib/ww/parse/expr.ww binary-op
|
||||
// table); all are handled above. 5-lite-b #34.
|
||||
return nil;
|
||||
};
|
||||
|
||||
// unoptype — derive the result tnode of an N_UN unary expression. Mirrors
|
||||
// cstage cmd/wcc/check.c:642-687 `cunop`. The slice/str .len/.cap
|
||||
// pseudo-field address-of widening to *i64 mirrors check.c:672-682
|
||||
// directly — its purpose is documented at the cstage site.
|
||||
// cstage cmd/wcc/check.c:642-687 `cunop` and harec ref/harec/src/types.c
|
||||
// type_promote. The slice/str .len/.cap pseudo-field address-of widening
|
||||
// to *i64 mirrors check.c:672-682 directly — its purpose is documented
|
||||
// at the cstage site.
|
||||
fn unoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let opt: *node = exprtype(c, e.lhs, nil);
|
||||
@@ -8386,9 +8402,18 @@ fn unoptype(c: *checker, e: *node) *node = {
|
||||
if (op == tkind.TK_NOT) { return mktname(c, "bool"); };
|
||||
if (op == tkind.TK_TILDE) { return opt; };
|
||||
if (op == tkind.TK_STAR) {
|
||||
// opt nil here means the operand was an inherent-IDENT bail
|
||||
// (exprtype N_IDENT arm L1596-1599 — SK_USE / pseudo-builtin
|
||||
// sym.decl == nil — or another helper's nil propagation);
|
||||
// 5-lite-b #34, A.6.2.1c #24. The `u == nil` post-resolvealias
|
||||
// check was eliminated here — unwrapbang(non-nil) returns
|
||||
// non-nil (parser invariant N_TBANG.lhs always set) and
|
||||
// resolvealias passes through non-nil unchanged (L513
|
||||
// `for (cur != nil)` only exits via `return cur` or `return n`).
|
||||
if (opt == nil) { return nil; };
|
||||
let u: *node = resolvealias(c, unwrapbang(opt));
|
||||
if (u == nil) { return nil; };
|
||||
// Invalid input (non-pointer dereference); cstage errors at
|
||||
// cmd/wcc/check.c:660. 5-lite-b #34.
|
||||
if (u.kind != nkind.N_TPTR) { return nil; };
|
||||
return u.lhs;
|
||||
};
|
||||
@@ -8412,23 +8437,33 @@ fn unoptype(c: *checker, e: *node) *node = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// opt nil → propagation from inherent-IDENT bail (5-lite-b
|
||||
// #34). Generic &expr widens to *opt; without opt we can't
|
||||
// synthesize the pointer node.
|
||||
if (opt == nil) { return nil; };
|
||||
let pp: *node = newnode(nkind.N_TPTR, "", 0, 0);
|
||||
pp.lhs = opt;
|
||||
return pp;
|
||||
};
|
||||
// Unreachable for valid input: op is one of TK_MINUS/PLUS/NOT/
|
||||
// TILDE/STAR/AMP per parser invariant (lib/ww/parse/expr.ww unary
|
||||
// op set); all are handled above. 5-lite-b #34.
|
||||
return nil;
|
||||
};
|
||||
|
||||
// indexresult — derive the result tnode of an N_INDEX expression.
|
||||
// Mirrors cstage cmd/wcc/check.c:870-894. Slice/array → elem; str →
|
||||
// u8; `*[N]T` decays to T (pointer-to-array); `*[]T` does NOT decay
|
||||
// (yields []T via the generic *U → U fallback — Hare-faithful, a
|
||||
// pointer-to-slice is a 1D array of slices, not of T); generic *T → T.
|
||||
// Mirrors cstage cmd/wcc/check.c:870-894 and harec ref/harec/src/types.c
|
||||
// type_promote dispatch. Slice/array → elem; str → u8; `*[N]T` decays
|
||||
// to T (pointer-to-array); `*[]T` does NOT decay (yields []T via the
|
||||
// generic *U → U fallback — Hare-faithful, a pointer-to-slice is a 1D
|
||||
// array of slices, not of T); generic *T → T.
|
||||
fn indexresult(c: *checker, e: *node) *node = {
|
||||
let basetn: *node = exprtype(c, e.lhs, nil);
|
||||
let _idx: *node = exprtype(c, e.rhs, nil);
|
||||
let u: *node = resolvealias(c, unwrapbang(basetn));
|
||||
// basetn nil → propagation from inherent-IDENT bail at exprtype
|
||||
// N_IDENT arm L1596-1599 (5-lite-b #34, A.6.2.1c #24).
|
||||
// unwrapbang(nil)=nil and resolvealias(nil)=nil pass through.
|
||||
if (u == nil) { return nil; };
|
||||
if (u.kind == nkind.N_TSLICE) { return u.lhs; };
|
||||
if (u.kind == nkind.N_TARRAY) { return u.lhs; };
|
||||
@@ -8443,6 +8478,8 @@ fn indexresult(c: *checker, e: *node) *node = {
|
||||
};
|
||||
return inner;
|
||||
};
|
||||
// Invalid input (non-indexable base — cstage errors at
|
||||
// cmd/wcc/check.c:893). 5-lite-b #34.
|
||||
return nil;
|
||||
};
|
||||
|
||||
@@ -8459,6 +8496,19 @@ fn indexresult(c: *checker, e: *node) *node = {
|
||||
// follows doesn't ripple a fresh signature change. Mirrors harec's
|
||||
// `check_expression(..., result_type, ...)` per
|
||||
// `feedback_hare_frontend_reference.md`.
|
||||
//
|
||||
// Dispatcher invariant (5-lite-a #33): every value-producing nkind
|
||||
// listed in resolvewalk's post-order dispatch (L474-489) reaches a
|
||||
// stamping arm here that sets e.type_ before returning. No
|
||||
// fall-through. Arms that return nil (binoptype trailing, unoptype
|
||||
// TK_STAR opt-nil, indexresult u-nil, N_DOT outer fold-miss, N_SLICE
|
||||
// non-sliceable base, etc.) are propagation from a callee's nil —
|
||||
// not silent gaps. Mirror of harec's
|
||||
// `assert(expr->result)` at ref/harec/src/check.c:3810. The
|
||||
// asserttyped pass at L2871 enforces the invariant on every
|
||||
// dispatched node post-checker, with gates for the residual
|
||||
// inherent-IDENT bails (SK_USE, pseudo-builtin sym.decl==nil,
|
||||
// N_DOT-LHS syntactic position) until #19 retires the bail shape.
|
||||
fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
if (e == nil) { return nil; };
|
||||
let k: nkind = e.kind;
|
||||
@@ -9753,6 +9803,101 @@ fn resolvefnbody(c: *checker, fnnode: *node) void = {
|
||||
c.cur = outer;
|
||||
};
|
||||
|
||||
// asserttyped — post-checker invariant gate (#15, A.6.2.1e). Walks the
|
||||
// file tree and fires (writes a one-line diagnostic to stderr) for any
|
||||
// node in resolvewalk's value-producing dispatch set (L474-489) whose
|
||||
// n.type_ remained nil. Mirror of harec's `assert(expr->result)` at
|
||||
// ref/harec/src/check.c:3810 — wwstage's checker is lenient (rule 7),
|
||||
// so this is a soft assertion (diagnostic, not abort) used by the
|
||||
// 990_selfhost probes to catch regressions in the stamping discipline.
|
||||
//
|
||||
// Gates (per Drew 2026-05-22 — "guards value-producing expression
|
||||
// nodes; SK_USE refs and bare builtin callees are syntactic positions,
|
||||
// gate them out with WHY pointing at #19"):
|
||||
//
|
||||
// 1. N_IDENT whose resolved sym kind is SK_USE — module references
|
||||
// (`os` in `os.write`). Harec models these via EXPR_ACCESS whose
|
||||
// lookup-target is an OBJ_USE directly; there is no intermediate
|
||||
// "ident-as-value" expr. Until #19 ports that AST shape, skip.
|
||||
// 2. N_IDENT whose resolved sym has decl == nil — pseudo-builtin
|
||||
// callees (len/append/free/alloc/size/align/offset, seeded in
|
||||
// checkinit L85-97 with decl=nil). Harec spells these as
|
||||
// dedicated EXPR_* kinds (EXPR_LEN, EXPR_APPEND, EXPR_FREE,
|
||||
// EXPR_ALLOC at ref/harec/src/check.c:2630/745/2443/...).
|
||||
// Drew's δ (#19) retires the seeded-SK_FN-with-nil-decl hack.
|
||||
// 3. N_IDENT at the LHS-of-N_DOT syntactic position — the bare
|
||||
// name half of a member-access expr is a lookup target, not a
|
||||
// value-producing sub-expression. Harec's EXPR_ACCESS stores the
|
||||
// member as a string, not a node.
|
||||
//
|
||||
// `indot` tracks gate 3: true only when the immediate caller is an
|
||||
// N_DOT recursing into its .lhs.
|
||||
fn asserttyped(c: *checker, n: *node, indot: bool) void = {
|
||||
if (n == nil) { return; };
|
||||
let k: nkind = n.kind;
|
||||
let isexpr: bool =
|
||||
k == nkind.N_INTLIT || k == nkind.N_FLOATLIT ||
|
||||
k == nkind.N_STRLIT || k == nkind.N_RUNELIT ||
|
||||
k == nkind.N_TRUE || k == nkind.N_FALSE ||
|
||||
k == nkind.N_NIL || k == nkind.N_VOIDLIT ||
|
||||
k == nkind.N_IDENT || k == nkind.N_BIN ||
|
||||
k == nkind.N_UN || k == nkind.N_CALL ||
|
||||
k == nkind.N_INDEX || k == nkind.N_CAST ||
|
||||
k == nkind.N_STRUCTLIT || k == nkind.N_ARRLIT ||
|
||||
k == nkind.N_RECV || k == nkind.N_DOT ||
|
||||
k == nkind.N_SLICE || k == nkind.N_SPREAD ||
|
||||
k == nkind.N_TUPLE || k == nkind.N_TRYPROP ||
|
||||
k == nkind.N_TRYUNW || k == nkind.N_TYPETEST ||
|
||||
k == nkind.N_TYPEASSERT || k == nkind.N_YIELD ||
|
||||
k == nkind.N_MATCH;
|
||||
let skip: bool = false;
|
||||
if (isexpr && k == nkind.N_IDENT) {
|
||||
if (indot) { skip = true; };
|
||||
if (!skip) {
|
||||
let s: *sym = scopelookup(c.cur, n.str);
|
||||
if (s != nil) {
|
||||
if (s.skind == skind.SK_USE) { skip = true; };
|
||||
if (s.decl == nil) { skip = true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
if (isexpr && !skip) {
|
||||
if (n.type_ == nil) {
|
||||
os.write(2, "asserttyped: ".ptr, 13u64);
|
||||
let kn: str = nkname(k);
|
||||
os.write(2, kn.ptr, kn.len: u64);
|
||||
os.write(2, " ".ptr, 1u64);
|
||||
if (n.file.len > 0) {
|
||||
os.write(2, n.file.ptr, n.file.len: u64);
|
||||
os.write(2, ":".ptr, 1u64);
|
||||
let ls: str = strconv.i32tos(n.line, strconv.base.DEC);
|
||||
os.write(2, ls.ptr, ls.len: u64);
|
||||
};
|
||||
if (n.str.len > 0) {
|
||||
os.write(2, " '".ptr, 2u64);
|
||||
os.write(2, n.str.ptr, n.str.len: u64);
|
||||
os.write(2, "'".ptr, 1u64);
|
||||
};
|
||||
os.write(2, "\n".ptr, 1u64);
|
||||
};
|
||||
};
|
||||
if (k == nkind.N_DOT) {
|
||||
if (n.lhs != nil) { asserttyped(c, n.lhs, true); };
|
||||
return;
|
||||
};
|
||||
if (n.attr != nil) { asserttyped(c, n.attr, false); };
|
||||
if (n.lhs != nil) { asserttyped(c, n.lhs, false); };
|
||||
if (n.rhs != nil) { asserttyped(c, n.rhs, false); };
|
||||
if (n.cond != nil) { asserttyped(c, n.cond, false); };
|
||||
if (n.body != nil) { asserttyped(c, n.body, false); };
|
||||
if (n.els != nil) { asserttyped(c, n.els, false); };
|
||||
let m: *node = n.list;
|
||||
for (m != nil) {
|
||||
asserttyped(c, m, false);
|
||||
m = m.next;
|
||||
};
|
||||
};
|
||||
|
||||
export fn checkinit(c: *checker, tc: *tctx) void = {
|
||||
c.tc = tc;
|
||||
c.top = newscope(nil);
|
||||
@@ -9808,6 +9953,17 @@ export fn checkfile(c: *checker, file: *node) void = {
|
||||
};};};};
|
||||
d = d.next;
|
||||
};
|
||||
|
||||
// Pass 3 (#15, A.6.2.1e): post-checker invariant gate. Walks each
|
||||
// decl with its curmod set so asserttyped's gate lookups resolve
|
||||
// against the same module context exprtype saw during pass 2.
|
||||
d = file.list;
|
||||
for (d != nil) {
|
||||
c.curmod = declmod(file, d);
|
||||
asserttyped(c, d, false);
|
||||
d = d.next;
|
||||
};
|
||||
|
||||
let empty: str;
|
||||
c.curmod = empty;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ package wcc;
|
||||
|
||||
import os;
|
||||
import tok;
|
||||
import strconv;
|
||||
|
||||
type checker = struct {
|
||||
tc: *tctx,
|
||||
@@ -1346,10 +1347,17 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
|
||||
};
|
||||
|
||||
// unifyarith — usual-arithmetic-conversion analogue at the AST-tnode
|
||||
// layer. Mirrors cstage cmd/wcc/check.c:580-596 `unify_arith`. The
|
||||
// trailing typeeq fallback covers same-typed operands; mismatched typed
|
||||
// pairs silently return ltn (cstage flags the same shape — wwstage's
|
||||
// checker stays silent here per existing discipline).
|
||||
// layer. Mirrors cstage cmd/wcc/check.c:580-596 `unify_arith` and harec
|
||||
// ref/harec/src/types.c type_promote. Trailing `return ltn` covers
|
||||
// mismatched typed pairs; cstage flags the same shape — wwstage's
|
||||
// checker stays silent here per existing discipline.
|
||||
//
|
||||
// Nil-on-valid classification (5-lite-b #34, A.6.2.1c #24): both ltn
|
||||
// and rtn can be nil when an operand was an inherent-IDENT bail
|
||||
// (exprtype N_IDENT arm L1596-1599 — SK_USE module ref or pseudo-
|
||||
// builtin callee with sym.decl == nil; #19 retires these as
|
||||
// dedicated AST kinds). Propagation, not silent gap — asserttyped
|
||||
// gates those idents at the consumer layer.
|
||||
fn unifyarith(c: *checker, ltn: *node, rtn: *node) *node = {
|
||||
let lu: bool = isuntypedint(ltn) || isuntypedfloat(ltn);
|
||||
let ru: bool = isuntypedint(rtn) || isuntypedfloat(rtn);
|
||||
@@ -1367,6 +1375,9 @@ fn unifyarith(c: *checker, ltn: *node, rtn: *node) *node = {
|
||||
if (isassignable(c, ltn, rtn, &conf)) { return ltn; };
|
||||
};
|
||||
if (typeeqast(ltn, rtn)) { return ltn; };
|
||||
// Mismatched typed pair — return ltn so the binop stamps something;
|
||||
// 5-lite-b: the trailing nil-on-mismatch shape was eliminated when
|
||||
// the helper was split out of binoptype.
|
||||
return ltn;
|
||||
};
|
||||
|
||||
@@ -1408,13 +1419,18 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
op == tkind.TK_AND || op == tkind.TK_OR) {
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
// Unreachable for valid input: op is one of TK_PLUS/MINUS/STAR/
|
||||
// SLASH/PERCENT/AMP/PIPE/CARET/LSHIFT/RSHIFT/EQ/NEQ/LT/LE/GT/GE/
|
||||
// AND/OR per parser invariant (lib/ww/parse/expr.ww binary-op
|
||||
// table); all are handled above. 5-lite-b #34.
|
||||
return nil;
|
||||
};
|
||||
|
||||
// unoptype — derive the result tnode of an N_UN unary expression. Mirrors
|
||||
// cstage cmd/wcc/check.c:642-687 `cunop`. The slice/str .len/.cap
|
||||
// pseudo-field address-of widening to *i64 mirrors check.c:672-682
|
||||
// directly — its purpose is documented at the cstage site.
|
||||
// cstage cmd/wcc/check.c:642-687 `cunop` and harec ref/harec/src/types.c
|
||||
// type_promote. The slice/str .len/.cap pseudo-field address-of widening
|
||||
// to *i64 mirrors check.c:672-682 directly — its purpose is documented
|
||||
// at the cstage site.
|
||||
fn unoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let opt: *node = exprtype(c, e.lhs, nil);
|
||||
@@ -1422,9 +1438,18 @@ fn unoptype(c: *checker, e: *node) *node = {
|
||||
if (op == tkind.TK_NOT) { return mktname(c, "bool"); };
|
||||
if (op == tkind.TK_TILDE) { return opt; };
|
||||
if (op == tkind.TK_STAR) {
|
||||
// opt nil here means the operand was an inherent-IDENT bail
|
||||
// (exprtype N_IDENT arm L1596-1599 — SK_USE / pseudo-builtin
|
||||
// sym.decl == nil — or another helper's nil propagation);
|
||||
// 5-lite-b #34, A.6.2.1c #24. The `u == nil` post-resolvealias
|
||||
// check was eliminated here — unwrapbang(non-nil) returns
|
||||
// non-nil (parser invariant N_TBANG.lhs always set) and
|
||||
// resolvealias passes through non-nil unchanged (L513
|
||||
// `for (cur != nil)` only exits via `return cur` or `return n`).
|
||||
if (opt == nil) { return nil; };
|
||||
let u: *node = resolvealias(c, unwrapbang(opt));
|
||||
if (u == nil) { return nil; };
|
||||
// Invalid input (non-pointer dereference); cstage errors at
|
||||
// cmd/wcc/check.c:660. 5-lite-b #34.
|
||||
if (u.kind != nkind.N_TPTR) { return nil; };
|
||||
return u.lhs;
|
||||
};
|
||||
@@ -1448,23 +1473,33 @@ fn unoptype(c: *checker, e: *node) *node = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// opt nil → propagation from inherent-IDENT bail (5-lite-b
|
||||
// #34). Generic &expr widens to *opt; without opt we can't
|
||||
// synthesize the pointer node.
|
||||
if (opt == nil) { return nil; };
|
||||
let pp: *node = newnode(nkind.N_TPTR, "", 0, 0);
|
||||
pp.lhs = opt;
|
||||
return pp;
|
||||
};
|
||||
// Unreachable for valid input: op is one of TK_MINUS/PLUS/NOT/
|
||||
// TILDE/STAR/AMP per parser invariant (lib/ww/parse/expr.ww unary
|
||||
// op set); all are handled above. 5-lite-b #34.
|
||||
return nil;
|
||||
};
|
||||
|
||||
// indexresult — derive the result tnode of an N_INDEX expression.
|
||||
// Mirrors cstage cmd/wcc/check.c:870-894. Slice/array → elem; str →
|
||||
// u8; `*[N]T` decays to T (pointer-to-array); `*[]T` does NOT decay
|
||||
// (yields []T via the generic *U → U fallback — Hare-faithful, a
|
||||
// pointer-to-slice is a 1D array of slices, not of T); generic *T → T.
|
||||
// Mirrors cstage cmd/wcc/check.c:870-894 and harec ref/harec/src/types.c
|
||||
// type_promote dispatch. Slice/array → elem; str → u8; `*[N]T` decays
|
||||
// to T (pointer-to-array); `*[]T` does NOT decay (yields []T via the
|
||||
// generic *U → U fallback — Hare-faithful, a pointer-to-slice is a 1D
|
||||
// array of slices, not of T); generic *T → T.
|
||||
fn indexresult(c: *checker, e: *node) *node = {
|
||||
let basetn: *node = exprtype(c, e.lhs, nil);
|
||||
let _idx: *node = exprtype(c, e.rhs, nil);
|
||||
let u: *node = resolvealias(c, unwrapbang(basetn));
|
||||
// basetn nil → propagation from inherent-IDENT bail at exprtype
|
||||
// N_IDENT arm L1596-1599 (5-lite-b #34, A.6.2.1c #24).
|
||||
// unwrapbang(nil)=nil and resolvealias(nil)=nil pass through.
|
||||
if (u == nil) { return nil; };
|
||||
if (u.kind == nkind.N_TSLICE) { return u.lhs; };
|
||||
if (u.kind == nkind.N_TARRAY) { return u.lhs; };
|
||||
@@ -1479,6 +1514,8 @@ fn indexresult(c: *checker, e: *node) *node = {
|
||||
};
|
||||
return inner;
|
||||
};
|
||||
// Invalid input (non-indexable base — cstage errors at
|
||||
// cmd/wcc/check.c:893). 5-lite-b #34.
|
||||
return nil;
|
||||
};
|
||||
|
||||
@@ -1495,6 +1532,19 @@ fn indexresult(c: *checker, e: *node) *node = {
|
||||
// follows doesn't ripple a fresh signature change. Mirrors harec's
|
||||
// `check_expression(..., result_type, ...)` per
|
||||
// `feedback_hare_frontend_reference.md`.
|
||||
//
|
||||
// Dispatcher invariant (5-lite-a #33): every value-producing nkind
|
||||
// listed in resolvewalk's post-order dispatch (L474-489) reaches a
|
||||
// stamping arm here that sets e.type_ before returning. No
|
||||
// fall-through. Arms that return nil (binoptype trailing, unoptype
|
||||
// TK_STAR opt-nil, indexresult u-nil, N_DOT outer fold-miss, N_SLICE
|
||||
// non-sliceable base, etc.) are propagation from a callee's nil —
|
||||
// not silent gaps. Mirror of harec's
|
||||
// `assert(expr->result)` at ref/harec/src/check.c:3810. The
|
||||
// asserttyped pass at L2871 enforces the invariant on every
|
||||
// dispatched node post-checker, with gates for the residual
|
||||
// inherent-IDENT bails (SK_USE, pseudo-builtin sym.decl==nil,
|
||||
// N_DOT-LHS syntactic position) until #19 retires the bail shape.
|
||||
fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
if (e == nil) { return nil; };
|
||||
let k: nkind = e.kind;
|
||||
@@ -2789,6 +2839,101 @@ fn resolvefnbody(c: *checker, fnnode: *node) void = {
|
||||
c.cur = outer;
|
||||
};
|
||||
|
||||
// asserttyped — post-checker invariant gate (#15, A.6.2.1e). Walks the
|
||||
// file tree and fires (writes a one-line diagnostic to stderr) for any
|
||||
// node in resolvewalk's value-producing dispatch set (L474-489) whose
|
||||
// n.type_ remained nil. Mirror of harec's `assert(expr->result)` at
|
||||
// ref/harec/src/check.c:3810 — wwstage's checker is lenient (rule 7),
|
||||
// so this is a soft assertion (diagnostic, not abort) used by the
|
||||
// 990_selfhost probes to catch regressions in the stamping discipline.
|
||||
//
|
||||
// Gates (per Drew 2026-05-22 — "guards value-producing expression
|
||||
// nodes; SK_USE refs and bare builtin callees are syntactic positions,
|
||||
// gate them out with WHY pointing at #19"):
|
||||
//
|
||||
// 1. N_IDENT whose resolved sym kind is SK_USE — module references
|
||||
// (`os` in `os.write`). Harec models these via EXPR_ACCESS whose
|
||||
// lookup-target is an OBJ_USE directly; there is no intermediate
|
||||
// "ident-as-value" expr. Until #19 ports that AST shape, skip.
|
||||
// 2. N_IDENT whose resolved sym has decl == nil — pseudo-builtin
|
||||
// callees (len/append/free/alloc/size/align/offset, seeded in
|
||||
// checkinit L85-97 with decl=nil). Harec spells these as
|
||||
// dedicated EXPR_* kinds (EXPR_LEN, EXPR_APPEND, EXPR_FREE,
|
||||
// EXPR_ALLOC at ref/harec/src/check.c:2630/745/2443/...).
|
||||
// Drew's δ (#19) retires the seeded-SK_FN-with-nil-decl hack.
|
||||
// 3. N_IDENT at the LHS-of-N_DOT syntactic position — the bare
|
||||
// name half of a member-access expr is a lookup target, not a
|
||||
// value-producing sub-expression. Harec's EXPR_ACCESS stores the
|
||||
// member as a string, not a node.
|
||||
//
|
||||
// `indot` tracks gate 3: true only when the immediate caller is an
|
||||
// N_DOT recursing into its .lhs.
|
||||
fn asserttyped(c: *checker, n: *node, indot: bool) void = {
|
||||
if (n == nil) { return; };
|
||||
let k: nkind = n.kind;
|
||||
let isexpr: bool =
|
||||
k == nkind.N_INTLIT || k == nkind.N_FLOATLIT ||
|
||||
k == nkind.N_STRLIT || k == nkind.N_RUNELIT ||
|
||||
k == nkind.N_TRUE || k == nkind.N_FALSE ||
|
||||
k == nkind.N_NIL || k == nkind.N_VOIDLIT ||
|
||||
k == nkind.N_IDENT || k == nkind.N_BIN ||
|
||||
k == nkind.N_UN || k == nkind.N_CALL ||
|
||||
k == nkind.N_INDEX || k == nkind.N_CAST ||
|
||||
k == nkind.N_STRUCTLIT || k == nkind.N_ARRLIT ||
|
||||
k == nkind.N_RECV || k == nkind.N_DOT ||
|
||||
k == nkind.N_SLICE || k == nkind.N_SPREAD ||
|
||||
k == nkind.N_TUPLE || k == nkind.N_TRYPROP ||
|
||||
k == nkind.N_TRYUNW || k == nkind.N_TYPETEST ||
|
||||
k == nkind.N_TYPEASSERT || k == nkind.N_YIELD ||
|
||||
k == nkind.N_MATCH;
|
||||
let skip: bool = false;
|
||||
if (isexpr && k == nkind.N_IDENT) {
|
||||
if (indot) { skip = true; };
|
||||
if (!skip) {
|
||||
let s: *sym = scopelookup(c.cur, n.str);
|
||||
if (s != nil) {
|
||||
if (s.skind == skind.SK_USE) { skip = true; };
|
||||
if (s.decl == nil) { skip = true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
if (isexpr && !skip) {
|
||||
if (n.type_ == nil) {
|
||||
os.write(2, "asserttyped: ".ptr, 13u64);
|
||||
let kn: str = nkname(k);
|
||||
os.write(2, kn.ptr, kn.len: u64);
|
||||
os.write(2, " ".ptr, 1u64);
|
||||
if (n.file.len > 0) {
|
||||
os.write(2, n.file.ptr, n.file.len: u64);
|
||||
os.write(2, ":".ptr, 1u64);
|
||||
let ls: str = strconv.i32tos(n.line, strconv.base.DEC);
|
||||
os.write(2, ls.ptr, ls.len: u64);
|
||||
};
|
||||
if (n.str.len > 0) {
|
||||
os.write(2, " '".ptr, 2u64);
|
||||
os.write(2, n.str.ptr, n.str.len: u64);
|
||||
os.write(2, "'".ptr, 1u64);
|
||||
};
|
||||
os.write(2, "\n".ptr, 1u64);
|
||||
};
|
||||
};
|
||||
if (k == nkind.N_DOT) {
|
||||
if (n.lhs != nil) { asserttyped(c, n.lhs, true); };
|
||||
return;
|
||||
};
|
||||
if (n.attr != nil) { asserttyped(c, n.attr, false); };
|
||||
if (n.lhs != nil) { asserttyped(c, n.lhs, false); };
|
||||
if (n.rhs != nil) { asserttyped(c, n.rhs, false); };
|
||||
if (n.cond != nil) { asserttyped(c, n.cond, false); };
|
||||
if (n.body != nil) { asserttyped(c, n.body, false); };
|
||||
if (n.els != nil) { asserttyped(c, n.els, false); };
|
||||
let m: *node = n.list;
|
||||
for (m != nil) {
|
||||
asserttyped(c, m, false);
|
||||
m = m.next;
|
||||
};
|
||||
};
|
||||
|
||||
export fn checkinit(c: *checker, tc: *tctx) void = {
|
||||
c.tc = tc;
|
||||
c.top = newscope(nil);
|
||||
@@ -2844,6 +2989,17 @@ export fn checkfile(c: *checker, file: *node) void = {
|
||||
};};};};
|
||||
d = d.next;
|
||||
};
|
||||
|
||||
// Pass 3 (#15, A.6.2.1e): post-checker invariant gate. Walks each
|
||||
// decl with its curmod set so asserttyped's gate lookups resolve
|
||||
// against the same module context exprtype saw during pass 2.
|
||||
d = file.list;
|
||||
for (d != nil) {
|
||||
c.curmod = declmod(file, d);
|
||||
asserttyped(c, d, false);
|
||||
d = d.next;
|
||||
};
|
||||
|
||||
let empty: str;
|
||||
c.curmod = empty;
|
||||
|
||||
|
||||
@@ -4569,7 +4569,7 @@ export fn newnode(k: nkind, file: str, line: i32, col: i32) *node = {
|
||||
|
||||
// ---- printer ----------------------------------------------------------
|
||||
|
||||
fn nkname(k: nkind) str = {
|
||||
export fn nkname(k: nkind) str = {
|
||||
if (k == nkind.N_NONE) { return "none"; };
|
||||
if (k == nkind.N_INTLIT) { return "int"; };
|
||||
if (k == nkind.N_FLOATLIT) { return "float"; };
|
||||
@@ -6985,6 +6985,7 @@ package wcc;
|
||||
|
||||
import os;
|
||||
import tok;
|
||||
import strconv;
|
||||
|
||||
type checker = struct {
|
||||
tc: *tctx,
|
||||
@@ -8310,10 +8311,17 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
|
||||
};
|
||||
|
||||
// unifyarith — usual-arithmetic-conversion analogue at the AST-tnode
|
||||
// layer. Mirrors cstage cmd/wcc/check.c:580-596 `unify_arith`. The
|
||||
// trailing typeeq fallback covers same-typed operands; mismatched typed
|
||||
// pairs silently return ltn (cstage flags the same shape — wwstage's
|
||||
// checker stays silent here per existing discipline).
|
||||
// layer. Mirrors cstage cmd/wcc/check.c:580-596 `unify_arith` and harec
|
||||
// ref/harec/src/types.c type_promote. Trailing `return ltn` covers
|
||||
// mismatched typed pairs; cstage flags the same shape — wwstage's
|
||||
// checker stays silent here per existing discipline.
|
||||
//
|
||||
// Nil-on-valid classification (5-lite-b #34, A.6.2.1c #24): both ltn
|
||||
// and rtn can be nil when an operand was an inherent-IDENT bail
|
||||
// (exprtype N_IDENT arm L1596-1599 — SK_USE module ref or pseudo-
|
||||
// builtin callee with sym.decl == nil; #19 retires these as
|
||||
// dedicated AST kinds). Propagation, not silent gap — asserttyped
|
||||
// gates those idents at the consumer layer.
|
||||
fn unifyarith(c: *checker, ltn: *node, rtn: *node) *node = {
|
||||
let lu: bool = isuntypedint(ltn) || isuntypedfloat(ltn);
|
||||
let ru: bool = isuntypedint(rtn) || isuntypedfloat(rtn);
|
||||
@@ -8331,6 +8339,9 @@ fn unifyarith(c: *checker, ltn: *node, rtn: *node) *node = {
|
||||
if (isassignable(c, ltn, rtn, &conf)) { return ltn; };
|
||||
};
|
||||
if (typeeqast(ltn, rtn)) { return ltn; };
|
||||
// Mismatched typed pair — return ltn so the binop stamps something;
|
||||
// 5-lite-b: the trailing nil-on-mismatch shape was eliminated when
|
||||
// the helper was split out of binoptype.
|
||||
return ltn;
|
||||
};
|
||||
|
||||
@@ -8372,13 +8383,18 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
op == tkind.TK_AND || op == tkind.TK_OR) {
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
// Unreachable for valid input: op is one of TK_PLUS/MINUS/STAR/
|
||||
// SLASH/PERCENT/AMP/PIPE/CARET/LSHIFT/RSHIFT/EQ/NEQ/LT/LE/GT/GE/
|
||||
// AND/OR per parser invariant (lib/ww/parse/expr.ww binary-op
|
||||
// table); all are handled above. 5-lite-b #34.
|
||||
return nil;
|
||||
};
|
||||
|
||||
// unoptype — derive the result tnode of an N_UN unary expression. Mirrors
|
||||
// cstage cmd/wcc/check.c:642-687 `cunop`. The slice/str .len/.cap
|
||||
// pseudo-field address-of widening to *i64 mirrors check.c:672-682
|
||||
// directly — its purpose is documented at the cstage site.
|
||||
// cstage cmd/wcc/check.c:642-687 `cunop` and harec ref/harec/src/types.c
|
||||
// type_promote. The slice/str .len/.cap pseudo-field address-of widening
|
||||
// to *i64 mirrors check.c:672-682 directly — its purpose is documented
|
||||
// at the cstage site.
|
||||
fn unoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let opt: *node = exprtype(c, e.lhs, nil);
|
||||
@@ -8386,9 +8402,18 @@ fn unoptype(c: *checker, e: *node) *node = {
|
||||
if (op == tkind.TK_NOT) { return mktname(c, "bool"); };
|
||||
if (op == tkind.TK_TILDE) { return opt; };
|
||||
if (op == tkind.TK_STAR) {
|
||||
// opt nil here means the operand was an inherent-IDENT bail
|
||||
// (exprtype N_IDENT arm L1596-1599 — SK_USE / pseudo-builtin
|
||||
// sym.decl == nil — or another helper's nil propagation);
|
||||
// 5-lite-b #34, A.6.2.1c #24. The `u == nil` post-resolvealias
|
||||
// check was eliminated here — unwrapbang(non-nil) returns
|
||||
// non-nil (parser invariant N_TBANG.lhs always set) and
|
||||
// resolvealias passes through non-nil unchanged (L513
|
||||
// `for (cur != nil)` only exits via `return cur` or `return n`).
|
||||
if (opt == nil) { return nil; };
|
||||
let u: *node = resolvealias(c, unwrapbang(opt));
|
||||
if (u == nil) { return nil; };
|
||||
// Invalid input (non-pointer dereference); cstage errors at
|
||||
// cmd/wcc/check.c:660. 5-lite-b #34.
|
||||
if (u.kind != nkind.N_TPTR) { return nil; };
|
||||
return u.lhs;
|
||||
};
|
||||
@@ -8412,23 +8437,33 @@ fn unoptype(c: *checker, e: *node) *node = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// opt nil → propagation from inherent-IDENT bail (5-lite-b
|
||||
// #34). Generic &expr widens to *opt; without opt we can't
|
||||
// synthesize the pointer node.
|
||||
if (opt == nil) { return nil; };
|
||||
let pp: *node = newnode(nkind.N_TPTR, "", 0, 0);
|
||||
pp.lhs = opt;
|
||||
return pp;
|
||||
};
|
||||
// Unreachable for valid input: op is one of TK_MINUS/PLUS/NOT/
|
||||
// TILDE/STAR/AMP per parser invariant (lib/ww/parse/expr.ww unary
|
||||
// op set); all are handled above. 5-lite-b #34.
|
||||
return nil;
|
||||
};
|
||||
|
||||
// indexresult — derive the result tnode of an N_INDEX expression.
|
||||
// Mirrors cstage cmd/wcc/check.c:870-894. Slice/array → elem; str →
|
||||
// u8; `*[N]T` decays to T (pointer-to-array); `*[]T` does NOT decay
|
||||
// (yields []T via the generic *U → U fallback — Hare-faithful, a
|
||||
// pointer-to-slice is a 1D array of slices, not of T); generic *T → T.
|
||||
// Mirrors cstage cmd/wcc/check.c:870-894 and harec ref/harec/src/types.c
|
||||
// type_promote dispatch. Slice/array → elem; str → u8; `*[N]T` decays
|
||||
// to T (pointer-to-array); `*[]T` does NOT decay (yields []T via the
|
||||
// generic *U → U fallback — Hare-faithful, a pointer-to-slice is a 1D
|
||||
// array of slices, not of T); generic *T → T.
|
||||
fn indexresult(c: *checker, e: *node) *node = {
|
||||
let basetn: *node = exprtype(c, e.lhs, nil);
|
||||
let _idx: *node = exprtype(c, e.rhs, nil);
|
||||
let u: *node = resolvealias(c, unwrapbang(basetn));
|
||||
// basetn nil → propagation from inherent-IDENT bail at exprtype
|
||||
// N_IDENT arm L1596-1599 (5-lite-b #34, A.6.2.1c #24).
|
||||
// unwrapbang(nil)=nil and resolvealias(nil)=nil pass through.
|
||||
if (u == nil) { return nil; };
|
||||
if (u.kind == nkind.N_TSLICE) { return u.lhs; };
|
||||
if (u.kind == nkind.N_TARRAY) { return u.lhs; };
|
||||
@@ -8443,6 +8478,8 @@ fn indexresult(c: *checker, e: *node) *node = {
|
||||
};
|
||||
return inner;
|
||||
};
|
||||
// Invalid input (non-indexable base — cstage errors at
|
||||
// cmd/wcc/check.c:893). 5-lite-b #34.
|
||||
return nil;
|
||||
};
|
||||
|
||||
@@ -8459,6 +8496,19 @@ fn indexresult(c: *checker, e: *node) *node = {
|
||||
// follows doesn't ripple a fresh signature change. Mirrors harec's
|
||||
// `check_expression(..., result_type, ...)` per
|
||||
// `feedback_hare_frontend_reference.md`.
|
||||
//
|
||||
// Dispatcher invariant (5-lite-a #33): every value-producing nkind
|
||||
// listed in resolvewalk's post-order dispatch (L474-489) reaches a
|
||||
// stamping arm here that sets e.type_ before returning. No
|
||||
// fall-through. Arms that return nil (binoptype trailing, unoptype
|
||||
// TK_STAR opt-nil, indexresult u-nil, N_DOT outer fold-miss, N_SLICE
|
||||
// non-sliceable base, etc.) are propagation from a callee's nil —
|
||||
// not silent gaps. Mirror of harec's
|
||||
// `assert(expr->result)` at ref/harec/src/check.c:3810. The
|
||||
// asserttyped pass at L2871 enforces the invariant on every
|
||||
// dispatched node post-checker, with gates for the residual
|
||||
// inherent-IDENT bails (SK_USE, pseudo-builtin sym.decl==nil,
|
||||
// N_DOT-LHS syntactic position) until #19 retires the bail shape.
|
||||
fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
if (e == nil) { return nil; };
|
||||
let k: nkind = e.kind;
|
||||
@@ -9753,6 +9803,101 @@ fn resolvefnbody(c: *checker, fnnode: *node) void = {
|
||||
c.cur = outer;
|
||||
};
|
||||
|
||||
// asserttyped — post-checker invariant gate (#15, A.6.2.1e). Walks the
|
||||
// file tree and fires (writes a one-line diagnostic to stderr) for any
|
||||
// node in resolvewalk's value-producing dispatch set (L474-489) whose
|
||||
// n.type_ remained nil. Mirror of harec's `assert(expr->result)` at
|
||||
// ref/harec/src/check.c:3810 — wwstage's checker is lenient (rule 7),
|
||||
// so this is a soft assertion (diagnostic, not abort) used by the
|
||||
// 990_selfhost probes to catch regressions in the stamping discipline.
|
||||
//
|
||||
// Gates (per Drew 2026-05-22 — "guards value-producing expression
|
||||
// nodes; SK_USE refs and bare builtin callees are syntactic positions,
|
||||
// gate them out with WHY pointing at #19"):
|
||||
//
|
||||
// 1. N_IDENT whose resolved sym kind is SK_USE — module references
|
||||
// (`os` in `os.write`). Harec models these via EXPR_ACCESS whose
|
||||
// lookup-target is an OBJ_USE directly; there is no intermediate
|
||||
// "ident-as-value" expr. Until #19 ports that AST shape, skip.
|
||||
// 2. N_IDENT whose resolved sym has decl == nil — pseudo-builtin
|
||||
// callees (len/append/free/alloc/size/align/offset, seeded in
|
||||
// checkinit L85-97 with decl=nil). Harec spells these as
|
||||
// dedicated EXPR_* kinds (EXPR_LEN, EXPR_APPEND, EXPR_FREE,
|
||||
// EXPR_ALLOC at ref/harec/src/check.c:2630/745/2443/...).
|
||||
// Drew's δ (#19) retires the seeded-SK_FN-with-nil-decl hack.
|
||||
// 3. N_IDENT at the LHS-of-N_DOT syntactic position — the bare
|
||||
// name half of a member-access expr is a lookup target, not a
|
||||
// value-producing sub-expression. Harec's EXPR_ACCESS stores the
|
||||
// member as a string, not a node.
|
||||
//
|
||||
// `indot` tracks gate 3: true only when the immediate caller is an
|
||||
// N_DOT recursing into its .lhs.
|
||||
fn asserttyped(c: *checker, n: *node, indot: bool) void = {
|
||||
if (n == nil) { return; };
|
||||
let k: nkind = n.kind;
|
||||
let isexpr: bool =
|
||||
k == nkind.N_INTLIT || k == nkind.N_FLOATLIT ||
|
||||
k == nkind.N_STRLIT || k == nkind.N_RUNELIT ||
|
||||
k == nkind.N_TRUE || k == nkind.N_FALSE ||
|
||||
k == nkind.N_NIL || k == nkind.N_VOIDLIT ||
|
||||
k == nkind.N_IDENT || k == nkind.N_BIN ||
|
||||
k == nkind.N_UN || k == nkind.N_CALL ||
|
||||
k == nkind.N_INDEX || k == nkind.N_CAST ||
|
||||
k == nkind.N_STRUCTLIT || k == nkind.N_ARRLIT ||
|
||||
k == nkind.N_RECV || k == nkind.N_DOT ||
|
||||
k == nkind.N_SLICE || k == nkind.N_SPREAD ||
|
||||
k == nkind.N_TUPLE || k == nkind.N_TRYPROP ||
|
||||
k == nkind.N_TRYUNW || k == nkind.N_TYPETEST ||
|
||||
k == nkind.N_TYPEASSERT || k == nkind.N_YIELD ||
|
||||
k == nkind.N_MATCH;
|
||||
let skip: bool = false;
|
||||
if (isexpr && k == nkind.N_IDENT) {
|
||||
if (indot) { skip = true; };
|
||||
if (!skip) {
|
||||
let s: *sym = scopelookup(c.cur, n.str);
|
||||
if (s != nil) {
|
||||
if (s.skind == skind.SK_USE) { skip = true; };
|
||||
if (s.decl == nil) { skip = true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
if (isexpr && !skip) {
|
||||
if (n.type_ == nil) {
|
||||
os.write(2, "asserttyped: ".ptr, 13u64);
|
||||
let kn: str = nkname(k);
|
||||
os.write(2, kn.ptr, kn.len: u64);
|
||||
os.write(2, " ".ptr, 1u64);
|
||||
if (n.file.len > 0) {
|
||||
os.write(2, n.file.ptr, n.file.len: u64);
|
||||
os.write(2, ":".ptr, 1u64);
|
||||
let ls: str = strconv.i32tos(n.line, strconv.base.DEC);
|
||||
os.write(2, ls.ptr, ls.len: u64);
|
||||
};
|
||||
if (n.str.len > 0) {
|
||||
os.write(2, " '".ptr, 2u64);
|
||||
os.write(2, n.str.ptr, n.str.len: u64);
|
||||
os.write(2, "'".ptr, 1u64);
|
||||
};
|
||||
os.write(2, "\n".ptr, 1u64);
|
||||
};
|
||||
};
|
||||
if (k == nkind.N_DOT) {
|
||||
if (n.lhs != nil) { asserttyped(c, n.lhs, true); };
|
||||
return;
|
||||
};
|
||||
if (n.attr != nil) { asserttyped(c, n.attr, false); };
|
||||
if (n.lhs != nil) { asserttyped(c, n.lhs, false); };
|
||||
if (n.rhs != nil) { asserttyped(c, n.rhs, false); };
|
||||
if (n.cond != nil) { asserttyped(c, n.cond, false); };
|
||||
if (n.body != nil) { asserttyped(c, n.body, false); };
|
||||
if (n.els != nil) { asserttyped(c, n.els, false); };
|
||||
let m: *node = n.list;
|
||||
for (m != nil) {
|
||||
asserttyped(c, m, false);
|
||||
m = m.next;
|
||||
};
|
||||
};
|
||||
|
||||
export fn checkinit(c: *checker, tc: *tctx) void = {
|
||||
c.tc = tc;
|
||||
c.top = newscope(nil);
|
||||
@@ -9808,6 +9953,17 @@ export fn checkfile(c: *checker, file: *node) void = {
|
||||
};};};};
|
||||
d = d.next;
|
||||
};
|
||||
|
||||
// Pass 3 (#15, A.6.2.1e): post-checker invariant gate. Walks each
|
||||
// decl with its curmod set so asserttyped's gate lookups resolve
|
||||
// against the same module context exprtype saw during pass 2.
|
||||
d = file.list;
|
||||
for (d != nil) {
|
||||
c.curmod = declmod(file, d);
|
||||
asserttyped(c, d, false);
|
||||
d = d.next;
|
||||
};
|
||||
|
||||
let empty: str;
|
||||
c.curmod = empty;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user