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 DequeUpdateMax = "praxis_deque_update_max": (Ctx, Gc, Gc, Gc) -> GcUnit, Faults;
279 DequeUpdateMin = "praxis_deque_update_min": (Ctx, Gc, Gc, Gc) -> GcUnit, Faults;
280 Dbg = "praxis_dbg": (Ctx, Gc) -> Gc, Pure;
281 Dfs = "praxis_dfs": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
282 DfsDistance = "praxis_dfs_distance": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
283 DfsPath = "praxis_dfs_path": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
284 Dijkstra = "praxis_dijkstra": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
285 DijkstraDistance = "praxis_dijkstra_distance": (Ctx, Gc, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
286 DijkstraPath = "praxis_dijkstra_path": (Ctx, Gc, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
287 EnumPayload = "praxis_enum_payload": (Ctx, Gc, RawI64) -> Gc, Pure;
288 EnumSetPayload = "praxis_enum_set_payload": (Ctx, Gc, RawI64, Gc) -> Gc, Pure;
289 EnumTag = "praxis_enum_tag": (Ctx, Gc) -> Gc, Allocates;
290 FloatAbs = "praxis_float_abs": (Ctx, Gc) -> Gc, Allocates;
291 FloatCeil = "praxis_float_ceil": (Ctx, Gc) -> Gc, Allocates;
292 FloatE = "praxis_float_e": (Ctx) -> Gc, Allocates;
293 FloatFloor = "praxis_float_floor": (Ctx, Gc) -> Gc, Allocates;
294 FloatIsInfinite = "praxis_float_is_infinite": (Ctx, Gc) -> Gc, Pure;
295 FloatIsNan = "praxis_float_is_nan": (Ctx, Gc) -> Gc, Pure;
296 FloatLoad = "praxis_float_load": (Ctx, Gc) -> RawI64, Pure;
297 FloatMax = "praxis_float_max": (Ctx, Gc, Gc) -> Gc, Allocates;
298 FloatMin = "praxis_float_min": (Ctx, Gc, Gc) -> Gc, Allocates;
299 FloatPi = "praxis_float_pi": (Ctx) -> Gc, Allocates;
300 FloatRound = "praxis_float_round": (Ctx, Gc) -> Gc, Allocates;
301 FloatSign = "praxis_float_sign": (Ctx, Gc) -> Gc, Allocates;
302 FloatSqrt = "praxis_float_sqrt": (Ctx, Gc) -> Gc, Allocates;
303 FloatToInt = "praxis_float_to_int": (Ctx, Gc) -> Gc, AllocatesAndFaults;
304 FloatToText = "praxis_float_to_text": (Ctx, Gc) -> Gc, Allocates;
305 FloodFill = "praxis_flood_fill": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
306 GetInput = "praxis_get_input": (Ctx) -> Gc, AllocatesAndFaults;
307 GridAround4 = "praxis_grid_around4": (Ctx, Gc, Gc) -> Gc, Allocates;
314 GridAround8 = "praxis_grid_around8": (Ctx, Gc, Gc) -> Gc, Allocates;
315 GridCells = "praxis_grid_cells": (Ctx, Gc) -> Gc, Allocates;
316 GridColumn = "praxis_grid_column": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
317 GridContains = "praxis_grid_contains": (Ctx, Gc, Gc, Gc) -> Gc, Pure;
318 GridCount4 = "praxis_grid_count4": (Ctx, Gc, Gc, Gc) -> Gc, Allocates;
324 GridCount4Where = "praxis_grid_count4_where": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
325 GridCount8 = "praxis_grid_count8": (Ctx, Gc, Gc, Gc) -> Gc, Allocates;
326 GridCount8Where = "praxis_grid_count8_where": (Ctx, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
327 GridFilled = "praxis_grid_filled": (Ctx, Ptr, Gc, Gc, Gc) -> Gc, AllocatesAndFaults;
335 GridFind = "praxis_grid_find": (Ctx, Gc, Gc) -> Gc, Allocates;
336 GridFindAll = "praxis_grid_find_all": (Ctx, Gc, Gc) -> Gc, Allocates;
337 GridGet = "praxis_grid_get": (Ctx, Gc, Gc, Gc) -> Gc, Faults;
338 GridHeight = "praxis_grid_height": (Ctx, Gc) -> Gc, Allocates;
339 GridNeighbors4 = "praxis_grid_neighbors4": (Ctx, Gc, Gc) -> Gc, Allocates;
340 GridNeighbors8 = "praxis_grid_neighbors8": (Ctx, Gc, Gc) -> Gc, Allocates;
341 GridNew = "praxis_grid_new": (Ctx, Ptr, RawI64, RawI64) -> Gc, AllocatesAndFaults;
342 GridPositions = "praxis_grid_positions": (Ctx, Gc) -> Gc, Allocates;
343 GridRotateLeft = "praxis_grid_rotate_left": (Ctx, Gc) -> Gc, Allocates;
344 GridRotateRight = "praxis_grid_rotate_right": (Ctx, Gc) -> Gc, Allocates;
345 GridRow = "praxis_grid_row": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
346 GridSet = "praxis_grid_set": (Ctx, Gc, Gc, Gc, Gc) -> GcUnit, Faults;
347 GridTranspose = "praxis_grid_transpose": (Ctx, Gc) -> Gc, Allocates;
348 GridUpdateMax = "praxis_grid_update_max": (Ctx, Gc, Gc, Gc, Gc) -> GcUnit, Faults;
349 GridUpdateMin = "praxis_grid_update_min": (Ctx, Gc, Gc, Gc, Gc) -> GcUnit, Faults;
350 GridWidth = "praxis_grid_width": (Ctx, Gc) -> Gc, Allocates;
351 IntAbs = "praxis_int_abs": (Ctx, Gc) -> Gc, AllocatesAndFaults;
352 IntAdd = "praxis_int_add": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
353 IntCheckedAdd = "praxis_int_checked_add": (Ctx, Gc, Gc) -> Gc, Allocates;
354 IntCheckedMul = "praxis_int_checked_mul": (Ctx, Gc, Gc) -> Gc, Allocates;
355 IntCheckedSub = "praxis_int_checked_sub": (Ctx, Gc, Gc) -> Gc, Allocates;
356 IntClamp = "praxis_int_clamp": (Ctx, Gc, Gc, Gc) -> Gc, Faults;
357 IntDiv = "praxis_int_div": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
358 IntEq = "praxis_int_eq": (Ctx, Gc, Gc) -> Gc, Pure;
359 IntGcd = "praxis_int_gcd": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
360 IntGe = "praxis_int_ge": (Ctx, Gc, Gc) -> Gc, Pure;
361 IntGt = "praxis_int_gt": (Ctx, Gc, Gc) -> Gc, Pure;
362 IntLcm = "praxis_int_lcm": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
363 IntLe = "praxis_int_le": (Ctx, Gc, Gc) -> Gc, Pure;
364 IntLoad = "praxis_int_load": (Ctx, Gc) -> RawI64, Pure;
365 IntLt = "praxis_int_lt": (Ctx, Gc, Gc) -> Gc, Pure;
366 IntMax = "praxis_int_max": (Ctx, Gc, Gc) -> Gc, Pure;
367 IntMin = "praxis_int_min": (Ctx, Gc, Gc) -> Gc, Pure;
368 IntMul = "praxis_int_mul": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
369 IntNe = "praxis_int_ne": (Ctx, Gc, Gc) -> Gc, Pure;
370 IntNeg = "praxis_int_neg": (Ctx, Gc) -> Gc, AllocatesAndFaults;
371 IntRem = "praxis_int_rem": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
372 IntSaturatingAdd = "praxis_int_saturating_add": (Ctx, Gc, Gc) -> Gc, Allocates;
373 IntSaturatingMul = "praxis_int_saturating_mul": (Ctx, Gc, Gc) -> Gc, Allocates;
374 IntSaturatingSub = "praxis_int_saturating_sub": (Ctx, Gc, Gc) -> Gc, Allocates;
375 IntSign = "praxis_int_sign": (Ctx, Gc) -> Gc, Allocates;
376 IntSub = "praxis_int_sub": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
377 IntToChar = "praxis_int_to_char": (Ctx, Gc) -> Gc, AllocatesAndFaults;
378 IntToFloat = "praxis_int_to_float": (Ctx, Gc) -> Gc, Allocates;
379 IntToText = "praxis_int_to_text": (Ctx, Gc) -> Gc, Allocates;
381 IntWrappingAdd = "praxis_int_wrapping_add": (Ctx, Gc, Gc) -> Gc, Allocates;
382 IntWrappingMul = "praxis_int_wrapping_mul": (Ctx, Gc, Gc) -> Gc, Allocates;
383 IntWrappingSub = "praxis_int_wrapping_sub": (Ctx, Gc, Gc) -> Gc, Allocates;
384 MapContains = "praxis_map_contains": (Ctx, Gc, Gc) -> Gc, Pure;
385 RangeGet = "praxis_range_get": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
386 RangeLen = "praxis_range_len": (Ctx, Gc) -> Gc, AllocatesAndFaults;
387 RangeNew = "praxis_range_new": (Ctx, Gc, Gc) -> Gc, Allocates;
388 RangeNewInclusive = "praxis_range_new_inclusive": (Ctx, Gc, Gc) -> Gc, Allocates;
389 MapGet = "praxis_map_get": (Ctx, Gc, Gc) -> Gc, Allocates;
390 MapIndex = "praxis_map_index": (Ctx, Gc, Gc) -> Gc, Faults;
391 MapInsert = "praxis_map_insert": (Ctx, Gc, Gc, Gc) -> GcUnit, Allocates;
392 MapIsEmpty = "praxis_map_is_empty": (Ctx, Gc) -> Gc, Pure;
393 MapKeys = "praxis_map_keys": (Ctx, Gc) -> Gc, Allocates;
394 MapLen = "praxis_map_len": (Ctx, Gc) -> Gc, Allocates;
395 MapNew = "praxis_map_new": (Ctx, Ptr) -> Gc, Allocates;
396 MapRemove = "praxis_map_remove": (Ctx, Gc, Gc) -> GcUnit, Pure;
397 MapUpdateMax = "praxis_map_update_max": (Ctx, Gc, Gc, Gc) -> GcUnit, Allocates;
398 MapValues = "praxis_map_values": (Ctx, Gc) -> Gc, Allocates;
399 MapUpdateMin = "praxis_map_update_min": (Ctx, Gc, Gc, Gc) -> GcUnit, Allocates;
400 MaxHeapIsEmpty = "praxis_max_heap_is_empty": (Ctx, Gc) -> Gc, Pure;
401 MaxHeapItems = "praxis_max_heap_items": (Ctx, Gc) -> Gc, Allocates;
402 MaxHeapLen = "praxis_max_heap_len": (Ctx, Gc) -> Gc, Allocates;
403 MaxHeapNew = "praxis_max_heap_new": (Ctx, Ptr) -> Gc, Allocates;
404 MaxHeapPeek = "praxis_max_heap_peek": (Ctx, Gc) -> Gc, Faults;
405 MaxHeapPop = "praxis_max_heap_pop": (Ctx, Gc) -> Gc, Faults;
406 MaxHeapPush = "praxis_max_heap_push": (Ctx, Gc, Gc) -> GcUnit, Allocates;
407 MinHeapIsEmpty = "praxis_min_heap_is_empty": (Ctx, Gc) -> Gc, Pure;
408 MinHeapItems = "praxis_min_heap_items": (Ctx, Gc) -> Gc, Allocates;
409 MinHeapLen = "praxis_min_heap_len": (Ctx, Gc) -> Gc, Allocates;
410 MinHeapNew = "praxis_min_heap_new": (Ctx, Ptr) -> Gc, Allocates;
411 MinHeapPeek = "praxis_min_heap_peek": (Ctx, Gc) -> Gc, Faults;
412 MinHeapPop = "praxis_min_heap_pop": (Ctx, Gc) -> Gc, Faults;
413 MinHeapPush = "praxis_min_heap_push": (Ctx, Gc, Gc) -> GcUnit, Allocates;
414 Panic = "praxis_panic": (Ctx, Gc) -> GcUnit, Faults;
415 RaiseDivByZeroIf = "praxis_raise_div_by_zero_if": (Ctx, RawI64) -> Void, Faults;
416 RaiseEmptyCollection = "praxis_raise_empty_collection": (Ctx) -> GcUnit, Faults;
417 RaiseIntOverflowIf = "praxis_raise_int_overflow_if": (Ctx, RawI64) -> Void, Faults;
418 RaiseStackOverflow = "praxis_raise_stack_overflow": (Ctx) -> Void, Faults;
419 RecordField = "praxis_record_field": (Ctx, Gc, RawU32) -> Gc, Pure;
420 RecordSetField = "praxis_record_set_field": (Ctx, Gc, RawU32, Gc) -> Gc, Pure;
421 RunParser = "praxis_run_parser": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
422 SetContains = "praxis_set_contains": (Ctx, Gc, Gc) -> Gc, Pure;
423 SetInsert = "praxis_set_insert": (Ctx, Gc, Gc) -> GcUnit, Allocates;
424 SetIsEmpty = "praxis_set_is_empty": (Ctx, Gc) -> Gc, Pure;
425 SetItems = "praxis_set_items": (Ctx, Gc) -> Gc, Allocates;
426 SetLen = "praxis_set_len": (Ctx, Gc) -> Gc, Allocates;
427 SetNew = "praxis_set_new": (Ctx, Ptr) -> Gc, Allocates;
428 SetRemove = "praxis_set_remove": (Ctx, Gc, Gc) -> GcUnit, Pure;
429 SnapshotDebugChain = "praxis_snapshot_debug_chain": (Ctx) -> Void, Pure;
430 StructEq = "praxis_struct_eq": (Ctx, Gc, Gc) -> RawI64, Pure;
431 TextConcat = "praxis_text_concat": (Ctx, Gc, Gc) -> Gc, Allocates;
432 TextGet = "praxis_text_get": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
433 TextFloat = "praxis_text_float": (Ctx, Gc) -> Gc, Allocates;
434 TextInt = "praxis_text_int": (Ctx, Gc) -> Gc, Allocates;
435 TextIsEmpty = "praxis_text_is_empty": (Ctx, Gc) -> Gc, Pure;
436 TextLen = "praxis_text_len": (Ctx, Gc) -> Gc, Allocates;
437 TupleGet = "praxis_tuple_get": (Ctx, Gc, RawI64) -> Gc, Pure;
438 TupleSet = "praxis_tuple_set": (Ctx, Gc, RawI64, Gc) -> Gc, Pure;
439 ValueCmp = "praxis_value_cmp": (Ctx, Gc, Gc) -> RawI64, Faults;
440 ValueKeepMax = "praxis_value_keep_max": (Ctx, Gc, Gc) -> Gc, Pure;
446 ValueKeepMin = "praxis_value_keep_min": (Ctx, Gc, Gc) -> Gc, Pure;
447 ValueToText = "praxis_value_to_text": (Ctx, Gc) -> Gc, Allocates;
453 VarCellGet = "praxis_var_cell_get": (Ctx, Gc) -> Gc, Pure;
454 VarCellSet = "praxis_var_cell_set": (Ctx, Gc, Gc) -> Gc, Pure;
455 VecChunks = "praxis_vec_chunks": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
463 VecFilled = "praxis_vec_filled": (Ctx, Ptr, Gc, Gc) -> Gc, AllocatesAndFaults;
467 VecFrequencies = "praxis_vec_frequencies": (Ctx, Gc) -> Gc, Allocates;
468 VecGet = "praxis_vec_get": (Ctx, Gc, Gc) -> Gc, Faults;
469 VecIsEmpty = "praxis_vec_is_empty": (Ctx, Gc) -> Gc, Pure;
470 VecJoin = "praxis_vec_join": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
476 VecLen = "praxis_vec_len": (Ctx, Gc) -> Gc, Allocates;
477 VecNew = "praxis_vec_new": (Ctx, Ptr) -> Gc, Allocates;
478 VecPush = "praxis_vec_push": (Ctx, Gc, Gc) -> GcUnit, AllocatesAndFaults;
479 VecReversed = "praxis_vec_reversed": (Ctx, Gc) -> Gc, Allocates;
484 VecSet = "praxis_vec_set": (Ctx, Gc, Gc, Gc) -> GcUnit, Faults;
485 VecSorted = "praxis_vec_sorted": (Ctx, Gc) -> Gc, AllocatesAndFaults;
491 VecSortedByKey = "praxis_vec_sorted_by_key": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
495 VecToText = "praxis_vec_to_text": (Ctx, Gc) -> Gc, AllocatesAndFaults;
496 VecUnique = "praxis_vec_unique": (Ctx, Gc) -> Gc, Allocates;
497 VecUpdateMax = "praxis_vec_update_max": (Ctx, Gc, Gc, Gc) -> GcUnit, Faults;
498 VecUpdateMin = "praxis_vec_update_min": (Ctx, Gc, Gc, Gc) -> GcUnit, Faults;
499 VecWindows = "praxis_vec_windows": (Ctx, Gc, Gc) -> Gc, AllocatesAndFaults;
501 WriteStdout = "praxis_write_stdout": (Ctx, Gc) -> GcUnit, Pure;
502}
503
504const _: () = {
515 assert!(!RuntimeSymbol::ALL.is_empty());
518
519 let mut i = 0;
520 while i < RuntimeSymbol::ALL.len() {
521 let sym = RuntimeSymbol::ALL[i];
522 let sig = sym.sig();
523
524 assert!(matches!(sig.params[0], AbiKind::Ctx));
529
530 assert!(!(matches!(sig.ret, AbiRet::Void) && sig.effect.allocates()));
534
535 assert!(sig.effect.allocates() == sym.allocates());
538 assert!(sig.effect.faults() == sym.faults());
539
540 i += 1;
548 }
549};
550
551#[cfg(test)]
552mod tests {
553 use super::*;
554 use std::collections::HashSet;
555
556 #[test]
560 fn names_are_unique_and_well_formed() {
561 let mut seen = HashSet::new();
562 for &sym in RuntimeSymbol::ALL {
563 assert!(
564 sym.name().starts_with("praxis_"),
565 "{sym} is not a praxis_* symbol"
566 );
567 assert!(seen.insert(sym.name()), "duplicate symbol name {sym}");
568 }
569 assert_eq!(seen.len(), RuntimeSymbol::ALL.len());
570 }
571
572 #[test]
575 fn from_name_round_trips_every_symbol() {
576 for &sym in RuntimeSymbol::ALL {
577 assert_eq!(RuntimeSymbol::from_name(sym.name()), Some(sym));
578 }
579 assert_eq!(RuntimeSymbol::from_name("praxis_not_a_symbol"), None);
580 }
581
582 #[test]
586 fn every_symbol_leads_with_the_context_pointer() {
587 for &sym in RuntimeSymbol::ALL {
588 let sig = sym.sig();
589 assert_eq!(
590 sig.params.first(),
591 Some(&AbiKind::Ctx),
592 "{sym} does not take ctx first"
593 );
594 assert!(
595 !sig.params[1..].contains(&AbiKind::Ctx),
596 "{sym} takes ctx more than once"
597 );
598 }
599 }
600
601 #[test]
602 fn effect_queries_agree_with_the_variants() {
603 assert!(!Effect::Pure.allocates() && !Effect::Pure.faults());
604 assert!(!Effect::Faults.allocates() && Effect::Faults.faults());
605 assert!(Effect::Allocates.allocates() && !Effect::Allocates.faults());
606 assert!(Effect::AllocatesAndFaults.allocates() && Effect::AllocatesAndFaults.faults());
607 }
608
609 #[test]
617 fn no_overflow_alternative_declares_that_it_faults() {
618 use RuntimeSymbol::*;
619 for sym in [
620 IntWrappingAdd,
621 IntSaturatingAdd,
622 IntCheckedAdd,
623 IntWrappingSub,
624 IntSaturatingSub,
625 IntCheckedSub,
626 IntWrappingMul,
627 IntSaturatingMul,
628 IntCheckedMul,
629 ] {
630 assert_eq!(
631 sym.sig().effect,
632 Effect::Allocates,
633 "`{}` answers a fresh number and cannot fault (§4.12)",
634 sym.name()
635 );
636 }
637 }
638
639 #[test]
648 fn the_to_text_family_allocates_and_cannot_fault() {
649 for sym in [
650 RuntimeSymbol::IntToText,
651 RuntimeSymbol::FloatToText,
652 RuntimeSymbol::CharToText,
653 ] {
654 assert_eq!(
655 sym.sig().effect,
656 Effect::Allocates,
657 "`{}` renders a payload validated at construction and answers a \
658 fresh Text; there is nothing for it to fault on",
659 sym.name()
660 );
661 }
662 }
663
664 #[test]
676 fn a_holes_renderer_allocates_and_cannot_fault() {
677 assert_eq!(RuntimeSymbol::ValueToText.sig().effect, Effect::Allocates);
678 assert_eq!(RuntimeSymbol::TextConcat.sig().effect, Effect::Allocates);
679 assert_eq!(RuntimeSymbol::WriteStdout.sig().effect, Effect::Pure);
682 }
683
684 #[test]
691 fn reversal_cannot_fault_where_ordering_can() {
692 assert_eq!(RuntimeSymbol::VecReversed.sig().effect, Effect::Allocates);
693 assert_eq!(
694 RuntimeSymbol::VecSorted.sig().effect,
695 Effect::AllocatesAndFaults,
696 "the neighbour this is contrasted with still orders through `compare`"
697 );
698 }
699
700 #[test]
710 fn a_grouping_declares_the_fault_a_reversal_has_not() {
711 for sym in [RuntimeSymbol::VecChunks, RuntimeSymbol::VecWindows] {
712 assert_eq!(sym.sig().effect, Effect::AllocatesAndFaults, "{sym}");
713 assert_eq!(
714 sym.sig().params.len(),
715 3,
716 "{sym} takes the context, the receiver and the size"
717 );
718 }
719 assert_eq!(
720 RuntimeSymbol::VecReversed.sig().effect,
721 Effect::Allocates,
722 "the neighbour this is contrasted with still has nothing to refuse"
723 );
724 }
725
726 #[test]
740 fn a_sized_constructor_declares_that_it_faults() {
741 for sym in [RuntimeSymbol::VecFilled, RuntimeSymbol::GridFilled] {
742 assert_eq!(
743 sym.sig().effect,
744 Effect::AllocatesAndFaults,
745 "`{}` refuses a negative or oversized extent, and only a \
746 declared fault gets a `CheckFault` to observe it",
747 sym.name()
748 );
749 }
750 assert_eq!(
751 RuntimeSymbol::VecNew.sig().effect,
752 Effect::Allocates,
753 "the empty form has no size to refuse, and marking it faulting \
754 would put a dead check after every `Vec()`"
755 );
756 assert_eq!(
758 RuntimeSymbol::VecFilled.sig().params,
759 &[AbiKind::Ctx, AbiKind::Ptr, AbiKind::Gc, AbiKind::Gc]
760 );
761 assert_eq!(
762 RuntimeSymbol::GridFilled.sig().params,
763 &[
764 AbiKind::Ctx,
765 AbiKind::Ptr,
766 AbiKind::Gc,
767 AbiKind::Gc,
768 AbiKind::Gc
769 ]
770 );
771 }
772
773 #[test]
791 fn alloc_text_trusts_its_bytes_and_the_row_says_so() {
792 assert_eq!(
793 RuntimeSymbol::AllocText.sig().effect,
794 Effect::Allocates,
795 "`praxis_alloc_text`'s UTF-8 requirement is its caller's precondition \
796 (ADR-111); declaring it faulting puts a check back after every text \
797 literal and takes `Text` back out of the ADR-108 hoist"
798 );
799 assert!(
802 RuntimeSymbol::GetInput.faults(),
803 "`praxis_get_input` holds raw host bytes and raises `InvalidText` \
804 itself, so the fault still lands at the `read`"
805 );
806 }
807
808 #[test]
813 fn narrow_and_faulting_rows_are_recorded_exactly() {
814 assert_eq!(
815 RuntimeSymbol::RecordField.sig().params,
816 &[AbiKind::Ctx, AbiKind::Gc, AbiKind::RawU32]
817 );
818 assert_eq!(
819 RuntimeSymbol::RecordSetField.sig().params,
820 &[AbiKind::Ctx, AbiKind::Gc, AbiKind::RawU32, AbiKind::Gc]
821 );
822
823 for sym in [
824 RuntimeSymbol::IntAdd,
825 RuntimeSymbol::IntSub,
826 RuntimeSymbol::IntMul,
827 RuntimeSymbol::IntDiv,
828 RuntimeSymbol::IntRem,
829 RuntimeSymbol::IntNeg,
830 ] {
831 assert_eq!(sym.sig().effect, Effect::AllocatesAndFaults, "{sym}");
832 }
833 for sym in [
836 RuntimeSymbol::IntEq,
837 RuntimeSymbol::IntLt,
838 RuntimeSymbol::IntGe,
839 ] {
840 assert_eq!(sym.sig().effect, Effect::Pure, "{sym}");
841 }
842 }
843}