1use crate::abi;
11use crate::catalog::MethodCatalog;
12use crate::type_pattern::{CollectionCtor, ScalarType};
13use crate::{MethodEntry, MethodLowering, Purity, TypePattern};
14
15fn vec_of_t() -> TypePattern {
17 TypePattern::Collection {
18 ctor: CollectionCtor::Vec,
19 args: vec![TypePattern::var("T")],
20 }
21}
22
23#[must_use]
29pub fn builtin_catalog() -> MethodCatalog {
30 MethodCatalog::build()
31 .entry(vec_push())
32 .entry(vec_len())
33 .entry(vec_get())
34 .entry(vec_is_empty())
35 .entry(vec_to_text())
36 .entry(deque_push_front())
37 .entry(deque_push_back())
38 .entry(deque_pop_front())
39 .entry(deque_pop_back())
40 .entry(deque_len())
41 .entry(deque_get())
42 .entry(deque_is_empty())
43 .entry(map_insert())
44 .entry(map_get())
45 .entry(map_contains())
46 .entry(map_remove())
47 .entry(map_len())
48 .entry(map_is_empty())
49 .entry(set_insert())
50 .entry(set_remove())
51 .entry(set_contains())
52 .entry(set_len())
53 .entry(set_is_empty())
54 .entry(counter_get())
55 .entry(counter_inc())
56 .entry(counter_len())
57 .entry(counter_is_empty())
58 .entry(max_heap_push())
59 .entry(max_heap_pop())
60 .entry(max_heap_peek())
61 .entry(max_heap_len())
62 .entry(max_heap_is_empty())
63 .entry(min_heap_push())
64 .entry(min_heap_pop())
65 .entry(min_heap_peek())
66 .entry(min_heap_len())
67 .entry(min_heap_is_empty())
68 .entry(bitset_insert())
69 .entry(bitset_remove())
70 .entry(bitset_contains())
71 .entry(bitset_len())
72 .entry(bitset_is_empty())
73 .entry(grid_width())
74 .entry(grid_height())
75 .entry(grid_get())
76 .entry(grid_set())
77 .entry(grid_contains())
78 .entry(grid_neighbors4())
79 .entry(grid_neighbors8())
80 .entry(grid_around4())
81 .entry(grid_around8())
82 .entry(grid_count4())
83 .entry(grid_count8())
84 .entry(grid_count4_where())
85 .entry(grid_count8_where())
86 .entry(grid_positions())
87 .entry(grid_cells())
88 .entry(grid_row())
89 .entry(grid_column())
90 .entry(grid_find())
91 .entry(grid_find_all())
92 .entry(grid_transpose())
93 .entry(grid_rotate_left())
94 .entry(grid_rotate_right())
95 .entry(seq_map())
102 .entry(seq_filter())
103 .entry(seq_fold())
104 .entry(seq_sum())
105 .entry(seq_count())
106 .entry(seq_count_if())
107 .entry(seq_sorted())
110 .entry(seq_sorted_by_key())
111 .entry(seq_unique())
112 .entry(seq_reversed())
113 .entry(seq_frequencies())
114 .entry(seq_join())
115 .entry(seq_chunks())
118 .entry(seq_windows())
119 .entry(seq_take())
122 .entry(seq_skip())
123 .entry(seq_take_while())
124 .entry(seq_enumerate())
125 .entry(seq_zip())
126 .entry(seq_flat_map())
127 .entry(seq_filter_map())
128 .entry(seq_product())
129 .entry(seq_min())
130 .entry(seq_max())
131 .entry(seq_min_by())
132 .entry(seq_max_by())
133 .entry(seq_any())
134 .entry(seq_all())
135 .entry(seq_find())
136 .entry(seq_position())
137 .entry(seq_reduce())
138 .entry(seq_to_vec())
142 .entry(seq_to_set())
143 .entry(seq_to_map())
144 .entry(seq_to_counter())
145 .entry(seq_to_deque())
146 .entry(seq_to_min_heap())
147 .entry(seq_to_max_heap())
148 .entry(seq_to_bitset())
149 .entry(text_len())
150 .entry(text_int())
151 .entry(text_float())
152 .entry(text_is_empty())
153 .entry(text_get())
154 .entry(float_abs())
157 .entry(float_sqrt())
158 .entry(float_floor())
159 .entry(float_ceil())
160 .entry(float_round())
161 .entry(float_sign())
162 .entry(float_to_int())
163 .entry(float_to_text())
164 .entry(float_is_nan())
165 .entry(float_is_infinite())
166 .entry(float_min())
167 .entry(float_max())
168 .entry(int_to_float())
170 .entry(char_to_int())
173 .entry(int_to_char())
174 .entry(int_to_text())
177 .entry(char_to_text())
178 .entry(int_wrapping_add())
179 .entry(int_saturating_add())
180 .entry(int_checked_add())
181 .entry(int_wrapping_sub())
182 .entry(int_saturating_sub())
183 .entry(int_checked_sub())
184 .entry(int_wrapping_mul())
185 .entry(int_saturating_mul())
186 .entry(int_checked_mul())
187 .entry(vec_index())
191 .entry(vec_index_set())
192 .entry(deque_index())
193 .entry(deque_index_set())
194 .entry(text_index())
195 .entry(map_index())
196 .entry(map_index_set())
197 .entry(counter_index())
198 .entry(counter_index_set())
199 .entry(grid_index())
200 .entry(grid_index_set())
201 .entry(map_index_min())
217 .entry(map_index_max())
218 .entry(vec_index_min())
219 .entry(vec_index_max())
220 .entry(deque_index_min())
221 .entry(deque_index_max())
222 .entry(grid_index_min())
223 .entry(grid_index_max())
224 .entry(counter_keys())
226 .entry(counter_values())
227 .entry(map_keys())
228 .entry(map_values())
229 .finish()
230 .expect("built-in catalog must be duplicate-free")
231}
232
233fn counter_keys() -> MethodEntry {
241 MethodEntry {
242 receiver: counter_of_t(),
243 name: "keys",
244 params: vec![],
245 result: TypePattern::Collection {
246 ctor: CollectionCtor::Vec,
247 args: vec![TypePattern::var("T")],
248 },
249 purity: Purity::Pure,
250 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterKeys),
251 doc: "Every key, as a `Vec[T]`, ordered with `values()`.",
252 }
253}
254
255fn counter_values() -> MethodEntry {
256 MethodEntry {
257 receiver: counter_of_t(),
258 name: "values",
259 params: vec![],
260 result: TypePattern::Collection {
261 ctor: CollectionCtor::Vec,
262 args: vec![TypePattern::Scalar(ScalarType::Int)],
263 },
264 purity: Purity::Pure,
265 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterValues),
266 doc: "Every count, as a `Vec[Int]`, ordered with `keys()`.",
267 }
268}
269
270fn map_keys() -> MethodEntry {
271 MethodEntry {
272 receiver: map_of_k_v(),
273 name: "keys",
274 params: vec![],
275 result: TypePattern::Collection {
276 ctor: CollectionCtor::Vec,
277 args: vec![TypePattern::var("K")],
278 },
279 purity: Purity::Pure,
280 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapKeys),
281 doc: "Every key, as a `Vec[K]`, ordered with `values()`.",
282 }
283}
284
285fn map_values() -> MethodEntry {
286 MethodEntry {
287 receiver: map_of_k_v(),
288 name: "values",
289 params: vec![],
290 result: TypePattern::Collection {
291 ctor: CollectionCtor::Vec,
292 args: vec![TypePattern::var("V")],
293 },
294 purity: Purity::Pure,
295 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapValues),
296 doc: "Every value, as a `Vec[V]`, ordered with `keys()`.",
297 }
298}
299
300fn vec_index() -> MethodEntry {
322 MethodEntry {
323 receiver: vec_of_t(),
324 name: crate::catalog::INDEX_READ,
325 params: vec![TypePattern::Scalar(ScalarType::Int)],
326 result: TypePattern::var("T"),
327 purity: Purity::Pure,
328 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecGet),
329 doc: "`v[i]` — the element at `i`; faults if out of range.",
330 }
331}
332
333fn vec_index_set() -> MethodEntry {
334 MethodEntry {
335 receiver: vec_of_t(),
336 name: crate::catalog::INDEX_STORE,
337 params: vec![TypePattern::Scalar(ScalarType::Int), TypePattern::var("T")],
338 result: TypePattern::Unit,
339 purity: Purity::Impure,
340 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecSet),
341 doc: "`v[i] = value` — replace the element at `i`; faults if out of range \
342 (it never appends — `push` is the spelling that grows a vector).",
343 }
344}
345
346fn vec_of_ordered_t() -> TypePattern {
349 TypePattern::Collection {
350 ctor: CollectionCtor::Vec,
351 args: vec![TypePattern::of_kind("T", crate::CapKind::Ord)],
352 }
353}
354
355fn vec_index_min() -> MethodEntry {
368 MethodEntry {
369 receiver: vec_of_ordered_t(),
370 name: crate::catalog::INDEX_STORE_MIN,
371 params: vec![TypePattern::Scalar(ScalarType::Int), TypePattern::var("T")],
372 result: TypePattern::Unit,
373 purity: Purity::Impure,
374 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecUpdateMin),
375 doc: "`v[i] min= candidate` — keep the smaller of the element and the \
376 candidate; faults if `i` is out of range. The element type must be \
377 orderable.",
378 }
379}
380
381fn vec_index_max() -> MethodEntry {
382 MethodEntry {
383 receiver: vec_of_ordered_t(),
384 name: crate::catalog::INDEX_STORE_MAX,
385 params: vec![TypePattern::Scalar(ScalarType::Int), TypePattern::var("T")],
386 result: TypePattern::Unit,
387 purity: Purity::Impure,
388 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecUpdateMax),
389 doc: "`v[i] max= candidate` — keep the larger of the element and the \
390 candidate; faults if `i` is out of range. The element type must be \
391 orderable.",
392 }
393}
394
395fn deque_index() -> MethodEntry {
396 MethodEntry {
397 receiver: deque_of_t(),
398 name: crate::catalog::INDEX_READ,
399 params: vec![TypePattern::Scalar(ScalarType::Int)],
400 result: TypePattern::var("T"),
401 purity: Purity::Pure,
402 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeGet),
403 doc: "`d[i]` — the element at `i` (0-based from the front); faults if out of range.",
404 }
405}
406
407fn deque_index_set() -> MethodEntry {
408 MethodEntry {
409 receiver: deque_of_t(),
410 name: crate::catalog::INDEX_STORE,
411 params: vec![TypePattern::Scalar(ScalarType::Int), TypePattern::var("T")],
412 result: TypePattern::Unit,
413 purity: Purity::Impure,
414 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeSet),
415 doc: "`d[i] = value` — replace the element at `i` (0-based from the front); \
416 faults if out of range (it never inserts).",
417 }
418}
419
420fn deque_of_ordered_t() -> TypePattern {
422 TypePattern::Collection {
423 ctor: CollectionCtor::Deque,
424 args: vec![TypePattern::of_kind("T", crate::CapKind::Ord)],
425 }
426}
427
428fn deque_index_min() -> MethodEntry {
432 MethodEntry {
433 receiver: deque_of_ordered_t(),
434 name: crate::catalog::INDEX_STORE_MIN,
435 params: vec![TypePattern::Scalar(ScalarType::Int), TypePattern::var("T")],
436 result: TypePattern::Unit,
437 purity: Purity::Impure,
438 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeUpdateMin),
439 doc: "`d[i] min= candidate` — keep the smaller of the element (0-based \
440 from the front) and the candidate; faults if `i` is out of range. \
441 The element type must be orderable.",
442 }
443}
444
445fn deque_index_max() -> MethodEntry {
446 MethodEntry {
447 receiver: deque_of_ordered_t(),
448 name: crate::catalog::INDEX_STORE_MAX,
449 params: vec![TypePattern::Scalar(ScalarType::Int), TypePattern::var("T")],
450 result: TypePattern::Unit,
451 purity: Purity::Impure,
452 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeUpdateMax),
453 doc: "`d[i] max= candidate` — keep the larger of the element (0-based \
454 from the front) and the candidate; faults if `i` is out of range. \
455 The element type must be orderable.",
456 }
457}
458
459fn text_index() -> MethodEntry {
460 MethodEntry {
461 receiver: text_receiver(),
462 name: crate::catalog::INDEX_READ,
463 params: vec![TypePattern::Scalar(ScalarType::Int)],
464 result: TypePattern::Scalar(ScalarType::Char),
465 purity: Purity::Pure,
466 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextGet),
467 doc: "`t[i]` — the `Char` at `i`, indexing by Unicode scalar value and not \
468 by byte; faults if out of range (ADR-086).",
469 }
470}
471
472fn map_index() -> MethodEntry {
473 MethodEntry {
474 receiver: map_of_k_v(),
475 name: crate::catalog::INDEX_READ,
476 params: vec![TypePattern::var("K")],
477 result: TypePattern::var("V"),
478 purity: Purity::Pure,
479 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapIndex),
480 doc: "`m[key]` — the value for `key`; **faults** if absent (§4.7; `.get` is the \
481 spelling that answers with absence).",
482 }
483}
484
485fn map_index_set() -> MethodEntry {
486 MethodEntry {
487 receiver: map_of_k_v(),
488 name: crate::catalog::INDEX_STORE,
489 params: vec![TypePattern::var("K"), TypePattern::var("V")],
490 result: TypePattern::Unit,
491 purity: Purity::Impure,
492 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapInsert),
493 doc: "`m[key] = value` — set `key`, replacing any prior value.",
494 }
495}
496
497fn map_of_k_ordered_value() -> TypePattern {
516 TypePattern::Collection {
517 ctor: CollectionCtor::Map,
518 args: vec![
519 TypePattern::var("K"),
520 TypePattern::of_kind("V", crate::CapKind::Ord),
521 ],
522 }
523}
524
525fn map_index_min() -> MethodEntry {
526 MethodEntry {
527 receiver: map_of_k_ordered_value(),
528 name: crate::catalog::INDEX_STORE_MIN,
529 params: vec![TypePattern::var("K"), TypePattern::var("V")],
530 result: TypePattern::Unit,
531 purity: Purity::Impure,
532 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapUpdateMin),
533 doc: "`d[key] min= candidate` — keep the smaller value; an absent entry \
534 accepts the first value. The value type must be orderable.",
535 }
536}
537
538fn map_index_max() -> MethodEntry {
539 MethodEntry {
540 receiver: map_of_k_ordered_value(),
541 name: crate::catalog::INDEX_STORE_MAX,
542 params: vec![TypePattern::var("K"), TypePattern::var("V")],
543 result: TypePattern::Unit,
544 purity: Purity::Impure,
545 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapUpdateMax),
546 doc: "`b[key] max= score` — keep the larger value; an absent entry accepts \
547 the first value. The value type must be orderable.",
548 }
549}
550
551fn counter_index() -> MethodEntry {
552 MethodEntry {
553 receiver: counter_of_t(),
554 name: crate::catalog::INDEX_READ,
555 params: vec![TypePattern::var("T")],
556 result: TypePattern::Scalar(ScalarType::Int),
557 purity: Purity::Pure,
558 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterGet),
559 doc: "`c[key]` — the count for `key`, or zero if absent; never faults.",
560 }
561}
562
563fn counter_index_set() -> MethodEntry {
564 MethodEntry {
565 receiver: counter_of_t(),
566 name: crate::catalog::INDEX_STORE,
567 params: vec![TypePattern::var("T"), TypePattern::Scalar(ScalarType::Int)],
568 result: TypePattern::Unit,
569 purity: Purity::Impure,
570 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterSet),
571 doc: "`c[key] = n` — set the count for `key`.",
572 }
573}
574
575fn grid_index() -> MethodEntry {
576 MethodEntry {
577 receiver: grid_of_t(),
578 name: crate::catalog::INDEX_READ,
579 params: vec![
580 TypePattern::Scalar(ScalarType::Int),
581 TypePattern::Scalar(ScalarType::Int),
582 ],
583 result: TypePattern::var("T"),
584 purity: Purity::Pure,
585 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridGet),
586 doc: "`grid[x, y]` — the cell at (x, y); faults if out of range.",
587 }
588}
589
590fn grid_index_set() -> MethodEntry {
591 MethodEntry {
592 receiver: grid_of_t(),
593 name: crate::catalog::INDEX_STORE,
594 params: vec![
595 TypePattern::Scalar(ScalarType::Int),
596 TypePattern::Scalar(ScalarType::Int),
597 TypePattern::var("T"),
598 ],
599 result: TypePattern::Unit,
600 purity: Purity::Impure,
601 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridSet),
602 doc: "`grid[x, y] = value` — set the cell at (x, y); faults if out of range.",
603 }
604}
605
606fn grid_of_ordered_t() -> TypePattern {
608 TypePattern::Collection {
609 ctor: CollectionCtor::Grid,
610 args: vec![TypePattern::of_kind("T", crate::CapKind::Ord)],
611 }
612}
613
614fn grid_index_min() -> MethodEntry {
621 MethodEntry {
622 receiver: grid_of_ordered_t(),
623 name: crate::catalog::INDEX_STORE_MIN,
624 params: vec![
625 TypePattern::Scalar(ScalarType::Int),
626 TypePattern::Scalar(ScalarType::Int),
627 TypePattern::var("T"),
628 ],
629 result: TypePattern::Unit,
630 purity: Purity::Impure,
631 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridUpdateMin),
632 doc: "`grid[x, y] min= candidate` — keep the smaller of the cell and the \
633 candidate; faults if (x, y) is out of range. The cell type must be \
634 orderable.",
635 }
636}
637
638fn grid_index_max() -> MethodEntry {
639 MethodEntry {
640 receiver: grid_of_ordered_t(),
641 name: crate::catalog::INDEX_STORE_MAX,
642 params: vec![
643 TypePattern::Scalar(ScalarType::Int),
644 TypePattern::Scalar(ScalarType::Int),
645 TypePattern::var("T"),
646 ],
647 result: TypePattern::Unit,
648 purity: Purity::Impure,
649 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridUpdateMax),
650 doc: "`grid[x, y] max= candidate` — keep the larger of the cell and the \
651 candidate; faults if (x, y) is out of range. The cell type must be \
652 orderable.",
653 }
654}
655
656fn text_receiver() -> TypePattern {
659 TypePattern::Scalar(ScalarType::Text)
660}
661
662fn text_len() -> MethodEntry {
663 MethodEntry {
664 receiver: text_receiver(),
665 name: "len",
666 params: vec![],
667 result: TypePattern::Scalar(ScalarType::Int),
668 purity: Purity::Pure,
669 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextLen),
670 doc: "Number of Unicode scalar values (chars) in the text.",
671 }
672}
673
674fn text_int() -> MethodEntry {
685 MethodEntry {
686 receiver: text_receiver(),
687 name: "int",
688 params: vec![],
689 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
690 purity: Purity::Pure,
691 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextInt),
692 doc: "The Int this text spells as `Some(n)`, or `None` if it spells none.",
693 }
694}
695
696fn text_float() -> MethodEntry {
704 MethodEntry {
705 receiver: text_receiver(),
706 name: "float",
707 params: vec![],
708 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Float))),
709 purity: Purity::Pure,
710 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextFloat),
711 doc: "The Float this text spells as `Some(x)`, or `None` if it spells none.",
712 }
713}
714
715fn text_is_empty() -> MethodEntry {
716 MethodEntry {
717 receiver: text_receiver(),
718 name: "is_empty",
719 params: vec![],
720 result: TypePattern::Scalar(ScalarType::Bool),
721 purity: Purity::Pure,
722 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextIsEmpty),
723 doc: "True iff the text has no chars.",
724 }
725}
726
727fn text_get() -> MethodEntry {
728 MethodEntry {
729 receiver: text_receiver(),
730 name: "get",
731 params: vec![TypePattern::Scalar(ScalarType::Int)],
732 result: TypePattern::Scalar(ScalarType::Char),
733 purity: Purity::Pure,
734 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextGet),
735 doc: "The `Char` at `index`; faults if out of range. `t[index]` is the \
736 same row and the same answer (ADR-086).",
737 }
738}
739
740fn float_receiver() -> TypePattern {
748 TypePattern::Scalar(ScalarType::Float)
749}
750
751fn float_abs() -> MethodEntry {
752 MethodEntry {
753 receiver: float_receiver(),
754 name: "abs",
755 params: vec![],
756 result: TypePattern::Scalar(ScalarType::Float),
757 purity: Purity::Pure,
758 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatAbs),
759 doc: "Absolute value.",
760 }
761}
762
763fn float_sqrt() -> MethodEntry {
764 MethodEntry {
765 receiver: float_receiver(),
766 name: "sqrt",
767 params: vec![],
768 result: TypePattern::Scalar(ScalarType::Float),
769 purity: Purity::Pure,
770 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatSqrt),
771 doc: "Square root. Negative inputs yield NaN (IEEE-754).",
772 }
773}
774
775fn float_floor() -> MethodEntry {
776 MethodEntry {
777 receiver: float_receiver(),
778 name: "floor",
779 params: vec![],
780 result: TypePattern::Scalar(ScalarType::Float),
781 purity: Purity::Pure,
782 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatFloor),
783 doc: "Round toward negative infinity.",
784 }
785}
786
787fn float_ceil() -> MethodEntry {
788 MethodEntry {
789 receiver: float_receiver(),
790 name: "ceil",
791 params: vec![],
792 result: TypePattern::Scalar(ScalarType::Float),
793 purity: Purity::Pure,
794 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatCeil),
795 doc: "Round toward positive infinity.",
796 }
797}
798
799fn float_round() -> MethodEntry {
800 MethodEntry {
801 receiver: float_receiver(),
802 name: "round",
803 params: vec![],
804 result: TypePattern::Scalar(ScalarType::Float),
805 purity: Purity::Pure,
806 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatRound),
807 doc: "Round half away from zero.",
808 }
809}
810
811fn float_sign() -> MethodEntry {
812 MethodEntry {
813 receiver: float_receiver(),
814 name: "sign",
815 params: vec![],
816 result: TypePattern::Scalar(ScalarType::Float),
817 purity: Purity::Pure,
818 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatSign),
819 doc: "Sign as -1.0 / 0.0 / 1.0. NaN yields NaN.",
820 }
821}
822
823fn float_to_int() -> MethodEntry {
824 MethodEntry {
825 receiver: float_receiver(),
826 name: "to_int",
827 params: vec![],
828 result: TypePattern::Scalar(ScalarType::Int),
829 purity: Purity::Pure,
830 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatToInt),
831 doc: "Truncate toward zero to an Int. Faults on NaN, ±inf, or out of i64 range.",
832 }
833}
834
835fn float_to_text() -> MethodEntry {
836 MethodEntry {
837 receiver: float_receiver(),
838 name: "to_text",
839 params: vec![],
840 result: TypePattern::Scalar(ScalarType::Text),
841 purity: Purity::Pure,
842 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatToText),
843 doc: "Format as Text (shortest round-trip form; inf/-inf/NaN as literals).",
844 }
845}
846
847fn float_is_nan() -> MethodEntry {
848 MethodEntry {
849 receiver: float_receiver(),
850 name: "is_nan",
851 params: vec![],
852 result: TypePattern::Scalar(ScalarType::Bool),
853 purity: Purity::Pure,
854 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatIsNan),
855 doc: "True iff NaN.",
856 }
857}
858
859fn float_is_infinite() -> MethodEntry {
860 MethodEntry {
861 receiver: float_receiver(),
862 name: "is_infinite",
863 params: vec![],
864 result: TypePattern::Scalar(ScalarType::Bool),
865 purity: Purity::Pure,
866 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatIsInfinite),
867 doc: "True iff ±infinity.",
868 }
869}
870
871fn float_min() -> MethodEntry {
872 MethodEntry {
873 receiver: float_receiver(),
874 name: "min",
875 params: vec![TypePattern::Scalar(ScalarType::Float)],
876 result: TypePattern::Scalar(ScalarType::Float),
877 purity: Purity::Pure,
878 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatMin),
879 doc: "The smaller of two floats. If either is NaN, returns the other.",
880 }
881}
882
883fn float_max() -> MethodEntry {
884 MethodEntry {
885 receiver: float_receiver(),
886 name: "max",
887 params: vec![TypePattern::Scalar(ScalarType::Float)],
888 result: TypePattern::Scalar(ScalarType::Float),
889 purity: Purity::Pure,
890 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatMax),
891 doc: "The larger of two floats. If either is NaN, returns the other.",
892 }
893}
894
895fn int_to_float() -> MethodEntry {
896 MethodEntry {
897 receiver: TypePattern::Scalar(ScalarType::Int),
898 name: "to_float",
899 params: vec![],
900 result: TypePattern::Scalar(ScalarType::Float),
901 purity: Purity::Pure,
902 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntToFloat),
903 doc: "Widen to Float (explicit Int→Float conversion, §4.12).",
904 }
905}
906
907fn int_to_text() -> MethodEntry {
936 MethodEntry {
937 receiver: TypePattern::Scalar(ScalarType::Int),
938 name: "to_text",
939 params: vec![],
940 result: TypePattern::Scalar(ScalarType::Text),
941 purity: Purity::Pure,
942 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntToText),
943 doc: "Format as Text — the same digits `out` writes (ADR-143).",
944 }
945}
946
947fn char_to_int() -> MethodEntry {
972 MethodEntry {
973 receiver: TypePattern::Scalar(ScalarType::Char),
974 name: "to_int",
975 params: vec![],
976 result: TypePattern::Scalar(ScalarType::Int),
977 purity: Purity::Pure,
978 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CharToInt),
979 doc: "The Unicode scalar value, as an `Int`. Never faults (ADR-086).",
980 }
981}
982
983fn char_to_text() -> MethodEntry {
984 MethodEntry {
985 receiver: TypePattern::Scalar(ScalarType::Char),
986 name: "to_text",
987 params: vec![],
988 result: TypePattern::Scalar(ScalarType::Text),
989 purity: Purity::Pure,
990 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CharToText),
991 doc: "The one-character Text holding this scalar — the same character \
992 `out` writes. Never faults (ADR-143).",
993 }
994}
995
996fn int_to_char() -> MethodEntry {
997 MethodEntry {
998 receiver: TypePattern::Scalar(ScalarType::Int),
999 name: "to_char",
1000 params: vec![],
1001 result: TypePattern::Scalar(ScalarType::Char),
1002 purity: Purity::Pure,
1003 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntToChar),
1004 doc: "The `Char` with this Unicode scalar value; **faults** \
1005 (`InvalidChar`) if it is negative, above `0x10FFFF`, or a \
1006 surrogate. The narrowing half of the pair, as `Float.to_int` is \
1007 (ADR-086).",
1008 }
1009}
1010
1011fn int_wrapping_add() -> MethodEntry {
1020 MethodEntry {
1021 receiver: TypePattern::Scalar(ScalarType::Int),
1022 name: "wrapping_add",
1023 params: vec![TypePattern::Scalar(ScalarType::Int)],
1024 result: TypePattern::Scalar(ScalarType::Int),
1025 purity: Purity::Pure,
1026 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntWrappingAdd),
1027 doc: "Add with two's-complement wraparound instead of a fault.",
1028 }
1029}
1030
1031fn int_saturating_add() -> MethodEntry {
1032 MethodEntry {
1033 receiver: TypePattern::Scalar(ScalarType::Int),
1034 name: "saturating_add",
1035 params: vec![TypePattern::Scalar(ScalarType::Int)],
1036 result: TypePattern::Scalar(ScalarType::Int),
1037 purity: Purity::Pure,
1038 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntSaturatingAdd),
1039 doc: "Add, clamping to Int's ends instead of faulting.",
1040 }
1041}
1042
1043fn int_checked_add() -> MethodEntry {
1044 MethodEntry {
1045 receiver: TypePattern::Scalar(ScalarType::Int),
1046 name: "checked_add",
1047 params: vec![TypePattern::Scalar(ScalarType::Int)],
1048 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
1049 purity: Purity::Pure,
1050 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntCheckedAdd),
1051 doc: "Add, answering None where the checked `+` would fault.",
1052 }
1053}
1054
1055fn int_wrapping_sub() -> MethodEntry {
1056 MethodEntry {
1057 receiver: TypePattern::Scalar(ScalarType::Int),
1058 name: "wrapping_sub",
1059 params: vec![TypePattern::Scalar(ScalarType::Int)],
1060 result: TypePattern::Scalar(ScalarType::Int),
1061 purity: Purity::Pure,
1062 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntWrappingSub),
1063 doc: "Subtract with two's-complement wraparound instead of a fault.",
1064 }
1065}
1066
1067fn int_saturating_sub() -> MethodEntry {
1068 MethodEntry {
1069 receiver: TypePattern::Scalar(ScalarType::Int),
1070 name: "saturating_sub",
1071 params: vec![TypePattern::Scalar(ScalarType::Int)],
1072 result: TypePattern::Scalar(ScalarType::Int),
1073 purity: Purity::Pure,
1074 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntSaturatingSub),
1075 doc: "Subtract, clamping to Int's ends instead of faulting.",
1076 }
1077}
1078
1079fn int_checked_sub() -> MethodEntry {
1080 MethodEntry {
1081 receiver: TypePattern::Scalar(ScalarType::Int),
1082 name: "checked_sub",
1083 params: vec![TypePattern::Scalar(ScalarType::Int)],
1084 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
1085 purity: Purity::Pure,
1086 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntCheckedSub),
1087 doc: "Subtract, answering None where the checked `-` would fault.",
1088 }
1089}
1090
1091fn int_wrapping_mul() -> MethodEntry {
1092 MethodEntry {
1093 receiver: TypePattern::Scalar(ScalarType::Int),
1094 name: "wrapping_mul",
1095 params: vec![TypePattern::Scalar(ScalarType::Int)],
1096 result: TypePattern::Scalar(ScalarType::Int),
1097 purity: Purity::Pure,
1098 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntWrappingMul),
1099 doc: "Multiply with two's-complement wraparound instead of a fault. The \
1100 one row here a program could not write for itself: every arithmetic \
1101 operator is checked and the language has no bitwise operators.",
1102 }
1103}
1104
1105fn int_saturating_mul() -> MethodEntry {
1106 MethodEntry {
1107 receiver: TypePattern::Scalar(ScalarType::Int),
1108 name: "saturating_mul",
1109 params: vec![TypePattern::Scalar(ScalarType::Int)],
1110 result: TypePattern::Scalar(ScalarType::Int),
1111 purity: Purity::Pure,
1112 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntSaturatingMul),
1113 doc: "Multiply, clamping to Int's ends instead of faulting.",
1114 }
1115}
1116
1117fn int_checked_mul() -> MethodEntry {
1118 MethodEntry {
1119 receiver: TypePattern::Scalar(ScalarType::Int),
1120 name: "checked_mul",
1121 params: vec![TypePattern::Scalar(ScalarType::Int)],
1122 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
1123 purity: Purity::Pure,
1124 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntCheckedMul),
1125 doc: "Multiply, answering None where the checked `*` would fault.",
1126 }
1127}
1128
1129fn vec_push() -> MethodEntry {
1130 MethodEntry {
1131 receiver: vec_of_t(),
1132 name: "push",
1133 params: vec![TypePattern::var("T")],
1134 result: TypePattern::Unit,
1135 purity: Purity::Impure,
1136 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecPush),
1137 doc: "Append a value to the end; returns Unit.",
1138 }
1139}
1140
1141fn vec_len() -> MethodEntry {
1142 MethodEntry {
1143 receiver: vec_of_t(),
1144 name: "len",
1145 params: vec![],
1146 result: TypePattern::Scalar(ScalarType::Int),
1147 purity: Purity::Pure,
1148 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecLen),
1149 doc: "Number of elements in the vector.",
1150 }
1151}
1152
1153fn vec_get() -> MethodEntry {
1154 MethodEntry {
1155 receiver: vec_of_t(),
1156 name: "get",
1157 params: vec![TypePattern::Scalar(ScalarType::Int)],
1158 result: TypePattern::var("T"),
1159 purity: Purity::Pure,
1160 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecGet),
1161 doc: "The element at `index`; faults `IndexOutOfBounds` if out of range.",
1162 }
1163}
1164
1165fn vec_is_empty() -> MethodEntry {
1166 MethodEntry {
1167 receiver: vec_of_t(),
1168 name: "is_empty",
1169 params: vec![],
1170 result: TypePattern::Scalar(ScalarType::Bool),
1171 purity: Purity::Pure,
1172 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecIsEmpty),
1173 doc: "True iff the vector has no elements.",
1174 }
1175}
1176
1177fn vec_of_char() -> TypePattern {
1185 TypePattern::Collection {
1186 ctor: CollectionCtor::Vec,
1187 args: vec![TypePattern::is_scalar("T", ScalarType::Char)],
1188 }
1189}
1190
1191fn vec_to_text() -> MethodEntry {
1204 MethodEntry {
1205 receiver: vec_of_char(),
1206 name: "to_text",
1207 params: vec![],
1208 result: TypePattern::Scalar(ScalarType::Text),
1209 purity: Purity::Pure,
1210 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecToText),
1211 doc: "These Chars as one Text, with nothing between them (ADR-144).",
1212 }
1213}
1214
1215fn deque_of_t() -> TypePattern {
1219 TypePattern::Collection {
1220 ctor: CollectionCtor::Deque,
1221 args: vec![TypePattern::var("T")],
1222 }
1223}
1224
1225fn deque_push_front() -> MethodEntry {
1226 MethodEntry {
1227 receiver: deque_of_t(),
1228 name: "push_front",
1229 params: vec![TypePattern::var("T")],
1230 result: TypePattern::Unit,
1231 purity: Purity::Impure,
1232 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePushFront),
1233 doc: "Prepend a value to the front; returns Unit.",
1234 }
1235}
1236
1237fn deque_push_back() -> MethodEntry {
1238 MethodEntry {
1239 receiver: deque_of_t(),
1240 name: "push_back",
1241 params: vec![TypePattern::var("T")],
1242 result: TypePattern::Unit,
1243 purity: Purity::Impure,
1244 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePushBack),
1245 doc: "Append a value to the back; returns Unit.",
1246 }
1247}
1248
1249fn deque_pop_front() -> MethodEntry {
1250 MethodEntry {
1251 receiver: deque_of_t(),
1252 name: "pop_front",
1253 params: vec![],
1254 result: TypePattern::var("T"),
1255 purity: Purity::Impure,
1256 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePopFront),
1257 doc: "Remove and return the front element; faults if empty.",
1258 }
1259}
1260
1261fn deque_pop_back() -> MethodEntry {
1262 MethodEntry {
1263 receiver: deque_of_t(),
1264 name: "pop_back",
1265 params: vec![],
1266 result: TypePattern::var("T"),
1267 purity: Purity::Impure,
1268 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePopBack),
1269 doc: "Remove and return the back element; faults if empty.",
1270 }
1271}
1272
1273fn deque_len() -> MethodEntry {
1274 MethodEntry {
1275 receiver: deque_of_t(),
1276 name: "len",
1277 params: vec![],
1278 result: TypePattern::Scalar(ScalarType::Int),
1279 purity: Purity::Pure,
1280 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeLen),
1281 doc: "Number of elements in the deque.",
1282 }
1283}
1284
1285fn deque_get() -> MethodEntry {
1286 MethodEntry {
1287 receiver: deque_of_t(),
1288 name: "get",
1289 params: vec![TypePattern::Scalar(ScalarType::Int)],
1290 result: TypePattern::var("T"),
1291 purity: Purity::Pure,
1292 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeGet),
1293 doc: "The element at `index` (0-based from the front); faults if out of range.",
1294 }
1295}
1296
1297fn deque_is_empty() -> MethodEntry {
1298 MethodEntry {
1299 receiver: deque_of_t(),
1300 name: "is_empty",
1301 params: vec![],
1302 result: TypePattern::Scalar(ScalarType::Bool),
1303 purity: Purity::Pure,
1304 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeIsEmpty),
1305 doc: "True iff the deque has no elements.",
1306 }
1307}
1308
1309fn map_of_k_v() -> TypePattern {
1313 TypePattern::Collection {
1314 ctor: CollectionCtor::Map,
1315 args: vec![TypePattern::var("K"), TypePattern::var("V")],
1316 }
1317}
1318
1319fn set_of_t() -> TypePattern {
1321 TypePattern::Collection {
1322 ctor: CollectionCtor::Set,
1323 args: vec![TypePattern::var("T")],
1324 }
1325}
1326
1327fn counter_of_t() -> TypePattern {
1329 TypePattern::Collection {
1330 ctor: CollectionCtor::Counter,
1331 args: vec![TypePattern::var("T")],
1332 }
1333}
1334
1335fn map_insert() -> MethodEntry {
1336 MethodEntry {
1337 receiver: map_of_k_v(),
1338 name: "insert",
1339 params: vec![TypePattern::var("K"), TypePattern::var("V")],
1340 result: TypePattern::Unit,
1341 purity: Purity::Impure,
1342 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapInsert),
1343 doc: "Set `key` to `value`, replacing any prior value; returns Unit.",
1344 }
1345}
1346
1347fn map_get() -> MethodEntry {
1348 MethodEntry {
1349 receiver: map_of_k_v(),
1350 name: "get",
1351 params: vec![TypePattern::var("K")],
1352 result: TypePattern::Option(Box::new(TypePattern::var("V"))),
1356 purity: Purity::Pure,
1357 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapGet),
1358 doc: "The value for `key` as `Some(value)`, or `None` if absent.",
1359 }
1360}
1361
1362fn map_contains() -> MethodEntry {
1363 MethodEntry {
1364 receiver: map_of_k_v(),
1365 name: "contains",
1366 params: vec![TypePattern::var("K")],
1367 result: TypePattern::Scalar(ScalarType::Bool),
1368 purity: Purity::Pure,
1369 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapContains),
1370 doc: "True iff `key` is present in the map.",
1371 }
1372}
1373
1374fn map_remove() -> MethodEntry {
1375 MethodEntry {
1376 receiver: map_of_k_v(),
1377 name: "remove",
1378 params: vec![TypePattern::var("K")],
1379 result: TypePattern::Unit,
1380 purity: Purity::Impure,
1381 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapRemove),
1382 doc: "Remove `key` if present; returns Unit.",
1383 }
1384}
1385
1386fn map_len() -> MethodEntry {
1387 MethodEntry {
1388 receiver: map_of_k_v(),
1389 name: "len",
1390 params: vec![],
1391 result: TypePattern::Scalar(ScalarType::Int),
1392 purity: Purity::Pure,
1393 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapLen),
1394 doc: "Number of entries in the map.",
1395 }
1396}
1397
1398fn map_is_empty() -> MethodEntry {
1399 MethodEntry {
1400 receiver: map_of_k_v(),
1401 name: "is_empty",
1402 params: vec![],
1403 result: TypePattern::Scalar(ScalarType::Bool),
1404 purity: Purity::Pure,
1405 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapIsEmpty),
1406 doc: "True iff the map has no entries.",
1407 }
1408}
1409
1410fn set_insert() -> MethodEntry {
1411 MethodEntry {
1412 receiver: set_of_t(),
1413 name: "insert",
1414 params: vec![TypePattern::var("T")],
1415 result: TypePattern::Unit,
1416 purity: Purity::Impure,
1417 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetInsert),
1418 doc: "Add `value` to the set; returns Unit.",
1419 }
1420}
1421
1422fn set_remove() -> MethodEntry {
1423 MethodEntry {
1424 receiver: set_of_t(),
1425 name: "remove",
1426 params: vec![TypePattern::var("T")],
1427 result: TypePattern::Unit,
1428 purity: Purity::Impure,
1429 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetRemove),
1430 doc: "Remove `value` if present; returns Unit.",
1431 }
1432}
1433
1434fn set_contains() -> MethodEntry {
1435 MethodEntry {
1436 receiver: set_of_t(),
1437 name: "contains",
1438 params: vec![TypePattern::var("T")],
1439 result: TypePattern::Scalar(ScalarType::Bool),
1440 purity: Purity::Pure,
1441 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetContains),
1442 doc: "True iff `value` is in the set.",
1443 }
1444}
1445
1446fn set_len() -> MethodEntry {
1447 MethodEntry {
1448 receiver: set_of_t(),
1449 name: "len",
1450 params: vec![],
1451 result: TypePattern::Scalar(ScalarType::Int),
1452 purity: Purity::Pure,
1453 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetLen),
1454 doc: "Number of elements in the set.",
1455 }
1456}
1457
1458fn set_is_empty() -> MethodEntry {
1459 MethodEntry {
1460 receiver: set_of_t(),
1461 name: "is_empty",
1462 params: vec![],
1463 result: TypePattern::Scalar(ScalarType::Bool),
1464 purity: Purity::Pure,
1465 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetIsEmpty),
1466 doc: "True iff the set has no elements.",
1467 }
1468}
1469
1470fn counter_get() -> MethodEntry {
1471 MethodEntry {
1472 receiver: counter_of_t(),
1473 name: "get",
1474 params: vec![TypePattern::var("T")],
1475 result: TypePattern::Scalar(ScalarType::Int),
1476 purity: Purity::Pure,
1477 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterGet),
1478 doc: "The count for `key`, or zero if absent (never faults).",
1479 }
1480}
1481
1482fn counter_inc() -> MethodEntry {
1483 MethodEntry {
1484 receiver: counter_of_t(),
1485 name: "inc",
1486 params: vec![TypePattern::var("T")],
1487 result: TypePattern::Unit,
1488 purity: Purity::Impure,
1489 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterInc),
1490 doc: "Increment the count for `key` by one; returns Unit.",
1491 }
1492}
1493
1494fn counter_len() -> MethodEntry {
1495 MethodEntry {
1496 receiver: counter_of_t(),
1497 name: "len",
1498 params: vec![],
1499 result: TypePattern::Scalar(ScalarType::Int),
1500 purity: Purity::Pure,
1501 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterLen),
1502 doc: "Number of distinct keys in the counter.",
1503 }
1504}
1505
1506fn counter_is_empty() -> MethodEntry {
1507 MethodEntry {
1508 receiver: counter_of_t(),
1509 name: "is_empty",
1510 params: vec![],
1511 result: TypePattern::Scalar(ScalarType::Bool),
1512 purity: Purity::Pure,
1513 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterIsEmpty),
1514 doc: "True iff the counter has no keys.",
1515 }
1516}
1517
1518fn min_heap_of_t() -> TypePattern {
1521 TypePattern::Collection {
1522 ctor: CollectionCtor::MinHeap,
1523 args: vec![TypePattern::var("T")],
1524 }
1525}
1526
1527fn max_heap_of_t() -> TypePattern {
1528 TypePattern::Collection {
1529 ctor: CollectionCtor::MaxHeap,
1530 args: vec![TypePattern::var("T")],
1531 }
1532}
1533
1534fn max_heap_push() -> MethodEntry {
1535 MethodEntry {
1536 receiver: max_heap_of_t(),
1537 name: "push",
1538 params: vec![TypePattern::var("T")],
1539 result: TypePattern::Unit,
1540 purity: Purity::Impure,
1541 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapPush),
1542 doc: "Push a value onto the max-heap; returns Unit.",
1543 }
1544}
1545
1546fn max_heap_pop() -> MethodEntry {
1547 MethodEntry {
1548 receiver: max_heap_of_t(),
1549 name: "pop",
1550 params: vec![],
1551 result: TypePattern::var("T"),
1552 purity: Purity::Impure,
1553 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapPop),
1554 doc: "Remove and return the largest element; faults if empty.",
1555 }
1556}
1557
1558fn max_heap_peek() -> MethodEntry {
1559 MethodEntry {
1560 receiver: max_heap_of_t(),
1561 name: "peek",
1562 params: vec![],
1563 result: TypePattern::var("T"),
1564 purity: Purity::Pure,
1565 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapPeek),
1566 doc: "The largest element without removing it; faults if empty.",
1567 }
1568}
1569
1570fn max_heap_len() -> MethodEntry {
1571 MethodEntry {
1572 receiver: max_heap_of_t(),
1573 name: "len",
1574 params: vec![],
1575 result: TypePattern::Scalar(ScalarType::Int),
1576 purity: Purity::Pure,
1577 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapLen),
1578 doc: "Number of elements in the max-heap.",
1579 }
1580}
1581
1582fn max_heap_is_empty() -> MethodEntry {
1583 MethodEntry {
1584 receiver: max_heap_of_t(),
1585 name: "is_empty",
1586 params: vec![],
1587 result: TypePattern::Scalar(ScalarType::Bool),
1588 purity: Purity::Pure,
1589 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapIsEmpty),
1590 doc: "True iff the max-heap has no elements.",
1591 }
1592}
1593
1594fn min_heap_push() -> MethodEntry {
1595 MethodEntry {
1596 receiver: min_heap_of_t(),
1597 name: "push",
1598 params: vec![TypePattern::var("T")],
1599 result: TypePattern::Unit,
1600 purity: Purity::Impure,
1601 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapPush),
1602 doc: "Push a value onto the min-heap; returns Unit.",
1603 }
1604}
1605
1606fn min_heap_pop() -> MethodEntry {
1607 MethodEntry {
1608 receiver: min_heap_of_t(),
1609 name: "pop",
1610 params: vec![],
1611 result: TypePattern::var("T"),
1612 purity: Purity::Impure,
1613 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapPop),
1614 doc: "Remove and return the smallest element; faults if empty.",
1615 }
1616}
1617
1618fn min_heap_peek() -> MethodEntry {
1619 MethodEntry {
1620 receiver: min_heap_of_t(),
1621 name: "peek",
1622 params: vec![],
1623 result: TypePattern::var("T"),
1624 purity: Purity::Pure,
1625 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapPeek),
1626 doc: "The smallest element without removing it; faults if empty.",
1627 }
1628}
1629
1630fn min_heap_len() -> MethodEntry {
1631 MethodEntry {
1632 receiver: min_heap_of_t(),
1633 name: "len",
1634 params: vec![],
1635 result: TypePattern::Scalar(ScalarType::Int),
1636 purity: Purity::Pure,
1637 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapLen),
1638 doc: "Number of elements in the min-heap.",
1639 }
1640}
1641
1642fn min_heap_is_empty() -> MethodEntry {
1643 MethodEntry {
1644 receiver: min_heap_of_t(),
1645 name: "is_empty",
1646 params: vec![],
1647 result: TypePattern::Scalar(ScalarType::Bool),
1648 purity: Purity::Pure,
1649 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapIsEmpty),
1650 doc: "True iff the min-heap has no elements.",
1651 }
1652}
1653
1654fn bitset_receiver() -> TypePattern {
1658 TypePattern::Collection {
1659 ctor: CollectionCtor::BitSet,
1660 args: vec![],
1661 }
1662}
1663
1664fn bitset_insert() -> MethodEntry {
1665 MethodEntry {
1666 receiver: bitset_receiver(),
1667 name: "insert",
1668 params: vec![TypePattern::Scalar(ScalarType::Int)],
1669 result: TypePattern::Unit,
1670 purity: Purity::Impure,
1671 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetInsert),
1672 doc: "Set the bit for a non-negative integer; returns Unit.",
1673 }
1674}
1675
1676fn bitset_remove() -> MethodEntry {
1677 MethodEntry {
1678 receiver: bitset_receiver(),
1679 name: "remove",
1680 params: vec![TypePattern::Scalar(ScalarType::Int)],
1681 result: TypePattern::Unit,
1682 purity: Purity::Impure,
1683 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetRemove),
1684 doc: "Clear the bit for an integer; returns Unit.",
1685 }
1686}
1687
1688fn bitset_contains() -> MethodEntry {
1689 MethodEntry {
1690 receiver: bitset_receiver(),
1691 name: "contains",
1692 params: vec![TypePattern::Scalar(ScalarType::Int)],
1693 result: TypePattern::Scalar(ScalarType::Bool),
1694 purity: Purity::Pure,
1695 lowering: MethodLowering::ScalarPrimitive(abi::RuntimeSymbol::BitsetContains),
1699 doc: "True iff the bit for the integer is set.",
1700 }
1701}
1702
1703fn bitset_len() -> MethodEntry {
1704 MethodEntry {
1705 receiver: bitset_receiver(),
1706 name: "len",
1707 params: vec![],
1708 result: TypePattern::Scalar(ScalarType::Int),
1709 purity: Purity::Pure,
1710 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetLen),
1711 doc: "Number of set bits (popcount).",
1712 }
1713}
1714
1715fn bitset_is_empty() -> MethodEntry {
1716 MethodEntry {
1717 receiver: bitset_receiver(),
1718 name: "is_empty",
1719 params: vec![],
1720 result: TypePattern::Scalar(ScalarType::Bool),
1721 purity: Purity::Pure,
1722 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetIsEmpty),
1723 doc: "True iff no bits are set.",
1724 }
1725}
1726
1727fn grid_of_t() -> TypePattern {
1731 TypePattern::Collection {
1732 ctor: CollectionCtor::Grid,
1733 args: vec![TypePattern::var("T")],
1734 }
1735}
1736
1737fn point_pattern() -> TypePattern {
1739 TypePattern::Tuple(vec![
1740 TypePattern::Scalar(ScalarType::Int),
1741 TypePattern::Scalar(ScalarType::Int),
1742 ])
1743}
1744
1745fn grid_width() -> MethodEntry {
1746 MethodEntry {
1747 receiver: grid_of_t(),
1748 name: "width",
1749 params: vec![],
1750 result: TypePattern::Scalar(ScalarType::Int),
1751 purity: Purity::Pure,
1752 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridWidth),
1753 doc: "The number of columns.",
1754 }
1755}
1756
1757fn grid_height() -> MethodEntry {
1758 MethodEntry {
1759 receiver: grid_of_t(),
1760 name: "height",
1761 params: vec![],
1762 result: TypePattern::Scalar(ScalarType::Int),
1763 purity: Purity::Pure,
1764 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridHeight),
1765 doc: "The number of rows.",
1766 }
1767}
1768
1769fn grid_get() -> MethodEntry {
1770 MethodEntry {
1771 receiver: grid_of_t(),
1772 name: "get",
1773 params: vec![
1774 TypePattern::Scalar(ScalarType::Int),
1775 TypePattern::Scalar(ScalarType::Int),
1776 ],
1777 result: TypePattern::var("T"),
1778 purity: Purity::Pure,
1779 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridGet),
1780 doc: "The cell at (x, y); faults if out of range.",
1781 }
1782}
1783
1784fn grid_set() -> MethodEntry {
1785 MethodEntry {
1786 receiver: grid_of_t(),
1787 name: "set",
1788 params: vec![
1789 TypePattern::Scalar(ScalarType::Int),
1790 TypePattern::Scalar(ScalarType::Int),
1791 TypePattern::var("T"),
1792 ],
1793 result: TypePattern::Unit,
1794 purity: Purity::Impure,
1795 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridSet),
1796 doc: "Set the cell at (x, y); faults if out of range.",
1797 }
1798}
1799
1800fn grid_contains() -> MethodEntry {
1801 MethodEntry {
1802 receiver: grid_of_t(),
1803 name: "contains",
1804 params: vec![
1805 TypePattern::Scalar(ScalarType::Int),
1806 TypePattern::Scalar(ScalarType::Int),
1807 ],
1808 result: TypePattern::Scalar(ScalarType::Bool),
1809 purity: Purity::Pure,
1810 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridContains),
1811 doc: "True iff (x, y) is within the grid.",
1812 }
1813}
1814
1815fn grid_neighbors4() -> MethodEntry {
1816 MethodEntry {
1817 receiver: grid_of_t(),
1818 name: "neighbors4",
1819 params: vec![point_pattern()],
1820 result: TypePattern::Collection {
1821 ctor: CollectionCtor::Vec,
1822 args: vec![point_pattern()],
1823 },
1824 purity: Purity::Pure,
1825 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridNeighbors4),
1826 doc: "The 4 orthogonal in-bounds neighbors of a point, as a Vec of (x, y).",
1827 }
1828}
1829
1830fn grid_neighbors8() -> MethodEntry {
1831 MethodEntry {
1832 receiver: grid_of_t(),
1833 name: "neighbors8",
1834 params: vec![point_pattern()],
1835 result: TypePattern::Collection {
1836 ctor: CollectionCtor::Vec,
1837 args: vec![point_pattern()],
1838 },
1839 purity: Purity::Pure,
1840 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridNeighbors8),
1841 doc: "The 8 in-bounds neighbors of a point, as a Vec of (x, y).",
1842 }
1843}
1844
1845fn maybe_point() -> TypePattern {
1849 TypePattern::Option(Box::new(point_pattern()))
1850}
1851
1852fn around4_pattern() -> TypePattern {
1863 TypePattern::Record {
1864 name: "Around4",
1865 fields: vec![
1866 ("up", maybe_point()),
1867 ("left", maybe_point()),
1868 ("right", maybe_point()),
1869 ("down", maybe_point()),
1870 ],
1871 }
1872}
1873
1874fn around8_pattern() -> TypePattern {
1877 TypePattern::Record {
1878 name: "Around8",
1879 fields: vec![
1880 ("up_left", maybe_point()),
1881 ("up", maybe_point()),
1882 ("up_right", maybe_point()),
1883 ("left", maybe_point()),
1884 ("right", maybe_point()),
1885 ("down_left", maybe_point()),
1886 ("down", maybe_point()),
1887 ("down_right", maybe_point()),
1888 ],
1889 }
1890}
1891
1892fn grid_around4() -> MethodEntry {
1902 MethodEntry {
1903 receiver: grid_of_t(),
1904 name: "around4",
1905 params: vec![point_pattern()],
1906 result: around4_pattern(),
1907 purity: Purity::Pure,
1908 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridAround4),
1909 doc: "The 4 orthogonal neighbors by name: { up, left, right, down }, \
1910 each Some((x, y)) or None off the grid.",
1911 }
1912}
1913
1914fn grid_around8() -> MethodEntry {
1916 MethodEntry {
1917 receiver: grid_of_t(),
1918 name: "around8",
1919 params: vec![point_pattern()],
1920 result: around8_pattern(),
1921 purity: Purity::Pure,
1922 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridAround8),
1923 doc: "The 8 neighbors by name, in reading order: { up_left, up, up_right, \
1924 left, right, down_left, down, down_right }, each Some((x, y)) or None.",
1925 }
1926}
1927
1928fn grid_count4() -> MethodEntry {
1935 MethodEntry {
1936 receiver: grid_of_t(),
1937 name: "count4",
1938 params: vec![point_pattern(), TypePattern::var("T")],
1939 result: TypePattern::Scalar(ScalarType::Int),
1940 purity: Purity::Pure,
1941 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCount4),
1942 doc: "How many of the 4 orthogonal neighbors hold `value`. \
1943 A neighbor off the grid has no cell and is not counted.",
1944 }
1945}
1946
1947fn grid_count8() -> MethodEntry {
1950 MethodEntry {
1951 receiver: grid_of_t(),
1952 name: "count8",
1953 params: vec![point_pattern(), TypePattern::var("T")],
1954 result: TypePattern::Scalar(ScalarType::Int),
1955 purity: Purity::Pure,
1956 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCount8),
1957 doc: "How many of the 8 neighbors hold `value`. \
1958 A neighbor off the grid has no cell and is not counted.",
1959 }
1960}
1961
1962fn grid_count4_where() -> MethodEntry {
1969 MethodEntry {
1970 receiver: grid_of_t(),
1971 name: "count4_where",
1972 params: vec![
1973 point_pattern(),
1974 TypePattern::Function {
1975 params: vec![TypePattern::var("T")],
1976 result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
1977 },
1978 ],
1979 result: TypePattern::Scalar(ScalarType::Int),
1980 purity: Purity::Pure,
1981 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCount4Where),
1982 doc: "How many of the 4 orthogonal neighbors hold a cell the closure accepts. \
1983 A neighbor off the grid has no cell, so the closure never sees one.",
1984 }
1985}
1986
1987fn grid_count8_where() -> MethodEntry {
1990 MethodEntry {
1991 receiver: grid_of_t(),
1992 name: "count8_where",
1993 params: vec![
1994 point_pattern(),
1995 TypePattern::Function {
1996 params: vec![TypePattern::var("T")],
1997 result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
1998 },
1999 ],
2000 result: TypePattern::Scalar(ScalarType::Int),
2001 purity: Purity::Pure,
2002 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCount8Where),
2003 doc: "How many of the 8 neighbors hold a cell the closure accepts. \
2004 A neighbor off the grid has no cell, so the closure never sees one.",
2005 }
2006}
2007
2008fn grid_positions() -> MethodEntry {
2009 MethodEntry {
2010 receiver: grid_of_t(),
2011 name: "positions",
2012 params: vec![],
2013 result: TypePattern::Collection {
2014 ctor: CollectionCtor::Vec,
2015 args: vec![point_pattern()],
2016 },
2017 purity: Purity::Pure,
2018 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridPositions),
2019 doc: "All (x, y) positions in row-major order, as a Vec.",
2020 }
2021}
2022
2023fn grid_cells() -> MethodEntry {
2024 MethodEntry {
2025 receiver: grid_of_t(),
2026 name: "cells",
2027 params: vec![],
2028 result: TypePattern::Collection {
2029 ctor: CollectionCtor::Vec,
2030 args: vec![TypePattern::var("T")],
2031 },
2032 purity: Purity::Pure,
2033 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCells),
2034 doc: "All cells in row-major order, as a Vec.",
2035 }
2036}
2037
2038fn grid_row() -> MethodEntry {
2039 MethodEntry {
2040 receiver: grid_of_t(),
2041 name: "row",
2042 params: vec![TypePattern::Scalar(ScalarType::Int)],
2043 result: TypePattern::Collection {
2044 ctor: CollectionCtor::Vec,
2045 args: vec![TypePattern::var("T")],
2046 },
2047 purity: Purity::Pure,
2048 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridRow),
2049 doc: "Row `y` as a Vec; faults if out of range.",
2050 }
2051}
2052
2053fn grid_column() -> MethodEntry {
2054 MethodEntry {
2055 receiver: grid_of_t(),
2056 name: "column",
2057 params: vec![TypePattern::Scalar(ScalarType::Int)],
2058 result: TypePattern::Collection {
2059 ctor: CollectionCtor::Vec,
2060 args: vec![TypePattern::var("T")],
2061 },
2062 purity: Purity::Pure,
2063 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridColumn),
2064 doc: "Column `x` as a Vec; faults if out of range.",
2065 }
2066}
2067
2068fn grid_find() -> MethodEntry {
2069 MethodEntry {
2070 receiver: grid_of_t(),
2071 name: "find",
2072 params: vec![TypePattern::var("T")],
2073 result: TypePattern::Option(Box::new(point_pattern())),
2077 purity: Purity::Pure,
2078 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridFind),
2079 doc: "The first (x, y) whose cell equals `value` as `Some((x, y))`, or `None`.",
2080 }
2081}
2082
2083fn grid_find_all() -> MethodEntry {
2084 MethodEntry {
2085 receiver: grid_of_t(),
2086 name: "find_all",
2087 params: vec![TypePattern::var("T")],
2088 result: TypePattern::Collection {
2089 ctor: CollectionCtor::Vec,
2090 args: vec![point_pattern()],
2091 },
2092 purity: Purity::Pure,
2093 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridFindAll),
2094 doc: "All (x, y) positions whose cell equals `value`, as a Vec.",
2095 }
2096}
2097
2098fn grid_transpose() -> MethodEntry {
2099 MethodEntry {
2100 receiver: grid_of_t(),
2101 name: "transpose",
2102 params: vec![],
2103 result: grid_of_t(),
2104 purity: Purity::Pure,
2105 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridTranspose),
2106 doc: "A transposed copy (rows ↔ columns).",
2107 }
2108}
2109
2110fn grid_rotate_left() -> MethodEntry {
2111 MethodEntry {
2112 receiver: grid_of_t(),
2113 name: "rotate_left",
2114 params: vec![],
2115 result: grid_of_t(),
2116 purity: Purity::Pure,
2117 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridRotateLeft),
2118 doc: "A copy rotated 90° counter-clockwise.",
2119 }
2120}
2121
2122fn grid_rotate_right() -> MethodEntry {
2123 MethodEntry {
2124 receiver: grid_of_t(),
2125 name: "rotate_right",
2126 params: vec![],
2127 result: grid_of_t(),
2128 purity: Purity::Pure,
2129 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridRotateRight),
2130 doc: "A copy rotated 90° clockwise.",
2131 }
2132}
2133
2134fn iterable_of_t() -> TypePattern {
2154 TypePattern::iterable(TypePattern::var("T"))
2155}
2156
2157fn t_to_u() -> TypePattern {
2159 TypePattern::Function {
2160 params: vec![TypePattern::var("T")],
2161 result: Box::new(TypePattern::var("U")),
2162 }
2163}
2164
2165fn t_to_bool() -> TypePattern {
2167 TypePattern::Function {
2168 params: vec![TypePattern::var("T")],
2169 result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
2170 }
2171}
2172
2173fn t_to_option_u() -> TypePattern {
2180 TypePattern::Function {
2181 params: vec![TypePattern::var("T")],
2182 result: Box::new(TypePattern::Option(Box::new(TypePattern::var("U")))),
2183 }
2184}
2185
2186fn acc_t_to_acc() -> TypePattern {
2188 TypePattern::Function {
2189 params: vec![TypePattern::var("Acc"), TypePattern::var("T")],
2190 result: Box::new(TypePattern::var("Acc")),
2191 }
2192}
2193
2194fn t_t_to_t() -> TypePattern {
2203 TypePattern::Function {
2204 params: vec![TypePattern::var("T"), TypePattern::var("T")],
2205 result: Box::new(TypePattern::var("T")),
2206 }
2207}
2208
2209fn seq_map() -> MethodEntry {
2210 MethodEntry {
2211 receiver: iterable_of_t(),
2212 name: "map",
2213 params: vec![t_to_u()],
2214 result: vec_of_u(),
2215 purity: Purity::Pure,
2216 lowering: MethodLowering::Intrinsic("seq_map"),
2217 doc: "Apply a function to each element, collecting into a Vec.",
2218 }
2219}
2220
2221fn vec_of_u() -> TypePattern {
2224 TypePattern::Collection {
2225 ctor: CollectionCtor::Vec,
2226 args: vec![TypePattern::var("U")],
2227 }
2228}
2229
2230fn seq_filter() -> MethodEntry {
2231 MethodEntry {
2232 receiver: iterable_of_t(),
2233 name: "filter",
2234 params: vec![t_to_bool()],
2235 result: vec_of_t(),
2236 purity: Purity::Pure,
2237 lowering: MethodLowering::Intrinsic("seq_filter"),
2238 doc: "Keep elements satisfying a predicate, collecting into a Vec.",
2239 }
2240}
2241
2242fn seq_fold() -> MethodEntry {
2243 MethodEntry {
2244 receiver: iterable_of_t(),
2245 name: "fold",
2246 params: vec![TypePattern::var("Acc"), acc_t_to_acc()],
2247 result: TypePattern::var("Acc"),
2248 purity: Purity::Pure,
2249 lowering: MethodLowering::Intrinsic("seq_fold"),
2250 doc: "Reduce elements left-to-right with an accumulator and combining closure.",
2251 }
2252}
2253
2254fn seq_sum() -> MethodEntry {
2255 MethodEntry {
2256 receiver: iterable_of_int_elem(),
2257 name: "sum",
2258 params: vec![],
2259 result: TypePattern::Scalar(ScalarType::Int),
2260 purity: Purity::Pure,
2261 lowering: MethodLowering::Intrinsic("seq_sum"),
2262 doc: "Sum the (Int) elements.",
2263 }
2264}
2265
2266fn seq_count() -> MethodEntry {
2267 MethodEntry {
2268 receiver: iterable_of_t(),
2269 name: "count",
2270 params: vec![],
2271 result: TypePattern::Scalar(ScalarType::Int),
2272 purity: Purity::Pure,
2273 lowering: MethodLowering::Intrinsic("seq_count"),
2274 doc: "Number of elements.",
2275 }
2276}
2277
2278fn seq_count_if() -> MethodEntry {
2284 MethodEntry {
2285 receiver: iterable_of_t(),
2286 name: "count",
2287 params: vec![t_to_bool()],
2288 result: TypePattern::Scalar(ScalarType::Int),
2289 purity: Purity::Pure,
2290 lowering: MethodLowering::Intrinsic("seq_count"),
2291 doc: "Number of elements satisfying the predicate.",
2292 }
2293}
2294
2295fn seq_sorted() -> MethodEntry {
2363 MethodEntry {
2364 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::Ord)),
2365 name: "sorted",
2366 params: vec![],
2367 result: vec_of_t(),
2368 purity: Purity::Pure,
2369 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecSorted),
2370 doc: "A new Vec holding these elements in ascending order.",
2371 }
2372}
2373
2374fn seq_unique() -> MethodEntry {
2381 MethodEntry {
2382 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::HashStable)),
2383 name: "unique",
2384 params: vec![],
2385 result: vec_of_t(),
2386 purity: Purity::Pure,
2387 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecUnique),
2388 doc: "A new Vec with duplicate elements removed, keeping first occurrences.",
2389 }
2390}
2391
2392fn seq_reversed() -> MethodEntry {
2404 MethodEntry {
2405 receiver: iterable_of_t(),
2406 name: "reversed",
2407 params: vec![],
2408 result: vec_of_t(),
2409 purity: Purity::Pure,
2410 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecReversed),
2411 doc: "A new Vec holding these elements in reverse order.",
2412 }
2413}
2414
2415fn vec_of_vec_of_t() -> TypePattern {
2422 TypePattern::Collection {
2423 ctor: CollectionCtor::Vec,
2424 args: vec![vec_of_t()],
2425 }
2426}
2427
2428fn seq_chunks() -> MethodEntry {
2448 MethodEntry {
2449 receiver: iterable_of_t(),
2450 name: "chunks",
2451 params: vec![TypePattern::Scalar(ScalarType::Int)],
2452 result: vec_of_vec_of_t(),
2453 purity: Purity::Pure,
2454 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecChunks),
2455 doc: "Consecutive non-overlapping runs of n; the last may be shorter. \
2456 Faults if n is not positive.",
2457 }
2458}
2459
2460fn seq_windows() -> MethodEntry {
2474 MethodEntry {
2475 receiver: iterable_of_t(),
2476 name: "windows",
2477 params: vec![TypePattern::Scalar(ScalarType::Int)],
2478 result: vec_of_vec_of_t(),
2479 purity: Purity::Pure,
2480 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecWindows),
2481 doc: "Every consecutive run of exactly n, sliding by one. Empty if n \
2482 exceeds the length; faults if n is not positive.",
2483 }
2484}
2485
2486fn iterable_of_text_elem() -> TypePattern {
2491 TypePattern::iterable(TypePattern::is_scalar("T", ScalarType::Text))
2492}
2493
2494fn seq_join() -> MethodEntry {
2507 MethodEntry {
2508 receiver: iterable_of_text_elem(),
2509 name: "join",
2510 params: vec![TypePattern::Scalar(ScalarType::Text)],
2511 result: TypePattern::Scalar(ScalarType::Text),
2512 purity: Purity::Pure,
2513 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecJoin),
2514 doc: "These Text items concatenated with `sep` between them.",
2515 }
2516}
2517
2518fn seq_frequencies() -> MethodEntry {
2527 MethodEntry {
2528 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::HashStable)),
2529 name: "frequencies",
2530 params: vec![],
2531 result: counter_of_t(),
2532 purity: Purity::Pure,
2533 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecFrequencies),
2534 doc: "A Counter holding how many times each element occurs.",
2535 }
2536}
2537
2538fn t_t_to_bool() -> TypePattern {
2545 TypePattern::Function {
2546 params: vec![TypePattern::var("T"), TypePattern::var("T")],
2547 result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
2548 }
2549}
2550
2551fn t_to_vec_u() -> TypePattern {
2553 TypePattern::Function {
2554 params: vec![TypePattern::var("T")],
2555 result: Box::new(vec_of_u()),
2556 }
2557}
2558
2559fn seq_take() -> MethodEntry {
2562 MethodEntry {
2563 receiver: iterable_of_t(),
2564 name: "take",
2565 params: vec![TypePattern::Scalar(ScalarType::Int)],
2566 result: vec_of_t(),
2567 purity: Purity::Pure,
2568 lowering: MethodLowering::Intrinsic("seq_take"),
2569 doc: "Keep at most the first n elements.",
2570 }
2571}
2572
2573fn seq_skip() -> MethodEntry {
2574 MethodEntry {
2575 receiver: iterable_of_t(),
2576 name: "skip",
2577 params: vec![TypePattern::Scalar(ScalarType::Int)],
2578 result: vec_of_t(),
2579 purity: Purity::Pure,
2580 lowering: MethodLowering::Intrinsic("seq_skip"),
2581 doc: "Drop the first n elements.",
2582 }
2583}
2584
2585fn seq_take_while() -> MethodEntry {
2586 MethodEntry {
2587 receiver: iterable_of_t(),
2588 name: "take_while",
2589 params: vec![t_to_bool()],
2590 result: vec_of_t(),
2591 purity: Purity::Pure,
2592 lowering: MethodLowering::Intrinsic("seq_take_while"),
2593 doc: "Keep elements until the predicate is false.",
2594 }
2595}
2596
2597fn vec_of_index_and_t() -> TypePattern {
2599 TypePattern::Collection {
2600 ctor: CollectionCtor::Vec,
2601 args: vec![TypePattern::Tuple(vec![
2602 TypePattern::Scalar(ScalarType::Int),
2603 TypePattern::var("T"),
2604 ])],
2605 }
2606}
2607
2608fn vec_of_t_and_u() -> TypePattern {
2611 TypePattern::Collection {
2612 ctor: CollectionCtor::Vec,
2613 args: vec![TypePattern::Tuple(vec![
2614 TypePattern::var("T"),
2615 TypePattern::var("U"),
2616 ])],
2617 }
2618}
2619
2620fn seq_enumerate() -> MethodEntry {
2621 MethodEntry {
2622 receiver: iterable_of_t(),
2623 name: "enumerate",
2624 params: vec![],
2625 result: vec_of_index_and_t(),
2626 purity: Purity::Pure,
2627 lowering: MethodLowering::Intrinsic("seq_enumerate"),
2628 doc: "Pair each element with its index.",
2629 }
2630}
2631
2632fn seq_zip() -> MethodEntry {
2633 MethodEntry {
2634 receiver: iterable_of_t(),
2635 name: "zip",
2636 params: vec![vec_of_u()],
2637 result: vec_of_t_and_u(),
2638 purity: Purity::Pure,
2639 lowering: MethodLowering::Intrinsic("seq_zip"),
2640 doc: "Pair elements with another sequence, stopping at the shorter length.",
2641 }
2642}
2643
2644fn seq_flat_map() -> MethodEntry {
2645 MethodEntry {
2646 receiver: iterable_of_t(),
2647 name: "flat_map",
2648 params: vec![t_to_vec_u()],
2649 result: vec_of_u(),
2650 purity: Purity::Pure,
2651 lowering: MethodLowering::Intrinsic("seq_flat_map"),
2652 doc: "Map each element to a Vec and concatenate the results.",
2653 }
2654}
2655
2656fn seq_filter_map() -> MethodEntry {
2657 MethodEntry {
2658 receiver: iterable_of_t(),
2659 name: "filter_map",
2660 params: vec![t_to_option_u()],
2661 result: vec_of_u(),
2662 purity: Purity::Pure,
2663 lowering: MethodLowering::Intrinsic("seq_filter_map"),
2664 doc: "Map each element to an Option and keep the Some payloads.",
2665 }
2666}
2667
2668fn iterable_of_int_elem() -> TypePattern {
2688 TypePattern::iterable(TypePattern::is_scalar("T", ScalarType::Int))
2689}
2690
2691fn seq_product() -> MethodEntry {
2692 MethodEntry {
2693 receiver: iterable_of_int_elem(),
2694 name: "product",
2695 params: vec![],
2696 result: TypePattern::Scalar(ScalarType::Int),
2697 purity: Purity::Pure,
2698 lowering: MethodLowering::Intrinsic("seq_product"),
2699 doc: "Multiply the (Int) elements.",
2700 }
2701}
2702
2703fn seq_min() -> MethodEntry {
2704 MethodEntry {
2705 receiver: iterable_of_int_elem(),
2706 name: "min",
2707 params: vec![],
2708 result: TypePattern::Scalar(ScalarType::Int),
2709 purity: Purity::Pure,
2710 lowering: MethodLowering::Intrinsic("seq_min"),
2711 doc: "Smallest (Int) element. Faults on an empty sequence (D1).",
2712 }
2713}
2714
2715fn seq_max() -> MethodEntry {
2716 MethodEntry {
2717 receiver: iterable_of_int_elem(),
2718 name: "max",
2719 params: vec![],
2720 result: TypePattern::Scalar(ScalarType::Int),
2721 purity: Purity::Pure,
2722 lowering: MethodLowering::Intrinsic("seq_max"),
2723 doc: "Largest (Int) element. Faults on an empty sequence (D1).",
2724 }
2725}
2726
2727fn seq_min_by() -> MethodEntry {
2728 MethodEntry {
2729 receiver: iterable_of_t(),
2730 name: "min_by",
2731 params: vec![t_t_to_bool()],
2732 result: TypePattern::var("T"),
2733 purity: Purity::Pure,
2734 lowering: MethodLowering::Intrinsic("seq_min_by"),
2735 doc: "Smallest element per a (T,T)->Bool \"less-than\" comparator.",
2736 }
2737}
2738
2739fn seq_max_by() -> MethodEntry {
2740 MethodEntry {
2741 receiver: iterable_of_t(),
2742 name: "max_by",
2743 params: vec![t_t_to_bool()],
2744 result: TypePattern::var("T"),
2745 purity: Purity::Pure,
2746 lowering: MethodLowering::Intrinsic("seq_max_by"),
2747 doc: "Largest element per a (T,T)->Bool \"less-than\" comparator.",
2748 }
2749}
2750
2751fn seq_any() -> MethodEntry {
2752 MethodEntry {
2753 receiver: iterable_of_t(),
2754 name: "any",
2755 params: vec![t_to_bool()],
2756 result: TypePattern::Scalar(ScalarType::Bool),
2757 purity: Purity::Pure,
2758 lowering: MethodLowering::Intrinsic("seq_any"),
2759 doc: "True if any element satisfies the predicate (short-circuits).",
2760 }
2761}
2762
2763fn seq_all() -> MethodEntry {
2764 MethodEntry {
2765 receiver: iterable_of_t(),
2766 name: "all",
2767 params: vec![t_to_bool()],
2768 result: TypePattern::Scalar(ScalarType::Bool),
2769 purity: Purity::Pure,
2770 lowering: MethodLowering::Intrinsic("seq_all"),
2771 doc: "True if all elements satisfy the predicate (short-circuits).",
2772 }
2773}
2774
2775fn seq_find() -> MethodEntry {
2776 MethodEntry {
2777 receiver: iterable_of_t(),
2778 name: "find",
2779 params: vec![t_to_bool()],
2780 result: TypePattern::Option(Box::new(TypePattern::var("T"))),
2781 purity: Purity::Pure,
2782 lowering: MethodLowering::Intrinsic("seq_find"),
2783 doc: "The first matching element, or None.",
2784 }
2785}
2786
2787fn seq_position() -> MethodEntry {
2788 MethodEntry {
2789 receiver: iterable_of_t(),
2790 name: "position",
2791 params: vec![t_to_bool()],
2792 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
2793 purity: Purity::Pure,
2794 lowering: MethodLowering::Intrinsic("seq_position"),
2795 doc: "The index of the first matching element, or None.",
2796 }
2797}
2798
2799fn seq_reduce() -> MethodEntry {
2800 MethodEntry {
2801 receiver: iterable_of_t(),
2802 name: "reduce",
2803 params: vec![t_t_to_t()],
2804 result: TypePattern::var("T"),
2805 purity: Purity::Pure,
2806 lowering: MethodLowering::Intrinsic("seq_reduce"),
2807 doc: "Reduce left-to-right, seeded with the first element.",
2808 }
2809}
2810
2811fn seq_sorted_by_key() -> MethodEntry {
2827 MethodEntry {
2828 receiver: iterable_of_t(),
2829 name: "sorted_by_key",
2830 params: vec![TypePattern::Function {
2831 params: vec![TypePattern::var("T")],
2832 result: Box::new(TypePattern::of_kind("K", crate::CapKind::Ord)),
2833 }],
2834 result: vec_of_t(),
2835 purity: Purity::Pure,
2836 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecSortedByKey),
2837 doc: "A new Vec ordered by the key the closure extracts.",
2838 }
2839}
2840
2841fn seq_to_vec() -> MethodEntry {
2878 MethodEntry {
2879 receiver: iterable_of_t(),
2880 name: "to_vec",
2881 params: vec![],
2882 result: vec_of_t(),
2883 purity: Purity::Pure,
2884 lowering: MethodLowering::Intrinsic("seq_to_vec"),
2885 doc: "The items as a Vec. On a Vec receiver this is the receiver itself.",
2886 }
2887}
2888
2889fn seq_to_set() -> MethodEntry {
2901 MethodEntry {
2902 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::HashStable)),
2903 name: "to_set",
2904 params: vec![],
2905 result: set_of_t(),
2906 purity: Purity::Pure,
2907 lowering: MethodLowering::Intrinsic("seq_to_set"),
2908 doc: "A Set holding these items, duplicates dropped.",
2909 }
2910}
2911
2912fn seq_to_map() -> MethodEntry {
2921 MethodEntry {
2922 receiver: TypePattern::iterable(TypePattern::Tuple(vec![
2923 TypePattern::of_kind("K", crate::CapKind::HashStable),
2924 TypePattern::var("V"),
2925 ])),
2926 name: "to_map",
2927 params: vec![],
2928 result: map_of_k_v(),
2929 purity: Purity::Pure,
2930 lowering: MethodLowering::Intrinsic("seq_to_map"),
2931 doc: "A Map built from (key, value) pairs. Duplicate keys: last wins.",
2932 }
2933}
2934
2935fn seq_to_counter() -> MethodEntry {
2943 MethodEntry {
2944 receiver: TypePattern::iterable(TypePattern::Tuple(vec![
2945 TypePattern::of_kind("T", crate::CapKind::HashStable),
2946 TypePattern::Scalar(ScalarType::Int),
2947 ])),
2948 name: "to_counter",
2949 params: vec![],
2950 result: counter_of_t(),
2951 purity: Purity::Pure,
2952 lowering: MethodLowering::Intrinsic("seq_to_counter"),
2953 doc: "A Counter built from (key, count) pairs. Duplicate keys: last wins.",
2954 }
2955}
2956
2957fn seq_to_deque() -> MethodEntry {
2958 MethodEntry {
2959 receiver: iterable_of_t(),
2960 name: "to_deque",
2961 params: vec![],
2962 result: deque_of_t(),
2963 purity: Purity::Pure,
2964 lowering: MethodLowering::Intrinsic("seq_to_deque"),
2965 doc: "A Deque holding these items, in order.",
2966 }
2967}
2968
2969fn seq_to_min_heap() -> MethodEntry {
2974 MethodEntry {
2975 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::Ord)),
2976 name: "to_min_heap",
2977 params: vec![],
2978 result: min_heap_of_t(),
2979 purity: Purity::Pure,
2980 lowering: MethodLowering::Intrinsic("seq_to_min_heap"),
2981 doc: "A MinHeap holding these items.",
2982 }
2983}
2984
2985fn seq_to_max_heap() -> MethodEntry {
2986 MethodEntry {
2987 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::Ord)),
2988 name: "to_max_heap",
2989 params: vec![],
2990 result: max_heap_of_t(),
2991 purity: Purity::Pure,
2992 lowering: MethodLowering::Intrinsic("seq_to_max_heap"),
2993 doc: "A MaxHeap holding these items.",
2994 }
2995}
2996
2997fn seq_to_bitset() -> MethodEntry {
3003 MethodEntry {
3004 receiver: iterable_of_int_elem(),
3005 name: "to_bitset",
3006 params: vec![],
3007 result: bitset_receiver(),
3008 purity: Purity::Pure,
3009 lowering: MethodLowering::Intrinsic("seq_to_bitset"),
3010 doc: "A BitSet holding these (Int) items. Faults on a negative or oversized member.",
3011 }
3012}
3013
3014#[cfg(test)]
3015mod tests {
3016 use super::*;
3017
3018 #[test]
3027 fn every_catalog_row_documents_itself() {
3028 for e in builtin_catalog().entries() {
3029 let what = format!("{}.{}/{}", e.receiver, e.name, e.arity());
3030 assert!(e.doc.len() > 8, "{what} has no real documentation");
3031 assert!(
3032 e.doc.ends_with('.') || e.doc.ends_with(')'),
3033 "{what}'s doc is not a sentence: {:?}",
3034 e.doc
3035 );
3036 assert!(
3040 e.doc
3041 .chars()
3042 .next()
3043 .is_some_and(|c| c.is_uppercase() || c == '`'),
3044 "{what}'s doc does not open a sentence: {:?}",
3045 e.doc
3046 );
3047 }
3048 }
3049
3050 #[test]
3051 fn builtin_catalog_has_vec_methods() {
3052 let cat = builtin_catalog();
3053 assert!(cat.len() >= 4);
3054 let vec_pat = vec_of_t();
3055 let push_hits: Vec<_> = cat.by_receiver_and_name(&vec_pat, "push").collect();
3056 assert_eq!(push_hits.len(), 1);
3057 assert_eq!(
3058 push_hits[0].lowering,
3059 MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecPush)
3060 );
3061 }
3062
3063 #[test]
3064 fn builtin_catalog_get_can_fault() {
3065 let cat = builtin_catalog();
3066 let vec_pat = vec_of_t();
3067 let get = cat
3068 .by_receiver_and_name(&vec_pat, "get")
3069 .next()
3070 .expect("vec.get exists");
3071 assert!(get.can_fault());
3072 let bitset_pat = bitset_receiver();
3076 let insert = cat
3077 .by_receiver_and_name(&bitset_pat, "insert")
3078 .next()
3079 .expect("bitset.insert exists");
3080 assert!(insert.can_fault());
3081 }
3082
3083 #[test]
3090 fn a_keyed_collection_enumerates_and_count_has_two_arities() {
3091 let cat = builtin_catalog();
3092 let map_pat = map_of_k_v();
3093 let counter_pat = counter_of_t();
3094
3095 for (pat, what) in [(map_pat.clone(), "Map"), (counter_pat.clone(), "Counter")] {
3097 for name in ["keys", "values"] {
3098 let hits: Vec<_> = cat.by_receiver_and_name(&pat, name).collect();
3099 assert_eq!(hits.len(), 1, "{what}.{name}()");
3100 assert_eq!(hits[0].arity(), 0, "{what}.{name}() takes no arguments");
3101 assert!(
3102 matches!(
3103 hits[0].result,
3104 TypePattern::Collection {
3105 ctor: CollectionCtor::Vec,
3106 ..
3107 }
3108 ),
3109 "{what}.{name}() answers a Vec so every §6.3 combinator applies"
3110 );
3111 }
3112 }
3113
3114 let counter_values = cat
3118 .by_receiver_and_name(&counter_pat, "values")
3119 .next()
3120 .expect("Counter.values");
3121 assert_eq!(
3122 counter_values.result,
3123 TypePattern::Collection {
3124 ctor: CollectionCtor::Vec,
3125 args: vec![TypePattern::Scalar(ScalarType::Int)]
3126 }
3127 );
3128 let map_values = cat
3129 .by_receiver_and_name(&map_pat, "values")
3130 .next()
3131 .expect("Map.values");
3132 assert_eq!(
3133 map_values.result,
3134 TypePattern::Collection {
3135 ctor: CollectionCtor::Vec,
3136 args: vec![TypePattern::var("V")]
3137 }
3138 );
3139 let map_keys = cat
3141 .by_receiver_and_name(&map_pat, "keys")
3142 .next()
3143 .expect("Map.keys");
3144 assert_eq!(
3145 map_keys.result,
3146 TypePattern::Collection {
3147 ctor: CollectionCtor::Vec,
3148 args: vec![TypePattern::var("K")]
3149 }
3150 );
3151
3152 let arities: Vec<usize> = cat
3155 .by_receiver_and_name(&iterable_of_t(), "count")
3156 .map(|e| e.arity())
3157 .collect();
3158 assert_eq!(arities.len(), 2, "count has two rows");
3159 assert!(arities.contains(&0) && arities.contains(&1));
3160 }
3161
3162 #[test]
3169 fn every_pipeline_combinator_is_one_row_on_the_generic_receiver() {
3170 let cat = builtin_catalog();
3171 let combinators = [
3179 "map",
3180 "filter",
3181 "filter_map",
3182 "flat_map",
3183 "take",
3184 "skip",
3185 "take_while",
3186 "enumerate",
3187 "zip",
3188 "fold",
3189 "reduce",
3190 "sum",
3191 "product",
3192 "count",
3193 "any",
3194 "all",
3195 "find",
3196 "position",
3197 "min",
3198 "max",
3199 "min_by",
3200 "max_by",
3201 "sorted",
3202 "sorted_by_key",
3203 "unique",
3204 "reversed",
3205 "frequencies",
3206 "join",
3207 "to_vec",
3208 "to_set",
3209 "to_map",
3210 "to_counter",
3211 "to_deque",
3212 "to_min_heap",
3213 "to_max_heap",
3214 "to_bitset",
3215 ];
3216 for name in combinators {
3217 let rows: Vec<_> = cat.entries().iter().filter(|e| e.name == name).collect();
3218 assert!(!rows.is_empty(), "`{name}` has no row at all");
3219 let mut generic: Vec<usize> = rows
3223 .iter()
3224 .filter(|e| matches!(e.receiver, TypePattern::Iterable { .. }))
3225 .map(|e| e.arity())
3226 .collect();
3227 assert!(!generic.is_empty(), "`{name}` has no generic row");
3228 let seen = generic.len();
3229 generic.sort_unstable();
3230 generic.dedup();
3231 assert_eq!(
3232 generic.len(),
3233 seen,
3234 "`{name}` has two generic rows at one arity — one receiver \
3235 getting a feature ten should have"
3236 );
3237 for row in rows {
3244 assert!(
3245 matches!(row.receiver, TypePattern::Iterable { .. })
3246 || !crate::is_pipeline_receiver(&row.receiver),
3247 "`{name}` also has a row on {}, which the generic row \
3248 accepts — both would match, and which one a call resolves \
3249 to would be insertion order",
3250 row.receiver
3251 );
3252 }
3253 }
3254
3255 assert!(
3260 !crate::PIPELINE_RECEIVERS.contains(&CollectionCtor::Grid),
3261 "a generic `map` would claim §6.4's name and answer a `Vec`"
3262 );
3263
3264 for e in cat.entries() {
3266 assert!(
3267 !matches!(
3268 e.receiver,
3269 TypePattern::Collection {
3270 ctor: CollectionCtor::Seq,
3271 ..
3272 }
3273 ),
3274 "`{}` is still on a `Seq`, which has no values",
3275 e.name
3276 );
3277 }
3278 }
3279
3280 #[test]
3289 fn a_conversion_exists_for_every_collection_that_can_be_constructed() {
3290 let cat = builtin_catalog();
3291 for (name, ctor) in [
3292 ("to_vec", CollectionCtor::Vec),
3293 ("to_set", CollectionCtor::Set),
3294 ("to_map", CollectionCtor::Map),
3295 ("to_counter", CollectionCtor::Counter),
3296 ("to_deque", CollectionCtor::Deque),
3297 ("to_min_heap", CollectionCtor::MinHeap),
3298 ("to_max_heap", CollectionCtor::MaxHeap),
3299 ("to_bitset", CollectionCtor::BitSet),
3300 ] {
3301 let row = cat
3302 .entries()
3303 .iter()
3304 .find(|e| e.name == name)
3305 .unwrap_or_else(|| panic!("`{name}` has no row"));
3306 assert_eq!(row.arity(), 0, "`{name}` takes no arguments");
3307 let built = match &row.result {
3308 TypePattern::Collection { ctor, .. } => *ctor,
3309 other => panic!("`{name}` answers {other}, not a collection"),
3310 };
3311 assert_eq!(built, ctor, "`{name}` builds the collection it names");
3312 }
3313 assert!(
3314 cat.entries().iter().all(|e| e.name != "to_grid"),
3315 "a grid needs a width, and an item sequence does not carry one"
3316 );
3317
3318 for name in ["to_map", "to_counter"] {
3322 let row = cat.entries().iter().find(|e| e.name == name).unwrap();
3323 let TypePattern::Iterable { item } = &row.receiver else {
3324 panic!("`{name}` is not on the generic receiver")
3325 };
3326 assert!(
3327 matches!(**item, TypePattern::Tuple(ref els) if els.len() == 2),
3328 "`{name}` accepts a `Map` or a `Counter` by saying its item is a pair"
3329 );
3330 }
3331 }
3332
3333 #[test]
3342 fn the_subscript_rows_are_a_closed_set_no_program_can_name() {
3343 let cat = builtin_catalog();
3344 let of = |ctor: CollectionCtor, args: usize| TypePattern::Collection {
3345 ctor,
3346 args: (0..args).map(|_| TypePattern::var("T")).collect(),
3347 };
3348 let map_pat = map_of_k_v();
3349
3350 for (pat, indices, what) in [
3352 (of(CollectionCtor::Vec, 1), 1, "Vec"),
3353 (of(CollectionCtor::Deque, 1), 1, "Deque"),
3354 (map_pat.clone(), 1, "Map"),
3355 (of(CollectionCtor::Counter, 1), 1, "Counter"),
3356 (of(CollectionCtor::Grid, 1), 2, "Grid"),
3357 (text_receiver(), 1, "Text"),
3358 ] {
3359 let hits: Vec<_> = cat
3360 .by_receiver_and_name(&pat, crate::catalog::INDEX_READ)
3361 .collect();
3362 assert_eq!(hits.len(), 1, "{what} reads through exactly one row");
3363 assert_eq!(hits[0].arity(), indices, "{what} indexes at {indices}");
3364 }
3365
3366 for (pat, args, what) in [
3370 (of(CollectionCtor::Vec, 1), 2, "Vec"),
3371 (of(CollectionCtor::Deque, 1), 2, "Deque"),
3372 (map_pat.clone(), 2, "Map"),
3373 (of(CollectionCtor::Counter, 1), 2, "Counter"),
3374 (of(CollectionCtor::Grid, 1), 3, "Grid"),
3375 ] {
3376 let hits: Vec<_> = cat
3377 .by_receiver_and_name(&pat, crate::catalog::INDEX_STORE)
3378 .collect();
3379 assert_eq!(hits.len(), 1, "{what} stores through exactly one row");
3380 assert_eq!(
3381 hits[0].arity(),
3382 args,
3383 "{what}'s store takes its indices and then the value"
3384 );
3385 }
3386
3387 for (pat, what) in [
3389 (of(CollectionCtor::Set, 1), "Set"),
3390 (of(CollectionCtor::MinHeap, 1), "MinHeap"),
3391 (of(CollectionCtor::MaxHeap, 1), "MaxHeap"),
3392 (of(CollectionCtor::BitSet, 0), "BitSet"),
3393 ] {
3394 for name in [crate::catalog::INDEX_READ, crate::catalog::INDEX_STORE] {
3395 assert_eq!(
3396 cat.by_receiver_and_name(&pat, name).count(),
3397 0,
3398 "{what} has no `{name}`"
3399 );
3400 }
3401 }
3402 assert_eq!(
3403 cat.by_receiver_and_name(&text_receiver(), crate::catalog::INDEX_STORE)
3404 .count(),
3405 0,
3406 "a `Text` is immutable: it reads through a subscript and has \
3407 no element store"
3408 );
3409
3410 for (pat, push, what) in [
3415 (of(CollectionCtor::Vec, 1), "push", "Vec"),
3416 (of(CollectionCtor::Deque, 1), "push_back", "Deque"),
3417 ] {
3418 let store = cat
3419 .by_receiver_and_name(&pat, crate::catalog::INDEX_STORE)
3420 .next()
3421 .unwrap_or_else(|| panic!("{what}'s store"));
3422 let appender = cat
3423 .by_receiver_and_name(&pat, push)
3424 .next()
3425 .unwrap_or_else(|| panic!("{what}.{push}"));
3426 assert_ne!(
3427 store.lowering, appender.lowering,
3428 "{what}'s store replaces; `{push}` appends"
3429 );
3430 assert!(
3431 store.can_fault(),
3432 "{what}'s store reports an index it does not hold"
3433 );
3434 }
3435
3436 let get = cat
3440 .by_receiver_and_name(&map_pat, "get")
3441 .next()
3442 .expect("Map.get");
3443 let index = cat
3444 .by_receiver_and_name(&map_pat, crate::catalog::INDEX_READ)
3445 .next()
3446 .expect("Map's subscript");
3447 assert_ne!(get.lowering, index.lowering);
3448 assert!(
3449 index.can_fault() && !get.can_fault(),
3450 "indexing faults where `.get` answers"
3451 );
3452
3453 let updating = |receiver: &TypePattern, name: &str| -> Vec<&MethodEntry> {
3466 cat.entries()
3467 .iter()
3468 .filter(|e| {
3469 e.name == name && crate::type_pattern::pattern_matches(&e.receiver, receiver)
3470 })
3471 .collect()
3472 };
3473 for (name, what) in [
3474 (crate::catalog::INDEX_STORE_MIN, "min="),
3475 (crate::catalog::INDEX_STORE_MAX, "max="),
3476 ] {
3477 for (pat, arity, var, who) in [
3479 (map_of_k_v(), 2, "V", "Map"),
3480 (of(CollectionCtor::Vec, 1), 2, "T", "Vec"),
3481 (of(CollectionCtor::Deque, 1), 2, "T", "Deque"),
3482 (of(CollectionCtor::Grid, 1), 3, "T", "Grid"),
3483 ] {
3484 let hits = updating(&pat, name);
3485 assert_eq!(hits.len(), 1, "`{what}` is one row on a {who}");
3486 assert_eq!(
3487 hits[0].arity(),
3488 arity,
3489 "{who}'s `{what}` takes its indices and its value"
3490 );
3491 let plain_store = cat
3492 .entries()
3493 .iter()
3494 .find(|e| {
3495 e.name == crate::catalog::INDEX_STORE
3496 && crate::type_pattern::pattern_matches(&e.receiver, &pat)
3497 })
3498 .unwrap_or_else(|| panic!("{who}'s store"));
3499 assert_ne!(
3500 hits[0].lowering, plain_store.lowering,
3501 "{who}'s `{what}` must not lower to its plain store"
3502 );
3503 assert_eq!(
3510 hits[0].bounds(),
3511 vec![(var, crate::Bound::Kind(crate::CapKind::Ord))],
3512 "{who}'s `{what}` is bound to an order and to nothing narrower"
3513 );
3514 }
3515 for (pat, other) in [
3520 (of(CollectionCtor::Counter, 1), "Counter"),
3521 (of(CollectionCtor::Set, 1), "Set"),
3522 (text_receiver(), "Text"),
3523 (of(CollectionCtor::MinHeap, 1), "MinHeap"),
3524 (of(CollectionCtor::BitSet, 0), "BitSet"),
3525 ] {
3526 assert!(updating(&pat, name).is_empty(), "{other} has no `{what}`");
3527 }
3528 }
3529 for (pat, who) in [
3532 (map_of_k_v(), "Map"),
3533 (of(CollectionCtor::Vec, 1), "Vec"),
3534 (of(CollectionCtor::Deque, 1), "Deque"),
3535 (of(CollectionCtor::Grid, 1), "Grid"),
3536 ] {
3537 assert_ne!(
3538 updating(&pat, crate::catalog::INDEX_STORE_MIN)[0].lowering,
3539 updating(&pat, crate::catalog::INDEX_STORE_MAX)[0].lowering,
3540 "{who}'s `min=` and `max=` are two wrappers"
3541 );
3542 }
3543
3544 for name in [
3547 crate::catalog::INDEX_READ,
3548 crate::catalog::INDEX_STORE,
3549 crate::catalog::INDEX_STORE_MIN,
3550 crate::catalog::INDEX_STORE_MAX,
3551 ] {
3552 assert!(
3553 !name
3554 .chars()
3555 .next()
3556 .is_some_and(|c| c.is_alphabetic() || c == '_'),
3557 "`{name}` must not be spellable as a method name"
3558 );
3559 }
3560 }
3561
3562 #[test]
3566 fn catalog_covers_every_collection_kind() {
3567 let cat = builtin_catalog();
3568 for (ctor, name) in [
3571 (CollectionCtor::Vec, "Vec"),
3572 (CollectionCtor::Deque, "Deque"),
3573 (CollectionCtor::Set, "Set"),
3574 (CollectionCtor::Counter, "Counter"),
3575 (CollectionCtor::MinHeap, "MinHeap"),
3576 (CollectionCtor::MaxHeap, "MaxHeap"),
3577 (CollectionCtor::BitSet, "BitSet"),
3578 ] {
3579 let args: Vec<TypePattern> = match ctor.arity() {
3580 0 => Vec::new(),
3581 n => (0..n).map(|_| TypePattern::var("T")).collect(),
3582 };
3583 let pat = TypePattern::Collection { ctor, args };
3584 let len = cat.by_receiver_and_name(&pat, "len").count();
3585 let is_empty = cat.by_receiver_and_name(&pat, "is_empty").count();
3586 assert!(len >= 1, "{name} missing len method");
3587 assert!(is_empty >= 1, "{name} missing is_empty method");
3588 }
3589 let map_pat = map_of_k_v();
3592 assert!(cat.by_receiver_and_name(&map_pat, "len").count() >= 1);
3593 assert!(cat.by_receiver_and_name(&map_pat, "is_empty").count() >= 1);
3594 let grid_pat = grid_of_t();
3596 assert!(cat.by_receiver_and_name(&grid_pat, "width").count() >= 1);
3597 assert!(cat.by_receiver_and_name(&grid_pat, "neighbors4").count() >= 1);
3598 }
3599
3600 #[test]
3613 fn the_neighbourhood_records_field_order_is_reading_order() {
3614 let cat = builtin_catalog();
3615 let grid_pat = grid_of_t();
3616 for (method, expected) in [
3617 ("around4", &["up", "left", "right", "down"][..]),
3618 (
3619 "around8",
3620 &[
3621 "up_left",
3622 "up",
3623 "up_right",
3624 "left",
3625 "right",
3626 "down_left",
3627 "down",
3628 "down_right",
3629 ][..],
3630 ),
3631 ] {
3632 let rows: Vec<_> = cat.by_receiver_and_name(&grid_pat, method).collect();
3633 assert_eq!(rows.len(), 1, "`Grid[T].{method}` is one row");
3634 let TypePattern::Record { name, fields } = &rows[0].result else {
3635 panic!("`Grid[T].{method}` answers a nominal record");
3636 };
3637 assert_eq!(
3638 *name,
3639 if method == "around4" {
3640 "Around4"
3641 } else {
3642 "Around8"
3643 }
3644 );
3645 let names: Vec<&str> = fields.iter().map(|(n, _)| *n).collect();
3646 assert_eq!(names, expected, "`{name}`'s fields are its slot order");
3647 for (fname, fpat) in fields {
3648 assert_eq!(
3649 *fpat,
3650 TypePattern::Option(Box::new(point_pattern())),
3651 "`{name}.{fname}` is an Option[(Int, Int)]"
3652 );
3653 }
3654 assert_eq!(rows[0].result.to_string(), *name);
3657 }
3658 }
3659
3660 #[test]
3669 fn the_clipped_vec_rows_still_answer_a_vec() {
3670 let cat = builtin_catalog();
3671 let grid_pat = grid_of_t();
3672 let vec_of_points = TypePattern::Collection {
3673 ctor: CollectionCtor::Vec,
3674 args: vec![point_pattern()],
3675 };
3676 for method in ["neighbors4", "neighbors8"] {
3677 let rows: Vec<_> = cat.by_receiver_and_name(&grid_pat, method).collect();
3678 assert_eq!(rows.len(), 1);
3679 assert_eq!(
3680 rows[0].result, vec_of_points,
3681 "`{method}` answers the clipped Vec a graph walk consumes"
3682 );
3683 }
3684 }
3685
3686 #[test]
3696 fn only_the_counts_that_run_a_closure_can_fault() {
3697 let cat = builtin_catalog();
3698 let grid_pat = grid_of_t();
3699 let predicate = TypePattern::Function {
3700 params: vec![TypePattern::var("T")],
3701 result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
3702 };
3703 for (method, second, can_fault) in [
3704 ("count4", TypePattern::var("T"), false),
3705 ("count8", TypePattern::var("T"), false),
3706 ("count4_where", predicate.clone(), true),
3707 ("count8_where", predicate, true),
3708 ] {
3709 let rows: Vec<_> = cat.by_receiver_and_name(&grid_pat, method).collect();
3710 assert_eq!(rows.len(), 1, "`Grid[T].{method}` is one row");
3711 let row = rows[0];
3712 assert_eq!(
3713 row.params,
3714 vec![point_pattern(), second],
3715 "`{method}` takes the position and then what to count"
3716 );
3717 assert_eq!(row.result, TypePattern::Scalar(ScalarType::Int));
3718 assert_eq!(
3719 row.can_fault(),
3720 can_fault,
3721 "`{method}`: only a row that calls back into the program \
3722 needs a fault check after it"
3723 );
3724 }
3725 }
3726
3727 #[test]
3752 fn a_non_faulting_row_with_a_value_result_cannot_answer_the_unit_sentinel() {
3753 let cat = builtin_catalog();
3754 let mut checked = 0;
3755 for entry in cat.entries() {
3756 let MethodLowering::RuntimeSymbol(sym) = entry.lowering else {
3759 continue;
3760 };
3761 checked += 1;
3762 let ret = sym.sig().ret;
3763 let result_is_unit = entry.result == TypePattern::Unit;
3764 match (ret, result_is_unit) {
3765 (abi::AbiRet::GcUnit, true) | (abi::AbiRet::Gc, false) => {}
3766 _ => panic!(
3767 "{}.{} declares `{}` and lowers to `{}`, whose manifest return is \
3768 {ret:?}. A wrapper answers either a value (`AbiRet::Gc`, \
3769 non-`Unit` result) or nothing (`AbiRet::GcUnit`, `Unit` \
3770 result); an answer that is sometimes absent is spelled \
3771 `Option[T]`, never a Unit sentinel under a value type.",
3772 entry.receiver,
3773 entry.name,
3774 entry.result,
3775 sym.name(),
3776 ),
3777 }
3778 }
3779 assert!(
3782 checked >= 40,
3783 "expected the sweep to reach most of the catalog, it reached {checked} rows"
3784 );
3785 }
3786
3787 #[test]
3801 fn a_scalar_primitive_row_answers_the_scalar_channel_and_a_scalar_type() {
3802 let cat = builtin_catalog();
3803 let mut rows = 0;
3804 for entry in cat.entries() {
3805 let MethodLowering::ScalarPrimitive(sym) = entry.lowering else {
3806 continue;
3807 };
3808 rows += 1;
3809 assert_eq!(
3810 sym.sig().ret,
3811 abi::AbiRet::RawI64,
3812 "{}.{} lowers as a scalar primitive, so `{}` must answer the \
3813 scalar channel; a `-> Gc` row here is a box the caller would \
3814 have to unwrap again, which is the whole thing this arm exists \
3815 to remove",
3816 entry.receiver,
3817 entry.name,
3818 sym.name(),
3819 );
3820 assert!(
3821 matches!(entry.result, TypePattern::Scalar(_)),
3822 "{}.{} answers `{}`, which the scalar channel cannot carry",
3823 entry.receiver,
3824 entry.name,
3825 entry.result,
3826 );
3827 assert!(
3830 !entry.allocates(),
3831 "{}.{} allocates, so it cannot be a non-safepoint instruction",
3832 entry.receiver,
3833 entry.name,
3834 );
3835 }
3836 assert_eq!(
3837 rows, 1,
3838 "`BitSet.contains` is the only scalar-primitive row today; a second \
3839 one wants an arm in `praxis-mir`'s `lower_scalar_primitive` before \
3840 this number moves"
3841 );
3842 }
3843
3844 #[test]
3848 fn map_get_and_grid_find_answer_an_option() {
3849 let cat = builtin_catalog();
3850 let map_pat = map_of_k_v();
3851 let get = cat
3852 .by_receiver_and_name(&map_pat, "get")
3853 .next()
3854 .expect("map.get exists");
3855 assert_eq!(
3856 get.result,
3857 TypePattern::Option(Box::new(TypePattern::var("V"))),
3858 "§5.7 writes `Map[K,V].get(K) -> Option[V]`"
3859 );
3860
3861 let grid_pat = grid_of_t();
3862 let find = cat
3863 .by_receiver_and_name(&grid_pat, "find")
3864 .next()
3865 .expect("grid.find exists");
3866 assert_eq!(find.result, TypePattern::Option(Box::new(point_pattern())));
3867
3868 let counter_pat = counter_of_t();
3871 let counter_get = cat
3872 .by_receiver_and_name(&counter_pat, "get")
3873 .next()
3874 .expect("counter.get exists");
3875 assert_eq!(
3876 counter_get.result,
3877 TypePattern::Scalar(ScalarType::Int),
3878 "§6.2: a Counter's absent values read as zero, deliberately"
3879 );
3880 }
3881
3882 #[test]
3895 fn text_int_and_float_answer_options() {
3896 let cat = builtin_catalog();
3897 for (name, scalar) in [("int", ScalarType::Int), ("float", ScalarType::Float)] {
3898 let entry = cat
3899 .by_receiver_and_name(&TypePattern::Scalar(ScalarType::Text), name)
3900 .next()
3901 .unwrap_or_else(|| panic!("`Text.{name}()` exists"));
3902 assert_eq!(
3903 entry.result,
3904 TypePattern::Option(Box::new(TypePattern::Scalar(scalar))),
3905 "a text that is not a number is absence, not a fault (\u{00a7}4.7)"
3906 );
3907 assert!(entry.params.is_empty(), "`{name}` takes no arguments");
3908 }
3909 }
3910
3911 #[test]
3918 fn the_two_text_reads_answer_a_char() {
3919 let cat = builtin_catalog();
3920 let text = text_receiver();
3921
3922 for name in [crate::catalog::INDEX_READ, "get"] {
3923 let row = cat
3924 .by_receiver_and_name(&text, name)
3925 .next()
3926 .unwrap_or_else(|| panic!("Text.{name} exists"));
3927 assert_eq!(
3928 row.result,
3929 TypePattern::Scalar(ScalarType::Char),
3930 "ADR-086: `Text.{name}` answers a Char, not the char's scalar value"
3931 );
3932 assert_eq!(
3935 row.lowering,
3936 MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextGet),
3937 "`Text.{name}` lowers through the one text read"
3938 );
3939 }
3940 }
3941
3942 #[test]
3947 fn char_and_int_convert_both_ways() {
3948 let cat = builtin_catalog();
3949
3950 let to_int = cat
3951 .by_receiver_and_name(&TypePattern::Scalar(ScalarType::Char), "to_int")
3952 .next()
3953 .expect("Char.to_int exists");
3954 assert_eq!(to_int.result, TypePattern::Scalar(ScalarType::Int));
3955 assert_eq!(to_int.purity, Purity::Pure);
3956
3957 let to_char = cat
3958 .by_receiver_and_name(&TypePattern::Scalar(ScalarType::Int), "to_char")
3959 .next()
3960 .expect("Int.to_char exists");
3961 assert_eq!(to_char.result, TypePattern::Scalar(ScalarType::Char));
3962
3963 assert!(
3967 to_char.can_fault(),
3968 "not every Int is a Unicode scalar value, so the narrowing faults"
3969 );
3970 assert!(
3971 !to_int.can_fault(),
3972 "every Unicode scalar value fits an Int, so the widening cannot"
3973 );
3974 }
3975
3976 #[test]
3983 fn the_to_text_family_is_int_float_and_char() {
3984 let cat = builtin_catalog();
3985 for scalar in [ScalarType::Int, ScalarType::Float, ScalarType::Char] {
3986 let receiver = TypePattern::Scalar(scalar);
3987 let row = cat
3988 .by_receiver_and_name(&receiver, "to_text")
3989 .next()
3990 .unwrap_or_else(|| panic!("{scalar:?}.to_text exists"));
3991 assert_eq!(row.result, TypePattern::Scalar(ScalarType::Text));
3992 assert_eq!(row.purity, Purity::Pure);
3993 assert!(row.allocates(), "{scalar:?}.to_text answers a fresh Text");
3994 assert!(!row.can_fault(), "{scalar:?}.to_text cannot fail");
3998 }
3999 for scalar in [ScalarType::Bool, ScalarType::Byte, ScalarType::Text] {
4000 let receiver = TypePattern::Scalar(scalar);
4001 assert_eq!(
4002 cat.by_receiver_and_name(&receiver, "to_text").count(),
4003 0,
4004 "the `to_text` family is three scalars; {scalar:?} is not one of \
4005 them, and a universal `to_text` is §8.1 interpolation's question"
4006 );
4007 }
4008 }
4009
4010 #[test]
4020 fn join_is_one_row_and_a_sequence_of_chars_has_its_own_name() {
4021 let cat = builtin_catalog();
4022
4023 let join: Vec<_> = cat.entries().iter().filter(|e| e.name == "join").collect();
4024 assert_eq!(join.len(), 1, "`join` is one row");
4025 assert_eq!(join[0].receiver, iterable_of_text_elem());
4026 assert_eq!(join[0].params, vec![TypePattern::Scalar(ScalarType::Text)]);
4027 assert_eq!(join[0].result, TypePattern::Scalar(ScalarType::Text));
4028
4029 assert!(
4033 !cat.entries()
4034 .iter()
4035 .any(|e| e.name == "to_text" && matches!(e.receiver, TypePattern::Iterable { .. })),
4036 "an `Iterable.to_text` would collide with the scalar `to_text` rows"
4037 );
4038 let chars_pat = vec_of_char();
4039 let chars = cat
4040 .by_receiver_and_name(&chars_pat, "to_text")
4041 .next()
4042 .expect("Vec[Char].to_text exists");
4043 assert_eq!(chars.result, TypePattern::Scalar(ScalarType::Text));
4044 assert!(chars.params.is_empty());
4045 }
4046
4047 #[test]
4056 fn reversed_is_a_barrier_with_no_bound_on_its_element() {
4057 let cat = builtin_catalog();
4058 let receiver = iterable_of_t();
4059 let row = cat
4060 .by_receiver_and_name(&receiver, "reversed")
4061 .next()
4062 .expect("Iterable.reversed exists");
4063 assert_eq!(row.result, vec_of_t());
4064 assert_eq!(row.purity, Purity::Pure);
4065 assert!(row.bounds().is_empty(), "reversal reads no callback");
4066 assert!(
4067 !row.can_fault(),
4068 "there is no element `reversed` can be handed that it cannot reverse"
4069 );
4070 assert!(
4071 matches!(
4072 row.lowering,
4073 MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecReversed)
4074 ),
4075 "a barrier is a runtime call, not a fused stage: reversal cannot \
4076 answer its first element until it has seen the last"
4077 );
4078 }
4079
4080 #[test]
4098 fn a_grouping_answers_a_nested_vec_with_no_bound_and_can_fault() {
4099 let cat = builtin_catalog();
4100 let receiver = iterable_of_t();
4101 for (name, symbol) in [
4102 ("chunks", abi::RuntimeSymbol::VecChunks),
4103 ("windows", abi::RuntimeSymbol::VecWindows),
4104 ] {
4105 let row = cat
4106 .by_receiver_and_name(&receiver, name)
4107 .next()
4108 .unwrap_or_else(|| panic!("Iterable.{name} exists"));
4109 assert_eq!(
4110 row.result,
4111 vec_of_vec_of_t(),
4112 "`{name}` groups without flattening"
4113 );
4114 assert_eq!(row.params, vec![TypePattern::Scalar(ScalarType::Int)]);
4115 assert_eq!(row.purity, Purity::Pure);
4116 assert!(
4117 row.bounds().is_empty(),
4118 "a grouping reads no descriptor callback ({name})"
4119 );
4120 assert!(
4121 row.can_fault(),
4122 "`{name}(0)` names no run, and the program has to be able to see that"
4123 );
4124 assert!(
4125 matches!(row.lowering, MethodLowering::RuntimeSymbol(s) if s == symbol),
4126 "a grouping is a barrier: it cannot answer its first group from \
4127 one element ({name})"
4128 );
4129 }
4130 }
4131
4132 #[test]
4145 fn the_overflow_alternative_family_is_three_modes_over_three_operators() {
4146 let cat = builtin_catalog();
4147 let int = TypePattern::Scalar(ScalarType::Int);
4148
4149 for mode in ["wrapping", "saturating", "checked"] {
4150 for op in ["add", "sub", "mul"] {
4151 let name = format!("{mode}_{op}");
4152 let row = cat
4153 .by_receiver_and_name(&int, &name)
4154 .next()
4155 .unwrap_or_else(|| panic!("§4.12's family includes `Int.{name}`"));
4156 assert_eq!(row.params, vec![TypePattern::Scalar(ScalarType::Int)]);
4157 assert!(!row.can_fault(), "`{name}` is an alternative to faulting");
4161
4162 let want = if mode == "checked" {
4163 TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int)))
4164 } else {
4165 TypePattern::Scalar(ScalarType::Int)
4166 };
4167 assert_eq!(row.result, want, "`{name}`'s result");
4168 }
4169 }
4170
4171 for absent in [
4173 "wrapping_div",
4174 "saturating_div",
4175 "checked_div",
4176 "wrapping_rem",
4177 "checked_rem",
4178 "wrapping_neg",
4179 "checked_neg",
4180 "saturating_abs",
4181 ] {
4182 assert!(
4183 cat.by_receiver_and_name(&int, absent).next().is_none(),
4184 "§4.12 closes the family before `{absent}`: division's escape \
4185 hatch is closed by \"Division by zero always faults\", and \
4186 `_neg`/`_abs` are spelled with `0.wrapping_sub(x)`"
4187 );
4188 }
4189 }
4190}