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:
@@ -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.
|
||||
|
||||
@@ -381,6 +381,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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -55,7 +55,7 @@ runwait(const char *cmd)
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row { const char *label; const char *src; int want_exit; };
|
||||
struct row { const char *label; const char *src; int want_exit; int chk_stamped; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* mixed (f64, i64): f=2.5 -> i32 2, i=7 -> i32 7, 2+7 = 9. The
|
||||
@@ -105,7 +105,7 @@ static const struct row rows[] = {
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet (f, i) = mk();\n"
|
||||
"\treturn (f: i32) + (i: i32);\n"
|
||||
"};\n", 9 },
|
||||
"};\n", 9, 1 },
|
||||
/* CONTROL — wide/str element single-var (i64, str) is a 32B tuple
|
||||
* handled by the separate 32B receive branch; the fix's sz==16 gate
|
||||
* excludes it, so it stays byte-id pre- and post-fix. t.0 = 7. */
|
||||
@@ -241,6 +241,29 @@ main(void)
|
||||
"byte-id violation)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
|
||||
/* (c) #121 A-narrow stamp gate: the un-annotated float-
|
||||
* destructure binding must now carry a checker type stamp, so
|
||||
* w6c_ww emits no `asserttyped:` diagnostic. Non-vacuous —
|
||||
* pre-stamp (HEAD) w6c_ww fires asserttyped on the f64 binding
|
||||
* ident (nil n.type_). */
|
||||
if (rows[i].chk_stamped) {
|
||||
char errf[80];
|
||||
snprintf(errf, sizeof errf,
|
||||
"/tmp/wwtup_%d_%d_err.txt", getpid(), i);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"%s -o /dev/null %s 2>%s", w6c_ww, src, errf);
|
||||
runwait(cmd);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"grep -q asserttyped %s", errf);
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww emitted "
|
||||
"asserttyped (destructure binding "
|
||||
"unstamped)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
unlink(errf);
|
||||
}
|
||||
unlink(src); unlink(cs_s); unlink(ws_s);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ runwait(const char *cmd)
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row { const char *label; const char *src; int want_exit; };
|
||||
struct row { const char *label; const char *src; int want_exit; int chk_stamped; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* BUG — minimal repro. norm is BRANCHED (inner issub() CALL clobbers
|
||||
@@ -133,7 +133,7 @@ static const struct row rows[] = {
|
||||
"\tif (m != 16.0) { return 1; };\n"
|
||||
"\tif (i != 0) { return 2; };\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n", 0 },
|
||||
"};\n", 0, 1 },
|
||||
/* BUG — DESTRUCTURE order-swap `let (i,m)=norm()`, (i64,f64). f64
|
||||
* binding m is element 1 (cursor DX); pre-fix MOVQ DX,slot garbage,
|
||||
* post-fix MOVSD X0,slot. i=0, m=16.0. */
|
||||
@@ -149,7 +149,7 @@ static const struct row rows[] = {
|
||||
"\tif (m != 16.0) { return 1; };\n"
|
||||
"\tif (i != 0) { return 2; };\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n", 0 },
|
||||
"};\n", 0, 1 },
|
||||
/* BUG — REASSIGN form `m,i = norm()` (N_MASSIGN / cgmassign+tupstore)
|
||||
* into pre-declared slots. Same f64-element-from-X0 defect. m=16.0. */
|
||||
{ "massign_f64_i64_br",
|
||||
@@ -347,6 +347,29 @@ main(void)
|
||||
"byte-id violation)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
|
||||
/* (c) #121 A-narrow stamp gate: the un-annotated float-
|
||||
* destructure binding must now carry a checker type stamp, so
|
||||
* w6c_ww emits no `asserttyped:` diagnostic. Non-vacuous —
|
||||
* pre-stamp (HEAD) w6c_ww fires asserttyped on the f64 binding
|
||||
* ident (nil n.type_). */
|
||||
if (rows[i].chk_stamped) {
|
||||
char errf[80];
|
||||
snprintf(errf, sizeof errf,
|
||||
"/tmp/wwtupf_%d_%d_err.txt", getpid(), i);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"%s -o /dev/null %s 2>%s", w6c_ww, src, errf);
|
||||
runwait(cmd);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"grep -q asserttyped %s", errf);
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww emitted "
|
||||
"asserttyped (destructure binding "
|
||||
"unstamped)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
unlink(errf);
|
||||
}
|
||||
unlink(src); unlink(cs_s); unlink(ws_s);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user