refactor: keep the front end, drop everything downstream of it
The milestone structure had stopped describing the compiler and started
shaping it: m7.c, check_m7.c, tests/m2..m9, and a checker and emitter that had
each grown past 2,500 lines because there was nowhere else to put anything.
Restart from the pipeline instead.
What is left is the front end -- lexer, parser, types, ownership, semantic
analysis -- and the fixtures that describe it. The C backend, the DOSBox-X
runner, the milestone registry and the batch build are removed. The driver now
stops after semantic analysis; a code generator attaches where emit_c did.
Fixtures move from milestone directories to what they check:
parse/ grammar own/ ownership and borrowing
types/ type rules optional/ optionals and error unions
format/ formatting, try units/ units and visibility
generic/ generics pending-backend/
pending-backend/ holds the three fixtures that can only be checked by running
a program -- that the bounds check traps, that --no-checks removes it, and that
drops and defers actually fire, verified through a fake allocator. Those are
not front-end tests and are not pretending to be; they come back first when
there is a code generator.
tests/run.py replaces the DOSBox-X harness. It builds the front end with the
pinned Watcom's Windows-hosted driver and runs every fixture in about two
seconds, and it does something the old runner structurally could not: it reads
the `// ERROR:line:text` marker each fixture carries and checks the diagnostic
against it. Those markers have been in the tree all along, unverified, because
DOS could not redirect the compiler's stderr and only the exit code was ever
compared.
133/188 pass. The 55 failures are not regressions -- they are what was already
true and invisible:
- units (27) and generic (23): `import`, `comptime` and generic declarations
parse and are then dropped on the floor. No pass looks at them. The old
registry did not list these fixtures at all, so nothing said so.
- five in own/, optional/ and generic/: a marker disagrees with the diagnostic
about the line or the wording. Each is either a wrong marker or a wrong
diagnostic and has to be read individually.
Everything removed is in git history.
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
# M6 fixtures
|
||||
|
||||
These fixtures pin the M6 ownership/borrow rules before implementation.
|
||||
|
||||
- `ok*.fe` must compile with `--target=bits32 --emit-c`.
|
||||
- `bad*.fe` must fail compilation; the first line follows the `// ERROR:<line>:<substring>` convention from SPEC §12.
|
||||
- They are registered in `src/ferrolang_vm/registry.py`, which is what decides whether a milestone runs.
|
||||
- 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, 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:8:self
|
||||
unit badarg;
|
||||
|
||||
struct Box {
|
||||
value: i32,
|
||||
|
||||
fn bad(self: &Self, other: &i32) -> &i32 {
|
||||
return other;
|
||||
}
|
||||
}
|
||||
@@ -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,12 @@
|
||||
// ERROR:10:borrow
|
||||
unit baddefer;
|
||||
|
||||
fn read(r: &i32) -> i32 { return r.^; }
|
||||
|
||||
fn bad() -> i32 {
|
||||
var x: i32 = 0;
|
||||
let r = &x;
|
||||
defer { let y = read(r); }
|
||||
x = 1;
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// ERROR:5:reference
|
||||
unit badfld;
|
||||
|
||||
struct Bad {
|
||||
value: &i32,
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// ERROR:7:global
|
||||
unit badglob;
|
||||
|
||||
var VALUE: i32 = 0;
|
||||
|
||||
fn bad() -> i32 {
|
||||
let r = &VALUE;
|
||||
return r.^;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// ERROR:7:global
|
||||
unit badgmut;
|
||||
|
||||
var VALUE: i32 = 0;
|
||||
|
||||
fn bad() -> void {
|
||||
let r = &mut VALUE;
|
||||
r.^ = 1;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// ERROR:6:borrow
|
||||
unit badinv;
|
||||
|
||||
fn bad(p: ^i32, q: ^i32) -> void {
|
||||
let r = &p;
|
||||
p = q;
|
||||
let keep = r;
|
||||
mem.destroy(p);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// ERROR:6:reference
|
||||
unit badlocsl;
|
||||
|
||||
fn bad() -> []u8 {
|
||||
let a: [2]u8 = [1 as u8, 2 as u8];
|
||||
return a[..];
|
||||
}
|
||||
@@ -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,10 @@
|
||||
// ERROR:8:borrow
|
||||
unit badmove;
|
||||
|
||||
fn take(p: ^i32) -> void { mem.destroy(p); }
|
||||
|
||||
fn bad(p: ^i32) -> void {
|
||||
let r = &p;
|
||||
take(p);
|
||||
let q = r;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// ERROR:7:borrow
|
||||
unit badmut;
|
||||
|
||||
fn bad() -> i32 {
|
||||
var x: i32 = 0;
|
||||
let r = &mut x;
|
||||
x = 1;
|
||||
r.^ = 2;
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// ERROR:7:borrow
|
||||
unit badmut2;
|
||||
|
||||
fn bad() -> i32 {
|
||||
var x: i32 = 0;
|
||||
let a = &mut x;
|
||||
let b = &mut x;
|
||||
a.^ = 1;
|
||||
b.^ = 2;
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// ERROR:4:reference
|
||||
unit badptr;
|
||||
|
||||
fn bad(p: *&i32) -> void {
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// ERROR:6:reference
|
||||
unit badret;
|
||||
|
||||
fn bad() -> &i32 {
|
||||
let x: i32 = 1;
|
||||
return &x;
|
||||
}
|
||||
@@ -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,12 @@
|
||||
// ERROR:9:reference
|
||||
unit badscop;
|
||||
|
||||
fn bad(cond: bool) -> i32 {
|
||||
let outer: i32 = 0;
|
||||
var r: &i32 = &outer;
|
||||
if cond {
|
||||
let inner: i32 = 3;
|
||||
r = &inner;
|
||||
}
|
||||
return r.^;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// ERROR:8:self
|
||||
unit badself;
|
||||
|
||||
struct Box {
|
||||
value: i32,
|
||||
|
||||
fn bad(self: Self) -> &i32 {
|
||||
return &self.value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// ERROR:9:borrow
|
||||
unit badshwr;
|
||||
|
||||
fn read(r: &i32) -> i32 { return r.^; }
|
||||
|
||||
fn bad() -> i32 {
|
||||
var x: i32 = 0;
|
||||
let r = &x;
|
||||
x = 1;
|
||||
return read(r);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// ERROR:5:reference
|
||||
unit badslfld;
|
||||
|
||||
struct Bad {
|
||||
bytes: []u8,
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// ERROR:5:reference
|
||||
unit badtwo;
|
||||
|
||||
fn choose(a: &i32, b: &i32) -> &i32 {
|
||||
return a;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// ERROR:8:mut
|
||||
unit badup;
|
||||
|
||||
struct Box {
|
||||
value: i32,
|
||||
|
||||
fn bad(self: &Self) -> &mut i32 {
|
||||
return &mut self.value;
|
||||
}
|
||||
}
|
||||
@@ -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,5 @@
|
||||
unit m5_defer;
|
||||
|
||||
pub fn cleanup(p: ^i32) -> void {
|
||||
defer { mem.destroy(p); }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
unit m5_owned;
|
||||
|
||||
fn main() -> !void {
|
||||
var p: ^i32 = try mem.create(0);
|
||||
p = try mem.create(0);
|
||||
p.^ = 7;
|
||||
let value: i32 = p.^;
|
||||
defer { mem.destroy(p); }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
unit okbranch;
|
||||
|
||||
fn read(r: &i32) -> i32 { return r.^; }
|
||||
|
||||
fn test(cond: bool) -> i32 {
|
||||
var x: i32 = 3;
|
||||
let r = &x;
|
||||
if cond {
|
||||
let a = read(r);
|
||||
} else {
|
||||
let b = read(r);
|
||||
}
|
||||
x += 1;
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
unit okdefer;
|
||||
|
||||
fn read(r: &i32) -> i32 { return r.^; }
|
||||
|
||||
fn test() -> i32 {
|
||||
var x: i32 = 3;
|
||||
if true {
|
||||
let r = &x;
|
||||
defer { let y = read(r); }
|
||||
}
|
||||
x += 1;
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
unit okglobcp;
|
||||
|
||||
var VALUE: i32 = 7;
|
||||
|
||||
fn read(r: &i32) -> i32 { return r.^; }
|
||||
|
||||
fn test() -> i32 {
|
||||
let local = VALUE;
|
||||
return read(&local);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
unit oklast;
|
||||
|
||||
fn test() -> i32 {
|
||||
var x: i32 = 0;
|
||||
let r = &mut x;
|
||||
r.^ = 1;
|
||||
x += 1;
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
unit okr8free;
|
||||
|
||||
fn head(s: []u8) -> &u8 {
|
||||
return &s[0];
|
||||
}
|
||||
|
||||
fn test() -> u8 {
|
||||
var a: [2]u8 = [1 as u8, 2 as u8];
|
||||
let r = head(a[..]);
|
||||
let v = r.^;
|
||||
a[0] = 7 as u8;
|
||||
return v;
|
||||
}
|
||||
@@ -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,17 @@
|
||||
unit okr8meth;
|
||||
|
||||
struct Box {
|
||||
value: i32,
|
||||
|
||||
pub fn get(self: &Self) -> &i32 {
|
||||
return &self.value;
|
||||
}
|
||||
}
|
||||
|
||||
fn test() -> i32 {
|
||||
var b = Box{ value: 4 };
|
||||
let r = b.get();
|
||||
let v = r.^;
|
||||
b.value = 5;
|
||||
return v;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
unit okr8stat;
|
||||
|
||||
fn name() -> str {
|
||||
return "main";
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
unit okrebor;
|
||||
|
||||
fn read(r: &i32) -> i32 { return r.^; }
|
||||
|
||||
fn test() -> i32 {
|
||||
var x: i32 = 1;
|
||||
let r = &mut x;
|
||||
let v = read(r);
|
||||
r.^ = v + 1;
|
||||
return r.^;
|
||||
}
|
||||
@@ -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,12 @@
|
||||
unit okshare;
|
||||
|
||||
fn add(a: &i32, b: &i32) -> i32 {
|
||||
return a.^ + b.^;
|
||||
}
|
||||
|
||||
fn test() -> i32 {
|
||||
let x: i32 = 4;
|
||||
let a = &x;
|
||||
let b = &x;
|
||||
return add(a, b);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
unit okslreb;
|
||||
|
||||
fn first(s: []u8) -> u8 { return s[0]; }
|
||||
|
||||
fn test() -> u8 {
|
||||
var a: [2]u8 = [1 as u8, 2 as u8];
|
||||
var s = a[..];
|
||||
let v = first(s);
|
||||
s[0] = 9 as u8;
|
||||
return v;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
unit okstatic;
|
||||
|
||||
static VALUE: i32 = 7;
|
||||
|
||||
fn get() -> &i32 {
|
||||
return &VALUE;
|
||||
}
|
||||
|
||||
fn test() -> i32 {
|
||||
return get().^;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
unit oktemp;
|
||||
|
||||
fn read(r: &i32) -> i32 { return r.^; }
|
||||
|
||||
fn test() -> i32 {
|
||||
var x: i32 = 1;
|
||||
let v = read(&x);
|
||||
x += 1;
|
||||
return v + x;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
unit oktrim;
|
||||
|
||||
fn trimmed(line: str) -> str {
|
||||
return line.trim();
|
||||
}
|
||||
@@ -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.^;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
unit m5_bad_consuming_close;
|
||||
|
||||
struct FileLike {
|
||||
handle: i32,
|
||||
fn close(self: Self) -> !void { self.handle = 0; }
|
||||
fn drop(self: &mut Self) { self.handle = 0; }
|
||||
}
|
||||
|
||||
fn bad() -> !void {
|
||||
let file: FileLike = FileLike{ handle: 7 };
|
||||
try file.close();
|
||||
file.close();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
unit m5_bad_conditional;
|
||||
|
||||
fn take(p: ^i32) -> void { mem.destroy(p); }
|
||||
|
||||
fn bad(p: ^i32, flag: bool) -> void {
|
||||
if flag { take(p); }
|
||||
p.^ = 3;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
unit m5_bad_double;
|
||||
|
||||
fn bad(p: ^i32) -> void {
|
||||
mem.destroy(p);
|
||||
mem.destroy(p);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
unit m5_bad_destroy;
|
||||
|
||||
fn bad(x: i32) -> void {
|
||||
mem.destroy(x);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
unit m5_bad_drop;
|
||||
|
||||
struct Box {
|
||||
value: i32,
|
||||
fn drop(self: &mut Self) { self.value = 0; }
|
||||
}
|
||||
|
||||
fn bad() -> void {
|
||||
var b: Box = Box{ value: 1 };
|
||||
b.drop();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
unit m5_bad_loop_move;
|
||||
|
||||
fn take(p: ^i32) -> void { mem.destroy(p); }
|
||||
|
||||
fn bad(p: ^i32, again: bool) -> void {
|
||||
while again { take(p); }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
unit m5_bad_move;
|
||||
|
||||
fn take(p: ^i32) -> void { mem.destroy(p); }
|
||||
|
||||
fn twice(p: ^i32) -> void {
|
||||
take(p);
|
||||
take(p);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
unit m5_bad_projection_move;
|
||||
|
||||
struct Holder { p: ^i32 }
|
||||
fn take(p: ^i32) -> void { mem.destroy(p); }
|
||||
|
||||
fn bad(h: Holder) -> void {
|
||||
take(h.p);
|
||||
}
|
||||
Reference in New Issue
Block a user