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,26 @@
|
||||
# M8 fixtures
|
||||
|
||||
M8 is the first multi-unit milestone, so cases live in subdirectories. Each case is compiled separately with that directory on the import path.
|
||||
|
||||
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.
|
||||
|
||||
Fail cases:
|
||||
- `privfn`: private declaration access.
|
||||
- `privfld`: private field access.
|
||||
- `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`.
|
||||
|
||||
They are not registered in `src/ferrolang_vm/registry.py` yet. 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,6 @@
|
||||
unit main;
|
||||
import util;
|
||||
|
||||
fn main() -> i32 {
|
||||
return util.answer();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
unit util;
|
||||
|
||||
pub fn answer() -> i32 {
|
||||
return 42;
|
||||
}
|
||||
@@ -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,4 @@
|
||||
unit a;
|
||||
import b;
|
||||
|
||||
pub fn a_value() -> i32 { return b.b_value(); }
|
||||
@@ -0,0 +1,5 @@
|
||||
// ERROR:3:cycle
|
||||
unit b;
|
||||
import a;
|
||||
|
||||
pub fn b_value() -> i32 { return 1; }
|
||||
@@ -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 alpha;
|
||||
|
||||
pub fn fail() -> !void {
|
||||
return error.Busy;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
unit beta;
|
||||
|
||||
pub fn fail() -> !void {
|
||||
return error.Busy;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
unit main;
|
||||
import beta;
|
||||
import alpha;
|
||||
|
||||
fn one() -> !void { return beta.fail(); }
|
||||
fn two() -> !void { return alpha.fail(); }
|
||||
@@ -0,0 +1,9 @@
|
||||
unit lib;
|
||||
|
||||
pub error LibError {
|
||||
Bad = 1,
|
||||
}
|
||||
|
||||
pub fn value() -> LibError!i32 {
|
||||
return LibError.Bad;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// ERROR:6:error
|
||||
unit main;
|
||||
import lib;
|
||||
|
||||
fn bad() -> !i32 {
|
||||
return try lib.value();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
unit alpha;
|
||||
|
||||
pub fn fail() -> !void {
|
||||
return error.Busy;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
unit beta;
|
||||
|
||||
pub fn fail() -> !void {
|
||||
return error.Busy;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
unit main;
|
||||
import alpha;
|
||||
import beta;
|
||||
|
||||
fn one() -> !void { return alpha.fail(); }
|
||||
fn two() -> !void { return beta.fail(); }
|
||||
@@ -0,0 +1,5 @@
|
||||
// ERROR:3:import
|
||||
unit main;
|
||||
import absent;
|
||||
|
||||
fn main() -> void {}
|
||||
@@ -0,0 +1,5 @@
|
||||
unit data;
|
||||
|
||||
pub struct Record {
|
||||
value: i32,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// ERROR:6:private
|
||||
unit main;
|
||||
import data;
|
||||
|
||||
fn main() -> i32 {
|
||||
let r = data.Record{ value: 7 };
|
||||
return r.value;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// ERROR:6:private
|
||||
unit main;
|
||||
import util;
|
||||
|
||||
fn main() -> i32 {
|
||||
return util.hidden();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
unit util;
|
||||
|
||||
fn hidden() -> i32 {
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
unit data;
|
||||
|
||||
pub struct Record {
|
||||
pub value: i32,
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
unit main;
|
||||
import data;
|
||||
|
||||
fn main() -> i32 {
|
||||
let r = data.Record{ value: 7 };
|
||||
return r.value;
|
||||
}
|
||||
@@ -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(); }
|
||||
@@ -0,0 +1,4 @@
|
||||
// ERROR:2:unit
|
||||
unit other;
|
||||
|
||||
fn main() -> void {}
|
||||
Reference in New Issue
Block a user