cstage+selfhost+test: refuse same-block let / param redecl (#32)
cmd/wcc/check.c silently accepted `let a; let a;` in the same block and similar redecls. Pre-#27 the localoff dedup masked it; post-#27 last-write-wins via head-first localfind. Surfaced by worker-27 during the #27 review. Cstage: 5 guard sites (check_scope_define-NULL → err) covering N_LET block-bind, N_MLET tuple binders (incl. same-tuple `let (a,a)`), N_FORRANGE tuple binders, top-level let, fn param. Voice: "<kind> '<name>' redeclared in same scope" for inner; "duplicate let %s" for top-let, matching the existing "duplicate <kind>" idiom at 1812/1851/1872. Wwstage: TODO(#11) comments at the 4 mirror sites (installdecl, N_FORRANGE, N_LET, installparams). Full enforcement waits on the checkfile pass per rob. **Unmasked by #32 (worth flagging):** selfhost/cmd/wcc/cgenexpr.ww cgcall had `let callee: *node = n.lhs;` twice at fn-body scope (copy-paste, identical value). Pre-fix silent-redecl absorbed it; post-fix the new guard rejects. Removed the second decl — outer `callee` stays visible across the intermediate block. Test 712 (redecl): 10 rows (6 neg + 4 pos), cstage-only per rob. Negative rows cover all 5 guard sites + same-tuple-dup. Positive rows pin the legal counter-shapes (cross-block, name-only bucket, forrange body, mcase-per-arm). Test 300 row 34 ("shadowing in inner scope; same scope flagged") was incorrectly asserting the bug; flipped to expect "redeclared" and added a sibling row pinning cross-block shadow stays ok. Test 709's `same_block_redecl_pin` canary (explicitly documented as flipping under #32) removed; pointer to 712 left in its place.
This commit is contained in:
@@ -1441,7 +1441,10 @@ clet(Checker *c, Node *n)
|
||||
if (n->str && n->str[0]) {
|
||||
check_module_shadow(c, n->str, n->pos, "let");
|
||||
Sym *s = scope_define(c->cur, n->str, SK_VAR, t, n);
|
||||
if (s && n->op == TK_CONST) s->is_const = 1;
|
||||
if (s == NULL)
|
||||
err(c, n->pos, "let '%s' redeclared in same scope",
|
||||
n->str);
|
||||
else if (n->op == TK_CONST) s->is_const = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1502,8 +1505,11 @@ cstmt(Checker *c, Node *n)
|
||||
if (nm->str && nm->str[0]) {
|
||||
check_module_shadow(c, nm->str,
|
||||
nm->pos, "binding");
|
||||
scope_define(c->cur, nm->str,
|
||||
SK_VAR, ft, nm);
|
||||
if (scope_define(c->cur, nm->str,
|
||||
SK_VAR, ft, nm) == NULL)
|
||||
err(c, nm->pos,
|
||||
"binding '%s' redeclared in same scope",
|
||||
nm->str);
|
||||
}
|
||||
if (tp) tp = tp->next;
|
||||
}
|
||||
@@ -1560,7 +1566,11 @@ cstmt(Checker *c, Node *n)
|
||||
if (l->str && l->str[0]) {
|
||||
check_module_shadow(c, l->str, l->pos, "let");
|
||||
Sym *s = scope_define(c->cur, l->str, SK_VAR, t, l);
|
||||
if (s && n->op == TK_CONST) s->is_const = 1;
|
||||
if (s == NULL)
|
||||
err(c, l->pos,
|
||||
"let '%s' redeclared in same scope",
|
||||
l->str);
|
||||
else if (n->op == TK_CONST) s->is_const = 1;
|
||||
}
|
||||
if (tp) tp = tp->next;
|
||||
}
|
||||
@@ -1876,9 +1886,10 @@ check_file(Checker *c, Node *file)
|
||||
prev->decl = d;
|
||||
prev->use_alias = 1;
|
||||
if (mod && prev->mod == NULL) prev->mod = mod;
|
||||
} else
|
||||
scope_define_in_module(c->cur, d->str,
|
||||
mod, SK_VAR, t, d);
|
||||
} else if (!scope_define_in_module(c->cur,
|
||||
d->str, mod, SK_VAR, t, d))
|
||||
err(c, d->pos, "duplicate let %s",
|
||||
d->str);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1911,8 +1922,11 @@ check_file(Checker *c, Node *file)
|
||||
if (p->name && p->name[0]) {
|
||||
check_module_shadow(c, p->name,
|
||||
d->pos, "param");
|
||||
scope_define(c->cur, p->name,
|
||||
SK_PARAM, p->type, d);
|
||||
if (scope_define(c->cur, p->name,
|
||||
SK_PARAM, p->type, d) == NULL)
|
||||
err(c, d->pos,
|
||||
"param '%s' redeclared",
|
||||
p->name);
|
||||
}
|
||||
}
|
||||
Type *prev = c->ret;
|
||||
|
||||
Reference in New Issue
Block a user