wcc: stamp N_IDENT-callee destructure bindings in checker (#121)

resolvewalk N_MLET arm distributes the N_IDENT-callee rhs return-tuple
element types onto unannotated bindings (the A-narrow slice). Byte-id-
neutral — cgen still classifies structurally, stamps inert until the
exprfloatkind collapse. N_DOT-callee destructure deferred to #16/#17.
Prereq for the #121 collapse (commits 2/3).
This commit is contained in:
2026-05-26 17:27:44 +09:00
parent 7d7ed964b0
commit 98e166504f
5 changed files with 201 additions and 5 deletions

View File

@@ -7532,6 +7532,56 @@ fn resolvewalk(c: *checker, n: *node) void = {
return;
};
// `let (a, b) = call();` / `let a, b = call();` — destructure
// bindings. #121 (Package B, A-narrow): distribute the callee's
// tuple return-type element types onto the un-annotated bindings so
// later references stamp n.type_, matching cgen's structural binding-
// type classifier (cgmlet's rettupleof→localadd path). This closes
// the unstamped-float-destructure gap that the exprfloatkind collapse
// (commit 2's bridge) needs: without it a `let (f,i)=mk()` f64 binding
// reads stamp nil → would disagree with the structural f64.
//
// GUARD bare N_IDENT callee only: exprtype's N_CALL N_DOT arm is the
// shelved #16/#17 cross-module-shadow gap (check.ww:2250-2253), so a
// module-qualified callee resolves to the wrong/nil return type —
// stamping off it would be worse than the nil it replaces. Bare same-
// module callees resolve correctly today. Faithful to harec
// create_unpack_bindings (ref/harec/src/check.c:1354-1416) and the
// cstage twin (cmd/wcc/check.c:1912 N_MLET, which uses cexpr ungated);
// narrowed to N_IDENT pending #16/#17. Annotated bindings keep their
// own type. Mirrors the N_FORRANGE binding-install shape above; the
// bindings would otherwise install (unstamped) via the generic N_LET
// walk, so this early return must register them itself.
if (k == nkind.N_MLET) {
if (n.rhs != nil) { resolvewalk(c, n.rhs); };
let pt: *node = nil;
if (n.rhs != nil) { if (n.rhs.kind == nkind.N_CALL) {
let callee: *node = n.rhs.lhs;
if (callee != nil) { if (callee.kind == nkind.N_IDENT) {
// `rt` would shadow the imported lib/rt module
// (checkmoduleshadow errors); `rty` avoids it.
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;
for (l != nil) {
if (l.lhs == nil) {
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;
};
if (k == nkind.N_DOT) {
// Walk only the base; the .field name is a member, not a
// free identifier.