diff options
| author | 2026-10-15 22:28:06 +0330 | |
|---|---|---|
| committer | 2026-10-15 22:28:06 +0330 | |
| commit | 5a4dc62a79af6135c7050e81a82dbfdfb0a7d4e0 (patch) | |
| tree | 805283b2d30b2cf0f53af42e5a3fc119af5e839f /src | |
| parent | definite assignment: reading a local before it is initialized is a compile er... (diff) | |
uninit: track aggregate fills per part, sticky computed-index fills, &a[k] and method calls set the aggregate; fix test/example hits; docs
Diffstat (limited to 'src')
| -rw-r--r-- | src/compiler/analyzer.zc | 35 | ||||
| -rw-r--r-- | src/compiler/base.zc | 4 | ||||
| -rw-r--r-- | src/compiler/expr.zc | 28 | ||||
| -rw-r--r-- | src/lib/std.zc | 2 |
4 files changed, 51 insertions, 18 deletions
diff --git a/src/compiler/analyzer.zc b/src/compiler/analyzer.zc index 3802437..e68e165 100644 --- a/src/compiler/analyzer.zc +++ b/src/compiler/analyzer.zc @@ -440,7 +440,7 @@ int newloc(int n) { if (!local_ended) local_ended = chk_resize(0, 0, loccap); local_ended[i] = 0; if (!local_end_info) local_end_info = chk_resize(0, 0, loccap); local_end_info[i] = 0; if (!local_shadow) local_shadow = chk_resize(0, 0, loccap); local_shadow[i] = 0; - if (!linited) linited = chk_resize(0, 0, loccap); linited[i] = 0; + if (!linited) linited = chk_resize(0, 0, loccap); linited[i] = 0; if (!lfill) lfill = chk_resize(0, 0, loccap); lfill[i] = 0; lpos[i] = 0; lreadpos[i] = 0; lowner[i] = 0; @@ -1027,13 +1027,32 @@ int luse(int i) { } // definite assignment (spec 4.1.5): a local must be written on every path // before it is read. linited[i] is 1 once that is true for the current path. +// linited[i] is -1 when the whole local is set; an array or struct being filled +// part by part holds a bit per field or constant element written so far (parts +// 0..61). a join ands the paths' words, so a part counts only when every path set it. int mark_inited(int i) { - if (i >= 0 && linited) linited[i] = 1; + if (i >= 0 && linited) linited[i] = -1; return 0; } int need_inited(int i) { if (!fchk || panic || dry || i < 0 || !linited) return 0; - if (!linited[i]) serrn(lname[i], "used before it is initialized"); + if (linited[i] != -1 && !(lfill && lfill[i])) serrn(lname[i], "used before it is initialized"); + return 0; +} +// part k (or -1: unknown) of aggregate local i was written; n is how many parts it has +int mark_part(int i, int k, int n) { + if (i < 0 || !linited || linited[i] == -1) return 0; + if (k < 0 || k > 61 || n > 62) { linited[i] = -1; if (lfill) lfill[i] = 1; return 0; } // a computed index: a fill loop, taken as setting it all (on every path: loops may not run, so this is sticky) + linited[i] = linited[i] | (1 << k); + if (n > 0 && linited[i] == (1 << n) - 1) linited[i] = -1; + return 0; +} +// part k (or -1) of aggregate local i is read +int need_part(int i, int k) { + if (!fchk || panic || dry || i < 0 || !linited || linited[i] == -1 || (lfill && lfill[i])) return 0; + if (k > 61) return 0; // past the parts tracked (or out of range: the bounds check reports it) + if (k >= 0 && k <= 61 && ((linited[i] >> k) & 1)) return 0; + serrn(lname[i], "used before it is initialized"); return 0; } @@ -1212,7 +1231,7 @@ int snap_push() { p = R_SNAP + b; p[0] = lbase; p[1] = nloc; p = p + 2; - while (i < nloc) { p[0] = lstate[i]; p[1] = lowner[i]; p[2] = lhome[i]; if (linited) p[3] = linited[i]; else p[3] = 1; p = p + 4; i++; } + while (i < nloc) { p[0] = lstate[i]; p[1] = lowner[i]; p[2] = lhome[i]; if (linited) p[3] = linited[i]; else p[3] = -1; p = p + 4; i++; } snaptop = p - R_SNAP; return b; } @@ -1295,7 +1314,7 @@ word jow(word p, int i) { return p[3 + 4 * (i - p[0])]; } word jhm(word p, int i) { return p[4 + 4 * (i - p[0])]; } // definite-assignment bit for local i on path p (1 if missing from the snapshot). word jin(word p, int i) { - if (i >= p[1]) return 1; + if (i >= p[1]) return -1; return p[5 + 4 * (i - p[0])]; } // the states at a break, with the jump to patch: [site] then a snapshot @@ -1304,7 +1323,7 @@ word exit_rec(int site) { reserve_R_EXIT(extop + 3 + 4 * (nloc - lbase)); p = R_EXIT + extop; p[0] = site; p[1] = lbase; p[2] = nloc; q = p + 3; - while (i < nloc) { q[0] = lstate[i]; q[1] = lowner[i]; q[2] = lhome[i]; if (linited) q[3] = linited[i]; else q[3] = 1; q = q + 4; i++; } + while (i < nloc) { q[0] = lstate[i]; q[1] = lowner[i]; q[2] = lhome[i]; if (linited) q[3] = linited[i]; else q[3] = -1; q = q + 4; i++; } extop = q - R_EXIT; return p; } @@ -1360,8 +1379,8 @@ int join(int n0, int what) { } // definite assignment: only initialized if every reaching path assigned it if (linited) { - j = 0; s = 1; - while (j < njps) { if (!jin(jps[j], i)) s = 0; j++; } + j = 0; s = -1; + while (j < njps) { s = s & jin(jps[j], i); j++; } linited[i] = s; } i++; diff --git a/src/compiler/base.zc b/src/compiler/base.zc index 03a575c..8eb107e 100644 --- a/src/compiler/base.zc +++ b/src/compiler/base.zc @@ -254,7 +254,7 @@ int fn_return_type; // a consistent return type inferred from the function body // effects come from the real compiler's uses, stores, returns and call edges; unknown bodies stay conservative. const int EF_READ = 1, EF_WRITE = 2, EF_RETAIN = 4, EF_CONSUME = 8, EF_UNKNOWN = 16, EF_ALIAS = 32, EF_IDENTITY = 64; word effect_symbols, effect_edges, effect_requirements, effect_current; int effect_floor, effect_argument_base; -word local_origin, local_origin_mask, local_cell, local_captured, local_ended, local_end_info, local_shadow, xshadow, effect_shadows, linited; int argowner_mask; word effect_value, argorigin_mask; int capture_enabled; word[1] capture_dummy = {}; +word local_origin, local_origin_mask, local_cell, local_captured, local_ended, local_end_info, local_shadow, xshadow, effect_shadows, linited, lfill; int argowner_mask; word effect_value, argorigin_mask; int capture_enabled; word[1] capture_dummy = {}; // drops (docs/design/destructors.md): the code position just past a drop form (a fresh // value with a drop still on the stack there), the type `this` gets in the next closure, // a local whose drop is set, a mailbox send whose arguments are checked, and o3 block depth. @@ -264,6 +264,7 @@ int argorigin = -1, argtemporary, argowner = -1; word argowner_fact; int stubgen; // 1 while an extern's generated stub is compiled: the stub gets no hints word xprojection; int af_mutation_tick; int xbor = -1; // local a pointer value is borrowed from, or -1 +int xpart = -1, xparts = 0; // an lvalue inside an aggregate local: which top-level part (-1 unknown) of how many int mdbg, mdbg_flags; // memdebug: compile checks in, and the flags start() turns on int lsp, loopbase, ctxsp, snaptop; // defer: the statements registered and not yet run, DFR_W words each (the block @@ -786,6 +787,7 @@ int reserve_locals(int need) { if (local_end_info) local_end_info = chk_resize(local_end_info, nloc, cap); if (local_shadow) local_shadow = chk_resize(local_shadow, nloc, cap); if (linited) linited = chk_resize(linited, nloc, cap); + if (lfill) lfill = chk_resize(lfill, nloc, cap); lafact = chk_resize(lafact, nloc, cap); laaddr = chk_resize(laaddr, nloc, cap); lprev = chk_resize(lprev, nloc, cap); if (lvkind) { lvkind = chk_resize(lvkind, nloc, cap); lvvalue = chk_resize(lvvalue, nloc, cap); lvescaped = chk_resize(lvescaped, nloc, cap); } o3cache = chk_resize(o3cache, nloc, cap); diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc index ecbb24c..11ca8e1 100644 --- a/src/compiler/expr.zc +++ b/src/compiler/expr.zc @@ -36,7 +36,7 @@ int rval_do(int check) { af_warning_end(); } } - if (lv && check && lvk == LV_ELEM && lvi >= 0) need_inited(lvi); + if (lv && check && lvk == LV_ELEM && lvi >= 0) { if (larr[lvi]) need_part(lvi, xpart); else need_inited(lvi); } if (lv) { if (ispk(xty)) pkld(xty); else if (!isagg(xty)) { dbg_addr(); op(O_LD); } @@ -1212,7 +1212,7 @@ int incdec(int post, int dlt) { 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); + else if (lvk == LV_ELEM && lvi >= 0) { if (larr[lvi]) need_part(lvi, xpart); else 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; @@ -1251,7 +1251,9 @@ word field(int c) { 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, base_local = -1; - if (lv && lvk == LV_LOCAL) base_local = lvi; + int chained = 0; + if (lv && lvk == LV_LOCAL && larr[lvi]) base_local = lvi; + else if (lv && lvk == LV_ELEM && lvi >= 0 && larr[lvi]) { base_local = lvi; chained = 1; } de = toff + toklen; next(); // is the place asked about on this member name (or just after the dot, with none typed) @@ -1296,6 +1298,7 @@ word field(int c) { else if (st == tword || isnum(st)) { if (fieldscan(n) == 0) ms = methscan(n); } if (ms == 0 - 2) { serrn(n, "is a method of more than one struct"); ms = 0 - 1; } if (ms >= 0) { + if (base_local >= 0) mark_inited(base_local); // the method gets its address and may fill it return methcall(ms, st, borrowed, b, base_mask); } } @@ -1350,8 +1353,8 @@ 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 if (base_local >= 0) { lvk = LV_ELEM; lvi = base_local; } + if (base_local >= 0) { lvk = LV_ELEM; lvi = base_local; if (!chained) { xpart = i; xparts = st[3]; } } + else if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER; return C_PLAIN; } @@ -1467,6 +1470,9 @@ int postfix_tail(int c) { if (o3on && lv && lvk == LV_LOCAL && o3cache[lvi]) ci = o3cache[lvi]; ub = !fbnd || o3on || (lv && lvk == LV_LOCAL && (ltag[lvi] & 16)); // unbounded, or o3 code bl = -1; if (lv && lvk == LV_LOCAL) bl = lvi; + int abase = -1, achain = 0; ic = 0; + if (lv && lvk == LV_LOCAL && larr[lvi]) abase = lvi; + else if (lv && lvk == LV_ELEM && lvi >= 0 && larr[lvi]) { abase = lvi; achain = 1; } next(); rval_do(0); // indexing forms an element address; reading the element checks later if (was) { @@ -1615,8 +1621,11 @@ 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 if (bl >= 0) { lvk = LV_ELEM; lvi = bl; } + if (abase >= 0) { + lvk = LV_ELEM; lvi = abase; + if (!achain) { xpart = -1; xparts = n; if (ic) { if (offset >= 0 && (n < 0 || offset < n)) xpart = offset; else xpart = 99; } } + } + else if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER; c = C_PLAIN; xfloat = 0; if (isfl(xty)) xfloat = 1; xstr = 0; @@ -1739,6 +1748,7 @@ int unary() { 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; mark_inited(lvi); } + else if (lv && lvk == LV_ELEM && lvi >= 0 && larr[lvi]) mark_inited(lvi); // the pointer reaches every element 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; @@ -1960,6 +1970,7 @@ int expr() { if (tv == s_eq) { if (!lv) die("this can't be assigned to"); k = lvk; i = lvi; lt = xty; require_mutable(lt); + int wpart = xpart, wparts = xparts; if (isagg(lt)) serr("a whole struct or array can't be assigned; assign its fields"); next(); a = o3on && k == LV_LOCAL && (ltag[i] & 16) && !larr[i]; @@ -1989,6 +2000,7 @@ int expr() { if (o3on && k != LV_ELEM && hasptr(lt)) { lit(lt); callname(s_fatten); } // o3 data going somewhere normal code can see storekeep(lt); } + if (k == LV_ELEM && i >= 0 && larr[i]) mark_part(i, wpart, wparts); lv = 0; lvk = LV_NONE; xty = lt; xzero = 0; return assigncheck(k, i, rc); } @@ -1999,7 +2011,7 @@ int expr() { 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); + else if (lvk == LV_ELEM && lvi >= 0) { if (larr[lvi]) need_part(lvi, xpart); else need_inited(lvi); } lt = xty; require_mutable(lt); fk = -1; lf = xfloat; if (lvk == LV_LOCAL) fk = lvi; diff --git a/src/lib/std.zc b/src/lib/std.zc index 5c86aa6..76c89f7 100644 --- a/src/lib/std.zc +++ b/src/lib/std.zc @@ -116,7 +116,7 @@ struct Text { // dst: where the text goes; 21 words is always enough (20 characters and the 0) // returns: how many characters were written, not counting the 0 static int itoa(int n, char* dst) { - int b[24], i = 0, j = 0, neg = 0; + int b[24] = {}, i = 0, j = 0, neg = 0; if (!n) { dst[0] = '0'; dst[1] = 0; return 1; } if (n < 0) { neg = 1; n = 0 - n; } if (n < 0) { rt.copy_w(dst, "-9223372036854775808", 21); return 20; } |
