diff options
| author | 2026-10-15 21:44:40 +0330 | |
|---|---|---|
| committer | 2026-10-15 21:44:40 +0330 | |
| commit | f3dcfd9805e1bd23b002f648d4391ec397122d9c (patch) | |
| tree | 91acab7da09c9225a2d838791d70e5c2e7db54b9 /src/compiler | |
| parent | merge work/small-oob: bytes test out-of-bounds read (diff) | |
| parent | examples/actors_demo: print the fan-out replies in worker order, not arrival ... (diff) | |
merge work/known-bugs: constant-expression precedence, local const in constant expressions, duplicate case labels, unterminated strings, droppable cycles in one collection, js vm exit, docs, actors_demo orderHEADmain
Diffstat (limited to 'src/compiler')
| -rw-r--r-- | src/compiler/base.zc | 3 | ||||
| -rw-r--r-- | src/compiler/expr.zc | 68 | ||||
| -rw-r--r-- | src/compiler/lex.zc | 6 | ||||
| -rw-r--r-- | src/compiler/stmt.zc | 14 |
4 files changed, 72 insertions, 19 deletions
diff --git a/src/compiler/base.zc b/src/compiler/base.zc index a209f2b..3135b39 100644 --- a/src/compiler/base.zc +++ b/src/compiler/base.zc @@ -400,7 +400,7 @@ int nerr, panic; 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] 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] lkval_initial; word lkval = lkval_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 // storage facts are shared by aliases. they describe one allocation, not a // variable slot: [length, lifetime, kind, allocation place, end place, owner]. @@ -772,6 +772,7 @@ int reserve_locals(int need) { lreadpos = chk_resize(lreadpos, nloc, cap); lnsz = chk_resize(lnsz, nloc, cap); lleaf = chk_resize(lleaf, nloc, cap); + lkval = chk_resize(lkval, nloc, cap); lrsoff = chk_resize(lrsoff, nloc, cap); lrskind = chk_resize(lrskind, nloc, cap); lptr = chk_resize(lptr, nloc, cap); diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc index 2935a9b..e437d2f 100644 --- a/src/compiler/expr.zc +++ b/src/compiler/expr.zc @@ -1072,6 +1072,7 @@ int primary() { if (isfl(ltype[i])) xfloat = 1; xstr = getstr(i); if (xty == tbstr) xstr = 1; lv = 1; lvk = LV_LOCAL; lvi = i; + if ((ltag[i] & LCONST) && treadonly(xty)) { xconst = 1; xcval = lkval[i]; } // a local const's value is known (spec 3.14.2) if (isalias(i)) { touch(i); xbor = lroot(i); return C_VAR + i; } if (tracked(i) || lrskind[i] == RS_PARAM || (local_origin_mask && local_origin_mask[i])) return C_VAR + i; return C_PLAIN; @@ -1762,17 +1763,31 @@ int unary() { // level tighter and emit its code sequence from the operator table. && and || // short-circuit with jumps; floats get their own opcodes; a * or / by a // constant that is a power of two gets a shift hint in check mode. +// fold a binary operator on two known integers; *ok is set to 0 when it can't be folded +int cfold(int o, int a, int b, word ok) { + if (o == bname[0]) return a * b; + if (o == bname[1] || o == bname[2]) { if (b == 0) { ok[0] = 0; return 0; } if (o == bname[1]) return a / b; return a % b; } + if (o == bname[3]) return a + b; + if (o == bname[4]) return a - b; + if (o == bname[5]) return a << b; + if (o == bname[6]) return a >> b; + if (o == bname[13]) return a & b; + if (o == bname[14]) return a ^ b; + if (o == bname[15]) return a | b; + ok[0] = 0; return 0; +} int if_comparison(int name) { return name == s_eqeq || name == s_ne || name == s_lt || name == s_gt || name == s_le || name == s_ge; } int bin(int minp) { - int c = unary(), b, p, a, j, lt, lb, rb, src, lf, rf, ls, rs, fresh, top = 0, il = -1, opp, lu, ru, xu; word borrowed, rightborrow, left_mask, right_mask, infer_left, infer_right; + int lk = 0, lkv = 0, c = unary(), b, p, a, j, lt, lb, rb, src, lf, rf, ls, rs, fresh, top = 0, il = -1, opp, lu, ru, xu; word borrowed, rightborrow, left_mask, right_mask, infer_left, infer_right; if (fortest == 1) { fortest = 2; top = 1; ft_nops = 0; } while (tk == T_OP) { b = binfind(tv); if (b < 0) return c; p = bprec[b]; if (p < minp) return c; + lk = 0; lb = -1; if (c >= C_VAR && tracked(c - C_VAR)) lb = lroot(c - C_VAR); else lb = xbor; borrowed = xaf; left_mask = effect_mask(c); lf = xfloat; ls = xstr; fresh = 0; @@ -1807,6 +1822,7 @@ int bin(int minp) { setlit(j, here); xfloat = 0; xstr = 0; } else { + lk = xconst && !lf && !ls; lkv = xcval; c = bin(p + 1); infer_right = if_value; rightborrow = xaf; right_mask = effect_mask(c); rb = -1; if (c >= C_VAR && tracked(c - C_VAR)) rb = lroot(c - C_VAR); else rb = xbor; @@ -1818,6 +1834,7 @@ int bin(int minp) { shifthint(opp, xcval); } rf = xfloat; rs = xstr; + if (lk && xconst && !rf && !rs && !isfl(xty)) { lk = 2; lkv = cfold(bname[b], lkv, xcval, &lk); } if (isfl(xty)) rf = 1; if ((ls || rs) && (bname[b] == s_plus || bname[b] == s_eqeq || bname[b] == s_ne || bname[b] == s_lt || bname[b] == s_gt || bname[b] == s_le || bname[b] == s_ge)) { if (!(ls && rs)) serr("a byte string needs a byte string on the other side"); @@ -1856,6 +1873,7 @@ int bin(int minp) { if (xu > 0x40000000) xu = 0 - 1; xub = xu; xubat = here; lv = 0; lvk = LV_NONE; c = C_PLAIN; xzero = 0; xconst = 0; xlenloc = -1; xstamp = 0; xaf = 0; xvk = 0; + if (lk == 2 && blen[b] > 0) { xconst = 1; xcval = lkv; } // both sides constant: the value is known effect_value = 0; if (isptr(xty) || xty == tword) effect_value = left_mask | right_mask; if (fresh) c = C_FRESH; if (borrowed && borrowed[2] == 5 && (isptr(xty) || xty == tword) && (bname[b] == s_plus || bname[b] == s_minus)) xaf = borrowed; @@ -2032,7 +2050,7 @@ int blockexpr() { // constant expressions, evaluated while compiling: numbers, named constants, // addresses of globals and functions, unary minus and ~, parentheses, and the -// operators + - * | & << >>. used for array lengths, enum values, const +// operators * / % + - << >> & ^ | at c precedence. used for array lengths, enum values, const // initializers and asm operands. word cprim() { int v, s, mc; @@ -2040,6 +2058,10 @@ word cprim() { if (accept(s_minus)) return 0 - cprim(); if (tk == T_OP && tv == uname[2]) { next(); return ~cprim(); } if (accept(s_lp)) { v = cexpr(); expect(s_rp); return v; } + if (tk == T_ID && nloc > 0) { // a local const with a constant initializer (spec 3.14.2) + s = findloc(tv); + if (s >= 0 && (ltag[s] & LCONST) && !larr[s] && ltype[s] && treadonly(ltype[s])) { next(); return lkval[s]; } + } if (tk == T_ID) { s = lookup(tv); if (mstruct && !ginst) { mc = lookup(methname(mstruct, tv)); if (mc >= 0 && skind[mc] == K_CONST) s = mc; } @@ -2065,20 +2087,38 @@ word cprim() { die("expected a constant"); return 0; } -// compile an expression: a primary, then the operators that follow it. -int cexpr() { - int v = cprim(), o; +// a constant expression with c precedence: * / % above + -, above << >>, +// above &, above ^, above | (spec 3.14). binary operators by precedence +// climbing, each level reading its right side one level tighter. +int cprec(int o) { + if (o == bname[0] || o == bname[1] || o == bname[2]) return 10; + if (o == bname[3] || o == bname[4]) return 9; + if (o == bname[5] || o == bname[6]) return 8; + if (o == bname[13]) return 5; + if (o == bname[14]) return 4; + if (o == bname[15]) return 3; + return 0; +} +int cbin(int minp) { + int v = cprim(), o, p, w; while (tk == T_OP) { - o = tv; - if (o == bname[3]) { next(); v = v + cprim(); } - else if (o == bname[4]) { next(); v = v - cprim(); } - else if (o == bname[0]) { next(); v = v * cprim(); } - else if (o == bname[15]) { next(); v = v | cprim(); } - else if (o == bname[13]) { next(); v = v & cprim(); } - else if (o == bname[5]) { next(); v = v << cprim(); } - else if (o == bname[6]) { next(); v = v >> cprim(); } - else return v; + o = tv; p = cprec(o); + if (p == 0 || p < minp) return v; + next(); w = cbin(p + 1); + if (o == bname[0]) v = v * w; + else if (o == bname[1] || o == bname[2]) { + if (w == 0) { die("division by zero in a constant expression"); return 0; } + if (o == bname[1]) v = v / w; else v = v % w; + } + else if (o == bname[3]) v = v + w; + else if (o == bname[4]) v = v - w; + else if (o == bname[5]) v = v << w; + else if (o == bname[6]) v = v >> w; + else if (o == bname[13]) v = v & w; + else if (o == bname[14]) v = v ^ w; + else v = v | w; } return v; } +int cexpr() { return cbin(1); } diff --git a/src/compiler/lex.zc b/src/compiler/lex.zc index 712eff8..95415a6 100644 --- a/src/compiler/lex.zc +++ b/src/compiler/lex.zc @@ -533,8 +533,9 @@ int lex1() { while (isidc(ch)) { n = token_char(n, ch); nextch(); } tbuf[n] = 0; if (n == 1 && tbuf[0] == 'b' && ch == 34) { - n = 0; nextch(); + n = 0; dg = line; nextch(); while (ch >= 0 && ch != 34) { v = escch(); n = token_char(n, v); } + if (ch < 0) { tline = dg; serr("unterminated string literal"); } // spec 1.4.4 nextch(); tbuf[n] = 0; tk = T_BSTR; tv = intern(tbuf); return 0; @@ -556,8 +557,9 @@ int lex1() { return 0; } if (ch == 34) { - nextch(); + dg = line; nextch(); while (ch >= 0 && ch != 34) { v = escch(); n = token_char(n, v); } + if (ch < 0) { tline = dg; serr("unterminated string literal"); } // spec 1.4.4 nextch(); tbuf[n] = 0; tk = T_STR; tv = intern(tbuf); return 0; diff --git a/src/compiler/stmt.zc b/src/compiler/stmt.zc index 007306c..79e5fb8 100644 --- a/src/compiler/stmt.zc +++ b/src/compiler/stmt.zc @@ -144,7 +144,7 @@ int localdecl(int typed) { newsz = -1; newleaf = 0; if (tk == T_NUM) nomagic = 1; if (t && t[0] == TY_DYN && is_op(s_lb)) c = array_dynamic_literal(t); else c = expr(); rval(); nomagic = 0; kc = xconst && !xfloat; - if (kc) ltag[i] = ltag[i] | LCONST; + if (kc) { ltag[i] = ltag[i] | LCONST; lkval[i] = xcval; } if (c == C_FRESH) { lnsz[i] = newsz; lleaf[i] = newleaf; if (drop_tail == here) local_dropset[i] = 1; } if (t) fcoerce(t); vaddr(i); @@ -566,11 +566,19 @@ int casebody(int s0, int live0) { live = 0; return 0; } +// a case label seen twice in one switch is an error (spec 4.7.2): the second +// arm could never run. the first 256 labels of a switch are checked. +int case_seen(word seen, int n, int v) { + int i = 0; + while (i < n) { if (seen[i] == v) { serr("duplicate case label"); return n; } i++; } + if (n < 256) { seen[n] = v; n++; } + return n; +} // compile a switch statement. int kw_switch() { int sl = tline; int save = nloc, tmp, c, s0, live0, defent = -1, skip, nxt, bj, v, a = -1, st, tg = 0, cov = 0, gi, nb = 0, j, arm, bi; - word[8] pbn; word[8] pbl; + word[8] pbn; word[8] pbl; word[256] seen; int nseen = 0; stmtonly(); next(); expect(s_lp); c = expr(); st = xty; rval(); plainuse(c); expect(s_rp); @@ -609,6 +617,7 @@ int kw_switch() { expect(s_rp); if (nb && nb != pm_nf[gi]) serr("a case binds every field of its member"); // member() with no names matches by tag (spec 4.7.3) v = pm_tag[gi]; + nseen = case_seen(seen, nseen, v); if (v < 63) cov = cov | (1 << v); laddr(lslot[tmp]); op(O_LD); op(O_LD); lit(v); op(O_SUB); bj = fixnew(hole(), bj); op(O_JZ); @@ -633,6 +642,7 @@ int kw_switch() { } else { while (1) { v = cexpr(); + nseen = case_seen(seen, nseen, v); if (tg && v >= 0 && v < 63) cov = cov | (1 << v); laddr(lslot[tmp]); op(O_LD); if (tg) op(O_LD); |
