diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/conformance/3-expressions/dyn_array_const_index_warning.zc | 2 | ||||
| -rw-r--r-- | tests/conformance/3-expressions/dyn_array_index_negative.zc | 2 | ||||
| -rw-r--r-- | tests/conformance/3-expressions/dyn_array_index_out_of_range.zc | 2 | ||||
| -rw-r--r-- | tests/conformance/3-expressions/dyn_array_pop_empty.zc | 2 | ||||
| -rw-r--r-- | tests/parity.known | 3 | ||||
| -rw-r--r-- | tests/programs/bounds_fixed.zc | 17 | ||||
| -rw-r--r-- | tests/programs/bounds_heap.zc | 16 | ||||
| -rw-r--r-- | tests/programs/bounds_hi.zc | 3 | ||||
| -rw-r--r-- | tests/programs/boundsrules.zc | 24 | ||||
| -rw-r--r-- | tests/programs/o3_bounds.zc | 3 | ||||
| -rw-r--r-- | tests/programs/o3call.zc | 2 | ||||
| -rw-r--r-- | tests/programs/zc0unbounded.zc | 16 | ||||
| -rwxr-xr-x | tests/test.sh | 11 |
13 files changed, 93 insertions, 10 deletions
diff --git a/tests/conformance/3-expressions/dyn_array_const_index_warning.zc b/tests/conformance/3-expressions/dyn_array_const_index_warning.zc index 36890d7..9639128 100644 --- a/tests/conformance/3-expressions/dyn_array_const_index_warning.zc +++ b/tests/conformance/3-expressions/dyn_array_const_index_warning.zc @@ -1,4 +1,4 @@ -// expect: zc: line 5: warning: out-of-bounds access: index 2 is outside length 2 (storage declared or allocated on line 4)/zy: index 2 out of range (length 2)/ +// expect: zc: line 5: warning: out-of-bounds access: index 2 is outside length 2 (storage declared or allocated on line 4)/zy: index 2 out of range (length 2)/ at <stdin>:5 in main/ include rt; fn main() { int[?] a = {1, 2}; diff --git a/tests/conformance/3-expressions/dyn_array_index_negative.zc b/tests/conformance/3-expressions/dyn_array_index_negative.zc index c87e786..023951e 100644 --- a/tests/conformance/3-expressions/dyn_array_index_negative.zc +++ b/tests/conformance/3-expressions/dyn_array_index_negative.zc @@ -1,4 +1,4 @@ -// expect: zy: index -1 out of range (length 2)/ +// expect: zy: index -1 out of range (length 2)/ at <stdin>:7 in main/ include rt; void pn(int x) { rt.put_n(x); rt.put_c(10); } fn main() { diff --git a/tests/conformance/3-expressions/dyn_array_index_out_of_range.zc b/tests/conformance/3-expressions/dyn_array_index_out_of_range.zc index eff8061..e613b70 100644 --- a/tests/conformance/3-expressions/dyn_array_index_out_of_range.zc +++ b/tests/conformance/3-expressions/dyn_array_index_out_of_range.zc @@ -1,4 +1,4 @@ -// expect: 2/zy: index 2 out of range (length 2)/ +// expect: 2/zy: index 2 out of range (length 2)/ at <stdin>:8 in main/ include rt; void pn(int x) { rt.put_n(x); rt.put_c(10); } fn main() { diff --git a/tests/conformance/3-expressions/dyn_array_pop_empty.zc b/tests/conformance/3-expressions/dyn_array_pop_empty.zc index 6f5fdd0..ce08fca 100644 --- a/tests/conformance/3-expressions/dyn_array_pop_empty.zc +++ b/tests/conformance/3-expressions/dyn_array_pop_empty.zc @@ -1,4 +1,4 @@ -// expect: zy: index -1 out of range (length 0)/ +// expect: zy: index -1 out of range (length 0)/ at <stdin>:6 in main/ include rt; void pn(int x) { rt.put_n(x); rt.put_c(10); } fn main() { diff --git a/tests/parity.known b/tests/parity.known index 0a73eb3..6892499 100644 --- a/tests/parity.known +++ b/tests/parity.known @@ -13,3 +13,6 @@ tests/programs/uaf_in.zc # memdebug on: only the self-hosted compiler emits the 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 +# zc0 reads unbounded and ignores it, and emits no index checks at all +tests/programs/bounds_fixed.zc # bounds checks: only the self-hosted compiler checks a fixed array index +tests/programs/bounds_heap.zc # bounds checks: only the self-hosted compiler checks a pointer into a heap block diff --git a/tests/programs/bounds_fixed.zc b/tests/programs/bounds_fixed.zc new file mode 100644 index 0000000..0b3f476 --- /dev/null +++ b/tests/programs/bounds_fixed.zc @@ -0,0 +1,17 @@ +namespace; +include rt; + +// a fixed array T[n] with a variable index is checked against n. a loop whose +// test is the constant n proves the index and drops the check; a constant +// index is checked when it is compiled. +int main() { + int[6] t; int i, s = 0, k = 6; + for (i = 0; i < 6; i++) t[i] = i * i; + for (i = 0; i < 6; i++) s = s + t[i]; + rt.put_n(s); rt.put_c(10); + i = 2; rt.put_n(t[i] + t[5]); rt.put_c(10); + i = 0; + while (i <= k) { s = t[i]; i++; } + rt.put_s("not reached\n"); + return 0; +} diff --git a/tests/programs/bounds_heap.zc b/tests/programs/bounds_heap.zc new file mode 100644 index 0000000..d0bd318 --- /dev/null +++ b/tests/programs/bounds_heap.zc @@ -0,0 +1,16 @@ +namespace; +include rt; + +// a pointer to the start of a live heap block is checked against the block's +// payload, which a buddy block rounds up: rt.new(4) holds 11 words. a pointer +// into the middle of a block, or to memory that is not a heap block, is not +// checked. a char is a word, so a char* counts words. +int main() { + int* p = rt.new(4); int* q; char* c = rt.new(2); int i = 10; + p[i] = 3; rt.put_n(p[i]); rt.put_c(10); + q = p + 1; i = 10; rt.put_n(q[i - 1] - 3); rt.put_c(10); + i = 2; c[i] = 'x'; rt.put_c(c[i]); rt.put_c(10); + i = 11; + rt.put_n(p[i]); rt.put_c(10); + return 0; +} diff --git a/tests/programs/bounds_hi.zc b/tests/programs/bounds_hi.zc index 49639c5..5475764 100644 --- a/tests/programs/bounds_hi.zc +++ b/tests/programs/bounds_hi.zc @@ -1,7 +1,8 @@ namespace; include rt; -// the index is not the induction variable. i + 1 walks off the end. +// the index is not the induction variable. i + 1 walks off the end, but o3 +// implies unbounded: nothing checks it, and the write lands in the block's slack o3 fn main() { fixed int[?] f = rt.new(fixed int[?], 4); int n = len(f), i; diff --git a/tests/programs/boundsrules.zc b/tests/programs/boundsrules.zc new file mode 100644 index 0000000..1ae6a56 --- /dev/null +++ b/tests/programs/boundsrules.zc @@ -0,0 +1,24 @@ +namespace; +include rt; + +// unbounded turns the checks off: as a function prefix (in any order with +// unchecked), as a block, for one statement. o3 implies it. the last read is +// checked and stops the program. +int[8] pad; +int[4] a; + +unbounded int pre(int i) { return a[i] * 0 + 1; } +unchecked unbounded int both(int i) { return a[i] * 0 + 1; } +o3 int fast(int i) { return a[i] * 0 + 1; } +int blk(int i) { int r = 0; unbounded { r = a[i] * 0 + 1; } return r; } +int one(int i) { int r = 0; unbounded r = a[i] * 0 + 1; return r; } +int checked(int i) { return a[i]; } + +int main() { + int i = 5; + a[3] = 7; + rt.put_n(pre(i) + both(i) + fast(i) + blk(i) + one(i)); rt.put_c(10); + rt.put_n(checked(3)); rt.put_c(10); + rt.put_n(checked(i)); rt.put_c(10); + return 0; +} diff --git a/tests/programs/o3_bounds.zc b/tests/programs/o3_bounds.zc index eaf92eb..cd65548 100644 --- a/tests/programs/o3_bounds.zc +++ b/tests/programs/o3_bounds.zc @@ -1,5 +1,6 @@ namespace; include rt; -// an index outside a T[?] inside o3 code: caught by the inline check +// an index outside a T[?] inside o3 code: o3 implies unbounded, so nothing checks it +// (the write lands in the slack of the buddy block) and the program ends normally fn main() { int[?] a = rt.new(int[?], 5); o3 { int i = 7; a[i] = 1; } rt.free(d*a); return 0; } diff --git a/tests/programs/o3call.zc b/tests/programs/o3call.zc index d2292ad..3ad977b 100644 --- a/tests/programs/o3call.zc +++ b/tests/programs/o3call.zc @@ -19,7 +19,7 @@ unchecked o3 int nest(int n) { if (n == 0) return 0; return n + nest(n - 1); } -unchecked fn main() { +unchecked unbounded fn main() { // reads p[-1], the type word, on purpose node* n = leaf(4); int* p = n; rt.put_n(p[-1] < 0); rt.put_c(' '); diff --git a/tests/programs/zc0unbounded.zc b/tests/programs/zc0unbounded.zc new file mode 100644 index 0000000..67c3ae0 --- /dev/null +++ b/tests/programs/zc0unbounded.zc @@ -0,0 +1,16 @@ +namespace; +include rt; + +// unbounded in all three places zc0 accepts it (and ignores): the unit, a +// function prefix and a statement. both compilers print the same. +unbounded; +int[4] a; +unbounded int get(int i) { return a[i]; } +int main() { + int i = 2, r = 0; + a[2] = 40; + unbounded r = get(i) + 2; + unbounded { r = r + a[i - 2]; } + rt.put_n(r); rt.put_c(10); + return 0; +} diff --git a/tests/test.sh b/tests/test.sh index a4e58e8..7a80c3c 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -220,10 +220,14 @@ ABC" "$l o3 lifts byte loads and stores" chk "$($vm < tests/programs/o3licm.zc)" "3184200 7950 315 50 260 -56" "$l o3 hoists loop invariants and shares values between blocks" chk "$($vm < tests/programs/o3deep.zc)" "30000" "$l o3 recursion thirty thousand deep" chk "$($vm < tests/programs/o3deep_over.zc 2>&1)" "100000" "$l o3 recursion grows past the original stack" - chk "$($vm < tests/programs/o3_bounds.zc 2>&1 | head -1)" "zy: index 7 out of range (length 5)" "$l o3 bounds check" + chk "$($vm < tests/programs/o3_bounds.zc 2>&1; echo "rc=$?")" "rc=0" "$l o3 implies unbounded: no check on a T[?] index" chk "$($vm < tests/programs/bounds.zc)" "55 55 8" "$l a proved +1 loop skips the bounds check" chk "$($vm < tests/programs/bounds_lo.zc 2>&1 | head -1)" "zy: index -1 out of range (length 4)" "$l a negative loop index is still checked" - chk "$($vm < tests/programs/bounds_hi.zc 2>&1 | head -1)" "zy: index 4 out of range (length 4)" "$l an index that is not the induction variable is still checked" + chk "$($vm < tests/programs/bounds_hi.zc 2>&1; echo "rc=$?")" "rc=0" "$l o3 implies unbounded: an index that is not the induction variable is not checked" + chk "$($vm < tests/programs/boundsrules.zc 2>&1 | head -3)" $'5\n7\nzy: index 5 out of range (length 4)' "$l unbounded as prefix, block and statement, o3 implies it; checked code reports" + chk "$($vm < tests/programs/bounds_fixed.zc 2>&1 | head -3)" $'55\n29\nzy: index 6 out of range (length 6)' "$l a fixed array with a variable index is checked against its length" + chk "$($vm < tests/programs/bounds_heap.zc 2>&1 | head -4)" $'3\n0\nx\nzy: index 11 out of range (length 11)' "$l a pointer to a heap block is checked against its payload" + chk "$($vm < tests/programs/zc0unbounded.zc)" "42" "$l unbounded in the unit, a prefix and a statement" local r; r=$(bash tests/runerr.sh "$vm" tests/errors | tail -1) chk "$r" "rejected: $(ls tests/errors/*.zc | wc -l | tr -d ' ') passed, 0 failed" "$l analyzer rejects every bad program" r=$(bash tests/runerr.sh "$vm" tests/typeerrors | tail -1) @@ -357,7 +361,7 @@ s_runonly() { runonly() { # $1 label, $2 source. the program is already in the image, so it reads no source. # the map is sized to the program: src/tools/automap.sh lays it out for the image's own length local a b d=$T/run-$1 input=$2 - if [ "$1" = gc ]; then input=$T/collector-progress.zc; sed 's/f >= 990/f > 0/' "$2" > "$input"; fi + if [ "$1" = gc ]; then input=$T/collector-progress.zc; sed 's/f >= 950/f > 0/' "$2" > "$input"; fi GENMAP=$T/genmap bash src/tools/automap.sh run $d src/runtime/run.zc "$input" >/dev/null || { chk 0 1 "run $1 builds"; return; } 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 0 1 "run $1 links"; return; } @@ -370,6 +374,7 @@ s_runonly() { runonly wide tests/programs/wide.zc runonly selfpatch examples/selfpatch.zc runonly gc tests/programs/gc.zc + runonly zc0unbounded tests/programs/zc0unbounded.zc # zc0 accepts unbounded in all three places and ignores it runonly voidmain tests/programs/void_main.zc runonly trace tests/programs/trace_run.zc # the image carries line tables: a fault and rt.trace() print the same backtrace as the resident compiler's runonly float tests/programs/float.zc |
