diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/compiler/analyzer.zc | 35 | ||||
| -rw-r--r-- | src/compiler/base.zc | 1 | ||||
| -rw-r--r-- | src/compiler/expr.zc | 11 |
3 files changed, 43 insertions, 4 deletions
diff --git a/src/compiler/analyzer.zc b/src/compiler/analyzer.zc index 34c0a8b..666a89d 100644 --- a/src/compiler/analyzer.zc +++ b/src/compiler/analyzer.zc @@ -987,12 +987,12 @@ int local_hints(int from) { if (!cmode || dry || panic || chkabort || stubgen) return 0; while (i < nloc) { f = ltag[i]; - if (lrskind[i] == RS_PARAM && lname[i] != s_this && !rt.eq_w(lname[i], "_") && !(f & LREAD)) { + if (lrskind[i] == RS_PARAM && lname[i] != s_this && !rt.eq_w(lname[i], "_") && !(f & (LREAD | LWASREAD))) { if (hint_begin(lpos[i], HINT_SMALL)) { rt.put_s("unused parameter `"); rt.put_s(lname[i]); rt.put_s("`"); dg_close(); } } else if ((f & LDECL) && !larr[i] && !rt.eq_w(lname[i], "_")) { // _ discards its value - if (!(f & LREAD)) hint_name(i, " is never read", HINT_SMALL); + if (!(f & (LREAD | LWASREAD))) hint_name(i, " is never read", HINT_SMALL); else if ((f & LCONST) && !(f & LASG) && !(ltype[i] && treadonly(ltype[i]))) hint_name(i, " is never reassigned; make it a const", HINT_ALL); else if (fchk && lstate[i] == S_M && lown[i] && lnsz[i] > 0 && lnsz[i] <= 64) hint_name(i, " never leaves this function; a stack buffer (let name[n]) avoids the heap", HINT_SMALL); else if (fchk && lstate[i] == S_M && lown[i] && lused[i] && tline - lused[i] >= 4) hint_name(i, " is last used several lines above the end of its block; close the block sooner so it is freed sooner", HINT_ALL); @@ -1347,6 +1347,36 @@ int emitown(int p) { if (o3on && hasptr(xty) && !xstamp) { lit(xty); callname(s_fatten); } return 0; } +// reassigning local i while it still owns a value (6.8.5): the right side is +// already on the stack, so free the old value here, running its drop, before the +// store; then assigncheck sees an empty owner. only an m owner that a closure +// doesn't capture is freed: a u or d object still has to leave. a new value that +// comes from the old one (a child, a field, a borrow of it) would be freed with it. +int asg_quiet; +int reassign_free(int i, int rc, int bor) { + word t = xty, af = xaf; int st = xstr, fl = xfloat, j = 0 - 1; + if (!fchk || lstate[i] != S_M || isalias(i) || (local_captured && local_captured[i])) return 0; + if (rc >= C_VAR && tracked(rc - C_VAR)) j = rc - C_VAR; + if ((j >= 0 && lroot(j) == i) || (bor >= 0 && tracked(bor) && lroot(bor) == i)) { + cerrn(i, "reassigning frees the old value, and the new value is part of it"); + asg_quiet = 1; // assigncheck says nothing more about this store + return 0; + } + if (hint_begin(tpack(), HINT_SMALL)) { rt.put_s(lname[i]); rt.put_s(": reassigning frees the old value"); dg_close(); } + ev_put(i, EV_FREE, -1); + if (o3on && ldroppable(i)) cerrn(i, "has drop and can't end inside an o3 region"); + // freevar without its clearing store: the new value is stored next, through + // the address taken before the right side (a captured local's slot holds its cell) + vaddr(i); op(O_LD); + if (lleaf[i] && !o3on && !dry && lookup(s_recycle_leaf) >= 0) { lit(lleaf[i]); lit(szclass(lnsz[i])); callname(s_recycle_leaf); } + else callname(s_recycle); + op(O_DROP); + end_owner(i); + if (local_dropset) local_dropset[i] = 0; + lnsz[i] = 0; lleaf[i] = 0; + xty = t; xaf = af; xstr = st; xfloat = fl; xbor = bor; + return 1; +} // a store of class rc into an lvalue of kind k (local or container index i) int assigncheck(int k, int i, int rc) { int st, j, r, js, bor, origin = effect_origin(rc); @@ -1367,6 +1397,7 @@ int assigncheck(int k, int i, int rc) { if (k == LV_LOCAL) { st = lstate[i]; if (isalias(i)) st = S_PLAIN; // a borrow isn't an owner; overwriting it drops the alias + if (asg_quiet) { asg_quiet = 0; return C_PLAIN; } // reassign_free reported it if (j < 0 && rc != C_FRESH) { if (!empty(st)) cerrn(i, "still owns an object; overwriting it would leak it"); else if (bor >= 0 && bor != i && (tracked(bor) || larr[bor])) { lstate[i] = S_CHILD; lowner[i] = bor; lown[i] = 0; } diff --git a/src/compiler/base.zc b/src/compiler/base.zc index 99ee10f..a8dc19f 100644 --- a/src/compiler/base.zc +++ b/src/compiler/base.zc @@ -391,6 +391,7 @@ int nerr, panic; // read, declared with let, initialized with a literal; the size of the object // a new gave it (bits 16 to 39); and how many times it was used (from bit 56) const int LASG = 0x1000, LREAD = 0x2000, LDECL = 0x4000, LCONST = 0x8000, LCNT1 = 0x100000000000000; +const int LWASREAD = 0x10000; // an earlier value was read: a fresh value assigned over it clears LREAD, so its drop can still be set (6.15.1) const int NEWTYPED = 0x800000; word[NL] lpos_initial; word lpos = lpos_initial; word[NL] lreadpos_initial; word lreadpos = lreadpos_initial; word[NL] lnsz_initial; word lnsz = lnsz_initial; word[NL] lleaf_initial; word lleaf = lleaf_initial; word[NL] lrsoff_initial; word lrsoff = lrsoff_initial; word[NL] lrskind_initial; word lrskind = lrskind_initial; int newsz, newleaf, nomagic, callpos; word[NL] lptr_initial; word lptr = lptr_initial; // annotate mode: the local was indexed or dereferenced, so it holds an address diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc index e6acfc8..7687394 100644 --- a/src/compiler/expr.zc +++ b/src/compiler/expr.zc @@ -1868,8 +1868,8 @@ int expr() { next(); a = o3on && k == LV_LOCAL && (ltag[i] & 16) && !larr[i]; if (a) op(O_DROP); // an o3 variable: its address comes after the value, so it never waits on the stack across a branch - xlend = -1; - rc = expr(); rval(); + xlend = -1; newsz = -1; newleaf = 0; + rc = expr(); rval(); fk = drop_tail == here; if (k != LV_LOCAL) af_escape(xaf); fcoerce(lt); if (k == LV_LOCAL) { @@ -1881,6 +1881,13 @@ int expr() { if (cmode && !dry && k == LV_GLOBAL) { vf_symbols(); svkind[i] = xvk; svvalue[i] = xvv; } else if (k != LV_LOCAL) vf_effect(); if (!compat(lt, xty)) tcerr("assignment", lt, xty); + if (k == LV_LOCAL) { + reassign_free(i, rc, xbor); + if (rc == C_FRESH && fchk) { // the new value's size and drop, as a declaration records them + lnsz[i] = newsz; lleaf[i] = newleaf; if (local_dropset) local_dropset[i] = fk; + if (ltag[i] & LREAD) ltag[i] = (ltag[i] - LREAD) | LWASREAD; + } + } if (a) { lit(0); op(O_PICK); vaddr(i); op(O_ST); o3refresh(i); } else { if (o3on && k != LV_ELEM && hasptr(lt)) { lit(lt); callname(s_fatten); } // o3 data going somewhere normal code can see |
