aboutsummaryrefslogtreecommitdiff
path: root/src/compiler/base.zc
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-15 11:29:26 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-15 11:29:26 +0330
commita3f769ea02b5686084c5b3c2a382b9934b535050 (patch)
tree47caed7e1bc49dbb9ee7aecd76f88c2048963f21 /src/compiler/base.zc
parentmerge work/stdio: std.Io and one-level module completion (diff)
parentxref: baseline records the borrow globals (end_why, end_whom, arg_site, local... (diff)
merge work/borrow: borrow returns before definition, borrows clause, borrow messages and LSP diagnostics, mkgenome header mismatch values
Diffstat (limited to 'src/compiler/base.zc')
-rw-r--r--src/compiler/base.zc54
1 files changed, 45 insertions, 9 deletions
diff --git a/src/compiler/base.zc b/src/compiler/base.zc
index dc786b5..7e4b5bc 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; 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; 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.
@@ -415,6 +415,11 @@ int s_new_k, s_recycle_leaf;
// server streams diagnostics and drops a check whose source has been replaced
word chk_hook;
int cmode, chkabort, chkfuel; word dgbuf, dggrow; int dgcap, dgpos, dgn, dgopen, dgprev, dgmode;
+// related places of check-mode records, for the editor: DGREL_W words each, [the record's
+// offset in dgbuf, the packed place where the owner ended, the packed place of the owner's
+// declaration, how it ended (end_why), the owner's name]. reset by chk_begin.
+const int DGREL_W = 5;
+word dgrel; int dgreln, dgrelcap;
// expand a diagnostic in progress, keeping its output offset within the buffer.
int dg_expand() {
dgbuf = chk_resize(dgbuf, dgpos + 5 + rt.outn, dgcap * 2);
@@ -433,8 +438,22 @@ int dg_expand() {
int probe_off = -1, probe_has, probe_n, probe_cap, probe_static;
char** probe_name; word probe_kind, probe_state, probe_type, probe_decl, probe_mem;
char* curinc; int incdepth, dgdrop; int incplace; // how many includes deep the compile is; a record to throw away; the place of the outermost include
+// an open record's range becomes the packed place p (the use itself, not the token after it).
+int dg_place(int p) {
+ if (!cmode || !dgopen || incdepth > 0 || !p || (p & 0xFFFFFF) != tline) return 0;
+ dgbuf[dgpos + 1] = p & 0xFFFFFF; dgbuf[dgpos + 2] = ((p >> 40) & 0x1FFFFF) - 1; dgbuf[dgpos + 3] = (p >> 24) & 0xFFFF;
+ return 0;
+}
+int dg_related(int end_place, int decl_place, int why, char* owner) {
+ if (!cmode || !dgopen || dgdrop || incdepth > 0) return 0;
+ if (dgreln == dgrelcap) { int cap = dgrelcap * 2; if (!cap) cap = 16; dgrel = chk_resize(dgrel, dgreln * DGREL_W, cap * DGREL_W); dgrelcap = cap; }
+ word e = dgrel + dgreln * DGREL_W;
+ e[0] = dgpos; e[1] = end_place; e[2] = decl_place; e[3] = why; e[4] = owner; dgreln++;
+ return 0;
+}
int dg_close() {
if (!dgopen) return 0;
+ if (dgdrop && dgreln && dgrel[(dgreln - 1) * DGREL_W] == dgpos) dgreln--; // its record is thrown away
if (!dgdrop) { dgbuf[dgpos + 4] = rt.outn; dgpos = dgpos + 5 + rt.outn; dgn++; }
dgdrop = 0;
dgopen = 0; rt.outmode = dgmode; rt.outgrow = dggrow;
@@ -470,7 +489,7 @@ word[256] dgseen_initial; word dgseen = dgseen_initial; int dgseen_cap = 256, dg
// start and stop a check run. chk_begin points the diagnostics at a buffer
// the caller owns; chk_end flushes any open record and restores output.
int chk_begin(int buf, int cap) {
- cmode = 1; chkabort = 0; chkfuel = 0; dgbuf = buf; dgcap = cap; dgpos = 0; dgn = 0; dgopen = 0;
+ cmode = 1; chkabort = 0; chkfuel = 0; dgbuf = buf; dgcap = cap; dgpos = 0; dgn = 0; dgopen = 0; dgreln = 0;
nerr = 0; panic = 0; dgmode = rt.outmode; rt.fill_w(dgseen, 0, dgseen_cap); dgseen_n = 0;
return 0;
}
@@ -758,6 +777,7 @@ int reserve_locals(int need) {
if (local_captured) local_captured = chk_resize(local_captured, nloc, cap);
if (local_ended) local_ended = chk_resize(local_ended, nloc, cap);
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);
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);
@@ -958,6 +978,27 @@ int cerr(char* msg) {
if (fchk && live) serr(msg);
return 0;
}
+// how an owner ended, from end_owner's record: the use, the owner, what ended it and where.
+// "r: used after it was given away; r borrows t, and t was freed on line 5 (t declared on line 4)"
+int ended_message(char* use, word info, char* root, int cause) {
+ char* owner = root; int why = 0; char* whom = 0;
+ if (info) { owner = info[0]; why = info[3]; whom = info[4]; dg_related(info[5], info[1], why, owner); }
+ rt.put_s(use); rt.put_s(": used after it was given away; ");
+ if (!rt.eq_w(use, owner)) { rt.put_s(use); rt.put_s(" borrows "); rt.put_s(owner); rt.put_s(", and "); }
+ rt.put_s(owner);
+ if (why == 1) rt.put_s(" was freed");
+ else if (why == 2) { rt.put_s(" was given to "); rt.put_s(whom); rt.put_s("'s .d parameter"); }
+ else if (why == 3) { rt.put_s(" was moved into "); rt.put_s(whom); }
+ else if (why == 4) { rt.put_s(" was stored into "); rt.put_s(whom); }
+ else if (why == 5) rt.put_s(" was returned");
+ else if (why == 6) rt.put_s(" was stored in a global or untracked memory");
+ else if (why == 7) rt.put_s(" was freed by reassigning it");
+ else if (why == 8) rt.put_s(" was pushed into an array");
+ else rt.put_s(" was transferred");
+ rt.put_s(" on line "); rt.put_n(cause & 0xFFFFFF);
+ if (info && info[1]) { rt.put_s(" ("); if (!rt.eq_w(use, owner)) { rt.put_s(owner); rt.put_c(' '); } rt.put_s("declared on line "); rt.put_n(info[1] & 0xFFFFFF); rt.put_c(')'); }
+ return 0;
+}
// report an analyzer error about local i, named in the message.
int cerrn(int i, char* msg) {
int root = i, cause = 0; word info = 0;
@@ -967,13 +1008,8 @@ int cerrn(int i, char* msg) {
if (!info && local_end_info) info = local_end_info[root];
if (info) cause = info[2]; else if (local_ended) cause = local_ended[root];
}
- if (cause && !panic && !dry) {
- errat(); rt.put_s(lname[i]); rt.put_s(": "); rt.put_s(msg);
- rt.put_s("; owner "); if (info) rt.put_s(info[0]); else rt.put_s(lname[root]);
- rt.put_s(" was transferred on line "); rt.put_n(cause & 0xFFFFFF);
- if (info && info[1]) { rt.put_s(" (declared on line "); rt.put_n(info[1] & 0xFFFFFF); rt.put_c(')'); }
- errend();
- } else serrn(lname[i], msg);
+ if (cause && !panic && !dry) { errat(); dg_place(lreadpos[i]); ended_message(lname[i], info, lname[root], cause); errend(); }
+ else serrn(lname[i], msg);
}
return 0;
}