1#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
21pub enum AbiKind {
22 Ctx,
24 Gc,
26 RawI64,
28 RawU32,
31 Ptr,
34}
35
36#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
49pub enum AbiRet {
50 Gc,
61 GcUnit,
67 RawI64,
69 Ptr,
71 Void,
73}
74
75#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
83pub enum Effect {
84 Pure,
86 Faults,
88 Allocates,
90 AllocatesAndFaults,
92}
93
94impl Effect {
95 #[inline]
97 pub const fn allocates(self) -> bool {
98 matches!(self, Effect::Allocates | Effect::AllocatesAndFaults)
99 }
100
101 #[inline]
103 pub const fn faults(self) -> bool {
104 matches!(self, Effect::Faults | Effect::AllocatesAndFaults)
105 }
106}
107
108#[derive(Clone, Copy, PartialEq, Eq, Debug)]
110pub struct AbiSig {
111 pub params: &'static [AbiKind],
113 pub ret: AbiRet,
115 pub effect: Effect,
117}
118
119impl AbiSig {
120 #[inline]
122 pub const fn arity(&self) -> usize {
123 self.params.len() - 1
124 }
125}
126
127macro_rules! runtime_symbols {
130 ($( $variant:ident = $name:literal : ( $($kind:ident),* ) -> $ret:ident , $effect:ident ; )*) => {
131 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
136 pub enum RuntimeSymbol {
137 $(
138 #[doc = concat!("`", $name, "`")]
139 $variant,
140 )*
141 }
142
143 impl RuntimeSymbol {
144 pub const ALL: &'static [RuntimeSymbol] = &[$(RuntimeSymbol::$variant),*];
146
147 #[inline]
150 pub const fn name(self) -> &'static str {
151 match self { $(RuntimeSymbol::$variant => $name,)* }
152 }
153
154 #[inline]
156 pub const fn sig(self) -> AbiSig {
157 match self {
158 $(RuntimeSymbol::$variant => AbiSig {
159 params: &[$(AbiKind::$kind),*],
160 ret: AbiRet::$ret,
161 effect: Effect::$effect,
162 },)*
163 }
164 }
165
166 pub fn from_name(name: &str) -> Option<RuntimeSymbol> {
170 match name {
171 $($name => Some(RuntimeSymbol::$variant),)*
172 _ => None,
173 }
174 }
175 }
176 };
177}
178
179impl RuntimeSymbol {
180 #[inline]
182 pub const fn allocates(self) -> bool {
183 self.sig().effect.allocates()
184 }
185
186 #[inline]
188 pub const fn faults(self) -> bool {
189 self.sig().effect.faults()
190 }
191
192 #[inline]
194 pub const fn arity(self) -> usize {
195 self.sig().arity()
196 }
197}
198
199impl std::fmt::Display for RuntimeSymbol {
200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201 f.write_str(self.name())
202 }
203}
204
205runtime_symbols! {
206 AllocBool = "praxis_alloc_bool": (Ctx, RawI64) -> Gc, Pure;
207 AllocChar = "praxis_alloc_char": (Ctx, RawI64) -> Gc, AllocatesAndFaults;
208 AllocClosure = "praxis_alloc_closure": (Ctx, Ptr, RawI64) -> Gc, Allocates;
209 AllocEnum = "praxis_alloc_enum": (Ctx, Ptr, RawI64) -> Gc, Allocates;
210 AllocFloat = "praxis_alloc_float": (Ctx, RawI64) -> Gc, Allocates;
211 AllocInt = "praxis_alloc_int": (Ctx, RawI64) -> Gc, Allocates;
212 AllocRecord = "praxis_alloc_record": (Ctx, Ptr) -> Gc, Allocates;
213 AllocText = "praxis_alloc_text": (Ctx, Ptr, Ptr) -> Gc, Allocates;
214 AllocTuple = "praxis_alloc_tuple": (Ctx, Ptr) -> Gc, Allocates;
215 AllocUnit = "praxis_alloc_unit": (Ctx) -> GcUnit, Pure;
216 AllocVarCell = "praxis_alloc_var_cell": (Ctx, Gc) -> Gc, Allocates;
217 Assert = "praxis_assert": (Ctx, Gc) -> GcUnit, Faults;
218 AStar = "praxis_a_star": (Ctx, Gc, Gc, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
219 Bfs = "praxis_bfs": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
220 BfsDistance = "praxis_bfs_distance": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
221 BitsetContains = "praxis_bitset_contains": (Ctx, Gc, Gc) -> RawI64, Pure;
227 BitsetInsert = "praxis_bitset_insert": (Ctx, Gc, Gc) -> GcUnit, AllocatesAndFaults;
228 BitsetIsEmpty = "praxis_bitset_is_empty": (Ctx, Gc) -> Gc, Pure;
229 BitsetItems = "praxis_bitset_items": (Ctx, Gc) -> Gc, Allocates;
230 BitsetLen = "praxis_bitset_len": (Ctx, Gc) -> Gc, Allocates;
231 BitsetNew = "praxis_bitset_new": (Ctx) -> Gc, Allocates;
232 Breakpoint = "praxis_breakpoint": (Ctx, RawU32, RawU32) -> Void, Pure;
240 BitsetRemove = "praxis_bitset_remove": (Ctx, Gc, Gc) -> GcUnit, Pure;
241 BoolLoad = "praxis_bool_load": (Ctx, Gc) -> RawI64, Pure;
242 CharLoad = "praxis_char_load": (Ctx, Gc) -> RawI64, Pure;
243 CharToInt = "praxis_char_to_int": (Ctx, Gc) -> Gc, Allocates;
244 CharToText = "praxis_char_to_text": (Ctx, Gc) -> Gc, Allocates;
250 CheckFault = "praxis_check_fault": (Ctx) -> RawI64, Pure;
251 ClosureCapture = "praxis_closure_capture": (Ctx, Gc, RawI64) -> Gc, Pure;
252 ClosureFnPtr = "praxis_closure_fn_ptr": (Ctx, Gc) -> Ptr, Pure;
253 ClosureSetCapture = "praxis_closure_set_capture": (Ctx, Gc, RawI64, Gc) -> Gc, Pure;
254 CounterGet = "praxis_counter_get": (Ctx, Gc, Gc) -> Gc, Allocates;
255 CounterInc = "praxis_counter_inc": (Ctx, Gc, Gc) -> GcUnit, AllocatesAndFaults;
256 CounterKeys = "praxis_counter_keys": (Ctx, Gc) -> Gc, Allocates;
257 CounterSet = "praxis_counter_set": (Ctx, Gc, Gc, Gc) -> GcUnit, Allocates;
258 CounterValues = "praxis_counter_values": (Ctx, Gc) -> Gc, Allocates;
259 CounterIsEmpty = "praxis_counter_is_empty": (Ctx, Gc) -> Gc, Pure;
260 CounterLen = "praxis_counter_len": (Ctx, Gc) -> Gc, Allocates;
261 CounterNew = "praxis_counter_new": (Ctx, Ptr) -> Gc, Allocates;
262 DequeGet = "praxis_deque_get": (Ctx, Gc, Gc) -> Gc, Faults;
263 DequeIsEmpty = "praxis_deque_is_empty": (Ctx, Gc) -> Gc, Pure;
264 DequeLen = "praxis_deque_len": (Ctx, Gc) -> Gc, Allocates;
265 DequeNew = "praxis_deque_new": (Ctx, Ptr) -> Gc, Allocates;
266 DequePopBack = "praxis_deque_pop_back": (Ctx, Gc) -> Gc, Faults;
267 DequePopFront = "praxis_deque_pop_front": (Ctx, Gc) -> Gc, Faults;
268 DequePushBack = "praxis_deque_push_back": (Ctx, Gc, Gc) -> GcUnit, AllocatesAndFaults;
269 DequePushFront = "praxis_deque_push_front": (Ctx, Gc, Gc) -> GcUnit, AllocatesAndFaults;
270 DequeSet = "praxis_deque_set": (Ctx, Gc, Gc, Gc) -> GcUnit, Faults;
271 Dbg = "praxis_dbg": (Ctx, Gc) -> Gc, Pure;
272 Dfs = "praxis_dfs": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
273 Dijkstra = "praxis_dijkstra": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
274 EnumPayload = "praxis_enum_payload": (Ctx, Gc, RawI64) -> Gc, Pure;
275 EnumSetPayload = "praxis_enum_set_payload": (Ctx, Gc, RawI64, Gc) -> Gc, Pure;
276 EnumTag = "praxis_enum_tag": (Ctx, Gc) -> Gc, Allocates;
277 FloatAbs = "praxis_float_abs": (Ctx, Gc) -> Gc, Allocates;
278 FloatCeil = "praxis_float_ceil": (Ctx, Gc) -> Gc, Allocates;
279 FloatE = "praxis_float_e": (Ctx) -> Gc, Allocates;
280 FloatFloor = "praxis_float_floor": (Ctx, Gc) -> Gc, Allocates;
281 FloatIsInfinite = "praxis_float_is_infinite": (Ctx, Gc) -> Gc, Pure;
282 FloatIsNan = "praxis_float_is_nan": (Ctx, Gc) -> Gc, Pure;
283 FloatLoad = "praxis_float_load": (Ctx, Gc) -> RawI64, Pure;
284 FloatMax = "praxis_float_max": (Ctx, Gc, Gc) -> Gc, Allocates;
285 FloatMin = "praxis_float_min": (Ctx, Gc, Gc) -> Gc, Allocates;
286 FloatPi = "praxis_float_pi": (Ctx) -> Gc, Allocates;
287 FloatRound = "praxis_float_round": (Ctx, Gc) -> Gc, Allocates;
288 FloatSign = "praxis_float_sign": (Ctx, Gc) -> Gc, Allocates;
289 FloatSqrt = "praxis_float_sqrt": (Ctx, Gc) -> Gc, Allocates;
290 FloatToInt = "praxis_float_to_int": (Ctx, Gc) -> Gc, AllocatesAndFaults;
291 FloatToText = "praxis_float_to_text": (Ctx, Gc) -> Gc, Allocates;
292 FloodFill = "praxis_flood_fill": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
293 GetInput = "praxis_get_input": (Ctx) -> Gc, AllocatesAndFaults;
294 GridCells = "praxis_grid_cells": (Ctx, Gc) -> Gc, Allocates;
295 GridColumn = "praxis_grid_column": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
296 GridContains = "praxis_grid_contains": (Ctx, Gc, Gc, Gc) -> Gc, Pure;
297 GridFilled = "praxis_grid_filled": (Ctx, Ptr, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
305 GridFind = "praxis_grid_find": (Ctx, Gc, Gc) -> Gc, Allocates;
306 GridFindAll = "praxis_grid_find_all": (Ctx, Gc, Gc) -> Gc, Allocates;
307 GridGet = "praxis_grid_get": (Ctx, Gc, Gc, Gc) -> Gc, Faults;
308 GridHeight = "praxis_grid_height": (Ctx, Gc) -> Gc, Allocates;
309 GridNeighbors4 = "praxis_grid_neighbors4": (Ctx, Gc, Gc) -> Gc, Allocates;
310 GridNeighbors8 = "praxis_grid_neighbors8": (Ctx, Gc, Gc) -> Gc, Allocates;
311 GridNew = "praxis_grid_new": (Ctx, Ptr, RawI64, RawI64) -> Gc, AllocatesAndFaults;
312 GridPositions = "praxis_grid_positions": (Ctx, Gc) -> Gc, Allocates;
313 GridRotateLeft = "praxis_grid_rotate_left": (Ctx, Gc) -> Gc, Allocates;
314 GridRotateRight = "praxis_grid_rotate_right": (Ctx, Gc) -> Gc, Allocates;
315 GridRow = "praxis_grid_row": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
316 GridSet = "praxis_grid_set": (Ctx, Gc, Gc, Gc, Gc) -> GcUnit, Faults;
317 GridTranspose = "praxis_grid_transpose": (Ctx, Gc) -> Gc, Allocates;
318 GridWidth = "praxis_grid_width": (Ctx, Gc) -> Gc, Allocates;
319 IntAbs = "praxis_int_abs": (Ctx, Gc) -> Gc, AllocatesAndFaults;
320 IntAdd = "praxis_int_add": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
321 IntCheckedAdd = "praxis_int_checked_add": (Ctx, Gc, Gc) -> Gc, Allocates;
322 IntCheckedMul = "praxis_int_checked_mul": (Ctx, Gc, Gc) -> Gc, Allocates;
323 IntCheckedSub = "praxis_int_checked_sub": (Ctx, Gc, Gc) -> Gc, Allocates;
324 IntClamp = "praxis_int_clamp": (Ctx, Gc, Gc, Gc) -> Gc, Faults;
325 IntDiv = "praxis_int_div": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
326 IntEq = "praxis_int_eq": (Ctx, Gc, Gc) -> Gc, Pure;
327 IntGcd = "praxis_int_gcd": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
328 IntGe = "praxis_int_ge": (Ctx, Gc, Gc) -> Gc, Pure;
329 IntGt = "praxis_int_gt": (Ctx, Gc, Gc) -> Gc, Pure;
330 IntLcm = "praxis_int_lcm": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
331 IntLe = "praxis_int_le": (Ctx, Gc, Gc) -> Gc, Pure;
332 IntLoad = "praxis_int_load": (Ctx, Gc) -> RawI64, Pure;
333 IntLt = "praxis_int_lt": (Ctx, Gc, Gc) -> Gc, Pure;
334 IntMax = "praxis_int_max": (Ctx, Gc, Gc) -> Gc, Pure;
335 IntMin = "praxis_int_min": (Ctx, Gc, Gc) -> Gc, Pure;
336 IntMul = "praxis_int_mul": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
337 IntNe = "praxis_int_ne": (Ctx, Gc, Gc) -> Gc, Pure;
338 IntNeg = "praxis_int_neg": (Ctx, Gc) -> Gc, AllocatesAndFaults;
339 IntRem = "praxis_int_rem": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
340 IntSaturatingAdd = "praxis_int_saturating_add": (Ctx, Gc, Gc) -> Gc, Allocates;
341 IntSaturatingMul = "praxis_int_saturating_mul": (Ctx, Gc, Gc) -> Gc, Allocates;
342 IntSaturatingSub = "praxis_int_saturating_sub": (Ctx, Gc, Gc) -> Gc, Allocates;
343 IntSign = "praxis_int_sign": (Ctx, Gc) -> Gc, Allocates;
344 IntSub = "praxis_int_sub": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
345 IntToChar = "praxis_int_to_char": (Ctx, Gc) -> Gc, AllocatesAndFaults;
346 IntToFloat = "praxis_int_to_float": (Ctx, Gc) -> Gc, Allocates;
347 IntToText = "praxis_int_to_text": (Ctx, Gc) -> Gc, Allocates;
349 IntWrappingAdd = "praxis_int_wrapping_add": (Ctx, Gc, Gc) -> Gc, Allocates;
350 IntWrappingMul = "praxis_int_wrapping_mul": (Ctx, Gc, Gc) -> Gc, Allocates;
351 IntWrappingSub = "praxis_int_wrapping_sub": (Ctx, Gc, Gc) -> Gc, Allocates;
352 MapContains = "praxis_map_contains": (Ctx, Gc, Gc) -> Gc, Pure;
353 RangeGet = "praxis_range_get": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
354 RangeLen = "praxis_range_len": (Ctx, Gc) -> Gc, AllocatesAndFaults;
355 RangeNew = "praxis_range_new": (Ctx, Gc, Gc) -> Gc, Allocates;
356 RangeNewInclusive = "praxis_range_new_inclusive": (Ctx, Gc, Gc) -> Gc, Allocates;
357 MapGet = "praxis_map_get": (Ctx, Gc, Gc) -> Gc, Allocates;
358 MapIndex = "praxis_map_index": (Ctx, Gc, Gc) -> Gc, Faults;
359 MapInsert = "praxis_map_insert": (Ctx, Gc, Gc, Gc) -> GcUnit, Allocates;
360 MapIsEmpty = "praxis_map_is_empty": (Ctx, Gc) -> Gc, Pure;
361 MapKeys = "praxis_map_keys": (Ctx, Gc) -> Gc, Allocates;
362 MapLen = "praxis_map_len": (Ctx, Gc) -> Gc, Allocates;
363 MapNew = "praxis_map_new": (Ctx, Ptr) -> Gc, Allocates;
364 MapRemove = "praxis_map_remove": (Ctx, Gc, Gc) -> GcUnit, Pure;
365 MapUpdateMax = "praxis_map_update_max": (Ctx, Gc, Gc, Gc) -> GcUnit, Allocates;
366 MapValues = "praxis_map_values": (Ctx, Gc) -> Gc, Allocates;
367 MapUpdateMin = "praxis_map_update_min": (Ctx, Gc, Gc, Gc) -> GcUnit, Allocates;
368 MaxHeapIsEmpty = "praxis_max_heap_is_empty": (Ctx, Gc) -> Gc, Pure;
369 MaxHeapItems = "praxis_max_heap_items": (Ctx, Gc) -> Gc, Allocates;
370 MaxHeapLen = "praxis_max_heap_len": (Ctx, Gc) -> Gc, Allocates;
371 MaxHeapNew = "praxis_max_heap_new": (Ctx, Ptr) -> Gc, Allocates;
372 MaxHeapPeek = "praxis_max_heap_peek": (Ctx, Gc) -> Gc, Faults;
373 MaxHeapPop = "praxis_max_heap_pop": (Ctx, Gc) -> Gc, Faults;
374 MaxHeapPush = "praxis_max_heap_push": (Ctx, Gc, Gc) -> GcUnit, Allocates;
375 MinHeapIsEmpty = "praxis_min_heap_is_empty": (Ctx, Gc) -> Gc, Pure;
376 MinHeapItems = "praxis_min_heap_items": (Ctx, Gc) -> Gc, Allocates;
377 MinHeapLen = "praxis_min_heap_len": (Ctx, Gc) -> Gc, Allocates;
378 MinHeapNew = "praxis_min_heap_new": (Ctx, Ptr) -> Gc, Allocates;
379 MinHeapPeek = "praxis_min_heap_peek": (Ctx, Gc) -> Gc, Faults;
380 MinHeapPop = "praxis_min_heap_pop": (Ctx, Gc) -> Gc, Faults;
381 MinHeapPush = "praxis_min_heap_push": (Ctx, Gc, Gc) -> GcUnit, Allocates;
382 Panic = "praxis_panic": (Ctx, Gc) -> GcUnit, Faults;
383 RaiseDivByZeroIf = "praxis_raise_div_by_zero_if": (Ctx, RawI64) -> Void, Faults;
384 RaiseEmptyCollection = "praxis_raise_empty_collection": (Ctx) -> GcUnit, Faults;
385 RaiseIntOverflowIf = "praxis_raise_int_overflow_if": (Ctx, RawI64) -> Void, Faults;
386 RaiseStackOverflow = "praxis_raise_stack_overflow": (Ctx) -> Void, Faults;
387 RecordField = "praxis_record_field": (Ctx, Gc, RawU32) -> Gc, Pure;
388 RecordSetField = "praxis_record_set_field": (Ctx, Gc, RawU32, Gc) -> Gc, Pure;
389 RunParser = "praxis_run_parser": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
390 SetContains = "praxis_set_contains": (Ctx, Gc, Gc) -> Gc, Pure;
391 SetInsert = "praxis_set_insert": (Ctx, Gc, Gc) -> GcUnit, Allocates;
392 SetIsEmpty = "praxis_set_is_empty": (Ctx, Gc) -> Gc, Pure;
393 SetItems = "praxis_set_items": (Ctx, Gc) -> Gc, Allocates;
394 SetLen = "praxis_set_len": (Ctx, Gc) -> Gc, Allocates;
395 SetNew = "praxis_set_new": (Ctx, Ptr) -> Gc, Allocates;
396 SetRemove = "praxis_set_remove": (Ctx, Gc, Gc) -> GcUnit, Pure;
397 SnapshotDebugChain = "praxis_snapshot_debug_chain": (Ctx) -> Void, Pure;
398 StructEq = "praxis_struct_eq": (Ctx, Gc, Gc) -> RawI64, Pure;
399 TextConcat = "praxis_text_concat": (Ctx, Gc, Gc) -> Gc, Allocates;
400 TextGet = "praxis_text_get": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
401 TextFloat = "praxis_text_float": (Ctx, Gc) -> Gc, Allocates;
402 TextInt = "praxis_text_int": (Ctx, Gc) -> Gc, Allocates;
403 TextIsEmpty = "praxis_text_is_empty": (Ctx, Gc) -> Gc, Pure;
404 TextLen = "praxis_text_len": (Ctx, Gc) -> Gc, Allocates;
405 TupleGet = "praxis_tuple_get": (Ctx, Gc, RawI64) -> Gc, Pure;
406 TupleSet = "praxis_tuple_set": (Ctx, Gc, RawI64, Gc) -> Gc, Pure;
407 ValueCmp = "praxis_value_cmp": (Ctx, Gc, Gc) -> RawI64, Faults;
408 ValueToText = "praxis_value_to_text": (Ctx, Gc) -> Gc, Allocates;
414 VarCellGet = "praxis_var_cell_get": (Ctx, Gc) -> Gc, Pure;
415 VarCellSet = "praxis_var_cell_set": (Ctx, Gc, Gc) -> Gc, Pure;
416 VecChunks = "praxis_vec_chunks": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
424 VecFilled = "praxis_vec_filled": (Ctx, Ptr, Gc, Gc) -> Gc, AllocatesAndFaults;
428 VecFrequencies = "praxis_vec_frequencies": (Ctx, Gc) -> Gc, Allocates;
429 VecGet = "praxis_vec_get": (Ctx, Gc, Gc) -> Gc, Faults;
430 VecIsEmpty = "praxis_vec_is_empty": (Ctx, Gc) -> Gc, Pure;
431 VecJoin = "praxis_vec_join": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
437 VecLen = "praxis_vec_len": (Ctx, Gc) -> Gc, Allocates;
438 VecNew = "praxis_vec_new": (Ctx, Ptr) -> Gc, Allocates;
439 VecPush = "praxis_vec_push": (Ctx, Gc, Gc) -> GcUnit, AllocatesAndFaults;
440 VecReversed = "praxis_vec_reversed": (Ctx, Gc) -> Gc, Allocates;
445 VecSet = "praxis_vec_set": (Ctx, Gc, Gc, Gc) -> GcUnit, Faults;
446 VecSorted = "praxis_vec_sorted": (Ctx, Gc) -> Gc, AllocatesAndFaults;
452 VecSortedByKey = "praxis_vec_sorted_by_key": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
456 VecToText = "praxis_vec_to_text": (Ctx, Gc) -> Gc, AllocatesAndFaults;
457 VecUnique = "praxis_vec_unique": (Ctx, Gc) -> Gc, Allocates;
458 VecWindows = "praxis_vec_windows": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
460 WriteStdout = "praxis_write_stdout": (Ctx, Gc) -> GcUnit, Pure;
461}
462
463const _: () = {
474 assert!(!RuntimeSymbol::ALL.is_empty());
477
478 let mut i = 0;
479 while i < RuntimeSymbol::ALL.len() {
480 let sym = RuntimeSymbol::ALL[i];
481 let sig = sym.sig();
482
483 assert!(matches!(sig.params[0], AbiKind::Ctx));
488
489 assert!(!(matches!(sig.ret, AbiRet::Void) && sig.effect.allocates()));
493
494 assert!(sig.effect.allocates() == sym.allocates());
497 assert!(sig.effect.faults() == sym.faults());
498
499 i += 1;
507 }
508};
509
510#[cfg(test)]
511mod tests {
512 use super::*;
513 use std::collections::HashSet;
514
515 #[test]
519 fn names_are_unique_and_well_formed() {
520 let mut seen = HashSet::new();
521 for &sym in RuntimeSymbol::ALL {
522 assert!(
523 sym.name().starts_with("praxis_"),
524 "{sym} is not a praxis_* symbol"
525 );
526 assert!(seen.insert(sym.name()), "duplicate symbol name {sym}");
527 }
528 assert_eq!(seen.len(), RuntimeSymbol::ALL.len());
529 }
530
531 #[test]
534 fn from_name_round_trips_every_symbol() {
535 for &sym in RuntimeSymbol::ALL {
536 assert_eq!(RuntimeSymbol::from_name(sym.name()), Some(sym));
537 }
538 assert_eq!(RuntimeSymbol::from_name("praxis_not_a_symbol"), None);
539 }
540
541 #[test]
545 fn every_symbol_leads_with_the_context_pointer() {
546 for &sym in RuntimeSymbol::ALL {
547 let sig = sym.sig();
548 assert_eq!(
549 sig.params.first(),
550 Some(&AbiKind::Ctx),
551 "{sym} does not take ctx first"
552 );
553 assert!(
554 !sig.params[1..].contains(&AbiKind::Ctx),
555 "{sym} takes ctx more than once"
556 );
557 }
558 }
559
560 #[test]
561 fn effect_queries_agree_with_the_variants() {
562 assert!(!Effect::Pure.allocates() && !Effect::Pure.faults());
563 assert!(!Effect::Faults.allocates() && Effect::Faults.faults());
564 assert!(Effect::Allocates.allocates() && !Effect::Allocates.faults());
565 assert!(Effect::AllocatesAndFaults.allocates() && Effect::AllocatesAndFaults.faults());
566 }
567
568 #[test]
576 fn no_overflow_alternative_declares_that_it_faults() {
577 use RuntimeSymbol::*;
578 for sym in [
579 IntWrappingAdd,
580 IntSaturatingAdd,
581 IntCheckedAdd,
582 IntWrappingSub,
583 IntSaturatingSub,
584 IntCheckedSub,
585 IntWrappingMul,
586 IntSaturatingMul,
587 IntCheckedMul,
588 ] {
589 assert_eq!(
590 sym.sig().effect,
591 Effect::Allocates,
592 "`{}` answers a fresh number and cannot fault (§4.12)",
593 sym.name()
594 );
595 }
596 }
597
598 #[test]
607 fn the_to_text_family_allocates_and_cannot_fault() {
608 for sym in [
609 RuntimeSymbol::IntToText,
610 RuntimeSymbol::FloatToText,
611 RuntimeSymbol::CharToText,
612 ] {
613 assert_eq!(
614 sym.sig().effect,
615 Effect::Allocates,
616 "`{}` renders a payload validated at construction and answers a \
617 fresh Text; there is nothing for it to fault on",
618 sym.name()
619 );
620 }
621 }
622
623 #[test]
635 fn a_holes_renderer_allocates_and_cannot_fault() {
636 assert_eq!(RuntimeSymbol::ValueToText.sig().effect, Effect::Allocates);
637 assert_eq!(RuntimeSymbol::TextConcat.sig().effect, Effect::Allocates);
638 assert_eq!(RuntimeSymbol::WriteStdout.sig().effect, Effect::Pure);
641 }
642
643 #[test]
650 fn reversal_cannot_fault_where_ordering_can() {
651 assert_eq!(RuntimeSymbol::VecReversed.sig().effect, Effect::Allocates);
652 assert_eq!(
653 RuntimeSymbol::VecSorted.sig().effect,
654 Effect::AllocatesAndFaults,
655 "the neighbour this is contrasted with still orders through `compare`"
656 );
657 }
658
659 #[test]
669 fn a_grouping_declares_the_fault_a_reversal_has_not() {
670 for sym in [RuntimeSymbol::VecChunks, RuntimeSymbol::VecWindows] {
671 assert_eq!(sym.sig().effect, Effect::AllocatesAndFaults, "{sym}");
672 assert_eq!(
673 sym.sig().params.len(),
674 3,
675 "{sym} takes the context, the receiver and the size"
676 );
677 }
678 assert_eq!(
679 RuntimeSymbol::VecReversed.sig().effect,
680 Effect::Allocates,
681 "the neighbour this is contrasted with still has nothing to refuse"
682 );
683 }
684
685 #[test]
699 fn a_sized_constructor_declares_that_it_faults() {
700 for sym in [RuntimeSymbol::VecFilled, RuntimeSymbol::GridFilled] {
701 assert_eq!(
702 sym.sig().effect,
703 Effect::AllocatesAndFaults,
704 "`{}` refuses a negative or oversized extent, and only a \
705 declared fault gets a `CheckFault` to observe it",
706 sym.name()
707 );
708 }
709 assert_eq!(
710 RuntimeSymbol::VecNew.sig().effect,
711 Effect::Allocates,
712 "the empty form has no size to refuse, and marking it faulting \
713 would put a dead check after every `Vec()`"
714 );
715 assert_eq!(
717 RuntimeSymbol::VecFilled.sig().params,
718 &[AbiKind::Ctx, AbiKind::Ptr, AbiKind::Gc, AbiKind::Gc]
719 );
720 assert_eq!(
721 RuntimeSymbol::GridFilled.sig().params,
722 &[
723 AbiKind::Ctx,
724 AbiKind::Ptr,
725 AbiKind::Gc,
726 AbiKind::Gc,
727 AbiKind::Gc
728 ]
729 );
730 }
731
732 #[test]
750 fn alloc_text_trusts_its_bytes_and_the_row_says_so() {
751 assert_eq!(
752 RuntimeSymbol::AllocText.sig().effect,
753 Effect::Allocates,
754 "`praxis_alloc_text`'s UTF-8 requirement is its caller's precondition \
755 (ADR-111); declaring it faulting puts a check back after every text \
756 literal and takes `Text` back out of the ADR-108 hoist"
757 );
758 assert!(
761 RuntimeSymbol::GetInput.faults(),
762 "`praxis_get_input` holds raw host bytes and raises `InvalidText` \
763 itself, so the fault still lands at the `read`"
764 );
765 }
766
767 #[test]
772 fn narrow_and_faulting_rows_are_recorded_exactly() {
773 assert_eq!(
774 RuntimeSymbol::RecordField.sig().params,
775 &[AbiKind::Ctx, AbiKind::Gc, AbiKind::RawU32]
776 );
777 assert_eq!(
778 RuntimeSymbol::RecordSetField.sig().params,
779 &[AbiKind::Ctx, AbiKind::Gc, AbiKind::RawU32, AbiKind::Gc]
780 );
781
782 for sym in [
783 RuntimeSymbol::IntAdd,
784 RuntimeSymbol::IntSub,
785 RuntimeSymbol::IntMul,
786 RuntimeSymbol::IntDiv,
787 RuntimeSymbol::IntRem,
788 RuntimeSymbol::IntNeg,
789 ] {
790 assert_eq!(sym.sig().effect, Effect::AllocatesAndFaults, "{sym}");
791 }
792 for sym in [
795 RuntimeSymbol::IntEq,
796 RuntimeSymbol::IntLt,
797 RuntimeSymbol::IntGe,
798 ] {
799 assert_eq!(sym.sig().effect, Effect::Pure, "{sym}");
800 }
801 }
802}