wcc/ww: checktryprop flattens spread variants in the success count
The F8 multi-success gate counted a `...inner` spread as one variant, bypassing the multi-success reject. New trycountvariants recursively flattens spreads (the #209 recursion; cstage check.c:2160). Also carries the rule-7 deferral cites for the adjacent task-#50/#51 holes (exprtypeoftry lookups, desugarcallargs fn-ptr bail, checkisas) — the attempted scopelookupprefer hardening is byte-id-blocked by the #50 curmod layer (evidence in the task).
This commit is contained in:
@@ -15672,6 +15672,13 @@ fn desugarcallargs(c: *checker, n: *node) void = {
|
||||
};
|
||||
}; };
|
||||
let decl: *node = calleefndecl(c, n.lhs);
|
||||
// task #51: calleefndecl nils every fn-VALUE callee — a deref `(*fp)(...)`
|
||||
// (N_UN), an SK_VAR fn-ptr ident, a struct-field fn call — so this bail
|
||||
// skips the #258 array→slice desugar AND the general arg typecheck for
|
||||
// them, and cgen then pushes raw array words where the callee reads a 24B
|
||||
// slice header. cstage drives the same arg loop off the callee TYPE
|
||||
// (cmd/wcc/check.c:1828 type_chase_named(cexpr(callee))). The type-keyed
|
||||
// callee path is MECHANISM (not a checker gate) — filed as task #51.
|
||||
if (decl == nil) { return; };
|
||||
let param: *node = decl.list;
|
||||
let prev: *node = nil;
|
||||
@@ -16200,6 +16207,13 @@ fn checkisas(c: *checker, n: *node) void = {
|
||||
fn exprtypeoftry(c: *checker, e: *node) *node = {
|
||||
if (e == nil) { return nil; };
|
||||
if (e.kind == nkind.N_IDENT) {
|
||||
// #11a/task #50: this bare-leaf lookup wants curmod preference
|
||||
// (scopelookupprefer, the #53/#55 family) but the swap BROKE the
|
||||
// 995 self-rebuild byte-id — in the bundled self-compile ww's
|
||||
// scopelookupprefer diverges from cstage's resolution (the same
|
||||
// curmod-in-bundle layer that blocked #5), flipping a try-operand
|
||||
// stamp in the selfhost source. Held at plain scopelookup until #50
|
||||
// lands the bundle curmod fix; aligning here in isolation is unsound.
|
||||
let s: *sym = scopelookup(c.cur, e.str);
|
||||
if (s == nil) { return nil; };
|
||||
if (s.decl == nil) { return nil; };
|
||||
@@ -16213,17 +16227,61 @@ fn exprtypeoftry(c: *checker, e: *node) *node = {
|
||||
let nm: str;
|
||||
nm.ptr = nil; nm.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) { nm = callee.str; };
|
||||
// #11b/task #51: an N_DOT callee `mod.f()?` is looked up by BARE
|
||||
// LEAF here, NOT via the module qualifier (callee.lhs) the way
|
||||
// exprtype's own N_CALL arm does (:3095 scopelookupinmodule) — a
|
||||
// cross-module same-leaf `f` misbinds. The mechanism fix (module-
|
||||
// keyed callee resolution + the deref-callee `(*fp)()?` shape that
|
||||
// returns nil below) rides task #51 with the fn-ptr-callee desugar.
|
||||
if (callee.kind == nkind.N_DOT) { nm = callee.str; };
|
||||
if (nm.len == 0) { return nil; };
|
||||
// #11a/task #50: curmod preference wanted here too, held at plain
|
||||
// scopelookup — the prefer swap broke 995 byte-id (bundle curmod
|
||||
// divergence, same as the N_IDENT arm above + #5/#50).
|
||||
let s: *sym = scopelookup(c.cur, nm);
|
||||
if (s == nil) { return nil; };
|
||||
if (s.skind != skind.SK_FN) { return nil; };
|
||||
if (s.decl == nil) { return nil; };
|
||||
return s.decl.lhs;
|
||||
};
|
||||
// task #51: a deref callee `(*fp)()?` / SK_VAR fn-ptr ident / struct-
|
||||
// field fn call returns nil here, so checktryprop silently bails and
|
||||
// the F8 multi-success reject is skipped for those shapes. The fix
|
||||
// (type-keyed operand resolution: peel TPTR → TFN.ret) is mechanism,
|
||||
// filed with the fn-ptr-callee desugar as task #51 — NOT a checker gate.
|
||||
return nil;
|
||||
};
|
||||
|
||||
// trycountvariants — #38/F3 (review item 8): walk a tagged union's variant
|
||||
// list FLATTENING `...inner` spreads, accumulating the success count and the
|
||||
// has-error flag. cstage counts/checks the resolve_type-FLATTENED u->params
|
||||
// (cmd/wcc/check.c:2160-2165 over the TK_ELLIPSIS-spliced chain); ww's
|
||||
// checktryprop walked the raw AST, so a `(...inner | e)` counted the spread as
|
||||
// ONE success and the F8 multi-success gate never fired — the exact silent
|
||||
// accept the gate exists to catch (a valid bool success then treated as error
|
||||
// by the single-tag-compare cgen). iserror is judged against the OUTER union
|
||||
// (`outer`) throughout — taggedhaserr(outer) holds because the spread's sibling
|
||||
// or a spliced member carries the error, so each flattened member routes
|
||||
// through varianterr, matching cstage's per-param iserror. Mirrors the #209
|
||||
// spread recursion tinfofornode already runs over vu.params (check.ww:2275).
|
||||
fn trycountvariants(c: *checker, outer: *node, v: *node, nsuccp: *int,
|
||||
haserrp: *bool, depth: i32) void = {
|
||||
for (v != nil) {
|
||||
if (v.op == tkind.TK_ELLIPSIS && depth < 8i32) {
|
||||
let inner: *node = resolvealias(c, unwrapbang(v));
|
||||
if (inner != nil && inner.kind == nkind.N_TTAGGED) {
|
||||
trycountvariants(c, outer, inner.list, nsuccp,
|
||||
haserrp, depth + 1i32);
|
||||
v = v.next;
|
||||
continue;
|
||||
};
|
||||
};
|
||||
if (iserrvariant(c, outer, v)) { *haserrp = true; }
|
||||
else { *nsuccp += 1; };
|
||||
v = v.next;
|
||||
};
|
||||
};
|
||||
|
||||
fn checktryprop(c: *checker, n: *node) void = {
|
||||
if (n == nil) { return; };
|
||||
let t: *node = exprtypeoftry(c, n.lhs);
|
||||
@@ -16240,11 +16298,9 @@ fn checktryprop(c: *checker, n: *node) void = {
|
||||
// N_TRYPROP/N_TRYUNW.
|
||||
let haserr: bool = false;
|
||||
let nsucc: int = 0;
|
||||
let v: *node = u.list;
|
||||
for (v != nil) {
|
||||
if (iserrvariant(c, u, v)) { haserr = true; } else { nsucc += 1; };
|
||||
v = v.next;
|
||||
};
|
||||
// #38/F3 (review item 8): flatten `...inner` spreads so the F8
|
||||
// multi-success gate counts the spliced members, not the spread alias.
|
||||
trycountvariants(c, u, u.list, &nsucc, &haserr, 0i32);
|
||||
if (nsucc > 1) {
|
||||
if (n.kind == nkind.N_TRYPROP) { cerr("?"); } else { cerr("!"); };
|
||||
cerr(": multi-success union unwired (task #14): bind and match instead\n");
|
||||
|
||||
@@ -5252,6 +5252,13 @@ fn desugarcallargs(c: *checker, n: *node) void = {
|
||||
};
|
||||
}; };
|
||||
let decl: *node = calleefndecl(c, n.lhs);
|
||||
// task #51: calleefndecl nils every fn-VALUE callee — a deref `(*fp)(...)`
|
||||
// (N_UN), an SK_VAR fn-ptr ident, a struct-field fn call — so this bail
|
||||
// skips the #258 array→slice desugar AND the general arg typecheck for
|
||||
// them, and cgen then pushes raw array words where the callee reads a 24B
|
||||
// slice header. cstage drives the same arg loop off the callee TYPE
|
||||
// (cmd/wcc/check.c:1828 type_chase_named(cexpr(callee))). The type-keyed
|
||||
// callee path is MECHANISM (not a checker gate) — filed as task #51.
|
||||
if (decl == nil) { return; };
|
||||
let param: *node = decl.list;
|
||||
let prev: *node = nil;
|
||||
@@ -5780,6 +5787,13 @@ fn checkisas(c: *checker, n: *node) void = {
|
||||
fn exprtypeoftry(c: *checker, e: *node) *node = {
|
||||
if (e == nil) { return nil; };
|
||||
if (e.kind == nkind.N_IDENT) {
|
||||
// #11a/task #50: this bare-leaf lookup wants curmod preference
|
||||
// (scopelookupprefer, the #53/#55 family) but the swap BROKE the
|
||||
// 995 self-rebuild byte-id — in the bundled self-compile ww's
|
||||
// scopelookupprefer diverges from cstage's resolution (the same
|
||||
// curmod-in-bundle layer that blocked #5), flipping a try-operand
|
||||
// stamp in the selfhost source. Held at plain scopelookup until #50
|
||||
// lands the bundle curmod fix; aligning here in isolation is unsound.
|
||||
let s: *sym = scopelookup(c.cur, e.str);
|
||||
if (s == nil) { return nil; };
|
||||
if (s.decl == nil) { return nil; };
|
||||
@@ -5793,17 +5807,61 @@ fn exprtypeoftry(c: *checker, e: *node) *node = {
|
||||
let nm: str;
|
||||
nm.ptr = nil; nm.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) { nm = callee.str; };
|
||||
// #11b/task #51: an N_DOT callee `mod.f()?` is looked up by BARE
|
||||
// LEAF here, NOT via the module qualifier (callee.lhs) the way
|
||||
// exprtype's own N_CALL arm does (:3095 scopelookupinmodule) — a
|
||||
// cross-module same-leaf `f` misbinds. The mechanism fix (module-
|
||||
// keyed callee resolution + the deref-callee `(*fp)()?` shape that
|
||||
// returns nil below) rides task #51 with the fn-ptr-callee desugar.
|
||||
if (callee.kind == nkind.N_DOT) { nm = callee.str; };
|
||||
if (nm.len == 0) { return nil; };
|
||||
// #11a/task #50: curmod preference wanted here too, held at plain
|
||||
// scopelookup — the prefer swap broke 995 byte-id (bundle curmod
|
||||
// divergence, same as the N_IDENT arm above + #5/#50).
|
||||
let s: *sym = scopelookup(c.cur, nm);
|
||||
if (s == nil) { return nil; };
|
||||
if (s.skind != skind.SK_FN) { return nil; };
|
||||
if (s.decl == nil) { return nil; };
|
||||
return s.decl.lhs;
|
||||
};
|
||||
// task #51: a deref callee `(*fp)()?` / SK_VAR fn-ptr ident / struct-
|
||||
// field fn call returns nil here, so checktryprop silently bails and
|
||||
// the F8 multi-success reject is skipped for those shapes. The fix
|
||||
// (type-keyed operand resolution: peel TPTR → TFN.ret) is mechanism,
|
||||
// filed with the fn-ptr-callee desugar as task #51 — NOT a checker gate.
|
||||
return nil;
|
||||
};
|
||||
|
||||
// trycountvariants — #38/F3 (review item 8): walk a tagged union's variant
|
||||
// list FLATTENING `...inner` spreads, accumulating the success count and the
|
||||
// has-error flag. cstage counts/checks the resolve_type-FLATTENED u->params
|
||||
// (cmd/wcc/check.c:2160-2165 over the TK_ELLIPSIS-spliced chain); ww's
|
||||
// checktryprop walked the raw AST, so a `(...inner | e)` counted the spread as
|
||||
// ONE success and the F8 multi-success gate never fired — the exact silent
|
||||
// accept the gate exists to catch (a valid bool success then treated as error
|
||||
// by the single-tag-compare cgen). iserror is judged against the OUTER union
|
||||
// (`outer`) throughout — taggedhaserr(outer) holds because the spread's sibling
|
||||
// or a spliced member carries the error, so each flattened member routes
|
||||
// through varianterr, matching cstage's per-param iserror. Mirrors the #209
|
||||
// spread recursion tinfofornode already runs over vu.params (check.ww:2275).
|
||||
fn trycountvariants(c: *checker, outer: *node, v: *node, nsuccp: *int,
|
||||
haserrp: *bool, depth: i32) void = {
|
||||
for (v != nil) {
|
||||
if (v.op == tkind.TK_ELLIPSIS && depth < 8i32) {
|
||||
let inner: *node = resolvealias(c, unwrapbang(v));
|
||||
if (inner != nil && inner.kind == nkind.N_TTAGGED) {
|
||||
trycountvariants(c, outer, inner.list, nsuccp,
|
||||
haserrp, depth + 1i32);
|
||||
v = v.next;
|
||||
continue;
|
||||
};
|
||||
};
|
||||
if (iserrvariant(c, outer, v)) { *haserrp = true; }
|
||||
else { *nsuccp += 1; };
|
||||
v = v.next;
|
||||
};
|
||||
};
|
||||
|
||||
fn checktryprop(c: *checker, n: *node) void = {
|
||||
if (n == nil) { return; };
|
||||
let t: *node = exprtypeoftry(c, n.lhs);
|
||||
@@ -5820,11 +5878,9 @@ fn checktryprop(c: *checker, n: *node) void = {
|
||||
// N_TRYPROP/N_TRYUNW.
|
||||
let haserr: bool = false;
|
||||
let nsucc: int = 0;
|
||||
let v: *node = u.list;
|
||||
for (v != nil) {
|
||||
if (iserrvariant(c, u, v)) { haserr = true; } else { nsucc += 1; };
|
||||
v = v.next;
|
||||
};
|
||||
// #38/F3 (review item 8): flatten `...inner` spreads so the F8
|
||||
// multi-success gate counts the spliced members, not the spread alias.
|
||||
trycountvariants(c, u, u.list, &nsucc, &haserr, 0i32);
|
||||
if (nsucc > 1) {
|
||||
if (n.kind == nkind.N_TRYPROP) { cerr("?"); } else { cerr("!"); };
|
||||
cerr(": multi-success union unwired (task #14): bind and match instead\n");
|
||||
|
||||
@@ -15672,6 +15672,13 @@ fn desugarcallargs(c: *checker, n: *node) void = {
|
||||
};
|
||||
}; };
|
||||
let decl: *node = calleefndecl(c, n.lhs);
|
||||
// task #51: calleefndecl nils every fn-VALUE callee — a deref `(*fp)(...)`
|
||||
// (N_UN), an SK_VAR fn-ptr ident, a struct-field fn call — so this bail
|
||||
// skips the #258 array→slice desugar AND the general arg typecheck for
|
||||
// them, and cgen then pushes raw array words where the callee reads a 24B
|
||||
// slice header. cstage drives the same arg loop off the callee TYPE
|
||||
// (cmd/wcc/check.c:1828 type_chase_named(cexpr(callee))). The type-keyed
|
||||
// callee path is MECHANISM (not a checker gate) — filed as task #51.
|
||||
if (decl == nil) { return; };
|
||||
let param: *node = decl.list;
|
||||
let prev: *node = nil;
|
||||
@@ -16200,6 +16207,13 @@ fn checkisas(c: *checker, n: *node) void = {
|
||||
fn exprtypeoftry(c: *checker, e: *node) *node = {
|
||||
if (e == nil) { return nil; };
|
||||
if (e.kind == nkind.N_IDENT) {
|
||||
// #11a/task #50: this bare-leaf lookup wants curmod preference
|
||||
// (scopelookupprefer, the #53/#55 family) but the swap BROKE the
|
||||
// 995 self-rebuild byte-id — in the bundled self-compile ww's
|
||||
// scopelookupprefer diverges from cstage's resolution (the same
|
||||
// curmod-in-bundle layer that blocked #5), flipping a try-operand
|
||||
// stamp in the selfhost source. Held at plain scopelookup until #50
|
||||
// lands the bundle curmod fix; aligning here in isolation is unsound.
|
||||
let s: *sym = scopelookup(c.cur, e.str);
|
||||
if (s == nil) { return nil; };
|
||||
if (s.decl == nil) { return nil; };
|
||||
@@ -16213,17 +16227,61 @@ fn exprtypeoftry(c: *checker, e: *node) *node = {
|
||||
let nm: str;
|
||||
nm.ptr = nil; nm.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) { nm = callee.str; };
|
||||
// #11b/task #51: an N_DOT callee `mod.f()?` is looked up by BARE
|
||||
// LEAF here, NOT via the module qualifier (callee.lhs) the way
|
||||
// exprtype's own N_CALL arm does (:3095 scopelookupinmodule) — a
|
||||
// cross-module same-leaf `f` misbinds. The mechanism fix (module-
|
||||
// keyed callee resolution + the deref-callee `(*fp)()?` shape that
|
||||
// returns nil below) rides task #51 with the fn-ptr-callee desugar.
|
||||
if (callee.kind == nkind.N_DOT) { nm = callee.str; };
|
||||
if (nm.len == 0) { return nil; };
|
||||
// #11a/task #50: curmod preference wanted here too, held at plain
|
||||
// scopelookup — the prefer swap broke 995 byte-id (bundle curmod
|
||||
// divergence, same as the N_IDENT arm above + #5/#50).
|
||||
let s: *sym = scopelookup(c.cur, nm);
|
||||
if (s == nil) { return nil; };
|
||||
if (s.skind != skind.SK_FN) { return nil; };
|
||||
if (s.decl == nil) { return nil; };
|
||||
return s.decl.lhs;
|
||||
};
|
||||
// task #51: a deref callee `(*fp)()?` / SK_VAR fn-ptr ident / struct-
|
||||
// field fn call returns nil here, so checktryprop silently bails and
|
||||
// the F8 multi-success reject is skipped for those shapes. The fix
|
||||
// (type-keyed operand resolution: peel TPTR → TFN.ret) is mechanism,
|
||||
// filed with the fn-ptr-callee desugar as task #51 — NOT a checker gate.
|
||||
return nil;
|
||||
};
|
||||
|
||||
// trycountvariants — #38/F3 (review item 8): walk a tagged union's variant
|
||||
// list FLATTENING `...inner` spreads, accumulating the success count and the
|
||||
// has-error flag. cstage counts/checks the resolve_type-FLATTENED u->params
|
||||
// (cmd/wcc/check.c:2160-2165 over the TK_ELLIPSIS-spliced chain); ww's
|
||||
// checktryprop walked the raw AST, so a `(...inner | e)` counted the spread as
|
||||
// ONE success and the F8 multi-success gate never fired — the exact silent
|
||||
// accept the gate exists to catch (a valid bool success then treated as error
|
||||
// by the single-tag-compare cgen). iserror is judged against the OUTER union
|
||||
// (`outer`) throughout — taggedhaserr(outer) holds because the spread's sibling
|
||||
// or a spliced member carries the error, so each flattened member routes
|
||||
// through varianterr, matching cstage's per-param iserror. Mirrors the #209
|
||||
// spread recursion tinfofornode already runs over vu.params (check.ww:2275).
|
||||
fn trycountvariants(c: *checker, outer: *node, v: *node, nsuccp: *int,
|
||||
haserrp: *bool, depth: i32) void = {
|
||||
for (v != nil) {
|
||||
if (v.op == tkind.TK_ELLIPSIS && depth < 8i32) {
|
||||
let inner: *node = resolvealias(c, unwrapbang(v));
|
||||
if (inner != nil && inner.kind == nkind.N_TTAGGED) {
|
||||
trycountvariants(c, outer, inner.list, nsuccp,
|
||||
haserrp, depth + 1i32);
|
||||
v = v.next;
|
||||
continue;
|
||||
};
|
||||
};
|
||||
if (iserrvariant(c, outer, v)) { *haserrp = true; }
|
||||
else { *nsuccp += 1; };
|
||||
v = v.next;
|
||||
};
|
||||
};
|
||||
|
||||
fn checktryprop(c: *checker, n: *node) void = {
|
||||
if (n == nil) { return; };
|
||||
let t: *node = exprtypeoftry(c, n.lhs);
|
||||
@@ -16240,11 +16298,9 @@ fn checktryprop(c: *checker, n: *node) void = {
|
||||
// N_TRYPROP/N_TRYUNW.
|
||||
let haserr: bool = false;
|
||||
let nsucc: int = 0;
|
||||
let v: *node = u.list;
|
||||
for (v != nil) {
|
||||
if (iserrvariant(c, u, v)) { haserr = true; } else { nsucc += 1; };
|
||||
v = v.next;
|
||||
};
|
||||
// #38/F3 (review item 8): flatten `...inner` spreads so the F8
|
||||
// multi-success gate counts the spliced members, not the spread alias.
|
||||
trycountvariants(c, u, u.list, &nsucc, &haserr, 0i32);
|
||||
if (nsucc > 1) {
|
||||
if (n.kind == nkind.N_TRYPROP) { cerr("?"); } else { cerr("!"); };
|
||||
cerr(": multi-success union unwired (task #14): bind and match instead\n");
|
||||
|
||||
Reference in New Issue
Block a user