iter 23.2.3: prelude — class Eq a + Eq Int/Bool/Str instances

This commit is contained in:
2026-05-10 22:35:51 +02:00
parent 65ab6c6b3a
commit 7172e7e8c9
2 changed files with 141 additions and 0 deletions
+36
View File
@@ -920,6 +920,42 @@ mod tests {
)),
"prelude must contain Ordering type def"
);
// Iter 23.2: prelude also ships the `Eq` class plus three
// primitive instances (Eq Int, Eq Bool, Eq Str).
assert!(
prelude.defs.iter().any(|d| matches!(
d,
crate::ast::Def::Class(c) if c.name == "Eq"
)),
"prelude must contain Eq class def"
);
let eq_instance_types: Vec<&str> = prelude
.defs
.iter()
.filter_map(|d| match d {
crate::ast::Def::Instance(i) if i.class == "Eq" => {
if let crate::ast::Type::Con { name, .. } = &i.type_ {
Some(name.as_str())
} else {
None
}
}
_ => None,
})
.collect();
assert!(
eq_instance_types.contains(&"Int"),
"prelude must contain `instance Eq Int`; saw Eq instances on: {eq_instance_types:?}"
);
assert!(
eq_instance_types.contains(&"Bool"),
"prelude must contain `instance Eq Bool`; saw Eq instances on: {eq_instance_types:?}"
);
assert!(
eq_instance_types.contains(&"Str"),
"prelude must contain `instance Eq Str`; saw Eq instances on: {eq_instance_types:?}"
);
}
#[test]
+105
View File
@@ -13,6 +13,111 @@
{ "name": "EQ", "fields": [] },
{ "name": "GT", "fields": [] }
]
},
{
"kind": "class",
"name": "Eq",
"param": "a",
"doc": "Structural equality. Ships in milestone 23 alongside Ord. The primitive `==` operator stays as the surface-level comparator this iter; routing `==` through `Eq.eq` is the declared P2 follow-up (see docs/specs/2026-05-10-23-eq-ord-prelude.md, `Out of scope`).",
"methods": [
{
"name": "eq",
"type": {
"k": "fn",
"params": [
{ "k": "var", "name": "a" },
{ "k": "var", "name": "a" }
],
"param_modes": ["borrow", "borrow"],
"ret": { "k": "con", "name": "Bool" },
"effects": []
}
}
]
},
{
"kind": "instance",
"class": "Eq",
"type": { "k": "con", "name": "Int" },
"doc": "Eq Int. Body lowers to `icmp eq i64` via the existing primitive `==` dispatch in `lower_eq`.",
"methods": [
{
"name": "eq",
"body": {
"t": "lam",
"params": ["x", "y"],
"paramTypes": [
{ "k": "var", "name": "a" },
{ "k": "var", "name": "a" }
],
"retType": { "k": "con", "name": "Bool" },
"body": {
"t": "app",
"fn": { "t": "var", "name": "==" },
"args": [
{ "t": "var", "name": "x" },
{ "t": "var", "name": "y" }
]
}
}
}
]
},
{
"kind": "instance",
"class": "Eq",
"type": { "k": "con", "name": "Bool" },
"doc": "Eq Bool. Body lowers to `icmp eq i1` via the existing primitive `==` dispatch in `lower_eq`.",
"methods": [
{
"name": "eq",
"body": {
"t": "lam",
"params": ["x", "y"],
"paramTypes": [
{ "k": "var", "name": "a" },
{ "k": "var", "name": "a" }
],
"retType": { "k": "con", "name": "Bool" },
"body": {
"t": "app",
"fn": { "t": "var", "name": "==" },
"args": [
{ "t": "var", "name": "x" },
{ "t": "var", "name": "y" }
]
}
}
}
]
},
{
"kind": "instance",
"class": "Eq",
"type": { "k": "con", "name": "Str" },
"doc": "Eq Str. The lambda body shape mirrors Int/Bool for round-trip stability, but the codegen intercept in `emit_fn` overrides the body to call `@ail_str_eq` directly — see `try_emit_primitive_instance_body` in `crates/ailang-codegen/src/lib.rs`.",
"methods": [
{
"name": "eq",
"body": {
"t": "lam",
"params": ["x", "y"],
"paramTypes": [
{ "k": "var", "name": "a" },
{ "k": "var", "name": "a" }
],
"retType": { "k": "con", "name": "Bool" },
"body": {
"t": "app",
"fn": { "t": "var", "name": "==" },
"args": [
{ "t": "var", "name": "x" },
{ "t": "var", "name": "y" }
]
}
}
}
]
}
]
}