aboutsummaryrefslogtreecommitdiff
path: root/src/compiler/expr.zc
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-15 22:15:38 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-15 22:15:38 +0330
commitbe43babc1886fc5efee898b2a3d0285012e511fb (patch)
treeb7ae73e7a6d4c896e2c49b7090168ef589b574af /src/compiler/expr.zc
parentmerge work/known-bugs: constant-expression precedence, local const in constan... (diff)
definite assignment: reading a local before it is initialized is a compile error (spec 4.1.5, b.1.9)
track linited per local alongside the ownership state; snapshots, exit records and joins carry it, and a join keeps a local set only when every reaching path set it. x = e, a declaration initializer, &x (out-parameter), a full array initializer, or using an aggregate as an address set a local; field and element writes do not set the aggregate. while/for bodies that may run zero times do not count; do/while does. compiler, runtime, std, actor and lsp sources initialize the locals the rule flagged; zc0 accepts = {} brace lists (zeroed). tests: tests/errors/uninit_*.zc, tests/conformance/4-statements/uninit_*.zc
Diffstat (limited to 'src/compiler/expr.zc')
-rw-r--r--src/compiler/expr.zc61
1 files changed, 41 insertions, 20 deletions
diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc
index e437d2f..ecbb24c 100644
--- a/src/compiler/expr.zc
+++ b/src/compiler/expr.zc
@@ -20,14 +20,23 @@ int dbg_addr() {
// variable, field or element that has not been loaded; rval loads it, unless
// the thing is an array or struct, whose address is its value. every place
// that needs a value calls this after parsing an expression.
-int rval() {
+// load or finish an lvalue as a value. check is 1 when the value is read
+// (definite assignment); 0 when forming a field or element address to write.
+int rval_do(int check) {
if (lv && lvk == LV_LOCAL) {
ltag[lvi] = ltag[lvi] | LREAD;
+ if (check) {
+ // an aggregate's value is its address (like &x), so using it initializes it;
+ // a scalar must already be set
+ if (isagg(xty) || larr[lvi]) mark_inited(lvi);
+ else need_inited(lvi);
+ }
if (rt.eq_w(lname[lvi], "_") && af_warning(lreadpos[lvi])) {
rt.put_s("discard placeholder `_` is read\nhelp: give the parameter or pattern binding a name when its value is needed");
af_warning_end();
}
}
+ if (lv && check && lvk == LV_ELEM && lvi >= 0) need_inited(lvi);
if (lv) {
if (ispk(xty)) pkld(xty);
else if (!isagg(xty)) { dbg_addr(); op(O_LD); }
@@ -35,6 +44,8 @@ int rval() {
}
return 0;
}
+int rval() { return rval_do(1); }
+
// run a keyword's handler (kw_len, kw_struct, a parse word...) from the spot
// where the parser met it. e is 1 in expression position, where statement-only
// keywords refuse to run. the handler reports the class of what it produced
@@ -441,7 +452,7 @@ int printcall(int line) {
return C_PLAIN;
}
int call(int s) {
- int np, tags = 0, ret = 0; word ft = stype[s]; word[8] bs; int rs, rhs_type = 0, kop, cp = callpos, original = s; word storage; int origin, temporary, owner; word origins, owner_fact;
+ int np, tags = 0, ret = 0; word ft = stype[s]; word[8] bs = {}; int rs, rhs_type = 0, kop, cp = callpos, original = s; word storage; int origin, temporary, owner; word origins, owner_fact;
word requirements = effect_requirements, edges = effect_edges;
if (rt.eq_w(sname[s], "std.Io.print")) return printcall(0);
if (rt.eq_w(sname[s], "std.Io.println")) return printcall(1);
@@ -702,7 +713,7 @@ int shared_use(int s) {
// compiler state around the body.
int closure() {
char* xcf;
- int np = 0; char*[24] pn; word[24] pty; int hop, entry, i, t, envl, ncap, c, result, declared = 0, tags = 0, tag, lt, lfl = tline; word[24] ptags;
+ int np = 0; char*[24] pn = {}; word[24] pty = {}; int hop, entry, i, t, envl, ncap, c, result, declared = 0, tags = 0, tag, lt, lfl = tline; word[24] ptags = {};
word expected = closure_expected, this_type = closure_this; closure_expected = 0; closure_this = 0;
reserve_closures(nclo + 1);
if (o3on) serr("closures can't be made inside an o3 region");
@@ -744,14 +755,14 @@ int closure() {
prologue(); borrow_prologue();
i = 0;
while (i < np) {
- t = newloc(pn[i]); lrskind[t] = RS_PARAM; lrsoff[t] = pnoffs[i]; lpos[t] = pnplaces[i]; fslots++; lsmark(1, pn[i]);
+ t = newloc(pn[i]); lrskind[t] = RS_PARAM; lrsoff[t] = pnoffs[i]; lpos[t] = pnplaces[i]; fslots++; lsmark(1, pn[i]); mark_inited(t);
ltype[t] = pty[i];
if (fchk && ptags[i] == 1) lstate[t] = S_BORROW;
if (fchk && ptags[i] == 2) { lstate[t] = S_M; lown[t] = 1; }
if (isfl(pty[i])) setflt(t, 1);
i++;
}
- envl = newloc(intern("<env>")); fslots++;
+ envl = newloc(intern("<env>")); fslots++; mark_inited(envl);
clo_env[nclo - 1] = envl;
i = np + 1;
while (i) { i--; laddr(i); op(O_ST); }
@@ -776,7 +787,7 @@ int closure() {
while (i < ncap) {
t = clo_loc[c * capture_width + i];
if (clo_via[c * capture_width + i] < 0) {
- touch(t); effect_note_mask(effect_mask(C_VAR + t), EF_RETAIN); ev_put(t, EV_CAPTURE, -1);
+ need_inited(t); touch(t); effect_note_mask(effect_mask(C_VAR + t), EF_RETAIN); ev_put(t, EV_CAPTURE, -1);
if (cmode && !dry) { lvescaped[t] = 1; lvkind[t] = 0; }
if (!isalias(t)) { vaddr(t); op(O_LD); callname(intern("share")); op(O_DROP); }
vaddr(t);
@@ -963,7 +974,7 @@ int array_literal_tokens(word q, word t) {
}
int i = newloc(intern("<array literal>")), place = tpack();
t = array_measure(q, t); ltype[i] = t; larr[i] = 1; fslots = fslots + tsz(t);
- array_initialize(q, t, lslot[i], 1);
+ array_initialize(q, t, lslot[i], 1); mark_inited(i);
lafact[i] = af_new(t[3], 4, place); laaddr[i] = lafact[i]; if (lafact[i]) lafact[i][5] = i;
laddr(lslot[i]); xty = t; lv = 0; lvk = LV_NONE; xbor = i; xaf = lafact[i];
xzero = 0; xfloat = 0; xstr = 0; xconst = 0; xvk = 0; rt.free(q);
@@ -1018,7 +1029,7 @@ int brace_primary() {
// variable the value is.
int primary() {
effect_value = 0; xprojection = 0; if_value = 0;
- int n, i, s, c, k, npos, sv; word[8] bs0; int ft, mn, ms;
+ int n, i, s, c, k, npos, sv; word[8] bs0 = {}; int ft, mn, ms;
lv = 0; lvk = LV_NONE; xzero = 0; xbor = -1;
xconst = 0; xfloat = 0; xstr = 0; xlenloc = -1; xstamp = 0; xaf = 0; xvk = 0;
if (tk == T_FLT) { op(OP_FLIT); emit(tv); xvk = 2; xvv = tv; xfloat = 1; xty = tfloat; next(); return C_PLAIN; }
@@ -1067,7 +1078,7 @@ int primary() {
}
vaddr(i);
xty = ltype[i];
- if (larr[i]) return C_PLAIN;
+ if (larr[i]) { lv = 1; lvk = LV_LOCAL; lvi = i; return C_PLAIN; }
xfloat = getflt(i);
if (isfl(ltype[i])) xfloat = 1;
xstr = getstr(i); if (xty == tbstr) xstr = 1;
@@ -1200,6 +1211,8 @@ int incdec(int post, int dlt) {
if (lvk == LV_LOCAL && fchk && tracked(lvi) && !isalias(lvi)) {
cerrn(lvi, "owns an object and can't be incremented");
}
+ if (lvk == LV_LOCAL) need_inited(lvi);
+ else if (lvk == LV_ELEM && lvi >= 0) need_inited(lvi);
tcneed(isnum(xty) || xty[0] == TY_PTR || xty == tword, "++ and -- need a number or a pointer", xty);
if (lvk == LV_LOCAL) {
ltag[lvi] = ltag[lvi] | LASG;
@@ -1237,7 +1250,8 @@ word field(int c) {
int readonly = treadonly(xty);
if_value = 0;
int n, fieldoff, fieldpos, sv; word st;
- word borrowed = xaf, base_mask = effect_mask(c); af_use(xaf, tpack(), c); xaf = 0; xvk = 0; int i, b = xbor, k = 0, scanned = 0, ms, de, pmatch = 0;
+ word borrowed = xaf, base_mask = effect_mask(c); af_use(xaf, tpack(), c); xaf = 0; xvk = 0; int i, b = xbor, k = 0, scanned = 0, ms, de, pmatch = 0, base_local = -1;
+ if (lv && lvk == LV_LOCAL) base_local = lvi;
de = toff + toklen;
next();
// is the place asked about on this member name (or just after the dot, with none typed)
@@ -1270,7 +1284,7 @@ word field(int c) {
}
}
}
- rval();
+ rval_do(0);
if (c == C_FRESH) cerr("use a field of a new object only after binding it to a name");
else if (c >= C_VAR) { touch(c - C_VAR); b = c - C_VAR; }
st = xty;
@@ -1336,7 +1350,9 @@ word field(int c) {
effect_value = base_mask;
xprojection = af_project(borrowed, k, tsz(xty), tpack(), 0);
lv = 1; xzero = 0;
- if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER;
+ if (b >= 0) { lvk = LV_ELEM; lvi = b; }
+ else if (base_local >= 0) { lvk = LV_ELEM; lvi = base_local; }
+ else lvk = LV_OTHER;
return C_PLAIN;
}
// array indexing, address arithmetic. mulsz scales an index by the element
@@ -1407,7 +1423,7 @@ int fixed_chk(int n) {
// and returns at once for any other pointer. sz is the element size in words, or
// minus the element size in bytes for packed elements.
int ptr_chk(int sz) {
- int k = lookup(s_bnd), j;
+ int k = lookup(s_bnd), j = 0;
if (k >= 0 && skind[k] == K_ENH) { // the test is one enhancement; the call only runs to report
lit(1); op(O_PICK); lit(1); op(O_PICK);
if (sz > 0) { lit(sz); lit(1); } else { lit(0 - sz); lit(8); }
@@ -1430,7 +1446,7 @@ int ptr_chk(int sz) {
// through rt.bchk, which only knows the size of a heap block's first word.
int postfix() { return postfix_tail(primary()); }
int postfix_tail(int c) {
- int b, n, t, et; word ft; word[8] bs; int k, ci, was, al, ix, enh, cz, cj, place, offset, temporary, call_owner, readonly, ub, bl, ic, inr, hg, pn, iu; word storage, storage_mask, call_origins;
+ int b, n, t, et; word ft; word[8] bs = {}; int k, ci, was, al, ix, enh, cz, cj, place, offset, temporary, call_owner, readonly, ub, bl, ic, inr, hg, pn, iu; word storage, storage_mask, call_origins;
while (1) {
if (tk == T_ID && tv == s_as) { // e as T: same bits, another type
next(); t = ptype(); rval(); if (!xaf || xaf[2] != 5) xaf = 0; vf_coerce(t, 0);
@@ -1452,7 +1468,7 @@ int postfix_tail(int c) {
ub = !fbnd || o3on || (lv && lvk == LV_LOCAL && (ltag[lvi] & 16)); // unbounded, or o3 code
bl = -1; if (lv && lvk == LV_LOCAL) bl = lvi;
next();
- rval();
+ rval_do(0); // indexing forms an element address; reading the element checks later
if (was) {
c = expr(); rval(); plainuse(c);
if (accept(s_colon)) {
@@ -1599,7 +1615,9 @@ int postfix_tail(int c) {
lv = 1; xty = et; xzero = 0; xconst = 0; xvk = 0; xlenloc = -1; xstamp = 0;
if (isref(xty)) { if (b >= 0) xbor = lroot(b); }
else xbor = -1;
- if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER;
+ if (b >= 0) { lvk = LV_ELEM; lvi = b; }
+ else if (bl >= 0) { lvk = LV_ELEM; lvi = bl; }
+ else lvk = LV_OTHER;
c = C_PLAIN;
xfloat = 0; if (isfl(xty)) xfloat = 1; xstr = 0;
continue;
@@ -1622,6 +1640,7 @@ int postfix_tail(int c) {
ifcall = -1; effect_argument_base = 0;
int call_tags = 0; if (ft) call_tags = ft[5] >> 6;
n = args(call_tags, ft, bs, o3on); temporary = argtemporary; call_owner = argowner; call_origins = argorigin_mask;
+ place = tpack();
// raw code can live in a heap segment; a closure has an environment block
lit(n); op(O_PICK); callname(intern("callable_code"));
cz = hole(); op(O_JZ);
@@ -1719,7 +1738,7 @@ int unary() {
if (!lv && !isagg(xty)) die("& needs something addressable");
if (lv && ispk(xty)) serr("an element of a sized-integer array has no address; it is a byte inside a word");
if (lv && lvk == LV_LOCAL && (ltag[lvi] & 16)) serr("an o3 variable has no address");
- if (lv && lvk == LV_LOCAL) ltag[lvi] = ltag[lvi] | LREAD | LASG;
+ if (lv && lvk == LV_LOCAL) { ltag[lvi] = ltag[lvi] | LREAD | LASG; mark_inited(lvi); }
if (c >= C_VAR) touch(c - C_VAR);
if (lvk == LV_ELEM && lvi >= 0) xbor = lroot(lvi);
lv = 0; lvk = LV_NONE; xzero = 0; xfloat = 0; xstr = 0;
@@ -1799,7 +1818,7 @@ int bin(int minp) {
next(); rval(); plainuse(c);
lt = xty;
if (isfl(lt)) lf = 1;
- src = -1;
+ src = -1; rf = 0; rs = 0; right_mask = 0;
if (blen[b] == -1) { // && : 0 if the left side is 0, else the right side as 0 or 1
af_forget();
ftest();
@@ -1892,7 +1911,7 @@ int storekeep(int t) {
// restored for the second side) and then joined, so a variable can't be given
// away on one side only. both sides are fresh objects, or neither is.
int ternary(int c) {
- int a, b, s0, sa, livea, live0 = live, n0 = nloc, ca, cb, ta, ba, bb, conv, skip, back; word temp_a, temp_b, mask_a, mask_b;
+ int a, b, s0, sa, livea, live0 = live, n0 = nloc, ca, cb, ta, ba, bb, conv, skip, back = 0; word temp_a, temp_b, mask_a, mask_b;
rval(); plainuse(c);
ftest();
next();
@@ -1950,7 +1969,7 @@ int expr() {
if (k != LV_LOCAL) af_escape(xaf);
fcoerce(lt);
if (k == LV_LOCAL) {
- ltag[i] = ltag[i] | LASG;
+ ltag[i] = ltag[i] | LASG; mark_inited(i);
setflt(i, xfloat);
setstr(i, xstr);
note_store(i); af_store(i); vf_store(i);
@@ -1979,6 +1998,8 @@ int expr() {
if (lvk == LV_LOCAL && fchk && tracked(lvi) && !isalias(lvi)) {
cerrn(lvi, "owns an object; compound assignment would lose it");
}
+ if (lvk == LV_LOCAL) need_inited(lvi);
+ else if (lvk == LV_ELEM && lvi >= 0) need_inited(lvi);
lt = xty; require_mutable(lt);
fk = -1; lf = xfloat;
if (lvk == LV_LOCAL) fk = lvi;