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

+137
View File
@@ -0,0 +1,137 @@
use super::checked;
const BASE: &str = "increment(value: Int) (answer: Int) pure { answer = value + 1 }\ndecrement(value: Int) (answer: Int) pure { answer = value - 1 }\napply(route: handler(value: Int) (answer: Int), value: Int) (answer: Int) { answer = route(value) }\nforward(route: handler(value: Int) (answer: Int), value: Int) (answer: Int) { answer = apply(route, value) }\n";
#[test]
fn specializes_handlers_and_reuses_identical_bindings() {
let source = BASE.to_owned()
+ "main() (answer: Int) pure { answer = forward(increment, 1) + forward(increment, 2) + forward(decrement, 3) }";
let program = checked(&source).unwrap();
assert_eq!(program.functions.len(), 7);
for name in ["apply", "forward"] {
let instances = program
.functions
.iter()
.filter(|function| function.signature.name == name)
.collect::<Vec<_>>();
assert_eq!(instances.len(), 2);
assert!(
instances
.iter()
.all(|function| function.signature.inputs.len() == 1
&& function.signature.static_parameters == 1)
);
}
let source =
BASE.to_owned() + "main() (answer: List<Int>) { answer = each(increment, [1, 2]) }";
let program = checked(&source).unwrap();
assert_eq!(program.functions.len(), 3);
}
#[test]
fn checks_unused_templates_and_handler_signatures() {
for body in [
"bad(route: handler(value: Int) (answer: Int)) (answer: Text) { answer = route(1) }",
"bad(route: handler(value: Int) (answer: Int)) (answer: Int) {}",
"bad(route: handler(value: List) ()) () {}",
"bad(route: handler() (answer: List<Int, Text>)) () {}",
"bad(route: handler(private value: Int) ()) () {}",
"bad(route: handler(value: Int) (value: Int)) () {}",
"bad(route: handler(value: Int) (answer: Int)) (route: Int) { route = 1 }",
"wrong(value: Int) (other: Int) { other = value }\nmain() (answer: Int) { answer = apply(wrong, 1) }",
"wrong(value: Text) (answer: Int) { answer = length(value) }\nmain() (answer: Int) { answer = apply(wrong, 1) }",
"main() (answer: Int) { answer = apply(increment(1), 1) }",
"main() (answer: Int) { increment = 1\n answer = apply(increment, 1) }",
"bad(route: handler(value: Text) (answer: Int), value: Int) (answer: Int) { answer = forward(route, value) }",
] {
assert!(checked(&(BASE.to_owned() + body)).is_err(), "{body}");
}
}
#[test]
fn handlers_are_not_values_or_clause_axes() {
for body in [
"bad(route: handler(value: Int) (answer: Int)) () { value = route }",
"bad(route: handler(value: Int) (answer: Int)) () { route = 1 }",
"bad(route: handler(value: Int) (answer: Int)) () { values = [route] }",
"bad(route: handler(value: Int) (answer: Int)) (answer: Bool) { answer = route == route }",
"bad(route: handler(value: Int) (answer: Int), value: Int) (answer: Int) { answer = route(value) }\nbad(route: other, value: other) (answer: Int) { answer = 0 }",
"bad(route: handler(value: Int) (answer: Int), value: Int) (answer: Int) { answer = route(value) }\nbad(route: handler(value: Int) (answer: Int), value: Int) (answer: Int) { answer = 0 }",
] {
assert!(checked(&(BASE.to_owned() + body)).is_err(), "{body}");
}
let source = BASE.to_owned()
+ "choose(route: handler(value: Int) (answer: Int), value: Int < 0) (answer: Int) { answer = 0 }\nchoose(route: handler(value: Int) (answer: Int), value: other) (answer: Int) { answer = route(value) }\nmain() (answer: Int) { answer = choose(increment, 1) }";
assert!(checked(&source).is_ok());
}
#[test]
fn handler_cycles_and_unrestricted_pure_effects_are_rejected() {
for source in [
BASE.to_owned() + "cycle(value: Int) (answer: Int) { answer = forward(cycle, value) }",
BASE.to_owned()
+ "unused(route: handler(value: Int) (answer: Int), value: Int) (answer: Int) { answer = unused(route, value) }",
] {
assert_eq!(checked(&source).unwrap_err().code, "C007");
}
for body in [
"bad(route: handler(value: Int) (answer: Int), value: Int) (answer: Int) pure { answer = route(value) }",
"bad(route: handler(value: Int) (answer: Int), value: Int) (answer: Int) pure { answer = forward(route, value) }",
"bad(route: handler(value: Int) (answer: Int), values: List<Int>) (answers: List<Int>) pure { answers = each(route, values) }",
] {
assert_eq!(
checked(&(BASE.to_owned() + body)).unwrap_err().code,
"C013",
"{body}"
);
}
let source = BASE.to_owned()
+ "ignore(route: handler(value: Int) (answer: Int), value: Int) (answer: Int) pure { answer = value }\npass(route: handler(value: Int) (answer: Int), value: Int) (answer: Int) pure { answer = ignore(route, value) }";
assert!(checked(&source).is_ok());
}
#[test]
fn generic_error_outputs_keep_handling_obligations() {
let prefix = "type Result { Ready(value: Int)\n error Failed(message: Text) }\n";
for body in [
"bad(route: handler(value: Int) (answer: Result)) () { lost = route(1) }",
"bad(route: handler(value: Int) (answer: Result), values: List<Int>) (answer: Int) { lost = each(route, values)\n answer = length(lost) }",
"bad(route: handler(value: Int) (answer: Result)) (answer: Result) { answer = route(1)\n answer = Result.Ready(0) }",
] {
assert_eq!(
checked(&(prefix.to_owned() + body)).unwrap_err().code,
"C012",
"{body}"
);
}
let source = prefix.to_owned()
+ "valid(route: handler(value: Int) (answer: Result)) (answer: Result) { answer = route(1) }";
assert!(checked(&source).is_ok());
}
#[test]
fn each_binding_keeps_its_own_nominal_failure_set() {
let source = BASE.to_owned()
+ "type First { error Failed(code: Int) }\ntype Second { error Failed(code: Int) }\ntype FirstResult { Ready(value: Int)\n error Failed(problem: First) }\ntype SecondResult { Ready(value: Int)\n error Failed(problem: Second) }\nfirst(value: Int) (answer: Int) { fail(First.Failed(value)) }\nsecond(value: Int) (answer: Int) { fail(Second.Failed(value)) }\nfirstEntry() (answer: FirstResult) { answer = result(forward(first, 1)) }\nsecondEntry() (answer: SecondResult) { answer = result(forward(second, 2)) }";
let program = checked(&source).unwrap();
let instances = program
.functions
.iter()
.filter(|function| function.signature.name == "apply")
.collect::<Vec<_>>();
assert_eq!(instances.len(), 2);
assert!(
instances
.iter()
.all(|function| function.signature.failures.len() == 1)
);
assert_ne!(
instances[0].signature.failures,
instances[1].signature.failures
);
let wrong = source.replace(
"firstEntry() (answer: FirstResult)",
"firstEntry() (answer: SecondResult)",
);
assert_eq!(checked(&wrong).unwrap_err().code, "C005");
}