aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-15 22:28:06 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-15 22:28:06 +0330
commit5a4dc62a79af6135c7050e81a82dbfdfb0a7d4e0 (patch)
tree805283b2d30b2cf0f53af42e5a3fc119af5e839f /src
parentdefinite 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.zc35
-rw-r--r--src/compiler/base.zc4
-rw-r--r--src/compiler/expr.zc28
-rw-r--r--src/lib/std.zc2
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; }