wcc: checker-seam drain — spreads, tuple aliases, yield scoping
Four seams from one review cluster, all landing on the same checker files; gates ran on the union (rule 11 body). ?-subset: the wwstage walk compared `...spread` ALIAS nodes on both sides of the error-subset check, falsely rejecting a spread-carried error return (`(i64 | ...errs)`, cs-accept/ww-reject) — both sides now flatten like trycountvariants (cstage Tparams are pre-flattened at type level). Multi-let/multi-assign: the tuple gates keyed on the RAW rhs kind, rejecting a NAMED tuple alias (`type pair = (i64,i64)`; cs clean- reject, ww asserttyped stop) — both stages chase per #99 alias transparency, wwstage rettupleof peels the alias so cgmassign keeps the str element's len/cap stores, and the catA massign-alias error pin re-rules to a run fixture. The N_MLET diagnostic also printed its operands swapped (elem/declared reversed vs its wording). Yield: match_yield_type didn't descend into N_SWITCH, so a yield inside a switch arm typed the match void and dropped the value (both stages); and a stray yield outside any match arm reached cgen unchecked — both stages now gate on a match-arm counter (the c->loops discipline).
This commit is contained in:
@@ -187,6 +187,16 @@ match_yield_type(Node *body)
|
||||
}
|
||||
if (body->kind == N_FOR || body->kind == N_FORRANGE)
|
||||
return match_yield_type(body->body);
|
||||
/* a yield inside a switch arm yields from the enclosing MATCH
|
||||
* (switch is a statement, not a yield scope) — invisible here,
|
||||
* the match typed void and the yielded value was dropped. */
|
||||
if (body->kind == N_SWITCH) {
|
||||
for (Node *cs = body->list; cs; cs = cs->next) {
|
||||
Type *t = match_yield_type(cs->body);
|
||||
if (t) return t;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2193,7 +2203,9 @@ cexpr(Checker *c, Node *n)
|
||||
scope_define(c->cur, cs->str, SK_VAR, vt, cs);
|
||||
}
|
||||
}
|
||||
c->matcharms++;
|
||||
cstmt(c, cs->body);
|
||||
c->matcharms--;
|
||||
c->cur = saved;
|
||||
}
|
||||
/* Exhaustiveness: every variant must be handled. A default arm
|
||||
@@ -2678,7 +2690,10 @@ cstmt(Checker *c, Node *n)
|
||||
}
|
||||
case N_MLET: {
|
||||
Type *rt = cexpr(c, n->rhs);
|
||||
Type *u = (rt && rt->kind == TY_TUPLE) ? rt : NULL;
|
||||
/* #99 alias transparency: a NAMED tuple alias rhs
|
||||
* (`type pair = (i64,i64)`) destructures like its base. */
|
||||
Type *ru = type_chase_named(rt);
|
||||
Type *u = (ru && ru->kind == TY_TUPLE) ? ru : NULL;
|
||||
if (u == NULL) {
|
||||
err(c, n->pos, "multi-let rhs is not a tuple (got %s)",
|
||||
type_name(c->a, rt));
|
||||
@@ -2691,8 +2706,8 @@ cstmt(Checker *c, Node *n)
|
||||
(elem ? type_default(elem) : ty_err);
|
||||
if (declared && elem && !type_assignable(declared, elem))
|
||||
err(c, l->pos, "let %s: %s not assignable from %s",
|
||||
l->str, type_name(c->a, elem),
|
||||
type_name(c->a, declared));
|
||||
l->str, type_name(c->a, declared),
|
||||
type_name(c->a, elem));
|
||||
l->type = t;
|
||||
if (l->str && l->str[0]) {
|
||||
check_module_shadow(c, l->str, l->pos, "let");
|
||||
@@ -2711,7 +2726,9 @@ cstmt(Checker *c, Node *n)
|
||||
}
|
||||
case N_MASSIGN: {
|
||||
Type *rt = cexpr(c, n->rhs);
|
||||
Type *u = (rt && rt->kind == TY_TUPLE) ? rt : NULL;
|
||||
/* #99 alias transparency — the N_MLET chase's twin. */
|
||||
Type *ru = type_chase_named(rt);
|
||||
Type *u = (ru && ru->kind == TY_TUPLE) ? ru : NULL;
|
||||
if (u == NULL) {
|
||||
err(c, n->pos, "multi-assign rhs is not a tuple (got %s)",
|
||||
type_name(c->a, rt));
|
||||
@@ -2733,7 +2750,14 @@ cstmt(Checker *c, Node *n)
|
||||
break;
|
||||
}
|
||||
case N_DEFER: (void)cexpr(c, n->lhs); break;
|
||||
case N_YIELD: if (n->lhs) (void)cexpr(c, n->lhs); break;
|
||||
case N_YIELD:
|
||||
/* break/continue get the c->loops gate; a stray yield
|
||||
* outside any match arm reached cgen unchecked and its
|
||||
* value silently vanished. */
|
||||
if (c->matcharms == 0)
|
||||
err(c, n->pos, "yield outside match");
|
||||
if (n->lhs) (void)cexpr(c, n->lhs);
|
||||
break;
|
||||
case N_BREAK:
|
||||
case N_CONTINUE:
|
||||
if (c->loops == 0)
|
||||
|
||||
Reference in New Issue
Block a user