test: audit M6-M9 fixtures for v0.1.8
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// ERROR:9:initialized
|
||||
unit badbinit;
|
||||
|
||||
fn bad(assign: bool) -> i32 {
|
||||
var value: i32;
|
||||
if assign {
|
||||
value = 1;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// ERROR:9:move
|
||||
unit badbrmov;
|
||||
|
||||
fn bad(p: ^i32, consume: bool) -> void {
|
||||
if consume {
|
||||
mem.destroy(p);
|
||||
}
|
||||
mem.destroy(p);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// ERROR:8:move
|
||||
unit badloop;
|
||||
|
||||
fn bad(p: ^i32, again: bool) -> void {
|
||||
while again {
|
||||
mem.destroy(p);
|
||||
}
|
||||
mem.destroy(p);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.^;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.^;
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// ERROR:6:duplicate
|
||||
unit badercod;
|
||||
|
||||
error E {
|
||||
One = 1,
|
||||
Two = 1,
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// ERROR:6:duplicate
|
||||
unit badernam;
|
||||
|
||||
error E {
|
||||
One = 1,
|
||||
One = 2,
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// ERROR:5:null
|
||||
unit badnull;
|
||||
|
||||
fn bad() -> void {
|
||||
let p = null;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
unit acme.math;
|
||||
|
||||
pub fn answer() -> i32 { return 42; }
|
||||
@@ -0,0 +1,4 @@
|
||||
unit main;
|
||||
import acme.math as calc;
|
||||
|
||||
fn main() -> i32 { return calc.answer(); }
|
||||
@@ -0,0 +1,4 @@
|
||||
// ERROR:2:unit
|
||||
unit toolonggg;
|
||||
|
||||
fn main() -> void {}
|
||||
@@ -0,0 +1,4 @@
|
||||
// ERROR:2:unit
|
||||
unit Upper;
|
||||
|
||||
fn main() -> void {}
|
||||
@@ -0,0 +1 @@
|
||||
unit alpha.net;
|
||||
@@ -0,0 +1 @@
|
||||
unit beta.net;
|
||||
@@ -0,0 +1,6 @@
|
||||
// ERROR:4:binding
|
||||
unit main;
|
||||
import alpha.net;
|
||||
import beta.net;
|
||||
|
||||
fn main() -> void {}
|
||||
@@ -0,0 +1,5 @@
|
||||
// ERROR:5:private
|
||||
unit game.bar;
|
||||
import game.foo;
|
||||
|
||||
pub fn read() -> i32 { return foo.hidden(); }
|
||||
@@ -0,0 +1,3 @@
|
||||
unit game.foo;
|
||||
|
||||
fn hidden() -> i32 { return 1; }
|
||||
@@ -0,0 +1,4 @@
|
||||
unit main;
|
||||
import game.bar;
|
||||
|
||||
fn main() -> i32 { return bar.read(); }
|
||||
@@ -0,0 +1,3 @@
|
||||
unit acme.math;
|
||||
|
||||
pub fn answer() -> i32 { return 42; }
|
||||
@@ -0,0 +1,4 @@
|
||||
unit main;
|
||||
import acme.math;
|
||||
|
||||
fn main() -> i32 { return math.answer(); }
|
||||
@@ -0,0 +1,5 @@
|
||||
unit lib;
|
||||
|
||||
struct Hidden { value: i32, }
|
||||
// ERROR:4:private
|
||||
pub fn make() -> Hidden { return Hidden{ value: 1 }; }
|
||||
@@ -0,0 +1,4 @@
|
||||
unit main;
|
||||
import lib;
|
||||
|
||||
fn main() -> void { let value = lib.make(); }
|
||||
@@ -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.
|
||||
|
||||
@@ -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); }
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// ERROR:4:comptime
|
||||
unit badvalue;
|
||||
|
||||
fn id(comptime N: usize, value: i32) -> i32 { return value; }
|
||||
@@ -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); }
|
||||
@@ -1,4 +1,3 @@
|
||||
// ERROR:5:helper
|
||||
unit lib;
|
||||
|
||||
pub fn call(comptime T: type, v: T) -> T {
|
||||
@@ -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); }
|
||||
Reference in New Issue
Block a user