wcc: forrange bindings get a per-loop scope; dup binders reject
The TODO(#11) silent-accept of `for (let (a, a) .. xs)` rested on a stale premise -- resolvewalk has per-block scopes since #53 and IS the live w6c_ww checker pass. Bindings now install in a per-LOOP scope (the N_MCASE pattern), so a duplicate name within one pattern errs "redeclared in same scope" (cstage parity via stamptuplebinds' scopedefine-nil check) while sequential same-name loops stay legal. kwtab restores its stated alphabetical order (`import` before `is`, both stages, kinds swapped in lockstep with names).
This commit is contained in:
@@ -443,7 +443,19 @@ fn stamptuplebinds(c: *checker, binds: *syntax.node, elems: *syntax.node,
|
||||
let bnm: str = b.str;
|
||||
if (bnm.len > 0) {
|
||||
checkmoduleshadow(c, bnm, what);
|
||||
syntax.scopedefine(c.cur, bnm, syntax.skind.SK_VAR, nil, b);
|
||||
// first registration wins; a nil return is a
|
||||
// same-scope duplicate — `let (a, a) = ..` /
|
||||
// `for (let (a, a) .. xs)`. Mirrors cstage
|
||||
// scope_define == NULL → "redeclared".
|
||||
let ds: *syntax.sym = syntax.scopedefine(c.cur, bnm, syntax.skind.SK_VAR, nil, b);
|
||||
if (ds == nil) {
|
||||
cerr("error: ");
|
||||
cerr(what);
|
||||
cerr(" '");
|
||||
cerr(bnm);
|
||||
cerr("' redeclared in same scope\n");
|
||||
c.errs += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (b.type_ == nil) {
|
||||
@@ -550,17 +562,17 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
// `for (let x .. slice) body` / `for (let (a, b) .. slice) body` —
|
||||
// each binding name becomes a fresh local. Walk the slice expr first
|
||||
// so its idents resolve before the bindings shadow anything, then
|
||||
// install bindings and walk the body/else.
|
||||
//
|
||||
// TODO(#11): cstage check.c (post-#32) errors `binding '%s'
|
||||
// redeclared in same scope` when the tuple-pattern lists the same
|
||||
// name twice (`for (let (a, a) .. xs)`). Wwstage's resolvewalk has
|
||||
// no per-block scope (see resolvefnbody's docstring) and is used
|
||||
// only by wwdump_ww as a diagnostic, so silent-accept here avoids
|
||||
// false-positives on legal cross-block shadow until #11 adds the
|
||||
// scoping infrastructure.
|
||||
// install bindings and walk the body/else. The bindings live in a
|
||||
// per-LOOP scope (the N_MCASE pattern): sequential same-name loops
|
||||
// each rebind fresh, and a duplicate name WITHIN one pattern
|
||||
// (`for (let (a, a) .. xs)`) now errs via stamptuplebinds' dup
|
||||
// check — the old TODO(#11) silent-accept rested on a stale
|
||||
// premise (resolvewalk HAS block scopes since #53 and IS the live
|
||||
// w6c_ww checker pass).
|
||||
if (k == syntax.nkind.N_FORRANGE) {
|
||||
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
|
||||
let frouter: *syntax.scope = c.cur;
|
||||
c.cur = syntax.newscope(frouter);
|
||||
if (n.list != nil) {
|
||||
// Tuple destructure `for (let (a,b) .. xs)`: peel the
|
||||
// iterable's element type and distribute its tuple
|
||||
@@ -628,10 +640,12 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
};
|
||||
// `break`/`continue` in the body target this loop; the `else`
|
||||
// block runs at normal cond-false exit (skipped by break) and
|
||||
// targets an ENCLOSING loop. Mirrors cstage cmd/wcc/check.c:2494.
|
||||
// targets an ENCLOSING loop — outside the loop scope, like
|
||||
// cstage's saved-scope restore. Mirrors cmd/wcc/check.c:2494.
|
||||
c.loops += 1;
|
||||
if (n.body != nil) { resolvewalk(c, n.body); };
|
||||
c.loops -= 1;
|
||||
c.cur = frouter;
|
||||
if (n.els != nil) { resolvewalk(c, n.els); };
|
||||
return;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user