diff --git a/fec/tests/m6/README.md b/fec/tests/m6/README.md index 9e1e0ef..743005f 100644 --- a/fec/tests/m6/README.md +++ b/fec/tests/m6/README.md @@ -8,4 +8,4 @@ These fixtures pin the M6 ownership/borrow rules before implementation. - When M6 starts, wire this directory into the DOS/QEMU gate without changing the expected result of any fixture. - M6 also owns the general-global borrow restriction from R10 because AGENTS.md explicitly groups that change with the `own.c` state-machine work. -Coverage: R4 storage restrictions, R5 scope, R6 shared/exclusive liveness and reborrows, R7 invalidation, R8 derived returns, defer lifetime extension, and global borrow restrictions. +Coverage: R4 storage restrictions, R5 scope, R6 shared/exclusive liveness, root-local field/index conflicts, call-only `&mut -> &` reborrows, branch/loop `MaybeMoved` and initialization-state merging, R7 invalidation, R8 derived-return provenance joins, defer lifetime extension, and global borrow restrictions. diff --git a/fec/tests/m6/badbinit.fe b/fec/tests/m6/badbinit.fe new file mode 100644 index 0000000..b6eb632 --- /dev/null +++ b/fec/tests/m6/badbinit.fe @@ -0,0 +1,10 @@ +// ERROR:9:initialized +unit badbinit; + +fn bad(assign: bool) -> i32 { + var value: i32; + if assign { + value = 1; + } + return value; +} diff --git a/fec/tests/m6/badbrmov.fe b/fec/tests/m6/badbrmov.fe new file mode 100644 index 0000000..7dbb11b --- /dev/null +++ b/fec/tests/m6/badbrmov.fe @@ -0,0 +1,9 @@ +// ERROR:9:move +unit badbrmov; + +fn bad(p: ^i32, consume: bool) -> void { + if consume { + mem.destroy(p); + } + mem.destroy(p); +} diff --git a/fec/tests/m6/badloop.fe b/fec/tests/m6/badloop.fe new file mode 100644 index 0000000..fed8c0f --- /dev/null +++ b/fec/tests/m6/badloop.fe @@ -0,0 +1,9 @@ +// ERROR:8:move +unit badloop; + +fn bad(p: ^i32, again: bool) -> void { + while again { + mem.destroy(p); + } + mem.destroy(p); +} diff --git a/fec/tests/m6/badrfld.fe b/fec/tests/m6/badrfld.fe new file mode 100644 index 0000000..65cb8b0 --- /dev/null +++ b/fec/tests/m6/badrfld.fe @@ -0,0 +1,11 @@ +// ERROR:9:borrow +unit badrfld; + +struct Pair { a: i32, b: i32, } + +fn bad() -> void { + var p = Pair{ a: 1, b: 2 }; + let left = &mut p.a; + p.b = 3; + left.^ = 4; +} diff --git a/fec/tests/m6/badridx.fe b/fec/tests/m6/badridx.fe new file mode 100644 index 0000000..d7597c5 --- /dev/null +++ b/fec/tests/m6/badridx.fe @@ -0,0 +1,10 @@ +// ERROR:7:borrow +unit badridx; + +fn bad() -> void { + var xs: [2]i32 = [1, 2]; + let a = &mut xs[0]; + let b = &mut xs[1]; + a.^ = 3; + b.^ = 4; +} diff --git a/fec/tests/m6/badweak.fe b/fec/tests/m6/badweak.fe new file mode 100644 index 0000000..8da6bc8 --- /dev/null +++ b/fec/tests/m6/badweak.fe @@ -0,0 +1,9 @@ +// ERROR:7:mut +unit badweak; + +fn bad() -> void { + var x: i32 = 0; + let m = &mut x; + let s: &i32 = m; + let v = s.^; +} diff --git a/fec/tests/m6/okr8join.fe b/fec/tests/m6/okr8join.fe new file mode 100644 index 0000000..b2e67dd --- /dev/null +++ b/fec/tests/m6/okr8join.fe @@ -0,0 +1,14 @@ +unit okr8join; + +fn select(s: str, from_param: bool) -> str { + if from_param { return s; } + return "static"; +} + +fn test() -> u8 { + var bytes: [1]u8 = [7 as u8]; + let view = select(bytes[..], true); + let value = view[0]; + bytes[0] = 8 as u8; + return value; +} diff --git a/fec/tests/m6/okrtlast.fe b/fec/tests/m6/okrtlast.fe new file mode 100644 index 0000000..bd87c4e --- /dev/null +++ b/fec/tests/m6/okrtlast.fe @@ -0,0 +1,11 @@ +unit okrtlast; + +struct Pair { a: i32, b: i32, } + +fn test() -> i32 { + var p = Pair{ a: 1, b: 2 }; + let left = &mut p.a; + left.^ = 3; + p.b = 4; + return p.a + p.b; +} diff --git a/fec/tests/m6/okwcall.fe b/fec/tests/m6/okwcall.fe new file mode 100644 index 0000000..a53efe2 --- /dev/null +++ b/fec/tests/m6/okwcall.fe @@ -0,0 +1,11 @@ +unit okwcall; + +fn read(r: &i32) -> i32 { return r.^; } + +fn test() -> i32 { + var x: i32 = 5; + let m = &mut x; + let value = read(m); + m.^ = value + 1; + return m.^; +} diff --git a/fec/tests/m7/README.md b/fec/tests/m7/README.md index 468c98b..3679b4b 100644 --- a/fec/tests/m7/README.md +++ b/fec/tests/m7/README.md @@ -5,4 +5,4 @@ M7 adds optionals and error unions on top of the M6 ownership model. `ok*.fe` must compile. `bad*.fe` must fail according to the first-line error marker. The files are not wired into `TEST-DOS.BAT` until M7 work begins. -Coverage: `?T`, `.?`, `Some`/`None` pattern-only destructuring, `mem.replace` extraction, `orelse`, nominal error unions, `try`, block/short `catch`, error code zero rejection, and R4/R7 interactions with optional references/owners. +Coverage: contextual `null`, `?T`, `.?`, `Some`/`None` pattern-only non-destructive views, `mem.replace` extraction, lazy `orelse`/`catch`, nominal error unions, `try`, block/short `catch`, and error code/name uniqueness, plus R4/R7 interactions with optional references/owners. diff --git a/fec/tests/m7/badercod.fe b/fec/tests/m7/badercod.fe new file mode 100644 index 0000000..ad5b34c --- /dev/null +++ b/fec/tests/m7/badercod.fe @@ -0,0 +1,7 @@ +// ERROR:6:duplicate +unit badercod; + +error E { + One = 1, + Two = 1, +} diff --git a/fec/tests/m7/badernam.fe b/fec/tests/m7/badernam.fe new file mode 100644 index 0000000..661e8c0 --- /dev/null +++ b/fec/tests/m7/badernam.fe @@ -0,0 +1,7 @@ +// ERROR:6:duplicate +unit badernam; + +error E { + One = 1, + One = 2, +} diff --git a/fec/tests/m7/badnull.fe b/fec/tests/m7/badnull.fe new file mode 100644 index 0000000..4ed896b --- /dev/null +++ b/fec/tests/m7/badnull.fe @@ -0,0 +1,6 @@ +// ERROR:5:null +unit badnull; + +fn bad() -> void { + let p = null; +} diff --git a/fec/tests/m7/okcatmov.fe b/fec/tests/m7/okcatmov.fe new file mode 100644 index 0000000..c2c7b37 --- /dev/null +++ b/fec/tests/m7/okcatmov.fe @@ -0,0 +1,8 @@ +unit okcatmov; + +error E { Bad = 1, } +struct Node { value: i32, } + +fn recover(result: E!^Node, fallback: ^Node) -> ^Node { + return result catch fallback; +} diff --git a/fec/tests/m7/oknull.fe b/fec/tests/m7/oknull.fe new file mode 100644 index 0000000..c12a917 --- /dev/null +++ b/fec/tests/m7/oknull.fe @@ -0,0 +1,10 @@ +unit oknull; + +struct Node { value: i32, } + +fn accepts(p: ?^Node) -> bool { return p == null; } + +fn test() -> bool { + let p: ?^Node = null; + return accepts(p); +} diff --git a/fec/tests/m7/okpatvw.fe b/fec/tests/m7/okpatvw.fe new file mode 100644 index 0000000..5797b46 --- /dev/null +++ b/fec/tests/m7/okpatvw.fe @@ -0,0 +1,12 @@ +unit okpatvw; + +struct Node { value: i32, } + +fn keep(p: ?^Node) -> void { + var slot = p; + if let Some(node) = slot { + let value = node.value; + } + let owned = mem.replace(&mut slot, null).?; + mem.destroy(owned); +} diff --git a/fec/tests/m8/README.md b/fec/tests/m8/README.md index 73e69e4..6271483 100644 --- a/fec/tests/m8/README.md +++ b/fec/tests/m8/README.md @@ -5,6 +5,8 @@ M8 is the first multi-unit milestone, so cases live in subdirectories. Each case Pass cases: - `basic`: public function across units. - `pubfld`: public type and public field across units. +- `dotted` and `alias`: canonical dotted unit paths, last-segment binding, and explicit import aliases. +- `dotpriv`: a dotted unit prefix does not grant access to another unit's private declarations. - `errsame`: two units use the same anonymous `error.Name`; the driver must assign one deterministic `core.Error` code. - `errdet`: the same anonymous error-name set appears in a different source/import order; generated `fe_errors.h` must be byte-identical to `errsame` modulo the intentionally different unit graph. @@ -14,6 +16,11 @@ Fail cases: - `missing`: unresolved import. - `cycle`: cyclic imports. - `unitbad`: filename/unit-name mismatch. +- `badupper` and `badlong`: DOS-safe lowercase, eight-character unit-segment limit. +- `bindconf`: two imports with the same last-segment binding require an alias. +- `pubpriv`: a public signature cannot expose a private nominal type. - `errnom`: nominal error cannot flow into `core.Error` via `try`. The current M5 `TEST-DOS.BAT` is intentionally unchanged. M8 should add procedural checks for `.fei` creation/hash invalidation and deterministic `fe_errors.h` using these fixtures. + +The M8 DOS gate must additionally construct two separate `-I` roots containing the same canonical unit and require an ambiguity error; repeat the case with two paths to the same canonical file and require deduplication. It must also verify that `std.*` resolves only from the built-in std root, ordinary user units never do, and that changing a private non-generic implementation preserves the dependent interface-cache hit. Those checks need temporary roots/cache inspection and deliberately remain procedural rather than encoding host paths in fixtures. diff --git a/fec/tests/m8/alias/acme/math.fe b/fec/tests/m8/alias/acme/math.fe new file mode 100644 index 0000000..ee6d1e8 --- /dev/null +++ b/fec/tests/m8/alias/acme/math.fe @@ -0,0 +1,3 @@ +unit acme.math; + +pub fn answer() -> i32 { return 42; } diff --git a/fec/tests/m8/alias/main.fe b/fec/tests/m8/alias/main.fe new file mode 100644 index 0000000..7e189d2 --- /dev/null +++ b/fec/tests/m8/alias/main.fe @@ -0,0 +1,4 @@ +unit main; +import acme.math as calc; + +fn main() -> i32 { return calc.answer(); } diff --git a/fec/tests/m8/badlong/main.fe b/fec/tests/m8/badlong/main.fe new file mode 100644 index 0000000..ae41a21 --- /dev/null +++ b/fec/tests/m8/badlong/main.fe @@ -0,0 +1,4 @@ +// ERROR:2:unit +unit toolonggg; + +fn main() -> void {} diff --git a/fec/tests/m8/badupper/main.fe b/fec/tests/m8/badupper/main.fe new file mode 100644 index 0000000..7ae7913 --- /dev/null +++ b/fec/tests/m8/badupper/main.fe @@ -0,0 +1,4 @@ +// ERROR:2:unit +unit Upper; + +fn main() -> void {} diff --git a/fec/tests/m8/bindconf/alpha/net.fe b/fec/tests/m8/bindconf/alpha/net.fe new file mode 100644 index 0000000..02af990 --- /dev/null +++ b/fec/tests/m8/bindconf/alpha/net.fe @@ -0,0 +1 @@ +unit alpha.net; diff --git a/fec/tests/m8/bindconf/beta/net.fe b/fec/tests/m8/bindconf/beta/net.fe new file mode 100644 index 0000000..1c5f6f1 --- /dev/null +++ b/fec/tests/m8/bindconf/beta/net.fe @@ -0,0 +1 @@ +unit beta.net; diff --git a/fec/tests/m8/bindconf/main.fe b/fec/tests/m8/bindconf/main.fe new file mode 100644 index 0000000..6b1a90d --- /dev/null +++ b/fec/tests/m8/bindconf/main.fe @@ -0,0 +1,6 @@ +// ERROR:4:binding +unit main; +import alpha.net; +import beta.net; + +fn main() -> void {} diff --git a/fec/tests/m8/dotpriv/game/bar.fe b/fec/tests/m8/dotpriv/game/bar.fe new file mode 100644 index 0000000..3dba2e9 --- /dev/null +++ b/fec/tests/m8/dotpriv/game/bar.fe @@ -0,0 +1,5 @@ +// ERROR:5:private +unit game.bar; +import game.foo; + +pub fn read() -> i32 { return foo.hidden(); } diff --git a/fec/tests/m8/dotpriv/game/foo.fe b/fec/tests/m8/dotpriv/game/foo.fe new file mode 100644 index 0000000..ef2220e --- /dev/null +++ b/fec/tests/m8/dotpriv/game/foo.fe @@ -0,0 +1,3 @@ +unit game.foo; + +fn hidden() -> i32 { return 1; } diff --git a/fec/tests/m8/dotpriv/main.fe b/fec/tests/m8/dotpriv/main.fe new file mode 100644 index 0000000..b14ac8d --- /dev/null +++ b/fec/tests/m8/dotpriv/main.fe @@ -0,0 +1,4 @@ +unit main; +import game.bar; + +fn main() -> i32 { return bar.read(); } diff --git a/fec/tests/m8/dotted/acme/math.fe b/fec/tests/m8/dotted/acme/math.fe new file mode 100644 index 0000000..ee6d1e8 --- /dev/null +++ b/fec/tests/m8/dotted/acme/math.fe @@ -0,0 +1,3 @@ +unit acme.math; + +pub fn answer() -> i32 { return 42; } diff --git a/fec/tests/m8/dotted/main.fe b/fec/tests/m8/dotted/main.fe new file mode 100644 index 0000000..df3abae --- /dev/null +++ b/fec/tests/m8/dotted/main.fe @@ -0,0 +1,4 @@ +unit main; +import acme.math; + +fn main() -> i32 { return math.answer(); } diff --git a/fec/tests/m8/pubpriv/lib.fe b/fec/tests/m8/pubpriv/lib.fe new file mode 100644 index 0000000..0e30a9b --- /dev/null +++ b/fec/tests/m8/pubpriv/lib.fe @@ -0,0 +1,5 @@ +unit lib; + +struct Hidden { value: i32, } +// ERROR:4:private +pub fn make() -> Hidden { return Hidden{ value: 1 }; } diff --git a/fec/tests/m8/pubpriv/main.fe b/fec/tests/m8/pubpriv/main.fe new file mode 100644 index 0000000..c53e7c5 --- /dev/null +++ b/fec/tests/m8/pubpriv/main.fe @@ -0,0 +1,4 @@ +unit main; +import lib; + +fn main() -> void { let value = lib.make(); } diff --git a/fec/tests/m9/README.md b/fec/tests/m9/README.md index ba80d35..5fec504 100644 --- a/fec/tests/m9/README.md +++ b/fec/tests/m9/README.md @@ -3,7 +3,7 @@ M9 adds comptime type parameters and monomorphization. `ok*.fe` must compile; `bad*.fe` must fail according to the first-line marker. -`defscope/` is a multi-unit definition-scope test and requires M8 imports. +`defscope/` and `okscope/` are multi-unit definition-scope tests and require M8 imports. `okscope/` verifies that an exported generic may use its definition unit's private helper through `.fei` support metadata. Coverage: - generic functions and structs, @@ -15,5 +15,7 @@ Coverage: - runtime `type` values forbidden, - definition-unit name lookup, - recursive instantiation depth limit. +- rejection of user value generics and generic type inference, +- same-instance recursive request reuse and selected-out `comptime if` semantic skipping. M9's DOS gate should additionally inspect generated `fe_generics.c`: `okdedup.fe` must emit one body for the repeated `(id, i32)` instantiation. diff --git a/fec/tests/m9/baddist.fe b/fec/tests/m9/baddist.fe new file mode 100644 index 0000000..bd1425e --- /dev/null +++ b/fec/tests/m9/baddist.fe @@ -0,0 +1,10 @@ +// ERROR:7:depth +unit baddist; + +struct Box(T) { value: T, } + +fn grow(comptime T: type) -> void { + grow(Box(T)); +} + +fn bad() -> void { grow(i32); } diff --git a/fec/tests/m9/badinfer.fe b/fec/tests/m9/badinfer.fe new file mode 100644 index 0000000..8bcccb8 --- /dev/null +++ b/fec/tests/m9/badinfer.fe @@ -0,0 +1,8 @@ +// ERROR:7:generic +unit badinfer; + +fn id(comptime T: type, value: T) -> T { return value; } + +fn bad() -> i32 { + return id(7); +} diff --git a/fec/tests/m9/badvalue.fe b/fec/tests/m9/badvalue.fe new file mode 100644 index 0000000..1e8ff97 --- /dev/null +++ b/fec/tests/m9/badvalue.fe @@ -0,0 +1,4 @@ +// ERROR:4:comptime +unit badvalue; + +fn id(comptime N: usize, value: i32) -> i32 { return value; } diff --git a/fec/tests/m9/oksamrec.fe b/fec/tests/m9/oksamrec.fe new file mode 100644 index 0000000..2419703 --- /dev/null +++ b/fec/tests/m9/oksamrec.fe @@ -0,0 +1,7 @@ +unit oksamrec; + +fn repeat(comptime T: type, value: T) -> T { + return repeat(T, value); +} + +fn test() -> i32 { return repeat(i32, 1); } diff --git a/fec/tests/m9/badscope/lib.fe b/fec/tests/m9/okscope/lib.fe similarity index 81% rename from fec/tests/m9/badscope/lib.fe rename to fec/tests/m9/okscope/lib.fe index bba2221..002e41b 100644 --- a/fec/tests/m9/badscope/lib.fe +++ b/fec/tests/m9/okscope/lib.fe @@ -1,4 +1,3 @@ -// ERROR:5:helper unit lib; pub fn call(comptime T: type, v: T) -> T { diff --git a/fec/tests/m9/badscope/main.fe b/fec/tests/m9/okscope/main.fe similarity index 100% rename from fec/tests/m9/badscope/main.fe rename to fec/tests/m9/okscope/main.fe diff --git a/fec/tests/m9/okskip.fe b/fec/tests/m9/okskip.fe new file mode 100644 index 0000000..e5fd24c --- /dev/null +++ b/fec/tests/m9/okskip.fe @@ -0,0 +1,11 @@ +unit okskip; + +fn select(comptime T: type, value: T) -> T { + comptime if T == i32 { + return value; + } else { + return unknown(value); + } +} + +fn test() -> i32 { return select(i32, 1); }