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 AStarDistance = "praxis_a_star_distance": (Ctx, Gc, Gc, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
219 AStarPath = "praxis_a_star_path": (Ctx, Gc, Gc, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
220 Bfs = "praxis_bfs": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
221 BfsDistance = "praxis_bfs_distance": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
222 BfsPath = "praxis_bfs_path": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
223 BitsetContains = "praxis_bitset_contains": (Ctx, Gc, Gc) -> RawI64, Pure;
229 BitsetInsert = "praxis_bitset_insert": (Ctx, Gc, Gc) -> GcUnit, AllocatesAndFaults;
230 BitsetIsEmpty = "praxis_bitset_is_empty": (Ctx, Gc) -> Gc, Pure;
231 BitsetItems = "praxis_bitset_items": (Ctx, Gc) -> Gc, Allocates;
232 BitsetLen = "praxis_bitset_len": (Ctx, Gc) -> Gc, Allocates;
233 BitsetNew = "praxis_bitset_new": (Ctx) -> Gc, Allocates;
234 Breakpoint = "praxis_breakpoint": (Ctx, RawU32, RawU32) -> Void, Pure;
242 BitsetRemove = "praxis_bitset_remove": (Ctx, Gc, Gc) -> GcUnit, Pure;
243 BoolLoad = "praxis_bool_load": (Ctx, Gc) -> RawI64, Pure;
244 CharLoad = "praxis_char_load": (Ctx, Gc) -> RawI64, Pure;
245 CharToInt = "praxis_char_to_int": (Ctx, Gc) -> Gc, Allocates;
246 CharToText = "praxis_char_to_text": (Ctx, Gc) -> Gc, Allocates;
252 CheckFault = "praxis_check_fault": (Ctx) -> RawI64, Pure;
253 ClosureCapture = "praxis_closure_capture": (Ctx, Gc, RawI64) -> Gc, Pure;
254 ClosureFnPtr = "praxis_closure_fn_ptr": (Ctx, Gc) -> Ptr, Pure;
255 ClosureSetCapture = "praxis_closure_set_capture": (Ctx, Gc, RawI64, Gc) -> Gc, Pure;
256 CounterGet = "praxis_counter_get": (Ctx, Gc, Gc) -> Gc, Allocates;
257 CounterInc = "praxis_counter_inc": (Ctx, Gc, Gc) -> GcUnit, AllocatesAndFaults;
258 CounterKeys = "praxis_counter_keys": (Ctx, Gc) -> Gc, Allocates;
259 CounterSet = "praxis_counter_set": (Ctx, Gc, Gc, Gc) -> GcUnit, Allocates;
260 CounterValues = "praxis_counter_values": (Ctx, Gc) -> Gc, Allocates;
261 CounterIsEmpty = "praxis_counter_is_empty": (Ctx, Gc) -> Gc, Pure;
262 CounterLen = "praxis_counter_len": (Ctx, Gc) -> Gc, Allocates;
263 CounterNew = "praxis_counter_new": (Ctx, Ptr) -> Gc, Allocates;
264 DequeGet = "praxis_deque_get": (Ctx, Gc, Gc) -> Gc, Faults;
265 DequeIsEmpty = "praxis_deque_is_empty": (Ctx, Gc) -> Gc, Pure;
266 DequeLen = "praxis_deque_len": (Ctx, Gc) -> Gc, Allocates;
267 DequeNew = "praxis_deque_new": (Ctx, Ptr) -> Gc, Allocates;
268 DequePopBack = "praxis_deque_pop_back": (Ctx, Gc) -> Gc, Faults;
269 DequePopFront = "praxis_deque_pop_front": (Ctx, Gc) -> Gc, Faults;
270 DequePushBack = "praxis_deque_push_back": (Ctx, Gc, Gc) -> GcUnit, AllocatesAndFaults;
271 DequePushFront = "praxis_deque_push_front": (Ctx, Gc, Gc) -> GcUnit, AllocatesAndFaults;
272 DequeSet = "praxis_deque_set": (Ctx, Gc, Gc, Gc) -> GcUnit, Faults;
273 Dbg = "praxis_dbg": (Ctx, Gc) -> Gc, Pure;
274 Dfs = "praxis_dfs": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
275 DfsDistance = "praxis_dfs_distance": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
276 DfsPath = "praxis_dfs_path": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
277 Dijkstra = "praxis_dijkstra": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
278 DijkstraDistance = "praxis_dijkstra_distance": (Ctx, Gc, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
279 DijkstraPath = "praxis_dijkstra_path": (Ctx, Gc, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
280 EnumPayload = "praxis_enum_payload": (Ctx, Gc, RawI64) -> Gc, Pure;
281 EnumSetPayload = "praxis_enum_set_payload": (Ctx, Gc, RawI64, Gc) -> Gc, Pure;
282 EnumTag = "praxis_enum_tag": (Ctx, Gc) -> Gc, Allocates;
283 FloatAbs = "praxis_float_abs": (Ctx, Gc) -> Gc, Allocates;
284 FloatCeil = "praxis_float_ceil": (Ctx, Gc) -> Gc, Allocates;
285 FloatE = "praxis_float_e": (Ctx) -> Gc, Allocates;
286 FloatFloor = "praxis_float_floor": (Ctx, Gc) -> Gc, Allocates;
287 FloatIsInfinite = "praxis_float_is_infinite": (Ctx, Gc) -> Gc, Pure;
288 FloatIsNan = "praxis_float_is_nan": (Ctx, Gc) -> Gc, Pure;
289 FloatLoad = "praxis_float_load": (Ctx, Gc) -> RawI64, Pure;
290 FloatMax = "praxis_float_max": (Ctx, Gc, Gc) -> Gc, Allocates;
291 FloatMin = "praxis_float_min": (Ctx, Gc, Gc) -> Gc, Allocates;
292 FloatPi = "praxis_float_pi": (Ctx) -> Gc, Allocates;
293 FloatRound = "praxis_float_round": (Ctx, Gc) -> Gc, Allocates;
294 FloatSign = "praxis_float_sign": (Ctx, Gc) -> Gc, Allocates;
295 FloatSqrt = "praxis_float_sqrt": (Ctx, Gc) -> Gc, Allocates;
296 FloatToInt = "praxis_float_to_int": (Ctx, Gc) -> Gc, AllocatesAndFaults;
297 FloatToText = "praxis_float_to_text": (Ctx, Gc) -> Gc, Allocates;
298 FloodFill = "praxis_flood_fill": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
299 GetInput = "praxis_get_input": (Ctx) -> Gc, AllocatesAndFaults;
300 GridAround4 = "praxis_grid_around4": (Ctx, Gc, Gc) -> Gc, Allocates;
307 GridAround8 = "praxis_grid_around8": (Ctx, Gc, Gc) -> Gc, Allocates;
308 GridCells = "praxis_grid_cells": (Ctx, Gc) -> Gc, Allocates;
309 GridColumn = "praxis_grid_column": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
310 GridContains = "praxis_grid_contains": (Ctx, Gc, Gc, Gc) -> Gc, Pure;
311 GridCount4 = "praxis_grid_count4": (Ctx, Gc, Gc, Gc) -> Gc, Allocates;
317 GridCount4Where = "praxis_grid_count4_where": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
318 GridCount8 = "praxis_grid_count8": (Ctx, Gc, Gc, Gc) -> Gc, Allocates;
319 GridCount8Where = "praxis_grid_count8_where": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
320 GridFilled = "praxis_grid_filled": (Ctx, Ptr, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
328 GridFind = "praxis_grid_find": (Ctx, Gc, Gc) -> Gc, Allocates;
329 GridFindAll = "praxis_grid_find_all": (Ctx, Gc, Gc) -> Gc, Allocates;
330 GridGet = "praxis_grid_get": (Ctx, Gc, Gc, Gc) -> Gc, Faults;
331 GridHeight = "praxis_grid_height": (Ctx, Gc) -> Gc, Allocates;
332 GridNeighbors4 = "praxis_grid_neighbors4": (Ctx, Gc, Gc) -> Gc, Allocates;
333 GridNeighbors8 = "praxis_grid_neighbors8": (Ctx, Gc, Gc) -> Gc, Allocates;
334 GridNew = "praxis_grid_new": (Ctx, Ptr, RawI64, RawI64) -> Gc, AllocatesAndFaults;
335 GridPositions = "praxis_grid_positions": (Ctx, Gc) -> Gc, Allocates;
336 GridRotateLeft = "praxis_grid_rotate_left": (Ctx, Gc) -> Gc, Allocates;
337 GridRotateRight = "praxis_grid_rotate_right": (Ctx, Gc) -> Gc, Allocates;
338 GridRow = "praxis_grid_row": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
339 GridSet = "praxis_grid_set": (Ctx, Gc, Gc, Gc, Gc) -> GcUnit, Faults;
340 GridTranspose = "praxis_grid_transpose": (Ctx, Gc) -> Gc, Allocates;
341 GridWidth = "praxis_grid_width": (Ctx, Gc) -> Gc, Allocates;
342 IntAbs = "praxis_int_abs": (Ctx, Gc) -> Gc, AllocatesAndFaults;
343 IntAdd = "praxis_int_add": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
344 IntCheckedAdd = "praxis_int_checked_add": (Ctx, Gc, Gc) -> Gc, Allocates;
345 IntCheckedMul = "praxis_int_checked_mul": (Ctx, Gc, Gc) -> Gc, Allocates;
346 IntCheckedSub = "praxis_int_checked_sub": (Ctx, Gc, Gc) -> Gc, Allocates;
347 IntClamp = "praxis_int_clamp": (Ctx, Gc, Gc, Gc) -> Gc, Faults;
348 IntDiv = "praxis_int_div": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
349 IntEq = "praxis_int_eq": (Ctx, Gc, Gc) -> Gc, Pure;
350 IntGcd = "praxis_int_gcd": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
351 IntGe = "praxis_int_ge": (Ctx, Gc, Gc) -> Gc, Pure;
352 IntGt = "praxis_int_gt": (Ctx, Gc, Gc) -> Gc, Pure;
353 IntLcm = "praxis_int_lcm": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
354 IntLe = "praxis_int_le": (Ctx, Gc, Gc) -> Gc, Pure;
355 IntLoad = "praxis_int_load": (Ctx, Gc) -> RawI64, Pure;
356 IntLt = "praxis_int_lt": (Ctx, Gc, Gc) -> Gc, Pure;
357 IntMax = "praxis_int_max": (Ctx, Gc, Gc) -> Gc, Pure;
358 IntMin = "praxis_int_min": (Ctx, Gc, Gc) -> Gc, Pure;
359 IntMul = "praxis_int_mul": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
360 IntNe = "praxis_int_ne": (Ctx, Gc, Gc) -> Gc, Pure;
361 IntNeg = "praxis_int_neg": (Ctx, Gc) -> Gc, AllocatesAndFaults;
362 IntRem = "praxis_int_rem": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
363 IntSaturatingAdd = "praxis_int_saturating_add": (Ctx, Gc, Gc) -> Gc, Allocates;
364 IntSaturatingMul = "praxis_int_saturating_mul": (Ctx, Gc, Gc) -> Gc, Allocates;
365 IntSaturatingSub = "praxis_int_saturating_sub": (Ctx, Gc, Gc) -> Gc, Allocates;
366 IntSign = "praxis_int_sign": (Ctx, Gc) -> Gc, Allocates;
367 IntSub = "praxis_int_sub": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
368 IntToChar = "praxis_int_to_char": (Ctx, Gc) -> Gc, AllocatesAndFaults;
369 IntToFloat = "praxis_int_to_float": (Ctx, Gc) -> Gc, Allocates;
370 IntToText = "praxis_int_to_text": (Ctx, Gc) -> Gc, Allocates;
372 IntWrappingAdd = "praxis_int_wrapping_add": (Ctx, Gc, Gc) -> Gc, Allocates;
373 IntWrappingMul = "praxis_int_wrapping_mul": (Ctx, Gc, Gc) -> Gc, Allocates;
374 IntWrappingSub = "praxis_int_wrapping_sub": (Ctx, Gc, Gc) -> Gc, Allocates;
375 MapContains = "praxis_map_contains": (Ctx, Gc, Gc) -> Gc, Pure;
376 RangeGet = "praxis_range_get": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
377 RangeLen = "praxis_range_len": (Ctx, Gc) -> Gc, AllocatesAndFaults;
378 RangeNew = "praxis_range_new": (Ctx, Gc, Gc) -> Gc, Allocates;
379 RangeNewInclusive = "praxis_range_new_inclusive": (Ctx, Gc, Gc) -> Gc, Allocates;
380 MapGet = "praxis_map_get": (Ctx, Gc, Gc) -> Gc, Allocates;
381 MapIndex = "praxis_map_index": (Ctx, Gc, Gc) -> Gc, Faults;
382 MapInsert = "praxis_map_insert": (Ctx, Gc, Gc, Gc) -> GcUnit, Allocates;
383 MapIsEmpty = "praxis_map_is_empty": (Ctx, Gc) -> Gc, Pure;
384 MapKeys = "praxis_map_keys": (Ctx, Gc) -> Gc, Allocates;
385 MapLen = "praxis_map_len": (Ctx, Gc) -> Gc, Allocates;
386 MapNew = "praxis_map_new": (Ctx, Ptr) -> Gc, Allocates;
387 MapRemove = "praxis_map_remove": (Ctx, Gc, Gc) -> GcUnit, Pure;
388 MapUpdateMax = "praxis_map_update_max": (Ctx, Gc, Gc, Gc) -> GcUnit, Allocates;
389 MapValues = "praxis_map_values": (Ctx, Gc) -> Gc, Allocates;
390 MapUpdateMin = "praxis_map_update_min": (Ctx, Gc, Gc, Gc) -> GcUnit, Allocates;
391 MaxHeapIsEmpty = "praxis_max_heap_is_empty": (Ctx, Gc) -> Gc, Pure;
392 MaxHeapItems = "praxis_max_heap_items": (Ctx, Gc) -> Gc, Allocates;
393 MaxHeapLen = "praxis_max_heap_len": (Ctx, Gc) -> Gc, Allocates;
394 MaxHeapNew = "praxis_max_heap_new": (Ctx, Ptr) -> Gc, Allocates;
395 MaxHeapPeek = "praxis_max_heap_peek": (Ctx, Gc) -> Gc, Faults;
396 MaxHeapPop = "praxis_max_heap_pop": (Ctx, Gc) -> Gc, Faults;
397 MaxHeapPush = "praxis_max_heap_push": (Ctx, Gc, Gc) -> GcUnit, Allocates;
398 MinHeapIsEmpty = "praxis_min_heap_is_empty": (Ctx, Gc) -> Gc, Pure;
399 MinHeapItems = "praxis_min_heap_items": (Ctx, Gc) -> Gc, Allocates;
400 MinHeapLen = "praxis_min_heap_len": (Ctx, Gc) -> Gc, Allocates;
401 MinHeapNew = "praxis_min_heap_new": (Ctx, Ptr) -> Gc, Allocates;
402 MinHeapPeek = "praxis_min_heap_peek": (Ctx, Gc) -> Gc, Faults;
403 MinHeapPop = "praxis_min_heap_pop": (Ctx, Gc) -> Gc, Faults;
404 MinHeapPush = "praxis_min_heap_push": (Ctx, Gc, Gc) -> GcUnit, Allocates;
405 Panic = "praxis_panic": (Ctx, Gc) -> GcUnit, Faults;
406 RaiseDivByZeroIf = "praxis_raise_div_by_zero_if": (Ctx, RawI64) -> Void, Faults;
407 RaiseEmptyCollection = "praxis_raise_empty_collection": (Ctx) -> GcUnit, Faults;
408 RaiseIntOverflowIf = "praxis_raise_int_overflow_if": (Ctx, RawI64) -> Void, Faults;
409 RaiseStackOverflow = "praxis_raise_stack_overflow": (Ctx) -> Void, Faults;
410 RecordField = "praxis_record_field": (Ctx, Gc, RawU32) -> Gc, Pure;
411 RecordSetField = "praxis_record_set_field": (Ctx, Gc, RawU32, Gc) -> Gc, Pure;
412 RunParser = "praxis_run_parser": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
413 SetContains = "praxis_set_contains": (Ctx, Gc, Gc) -> Gc, Pure;
414 SetInsert = "praxis_set_insert": (Ctx, Gc, Gc) -> GcUnit, Allocates;
415 SetIsEmpty = "praxis_set_is_empty": (Ctx, Gc) -> Gc, Pure;
416 SetItems = "praxis_set_items": (Ctx, Gc) -> Gc, Allocates;
417 SetLen = "praxis_set_len": (Ctx, Gc) -> Gc, Allocates;
418 SetNew = "praxis_set_new": (Ctx, Ptr) -> Gc, Allocates;
419 SetRemove = "praxis_set_remove": (Ctx, Gc, Gc) -> GcUnit, Pure;
420 SnapshotDebugChain = "praxis_snapshot_debug_chain": (Ctx) -> Void, Pure;
421 StructEq = "praxis_struct_eq": (Ctx, Gc, Gc) -> RawI64, Pure;
422 TextConcat = "praxis_text_concat": (Ctx, Gc, Gc) -> Gc, Allocates;
423 TextGet = "praxis_text_get": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
424 TextFloat = "praxis_text_float": (Ctx, Gc) -> Gc, Allocates;
425 TextInt = "praxis_text_int": (Ctx, Gc) -> Gc, Allocates;
426 TextIsEmpty = "praxis_text_is_empty": (Ctx, Gc) -> Gc, Pure;
427 TextLen = "praxis_text_len": (Ctx, Gc) -> Gc, Allocates;
428 TupleGet = "praxis_tuple_get": (Ctx, Gc, RawI64) -> Gc, Pure;
429 TupleSet = "praxis_tuple_set": (Ctx, Gc, RawI64, Gc) -> Gc, Pure;
430 ValueCmp = "praxis_value_cmp": (Ctx, Gc, Gc) -> RawI64, Faults;
431 ValueToText = "praxis_value_to_text": (Ctx, Gc) -> Gc, Allocates;
437 VarCellGet = "praxis_var_cell_get": (Ctx, Gc) -> Gc, Pure;
438 VarCellSet = "praxis_var_cell_set": (Ctx, Gc, Gc) -> Gc, Pure;
439 VecChunks = "praxis_vec_chunks": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
447 VecFilled = "praxis_vec_filled": (Ctx, Ptr, Gc, Gc) -> Gc, AllocatesAndFaults;
451 VecFrequencies = "praxis_vec_frequencies": (Ctx, Gc) -> Gc, Allocates;
452 VecGet = "praxis_vec_get": (Ctx, Gc, Gc) -> Gc, Faults;
453 VecIsEmpty = "praxis_vec_is_empty": (Ctx, Gc) -> Gc, Pure;
454 VecJoin = "praxis_vec_join": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
460 VecLen = "praxis_vec_len": (Ctx, Gc) -> Gc, Allocates;
461 VecNew = "praxis_vec_new": (Ctx, Ptr) -> Gc, Allocates;
462 VecPush = "praxis_vec_push": (Ctx, Gc, Gc) -> GcUnit, AllocatesAndFaults;
463 VecReversed = "praxis_vec_reversed": (Ctx, Gc) -> Gc, Allocates;
468 VecSet = "praxis_vec_set": (Ctx, Gc, Gc, Gc) -> GcUnit, Faults;
469 VecSorted = "praxis_vec_sorted": (Ctx, Gc) -> Gc, AllocatesAndFaults;
475 VecSortedByKey = "praxis_vec_sorted_by_key": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
479 VecToText = "praxis_vec_to_text": (Ctx, Gc) -> Gc, AllocatesAndFaults;
480 VecUnique = "praxis_vec_unique": (Ctx, Gc) -> Gc, Allocates;
481 VecWindows = "praxis_vec_windows": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
483 WriteStdout = "praxis_write_stdout": (Ctx, Gc) -> GcUnit, Pure;
484}
485
486const _: () = {
497 assert!(!RuntimeSymbol::ALL.is_empty());
500
501 let mut i = 0;
502 while i < RuntimeSymbol::ALL.len() {
503 let sym = RuntimeSymbol::ALL[i];
504 let sig = sym.sig();
505
506 assert!(matches!(sig.params[0], AbiKind::Ctx));
511
512 assert!(!(matches!(sig.ret, AbiRet::Void) && sig.effect.allocates()));
516
517 assert!(sig.effect.allocates() == sym.allocates());
520 assert!(sig.effect.faults() == sym.faults());
521
522 i += 1;
530 }
531};
532
533#[cfg(test)]
534mod tests {
535 use super::*;
536 use std::collections::HashSet;
537
538 #[test]
542 fn names_are_unique_and_well_formed() {
543 let mut seen = HashSet::new();
544 for &sym in RuntimeSymbol::ALL {
545 assert!(
546 sym.name().starts_with("praxis_"),
547 "{sym} is not a praxis_* symbol"
548 );
549 assert!(seen.insert(sym.name()), "duplicate symbol name {sym}");
550 }
551 assert_eq!(seen.len(), RuntimeSymbol::ALL.len());
552 }
553
554 #[test]
557 fn from_name_round_trips_every_symbol() {
558 for &sym in RuntimeSymbol::ALL {
559 assert_eq!(RuntimeSymbol::from_name(sym.name()), Some(sym));
560 }
561 assert_eq!(RuntimeSymbol::from_name("praxis_not_a_symbol"), None);
562 }
563
564 #[test]
568 fn every_symbol_leads_with_the_context_pointer() {
569 for &sym in RuntimeSymbol::ALL {
570 let sig = sym.sig();
571 assert_eq!(
572 sig.params.first(),
573 Some(&AbiKind::Ctx),
574 "{sym} does not take ctx first"
575 );
576 assert!(
577 !sig.params[1..].contains(&AbiKind::Ctx),
578 "{sym} takes ctx more than once"
579 );
580 }
581 }
582
583 #[test]
584 fn effect_queries_agree_with_the_variants() {
585 assert!(!Effect::Pure.allocates() && !Effect::Pure.faults());
586 assert!(!Effect::Faults.allocates() && Effect::Faults.faults());
587 assert!(Effect::Allocates.allocates() && !Effect::Allocates.faults());
588 assert!(Effect::AllocatesAndFaults.allocates() && Effect::AllocatesAndFaults.faults());
589 }
590
591 #[test]
599 fn no_overflow_alternative_declares_that_it_faults() {
600 use RuntimeSymbol::*;
601 for sym in [
602 IntWrappingAdd,
603 IntSaturatingAdd,
604 IntCheckedAdd,
605 IntWrappingSub,
606 IntSaturatingSub,
607 IntCheckedSub,
608 IntWrappingMul,
609 IntSaturatingMul,
610 IntCheckedMul,
611 ] {
612 assert_eq!(
613 sym.sig().effect,
614 Effect::Allocates,
615 "`{}` answers a fresh number and cannot fault (§4.12)",
616 sym.name()
617 );
618 }
619 }
620
621 #[test]
630 fn the_to_text_family_allocates_and_cannot_fault() {
631 for sym in [
632 RuntimeSymbol::IntToText,
633 RuntimeSymbol::FloatToText,
634 RuntimeSymbol::CharToText,
635 ] {
636 assert_eq!(
637 sym.sig().effect,
638 Effect::Allocates,
639 "`{}` renders a payload validated at construction and answers a \
640 fresh Text; there is nothing for it to fault on",
641 sym.name()
642 );
643 }
644 }
645
646 #[test]
658 fn a_holes_renderer_allocates_and_cannot_fault() {
659 assert_eq!(RuntimeSymbol::ValueToText.sig().effect, Effect::Allocates);
660 assert_eq!(RuntimeSymbol::TextConcat.sig().effect, Effect::Allocates);
661 assert_eq!(RuntimeSymbol::WriteStdout.sig().effect, Effect::Pure);
664 }
665
666 #[test]
673 fn reversal_cannot_fault_where_ordering_can() {
674 assert_eq!(RuntimeSymbol::VecReversed.sig().effect, Effect::Allocates);
675 assert_eq!(
676 RuntimeSymbol::VecSorted.sig().effect,
677 Effect::AllocatesAndFaults,
678 "the neighbour this is contrasted with still orders through `compare`"
679 );
680 }
681
682 #[test]
692 fn a_grouping_declares_the_fault_a_reversal_has_not() {
693 for sym in [RuntimeSymbol::VecChunks, RuntimeSymbol::VecWindows] {
694 assert_eq!(sym.sig().effect, Effect::AllocatesAndFaults, "{sym}");
695 assert_eq!(
696 sym.sig().params.len(),
697 3,
698 "{sym} takes the context, the receiver and the size"
699 );
700 }
701 assert_eq!(
702 RuntimeSymbol::VecReversed.sig().effect,
703 Effect::Allocates,
704 "the neighbour this is contrasted with still has nothing to refuse"
705 );
706 }
707
708 #[test]
722 fn a_sized_constructor_declares_that_it_faults() {
723 for sym in [RuntimeSymbol::VecFilled, RuntimeSymbol::GridFilled] {
724 assert_eq!(
725 sym.sig().effect,
726 Effect::AllocatesAndFaults,
727 "`{}` refuses a negative or oversized extent, and only a \
728 declared fault gets a `CheckFault` to observe it",
729 sym.name()
730 );
731 }
732 assert_eq!(
733 RuntimeSymbol::VecNew.sig().effect,
734 Effect::Allocates,
735 "the empty form has no size to refuse, and marking it faulting \
736 would put a dead check after every `Vec()`"
737 );
738 assert_eq!(
740 RuntimeSymbol::VecFilled.sig().params,
741 &[AbiKind::Ctx, AbiKind::Ptr, AbiKind::Gc, AbiKind::Gc]
742 );
743 assert_eq!(
744 RuntimeSymbol::GridFilled.sig().params,
745 &[
746 AbiKind::Ctx,
747 AbiKind::Ptr,
748 AbiKind::Gc,
749 AbiKind::Gc,
750 AbiKind::Gc
751 ]
752 );
753 }
754
755 #[test]
773 fn alloc_text_trusts_its_bytes_and_the_row_says_so() {
774 assert_eq!(
775 RuntimeSymbol::AllocText.sig().effect,
776 Effect::Allocates,
777 "`praxis_alloc_text`'s UTF-8 requirement is its caller's precondition \
778 (ADR-111); declaring it faulting puts a check back after every text \
779 literal and takes `Text` back out of the ADR-108 hoist"
780 );
781 assert!(
784 RuntimeSymbol::GetInput.faults(),
785 "`praxis_get_input` holds raw host bytes and raises `InvalidText` \
786 itself, so the fault still lands at the `read`"
787 );
788 }
789
790 #[test]
795 fn narrow_and_faulting_rows_are_recorded_exactly() {
796 assert_eq!(
797 RuntimeSymbol::RecordField.sig().params,
798 &[AbiKind::Ctx, AbiKind::Gc, AbiKind::RawU32]
799 );
800 assert_eq!(
801 RuntimeSymbol::RecordSetField.sig().params,
802 &[AbiKind::Ctx, AbiKind::Gc, AbiKind::RawU32, AbiKind::Gc]
803 );
804
805 for sym in [
806 RuntimeSymbol::IntAdd,
807 RuntimeSymbol::IntSub,
808 RuntimeSymbol::IntMul,
809 RuntimeSymbol::IntDiv,
810 RuntimeSymbol::IntRem,
811 RuntimeSymbol::IntNeg,
812 ] {
813 assert_eq!(sym.sig().effect, Effect::AllocatesAndFaults, "{sym}");
814 }
815 for sym in [
818 RuntimeSymbol::IntEq,
819 RuntimeSymbol::IntLt,
820 RuntimeSymbol::IntGe,
821 ] {
822 assert_eq!(sym.sig().effect, Effect::Pure, "{sym}");
823 }
824 }
825}