aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-15 02:40:12 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-15 02:40:12 +0330
commit0970a68bcb9165aca8512ecd9ee9c0077abb9468 (patch)
tree340714bee0b60a9b028924794573409b8e8b439b
parenttests: zc0typed.zc drops the .u on a typed parameter, which zc0 rejects since... (diff)
compiler: a //:lines marker starts a new file, so unbounded; and unchecked; of the source before it end there: a program joined to the unbounded runtime in a self-hosted run-only image is bounds-checked again (since d55b5fd its index errors went unreported); runtime_image_test expects the trap's exit status 2; spec 4.15.1, 4.x unchecked, 5.15.3
-rw-r--r--docs/SPEC.md6
-rw-r--r--src/compiler/base.zc4
-rw-r--r--tests/drivers/runtime_image_test.py8
-rw-r--r--tests/xref.baseline4
4 files changed, 13 insertions, 9 deletions
diff --git a/docs/SPEC.md b/docs/SPEC.md
index 7177b14..ed7822b 100644
--- a/docs/SPEC.md
+++ b/docs/SPEC.md
@@ -610,7 +610,7 @@ switch (s) { case circle(r): pn(r); case rect(w, h): pn(w * h); case dot: ps("do
### 4.9 unchecked
-`unchecked stmt` runs the statement with the analyzer off (6.12). `unchecked;` at top level switches the analyzer off for the rest of the unit. `unchecked fn f() {}` makes one function unchecked. code in an unchecked block cannot touch a local that the analyzer tracks (error: x: is tracked by the analyzer and can't be used in unchecked code).
+`unchecked stmt` runs the statement with the analyzer off (6.12). `unchecked;` at top level switches the analyzer off for the rest of the unit, or up to the next `//:lines` marker (5.15.3). `unchecked fn f() {}` makes one function unchecked. code in an unchecked block cannot touch a local that the analyzer tracks (error: x: is tracked by the analyzer and can't be used in unchecked code).
### 4.10 o3
@@ -663,7 +663,7 @@ int y = 7; asm { y @ 1 + y ! } // y is 8
### 4.15 unbounded
-4.15.1 `unbounded stmt` (a block or a single statement) compiles the statement without index bounds checks (3.7.4). `unbounded;` at top level does it for the rest of the unit. `unbounded fn f() {}` (also `unbounded T f(...)`, a method or a closure) makes one function unbounded; the prefix combines with `unchecked`, `immediate` and `o3` in any order. o3 code is unbounded. `unbounded` changes no result of a program whose indexes are in range.
+4.15.1 `unbounded stmt` (a block or a single statement) compiles the statement without index bounds checks (3.7.4). `unbounded;` at top level does it for the rest of the unit, or up to the next `//:lines` marker (5.15.3), so a program joined to the unbounded runtime in a run-only image is checked. `unbounded fn f() {}` (also `unbounded T f(...)`, a method or a closure) makes one function unbounded; the prefix combines with `unchecked`, `immediate` and `o3` in any order. o3 code is unbounded. `unbounded` changes no result of a program whose indexes are in range.
4.15.2 the runtime, the core, the compiler, the language server, `run.zc` and `build.zc` are unbounded; std, icon and actor are checked except where a function reads headers on purpose.
@@ -809,7 +809,7 @@ rt.put_n(util.twice(1)); // error: module access requires `include util;`
5.15.2 a line table changes no result of a program, only what a trace prints (8.4). a function compiled with lines off shows in a trace by its address and nearest name, or not at all when it belongs to the genome.
-5.15.3 the comment `//:lines NAME` names the source after it NAME, counts its lines from the next line, and turns lines on from the next top-level item.
+5.15.3 the comment `//:lines NAME` names the source after it NAME, counts its lines from the next line, and turns lines on from the next top-level item. it starts a new file: from the same item, `unbounded;` and `unchecked;` of the source before it no longer apply.
## 6. ownership and lifetimes
diff --git a/src/compiler/base.zc b/src/compiler/base.zc
index 88708f0..99ee10f 100644
--- a/src/compiler/base.zc
+++ b/src/compiler/base.zc
@@ -1321,11 +1321,13 @@ int lt_end(int b, int end) {
return 0;
}
// at the top level, a marker that comes before the item takes effect: lines are on, in its file.
+// a marker starts a new file, so the file switches of the one before it (unbounded; and
+// unchecked; in run.zc or rt.zc) end there: a program joined to the runtime is checked.
int lt_apply_marks() {
if (incdepth || ginst) return 0;
while (lt_next_mark < lt_nmarks && lt_marks[lt_next_mark * 2] < tline) {
lt_mark_base = lt_marks[lt_next_mark * 2]; lt_mark_file = lt_marks[lt_next_mark * 2 + 1];
- lines_on = 1; lt_next_mark++;
+ lines_on = 1; lt_next_mark++; bound_on = 1; check_on = 1;
}
return 0;
}
diff --git a/tests/drivers/runtime_image_test.py b/tests/drivers/runtime_image_test.py
index 64aa353..e408dca 100644
--- a/tests/drivers/runtime_image_test.py
+++ b/tests/drivers/runtime_image_test.py
@@ -17,13 +17,15 @@ args = parser.parse_args()
cases = [
('enhancements', 'include rt; fn main() { rt.put_n(17 % 5); rt.put_c(32); rt.put_n(17 / 5); }', b'2 3'),
('array algorithms', Path('examples/learn/31-array-algorithms.zc').read_text(), Path('examples/learn/31-array-algorithms.out').read_bytes()),
- ('line tables', '\n//:lines trace.zc\n' + Path('tests/programs/trace.zc').read_text(), Path('tests/programs/trace.expected').read_bytes().replace(b'<stdin>', b'trace.zc')),
+ # the index error traps: exit status 2, like zy (and the program is checked although run.zc is unbounded)
+ ('line tables', '\n//:lines trace.zc\n' + Path('tests/programs/trace.zc').read_text(), Path('tests/programs/trace.expected').read_bytes().replace(b'<stdin>', b'trace.zc'), 2),
('owned pop', 'include rt; int*.u take() { int*[?] a = rt.new(int*[?]); int* p = rt.new(int); *p = 8; a.push(p); return a.pop(); } fn main() { let p = take(); rt.put_n(rt.isobj(p)); rt.put_c(32); rt.put_n(*p); }', b'1 8'),
# main may be void: run.zc's prototype of main must not fix its type (docs/SPEC.md 5.2.5)
('void main', Path('tests/programs/void_main.zc').read_text(), b'void main\n'),
]
prefix = b''.join(Path(f).read_bytes() + b'\n' for f in ['src/runtime/rt.zc', 'src/core/core.zc', 'src/runtime/run.zc'])
-for name, source, expected in cases:
+for name, source, expected, *status in cases:
+ status = status[0] if status else 0
if args.case and args.case not in name:
continue
with tempfile.TemporaryDirectory(prefix='tinyvm-image-') as work:
@@ -34,5 +36,5 @@ for name, source, expected in cases:
assert image.returncode == 0, (name, image.stderr)
binary = micro_image(directory, image.stdout, args.map)
r = subprocess.run([str(binary)], capture_output=True, timeout=10)
- assert r.returncode == 0 and r.stdout == expected, (name, r.returncode, r.stdout, r.stderr, expected)
+ assert r.returncode == status and r.stdout == expected, (name, r.returncode, r.stdout, r.stderr, expected)
print('ok self-hosted run-only image: ' + name)
diff --git a/tests/xref.baseline b/tests/xref.baseline
index 8731091..b8b1e1f 100644
--- a/tests/xref.baseline
+++ b/tests/xref.baseline
@@ -41,7 +41,7 @@ borrow_entry borrow_prologue ctx_pop fn_begin
borrow_jump borrow_prologue ctx_pop fn_begin
borrow_pending ctx_pop emit_borrow_hold fn_begin
borrow_slots ctx_pop emit_borrow_hold fn_begin
-bound_on chk_reset topitem1
+bound_on chk_reset lt_apply_marks topitem1
bprec add_binop reserve_binary
bs_pk body_scan bs_adv
bs_pk2 body_scan bs_adv
@@ -55,7 +55,7 @@ capture_enabled capture_prepare ctx_pop fn_begin
capture_width reserve_captures
ch chk_reset nextch src_pop
chbase src_pop src_push
-check_on chk_reset topitem1
+check_on chk_reset lt_apply_marks topitem1
chk_heap chk_unit start
chk_hook
chkabort block body chk_begin errend fatal giveup hl_begin primary probe_member probe_stmt replace_function unit_top