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_positions())
81 .entry(grid_cells())
82 .entry(grid_row())
83 .entry(grid_column())
84 .entry(grid_find())
85 .entry(grid_find_all())
86 .entry(grid_transpose())
87 .entry(grid_rotate_left())
88 .entry(grid_rotate_right())
89 .entry(seq_map())
96 .entry(seq_filter())
97 .entry(seq_fold())
98 .entry(seq_sum())
99 .entry(seq_count())
100 .entry(seq_count_if())
101 .entry(seq_sorted())
104 .entry(seq_sorted_by_key())
105 .entry(seq_unique())
106 .entry(seq_reversed())
107 .entry(seq_frequencies())
108 .entry(seq_join())
109 .entry(seq_chunks())
112 .entry(seq_windows())
113 .entry(seq_take())
116 .entry(seq_skip())
117 .entry(seq_take_while())
118 .entry(seq_enumerate())
119 .entry(seq_zip())
120 .entry(seq_flat_map())
121 .entry(seq_filter_map())
122 .entry(seq_product())
123 .entry(seq_min())
124 .entry(seq_max())
125 .entry(seq_min_by())
126 .entry(seq_max_by())
127 .entry(seq_any())
128 .entry(seq_all())
129 .entry(seq_find())
130 .entry(seq_position())
131 .entry(seq_reduce())
132 .entry(seq_to_vec())
136 .entry(seq_to_set())
137 .entry(seq_to_map())
138 .entry(seq_to_counter())
139 .entry(seq_to_deque())
140 .entry(seq_to_min_heap())
141 .entry(seq_to_max_heap())
142 .entry(seq_to_bitset())
143 .entry(text_len())
144 .entry(text_int())
145 .entry(text_float())
146 .entry(text_is_empty())
147 .entry(text_get())
148 .entry(float_abs())
151 .entry(float_sqrt())
152 .entry(float_floor())
153 .entry(float_ceil())
154 .entry(float_round())
155 .entry(float_sign())
156 .entry(float_to_int())
157 .entry(float_to_text())
158 .entry(float_is_nan())
159 .entry(float_is_infinite())
160 .entry(float_min())
161 .entry(float_max())
162 .entry(int_to_float())
164 .entry(char_to_int())
167 .entry(int_to_char())
168 .entry(int_to_text())
171 .entry(char_to_text())
172 .entry(int_wrapping_add())
173 .entry(int_saturating_add())
174 .entry(int_checked_add())
175 .entry(int_wrapping_sub())
176 .entry(int_saturating_sub())
177 .entry(int_checked_sub())
178 .entry(int_wrapping_mul())
179 .entry(int_saturating_mul())
180 .entry(int_checked_mul())
181 .entry(vec_index())
185 .entry(vec_index_set())
186 .entry(deque_index())
187 .entry(deque_index_set())
188 .entry(text_index())
189 .entry(map_index())
190 .entry(map_index_set())
191 .entry(counter_index())
192 .entry(counter_index_set())
193 .entry(grid_index())
194 .entry(grid_index_set())
195 .entry(map_index_min())
200 .entry(map_index_max())
201 .entry(counter_keys())
203 .entry(counter_values())
204 .entry(map_keys())
205 .entry(map_values())
206 .finish()
207 .expect("built-in catalog must be duplicate-free")
208}
209
210fn counter_keys() -> MethodEntry {
218 MethodEntry {
219 receiver: counter_of_t(),
220 name: "keys",
221 params: vec![],
222 result: TypePattern::Collection {
223 ctor: CollectionCtor::Vec,
224 args: vec![TypePattern::var("T")],
225 },
226 purity: Purity::Pure,
227 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterKeys),
228 doc: "Every key, as a `Vec[T]`, ordered with `values()`.",
229 }
230}
231
232fn counter_values() -> MethodEntry {
233 MethodEntry {
234 receiver: counter_of_t(),
235 name: "values",
236 params: vec![],
237 result: TypePattern::Collection {
238 ctor: CollectionCtor::Vec,
239 args: vec![TypePattern::Scalar(ScalarType::Int)],
240 },
241 purity: Purity::Pure,
242 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterValues),
243 doc: "Every count, as a `Vec[Int]`, ordered with `keys()`.",
244 }
245}
246
247fn map_keys() -> MethodEntry {
248 MethodEntry {
249 receiver: map_of_k_v(),
250 name: "keys",
251 params: vec![],
252 result: TypePattern::Collection {
253 ctor: CollectionCtor::Vec,
254 args: vec![TypePattern::var("K")],
255 },
256 purity: Purity::Pure,
257 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapKeys),
258 doc: "Every key, as a `Vec[K]`, ordered with `values()`.",
259 }
260}
261
262fn map_values() -> MethodEntry {
263 MethodEntry {
264 receiver: map_of_k_v(),
265 name: "values",
266 params: vec![],
267 result: TypePattern::Collection {
268 ctor: CollectionCtor::Vec,
269 args: vec![TypePattern::var("V")],
270 },
271 purity: Purity::Pure,
272 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapValues),
273 doc: "Every value, as a `Vec[V]`, ordered with `keys()`.",
274 }
275}
276
277fn vec_index() -> MethodEntry {
299 MethodEntry {
300 receiver: vec_of_t(),
301 name: crate::catalog::INDEX_READ,
302 params: vec![TypePattern::Scalar(ScalarType::Int)],
303 result: TypePattern::var("T"),
304 purity: Purity::Pure,
305 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecGet),
306 doc: "`v[i]` — the element at `i`; faults if out of range.",
307 }
308}
309
310fn vec_index_set() -> MethodEntry {
311 MethodEntry {
312 receiver: vec_of_t(),
313 name: crate::catalog::INDEX_STORE,
314 params: vec![TypePattern::Scalar(ScalarType::Int), TypePattern::var("T")],
315 result: TypePattern::Unit,
316 purity: Purity::Impure,
317 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecSet),
318 doc: "`v[i] = value` — replace the element at `i`; faults if out of range \
319 (it never appends — `push` is the spelling that grows a vector).",
320 }
321}
322
323fn deque_index() -> MethodEntry {
324 MethodEntry {
325 receiver: deque_of_t(),
326 name: crate::catalog::INDEX_READ,
327 params: vec![TypePattern::Scalar(ScalarType::Int)],
328 result: TypePattern::var("T"),
329 purity: Purity::Pure,
330 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeGet),
331 doc: "`d[i]` — the element at `i` (0-based from the front); faults if out of range.",
332 }
333}
334
335fn deque_index_set() -> MethodEntry {
336 MethodEntry {
337 receiver: deque_of_t(),
338 name: crate::catalog::INDEX_STORE,
339 params: vec![TypePattern::Scalar(ScalarType::Int), TypePattern::var("T")],
340 result: TypePattern::Unit,
341 purity: Purity::Impure,
342 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeSet),
343 doc: "`d[i] = value` — replace the element at `i` (0-based from the front); \
344 faults if out of range (it never inserts).",
345 }
346}
347
348fn text_index() -> MethodEntry {
349 MethodEntry {
350 receiver: text_receiver(),
351 name: crate::catalog::INDEX_READ,
352 params: vec![TypePattern::Scalar(ScalarType::Int)],
353 result: TypePattern::Scalar(ScalarType::Char),
354 purity: Purity::Pure,
355 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextGet),
356 doc: "`t[i]` — the `Char` at `i`, indexing by Unicode scalar value and not \
357 by byte; faults if out of range (ADR-086).",
358 }
359}
360
361fn map_index() -> MethodEntry {
362 MethodEntry {
363 receiver: map_of_k_v(),
364 name: crate::catalog::INDEX_READ,
365 params: vec![TypePattern::var("K")],
366 result: TypePattern::var("V"),
367 purity: Purity::Pure,
368 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapIndex),
369 doc: "`m[key]` — the value for `key`; **faults** if absent (§4.7; `.get` is the \
370 spelling that answers with absence).",
371 }
372}
373
374fn map_index_set() -> MethodEntry {
375 MethodEntry {
376 receiver: map_of_k_v(),
377 name: crate::catalog::INDEX_STORE,
378 params: vec![TypePattern::var("K"), TypePattern::var("V")],
379 result: TypePattern::Unit,
380 purity: Purity::Impure,
381 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapInsert),
382 doc: "`m[key] = value` — set `key`, replacing any prior value.",
383 }
384}
385
386fn map_of_k_int_value() -> TypePattern {
395 TypePattern::Collection {
396 ctor: CollectionCtor::Map,
397 args: vec![
398 TypePattern::var("K"),
399 TypePattern::is_scalar("V", ScalarType::Int),
400 ],
401 }
402}
403
404fn map_index_min() -> MethodEntry {
405 MethodEntry {
406 receiver: map_of_k_int_value(),
407 name: crate::catalog::INDEX_STORE_MIN,
408 params: vec![TypePattern::var("K"), TypePattern::var("V")],
409 result: TypePattern::Unit,
410 purity: Purity::Impure,
411 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapUpdateMin),
412 doc: "`d[key] min= candidate` — keep the smaller value; an absent entry \
413 accepts the first value.",
414 }
415}
416
417fn map_index_max() -> MethodEntry {
418 MethodEntry {
419 receiver: map_of_k_int_value(),
420 name: crate::catalog::INDEX_STORE_MAX,
421 params: vec![TypePattern::var("K"), TypePattern::var("V")],
422 result: TypePattern::Unit,
423 purity: Purity::Impure,
424 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapUpdateMax),
425 doc: "`b[key] max= score` — keep the larger value; an absent entry accepts \
426 the first value.",
427 }
428}
429
430fn counter_index() -> MethodEntry {
431 MethodEntry {
432 receiver: counter_of_t(),
433 name: crate::catalog::INDEX_READ,
434 params: vec![TypePattern::var("T")],
435 result: TypePattern::Scalar(ScalarType::Int),
436 purity: Purity::Pure,
437 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterGet),
438 doc: "`c[key]` — the count for `key`, or zero if absent; never faults.",
439 }
440}
441
442fn counter_index_set() -> MethodEntry {
443 MethodEntry {
444 receiver: counter_of_t(),
445 name: crate::catalog::INDEX_STORE,
446 params: vec![TypePattern::var("T"), TypePattern::Scalar(ScalarType::Int)],
447 result: TypePattern::Unit,
448 purity: Purity::Impure,
449 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterSet),
450 doc: "`c[key] = n` — set the count for `key`.",
451 }
452}
453
454fn grid_index() -> MethodEntry {
455 MethodEntry {
456 receiver: grid_of_t(),
457 name: crate::catalog::INDEX_READ,
458 params: vec![
459 TypePattern::Scalar(ScalarType::Int),
460 TypePattern::Scalar(ScalarType::Int),
461 ],
462 result: TypePattern::var("T"),
463 purity: Purity::Pure,
464 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridGet),
465 doc: "`grid[x, y]` — the cell at (x, y); faults if out of range.",
466 }
467}
468
469fn grid_index_set() -> MethodEntry {
470 MethodEntry {
471 receiver: grid_of_t(),
472 name: crate::catalog::INDEX_STORE,
473 params: vec![
474 TypePattern::Scalar(ScalarType::Int),
475 TypePattern::Scalar(ScalarType::Int),
476 TypePattern::var("T"),
477 ],
478 result: TypePattern::Unit,
479 purity: Purity::Impure,
480 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridSet),
481 doc: "`grid[x, y] = value` — set the cell at (x, y); faults if out of range.",
482 }
483}
484
485fn text_receiver() -> TypePattern {
488 TypePattern::Scalar(ScalarType::Text)
489}
490
491fn text_len() -> MethodEntry {
492 MethodEntry {
493 receiver: text_receiver(),
494 name: "len",
495 params: vec![],
496 result: TypePattern::Scalar(ScalarType::Int),
497 purity: Purity::Pure,
498 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextLen),
499 doc: "Number of Unicode scalar values (chars) in the text.",
500 }
501}
502
503fn text_int() -> MethodEntry {
514 MethodEntry {
515 receiver: text_receiver(),
516 name: "int",
517 params: vec![],
518 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
519 purity: Purity::Pure,
520 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextInt),
521 doc: "The Int this text spells as `Some(n)`, or `None` if it spells none.",
522 }
523}
524
525fn text_float() -> MethodEntry {
533 MethodEntry {
534 receiver: text_receiver(),
535 name: "float",
536 params: vec![],
537 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Float))),
538 purity: Purity::Pure,
539 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextFloat),
540 doc: "The Float this text spells as `Some(x)`, or `None` if it spells none.",
541 }
542}
543
544fn text_is_empty() -> MethodEntry {
545 MethodEntry {
546 receiver: text_receiver(),
547 name: "is_empty",
548 params: vec![],
549 result: TypePattern::Scalar(ScalarType::Bool),
550 purity: Purity::Pure,
551 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextIsEmpty),
552 doc: "True iff the text has no chars.",
553 }
554}
555
556fn text_get() -> MethodEntry {
557 MethodEntry {
558 receiver: text_receiver(),
559 name: "get",
560 params: vec![TypePattern::Scalar(ScalarType::Int)],
561 result: TypePattern::Scalar(ScalarType::Char),
562 purity: Purity::Pure,
563 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextGet),
564 doc: "The `Char` at `index`; faults if out of range. `t[index]` is the \
565 same row and the same answer (ADR-086).",
566 }
567}
568
569fn float_receiver() -> TypePattern {
577 TypePattern::Scalar(ScalarType::Float)
578}
579
580fn float_abs() -> MethodEntry {
581 MethodEntry {
582 receiver: float_receiver(),
583 name: "abs",
584 params: vec![],
585 result: TypePattern::Scalar(ScalarType::Float),
586 purity: Purity::Pure,
587 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatAbs),
588 doc: "Absolute value.",
589 }
590}
591
592fn float_sqrt() -> MethodEntry {
593 MethodEntry {
594 receiver: float_receiver(),
595 name: "sqrt",
596 params: vec![],
597 result: TypePattern::Scalar(ScalarType::Float),
598 purity: Purity::Pure,
599 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatSqrt),
600 doc: "Square root. Negative inputs yield NaN (IEEE-754).",
601 }
602}
603
604fn float_floor() -> MethodEntry {
605 MethodEntry {
606 receiver: float_receiver(),
607 name: "floor",
608 params: vec![],
609 result: TypePattern::Scalar(ScalarType::Float),
610 purity: Purity::Pure,
611 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatFloor),
612 doc: "Round toward negative infinity.",
613 }
614}
615
616fn float_ceil() -> MethodEntry {
617 MethodEntry {
618 receiver: float_receiver(),
619 name: "ceil",
620 params: vec![],
621 result: TypePattern::Scalar(ScalarType::Float),
622 purity: Purity::Pure,
623 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatCeil),
624 doc: "Round toward positive infinity.",
625 }
626}
627
628fn float_round() -> MethodEntry {
629 MethodEntry {
630 receiver: float_receiver(),
631 name: "round",
632 params: vec![],
633 result: TypePattern::Scalar(ScalarType::Float),
634 purity: Purity::Pure,
635 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatRound),
636 doc: "Round half away from zero.",
637 }
638}
639
640fn float_sign() -> MethodEntry {
641 MethodEntry {
642 receiver: float_receiver(),
643 name: "sign",
644 params: vec![],
645 result: TypePattern::Scalar(ScalarType::Float),
646 purity: Purity::Pure,
647 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatSign),
648 doc: "Sign as -1.0 / 0.0 / 1.0. NaN yields NaN.",
649 }
650}
651
652fn float_to_int() -> MethodEntry {
653 MethodEntry {
654 receiver: float_receiver(),
655 name: "to_int",
656 params: vec![],
657 result: TypePattern::Scalar(ScalarType::Int),
658 purity: Purity::Pure,
659 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatToInt),
660 doc: "Truncate toward zero to an Int. Faults on NaN, ±inf, or out of i64 range.",
661 }
662}
663
664fn float_to_text() -> MethodEntry {
665 MethodEntry {
666 receiver: float_receiver(),
667 name: "to_text",
668 params: vec![],
669 result: TypePattern::Scalar(ScalarType::Text),
670 purity: Purity::Pure,
671 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatToText),
672 doc: "Format as Text (shortest round-trip form; inf/-inf/NaN as literals).",
673 }
674}
675
676fn float_is_nan() -> MethodEntry {
677 MethodEntry {
678 receiver: float_receiver(),
679 name: "is_nan",
680 params: vec![],
681 result: TypePattern::Scalar(ScalarType::Bool),
682 purity: Purity::Pure,
683 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatIsNan),
684 doc: "True iff NaN.",
685 }
686}
687
688fn float_is_infinite() -> MethodEntry {
689 MethodEntry {
690 receiver: float_receiver(),
691 name: "is_infinite",
692 params: vec![],
693 result: TypePattern::Scalar(ScalarType::Bool),
694 purity: Purity::Pure,
695 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatIsInfinite),
696 doc: "True iff ±infinity.",
697 }
698}
699
700fn float_min() -> MethodEntry {
701 MethodEntry {
702 receiver: float_receiver(),
703 name: "min",
704 params: vec![TypePattern::Scalar(ScalarType::Float)],
705 result: TypePattern::Scalar(ScalarType::Float),
706 purity: Purity::Pure,
707 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatMin),
708 doc: "The smaller of two floats. If either is NaN, returns the other.",
709 }
710}
711
712fn float_max() -> MethodEntry {
713 MethodEntry {
714 receiver: float_receiver(),
715 name: "max",
716 params: vec![TypePattern::Scalar(ScalarType::Float)],
717 result: TypePattern::Scalar(ScalarType::Float),
718 purity: Purity::Pure,
719 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatMax),
720 doc: "The larger of two floats. If either is NaN, returns the other.",
721 }
722}
723
724fn int_to_float() -> MethodEntry {
725 MethodEntry {
726 receiver: TypePattern::Scalar(ScalarType::Int),
727 name: "to_float",
728 params: vec![],
729 result: TypePattern::Scalar(ScalarType::Float),
730 purity: Purity::Pure,
731 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntToFloat),
732 doc: "Widen to Float (explicit Int→Float conversion, §4.12).",
733 }
734}
735
736fn int_to_text() -> MethodEntry {
765 MethodEntry {
766 receiver: TypePattern::Scalar(ScalarType::Int),
767 name: "to_text",
768 params: vec![],
769 result: TypePattern::Scalar(ScalarType::Text),
770 purity: Purity::Pure,
771 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntToText),
772 doc: "Format as Text — the same digits `out` writes (ADR-143).",
773 }
774}
775
776fn char_to_int() -> MethodEntry {
801 MethodEntry {
802 receiver: TypePattern::Scalar(ScalarType::Char),
803 name: "to_int",
804 params: vec![],
805 result: TypePattern::Scalar(ScalarType::Int),
806 purity: Purity::Pure,
807 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CharToInt),
808 doc: "The Unicode scalar value, as an `Int`. Never faults (ADR-086).",
809 }
810}
811
812fn char_to_text() -> MethodEntry {
813 MethodEntry {
814 receiver: TypePattern::Scalar(ScalarType::Char),
815 name: "to_text",
816 params: vec![],
817 result: TypePattern::Scalar(ScalarType::Text),
818 purity: Purity::Pure,
819 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CharToText),
820 doc: "The one-character Text holding this scalar — the same character \
821 `out` writes. Never faults (ADR-143).",
822 }
823}
824
825fn int_to_char() -> MethodEntry {
826 MethodEntry {
827 receiver: TypePattern::Scalar(ScalarType::Int),
828 name: "to_char",
829 params: vec![],
830 result: TypePattern::Scalar(ScalarType::Char),
831 purity: Purity::Pure,
832 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntToChar),
833 doc: "The `Char` with this Unicode scalar value; **faults** \
834 (`InvalidChar`) if it is negative, above `0x10FFFF`, or a \
835 surrogate. The narrowing half of the pair, as `Float.to_int` is \
836 (ADR-086).",
837 }
838}
839
840fn int_wrapping_add() -> MethodEntry {
849 MethodEntry {
850 receiver: TypePattern::Scalar(ScalarType::Int),
851 name: "wrapping_add",
852 params: vec![TypePattern::Scalar(ScalarType::Int)],
853 result: TypePattern::Scalar(ScalarType::Int),
854 purity: Purity::Pure,
855 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntWrappingAdd),
856 doc: "Add with two's-complement wraparound instead of a fault.",
857 }
858}
859
860fn int_saturating_add() -> MethodEntry {
861 MethodEntry {
862 receiver: TypePattern::Scalar(ScalarType::Int),
863 name: "saturating_add",
864 params: vec![TypePattern::Scalar(ScalarType::Int)],
865 result: TypePattern::Scalar(ScalarType::Int),
866 purity: Purity::Pure,
867 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntSaturatingAdd),
868 doc: "Add, clamping to Int's ends instead of faulting.",
869 }
870}
871
872fn int_checked_add() -> MethodEntry {
873 MethodEntry {
874 receiver: TypePattern::Scalar(ScalarType::Int),
875 name: "checked_add",
876 params: vec![TypePattern::Scalar(ScalarType::Int)],
877 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
878 purity: Purity::Pure,
879 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntCheckedAdd),
880 doc: "Add, answering None where the checked `+` would fault.",
881 }
882}
883
884fn int_wrapping_sub() -> MethodEntry {
885 MethodEntry {
886 receiver: TypePattern::Scalar(ScalarType::Int),
887 name: "wrapping_sub",
888 params: vec![TypePattern::Scalar(ScalarType::Int)],
889 result: TypePattern::Scalar(ScalarType::Int),
890 purity: Purity::Pure,
891 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntWrappingSub),
892 doc: "Subtract with two's-complement wraparound instead of a fault.",
893 }
894}
895
896fn int_saturating_sub() -> MethodEntry {
897 MethodEntry {
898 receiver: TypePattern::Scalar(ScalarType::Int),
899 name: "saturating_sub",
900 params: vec![TypePattern::Scalar(ScalarType::Int)],
901 result: TypePattern::Scalar(ScalarType::Int),
902 purity: Purity::Pure,
903 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntSaturatingSub),
904 doc: "Subtract, clamping to Int's ends instead of faulting.",
905 }
906}
907
908fn int_checked_sub() -> MethodEntry {
909 MethodEntry {
910 receiver: TypePattern::Scalar(ScalarType::Int),
911 name: "checked_sub",
912 params: vec![TypePattern::Scalar(ScalarType::Int)],
913 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
914 purity: Purity::Pure,
915 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntCheckedSub),
916 doc: "Subtract, answering None where the checked `-` would fault.",
917 }
918}
919
920fn int_wrapping_mul() -> MethodEntry {
921 MethodEntry {
922 receiver: TypePattern::Scalar(ScalarType::Int),
923 name: "wrapping_mul",
924 params: vec![TypePattern::Scalar(ScalarType::Int)],
925 result: TypePattern::Scalar(ScalarType::Int),
926 purity: Purity::Pure,
927 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntWrappingMul),
928 doc: "Multiply with two's-complement wraparound instead of a fault. The \
929 one row here a program could not write for itself: every arithmetic \
930 operator is checked and the language has no bitwise operators.",
931 }
932}
933
934fn int_saturating_mul() -> MethodEntry {
935 MethodEntry {
936 receiver: TypePattern::Scalar(ScalarType::Int),
937 name: "saturating_mul",
938 params: vec![TypePattern::Scalar(ScalarType::Int)],
939 result: TypePattern::Scalar(ScalarType::Int),
940 purity: Purity::Pure,
941 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntSaturatingMul),
942 doc: "Multiply, clamping to Int's ends instead of faulting.",
943 }
944}
945
946fn int_checked_mul() -> MethodEntry {
947 MethodEntry {
948 receiver: TypePattern::Scalar(ScalarType::Int),
949 name: "checked_mul",
950 params: vec![TypePattern::Scalar(ScalarType::Int)],
951 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
952 purity: Purity::Pure,
953 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntCheckedMul),
954 doc: "Multiply, answering None where the checked `*` would fault.",
955 }
956}
957
958fn vec_push() -> MethodEntry {
959 MethodEntry {
960 receiver: vec_of_t(),
961 name: "push",
962 params: vec![TypePattern::var("T")],
963 result: TypePattern::Unit,
964 purity: Purity::Impure,
965 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecPush),
966 doc: "Append a value to the end; returns Unit.",
967 }
968}
969
970fn vec_len() -> MethodEntry {
971 MethodEntry {
972 receiver: vec_of_t(),
973 name: "len",
974 params: vec![],
975 result: TypePattern::Scalar(ScalarType::Int),
976 purity: Purity::Pure,
977 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecLen),
978 doc: "Number of elements in the vector.",
979 }
980}
981
982fn vec_get() -> MethodEntry {
983 MethodEntry {
984 receiver: vec_of_t(),
985 name: "get",
986 params: vec![TypePattern::Scalar(ScalarType::Int)],
987 result: TypePattern::var("T"),
988 purity: Purity::Pure,
989 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecGet),
990 doc: "The element at `index`; faults `IndexOutOfBounds` if out of range.",
991 }
992}
993
994fn vec_is_empty() -> MethodEntry {
995 MethodEntry {
996 receiver: vec_of_t(),
997 name: "is_empty",
998 params: vec![],
999 result: TypePattern::Scalar(ScalarType::Bool),
1000 purity: Purity::Pure,
1001 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecIsEmpty),
1002 doc: "True iff the vector has no elements.",
1003 }
1004}
1005
1006fn vec_of_char() -> TypePattern {
1014 TypePattern::Collection {
1015 ctor: CollectionCtor::Vec,
1016 args: vec![TypePattern::is_scalar("T", ScalarType::Char)],
1017 }
1018}
1019
1020fn vec_to_text() -> MethodEntry {
1033 MethodEntry {
1034 receiver: vec_of_char(),
1035 name: "to_text",
1036 params: vec![],
1037 result: TypePattern::Scalar(ScalarType::Text),
1038 purity: Purity::Pure,
1039 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecToText),
1040 doc: "These Chars as one Text, with nothing between them (ADR-144).",
1041 }
1042}
1043
1044fn deque_of_t() -> TypePattern {
1048 TypePattern::Collection {
1049 ctor: CollectionCtor::Deque,
1050 args: vec![TypePattern::var("T")],
1051 }
1052}
1053
1054fn deque_push_front() -> MethodEntry {
1055 MethodEntry {
1056 receiver: deque_of_t(),
1057 name: "push_front",
1058 params: vec![TypePattern::var("T")],
1059 result: TypePattern::Unit,
1060 purity: Purity::Impure,
1061 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePushFront),
1062 doc: "Prepend a value to the front; returns Unit.",
1063 }
1064}
1065
1066fn deque_push_back() -> MethodEntry {
1067 MethodEntry {
1068 receiver: deque_of_t(),
1069 name: "push_back",
1070 params: vec![TypePattern::var("T")],
1071 result: TypePattern::Unit,
1072 purity: Purity::Impure,
1073 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePushBack),
1074 doc: "Append a value to the back; returns Unit.",
1075 }
1076}
1077
1078fn deque_pop_front() -> MethodEntry {
1079 MethodEntry {
1080 receiver: deque_of_t(),
1081 name: "pop_front",
1082 params: vec![],
1083 result: TypePattern::var("T"),
1084 purity: Purity::Impure,
1085 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePopFront),
1086 doc: "Remove and return the front element; faults if empty.",
1087 }
1088}
1089
1090fn deque_pop_back() -> MethodEntry {
1091 MethodEntry {
1092 receiver: deque_of_t(),
1093 name: "pop_back",
1094 params: vec![],
1095 result: TypePattern::var("T"),
1096 purity: Purity::Impure,
1097 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePopBack),
1098 doc: "Remove and return the back element; faults if empty.",
1099 }
1100}
1101
1102fn deque_len() -> MethodEntry {
1103 MethodEntry {
1104 receiver: deque_of_t(),
1105 name: "len",
1106 params: vec![],
1107 result: TypePattern::Scalar(ScalarType::Int),
1108 purity: Purity::Pure,
1109 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeLen),
1110 doc: "Number of elements in the deque.",
1111 }
1112}
1113
1114fn deque_get() -> MethodEntry {
1115 MethodEntry {
1116 receiver: deque_of_t(),
1117 name: "get",
1118 params: vec![TypePattern::Scalar(ScalarType::Int)],
1119 result: TypePattern::var("T"),
1120 purity: Purity::Pure,
1121 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeGet),
1122 doc: "The element at `index` (0-based from the front); faults if out of range.",
1123 }
1124}
1125
1126fn deque_is_empty() -> MethodEntry {
1127 MethodEntry {
1128 receiver: deque_of_t(),
1129 name: "is_empty",
1130 params: vec![],
1131 result: TypePattern::Scalar(ScalarType::Bool),
1132 purity: Purity::Pure,
1133 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeIsEmpty),
1134 doc: "True iff the deque has no elements.",
1135 }
1136}
1137
1138fn map_of_k_v() -> TypePattern {
1142 TypePattern::Collection {
1143 ctor: CollectionCtor::Map,
1144 args: vec![TypePattern::var("K"), TypePattern::var("V")],
1145 }
1146}
1147
1148fn set_of_t() -> TypePattern {
1150 TypePattern::Collection {
1151 ctor: CollectionCtor::Set,
1152 args: vec![TypePattern::var("T")],
1153 }
1154}
1155
1156fn counter_of_t() -> TypePattern {
1158 TypePattern::Collection {
1159 ctor: CollectionCtor::Counter,
1160 args: vec![TypePattern::var("T")],
1161 }
1162}
1163
1164fn map_insert() -> MethodEntry {
1165 MethodEntry {
1166 receiver: map_of_k_v(),
1167 name: "insert",
1168 params: vec![TypePattern::var("K"), TypePattern::var("V")],
1169 result: TypePattern::Unit,
1170 purity: Purity::Impure,
1171 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapInsert),
1172 doc: "Set `key` to `value`, replacing any prior value; returns Unit.",
1173 }
1174}
1175
1176fn map_get() -> MethodEntry {
1177 MethodEntry {
1178 receiver: map_of_k_v(),
1179 name: "get",
1180 params: vec![TypePattern::var("K")],
1181 result: TypePattern::Option(Box::new(TypePattern::var("V"))),
1185 purity: Purity::Pure,
1186 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapGet),
1187 doc: "The value for `key` as `Some(value)`, or `None` if absent.",
1188 }
1189}
1190
1191fn map_contains() -> MethodEntry {
1192 MethodEntry {
1193 receiver: map_of_k_v(),
1194 name: "contains",
1195 params: vec![TypePattern::var("K")],
1196 result: TypePattern::Scalar(ScalarType::Bool),
1197 purity: Purity::Pure,
1198 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapContains),
1199 doc: "True iff `key` is present in the map.",
1200 }
1201}
1202
1203fn map_remove() -> MethodEntry {
1204 MethodEntry {
1205 receiver: map_of_k_v(),
1206 name: "remove",
1207 params: vec![TypePattern::var("K")],
1208 result: TypePattern::Unit,
1209 purity: Purity::Impure,
1210 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapRemove),
1211 doc: "Remove `key` if present; returns Unit.",
1212 }
1213}
1214
1215fn map_len() -> MethodEntry {
1216 MethodEntry {
1217 receiver: map_of_k_v(),
1218 name: "len",
1219 params: vec![],
1220 result: TypePattern::Scalar(ScalarType::Int),
1221 purity: Purity::Pure,
1222 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapLen),
1223 doc: "Number of entries in the map.",
1224 }
1225}
1226
1227fn map_is_empty() -> MethodEntry {
1228 MethodEntry {
1229 receiver: map_of_k_v(),
1230 name: "is_empty",
1231 params: vec![],
1232 result: TypePattern::Scalar(ScalarType::Bool),
1233 purity: Purity::Pure,
1234 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapIsEmpty),
1235 doc: "True iff the map has no entries.",
1236 }
1237}
1238
1239fn set_insert() -> MethodEntry {
1240 MethodEntry {
1241 receiver: set_of_t(),
1242 name: "insert",
1243 params: vec![TypePattern::var("T")],
1244 result: TypePattern::Unit,
1245 purity: Purity::Impure,
1246 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetInsert),
1247 doc: "Add `value` to the set; returns Unit.",
1248 }
1249}
1250
1251fn set_remove() -> MethodEntry {
1252 MethodEntry {
1253 receiver: set_of_t(),
1254 name: "remove",
1255 params: vec![TypePattern::var("T")],
1256 result: TypePattern::Unit,
1257 purity: Purity::Impure,
1258 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetRemove),
1259 doc: "Remove `value` if present; returns Unit.",
1260 }
1261}
1262
1263fn set_contains() -> MethodEntry {
1264 MethodEntry {
1265 receiver: set_of_t(),
1266 name: "contains",
1267 params: vec![TypePattern::var("T")],
1268 result: TypePattern::Scalar(ScalarType::Bool),
1269 purity: Purity::Pure,
1270 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetContains),
1271 doc: "True iff `value` is in the set.",
1272 }
1273}
1274
1275fn set_len() -> MethodEntry {
1276 MethodEntry {
1277 receiver: set_of_t(),
1278 name: "len",
1279 params: vec![],
1280 result: TypePattern::Scalar(ScalarType::Int),
1281 purity: Purity::Pure,
1282 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetLen),
1283 doc: "Number of elements in the set.",
1284 }
1285}
1286
1287fn set_is_empty() -> MethodEntry {
1288 MethodEntry {
1289 receiver: set_of_t(),
1290 name: "is_empty",
1291 params: vec![],
1292 result: TypePattern::Scalar(ScalarType::Bool),
1293 purity: Purity::Pure,
1294 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetIsEmpty),
1295 doc: "True iff the set has no elements.",
1296 }
1297}
1298
1299fn counter_get() -> MethodEntry {
1300 MethodEntry {
1301 receiver: counter_of_t(),
1302 name: "get",
1303 params: vec![TypePattern::var("T")],
1304 result: TypePattern::Scalar(ScalarType::Int),
1305 purity: Purity::Pure,
1306 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterGet),
1307 doc: "The count for `key`, or zero if absent (never faults).",
1308 }
1309}
1310
1311fn counter_inc() -> MethodEntry {
1312 MethodEntry {
1313 receiver: counter_of_t(),
1314 name: "inc",
1315 params: vec![TypePattern::var("T")],
1316 result: TypePattern::Unit,
1317 purity: Purity::Impure,
1318 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterInc),
1319 doc: "Increment the count for `key` by one; returns Unit.",
1320 }
1321}
1322
1323fn counter_len() -> MethodEntry {
1324 MethodEntry {
1325 receiver: counter_of_t(),
1326 name: "len",
1327 params: vec![],
1328 result: TypePattern::Scalar(ScalarType::Int),
1329 purity: Purity::Pure,
1330 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterLen),
1331 doc: "Number of distinct keys in the counter.",
1332 }
1333}
1334
1335fn counter_is_empty() -> MethodEntry {
1336 MethodEntry {
1337 receiver: counter_of_t(),
1338 name: "is_empty",
1339 params: vec![],
1340 result: TypePattern::Scalar(ScalarType::Bool),
1341 purity: Purity::Pure,
1342 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterIsEmpty),
1343 doc: "True iff the counter has no keys.",
1344 }
1345}
1346
1347fn min_heap_of_t() -> TypePattern {
1350 TypePattern::Collection {
1351 ctor: CollectionCtor::MinHeap,
1352 args: vec![TypePattern::var("T")],
1353 }
1354}
1355
1356fn max_heap_of_t() -> TypePattern {
1357 TypePattern::Collection {
1358 ctor: CollectionCtor::MaxHeap,
1359 args: vec![TypePattern::var("T")],
1360 }
1361}
1362
1363fn max_heap_push() -> MethodEntry {
1364 MethodEntry {
1365 receiver: max_heap_of_t(),
1366 name: "push",
1367 params: vec![TypePattern::var("T")],
1368 result: TypePattern::Unit,
1369 purity: Purity::Impure,
1370 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapPush),
1371 doc: "Push a value onto the max-heap; returns Unit.",
1372 }
1373}
1374
1375fn max_heap_pop() -> MethodEntry {
1376 MethodEntry {
1377 receiver: max_heap_of_t(),
1378 name: "pop",
1379 params: vec![],
1380 result: TypePattern::var("T"),
1381 purity: Purity::Impure,
1382 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapPop),
1383 doc: "Remove and return the largest element; faults if empty.",
1384 }
1385}
1386
1387fn max_heap_peek() -> MethodEntry {
1388 MethodEntry {
1389 receiver: max_heap_of_t(),
1390 name: "peek",
1391 params: vec![],
1392 result: TypePattern::var("T"),
1393 purity: Purity::Pure,
1394 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapPeek),
1395 doc: "The largest element without removing it; faults if empty.",
1396 }
1397}
1398
1399fn max_heap_len() -> MethodEntry {
1400 MethodEntry {
1401 receiver: max_heap_of_t(),
1402 name: "len",
1403 params: vec![],
1404 result: TypePattern::Scalar(ScalarType::Int),
1405 purity: Purity::Pure,
1406 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapLen),
1407 doc: "Number of elements in the max-heap.",
1408 }
1409}
1410
1411fn max_heap_is_empty() -> MethodEntry {
1412 MethodEntry {
1413 receiver: max_heap_of_t(),
1414 name: "is_empty",
1415 params: vec![],
1416 result: TypePattern::Scalar(ScalarType::Bool),
1417 purity: Purity::Pure,
1418 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapIsEmpty),
1419 doc: "True iff the max-heap has no elements.",
1420 }
1421}
1422
1423fn min_heap_push() -> MethodEntry {
1424 MethodEntry {
1425 receiver: min_heap_of_t(),
1426 name: "push",
1427 params: vec![TypePattern::var("T")],
1428 result: TypePattern::Unit,
1429 purity: Purity::Impure,
1430 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapPush),
1431 doc: "Push a value onto the min-heap; returns Unit.",
1432 }
1433}
1434
1435fn min_heap_pop() -> MethodEntry {
1436 MethodEntry {
1437 receiver: min_heap_of_t(),
1438 name: "pop",
1439 params: vec![],
1440 result: TypePattern::var("T"),
1441 purity: Purity::Impure,
1442 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapPop),
1443 doc: "Remove and return the smallest element; faults if empty.",
1444 }
1445}
1446
1447fn min_heap_peek() -> MethodEntry {
1448 MethodEntry {
1449 receiver: min_heap_of_t(),
1450 name: "peek",
1451 params: vec![],
1452 result: TypePattern::var("T"),
1453 purity: Purity::Pure,
1454 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapPeek),
1455 doc: "The smallest element without removing it; faults if empty.",
1456 }
1457}
1458
1459fn min_heap_len() -> MethodEntry {
1460 MethodEntry {
1461 receiver: min_heap_of_t(),
1462 name: "len",
1463 params: vec![],
1464 result: TypePattern::Scalar(ScalarType::Int),
1465 purity: Purity::Pure,
1466 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapLen),
1467 doc: "Number of elements in the min-heap.",
1468 }
1469}
1470
1471fn min_heap_is_empty() -> MethodEntry {
1472 MethodEntry {
1473 receiver: min_heap_of_t(),
1474 name: "is_empty",
1475 params: vec![],
1476 result: TypePattern::Scalar(ScalarType::Bool),
1477 purity: Purity::Pure,
1478 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapIsEmpty),
1479 doc: "True iff the min-heap has no elements.",
1480 }
1481}
1482
1483fn bitset_receiver() -> TypePattern {
1487 TypePattern::Collection {
1488 ctor: CollectionCtor::BitSet,
1489 args: vec![],
1490 }
1491}
1492
1493fn bitset_insert() -> MethodEntry {
1494 MethodEntry {
1495 receiver: bitset_receiver(),
1496 name: "insert",
1497 params: vec![TypePattern::Scalar(ScalarType::Int)],
1498 result: TypePattern::Unit,
1499 purity: Purity::Impure,
1500 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetInsert),
1501 doc: "Set the bit for a non-negative integer; returns Unit.",
1502 }
1503}
1504
1505fn bitset_remove() -> MethodEntry {
1506 MethodEntry {
1507 receiver: bitset_receiver(),
1508 name: "remove",
1509 params: vec![TypePattern::Scalar(ScalarType::Int)],
1510 result: TypePattern::Unit,
1511 purity: Purity::Impure,
1512 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetRemove),
1513 doc: "Clear the bit for an integer; returns Unit.",
1514 }
1515}
1516
1517fn bitset_contains() -> MethodEntry {
1518 MethodEntry {
1519 receiver: bitset_receiver(),
1520 name: "contains",
1521 params: vec![TypePattern::Scalar(ScalarType::Int)],
1522 result: TypePattern::Scalar(ScalarType::Bool),
1523 purity: Purity::Pure,
1524 lowering: MethodLowering::ScalarPrimitive(abi::RuntimeSymbol::BitsetContains),
1528 doc: "True iff the bit for the integer is set.",
1529 }
1530}
1531
1532fn bitset_len() -> MethodEntry {
1533 MethodEntry {
1534 receiver: bitset_receiver(),
1535 name: "len",
1536 params: vec![],
1537 result: TypePattern::Scalar(ScalarType::Int),
1538 purity: Purity::Pure,
1539 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetLen),
1540 doc: "Number of set bits (popcount).",
1541 }
1542}
1543
1544fn bitset_is_empty() -> MethodEntry {
1545 MethodEntry {
1546 receiver: bitset_receiver(),
1547 name: "is_empty",
1548 params: vec![],
1549 result: TypePattern::Scalar(ScalarType::Bool),
1550 purity: Purity::Pure,
1551 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetIsEmpty),
1552 doc: "True iff no bits are set.",
1553 }
1554}
1555
1556fn grid_of_t() -> TypePattern {
1560 TypePattern::Collection {
1561 ctor: CollectionCtor::Grid,
1562 args: vec![TypePattern::var("T")],
1563 }
1564}
1565
1566fn point_pattern() -> TypePattern {
1568 TypePattern::Tuple(vec![
1569 TypePattern::Scalar(ScalarType::Int),
1570 TypePattern::Scalar(ScalarType::Int),
1571 ])
1572}
1573
1574fn grid_width() -> MethodEntry {
1575 MethodEntry {
1576 receiver: grid_of_t(),
1577 name: "width",
1578 params: vec![],
1579 result: TypePattern::Scalar(ScalarType::Int),
1580 purity: Purity::Pure,
1581 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridWidth),
1582 doc: "The number of columns.",
1583 }
1584}
1585
1586fn grid_height() -> MethodEntry {
1587 MethodEntry {
1588 receiver: grid_of_t(),
1589 name: "height",
1590 params: vec![],
1591 result: TypePattern::Scalar(ScalarType::Int),
1592 purity: Purity::Pure,
1593 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridHeight),
1594 doc: "The number of rows.",
1595 }
1596}
1597
1598fn grid_get() -> MethodEntry {
1599 MethodEntry {
1600 receiver: grid_of_t(),
1601 name: "get",
1602 params: vec![
1603 TypePattern::Scalar(ScalarType::Int),
1604 TypePattern::Scalar(ScalarType::Int),
1605 ],
1606 result: TypePattern::var("T"),
1607 purity: Purity::Pure,
1608 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridGet),
1609 doc: "The cell at (x, y); faults if out of range.",
1610 }
1611}
1612
1613fn grid_set() -> MethodEntry {
1614 MethodEntry {
1615 receiver: grid_of_t(),
1616 name: "set",
1617 params: vec![
1618 TypePattern::Scalar(ScalarType::Int),
1619 TypePattern::Scalar(ScalarType::Int),
1620 TypePattern::var("T"),
1621 ],
1622 result: TypePattern::Unit,
1623 purity: Purity::Impure,
1624 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridSet),
1625 doc: "Set the cell at (x, y); faults if out of range.",
1626 }
1627}
1628
1629fn grid_contains() -> MethodEntry {
1630 MethodEntry {
1631 receiver: grid_of_t(),
1632 name: "contains",
1633 params: vec![
1634 TypePattern::Scalar(ScalarType::Int),
1635 TypePattern::Scalar(ScalarType::Int),
1636 ],
1637 result: TypePattern::Scalar(ScalarType::Bool),
1638 purity: Purity::Pure,
1639 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridContains),
1640 doc: "True iff (x, y) is within the grid.",
1641 }
1642}
1643
1644fn grid_neighbors4() -> MethodEntry {
1645 MethodEntry {
1646 receiver: grid_of_t(),
1647 name: "neighbors4",
1648 params: vec![point_pattern()],
1649 result: TypePattern::Collection {
1650 ctor: CollectionCtor::Vec,
1651 args: vec![point_pattern()],
1652 },
1653 purity: Purity::Pure,
1654 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridNeighbors4),
1655 doc: "The 4 orthogonal in-bounds neighbors of a point, as a Vec of (x, y).",
1656 }
1657}
1658
1659fn grid_neighbors8() -> MethodEntry {
1660 MethodEntry {
1661 receiver: grid_of_t(),
1662 name: "neighbors8",
1663 params: vec![point_pattern()],
1664 result: TypePattern::Collection {
1665 ctor: CollectionCtor::Vec,
1666 args: vec![point_pattern()],
1667 },
1668 purity: Purity::Pure,
1669 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridNeighbors8),
1670 doc: "The 8 in-bounds neighbors of a point, as a Vec of (x, y).",
1671 }
1672}
1673
1674fn grid_positions() -> MethodEntry {
1675 MethodEntry {
1676 receiver: grid_of_t(),
1677 name: "positions",
1678 params: vec![],
1679 result: TypePattern::Collection {
1680 ctor: CollectionCtor::Vec,
1681 args: vec![point_pattern()],
1682 },
1683 purity: Purity::Pure,
1684 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridPositions),
1685 doc: "All (x, y) positions in row-major order, as a Vec.",
1686 }
1687}
1688
1689fn grid_cells() -> MethodEntry {
1690 MethodEntry {
1691 receiver: grid_of_t(),
1692 name: "cells",
1693 params: vec![],
1694 result: TypePattern::Collection {
1695 ctor: CollectionCtor::Vec,
1696 args: vec![TypePattern::var("T")],
1697 },
1698 purity: Purity::Pure,
1699 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCells),
1700 doc: "All cells in row-major order, as a Vec.",
1701 }
1702}
1703
1704fn grid_row() -> MethodEntry {
1705 MethodEntry {
1706 receiver: grid_of_t(),
1707 name: "row",
1708 params: vec![TypePattern::Scalar(ScalarType::Int)],
1709 result: TypePattern::Collection {
1710 ctor: CollectionCtor::Vec,
1711 args: vec![TypePattern::var("T")],
1712 },
1713 purity: Purity::Pure,
1714 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridRow),
1715 doc: "Row `y` as a Vec; faults if out of range.",
1716 }
1717}
1718
1719fn grid_column() -> MethodEntry {
1720 MethodEntry {
1721 receiver: grid_of_t(),
1722 name: "column",
1723 params: vec![TypePattern::Scalar(ScalarType::Int)],
1724 result: TypePattern::Collection {
1725 ctor: CollectionCtor::Vec,
1726 args: vec![TypePattern::var("T")],
1727 },
1728 purity: Purity::Pure,
1729 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridColumn),
1730 doc: "Column `x` as a Vec; faults if out of range.",
1731 }
1732}
1733
1734fn grid_find() -> MethodEntry {
1735 MethodEntry {
1736 receiver: grid_of_t(),
1737 name: "find",
1738 params: vec![TypePattern::var("T")],
1739 result: TypePattern::Option(Box::new(point_pattern())),
1743 purity: Purity::Pure,
1744 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridFind),
1745 doc: "The first (x, y) whose cell equals `value` as `Some((x, y))`, or `None`.",
1746 }
1747}
1748
1749fn grid_find_all() -> MethodEntry {
1750 MethodEntry {
1751 receiver: grid_of_t(),
1752 name: "find_all",
1753 params: vec![TypePattern::var("T")],
1754 result: TypePattern::Collection {
1755 ctor: CollectionCtor::Vec,
1756 args: vec![point_pattern()],
1757 },
1758 purity: Purity::Pure,
1759 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridFindAll),
1760 doc: "All (x, y) positions whose cell equals `value`, as a Vec.",
1761 }
1762}
1763
1764fn grid_transpose() -> MethodEntry {
1765 MethodEntry {
1766 receiver: grid_of_t(),
1767 name: "transpose",
1768 params: vec![],
1769 result: grid_of_t(),
1770 purity: Purity::Pure,
1771 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridTranspose),
1772 doc: "A transposed copy (rows ↔ columns).",
1773 }
1774}
1775
1776fn grid_rotate_left() -> MethodEntry {
1777 MethodEntry {
1778 receiver: grid_of_t(),
1779 name: "rotate_left",
1780 params: vec![],
1781 result: grid_of_t(),
1782 purity: Purity::Pure,
1783 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridRotateLeft),
1784 doc: "A copy rotated 90° counter-clockwise.",
1785 }
1786}
1787
1788fn grid_rotate_right() -> MethodEntry {
1789 MethodEntry {
1790 receiver: grid_of_t(),
1791 name: "rotate_right",
1792 params: vec![],
1793 result: grid_of_t(),
1794 purity: Purity::Pure,
1795 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridRotateRight),
1796 doc: "A copy rotated 90° clockwise.",
1797 }
1798}
1799
1800fn iterable_of_t() -> TypePattern {
1820 TypePattern::iterable(TypePattern::var("T"))
1821}
1822
1823fn t_to_u() -> TypePattern {
1825 TypePattern::Function {
1826 params: vec![TypePattern::var("T")],
1827 result: Box::new(TypePattern::var("U")),
1828 }
1829}
1830
1831fn t_to_bool() -> TypePattern {
1833 TypePattern::Function {
1834 params: vec![TypePattern::var("T")],
1835 result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
1836 }
1837}
1838
1839fn t_to_option_u() -> TypePattern {
1846 TypePattern::Function {
1847 params: vec![TypePattern::var("T")],
1848 result: Box::new(TypePattern::Option(Box::new(TypePattern::var("U")))),
1849 }
1850}
1851
1852fn acc_t_to_acc() -> TypePattern {
1854 TypePattern::Function {
1855 params: vec![TypePattern::var("Acc"), TypePattern::var("T")],
1856 result: Box::new(TypePattern::var("Acc")),
1857 }
1858}
1859
1860fn t_t_to_t() -> TypePattern {
1869 TypePattern::Function {
1870 params: vec![TypePattern::var("T"), TypePattern::var("T")],
1871 result: Box::new(TypePattern::var("T")),
1872 }
1873}
1874
1875fn seq_map() -> MethodEntry {
1876 MethodEntry {
1877 receiver: iterable_of_t(),
1878 name: "map",
1879 params: vec![t_to_u()],
1880 result: vec_of_u(),
1881 purity: Purity::Pure,
1882 lowering: MethodLowering::Intrinsic("seq_map"),
1883 doc: "Apply a function to each element, collecting into a Vec.",
1884 }
1885}
1886
1887fn vec_of_u() -> TypePattern {
1890 TypePattern::Collection {
1891 ctor: CollectionCtor::Vec,
1892 args: vec![TypePattern::var("U")],
1893 }
1894}
1895
1896fn seq_filter() -> MethodEntry {
1897 MethodEntry {
1898 receiver: iterable_of_t(),
1899 name: "filter",
1900 params: vec![t_to_bool()],
1901 result: vec_of_t(),
1902 purity: Purity::Pure,
1903 lowering: MethodLowering::Intrinsic("seq_filter"),
1904 doc: "Keep elements satisfying a predicate, collecting into a Vec.",
1905 }
1906}
1907
1908fn seq_fold() -> MethodEntry {
1909 MethodEntry {
1910 receiver: iterable_of_t(),
1911 name: "fold",
1912 params: vec![TypePattern::var("Acc"), acc_t_to_acc()],
1913 result: TypePattern::var("Acc"),
1914 purity: Purity::Pure,
1915 lowering: MethodLowering::Intrinsic("seq_fold"),
1916 doc: "Reduce elements left-to-right with an accumulator and combining closure.",
1917 }
1918}
1919
1920fn seq_sum() -> MethodEntry {
1921 MethodEntry {
1922 receiver: iterable_of_int_elem(),
1923 name: "sum",
1924 params: vec![],
1925 result: TypePattern::Scalar(ScalarType::Int),
1926 purity: Purity::Pure,
1927 lowering: MethodLowering::Intrinsic("seq_sum"),
1928 doc: "Sum the (Int) elements.",
1929 }
1930}
1931
1932fn seq_count() -> MethodEntry {
1933 MethodEntry {
1934 receiver: iterable_of_t(),
1935 name: "count",
1936 params: vec![],
1937 result: TypePattern::Scalar(ScalarType::Int),
1938 purity: Purity::Pure,
1939 lowering: MethodLowering::Intrinsic("seq_count"),
1940 doc: "Number of elements.",
1941 }
1942}
1943
1944fn seq_count_if() -> MethodEntry {
1950 MethodEntry {
1951 receiver: iterable_of_t(),
1952 name: "count",
1953 params: vec![t_to_bool()],
1954 result: TypePattern::Scalar(ScalarType::Int),
1955 purity: Purity::Pure,
1956 lowering: MethodLowering::Intrinsic("seq_count"),
1957 doc: "Number of elements satisfying the predicate.",
1958 }
1959}
1960
1961fn seq_sorted() -> MethodEntry {
2029 MethodEntry {
2030 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::Ord)),
2031 name: "sorted",
2032 params: vec![],
2033 result: vec_of_t(),
2034 purity: Purity::Pure,
2035 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecSorted),
2036 doc: "A new Vec holding these elements in ascending order.",
2037 }
2038}
2039
2040fn seq_unique() -> MethodEntry {
2047 MethodEntry {
2048 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::HashStable)),
2049 name: "unique",
2050 params: vec![],
2051 result: vec_of_t(),
2052 purity: Purity::Pure,
2053 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecUnique),
2054 doc: "A new Vec with duplicate elements removed, keeping first occurrences.",
2055 }
2056}
2057
2058fn seq_reversed() -> MethodEntry {
2070 MethodEntry {
2071 receiver: iterable_of_t(),
2072 name: "reversed",
2073 params: vec![],
2074 result: vec_of_t(),
2075 purity: Purity::Pure,
2076 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecReversed),
2077 doc: "A new Vec holding these elements in reverse order.",
2078 }
2079}
2080
2081fn vec_of_vec_of_t() -> TypePattern {
2088 TypePattern::Collection {
2089 ctor: CollectionCtor::Vec,
2090 args: vec![vec_of_t()],
2091 }
2092}
2093
2094fn seq_chunks() -> MethodEntry {
2114 MethodEntry {
2115 receiver: iterable_of_t(),
2116 name: "chunks",
2117 params: vec![TypePattern::Scalar(ScalarType::Int)],
2118 result: vec_of_vec_of_t(),
2119 purity: Purity::Pure,
2120 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecChunks),
2121 doc: "Consecutive non-overlapping runs of n; the last may be shorter. \
2122 Faults if n is not positive.",
2123 }
2124}
2125
2126fn seq_windows() -> MethodEntry {
2140 MethodEntry {
2141 receiver: iterable_of_t(),
2142 name: "windows",
2143 params: vec![TypePattern::Scalar(ScalarType::Int)],
2144 result: vec_of_vec_of_t(),
2145 purity: Purity::Pure,
2146 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecWindows),
2147 doc: "Every consecutive run of exactly n, sliding by one. Empty if n \
2148 exceeds the length; faults if n is not positive.",
2149 }
2150}
2151
2152fn iterable_of_text_elem() -> TypePattern {
2157 TypePattern::iterable(TypePattern::is_scalar("T", ScalarType::Text))
2158}
2159
2160fn seq_join() -> MethodEntry {
2173 MethodEntry {
2174 receiver: iterable_of_text_elem(),
2175 name: "join",
2176 params: vec![TypePattern::Scalar(ScalarType::Text)],
2177 result: TypePattern::Scalar(ScalarType::Text),
2178 purity: Purity::Pure,
2179 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecJoin),
2180 doc: "These Text items concatenated with `sep` between them.",
2181 }
2182}
2183
2184fn seq_frequencies() -> MethodEntry {
2193 MethodEntry {
2194 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::HashStable)),
2195 name: "frequencies",
2196 params: vec![],
2197 result: counter_of_t(),
2198 purity: Purity::Pure,
2199 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecFrequencies),
2200 doc: "A Counter holding how many times each element occurs.",
2201 }
2202}
2203
2204fn t_t_to_bool() -> TypePattern {
2211 TypePattern::Function {
2212 params: vec![TypePattern::var("T"), TypePattern::var("T")],
2213 result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
2214 }
2215}
2216
2217fn t_to_vec_u() -> TypePattern {
2219 TypePattern::Function {
2220 params: vec![TypePattern::var("T")],
2221 result: Box::new(vec_of_u()),
2222 }
2223}
2224
2225fn seq_take() -> MethodEntry {
2228 MethodEntry {
2229 receiver: iterable_of_t(),
2230 name: "take",
2231 params: vec![TypePattern::Scalar(ScalarType::Int)],
2232 result: vec_of_t(),
2233 purity: Purity::Pure,
2234 lowering: MethodLowering::Intrinsic("seq_take"),
2235 doc: "Keep at most the first n elements.",
2236 }
2237}
2238
2239fn seq_skip() -> MethodEntry {
2240 MethodEntry {
2241 receiver: iterable_of_t(),
2242 name: "skip",
2243 params: vec![TypePattern::Scalar(ScalarType::Int)],
2244 result: vec_of_t(),
2245 purity: Purity::Pure,
2246 lowering: MethodLowering::Intrinsic("seq_skip"),
2247 doc: "Drop the first n elements.",
2248 }
2249}
2250
2251fn seq_take_while() -> MethodEntry {
2252 MethodEntry {
2253 receiver: iterable_of_t(),
2254 name: "take_while",
2255 params: vec![t_to_bool()],
2256 result: vec_of_t(),
2257 purity: Purity::Pure,
2258 lowering: MethodLowering::Intrinsic("seq_take_while"),
2259 doc: "Keep elements until the predicate is false.",
2260 }
2261}
2262
2263fn vec_of_index_and_t() -> TypePattern {
2265 TypePattern::Collection {
2266 ctor: CollectionCtor::Vec,
2267 args: vec![TypePattern::Tuple(vec![
2268 TypePattern::Scalar(ScalarType::Int),
2269 TypePattern::var("T"),
2270 ])],
2271 }
2272}
2273
2274fn vec_of_t_and_u() -> TypePattern {
2277 TypePattern::Collection {
2278 ctor: CollectionCtor::Vec,
2279 args: vec![TypePattern::Tuple(vec![
2280 TypePattern::var("T"),
2281 TypePattern::var("U"),
2282 ])],
2283 }
2284}
2285
2286fn seq_enumerate() -> MethodEntry {
2287 MethodEntry {
2288 receiver: iterable_of_t(),
2289 name: "enumerate",
2290 params: vec![],
2291 result: vec_of_index_and_t(),
2292 purity: Purity::Pure,
2293 lowering: MethodLowering::Intrinsic("seq_enumerate"),
2294 doc: "Pair each element with its index.",
2295 }
2296}
2297
2298fn seq_zip() -> MethodEntry {
2299 MethodEntry {
2300 receiver: iterable_of_t(),
2301 name: "zip",
2302 params: vec![vec_of_u()],
2303 result: vec_of_t_and_u(),
2304 purity: Purity::Pure,
2305 lowering: MethodLowering::Intrinsic("seq_zip"),
2306 doc: "Pair elements with another sequence, stopping at the shorter length.",
2307 }
2308}
2309
2310fn seq_flat_map() -> MethodEntry {
2311 MethodEntry {
2312 receiver: iterable_of_t(),
2313 name: "flat_map",
2314 params: vec![t_to_vec_u()],
2315 result: vec_of_u(),
2316 purity: Purity::Pure,
2317 lowering: MethodLowering::Intrinsic("seq_flat_map"),
2318 doc: "Map each element to a Vec and concatenate the results.",
2319 }
2320}
2321
2322fn seq_filter_map() -> MethodEntry {
2323 MethodEntry {
2324 receiver: iterable_of_t(),
2325 name: "filter_map",
2326 params: vec![t_to_option_u()],
2327 result: vec_of_u(),
2328 purity: Purity::Pure,
2329 lowering: MethodLowering::Intrinsic("seq_filter_map"),
2330 doc: "Map each element to an Option and keep the Some payloads.",
2331 }
2332}
2333
2334fn iterable_of_int_elem() -> TypePattern {
2354 TypePattern::iterable(TypePattern::is_scalar("T", ScalarType::Int))
2355}
2356
2357fn seq_product() -> MethodEntry {
2358 MethodEntry {
2359 receiver: iterable_of_int_elem(),
2360 name: "product",
2361 params: vec![],
2362 result: TypePattern::Scalar(ScalarType::Int),
2363 purity: Purity::Pure,
2364 lowering: MethodLowering::Intrinsic("seq_product"),
2365 doc: "Multiply the (Int) elements.",
2366 }
2367}
2368
2369fn seq_min() -> MethodEntry {
2370 MethodEntry {
2371 receiver: iterable_of_int_elem(),
2372 name: "min",
2373 params: vec![],
2374 result: TypePattern::Scalar(ScalarType::Int),
2375 purity: Purity::Pure,
2376 lowering: MethodLowering::Intrinsic("seq_min"),
2377 doc: "Smallest (Int) element. Faults on an empty sequence (D1).",
2378 }
2379}
2380
2381fn seq_max() -> MethodEntry {
2382 MethodEntry {
2383 receiver: iterable_of_int_elem(),
2384 name: "max",
2385 params: vec![],
2386 result: TypePattern::Scalar(ScalarType::Int),
2387 purity: Purity::Pure,
2388 lowering: MethodLowering::Intrinsic("seq_max"),
2389 doc: "Largest (Int) element. Faults on an empty sequence (D1).",
2390 }
2391}
2392
2393fn seq_min_by() -> MethodEntry {
2394 MethodEntry {
2395 receiver: iterable_of_t(),
2396 name: "min_by",
2397 params: vec![t_t_to_bool()],
2398 result: TypePattern::var("T"),
2399 purity: Purity::Pure,
2400 lowering: MethodLowering::Intrinsic("seq_min_by"),
2401 doc: "Smallest element per a (T,T)->Bool \"less-than\" comparator.",
2402 }
2403}
2404
2405fn seq_max_by() -> MethodEntry {
2406 MethodEntry {
2407 receiver: iterable_of_t(),
2408 name: "max_by",
2409 params: vec![t_t_to_bool()],
2410 result: TypePattern::var("T"),
2411 purity: Purity::Pure,
2412 lowering: MethodLowering::Intrinsic("seq_max_by"),
2413 doc: "Largest element per a (T,T)->Bool \"less-than\" comparator.",
2414 }
2415}
2416
2417fn seq_any() -> MethodEntry {
2418 MethodEntry {
2419 receiver: iterable_of_t(),
2420 name: "any",
2421 params: vec![t_to_bool()],
2422 result: TypePattern::Scalar(ScalarType::Bool),
2423 purity: Purity::Pure,
2424 lowering: MethodLowering::Intrinsic("seq_any"),
2425 doc: "True if any element satisfies the predicate (short-circuits).",
2426 }
2427}
2428
2429fn seq_all() -> MethodEntry {
2430 MethodEntry {
2431 receiver: iterable_of_t(),
2432 name: "all",
2433 params: vec![t_to_bool()],
2434 result: TypePattern::Scalar(ScalarType::Bool),
2435 purity: Purity::Pure,
2436 lowering: MethodLowering::Intrinsic("seq_all"),
2437 doc: "True if all elements satisfy the predicate (short-circuits).",
2438 }
2439}
2440
2441fn seq_find() -> MethodEntry {
2442 MethodEntry {
2443 receiver: iterable_of_t(),
2444 name: "find",
2445 params: vec![t_to_bool()],
2446 result: TypePattern::Option(Box::new(TypePattern::var("T"))),
2447 purity: Purity::Pure,
2448 lowering: MethodLowering::Intrinsic("seq_find"),
2449 doc: "The first matching element, or None.",
2450 }
2451}
2452
2453fn seq_position() -> MethodEntry {
2454 MethodEntry {
2455 receiver: iterable_of_t(),
2456 name: "position",
2457 params: vec![t_to_bool()],
2458 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
2459 purity: Purity::Pure,
2460 lowering: MethodLowering::Intrinsic("seq_position"),
2461 doc: "The index of the first matching element, or None.",
2462 }
2463}
2464
2465fn seq_reduce() -> MethodEntry {
2466 MethodEntry {
2467 receiver: iterable_of_t(),
2468 name: "reduce",
2469 params: vec![t_t_to_t()],
2470 result: TypePattern::var("T"),
2471 purity: Purity::Pure,
2472 lowering: MethodLowering::Intrinsic("seq_reduce"),
2473 doc: "Reduce left-to-right, seeded with the first element.",
2474 }
2475}
2476
2477fn seq_sorted_by_key() -> MethodEntry {
2493 MethodEntry {
2494 receiver: iterable_of_t(),
2495 name: "sorted_by_key",
2496 params: vec![TypePattern::Function {
2497 params: vec![TypePattern::var("T")],
2498 result: Box::new(TypePattern::of_kind("K", crate::CapKind::Ord)),
2499 }],
2500 result: vec_of_t(),
2501 purity: Purity::Pure,
2502 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecSortedByKey),
2503 doc: "A new Vec ordered by the key the closure extracts.",
2504 }
2505}
2506
2507fn seq_to_vec() -> MethodEntry {
2544 MethodEntry {
2545 receiver: iterable_of_t(),
2546 name: "to_vec",
2547 params: vec![],
2548 result: vec_of_t(),
2549 purity: Purity::Pure,
2550 lowering: MethodLowering::Intrinsic("seq_to_vec"),
2551 doc: "The items as a Vec. On a Vec receiver this is the receiver itself.",
2552 }
2553}
2554
2555fn seq_to_set() -> MethodEntry {
2567 MethodEntry {
2568 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::HashStable)),
2569 name: "to_set",
2570 params: vec![],
2571 result: set_of_t(),
2572 purity: Purity::Pure,
2573 lowering: MethodLowering::Intrinsic("seq_to_set"),
2574 doc: "A Set holding these items, duplicates dropped.",
2575 }
2576}
2577
2578fn seq_to_map() -> MethodEntry {
2587 MethodEntry {
2588 receiver: TypePattern::iterable(TypePattern::Tuple(vec![
2589 TypePattern::of_kind("K", crate::CapKind::HashStable),
2590 TypePattern::var("V"),
2591 ])),
2592 name: "to_map",
2593 params: vec![],
2594 result: map_of_k_v(),
2595 purity: Purity::Pure,
2596 lowering: MethodLowering::Intrinsic("seq_to_map"),
2597 doc: "A Map built from (key, value) pairs. Duplicate keys: last wins.",
2598 }
2599}
2600
2601fn seq_to_counter() -> MethodEntry {
2609 MethodEntry {
2610 receiver: TypePattern::iterable(TypePattern::Tuple(vec![
2611 TypePattern::of_kind("T", crate::CapKind::HashStable),
2612 TypePattern::Scalar(ScalarType::Int),
2613 ])),
2614 name: "to_counter",
2615 params: vec![],
2616 result: counter_of_t(),
2617 purity: Purity::Pure,
2618 lowering: MethodLowering::Intrinsic("seq_to_counter"),
2619 doc: "A Counter built from (key, count) pairs. Duplicate keys: last wins.",
2620 }
2621}
2622
2623fn seq_to_deque() -> MethodEntry {
2624 MethodEntry {
2625 receiver: iterable_of_t(),
2626 name: "to_deque",
2627 params: vec![],
2628 result: deque_of_t(),
2629 purity: Purity::Pure,
2630 lowering: MethodLowering::Intrinsic("seq_to_deque"),
2631 doc: "A Deque holding these items, in order.",
2632 }
2633}
2634
2635fn seq_to_min_heap() -> MethodEntry {
2640 MethodEntry {
2641 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::Ord)),
2642 name: "to_min_heap",
2643 params: vec![],
2644 result: min_heap_of_t(),
2645 purity: Purity::Pure,
2646 lowering: MethodLowering::Intrinsic("seq_to_min_heap"),
2647 doc: "A MinHeap holding these items.",
2648 }
2649}
2650
2651fn seq_to_max_heap() -> MethodEntry {
2652 MethodEntry {
2653 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::Ord)),
2654 name: "to_max_heap",
2655 params: vec![],
2656 result: max_heap_of_t(),
2657 purity: Purity::Pure,
2658 lowering: MethodLowering::Intrinsic("seq_to_max_heap"),
2659 doc: "A MaxHeap holding these items.",
2660 }
2661}
2662
2663fn seq_to_bitset() -> MethodEntry {
2669 MethodEntry {
2670 receiver: iterable_of_int_elem(),
2671 name: "to_bitset",
2672 params: vec![],
2673 result: bitset_receiver(),
2674 purity: Purity::Pure,
2675 lowering: MethodLowering::Intrinsic("seq_to_bitset"),
2676 doc: "A BitSet holding these (Int) items. Faults on a negative or oversized member.",
2677 }
2678}
2679
2680#[cfg(test)]
2681mod tests {
2682 use super::*;
2683
2684 #[test]
2693 fn every_catalog_row_documents_itself() {
2694 for e in builtin_catalog().entries() {
2695 let what = format!("{}.{}/{}", e.receiver, e.name, e.arity());
2696 assert!(e.doc.len() > 8, "{what} has no real documentation");
2697 assert!(
2698 e.doc.ends_with('.') || e.doc.ends_with(')'),
2699 "{what}'s doc is not a sentence: {:?}",
2700 e.doc
2701 );
2702 assert!(
2706 e.doc
2707 .chars()
2708 .next()
2709 .is_some_and(|c| c.is_uppercase() || c == '`'),
2710 "{what}'s doc does not open a sentence: {:?}",
2711 e.doc
2712 );
2713 }
2714 }
2715
2716 #[test]
2717 fn builtin_catalog_has_vec_methods() {
2718 let cat = builtin_catalog();
2719 assert!(cat.len() >= 4);
2720 let vec_pat = vec_of_t();
2721 let push_hits: Vec<_> = cat.by_receiver_and_name(&vec_pat, "push").collect();
2722 assert_eq!(push_hits.len(), 1);
2723 assert_eq!(
2724 push_hits[0].lowering,
2725 MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecPush)
2726 );
2727 }
2728
2729 #[test]
2730 fn builtin_catalog_get_can_fault() {
2731 let cat = builtin_catalog();
2732 let vec_pat = vec_of_t();
2733 let get = cat
2734 .by_receiver_and_name(&vec_pat, "get")
2735 .next()
2736 .expect("vec.get exists");
2737 assert!(get.can_fault());
2738 let bitset_pat = bitset_receiver();
2742 let insert = cat
2743 .by_receiver_and_name(&bitset_pat, "insert")
2744 .next()
2745 .expect("bitset.insert exists");
2746 assert!(insert.can_fault());
2747 }
2748
2749 #[test]
2756 fn a_keyed_collection_enumerates_and_count_has_two_arities() {
2757 let cat = builtin_catalog();
2758 let map_pat = map_of_k_v();
2759 let counter_pat = counter_of_t();
2760
2761 for (pat, what) in [(map_pat.clone(), "Map"), (counter_pat.clone(), "Counter")] {
2763 for name in ["keys", "values"] {
2764 let hits: Vec<_> = cat.by_receiver_and_name(&pat, name).collect();
2765 assert_eq!(hits.len(), 1, "{what}.{name}()");
2766 assert_eq!(hits[0].arity(), 0, "{what}.{name}() takes no arguments");
2767 assert!(
2768 matches!(
2769 hits[0].result,
2770 TypePattern::Collection {
2771 ctor: CollectionCtor::Vec,
2772 ..
2773 }
2774 ),
2775 "{what}.{name}() answers a Vec so every §6.3 combinator applies"
2776 );
2777 }
2778 }
2779
2780 let counter_values = cat
2784 .by_receiver_and_name(&counter_pat, "values")
2785 .next()
2786 .expect("Counter.values");
2787 assert_eq!(
2788 counter_values.result,
2789 TypePattern::Collection {
2790 ctor: CollectionCtor::Vec,
2791 args: vec![TypePattern::Scalar(ScalarType::Int)]
2792 }
2793 );
2794 let map_values = cat
2795 .by_receiver_and_name(&map_pat, "values")
2796 .next()
2797 .expect("Map.values");
2798 assert_eq!(
2799 map_values.result,
2800 TypePattern::Collection {
2801 ctor: CollectionCtor::Vec,
2802 args: vec![TypePattern::var("V")]
2803 }
2804 );
2805 let map_keys = cat
2807 .by_receiver_and_name(&map_pat, "keys")
2808 .next()
2809 .expect("Map.keys");
2810 assert_eq!(
2811 map_keys.result,
2812 TypePattern::Collection {
2813 ctor: CollectionCtor::Vec,
2814 args: vec![TypePattern::var("K")]
2815 }
2816 );
2817
2818 let arities: Vec<usize> = cat
2821 .by_receiver_and_name(&iterable_of_t(), "count")
2822 .map(|e| e.arity())
2823 .collect();
2824 assert_eq!(arities.len(), 2, "count has two rows");
2825 assert!(arities.contains(&0) && arities.contains(&1));
2826 }
2827
2828 #[test]
2835 fn every_pipeline_combinator_is_one_row_on_the_generic_receiver() {
2836 let cat = builtin_catalog();
2837 let combinators = [
2845 "map",
2846 "filter",
2847 "filter_map",
2848 "flat_map",
2849 "take",
2850 "skip",
2851 "take_while",
2852 "enumerate",
2853 "zip",
2854 "fold",
2855 "reduce",
2856 "sum",
2857 "product",
2858 "count",
2859 "any",
2860 "all",
2861 "find",
2862 "position",
2863 "min",
2864 "max",
2865 "min_by",
2866 "max_by",
2867 "sorted",
2868 "sorted_by_key",
2869 "unique",
2870 "reversed",
2871 "frequencies",
2872 "join",
2873 "to_vec",
2874 "to_set",
2875 "to_map",
2876 "to_counter",
2877 "to_deque",
2878 "to_min_heap",
2879 "to_max_heap",
2880 "to_bitset",
2881 ];
2882 for name in combinators {
2883 let rows: Vec<_> = cat.entries().iter().filter(|e| e.name == name).collect();
2884 assert!(!rows.is_empty(), "`{name}` has no row at all");
2885 let mut generic: Vec<usize> = rows
2889 .iter()
2890 .filter(|e| matches!(e.receiver, TypePattern::Iterable { .. }))
2891 .map(|e| e.arity())
2892 .collect();
2893 assert!(!generic.is_empty(), "`{name}` has no generic row");
2894 let seen = generic.len();
2895 generic.sort_unstable();
2896 generic.dedup();
2897 assert_eq!(
2898 generic.len(),
2899 seen,
2900 "`{name}` has two generic rows at one arity — one receiver \
2901 getting a feature ten should have"
2902 );
2903 for row in rows {
2910 assert!(
2911 matches!(row.receiver, TypePattern::Iterable { .. })
2912 || !crate::is_pipeline_receiver(&row.receiver),
2913 "`{name}` also has a row on {}, which the generic row \
2914 accepts — both would match, and which one a call resolves \
2915 to would be insertion order",
2916 row.receiver
2917 );
2918 }
2919 }
2920
2921 assert!(
2926 !crate::PIPELINE_RECEIVERS.contains(&CollectionCtor::Grid),
2927 "a generic `map` would claim §6.4's name and answer a `Vec`"
2928 );
2929
2930 for e in cat.entries() {
2932 assert!(
2933 !matches!(
2934 e.receiver,
2935 TypePattern::Collection {
2936 ctor: CollectionCtor::Seq,
2937 ..
2938 }
2939 ),
2940 "`{}` is still on a `Seq`, which has no values",
2941 e.name
2942 );
2943 }
2944 }
2945
2946 #[test]
2955 fn a_conversion_exists_for_every_collection_that_can_be_constructed() {
2956 let cat = builtin_catalog();
2957 for (name, ctor) in [
2958 ("to_vec", CollectionCtor::Vec),
2959 ("to_set", CollectionCtor::Set),
2960 ("to_map", CollectionCtor::Map),
2961 ("to_counter", CollectionCtor::Counter),
2962 ("to_deque", CollectionCtor::Deque),
2963 ("to_min_heap", CollectionCtor::MinHeap),
2964 ("to_max_heap", CollectionCtor::MaxHeap),
2965 ("to_bitset", CollectionCtor::BitSet),
2966 ] {
2967 let row = cat
2968 .entries()
2969 .iter()
2970 .find(|e| e.name == name)
2971 .unwrap_or_else(|| panic!("`{name}` has no row"));
2972 assert_eq!(row.arity(), 0, "`{name}` takes no arguments");
2973 let built = match &row.result {
2974 TypePattern::Collection { ctor, .. } => *ctor,
2975 other => panic!("`{name}` answers {other}, not a collection"),
2976 };
2977 assert_eq!(built, ctor, "`{name}` builds the collection it names");
2978 }
2979 assert!(
2980 cat.entries().iter().all(|e| e.name != "to_grid"),
2981 "a grid needs a width, and an item sequence does not carry one"
2982 );
2983
2984 for name in ["to_map", "to_counter"] {
2988 let row = cat.entries().iter().find(|e| e.name == name).unwrap();
2989 let TypePattern::Iterable { item } = &row.receiver else {
2990 panic!("`{name}` is not on the generic receiver")
2991 };
2992 assert!(
2993 matches!(**item, TypePattern::Tuple(ref els) if els.len() == 2),
2994 "`{name}` accepts a `Map` or a `Counter` by saying its item is a pair"
2995 );
2996 }
2997 }
2998
2999 #[test]
3008 fn the_subscript_rows_are_a_closed_set_no_program_can_name() {
3009 let cat = builtin_catalog();
3010 let of = |ctor: CollectionCtor, args: usize| TypePattern::Collection {
3011 ctor,
3012 args: (0..args).map(|_| TypePattern::var("T")).collect(),
3013 };
3014 let map_pat = map_of_k_v();
3015
3016 for (pat, indices, what) in [
3018 (of(CollectionCtor::Vec, 1), 1, "Vec"),
3019 (of(CollectionCtor::Deque, 1), 1, "Deque"),
3020 (map_pat.clone(), 1, "Map"),
3021 (of(CollectionCtor::Counter, 1), 1, "Counter"),
3022 (of(CollectionCtor::Grid, 1), 2, "Grid"),
3023 (text_receiver(), 1, "Text"),
3024 ] {
3025 let hits: Vec<_> = cat
3026 .by_receiver_and_name(&pat, crate::catalog::INDEX_READ)
3027 .collect();
3028 assert_eq!(hits.len(), 1, "{what} reads through exactly one row");
3029 assert_eq!(hits[0].arity(), indices, "{what} indexes at {indices}");
3030 }
3031
3032 for (pat, args, what) in [
3036 (of(CollectionCtor::Vec, 1), 2, "Vec"),
3037 (of(CollectionCtor::Deque, 1), 2, "Deque"),
3038 (map_pat.clone(), 2, "Map"),
3039 (of(CollectionCtor::Counter, 1), 2, "Counter"),
3040 (of(CollectionCtor::Grid, 1), 3, "Grid"),
3041 ] {
3042 let hits: Vec<_> = cat
3043 .by_receiver_and_name(&pat, crate::catalog::INDEX_STORE)
3044 .collect();
3045 assert_eq!(hits.len(), 1, "{what} stores through exactly one row");
3046 assert_eq!(
3047 hits[0].arity(),
3048 args,
3049 "{what}'s store takes its indices and then the value"
3050 );
3051 }
3052
3053 for (pat, what) in [
3055 (of(CollectionCtor::Set, 1), "Set"),
3056 (of(CollectionCtor::MinHeap, 1), "MinHeap"),
3057 (of(CollectionCtor::MaxHeap, 1), "MaxHeap"),
3058 (of(CollectionCtor::BitSet, 0), "BitSet"),
3059 ] {
3060 for name in [crate::catalog::INDEX_READ, crate::catalog::INDEX_STORE] {
3061 assert_eq!(
3062 cat.by_receiver_and_name(&pat, name).count(),
3063 0,
3064 "{what} has no `{name}`"
3065 );
3066 }
3067 }
3068 assert_eq!(
3069 cat.by_receiver_and_name(&text_receiver(), crate::catalog::INDEX_STORE)
3070 .count(),
3071 0,
3072 "a `Text` is immutable: it reads through a subscript and has \
3073 no element store"
3074 );
3075
3076 for (pat, push, what) in [
3081 (of(CollectionCtor::Vec, 1), "push", "Vec"),
3082 (of(CollectionCtor::Deque, 1), "push_back", "Deque"),
3083 ] {
3084 let store = cat
3085 .by_receiver_and_name(&pat, crate::catalog::INDEX_STORE)
3086 .next()
3087 .unwrap_or_else(|| panic!("{what}'s store"));
3088 let appender = cat
3089 .by_receiver_and_name(&pat, push)
3090 .next()
3091 .unwrap_or_else(|| panic!("{what}.{push}"));
3092 assert_ne!(
3093 store.lowering, appender.lowering,
3094 "{what}'s store replaces; `{push}` appends"
3095 );
3096 assert!(
3097 store.can_fault(),
3098 "{what}'s store reports an index it does not hold"
3099 );
3100 }
3101
3102 let get = cat
3106 .by_receiver_and_name(&map_pat, "get")
3107 .next()
3108 .expect("Map.get");
3109 let index = cat
3110 .by_receiver_and_name(&map_pat, crate::catalog::INDEX_READ)
3111 .next()
3112 .expect("Map's subscript");
3113 assert_ne!(get.lowering, index.lowering);
3114 assert!(
3115 index.can_fault() && !get.can_fault(),
3116 "indexing faults where `.get` answers"
3117 );
3118
3119 let map_int_value = map_of_k_int_value();
3123 let plain_store = cat
3124 .by_receiver_and_name(&map_pat, crate::catalog::INDEX_STORE)
3125 .next()
3126 .expect("Map's store")
3127 .lowering
3128 .clone();
3129 for (name, what) in [
3130 (crate::catalog::INDEX_STORE_MIN, "min="),
3131 (crate::catalog::INDEX_STORE_MAX, "max="),
3132 ] {
3133 let hits: Vec<_> = cat.by_receiver_and_name(&map_int_value, name).collect();
3134 assert_eq!(hits.len(), 1, "`{what}` is one row on a Map");
3135 assert_eq!(hits[0].arity(), 2, "`{what}` takes its key and its value");
3136 assert_ne!(
3137 hits[0].lowering, plain_store,
3138 "`{what}` must not lower to the plain store"
3139 );
3140 for (pat, other) in [
3143 (of(CollectionCtor::Counter, 1), "Counter"),
3144 (of(CollectionCtor::Grid, 1), "Grid"),
3145 (of(CollectionCtor::Vec, 1), "Vec"),
3146 (of(CollectionCtor::Set, 1), "Set"),
3147 ] {
3148 assert_eq!(
3149 cat.by_receiver_and_name(&pat, name).count(),
3150 0,
3151 "{other} has no `{what}`"
3152 );
3153 }
3154 }
3155 assert_ne!(
3158 cat.by_receiver_and_name(&map_int_value, crate::catalog::INDEX_STORE_MIN)
3159 .next()
3160 .expect("min=")
3161 .lowering,
3162 cat.by_receiver_and_name(&map_int_value, crate::catalog::INDEX_STORE_MAX)
3163 .next()
3164 .expect("max=")
3165 .lowering,
3166 );
3167
3168 for name in [
3171 crate::catalog::INDEX_READ,
3172 crate::catalog::INDEX_STORE,
3173 crate::catalog::INDEX_STORE_MIN,
3174 crate::catalog::INDEX_STORE_MAX,
3175 ] {
3176 assert!(
3177 !name
3178 .chars()
3179 .next()
3180 .is_some_and(|c| c.is_alphabetic() || c == '_'),
3181 "`{name}` must not be spellable as a method name"
3182 );
3183 }
3184 }
3185
3186 #[test]
3190 fn catalog_covers_every_collection_kind() {
3191 let cat = builtin_catalog();
3192 for (ctor, name) in [
3195 (CollectionCtor::Vec, "Vec"),
3196 (CollectionCtor::Deque, "Deque"),
3197 (CollectionCtor::Set, "Set"),
3198 (CollectionCtor::Counter, "Counter"),
3199 (CollectionCtor::MinHeap, "MinHeap"),
3200 (CollectionCtor::MaxHeap, "MaxHeap"),
3201 (CollectionCtor::BitSet, "BitSet"),
3202 ] {
3203 let args: Vec<TypePattern> = match ctor.arity() {
3204 0 => Vec::new(),
3205 n => (0..n).map(|_| TypePattern::var("T")).collect(),
3206 };
3207 let pat = TypePattern::Collection { ctor, args };
3208 let len = cat.by_receiver_and_name(&pat, "len").count();
3209 let is_empty = cat.by_receiver_and_name(&pat, "is_empty").count();
3210 assert!(len >= 1, "{name} missing len method");
3211 assert!(is_empty >= 1, "{name} missing is_empty method");
3212 }
3213 let map_pat = map_of_k_v();
3216 assert!(cat.by_receiver_and_name(&map_pat, "len").count() >= 1);
3217 assert!(cat.by_receiver_and_name(&map_pat, "is_empty").count() >= 1);
3218 let grid_pat = grid_of_t();
3220 assert!(cat.by_receiver_and_name(&grid_pat, "width").count() >= 1);
3221 assert!(cat.by_receiver_and_name(&grid_pat, "neighbors4").count() >= 1);
3222 }
3223
3224 #[test]
3249 fn a_non_faulting_row_with_a_value_result_cannot_answer_the_unit_sentinel() {
3250 let cat = builtin_catalog();
3251 let mut checked = 0;
3252 for entry in cat.entries() {
3253 let MethodLowering::RuntimeSymbol(sym) = entry.lowering else {
3256 continue;
3257 };
3258 checked += 1;
3259 let ret = sym.sig().ret;
3260 let result_is_unit = entry.result == TypePattern::Unit;
3261 match (ret, result_is_unit) {
3262 (abi::AbiRet::GcUnit, true) | (abi::AbiRet::Gc, false) => {}
3263 _ => panic!(
3264 "{}.{} declares `{}` and lowers to `{}`, whose manifest return is \
3265 {ret:?}. A wrapper answers either a value (`AbiRet::Gc`, \
3266 non-`Unit` result) or nothing (`AbiRet::GcUnit`, `Unit` \
3267 result); an answer that is sometimes absent is spelled \
3268 `Option[T]`, never a Unit sentinel under a value type.",
3269 entry.receiver,
3270 entry.name,
3271 entry.result,
3272 sym.name(),
3273 ),
3274 }
3275 }
3276 assert!(
3279 checked >= 40,
3280 "expected the sweep to reach most of the catalog, it reached {checked} rows"
3281 );
3282 }
3283
3284 #[test]
3298 fn a_scalar_primitive_row_answers_the_scalar_channel_and_a_scalar_type() {
3299 let cat = builtin_catalog();
3300 let mut rows = 0;
3301 for entry in cat.entries() {
3302 let MethodLowering::ScalarPrimitive(sym) = entry.lowering else {
3303 continue;
3304 };
3305 rows += 1;
3306 assert_eq!(
3307 sym.sig().ret,
3308 abi::AbiRet::RawI64,
3309 "{}.{} lowers as a scalar primitive, so `{}` must answer the \
3310 scalar channel; a `-> Gc` row here is a box the caller would \
3311 have to unwrap again, which is the whole thing this arm exists \
3312 to remove",
3313 entry.receiver,
3314 entry.name,
3315 sym.name(),
3316 );
3317 assert!(
3318 matches!(entry.result, TypePattern::Scalar(_)),
3319 "{}.{} answers `{}`, which the scalar channel cannot carry",
3320 entry.receiver,
3321 entry.name,
3322 entry.result,
3323 );
3324 assert!(
3327 !entry.allocates(),
3328 "{}.{} allocates, so it cannot be a non-safepoint instruction",
3329 entry.receiver,
3330 entry.name,
3331 );
3332 }
3333 assert_eq!(
3334 rows, 1,
3335 "`BitSet.contains` is the only scalar-primitive row today; a second \
3336 one wants an arm in `praxis-mir`'s `lower_scalar_primitive` before \
3337 this number moves"
3338 );
3339 }
3340
3341 #[test]
3345 fn map_get_and_grid_find_answer_an_option() {
3346 let cat = builtin_catalog();
3347 let map_pat = map_of_k_v();
3348 let get = cat
3349 .by_receiver_and_name(&map_pat, "get")
3350 .next()
3351 .expect("map.get exists");
3352 assert_eq!(
3353 get.result,
3354 TypePattern::Option(Box::new(TypePattern::var("V"))),
3355 "§5.7 writes `Map[K,V].get(K) -> Option[V]`"
3356 );
3357
3358 let grid_pat = grid_of_t();
3359 let find = cat
3360 .by_receiver_and_name(&grid_pat, "find")
3361 .next()
3362 .expect("grid.find exists");
3363 assert_eq!(find.result, TypePattern::Option(Box::new(point_pattern())));
3364
3365 let counter_pat = counter_of_t();
3368 let counter_get = cat
3369 .by_receiver_and_name(&counter_pat, "get")
3370 .next()
3371 .expect("counter.get exists");
3372 assert_eq!(
3373 counter_get.result,
3374 TypePattern::Scalar(ScalarType::Int),
3375 "§6.2: a Counter's absent values read as zero, deliberately"
3376 );
3377 }
3378
3379 #[test]
3392 fn text_int_and_float_answer_options() {
3393 let cat = builtin_catalog();
3394 for (name, scalar) in [("int", ScalarType::Int), ("float", ScalarType::Float)] {
3395 let entry = cat
3396 .by_receiver_and_name(&TypePattern::Scalar(ScalarType::Text), name)
3397 .next()
3398 .unwrap_or_else(|| panic!("`Text.{name}()` exists"));
3399 assert_eq!(
3400 entry.result,
3401 TypePattern::Option(Box::new(TypePattern::Scalar(scalar))),
3402 "a text that is not a number is absence, not a fault (\u{00a7}4.7)"
3403 );
3404 assert!(entry.params.is_empty(), "`{name}` takes no arguments");
3405 }
3406 }
3407
3408 #[test]
3415 fn the_two_text_reads_answer_a_char() {
3416 let cat = builtin_catalog();
3417 let text = text_receiver();
3418
3419 for name in [crate::catalog::INDEX_READ, "get"] {
3420 let row = cat
3421 .by_receiver_and_name(&text, name)
3422 .next()
3423 .unwrap_or_else(|| panic!("Text.{name} exists"));
3424 assert_eq!(
3425 row.result,
3426 TypePattern::Scalar(ScalarType::Char),
3427 "ADR-086: `Text.{name}` answers a Char, not the char's scalar value"
3428 );
3429 assert_eq!(
3432 row.lowering,
3433 MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextGet),
3434 "`Text.{name}` lowers through the one text read"
3435 );
3436 }
3437 }
3438
3439 #[test]
3444 fn char_and_int_convert_both_ways() {
3445 let cat = builtin_catalog();
3446
3447 let to_int = cat
3448 .by_receiver_and_name(&TypePattern::Scalar(ScalarType::Char), "to_int")
3449 .next()
3450 .expect("Char.to_int exists");
3451 assert_eq!(to_int.result, TypePattern::Scalar(ScalarType::Int));
3452 assert_eq!(to_int.purity, Purity::Pure);
3453
3454 let to_char = cat
3455 .by_receiver_and_name(&TypePattern::Scalar(ScalarType::Int), "to_char")
3456 .next()
3457 .expect("Int.to_char exists");
3458 assert_eq!(to_char.result, TypePattern::Scalar(ScalarType::Char));
3459
3460 assert!(
3464 to_char.can_fault(),
3465 "not every Int is a Unicode scalar value, so the narrowing faults"
3466 );
3467 assert!(
3468 !to_int.can_fault(),
3469 "every Unicode scalar value fits an Int, so the widening cannot"
3470 );
3471 }
3472
3473 #[test]
3480 fn the_to_text_family_is_int_float_and_char() {
3481 let cat = builtin_catalog();
3482 for scalar in [ScalarType::Int, ScalarType::Float, ScalarType::Char] {
3483 let receiver = TypePattern::Scalar(scalar);
3484 let row = cat
3485 .by_receiver_and_name(&receiver, "to_text")
3486 .next()
3487 .unwrap_or_else(|| panic!("{scalar:?}.to_text exists"));
3488 assert_eq!(row.result, TypePattern::Scalar(ScalarType::Text));
3489 assert_eq!(row.purity, Purity::Pure);
3490 assert!(row.allocates(), "{scalar:?}.to_text answers a fresh Text");
3491 assert!(!row.can_fault(), "{scalar:?}.to_text cannot fail");
3495 }
3496 for scalar in [ScalarType::Bool, ScalarType::Byte, ScalarType::Text] {
3497 let receiver = TypePattern::Scalar(scalar);
3498 assert_eq!(
3499 cat.by_receiver_and_name(&receiver, "to_text").count(),
3500 0,
3501 "the `to_text` family is three scalars; {scalar:?} is not one of \
3502 them, and a universal `to_text` is §8.1 interpolation's question"
3503 );
3504 }
3505 }
3506
3507 #[test]
3517 fn join_is_one_row_and_a_sequence_of_chars_has_its_own_name() {
3518 let cat = builtin_catalog();
3519
3520 let join: Vec<_> = cat.entries().iter().filter(|e| e.name == "join").collect();
3521 assert_eq!(join.len(), 1, "`join` is one row");
3522 assert_eq!(join[0].receiver, iterable_of_text_elem());
3523 assert_eq!(join[0].params, vec![TypePattern::Scalar(ScalarType::Text)]);
3524 assert_eq!(join[0].result, TypePattern::Scalar(ScalarType::Text));
3525
3526 assert!(
3530 !cat.entries()
3531 .iter()
3532 .any(|e| e.name == "to_text" && matches!(e.receiver, TypePattern::Iterable { .. })),
3533 "an `Iterable.to_text` would collide with the scalar `to_text` rows"
3534 );
3535 let chars_pat = vec_of_char();
3536 let chars = cat
3537 .by_receiver_and_name(&chars_pat, "to_text")
3538 .next()
3539 .expect("Vec[Char].to_text exists");
3540 assert_eq!(chars.result, TypePattern::Scalar(ScalarType::Text));
3541 assert!(chars.params.is_empty());
3542 }
3543
3544 #[test]
3553 fn reversed_is_a_barrier_with_no_bound_on_its_element() {
3554 let cat = builtin_catalog();
3555 let receiver = iterable_of_t();
3556 let row = cat
3557 .by_receiver_and_name(&receiver, "reversed")
3558 .next()
3559 .expect("Iterable.reversed exists");
3560 assert_eq!(row.result, vec_of_t());
3561 assert_eq!(row.purity, Purity::Pure);
3562 assert!(row.bounds().is_empty(), "reversal reads no callback");
3563 assert!(
3564 !row.can_fault(),
3565 "there is no element `reversed` can be handed that it cannot reverse"
3566 );
3567 assert!(
3568 matches!(
3569 row.lowering,
3570 MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecReversed)
3571 ),
3572 "a barrier is a runtime call, not a fused stage: reversal cannot \
3573 answer its first element until it has seen the last"
3574 );
3575 }
3576
3577 #[test]
3595 fn a_grouping_answers_a_nested_vec_with_no_bound_and_can_fault() {
3596 let cat = builtin_catalog();
3597 let receiver = iterable_of_t();
3598 for (name, symbol) in [
3599 ("chunks", abi::RuntimeSymbol::VecChunks),
3600 ("windows", abi::RuntimeSymbol::VecWindows),
3601 ] {
3602 let row = cat
3603 .by_receiver_and_name(&receiver, name)
3604 .next()
3605 .unwrap_or_else(|| panic!("Iterable.{name} exists"));
3606 assert_eq!(
3607 row.result,
3608 vec_of_vec_of_t(),
3609 "`{name}` groups without flattening"
3610 );
3611 assert_eq!(row.params, vec![TypePattern::Scalar(ScalarType::Int)]);
3612 assert_eq!(row.purity, Purity::Pure);
3613 assert!(
3614 row.bounds().is_empty(),
3615 "a grouping reads no descriptor callback ({name})"
3616 );
3617 assert!(
3618 row.can_fault(),
3619 "`{name}(0)` names no run, and the program has to be able to see that"
3620 );
3621 assert!(
3622 matches!(row.lowering, MethodLowering::RuntimeSymbol(s) if s == symbol),
3623 "a grouping is a barrier: it cannot answer its first group from \
3624 one element ({name})"
3625 );
3626 }
3627 }
3628
3629 #[test]
3642 fn the_overflow_alternative_family_is_three_modes_over_three_operators() {
3643 let cat = builtin_catalog();
3644 let int = TypePattern::Scalar(ScalarType::Int);
3645
3646 for mode in ["wrapping", "saturating", "checked"] {
3647 for op in ["add", "sub", "mul"] {
3648 let name = format!("{mode}_{op}");
3649 let row = cat
3650 .by_receiver_and_name(&int, &name)
3651 .next()
3652 .unwrap_or_else(|| panic!("§4.12's family includes `Int.{name}`"));
3653 assert_eq!(row.params, vec![TypePattern::Scalar(ScalarType::Int)]);
3654 assert!(!row.can_fault(), "`{name}` is an alternative to faulting");
3658
3659 let want = if mode == "checked" {
3660 TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int)))
3661 } else {
3662 TypePattern::Scalar(ScalarType::Int)
3663 };
3664 assert_eq!(row.result, want, "`{name}`'s result");
3665 }
3666 }
3667
3668 for absent in [
3670 "wrapping_div",
3671 "saturating_div",
3672 "checked_div",
3673 "wrapping_rem",
3674 "checked_rem",
3675 "wrapping_neg",
3676 "checked_neg",
3677 "saturating_abs",
3678 ] {
3679 assert!(
3680 cat.by_receiver_and_name(&int, absent).next().is_none(),
3681 "§4.12 closes the family before `{absent}`: division's escape \
3682 hatch is closed by \"Division by zero always faults\", and \
3683 `_neg`/`_abs` are spelled with `0.wrapping_sub(x)`"
3684 );
3685 }
3686 }
3687}