aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/drivers/cli_help_test.py2
-rw-r--r--tests/drivers/lsp_test.js18
-rw-r--r--tests/langerrors/typedef_generic_bare.zc6
-rw-r--r--tests/langerrors/typedef_local_redefine.zc8
-rw-r--r--tests/langerrors/typedef_redefine.zc6
-rw-r--r--tests/langerrors/typedef_scope.zc8
-rw-r--r--tests/langerrors/typedef_string_redefine.zc5
-rw-r--r--tests/langerrors/typedef_tag.zc5
-rw-r--r--tests/langerrors/typedef_unknown.zc5
-rw-r--r--tests/langerrors/typedef_variable_clash.zc8
-rw-r--r--tests/langerrors/typedef_void_variable.zc5
-rw-r--r--tests/parity.known15
-rw-r--r--tests/programs/bareio.zc2
-rw-r--r--tests/programs/parity_ops.zc102
-rw-r--r--tests/programs/typedef.zc89
-rw-r--r--tests/programs/zc0bstr.zc96
-rw-r--r--tests/programs/zc0umd.zc58
-rwxr-xr-xtests/test.sh5
-rw-r--r--tests/typeerrors/typedef_mismatch.zc6
-rw-r--r--tests/typeerrors/typedef_pointer.zc7
20 files changed, 454 insertions, 2 deletions
diff --git a/tests/drivers/cli_help_test.py b/tests/drivers/cli_help_test.py
index 97eebea..1231f74 100644
--- a/tests/drivers/cli_help_test.py
+++ b/tests/drivers/cli_help_test.py
@@ -14,7 +14,7 @@ commands = []
if args.group in ('binaries', 'all'):
commands += [[str(Path(args.bin_dir) / name)] for name in ['zy', 'zym', 'zclsp', 'zc0', 'mkgenome', 'genmap']]
if args.group in ('scripts', 'all'):
- commands += [['bash', name] for name in ['tinyvm.sh', 'src/tools/zcc', 'src/tools/automap.sh', 'src/tools/findcc.sh', 'src/tools/bootstrap-check.sh', 'src/tools/mklsp.sh', 'src/tools/mklspwasm.sh', 'src/tools/mkvsix.sh', 'src/web/build.sh', 'src/baremetal/run.sh']]
+ commands += [['bash', name] for name in ['tinyvm.sh', 'src/tools/zcc', 'src/tools/automap.sh', 'src/tools/findcc.sh', 'src/tools/bootstrap-check.sh', 'src/tools/parity.sh', 'src/tools/mklsp.sh', 'src/tools/mklspwasm.sh', 'src/tools/mkvsix.sh', 'src/web/build.sh', 'src/baremetal/run.sh']]
if args.group in ('js', 'all'):
commands += [['node', name] for name in [str(Path(args.js_dir) / 'tinyvm.js'), 'src/js/mklayout.js', 'src/js/extract_lsp.js']]
diff --git a/tests/drivers/lsp_test.js b/tests/drivers/lsp_test.js
index 18d8bf7..0f4abe1 100644
--- a/tests/drivers/lsp_test.js
+++ b/tests/drivers/lsp_test.js
@@ -250,5 +250,23 @@ check(hcv && hcv.includes('parameter'), 'hover on a use of a parameter named lik
hcv = hover(scp, 4, at(scp, 4, 'len'));
check(hcv && hcv.includes('the number of items'), 'hover on the built-in still shows its text where no local shadows it', hcv);
+// typedefs: the name shows its declaration, at the declaration, at a use in a declaration, and in a block
+const tdsrc = 'struct node { int v; node* next; };\n// another name for node*\ntypedef node* link;\ntypedef int(int, int) binop;\nlink head(link n) { return n; }\nint use(string s) {\n\ttypedef int cell;\n\tcell c = 1;\n\tbinop f = 0;\n\treturn c;\n}\n';
+hv = hover(tdsrc, 2, at(tdsrc, 2, 'link'));
+check(hv && hv.includes('typedef node* link;') && hv.includes('another name for node*'), 'hover on the name in a typedef shows the typedef and its doc', hv);
+hv = hover(tdsrc, 4, at(tdsrc, 4, 'link'));
+check(hv && hv.includes('typedef node* link;') && hv.includes('a pointer to `node`'), 'hover on a typedef name used as a type shows the typedef and the type', hv);
+hv = hover(tdsrc, 4, at(tdsrc, 4, 'link', 1));
+check(hv && hv.includes('typedef node* link;'), 'hover on a typedef name used as a parameter type shows the typedef', hv);
+hv = hover(tdsrc, 3, at(tdsrc, 3, 'binop'));
+check(hv && hv.includes('typedef int(int, int) binop;'), 'hover on a function type typedef shows the whole typedef', hv);
+hv = hover(tdsrc, 7, at(tdsrc, 7, 'cell'));
+check(hv && hv.includes('typedef int cell;'), 'hover on a typedef made in a block shows the typedef', hv);
+hv = hover(tdsrc, 5, at(tdsrc, 5, 'string'));
+check(hv && hv.includes('char*') && hv.includes('built-in alias'), 'hover on string shows char* and what string is', hv);
+hv = hover(tdsrc, 2, 3);
+check(hv && hv.includes('typedef T name;') && hv.includes('type name = T;'), 'hover on the typedef keyword explains it', hv);
+check(tokens(tdsrc).includes('typedef:keyword'), 'typedef is highlighted as a keyword', tokens(tdsrc));
+
console.log(ok + ' lsp checks passed, ' + bad + ' failed');
process.exit(bad ? 1 : 0);
diff --git a/tests/langerrors/typedef_generic_bare.zc b/tests/langerrors/typedef_generic_bare.zc
new file mode 100644
index 0000000..5f3e906
--- /dev/null
+++ b/tests/langerrors/typedef_generic_bare.zc
@@ -0,0 +1,6 @@
+namespace;
+
+// expect: expected '<', found 'plain'
+struct box<T> { T item; };
+typedef box plain;
+fn main() { return 0; }
diff --git a/tests/langerrors/typedef_local_redefine.zc b/tests/langerrors/typedef_local_redefine.zc
new file mode 100644
index 0000000..e51c32f
--- /dev/null
+++ b/tests/langerrors/typedef_local_redefine.zc
@@ -0,0 +1,8 @@
+namespace;
+
+// expect: cell: already defined
+fn main() {
+ typedef int cell;
+ typedef char* cell;
+ return 0;
+}
diff --git a/tests/langerrors/typedef_redefine.zc b/tests/langerrors/typedef_redefine.zc
new file mode 100644
index 0000000..05085ec
--- /dev/null
+++ b/tests/langerrors/typedef_redefine.zc
@@ -0,0 +1,6 @@
+namespace;
+
+// expect: num: already defined
+typedef int num;
+typedef char* num;
+fn main() { return 0; }
diff --git a/tests/langerrors/typedef_scope.zc b/tests/langerrors/typedef_scope.zc
new file mode 100644
index 0000000..833e30e
--- /dev/null
+++ b/tests/langerrors/typedef_scope.zc
@@ -0,0 +1,8 @@
+namespace;
+
+// expect: expected ';', found 'x'
+fn main() {
+ { typedef int cell; cell a = 1; }
+ cell x = 2;
+ return 0;
+}
diff --git a/tests/langerrors/typedef_string_redefine.zc b/tests/langerrors/typedef_string_redefine.zc
new file mode 100644
index 0000000..226a94e
--- /dev/null
+++ b/tests/langerrors/typedef_string_redefine.zc
@@ -0,0 +1,5 @@
+namespace;
+
+// expect: string: already defined
+typedef int string;
+fn main() { return 0; }
diff --git a/tests/langerrors/typedef_tag.zc b/tests/langerrors/typedef_tag.zc
new file mode 100644
index 0000000..70ab491
--- /dev/null
+++ b/tests/langerrors/typedef_tag.zc
@@ -0,0 +1,5 @@
+namespace;
+
+// expect: a typedef names a type, which has no tag
+typedef char*.u owned;
+fn main() { return 0; }
diff --git a/tests/langerrors/typedef_unknown.zc b/tests/langerrors/typedef_unknown.zc
new file mode 100644
index 0000000..8076e86
--- /dev/null
+++ b/tests/langerrors/typedef_unknown.zc
@@ -0,0 +1,5 @@
+namespace;
+
+// expect: missing_t: isn't a type
+typedef missing_t thing;
+fn main() { return 0; }
diff --git a/tests/langerrors/typedef_variable_clash.zc b/tests/langerrors/typedef_variable_clash.zc
new file mode 100644
index 0000000..2c843f2
--- /dev/null
+++ b/tests/langerrors/typedef_variable_clash.zc
@@ -0,0 +1,8 @@
+namespace;
+
+// expect: count: already defined
+fn main() {
+ int count = 1;
+ typedef int count;
+ return count;
+}
diff --git a/tests/langerrors/typedef_void_variable.zc b/tests/langerrors/typedef_void_variable.zc
new file mode 100644
index 0000000..c5c7ced
--- /dev/null
+++ b/tests/langerrors/typedef_void_variable.zc
@@ -0,0 +1,5 @@
+namespace;
+
+// expect: a variable can't be void
+typedef void nothing;
+fn main() { nothing x; return 0; }
diff --git a/tests/parity.known b/tests/parity.known
new file mode 100644
index 0000000..0a73eb3
--- /dev/null
+++ b/tests/parity.known
@@ -0,0 +1,15 @@
+# programs where zc0 and the self-hosted compiler print different things on purpose.
+# src/tools/parity.sh still runs them and shows the difference as known, and it does
+# not fail the run; when one starts to agree it says so, and its line can go.
+# one program per line: the path, then # and the reason.
+#
+# zc0 reads types and ownership tags and throws them away. it has no ownership
+# analysis and no memdebug checks, so it neither warns about, traps on nor frees what
+# the self-hosted compiler does.
+tests/programs/doublefree.zc # memdebug on: only the self-hosted compiler emits the double free check
+tests/programs/scrub.zc # ownership: the analyzer warns about the use after free, zc0 runs it
+tests/programs/uaf.zc # ownership: the analyzer warns about the use after free; zc0 prints nothing
+tests/programs/uaf_in.zc # memdebug on: only the self-hosted compiler emits the use after free check
+examples/lists.zc # ownership: the block's owner frees at its end, zc0 never frees
+# the next program reads a local it never sets when stdin is empty, so it prints stack garbage
+tests/programs/bench_strings.zc # reads the repeat count from stdin; with none, acc is uninitialized
diff --git a/tests/programs/bareio.zc b/tests/programs/bareio.zc
index a06ef38..21d2b40 100644
--- a/tests/programs/bareio.zc
+++ b/tests/programs/bareio.zc
@@ -2,7 +2,7 @@ namespace;
include rt;
// bare-metal syscalls: clock, a RAM file, random, and the machine size.
-// zc0 subset, so a run-only image can carry it. names stay off u/m/d.
+// zc0 subset, so a run-only image can carry it.
fn main() {
let buf[16], n, t0, t1, i, fd, r, board[8];
t0 = rt.syscall(6, 0, 0, 0);
diff --git a/tests/programs/parity_ops.zc b/tests/programs/parity_ops.zc
new file mode 100644
index 0000000..1c3c760
--- /dev/null
+++ b/tests/programs/parity_ops.zc
@@ -0,0 +1,102 @@
+// operator precedence and associativity, the same in both compilers. every binary
+// operator is placed next to its neighbours in the table and between two of the same
+// kind, so a wrong binding changes a printed number. src/tools/parity.sh compiles
+// this program with zc0 and with the self-hosted compiler and compares the output.
+// it stays inside zc0's subset: no ?: (zc0 has none), no types beyond int.
+//
+// the table, tightest first: unary - ! ~ * &, then * / %, + -, << >>, < > <= >=,
+// == !=, &, ^, |, &&, ||, then the assignments, which group to the right. every
+// binary operator groups to the left. the expected output follows the c rules and
+// was checked by compiling the same expressions with a c compiler.
+namespace;
+include rt;
+
+int trace, calls;
+int seen(int v) { calls++; trace = trace * 10 + v; return v; }
+int id(int v) { return v; }
+int out(int v) { rt.put_n(v); rt.put_c(32); return v; }
+int endl() { rt.put_c(10); return 0; }
+
+int main() {
+ // operands the compilers cannot fold away
+ int a = id(2), b = id(3), c = id(4), e = id(5), z = id(0), one = id(1);
+
+ // * / % against + -, and left grouping
+ out(a + b * c); out(a * b + c); out(a - b * c); out(a * b - c); out((a + b) * c);
+ out(id(100) - 30 - 20); out(1 - 2 + 3); out(id(100) / 5 / 2); out(id(20) / 3 * 3);
+ out(a * b % c); out(id(100) % 7 % 3); out(id(100) / 10 % 4); out(id(7) % 4 * 3);
+ endl();
+
+ // + - against << >>, and left grouping
+ out(one << a + one); out(id(32) >> a - one); out(one << a << b); out(id(256) >> a >> one);
+ out(one << b * a); out(id(8) >> one << one);
+ endl();
+
+ // shifts against < > <= >=, and the relational chain
+ out(one << a < e); out(id(16) >> one > 7); out(a < b < c); out(e > c > b);
+ out(z <= e <= z); out(a >= b >= z);
+ endl();
+
+ // relations against == !=, and the equality chain
+ out(a < b == one); out(one == a > b); out(e == e == one); out(b != c != one);
+ out(id(3) > a == one < a);
+ endl();
+
+ // == against & ^ |
+ out(c & c == c); out(one | a == a); out(b ^ b == b); out(a == a & one); out(c != z | z);
+ endl();
+
+ // & ^ | against each other, and against + and <<
+ out(c | b & one); out(id(7) ^ e & b); out(one | 6 ^ b); out(id(6) & b | 8);
+ out(a + 6 & c); out(one & b << one); out(id(12) | one << a);
+ endl();
+
+ // && and || against | & and each other; the result of both is 0 or 1
+ out(one || z && z); out(z && z || one); out(z || one && one || z); out(z && one | one);
+ out(one || z & z); out(b && c); out(b & c); out(z || id(9)); out(id(7) && id(8) && z);
+ out(a < b && c < e || z); out(z == z || z && z);
+ endl();
+
+ // unary operators
+ out(-a * b); out(- -b); out(-a + e); out(!z + one); out(!a == z); out(!!id(7));
+ out(~z & 255); out(~one + one); out(~a & 7); out(-a * -b); out(a - -b); out(-id(7) % 4);
+ out(!(a < b) + one); out(*&a + one); out(-*&b);
+ endl();
+
+ // assignment groups to the right and takes the whole expression on its right
+ int x, y, w;
+ x = y = w = id(7); out(x); out(y); out(w);
+ x = id(10); x -= 3 - 1; out(x);
+ x *= a + b; out(x);
+ x <<= one + one; out(x);
+ x |= one << b; out(x);
+ x += y -= 2; out(x); out(y);
+ x = id(100); x /= a * b; out(x);
+ x %= 7 - 2; out(x);
+ x = id(6); x &= 3 | 8; out(x);
+ x ^= 1 + 2 * 3; out(x);
+ x = id(255); x >>= 2 + 2; out(x);
+ endl();
+
+ // division, remainder and shifts of negative numbers
+ out(id(0) - 7 / 2); out((id(0) - 7) / 2); out((id(0) - 7) % 3); out(id(7) % (id(0) - 3));
+ out((id(0) - 8) >> 1); out((id(0) - 8) << 1);
+ endl();
+
+ // short circuit: how many operands ran, and in what order
+ calls = 0; trace = 0; x = seen(0) && seen(1); out(x); out(calls); out(trace);
+ calls = 0; trace = 0; x = seen(1) || seen(2); out(x); out(calls); out(trace);
+ calls = 0; trace = 0; x = seen(1) || seen(2) && seen(3); out(x); out(calls); out(trace);
+ calls = 0; trace = 0; x = seen(0) || seen(2) && seen(3); out(x); out(calls); out(trace);
+ calls = 0; trace = 0; x = seen(0) && seen(2) || seen(3); out(x); out(calls); out(trace);
+ calls = 0; trace = 0; x = seen(1) && seen(0) || seen(2) && seen(3); out(x); out(calls); out(trace);
+ calls = 0; trace = 0; x = seen(1) | seen(2) && seen(0) & seen(3); out(x); out(calls); out(trace);
+ endl();
+
+ // operands run left to right whatever their binding
+ calls = 0; trace = 0; x = seen(1) + seen(2) * seen(3) - seen(4); out(x); out(calls); out(trace);
+ calls = 0; trace = 0; x = seen(1) << seen(2) < seen(3) == seen(4); out(x); out(calls); out(trace);
+ calls = 0; trace = 0; x = out(seen(1) - seen(2)) * out(seen(3)); out(x); out(calls); out(trace);
+ endl();
+ return 0;
+}
diff --git a/tests/programs/typedef.zc b/tests/programs/typedef.zc
new file mode 100644
index 0000000..307ef75
--- /dev/null
+++ b/tests/programs/typedef.zc
@@ -0,0 +1,89 @@
+namespace;
+include rt;
+typecheck on;
+
+// typedef T name; makes name another way to write T: the same type, so the two mix freely
+struct node { int v; node* next; };
+struct pair<A, B> { A a; B b; };
+
+typedef int count;
+typedef char* label;
+typedef node* link;
+typedef link* links;
+typedef int[4] quad;
+typedef int(int, int) binop;
+typedef pair<int, int> ipair;
+typedef int[?] ints;
+typedef count counter;
+
+struct box { count n; link head; ipair p; };
+
+count twice(count x) { return x + x; }
+count add(int a, int b) { return a + b; }
+count apply(binop f, count a, count b) { return f(a, b); }
+count sum(link n) { count s = 0; while (n) { s += n.v; n = n.next; } return s; }
+label hello() { return "hello"; }
+string greet(string s) { return s; }
+// string is only a type where a type is expected: an untyped parameter can still be called string
+fn twice_name(string) { return string + string; }
+count first_of(links ls) { return ls[0].v; }
+T pick<T>(T x) { typedef T item; item y = x; return y; }
+count scoped() {
+ count total = 0;
+ { typedef int cell; cell c = 3; total += c; }
+ { typedef char* cell; cell c = "ab"; total += rt.len_w(c); }
+ return total;
+}
+count scoped_again() { typedef quad cell; cell c; c[2] = 7; return c[2] + sizeof(cell); }
+
+// a link is a node*: every use below mixes the two
+count same_ptr(node* p, link q) { return p == q; }
+count casts(link p) {
+ word w = p as word;
+ link back = w as link;
+ node* n = (node*)back;
+ return back.v + ((link)n == p);
+}
+count boxed(link p) {
+ box bx; bx.n = 6; bx.head = 0; bx.p.a = 1; bx.p.b = 2;
+ pair<link, count> pl; pl.a = 0; pl.b = 5;
+ return bx.n + p.v + bx.p.a + bx.p.b + pl.b + !bx.head + !pl.a;
+}
+count by_ref(links ls) { return first_of(ls); }
+
+fn main() {
+ link a = rt.new(node), b = rt.new(node);
+ a.v = 3; b.v = 4; a.next = b;
+ rt.put_n(sum(a)); rt.put_c(' ');
+ rt.put_n(same_ptr(a, a)); rt.put_c(' ');
+ rt.put_n(twice(21)); rt.put_c(' ');
+ rt.put_n(apply(add, 2, 3)); rt.put_c(' ');
+ binop op = add;
+ rt.put_n(op(10, 20)); rt.put_c(10);
+
+ quad q; q[3] = 9;
+ rt.put_n(q[3] + sizeof(quad)); rt.put_c(' ');
+ rt.put_n(sizeof(count) + sizeof(link) + sizeof(ipair)); rt.put_c(' ');
+ rt.put_n(boxed(a)); rt.put_c(' ');
+ ints xs = {1, 2, 3};
+ push(xs, 4);
+ rt.put_n(len(xs)); rt.put_c(10);
+
+ rt.put_n(casts(a)); rt.put_c(' ');
+ counter k = (count)3;
+ rt.put_n(k); rt.put_c(' ');
+ links ls = &a;
+ rt.put_n(by_ref(ls)); rt.put_c(' ');
+ rt.put_n(pick<count>(11) + pick(1)); rt.put_c(10);
+
+ rt.put_n(scoped()); rt.put_c(' ');
+ rt.put_n(scoped_again()); rt.put_c(10);
+
+ string s = "hi";
+ label l = s;
+ char* c = l;
+ rt.put_s(s); rt.put_s(l); rt.put_s(c); rt.put_s(hello()); rt.put_s(greet("ok")); rt.put_c(10);
+ rt.put_n(twice_name(4)); rt.put_c(10);
+ rt.free(d*a);
+ return 0;
+}
diff --git a/tests/programs/zc0bstr.zc b/tests/programs/zc0bstr.zc
new file mode 100644
index 0000000..bb4ffbc
--- /dev/null
+++ b/tests/programs/zc0bstr.zc
@@ -0,0 +1,96 @@
+namespace;
+include rt;
+
+// packed byte strings in the zc0 subset: the same program runs on the resident
+// compiler and, built into a run-only image, on stage 0 (src/tools/zc0.c). it
+// covers every b"..." escape, the packed layout, bstr locals, globals,
+// parameters and returns, + and the six comparisons, indexing, slicing, len
+// and embed. run from the repository root: the embeds read tests/data/embed.
+
+bstr greeting = b"hello";
+bstr shout;
+bstr plain = b"plain";
+embed "tests/data/embed/text.txt" as bstr text;
+embed "tests/data/embed/nul.bin" as bstr nul;
+embed "tests/data/embed/text.txt" as untyped;
+
+bstr tag(bstr s) { return b"<" + s + b">"; }
+bstr twice(bstr s);
+bstr twice(bstr s) { return s + s; }
+bstr pick(int n) {
+ bstr r;
+ if (n) r = b"yes"; else r = b"no";
+ return r;
+}
+int rank(bstr a, bstr b) { return (a < b) * 8 + (a <= b) * 4 + (a == b) * 2 + (a > b); }
+int count(bstr s, int c) {
+ int n = 0, i = 0;
+ while (i < len(s)) { if (s[i] == c) n++; i++; }
+ return n;
+}
+int num(int n) { rt.put_n(n); rt.put_c(32); return 0; }
+int nl() { rt.put_c(10); return 0; }
+int bytes(bstr s) {
+ int i = 0;
+ num(len(s));
+ while (i < len(s)) { num(s[i]); i++; }
+ nl();
+ return 0;
+}
+int words(bstr s) {
+ word* w = rt.bstr_storage(s);
+ int n = 1 + (w[0] >> 3) + 1, i = 0;
+ while (i < n) { num(w[i]); i++; }
+ nl();
+ return 0;
+}
+
+fn main() {
+ bstr a = b"abc", b, c;
+ bstr e = b"";
+ let u = b"x";
+ // every escape the lexer knows: n t r 0 e, and any other character stands for itself
+ bytes(b"\n\t\r\e\\\"\'\q\x41");
+ bytes(b"ab\0cd");
+ bytes(b"\0");
+ bytes(b"café");
+ bytes(b"caf\303\251");
+ bytes(b"quote \" inside, 'single', back\\slash");
+ // the layout: a length word, bytes packed 8 to a word, a 0 word after a full last word
+ words(e);
+ words(b"a");
+ words(b"abcdefg");
+ words(b"abcdefgh");
+ words(b"abcdefghi");
+ words(b"0123456789abcdef");
+ words(b"0123456789abcdefg");
+ words(text);
+ words(nul);
+ // declarations, assignment later, a return value, concatenation
+ b = tag(a);
+ c = twice(b) + e + u;
+ rt.put_s(a); rt.put_c(32); rt.put_s(b); rt.put_c(32); rt.put_s(c); nl();
+ shout = twice(greeting) + b"!";
+ rt.put_s(greeting); rt.put_c(32); rt.put_s(shout); rt.put_c(32); rt.put_s(plain); nl();
+ rt.put_s(tag(twice(tag(b"z")))); nl();
+ rt.put_s(pick(1) + b"/" + pick(0)); nl();
+ // comparisons: below, equal, above, a prefix, the empty string, a 9 byte string
+ num(rank(b"abc", b"abd")); num(rank(b"abc", b"abc")); num(rank(b"abd", b"abc"));
+ num(rank(b"ab", b"abc")); num(rank(b"", b"")); num(rank(b"", b"a"));
+ num(rank(b"abcdefghi", b"abcdefghi")); num(rank(b"abcdefgh", b"abcdefghi")); nl();
+ num(a == b"abc"); num(a != b"abc"); num(a == b); num(a != b);
+ num(tag(a) == b"<abc>"); num(shout > greeting); num(greeting >= shout); num(plain <= plain); nl();
+ // indexing, slicing, len
+ num(a[0]); num(a[2]); num(a[3]); num(len(a)); num(len(e)); num(len(c)); num(len(b"123456789")); nl();
+ rt.put_s(c[1:4]); rt.put_c(32); rt.put_s(a[1:2]); rt.put_c(32); rt.put_s(a[0:0]); rt.put_c('|');
+ rt.put_s(a[2:99]); rt.put_c('|'); rt.put_s(a[3:1]); rt.put_c('|'); rt.put_s((a + a)[2:4]); nl();
+ num(count(c, '<')); num(count(shout, 'l')); num(count(b"mississippi", 's')); num(count(e, 'x')); nl();
+ num(b"xyz"[1]); num(len(b"xyz")); num(len(a + a + a)); num(len(tag(e))); nl();
+ // embed: a global of the file's bytes, with or without the type, and a local
+ rt.put_s(text); num(len(text)); num(text[0]); num(text == untyped); num(len(nul)); num(nul[2]); nl();
+ embed "tests/data/embed/text.txt" as bstr here;
+ embed "tests/data/embed/answer.bin" as int answer;
+ embed "tests/data/embed/text.txt" as char* chars;
+ rt.put_s(here + b"|" + untyped); num(len(here)); num(here == text); num(answer); num(chars[6]); nl();
+ return 0;
+}
diff --git a/tests/programs/zc0umd.zc b/tests/programs/zc0umd.zc
new file mode 100644
index 0000000..9693111
--- /dev/null
+++ b/tests/programs/zc0umd.zc
@@ -0,0 +1,58 @@
+namespace;
+include rt;
+
+// u, m and d are ordinary names, and a tag is written .u, .m or .d after a type.
+// u*x, m*x and d*x with no space before the star is a recast, and u * x with a
+// space is a product. the same program runs on the resident compiler and, built
+// into a run-only image, on stage 0 (src/tools/zc0.c), which reads the tags and
+// recasts and does nothing with them.
+
+int u = 2, m = 3; // globals
+int[4] d4;
+int d(int x) { return x + 1; } // a function
+
+int mul3(int u, int m, int d) { return u * m * d; } // parameters
+int mixed(int.d u, int m, int d) { return u * m - d; }
+int tagged(int(int) f, int.m x) { return f(x); }
+
+// owned objects through locals named for their tags
+word*.u box(int v) { word* m = rt.new(2); m[0] = v; m[1] = v * 2; return u*m; }
+word*.u pair(int u, int m) {
+ word* d = rt.new(2);
+ d[0] = u; d[1] = m;
+ return u*d;
+}
+.u single(int v) { let u = rt.new(1); u[0] = v; return u*u; }
+int sum(word*.m m) { return m[0] + m[1]; }
+int eat(word*.d d) { return d[0] + d[1]; }
+int hold(.m u, .d d) { return u[0] * 100 + d[0]; }
+int dig(word*.m u) { int m = u[0], d = u[1]; return m * 10 + d; }
+
+fn main() {
+ int a = 4, b = 5;
+ int.m n = 9;
+ word*.m p = box(7);
+ .m q = single(6);
+ word* r = pair(8, 9);
+ let c = box(10), e = pair(1, 2);
+ rt.put_n(u * m); rt.put_c(32); // two globals multiplied
+ rt.put_n(u*=m); rt.put_c(32); // u *= m is not a recast either
+ rt.put_n(d(u) * d(m)); rt.put_c(32);
+ rt.put_n(mul3(2, 3, 4)); rt.put_c(32);
+ rt.put_n(mixed(7, 6, 5)); rt.put_c(32);
+ rt.put_n(tagged(d, n)); rt.put_c(10);
+ {
+ int u = 6, m = 7, d = 8; // locals shadow the globals
+ rt.put_n(u * m); rt.put_c(32);
+ rt.put_n(u * d - m); rt.put_c(32);
+ rt.put_n(m * u * d); rt.put_c(32);
+ rt.put_n(a * u + m * b); rt.put_c(10);
+ }
+ rt.put_n(sum(p)); rt.put_c(32);
+ rt.put_n(q[0]); rt.put_c(32);
+ rt.put_n(r[0] * r[1]); rt.put_c(32);
+ rt.put_n(dig(c)); rt.put_c(32);
+ rt.put_n(hold(e, single(5))); rt.put_c(32);
+ rt.put_n(eat(d*p)); rt.put_c(10);
+ return 0;
+}
diff --git a/tests/test.sh b/tests/test.sh
index 7ae7b9d..223c9aa 100755
--- a/tests/test.sh
+++ b/tests/test.sh
@@ -102,10 +102,12 @@ run(){ # $1 = vm command, $2 = label
chk "$($vm < tests/programs/basics.zc | tr '\n' '/')" "285 610 42 512/hey 65 -3 -2 1024 -8/12456/757/9 100-101/5/ok/" "$l language basics"
chk "$($vm < tests/programs/umd.zc)" "100 6 0 15 99 5 5" "$l u m d lifetimes"
chk "$($vm < tests/programs/names.zc)" "12 1 8" "$l u, m and d are ordinary names"
+ chk "$($vm < tests/programs/zc0umd.zc | tr '\n' '/')" "6 6 28 24 37 10/42 41 336 59/21 6 72 120 105 21/" "$l u, m and d as names, products, recasts and tags (the zc0 subset)"
chk "$($vm < tests/programs/gc.zc)" "1000 1 900 1 199900" "$l mark-sweep collector"
chk "$($vm < tests/programs/meta.zc | tr '\n' '/')" "1024 19/unless ok/rrr/15 42/7/42/7/144/" "$l parse words, quote, eval, load, operator tables"
chk "$($vm < tests/programs/misc.zc | tr '\n' '/')" "45/54321/ok/" "$l compile-time eval, patched mnemonics, unchecked"
chk "$($vm < tests/programs/types.zc | tr '\n' '/')" "12 5 2 15 56 42 7 14 3/rect rect* list<int>* list<T>*.u(int) T(list<T>*) fn(word.d)/a:point@0 b:point@2 tag:int@4 /point 1 1/" "$l types, generics, reflection"
+ chk "$($vm < tests/programs/typedef.zc | tr '\n' '/')" "7 1 42 5 30/13 4 19 4/4 3 3 12/5 11/hihihihellook/8/" "$l typedef, global and local, in every place a type goes, and string"
chk "$($vm < tests/programs/typeok.zc)" "14" "$l a program the typechecker accepts"
chk "$($vm < tests/programs/typeoff.zc)" "6" "$l the typechecker is off by default"
chk "$($vm < tests/programs/enhance.zc)" "100 0 343 3,2 5 -2 0" "$l virtual enhancements"
@@ -357,6 +359,9 @@ s_runonly() {
runonly strings-ex examples/strings.zc
runonly share tests/programs/share.zc
runonly embed tests/programs/embed_top.zc
+ runonly zc0bstr tests/programs/zc0bstr.zc
+ runonly zc0umd tests/programs/zc0umd.zc
+ runonly wiring examples/wiring.zc
d=$T/run-echo; GENMAP=$T/genmap bash src/tools/automap.sh run $d src/runtime/run.zc examples/echo.zc >/dev/null && cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h $d/ \
&& $CC -pthread -DZY_THREADS -O2 -w -fwrapv $d/micro.c $d/port_linux.c -o $T/zymr \
&& chk "$(printf 'abc\n' | $T/zymr | tr '\n' '/')" "$( (cat examples/echo.zc; printf '`abc\n') | $T/zy | tr '\n' '/')" "run echo" \
diff --git a/tests/typeerrors/typedef_mismatch.zc b/tests/typeerrors/typedef_mismatch.zc
new file mode 100644
index 0000000..26453dd
--- /dev/null
+++ b/tests/typeerrors/typedef_mismatch.zc
@@ -0,0 +1,6 @@
+namespace;
+
+// expect: type error: initializer: expected int, got char*
+typecheck on;
+typedef int count;
+fn main() { count c = "text"; return 0; }
diff --git a/tests/typeerrors/typedef_pointer.zc b/tests/typeerrors/typedef_pointer.zc
new file mode 100644
index 0000000..5dbbb17
--- /dev/null
+++ b/tests/typeerrors/typedef_pointer.zc
@@ -0,0 +1,7 @@
+namespace;
+
+// expect: type error: initializer: expected node*, got int*
+typecheck on;
+struct node { int v; };
+typedef node* link;
+fn main() { int n = 0; link p = &n; return 0; }