use crate::abi;
use crate::catalog::MethodCatalog;
use crate::type_pattern::{CollectionCtor, ScalarType};
use crate::{MethodEntry, MethodLowering, Purity, TypePattern};
fn vec_of_t() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::var("T")],
}
}
#[must_use]
pub fn builtin_catalog() -> MethodCatalog {
MethodCatalog::build()
.entry(vec_push())
.entry(vec_len())
.entry(vec_get())
.entry(vec_is_empty())
.entry(vec_to_text())
.entry(deque_push_front())
.entry(deque_push_back())
.entry(deque_pop_front())
.entry(deque_pop_back())
.entry(deque_len())
.entry(deque_get())
.entry(deque_is_empty())
.entry(map_insert())
.entry(map_get())
.entry(map_contains())
.entry(map_remove())
.entry(map_len())
.entry(map_is_empty())
.entry(set_insert())
.entry(set_remove())
.entry(set_contains())
.entry(set_len())
.entry(set_is_empty())
.entry(counter_get())
.entry(counter_inc())
.entry(counter_len())
.entry(counter_is_empty())
.entry(max_heap_push())
.entry(max_heap_pop())
.entry(max_heap_peek())
.entry(max_heap_len())
.entry(max_heap_is_empty())
.entry(min_heap_push())
.entry(min_heap_pop())
.entry(min_heap_peek())
.entry(min_heap_len())
.entry(min_heap_is_empty())
.entry(bitset_insert())
.entry(bitset_remove())
.entry(bitset_contains())
.entry(bitset_len())
.entry(bitset_is_empty())
.entry(grid_width())
.entry(grid_height())
.entry(grid_get())
.entry(grid_set())
.entry(grid_contains())
.entry(grid_neighbors4())
.entry(grid_neighbors8())
.entry(grid_around4())
.entry(grid_around8())
.entry(grid_count4())
.entry(grid_count8())
.entry(grid_count4_where())
.entry(grid_count8_where())
.entry(grid_positions())
.entry(grid_cells())
.entry(grid_row())
.entry(grid_column())
.entry(grid_find())
.entry(grid_find_all())
.entry(grid_transpose())
.entry(grid_rotate_left())
.entry(grid_rotate_right())
.entry(seq_map())
.entry(seq_filter())
.entry(seq_fold())
.entry(seq_sum())
.entry(seq_count())
.entry(seq_count_if())
.entry(seq_sorted())
.entry(seq_sorted_by_key())
.entry(seq_unique())
.entry(seq_reversed())
.entry(seq_frequencies())
.entry(seq_join())
.entry(seq_chunks())
.entry(seq_windows())
.entry(seq_take())
.entry(seq_skip())
.entry(seq_take_while())
.entry(seq_enumerate())
.entry(seq_zip())
.entry(seq_flat_map())
.entry(seq_filter_map())
.entry(seq_product())
.entry(seq_min())
.entry(seq_max())
.entry(seq_min_by())
.entry(seq_max_by())
.entry(seq_any())
.entry(seq_all())
.entry(seq_find())
.entry(seq_position())
.entry(seq_reduce())
.entry(seq_to_vec())
.entry(seq_to_set())
.entry(seq_to_map())
.entry(seq_to_counter())
.entry(seq_to_deque())
.entry(seq_to_min_heap())
.entry(seq_to_max_heap())
.entry(seq_to_bitset())
.entry(text_len())
.entry(text_int())
.entry(text_float())
.entry(text_is_empty())
.entry(text_get())
.entry(float_abs())
.entry(float_sqrt())
.entry(float_floor())
.entry(float_ceil())
.entry(float_round())
.entry(float_sign())
.entry(float_to_int())
.entry(float_to_text())
.entry(float_is_nan())
.entry(float_is_infinite())
.entry(float_min())
.entry(float_max())
.entry(int_to_float())
.entry(char_to_int())
.entry(int_to_char())
.entry(int_to_text())
.entry(char_to_text())
.entry(int_wrapping_add())
.entry(int_saturating_add())
.entry(int_checked_add())
.entry(int_wrapping_sub())
.entry(int_saturating_sub())
.entry(int_checked_sub())
.entry(int_wrapping_mul())
.entry(int_saturating_mul())
.entry(int_checked_mul())
.entry(vec_index())
.entry(vec_index_set())
.entry(deque_index())
.entry(deque_index_set())
.entry(text_index())
.entry(map_index())
.entry(map_index_set())
.entry(counter_index())
.entry(counter_index_set())
.entry(grid_index())
.entry(grid_index_set())
.entry(map_index_min())
.entry(map_index_max())
.entry(counter_keys())
.entry(counter_values())
.entry(map_keys())
.entry(map_values())
.finish()
.expect("built-in catalog must be duplicate-free")
}
fn counter_keys() -> MethodEntry {
MethodEntry {
receiver: counter_of_t(),
name: "keys",
params: vec![],
result: TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::var("T")],
},
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterKeys),
doc: "Every key, as a `Vec[T]`, ordered with `values()`.",
}
}
fn counter_values() -> MethodEntry {
MethodEntry {
receiver: counter_of_t(),
name: "values",
params: vec![],
result: TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::Scalar(ScalarType::Int)],
},
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterValues),
doc: "Every count, as a `Vec[Int]`, ordered with `keys()`.",
}
}
fn map_keys() -> MethodEntry {
MethodEntry {
receiver: map_of_k_v(),
name: "keys",
params: vec![],
result: TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::var("K")],
},
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapKeys),
doc: "Every key, as a `Vec[K]`, ordered with `values()`.",
}
}
fn map_values() -> MethodEntry {
MethodEntry {
receiver: map_of_k_v(),
name: "values",
params: vec![],
result: TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::var("V")],
},
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapValues),
doc: "Every value, as a `Vec[V]`, ordered with `keys()`.",
}
}
fn vec_index() -> MethodEntry {
MethodEntry {
receiver: vec_of_t(),
name: crate::catalog::INDEX_READ,
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::var("T"),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecGet),
doc: "`v[i]` — the element at `i`; faults if out of range.",
}
}
fn vec_index_set() -> MethodEntry {
MethodEntry {
receiver: vec_of_t(),
name: crate::catalog::INDEX_STORE,
params: vec![TypePattern::Scalar(ScalarType::Int), TypePattern::var("T")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecSet),
doc: "`v[i] = value` — replace the element at `i`; faults if out of range \
(it never appends — `push` is the spelling that grows a vector).",
}
}
fn deque_index() -> MethodEntry {
MethodEntry {
receiver: deque_of_t(),
name: crate::catalog::INDEX_READ,
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::var("T"),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeGet),
doc: "`d[i]` — the element at `i` (0-based from the front); faults if out of range.",
}
}
fn deque_index_set() -> MethodEntry {
MethodEntry {
receiver: deque_of_t(),
name: crate::catalog::INDEX_STORE,
params: vec![TypePattern::Scalar(ScalarType::Int), TypePattern::var("T")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeSet),
doc: "`d[i] = value` — replace the element at `i` (0-based from the front); \
faults if out of range (it never inserts).",
}
}
fn text_index() -> MethodEntry {
MethodEntry {
receiver: text_receiver(),
name: crate::catalog::INDEX_READ,
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Scalar(ScalarType::Char),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextGet),
doc: "`t[i]` — the `Char` at `i`, indexing by Unicode scalar value and not \
by byte; faults if out of range (ADR-086).",
}
}
fn map_index() -> MethodEntry {
MethodEntry {
receiver: map_of_k_v(),
name: crate::catalog::INDEX_READ,
params: vec![TypePattern::var("K")],
result: TypePattern::var("V"),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapIndex),
doc: "`m[key]` — the value for `key`; **faults** if absent (§4.7; `.get` is the \
spelling that answers with absence).",
}
}
fn map_index_set() -> MethodEntry {
MethodEntry {
receiver: map_of_k_v(),
name: crate::catalog::INDEX_STORE,
params: vec![TypePattern::var("K"), TypePattern::var("V")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapInsert),
doc: "`m[key] = value` — set `key`, replacing any prior value.",
}
}
fn map_of_k_int_value() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::Map,
args: vec![
TypePattern::var("K"),
TypePattern::is_scalar("V", ScalarType::Int),
],
}
}
fn map_index_min() -> MethodEntry {
MethodEntry {
receiver: map_of_k_int_value(),
name: crate::catalog::INDEX_STORE_MIN,
params: vec![TypePattern::var("K"), TypePattern::var("V")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapUpdateMin),
doc: "`d[key] min= candidate` — keep the smaller value; an absent entry \
accepts the first value.",
}
}
fn map_index_max() -> MethodEntry {
MethodEntry {
receiver: map_of_k_int_value(),
name: crate::catalog::INDEX_STORE_MAX,
params: vec![TypePattern::var("K"), TypePattern::var("V")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapUpdateMax),
doc: "`b[key] max= score` — keep the larger value; an absent entry accepts \
the first value.",
}
}
fn counter_index() -> MethodEntry {
MethodEntry {
receiver: counter_of_t(),
name: crate::catalog::INDEX_READ,
params: vec![TypePattern::var("T")],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterGet),
doc: "`c[key]` — the count for `key`, or zero if absent; never faults.",
}
}
fn counter_index_set() -> MethodEntry {
MethodEntry {
receiver: counter_of_t(),
name: crate::catalog::INDEX_STORE,
params: vec![TypePattern::var("T"), TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterSet),
doc: "`c[key] = n` — set the count for `key`.",
}
}
fn grid_index() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: crate::catalog::INDEX_READ,
params: vec![
TypePattern::Scalar(ScalarType::Int),
TypePattern::Scalar(ScalarType::Int),
],
result: TypePattern::var("T"),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridGet),
doc: "`grid[x, y]` — the cell at (x, y); faults if out of range.",
}
}
fn grid_index_set() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: crate::catalog::INDEX_STORE,
params: vec![
TypePattern::Scalar(ScalarType::Int),
TypePattern::Scalar(ScalarType::Int),
TypePattern::var("T"),
],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridSet),
doc: "`grid[x, y] = value` — set the cell at (x, y); faults if out of range.",
}
}
fn text_receiver() -> TypePattern {
TypePattern::Scalar(ScalarType::Text)
}
fn text_len() -> MethodEntry {
MethodEntry {
receiver: text_receiver(),
name: "len",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextLen),
doc: "Number of Unicode scalar values (chars) in the text.",
}
}
fn text_int() -> MethodEntry {
MethodEntry {
receiver: text_receiver(),
name: "int",
params: vec![],
result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextInt),
doc: "The Int this text spells as `Some(n)`, or `None` if it spells none.",
}
}
fn text_float() -> MethodEntry {
MethodEntry {
receiver: text_receiver(),
name: "float",
params: vec![],
result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Float))),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextFloat),
doc: "The Float this text spells as `Some(x)`, or `None` if it spells none.",
}
}
fn text_is_empty() -> MethodEntry {
MethodEntry {
receiver: text_receiver(),
name: "is_empty",
params: vec![],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextIsEmpty),
doc: "True iff the text has no chars.",
}
}
fn text_get() -> MethodEntry {
MethodEntry {
receiver: text_receiver(),
name: "get",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Scalar(ScalarType::Char),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextGet),
doc: "The `Char` at `index`; faults if out of range. `t[index]` is the \
same row and the same answer (ADR-086).",
}
}
fn float_receiver() -> TypePattern {
TypePattern::Scalar(ScalarType::Float)
}
fn float_abs() -> MethodEntry {
MethodEntry {
receiver: float_receiver(),
name: "abs",
params: vec![],
result: TypePattern::Scalar(ScalarType::Float),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatAbs),
doc: "Absolute value.",
}
}
fn float_sqrt() -> MethodEntry {
MethodEntry {
receiver: float_receiver(),
name: "sqrt",
params: vec![],
result: TypePattern::Scalar(ScalarType::Float),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatSqrt),
doc: "Square root. Negative inputs yield NaN (IEEE-754).",
}
}
fn float_floor() -> MethodEntry {
MethodEntry {
receiver: float_receiver(),
name: "floor",
params: vec![],
result: TypePattern::Scalar(ScalarType::Float),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatFloor),
doc: "Round toward negative infinity.",
}
}
fn float_ceil() -> MethodEntry {
MethodEntry {
receiver: float_receiver(),
name: "ceil",
params: vec![],
result: TypePattern::Scalar(ScalarType::Float),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatCeil),
doc: "Round toward positive infinity.",
}
}
fn float_round() -> MethodEntry {
MethodEntry {
receiver: float_receiver(),
name: "round",
params: vec![],
result: TypePattern::Scalar(ScalarType::Float),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatRound),
doc: "Round half away from zero.",
}
}
fn float_sign() -> MethodEntry {
MethodEntry {
receiver: float_receiver(),
name: "sign",
params: vec![],
result: TypePattern::Scalar(ScalarType::Float),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatSign),
doc: "Sign as -1.0 / 0.0 / 1.0. NaN yields NaN.",
}
}
fn float_to_int() -> MethodEntry {
MethodEntry {
receiver: float_receiver(),
name: "to_int",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatToInt),
doc: "Truncate toward zero to an Int. Faults on NaN, ±inf, or out of i64 range.",
}
}
fn float_to_text() -> MethodEntry {
MethodEntry {
receiver: float_receiver(),
name: "to_text",
params: vec![],
result: TypePattern::Scalar(ScalarType::Text),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatToText),
doc: "Format as Text (shortest round-trip form; inf/-inf/NaN as literals).",
}
}
fn float_is_nan() -> MethodEntry {
MethodEntry {
receiver: float_receiver(),
name: "is_nan",
params: vec![],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatIsNan),
doc: "True iff NaN.",
}
}
fn float_is_infinite() -> MethodEntry {
MethodEntry {
receiver: float_receiver(),
name: "is_infinite",
params: vec![],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatIsInfinite),
doc: "True iff ±infinity.",
}
}
fn float_min() -> MethodEntry {
MethodEntry {
receiver: float_receiver(),
name: "min",
params: vec![TypePattern::Scalar(ScalarType::Float)],
result: TypePattern::Scalar(ScalarType::Float),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatMin),
doc: "The smaller of two floats. If either is NaN, returns the other.",
}
}
fn float_max() -> MethodEntry {
MethodEntry {
receiver: float_receiver(),
name: "max",
params: vec![TypePattern::Scalar(ScalarType::Float)],
result: TypePattern::Scalar(ScalarType::Float),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatMax),
doc: "The larger of two floats. If either is NaN, returns the other.",
}
}
fn int_to_float() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Int),
name: "to_float",
params: vec![],
result: TypePattern::Scalar(ScalarType::Float),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntToFloat),
doc: "Widen to Float (explicit Int→Float conversion, §4.12).",
}
}
fn int_to_text() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Int),
name: "to_text",
params: vec![],
result: TypePattern::Scalar(ScalarType::Text),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntToText),
doc: "Format as Text — the same digits `out` writes (ADR-143).",
}
}
fn char_to_int() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Char),
name: "to_int",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CharToInt),
doc: "The Unicode scalar value, as an `Int`. Never faults (ADR-086).",
}
}
fn char_to_text() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Char),
name: "to_text",
params: vec![],
result: TypePattern::Scalar(ScalarType::Text),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CharToText),
doc: "The one-character Text holding this scalar — the same character \
`out` writes. Never faults (ADR-143).",
}
}
fn int_to_char() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Int),
name: "to_char",
params: vec![],
result: TypePattern::Scalar(ScalarType::Char),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntToChar),
doc: "The `Char` with this Unicode scalar value; **faults** \
(`InvalidChar`) if it is negative, above `0x10FFFF`, or a \
surrogate. The narrowing half of the pair, as `Float.to_int` is \
(ADR-086).",
}
}
fn int_wrapping_add() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Int),
name: "wrapping_add",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntWrappingAdd),
doc: "Add with two's-complement wraparound instead of a fault.",
}
}
fn int_saturating_add() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Int),
name: "saturating_add",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntSaturatingAdd),
doc: "Add, clamping to Int's ends instead of faulting.",
}
}
fn int_checked_add() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Int),
name: "checked_add",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntCheckedAdd),
doc: "Add, answering None where the checked `+` would fault.",
}
}
fn int_wrapping_sub() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Int),
name: "wrapping_sub",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntWrappingSub),
doc: "Subtract with two's-complement wraparound instead of a fault.",
}
}
fn int_saturating_sub() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Int),
name: "saturating_sub",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntSaturatingSub),
doc: "Subtract, clamping to Int's ends instead of faulting.",
}
}
fn int_checked_sub() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Int),
name: "checked_sub",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntCheckedSub),
doc: "Subtract, answering None where the checked `-` would fault.",
}
}
fn int_wrapping_mul() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Int),
name: "wrapping_mul",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntWrappingMul),
doc: "Multiply with two's-complement wraparound instead of a fault. The \
one row here a program could not write for itself: every arithmetic \
operator is checked and the language has no bitwise operators.",
}
}
fn int_saturating_mul() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Int),
name: "saturating_mul",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntSaturatingMul),
doc: "Multiply, clamping to Int's ends instead of faulting.",
}
}
fn int_checked_mul() -> MethodEntry {
MethodEntry {
receiver: TypePattern::Scalar(ScalarType::Int),
name: "checked_mul",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntCheckedMul),
doc: "Multiply, answering None where the checked `*` would fault.",
}
}
fn vec_push() -> MethodEntry {
MethodEntry {
receiver: vec_of_t(),
name: "push",
params: vec![TypePattern::var("T")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecPush),
doc: "Append a value to the end; returns Unit.",
}
}
fn vec_len() -> MethodEntry {
MethodEntry {
receiver: vec_of_t(),
name: "len",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecLen),
doc: "Number of elements in the vector.",
}
}
fn vec_get() -> MethodEntry {
MethodEntry {
receiver: vec_of_t(),
name: "get",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::var("T"),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecGet),
doc: "The element at `index`; faults `IndexOutOfBounds` if out of range.",
}
}
fn vec_is_empty() -> MethodEntry {
MethodEntry {
receiver: vec_of_t(),
name: "is_empty",
params: vec![],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecIsEmpty),
doc: "True iff the vector has no elements.",
}
}
fn vec_of_char() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::is_scalar("T", ScalarType::Char)],
}
}
fn vec_to_text() -> MethodEntry {
MethodEntry {
receiver: vec_of_char(),
name: "to_text",
params: vec![],
result: TypePattern::Scalar(ScalarType::Text),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecToText),
doc: "These Chars as one Text, with nothing between them (ADR-144).",
}
}
fn deque_of_t() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::Deque,
args: vec![TypePattern::var("T")],
}
}
fn deque_push_front() -> MethodEntry {
MethodEntry {
receiver: deque_of_t(),
name: "push_front",
params: vec![TypePattern::var("T")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePushFront),
doc: "Prepend a value to the front; returns Unit.",
}
}
fn deque_push_back() -> MethodEntry {
MethodEntry {
receiver: deque_of_t(),
name: "push_back",
params: vec![TypePattern::var("T")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePushBack),
doc: "Append a value to the back; returns Unit.",
}
}
fn deque_pop_front() -> MethodEntry {
MethodEntry {
receiver: deque_of_t(),
name: "pop_front",
params: vec![],
result: TypePattern::var("T"),
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePopFront),
doc: "Remove and return the front element; faults if empty.",
}
}
fn deque_pop_back() -> MethodEntry {
MethodEntry {
receiver: deque_of_t(),
name: "pop_back",
params: vec![],
result: TypePattern::var("T"),
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePopBack),
doc: "Remove and return the back element; faults if empty.",
}
}
fn deque_len() -> MethodEntry {
MethodEntry {
receiver: deque_of_t(),
name: "len",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeLen),
doc: "Number of elements in the deque.",
}
}
fn deque_get() -> MethodEntry {
MethodEntry {
receiver: deque_of_t(),
name: "get",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::var("T"),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeGet),
doc: "The element at `index` (0-based from the front); faults if out of range.",
}
}
fn deque_is_empty() -> MethodEntry {
MethodEntry {
receiver: deque_of_t(),
name: "is_empty",
params: vec![],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeIsEmpty),
doc: "True iff the deque has no elements.",
}
}
fn map_of_k_v() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::Map,
args: vec![TypePattern::var("K"), TypePattern::var("V")],
}
}
fn set_of_t() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::Set,
args: vec![TypePattern::var("T")],
}
}
fn counter_of_t() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::Counter,
args: vec![TypePattern::var("T")],
}
}
fn map_insert() -> MethodEntry {
MethodEntry {
receiver: map_of_k_v(),
name: "insert",
params: vec![TypePattern::var("K"), TypePattern::var("V")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapInsert),
doc: "Set `key` to `value`, replacing any prior value; returns Unit.",
}
}
fn map_get() -> MethodEntry {
MethodEntry {
receiver: map_of_k_v(),
name: "get",
params: vec![TypePattern::var("K")],
result: TypePattern::Option(Box::new(TypePattern::var("V"))),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapGet),
doc: "The value for `key` as `Some(value)`, or `None` if absent.",
}
}
fn map_contains() -> MethodEntry {
MethodEntry {
receiver: map_of_k_v(),
name: "contains",
params: vec![TypePattern::var("K")],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapContains),
doc: "True iff `key` is present in the map.",
}
}
fn map_remove() -> MethodEntry {
MethodEntry {
receiver: map_of_k_v(),
name: "remove",
params: vec![TypePattern::var("K")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapRemove),
doc: "Remove `key` if present; returns Unit.",
}
}
fn map_len() -> MethodEntry {
MethodEntry {
receiver: map_of_k_v(),
name: "len",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapLen),
doc: "Number of entries in the map.",
}
}
fn map_is_empty() -> MethodEntry {
MethodEntry {
receiver: map_of_k_v(),
name: "is_empty",
params: vec![],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapIsEmpty),
doc: "True iff the map has no entries.",
}
}
fn set_insert() -> MethodEntry {
MethodEntry {
receiver: set_of_t(),
name: "insert",
params: vec![TypePattern::var("T")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetInsert),
doc: "Add `value` to the set; returns Unit.",
}
}
fn set_remove() -> MethodEntry {
MethodEntry {
receiver: set_of_t(),
name: "remove",
params: vec![TypePattern::var("T")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetRemove),
doc: "Remove `value` if present; returns Unit.",
}
}
fn set_contains() -> MethodEntry {
MethodEntry {
receiver: set_of_t(),
name: "contains",
params: vec![TypePattern::var("T")],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetContains),
doc: "True iff `value` is in the set.",
}
}
fn set_len() -> MethodEntry {
MethodEntry {
receiver: set_of_t(),
name: "len",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetLen),
doc: "Number of elements in the set.",
}
}
fn set_is_empty() -> MethodEntry {
MethodEntry {
receiver: set_of_t(),
name: "is_empty",
params: vec![],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetIsEmpty),
doc: "True iff the set has no elements.",
}
}
fn counter_get() -> MethodEntry {
MethodEntry {
receiver: counter_of_t(),
name: "get",
params: vec![TypePattern::var("T")],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterGet),
doc: "The count for `key`, or zero if absent (never faults).",
}
}
fn counter_inc() -> MethodEntry {
MethodEntry {
receiver: counter_of_t(),
name: "inc",
params: vec![TypePattern::var("T")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterInc),
doc: "Increment the count for `key` by one; returns Unit.",
}
}
fn counter_len() -> MethodEntry {
MethodEntry {
receiver: counter_of_t(),
name: "len",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterLen),
doc: "Number of distinct keys in the counter.",
}
}
fn counter_is_empty() -> MethodEntry {
MethodEntry {
receiver: counter_of_t(),
name: "is_empty",
params: vec![],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterIsEmpty),
doc: "True iff the counter has no keys.",
}
}
fn min_heap_of_t() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::MinHeap,
args: vec![TypePattern::var("T")],
}
}
fn max_heap_of_t() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::MaxHeap,
args: vec![TypePattern::var("T")],
}
}
fn max_heap_push() -> MethodEntry {
MethodEntry {
receiver: max_heap_of_t(),
name: "push",
params: vec![TypePattern::var("T")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapPush),
doc: "Push a value onto the max-heap; returns Unit.",
}
}
fn max_heap_pop() -> MethodEntry {
MethodEntry {
receiver: max_heap_of_t(),
name: "pop",
params: vec![],
result: TypePattern::var("T"),
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapPop),
doc: "Remove and return the largest element; faults if empty.",
}
}
fn max_heap_peek() -> MethodEntry {
MethodEntry {
receiver: max_heap_of_t(),
name: "peek",
params: vec![],
result: TypePattern::var("T"),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapPeek),
doc: "The largest element without removing it; faults if empty.",
}
}
fn max_heap_len() -> MethodEntry {
MethodEntry {
receiver: max_heap_of_t(),
name: "len",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapLen),
doc: "Number of elements in the max-heap.",
}
}
fn max_heap_is_empty() -> MethodEntry {
MethodEntry {
receiver: max_heap_of_t(),
name: "is_empty",
params: vec![],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapIsEmpty),
doc: "True iff the max-heap has no elements.",
}
}
fn min_heap_push() -> MethodEntry {
MethodEntry {
receiver: min_heap_of_t(),
name: "push",
params: vec![TypePattern::var("T")],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapPush),
doc: "Push a value onto the min-heap; returns Unit.",
}
}
fn min_heap_pop() -> MethodEntry {
MethodEntry {
receiver: min_heap_of_t(),
name: "pop",
params: vec![],
result: TypePattern::var("T"),
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapPop),
doc: "Remove and return the smallest element; faults if empty.",
}
}
fn min_heap_peek() -> MethodEntry {
MethodEntry {
receiver: min_heap_of_t(),
name: "peek",
params: vec![],
result: TypePattern::var("T"),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapPeek),
doc: "The smallest element without removing it; faults if empty.",
}
}
fn min_heap_len() -> MethodEntry {
MethodEntry {
receiver: min_heap_of_t(),
name: "len",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapLen),
doc: "Number of elements in the min-heap.",
}
}
fn min_heap_is_empty() -> MethodEntry {
MethodEntry {
receiver: min_heap_of_t(),
name: "is_empty",
params: vec![],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapIsEmpty),
doc: "True iff the min-heap has no elements.",
}
}
fn bitset_receiver() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::BitSet,
args: vec![],
}
}
fn bitset_insert() -> MethodEntry {
MethodEntry {
receiver: bitset_receiver(),
name: "insert",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetInsert),
doc: "Set the bit for a non-negative integer; returns Unit.",
}
}
fn bitset_remove() -> MethodEntry {
MethodEntry {
receiver: bitset_receiver(),
name: "remove",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetRemove),
doc: "Clear the bit for an integer; returns Unit.",
}
}
fn bitset_contains() -> MethodEntry {
MethodEntry {
receiver: bitset_receiver(),
name: "contains",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::ScalarPrimitive(abi::RuntimeSymbol::BitsetContains),
doc: "True iff the bit for the integer is set.",
}
}
fn bitset_len() -> MethodEntry {
MethodEntry {
receiver: bitset_receiver(),
name: "len",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetLen),
doc: "Number of set bits (popcount).",
}
}
fn bitset_is_empty() -> MethodEntry {
MethodEntry {
receiver: bitset_receiver(),
name: "is_empty",
params: vec![],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetIsEmpty),
doc: "True iff no bits are set.",
}
}
fn grid_of_t() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::Grid,
args: vec![TypePattern::var("T")],
}
}
fn point_pattern() -> TypePattern {
TypePattern::Tuple(vec![
TypePattern::Scalar(ScalarType::Int),
TypePattern::Scalar(ScalarType::Int),
])
}
fn grid_width() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "width",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridWidth),
doc: "The number of columns.",
}
}
fn grid_height() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "height",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridHeight),
doc: "The number of rows.",
}
}
fn grid_get() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "get",
params: vec![
TypePattern::Scalar(ScalarType::Int),
TypePattern::Scalar(ScalarType::Int),
],
result: TypePattern::var("T"),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridGet),
doc: "The cell at (x, y); faults if out of range.",
}
}
fn grid_set() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "set",
params: vec![
TypePattern::Scalar(ScalarType::Int),
TypePattern::Scalar(ScalarType::Int),
TypePattern::var("T"),
],
result: TypePattern::Unit,
purity: Purity::Impure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridSet),
doc: "Set the cell at (x, y); faults if out of range.",
}
}
fn grid_contains() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "contains",
params: vec![
TypePattern::Scalar(ScalarType::Int),
TypePattern::Scalar(ScalarType::Int),
],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridContains),
doc: "True iff (x, y) is within the grid.",
}
}
fn grid_neighbors4() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "neighbors4",
params: vec![point_pattern()],
result: TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![point_pattern()],
},
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridNeighbors4),
doc: "The 4 orthogonal in-bounds neighbors of a point, as a Vec of (x, y).",
}
}
fn grid_neighbors8() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "neighbors8",
params: vec![point_pattern()],
result: TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![point_pattern()],
},
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridNeighbors8),
doc: "The 8 in-bounds neighbors of a point, as a Vec of (x, y).",
}
}
fn maybe_point() -> TypePattern {
TypePattern::Option(Box::new(point_pattern()))
}
fn around4_pattern() -> TypePattern {
TypePattern::Record {
name: "Around4",
fields: vec![
("up", maybe_point()),
("left", maybe_point()),
("right", maybe_point()),
("down", maybe_point()),
],
}
}
fn around8_pattern() -> TypePattern {
TypePattern::Record {
name: "Around8",
fields: vec![
("up_left", maybe_point()),
("up", maybe_point()),
("up_right", maybe_point()),
("left", maybe_point()),
("right", maybe_point()),
("down_left", maybe_point()),
("down", maybe_point()),
("down_right", maybe_point()),
],
}
}
fn grid_around4() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "around4",
params: vec![point_pattern()],
result: around4_pattern(),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridAround4),
doc: "The 4 orthogonal neighbors by name: { up, left, right, down }, \
each Some((x, y)) or None off the grid.",
}
}
fn grid_around8() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "around8",
params: vec![point_pattern()],
result: around8_pattern(),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridAround8),
doc: "The 8 neighbors by name, in reading order: { up_left, up, up_right, \
left, right, down_left, down, down_right }, each Some((x, y)) or None.",
}
}
fn grid_count4() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "count4",
params: vec![point_pattern(), TypePattern::var("T")],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCount4),
doc: "How many of the 4 orthogonal neighbors hold `value`. \
A neighbor off the grid has no cell and is not counted.",
}
}
fn grid_count8() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "count8",
params: vec![point_pattern(), TypePattern::var("T")],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCount8),
doc: "How many of the 8 neighbors hold `value`. \
A neighbor off the grid has no cell and is not counted.",
}
}
fn grid_count4_where() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "count4_where",
params: vec![
point_pattern(),
TypePattern::Function {
params: vec![TypePattern::var("T")],
result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
},
],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCount4Where),
doc: "How many of the 4 orthogonal neighbors hold a cell the closure accepts. \
A neighbor off the grid has no cell, so the closure never sees one.",
}
}
fn grid_count8_where() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "count8_where",
params: vec![
point_pattern(),
TypePattern::Function {
params: vec![TypePattern::var("T")],
result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
},
],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCount8Where),
doc: "How many of the 8 neighbors hold a cell the closure accepts. \
A neighbor off the grid has no cell, so the closure never sees one.",
}
}
fn grid_positions() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "positions",
params: vec![],
result: TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![point_pattern()],
},
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridPositions),
doc: "All (x, y) positions in row-major order, as a Vec.",
}
}
fn grid_cells() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "cells",
params: vec![],
result: TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::var("T")],
},
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCells),
doc: "All cells in row-major order, as a Vec.",
}
}
fn grid_row() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "row",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::var("T")],
},
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridRow),
doc: "Row `y` as a Vec; faults if out of range.",
}
}
fn grid_column() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "column",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::var("T")],
},
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridColumn),
doc: "Column `x` as a Vec; faults if out of range.",
}
}
fn grid_find() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "find",
params: vec![TypePattern::var("T")],
result: TypePattern::Option(Box::new(point_pattern())),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridFind),
doc: "The first (x, y) whose cell equals `value` as `Some((x, y))`, or `None`.",
}
}
fn grid_find_all() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "find_all",
params: vec![TypePattern::var("T")],
result: TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![point_pattern()],
},
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridFindAll),
doc: "All (x, y) positions whose cell equals `value`, as a Vec.",
}
}
fn grid_transpose() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "transpose",
params: vec![],
result: grid_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridTranspose),
doc: "A transposed copy (rows ↔ columns).",
}
}
fn grid_rotate_left() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "rotate_left",
params: vec![],
result: grid_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridRotateLeft),
doc: "A copy rotated 90° counter-clockwise.",
}
}
fn grid_rotate_right() -> MethodEntry {
MethodEntry {
receiver: grid_of_t(),
name: "rotate_right",
params: vec![],
result: grid_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridRotateRight),
doc: "A copy rotated 90° clockwise.",
}
}
fn iterable_of_t() -> TypePattern {
TypePattern::iterable(TypePattern::var("T"))
}
fn t_to_u() -> TypePattern {
TypePattern::Function {
params: vec![TypePattern::var("T")],
result: Box::new(TypePattern::var("U")),
}
}
fn t_to_bool() -> TypePattern {
TypePattern::Function {
params: vec![TypePattern::var("T")],
result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
}
}
fn t_to_option_u() -> TypePattern {
TypePattern::Function {
params: vec![TypePattern::var("T")],
result: Box::new(TypePattern::Option(Box::new(TypePattern::var("U")))),
}
}
fn acc_t_to_acc() -> TypePattern {
TypePattern::Function {
params: vec![TypePattern::var("Acc"), TypePattern::var("T")],
result: Box::new(TypePattern::var("Acc")),
}
}
fn t_t_to_t() -> TypePattern {
TypePattern::Function {
params: vec![TypePattern::var("T"), TypePattern::var("T")],
result: Box::new(TypePattern::var("T")),
}
}
fn seq_map() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "map",
params: vec![t_to_u()],
result: vec_of_u(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_map"),
doc: "Apply a function to each element, collecting into a Vec.",
}
}
fn vec_of_u() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::var("U")],
}
}
fn seq_filter() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "filter",
params: vec![t_to_bool()],
result: vec_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_filter"),
doc: "Keep elements satisfying a predicate, collecting into a Vec.",
}
}
fn seq_fold() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "fold",
params: vec![TypePattern::var("Acc"), acc_t_to_acc()],
result: TypePattern::var("Acc"),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_fold"),
doc: "Reduce elements left-to-right with an accumulator and combining closure.",
}
}
fn seq_sum() -> MethodEntry {
MethodEntry {
receiver: iterable_of_int_elem(),
name: "sum",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_sum"),
doc: "Sum the (Int) elements.",
}
}
fn seq_count() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "count",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_count"),
doc: "Number of elements.",
}
}
fn seq_count_if() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "count",
params: vec![t_to_bool()],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_count"),
doc: "Number of elements satisfying the predicate.",
}
}
fn seq_sorted() -> MethodEntry {
MethodEntry {
receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::Ord)),
name: "sorted",
params: vec![],
result: vec_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecSorted),
doc: "A new Vec holding these elements in ascending order.",
}
}
fn seq_unique() -> MethodEntry {
MethodEntry {
receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::HashStable)),
name: "unique",
params: vec![],
result: vec_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecUnique),
doc: "A new Vec with duplicate elements removed, keeping first occurrences.",
}
}
fn seq_reversed() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "reversed",
params: vec![],
result: vec_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecReversed),
doc: "A new Vec holding these elements in reverse order.",
}
}
fn vec_of_vec_of_t() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![vec_of_t()],
}
}
fn seq_chunks() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "chunks",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: vec_of_vec_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecChunks),
doc: "Consecutive non-overlapping runs of n; the last may be shorter. \
Faults if n is not positive.",
}
}
fn seq_windows() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "windows",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: vec_of_vec_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecWindows),
doc: "Every consecutive run of exactly n, sliding by one. Empty if n \
exceeds the length; faults if n is not positive.",
}
}
fn iterable_of_text_elem() -> TypePattern {
TypePattern::iterable(TypePattern::is_scalar("T", ScalarType::Text))
}
fn seq_join() -> MethodEntry {
MethodEntry {
receiver: iterable_of_text_elem(),
name: "join",
params: vec![TypePattern::Scalar(ScalarType::Text)],
result: TypePattern::Scalar(ScalarType::Text),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecJoin),
doc: "These Text items concatenated with `sep` between them.",
}
}
fn seq_frequencies() -> MethodEntry {
MethodEntry {
receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::HashStable)),
name: "frequencies",
params: vec![],
result: counter_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecFrequencies),
doc: "A Counter holding how many times each element occurs.",
}
}
fn t_t_to_bool() -> TypePattern {
TypePattern::Function {
params: vec![TypePattern::var("T"), TypePattern::var("T")],
result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
}
}
fn t_to_vec_u() -> TypePattern {
TypePattern::Function {
params: vec![TypePattern::var("T")],
result: Box::new(vec_of_u()),
}
}
fn seq_take() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "take",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: vec_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_take"),
doc: "Keep at most the first n elements.",
}
}
fn seq_skip() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "skip",
params: vec![TypePattern::Scalar(ScalarType::Int)],
result: vec_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_skip"),
doc: "Drop the first n elements.",
}
}
fn seq_take_while() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "take_while",
params: vec![t_to_bool()],
result: vec_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_take_while"),
doc: "Keep elements until the predicate is false.",
}
}
fn vec_of_index_and_t() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::Tuple(vec![
TypePattern::Scalar(ScalarType::Int),
TypePattern::var("T"),
])],
}
}
fn vec_of_t_and_u() -> TypePattern {
TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::Tuple(vec![
TypePattern::var("T"),
TypePattern::var("U"),
])],
}
}
fn seq_enumerate() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "enumerate",
params: vec![],
result: vec_of_index_and_t(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_enumerate"),
doc: "Pair each element with its index.",
}
}
fn seq_zip() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "zip",
params: vec![vec_of_u()],
result: vec_of_t_and_u(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_zip"),
doc: "Pair elements with another sequence, stopping at the shorter length.",
}
}
fn seq_flat_map() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "flat_map",
params: vec![t_to_vec_u()],
result: vec_of_u(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_flat_map"),
doc: "Map each element to a Vec and concatenate the results.",
}
}
fn seq_filter_map() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "filter_map",
params: vec![t_to_option_u()],
result: vec_of_u(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_filter_map"),
doc: "Map each element to an Option and keep the Some payloads.",
}
}
fn iterable_of_int_elem() -> TypePattern {
TypePattern::iterable(TypePattern::is_scalar("T", ScalarType::Int))
}
fn seq_product() -> MethodEntry {
MethodEntry {
receiver: iterable_of_int_elem(),
name: "product",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_product"),
doc: "Multiply the (Int) elements.",
}
}
fn seq_min() -> MethodEntry {
MethodEntry {
receiver: iterable_of_int_elem(),
name: "min",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_min"),
doc: "Smallest (Int) element. Faults on an empty sequence (D1).",
}
}
fn seq_max() -> MethodEntry {
MethodEntry {
receiver: iterable_of_int_elem(),
name: "max",
params: vec![],
result: TypePattern::Scalar(ScalarType::Int),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_max"),
doc: "Largest (Int) element. Faults on an empty sequence (D1).",
}
}
fn seq_min_by() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "min_by",
params: vec![t_t_to_bool()],
result: TypePattern::var("T"),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_min_by"),
doc: "Smallest element per a (T,T)->Bool \"less-than\" comparator.",
}
}
fn seq_max_by() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "max_by",
params: vec![t_t_to_bool()],
result: TypePattern::var("T"),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_max_by"),
doc: "Largest element per a (T,T)->Bool \"less-than\" comparator.",
}
}
fn seq_any() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "any",
params: vec![t_to_bool()],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_any"),
doc: "True if any element satisfies the predicate (short-circuits).",
}
}
fn seq_all() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "all",
params: vec![t_to_bool()],
result: TypePattern::Scalar(ScalarType::Bool),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_all"),
doc: "True if all elements satisfy the predicate (short-circuits).",
}
}
fn seq_find() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "find",
params: vec![t_to_bool()],
result: TypePattern::Option(Box::new(TypePattern::var("T"))),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_find"),
doc: "The first matching element, or None.",
}
}
fn seq_position() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "position",
params: vec![t_to_bool()],
result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_position"),
doc: "The index of the first matching element, or None.",
}
}
fn seq_reduce() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "reduce",
params: vec![t_t_to_t()],
result: TypePattern::var("T"),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_reduce"),
doc: "Reduce left-to-right, seeded with the first element.",
}
}
fn seq_sorted_by_key() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "sorted_by_key",
params: vec![TypePattern::Function {
params: vec![TypePattern::var("T")],
result: Box::new(TypePattern::of_kind("K", crate::CapKind::Ord)),
}],
result: vec_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecSortedByKey),
doc: "A new Vec ordered by the key the closure extracts.",
}
}
fn seq_to_vec() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "to_vec",
params: vec![],
result: vec_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_to_vec"),
doc: "The items as a Vec. On a Vec receiver this is the receiver itself.",
}
}
fn seq_to_set() -> MethodEntry {
MethodEntry {
receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::HashStable)),
name: "to_set",
params: vec![],
result: set_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_to_set"),
doc: "A Set holding these items, duplicates dropped.",
}
}
fn seq_to_map() -> MethodEntry {
MethodEntry {
receiver: TypePattern::iterable(TypePattern::Tuple(vec![
TypePattern::of_kind("K", crate::CapKind::HashStable),
TypePattern::var("V"),
])),
name: "to_map",
params: vec![],
result: map_of_k_v(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_to_map"),
doc: "A Map built from (key, value) pairs. Duplicate keys: last wins.",
}
}
fn seq_to_counter() -> MethodEntry {
MethodEntry {
receiver: TypePattern::iterable(TypePattern::Tuple(vec![
TypePattern::of_kind("T", crate::CapKind::HashStable),
TypePattern::Scalar(ScalarType::Int),
])),
name: "to_counter",
params: vec![],
result: counter_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_to_counter"),
doc: "A Counter built from (key, count) pairs. Duplicate keys: last wins.",
}
}
fn seq_to_deque() -> MethodEntry {
MethodEntry {
receiver: iterable_of_t(),
name: "to_deque",
params: vec![],
result: deque_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_to_deque"),
doc: "A Deque holding these items, in order.",
}
}
fn seq_to_min_heap() -> MethodEntry {
MethodEntry {
receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::Ord)),
name: "to_min_heap",
params: vec![],
result: min_heap_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_to_min_heap"),
doc: "A MinHeap holding these items.",
}
}
fn seq_to_max_heap() -> MethodEntry {
MethodEntry {
receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::Ord)),
name: "to_max_heap",
params: vec![],
result: max_heap_of_t(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_to_max_heap"),
doc: "A MaxHeap holding these items.",
}
}
fn seq_to_bitset() -> MethodEntry {
MethodEntry {
receiver: iterable_of_int_elem(),
name: "to_bitset",
params: vec![],
result: bitset_receiver(),
purity: Purity::Pure,
lowering: MethodLowering::Intrinsic("seq_to_bitset"),
doc: "A BitSet holding these (Int) items. Faults on a negative or oversized member.",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_catalog_row_documents_itself() {
for e in builtin_catalog().entries() {
let what = format!("{}.{}/{}", e.receiver, e.name, e.arity());
assert!(e.doc.len() > 8, "{what} has no real documentation");
assert!(
e.doc.ends_with('.') || e.doc.ends_with(')'),
"{what}'s doc is not a sentence: {:?}",
e.doc
);
assert!(
e.doc
.chars()
.next()
.is_some_and(|c| c.is_uppercase() || c == '`'),
"{what}'s doc does not open a sentence: {:?}",
e.doc
);
}
}
#[test]
fn builtin_catalog_has_vec_methods() {
let cat = builtin_catalog();
assert!(cat.len() >= 4);
let vec_pat = vec_of_t();
let push_hits: Vec<_> = cat.by_receiver_and_name(&vec_pat, "push").collect();
assert_eq!(push_hits.len(), 1);
assert_eq!(
push_hits[0].lowering,
MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecPush)
);
}
#[test]
fn builtin_catalog_get_can_fault() {
let cat = builtin_catalog();
let vec_pat = vec_of_t();
let get = cat
.by_receiver_and_name(&vec_pat, "get")
.next()
.expect("vec.get exists");
assert!(get.can_fault());
let bitset_pat = bitset_receiver();
let insert = cat
.by_receiver_and_name(&bitset_pat, "insert")
.next()
.expect("bitset.insert exists");
assert!(insert.can_fault());
}
#[test]
fn a_keyed_collection_enumerates_and_count_has_two_arities() {
let cat = builtin_catalog();
let map_pat = map_of_k_v();
let counter_pat = counter_of_t();
for (pat, what) in [(map_pat.clone(), "Map"), (counter_pat.clone(), "Counter")] {
for name in ["keys", "values"] {
let hits: Vec<_> = cat.by_receiver_and_name(&pat, name).collect();
assert_eq!(hits.len(), 1, "{what}.{name}()");
assert_eq!(hits[0].arity(), 0, "{what}.{name}() takes no arguments");
assert!(
matches!(
hits[0].result,
TypePattern::Collection {
ctor: CollectionCtor::Vec,
..
}
),
"{what}.{name}() answers a Vec so every §6.3 combinator applies"
);
}
}
let counter_values = cat
.by_receiver_and_name(&counter_pat, "values")
.next()
.expect("Counter.values");
assert_eq!(
counter_values.result,
TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::Scalar(ScalarType::Int)]
}
);
let map_values = cat
.by_receiver_and_name(&map_pat, "values")
.next()
.expect("Map.values");
assert_eq!(
map_values.result,
TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::var("V")]
}
);
let map_keys = cat
.by_receiver_and_name(&map_pat, "keys")
.next()
.expect("Map.keys");
assert_eq!(
map_keys.result,
TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![TypePattern::var("K")]
}
);
let arities: Vec<usize> = cat
.by_receiver_and_name(&iterable_of_t(), "count")
.map(|e| e.arity())
.collect();
assert_eq!(arities.len(), 2, "count has two rows");
assert!(arities.contains(&0) && arities.contains(&1));
}
#[test]
fn every_pipeline_combinator_is_one_row_on_the_generic_receiver() {
let cat = builtin_catalog();
let combinators = [
"map",
"filter",
"filter_map",
"flat_map",
"take",
"skip",
"take_while",
"enumerate",
"zip",
"fold",
"reduce",
"sum",
"product",
"count",
"any",
"all",
"find",
"position",
"min",
"max",
"min_by",
"max_by",
"sorted",
"sorted_by_key",
"unique",
"reversed",
"frequencies",
"join",
"to_vec",
"to_set",
"to_map",
"to_counter",
"to_deque",
"to_min_heap",
"to_max_heap",
"to_bitset",
];
for name in combinators {
let rows: Vec<_> = cat.entries().iter().filter(|e| e.name == name).collect();
assert!(!rows.is_empty(), "`{name}` has no row at all");
let mut generic: Vec<usize> = rows
.iter()
.filter(|e| matches!(e.receiver, TypePattern::Iterable { .. }))
.map(|e| e.arity())
.collect();
assert!(!generic.is_empty(), "`{name}` has no generic row");
let seen = generic.len();
generic.sort_unstable();
generic.dedup();
assert_eq!(
generic.len(),
seen,
"`{name}` has two generic rows at one arity — one receiver \
getting a feature ten should have"
);
for row in rows {
assert!(
matches!(row.receiver, TypePattern::Iterable { .. })
|| !crate::is_pipeline_receiver(&row.receiver),
"`{name}` also has a row on {}, which the generic row \
accepts — both would match, and which one a call resolves \
to would be insertion order",
row.receiver
);
}
}
assert!(
!crate::PIPELINE_RECEIVERS.contains(&CollectionCtor::Grid),
"a generic `map` would claim §6.4's name and answer a `Vec`"
);
for e in cat.entries() {
assert!(
!matches!(
e.receiver,
TypePattern::Collection {
ctor: CollectionCtor::Seq,
..
}
),
"`{}` is still on a `Seq`, which has no values",
e.name
);
}
}
#[test]
fn a_conversion_exists_for_every_collection_that_can_be_constructed() {
let cat = builtin_catalog();
for (name, ctor) in [
("to_vec", CollectionCtor::Vec),
("to_set", CollectionCtor::Set),
("to_map", CollectionCtor::Map),
("to_counter", CollectionCtor::Counter),
("to_deque", CollectionCtor::Deque),
("to_min_heap", CollectionCtor::MinHeap),
("to_max_heap", CollectionCtor::MaxHeap),
("to_bitset", CollectionCtor::BitSet),
] {
let row = cat
.entries()
.iter()
.find(|e| e.name == name)
.unwrap_or_else(|| panic!("`{name}` has no row"));
assert_eq!(row.arity(), 0, "`{name}` takes no arguments");
let built = match &row.result {
TypePattern::Collection { ctor, .. } => *ctor,
other => panic!("`{name}` answers {other}, not a collection"),
};
assert_eq!(built, ctor, "`{name}` builds the collection it names");
}
assert!(
cat.entries().iter().all(|e| e.name != "to_grid"),
"a grid needs a width, and an item sequence does not carry one"
);
for name in ["to_map", "to_counter"] {
let row = cat.entries().iter().find(|e| e.name == name).unwrap();
let TypePattern::Iterable { item } = &row.receiver else {
panic!("`{name}` is not on the generic receiver")
};
assert!(
matches!(**item, TypePattern::Tuple(ref els) if els.len() == 2),
"`{name}` accepts a `Map` or a `Counter` by saying its item is a pair"
);
}
}
#[test]
fn the_subscript_rows_are_a_closed_set_no_program_can_name() {
let cat = builtin_catalog();
let of = |ctor: CollectionCtor, args: usize| TypePattern::Collection {
ctor,
args: (0..args).map(|_| TypePattern::var("T")).collect(),
};
let map_pat = map_of_k_v();
for (pat, indices, what) in [
(of(CollectionCtor::Vec, 1), 1, "Vec"),
(of(CollectionCtor::Deque, 1), 1, "Deque"),
(map_pat.clone(), 1, "Map"),
(of(CollectionCtor::Counter, 1), 1, "Counter"),
(of(CollectionCtor::Grid, 1), 2, "Grid"),
(text_receiver(), 1, "Text"),
] {
let hits: Vec<_> = cat
.by_receiver_and_name(&pat, crate::catalog::INDEX_READ)
.collect();
assert_eq!(hits.len(), 1, "{what} reads through exactly one row");
assert_eq!(hits[0].arity(), indices, "{what} indexes at {indices}");
}
for (pat, args, what) in [
(of(CollectionCtor::Vec, 1), 2, "Vec"),
(of(CollectionCtor::Deque, 1), 2, "Deque"),
(map_pat.clone(), 2, "Map"),
(of(CollectionCtor::Counter, 1), 2, "Counter"),
(of(CollectionCtor::Grid, 1), 3, "Grid"),
] {
let hits: Vec<_> = cat
.by_receiver_and_name(&pat, crate::catalog::INDEX_STORE)
.collect();
assert_eq!(hits.len(), 1, "{what} stores through exactly one row");
assert_eq!(
hits[0].arity(),
args,
"{what}'s store takes its indices and then the value"
);
}
for (pat, what) in [
(of(CollectionCtor::Set, 1), "Set"),
(of(CollectionCtor::MinHeap, 1), "MinHeap"),
(of(CollectionCtor::MaxHeap, 1), "MaxHeap"),
(of(CollectionCtor::BitSet, 0), "BitSet"),
] {
for name in [crate::catalog::INDEX_READ, crate::catalog::INDEX_STORE] {
assert_eq!(
cat.by_receiver_and_name(&pat, name).count(),
0,
"{what} has no `{name}`"
);
}
}
assert_eq!(
cat.by_receiver_and_name(&text_receiver(), crate::catalog::INDEX_STORE)
.count(),
0,
"a `Text` is immutable: it reads through a subscript and has \
no element store"
);
for (pat, push, what) in [
(of(CollectionCtor::Vec, 1), "push", "Vec"),
(of(CollectionCtor::Deque, 1), "push_back", "Deque"),
] {
let store = cat
.by_receiver_and_name(&pat, crate::catalog::INDEX_STORE)
.next()
.unwrap_or_else(|| panic!("{what}'s store"));
let appender = cat
.by_receiver_and_name(&pat, push)
.next()
.unwrap_or_else(|| panic!("{what}.{push}"));
assert_ne!(
store.lowering, appender.lowering,
"{what}'s store replaces; `{push}` appends"
);
assert!(
store.can_fault(),
"{what}'s store reports an index it does not hold"
);
}
let get = cat
.by_receiver_and_name(&map_pat, "get")
.next()
.expect("Map.get");
let index = cat
.by_receiver_and_name(&map_pat, crate::catalog::INDEX_READ)
.next()
.expect("Map's subscript");
assert_ne!(get.lowering, index.lowering);
assert!(
index.can_fault() && !get.can_fault(),
"indexing faults where `.get` answers"
);
let map_int_value = map_of_k_int_value();
let plain_store = cat
.by_receiver_and_name(&map_pat, crate::catalog::INDEX_STORE)
.next()
.expect("Map's store")
.lowering
.clone();
for (name, what) in [
(crate::catalog::INDEX_STORE_MIN, "min="),
(crate::catalog::INDEX_STORE_MAX, "max="),
] {
let hits: Vec<_> = cat.by_receiver_and_name(&map_int_value, name).collect();
assert_eq!(hits.len(), 1, "`{what}` is one row on a Map");
assert_eq!(hits[0].arity(), 2, "`{what}` takes its key and its value");
assert_ne!(
hits[0].lowering, plain_store,
"`{what}` must not lower to the plain store"
);
for (pat, other) in [
(of(CollectionCtor::Counter, 1), "Counter"),
(of(CollectionCtor::Grid, 1), "Grid"),
(of(CollectionCtor::Vec, 1), "Vec"),
(of(CollectionCtor::Set, 1), "Set"),
] {
assert_eq!(
cat.by_receiver_and_name(&pat, name).count(),
0,
"{other} has no `{what}`"
);
}
}
assert_ne!(
cat.by_receiver_and_name(&map_int_value, crate::catalog::INDEX_STORE_MIN)
.next()
.expect("min=")
.lowering,
cat.by_receiver_and_name(&map_int_value, crate::catalog::INDEX_STORE_MAX)
.next()
.expect("max=")
.lowering,
);
for name in [
crate::catalog::INDEX_READ,
crate::catalog::INDEX_STORE,
crate::catalog::INDEX_STORE_MIN,
crate::catalog::INDEX_STORE_MAX,
] {
assert!(
!name
.chars()
.next()
.is_some_and(|c| c.is_alphabetic() || c == '_'),
"`{name}` must not be spellable as a method name"
);
}
}
#[test]
fn catalog_covers_every_collection_kind() {
let cat = builtin_catalog();
for (ctor, name) in [
(CollectionCtor::Vec, "Vec"),
(CollectionCtor::Deque, "Deque"),
(CollectionCtor::Set, "Set"),
(CollectionCtor::Counter, "Counter"),
(CollectionCtor::MinHeap, "MinHeap"),
(CollectionCtor::MaxHeap, "MaxHeap"),
(CollectionCtor::BitSet, "BitSet"),
] {
let args: Vec<TypePattern> = match ctor.arity() {
0 => Vec::new(),
n => (0..n).map(|_| TypePattern::var("T")).collect(),
};
let pat = TypePattern::Collection { ctor, args };
let len = cat.by_receiver_and_name(&pat, "len").count();
let is_empty = cat.by_receiver_and_name(&pat, "is_empty").count();
assert!(len >= 1, "{name} missing len method");
assert!(is_empty >= 1, "{name} missing is_empty method");
}
let map_pat = map_of_k_v();
assert!(cat.by_receiver_and_name(&map_pat, "len").count() >= 1);
assert!(cat.by_receiver_and_name(&map_pat, "is_empty").count() >= 1);
let grid_pat = grid_of_t();
assert!(cat.by_receiver_and_name(&grid_pat, "width").count() >= 1);
assert!(cat.by_receiver_and_name(&grid_pat, "neighbors4").count() >= 1);
}
#[test]
fn the_neighbourhood_records_field_order_is_reading_order() {
let cat = builtin_catalog();
let grid_pat = grid_of_t();
for (method, expected) in [
("around4", &["up", "left", "right", "down"][..]),
(
"around8",
&[
"up_left",
"up",
"up_right",
"left",
"right",
"down_left",
"down",
"down_right",
][..],
),
] {
let rows: Vec<_> = cat.by_receiver_and_name(&grid_pat, method).collect();
assert_eq!(rows.len(), 1, "`Grid[T].{method}` is one row");
let TypePattern::Record { name, fields } = &rows[0].result else {
panic!("`Grid[T].{method}` answers a nominal record");
};
assert_eq!(
*name,
if method == "around4" {
"Around4"
} else {
"Around8"
}
);
let names: Vec<&str> = fields.iter().map(|(n, _)| *n).collect();
assert_eq!(names, expected, "`{name}`'s fields are its slot order");
for (fname, fpat) in fields {
assert_eq!(
*fpat,
TypePattern::Option(Box::new(point_pattern())),
"`{name}.{fname}` is an Option[(Int, Int)]"
);
}
assert_eq!(rows[0].result.to_string(), *name);
}
}
#[test]
fn the_clipped_vec_rows_still_answer_a_vec() {
let cat = builtin_catalog();
let grid_pat = grid_of_t();
let vec_of_points = TypePattern::Collection {
ctor: CollectionCtor::Vec,
args: vec![point_pattern()],
};
for method in ["neighbors4", "neighbors8"] {
let rows: Vec<_> = cat.by_receiver_and_name(&grid_pat, method).collect();
assert_eq!(rows.len(), 1);
assert_eq!(
rows[0].result, vec_of_points,
"`{method}` answers the clipped Vec a graph walk consumes"
);
}
}
#[test]
fn only_the_counts_that_run_a_closure_can_fault() {
let cat = builtin_catalog();
let grid_pat = grid_of_t();
let predicate = TypePattern::Function {
params: vec![TypePattern::var("T")],
result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
};
for (method, second, can_fault) in [
("count4", TypePattern::var("T"), false),
("count8", TypePattern::var("T"), false),
("count4_where", predicate.clone(), true),
("count8_where", predicate, true),
] {
let rows: Vec<_> = cat.by_receiver_and_name(&grid_pat, method).collect();
assert_eq!(rows.len(), 1, "`Grid[T].{method}` is one row");
let row = rows[0];
assert_eq!(
row.params,
vec![point_pattern(), second],
"`{method}` takes the position and then what to count"
);
assert_eq!(row.result, TypePattern::Scalar(ScalarType::Int));
assert_eq!(
row.can_fault(),
can_fault,
"`{method}`: only a row that calls back into the program \
needs a fault check after it"
);
}
}
#[test]
fn a_non_faulting_row_with_a_value_result_cannot_answer_the_unit_sentinel() {
let cat = builtin_catalog();
let mut checked = 0;
for entry in cat.entries() {
let MethodLowering::RuntimeSymbol(sym) = entry.lowering else {
continue;
};
checked += 1;
let ret = sym.sig().ret;
let result_is_unit = entry.result == TypePattern::Unit;
match (ret, result_is_unit) {
(abi::AbiRet::GcUnit, true) | (abi::AbiRet::Gc, false) => {}
_ => panic!(
"{}.{} declares `{}` and lowers to `{}`, whose manifest return is \
{ret:?}. A wrapper answers either a value (`AbiRet::Gc`, \
non-`Unit` result) or nothing (`AbiRet::GcUnit`, `Unit` \
result); an answer that is sometimes absent is spelled \
`Option[T]`, never a Unit sentinel under a value type.",
entry.receiver,
entry.name,
entry.result,
sym.name(),
),
}
}
assert!(
checked >= 40,
"expected the sweep to reach most of the catalog, it reached {checked} rows"
);
}
#[test]
fn a_scalar_primitive_row_answers_the_scalar_channel_and_a_scalar_type() {
let cat = builtin_catalog();
let mut rows = 0;
for entry in cat.entries() {
let MethodLowering::ScalarPrimitive(sym) = entry.lowering else {
continue;
};
rows += 1;
assert_eq!(
sym.sig().ret,
abi::AbiRet::RawI64,
"{}.{} lowers as a scalar primitive, so `{}` must answer the \
scalar channel; a `-> Gc` row here is a box the caller would \
have to unwrap again, which is the whole thing this arm exists \
to remove",
entry.receiver,
entry.name,
sym.name(),
);
assert!(
matches!(entry.result, TypePattern::Scalar(_)),
"{}.{} answers `{}`, which the scalar channel cannot carry",
entry.receiver,
entry.name,
entry.result,
);
assert!(
!entry.allocates(),
"{}.{} allocates, so it cannot be a non-safepoint instruction",
entry.receiver,
entry.name,
);
}
assert_eq!(
rows, 1,
"`BitSet.contains` is the only scalar-primitive row today; a second \
one wants an arm in `praxis-mir`'s `lower_scalar_primitive` before \
this number moves"
);
}
#[test]
fn map_get_and_grid_find_answer_an_option() {
let cat = builtin_catalog();
let map_pat = map_of_k_v();
let get = cat
.by_receiver_and_name(&map_pat, "get")
.next()
.expect("map.get exists");
assert_eq!(
get.result,
TypePattern::Option(Box::new(TypePattern::var("V"))),
"§5.7 writes `Map[K,V].get(K) -> Option[V]`"
);
let grid_pat = grid_of_t();
let find = cat
.by_receiver_and_name(&grid_pat, "find")
.next()
.expect("grid.find exists");
assert_eq!(find.result, TypePattern::Option(Box::new(point_pattern())));
let counter_pat = counter_of_t();
let counter_get = cat
.by_receiver_and_name(&counter_pat, "get")
.next()
.expect("counter.get exists");
assert_eq!(
counter_get.result,
TypePattern::Scalar(ScalarType::Int),
"§6.2: a Counter's absent values read as zero, deliberately"
);
}
#[test]
fn text_int_and_float_answer_options() {
let cat = builtin_catalog();
for (name, scalar) in [("int", ScalarType::Int), ("float", ScalarType::Float)] {
let entry = cat
.by_receiver_and_name(&TypePattern::Scalar(ScalarType::Text), name)
.next()
.unwrap_or_else(|| panic!("`Text.{name}()` exists"));
assert_eq!(
entry.result,
TypePattern::Option(Box::new(TypePattern::Scalar(scalar))),
"a text that is not a number is absence, not a fault (\u{00a7}4.7)"
);
assert!(entry.params.is_empty(), "`{name}` takes no arguments");
}
}
#[test]
fn the_two_text_reads_answer_a_char() {
let cat = builtin_catalog();
let text = text_receiver();
for name in [crate::catalog::INDEX_READ, "get"] {
let row = cat
.by_receiver_and_name(&text, name)
.next()
.unwrap_or_else(|| panic!("Text.{name} exists"));
assert_eq!(
row.result,
TypePattern::Scalar(ScalarType::Char),
"ADR-086: `Text.{name}` answers a Char, not the char's scalar value"
);
assert_eq!(
row.lowering,
MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextGet),
"`Text.{name}` lowers through the one text read"
);
}
}
#[test]
fn char_and_int_convert_both_ways() {
let cat = builtin_catalog();
let to_int = cat
.by_receiver_and_name(&TypePattern::Scalar(ScalarType::Char), "to_int")
.next()
.expect("Char.to_int exists");
assert_eq!(to_int.result, TypePattern::Scalar(ScalarType::Int));
assert_eq!(to_int.purity, Purity::Pure);
let to_char = cat
.by_receiver_and_name(&TypePattern::Scalar(ScalarType::Int), "to_char")
.next()
.expect("Int.to_char exists");
assert_eq!(to_char.result, TypePattern::Scalar(ScalarType::Char));
assert!(
to_char.can_fault(),
"not every Int is a Unicode scalar value, so the narrowing faults"
);
assert!(
!to_int.can_fault(),
"every Unicode scalar value fits an Int, so the widening cannot"
);
}
#[test]
fn the_to_text_family_is_int_float_and_char() {
let cat = builtin_catalog();
for scalar in [ScalarType::Int, ScalarType::Float, ScalarType::Char] {
let receiver = TypePattern::Scalar(scalar);
let row = cat
.by_receiver_and_name(&receiver, "to_text")
.next()
.unwrap_or_else(|| panic!("{scalar:?}.to_text exists"));
assert_eq!(row.result, TypePattern::Scalar(ScalarType::Text));
assert_eq!(row.purity, Purity::Pure);
assert!(row.allocates(), "{scalar:?}.to_text answers a fresh Text");
assert!(!row.can_fault(), "{scalar:?}.to_text cannot fail");
}
for scalar in [ScalarType::Bool, ScalarType::Byte, ScalarType::Text] {
let receiver = TypePattern::Scalar(scalar);
assert_eq!(
cat.by_receiver_and_name(&receiver, "to_text").count(),
0,
"the `to_text` family is three scalars; {scalar:?} is not one of \
them, and a universal `to_text` is §8.1 interpolation's question"
);
}
}
#[test]
fn join_is_one_row_and_a_sequence_of_chars_has_its_own_name() {
let cat = builtin_catalog();
let join: Vec<_> = cat.entries().iter().filter(|e| e.name == "join").collect();
assert_eq!(join.len(), 1, "`join` is one row");
assert_eq!(join[0].receiver, iterable_of_text_elem());
assert_eq!(join[0].params, vec![TypePattern::Scalar(ScalarType::Text)]);
assert_eq!(join[0].result, TypePattern::Scalar(ScalarType::Text));
assert!(
!cat.entries()
.iter()
.any(|e| e.name == "to_text" && matches!(e.receiver, TypePattern::Iterable { .. })),
"an `Iterable.to_text` would collide with the scalar `to_text` rows"
);
let chars_pat = vec_of_char();
let chars = cat
.by_receiver_and_name(&chars_pat, "to_text")
.next()
.expect("Vec[Char].to_text exists");
assert_eq!(chars.result, TypePattern::Scalar(ScalarType::Text));
assert!(chars.params.is_empty());
}
#[test]
fn reversed_is_a_barrier_with_no_bound_on_its_element() {
let cat = builtin_catalog();
let receiver = iterable_of_t();
let row = cat
.by_receiver_and_name(&receiver, "reversed")
.next()
.expect("Iterable.reversed exists");
assert_eq!(row.result, vec_of_t());
assert_eq!(row.purity, Purity::Pure);
assert!(row.bounds().is_empty(), "reversal reads no callback");
assert!(
!row.can_fault(),
"there is no element `reversed` can be handed that it cannot reverse"
);
assert!(
matches!(
row.lowering,
MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecReversed)
),
"a barrier is a runtime call, not a fused stage: reversal cannot \
answer its first element until it has seen the last"
);
}
#[test]
fn a_grouping_answers_a_nested_vec_with_no_bound_and_can_fault() {
let cat = builtin_catalog();
let receiver = iterable_of_t();
for (name, symbol) in [
("chunks", abi::RuntimeSymbol::VecChunks),
("windows", abi::RuntimeSymbol::VecWindows),
] {
let row = cat
.by_receiver_and_name(&receiver, name)
.next()
.unwrap_or_else(|| panic!("Iterable.{name} exists"));
assert_eq!(
row.result,
vec_of_vec_of_t(),
"`{name}` groups without flattening"
);
assert_eq!(row.params, vec![TypePattern::Scalar(ScalarType::Int)]);
assert_eq!(row.purity, Purity::Pure);
assert!(
row.bounds().is_empty(),
"a grouping reads no descriptor callback ({name})"
);
assert!(
row.can_fault(),
"`{name}(0)` names no run, and the program has to be able to see that"
);
assert!(
matches!(row.lowering, MethodLowering::RuntimeSymbol(s) if s == symbol),
"a grouping is a barrier: it cannot answer its first group from \
one element ({name})"
);
}
}
#[test]
fn the_overflow_alternative_family_is_three_modes_over_three_operators() {
let cat = builtin_catalog();
let int = TypePattern::Scalar(ScalarType::Int);
for mode in ["wrapping", "saturating", "checked"] {
for op in ["add", "sub", "mul"] {
let name = format!("{mode}_{op}");
let row = cat
.by_receiver_and_name(&int, &name)
.next()
.unwrap_or_else(|| panic!("§4.12's family includes `Int.{name}`"));
assert_eq!(row.params, vec![TypePattern::Scalar(ScalarType::Int)]);
assert!(!row.can_fault(), "`{name}` is an alternative to faulting");
let want = if mode == "checked" {
TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int)))
} else {
TypePattern::Scalar(ScalarType::Int)
};
assert_eq!(row.result, want, "`{name}`'s result");
}
}
for absent in [
"wrapping_div",
"saturating_div",
"checked_div",
"wrapping_rem",
"checked_rem",
"wrapping_neg",
"checked_neg",
"saturating_abs",
] {
assert!(
cat.by_receiver_and_name(&int, absent).next().is_none(),
"§4.12 closes the family before `{absent}`: division's escape \
hatch is closed by \"Division by zero always faults\", and \
`_neg`/`_abs` are spelled with `0.wrapping_sub(x)`"
);
}
}
}