diff options
| author | 2026-10-15 22:15:38 +0330 | |
|---|---|---|
| committer | 2026-10-15 22:15:38 +0330 | |
| commit | be43babc1886fc5efee898b2a3d0285012e511fb (patch) | |
| tree | b7ae73e7a6d4c896e2c49b7090168ef589b574af /src | |
| parent | merge work/known-bugs: constant-expression precedence, local const in constan... (diff) | |
definite assignment: reading a local before it is initialized is a compile error (spec 4.1.5, b.1.9)
track linited per local alongside the ownership state; snapshots, exit records and joins carry it, and a join keeps a local set only when every reaching path set it. x = e, a declaration initializer, &x (out-parameter), a full array initializer, or using an aggregate as an address set a local; field and element writes do not set the aggregate. while/for bodies that may run zero times do not count; do/while does. compiler, runtime, std, actor and lsp sources initialize the locals the rule flagged; zc0 accepts = {} brace lists (zeroed). tests: tests/errors/uninit_*.zc, tests/conformance/4-statements/uninit_*.zc
Diffstat (limited to 'src')
| -rw-r--r-- | src/compiler/analyzer.zc | 46 | ||||
| -rw-r--r-- | src/compiler/base.zc | 25 | ||||
| -rw-r--r-- | src/compiler/builtins.zc | 6 | ||||
| -rw-r--r-- | src/compiler/decl.zc | 18 | ||||
| -rw-r--r-- | src/compiler/expr.zc | 61 | ||||
| -rw-r--r-- | src/compiler/lex.zc | 2 | ||||
| -rw-r--r-- | src/compiler/ops.zc | 2 | ||||
| -rw-r--r-- | src/compiler/stmt.zc | 16 | ||||
| -rw-r--r-- | src/compiler/types.zc | 10 | ||||
| -rw-r--r-- | src/lib/actor.zc | 2 | ||||
| -rw-r--r-- | src/lsp/server.zc | 2 | ||||
| -rw-r--r-- | src/runtime/rt.zc | 8 | ||||
| -rw-r--r-- | src/tools/zc0.c | 25 |
13 files changed, 142 insertions, 81 deletions
diff --git a/src/compiler/analyzer.zc b/src/compiler/analyzer.zc index 55a96bb..3802437 100644 --- a/src/compiler/analyzer.zc +++ b/src/compiler/analyzer.zc @@ -440,6 +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; lpos[i] = 0; lreadpos[i] = 0; lowner[i] = 0; @@ -1024,6 +1025,18 @@ int luse(int i) { if ((ltag[i] >> 56) < 100) ltag[i] = ltag[i] + LCNT1; return 0; } +// 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. +int mark_inited(int i) { + 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"); + return 0; +} + // touch records a use of a tracked local and rejects dead objects. a d object // read in an inner block moves there, where the block owns it as m. int touch(int i) { @@ -1190,16 +1203,16 @@ int alias_end(int dep, int save) { } return 0; } -// snapshots: [lbase] [nloc] then state, owner, home per local +// snapshots: [lbase] [nloc] then state, owner, home, inited per local // save the states of all current locals; returns the snapshot offset int snap_push() { int b = snaptop, i = lbase; word p; af_forget(); - reserve_R_SNAP(b + 2 + 3 * (nloc - lbase)); + reserve_R_SNAP(b + 2 + 4 * (nloc - lbase)); 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]; p = p + 3; 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; } @@ -1211,7 +1224,7 @@ int snap_restore(int b) { af_forget(); if (e > nloc) e = nloc; p = p + 2; - while (i < e) { lstate[i] = p[0]; lowner[i] = p[1]; lhome[i] = p[2]; p = p + 3; i++; } + while (i < e) { lstate[i] = p[0]; lowner[i] = p[1]; lhome[i] = p[2]; if (linited) linited[i] = p[3]; p = p + 4; i++; } return 0; } // true for a plain or dead local state, which merge to dead. @@ -1226,7 +1239,7 @@ int snap_cmp(int b) { s = lstate[i]; if (empty(s) && empty(p[0])) { if (p[0] == S_DEAD) lstate[i] = S_DEAD; } else if (s != p[0] || lhome[i] != p[2] || (s == S_CHILD && lowner[i] != p[1])) return i; - p = p + 3; i++; + p = p + 4; i++; } return -1; } @@ -1258,7 +1271,7 @@ int merge_err(int i, int sa, int what) { // to analyze an if, the compiler snapshots the locals' states before the // branch, compiles the then path, restores the snapshot for the else path, and // then compares or merges the two. a snapshot is [lbase, nloc] followed by -// state, owner and home for each local; the stack of them is at R_SNAP. +// state, owner, home and inited for each local; the stack of them is at R_SNAP. // ---- joins ---- // where control flow comes back together (after an if, a loop, a switch or @@ -1274,19 +1287,24 @@ int extop; word[JOIN_PATHS] jps_initial; word jps = jps_initial; int jpscap = JO // read as plain. word jst(word p, int i) { if (i >= p[1]) return S_PLAIN; - return p[2 + 3 * (i - p[0])]; + return p[2 + 4 * (i - p[0])]; } // the owner recorded in a jump's state for local i. -word jow(word p, int i) { return p[3 + 3 * (i - p[0])]; } +word jow(word p, int i) { return p[3 + 4 * (i - p[0])]; } // the jump's marker for local i, in the same record as jow. -word jhm(word p, int i) { return p[4 + 3 * (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; + return p[5 + 4 * (i - p[0])]; +} // the states at a break, with the jump to patch: [site] then a snapshot word exit_rec(int site) { word p; int i = lbase; word q; - reserve_R_EXIT(extop + 3 + 3 * (nloc - lbase)); p = R_EXIT + extop; + 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]; q = q + 3; 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; } @@ -1340,6 +1358,12 @@ int join(int n0, int what) { rt.put_s(")"); errend(); } + // 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++; } + linited[i] = s; + } i++; } return 0; diff --git a/src/compiler/base.zc b/src/compiler/base.zc index 3135b39..03a575c 100644 --- a/src/compiler/base.zc +++ b/src/compiler/base.zc @@ -240,7 +240,7 @@ word lt_marks; int lt_nmarks, lt_markcap, lt_next_mark; char* lt_mark_file; int word lt_buf, lt_head, lt_file_at; int lt_top, lt_cap, lt_base = 0 - 1, lt_file_img; char* lt_file; char[2100] dbuf_initial; char* dbuf = dbuf_initial; int docbufcap = 2100; int dn, dlast, dline, docpending, dsect; char[2100] dsnap_initial; char* dsnap = dsnap_initial; int docsnapcap = 2100; int dsn, dnm; char[300] dnmb_initial; char* dnmb = dnmb_initial; int docnamecap = 300; -int pk_tk, pk_tv, pq_n; word[4] pq_tk; word[4] pq_tv; word[4] pq_gl; word[4] pq_ln; // up to 4 tokens of lookahead +int pk_tk, pk_tv, pq_n; word[4] pq_tk = {}; word[4] pq_tv = {}; word[4] pq_gl = {}; word[4] pq_ln = {}; // up to 4 tokens of lookahead 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; @@ -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; 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; 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. @@ -308,7 +308,7 @@ int ifon; word ifs, ifr; int nif; word ifx; int nifx; word ift; int nift; word i word if_edges, if_value, if_local; int ifrcap = NIF, ifxcap = IFN, iftcap = IFT; int fldst, fldi; // the one struct a bare address's field name resolved to -char*[8] tpn; int ntp; // type parameters in scope +char*[8] tpn = {}; int ntp; // type parameters in scope // reading a return type: unknown names are type parameters declared after the function's name int tvauto; // the typedefs of the blocks being compiled: three words each (the symbol, the block depth and the @@ -328,8 +328,8 @@ int s_ld8, s_st8, s_ld16, s_st16, s_ld32, s_st32; // table of shared variables, hold_count the blocks so far. a shared variable's symbol has // F_SHARED and its block's rank above it in sflag. const int F_SHARED = 131072, F_SHARED_RANK = 262144; -int hold_cur, fhold, hold_count, hold_desc, hold_nshared; word[128] hold_tab; -int o3on, o3nxt, o3one; word[32] o3sl; word[32] o3rg; int no3sl; word[O3_REGIONS * 67] o3pend_initial; word o3pend = o3pend_initial; int o3pendcap = O3_REGIONS, no3pend; word[80] o3dsc; int o3fs, o3na, o3fn0; +int hold_cur, fhold, hold_count, hold_desc, hold_nshared; word[128] hold_tab = {}; +int o3on, o3nxt, o3one; word[32] o3sl = {}; word[32] o3rg = {}; int no3sl; word[O3_REGIONS * 67] o3pend_initial; word o3pend = o3pend_initial; int o3pendcap = O3_REGIONS, no3pend; word[80] o3dsc = {}; int o3fs, o3na, o3fn0; // o3free holds registers released by closed scopes; o3cache holds the items pointer and length slots for fixed arrays. 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. @@ -348,18 +348,18 @@ int sx_push, sx_pop, sx_resize, sx_defer, sx_asm, sx_quote, sx_new, sx_int, sx_c // 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; -int mdg; word[8] mdn; // the type parameters of the generic struct whose methods are being compiled +int mdg; word[8] mdn = {}; // the type parameters of the generic struct whose methods are being compiled word[8] gbind; int ngbind; // explicit type arguments for the next call: f<int>(...) // generic functions. the template (type parameters stay variables) is compiled once, as before, // and its tokens are kept; a call whose type arguments are all known gets an instance, the same // tokens compiled again with the parameters bound to those types, so new(T) and sizeof(T) // inside it mean the real type. instances are compiled after the item that asked for them. int grec; word gbuf; int gn, gcap; // the tokens of the top-level item being read -int gnew, gnew_np; word[8] gnew_nm; // a generic function was just defined +int gnew, gnew_np; word[8] gnew_nm = {}; // a generic function was just defined int fn_method_compile, fnpatchseq; word gtab; int gt_n, gtcap = 64, gicap = 128; word gt_sym, gt_tok, gt_np, gt_module, gt_imports, gt_source; int gt_nm; // the columns are allocated at the first generic function int gi_n; word gi_t, gi_sym, gi_ar, gi_done; -int ginst; word[8] gnm; word[8] gib; int gng, ginsn; // compiling an instance: the names and what they stand for +int ginst; word[8] gnm = {}; word[8] gib = {}; int gng, ginsn; // compiling an instance: the names and what they stand for int s_this; const int NCLO = 8; /* initial closure nesting capacity; grows when more closures are compiled */ const int NCAP = 32; /* initial capture capacity per closure; grows when more variables are captured */ @@ -376,7 +376,7 @@ word[NCLO] clo_hi_initial; word clo_hi = clo_hi_initial; word[NCLO] clo_n_initial; word clo_n = clo_n_initial; word[NCLO] clo_env_initial; word clo_env = clo_env_initial; word[NCLO] clo_ret_initial; word clo_ret = clo_ret_initial; -char*[256] clo_name_initial; char** clo_name = clo_name_initial; +char*[256] clo_name_initial = {}; char** clo_name = clo_name_initial; word[256] clo_loc_initial; word clo_loc = clo_loc_initial; word[256] clo_via_initial; word clo_via = clo_via_initial; @@ -704,7 +704,7 @@ word sdoff, sdsrc; // workspace folder an editor passes in, inc_hook a function that gives the text of a file the editor // sent (a host with no files of its own), incres the path an include was found at char* incdir, increoot, incres, std_buf, icon_buf; char*(char*.m) inc_hook; int std_done, icon_done; -word rsrec, rstmp; word[32] pnoffs; word[32] pnplaces; int rsn, rscap, rsbase, nameoff; +word rsrec, rstmp; word[32] pnoffs = {}; word[32] pnplaces = {}; int rsn, rscap, rsbase, nameoff; // ownership at a reference: record word 11 holds the local's state (S_* + 1, plus 16 when // it owns its object) and word 12 the declaration offset (+ 1) of the owner it borrows from. // transfers are logged as events, EV_W words each: the local's declaration offset, what @@ -785,6 +785,7 @@ int reserve_locals(int need) { 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); + if (linited) linited = chk_resize(linited, 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); @@ -1506,8 +1507,8 @@ int o3promote(int i) { int o3mkcache(int i) { int k; if (!o3on || ltype[i][0] != TY_DYN || !ltype[i][3] || tbytes(ltype[i][4])) return 0; - k = newloc(s_hitems); fslots++; ltag[k] = 16; o3promote(k); - newloc(s_hlen); fslots++; ltag[k + 1] = 16; o3promote(k + 1); + k = newloc(s_hitems); fslots++; ltag[k] = 16; o3promote(k); mark_inited(k); + newloc(s_hlen); fslots++; ltag[k + 1] = 16; o3promote(k + 1); mark_inited(k + 1); o3cache[i] = k; return 0; } diff --git a/src/compiler/builtins.zc b/src/compiler/builtins.zc index f40e223..56895e9 100644 --- a/src/compiler/builtins.zc +++ b/src/compiler/builtins.zc @@ -464,7 +464,7 @@ int embed_bind(int n, int off, word t, int a, int kind) { return 0; } i = newloc(n); lrsoff[i] = off; rs_note(RS_LOCAL, off); - ltag[i] = LDECL; lpos[i] = tpack(); lnsz[i] = 0; lleaf[i] = 0; ltype[i] = t; + ltag[i] = LDECL; lpos[i] = tpack(); lnsz[i] = 0; lleaf[i] = 0; ltype[i] = t; mark_inited(i); if (kind == 1) { larr[i] = 1; fslots = fslots + tsz(t); capture_cell(i, 0); vaddr(i); lit(a); lit(tsz(t)); callname(s_copy_w); op(O_DROP); @@ -1094,7 +1094,7 @@ int ckind(int skipname) { int ckindfloat(int k) { return k == 2 || k == 5; } // parse an extern declaration: a c function with its signature, and a zc stub for it. int kw_extern() { - int rk; word[16] ks; word[16] kst; char*[16] ksn; int np = 0, k; char* fname; char* cname; int i, a, j, n, w, sig, bad = 0; char* buf; word oldimports = module_imports; int pos, len, g; word st, rst; int rn, rsd, sret = 0; + int rk; word[16] ks = {}; word[16] kst = {}; char*[16] ksn = {}; int np = 0, k; char* fname; char* cname; int i, a, j, n, w, sig, bad = 0; char* buf; word oldimports = module_imports; int pos, len, g; word st, rst = 0; int rn = 0, rsd, sret = 0; stmtonly(); next(); rk = ckind(0); @@ -1544,7 +1544,7 @@ word targ(word t, int i) { return t[6][1 + i]; } // ---- function types by address ---- word sigof(int f) { - int i = nsym, s = -1; word[24] ws; int n, k; + int i = nsym, s = -1; word[24] ws = {}; int n, k; while (i) { i--; if (skind[i] == K_FN && sval[i] == f && (sflag[i] & F_DEF)) { s = i; break; } } if (s < 0) return 0; if (stype[s]) return stype[s]; diff --git a/src/compiler/decl.zc b/src/compiler/decl.zc index 313d1ab..b6669b1 100644 --- a/src/compiler/decl.zc +++ b/src/compiler/decl.zc @@ -94,7 +94,7 @@ int prescan_maker() { // the guess only makes the function compile as if declared .u; its body is then // checked against that, so a wrong guess is an error, never a silent leak. int prescan_owned(int self) { - int depth = 0, n, k, found = 0, nown = 0, decl = 0; let owned[16]; + int depth = 0, n, k, found = 0, nown = 0, decl = 0; let owned[16] = {}; while (tk != T_EOF) { // a declaration's name follows a type name, a *, let or a comma; a plain // assignment (to a global, say) follows ; { or } @@ -385,7 +385,7 @@ int borrows_check(int s, int np, char** pn, word pty) { return 0; } int fndef(int chk, int rty, int ret, int n) { - int s, np = 0, tags = 0, t, sig, i; char*[24] pn; word[24] pt; word[24] pty; int typed = 0, ft, explicit_rty = !!rty, o3f = o3nxt, lo, hi, pk, lt, lend, lfl = tline, redef, amb = 0, nonm = 0; + int s, np = 0, tags = 0, t, sig, i; char*[24] pn = {}; word[24] pt = {}; word[24] pty = {}; int typed = 0, ft, explicit_rty = !!rty, o3f = o3nxt, lo, hi, pk, lt, lend, lfl = tline, redef, amb = 0, nonm = 0; o3nxt = 0; if (rty || ntp) typed = 1; if (!dnm) dnm = n; @@ -500,7 +500,7 @@ int fndef(int chk, int rty, int ret, int n) { prologue(); borrow_prologue(); hold_gate(57); i = 0; while (i < np) { - t = newloc(pn[i]); lrskind[t] = RS_PARAM; lrsoff[t] = pnoffs[i]; lpos[t] = pnplaces[i]; fslots++; lsmark(1, pn[i]); + t = newloc(pn[i]); lrskind[t] = RS_PARAM; lrsoff[t] = pnoffs[i]; lpos[t] = pnplaces[i]; fslots++; lsmark(1, pn[i]); mark_inited(t); ltype[t] = pty[i]; lptr[t] = 0; if (ifcur && if_local && i < IFP) if_local[t] = ifcur + 8 + 3 * i; lt_note(pnoffs[i], pty[i]); @@ -645,7 +645,7 @@ int gconst() { // lays out the descriptor the install opcode reads, and wires it in right // away (an image wires its own in when it boots). int enhdef() { - int n, np = 0, nout = 1, dsc, na, pay, len = 0, kind = 0, i; word p; int s, slot, r; char*[8] pn; + int n, np = 0, nout = 1, dsc, na, pay = 0, len = 0, kind = 0, i; word p; int s, slot, r; char*[8] pn = {}; next(); // enhance 2 divmod(a, b) if (tk == T_NUM) { @@ -711,7 +711,7 @@ int topsync() { // after a function's name: <T, U> declares its type parameters. any the return // type used were taken on trust; now they get their real numbers int genparams() { - int i, j, n = 0; char*[8] ps; word[8] bs; + int i, j, n = 0; char*[8] ps = {}; word[8] bs = {}; if (!is_op(s_lt)) { if (ntp) serrn(tpn[0], "isn't a type"); ntp = 0; return 0; } next(); while (1) { @@ -1012,7 +1012,7 @@ int fn_remember(int s, int name) { // their original globals and entry addresses. int syntax_host_function(int symbol) { word meta = fnmeta[symbol], source, full; int clone, count, name; - char[40] generated; char[24] reversed; int sequence = fnpatchseq, digits = 0, i = 0; + char[40] generated = {}; char[24] reversed = {}; int sequence = fnpatchseq, digits = 0, i = 0; if (syntax_host_symbols[symbol]) return syntax_host_symbols[symbol] - 1; if (!meta || (sflag[symbol] & F_GEN)) { serr("an emitted syntax dependency requires retained nongeneric source"); return -1; } fnpatchseq++; @@ -1049,7 +1049,7 @@ int syntax_resident(int symbol) { // queued to be compiled once the template's tokens are in. a type parameter // that nothing bound is a plain word. word ginstance(int s, word bs) { - int k = 0, t = 0 - 1, np, i, j, ns; char* nm; char[24] dg; int nd; char* a; + int k = 0, t = 0 - 1, np, i, j, ns; char* nm; char[24] dg = {}; int nd; char* a; if (dry || !gtab) return s; while (k < gt_n) { if (gt_sym[k] == s) { t = k; break; } @@ -1159,7 +1159,7 @@ int unit_top() { } // the edit distance between two names (levenshtein), or 99 when one is too long word editdist(word a, word b) { - int la = rt.len_w(a), lb = rt.len_w(b), i, j, x, y, z, last, row[65]; + int la = rt.len_w(a), lb = rt.len_w(b), i, j, x, y, z, last, row[65] = {}; if (la > 60 || lb > 60) return 99; j = 0; while (j <= lb) { row[j] = j; j++; } i = 1; @@ -1519,7 +1519,7 @@ word*.u function_tokens(int addr) { int replace_function(int addr, word* body_tokens) { int s = 0, open, close, namepos, olderr = nerr, oldpanic = panic, oldabort = chkabort; int errors, generated_id, new_symbol, count, i = 0, out = 0, cap, diag, seq, digits = 0, published; - char[40] generated; char[24] reversed; word meta, source, full, imports; char* module; + char[40] generated = {}; char[24] reversed = {}; word meta, source, full, imports; char* module; while (s < nsym && !(skind[s] == K_FN && sval[s] == addr && fnmeta[s])) s++; if (s >= nsym || !body_tokens || body_tokens[0] < 0) return 0; meta = fnmeta[s]; source = meta[0]; open = meta[4]; close = meta[5]; namepos = meta[3]; module = meta[1]; imports = meta[2]; diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc index e437d2f..ecbb24c 100644 --- a/src/compiler/expr.zc +++ b/src/compiler/expr.zc @@ -20,14 +20,23 @@ int dbg_addr() { // variable, field or element that has not been loaded; rval loads it, unless // the thing is an array or struct, whose address is its value. every place // that needs a value calls this after parsing an expression. -int rval() { +// load or finish an lvalue as a value. check is 1 when the value is read +// (definite assignment); 0 when forming a field or element address to write. +int rval_do(int check) { if (lv && lvk == LV_LOCAL) { ltag[lvi] = ltag[lvi] | LREAD; + if (check) { + // an aggregate's value is its address (like &x), so using it initializes it; + // a scalar must already be set + if (isagg(xty) || larr[lvi]) mark_inited(lvi); + else need_inited(lvi); + } if (rt.eq_w(lname[lvi], "_") && af_warning(lreadpos[lvi])) { rt.put_s("discard placeholder `_` is read\nhelp: give the parameter or pattern binding a name when its value is needed"); af_warning_end(); } } + if (lv && check && lvk == LV_ELEM && lvi >= 0) need_inited(lvi); if (lv) { if (ispk(xty)) pkld(xty); else if (!isagg(xty)) { dbg_addr(); op(O_LD); } @@ -35,6 +44,8 @@ int rval() { } return 0; } +int rval() { return rval_do(1); } + // run a keyword's handler (kw_len, kw_struct, a parse word...) from the spot // where the parser met it. e is 1 in expression position, where statement-only // keywords refuse to run. the handler reports the class of what it produced @@ -441,7 +452,7 @@ int printcall(int line) { return C_PLAIN; } int call(int s) { - int np, tags = 0, ret = 0; word ft = stype[s]; word[8] bs; int rs, rhs_type = 0, kop, cp = callpos, original = s; word storage; int origin, temporary, owner; word origins, owner_fact; + int np, tags = 0, ret = 0; word ft = stype[s]; word[8] bs = {}; int rs, rhs_type = 0, kop, cp = callpos, original = s; word storage; int origin, temporary, owner; word origins, owner_fact; word requirements = effect_requirements, edges = effect_edges; if (rt.eq_w(sname[s], "std.Io.print")) return printcall(0); if (rt.eq_w(sname[s], "std.Io.println")) return printcall(1); @@ -702,7 +713,7 @@ int shared_use(int s) { // compiler state around the body. int closure() { char* xcf; - int np = 0; char*[24] pn; word[24] pty; int hop, entry, i, t, envl, ncap, c, result, declared = 0, tags = 0, tag, lt, lfl = tline; word[24] ptags; + int np = 0; char*[24] pn = {}; word[24] pty = {}; int hop, entry, i, t, envl, ncap, c, result, declared = 0, tags = 0, tag, lt, lfl = tline; word[24] ptags = {}; word expected = closure_expected, this_type = closure_this; closure_expected = 0; closure_this = 0; reserve_closures(nclo + 1); if (o3on) serr("closures can't be made inside an o3 region"); @@ -744,14 +755,14 @@ int closure() { prologue(); borrow_prologue(); i = 0; while (i < np) { - t = newloc(pn[i]); lrskind[t] = RS_PARAM; lrsoff[t] = pnoffs[i]; lpos[t] = pnplaces[i]; fslots++; lsmark(1, pn[i]); + t = newloc(pn[i]); lrskind[t] = RS_PARAM; lrsoff[t] = pnoffs[i]; lpos[t] = pnplaces[i]; fslots++; lsmark(1, pn[i]); mark_inited(t); ltype[t] = pty[i]; if (fchk && ptags[i] == 1) lstate[t] = S_BORROW; if (fchk && ptags[i] == 2) { lstate[t] = S_M; lown[t] = 1; } if (isfl(pty[i])) setflt(t, 1); i++; } - envl = newloc(intern("<env>")); fslots++; + envl = newloc(intern("<env>")); fslots++; mark_inited(envl); clo_env[nclo - 1] = envl; i = np + 1; while (i) { i--; laddr(i); op(O_ST); } @@ -776,7 +787,7 @@ int closure() { while (i < ncap) { t = clo_loc[c * capture_width + i]; if (clo_via[c * capture_width + i] < 0) { - touch(t); effect_note_mask(effect_mask(C_VAR + t), EF_RETAIN); ev_put(t, EV_CAPTURE, -1); + need_inited(t); touch(t); effect_note_mask(effect_mask(C_VAR + t), EF_RETAIN); ev_put(t, EV_CAPTURE, -1); if (cmode && !dry) { lvescaped[t] = 1; lvkind[t] = 0; } if (!isalias(t)) { vaddr(t); op(O_LD); callname(intern("share")); op(O_DROP); } vaddr(t); @@ -963,7 +974,7 @@ int array_literal_tokens(word q, word t) { } int i = newloc(intern("<array literal>")), place = tpack(); t = array_measure(q, t); ltype[i] = t; larr[i] = 1; fslots = fslots + tsz(t); - array_initialize(q, t, lslot[i], 1); + array_initialize(q, t, lslot[i], 1); mark_inited(i); lafact[i] = af_new(t[3], 4, place); laaddr[i] = lafact[i]; if (lafact[i]) lafact[i][5] = i; laddr(lslot[i]); xty = t; lv = 0; lvk = LV_NONE; xbor = i; xaf = lafact[i]; xzero = 0; xfloat = 0; xstr = 0; xconst = 0; xvk = 0; rt.free(q); @@ -1018,7 +1029,7 @@ int brace_primary() { // variable the value is. int primary() { effect_value = 0; xprojection = 0; if_value = 0; - int n, i, s, c, k, npos, sv; word[8] bs0; int ft, mn, ms; + int n, i, s, c, k, npos, sv; word[8] bs0 = {}; int ft, mn, ms; lv = 0; lvk = LV_NONE; xzero = 0; xbor = -1; xconst = 0; xfloat = 0; xstr = 0; xlenloc = -1; xstamp = 0; xaf = 0; xvk = 0; if (tk == T_FLT) { op(OP_FLIT); emit(tv); xvk = 2; xvv = tv; xfloat = 1; xty = tfloat; next(); return C_PLAIN; } @@ -1067,7 +1078,7 @@ int primary() { } vaddr(i); xty = ltype[i]; - if (larr[i]) return C_PLAIN; + if (larr[i]) { lv = 1; lvk = LV_LOCAL; lvi = i; return C_PLAIN; } xfloat = getflt(i); if (isfl(ltype[i])) xfloat = 1; xstr = getstr(i); if (xty == tbstr) xstr = 1; @@ -1200,6 +1211,8 @@ int incdec(int post, int dlt) { if (lvk == LV_LOCAL && fchk && tracked(lvi) && !isalias(lvi)) { 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); 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; @@ -1237,7 +1250,8 @@ word field(int c) { int readonly = treadonly(xty); 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; + 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; de = toff + toklen; next(); // is the place asked about on this member name (or just after the dot, with none typed) @@ -1270,7 +1284,7 @@ word field(int c) { } } } - rval(); + rval_do(0); if (c == C_FRESH) cerr("use a field of a new object only after binding it to a name"); else if (c >= C_VAR) { touch(c - C_VAR); b = c - C_VAR; } st = xty; @@ -1336,7 +1350,9 @@ 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 lvk = LV_OTHER; + if (b >= 0) { lvk = LV_ELEM; lvi = b; } + else if (base_local >= 0) { lvk = LV_ELEM; lvi = base_local; } + else lvk = LV_OTHER; return C_PLAIN; } // array indexing, address arithmetic. mulsz scales an index by the element @@ -1407,7 +1423,7 @@ int fixed_chk(int n) { // 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; + int k = lookup(s_bnd), j = 0; 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); } @@ -1430,7 +1446,7 @@ int ptr_chk(int sz) { // 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, ub, bl, ic, inr, hg, pn, iu; 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, hg, pn, iu; 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); @@ -1452,7 +1468,7 @@ int postfix_tail(int c) { 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(); + rval_do(0); // indexing forms an element address; reading the element checks later if (was) { c = expr(); rval(); plainuse(c); if (accept(s_colon)) { @@ -1599,7 +1615,9 @@ 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 lvk = LV_OTHER; + if (b >= 0) { lvk = LV_ELEM; lvi = b; } + else if (bl >= 0) { lvk = LV_ELEM; lvi = bl; } + else lvk = LV_OTHER; c = C_PLAIN; xfloat = 0; if (isfl(xty)) xfloat = 1; xstr = 0; continue; @@ -1622,6 +1640,7 @@ int postfix_tail(int c) { ifcall = -1; effect_argument_base = 0; int call_tags = 0; if (ft) call_tags = ft[5] >> 6; n = args(call_tags, ft, bs, o3on); temporary = argtemporary; call_owner = argowner; call_origins = argorigin_mask; + place = tpack(); // raw code can live in a heap segment; a closure has an environment block lit(n); op(O_PICK); callname(intern("callable_code")); cz = hole(); op(O_JZ); @@ -1719,7 +1738,7 @@ int unary() { if (!lv && !isagg(xty)) die("& needs something addressable"); 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; + if (lv && lvk == LV_LOCAL) { ltag[lvi] = ltag[lvi] | LREAD | LASG; mark_inited(lvi); } 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; @@ -1799,7 +1818,7 @@ int bin(int minp) { next(); rval(); plainuse(c); lt = xty; if (isfl(lt)) lf = 1; - src = -1; + src = -1; rf = 0; rs = 0; right_mask = 0; if (blen[b] == -1) { // && : 0 if the left side is 0, else the right side as 0 or 1 af_forget(); ftest(); @@ -1892,7 +1911,7 @@ int storekeep(int t) { // restored for the second side) and then joined, so a variable can't be given // away on one side only. both sides are fresh objects, or neither is. int ternary(int c) { - int a, b, s0, sa, livea, live0 = live, n0 = nloc, ca, cb, ta, ba, bb, conv, skip, back; word temp_a, temp_b, mask_a, mask_b; + int a, b, s0, sa, livea, live0 = live, n0 = nloc, ca, cb, ta, ba, bb, conv, skip, back = 0; word temp_a, temp_b, mask_a, mask_b; rval(); plainuse(c); ftest(); next(); @@ -1950,7 +1969,7 @@ int expr() { if (k != LV_LOCAL) af_escape(xaf); fcoerce(lt); if (k == LV_LOCAL) { - ltag[i] = ltag[i] | LASG; + ltag[i] = ltag[i] | LASG; mark_inited(i); setflt(i, xfloat); setstr(i, xstr); note_store(i); af_store(i); vf_store(i); @@ -1979,6 +1998,8 @@ int expr() { if (lvk == LV_LOCAL && fchk && tracked(lvi) && !isalias(lvi)) { 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); lt = xty; require_mutable(lt); fk = -1; lf = xfloat; if (lvk == LV_LOCAL) fk = lvi; diff --git a/src/compiler/lex.zc b/src/compiler/lex.zc index 95415a6..3b810ba 100644 --- a/src/compiler/lex.zc +++ b/src/compiler/lex.zc @@ -377,7 +377,7 @@ int fieldscan(int n) { // before a program it joins to the runtime. the rest of the line is read here and the marker kept: the // source is read once to find the signatures and again to compile, and it counts only when compiling reaches it. int mark_line() { - char[300] b; int n = 0, i, base; word name; + char[300] b = {}; int n = 0, i, base; word name; while (ch >= 0 && ch != 10) { if (n < 298) { b[n] = ch; n++; } nextch(); } while (n && (b[n - 1] == 13 || b[n - 1] == 32)) n--; b[n] = 0; diff --git a/src/compiler/ops.zc b/src/compiler/ops.zc index a396f72..0129e75 100644 --- a/src/compiler/ops.zc +++ b/src/compiler/ops.zc @@ -141,7 +141,7 @@ int add_method(char* member, char* function_name) { return s; } int receiver_method(word receiver_type, int member) { - word link = receiver_methods, ft, actual; word[8] bs; int found = -1, s; + word link = receiver_methods, ft, actual; word[8] bs = {}; int found = -1, s; while (link) { s = link[2]; if (s < nsym && link[1] == member) { diff --git a/src/compiler/stmt.zc b/src/compiler/stmt.zc index 79e5fb8..2e0cfe3 100644 --- a/src/compiler/stmt.zc +++ b/src/compiler/stmt.zc @@ -138,7 +138,7 @@ int localdecl(int typed) { if (larr[i] && ltype[i][0] == TY_ARR && (is_op(s_lb) || tk == T_STR || tk == T_BSTR)) { iq = array_initializer_tokens(ltype[i]); it = array_measure(iq, ltype[i]); fslots = fslots + tsz(it) - tsz(ltype[i]); - ltype[i] = it; array_initialize(iq, it, lslot[i], 1); rt.free(iq); + ltype[i] = it; array_initialize(iq, it, lslot[i], 1); rt.free(iq); mark_inited(i); } else { if (larr[i]) serr("an array or a struct needs an initializer list"); newsz = -1; newleaf = 0; if (tk == T_NUM) nomagic = 1; @@ -159,7 +159,7 @@ int localdecl(int typed) { else if (xty[0] == TY_STRUCT) ltype[i] = tptr(xty); else ltype[i] = xty; } - assigncheck(LV_LOCAL, i, c); + assigncheck(LV_LOCAL, i, c); mark_inited(i); if (ro) { k = ltype[i]; if (!k) { k = tword; if (isnum(xty)) k = xty; } ltype[i] = tconst(k); } if (gok) { k = xty; if (isagg(k)) k = ltype[i]; if (!ifpure(k) || (gty && gty != k)) gok = 0; else gty = k; } } @@ -271,7 +271,7 @@ int loop_end(int a, int sc) { njps = 0; if (sc >= 0) jadd(R_SNAP + sc); p = R_EXIT + f[6]; e = R_EXIT + extop; - while (p < e) { jadd(p + 1); p = p + 3 + 3 * (p[2] - p[1]); } + while (p < e) { jadd(p + 1); p = p + 3 + 4 * (p[2] - p[1]); } join(n0, "leaves the loop in different states on different paths"); live = njps > 0; p = R_EXIT + f[6]; @@ -283,7 +283,7 @@ int loop_end(int a, int sc) { op(O_JMP); } else outs = fixnew(p[0], outs); - p = p + 3 + 3 * (p[2] - p[1]); + p = p + 3 + 4 * (p[2] - p[1]); } if (a >= 0) setlit(a, here); if (sc >= 0) fixups(R_SNAP + sc, n0, 1); @@ -578,14 +578,14 @@ int case_seen(word seen, int n, int v) { 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[256] seen; int nseen = 0; + 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); tg = ptagged(st); fdepth++; tmp = newloc(0); fslots++; // a hidden local holds the value - laddr(lslot[tmp]); op(O_ST); + laddr(lslot[tmp]); op(O_ST); mark_inited(tmp); live0 = live; s0 = snap_push(); loop_push(-1, s0); @@ -634,7 +634,7 @@ int kw_switch() { for (j = 0; j < nb && j < pm_nf[gi]; j++) { capture_cell(pbl[j], 0); laddr(lslot[tmp]); op(O_LD); lit(pm_off[gi * 8 + j]); op(O_ADD); op(O_LD); - vaddr(pbl[j]); op(O_ST); + vaddr(pbl[j]); op(O_ST); mark_inited(pbl[j]); } casebody(s0, live0); block_close(arm); @@ -848,7 +848,7 @@ int asmblock() { if (tk != T_ID) die("bad token in asm"); i = findloc(tv); if (i >= 0 && (ltag[i] & 16) && !larr[i]) serr("asm can't reach an o3 variable"); - if (i >= 0) { luse(i); ltag[i] = ltag[i] | LREAD | LASG; vaddr(i); next(); continue; } + if (i >= 0) { need_inited(i); luse(i); ltag[i] = ltag[i] | LREAD | LASG; mark_inited(i); vaddr(i); next(); continue; } if (nclo) { k = clocap(tv); if (k >= 0) { vaddr(clo_env[nclo - 1]); op(O_LD); lit(k); op(O_ADD); op(O_LD); next(); continue; } diff --git a/src/compiler/types.zc b/src/compiler/types.zc index 322d0fd..615af93 100644 --- a/src/compiler/types.zc +++ b/src/compiler/types.zc @@ -181,7 +181,7 @@ word tinst(word g, word args, int n) { } // lay out a generic instance on first use word tresolve(word t) { - word g, f; int n, i; word fs; int off = 0, ft; word[8] bs; + word g, f; int n, i; word fs; int off = 0, ft; word[8] bs = {}; if (t[0] != TY_STRUCT || t[3] >= 0) return t; if (t[3] == -2) { serr("a struct can't contain itself by value"); t[3] = 0; return t; } t[3] = -2; @@ -206,7 +206,7 @@ word tsz(word t) { // replace type parameters with what they were bound to (unbound ones become word) word subst(word t, word bs) { if (treadonly(t)) return tconst(subst(tunconst(t), bs)); - int k = t[0], i, n; word[24] v; word vec; + int k = t[0], i, n; word[24] v = {}; word vec; if (k == TY_VAR) { if (bs[t[3]]) return bs[t[3]]; return tword; } if (k == TY_PTR) return tptr(subst(t[4], bs)); if (k == TY_ARR) return tarr(subst(t[4], bs), t[3]); @@ -399,7 +399,7 @@ int closeangle() { // the first thing in ptype's comment applies here too: fn(int) is a function // returning a plain word. int ptype() { - int start = toff, t = ptype0(), np, tags, tag; word[24] ps; + int start = toff, t = ptype0(), np, tags, tag; word[24] ps = {}; while (is_op(s_lp)) { next(); np = 0; tags = 0; if (!is_op(s_rp)) { @@ -445,7 +445,7 @@ int ptype0() { } // parse one type with no trailing function parts. word ptype1() { - int n, s; word t; word[8] args; int na = 0, i; + int n, s; word t; word[8] args = {}; int na = 0, i; if (is_op(s_lk)) { die("array types are postfix: T[N], T[] or T[?]"); return tword; } if (is_id(s_const)) { next(); return tconst(ptype0()); } if (is_id(s_fixed)) { // fixed T[?] can't grow; fixed T[n] says what T[n] already means @@ -880,7 +880,7 @@ int methdef(char* sn, char* mn, word q, int stat, int typed, int rty, int rtag) // has been compiled, and its result class is fresh if the method returns .u. int methcall(int s, word rst, word receiver_fact, int receiver, word receiver_mask) { af_forget(); - int np, tags = 0, ret = 0, temporary, owner, aliases, i; word ft = stype[s]; word[8] bs; word owner_fact, origins; + int np, tags = 0, ret = 0, temporary, owner, aliases, i; word ft = stype[s]; word[8] bs = {}; word owner_fact, origins; word requirements = effect_requirements, edges = effect_edges; int original = s; effect_args_mask(s, 0, receiver_mask); if (receiver_fact && (receiver_fact[2] == 4 || receiver_fact[2] == 5)) effect_require(s, 0, callpos); diff --git a/src/lib/actor.zc b/src/lib/actor.zc index c013441..d0c0f10 100644 --- a/src/lib/actor.zc +++ b/src/lib/actor.zc @@ -121,7 +121,7 @@ unchecked int write_message(int fd, word address, word type) { rt.free(refs); rt.free(data); return result; } unchecked int read_message(int fd, word address, word type) { - int count = 0; word data, refs, frame[2]; + int count = 0; word data, refs, frame[2] = {}; if (rt.syscall(45, fd, &count, 1) < 0 || count < 0 || count > 1048576) return -1; data = rt.dnew(1, count, 0); refs = vector(); frame[0] = data; frame[1] = 0; if (rt.syscall(45, fd, data[2], count) < 0) { rt.free(refs); rt.free(data); return -1; } diff --git a/src/lsp/server.zc b/src/lsp/server.zc index 536d71c..79afb36 100644 --- a/src/lsp/server.zc +++ b/src/lsp/server.zc @@ -67,7 +67,7 @@ int ws(char* s) { } // writes n in decimal, with a minus sign when it is negative. int wn(int n) { - word[24] b; int i = 0; + word[24] b = {}; int i = 0; if (n < 0) { wc('-'); n = 0 - n; } while (1) { b[i] = '0' + n % 10; i++; n = n / 10; if (!n) break; } while (i) { i--; wc(b[i]); } diff --git a/src/runtime/rt.zc b/src/runtime/rt.zc index 4f96320..a79387f 100644 --- a/src/runtime/rt.zc +++ b/src/runtime/rt.zc @@ -182,7 +182,7 @@ int put_s(char* s) { // write the decimal form of a number, with a minus sign for a negative one. // the smallest integer is handled as its own case. int put_n(int n) { - char[24] b; int i = 0; + char[24] b = {}; int i = 0; if (n < 0) { put_c('-'); n = 0 - n; } if (n < 0) { put_s("9223372036854775808"); return 0; } while (1) { b[i++] = '0' + n % 10; n = n / 10; if (!n) break; } @@ -340,7 +340,7 @@ int s_to_n(bstr s) { } // a new packed byte string holding the decimal form of n. bstr n_to_s(int n) { - word[24] b; int i = 0, j = 0, neg = 0; bstr p; char* t; + word[24] b = {}; int i = 0, j = 0, neg = 0; bstr p; char* t; if (!n) { p = new_s(1); sput(p, 0, '0'); sput(p, 1, 0); return p; } if (n < 0) { neg = 1; n = 0 - n; } if (n < 0) { @@ -431,7 +431,7 @@ const int F_HASDROP = 16, F_DROPPED = 32, F_FINAL = 64, F_CYCLE = 128; // F_CYCL int o3_depth, gc_pending, gc_cyc; // gc_cyc: the last sweep found a cycle with drops to run // flags live in these bits; the size class is above them. const int F_LOW = 65535; -int hbump; word[32] freel; int gc_runs, gc_freed, gc_auto = 1; +int hbump; word[32] freel = {}; int gc_runs, gc_freed, gc_auto = 1; // block walks and new chunks stop here. int hstop; // while nonzero, balloc will not return a block overlapping this range. @@ -1201,7 +1201,7 @@ int line_of(word b, int a) { } // write the hexadecimal form of a number int put_hex(int n) { - char[20] b; int i = 0, d; + char[20] b = {}; int i = 0, d; put_s("0x"); if (!n) { put_c('0'); return 0; } while (n && i < 16) { d = n & 15; if (d < 10) b[i++] = '0' + d; else b[i++] = 'a' + d - 10; n = n >> 4; } diff --git a/src/tools/zc0.c b/src/tools/zc0.c index 229734c..07a5933 100644 --- a/src/tools/zc0.c +++ b/src/tools/zc0.c @@ -824,14 +824,24 @@ static void stmt(void) if (nw >= 0 || accept("let") || accept("var")) { if (nw < 0) nw = 1; do { - Loc *l = &lo[nlo++]; strcpy(l->name, ident()); l->arr = 0; - if (nw > 1) { l->slot = nslot; nslot += nw; l->arr = 1; } + Loc *l = &lo[nlo++]; strcpy(l->name, ident()); l->arr = 0; int asz = 1; + if (nw > 1) { l->slot = nslot; nslot += nw; l->arr = 1; asz = nw; } else { - if (accept("[")) { n = cexpr(); expect("]"); nslot += n; l->slot = nslot - n; l->arr = 1; } + if (accept("[")) { n = cexpr(); expect("]"); nslot += n; l->slot = nslot - n; l->arr = 1; asz = n; } else l->slot = nslot++; } l->flt = 0; l->dstr = tstr && !l->arr; l->str = l->dstr; - if (accept("=")) { expr(); rval(); laddr(l->slot); op(O_ST); l->flt = xf; l->str = xs || l->dstr; } + if (accept("=")) { + if (is("{")) { /* brace initializer: skip the list and zero the slots */ + int depth = 0, zi; + do { + if (is("{")) { depth++; next(); } + else if (is("}")) { depth--; next(); } + else next(); + } while (depth > 0 && tk); + for (zi = 0; zi < asz; zi++) { lit(0); laddr(l->slot + zi); op(O_ST); } + } else { expr(); rval(); laddr(l->slot); op(O_ST); l->flt = xf; l->str = xs || l->dstr; } + } } while (accept(",")); expect(";"); return; } @@ -1106,10 +1116,15 @@ static void top1(void) else if (accept("[")) { k = (int)cexpr(); expect("]"); } if (k > 1) { a = here; while (k--) emit(0); add(n, K_GARR, a); + if (accept("=")) { /* = {} or = {...}: already zeroed */ + if (!is("{")) die("a global array initializer is a brace list"); + { int depth = 0; do { if (is("{")) { depth++; next(); } else if (is("}")) { depth--; next(); } else next(); } while (depth > 0 && tk); } + } } else { a = here; gs = add(n, K_GVAR, a); gs->str = gstr; emit(0); if (accept("=")) { - if (tk == T_STR) { int i, l = (int)strlen(ts); img[a - GORG] = here; for (i = 0; i < l; i++) emit((unsigned char)ts[i]); emit(0); next(); } + if (is("{")) { int depth = 0; do { if (is("{")) { depth++; next(); } else if (is("}")) { depth--; next(); } else next(); } while (depth > 0 && tk); } + else if (tk == T_STR) { int i, l = (int)strlen(ts); img[a - GORG] = here; for (i = 0; i < l; i++) emit((unsigned char)ts[i]); emit(0); next(); } else if (tk == T_BSTR) { int d = bstrwords((const unsigned char *)ts, (long)strlen(ts)); img[a - GORG] = d; gs->str = 1; next(); } else img[a - GORG] = cexpr(); } |
