Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+110
View File
@@ -0,0 +1,110 @@
use super::checked;
const TYPES: &str = "type Result { Ready(value: Int)\n error Failed(message: Text) }\ntype Box { result: Result\n label: Text }\nfailValue(message: Text) (value: Int) { fail(Result.Failed(message)) }\nfailure(message: Text) (value: Result) { value = result(failValue(message)) }\n";
#[test]
fn rejects_discard_overwrite_and_nested_failure_loss() {
for body in [
"main() () { value = failure(\"bad\") }",
"main() (answer: Result) { answer = failure(\"bad\")\n answer = Result.Ready(1) }",
"main() (answer: Text) { box = Box { result = failure(\"bad\")\n label = \"x\" }\n answer = box.label }",
"main() (answer: Int) { values = [failure(\"bad\")]\n answer = length(values) }",
"ignore(value: Result) () {}",
"read(value: Result.Ready(number)) (answer: Int) { answer = number }\nread(value: Result.Failed(message)) (answer: Int) { answer = length(message) }",
"main() (answer: Result?) { old = failure(\"bad\")\n answer = null }",
"main(old: Result, next: Result?) (answer: Result?) { answer = next }",
"main(old: Result, next: List<Result>) (answer: List<Result>) { answer = next }",
] {
let error = checked(&(TYPES.to_owned() + body)).unwrap_err();
assert_eq!(error.code, "C012", "{body}: {error:?}");
}
}
#[test]
fn capture_arguments_keep_default_propagation() {
let source = TYPES.to_owned()
+ "identity(value: Int) (answer: Int) { answer = value }\nmain() (answer: Result) { answer = result(identity(failValue(\"bad\"))) }";
let program = checked(&source).unwrap();
let main = program
.functions
.iter()
.find(|function| function.signature.name == "main")
.unwrap();
assert_eq!(main.signature.failures.len(), 1);
assert!(main.signature.returns.is_none());
}
#[test]
fn accepts_return_propagation_translation_and_explicit_recovery() {
for body in [
"main() (answer: Result) { answer = failure(\"bad\") }",
"main() (answer: Int) { answer = failValue(\"bad\") }",
"main() (answer: Result) { old = failure(\"bad\")\n answer = Result.Failed(\"replacement\") }",
"identity(value: Result) (answer: Result) { answer = value }\nmain() (answer: Result) { first = failure(\"bad\")\n second = identity(first)\n answer = first }",
"read(value: Result.Ready(number)) (answer: Int) { answer = number }\nread(value: Result.Failed(message)) (answer: Int) { fail(Result.Failed(message)) }",
"read(value: Result.Ready(number)) (answer: Int) recover(\"use zero when unavailable\") pure { answer = number }\nread(value: Result.Failed(message)) (answer: Int) pure recover(\"use zero when unavailable\") { answer = 0 }",
] {
let result = checked(&(TYPES.to_owned() + body));
assert!(result.is_ok(), "{body}: {result:?}");
}
}
#[test]
fn recovery_and_fail_are_not_generic_error_bypasses() {
for body in [
"main() (answer: Int) { fail(Result.Ready(1)) }",
"main() (answer: Int) { fail(1) }",
"main() (answer: Int) recover(\" \") { answer = 0 }",
"read(value: Result.Ready(number)) (answer: Int) recover(\"fallback\") { answer = number }\nread(value: Result.Failed(message)) (answer: Int) { answer = 0 }",
"main() (answer: Int) { fail(Result.Failed(\"bad\"))\n answer = 1 }",
] {
assert!(checked(&(TYPES.to_owned() + body)).is_err(), "{body}");
}
let recovery = "handled(value: Result) (answer: Bool) recover(\"discard unavailable optional probe\") { answer = true }\n";
for expression in [
"false and handled(value)",
"true or handled(value)",
"flag and handled(value)",
] {
let body = format!(
"main(flag: Bool) (answer: Bool) {{ value = failure(\"bad\")\n answer = {expression} }}"
);
assert_eq!(
checked(&(TYPES.to_owned() + recovery + &body))
.unwrap_err()
.code,
"C012",
"{body}"
);
}
}
#[test]
fn capture_requires_exact_nominal_error_identity() {
let prefix = "type Problem { error Denied(code: Int)\n error Missing(message: Text) }\ntype Result { Ready(value: Int)\n error Failed(problem: Problem) }\nraise() (value: Int) { fail(Problem.Denied(7)) }\nmain() (answer: Result) { answer = result(raise()) }";
assert!(checked(prefix).is_ok());
let ambiguous = prefix.replace(
"error Failed(problem: Problem)",
"error Failed(problem: Problem)\n error Again(problem: Problem)",
);
assert!(checked(&ambiguous).is_err());
let wrong = prefix.replace("Failed(problem: Problem)", "Failed(problem: Int)");
assert!(checked(&wrong).is_err());
let normal = prefix.replace("error Missing", "Missing");
assert!(checked(&normal).is_ok());
let unrelated = format!(
"type Other {{ error Denied(code: Int) }}\n{}",
prefix.replace("Failed(problem: Problem)", "Failed(problem: Other)")
);
assert!(checked(&unrelated).is_err());
}
#[test]
fn capture_preserves_zero_and_multiple_success_outputs() {
for source in [
"type Done { Complete }\nwork() () {}\nmain() (value: Done) { value = result(work()) }",
"type Pair { Complete(number: Int, text: Text) }\nwork() (number: Int, text: Text) { number = 1\n text = \"ok\" }\nmain() (value: Pair) { value = result(work()) }",
] {
checked(source).unwrap();
}
}