w6c+wwstage: implicit [N]T->[]T array-to-slice coercion via desugar (#258)
Hare admits an array with a defined length wherever its element slice is
expected (assign / return / call-arg / init) as a borrow; ww rejected it
everywhere (the #108(c) exclusion), so base64 worked around the gap with
explicit a[0:n] slices.
type_assignable / isassignable now admit array->slice on an exact element
match (mirror ref/harec/src/types.c:1080-1097, the SLICE-dst arm). The four
acceptance sites route through one shared helper (desugar_arrayslice /
desugararrayslice) that rewrites the array expr to the explicit full slice
arr[0:len(arr)] — an N_SLICE over the array base. cgen is untouched: the
existing slice lowering (#252/#257/#135 made array bases, incl struct-field
arrays, correct) materialises the borrow header {.ptr=&arr[0], .len=N,
.cap=N}, byte-identically in both stages.
wwstage runs no general call-arg / N_ASSIGN typecheck, so checkassign +
desugarcallargs are added solely to route those two contexts through the
shared desugar (rule-10). desugarcallargs additionally loud-rejects an
element-MISMATCH array into a []T param, scoped to that shape so wwstage's
broader call-arg leniency is untouched.
953_arraytoslice_run covers the four contexts + a borrow-alias proof + the
i32/u8 element axis (dual-stage run + cs==ww byte-id), plus mismatch-reject
rows asserting both stages refuse [4]i32 -> []u8. Regen'd w6c + wwdump
combined.ww (#110).
This commit is contained in:
@@ -11020,6 +11020,16 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
||||
// are not value-typed nodes; their expression children get stamped on
|
||||
// the recursive descent into them. Type-expression kinds (N_T*) are
|
||||
// covered separately by the tinfofornode block above.
|
||||
// #258: desugar an array arg/rhs into an implicit full slice at the
|
||||
// call-arg and assignment contexts (let / return drive their own
|
||||
// desugar in checkletassign / checkretassign). Placed post-child-walk
|
||||
// so arg/operand types are stamped, and before the end-dispatch
|
||||
// exprtype below so a regular N_CALL is still an N_CALL (not folded to
|
||||
// an N_INTLIT by the size/align intercept). Mirrors cstage's post-
|
||||
// order cexpr desugar at the call-arg / N_ASSIGN sites.
|
||||
if (k == nkind.N_CALL) { desugarcallargs(c, n); };
|
||||
if (k == nkind.N_ASSIGN) { checkassign(c, n); };
|
||||
|
||||
if (k == nkind.N_INTLIT || k == nkind.N_FLOATLIT ||
|
||||
k == nkind.N_STRLIT || k == nkind.N_RUNELIT ||
|
||||
k == nkind.N_TRUE || k == nkind.N_FALSE ||
|
||||
@@ -13618,6 +13628,19 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
||||
if (du == nil) { *confident = false; return true; };
|
||||
if (su == nil) { *confident = false; return true; };
|
||||
if (typeeqast(du, su)) { return true; };
|
||||
// #258: implicit [N]T -> []T array-to-slice borrow. Hare admits an
|
||||
// array with a defined length wherever its element slice is expected
|
||||
// (ref/harec/src/types.c:1080-1097, the SLICE-dst arm). Element types
|
||||
// must match exactly — no element decay; a mismatch is a CONFIDENT
|
||||
// reject (mirror cstage type.c type_assignable's #258 arm + fallthrough
|
||||
// to 0). The acceptance sites then desugar the array expr to an
|
||||
// explicit full slice via desugararrayslice; cgen is untouched.
|
||||
if (du.kind == nkind.N_TSLICE) {
|
||||
if (su.kind == nkind.N_TARRAY) {
|
||||
if (typeeqast(du.lhs, su.lhs)) { return true; };
|
||||
return false;
|
||||
};
|
||||
};
|
||||
// untyped numeric → any numeric named type.
|
||||
if (isuntypedint(su)) {
|
||||
if (isnumerictname(du)) { return true; };
|
||||
@@ -14114,6 +14137,139 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// desugararrayslice — #258. The single shared lowering for the implicit
|
||||
// [N]T -> []T borrow. isassignable already admits an array with a defined
|
||||
// length into a matching []T slot (see isassignable's #258 arm); here we
|
||||
// rewrite the array expr to the explicit full slice `arr[0:len(arr)]` (an
|
||||
// N_SLICE over the array base), reusing the existing slice cgen — #252/
|
||||
// #257/#135 made array bases (incl struct-field arrays) correct. No new
|
||||
// array->slice store cgen; pushargsrev / cgslice / cglet already lower an
|
||||
// N_SLICE identically to cstage, so the borrow header is byte-id across
|
||||
// stages. Twin of cstage cmd/wcc/check.c desugar_arrayslice.
|
||||
//
|
||||
// Returns the (possibly new) node for the caller's tree slot: `val`
|
||||
// unchanged when the shape doesn't match, else a fresh N_SLICE whose base
|
||||
// is `val` (which keeps its stamped array type_). The original sibling
|
||||
// link transfers to the N_SLICE so a desugared call-arg keeps its place.
|
||||
fn desugararrayslice(c: *checker, dsttn: *node, srctn: *node, val: *node) *node = {
|
||||
if (dsttn == nil) { return val; };
|
||||
if (srctn == nil) { return val; };
|
||||
if (val == nil) { return val; };
|
||||
let du: *node = resolvealias(c, unwrapbang(dsttn));
|
||||
let su: *node = resolvealias(c, unwrapbang(srctn));
|
||||
if (du == nil) { return val; };
|
||||
if (su == nil) { return val; };
|
||||
if (du.kind != nkind.N_TSLICE) { return val; };
|
||||
if (su.kind != nkind.N_TARRAY) { return val; };
|
||||
if (!typeeqast(du.lhs, su.lhs)) { return val; };
|
||||
let sl: *node = newnode(nkind.N_SLICE, val.file, val.line, val.col);
|
||||
sl.lhs = val; // sliced base; lo (.rhs) / hi (.cond) nil → 0 : len(arr)
|
||||
let slt: *node = newnode(nkind.N_TSLICE, "", 0, 0);
|
||||
slt.lhs = su.lhs;
|
||||
sl.type_ = tinfofornode(c, slt): *void;
|
||||
sl.next = val.next;
|
||||
val.next = nil;
|
||||
return sl;
|
||||
};
|
||||
|
||||
// calleefndecl — resolve a call's callee to its fn-decl node so the
|
||||
// arg/param lockstep (desugarcallargs) can read declared param types.
|
||||
// Mirrors exprtype's N_CALL resolution (bare-leaf via scopelookupprefer,
|
||||
// module-qualified via the SK_USE receiver). nil for builtins / fn-value
|
||||
// callees — they have no declared param list to drive the #258 desugar,
|
||||
// and the selfhost corpus has no array→slice arg there anyway.
|
||||
fn calleefndecl(c: *checker, callee: *node) *node = {
|
||||
if (callee == nil) { return nil; };
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
let s: *sym = scopelookupprefer(c.cur, c.curmod, callee.str);
|
||||
if (s != nil) {
|
||||
if (s.skind == skind.SK_FN) { return s.decl; };
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
if (callee.kind == nkind.N_DOT) {
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == nkind.N_IDENT) {
|
||||
let ms: *sym = scopelookupprefer(c.cur, c.curmod, callee.lhs.str);
|
||||
if (ms != nil && ms.skind != skind.SK_USE) {
|
||||
let mu: *sym = scopelookupuselocal(ms.scope, callee.lhs.str);
|
||||
if (mu != nil) { ms = mu; };
|
||||
};
|
||||
if (ms != nil) {
|
||||
if (ms.skind == skind.SK_USE) {
|
||||
let fs: *sym = scopelookupinmodule(c.cur, callee.lhs.str, callee.str);
|
||||
if (fs != nil) {
|
||||
if (fs.skind == skind.SK_FN) { return fs.decl; };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// desugarcallargs — #258 at the call-arg context. Lockstep the call's
|
||||
// args against the callee's declared params and desugar an array arg
|
||||
// passed where a []T param is expected. Mirrors cstage cmd/wcc/check.c's
|
||||
// non-variadic call-arg arm. Variadic (`T...`) slots are skipped (the
|
||||
// arg flows into the gather as an element, not the slice itself).
|
||||
fn desugarcallargs(c: *checker, n: *node) void = {
|
||||
if (n == nil) { return; };
|
||||
let decl: *node = calleefndecl(c, n.lhs);
|
||||
if (decl == nil) { return; };
|
||||
let param: *node = decl.list;
|
||||
let prev: *node = nil;
|
||||
let a: *node = n.list;
|
||||
for (a != nil) {
|
||||
let nexta: *node = a.next;
|
||||
if (param != nil) {
|
||||
if (param.kind == nkind.N_PARAM) {
|
||||
if (param.op != tkind.TK_ELLIPSIS) {
|
||||
let atype: *node = exprtype(c, a, nil);
|
||||
// #258: an array arg into a []T param with a
|
||||
// MISMATCHED element is not a borrow — loud reject,
|
||||
// mirror cstage's call-arg type_assignable failure.
|
||||
// Gated to the array→slice-param shape so wwstage's
|
||||
// broader call-arg leniency (it runs no general
|
||||
// param typecheck) is untouched.
|
||||
let pu: *node = resolvealias(c, unwrapbang(param.lhs));
|
||||
let au: *node = resolvealias(c, unwrapbang(atype));
|
||||
if (pu != nil) { if (au != nil) {
|
||||
if (pu.kind == nkind.N_TSLICE) { if (au.kind == nkind.N_TARRAY) {
|
||||
if (!typeeqast(pu.lhs, au.lhs)) {
|
||||
errnotassign(c, param.lhs, atype, "argument");
|
||||
};
|
||||
}; };
|
||||
}; };
|
||||
let rep: *node = desugararrayslice(c, param.lhs, atype, a);
|
||||
if (rep != a) {
|
||||
if (prev == nil) { n.list = rep; } else { prev.next = rep; };
|
||||
a = rep;
|
||||
};
|
||||
};
|
||||
if (param.op != tkind.TK_ELLIPSIS) { param = param.next; };
|
||||
};
|
||||
};
|
||||
prev = a;
|
||||
a = nexta;
|
||||
};
|
||||
};
|
||||
|
||||
// checkassign — #258 at the assignment context. wwstage runs no other
|
||||
// N_ASSIGN typecheck (cstage's lives in cexpr); this exists solely to
|
||||
// route an array→slice rhs through the shared desugar so w6c_ww emits
|
||||
// the same borrow as w6c (rule-10). No error diagnostics — cstage gates
|
||||
// the shape.
|
||||
fn checkassign(c: *checker, n: *node) void = {
|
||||
if (n == nil) { return; };
|
||||
if (n.lhs == nil) { return; };
|
||||
if (n.rhs == nil) { return; };
|
||||
let ltn: *node = exprtype(c, n.lhs, nil);
|
||||
let rtn: *node = exprtype(c, n.rhs, nil);
|
||||
n.rhs = desugararrayslice(c, ltn, rtn, n.rhs);
|
||||
};
|
||||
|
||||
fn checkletassign(c: *checker, n: *node) void = {
|
||||
if (n == nil) { return; };
|
||||
if (n.rhs == nil) { return; }; // no init
|
||||
@@ -14205,6 +14361,8 @@ fn checkletassign(c: *checker, n: *node) void = {
|
||||
if (!ok) { if (assignableaddrfn(c, n.lhs, n.rhs)) { ok = true; }; };
|
||||
if (!conf) { return; };
|
||||
if (!ok) { errnotassign(c, n.lhs, src, "let"); };
|
||||
// #258: `let s: []T = arr` borrows the array as a full slice.
|
||||
n.rhs = desugararrayslice(c, n.lhs, src, n.rhs);
|
||||
};
|
||||
|
||||
fn checkretassign(c: *checker, n: *node) void = {
|
||||
@@ -14224,6 +14382,8 @@ fn checkretassign(c: *checker, n: *node) void = {
|
||||
if (!ok) { if (assignableaddrfn(c, c.fnret, n.lhs)) { ok = true; }; };
|
||||
if (!conf) { return; };
|
||||
if (!ok) { errnotassign(c, c.fnret, src, "return"); };
|
||||
// #258: `return arr` borrows the array as a full slice.
|
||||
n.lhs = desugararrayslice(c, c.fnret, src, n.lhs);
|
||||
};
|
||||
|
||||
// ---- is / as validity ------------------------------------------------
|
||||
|
||||
@@ -615,6 +615,16 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
||||
// are not value-typed nodes; their expression children get stamped on
|
||||
// the recursive descent into them. Type-expression kinds (N_T*) are
|
||||
// covered separately by the tinfofornode block above.
|
||||
// #258: desugar an array arg/rhs into an implicit full slice at the
|
||||
// call-arg and assignment contexts (let / return drive their own
|
||||
// desugar in checkletassign / checkretassign). Placed post-child-walk
|
||||
// so arg/operand types are stamped, and before the end-dispatch
|
||||
// exprtype below so a regular N_CALL is still an N_CALL (not folded to
|
||||
// an N_INTLIT by the size/align intercept). Mirrors cstage's post-
|
||||
// order cexpr desugar at the call-arg / N_ASSIGN sites.
|
||||
if (k == nkind.N_CALL) { desugarcallargs(c, n); };
|
||||
if (k == nkind.N_ASSIGN) { checkassign(c, n); };
|
||||
|
||||
if (k == nkind.N_INTLIT || k == nkind.N_FLOATLIT ||
|
||||
k == nkind.N_STRLIT || k == nkind.N_RUNELIT ||
|
||||
k == nkind.N_TRUE || k == nkind.N_FALSE ||
|
||||
@@ -3213,6 +3223,19 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
||||
if (du == nil) { *confident = false; return true; };
|
||||
if (su == nil) { *confident = false; return true; };
|
||||
if (typeeqast(du, su)) { return true; };
|
||||
// #258: implicit [N]T -> []T array-to-slice borrow. Hare admits an
|
||||
// array with a defined length wherever its element slice is expected
|
||||
// (ref/harec/src/types.c:1080-1097, the SLICE-dst arm). Element types
|
||||
// must match exactly — no element decay; a mismatch is a CONFIDENT
|
||||
// reject (mirror cstage type.c type_assignable's #258 arm + fallthrough
|
||||
// to 0). The acceptance sites then desugar the array expr to an
|
||||
// explicit full slice via desugararrayslice; cgen is untouched.
|
||||
if (du.kind == nkind.N_TSLICE) {
|
||||
if (su.kind == nkind.N_TARRAY) {
|
||||
if (typeeqast(du.lhs, su.lhs)) { return true; };
|
||||
return false;
|
||||
};
|
||||
};
|
||||
// untyped numeric → any numeric named type.
|
||||
if (isuntypedint(su)) {
|
||||
if (isnumerictname(du)) { return true; };
|
||||
@@ -3709,6 +3732,139 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// desugararrayslice — #258. The single shared lowering for the implicit
|
||||
// [N]T -> []T borrow. isassignable already admits an array with a defined
|
||||
// length into a matching []T slot (see isassignable's #258 arm); here we
|
||||
// rewrite the array expr to the explicit full slice `arr[0:len(arr)]` (an
|
||||
// N_SLICE over the array base), reusing the existing slice cgen — #252/
|
||||
// #257/#135 made array bases (incl struct-field arrays) correct. No new
|
||||
// array->slice store cgen; pushargsrev / cgslice / cglet already lower an
|
||||
// N_SLICE identically to cstage, so the borrow header is byte-id across
|
||||
// stages. Twin of cstage cmd/wcc/check.c desugar_arrayslice.
|
||||
//
|
||||
// Returns the (possibly new) node for the caller's tree slot: `val`
|
||||
// unchanged when the shape doesn't match, else a fresh N_SLICE whose base
|
||||
// is `val` (which keeps its stamped array type_). The original sibling
|
||||
// link transfers to the N_SLICE so a desugared call-arg keeps its place.
|
||||
fn desugararrayslice(c: *checker, dsttn: *node, srctn: *node, val: *node) *node = {
|
||||
if (dsttn == nil) { return val; };
|
||||
if (srctn == nil) { return val; };
|
||||
if (val == nil) { return val; };
|
||||
let du: *node = resolvealias(c, unwrapbang(dsttn));
|
||||
let su: *node = resolvealias(c, unwrapbang(srctn));
|
||||
if (du == nil) { return val; };
|
||||
if (su == nil) { return val; };
|
||||
if (du.kind != nkind.N_TSLICE) { return val; };
|
||||
if (su.kind != nkind.N_TARRAY) { return val; };
|
||||
if (!typeeqast(du.lhs, su.lhs)) { return val; };
|
||||
let sl: *node = newnode(nkind.N_SLICE, val.file, val.line, val.col);
|
||||
sl.lhs = val; // sliced base; lo (.rhs) / hi (.cond) nil → 0 : len(arr)
|
||||
let slt: *node = newnode(nkind.N_TSLICE, "", 0, 0);
|
||||
slt.lhs = su.lhs;
|
||||
sl.type_ = tinfofornode(c, slt): *void;
|
||||
sl.next = val.next;
|
||||
val.next = nil;
|
||||
return sl;
|
||||
};
|
||||
|
||||
// calleefndecl — resolve a call's callee to its fn-decl node so the
|
||||
// arg/param lockstep (desugarcallargs) can read declared param types.
|
||||
// Mirrors exprtype's N_CALL resolution (bare-leaf via scopelookupprefer,
|
||||
// module-qualified via the SK_USE receiver). nil for builtins / fn-value
|
||||
// callees — they have no declared param list to drive the #258 desugar,
|
||||
// and the selfhost corpus has no array→slice arg there anyway.
|
||||
fn calleefndecl(c: *checker, callee: *node) *node = {
|
||||
if (callee == nil) { return nil; };
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
let s: *sym = scopelookupprefer(c.cur, c.curmod, callee.str);
|
||||
if (s != nil) {
|
||||
if (s.skind == skind.SK_FN) { return s.decl; };
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
if (callee.kind == nkind.N_DOT) {
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == nkind.N_IDENT) {
|
||||
let ms: *sym = scopelookupprefer(c.cur, c.curmod, callee.lhs.str);
|
||||
if (ms != nil && ms.skind != skind.SK_USE) {
|
||||
let mu: *sym = scopelookupuselocal(ms.scope, callee.lhs.str);
|
||||
if (mu != nil) { ms = mu; };
|
||||
};
|
||||
if (ms != nil) {
|
||||
if (ms.skind == skind.SK_USE) {
|
||||
let fs: *sym = scopelookupinmodule(c.cur, callee.lhs.str, callee.str);
|
||||
if (fs != nil) {
|
||||
if (fs.skind == skind.SK_FN) { return fs.decl; };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// desugarcallargs — #258 at the call-arg context. Lockstep the call's
|
||||
// args against the callee's declared params and desugar an array arg
|
||||
// passed where a []T param is expected. Mirrors cstage cmd/wcc/check.c's
|
||||
// non-variadic call-arg arm. Variadic (`T...`) slots are skipped (the
|
||||
// arg flows into the gather as an element, not the slice itself).
|
||||
fn desugarcallargs(c: *checker, n: *node) void = {
|
||||
if (n == nil) { return; };
|
||||
let decl: *node = calleefndecl(c, n.lhs);
|
||||
if (decl == nil) { return; };
|
||||
let param: *node = decl.list;
|
||||
let prev: *node = nil;
|
||||
let a: *node = n.list;
|
||||
for (a != nil) {
|
||||
let nexta: *node = a.next;
|
||||
if (param != nil) {
|
||||
if (param.kind == nkind.N_PARAM) {
|
||||
if (param.op != tkind.TK_ELLIPSIS) {
|
||||
let atype: *node = exprtype(c, a, nil);
|
||||
// #258: an array arg into a []T param with a
|
||||
// MISMATCHED element is not a borrow — loud reject,
|
||||
// mirror cstage's call-arg type_assignable failure.
|
||||
// Gated to the array→slice-param shape so wwstage's
|
||||
// broader call-arg leniency (it runs no general
|
||||
// param typecheck) is untouched.
|
||||
let pu: *node = resolvealias(c, unwrapbang(param.lhs));
|
||||
let au: *node = resolvealias(c, unwrapbang(atype));
|
||||
if (pu != nil) { if (au != nil) {
|
||||
if (pu.kind == nkind.N_TSLICE) { if (au.kind == nkind.N_TARRAY) {
|
||||
if (!typeeqast(pu.lhs, au.lhs)) {
|
||||
errnotassign(c, param.lhs, atype, "argument");
|
||||
};
|
||||
}; };
|
||||
}; };
|
||||
let rep: *node = desugararrayslice(c, param.lhs, atype, a);
|
||||
if (rep != a) {
|
||||
if (prev == nil) { n.list = rep; } else { prev.next = rep; };
|
||||
a = rep;
|
||||
};
|
||||
};
|
||||
if (param.op != tkind.TK_ELLIPSIS) { param = param.next; };
|
||||
};
|
||||
};
|
||||
prev = a;
|
||||
a = nexta;
|
||||
};
|
||||
};
|
||||
|
||||
// checkassign — #258 at the assignment context. wwstage runs no other
|
||||
// N_ASSIGN typecheck (cstage's lives in cexpr); this exists solely to
|
||||
// route an array→slice rhs through the shared desugar so w6c_ww emits
|
||||
// the same borrow as w6c (rule-10). No error diagnostics — cstage gates
|
||||
// the shape.
|
||||
fn checkassign(c: *checker, n: *node) void = {
|
||||
if (n == nil) { return; };
|
||||
if (n.lhs == nil) { return; };
|
||||
if (n.rhs == nil) { return; };
|
||||
let ltn: *node = exprtype(c, n.lhs, nil);
|
||||
let rtn: *node = exprtype(c, n.rhs, nil);
|
||||
n.rhs = desugararrayslice(c, ltn, rtn, n.rhs);
|
||||
};
|
||||
|
||||
fn checkletassign(c: *checker, n: *node) void = {
|
||||
if (n == nil) { return; };
|
||||
if (n.rhs == nil) { return; }; // no init
|
||||
@@ -3800,6 +3956,8 @@ fn checkletassign(c: *checker, n: *node) void = {
|
||||
if (!ok) { if (assignableaddrfn(c, n.lhs, n.rhs)) { ok = true; }; };
|
||||
if (!conf) { return; };
|
||||
if (!ok) { errnotassign(c, n.lhs, src, "let"); };
|
||||
// #258: `let s: []T = arr` borrows the array as a full slice.
|
||||
n.rhs = desugararrayslice(c, n.lhs, src, n.rhs);
|
||||
};
|
||||
|
||||
fn checkretassign(c: *checker, n: *node) void = {
|
||||
@@ -3819,6 +3977,8 @@ fn checkretassign(c: *checker, n: *node) void = {
|
||||
if (!ok) { if (assignableaddrfn(c, c.fnret, n.lhs)) { ok = true; }; };
|
||||
if (!conf) { return; };
|
||||
if (!ok) { errnotassign(c, c.fnret, src, "return"); };
|
||||
// #258: `return arr` borrows the array as a full slice.
|
||||
n.lhs = desugararrayslice(c, c.fnret, src, n.lhs);
|
||||
};
|
||||
|
||||
// ---- is / as validity ------------------------------------------------
|
||||
|
||||
@@ -11020,6 +11020,16 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
||||
// are not value-typed nodes; their expression children get stamped on
|
||||
// the recursive descent into them. Type-expression kinds (N_T*) are
|
||||
// covered separately by the tinfofornode block above.
|
||||
// #258: desugar an array arg/rhs into an implicit full slice at the
|
||||
// call-arg and assignment contexts (let / return drive their own
|
||||
// desugar in checkletassign / checkretassign). Placed post-child-walk
|
||||
// so arg/operand types are stamped, and before the end-dispatch
|
||||
// exprtype below so a regular N_CALL is still an N_CALL (not folded to
|
||||
// an N_INTLIT by the size/align intercept). Mirrors cstage's post-
|
||||
// order cexpr desugar at the call-arg / N_ASSIGN sites.
|
||||
if (k == nkind.N_CALL) { desugarcallargs(c, n); };
|
||||
if (k == nkind.N_ASSIGN) { checkassign(c, n); };
|
||||
|
||||
if (k == nkind.N_INTLIT || k == nkind.N_FLOATLIT ||
|
||||
k == nkind.N_STRLIT || k == nkind.N_RUNELIT ||
|
||||
k == nkind.N_TRUE || k == nkind.N_FALSE ||
|
||||
@@ -13618,6 +13628,19 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
||||
if (du == nil) { *confident = false; return true; };
|
||||
if (su == nil) { *confident = false; return true; };
|
||||
if (typeeqast(du, su)) { return true; };
|
||||
// #258: implicit [N]T -> []T array-to-slice borrow. Hare admits an
|
||||
// array with a defined length wherever its element slice is expected
|
||||
// (ref/harec/src/types.c:1080-1097, the SLICE-dst arm). Element types
|
||||
// must match exactly — no element decay; a mismatch is a CONFIDENT
|
||||
// reject (mirror cstage type.c type_assignable's #258 arm + fallthrough
|
||||
// to 0). The acceptance sites then desugar the array expr to an
|
||||
// explicit full slice via desugararrayslice; cgen is untouched.
|
||||
if (du.kind == nkind.N_TSLICE) {
|
||||
if (su.kind == nkind.N_TARRAY) {
|
||||
if (typeeqast(du.lhs, su.lhs)) { return true; };
|
||||
return false;
|
||||
};
|
||||
};
|
||||
// untyped numeric → any numeric named type.
|
||||
if (isuntypedint(su)) {
|
||||
if (isnumerictname(du)) { return true; };
|
||||
@@ -14114,6 +14137,139 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// desugararrayslice — #258. The single shared lowering for the implicit
|
||||
// [N]T -> []T borrow. isassignable already admits an array with a defined
|
||||
// length into a matching []T slot (see isassignable's #258 arm); here we
|
||||
// rewrite the array expr to the explicit full slice `arr[0:len(arr)]` (an
|
||||
// N_SLICE over the array base), reusing the existing slice cgen — #252/
|
||||
// #257/#135 made array bases (incl struct-field arrays) correct. No new
|
||||
// array->slice store cgen; pushargsrev / cgslice / cglet already lower an
|
||||
// N_SLICE identically to cstage, so the borrow header is byte-id across
|
||||
// stages. Twin of cstage cmd/wcc/check.c desugar_arrayslice.
|
||||
//
|
||||
// Returns the (possibly new) node for the caller's tree slot: `val`
|
||||
// unchanged when the shape doesn't match, else a fresh N_SLICE whose base
|
||||
// is `val` (which keeps its stamped array type_). The original sibling
|
||||
// link transfers to the N_SLICE so a desugared call-arg keeps its place.
|
||||
fn desugararrayslice(c: *checker, dsttn: *node, srctn: *node, val: *node) *node = {
|
||||
if (dsttn == nil) { return val; };
|
||||
if (srctn == nil) { return val; };
|
||||
if (val == nil) { return val; };
|
||||
let du: *node = resolvealias(c, unwrapbang(dsttn));
|
||||
let su: *node = resolvealias(c, unwrapbang(srctn));
|
||||
if (du == nil) { return val; };
|
||||
if (su == nil) { return val; };
|
||||
if (du.kind != nkind.N_TSLICE) { return val; };
|
||||
if (su.kind != nkind.N_TARRAY) { return val; };
|
||||
if (!typeeqast(du.lhs, su.lhs)) { return val; };
|
||||
let sl: *node = newnode(nkind.N_SLICE, val.file, val.line, val.col);
|
||||
sl.lhs = val; // sliced base; lo (.rhs) / hi (.cond) nil → 0 : len(arr)
|
||||
let slt: *node = newnode(nkind.N_TSLICE, "", 0, 0);
|
||||
slt.lhs = su.lhs;
|
||||
sl.type_ = tinfofornode(c, slt): *void;
|
||||
sl.next = val.next;
|
||||
val.next = nil;
|
||||
return sl;
|
||||
};
|
||||
|
||||
// calleefndecl — resolve a call's callee to its fn-decl node so the
|
||||
// arg/param lockstep (desugarcallargs) can read declared param types.
|
||||
// Mirrors exprtype's N_CALL resolution (bare-leaf via scopelookupprefer,
|
||||
// module-qualified via the SK_USE receiver). nil for builtins / fn-value
|
||||
// callees — they have no declared param list to drive the #258 desugar,
|
||||
// and the selfhost corpus has no array→slice arg there anyway.
|
||||
fn calleefndecl(c: *checker, callee: *node) *node = {
|
||||
if (callee == nil) { return nil; };
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
let s: *sym = scopelookupprefer(c.cur, c.curmod, callee.str);
|
||||
if (s != nil) {
|
||||
if (s.skind == skind.SK_FN) { return s.decl; };
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
if (callee.kind == nkind.N_DOT) {
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == nkind.N_IDENT) {
|
||||
let ms: *sym = scopelookupprefer(c.cur, c.curmod, callee.lhs.str);
|
||||
if (ms != nil && ms.skind != skind.SK_USE) {
|
||||
let mu: *sym = scopelookupuselocal(ms.scope, callee.lhs.str);
|
||||
if (mu != nil) { ms = mu; };
|
||||
};
|
||||
if (ms != nil) {
|
||||
if (ms.skind == skind.SK_USE) {
|
||||
let fs: *sym = scopelookupinmodule(c.cur, callee.lhs.str, callee.str);
|
||||
if (fs != nil) {
|
||||
if (fs.skind == skind.SK_FN) { return fs.decl; };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// desugarcallargs — #258 at the call-arg context. Lockstep the call's
|
||||
// args against the callee's declared params and desugar an array arg
|
||||
// passed where a []T param is expected. Mirrors cstage cmd/wcc/check.c's
|
||||
// non-variadic call-arg arm. Variadic (`T...`) slots are skipped (the
|
||||
// arg flows into the gather as an element, not the slice itself).
|
||||
fn desugarcallargs(c: *checker, n: *node) void = {
|
||||
if (n == nil) { return; };
|
||||
let decl: *node = calleefndecl(c, n.lhs);
|
||||
if (decl == nil) { return; };
|
||||
let param: *node = decl.list;
|
||||
let prev: *node = nil;
|
||||
let a: *node = n.list;
|
||||
for (a != nil) {
|
||||
let nexta: *node = a.next;
|
||||
if (param != nil) {
|
||||
if (param.kind == nkind.N_PARAM) {
|
||||
if (param.op != tkind.TK_ELLIPSIS) {
|
||||
let atype: *node = exprtype(c, a, nil);
|
||||
// #258: an array arg into a []T param with a
|
||||
// MISMATCHED element is not a borrow — loud reject,
|
||||
// mirror cstage's call-arg type_assignable failure.
|
||||
// Gated to the array→slice-param shape so wwstage's
|
||||
// broader call-arg leniency (it runs no general
|
||||
// param typecheck) is untouched.
|
||||
let pu: *node = resolvealias(c, unwrapbang(param.lhs));
|
||||
let au: *node = resolvealias(c, unwrapbang(atype));
|
||||
if (pu != nil) { if (au != nil) {
|
||||
if (pu.kind == nkind.N_TSLICE) { if (au.kind == nkind.N_TARRAY) {
|
||||
if (!typeeqast(pu.lhs, au.lhs)) {
|
||||
errnotassign(c, param.lhs, atype, "argument");
|
||||
};
|
||||
}; };
|
||||
}; };
|
||||
let rep: *node = desugararrayslice(c, param.lhs, atype, a);
|
||||
if (rep != a) {
|
||||
if (prev == nil) { n.list = rep; } else { prev.next = rep; };
|
||||
a = rep;
|
||||
};
|
||||
};
|
||||
if (param.op != tkind.TK_ELLIPSIS) { param = param.next; };
|
||||
};
|
||||
};
|
||||
prev = a;
|
||||
a = nexta;
|
||||
};
|
||||
};
|
||||
|
||||
// checkassign — #258 at the assignment context. wwstage runs no other
|
||||
// N_ASSIGN typecheck (cstage's lives in cexpr); this exists solely to
|
||||
// route an array→slice rhs through the shared desugar so w6c_ww emits
|
||||
// the same borrow as w6c (rule-10). No error diagnostics — cstage gates
|
||||
// the shape.
|
||||
fn checkassign(c: *checker, n: *node) void = {
|
||||
if (n == nil) { return; };
|
||||
if (n.lhs == nil) { return; };
|
||||
if (n.rhs == nil) { return; };
|
||||
let ltn: *node = exprtype(c, n.lhs, nil);
|
||||
let rtn: *node = exprtype(c, n.rhs, nil);
|
||||
n.rhs = desugararrayslice(c, ltn, rtn, n.rhs);
|
||||
};
|
||||
|
||||
fn checkletassign(c: *checker, n: *node) void = {
|
||||
if (n == nil) { return; };
|
||||
if (n.rhs == nil) { return; }; // no init
|
||||
@@ -14205,6 +14361,8 @@ fn checkletassign(c: *checker, n: *node) void = {
|
||||
if (!ok) { if (assignableaddrfn(c, n.lhs, n.rhs)) { ok = true; }; };
|
||||
if (!conf) { return; };
|
||||
if (!ok) { errnotassign(c, n.lhs, src, "let"); };
|
||||
// #258: `let s: []T = arr` borrows the array as a full slice.
|
||||
n.rhs = desugararrayslice(c, n.lhs, src, n.rhs);
|
||||
};
|
||||
|
||||
fn checkretassign(c: *checker, n: *node) void = {
|
||||
@@ -14224,6 +14382,8 @@ fn checkretassign(c: *checker, n: *node) void = {
|
||||
if (!ok) { if (assignableaddrfn(c, c.fnret, n.lhs)) { ok = true; }; };
|
||||
if (!conf) { return; };
|
||||
if (!ok) { errnotassign(c, c.fnret, src, "return"); };
|
||||
// #258: `return arr` borrows the array as a full slice.
|
||||
n.lhs = desugararrayslice(c, c.fnret, src, n.lhs);
|
||||
};
|
||||
|
||||
// ---- is / as validity ------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user