Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Proof and design history is in `formal/PRESERVATION-DESIGN.adoc`; current status
- Formal-proof work spans Coq, Idris2, and the applicable estate policy. Checker output is evidence only for the exact proposition and assumptions run; a green build does not establish general language soundness.
- The legacy preservation closure plan is obsolete. `formal/PRESERVATION-DESIGN.adoc` is design/history, not a proof result; `formal/PRESERVATION-HANDOFF.adoc` is historical diagnosis only.
- Modality and `linear_to_affine` artifacts are mechanised, but no unconditional L1/L2 preservation theorem is established. The modality-polymorphic L1 statement is refuted as stated; see `PROOF-NEEDS.adoc` and the EXPLAINME ledger.
- All commits GPG-signed (key `4A03639C1EB1F86C7F0C97A91835A14A2867091E`), email `6759885+hyperpolymath@users.noreply.github.com`. Auto-merge ON for every PR.
- Commit signing (owner ruling 2026-11-10). Each commit on `main` is a squash merge made on github.com: GitHub signs it with its own key, and it shows as Verified. The owner key `4A03639C1EB1F86C7F0C97A91835A14A2867091E` (email `6759885+hyperpolymath@users.noreply.github.com`) signs the commits that the owner makes on a local computer. A cloud agent session signs its branch commits with its own session key; never put a private key of the owner into such a session. Auto-merge ON for every PR.

---

Expand Down
6 changes: 3 additions & 3 deletions TEST-NEEDS.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ compliance, 16 tests)
performance, correctness, 13 tests)
* Benchmarks: `+src/ephapax-parser/benches/parse_bench.rs+`,
`+src/ephapax-vram-cache/benches/cache_bench.rs+`
* Total: *620 tests* (`+cargo test --workspace --all-targets+`);
* Total: *621 tests* (`+cargo test --workspace --all-targets+`);
pass/fail enforced by `+rust-ci.yml+`
* Documented all-target tests: 620
* Documented all-target tests: 621

=== CRG Testing Taxonomy — Status

Expand Down Expand Up @@ -83,7 +83,7 @@ eliminator-fork invariant + its gated capstone, per PROOF-NEEDS.adoc §4)
==== Build & Execution

* [x] cargo build — compiles
* [x] cargo test –workspace –all-targets — 620 pass, 0 fail
* [x] cargo test –workspace –all-targets — 621 pass, 0 fail
* [x] idris2/ephapax-parse-tests.ipkg — compiles AND runs green
(2026-07-07, idris2 0.8.0 + zig-built libephapax_tokbuf.so); CI-gated
compile-only by abi-verify.yml `+idris2-parse-front+` (runtime run stays
Expand Down
99 changes: 99 additions & 0 deletions src/ephapax-cli/tests/run_wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,105 @@ fn a_module_names_a_data_type_only_through_a_pub_import() {
}
}

/// A `type` alias and an extern type follow the rule of data types (owner
/// decision 2026-11-10): a module names one when it declares it, or when
/// it imports the module that declares it. An alias must also be `pub`;
/// an extern type is public, as an extern function is. Each module used
/// to see each alias and each extern type of the program.
#[test]
fn a_module_names_an_alias_or_an_extern_type_only_through_an_import() {
let tmp = tempfile::tempdir().expect("tempdir");
let write =
|name: &str, text: &str| std::fs::write(tmp.path().join(name), text).expect("write module");
write(
"lib.eph",
"module lib\n\npub type Meters = I32\n\ntype Feet = I32\n\n\
pub fn double(m: Meters): Meters = m + m\n\n\
pub fn feet(n: I32): Feet = n\n",
);
write(
"host.eph",
"module host\n\nextern \"c\" {\n type Handle\n fn open(n: I32): Handle\n \
fn close(h: Handle): I32\n}\n",
);
write(
"mid.eph",
"module mid\n\nimport host\n\npub fn via(n: I32): I32 = close(open(n))\n",
);
let keep = "fn keep(m: Meters): Meters = m\n";
let pass = "fn pass(h: Handle): Handle = h\n";

// (imports and declarations, `main` body, output)
for (head, main, want) in [
(format!("import lib\n\n{keep}"), "double(keep(4))", "8"),
// An import list names the alias.
(
format!("import lib(Meters, double)\n\n{keep}"),
"double(keep(4))",
"8",
),
// A function of `lib` has the private alias in its signature.
("import lib\n".to_string(), "feet(3)", "3"),
// A declaration of the module shadows an imported alias.
(
"import lib\n\ndata Meters = M(I32)\n".to_string(),
"match M(5) of | M(n) => n end",
"5",
),
] {
write(
"app.eph",
&format!("module app\n\n{head}\nfn main(): I32 = {main}\n"),
);
for args in [&["run", "app.eph"][..], &["run", "--interp", "app.eph"]] {
assert_eq!(stdout_of(tmp.path(), args), want, "{main} under {args:?}");
}
}

// (imports and declarations, text of the load error; `None`: it compiles).
// The host of `run` does not have `open`, so these programs do not run.
let main = "close(pass(open(1)))";
for (head, error) in [
(format!("import host\n\n{pass}"), None),
(format!("import host(Handle, open, close)\n\n{pass}"), None),
// The import list does not name the type.
(
format!("import host(open, close)\n\n{pass}"),
Some("unknown type `Handle`"),
),
// An extern type of a module that this module does not import.
(
format!("import mid\n\n{pass}"),
Some("unknown type `Handle`"),
),
// A private alias, and an alias that the import list does not name.
(
"import lib\n\nfn keep(f: Feet): Feet = f\n".to_string(),
Some("unknown type `Feet`"),
),
(
format!("import lib(double)\n\n{keep}"),
Some("unknown type `Meters`"),
),
] {
let body = if head.contains("Handle") { main } else { "0" };
write(
"app.eph",
&format!("module app\n\n{head}\nfn main(): I32 = {body}\n"),
);
let out = ephapax(tmp.path(), &["compile", "app.eph"]);
let stderr = String::from_utf8_lossy(&out.stderr);
match error {
None => assert!(out.status.success(), "{head}: stderr:\n{stderr}"),
Some(error) => assert!(
!out.status.success() && stderr.contains(error),
"{head}: exit {:?}, stderr:\n{stderr}",
out.status.code()
),
}
}
}

/// v2 plan rung E7: an arm with a false guard gives the value to the next
/// arm, as wasm and in the interpreter. The desugarer used to take a
/// guarded constructor arm without a look at its guard.
Expand Down
Loading
Loading