diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index 780907fe..e2105327 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -10112,6 +10112,14 @@ cgexpr(Cg *c, Node *n, Local *locals) } else { ins2(c, A_MOVQ, amem(D_BP, boff + 8), areg(D_AX)); } + } else if (base->kind == N_DOT && bu && + (bu->kind == TY_SLICE || bu->kind == TY_STR)) { + /* slice/str FIELD base — the arg twin + * of the cgexpr N_SLICE default-hi + * arm above; same re-eval-for-header + * rationale. */ + cgexpr(c, base, locals); + ins2(c, A_MOVQ, areg(D_BX), areg(D_AX)); } else { cgexpr_int(c, 0); } @@ -13242,6 +13250,18 @@ cgexpr(Cg *c, Node *n, Local *locals) ins2(c, A_MOVQ, amem(D_BP, boff + 8), areg(D_AX)); } + } else if (base && base->kind == N_DOT && bu && + (bu->kind == TY_SLICE || bu->kind == TY_STR)) { + /* slice/str FIELD base (#252's slice twin): the old + * fall-through emitted $0 — a silent 0/negative len on + * BOTH stages, byteid-blind. Re-evaluate the field + * read for its header (a pure place: call inners + * loud-reject upstream; cgexpr's N_DOT slice/str arms + * leave AX=ptr, BX=len, CX=cap for every supported + * inner — ident, chain, (*p), arr[i]) and take .len. + * All scratch regs are dead at the hi step. */ + cgexpr(c, base, locals); + ins2(c, A_MOVQ, areg(D_BX), areg(D_AX)); } else { cgexpr_int(c, 0); } diff --git a/internal/wwfixture/types.ww b/internal/wwfixture/types.ww index 6f663be6..df66604a 100644 --- a/internal/wwfixture/types.ww +++ b/internal/wwfixture/types.ww @@ -1,13 +1,13 @@ package wwfixture; def protocolversion: i32 = 1; -def corpuscount: i32 = 1230; +def corpuscount: i32 = 1233; def errorcount: i32 = 314; def compilecount: i32 = 12; -def runcount: i32 = 138; +def runcount: i32 = 141; def runexitcount: i32 = 766; -def nativecount: i32 = 2460; -def corpushash: str = "fd04059b05a19ae63f1bec3edaefedf1d8ce9a505302a153434457d0fb9b9eca"; +def nativecount: i32 = 2466; +def corpushash: str = "65d3642dfb77b486f5aed23044a979bbce37c36fbbcb7a7aedae20d87c7b0e4f"; type directive = enum i32 { ERROR = 0, diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index 0ae5fe2f..20d70994 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -3000,6 +3000,17 @@ fn cgslice(c: *cgen, n: *syntax.node) void = { emitline("\tMOVQ\t$"); emitint(dotbu.alen: i64); emitline(", AX\n"); + } else { if (dotbu != nil && (dotbu.kind == syntax.tykind.TY_SLICE + || dotbu.kind == syntax.tykind.TY_STR)) { + // slice/str FIELD base (#252's slice twin): the old + // fall-through emitted $0 — silent 0/negative len on BOTH + // stages, byteid-blind. Re-evaluate the field read for its + // header (pure place: call inners loud-reject upstream; + // cgdot's slice/str arms leave AX=ptr, BX=len, CX=cap for + // every supported inner) and take .len. Scratch regs are + // dead at the hi step. Cstage twin: cgen.c N_SLICE. + cgexpr(c, base); + emitline("\tMOVQ\tBX, AX\n"); } else { if (arrlittn != nil) { // #31: default-hi for the arrlit base = its element count (the // stashed [count]T tnode's .rhs intlit). @@ -3014,7 +3025,7 @@ fn cgslice(c: *cgen, n: *syntax.node) void = { emitline(", AX\n"); } else { emitline("\tMOVQ\t$0, AX\n"); - };};};};}; + };};};};};}; emitline("\tMOVQ\tAX, BX\n"); emitline("\tPOPQ\tCX\n"); emitline("\tPOPQ\tAX\n"); diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index 95b97444..62cb8f83 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -782,9 +782,16 @@ fn pushargsrev(c: *cgen, arg: *syntax.node, param: *syntax.node, memphase: bool, emitline("\tMOVQ\t$"); emitint(dotbu.alen: i64); emitline(", AX\n"); + } else { if (dotbu != nil && (dotbu.kind == syntax.tykind.TY_SLICE + || dotbu.kind == syntax.tykind.TY_STR)) { + // slice/str FIELD base — the arg twin of the cgslice + // default-hi arm; same re-eval-for-header rationale. + // Cstage twin: cgen.c pushargs N_SLICE. + cgexpr(c, base); + emitline("\tMOVQ\tBX, AX\n"); } else { emitline("\tMOVQ\t$0, AX\n"); - };};};}; + };};};};}; emitline("\tPUSHQ\tAX\n"); // lo (default 0) → AX if (lo != nil) { cgexpr(c, lo); } diff --git a/test/wcc/data/dotfield_slice_defhi/case.ww b/test/wcc/data/dotfield_slice_defhi/case.ww new file mode 100644 index 00000000..913e2d1b --- /dev/null +++ b/test/wcc/data/dotfield_slice_defhi/case.ww @@ -0,0 +1,49 @@ +//ww:run +// Default-hi slice of a slice/str FIELD (`x.f[:]`, `x.f[2:]`) — +// expression position. The pre-fix default-hi dispatch was +// N_IDENT-gated on BOTH stages, so an N_DOT base fell to MOVQ $0 +// (len 0 / negative, byteid-blind). Covers local, viaptr, dot-chain, +// (*p) spelling, and struct-array-element inners. +package main; + +type inner = struct { + buf: []i32, + name: str, +}; + +type outer = struct { + pad: i64, + in: inner, +}; + +export fn main() int = { + let arr: [4]i32 = [10, 20, 30, 40]; + let v: inner = inner{ buf = arr[:], name = "hello" }; + let b1: []i32 = v.buf[:]; + if (len(b1) != 4) { return 1; }; + let b2: []i32 = v.buf[1:]; + if (len(b2) != 3) { return 2; }; + if (b2[0] != 20) { return 3; }; + let s1: str = v.name[2:]; + if (len(s1) != 3) { return 4; }; + let p: *inner = &v; + let b3: []i32 = p.buf[:]; + if (len(b3) != 4) { return 5; }; + let s2: str = p.name[1:]; + if (len(s2) != 4) { return 6; }; + let o: outer = outer{ pad = 7, in = inner{ buf = arr[:], name = "world" } }; + let b4: []i32 = o.in.buf[:]; + if (len(b4) != 4) { return 7; }; + let b5: []i32 = o.in.buf[2:]; + if (len(b5) != 2) { return 8; }; + if (b5[1] != 40) { return 9; }; + let s3: str = o.in.name[3:]; + if (len(s3) != 2) { return 10; }; + let b6: []i32 = (*p).buf[:]; + if (len(b6) != 4) { return 11; }; + let xs: [2]inner = [v, v]; + let b7: []i32 = xs[1].buf[:]; + if (len(b7) != 4) { return 12; }; + if (b7[3] != 40) { return 13; }; + return 0; +}; diff --git a/test/wcc/data/dotfield_slice_defhi_arg/case.ww b/test/wcc/data/dotfield_slice_defhi_arg/case.ww new file mode 100644 index 00000000..78e8152a --- /dev/null +++ b/test/wcc/data/dotfield_slice_defhi_arg/case.ww @@ -0,0 +1,46 @@ +//ww:run +// Default-hi slice of a slice/str FIELD in call-ARG position — the +// pushargs twin of dotfield_slice_defhi. Pre-fix the pushargs +// N_SLICE default-hi arm was N_IDENT-gated on BOTH stages, so +// callees received a 0-length slice. +package main; + +type inner = struct { + buf: []i32, + name: str, +}; + +type outer = struct { + pad: i64, + in: inner, +}; + +fn sum(xs: []i32) i32 = { + let t: i32 = 0; + let i: i32 = 0; + for (i < len(xs)) { + t += xs[i]; + i += 1; + }; + return t; +}; + +fn slen(s: str) i32 = { + return len(s); +}; + +export fn main() int = { + let arr: [4]i32 = [10, 20, 30, 40]; + let v: inner = inner{ buf = arr[:], name = "hello" }; + if (sum(v.buf[:]) != 100) { return 1; }; + if (sum(v.buf[2:]) != 70) { return 2; }; + if (slen(v.name[1:]) != 4) { return 3; }; + let p: *inner = &v; + if (sum(p.buf[:]) != 100) { return 4; }; + if (slen(p.name[2:]) != 3) { return 5; }; + let o: outer = outer{ pad = 7, in = inner{ buf = arr[:], name = "world" } }; + if (sum(o.in.buf[:]) != 100) { return 6; }; + if (sum(o.in.buf[1:]) != 90) { return 7; }; + if (slen(o.in.name[4:]) != 1) { return 8; }; + return 0; +}; diff --git a/test/wcc/data/dotfield_slice_defhi_global/case.ww b/test/wcc/data/dotfield_slice_defhi_global/case.ww new file mode 100644 index 00000000..ca847cea --- /dev/null +++ b/test/wcc/data/dotfield_slice_defhi_global/case.ww @@ -0,0 +1,51 @@ +//ww:run +// Default-hi slice of a slice/str FIELD on module-GLOBAL struct +// bases (single dot and chain), expression + arg positions — the +// global leg of dotfield_slice_defhi. +package main; + +type inner = struct { + buf: []i32, + name: str, +}; + +type outer = struct { + pad: i64, + in: inner, +}; + +let g: inner = inner{}; +let go: outer = outer{}; + +fn sum(xs: []i32) i32 = { + let t: i32 = 0; + let i: i32 = 0; + for (i < len(xs)) { + t += xs[i]; + i += 1; + }; + return t; +}; + +export fn main() int = { + let arr: [4]i32 = [10, 20, 30, 40]; + g.buf = arr[:]; + g.name = "globals"; + let b1: []i32 = g.buf[:]; + if (len(b1) != 4) { return 1; }; + let b2: []i32 = g.buf[2:]; + if (len(b2) != 2) { return 2; }; + if (b2[0] != 30) { return 3; }; + let s1: str = g.name[3:]; + if (len(s1) != 4) { return 4; }; + if (sum(g.buf[:]) != 100) { return 5; }; + if (sum(g.buf[1:]) != 90) { return 6; }; + go.in.buf = arr[:]; + go.in.name = "chain"; + let b3: []i32 = go.in.buf[:]; + if (len(b3) != 4) { return 7; }; + if (sum(go.in.buf[2:]) != 70) { return 8; }; + let s2: str = go.in.name[1:]; + if (len(s2) != 4) { return 9; }; + return 0; +};