diff options
| author | 2026-10-15 22:15:38 +0330 | |
|---|---|---|
| committer | 2026-10-15 22:15:38 +0330 | |
| commit | be43babc1886fc5efee898b2a3d0285012e511fb (patch) | |
| tree | b7ae73e7a6d4c896e2c49b7090168ef589b574af /src/compiler/expr.zc | |
| parent | merge 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.zc | 61 |
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; |
