diff --git a/fec/tests/exec/sliceok.fe b/fec/tests/exec/sliceok.fe new file mode 100644 index 0000000..43bd34a --- /dev/null +++ b/fec/tests/exec/sliceok.fe @@ -0,0 +1,14 @@ +// EXIT:9 +unit sliceok; + +fn total(s: []i32) -> i32 { + var sum: i32 = 0; + for v in s { sum = sum + v.^; } + return sum; +} + +fn main() -> i32 { + let a: [5]i32 = [1, 2, 3, 4, 5]; + let mid: []i32 = a[1..4]; + return total(mid); +} diff --git a/fec/tests/exec/slicerng.fe b/fec/tests/exec/slicerng.fe new file mode 100644 index 0000000..78aef2f --- /dev/null +++ b/fec/tests/exec/slicerng.fe @@ -0,0 +1,11 @@ +// EXIT:3 +// OUTPUT:index out of bounds +// NOCHECKS:0 +unit slicerng; + +fn main() -> i32 { + let a: [2]i32 = [1, 2]; + let s: []i32 = a[0..3]; + let n: i32 = s.n as i32; + return n - n; +} diff --git a/fec/tests/pending-backend/README.md b/fec/tests/pending-backend/README.md deleted file mode 100644 index a653444..0000000 --- a/fec/tests/pending-backend/README.md +++ /dev/null @@ -1,10 +0,0 @@ -`fec/tests/pending-backend/`에 남겨둔 fixture는 현재 프론트엔드 전용 상태에서 실행할 수 없습니다. - -- `bounds_trap.fe` / `bounds_nocheck.fe`: 경계 검사 실패가 실제로 trap되는지, - `--no-checks` 플래그가 그 검사를 제거하는지 확인하는 런타임 동작 테스트입니다. -- `ownership_drop.fe` + `ownership-drop.c`: 삽입된 `drop`/`defer`가 실제로 실행되는지 확인하는 테스트입니다. - `ownership-drop.c`는 해제 횟수/순서/이중 해제를 검증하는 하네스입니다. -- `format-prop.c`: 포맷 프로퍼티 동작을 확인하는 런타임 검사입니다. - -이들은 코드 생성기가 없는 현재 단계에서는 실행할 수 없어서 `tests/run.py`가 건너뜁니다. -백엔드(코드 생성기)가 돌아오면 가장 먼저 재활성화할 대상입니다. diff --git a/fec/tests/pending-backend/bounds_nocheck.fe b/fec/tests/pending-backend/bounds_nocheck.fe deleted file mode 100644 index e8e62f3..0000000 --- a/fec/tests/pending-backend/bounds_nocheck.fe +++ /dev/null @@ -1,7 +0,0 @@ -unit m3_no_checks; - -fn main() -> i32 { - let a: [2]i32 = [1, 2]; - let x: i32 = a[2]; - return x - x; -} diff --git a/fec/tests/pending-backend/bounds_trap.fe b/fec/tests/pending-backend/bounds_trap.fe deleted file mode 100644 index ac581ee..0000000 --- a/fec/tests/pending-backend/bounds_trap.fe +++ /dev/null @@ -1,6 +0,0 @@ -unit m3_bounds; - -fn main() -> i32 { - let a: [2]i32 = [1, 2]; - return a[2]; -} diff --git a/fec/tests/pending-backend/format-prop.c b/fec/tests/pending-backend/format-prop.c deleted file mode 100644 index 66bba1a..0000000 --- a/fec/tests/pending-backend/format-prop.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "prop.c" - -int main(void) -{ - fe_writer w; - unsigned short result; - w.tag=2; - w.handle=99; - result=fe_m4_prop_propagate(w); - return result==1 ? 0 : 1; -} diff --git a/fec/tests/pending-backend/ownership-drop.c b/fec/tests/pending-backend/ownership-drop.c deleted file mode 100644 index 5d3e6bb..0000000 --- a/fec/tests/pending-backend/ownership-drop.c +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include - -#undef malloc -#undef free - -extern void *malloc(size_t size); -extern void free(void *p); - -/* `run` returns !i32, which lowers to { error, value }. */ -struct fe_result_value_9 { unsigned short e; long v; }; -extern struct fe_result_value_9 fe_m5_runtime_run(long mode); -extern unsigned short fe_m5_runtime_conditional(unsigned char flag); -extern unsigned short fe_m5_runtime_argument_cleanup(void); -extern unsigned short fe_m5_runtime_owned_slice(unsigned long n); -extern unsigned short fe_m5_runtime_replace_field(void); -extern unsigned short fe_m5_runtime_loop_cleanup(void); -extern unsigned short fe_m5_runtime_try_cleanup(void); -extern unsigned short fe_m5_runtime_field_order(void); -extern unsigned short fe_m5_runtime_defer_order(void); -extern unsigned short fe_m5_runtime_match_cleanup(unsigned char flag); -extern unsigned short fe_m5_runtime_close_once(void); -extern unsigned short fe_m5_runtime_reassign_struct(void); - -static void *live_ptrs[64]; -static unsigned live_count; -static unsigned alloc_count; -static unsigned free_count; -static unsigned double_free_count; -static long fail_after = -1; -static unsigned malloc_attempts; -static int track_order; -static void *order_ptrs[2]; -static unsigned order_allocs; -static unsigned order_frees; -static unsigned order_bad; - -void *m5_malloc(size_t size) -{ - void *p; - if (fail_after >= 0 && (long)malloc_attempts++ == fail_after) return 0; - p = malloc(size); - if (p && live_count < 64) live_ptrs[live_count++] = p; - if (p) ++alloc_count; - if (p && track_order && order_allocs < 2) order_ptrs[order_allocs++] = p; - return p; -} - -void m5_free(void *p) -{ - unsigned i; - if (!p) return; - for (i = 0; i < live_count; ++i) { - if (live_ptrs[i] == p) { - if (track_order && order_frees < 2 && - p != order_ptrs[1-order_frees]) ++order_bad; - if (track_order && order_frees < 2) ++order_frees; - live_ptrs[i] = live_ptrs[--live_count]; - ++free_count; - free(p); - return; - } - } - ++double_free_count; -} - -int main(void) -{ - struct fe_result_value_9 r; - r = fe_m5_runtime_run(0); if (r.e != 0 || r.v != 0) return 1; - r = fe_m5_runtime_run(1); if (r.e != 0 || r.v != 9) return 2; - r = fe_m5_runtime_run(2); if (r.e != 0 || r.v != 0) return 3; - if (fe_m5_runtime_conditional(0) != 0) return 4; - if (fe_m5_runtime_conditional(1) != 0) return 5; - if (fe_m5_runtime_argument_cleanup() != 0) return 6; - if (fe_m5_runtime_owned_slice(17) != 0) return 7; - if (fe_m5_runtime_replace_field() != 0) return 8; - if (fe_m5_runtime_loop_cleanup() != 0) return 9; - fail_after=1; - malloc_attempts=0; - if (fe_m5_runtime_try_cleanup() == 0) return 10; - fail_after=-1; - track_order=1; - order_allocs=order_frees=order_bad=0; - if (fe_m5_runtime_field_order() != 0) return 11; - track_order=0; - if (order_allocs != 2 || order_frees != 2 || order_bad != 0) return 12; - track_order=1; - order_allocs=order_frees=order_bad=0; - if (fe_m5_runtime_defer_order() != 0) return 13; - track_order=0; - if (order_allocs != 2 || order_frees != 2 || order_bad != 0) return 14; - if (fe_m5_runtime_match_cleanup(0) != 0) return 15; - if (fe_m5_runtime_match_cleanup(1) != 0) return 16; - if (fe_m5_runtime_close_once() != 0) return 17; - if (fe_m5_runtime_reassign_struct() != 0) return 18; - if (double_free_count != 0) return 19; - if (live_count != 0) return 20; - if (alloc_count != free_count) return 21; - return 0; -} diff --git a/fec/tests/pending-backend/ownership_drop.fe b/fec/tests/pending-backend/ownership_drop.fe deleted file mode 100644 index b9dab0c..0000000 --- a/fec/tests/pending-backend/ownership_drop.fe +++ /dev/null @@ -1,100 +0,0 @@ -unit m5_runtime; - -fn take(p: ^i32) -> void { mem.destroy(p); } - -pub fn run(mode: i32) -> !i32 { - var p: ^i32 = try mem.create(0); - defer { mem.destroy(p); } - p.^ = 7; - if mode == 1 { - p = try mem.create(0); - p.^ = 9; - return p.^; - } - while true { break; } - if mode == 2 { return 0; } - return p.^ - 7; -} - -pub fn conditional(flag: bool) -> !void { - var p: ^i32 = try mem.create(0); - if flag { take(p); } -} - -pub fn argument_cleanup() -> !void { - let p: ^i32 = try mem.create(0); - take(p); -} - -pub fn owned_slice(n: usize) -> !void { - let bytes: ^[]u8 = try mem.alloc_slice(u8, n); -} - -struct Holder { p: ^i32 } - -pub fn replace_field() -> !void { - let first: ^i32 = try mem.create(1); - var h: Holder = Holder{ p: first }; - let second: ^i32 = try mem.create(2); - let old: ^i32 = mem.replace(&mut h.p, second); - mem.destroy(old); -} - -pub fn loop_cleanup() -> !void { - var i: i32 = 0; - while i < 2 { - let p: ^i32 = try mem.create(i); - i += 1; - if i == 1 { continue; } - break; - } -} - -pub fn try_cleanup() -> !void { - let first: ^i32 = try mem.create(1); - let second: ^i32 = try mem.create(2); -} - -struct PairOwners { first: ^i32, second: ^i32 } - -pub fn field_order() -> !void { - let first: ^i32 = try mem.create(1); - let second: ^i32 = try mem.create(2); - let pair: PairOwners = PairOwners{ first: first, second: second }; -} - -pub fn defer_order() -> !void { - let first: ^i32 = try mem.create(1); - defer { mem.destroy(first); } - let second: ^i32 = try mem.create(2); -} - -enum Choice { A, B } - -pub fn match_cleanup(flag: bool) -> !void { - var choice: Choice = Choice.A; - if flag { choice = Choice.B; } - let p: ^i32 = try mem.create(1); - match choice { - A => { take(p); } - B => { take(p); } - } -} - -struct FileLike { - handle: i32, - fn close(self: Self) -> !void { self.handle = 0; } - fn drop(self: &mut Self) { self.handle = 0; } -} - -pub fn close_once() -> !void { - let file: FileLike = FileLike{ handle: 7 }; - try file.close(); -} - -pub fn reassign_struct() -> !void { - let first: ^i32 = try mem.create(1); - var owner: Holder = Holder{ p: first }; - let second: ^i32 = try mem.create(2); - owner = Holder{ p: second }; -} diff --git a/fec/tests/pending-backend/slice_bounds_trap.fe b/fec/tests/pending-backend/slice_bounds_trap.fe deleted file mode 100644 index 89a8d88..0000000 --- a/fec/tests/pending-backend/slice_bounds_trap.fe +++ /dev/null @@ -1,7 +0,0 @@ -unit m3_slice_bounds; - -fn main() -> i32 { - let a: [2]i32 = [1, 2]; - let s: []i32 = a[0..3]; - return s.n as i32; -} diff --git a/tests/run.py b/tests/run.py index f4d3575..4e50fdd 100644 --- a/tests/run.py +++ b/tests/run.py @@ -1,9 +1,8 @@ """Run every fixture through the front end and check what it reports. -The compiler is a front end now -- lexer, parser, types, ownership -- so a -fixture is checked by running `fec` on it and looking at two things: whether it -was accepted, and, when it was rejected, whether the diagnostic is the one the -fixture asked for. +A fixture is checked by running `fec` on it and looking at two things: whether +it was accepted, and, when it was rejected, whether the diagnostic is the one +the fixture asked for. A fixture states its expectation in its first line: @@ -17,8 +16,8 @@ not pin the message yet. Anything else must be accepted. Fixtures under `parse/` are checked with --dump-ast rather than --check: they exercise the grammar, and several are deliberately not well-typed. -This runs on the host in about a second. There is no VM: nothing here executes -generated code, because there is no code generator. +This runs on the host in about a second. It checks what the compiler says; what +the compiled programs actually do is `exec.py`. """ from __future__ import annotations @@ -35,8 +34,6 @@ FIXTURES = ROOT / "fec" / "tests" WATCOM = ROOT / ".dosboxx" / "watcom" SOURCES = ("arena", "diag", "lexer", "ast", "parser", "types", "m7", "own", "check", "resolve", "ir", "lower", "x86", "driver") -# Fixtures live here until there is a code generator to run them against. -QUARANTINE = "pending-backend" MARKER = re.compile(r"^//\s*ERROR:(?:(\d+):)?(.*)$") @@ -116,7 +113,7 @@ def main() -> int: args = ap.parse_args() fec = build(ROOT / ".build") - cases = sorted(p for p in FIXTURES.rglob("*.fe") if QUARANTINE not in p.parts) + cases = sorted(FIXTURES.rglob("*.fe")) if args.select: cases = [p for p in cases if args.select in p.as_posix()]