wcc: resolve value-receiver field-call type via callee, not global leaf (#208)
wwstage exprtype's N_CALL arm fell into a global-leaf scopelookup for an N_DOT callee with a value or chained receiver, binding whatever same-named global headed the scope bucket. Under a late-os combined.ww concat order this resolved io's `s.read(...)` to os.read (i64) instead of the field's fn type, so checkretassign confidently rejected a valid tagged return — an import-order-sensitive false positive. cstage resolves a call result solely from the callee expr's own type (check.c:1378-1433, mirroring harec check_autodereference 1566-1581); drop the global-leaf else-arm so value and chained receivers fall through to the existing fn-VALUE path at check.ww:2455. SK_USE module-qualified calls are unchanged. ww has no methods, so `value.leaf()` is only ever a fn-ptr field access; the global hit was never legitimate. Zero .s delta across all 5 bootstrap tools (the branch is dead in the bootstrap); test 776 graduates to both stages (os-late order, byte-identical). The fix unmasks a pre-existing wwstage cgen bug (#211): cgen also re-derives a call's return shape by name (fnretlookup), so a value-receiver field call whose leaf collides with a same-named global of a different register shape mis-resolves cs!=ww. Documented at the cgen site; pinned cstage-only by test/wcc/782 (graduates to STAGE_WW on #211 close).
This commit is contained in:
@@ -46,33 +46,20 @@
|
||||
* | (mirror of 775's branched_readers
|
||||
* | row for the vtable-init side).
|
||||
*
|
||||
* IMPORT-ORDER WORKAROUND (pre-existing wwstage gap, NOT introduced
|
||||
* here): every row places `import os;` FIRST. ww_ww's combined.ww
|
||||
* concatenation follows the import-discovery order; an os-late
|
||||
* ordering (io → rt → os) trips the wwstage checker on os.tryread /
|
||||
* trywrite / tryopen's bare `return r;` over a tagged return type
|
||||
* (3 false-positive "return: not assignable (i64 → )"/"(i32 → )"
|
||||
* errors). The os-first ordering matches selfhost's combined.ww
|
||||
* (time → os → rt → …) where the checker resolves cleanly. The 980
|
||||
* memio_run / 990 / 995 byte-id gates do not hit this path because
|
||||
* they either drive cs-only (980) or operate on a much wider type
|
||||
* surface (990/995). Filed as a sibling task; bare `os` import in a
|
||||
* small probe context is what surfaces it. Workaround drops out once
|
||||
* the wwstage checker stops ordering-sensitively on bare-int return
|
||||
* to tagged-int union.
|
||||
* IMPORT ORDER (#208 CLOSED): every row imports `memio; io; os;` —
|
||||
* os LAST. This is the formerly-failing os-late ordering. Pre-#208 it
|
||||
* tripped the wwstage checker on io.read/write/close's `return s.read(
|
||||
* s, buf)` (a fn-pointer field call) with 3 false "return: not
|
||||
* assignable (i64 → )"/"(i32 → )" — because exprtype re-bound the field
|
||||
* leaf to a same-named scalar global (os.read:i64) instead of the
|
||||
* field's tagged fn type. #208 fixed exprtype to resolve value-receiver
|
||||
* field calls from the field type (cmd/wcc/check.c:1378-1433 twin), so
|
||||
* the order no longer matters and all 4 rows now run STAGE_CS|STAGE_WW
|
||||
* byte-id. The earlier `import os;`-FIRST workaround is retired here.
|
||||
*
|
||||
* SIBLINGS (filed inline, NOT fixed here — fold-e2 is purely
|
||||
* additive over fold-e1's frozen io.* surface):
|
||||
*
|
||||
* - WWSTAGE-IMPORT-ORDER: `import os;` must appear before `import
|
||||
* memio;` / `import io;` in a small probe context. ww_ww's
|
||||
* combined.ww concat order trips the wwstage checker's
|
||||
* `(i64 | T)` / `(i32 | T)` return-assignability when bare
|
||||
* int-returning fns (os.tryread / trywrite / tryopen) are
|
||||
* checked before certain ordering-sensitive pre-resolved types.
|
||||
* cstage accepts unconditionally. Symmetric to the wwstage
|
||||
* ordering gaps already filed as #189 / #190 / #202.
|
||||
*
|
||||
* - #173 (TRY-on-tagged-return both-stages broken) blocks
|
||||
* fixed_write_v from returning Hare's `nomem` when full;
|
||||
* vstream.ww surfaces 0 instead (memio.ww:155 OLD divergence).
|
||||
@@ -120,9 +107,9 @@ struct row {
|
||||
static const struct row rows[] = {
|
||||
{ "fixed_read_5",
|
||||
"package main;\n"
|
||||
"import os;\n"
|
||||
"import memio;\n"
|
||||
"import io;\n"
|
||||
"import os;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [8]u8;\n"
|
||||
" buf[0] = 65u8; buf[1] = 66u8; buf[2] = 67u8; buf[3] = 68u8;\n"
|
||||
@@ -144,9 +131,9 @@ static const struct row rows[] = {
|
||||
STAGE_CS | STAGE_WW, 1 },
|
||||
{ "dynamic_write_grow",
|
||||
"package main;\n"
|
||||
"import os;\n"
|
||||
"import memio;\n"
|
||||
"import io;\n"
|
||||
"import os;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let r = memio.dynamic_vstream();\n"
|
||||
" let s: io.vstream = nil: *io.vtable;\n"
|
||||
@@ -167,9 +154,9 @@ static const struct row rows[] = {
|
||||
STAGE_CS | STAGE_WW, 1 },
|
||||
{ "dynamicfrom_alt_rw",
|
||||
"package main;\n"
|
||||
"import os;\n"
|
||||
"import memio;\n"
|
||||
"import io;\n"
|
||||
"import os;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let seed: [4]u8;\n"
|
||||
" seed[0] = 1u8; seed[1] = 2u8; seed[2] = 3u8; seed[3] = 4u8;\n"
|
||||
@@ -195,9 +182,9 @@ static const struct row rows[] = {
|
||||
STAGE_CS | STAGE_WW, 1 },
|
||||
{ "branched_fixed",
|
||||
"package main;\n"
|
||||
"import os;\n"
|
||||
"import memio;\n"
|
||||
"import io;\n"
|
||||
"import os;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [3]u8;\n"
|
||||
" a[0] = 10u8; a[1] = 11u8; a[2] = 12u8;\n"
|
||||
|
||||
Reference in New Issue
Block a user