wcc: stamp for-range tuple-binds and massign blank via shared helper
For-range tuple-destructure binders (for (let (k,v) .. s)) and the tuple massign discard _ were left nil-typed: the for-range binders are N_IDENT use-sites and the _ slot, though unbound, has a real element type. Add a shared stamptuplebinds helper — one lockstep walk distributing an N_TTUPLE's per-element types onto a binder chain — refactoring the existing N_MLET destructure loop into it (behavior identical) and adding N_FORRANGE and N_MASSIGN call-sites. _ is STAMPED with its slot's element type (unbound is not untyped), not exempted. Mirrors harec create_unpack_bindings (ref/harec/src/check.c:1354-1419), the routine harec shares between let-unpack and the for-each header (:2308-2317). A prerequisite for arming the wwstage asserttyped bail. byte-id holds (cgen derives binder/elem widths structurally, never off type_; 990-997 green). Extends the 901 gap-corpus with 901_forrange_tuple.ww + 901_massign_blank.ww.
This commit is contained in:
@@ -10261,6 +10261,56 @@ fn installdecl(c: *checker, file: *node, d: *node) void = {
|
|||||||
if (k == nkind.N_LET) { scopedefineinmodule(c.top, nm, mod, skind.SK_VAR, nil, d); return; };
|
if (k == nkind.N_LET) { scopedefineinmodule(c.top, nm, mod, skind.SK_VAR, nil, d); return; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// stamptuplebinds — distribute a tuple's per-element types onto a
|
||||||
|
// destructure binding chain, walked in lockstep with the resolved
|
||||||
|
// N_TTUPLE element chain (each `elems` link carries its element type on
|
||||||
|
// .lhs). Mirror of harec's create_unpack_bindings
|
||||||
|
// (ref/harec/src/check.c:1354-1419), which harec shares between
|
||||||
|
// let-unpack (check_expr_binding) and the for-each loop header
|
||||||
|
// (ref/harec/src/check.c:2308-2317) — the one shape behind ww's
|
||||||
|
// `let (a,b) = f()`, `for (let (a,b) .. s)`, and the ww-extension
|
||||||
|
// multi-assign `a, _ = f()`.
|
||||||
|
//
|
||||||
|
// `define` (the binding contexts: let-unpack + for-range) installs each
|
||||||
|
// named binder as a fresh SK_VAR and back-fills its declared type onto
|
||||||
|
// .lhs so use sites resolve through the N_IDENT exprtype path. Multi-
|
||||||
|
// assign targets are pre-declared lvalues, so it passes false: .lhs is
|
||||||
|
// left untouched (an N_INDEX/N_DOT target carries a live operand there)
|
||||||
|
// and only the type_ stamp fires on the still-untyped slots.
|
||||||
|
//
|
||||||
|
// Each binder/target node's own type_ is stamped from its element type so
|
||||||
|
// the asserttyped gate sees a typed node. This covers the discard `_` (an
|
||||||
|
// empty-str N_IDENT with no decl to read a type back from): harec drops
|
||||||
|
// `_` yet still advances the tuple slot, so that slot's element type is
|
||||||
|
// the honest type to stamp — `_` is UNBOUND, not UNTYPED.
|
||||||
|
fn stamptuplebinds(c: *checker, binds: *node, elems: *node,
|
||||||
|
define: bool, what: str) void = {
|
||||||
|
let b: *node = binds;
|
||||||
|
let pt: *node = elems;
|
||||||
|
for (b != nil) {
|
||||||
|
let et: *node = nil;
|
||||||
|
if (pt != nil) { et = pt.lhs; };
|
||||||
|
if (define) {
|
||||||
|
if (b.lhs == nil) { b.lhs = et; };
|
||||||
|
let bnm: str = b.str;
|
||||||
|
if (bnm.len > 0) {
|
||||||
|
checkmoduleshadow(c, bnm, what);
|
||||||
|
scopedefine(c.cur, bnm, skind.SK_VAR, nil, b);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (b.type_ == nil) {
|
||||||
|
let src: *node = b.lhs;
|
||||||
|
if (src == nil) { src = et; };
|
||||||
|
if (src != nil) {
|
||||||
|
let ti: *tinfo = tinfofornode(c, src);
|
||||||
|
if (ti != nil) { b.type_ = ti: *void; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
b = b.next;
|
||||||
|
if (pt != nil) { pt = pt.next; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// resolvewalk — recursive AST walk that, for every nkind.N_IDENT and
|
// resolvewalk — recursive AST walk that, for every nkind.N_IDENT and
|
||||||
// nkind.N_TNAME seen, looks up the name and bumps the resolved/unresolved
|
// nkind.N_TNAME seen, looks up the name and bumps the resolved/unresolved
|
||||||
// counters. Local lets are installed in the current scope as soon as
|
// counters. Local lets are installed in the current scope as soon as
|
||||||
@@ -10354,15 +10404,22 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
|||||||
if (k == nkind.N_FORRANGE) {
|
if (k == nkind.N_FORRANGE) {
|
||||||
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
|
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
|
||||||
if (n.list != nil) {
|
if (n.list != nil) {
|
||||||
let m: *node = n.list;
|
// Tuple destructure `for (let (a,b) .. xs)`: peel the
|
||||||
for (m != nil) {
|
// iterable's element type and distribute its tuple
|
||||||
let bnm: str = m.str;
|
// element types onto the binders, the same lockstep walk
|
||||||
if (bnm.len > 0) {
|
// harec runs for the for-each header
|
||||||
checkmoduleshadow(c, bnm, "binding");
|
// (ref/harec/src/check.c:2308-2317 → create_unpack_bindings).
|
||||||
scopedefine(c.cur, bnm, skind.SK_VAR, nil, m);
|
let elems: *node = nil;
|
||||||
};
|
let it: *node = exprtype(c, n.lhs, nil);
|
||||||
m = m.next;
|
if (it != nil) {
|
||||||
|
let et: *node = nil;
|
||||||
|
if (it.kind == nkind.N_TSLICE) { et = it.lhs; };
|
||||||
|
if (it.kind == nkind.N_TARRAY) { et = it.lhs; };
|
||||||
|
if (et != nil) { if (et.kind == nkind.N_TTUPLE) {
|
||||||
|
elems = et.list;
|
||||||
|
}; };
|
||||||
};
|
};
|
||||||
|
stamptuplebinds(c, n.list, elems, true, "binding");
|
||||||
} else {
|
} else {
|
||||||
let bnm: str = n.str;
|
let bnm: str = n.str;
|
||||||
if (bnm.len > 0) {
|
if (bnm.len > 0) {
|
||||||
@@ -10452,19 +10509,29 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
|||||||
pt = rty.list;
|
pt = rty.list;
|
||||||
}; };
|
}; };
|
||||||
}; };
|
}; };
|
||||||
|
stamptuplebinds(c, n.list, pt, true, "let");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// `a, _ = call();` — tuple multi-assign (a retained ww extension over
|
||||||
|
// Hare; harec has no statement-position unpack-assign). Targets are
|
||||||
|
// pre-declared lvalues, resolved by the per-target resolvewalk below
|
||||||
|
// before the distribution; stamptuplebinds(define=false) only stamps
|
||||||
|
// still-nil slots, which is exactly the discard `_` (no decl, so the
|
||||||
|
// N_IDENT exprtype path leaves it untyped). Distribution mirrors the
|
||||||
|
// N_MLET/N_FORRANGE binders; see stamptuplebinds.
|
||||||
|
if (k == nkind.N_MASSIGN) {
|
||||||
|
if (n.rhs != nil) { resolvewalk(c, n.rhs); };
|
||||||
|
let pt: *node = nil;
|
||||||
|
if (n.rhs != nil) { if (n.rhs.kind == nkind.N_CALL) {
|
||||||
|
let rty: *node = exprtype(c, n.rhs, nil);
|
||||||
|
if (rty != nil) { if (rty.kind == nkind.N_TTUPLE) {
|
||||||
|
pt = rty.list;
|
||||||
|
}; };
|
||||||
|
}; };
|
||||||
let l: *node = n.list;
|
let l: *node = n.list;
|
||||||
for (l != nil) {
|
for (l != nil) { resolvewalk(c, l); l = l.next; };
|
||||||
if (l.lhs == nil) {
|
stamptuplebinds(c, n.list, pt, false, "");
|
||||||
if (pt != nil) { l.lhs = pt.lhs; };
|
|
||||||
};
|
|
||||||
let bnm: str = l.str;
|
|
||||||
if (bnm.len > 0) {
|
|
||||||
checkmoduleshadow(c, bnm, "let");
|
|
||||||
scopedefine(c.cur, bnm, skind.SK_VAR, nil, l);
|
|
||||||
};
|
|
||||||
l = l.next;
|
|
||||||
if (pt != nil) { pt = pt.next; };
|
|
||||||
};
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -231,6 +231,56 @@ fn installdecl(c: *checker, file: *node, d: *node) void = {
|
|||||||
if (k == nkind.N_LET) { scopedefineinmodule(c.top, nm, mod, skind.SK_VAR, nil, d); return; };
|
if (k == nkind.N_LET) { scopedefineinmodule(c.top, nm, mod, skind.SK_VAR, nil, d); return; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// stamptuplebinds — distribute a tuple's per-element types onto a
|
||||||
|
// destructure binding chain, walked in lockstep with the resolved
|
||||||
|
// N_TTUPLE element chain (each `elems` link carries its element type on
|
||||||
|
// .lhs). Mirror of harec's create_unpack_bindings
|
||||||
|
// (ref/harec/src/check.c:1354-1419), which harec shares between
|
||||||
|
// let-unpack (check_expr_binding) and the for-each loop header
|
||||||
|
// (ref/harec/src/check.c:2308-2317) — the one shape behind ww's
|
||||||
|
// `let (a,b) = f()`, `for (let (a,b) .. s)`, and the ww-extension
|
||||||
|
// multi-assign `a, _ = f()`.
|
||||||
|
//
|
||||||
|
// `define` (the binding contexts: let-unpack + for-range) installs each
|
||||||
|
// named binder as a fresh SK_VAR and back-fills its declared type onto
|
||||||
|
// .lhs so use sites resolve through the N_IDENT exprtype path. Multi-
|
||||||
|
// assign targets are pre-declared lvalues, so it passes false: .lhs is
|
||||||
|
// left untouched (an N_INDEX/N_DOT target carries a live operand there)
|
||||||
|
// and only the type_ stamp fires on the still-untyped slots.
|
||||||
|
//
|
||||||
|
// Each binder/target node's own type_ is stamped from its element type so
|
||||||
|
// the asserttyped gate sees a typed node. This covers the discard `_` (an
|
||||||
|
// empty-str N_IDENT with no decl to read a type back from): harec drops
|
||||||
|
// `_` yet still advances the tuple slot, so that slot's element type is
|
||||||
|
// the honest type to stamp — `_` is UNBOUND, not UNTYPED.
|
||||||
|
fn stamptuplebinds(c: *checker, binds: *node, elems: *node,
|
||||||
|
define: bool, what: str) void = {
|
||||||
|
let b: *node = binds;
|
||||||
|
let pt: *node = elems;
|
||||||
|
for (b != nil) {
|
||||||
|
let et: *node = nil;
|
||||||
|
if (pt != nil) { et = pt.lhs; };
|
||||||
|
if (define) {
|
||||||
|
if (b.lhs == nil) { b.lhs = et; };
|
||||||
|
let bnm: str = b.str;
|
||||||
|
if (bnm.len > 0) {
|
||||||
|
checkmoduleshadow(c, bnm, what);
|
||||||
|
scopedefine(c.cur, bnm, skind.SK_VAR, nil, b);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (b.type_ == nil) {
|
||||||
|
let src: *node = b.lhs;
|
||||||
|
if (src == nil) { src = et; };
|
||||||
|
if (src != nil) {
|
||||||
|
let ti: *tinfo = tinfofornode(c, src);
|
||||||
|
if (ti != nil) { b.type_ = ti: *void; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
b = b.next;
|
||||||
|
if (pt != nil) { pt = pt.next; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// resolvewalk — recursive AST walk that, for every nkind.N_IDENT and
|
// resolvewalk — recursive AST walk that, for every nkind.N_IDENT and
|
||||||
// nkind.N_TNAME seen, looks up the name and bumps the resolved/unresolved
|
// nkind.N_TNAME seen, looks up the name and bumps the resolved/unresolved
|
||||||
// counters. Local lets are installed in the current scope as soon as
|
// counters. Local lets are installed in the current scope as soon as
|
||||||
@@ -324,15 +374,22 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
|||||||
if (k == nkind.N_FORRANGE) {
|
if (k == nkind.N_FORRANGE) {
|
||||||
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
|
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
|
||||||
if (n.list != nil) {
|
if (n.list != nil) {
|
||||||
let m: *node = n.list;
|
// Tuple destructure `for (let (a,b) .. xs)`: peel the
|
||||||
for (m != nil) {
|
// iterable's element type and distribute its tuple
|
||||||
let bnm: str = m.str;
|
// element types onto the binders, the same lockstep walk
|
||||||
if (bnm.len > 0) {
|
// harec runs for the for-each header
|
||||||
checkmoduleshadow(c, bnm, "binding");
|
// (ref/harec/src/check.c:2308-2317 → create_unpack_bindings).
|
||||||
scopedefine(c.cur, bnm, skind.SK_VAR, nil, m);
|
let elems: *node = nil;
|
||||||
};
|
let it: *node = exprtype(c, n.lhs, nil);
|
||||||
m = m.next;
|
if (it != nil) {
|
||||||
|
let et: *node = nil;
|
||||||
|
if (it.kind == nkind.N_TSLICE) { et = it.lhs; };
|
||||||
|
if (it.kind == nkind.N_TARRAY) { et = it.lhs; };
|
||||||
|
if (et != nil) { if (et.kind == nkind.N_TTUPLE) {
|
||||||
|
elems = et.list;
|
||||||
|
}; };
|
||||||
};
|
};
|
||||||
|
stamptuplebinds(c, n.list, elems, true, "binding");
|
||||||
} else {
|
} else {
|
||||||
let bnm: str = n.str;
|
let bnm: str = n.str;
|
||||||
if (bnm.len > 0) {
|
if (bnm.len > 0) {
|
||||||
@@ -422,19 +479,29 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
|||||||
pt = rty.list;
|
pt = rty.list;
|
||||||
}; };
|
}; };
|
||||||
}; };
|
}; };
|
||||||
|
stamptuplebinds(c, n.list, pt, true, "let");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// `a, _ = call();` — tuple multi-assign (a retained ww extension over
|
||||||
|
// Hare; harec has no statement-position unpack-assign). Targets are
|
||||||
|
// pre-declared lvalues, resolved by the per-target resolvewalk below
|
||||||
|
// before the distribution; stamptuplebinds(define=false) only stamps
|
||||||
|
// still-nil slots, which is exactly the discard `_` (no decl, so the
|
||||||
|
// N_IDENT exprtype path leaves it untyped). Distribution mirrors the
|
||||||
|
// N_MLET/N_FORRANGE binders; see stamptuplebinds.
|
||||||
|
if (k == nkind.N_MASSIGN) {
|
||||||
|
if (n.rhs != nil) { resolvewalk(c, n.rhs); };
|
||||||
|
let pt: *node = nil;
|
||||||
|
if (n.rhs != nil) { if (n.rhs.kind == nkind.N_CALL) {
|
||||||
|
let rty: *node = exprtype(c, n.rhs, nil);
|
||||||
|
if (rty != nil) { if (rty.kind == nkind.N_TTUPLE) {
|
||||||
|
pt = rty.list;
|
||||||
|
}; };
|
||||||
|
}; };
|
||||||
let l: *node = n.list;
|
let l: *node = n.list;
|
||||||
for (l != nil) {
|
for (l != nil) { resolvewalk(c, l); l = l.next; };
|
||||||
if (l.lhs == nil) {
|
stamptuplebinds(c, n.list, pt, false, "");
|
||||||
if (pt != nil) { l.lhs = pt.lhs; };
|
|
||||||
};
|
|
||||||
let bnm: str = l.str;
|
|
||||||
if (bnm.len > 0) {
|
|
||||||
checkmoduleshadow(c, bnm, "let");
|
|
||||||
scopedefine(c.cur, bnm, skind.SK_VAR, nil, l);
|
|
||||||
};
|
|
||||||
l = l.next;
|
|
||||||
if (pt != nil) { pt = pt.next; };
|
|
||||||
};
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10261,6 +10261,56 @@ fn installdecl(c: *checker, file: *node, d: *node) void = {
|
|||||||
if (k == nkind.N_LET) { scopedefineinmodule(c.top, nm, mod, skind.SK_VAR, nil, d); return; };
|
if (k == nkind.N_LET) { scopedefineinmodule(c.top, nm, mod, skind.SK_VAR, nil, d); return; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// stamptuplebinds — distribute a tuple's per-element types onto a
|
||||||
|
// destructure binding chain, walked in lockstep with the resolved
|
||||||
|
// N_TTUPLE element chain (each `elems` link carries its element type on
|
||||||
|
// .lhs). Mirror of harec's create_unpack_bindings
|
||||||
|
// (ref/harec/src/check.c:1354-1419), which harec shares between
|
||||||
|
// let-unpack (check_expr_binding) and the for-each loop header
|
||||||
|
// (ref/harec/src/check.c:2308-2317) — the one shape behind ww's
|
||||||
|
// `let (a,b) = f()`, `for (let (a,b) .. s)`, and the ww-extension
|
||||||
|
// multi-assign `a, _ = f()`.
|
||||||
|
//
|
||||||
|
// `define` (the binding contexts: let-unpack + for-range) installs each
|
||||||
|
// named binder as a fresh SK_VAR and back-fills its declared type onto
|
||||||
|
// .lhs so use sites resolve through the N_IDENT exprtype path. Multi-
|
||||||
|
// assign targets are pre-declared lvalues, so it passes false: .lhs is
|
||||||
|
// left untouched (an N_INDEX/N_DOT target carries a live operand there)
|
||||||
|
// and only the type_ stamp fires on the still-untyped slots.
|
||||||
|
//
|
||||||
|
// Each binder/target node's own type_ is stamped from its element type so
|
||||||
|
// the asserttyped gate sees a typed node. This covers the discard `_` (an
|
||||||
|
// empty-str N_IDENT with no decl to read a type back from): harec drops
|
||||||
|
// `_` yet still advances the tuple slot, so that slot's element type is
|
||||||
|
// the honest type to stamp — `_` is UNBOUND, not UNTYPED.
|
||||||
|
fn stamptuplebinds(c: *checker, binds: *node, elems: *node,
|
||||||
|
define: bool, what: str) void = {
|
||||||
|
let b: *node = binds;
|
||||||
|
let pt: *node = elems;
|
||||||
|
for (b != nil) {
|
||||||
|
let et: *node = nil;
|
||||||
|
if (pt != nil) { et = pt.lhs; };
|
||||||
|
if (define) {
|
||||||
|
if (b.lhs == nil) { b.lhs = et; };
|
||||||
|
let bnm: str = b.str;
|
||||||
|
if (bnm.len > 0) {
|
||||||
|
checkmoduleshadow(c, bnm, what);
|
||||||
|
scopedefine(c.cur, bnm, skind.SK_VAR, nil, b);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (b.type_ == nil) {
|
||||||
|
let src: *node = b.lhs;
|
||||||
|
if (src == nil) { src = et; };
|
||||||
|
if (src != nil) {
|
||||||
|
let ti: *tinfo = tinfofornode(c, src);
|
||||||
|
if (ti != nil) { b.type_ = ti: *void; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
b = b.next;
|
||||||
|
if (pt != nil) { pt = pt.next; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// resolvewalk — recursive AST walk that, for every nkind.N_IDENT and
|
// resolvewalk — recursive AST walk that, for every nkind.N_IDENT and
|
||||||
// nkind.N_TNAME seen, looks up the name and bumps the resolved/unresolved
|
// nkind.N_TNAME seen, looks up the name and bumps the resolved/unresolved
|
||||||
// counters. Local lets are installed in the current scope as soon as
|
// counters. Local lets are installed in the current scope as soon as
|
||||||
@@ -10354,15 +10404,22 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
|||||||
if (k == nkind.N_FORRANGE) {
|
if (k == nkind.N_FORRANGE) {
|
||||||
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
|
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
|
||||||
if (n.list != nil) {
|
if (n.list != nil) {
|
||||||
let m: *node = n.list;
|
// Tuple destructure `for (let (a,b) .. xs)`: peel the
|
||||||
for (m != nil) {
|
// iterable's element type and distribute its tuple
|
||||||
let bnm: str = m.str;
|
// element types onto the binders, the same lockstep walk
|
||||||
if (bnm.len > 0) {
|
// harec runs for the for-each header
|
||||||
checkmoduleshadow(c, bnm, "binding");
|
// (ref/harec/src/check.c:2308-2317 → create_unpack_bindings).
|
||||||
scopedefine(c.cur, bnm, skind.SK_VAR, nil, m);
|
let elems: *node = nil;
|
||||||
};
|
let it: *node = exprtype(c, n.lhs, nil);
|
||||||
m = m.next;
|
if (it != nil) {
|
||||||
|
let et: *node = nil;
|
||||||
|
if (it.kind == nkind.N_TSLICE) { et = it.lhs; };
|
||||||
|
if (it.kind == nkind.N_TARRAY) { et = it.lhs; };
|
||||||
|
if (et != nil) { if (et.kind == nkind.N_TTUPLE) {
|
||||||
|
elems = et.list;
|
||||||
|
}; };
|
||||||
};
|
};
|
||||||
|
stamptuplebinds(c, n.list, elems, true, "binding");
|
||||||
} else {
|
} else {
|
||||||
let bnm: str = n.str;
|
let bnm: str = n.str;
|
||||||
if (bnm.len > 0) {
|
if (bnm.len > 0) {
|
||||||
@@ -10452,19 +10509,29 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
|||||||
pt = rty.list;
|
pt = rty.list;
|
||||||
}; };
|
}; };
|
||||||
}; };
|
}; };
|
||||||
|
stamptuplebinds(c, n.list, pt, true, "let");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// `a, _ = call();` — tuple multi-assign (a retained ww extension over
|
||||||
|
// Hare; harec has no statement-position unpack-assign). Targets are
|
||||||
|
// pre-declared lvalues, resolved by the per-target resolvewalk below
|
||||||
|
// before the distribution; stamptuplebinds(define=false) only stamps
|
||||||
|
// still-nil slots, which is exactly the discard `_` (no decl, so the
|
||||||
|
// N_IDENT exprtype path leaves it untyped). Distribution mirrors the
|
||||||
|
// N_MLET/N_FORRANGE binders; see stamptuplebinds.
|
||||||
|
if (k == nkind.N_MASSIGN) {
|
||||||
|
if (n.rhs != nil) { resolvewalk(c, n.rhs); };
|
||||||
|
let pt: *node = nil;
|
||||||
|
if (n.rhs != nil) { if (n.rhs.kind == nkind.N_CALL) {
|
||||||
|
let rty: *node = exprtype(c, n.rhs, nil);
|
||||||
|
if (rty != nil) { if (rty.kind == nkind.N_TTUPLE) {
|
||||||
|
pt = rty.list;
|
||||||
|
}; };
|
||||||
|
}; };
|
||||||
let l: *node = n.list;
|
let l: *node = n.list;
|
||||||
for (l != nil) {
|
for (l != nil) { resolvewalk(c, l); l = l.next; };
|
||||||
if (l.lhs == nil) {
|
stamptuplebinds(c, n.list, pt, false, "");
|
||||||
if (pt != nil) { l.lhs = pt.lhs; };
|
|
||||||
};
|
|
||||||
let bnm: str = l.str;
|
|
||||||
if (bnm.len > 0) {
|
|
||||||
checkmoduleshadow(c, bnm, "let");
|
|
||||||
scopedefine(c.cur, bnm, skind.SK_VAR, nil, l);
|
|
||||||
};
|
|
||||||
l = l.next;
|
|
||||||
if (pt != nil) { pt = pt.next; };
|
|
||||||
};
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
* D module-leaf == type/fn name fnmatch 0 (closed)
|
* D module-leaf == type/fn name fnmatch 0 (closed)
|
||||||
* D module-leaf == type/fn name random 0 (closed)
|
* D module-leaf == type/fn name random 0 (closed)
|
||||||
* E computed enum-member value-expr enum_corpus 0 (closed)
|
* E computed enum-member value-expr enum_corpus 0 (closed)
|
||||||
|
* F for-range tuple-destructure bind forrange_tuple 0 (closed)
|
||||||
|
* G tuple multi-assign discard `_` massign_blank 0 (closed)
|
||||||
*
|
*
|
||||||
* Exit code of wwdump_ww is intentionally not gated: the diagnostics
|
* Exit code of wwdump_ww is intentionally not gated: the diagnostics
|
||||||
* land on stderr regardless of the run's success, and arming the bail
|
* land on stderr regardless of the run's success, and arming the bail
|
||||||
@@ -119,6 +121,10 @@ main(void)
|
|||||||
"D module-leaf == type/fn name", 0 },
|
"D module-leaf == type/fn name", 0 },
|
||||||
{ "test/wcc/901_enum_corpus.ww",
|
{ "test/wcc/901_enum_corpus.ww",
|
||||||
"E computed enum-member value-expr", 0 },
|
"E computed enum-member value-expr", 0 },
|
||||||
|
{ "test/wcc/901_forrange_tuple.ww",
|
||||||
|
"F for-range tuple-destructure bind", 0 },
|
||||||
|
{ "test/wcc/901_massign_blank.ww",
|
||||||
|
"G tuple multi-assign discard `_`", 0 },
|
||||||
{ NULL, NULL, 0 },
|
{ NULL, NULL, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -154,6 +160,6 @@ main(void)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
printf("asserttyped_gap: ww-stage checker warn set matches manifest "
|
printf("asserttyped_gap: ww-stage checker warn set matches manifest "
|
||||||
"on %d gap-corpus fixtures (C pinned, A+B+D+E closed)\n", n);
|
"on %d gap-corpus fixtures (C pinned, A+B+D+E+F+G closed)\n", n);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
24
test/wcc/901_forrange_tuple.ww
Normal file
24
test/wcc/901_forrange_tuple.ww
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// 901_forrange_tuple — for-range tuple-destructure binder gap fixture
|
||||||
|
// for the 901 asserttyped net (class F). `for (let (k, v) .. s)` over a
|
||||||
|
// `[](i64, i64)` left both binders — and every body use site of them,
|
||||||
|
// plus the N_BIN wrapping them — type_=nil: the N_FORRANGE handler
|
||||||
|
// installed the binders as SK_VAR but never back-filled their element
|
||||||
|
// type, so the N_IDENT exprtype path read a nil decl type. stamptuplebinds
|
||||||
|
// (the shared binder distributor, harec create_unpack_bindings) now peels
|
||||||
|
// the iterable element tuple and stamps each binder, so this reads 0.
|
||||||
|
package main;
|
||||||
|
|
||||||
|
export fn main() i32 = {
|
||||||
|
let buf: [2]i64;
|
||||||
|
buf[0] = 1i64;
|
||||||
|
buf[1] = 2i64;
|
||||||
|
let s: [](i64, i64);
|
||||||
|
s.ptr = buf.ptr: *(i64, i64);
|
||||||
|
s.len = 1;
|
||||||
|
s.cap = 1;
|
||||||
|
let total: i64 = 0i64;
|
||||||
|
for (let (k, v) .. s) {
|
||||||
|
total += k + v;
|
||||||
|
};
|
||||||
|
return total: i32;
|
||||||
|
};
|
||||||
25
test/wcc/901_massign_blank.ww
Normal file
25
test/wcc/901_massign_blank.ww
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// 901_massign_blank — tuple multi-assign discard `_` gap fixture for the
|
||||||
|
// 901 asserttyped net (class G). In `_, a = f()` the blank `_` parses to
|
||||||
|
// an empty-str N_IDENT with no decl, so the generic N_IDENT exprtype path
|
||||||
|
// left it type_=nil (945's own comment documents the gap). The `_` is a
|
||||||
|
// GAP, not an exemption — the slot has a real element type ([]u8 here), so
|
||||||
|
// stamptuplebinds(define=false) stamps it with that element type (harec
|
||||||
|
// drops `_` but still advances the tuple slot). a is pre-declared and
|
||||||
|
// resolves the normal way; this reads 0.
|
||||||
|
package main;
|
||||||
|
|
||||||
|
fn mk() ([]u8, i64) = {
|
||||||
|
let b: str = "hi";
|
||||||
|
let p: []u8;
|
||||||
|
p.ptr = b.ptr;
|
||||||
|
p.len = 2;
|
||||||
|
p.cap = 5;
|
||||||
|
return (p, 7i64);
|
||||||
|
};
|
||||||
|
|
||||||
|
export fn main() i32 = {
|
||||||
|
let a: i64 = 0i64;
|
||||||
|
_, a = mk();
|
||||||
|
if (a != 7) { return 1; };
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user