check: reject duplicate struct field name in wwstage (catB-7/14)
The wwstage checker silently accepted a struct with repeated field names; cstage already rejects it (cmd/wcc/check.c:925-947). Add validatestructfields, dispatched once per struct decl from resolvewalk's eager type-decl arm (check.ww:792, sibling to the N_TENUM stampenumvals fire): a pure read-only O(n^2) named-field dup walk that emits "duplicate field 'X'" via cerr + c.errs, with no mutation -- valid-program codegen is unchanged so cstage==wwstage byte-id holds. Named fields only; ww has no struct embedding, so cstage's embed-collision arm is intentionally not ported (separate parser gap, catB-89). Test: new table-driven both-stage reject test 849_dupfield_reject (adjacent / non-adjacent / different-type dup rows + a distinct-field control that builds and runs). Full make test: 451 green incl. 990-997 byte-id.
This commit is contained in:
@@ -789,6 +789,7 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
};
|
||||
|
||||
if (k == syntax.nkind.N_TENUM) { stampenumvals(c, n); };
|
||||
if (k == syntax.nkind.N_TSTRUCT) { validatestructfields(c, n); };
|
||||
|
||||
// #42's size/align/offset fold trigger lived here pre-A.6.0; the
|
||||
// A.6.0 end-of-fn general dispatch (below) now fires exprtype on
|
||||
@@ -1859,6 +1860,40 @@ fn stampenumvals(c: *checker, n: *syntax.node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// validatestructfields — reject a struct decl carrying two fields with
|
||||
// the same name. Pure diagnostic: a read-only O(n^2) walk over the named
|
||||
// field list, no n.type_ / offset / checker-state mutation (the byte-id
|
||||
// safety condition — valid programs have no dup, so codegen is untouched).
|
||||
// Cstage twin: cmd/wcc/check.c:943-950 (the regular-named-field arm of
|
||||
// resolve_type's N_TSTRUCT). The embed-collision arm there (check.c:961-
|
||||
// 985) is NOT ported — ww has no struct embedding (parser gap catB-89),
|
||||
// so every N_TFIELD is a regular named field. Fires once per struct decl
|
||||
// from resolvewalk's eager type-decl dispatch (rule-10 symmetric with
|
||||
// cstage's once-per-resolve_type), not from the per-query size/align/
|
||||
// offset recompute arms (use-site = double-fire, drew-dv ruling).
|
||||
fn validatestructfields(c: *checker, n: *syntax.node) void = {
|
||||
let f: *syntax.node = n.list;
|
||||
for (f != nil) {
|
||||
if (f.kind == syntax.nkind.N_TFIELD && f.str.len != 0) {
|
||||
let e: *syntax.node = n.list;
|
||||
for (e != f) {
|
||||
if (e.kind == syntax.nkind.N_TFIELD
|
||||
&& e.str.len != 0
|
||||
&& syntax.streq(e.str, f.str)) {
|
||||
cerr(f.file);
|
||||
cerr(": error: duplicate field '");
|
||||
cerr(f.str);
|
||||
cerr("'\n");
|
||||
c.errs += 1;
|
||||
break;
|
||||
};
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
};
|
||||
|
||||
// stampnilexpr — stamp nil-typed nodes in a constant expr subtree to
|
||||
// `ti`. lhs/rhs cover the enum constexpr grammar enumvalfold accepts
|
||||
// (literals, unary, binary, sibling backref); non-nil nodes keep the
|
||||
|
||||
Reference in New Issue
Block a user