aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 16:51:28 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 16:51:28 +0330
commit90f96d4d7e2eba0295dd799318caa7e7fcd8925e (patch)
tree1b401b6ef889ef40456cb452d39add94a1cc93cf
parentmerge work/jit2: x86-64 jit inline enhancements (diff)
parentzc0: a store into a local embed array is an error (its elements are in the co... (diff)
merge work/xref: zc0 u*d redo and follow-ups
-rw-r--r--src/tools/zc0.c40
-rw-r--r--tests/conformance/known.txt1
-rw-r--r--tests/programs/zc0embed_write.zc9
-rwxr-xr-xtests/test.sh1
4 files changed, 36 insertions, 15 deletions
diff --git a/src/tools/zc0.c b/src/tools/zc0.c
index 664ea79..6c62abf 100644
--- a/src/tools/zc0.c
+++ b/src/tools/zc0.c
@@ -48,6 +48,9 @@ static void tsroom(size_t n)
ts = nb; tscap = c;
}
static int xf, xs, lvi; /* float, byte string; the plain local it came from, or -1 */
+/* xro: the value is a local embed array (Loc.ro); lro: the lvalue is one of its elements, which zc0 refuses to write */
+static int xro, lro;
+static void rocheck(void);
static int64_t *img; static int imgcap, here = GORG;
static void emit(int64_t w) {
@@ -257,12 +260,8 @@ static int accept(const char *s) { if (is(s)) { next(); return 1; } return 0; }
static void expect(const char *s) { if (!accept(s)) { char b[64]; sprintf(b, "expected '%s'", s); die(b); } }
static char *ident(void) { static char b[8][256]; static int k; char *r = b[k++ & 7]; if (tk != T_ID) die("expected a name"); strcpy(r, ts); next(); return r; }
/* u, m and d are ordinary names. u*x, m*x and d*x are recasts when the star touches
- the letter, which the raw character after the name tells: u * x is a product, and
+ the letter and no variable of that name is visible, which the raw character after the name tells: u * x is a product, and
so is u*=x. the current token is the name, so pos is just past it. */
-static int isrecast(void)
-{
- return tk == T_ID && ts[1] == 0 && (ts[0] == 'u' || ts[0] == 'm' || ts[0] == 'd') && src[pos] == '*' && src[pos + 1] != '=';
-}
/* ---- symbols ---- */
typedef struct { char name[64]; int kind, sig, defd, str; int64_t val; } Sym; /* str: a global holding a byte string, or a function returning one */
@@ -300,9 +299,16 @@ static void fnref(const char *n) /* push a function address, fixing it up later
}
/* ---- locals ---- */
-typedef struct { char name[64]; int slot, arr, flt, str, dstr; } Loc; /* dstr: declared bstr, so always a byte string */
+typedef struct { char name[64]; int slot, arr, flt, str, dstr, ro; } Loc; /* dstr: declared bstr, so always a byte string; ro: a local embed array, whose elements are in the code */
static Loc lo[1024]; static int nlo, nslot, infn;
static Loc *findl(const char *n) { int i; for (i = nlo; i--;) if (!strcmp(lo[i].name, n)) return &lo[i]; return 0; }
+static int isrecast(void)
+{
+ Sym *g;
+ if (!(tk == T_ID && ts[1] == 0 && (ts[0] == 'u' || ts[0] == 'm' || ts[0] == 'd') && src[pos] == '*' && src[pos + 1] != '=')) return 0;
+ if (findl(ts)) return 0; /* a visible variable named u, m or d: a product (spec 1.6.3) */
+ g = find(ts); return !(g && (g->kind == K_GVAR || g->kind == K_GARR));
+}
static void laddr(int k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } }
/* ---- operator tables ---- */
@@ -594,10 +600,11 @@ static int returns_bstr(const char *n)
}
static int args(void) { int n = 0; expect("("); if (!accept(")")) { do { expr(); rval(); n++; } while (accept(",")); expect(")"); } return n; }
static void asmblock(void);
+static void rocheck(void) { if (lro) die("a local embed array is part of the code; zc0 does not let a program write it (embed it at the top level)"); }
static void primary(void)
{
char n[64]; Loc *l; Sym *s;
- lv = 0; lvi = -1; xf = 0; xs = 0;
+ lv = 0; lvi = -1; xf = 0; xs = 0; xro = 0; lro = 0;
if (tk == T_FLT) { emit(1200); emit(tv); xf = 1; next(); return; }
if (tk == T_NUM) { lit(tv); next(); return; }
if (tk == T_BSTR) { bstrlit(); xs = 1; return; }
@@ -605,7 +612,7 @@ static void primary(void)
if (accept("(")) { expr(); expect(")"); return; }
if (is("asm")) { next(); asmblock(); return; }
strcpy(n, ident());
- if ((l = findl(n))) { laddr(l->slot); lv = !l->arr; if (!l->arr) { xf = l->flt; xs = l->str; lvi = (int)(l - lo); } return; }
+ if ((l = findl(n))) { laddr(l->slot); lv = !l->arr; if (!l->arr) { xf = l->flt; xs = l->str; lvi = (int)(l - lo); } xro = l->ro; return; }
s = find(n);
if (s && s->kind == K_CONST) { lit(s->val); return; }
if (s && s->kind == K_GVAR) { lit(s->val); lv = 1; xs = s->str; return; }
@@ -631,6 +638,7 @@ static void primary(void)
static void incdec(int post, int d)
{
if (!lv) die("++/-- needs an lvalue");
+ rocheck();
lv = 0;
if (lvi >= 0) lo[lvi].str = lo[lvi].dstr;
xs = 0;
@@ -643,6 +651,7 @@ static void postfix(void)
primary();
for (;;) {
if (accept("[")) {
+ int ro = xro;
was = xs; rval(); expr(); rval();
if (was && accept(":")) {
expr(); rval(); expect("]"); fnref("slice_s"); op(O_CALL);
@@ -651,8 +660,9 @@ static void postfix(void)
expect("]"); fnref("at_s"); op(O_CALL);
lv = 0; xs = 0; xf = 0; lvi = -1;
} else { expect("]"); op(O_ADD); lv = 1; xs = 0; xf = 0; lvi = -1; }
+ xro = 0; lro = ro;
}
- else if (is("(")) { rval(); n = args(); lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); xf = 0; xs = 0; lvi = -1; }
+ else if (is("(")) { rval(); n = args(); lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); xf = 0; xs = 0; lvi = -1; xro = 0; lro = 0; }
else if (accept("++")) incdec(1, 1);
else if (accept("--")) incdec(1, -1);
else if (accept("as")) ptype0(); /* the whole type, as the compiler reads it: x as int*, x as char** */
@@ -668,7 +678,7 @@ static void unary(void)
}
else if (accept("!")) { unary(); rval(); ftest(); seq_isz(); xf = 0; xs = 0; }
else if (accept("~")) { unary(); rval(); lit(-1); op(O_XOR); xf = 0; xs = 0; }
- else if (accept("*")) { unary(); rval(); lv = 1; xs = 0; }
+ else if (accept("*")) { unary(); rval(); lv = 1; xs = 0; xro = 0; lro = 0; }
else if (accept("&")) { unary(); if (!lv) die("& needs an lvalue"); lv = 0; xs = 0; }
else if (accept("++")) { unary(); incdec(0, 1); }
else if (accept("--")) { unary(); incdec(0, -1); }
@@ -695,7 +705,7 @@ static void bin(int minp)
} else if ((lf || rf) && fis(o)) { fbinop(o, lf, rf); xs = 0; }
else { binop(o); xf = 0; xs = 0; }
}
- lv = 0; lvi = -1;
+ lv = 0; lvi = -1; xro = 0; lro = 0;
}
}
static void expr(void)
@@ -704,11 +714,13 @@ static void expr(void)
bin(1); dst = lvi;
if (is("=")) {
if (!lv) die("assignment needs an lvalue");
+ rocheck();
next(); expr(); rval();
op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0;
if (dst >= 0) { lo[dst].flt = xf; lo[dst].str = xs || lo[dst].dstr; }
} else if (tk == T_OP && strlen(ts) >= 2 && ts[strlen(ts) - 1] == '=' && strcmp(ts, "==") && strcmp(ts, "!=") && strcmp(ts, "<=") && strcmp(ts, ">=")) {
if (!lv) die("assignment needs an lvalue");
+ rocheck();
lf = xf; strcpy(o, ts); o[strlen(o) - 1] = 0; next();
lit(0); op(O_PICK); op(O_LD); lv = 0; expr(); rval();
if ((lf || xf) && fis(o)) fbinop(o, lf, xf);
@@ -780,7 +792,7 @@ static void stmt(void)
else if (is(".")) { next(); ptagname(); nw = 1; } /* a bare tag: .m p = new(3); */
else if (is("embed")) { /* embed "file" as [type] name; makes a local */
Lex L; lexsave(&L); next();
- if (tk == T_STR) { embeddecl(1); return; }
+ if (tk == T_STR) { free(L.ts); embeddecl(1); return; } /* the saved token is not restored */
lexrest(&L);
}
if (nw >= 0 || accept("let") || accept("var")) {
@@ -953,7 +965,7 @@ static int embed_width(const char *t, int star)
}
static Loc *newloc(const char *n)
{
- Loc *l = &lo[nlo++]; strcpy(l->name, n); l->slot = nslot++; l->arr = l->flt = l->str = l->dstr = 0;
+ Loc *l = &lo[nlo++]; strcpy(l->name, n); l->slot = nslot++; l->arr = l->flt = l->str = l->dstr = l->ro = 0;
return l;
}
static void embeddecl(int local)
@@ -1028,7 +1040,7 @@ static void embeddecl(int local)
emit((int64_t)u);
}
if (!count) emit(0);
- if (local) { setlit(j, here); l = newloc(n); lit(a); laddr(l->slot); op(O_ST); }
+ if (local) { setlit(j, here); l = newloc(n); l->ro = 1; lit(a); laddr(l->slot); op(O_ST); }
else add(n, K_GARR, a);
}
free(buf);
diff --git a/tests/conformance/known.txt b/tests/conformance/known.txt
index 9bec99b..54e7141 100644
--- a/tests/conformance/known.txt
+++ b/tests/conformance/known.txt
@@ -16,7 +16,6 @@
9-appendix-b/b2_14_len_char_ptr_typecheck_off.zc zy a appendix b.2.14 confirmed
9-appendix-b/b2_15_duplicate_case.zc zy c appendix b.2.15 confirmed; 4.7.2 says duplicates are allowed
9-appendix-b/b2_16_local_const_in_const_expr.zc zy c appendix b.2.16 confirmed; 3.14.2 documents it
-1-source/ident_tags_as_variables_glued_star.zc zc0 b u*d with visible u and d is read as a recast (no code); zy follows 1.6.3
2-types/conv_argument_and_return.zc zc0 b types are read and dropped: no conversion, no sized cut (appendix a)
2-types/conv_assignment_to_float.zc zc0 b types are read and dropped: no conversion, no sized cut (appendix a)
2-types/conv_f32_rounds.zc zc0 b types are read and dropped: no conversion, no sized cut (appendix a)
diff --git a/tests/programs/zc0embed_write.zc b/tests/programs/zc0embed_write.zc
new file mode 100644
index 0000000..09686b5
--- /dev/null
+++ b/tests/programs/zc0embed_write.zc
@@ -0,0 +1,9 @@
+include rt;
+// zc0 keeps a local embed array in the code, so it refuses a store into one; the
+// resident compiler accepts it (prints 9). tests/test.sh checks zc0's message.
+fn main() {
+ embed "tests/data/embed/bytes.bin" as u8[] b;
+ b[1] = 9;
+ rt.put_n(b[1]); rt.put_c(10);
+ return 0;
+}
diff --git a/tests/test.sh b/tests/test.sh
index cdce083..bc199e1 100755
--- a/tests/test.sh
+++ b/tests/test.sh
@@ -82,6 +82,7 @@ GI="-Isrc/core -I$MAP --embed-dir=$T/s1"
# it, and the run-only and bare-metal sections read it as the reference.
$CC -pthread -O2 -w -fwrapv -DJTYPE=int src/core/core.c src/core/io_linux.c $GI -o $T/zy || exit 1
chk "$($T/zc0 < tests/programs/zc0typed.zc | md5sum)" "$($T/zc0 < tests/programs/zc0plain.zc | md5sum)" "zc0 reads types and throws them away: typed and plain sources give one image"
+case "$($T/zc0 < tests/programs/zc0embed_write.zc 2>&1 >/dev/null)" in *"line 6: a local embed array is part of the code"*) chk 1 1 "zc0 refuses a store into a local embed array";; *) chk 0 1 "zc0 refuses a store into a local embed array";; esac
for n in $SECS; do
mkdir -p $ROOT/$n