diff options
Diffstat (limited to 'src/compiler')
| -rw-r--r-- | src/compiler/analyzer.zc | 20 | ||||
| -rw-r--r-- | src/compiler/base.zc | 12 | ||||
| -rw-r--r-- | src/compiler/builtins.zc | 1 | ||||
| -rw-r--r-- | src/compiler/check.zc | 4 | ||||
| -rw-r--r-- | src/compiler/decl.zc | 34 | ||||
| -rw-r--r-- | src/compiler/expr.zc | 75 | ||||
| -rw-r--r-- | src/compiler/setup.zc | 3 | ||||
| -rw-r--r-- | src/compiler/stmt.zc | 15 | ||||
| -rw-r--r-- | src/compiler/types.zc | 9 |
9 files changed, 137 insertions, 36 deletions
diff --git a/src/compiler/analyzer.zc b/src/compiler/analyzer.zc index 8c63bdf..89713ff 100644 --- a/src/compiler/analyzer.zc +++ b/src/compiler/analyzer.zc @@ -500,6 +500,26 @@ int proven(int a, int ix) { } return 0; } +// i >= 0 and i < n for a fixed array of n elements, proved by a loop test +// against a constant of at most n and a step of +1 (kw_for records the +// constant as the array field, minus two, with -1 for the bound local) +int proven_n(int ix, int n) { + int k = lsp, w; + while (k > loopbase) { + k--; + w = lpfact[k]; + if (w >= 0 && p_iv(w) == ix && p_bd(w) == -1 && 0 - p_ar(w) - 2 <= n) return 1; + } + return 0; +} +// is the constant index xcval known to be inside the allocation f? the same +// facts af_index warns from, so a possible failure is never called definite. +int af_inrange(word f) { + int lower = 0; word base; + if (!f || !xconst || f[1] == 2 || f[0] < 0) return 0; + if (f[6]) { base = af_base(f); if (base[1] == 0 || base[10] < 0) return 0; if (f[14]) lower = f[13]; } + return xcval >= lower && xcval < f[0]; +} // follow child links up to the local that really owns the object int lroot(int i) { while (lstate[i] == S_CHILD) i = lowner[i]; diff --git a/src/compiler/base.zc b/src/compiler/base.zc index 2f802b9..8c13201 100644 --- a/src/compiler/base.zc +++ b/src/compiler/base.zc @@ -1,6 +1,9 @@ namespace run; include rt; +// the compiler pokes at headers and raw memory on purpose and runs at full speed: no index bounds checks +unbounded; + // ============================================================================ // compiler.zc: the resident zc compiler. it lives in the genome next to the // runtime, compiles source straight to vm words in memory, and is visible to @@ -213,6 +216,7 @@ char* module_name; word module_imports; int nbin, nun, nasg, nkop, seqtop; char* mnem = "q@!$_~cjzr,?.+-*&|^><%"; int check_on = 1; // set to 0 to switch the analyzer off for what follows +int bound_on = 1; // set to 0 by unbounded; to switch index bounds checks off for what follows int tk; word tv; int tglue, tline, line = 1, ch, chkind; word chptr, chbase; int qdone, isp = -1, pendot; // where the current token sits in its source (chars from the start, -1 for a token list), how long it is, and where the one before it ended int toff = -1, toklen, tpend = -1, tpline; @@ -240,7 +244,7 @@ int pk_tk, pk_tv, pq_n; word[4] pq_tk; word[4] pq_tv; word[4] pq_gl; word[4] pq_ int here, hdelta, hlimit, ptop; int code_chunk = rt.CODE_END; word image_buffer, image_words; int image_active, image_org, image_capacity, image_size; -int nloc, lbase, fdepth, fslots, fchk, fret, live, bxdepth, epichain, fframe, infn; +int nloc, lbase, fdepth, fslots, fchk, fbnd, bnxt, fret, live, bxdepth, epichain, fframe, infn; // a global that borrows a local owner through g = m*x. each entry is the global's // symbol, the local, and the block depth of the store; the lend ends when the same // global is assigned again in that block, and is an error if the block can end first. @@ -271,7 +275,7 @@ int lv, lvk, lvi, kw_expr, xcls, fty; int s_lp, s_rp, s_lb, s_rb, s_lk, s_rk, s_comma, s_semi, s_eq, s_arrow, s_inc, s_dec; int s_star, s_slash, s_amp, s_colon, s_hash, s_minus, s_andand, s_oror; -int s_dot, s_fn, s_var, s_const, s_else, s_as, s_unchecked, s_immediate, s_m, s_d, s_u; +int s_dot, s_fn, s_var, s_const, s_else, s_as, s_unchecked, s_unbounded, s_immediate, s_m, s_d, s_u; int s_q, s_case, s_default, s_fovf, s_start, s_main, s_free, s_recycle, s_own, s_qnew, s_enhance, s_micro, s_vm; int s_frame_floor, s_frame_top, s_frame_node, s_frame_busy, s_frame_guard, s_frame_grow, s_frame_leave; int s_copy_w; @@ -310,7 +314,7 @@ word readonly_types; int dty, dtag; // result of dtype() int s_typedef; int s_lt, s_gt, s_shr, s_le, s_ge, s_eqeq, s_ne, s_plus, s_struct, s_type, s_typecheck, s_docs, s_lines, s_small, s_std, s_icon, s_on, s_off, s_new, s_new_t; -int s_o3, s_lfa, s_outside, s_new_raw, s_new_oz, s_oob; +int s_o3, s_lfa, s_outside, s_new_raw, s_new_oz, s_oob, s_bchk, s_bnd; int s_ld8, s_st8, s_ld16, s_st16, s_ld32, s_st32; // ---- o3 region state ---- @@ -320,7 +324,7 @@ int o3on, o3nxt, o3one; word[32] o3sl; word[32] o3rg; int no3sl; word[O3_REGIONS word[8] o3free; int no3free, o3nreg; word[NL] o3cache_initial; word o3cache = o3cache_initial; int argloc, s_hitems, s_hlen, s_fatten; // operator names used by numeric type checks. word[12] numops; int nnumops, s_fixed, s_dlen, s_dcap, s_dnew, s_dat, s_dpush, s_dpop, s_dresize, xconst, xcval; -int xlenloc, arrloc, fortest, forstep, ft_iv, ft_bd, ft_ir, ft_nops, fs_ok, xstamp; +int xlenloc, arrloc, fortest, forstep, ft_iv, ft_bd, ft_ir, ft_ck, ft_nops, fs_ok, xstamp; // local lower bound / len(array); one proof record per live loop int nfact, factcap = RANGE_FACTS; word[RANGE_FACTS] floc_initial, flo_initial, flen_initial; word floc = floc_initial, flo = flo_initial, flen = flen_initial; int s_len_s, s_at_s, s_slice_s, s_cat_s, s_eq_s, s_lt_s, s_le_s, s_put_bs, s_put_s, s_n_to_s, s_new_s; diff --git a/src/compiler/builtins.zc b/src/compiler/builtins.zc index 5d7bc9c..dd01bd8 100644 --- a/src/compiler/builtins.zc +++ b/src/compiler/builtins.zc @@ -1190,6 +1190,7 @@ int types_init() { s_outside = intern("rt.outside"); s_new_raw = intern("rt.new_raw"); s_oob = intern("rt.oob"); + s_bchk = intern("rt.bchk"); s_bnd = intern("rt.bnd"); s_new_oz = intern("rt.new_oz"); s_ld8 = intern("rt.ld8"); s_st8 = intern("rt.st8"); s_ld16 = intern("rt.ld16"); s_st16 = intern("rt.st16"); s_ld32 = intern("rt.ld32"); s_st32 = intern("rt.st32"); diff --git a/src/compiler/check.zc b/src/compiler/check.zc index f3374b1..945b800 100644 --- a/src/compiler/check.zc +++ b/src/compiler/check.zc @@ -32,7 +32,7 @@ int chk_reset() { defer_drop(0); dfr_base = 0; dfr_on = 0; fdepth = 0; fslots = 0; - fchk = 0; + fchk = 0; fbnd = 0; bnxt = 0; fret = 0; live = 0; bxdepth = 0; @@ -41,7 +41,7 @@ int chk_reset() { xbor = 0 - 1; xaf = 0; argfact = 0; xvk = 0; if (svkind) rt.fill_w(svkind, 0, symcap); mdbg = 0; mdbg_flags = 0; lv = 0; lvk = 0; lvi = 0; kw_expr = 0; xcls = 0; fty = 0; - check_on = 1; tc_on = ifstrict; dry = 0; ntp = 0; tvauto = 0; ngbind = 0; xgive = 0; xconst = 0; + check_on = 1; bound_on = 1; tc_on = ifstrict; dry = 0; ntp = 0; tvauto = 0; ngbind = 0; xgive = 0; xconst = 0; o3on = 0; o3nxt = 0; no3pend = 0; no3sl = 0; no3free = 0; o3nreg = 0; o3na = 0; o3fn0 = 0; nfact = 0; fortest = 0; forstep = 0; argloc = 0; arrloc = 0; njps = 0; extop = 0; nnumops = 0; nenh = 0; panic = 0; nerr = 0; dgprev = 0; nclo = 0; newsz = 0 - 1; nomagic = 0; callpos = 0; diff --git a/src/compiler/decl.zc b/src/compiler/decl.zc index cc84603..36a307d 100644 --- a/src/compiler/decl.zc +++ b/src/compiler/decl.zc @@ -153,7 +153,7 @@ int unit_stdin() { // that a nested compile would otherwise clobber (locals, depth, frame size, // loop stack position, the expression-state globals). closures use the same // pair. -const int CTX_WORDS = 49; +const int CTX_WORDS = 50; int ctx_push() { af_forget(); word f; @@ -164,7 +164,7 @@ int ctx_push() { f[13] = lv; f[14] = lvk; f[15] = lvi; f[16] = kw_expr; f[17] = xcls; f[18] = snaptop; f[19] = fty; f[20] = ntp; f[21] = xty; rt.copy_w(f + 22, tpn, 8); f[30] = finsym; f[31] = finfer; f[32] = ifcur; f[33] = ifcall; f[34] = effect_current; f[35] = borrow_slots; f[36] = borrow_pending; f[37] = borrow_depth; f[38] = borrow_jump; f[39] = borrow_entry; f[40] = capture_enabled; f[41] = effect_value; f[42] = xprojection; f[43] = effect_floor; f[44] = module_name; f[45] = module_imports; - f[46] = fn_return_type; f[47] = if_value; f[48] = dfr_base; + f[46] = fn_return_type; f[47] = if_value; f[48] = dfr_base; f[49] = fbnd; return 0; } // pop the innermost function context. @@ -186,7 +186,7 @@ int ctx_pop() { lv = f[13]; lvk = f[14]; lvi = f[15]; kw_expr = f[16]; xcls = f[17]; snaptop = f[18]; fty = f[19]; ntp = f[20]; xty = f[21]; rt.copy_w(tpn, f + 22, 8); finsym = f[30]; finfer = f[31]; ifcur = f[32]; ifcall = f[33]; effect_current = f[34]; borrow_slots = f[35]; borrow_pending = f[36]; borrow_depth = f[37]; borrow_jump = f[38]; borrow_entry = f[39]; capture_enabled = f[40]; effect_value = f[41]; xprojection = f[42]; effect_floor = f[43]; module_name = f[44]; module_imports = f[45]; xvk = 0; - fn_return_type = f[46]; if_value = f[47]; + fn_return_type = f[46]; if_value = f[47]; fbnd = f[49]; return 0; } @@ -206,10 +206,12 @@ int fn_begin(int chk, int ret) { if (cmode && svkind && !dry) rt.fill_w(svkind, 0, nsym); xvk = 0; lend_drop(1 << 30, nloc); dfr_base = ndfr; - lbase = nloc; fdepth = 1; fslots = 0; fchk = chk && check_on; fret = ret; fty = 0; finfer = 0; fn_return_type = 0; + lbase = nloc; fdepth = 1; fslots = 0; fchk = chk && check_on; fbnd = !bnxt && bound_on; bnxt = 0; fret = ret; fty = 0; finfer = 0; fn_return_type = 0; live = 1; bxdepth = 0; epichain = 0; infn = 1; loopbase = lsp; lv = 0; lvk = LV_NONE; xbor = -1; return 0; } +// the bounds flag of the function that was being compiled when the innermost context was pushed +int ctx_bnd() { return R_CTX[(ctxsp - 1) * CTX_WORDS + 49]; } // allocate the frame, retaining the caller's pointer until any growth finishes. int prologue() { int k, busy, done, helper = !nclo && (sname[finsym] == s_frame_grow || sname[finsym] == s_frame_leave); @@ -570,7 +572,7 @@ int topsync() { if (is_op(s_lb)) depth++; else if (is_op(s_rb)) { depth--; if (depth < 0) depth = 0; } else if (!depth && tk == T_ID && (tv == s_fn || tv == s_var || tv == s_const || tv == s_struct || tv == s_type || tv == s_typedef - || tv == s_enhance || tv == s_typecheck || tv == s_docs || tv == s_lines || tv == s_unchecked || tv == s_immediate || tv == s_o3 + || tv == s_enhance || tv == s_typecheck || tv == s_docs || tv == s_lines || tv == s_unchecked || tv == s_unbounded || tv == s_immediate || tv == s_o3 || tv == s_memdebug || tv == s_enum || tv == s_include)) break; next(); } @@ -647,14 +649,20 @@ int topitem0() { int topitem1() { int chk = 1, imm = 0, s, k, n; if (panic) return topsync(); - if (is_id(s_unchecked)) { - next(); - if (accept(s_semi)) { check_on = 0; return 0; } // unchecked; switches the analyzer off from here on - chk = 0; + o3nxt = 0; bnxt = 0; + while (1) { // the prefixes, in any order + if (is_id(s_unchecked)) { + next(); + if (accept(s_semi)) { check_on = 0; return 0; } // unchecked; switches the analyzer off from here on + chk = 0; + } else if (is_id(s_unbounded)) { + next(); + if (accept(s_semi)) { bound_on = 0; return 0; } // unbounded; switches the index bounds checks off from here on + bnxt = 1; + } else if (is_id(s_immediate)) { next(); imm = 1; } + else if (is_id(s_o3)) { next(); o3nxt = 1; } + else break; } - if (is_id(s_immediate)) { next(); imm = 1; } - o3nxt = 0; - if (is_id(s_o3)) { next(); o3nxt = 1; } ntp = 0; ifkk = 0; if (is_id(s_fn)) { peek(); @@ -669,7 +677,7 @@ int topitem1() { return fndone(fndef(chk, 0, 0, n), imm); } } - if (!chk || imm || o3nxt) { // has to be a function, with a return type + if (!chk || imm || o3nxt || bnxt) { // has to be a function, with a return type ifkp = tpack(); ifkk = 0 - 1; if (is_op(s_dot)) ifkk = 2; if (isp != 0) ifkk = 0; rettype(); n = name(); if (ginst) n = ginsn; genparams(); diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc index f41109e..c76b74c 100644 --- a/src/compiler/expr.zc +++ b/src/compiler/expr.zc @@ -678,7 +678,7 @@ int closure() { nclo++; hop = hole(); op(O_JMP); entry = here; - fn_begin(1, 0); + fn_begin(1, 0); fbnd = ctx_bnd(); lt = lt_begin(entry, "<closure>", lfl); if (declared) fty = declared; prologue(); borrow_prologue(); @@ -1321,17 +1321,47 @@ int range_jz(int ix, int src, int fld) { setlit(k, here); return 0; } +// the inline check of a fixed array T[n]: the index is on top of the stack and +// stays there. one "outside" enhancement against the constant length and a +// branch to the oob report, the same shape as range_jz. +int fixed_chk(int n) { + int k = lookup(s_outside), j; + if (k < 0 || skind[k] != K_ENH) return 0; + lit(0); op(O_PICK); lit(n); op(XOP0 + sval[k]); + j = hole(); op(O_JZ); + lit(n); callname(s_oob); + setlit(j, here); + return 0; +} +// the check of a pointer index: base and index are on the stack and stay there. +// the runtime knows a size only for a pointer to the start of a live heap block +// and returns at once for any other pointer. sz is the element size in words, or +// minus the element size in bytes for packed elements. +int ptr_chk(int sz) { + int k = lookup(s_bnd), j; + if (k >= 0 && skind[k] == K_ENH) { // the test is one enhancement; the call only runs to report + lit(1); op(O_PICK); lit(1); op(O_PICK); + if (sz > 0) { lit(sz); lit(1); } else { lit(0 - sz); lit(8); } + op(XOP0 + sval[k]); + j = hole(); op(O_JZ); + } + lit(1); op(O_PICK); lit(1); op(O_PICK); lit(sz); callname(s_bchk); op(O_DROP); + if (k >= 0 && skind[k] == K_ENH) setlit(j, here); + return 0; +} // suffixes after a primary, in a loop: e as T, [index], .field, ->field, // (call through a value), ++ and --. the index case is the long one. a byte -// string takes an index or a slice. a growable array is checked: through dat() -// normally, but inline when the compiler can prove or cheaply test the bound -// (o3 code with the cache variables, or an index the loop test already -// compared with the length). a plain pointer or fixed array is just base + -// index * size, and a constant index against a known length is checked at -// compile time. +// string takes an index or a slice. every index is bounds checked unless the +// function, block or unit is unbounded or the code is o3. a growable array is +// checked through dat() normally, but inline when the compiler can prove or +// cheaply test the bound (o3 code with the cache variables, or an index the +// loop test already compared with the length). a fixed array T[n] is checked +// inline against n (at compile time for a constant index, not at all when a +// loop test against a constant already proved it). a plain pointer goes +// through rt.bchk, which only knows the size of a heap block's first word. int postfix() { return postfix_tail(primary()); } int postfix_tail(int c) { - int b, n, t, et; word ft; word[8] bs; int k, ci, was, al, ix, enh, cz, cj, place, offset, temporary, call_owner, readonly; word storage, storage_mask, call_origins; + int b, n, t, et; word ft; word[8] bs; int k, ci, was, al, ix, enh, cz, cj, place, offset, temporary, call_owner, readonly, ub, bl, ic, inr; word storage, storage_mask, call_origins; while (1) { if (tk == T_ID && tv == s_as) { // e as T: same bits, another type next(); t = ptype(); rval(); if (!xaf || xaf[2] != 5) xaf = 0; vf_coerce(t, 0); @@ -1350,6 +1380,8 @@ int postfix_tail(int c) { ci = 0; if (lv && lvk == LV_LOCAL) lptr[lvi] = 1; 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; next(); rval(); if (was) { @@ -1377,7 +1409,11 @@ int postfix_tail(int c) { if (xconst) offset = xcval; af_index(storage, place); tcneed(isnum(xty) || xty == tword, "an index has to be a number", xty); expect(s_rk); - callname(intern("datb")); + if (ub) { // the byte address of the element, with no check + op(O_SWAP); lit(2); op(O_ADD); op(O_LD); lit(8); op(15); op(O_SWAP); + if (tbytes(et) != 1) { lit(tbytes(et)); op(15); } + op(O_ADD); + } else callname(intern("datb")); et = tpk(et); } else if (xty[0] == TY_DYN) { // a growable array: bounds-checked, through the runtime et = xty[4]; @@ -1393,7 +1429,10 @@ int postfix_tail(int c) { expect(s_rk); k = lookup(s_outside); enh = k >= 0 && skind[k] == K_ENH; - if (!mdbg && ix >= 0 && al >= 0 && proven(al, ix)) { // the loop test already compared this index with the length + if (ub) { // unbounded, or o3: just the address + if (ci && !mdbg && lookup(s_outside) >= 0) { vaddr(ci); op(O_LD); op(O_SWAP); mulsz(et); op(O_ADD); } + else addr_stk(et); + } else if (!mdbg && ix >= 0 && al >= 0 && proven(al, ix)) { // the loop test already compared this index with the length if (ci) { vaddr(ci); op(O_LD); op(O_SWAP); mulsz(et); op(O_ADD); } else addr_stk(et); } else if (ci && !mdbg && enh && ix >= 0) { @@ -1433,13 +1472,26 @@ int postfix_tail(int c) { et = telem(xty); n = 0 - 1; if (xty[0] == TY_ARR && xty[3] > 0) n = xty[3]; xconst = 0; - c = expr(); rval(); plainuse(c); + c = expr(); + ix = -1; + if (lv && lvk == LV_LOCAL) ix = lvi; + rval(); plainuse(c); + ic = xconst; if (xconst) offset = xcval; if (n < 0) af_index(storage, place); if (n >= 0 && xconst && (xcval < 0 || xcval >= n)) { serr("the index is out of range for this array"); } + inr = 0; // no check needed: the index is 0, or the analyzer knows it is inside the allocation + if (ic && xcval == 0) inr = 1; + if (ic && n < 0 && !tbytes(et) && tsz(et) == 1 && af_inrange(storage)) inr = 1; + if (bl >= 0 && n < 0 && larr[bl]) inr = 1; // an array in a frame has no heap header to read tcneed(isnum(xty) || xty == tword, "an index has to be a number", xty); expect(s_rk); + if (!ub && !inr) { + if (n >= 0) { if (!ic && !(ix >= 0 && proven_n(ix, n))) fixed_chk(n); } + else if (tbytes(et)) ptr_chk(0 - tbytes(et)); + else ptr_chk(tsz(et)); + } if (tbytes(et)) { // packed: byte address = base * 8 + index * bytes if (tbytes(et) != 1) { lit(tbytes(et)); op(15); } op(O_SWAP); lit(8); op(15); op(O_ADD); @@ -1665,6 +1717,7 @@ int bin(int minp) { if (c >= C_VAR && tracked(c - C_VAR)) rb = lroot(c - C_VAR); else rb = xbor; if (top) { ft_ir = -1; if (lv && lvk == LV_LOCAL) ft_ir = lvi; } rval(); plainuse(c); + if (top) { ft_ck = -1; if (xconst && xcval >= 0) ft_ck = xcval; } if (cmode && bname[b] == s_star && xconst && xcval >= 2 && !(xcval & (xcval - 1)) && !lf) { shifthint(opp, xcval); } diff --git a/src/compiler/setup.zc b/src/compiler/setup.zc index 9ed938b..26a18e0 100644 --- a/src/compiler/setup.zc +++ b/src/compiler/setup.zc @@ -46,7 +46,7 @@ int zc_init() { s_andand = intern("&&"); s_oror = intern("||"); s_fn = intern("fn"); s_var = intern("let"); s_const = intern("const"); s_else = intern("else"); - s_as = intern("as"); s_unchecked = intern("unchecked"); s_immediate = intern("immediate"); + s_as = intern("as"); s_unchecked = intern("unchecked"); s_unbounded = intern("unbounded"); s_immediate = intern("immediate"); s_m = intern("m"); s_d = intern("d"); s_u = intern("u"); s_static = intern("static"); s_start = intern("run.start"); s_main = intern("main"); @@ -113,6 +113,7 @@ int zc_tables() { add_kw("defer", kw_defer, F_STMT); add_kw("return", kw_return, F_STMT); add_kw("unchecked", kw_unchecked, F_STMT); + add_kw("unbounded", kw_unbounded, F_STMT); add_kw("asm", kw_asm, F_STMT | F_EXPR); add_kw("quote", kw_quote, F_EXPR); add_kw("sizeof", kw_sizeof, F_EXPR); diff --git a/src/compiler/stmt.zc b/src/compiler/stmt.zc index 4dafe4f..505e466 100644 --- a/src/compiler/stmt.zc +++ b/src/compiler/stmt.zc @@ -323,7 +323,7 @@ int kw_for() { borrow_cleanup_since(borrow_mark); s0 = snap_push(); c0 = here; - ft_iv = -1; fs_ok = 0; fortest = 1; + ft_iv = -1; ft_ck = -1; fs_ok = 0; fortest = 1; if (!is_op(s_semi)) { if (tk == T_NUM && tv) { peek(); if (pk_tk == T_OP && pk_tv == s_semi) inf = 1; } c = borrow_condition(); @@ -344,6 +344,8 @@ int kw_for() { loop_push(st, s0); if (ft_iv >= 0 && ft_bd >= 0 && fs_ok && getlo(ft_iv) >= 0 && getln(ft_bd) >= 0) { lpfact[lsp - 1] = pfact(ft_iv, ft_bd, getln(ft_bd)); + } else if (ft_iv >= 0 && ft_ck >= 0 && fs_ok && getlo(ft_iv) >= 0) { // i < constant: proves a fixed array that long + lpfact[lsp - 1] = pfact(ft_iv, -1, 0 - ft_ck - 2); } substmt(); backedge(s0); @@ -724,6 +726,17 @@ int kw_unchecked() { return 0; } +// unbounded turns the index bounds checks off for one statement +int kw_unbounded() { + int old = fbnd; + stmtonly(); + next(); + fbnd = 0; + substmt(); + fbnd = old; + return 0; +} + // ---- asm blocks: raw vm words, one per token ---- // numbers push, -n pushes a negative number, #n emits a raw word, one-char diff --git a/src/compiler/types.zc b/src/compiler/types.zc index 6dda110..ca31248 100644 --- a/src/compiler/types.zc +++ b/src/compiler/types.zc @@ -567,7 +567,7 @@ int kw_struct() { if (tk == T_EOF) { die("missing }"); break; } stat = 0; if (struct_const_start()) { struct_const(n); continue; } - if (is_id(s_unchecked)) { next(); stat = 2; } + while (is_id(s_unchecked) || is_id(s_unbounded)) { if (is_id(s_unchecked)) stat = stat | 2; else stat = stat | 4; next(); } if (is_id(s_static)) { // static fn name(params) { body }, or static T name(params) { body }: a function of the struct, called as Struct.name(args), with no this next(); stat = stat | 1; @@ -746,7 +746,7 @@ int tkput(word r, int k, int kind, int val, int pl) { } // compile a struct method once the struct is complete. int methdef(char* sn, char* mn, word q, int stat, int typed, int rty, int rtag) { - int n = q[0]; word r; int i, k = 0, lnp, unchecked_method = stat & 2; + int n = q[0]; word r; int i, k = 0, lnp, unchecked_method = stat & 2, unbounded_method = stat & 4, pc = (stat & 2) / 2 + (stat & 4) / 4; stat = stat & 1; if (!n) { rt.free(q); return 0; } if (!stat) { @@ -759,6 +759,7 @@ int methdef(char* sn, char* mn, word q, int stat, int typed, int rty, int rtag) r = rt.new((n + 8 + mdg * 4) * 3 + 1); lnp = q[3]; if (unchecked_method) k = tkput(r, k, T_ID, s_unchecked, lnp); + if (unbounded_method) k = tkput(r, k, T_ID, s_unbounded, lnp); k = tkput(r, k, T_ID, s_fn, lnp); r[1 + k * 3] = T_ID; r[2 + k * 3] = methname(sn, mn); r[3 + k * 3] = lnp; k++; if (mdg) { // a method of a generic struct is a generic function over the struct's parameters: fn S.m<K, V>(S<K, V>* this, ...) @@ -806,7 +807,7 @@ int methdef(char* sn, char* mn, word q, int stat, int typed, int rty, int rtag) rt.free(q); if (typed && mdg) { // retain a typed generic method as ordinary typed function tokens. - int mode = rt.outmode, length = rt.outn, capacity = rt.outcap, prefix = unchecked_method != 0; + int mode = rt.outmode, length = rt.outn, capacity = rt.outcap, prefix = pc; word output = rt.outbuf, grow = rt.outgrow, buffer = rt.new(4096), return_tokens, joined; rt.outmode = 2; rt.outbuf = m*buffer; rt.outn = 0; rt.outcap = 4095; rt.outgrow = 0; tprint(rty); if (rtag) rt.put_s(".u"); buffer[rt.outn] = 0; @@ -824,7 +825,7 @@ int methdef(char* sn, char* mn, word q, int stat, int typed, int rty, int rtag) } joined = rt.new((r[0] + return_tokens[0]) * 3 + 1); joined[0] = r[0] - 1 + return_tokens[0]; - if (prefix) rt.copy_w(joined + 1, r + 1, 3); + if (prefix) rt.copy_w(joined + 1, r + 1, prefix * 3); rt.copy_w(joined + 1 + prefix * 3, return_tokens + 1, return_tokens[0] * 3); rt.copy_w(joined + 1 + (prefix + return_tokens[0]) * 3, r + 1 + (prefix + 1) * 3, (r[0] - prefix - 1) * 3); rt.free(r); r = joined; rt.free(return_tokens); rt.free(buffer); |
