selfhost/cmd/wcc: stamp e.type_ for N_BIN + N_UN (A.6.1.3)
Adds unifyarith / binoptype / unoptype mirroring cstage cmd/wcc/check.c:580-596, 598-640, 642-687. Two new exprtype arms stamp e.type_ for N_BIN and N_UN via the same tinfofornode pattern as A.6.1.1/.2. unifyarith: untyped+untyped (prefer float), untyped+typed (typed via isassignable), typed+typed (typeeqast), fallback ltn. binoptype: ptr arith (ptr±int → ptr, ptr-ptr → i64), bitwise/ arith → unifyarith, comparison + logical → bool. unoptype: deref (resolvealias+unwrapbang then N_TPTR.lhs), addrof → *T with slice/str pseudo-field .len/.cap widening to *i64 (mirrors check.c:672-682), unary +/-/^/!. Wwstage stays silent on operator-type errors per existing checker discipline; cstage flags the same shapes. Verified 132/132 incl. 995_self_rebuild byte-identity.
This commit is contained in:
@@ -8160,9 +8160,120 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
|
||||
return r;
|
||||
};
|
||||
|
||||
// unifyarith — usual-arithmetic-conversion analogue at the AST-tnode
|
||||
// layer. Mirrors cstage cmd/wcc/check.c:580-596 `unify_arith`. The
|
||||
// trailing typeeq fallback covers same-typed operands; mismatched typed
|
||||
// pairs silently return ltn (cstage flags the same shape — wwstage's
|
||||
// checker stays silent here per existing discipline).
|
||||
fn unifyarith(c: *checker, ltn: *node, rtn: *node) *node = {
|
||||
let lu: bool = isuntypedint(ltn) || isuntypedfloat(ltn);
|
||||
let ru: bool = isuntypedint(rtn) || isuntypedfloat(rtn);
|
||||
if (lu && ru) {
|
||||
if (isuntypedfloat(ltn) || isuntypedfloat(rtn)) {
|
||||
return mktname(c, "untyped_float");
|
||||
};
|
||||
return mktname(c, "untyped_int");
|
||||
};
|
||||
let conf: bool = false;
|
||||
if (lu) {
|
||||
if (isassignable(c, rtn, ltn, &conf)) { return rtn; };
|
||||
};
|
||||
if (ru) {
|
||||
if (isassignable(c, ltn, rtn, &conf)) { return ltn; };
|
||||
};
|
||||
if (typeeqast(ltn, rtn)) { return ltn; };
|
||||
return ltn;
|
||||
};
|
||||
|
||||
// binoptype — derive the result tnode of an N_BIN operator expression.
|
||||
// Mirrors cstage cmd/wcc/check.c:598-640 `cbinop`. Operates on tnodes
|
||||
// returned by exprtype; ptr arithmetic / bitwise / shifts / comparisons /
|
||||
// logicals all reflect cstage's rules. Type-mismatch diagnostics are
|
||||
// elided here (cstage gates the same shape).
|
||||
fn binoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let ltn: *node = exprtype(c, e.lhs, nil);
|
||||
let rtn: *node = exprtype(c, e.rhs, nil);
|
||||
if (op == tkind.TK_PLUS || op == tkind.TK_MINUS) {
|
||||
if (ltn != nil && ltn.kind == nkind.N_TPTR && isinttypeast(rtn)) {
|
||||
return ltn;
|
||||
};
|
||||
};
|
||||
if (op == tkind.TK_PLUS) {
|
||||
if (isinttypeast(ltn) && rtn != nil && rtn.kind == nkind.N_TPTR) {
|
||||
return rtn;
|
||||
};
|
||||
};
|
||||
if (op == tkind.TK_MINUS) {
|
||||
if (ltn != nil && rtn != nil
|
||||
&& ltn.kind == nkind.N_TPTR && rtn.kind == nkind.N_TPTR) {
|
||||
return mktname(c, "i64");
|
||||
};
|
||||
};
|
||||
if (op == tkind.TK_PLUS || op == tkind.TK_MINUS ||
|
||||
op == tkind.TK_STAR || op == tkind.TK_SLASH ||
|
||||
op == tkind.TK_PERCENT || op == tkind.TK_AMP ||
|
||||
op == tkind.TK_PIPE || op == tkind.TK_CARET ||
|
||||
op == tkind.TK_LSHIFT || op == tkind.TK_RSHIFT) {
|
||||
return unifyarith(c, ltn, rtn);
|
||||
};
|
||||
if (op == tkind.TK_EQ || op == tkind.TK_NEQ ||
|
||||
op == tkind.TK_LT || op == tkind.TK_LE ||
|
||||
op == tkind.TK_GT || op == tkind.TK_GE ||
|
||||
op == tkind.TK_AND || op == tkind.TK_OR) {
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// unoptype — derive the result tnode of an N_UN unary expression. Mirrors
|
||||
// cstage cmd/wcc/check.c:642-687 `cunop`. The slice/str .len/.cap
|
||||
// pseudo-field address-of widening to *i64 mirrors check.c:672-682
|
||||
// directly — its purpose is documented at the cstage site.
|
||||
fn unoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let opt: *node = exprtype(c, e.lhs, nil);
|
||||
if (op == tkind.TK_MINUS || op == tkind.TK_PLUS) { return opt; };
|
||||
if (op == tkind.TK_NOT) { return mktname(c, "bool"); };
|
||||
if (op == tkind.TK_TILDE) { return opt; };
|
||||
if (op == tkind.TK_STAR) {
|
||||
if (opt == nil) { return nil; };
|
||||
let u: *node = resolvealias(c, unwrapbang(opt));
|
||||
if (u == nil) { return nil; };
|
||||
if (u.kind != nkind.N_TPTR) { return nil; };
|
||||
return u.lhs;
|
||||
};
|
||||
if (op == tkind.TK_AMP) {
|
||||
if (e.lhs != nil && e.lhs.kind == nkind.N_DOT) {
|
||||
let fld: str = e.lhs.str;
|
||||
if (streq(fld, "len") || streq(fld, "cap")) {
|
||||
let base: *node = e.lhs.lhs;
|
||||
if (base != nil && base.type_ != nil) {
|
||||
let bu: *tinfo = base.type_: *tinfo;
|
||||
if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
|
||||
if (bu != nil && bu.kind == tykind.TY_PTR) { bu = bu.sub; };
|
||||
if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
|
||||
if (bu != nil) {
|
||||
if (bu.kind == tykind.TY_SLICE || bu.kind == tykind.TY_STR) {
|
||||
let pp: *node = newnode(nkind.N_TPTR, "", 0, 0);
|
||||
pp.lhs = mktname(c, "i64");
|
||||
return pp;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (opt == nil) { return nil; };
|
||||
let pp: *node = newnode(nkind.N_TPTR, "", 0, 0);
|
||||
pp.lhs = opt;
|
||||
return pp;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// exprtype — best-effort type-AST inference for an expression
|
||||
// node. Handles literals, identifiers, calls, and casts; returns
|
||||
// nil for shapes we don't statically know (binary ops, struct
|
||||
// node. Handles literals, identifiers, calls, casts, binary/unary
|
||||
// ops; returns nil for shapes we don't statically know (struct
|
||||
// field access into non-primitive types, etc).
|
||||
// `hint`: optional declared-type AST passed by the caller (let
|
||||
// target, assign target). nil = "no hint, derive from self". Threaded
|
||||
@@ -8240,6 +8351,16 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
};
|
||||
return t;
|
||||
};
|
||||
if (k == nkind.N_BIN) {
|
||||
let tn: *node = binoptype(c, e);
|
||||
e.type_ = tinfofornode(c, tn): *void;
|
||||
return tn;
|
||||
};
|
||||
if (k == nkind.N_UN) {
|
||||
let tn: *node = unoptype(c, e);
|
||||
e.type_ = tinfofornode(c, tn): *void;
|
||||
return tn;
|
||||
};
|
||||
if (k == nkind.N_CAST) {
|
||||
// `expr: T` — explicit cast; the type expr is e.rhs. Mirrors
|
||||
// cstage cmd/wcc/check.c:737 `n->type = resolve_type(c, n->rhs)`.
|
||||
|
||||
@@ -1228,9 +1228,120 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
|
||||
return r;
|
||||
};
|
||||
|
||||
// unifyarith — usual-arithmetic-conversion analogue at the AST-tnode
|
||||
// layer. Mirrors cstage cmd/wcc/check.c:580-596 `unify_arith`. The
|
||||
// trailing typeeq fallback covers same-typed operands; mismatched typed
|
||||
// pairs silently return ltn (cstage flags the same shape — wwstage's
|
||||
// checker stays silent here per existing discipline).
|
||||
fn unifyarith(c: *checker, ltn: *node, rtn: *node) *node = {
|
||||
let lu: bool = isuntypedint(ltn) || isuntypedfloat(ltn);
|
||||
let ru: bool = isuntypedint(rtn) || isuntypedfloat(rtn);
|
||||
if (lu && ru) {
|
||||
if (isuntypedfloat(ltn) || isuntypedfloat(rtn)) {
|
||||
return mktname(c, "untyped_float");
|
||||
};
|
||||
return mktname(c, "untyped_int");
|
||||
};
|
||||
let conf: bool = false;
|
||||
if (lu) {
|
||||
if (isassignable(c, rtn, ltn, &conf)) { return rtn; };
|
||||
};
|
||||
if (ru) {
|
||||
if (isassignable(c, ltn, rtn, &conf)) { return ltn; };
|
||||
};
|
||||
if (typeeqast(ltn, rtn)) { return ltn; };
|
||||
return ltn;
|
||||
};
|
||||
|
||||
// binoptype — derive the result tnode of an N_BIN operator expression.
|
||||
// Mirrors cstage cmd/wcc/check.c:598-640 `cbinop`. Operates on tnodes
|
||||
// returned by exprtype; ptr arithmetic / bitwise / shifts / comparisons /
|
||||
// logicals all reflect cstage's rules. Type-mismatch diagnostics are
|
||||
// elided here (cstage gates the same shape).
|
||||
fn binoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let ltn: *node = exprtype(c, e.lhs, nil);
|
||||
let rtn: *node = exprtype(c, e.rhs, nil);
|
||||
if (op == tkind.TK_PLUS || op == tkind.TK_MINUS) {
|
||||
if (ltn != nil && ltn.kind == nkind.N_TPTR && isinttypeast(rtn)) {
|
||||
return ltn;
|
||||
};
|
||||
};
|
||||
if (op == tkind.TK_PLUS) {
|
||||
if (isinttypeast(ltn) && rtn != nil && rtn.kind == nkind.N_TPTR) {
|
||||
return rtn;
|
||||
};
|
||||
};
|
||||
if (op == tkind.TK_MINUS) {
|
||||
if (ltn != nil && rtn != nil
|
||||
&& ltn.kind == nkind.N_TPTR && rtn.kind == nkind.N_TPTR) {
|
||||
return mktname(c, "i64");
|
||||
};
|
||||
};
|
||||
if (op == tkind.TK_PLUS || op == tkind.TK_MINUS ||
|
||||
op == tkind.TK_STAR || op == tkind.TK_SLASH ||
|
||||
op == tkind.TK_PERCENT || op == tkind.TK_AMP ||
|
||||
op == tkind.TK_PIPE || op == tkind.TK_CARET ||
|
||||
op == tkind.TK_LSHIFT || op == tkind.TK_RSHIFT) {
|
||||
return unifyarith(c, ltn, rtn);
|
||||
};
|
||||
if (op == tkind.TK_EQ || op == tkind.TK_NEQ ||
|
||||
op == tkind.TK_LT || op == tkind.TK_LE ||
|
||||
op == tkind.TK_GT || op == tkind.TK_GE ||
|
||||
op == tkind.TK_AND || op == tkind.TK_OR) {
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// unoptype — derive the result tnode of an N_UN unary expression. Mirrors
|
||||
// cstage cmd/wcc/check.c:642-687 `cunop`. The slice/str .len/.cap
|
||||
// pseudo-field address-of widening to *i64 mirrors check.c:672-682
|
||||
// directly — its purpose is documented at the cstage site.
|
||||
fn unoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let opt: *node = exprtype(c, e.lhs, nil);
|
||||
if (op == tkind.TK_MINUS || op == tkind.TK_PLUS) { return opt; };
|
||||
if (op == tkind.TK_NOT) { return mktname(c, "bool"); };
|
||||
if (op == tkind.TK_TILDE) { return opt; };
|
||||
if (op == tkind.TK_STAR) {
|
||||
if (opt == nil) { return nil; };
|
||||
let u: *node = resolvealias(c, unwrapbang(opt));
|
||||
if (u == nil) { return nil; };
|
||||
if (u.kind != nkind.N_TPTR) { return nil; };
|
||||
return u.lhs;
|
||||
};
|
||||
if (op == tkind.TK_AMP) {
|
||||
if (e.lhs != nil && e.lhs.kind == nkind.N_DOT) {
|
||||
let fld: str = e.lhs.str;
|
||||
if (streq(fld, "len") || streq(fld, "cap")) {
|
||||
let base: *node = e.lhs.lhs;
|
||||
if (base != nil && base.type_ != nil) {
|
||||
let bu: *tinfo = base.type_: *tinfo;
|
||||
if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
|
||||
if (bu != nil && bu.kind == tykind.TY_PTR) { bu = bu.sub; };
|
||||
if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
|
||||
if (bu != nil) {
|
||||
if (bu.kind == tykind.TY_SLICE || bu.kind == tykind.TY_STR) {
|
||||
let pp: *node = newnode(nkind.N_TPTR, "", 0, 0);
|
||||
pp.lhs = mktname(c, "i64");
|
||||
return pp;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (opt == nil) { return nil; };
|
||||
let pp: *node = newnode(nkind.N_TPTR, "", 0, 0);
|
||||
pp.lhs = opt;
|
||||
return pp;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// exprtype — best-effort type-AST inference for an expression
|
||||
// node. Handles literals, identifiers, calls, and casts; returns
|
||||
// nil for shapes we don't statically know (binary ops, struct
|
||||
// node. Handles literals, identifiers, calls, casts, binary/unary
|
||||
// ops; returns nil for shapes we don't statically know (struct
|
||||
// field access into non-primitive types, etc).
|
||||
// `hint`: optional declared-type AST passed by the caller (let
|
||||
// target, assign target). nil = "no hint, derive from self". Threaded
|
||||
@@ -1308,6 +1419,16 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
};
|
||||
return t;
|
||||
};
|
||||
if (k == nkind.N_BIN) {
|
||||
let tn: *node = binoptype(c, e);
|
||||
e.type_ = tinfofornode(c, tn): *void;
|
||||
return tn;
|
||||
};
|
||||
if (k == nkind.N_UN) {
|
||||
let tn: *node = unoptype(c, e);
|
||||
e.type_ = tinfofornode(c, tn): *void;
|
||||
return tn;
|
||||
};
|
||||
if (k == nkind.N_CAST) {
|
||||
// `expr: T` — explicit cast; the type expr is e.rhs. Mirrors
|
||||
// cstage cmd/wcc/check.c:737 `n->type = resolve_type(c, n->rhs)`.
|
||||
|
||||
@@ -8160,9 +8160,120 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
|
||||
return r;
|
||||
};
|
||||
|
||||
// unifyarith — usual-arithmetic-conversion analogue at the AST-tnode
|
||||
// layer. Mirrors cstage cmd/wcc/check.c:580-596 `unify_arith`. The
|
||||
// trailing typeeq fallback covers same-typed operands; mismatched typed
|
||||
// pairs silently return ltn (cstage flags the same shape — wwstage's
|
||||
// checker stays silent here per existing discipline).
|
||||
fn unifyarith(c: *checker, ltn: *node, rtn: *node) *node = {
|
||||
let lu: bool = isuntypedint(ltn) || isuntypedfloat(ltn);
|
||||
let ru: bool = isuntypedint(rtn) || isuntypedfloat(rtn);
|
||||
if (lu && ru) {
|
||||
if (isuntypedfloat(ltn) || isuntypedfloat(rtn)) {
|
||||
return mktname(c, "untyped_float");
|
||||
};
|
||||
return mktname(c, "untyped_int");
|
||||
};
|
||||
let conf: bool = false;
|
||||
if (lu) {
|
||||
if (isassignable(c, rtn, ltn, &conf)) { return rtn; };
|
||||
};
|
||||
if (ru) {
|
||||
if (isassignable(c, ltn, rtn, &conf)) { return ltn; };
|
||||
};
|
||||
if (typeeqast(ltn, rtn)) { return ltn; };
|
||||
return ltn;
|
||||
};
|
||||
|
||||
// binoptype — derive the result tnode of an N_BIN operator expression.
|
||||
// Mirrors cstage cmd/wcc/check.c:598-640 `cbinop`. Operates on tnodes
|
||||
// returned by exprtype; ptr arithmetic / bitwise / shifts / comparisons /
|
||||
// logicals all reflect cstage's rules. Type-mismatch diagnostics are
|
||||
// elided here (cstage gates the same shape).
|
||||
fn binoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let ltn: *node = exprtype(c, e.lhs, nil);
|
||||
let rtn: *node = exprtype(c, e.rhs, nil);
|
||||
if (op == tkind.TK_PLUS || op == tkind.TK_MINUS) {
|
||||
if (ltn != nil && ltn.kind == nkind.N_TPTR && isinttypeast(rtn)) {
|
||||
return ltn;
|
||||
};
|
||||
};
|
||||
if (op == tkind.TK_PLUS) {
|
||||
if (isinttypeast(ltn) && rtn != nil && rtn.kind == nkind.N_TPTR) {
|
||||
return rtn;
|
||||
};
|
||||
};
|
||||
if (op == tkind.TK_MINUS) {
|
||||
if (ltn != nil && rtn != nil
|
||||
&& ltn.kind == nkind.N_TPTR && rtn.kind == nkind.N_TPTR) {
|
||||
return mktname(c, "i64");
|
||||
};
|
||||
};
|
||||
if (op == tkind.TK_PLUS || op == tkind.TK_MINUS ||
|
||||
op == tkind.TK_STAR || op == tkind.TK_SLASH ||
|
||||
op == tkind.TK_PERCENT || op == tkind.TK_AMP ||
|
||||
op == tkind.TK_PIPE || op == tkind.TK_CARET ||
|
||||
op == tkind.TK_LSHIFT || op == tkind.TK_RSHIFT) {
|
||||
return unifyarith(c, ltn, rtn);
|
||||
};
|
||||
if (op == tkind.TK_EQ || op == tkind.TK_NEQ ||
|
||||
op == tkind.TK_LT || op == tkind.TK_LE ||
|
||||
op == tkind.TK_GT || op == tkind.TK_GE ||
|
||||
op == tkind.TK_AND || op == tkind.TK_OR) {
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// unoptype — derive the result tnode of an N_UN unary expression. Mirrors
|
||||
// cstage cmd/wcc/check.c:642-687 `cunop`. The slice/str .len/.cap
|
||||
// pseudo-field address-of widening to *i64 mirrors check.c:672-682
|
||||
// directly — its purpose is documented at the cstage site.
|
||||
fn unoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let opt: *node = exprtype(c, e.lhs, nil);
|
||||
if (op == tkind.TK_MINUS || op == tkind.TK_PLUS) { return opt; };
|
||||
if (op == tkind.TK_NOT) { return mktname(c, "bool"); };
|
||||
if (op == tkind.TK_TILDE) { return opt; };
|
||||
if (op == tkind.TK_STAR) {
|
||||
if (opt == nil) { return nil; };
|
||||
let u: *node = resolvealias(c, unwrapbang(opt));
|
||||
if (u == nil) { return nil; };
|
||||
if (u.kind != nkind.N_TPTR) { return nil; };
|
||||
return u.lhs;
|
||||
};
|
||||
if (op == tkind.TK_AMP) {
|
||||
if (e.lhs != nil && e.lhs.kind == nkind.N_DOT) {
|
||||
let fld: str = e.lhs.str;
|
||||
if (streq(fld, "len") || streq(fld, "cap")) {
|
||||
let base: *node = e.lhs.lhs;
|
||||
if (base != nil && base.type_ != nil) {
|
||||
let bu: *tinfo = base.type_: *tinfo;
|
||||
if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
|
||||
if (bu != nil && bu.kind == tykind.TY_PTR) { bu = bu.sub; };
|
||||
if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
|
||||
if (bu != nil) {
|
||||
if (bu.kind == tykind.TY_SLICE || bu.kind == tykind.TY_STR) {
|
||||
let pp: *node = newnode(nkind.N_TPTR, "", 0, 0);
|
||||
pp.lhs = mktname(c, "i64");
|
||||
return pp;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (opt == nil) { return nil; };
|
||||
let pp: *node = newnode(nkind.N_TPTR, "", 0, 0);
|
||||
pp.lhs = opt;
|
||||
return pp;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// exprtype — best-effort type-AST inference for an expression
|
||||
// node. Handles literals, identifiers, calls, and casts; returns
|
||||
// nil for shapes we don't statically know (binary ops, struct
|
||||
// node. Handles literals, identifiers, calls, casts, binary/unary
|
||||
// ops; returns nil for shapes we don't statically know (struct
|
||||
// field access into non-primitive types, etc).
|
||||
// `hint`: optional declared-type AST passed by the caller (let
|
||||
// target, assign target). nil = "no hint, derive from self". Threaded
|
||||
@@ -8240,6 +8351,16 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
};
|
||||
return t;
|
||||
};
|
||||
if (k == nkind.N_BIN) {
|
||||
let tn: *node = binoptype(c, e);
|
||||
e.type_ = tinfofornode(c, tn): *void;
|
||||
return tn;
|
||||
};
|
||||
if (k == nkind.N_UN) {
|
||||
let tn: *node = unoptype(c, e);
|
||||
e.type_ = tinfofornode(c, tn): *void;
|
||||
return tn;
|
||||
};
|
||||
if (k == nkind.N_CAST) {
|
||||
// `expr: T` — explicit cast; the type expr is e.rhs. Mirrors
|
||||
// cstage cmd/wcc/check.c:737 `n->type = resolve_type(c, n->rhs)`.
|
||||
|
||||
Reference in New Issue
Block a user