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:
@@ -44,10 +44,10 @@
|
||||
* same_name_diff_type | `let a: i32 = 5;` then disjoint | exit=2
|
||||
* | block `let a: str = "hi";`. Returns|
|
||||
* | a.len from the str scope. |
|
||||
* same_block_redecl_pin | `let a: i32 = 1; let a: i32 = 9;` | exit=9
|
||||
* | in same block (checker silently |
|
||||
* | accepts today). Pins last-write- |
|
||||
* | wins via head-first localfind. |
|
||||
* (removed) | `let a: i32 = 1; let a: i32 = 9;` | --
|
||||
* | post-#32 the checker rejects this; |
|
||||
* | covered by 712_redecl's |
|
||||
* | neg_let_same_block row. |
|
||||
* defer_shadow | outer `a`, deferred call captures | exit=42
|
||||
* | &outer-a, inner-block shadow `a`, |
|
||||
* | return outer a. Pins both: cgfn |
|
||||
@@ -165,23 +165,13 @@ static const struct row rows[] = {
|
||||
" return r;\n"
|
||||
"};\n",
|
||||
2 },
|
||||
/* 5. Same-block re-declaration. Today's checker silently accepts
|
||||
* `let a: i32 = 0; let a: i32 = 9;` (scope_define returns NULL
|
||||
* on dup but the caller in cmd/wcc/check.c:1443 doesn't error;
|
||||
* wwstage check.ww behaves the same). Pre-fix: both lets shared
|
||||
* one slot, last write wins by storage. Post-fix: each let gets
|
||||
* its own slot but localfind walks head-first → still last-
|
||||
* write-wins observably. This row pins that observable contract
|
||||
* — if the checker tightens later to reject same-scope redecl
|
||||
* (task #32), this row is the canary that flips from "exit 9"
|
||||
* to "build fails", explicitly opting in to the new shape. */
|
||||
{ "same_block_redecl_pin",
|
||||
"fn main() i32 = {\n"
|
||||
" let a: i32 = 1;\n"
|
||||
" let a: i32 = 9;\n"
|
||||
" return a;\n"
|
||||
"};\n",
|
||||
9 },
|
||||
/* 5. Same-block re-declaration moved to test/wcc/712_redecl
|
||||
* (`neg_let_same_block`) when #32 made it a build-time error.
|
||||
* The pin's purpose — exercising the localoff fresh-stub path on
|
||||
* a same-name same-block dup — is now an upstream-rejected shape,
|
||||
* so the codegen branch it covered is no longer reachable through
|
||||
* legal source. Row slot kept empty for stability of the
|
||||
* surrounding row numbering. */
|
||||
/* 6. Defer + inner-block shadow + outer-scope post-defer read.
|
||||
*
|
||||
* `defer touch(&a)` queues the call; at fn-exit the defer's
|
||||
|
||||
Reference in New Issue
Block a user