1use std::collections::{BTreeMap, HashMap, HashSet};
4use std::fmt::Write as _;
5use std::future::Future;
6use std::sync::{Arc, Mutex};
7
8use async_recursion::async_recursion;
9use futures::{FutureExt, future::BoxFuture, pin_mut};
10use rexlang_ast::expr::{
11 ClassDecl, Decl, DeclareFnDecl, Expr, FnDecl, InstanceDecl, Pattern, Scope, Symbol, TypeDecl,
12 sym, sym_eq,
13};
14use rexlang_lexer::span::Span;
15use rexlang_typesystem::{
16 AdtDecl, BuiltinTypeId, CollectAdtsError, Instance, Predicate, PreparedInstanceDecl, Scheme,
17 Subst, Type, TypeError, TypeKind, TypeSystem, TypeVarSupply, TypedExpr, TypedExprKind, Types,
18 collect_adts_in_types, compose_subst, entails, instantiate, unify,
19};
20use rexlang_util::GasMeter;
21
22use crate::libraries::{
23 CanonicalSymbol, LibraryExports, LibraryId, LibrarySystem, ResolveRequest, ResolvedLibrary,
24 SymbolKind, library_key_for_library, virtual_export_name,
25};
26use crate::prelude::{
27 inject_boolean_ops, inject_equality_ops, inject_json_primops, inject_list_builtins,
28 inject_numeric_ops, inject_option_result_builtins, inject_order_ops, inject_prelude_adts,
29 inject_show_ops,
30};
31use crate::value::{Closure, Heap, Pointer, Value, list_to_vec};
32use crate::{CancellationToken, EngineError, Env, FromPointer, IntoPointer, RexType};
33
34pub trait RexDefault<State>
35where
36 State: Clone + Send + Sync + 'static,
37{
38 fn rex_default(engine: &Engine<State>) -> Result<Pointer, EngineError>;
39}
40
41pub const ROOT_LIBRARY_NAME: &str = "__root__";
42pub const PRELUDE_LIBRARY_NAME: &str = "Prelude";
43
44#[derive(Clone, Debug, PartialEq, Eq)]
45pub enum PreludeMode {
46 Enabled,
47 Disabled,
48}
49
50#[derive(Clone, Debug)]
51pub struct EngineOptions {
52 pub prelude: PreludeMode,
53 pub default_imports: Vec<String>,
54}
55
56impl Default for EngineOptions {
57 fn default() -> Self {
58 Self {
59 prelude: PreludeMode::Enabled,
60 default_imports: vec![PRELUDE_LIBRARY_NAME.to_string()],
61 }
62 }
63}
64
65pub trait RexAdt: RexType {
67 fn rex_adt_decl<State: Clone + Send + Sync + 'static>(
68 engine: &mut Engine<State>,
69 ) -> Result<AdtDecl, EngineError>;
70
71 fn inject_rex<State: Clone + Send + Sync + 'static>(
72 engine: &mut Engine<State>,
73 ) -> Result<(), EngineError> {
74 let adt = Self::rex_adt_decl(engine)?;
75 engine.inject_adt(adt)
76 }
77}
78
79fn check_cancelled<State: Clone + Send + Sync + 'static>(
80 engine: &Engine<State>,
81) -> Result<(), EngineError> {
82 if engine.cancel.is_cancelled() {
83 Err(EngineError::Cancelled)
84 } else {
85 Ok(())
86 }
87}
88
89fn alloc_uint_literal_as<State: Clone + Send + Sync + 'static>(
90 engine: &Engine<State>,
91 value: u64,
92 typ: &Type,
93) -> Result<Pointer, EngineError> {
94 match typ.as_ref() {
95 TypeKind::Var(_) => {
96 engine
97 .heap
98 .alloc_i32(i32::try_from(value).map_err(|_| EngineError::NativeType {
99 expected: "i32".into(),
100 got: value.to_string(),
101 })?)
102 }
103 TypeKind::Con(tc) => match tc.builtin_id {
104 Some(BuiltinTypeId::U8) => {
105 engine
106 .heap
107 .alloc_u8(u8::try_from(value).map_err(|_| EngineError::NativeType {
108 expected: "u8".into(),
109 got: value.to_string(),
110 })?)
111 }
112 Some(BuiltinTypeId::U16) => {
113 engine.heap.alloc_u16(u16::try_from(value).map_err(|_| {
114 EngineError::NativeType {
115 expected: "u16".into(),
116 got: value.to_string(),
117 }
118 })?)
119 }
120 Some(BuiltinTypeId::U32) => {
121 engine.heap.alloc_u32(u32::try_from(value).map_err(|_| {
122 EngineError::NativeType {
123 expected: "u32".into(),
124 got: value.to_string(),
125 }
126 })?)
127 }
128 Some(BuiltinTypeId::U64) => engine.heap.alloc_u64(value),
129 Some(BuiltinTypeId::I8) => {
130 engine
131 .heap
132 .alloc_i8(i8::try_from(value).map_err(|_| EngineError::NativeType {
133 expected: "i8".into(),
134 got: value.to_string(),
135 })?)
136 }
137 Some(BuiltinTypeId::I16) => {
138 engine.heap.alloc_i16(i16::try_from(value).map_err(|_| {
139 EngineError::NativeType {
140 expected: "i16".into(),
141 got: value.to_string(),
142 }
143 })?)
144 }
145 Some(BuiltinTypeId::I32) => {
146 engine.heap.alloc_i32(i32::try_from(value).map_err(|_| {
147 EngineError::NativeType {
148 expected: "i32".into(),
149 got: value.to_string(),
150 }
151 })?)
152 }
153 Some(BuiltinTypeId::I64) => {
154 engine.heap.alloc_i64(i64::try_from(value).map_err(|_| {
155 EngineError::NativeType {
156 expected: "i64".into(),
157 got: value.to_string(),
158 }
159 })?)
160 }
161 _ => Err(EngineError::NativeType {
162 expected: "integral".into(),
163 got: typ.to_string(),
164 }),
165 },
166 _ => Err(EngineError::NativeType {
167 expected: "integral".into(),
168 got: typ.to_string(),
169 }),
170 }
171}
172
173fn alloc_int_literal_as<State: Clone + Send + Sync + 'static>(
174 engine: &Engine<State>,
175 value: i64,
176 typ: &Type,
177) -> Result<Pointer, EngineError> {
178 match typ.as_ref() {
179 TypeKind::Var(_) => {
180 engine
181 .heap
182 .alloc_i32(i32::try_from(value).map_err(|_| EngineError::NativeType {
183 expected: "i32".into(),
184 got: value.to_string(),
185 })?)
186 }
187 TypeKind::Con(tc) => match tc.builtin_id {
188 Some(BuiltinTypeId::I8) => {
189 engine
190 .heap
191 .alloc_i8(i8::try_from(value).map_err(|_| EngineError::NativeType {
192 expected: "i8".into(),
193 got: value.to_string(),
194 })?)
195 }
196 Some(BuiltinTypeId::I16) => {
197 engine.heap.alloc_i16(i16::try_from(value).map_err(|_| {
198 EngineError::NativeType {
199 expected: "i16".into(),
200 got: value.to_string(),
201 }
202 })?)
203 }
204 Some(BuiltinTypeId::I32) => {
205 engine.heap.alloc_i32(i32::try_from(value).map_err(|_| {
206 EngineError::NativeType {
207 expected: "i32".into(),
208 got: value.to_string(),
209 }
210 })?)
211 }
212 Some(BuiltinTypeId::I64) => engine.heap.alloc_i64(value),
213 Some(BuiltinTypeId::U8) => {
214 engine
215 .heap
216 .alloc_u8(u8::try_from(value).map_err(|_| EngineError::NativeType {
217 expected: "u8".into(),
218 got: value.to_string(),
219 })?)
220 }
221 Some(BuiltinTypeId::U16) => {
222 engine.heap.alloc_u16(u16::try_from(value).map_err(|_| {
223 EngineError::NativeType {
224 expected: "u16".into(),
225 got: value.to_string(),
226 }
227 })?)
228 }
229 Some(BuiltinTypeId::U32) => {
230 engine.heap.alloc_u32(u32::try_from(value).map_err(|_| {
231 EngineError::NativeType {
232 expected: "u32".into(),
233 got: value.to_string(),
234 }
235 })?)
236 }
237 Some(BuiltinTypeId::U64) => {
238 engine.heap.alloc_u64(u64::try_from(value).map_err(|_| {
239 EngineError::NativeType {
240 expected: "u64".into(),
241 got: value.to_string(),
242 }
243 })?)
244 }
245 _ => Err(EngineError::NativeType {
246 expected: "integral".into(),
247 got: typ.to_string(),
248 }),
249 },
250 _ => Err(EngineError::NativeType {
251 expected: "integral".into(),
252 got: typ.to_string(),
253 }),
254 }
255}
256
257fn type_head_is_var(typ: &Type) -> bool {
258 let mut cur = typ;
259 while let TypeKind::App(head, _) = cur.as_ref() {
260 cur = head;
261 }
262 matches!(cur.as_ref(), TypeKind::Var(..))
263}
264
265fn sanitize_type_name_for_symbol(typ: &Type) -> String {
266 typ.to_string()
267 .chars()
268 .map(|ch| if ch.is_ascii_alphanumeric() { ch } else { '_' })
269 .collect()
270}
271
272pub type NativeFuture<'a> = BoxFuture<'a, Result<Pointer, EngineError>>;
273type NativeId = u64;
274pub type SyncNativeCallable<State> = Arc<
275 dyn Fn(&Engine<State>, &Type, &[Pointer]) -> Result<Pointer, EngineError>
276 + Send
277 + Sync
278 + 'static,
279>;
280pub type AsyncNativeCallable<State> = Arc<
281 dyn for<'a> Fn(&'a Engine<State>, Type, &'a [Pointer]) -> NativeFuture<'a>
282 + Send
283 + Sync
284 + 'static,
285>;
286pub type AsyncNativeCallableCancellable<State> = Arc<
287 dyn for<'a> Fn(&'a Engine<State>, CancellationToken, Type, &'a [Pointer]) -> NativeFuture<'a>
288 + Send
289 + Sync
290 + 'static,
291>;
292
293type ExportInjector<State> =
294 Box<dyn FnOnce(&mut Engine<State>, &str) -> Result<(), EngineError> + Send + 'static>;
295
296struct NativeRegistration<State: Clone + Send + Sync + 'static> {
297 scheme: Scheme,
298 arity: usize,
299 callable: NativeCallable<State>,
300 gas_cost: u64,
301}
302
303impl<State: Clone + Send + Sync + 'static> NativeRegistration<State> {
304 fn sync(scheme: Scheme, arity: usize, func: SyncNativeCallable<State>, gas_cost: u64) -> Self {
305 Self {
306 scheme,
307 arity,
308 callable: NativeCallable::Sync(func),
309 gas_cost,
310 }
311 }
312
313 fn r#async(
314 scheme: Scheme,
315 arity: usize,
316 func: AsyncNativeCallable<State>,
317 gas_cost: u64,
318 ) -> Self {
319 Self {
320 scheme,
321 arity,
322 callable: NativeCallable::Async(func),
323 gas_cost,
324 }
325 }
326
327 fn async_cancellable(
328 scheme: Scheme,
329 arity: usize,
330 func: AsyncNativeCallableCancellable<State>,
331 gas_cost: u64,
332 ) -> Self {
333 Self {
334 scheme,
335 arity,
336 callable: NativeCallable::AsyncCancellable(func),
337 gas_cost,
338 }
339 }
340}
341
342pub trait Handler<State: Clone + Send + Sync + 'static, Sig>: Send + Sync + 'static {
343 fn declaration(export_name: &str) -> String;
344 fn declaration_for(&self, export_name: &str) -> String {
345 Self::declaration(export_name)
346 }
347 fn inject(self, engine: &mut Engine<State>, export_name: &str) -> Result<(), EngineError>;
348}
349
350pub trait AsyncHandler<State: Clone + Send + Sync + 'static, Sig>: Send + Sync + 'static {
351 fn declaration(export_name: &str) -> String;
352 fn declaration_for(&self, export_name: &str) -> String {
353 Self::declaration(export_name)
354 }
355 fn inject_async(self, engine: &mut Engine<State>, export_name: &str)
356 -> Result<(), EngineError>;
357}
358
359#[derive(Debug, Clone, Copy)]
360struct NativeCallableSig;
361
362#[derive(Debug, Clone, Copy)]
363struct AsyncNativeCallableSig;
364
365pub struct Export<State: Clone + Send + Sync + 'static> {
366 pub name: String,
367 declaration: String,
368 injector: ExportInjector<State>,
369}
370
371impl<State> Export<State>
372where
373 State: Clone + Send + Sync + 'static,
374{
375 fn from_injector(
376 name: impl Into<String>,
377 declaration: String,
378 injector: ExportInjector<State>,
379 ) -> Result<Self, EngineError> {
380 let name = name.into();
381 if name.trim().is_empty() {
382 return Err(EngineError::Internal("export name cannot be empty".into()));
383 }
384 let normalized = normalize_name(&name).to_string();
385 Ok(Self {
386 name: normalized,
387 declaration,
388 injector,
389 })
390 }
391
392 pub fn from_handler<Sig, H>(name: impl Into<String>, handler: H) -> Result<Self, EngineError>
393 where
394 H: Handler<State, Sig>,
395 {
396 let name = name.into();
397 let normalized = normalize_name(&name).to_string();
398 let declaration = handler.declaration_for(&normalized);
399 let injector: ExportInjector<State> =
400 Box::new(move |engine, qualified_name| handler.inject(engine, qualified_name));
401 Self::from_injector(name, declaration, injector)
402 }
403
404 pub fn from_async_handler<Sig, H>(
405 name: impl Into<String>,
406 handler: H,
407 ) -> Result<Self, EngineError>
408 where
409 H: AsyncHandler<State, Sig>,
410 {
411 let name = name.into();
412 let normalized = normalize_name(&name).to_string();
413 let declaration = handler.declaration_for(&normalized);
414 let injector: ExportInjector<State> =
415 Box::new(move |engine, qualified_name| handler.inject_async(engine, qualified_name));
416 Self::from_injector(name, declaration, injector)
417 }
418
419 pub fn from_native<F>(
420 name: impl Into<String>,
421 scheme: Scheme,
422 arity: usize,
423 handler: F,
424 ) -> Result<Self, EngineError>
425 where
426 F: for<'a> Fn(&'a Engine<State>, &'a Type, &'a [Pointer]) -> Result<Pointer, EngineError>
427 + Send
428 + Sync
429 + 'static,
430 {
431 validate_native_export_scheme(&scheme, arity)?;
432 let handler = Arc::new(handler);
433 let func: SyncNativeCallable<State> = Arc::new(
434 move |engine: &Engine<State>, typ: &Type, args: &[Pointer]| handler(engine, typ, args),
435 );
436 Self::from_handler::<NativeCallableSig, _>(name, (scheme, arity, func))
437 }
438
439 pub fn from_native_async<F>(
440 name: impl Into<String>,
441 scheme: Scheme,
442 arity: usize,
443 handler: F,
444 ) -> Result<Self, EngineError>
445 where
446 F: for<'a> Fn(&'a Engine<State>, Type, Vec<Pointer>) -> NativeFuture<'a>
447 + Send
448 + Sync
449 + 'static,
450 {
451 validate_native_export_scheme(&scheme, arity)?;
452 let handler = Arc::new(handler);
453 let func: AsyncNativeCallable<State> = Arc::new(move |engine, typ, args| {
454 let args = args.to_vec();
455 let handler = Arc::clone(&handler);
456 handler(engine, typ, args)
457 });
458 Self::from_async_handler::<AsyncNativeCallableSig, _>(name, (scheme, arity, func))
459 }
460}
461
462pub struct Library<State: Clone + Send + Sync + 'static> {
463 pub name: String,
464 declarations: Vec<String>,
465 exports: Vec<Export<State>>,
466}
467
468impl<State> Library<State>
469where
470 State: Clone + Send + Sync + 'static,
471{
472 pub fn new(name: impl Into<String>) -> Self {
473 Self {
474 name: name.into(),
475 declarations: Vec::new(),
476 exports: Vec::new(),
477 }
478 }
479
480 pub fn add_declaration(&mut self, declaration: impl Into<String>) -> Result<(), EngineError> {
484 let declaration = declaration.into();
485 if declaration.trim().is_empty() {
486 return Err(EngineError::Internal(
487 "library declaration cannot be empty".into(),
488 ));
489 }
490 self.declarations.push(declaration);
491 Ok(())
492 }
493
494 pub fn add_adt_decl(&mut self, adt: AdtDecl) -> Result<(), EngineError> {
496 self.add_declaration(adt_declaration_line(&adt))
497 }
498
499 pub fn add_adt_decls_from_types(
522 &mut self,
523 engine: &mut Engine<State>,
524 types: Vec<Type>,
525 ) -> Result<(), EngineError> {
526 let adts = collect_adts_in_types(types).map_err(collect_adts_error_to_engine)?;
527 for typ in adts {
528 let adt = engine.adt_decl_from_type(&typ)?;
529 self.add_adt_decl(adt)?;
530 }
531 Ok(())
532 }
533
534 pub fn inject_rex_adt<T>(&mut self, engine: &mut Engine<State>) -> Result<(), EngineError>
536 where
537 T: RexAdt,
538 {
539 let adt = T::rex_adt_decl(engine)?;
540 self.add_adt_decl(adt)
541 }
542
543 pub fn add_export(&mut self, export: Export<State>) {
544 self.exports.push(export);
545 }
546
547 pub fn export<Sig, H>(&mut self, name: impl Into<String>, handler: H) -> Result<(), EngineError>
548 where
549 H: Handler<State, Sig>,
550 {
551 self.exports.push(Export::from_handler(name, handler)?);
552 Ok(())
553 }
554
555 pub fn export_async<Sig, H>(
556 &mut self,
557 name: impl Into<String>,
558 handler: H,
559 ) -> Result<(), EngineError>
560 where
561 H: AsyncHandler<State, Sig>,
562 {
563 self.exports
564 .push(Export::from_async_handler(name, handler)?);
565 Ok(())
566 }
567
568 pub fn export_native<F>(
569 &mut self,
570 name: impl Into<String>,
571 scheme: Scheme,
572 arity: usize,
573 handler: F,
574 ) -> Result<(), EngineError>
575 where
576 F: for<'a> Fn(&'a Engine<State>, &'a Type, &'a [Pointer]) -> Result<Pointer, EngineError>
577 + Send
578 + Sync
579 + 'static,
580 {
581 self.exports
582 .push(Export::from_native(name, scheme, arity, handler)?);
583 Ok(())
584 }
585
586 pub fn export_native_async<F>(
587 &mut self,
588 name: impl Into<String>,
589 scheme: Scheme,
590 arity: usize,
591 handler: F,
592 ) -> Result<(), EngineError>
593 where
594 F: for<'a> Fn(&'a Engine<State>, Type, Vec<Pointer>) -> NativeFuture<'a>
595 + Send
596 + Sync
597 + 'static,
598 {
599 self.exports
600 .push(Export::from_native_async(name, scheme, arity, handler)?);
601 Ok(())
602 }
603}
604
605fn declaration_type_string(arg_types: &[Type], ret: Type) -> String {
606 if arg_types.is_empty() {
607 return ret.to_string();
608 }
609 let mut out = ret.to_string();
610 for arg in arg_types.iter().rev() {
611 out = format!("{arg} -> {out}");
612 }
613 out
614}
615
616fn declaration_line(export_name: &str, arg_types: &[Type], ret: Type) -> String {
617 format!(
618 "pub declare fn {export_name} {}",
619 declaration_type_string(arg_types, ret)
620 )
621}
622
623fn declaration_line_from_scheme(export_name: &str, scheme: &Scheme) -> String {
624 let mut out = format!("pub declare fn {export_name} : {}", scheme.typ);
625 if !scheme.preds.is_empty() {
626 let preds = scheme
627 .preds
628 .iter()
629 .map(|p| format!("{} {}", p.class, p.typ))
630 .collect::<Vec<_>>()
631 .join(", ");
632 out.push_str(" where ");
633 out.push_str(&preds);
634 }
635 out
636}
637
638fn adt_variant_arg_string(typ: &Type) -> String {
639 match typ.as_ref() {
640 TypeKind::Fun(..) => format!("({typ})"),
641 _ => typ.to_string(),
642 }
643}
644
645fn library_local_type_names_from_declarations(declarations: &[String]) -> HashSet<Symbol> {
646 let mut out = HashSet::new();
647 for declaration in declarations {
648 let mut s = declaration.trim_start();
649 if let Some(rest) = s.strip_prefix("pub ") {
650 s = rest.trim_start();
651 }
652 let Some(rest) = s.strip_prefix("type ") else {
653 continue;
654 };
655 let name: String = rest
656 .chars()
657 .take_while(|c| c.is_ascii_alphanumeric() || *c == '_')
658 .collect();
659 if !name.is_empty() {
660 out.insert(sym(&name));
661 }
662 }
663 out
664}
665
666fn qualify_library_type_refs(
667 typ: &Type,
668 library_name: &str,
669 local_type_names: &HashSet<Symbol>,
670) -> Type {
671 match typ.as_ref() {
672 TypeKind::Con(tc) => {
673 if local_type_names.contains(&tc.name) {
674 Type::con(
675 virtual_export_name(library_name, tc.name.as_ref()),
676 tc.arity,
677 )
678 } else {
679 typ.clone()
680 }
681 }
682 TypeKind::App(f, x) => Type::app(
683 qualify_library_type_refs(f, library_name, local_type_names),
684 qualify_library_type_refs(x, library_name, local_type_names),
685 ),
686 TypeKind::Fun(a, b) => Type::fun(
687 qualify_library_type_refs(a, library_name, local_type_names),
688 qualify_library_type_refs(b, library_name, local_type_names),
689 ),
690 TypeKind::Tuple(elems) => Type::tuple(
691 elems
692 .iter()
693 .map(|t| qualify_library_type_refs(t, library_name, local_type_names))
694 .collect(),
695 ),
696 TypeKind::Record(fields) => Type::new(TypeKind::Record(
697 fields
698 .iter()
699 .map(|(k, v)| {
700 (
701 k.clone(),
702 qualify_library_type_refs(v, library_name, local_type_names),
703 )
704 })
705 .collect(),
706 )),
707 TypeKind::Var(_) => typ.clone(),
708 }
709}
710
711fn qualify_library_scheme_refs(
712 scheme: &Scheme,
713 library_name: &str,
714 local_type_names: &HashSet<Symbol>,
715) -> Scheme {
716 let typ = qualify_library_type_refs(&scheme.typ, library_name, local_type_names);
717 let preds = scheme
718 .preds
719 .iter()
720 .map(|pred| {
721 Predicate::new(
722 pred.class.clone(),
723 qualify_library_type_refs(&pred.typ, library_name, local_type_names),
724 )
725 })
726 .collect();
727 Scheme::new(scheme.vars.clone(), preds, typ)
728}
729
730fn adt_declaration_line(adt: &AdtDecl) -> String {
731 let head = if adt.params.is_empty() {
732 adt.name.to_string()
733 } else {
734 let params = adt
735 .params
736 .iter()
737 .map(|p| p.name.to_string())
738 .collect::<Vec<_>>()
739 .join(" ");
740 format!("{} {}", adt.name, params)
741 };
742 let variants = adt
743 .variants
744 .iter()
745 .map(|variant| {
746 if variant.args.is_empty() {
747 variant.name.to_string()
748 } else {
749 let args = variant
750 .args
751 .iter()
752 .map(adt_variant_arg_string)
753 .collect::<Vec<_>>()
754 .join(" ");
755 format!("{} {}", variant.name, args)
756 }
757 })
758 .collect::<Vec<_>>()
759 .join(" | ");
760 format!("pub type {head} = {variants}")
761}
762
763pub fn collect_adts_error_to_engine(err: CollectAdtsError) -> EngineError {
781 let details = err
782 .conflicts
783 .into_iter()
784 .map(|conflict| {
785 let defs = conflict
786 .definitions
787 .iter()
788 .map(ToString::to_string)
789 .collect::<Vec<_>>()
790 .join(", ");
791 format!("{}: [{defs}]", conflict.name)
792 })
793 .collect::<Vec<_>>()
794 .join("; ");
795 EngineError::Custom(format!(
796 "conflicting ADT definitions discovered in input types: {details}"
797 ))
798}
799
800fn native_export_arg_types(
801 scheme: &Scheme,
802 arity: usize,
803) -> Result<(Vec<Type>, Type), EngineError> {
804 let mut args = Vec::with_capacity(arity);
805 let mut rest = scheme.typ.clone();
806 for _ in 0..arity {
807 let Some((arg, tail)) = split_fun(&rest) else {
808 return Err(EngineError::Internal(format!(
809 "native export type `{}` does not accept {arity} argument(s)",
810 scheme.typ
811 )));
812 };
813 args.push(arg);
814 rest = tail;
815 }
816 Ok((args, rest))
817}
818
819fn validate_native_export_scheme(scheme: &Scheme, arity: usize) -> Result<(), EngineError> {
820 let _ = native_export_arg_types(scheme, arity)?;
821 Ok(())
822}
823
824macro_rules! define_handler_impl {
825 ([] ; $arity:literal ; $sig:ty) => {
826 impl<State, F, R> Handler<State, $sig> for F
827 where
828 State: Clone + Send + Sync + 'static,
829 F: for<'a> Fn(&'a State) -> Result<R, EngineError> + Send + Sync + 'static,
830 R: IntoPointer + RexType,
831 {
832 fn declaration(export_name: &str) -> String {
833 declaration_line(export_name, &[], R::rex_type())
834 }
835
836 fn inject(
837 self,
838 engine: &mut Engine<State>,
839 export_name: &str,
840 ) -> Result<(), EngineError> {
841 let name_sym = normalize_name(export_name);
842 let func: SyncNativeCallable<State> = Arc::new(
843 move |engine: &Engine<State>, _: &Type, args: &[Pointer]| {
844 if args.len() != $arity {
845 return Err(EngineError::NativeArity {
846 name: name_sym.clone(),
847 expected: $arity,
848 got: args.len(),
849 });
850 }
851 let value = self(engine.state.as_ref())?;
852 value.into_pointer(&engine.heap)
853 },
854 );
855 let scheme = Scheme::new(vec![], vec![], R::rex_type());
856 let registration = NativeRegistration::sync(scheme, $arity, func, 0);
857 engine.register_native_registration(ROOT_LIBRARY_NAME, export_name, registration)
858 }
859 }
860
861 };
862 ([ $(($arg_ty:ident, $arg_name:ident, $idx:tt)),+ ] ; $arity:literal ; $sig:ty) => {
863 impl<State, F, R, $($arg_ty),+> Handler<State, $sig> for F
864 where
865 State: Clone + Send + Sync + 'static,
866 F: for<'a> Fn(&'a State, $($arg_ty),+) -> Result<R, EngineError> + Send + Sync + 'static,
867 R: IntoPointer + RexType,
868 $($arg_ty: FromPointer + RexType),+
869 {
870 fn declaration(export_name: &str) -> String {
871 let args = vec![$($arg_ty::rex_type()),+];
872 declaration_line(export_name, &args, R::rex_type())
873 }
874
875 fn inject(
876 self,
877 engine: &mut Engine<State>,
878 export_name: &str,
879 ) -> Result<(), EngineError> {
880 let name_sym = normalize_name(export_name);
881 let func: SyncNativeCallable<State> = Arc::new(
882 move |engine: &Engine<State>, _: &Type, args: &[Pointer]| {
883 if args.len() != $arity {
884 return Err(EngineError::NativeArity {
885 name: name_sym.clone(),
886 expected: $arity,
887 got: args.len(),
888 });
889 }
890 $(let $arg_name = $arg_ty::from_pointer(&engine.heap, &args[$idx])?;)*
891 let value = self(engine.state.as_ref(), $($arg_name),+)?;
892 value.into_pointer(&engine.heap)
893 },
894 );
895 let typ = native_fn_type!($($arg_ty),+ ; R);
896 let scheme = Scheme::new(vec![], vec![], typ);
897 let registration = NativeRegistration::sync(scheme, $arity, func, 0);
898 engine.register_native_registration(ROOT_LIBRARY_NAME, export_name, registration)
899 }
900 }
901
902 };
903}
904
905impl<State> Handler<State, NativeCallableSig> for (Scheme, usize, SyncNativeCallable<State>)
906where
907 State: Clone + Send + Sync + 'static,
908{
909 fn declaration(_export_name: &str) -> String {
910 unreachable!("native callable handlers use declaration_for")
911 }
912
913 fn declaration_for(&self, export_name: &str) -> String {
914 let (scheme, _, _) = self;
915 declaration_line_from_scheme(export_name, scheme)
916 }
917
918 fn inject(self, engine: &mut Engine<State>, export_name: &str) -> Result<(), EngineError> {
919 let (scheme, arity, func) = self;
920 validate_native_export_scheme(&scheme, arity)?;
921 let registration = NativeRegistration::sync(scheme, arity, func, 0);
922 engine.register_native_registration(ROOT_LIBRARY_NAME, export_name, registration)
923 }
924}
925
926macro_rules! define_async_handler_impl {
927 ([] ; $arity:literal ; $sig:ty) => {
928 impl<State, F, Fut, R> AsyncHandler<State, $sig> for F
929 where
930 State: Clone + Send + Sync + 'static,
931 F: for<'a> Fn(&'a State) -> Fut + Send + Sync + 'static,
932 Fut: Future<Output = Result<R, EngineError>> + Send + 'static,
933 R: IntoPointer + RexType,
934 {
935 fn declaration(export_name: &str) -> String {
936 declaration_line(export_name, &[], R::rex_type())
937 }
938
939 fn inject_async(
940 self,
941 engine: &mut Engine<State>,
942 export_name: &str,
943 ) -> Result<(), EngineError> {
944 let f = Arc::new(self);
945 let name_sym = normalize_name(export_name);
946 let func: AsyncNativeCallable<State> = Arc::new(
947 move |engine: &Engine<State>, _: Type, args: &[Pointer]| -> NativeFuture<'_> {
948 let f = Arc::clone(&f);
949 let name_sym = name_sym.clone();
950 async move {
951 if args.len() != $arity {
952 return Err(EngineError::NativeArity {
953 name: name_sym.clone(),
954 expected: $arity,
955 got: args.len(),
956 });
957 }
958 let value = f(engine.state.as_ref()).await?;
959 value.into_pointer(&engine.heap)
960 }
961 .boxed()
962 },
963 );
964 let scheme = Scheme::new(vec![], vec![], R::rex_type());
965 let registration = NativeRegistration::r#async(scheme, $arity, func, 0);
966 engine.register_native_registration(ROOT_LIBRARY_NAME, export_name, registration)
967 }
968 }
969 };
970 ([ $(($arg_ty:ident, $arg_name:ident, $idx:tt)),+ ] ; $arity:literal ; $sig:ty) => {
971 impl<State, F, Fut, R, $($arg_ty),+> AsyncHandler<State, $sig> for F
972 where
973 State: Clone + Send + Sync + 'static,
974 F: for<'a> Fn(&'a State, $($arg_ty),+) -> Fut + Send + Sync + 'static,
975 Fut: Future<Output = Result<R, EngineError>> + Send + 'static,
976 R: IntoPointer + RexType,
977 $($arg_ty: FromPointer + RexType),+
978 {
979 fn declaration(export_name: &str) -> String {
980 let args = vec![$($arg_ty::rex_type()),+];
981 declaration_line(export_name, &args, R::rex_type())
982 }
983
984 fn inject_async(
985 self,
986 engine: &mut Engine<State>,
987 export_name: &str,
988 ) -> Result<(), EngineError> {
989 let f = Arc::new(self);
990 let name_sym = normalize_name(export_name);
991 let func: AsyncNativeCallable<State> = Arc::new(
992 move |engine: &Engine<State>, _: Type, args: &[Pointer]| -> NativeFuture<'_> {
993 let f = Arc::clone(&f);
994 let name_sym = name_sym.clone();
995 async move {
996 if args.len() != $arity {
997 return Err(EngineError::NativeArity {
998 name: name_sym.clone(),
999 expected: $arity,
1000 got: args.len(),
1001 });
1002 }
1003 $(let $arg_name = $arg_ty::from_pointer(&engine.heap, &args[$idx])?;)*
1004 let value = f(engine.state.as_ref(), $($arg_name),+).await?;
1005 value.into_pointer(&engine.heap)
1006 }
1007 .boxed()
1008 },
1009 );
1010 let typ = native_fn_type!($($arg_ty),+ ; R);
1011 let scheme = Scheme::new(vec![], vec![], typ);
1012 let registration = NativeRegistration::r#async(scheme, $arity, func, 0);
1013 engine.register_native_registration(ROOT_LIBRARY_NAME, export_name, registration)
1014 }
1015 }
1016 };
1017}
1018
1019impl<State> AsyncHandler<State, AsyncNativeCallableSig>
1020 for (Scheme, usize, AsyncNativeCallable<State>)
1021where
1022 State: Clone + Send + Sync + 'static,
1023{
1024 fn declaration(_export_name: &str) -> String {
1025 unreachable!("native async callable handlers use declaration_for")
1026 }
1027
1028 fn declaration_for(&self, export_name: &str) -> String {
1029 let (scheme, _, _) = self;
1030 declaration_line_from_scheme(export_name, scheme)
1031 }
1032
1033 fn inject_async(
1034 self,
1035 engine: &mut Engine<State>,
1036 export_name: &str,
1037 ) -> Result<(), EngineError> {
1038 let (scheme, arity, func) = self;
1039 validate_native_export_scheme(&scheme, arity)?;
1040 let registration = NativeRegistration::r#async(scheme, arity, func, 0);
1041 engine.register_native_registration(ROOT_LIBRARY_NAME, export_name, registration)
1042 }
1043}
1044
1045#[derive(Clone)]
1046pub(crate) enum NativeCallable<State: Clone + Send + Sync + 'static> {
1047 Sync(SyncNativeCallable<State>),
1048 Async(AsyncNativeCallable<State>),
1049 AsyncCancellable(AsyncNativeCallableCancellable<State>),
1050}
1051
1052impl<State: Clone + Send + Sync + 'static> PartialEq for NativeCallable<State> {
1053 fn eq(&self, _other: &NativeCallable<State>) -> bool {
1054 false
1055 }
1056}
1057
1058impl<State: Clone + Send + Sync + 'static> std::fmt::Debug for NativeCallable<State> {
1059 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
1060 match self {
1061 NativeCallable::Sync(_) => write!(f, "Sync"),
1062 NativeCallable::Async(_) => write!(f, "Async"),
1063 NativeCallable::AsyncCancellable(_) => write!(f, "AsyncCancellable"),
1064 }
1065 }
1066}
1067
1068impl<State: Clone + Send + Sync + 'static> NativeCallable<State> {
1069 async fn call(
1070 &self,
1071 engine: &Engine<State>,
1072 typ: Type,
1073 args: &[Pointer],
1074 ) -> Result<Pointer, EngineError> {
1075 let token = engine.cancellation_token();
1076 if token.is_cancelled() {
1077 return Err(EngineError::Cancelled);
1078 }
1079
1080 match self {
1081 NativeCallable::Sync(f) => (f)(engine, &typ, args),
1082 NativeCallable::Async(f) => {
1083 let call_fut = (f)(engine, typ, args).fuse();
1084 let cancel_fut = token.cancelled().fuse();
1085 pin_mut!(call_fut, cancel_fut);
1086 futures::select! {
1087 _ = cancel_fut => Err(EngineError::Cancelled),
1088 res = call_fut => {
1089 if token.is_cancelled() {
1090 Err(EngineError::Cancelled)
1091 } else {
1092 res
1093 }
1094 },
1095 }
1096 }
1097 NativeCallable::AsyncCancellable(f) => {
1098 let call_fut = (f)(engine, token.clone(), typ, args).fuse();
1099 let cancel_fut = token.cancelled().fuse();
1100 pin_mut!(call_fut, cancel_fut);
1101 futures::select! {
1102 _ = cancel_fut => Err(EngineError::Cancelled),
1103 res = call_fut => {
1104 if token.is_cancelled() {
1105 Err(EngineError::Cancelled)
1106 } else {
1107 res
1108 }
1109 },
1110 }
1111 }
1112 }
1113 }
1114}
1115
1116#[derive(Clone, Debug, PartialEq)]
1117pub struct NativeFn {
1118 native_id: NativeId,
1119 name: Symbol,
1120 arity: usize,
1121 typ: Type,
1122 gas_cost: u64,
1123 applied: Vec<Pointer>,
1124 applied_types: Vec<Type>,
1125}
1126
1127impl NativeFn {
1128 fn new(native_id: NativeId, name: Symbol, arity: usize, typ: Type, gas_cost: u64) -> Self {
1129 Self {
1130 native_id,
1131 name,
1132 arity,
1133 typ,
1134 gas_cost,
1135 applied: Vec::new(),
1136 applied_types: Vec::new(),
1137 }
1138 }
1139
1140 pub(crate) fn from_parts(
1141 native_id: NativeId,
1142 name: Symbol,
1143 arity: usize,
1144 typ: Type,
1145 gas_cost: u64,
1146 applied: Vec<Pointer>,
1147 applied_types: Vec<Type>,
1148 ) -> Self {
1149 Self {
1150 native_id,
1151 name,
1152 arity,
1153 typ,
1154 gas_cost,
1155 applied,
1156 applied_types,
1157 }
1158 }
1159
1160 pub(crate) fn name(&self) -> &Symbol {
1161 &self.name
1162 }
1163
1164 async fn call_zero<State: Clone + Send + Sync + 'static>(
1165 &self,
1166 engine: &Engine<State>,
1167 gas: &mut GasMeter,
1168 ) -> Result<Pointer, EngineError> {
1169 let amount = gas
1170 .costs
1171 .native_call_base
1172 .saturating_add(self.gas_cost)
1173 .saturating_add(gas.costs.native_call_per_arg.saturating_mul(0));
1174 gas.charge(amount)?;
1175 if self.arity != 0 {
1176 return Err(EngineError::NativeArity {
1177 name: self.name.clone(),
1178 expected: self.arity,
1179 got: 0,
1180 });
1181 }
1182 engine
1183 .native_callable(self.native_id)?
1184 .call(engine, self.typ.clone(), &[])
1185 .await
1186 }
1187
1188 async fn apply<State: Clone + Send + Sync + 'static>(
1189 mut self,
1190 engine: &Engine<State>,
1191 arg: Pointer,
1192 arg_type: Option<&Type>,
1193 gas: &mut GasMeter,
1194 ) -> Result<Pointer, EngineError> {
1195 if self.arity == 0 {
1198 return Err(EngineError::NativeArity {
1199 name: self.name,
1200 expected: 0,
1201 got: 1,
1202 });
1203 }
1204 let (arg_ty, rest_ty) =
1205 split_fun(&self.typ).ok_or_else(|| EngineError::NotCallable(self.typ.to_string()))?;
1206 let actual_ty = resolve_arg_type(&engine.heap, arg_type, &arg)?;
1207 let subst = unify(&arg_ty, &actual_ty).map_err(|_| EngineError::NativeType {
1208 expected: arg_ty.to_string(),
1209 got: actual_ty.to_string(),
1210 })?;
1211 self.typ = rest_ty.apply(&subst);
1212 self.applied.push(arg);
1213 self.applied_types.push(actual_ty);
1214 if is_function_type(&self.typ) {
1215 let NativeFn {
1216 native_id,
1217 name,
1218 arity,
1219 typ,
1220 gas_cost,
1221 applied,
1222 applied_types,
1223 } = self;
1224 return engine.heap.alloc_native(
1225 native_id,
1226 name,
1227 arity,
1228 typ,
1229 gas_cost,
1230 applied,
1231 applied_types,
1232 );
1233 }
1234
1235 let mut full_ty = self.typ.clone();
1236 for arg_ty in self.applied_types.iter().rev() {
1237 full_ty = Type::fun(arg_ty.clone(), full_ty);
1238 }
1239
1240 let amount = gas
1241 .costs
1242 .native_call_base
1243 .saturating_add(self.gas_cost)
1244 .saturating_add(
1245 gas.costs
1246 .native_call_per_arg
1247 .saturating_mul(self.applied.len() as u64),
1248 );
1249 gas.charge(amount)?;
1250 engine
1251 .native_callable(self.native_id)?
1252 .call(engine, full_ty, &self.applied)
1253 .await
1254 }
1255}
1256
1257#[derive(Clone, Debug, PartialEq)]
1258pub struct OverloadedFn {
1259 name: Symbol,
1260 typ: Type,
1261 applied: Vec<Pointer>,
1262 applied_types: Vec<Type>,
1263}
1264
1265impl OverloadedFn {
1266 pub(crate) fn new(name: Symbol, typ: Type) -> Self {
1267 Self {
1268 name,
1269 typ,
1270 applied: Vec::new(),
1271 applied_types: Vec::new(),
1272 }
1273 }
1274
1275 pub(crate) fn from_parts(
1276 name: Symbol,
1277 typ: Type,
1278 applied: Vec<Pointer>,
1279 applied_types: Vec<Type>,
1280 ) -> Self {
1281 Self {
1282 name,
1283 typ,
1284 applied,
1285 applied_types,
1286 }
1287 }
1288
1289 pub(crate) fn name(&self) -> &Symbol {
1290 &self.name
1291 }
1292
1293 pub(crate) fn into_parts(self) -> (Symbol, Type, Vec<Pointer>, Vec<Type>) {
1294 (self.name, self.typ, self.applied, self.applied_types)
1295 }
1296
1297 async fn apply<State: Clone + Send + Sync + 'static>(
1298 mut self,
1299 engine: &Engine<State>,
1300 arg: Pointer,
1301 func_type: Option<&Type>,
1302 arg_type: Option<&Type>,
1303 gas: &mut GasMeter,
1304 ) -> Result<Pointer, EngineError> {
1305 if let Some(expected) = func_type {
1306 let subst = unify(&self.typ, expected).map_err(|_| EngineError::NativeType {
1307 expected: self.typ.to_string(),
1308 got: expected.to_string(),
1309 })?;
1310 self.typ = self.typ.apply(&subst);
1311 }
1312 let (arg_ty, rest_ty) =
1313 split_fun(&self.typ).ok_or_else(|| EngineError::NotCallable(self.typ.to_string()))?;
1314 let actual_ty = resolve_arg_type(&engine.heap, arg_type, &arg)?;
1315 let subst = unify(&arg_ty, &actual_ty).map_err(|_| EngineError::NativeType {
1316 expected: arg_ty.to_string(),
1317 got: actual_ty.to_string(),
1318 })?;
1319 let rest_ty = rest_ty.apply(&subst);
1320 self.applied.push(arg);
1321 self.applied_types.push(actual_ty);
1322 if is_function_type(&rest_ty) {
1323 return engine.heap.alloc_overloaded(
1324 self.name,
1325 rest_ty,
1326 self.applied,
1327 self.applied_types,
1328 );
1329 }
1330 let mut full_ty = rest_ty;
1331 for arg_ty in self.applied_types.iter().rev() {
1332 full_ty = Type::fun(arg_ty.clone(), full_ty);
1333 }
1334 if engine.type_system.class_methods.contains_key(&self.name) {
1335 let mut func = engine
1336 .resolve_class_method(&self.name, &full_ty, gas)
1337 .await?;
1338 let mut cur_ty = full_ty;
1339 for (applied, applied_ty) in self.applied.into_iter().zip(self.applied_types.iter()) {
1340 let (arg_ty, rest_ty) = split_fun(&cur_ty)
1341 .ok_or_else(|| EngineError::NotCallable(cur_ty.to_string()))?;
1342 let subst = unify(&arg_ty, applied_ty).map_err(|_| EngineError::NativeType {
1343 expected: arg_ty.to_string(),
1344 got: applied_ty.to_string(),
1345 })?;
1346 let rest_ty = rest_ty.apply(&subst);
1347 func = apply(engine, func, applied, Some(&cur_ty), Some(applied_ty), gas).await?;
1348 cur_ty = rest_ty;
1349 }
1350 return Ok(func);
1351 }
1352
1353 let imp = engine.resolve_native_impl(self.name.as_ref(), &full_ty)?;
1354 let amount = gas
1355 .costs
1356 .native_call_base
1357 .saturating_add(imp.gas_cost)
1358 .saturating_add(
1359 gas.costs
1360 .native_call_per_arg
1361 .saturating_mul(self.applied.len() as u64),
1362 );
1363 gas.charge(amount)?;
1364 imp.func.call(engine, full_ty, &self.applied).await
1365 }
1366}
1367
1368#[derive(Clone)]
1369struct NativeImpl<State: Clone + Send + Sync + 'static> {
1370 id: NativeId,
1371 name: Symbol,
1372 arity: usize,
1373 scheme: Scheme,
1374 func: NativeCallable<State>,
1375 gas_cost: u64,
1376}
1377
1378impl<State: Clone + Send + Sync + 'static> NativeImpl<State> {
1379 fn to_native_fn(&self, typ: Type) -> NativeFn {
1380 NativeFn::new(self.id, self.name.clone(), self.arity, typ, self.gas_cost)
1381 }
1382}
1383
1384#[derive(Clone)]
1385struct NativeRegistry<State: Clone + Send + Sync + 'static> {
1386 next_id: NativeId,
1387 entries: HashMap<Symbol, Vec<NativeImpl<State>>>,
1388 by_id: HashMap<NativeId, NativeImpl<State>>,
1389}
1390
1391impl<State: Clone + Send + Sync + 'static> NativeRegistry<State> {
1392 fn insert(
1393 &mut self,
1394 name: Symbol,
1395 arity: usize,
1396 scheme: Scheme,
1397 func: NativeCallable<State>,
1398 gas_cost: u64,
1399 ) -> Result<(), EngineError> {
1400 let entry = self.entries.entry(name.clone()).or_default();
1401 if entry.iter().any(|existing| existing.scheme == scheme) {
1402 return Err(EngineError::DuplicateImpl {
1403 name,
1404 typ: scheme.typ.to_string(),
1405 });
1406 }
1407 let id = self.next_id;
1408 self.next_id = self.next_id.saturating_add(1);
1409 let imp = NativeImpl::<State> {
1410 id,
1411 name: name.clone(),
1412 arity,
1413 scheme,
1414 func,
1415 gas_cost,
1416 };
1417 self.by_id.insert(id, imp.clone());
1418 entry.push(imp);
1419 Ok(())
1420 }
1421
1422 fn get(&self, name: &Symbol) -> Option<&[NativeImpl<State>]> {
1423 self.entries.get(name).map(|v| v.as_slice())
1424 }
1425
1426 fn has_name(&self, name: &Symbol) -> bool {
1427 self.entries.contains_key(name)
1428 }
1429
1430 fn by_id(&self, id: NativeId) -> Option<&NativeImpl<State>> {
1431 self.by_id.get(&id)
1432 }
1433}
1434
1435impl<State: Clone + Send + Sync + 'static> Default for NativeRegistry<State> {
1436 fn default() -> Self {
1437 Self {
1438 next_id: 0,
1439 entries: HashMap::new(),
1440 by_id: HashMap::new(),
1441 }
1442 }
1443}
1444
1445#[derive(Clone)]
1446struct TypeclassInstance {
1447 head: Type,
1448 def_env: Env,
1449 methods: HashMap<Symbol, Arc<TypedExpr>>,
1450}
1451
1452#[derive(Default, Clone)]
1453struct TypeclassRegistry {
1454 entries: HashMap<Symbol, Vec<TypeclassInstance>>,
1455}
1456
1457impl TypeclassRegistry {
1458 fn insert(
1459 &mut self,
1460 class: Symbol,
1461 head: Type,
1462 def_env: Env,
1463 methods: HashMap<Symbol, Arc<TypedExpr>>,
1464 ) -> Result<(), EngineError> {
1465 let entry = self.entries.entry(class.clone()).or_default();
1466 for existing in entry.iter() {
1467 if unify(&existing.head, &head).is_ok() {
1468 return Err(EngineError::DuplicateTypeclassImpl {
1469 class,
1470 typ: head.to_string(),
1471 });
1472 }
1473 }
1474 entry.push(TypeclassInstance {
1475 head,
1476 def_env,
1477 methods,
1478 });
1479 Ok(())
1480 }
1481
1482 fn resolve(
1483 &self,
1484 class: &Symbol,
1485 method: &Symbol,
1486 param_type: &Type,
1487 ) -> Result<(Env, Arc<TypedExpr>, Subst), EngineError> {
1488 let instances =
1489 self.entries
1490 .get(class)
1491 .ok_or_else(|| EngineError::MissingTypeclassImpl {
1492 class: class.clone(),
1493 typ: param_type.to_string(),
1494 })?;
1495
1496 let mut matches = Vec::new();
1497 for inst in instances {
1498 if let Ok(s) = unify(&inst.head, param_type) {
1499 matches.push((inst, s));
1500 }
1501 }
1502 match matches.len() {
1503 0 => Err(EngineError::MissingTypeclassImpl {
1504 class: class.clone(),
1505 typ: param_type.to_string(),
1506 }),
1507 1 => {
1508 let (inst, s) = matches.remove(0);
1509 let typed =
1510 inst.methods
1511 .get(method)
1512 .ok_or_else(|| EngineError::MissingTypeclassImpl {
1513 class: class.clone(),
1514 typ: param_type.to_string(),
1515 })?;
1516 Ok((inst.def_env.clone(), typed.clone(), s))
1517 }
1518 _ => Err(EngineError::AmbiguousTypeclassImpl {
1519 class: class.clone(),
1520 typ: param_type.to_string(),
1521 }),
1522 }
1523 }
1524}
1525
1526pub struct Engine<State = ()>
1527where
1528 State: Clone + Send + Sync + 'static,
1529{
1530 pub state: Arc<State>,
1531 env: Env,
1532 natives: NativeRegistry<State>,
1533 typeclasses: TypeclassRegistry,
1534 pub type_system: TypeSystem,
1535 typeclass_cache: Arc<Mutex<HashMap<(Symbol, Type), Pointer>>>,
1536 pub(crate) modules: LibrarySystem,
1537 injected_libraries: HashSet<String>,
1538 pub(crate) library_exports_cache: HashMap<LibraryId, LibraryExports>,
1539 pub(crate) library_interface_cache: HashMap<LibraryId, Vec<Decl>>,
1540 pub(crate) library_sources: HashMap<LibraryId, String>,
1541 pub(crate) library_source_fingerprints: HashMap<LibraryId, String>,
1542 pub(crate) published_cycle_interfaces: HashSet<LibraryId>,
1543 default_imports: Vec<String>,
1544 virtual_libraries: HashMap<String, LibraryExports>,
1545 library_local_type_names: HashMap<String, HashSet<Symbol>>,
1546 registration_library_context: Option<String>,
1547 cancel: CancellationToken,
1548 pub heap: Heap,
1549}
1550
1551impl<State> Default for Engine<State>
1552where
1553 State: Clone + Send + Sync + 'static + Default,
1554{
1555 fn default() -> Self {
1556 Self::new(State::default())
1557 }
1558}
1559
1560macro_rules! native_fn_type {
1561 (; $ret:ident) => {
1562 $ret::rex_type()
1563 };
1564 ($arg_ty:ident $(, $rest:ident)* ; $ret:ident) => {
1565 Type::fun($arg_ty::rex_type(), native_fn_type!($($rest),* ; $ret))
1566 };
1567}
1568
1569define_handler_impl!([] ; 0 ; fn() -> R);
1570define_handler_impl!([(A, a, 0)] ; 1 ; fn(A) -> R);
1571define_handler_impl!([(A, a, 0), (B, b, 1)] ; 2 ; fn(A, B) -> R);
1572define_handler_impl!([(A, a, 0), (B, b, 1), (C, c, 2)] ; 3 ; fn(A, B, C) -> R);
1573define_handler_impl!(
1574 [(A, a, 0), (B, b, 1), (C, c, 2), (D, d, 3)] ; 4 ; fn(A, B, C, D) -> R
1575);
1576define_handler_impl!(
1577 [(A, a, 0), (B, b, 1), (C, c, 2), (D, d, 3), (E, e, 4)] ; 5 ; fn(A, B, C, D, E) -> R
1578);
1579define_handler_impl!(
1580 [(A, a, 0), (B, b, 1), (C, c, 2), (D, d, 3), (E, e, 4), (G, g, 5)] ; 6 ; fn(A, B, C, D, E, G) -> R
1581);
1582define_handler_impl!(
1583 [(A, a, 0), (B, b, 1), (C, c, 2), (D, d, 3), (E, e, 4), (G, g, 5), (H, h, 6)] ; 7 ; fn(A, B, C, D, E, G, H) -> R
1584);
1585define_handler_impl!(
1586 [(A, a, 0), (B, b, 1), (C, c, 2), (D, d, 3), (E, e, 4), (G, g, 5), (H, h, 6), (I, i, 7)] ; 8 ; fn(A, B, C, D, E, G, H, I) -> R
1587);
1588
1589define_async_handler_impl!([] ; 0 ; fn() -> R);
1590define_async_handler_impl!([(A, a, 0)] ; 1 ; fn(A) -> R);
1591define_async_handler_impl!([(A, a, 0), (B, b, 1)] ; 2 ; fn(A, B) -> R);
1592define_async_handler_impl!([(A, a, 0), (B, b, 1), (C, c, 2)] ; 3 ; fn(A, B, C) -> R);
1593define_async_handler_impl!(
1594 [(A, a, 0), (B, b, 1), (C, c, 2), (D, d, 3)] ; 4 ; fn(A, B, C, D) -> R
1595);
1596define_async_handler_impl!(
1597 [(A, a, 0), (B, b, 1), (C, c, 2), (D, d, 3), (E, e, 4)] ; 5 ; fn(A, B, C, D, E) -> R
1598);
1599define_async_handler_impl!(
1600 [(A, a, 0), (B, b, 1), (C, c, 2), (D, d, 3), (E, e, 4), (G, g, 5)] ; 6 ; fn(A, B, C, D, E, G) -> R
1601);
1602define_async_handler_impl!(
1603 [(A, a, 0), (B, b, 1), (C, c, 2), (D, d, 3), (E, e, 4), (G, g, 5), (H, h, 6)] ; 7 ; fn(A, B, C, D, E, G, H) -> R
1604);
1605define_async_handler_impl!(
1606 [(A, a, 0), (B, b, 1), (C, c, 2), (D, d, 3), (E, e, 4), (G, g, 5), (H, h, 6), (I, i, 7)] ; 8 ; fn(A, B, C, D, E, G, H, I) -> R
1607);
1608
1609impl<State> Engine<State>
1610where
1611 State: Clone + Send + Sync + 'static,
1612{
1613 pub fn new(state: State) -> Self {
1614 Self {
1615 state: Arc::new(state),
1616 env: Env::new(),
1617 natives: NativeRegistry::<State>::default(),
1618 typeclasses: TypeclassRegistry::default(),
1619 type_system: TypeSystem::new(),
1620 typeclass_cache: Arc::new(Mutex::new(HashMap::new())),
1621 modules: LibrarySystem::default(),
1622 injected_libraries: HashSet::new(),
1623 library_exports_cache: HashMap::new(),
1624 library_interface_cache: HashMap::new(),
1625 library_sources: HashMap::new(),
1626 library_source_fingerprints: HashMap::new(),
1627 published_cycle_interfaces: HashSet::new(),
1628 default_imports: Vec::new(),
1629 virtual_libraries: HashMap::new(),
1630 library_local_type_names: HashMap::new(),
1631 registration_library_context: None,
1632 cancel: CancellationToken::new(),
1633 heap: Heap::new(),
1634 }
1635 }
1636
1637 pub fn with_prelude(state: State) -> Result<Self, EngineError> {
1638 Self::with_options(state, EngineOptions::default())
1639 }
1640
1641 pub fn with_options(state: State, options: EngineOptions) -> Result<Self, EngineError> {
1642 let type_system = match options.prelude {
1643 PreludeMode::Enabled => TypeSystem::with_prelude()?,
1644 PreludeMode::Disabled => TypeSystem::new(),
1645 };
1646 let mut engine = Engine {
1647 state: Arc::new(state),
1648 env: Env::new(),
1649 natives: NativeRegistry::<State>::default(),
1650 typeclasses: TypeclassRegistry::default(),
1651 type_system,
1652 typeclass_cache: Arc::new(Mutex::new(HashMap::new())),
1653 modules: LibrarySystem::default(),
1654 injected_libraries: HashSet::new(),
1655 library_exports_cache: HashMap::new(),
1656 library_interface_cache: HashMap::new(),
1657 library_sources: HashMap::new(),
1658 library_source_fingerprints: HashMap::new(),
1659 published_cycle_interfaces: HashSet::new(),
1660 default_imports: options.default_imports,
1661 virtual_libraries: HashMap::new(),
1662 library_local_type_names: HashMap::new(),
1663 registration_library_context: None,
1664 cancel: CancellationToken::new(),
1665 heap: Heap::new(),
1666 };
1667 if matches!(options.prelude, PreludeMode::Enabled) {
1668 engine.inject_prelude()?;
1669 engine.inject_prelude_virtual_module()?;
1670 }
1671 Ok(engine)
1672 }
1673
1674 pub fn into_heap(self) -> Heap {
1675 self.heap
1676 }
1677
1678 pub fn inject_tracing_log_functions(&mut self) -> Result<(), EngineError> {
1683 let string = Type::builtin(BuiltinTypeId::String);
1684
1685 let make_scheme = |engine: &mut Engine<State>| {
1686 let a_tv = engine.type_system.supply.fresh(Some("a".into()));
1687 let a = Type::var(a_tv.clone());
1688 Scheme::new(
1689 vec![a_tv],
1690 vec![Predicate::new("Show", a.clone())],
1691 Type::fun(a, string.clone()),
1692 )
1693 };
1694
1695 let debug_scheme = make_scheme(self);
1696 self.inject_tracing_log_function_with_scheme("debug", debug_scheme, |s| {
1697 tracing::debug!("{s}")
1698 })?;
1699 let info_scheme = make_scheme(self);
1700 self.inject_tracing_log_function_with_scheme("info", info_scheme, |s| {
1701 tracing::info!("{s}")
1702 })?;
1703 let warn_scheme = make_scheme(self);
1704 self.inject_tracing_log_function_with_scheme("warn", warn_scheme, |s| {
1705 tracing::warn!("{s}")
1706 })?;
1707 let error_scheme = make_scheme(self);
1708 self.inject_tracing_log_function_with_scheme("error", error_scheme, |s| {
1709 tracing::error!("{s}")
1710 })?;
1711 Ok(())
1712 }
1713
1714 pub fn inject_tracing_log_function(
1715 &mut self,
1716 name: &str,
1717 log: fn(&str),
1718 ) -> Result<(), EngineError> {
1719 let string = Type::builtin(BuiltinTypeId::String);
1720 let a_tv = self.type_system.supply.fresh(Some("a".into()));
1721 let a = Type::var(a_tv.clone());
1722 let scheme = Scheme::new(
1723 vec![a_tv],
1724 vec![Predicate::new("Show", a.clone())],
1725 Type::fun(a, string),
1726 );
1727 self.inject_tracing_log_function_with_scheme(name, scheme, log)
1728 }
1729
1730 fn inject_tracing_log_function_with_scheme(
1731 &mut self,
1732 name: &str,
1733 scheme: Scheme,
1734 log: fn(&str),
1735 ) -> Result<(), EngineError> {
1736 let name_sym = sym(name);
1737 self.export_native_async(name, scheme, 1, move |engine, call_type, args| {
1738 let name_sym = name_sym.clone();
1739 async move {
1740 if args.len() != 1 {
1741 return Err(EngineError::NativeArity {
1742 name: name_sym.clone(),
1743 expected: 1,
1744 got: args.len(),
1745 });
1746 }
1747
1748 let (arg_ty, _ret_ty) = split_fun(&call_type)
1749 .ok_or_else(|| EngineError::NotCallable(call_type.to_string()))?;
1750 let show_ty = Type::fun(arg_ty.clone(), Type::builtin(BuiltinTypeId::String));
1751 let mut gas = GasMeter::default();
1752 let show_ptr = engine
1753 .resolve_class_method(&sym("show"), &show_ty, &mut gas)
1754 .await?;
1755 let rendered_ptr = apply(
1756 engine,
1757 show_ptr,
1758 args[0],
1759 Some(&show_ty),
1760 Some(&arg_ty),
1761 &mut gas,
1762 )
1763 .await?;
1764 let message = engine.heap.pointer_as_string(&rendered_ptr)?;
1765
1766 log(&message);
1767 engine.heap.alloc_string(message)
1768 }
1769 .boxed()
1770 })
1771 }
1772
1773 pub fn cancellation_token(&self) -> CancellationToken {
1774 self.cancel.clone()
1775 }
1776
1777 pub fn set_default_imports(&mut self, imports: Vec<String>) {
1778 self.default_imports = imports;
1779 }
1780
1781 pub fn default_imports(&self) -> &[String] {
1782 &self.default_imports
1783 }
1784
1785 pub fn registry_markdown(&self) -> String {
1808 fn library_anchor(id: &LibraryId) -> String {
1809 let raw = format!("library-{id}").to_ascii_lowercase();
1810 let mut out = String::with_capacity(raw.len());
1811 let mut prev_dash = false;
1812 for ch in raw.chars() {
1813 let keep = ch.is_ascii_alphanumeric();
1814 let mapped = if keep { ch } else { '-' };
1815 if mapped == '-' {
1816 if prev_dash {
1817 continue;
1818 }
1819 prev_dash = true;
1820 } else {
1821 prev_dash = false;
1822 }
1823 out.push(mapped);
1824 }
1825 out.trim_matches('-').to_string()
1826 }
1827
1828 fn symbol_list(symbols: &[Symbol]) -> String {
1829 if symbols.is_empty() {
1830 "(none)".to_string()
1831 } else {
1832 symbols
1833 .iter()
1834 .map(|s| format!("`{s}`"))
1835 .collect::<Vec<_>>()
1836 .join(", ")
1837 }
1838 }
1839
1840 let mut out = String::new();
1841 let _ = writeln!(&mut out, "# Engine Registry");
1842 let _ = writeln!(&mut out);
1843 let mut library_ids: BTreeMap<String, LibraryId> = BTreeMap::new();
1844 for id in self.library_exports_cache.keys() {
1845 library_ids.insert(id.to_string(), id.clone());
1846 }
1847 for id in self.library_sources.keys() {
1848 library_ids.insert(id.to_string(), id.clone());
1849 }
1850 for library_name in self.virtual_libraries.keys() {
1851 let id = LibraryId::Virtual(library_name.clone());
1852 library_ids.insert(id.to_string(), id);
1853 }
1854 for library_name in &self.injected_libraries {
1855 let id = LibraryId::Virtual(library_name.clone());
1856 library_ids.insert(id.to_string(), id);
1857 }
1858
1859 let _ = writeln!(&mut out, "## Summary");
1860 let env_value_count = self.type_system.env.values.size();
1861 let native_impl_count: usize = self.natives.entries.values().map(Vec::len).sum();
1862 let class_count = self.type_system.classes.classes.len();
1863 let class_instance_count: usize = self
1864 .type_system
1865 .classes
1866 .instances
1867 .values()
1868 .map(Vec::len)
1869 .sum();
1870 let _ = writeln!(&mut out, "- Libraries (all kinds): {}", library_ids.len());
1871 let _ = writeln!(
1872 &mut out,
1873 "- Injected libraries: {}",
1874 self.injected_libraries.len()
1875 );
1876 let _ = writeln!(
1877 &mut out,
1878 "- Virtual libraries: {}",
1879 self.virtual_libraries.len()
1880 );
1881 let _ = writeln!(&mut out, "- ADTs: {}", self.type_system.adts.len());
1882 let _ = writeln!(
1883 &mut out,
1884 "- Values/functions in type env: {env_value_count}"
1885 );
1886 let _ = writeln!(&mut out, "- Type classes: {class_count}");
1887 let _ = writeln!(&mut out, "- Type class instances: {class_instance_count}");
1888 let _ = writeln!(&mut out, "- Native implementations: {native_impl_count}");
1889 let _ = writeln!(&mut out);
1890
1891 let _ = writeln!(&mut out, "## Library Index");
1892 if library_ids.is_empty() {
1893 let _ = writeln!(&mut out, "_No libraries registered._");
1894 } else {
1895 for (display, id) in &library_ids {
1896 let anchor = library_anchor(id);
1897 let _ = writeln!(&mut out, "- [`{display}`](#{anchor})");
1898 }
1899 }
1900 let _ = writeln!(&mut out);
1901
1902 let _ = writeln!(&mut out, "## Libraries");
1903 if library_ids.is_empty() {
1904 let _ = writeln!(&mut out, "_No libraries registered._");
1905 let _ = writeln!(&mut out);
1906 } else {
1907 for (display, id) in library_ids {
1908 let anchor = library_anchor(&id);
1909 let _ = writeln!(&mut out, "<a id=\"{anchor}\"></a>");
1910 let _ = writeln!(&mut out, "### `{display}`");
1911 if let Some(source) = self.library_sources.get(&id) {
1912 if source.trim().is_empty() {
1913 let _ = writeln!(&mut out, "_Module source is empty._");
1914 } else {
1915 let _ = writeln!(&mut out, "```rex");
1916 let _ = writeln!(&mut out, "{}", source.trim_end());
1917 let _ = writeln!(&mut out, "```");
1918 }
1919 } else {
1920 let _ = writeln!(&mut out, "_No captured source for this library._");
1921 }
1922
1923 let exports = self.library_exports_cache.get(&id).or_else(|| match &id {
1924 LibraryId::Virtual(name) => self.virtual_libraries.get(name),
1925 _ => None,
1926 });
1927 if let Some(exports) = exports {
1928 let mut values: Vec<Symbol> = exports.values.keys().cloned().collect();
1929 let mut types: Vec<Symbol> = exports.types.keys().cloned().collect();
1930 let mut classes: Vec<Symbol> = exports.classes.keys().cloned().collect();
1931 values.sort();
1932 types.sort();
1933 classes.sort();
1934 let _ = writeln!(&mut out, "- Values: {}", symbol_list(&values));
1935 let _ = writeln!(&mut out, "- Types: {}", symbol_list(&types));
1936 let _ = writeln!(&mut out, "- Classes: {}", symbol_list(&classes));
1937 } else {
1938 let _ = writeln!(&mut out, "- Exports: (none cached)");
1939 }
1940 let _ = writeln!(&mut out);
1941 }
1942 }
1943
1944 let _ = writeln!(&mut out, "## ADTs");
1945 if self.type_system.adts.is_empty() {
1946 let _ = writeln!(&mut out, "_No ADTs registered._");
1947 let _ = writeln!(&mut out);
1948 } else {
1949 let mut adts: Vec<&AdtDecl> = self.type_system.adts.values().collect();
1950 adts.sort_by(|a, b| a.name.cmp(&b.name));
1951 for adt in adts {
1952 let params = if adt.params.is_empty() {
1953 "(none)".to_string()
1954 } else {
1955 adt.params
1956 .iter()
1957 .map(|p| format!("`{}`", p.name))
1958 .collect::<Vec<_>>()
1959 .join(", ")
1960 };
1961 let _ = writeln!(&mut out, "### `{}`", adt.name);
1962 let _ = writeln!(&mut out, "- Parameters: {params}");
1963 if adt.variants.is_empty() {
1964 let _ = writeln!(&mut out, "- Variants: (none)");
1965 } else {
1966 let mut variants = adt.variants.clone();
1967 variants.sort_by(|a, b| a.name.cmp(&b.name));
1968 let _ = writeln!(&mut out, "- Variants:");
1969 for variant in variants {
1970 if variant.args.is_empty() {
1971 let _ = writeln!(&mut out, " - `{}`", variant.name);
1972 } else {
1973 let args = variant
1974 .args
1975 .iter()
1976 .map(ToString::to_string)
1977 .collect::<Vec<_>>()
1978 .join(", ");
1979 let _ = writeln!(&mut out, " - `{}`({args})", variant.name);
1980 }
1981 }
1982 }
1983 let _ = writeln!(&mut out);
1984 }
1985 }
1986
1987 let _ = writeln!(&mut out, "## Functions and Values");
1988 if self.type_system.env.values.is_empty() {
1989 let _ = writeln!(&mut out, "_No values registered._");
1990 let _ = writeln!(&mut out);
1991 } else {
1992 let mut names: Vec<Symbol> = self
1993 .type_system
1994 .env
1995 .values
1996 .iter()
1997 .map(|(name, _)| name.clone())
1998 .collect();
1999 names.sort();
2000 for name in names {
2001 if let Some(schemes) = self.type_system.env.lookup(&name) {
2002 let mut scheme_strs: Vec<String> =
2003 schemes.iter().map(|s| s.typ.to_string()).collect();
2004 scheme_strs.sort();
2005 scheme_strs.dedup();
2006 let joined = scheme_strs
2007 .into_iter()
2008 .map(|s| format!("`{s}`"))
2009 .collect::<Vec<_>>()
2010 .join(", ");
2011 let _ = writeln!(&mut out, "- `{name}`: {joined}");
2012 }
2013 }
2014 let _ = writeln!(&mut out);
2015 }
2016
2017 let _ = writeln!(&mut out, "## Type Classes");
2018 if self.type_system.classes.classes.is_empty() {
2019 let _ = writeln!(&mut out, "_No type classes registered._");
2020 let _ = writeln!(&mut out);
2021 } else {
2022 let mut class_names: Vec<Symbol> =
2023 self.type_system.classes.classes.keys().cloned().collect();
2024 class_names.sort();
2025 for class_name in class_names {
2026 let supers = self.type_system.classes.supers_of(&class_name);
2027 let mut supers_sorted = supers;
2028 supers_sorted.sort();
2029 let _ = writeln!(&mut out, "### `{class_name}`");
2030 let _ = writeln!(&mut out, "- Superclasses: {}", symbol_list(&supers_sorted));
2031
2032 let mut methods: Vec<(Symbol, String)> = self
2033 .type_system
2034 .class_methods
2035 .iter()
2036 .filter(|(_, info)| info.class == class_name)
2037 .map(|(name, info)| (name.clone(), info.scheme.typ.to_string()))
2038 .collect();
2039 methods.sort_by(|a, b| a.0.cmp(&b.0));
2040 if methods.is_empty() {
2041 let _ = writeln!(&mut out, "- Methods: (none)");
2042 } else {
2043 let _ = writeln!(&mut out, "- Methods:");
2044 for (method, scheme) in methods {
2045 let _ = writeln!(&mut out, " - `{method}`: `{scheme}`");
2046 }
2047 }
2048
2049 let mut instances = self
2050 .type_system
2051 .classes
2052 .instances
2053 .get(&class_name)
2054 .cloned()
2055 .unwrap_or_default();
2056 instances.sort_by(|a, b| a.head.typ.to_string().cmp(&b.head.typ.to_string()));
2057 if instances.is_empty() {
2058 let _ = writeln!(&mut out, "- Instances: (none)");
2059 } else {
2060 let _ = writeln!(&mut out, "- Instances:");
2061 for instance in instances {
2062 let ctx = if instance.context.is_empty() {
2063 String::new()
2064 } else {
2065 let mut parts: Vec<String> = instance
2066 .context
2067 .iter()
2068 .map(|pred| format!("{} {}", pred.class, pred.typ))
2069 .collect();
2070 parts.sort();
2071 format!("({}) => ", parts.join(", "))
2072 };
2073 let _ = writeln!(
2074 &mut out,
2075 " - `{}{} {}`",
2076 ctx, instance.head.class, instance.head.typ
2077 );
2078 }
2079 }
2080 let _ = writeln!(&mut out);
2081 }
2082 }
2083
2084 let _ = writeln!(&mut out, "## Native Implementations");
2085 if self.natives.entries.is_empty() {
2086 let _ = writeln!(&mut out, "_No native implementations registered._");
2087 } else {
2088 let mut native_names: Vec<Symbol> = self.natives.entries.keys().cloned().collect();
2089 native_names.sort();
2090 for name in native_names {
2091 if let Some(impls) = self.natives.get(&name) {
2092 let mut rows: Vec<(usize, String, u64)> = impls
2093 .iter()
2094 .map(|imp| (imp.arity, imp.scheme.typ.to_string(), imp.gas_cost))
2095 .collect();
2096 rows.sort_by(|a, b| a.1.cmp(&b.1));
2097 let _ = writeln!(&mut out, "### `{name}`");
2098 for (arity, typ, gas_cost) in rows {
2099 let _ = writeln!(
2100 &mut out,
2101 "- arity `{arity}`, gas `{gas_cost}`, type `{typ}`"
2102 );
2103 }
2104 let _ = writeln!(&mut out);
2105 }
2106 }
2107 }
2108
2109 out
2110 }
2111
2112 pub fn cancel(&self) {
2113 self.cancel.cancel();
2114 }
2115
2116 pub fn inject_library(&mut self, library: Library<State>) -> Result<(), EngineError> {
2117 let library_name = library.name.trim().to_string();
2118 if library_name.is_empty() {
2119 return Err(EngineError::Internal("library name cannot be empty".into()));
2120 }
2121 if self.injected_libraries.contains(&library_name) {
2122 return Err(EngineError::Internal(format!(
2123 "library `{library_name}` already injected"
2124 )));
2125 }
2126
2127 let mut source = String::new();
2128 for declaration in &library.declarations {
2129 source.push_str(declaration);
2130 source.push('\n');
2131 }
2132 for export in &library.exports {
2133 source.push_str(&export.declaration);
2134 source.push('\n');
2135 }
2136 self.library_sources
2137 .insert(LibraryId::Virtual(library_name.clone()), source.clone());
2138
2139 let local_type_names = library_local_type_names_from_declarations(&library.declarations);
2140 self.library_local_type_names
2141 .insert(library_name.clone(), local_type_names);
2142
2143 for export in library.exports {
2144 self.inject_library_export(&library_name, export)?;
2145 }
2146
2147 let resolver_module_name = library_name.clone();
2148 let resolver_source = source.clone();
2149 self.add_resolver(
2150 format!("injected:{library_name}"),
2151 move |req: ResolveRequest| {
2152 let requested = req
2153 .library_name
2154 .split_once('#')
2155 .map(|(base, _)| base)
2156 .unwrap_or(req.library_name.as_str());
2157 if requested != resolver_module_name {
2158 return Ok(None);
2159 }
2160 Ok(Some(ResolvedLibrary {
2161 id: LibraryId::Virtual(resolver_module_name.clone()),
2162 source: resolver_source.clone(),
2163 }))
2164 },
2165 );
2166
2167 self.injected_libraries.insert(library_name);
2168 Ok(())
2169 }
2170
2171 fn library_export_symbol(library_name: &str, export_name: &str) -> String {
2172 if library_name == ROOT_LIBRARY_NAME {
2173 normalize_name(export_name).to_string()
2174 } else {
2175 virtual_export_name(library_name, export_name)
2176 }
2177 }
2178
2179 fn inject_library_export(
2180 &mut self,
2181 library_name: &str,
2182 export: Export<State>,
2183 ) -> Result<(), EngineError> {
2184 let Export {
2185 name,
2186 declaration: _,
2187 injector,
2188 } = export;
2189 let qualified_name = Self::library_export_symbol(library_name, &name);
2190 let previous_context = self.registration_library_context.clone();
2191 self.registration_library_context = if library_name == ROOT_LIBRARY_NAME {
2192 None
2193 } else {
2194 Some(library_name.to_string())
2195 };
2196 let result = injector(self, &qualified_name);
2197 self.registration_library_context = previous_context;
2198 result
2199 }
2200
2201 fn inject_root_export(&mut self, export: Export<State>) -> Result<(), EngineError> {
2202 self.inject_library_export(ROOT_LIBRARY_NAME, export)
2203 }
2204
2205 fn register_native_registration(
2206 &mut self,
2207 library_name: &str,
2208 export_name: &str,
2209 registration: NativeRegistration<State>,
2210 ) -> Result<(), EngineError> {
2211 let NativeRegistration {
2212 mut scheme,
2213 arity,
2214 callable,
2215 gas_cost,
2216 } = registration;
2217 let scheme_module = if library_name == ROOT_LIBRARY_NAME {
2218 self.registration_library_context
2219 .as_deref()
2220 .unwrap_or(ROOT_LIBRARY_NAME)
2221 } else {
2222 library_name
2223 };
2224 if scheme_module != ROOT_LIBRARY_NAME
2225 && let Some(local_type_names) = self.library_local_type_names.get(scheme_module)
2226 {
2227 scheme = qualify_library_scheme_refs(&scheme, scheme_module, local_type_names);
2228 }
2229 let name = normalize_name(&Self::library_export_symbol(library_name, export_name));
2230 self.register_native(name, scheme, arity, callable, gas_cost)
2231 }
2232
2233 pub fn export<Sig, H>(&mut self, name: impl Into<String>, handler: H) -> Result<(), EngineError>
2234 where
2235 H: Handler<State, Sig>,
2236 {
2237 self.inject_root_export(Export::from_handler(name, handler)?)
2238 }
2239
2240 pub fn export_async<Sig, H>(
2241 &mut self,
2242 name: impl Into<String>,
2243 handler: H,
2244 ) -> Result<(), EngineError>
2245 where
2246 H: AsyncHandler<State, Sig>,
2247 {
2248 self.inject_root_export(Export::from_async_handler(name, handler)?)
2249 }
2250
2251 pub fn export_native<F>(
2252 &mut self,
2253 name: impl Into<String>,
2254 scheme: Scheme,
2255 arity: usize,
2256 handler: F,
2257 ) -> Result<(), EngineError>
2258 where
2259 F: for<'a> Fn(&'a Engine<State>, &'a Type, &'a [Pointer]) -> Result<Pointer, EngineError>
2260 + Send
2261 + Sync
2262 + 'static,
2263 {
2264 self.export_native_with_gas_cost(name, scheme, arity, 0, handler)
2265 }
2266
2267 pub fn inject_rex_default_instance<T>(&mut self) -> Result<(), EngineError>
2268 where
2269 T: RexType + RexDefault<State>,
2270 {
2271 let class = sym("Default");
2272 let method = sym("default");
2273 let head_ty = T::rex_type();
2274
2275 if !self.type_system.class_methods.contains_key(&method) {
2276 return Err(EngineError::UnknownVar(method));
2277 }
2278 if !head_ty.ftv().is_empty() {
2279 return Err(EngineError::UnsupportedExpr);
2280 }
2281
2282 if let Some(instances) = self.type_system.classes.instances.get(&class)
2283 && instances
2284 .iter()
2285 .any(|existing| unify(&existing.head.typ, &head_ty).is_ok())
2286 {
2287 return Err(EngineError::DuplicateTypeclassImpl {
2288 class,
2289 typ: head_ty.to_string(),
2290 });
2291 }
2292
2293 let native_name = format!(
2294 "__rex_default_for_{}",
2295 sanitize_type_name_for_symbol(&head_ty)
2296 );
2297 let native_scheme = Scheme::new(vec![], vec![], head_ty.clone());
2298 self.export_native(native_name.clone(), native_scheme, 0, |engine, _, _| {
2299 T::rex_default(engine)
2300 })?;
2301
2302 self.type_system.inject_instance(
2303 "Default",
2304 Instance::new(vec![], Predicate::new(class.clone(), head_ty.clone())),
2305 );
2306
2307 let mut methods: HashMap<Symbol, Arc<TypedExpr>> = HashMap::new();
2308 methods.insert(
2309 method.clone(),
2310 Arc::new(TypedExpr::new(
2311 head_ty.clone(),
2312 TypedExprKind::Var {
2313 name: sym(&native_name),
2314 overloads: vec![],
2315 },
2316 )),
2317 );
2318
2319 self.typeclasses
2320 .insert(class, head_ty, self.env.clone(), methods)?;
2321
2322 Ok(())
2323 }
2324
2325 pub fn export_native_async<F>(
2326 &mut self,
2327 name: impl Into<String>,
2328 scheme: Scheme,
2329 arity: usize,
2330 handler: F,
2331 ) -> Result<(), EngineError>
2332 where
2333 F: for<'a> Fn(&'a Engine<State>, Type, Vec<Pointer>) -> NativeFuture<'a>
2334 + Send
2335 + Sync
2336 + 'static,
2337 {
2338 self.export_native_async_with_gas_cost(name, scheme, arity, 0, handler)
2339 }
2340
2341 pub fn export_value<V: IntoPointer + RexType>(
2342 &mut self,
2343 name: &str,
2344 value: V,
2345 ) -> Result<(), EngineError> {
2346 let typ = V::rex_type();
2347 let value = value.into_pointer(&self.heap)?;
2348 let func: SyncNativeCallable<State> =
2349 Arc::new(move |_engine: &Engine<State>, _: &Type, _args: &[Pointer]| Ok(value));
2350 let scheme = Scheme::new(vec![], vec![], typ);
2351 let registration = NativeRegistration::sync(scheme, 0, func, 0);
2352 self.register_native_registration(ROOT_LIBRARY_NAME, name, registration)
2353 }
2354
2355 pub fn export_value_typed(
2356 &mut self,
2357 name: &str,
2358 typ: Type,
2359 value: Value,
2360 ) -> Result<(), EngineError> {
2361 let value = self.heap.alloc_value(value)?;
2362 let func: SyncNativeCallable<State> =
2363 Arc::new(move |_engine: &Engine<State>, _: &Type, _args: &[Pointer]| Ok(value));
2364 let scheme = Scheme::new(vec![], vec![], typ);
2365 let registration = NativeRegistration::sync(scheme, 0, func, 0);
2366 self.register_native_registration(ROOT_LIBRARY_NAME, name, registration)
2367 }
2368
2369 pub fn export_native_with_gas_cost<F>(
2370 &mut self,
2371 name: impl Into<String>,
2372 scheme: Scheme,
2373 arity: usize,
2374 gas_cost: u64,
2375 handler: F,
2376 ) -> Result<(), EngineError>
2377 where
2378 F: for<'a> Fn(&'a Engine<State>, &'a Type, &'a [Pointer]) -> Result<Pointer, EngineError>
2379 + Send
2380 + Sync
2381 + 'static,
2382 {
2383 validate_native_export_scheme(&scheme, arity)?;
2384 let name = name.into();
2385 let handler = Arc::new(handler);
2386 let func: SyncNativeCallable<State> = Arc::new(
2387 move |engine: &Engine<State>, typ: &Type, args: &[Pointer]| handler(engine, typ, args),
2388 );
2389 let registration = NativeRegistration::sync(scheme, arity, func, gas_cost);
2390 self.register_native_registration(ROOT_LIBRARY_NAME, &name, registration)
2391 }
2392
2393 pub fn export_native_async_with_gas_cost<F>(
2394 &mut self,
2395 name: impl Into<String>,
2396 scheme: Scheme,
2397 arity: usize,
2398 gas_cost: u64,
2399 handler: F,
2400 ) -> Result<(), EngineError>
2401 where
2402 F: for<'a> Fn(&'a Engine<State>, Type, Vec<Pointer>) -> NativeFuture<'a>
2403 + Send
2404 + Sync
2405 + 'static,
2406 {
2407 validate_native_export_scheme(&scheme, arity)?;
2408 let name = name.into();
2409 let handler = Arc::new(handler);
2410 let func: AsyncNativeCallable<State> = Arc::new(move |engine, typ, args| {
2411 let handler = Arc::clone(&handler);
2412 handler(engine, typ, args.to_vec())
2413 });
2414 let registration = NativeRegistration::r#async(scheme, arity, func, gas_cost);
2415 self.register_native_registration(ROOT_LIBRARY_NAME, &name, registration)
2416 }
2417
2418 pub fn export_native_async_cancellable<F>(
2419 &mut self,
2420 name: impl Into<String>,
2421 scheme: Scheme,
2422 arity: usize,
2423 handler: F,
2424 ) -> Result<(), EngineError>
2425 where
2426 F: for<'a> Fn(
2427 &'a Engine<State>,
2428 CancellationToken,
2429 Type,
2430 &'a [Pointer],
2431 ) -> NativeFuture<'a>
2432 + Send
2433 + Sync
2434 + 'static,
2435 {
2436 self.export_native_async_cancellable_with_gas_cost(name, scheme, arity, 0, handler)
2437 }
2438
2439 pub fn export_native_async_cancellable_with_gas_cost<F>(
2440 &mut self,
2441 name: impl Into<String>,
2442 scheme: Scheme,
2443 arity: usize,
2444 gas_cost: u64,
2445 handler: F,
2446 ) -> Result<(), EngineError>
2447 where
2448 F: for<'a> Fn(
2449 &'a Engine<State>,
2450 CancellationToken,
2451 Type,
2452 &'a [Pointer],
2453 ) -> NativeFuture<'a>
2454 + Send
2455 + Sync
2456 + 'static,
2457 {
2458 validate_native_export_scheme(&scheme, arity)?;
2459 let name = name.into();
2460 let handler = Arc::new(handler);
2461 let func: AsyncNativeCallableCancellable<State> =
2462 Arc::new(move |engine, token, typ, args| handler(engine, token, typ, args));
2463 let registration = NativeRegistration::async_cancellable(scheme, arity, func, gas_cost);
2464 self.register_native_registration(ROOT_LIBRARY_NAME, &name, registration)
2465 }
2466
2467 pub fn adt_decl(&mut self, name: &str, params: &[&str]) -> AdtDecl {
2468 let name_sym = sym(name);
2469 let param_syms: Vec<Symbol> = params.iter().map(|p| sym(p)).collect();
2470 AdtDecl::new(&name_sym, ¶m_syms, &mut self.type_system.supply)
2471 }
2472
2473 pub fn adt_decl_from_type(&mut self, typ: &Type) -> Result<AdtDecl, EngineError> {
2480 let (name, arity, args) = type_head_and_args(typ)?;
2481 let param_names: Vec<String> = if args.is_empty() {
2482 (0..arity).map(|i| format!("t{i}")).collect()
2483 } else {
2484 let mut names = Vec::with_capacity(args.len());
2485 for arg in args {
2486 match arg.as_ref() {
2487 TypeKind::Var(tv) => {
2488 let name = tv
2489 .name
2490 .as_ref()
2491 .map(|s| s.to_string())
2492 .unwrap_or_else(|| format!("t{}", tv.id));
2493 names.push(name);
2494 }
2495 _ => {
2496 return Err(EngineError::Custom(format!(
2497 "cannot infer ADT params from `{typ}`: expected type variables, got `{arg}`"
2498 )));
2499 }
2500 }
2501 }
2502 names
2503 };
2504 let param_refs: Vec<&str> = param_names.iter().map(|s| s.as_str()).collect();
2505 Ok(self.adt_decl(name.as_ref(), ¶m_refs))
2506 }
2507
2508 pub fn adt_decl_from_type_with_params(
2510 &mut self,
2511 typ: &Type,
2512 params: &[&str],
2513 ) -> Result<AdtDecl, EngineError> {
2514 let (name, arity, _args) = type_head_and_args(typ)?;
2515 if arity != params.len() {
2516 return Err(EngineError::Custom(format!(
2517 "type `{}` expects {} parameters, got {}",
2518 name,
2519 arity,
2520 params.len()
2521 )));
2522 }
2523 Ok(self.adt_decl(name.as_ref(), params))
2524 }
2525
2526 pub fn inject_adt(&mut self, adt: AdtDecl) -> Result<(), EngineError> {
2527 self.type_system.inject_adt(&adt);
2530 for (ctor, scheme) in adt.constructor_schemes() {
2531 let ctor_name = ctor.clone();
2532 let func = Arc::new(move |engine: &Engine<State>, _: &Type, args: &[Pointer]| {
2533 engine.heap.alloc_adt(ctor_name.clone(), args.to_vec())
2534 });
2535 let arity = type_arity(&scheme.typ);
2536 self.register_native(ctor, scheme, arity, NativeCallable::Sync(func), 0)?;
2537 }
2538 Ok(())
2539 }
2540
2541 pub fn inject_type_decl(&mut self, decl: &TypeDecl) -> Result<(), EngineError> {
2542 let adt = self
2543 .type_system
2544 .adt_from_decl(decl)
2545 .map_err(EngineError::Type)?;
2546 self.inject_adt(adt)
2547 }
2548
2549 pub fn inject_class_decl(&mut self, decl: &ClassDecl) -> Result<(), EngineError> {
2550 self.type_system
2551 .inject_class_decl(decl)
2552 .map_err(EngineError::Type)
2553 }
2554
2555 pub fn inject_instance_decl(&mut self, decl: &InstanceDecl) -> Result<(), EngineError> {
2556 let prepared = self
2557 .type_system
2558 .inject_instance_decl(decl)
2559 .map_err(EngineError::Type)?;
2560 self.register_typeclass_instance(decl, &prepared)
2561 }
2562
2563 pub fn inject_fn_decl(&mut self, decl: &FnDecl) -> Result<(), EngineError> {
2564 self.inject_fn_decls(std::slice::from_ref(decl))
2565 }
2566
2567 pub fn inject_fn_decls(&mut self, decls: &[FnDecl]) -> Result<(), EngineError> {
2568 if decls.is_empty() {
2569 return Ok(());
2570 }
2571
2572 self.type_system
2574 .inject_fn_decls(decls)
2575 .map_err(EngineError::Type)?;
2576
2577 let mut env_rec = self.env.clone();
2579 let mut slots = Vec::with_capacity(decls.len());
2580 for decl in decls {
2581 if let Some(existing) = env_rec.get(&decl.name.name) {
2582 slots.push(existing);
2583 } else {
2584 let placeholder = self.heap.alloc_uninitialized(decl.name.name.clone())?;
2585 env_rec = env_rec.extend(decl.name.name.clone(), placeholder);
2586 slots.push(placeholder);
2587 }
2588 }
2589
2590 let saved_env = self.env.clone();
2591 self.env = env_rec.clone();
2592
2593 let result: Result<(), EngineError> = (|| {
2594 for (decl, slot) in decls.iter().zip(slots.iter()) {
2595 let mut lam_body = decl.body.clone();
2596 for (idx, (param, ann)) in decl.params.iter().enumerate().rev() {
2597 let lam_constraints = if idx == 0 {
2598 decl.constraints.clone()
2599 } else {
2600 Vec::new()
2601 };
2602 let span = param.span;
2603 lam_body = Arc::new(Expr::Lam(
2604 span,
2605 Scope::new_sync(),
2606 param.clone(),
2607 Some(ann.clone()),
2608 lam_constraints,
2609 lam_body,
2610 ));
2611 }
2612
2613 let typed = self.type_check(lam_body.as_ref())?;
2614 let (param_ty, _ret_ty) = split_fun(&typed.typ)
2615 .ok_or_else(|| EngineError::NotCallable(typed.typ.to_string()))?;
2616 let TypedExprKind::Lam { param, body } = &typed.kind else {
2617 return Err(EngineError::Internal(
2618 "fn declaration did not lower to lambda".into(),
2619 ));
2620 };
2621 let ptr = self.heap.alloc_closure(
2622 self.env.clone(),
2623 param.clone(),
2624 param_ty,
2625 typed.typ.clone(),
2626 Arc::new(body.as_ref().clone()),
2627 )?;
2628 let value = self.heap.get(&ptr)?;
2629 self.heap.overwrite(slot, value.as_ref().clone())?;
2630 }
2631 Ok(())
2632 })();
2633
2634 if result.is_err() {
2635 self.env = saved_env;
2636 return result;
2637 }
2638
2639 self.env = env_rec;
2640 Ok(())
2641 }
2642
2643 pub fn inject_decls(&mut self, decls: &[Decl]) -> Result<(), EngineError> {
2644 let mut pending_fns: Vec<FnDecl> = Vec::new();
2645 for decl in decls {
2646 if let Decl::Fn(fd) = decl {
2647 pending_fns.push(fd.clone());
2648 continue;
2649 }
2650 if !pending_fns.is_empty() {
2651 self.inject_fn_decls(&pending_fns)?;
2652 pending_fns.clear();
2653 }
2654
2655 match decl {
2656 Decl::Type(ty) => self.inject_type_decl(ty)?,
2657 Decl::Class(class_decl) => self.inject_class_decl(class_decl)?,
2658 Decl::Instance(inst_decl) => self.inject_instance_decl(inst_decl)?,
2659 Decl::Fn(..) => {}
2660 Decl::DeclareFn(df) => {
2661 self.type_system
2662 .inject_declare_fn_decl(df)
2663 .map_err(EngineError::Type)?;
2664 }
2665 Decl::Import(..) => {}
2666 }
2667 }
2668 if !pending_fns.is_empty() {
2669 self.inject_fn_decls(&pending_fns)?;
2670 }
2671 Ok(())
2672 }
2673
2674 pub(crate) fn publish_runtime_decl_interfaces(
2675 &mut self,
2676 decls: &[DeclareFnDecl],
2677 ) -> Result<(), EngineError> {
2678 for df in decls {
2679 if self.env.get(&df.name.name).is_some() {
2680 continue;
2681 }
2682 let placeholder = self.heap.alloc_uninitialized(df.name.name.clone())?;
2683 self.env = self.env.extend(df.name.name.clone(), placeholder);
2684 }
2685 Ok(())
2686 }
2687
2688 pub fn inject_class(&mut self, name: &str, supers: Vec<String>) {
2689 let supers = supers.into_iter().map(|s| sym(&s)).collect();
2690 self.type_system.inject_class(name, supers);
2691 }
2692
2693 pub fn inject_instance(&mut self, class: &str, inst: Instance) {
2694 self.type_system.inject_instance(class, inst);
2695 }
2696
2697 pub async fn eval(
2698 &mut self,
2699 expr: &Expr,
2700 gas: &mut GasMeter,
2701 ) -> Result<(Pointer, Type), EngineError> {
2702 check_cancelled(self)?;
2703 let typed = self.type_check(expr)?;
2704 let typ = typed.typ.clone();
2705 let value = eval_typed_expr(self, &self.env, &typed, gas).await?;
2706 Ok((value, typ))
2707 }
2708
2709 fn inject_prelude(&mut self) -> Result<(), EngineError> {
2710 inject_prelude_adts(self)?;
2711 inject_equality_ops(self)?;
2712 inject_order_ops(self)?;
2713 inject_show_ops(self)?;
2714 inject_boolean_ops(self)?;
2715 inject_numeric_ops(self)?;
2716 inject_list_builtins(self)?;
2717 inject_option_result_builtins(self)?;
2718 inject_json_primops(self)?;
2719 self.register_prelude_typeclass_instances()?;
2720 Ok(())
2721 }
2722
2723 fn inject_prelude_virtual_module(&mut self) -> Result<(), EngineError> {
2724 if self.virtual_libraries.contains_key(PRELUDE_LIBRARY_NAME) {
2725 return Ok(());
2726 }
2727
2728 let module_key =
2729 library_key_for_library(&LibraryId::Virtual(PRELUDE_LIBRARY_NAME.to_string()));
2730 let mut values: HashMap<Symbol, CanonicalSymbol> = HashMap::new();
2731 for (name, _) in self.type_system.env.values.iter() {
2732 if !name.as_ref().starts_with("@m") {
2733 values.insert(
2734 name.clone(),
2735 CanonicalSymbol::from_symbol(
2736 module_key,
2737 SymbolKind::Value,
2738 name.clone(),
2739 name.clone(),
2740 ),
2741 );
2742 }
2743 }
2744
2745 let mut types: HashMap<Symbol, CanonicalSymbol> = HashMap::new();
2746 for name in self.type_system.adts.keys() {
2747 if !name.as_ref().starts_with("@m") {
2748 types.insert(
2749 name.clone(),
2750 CanonicalSymbol::from_symbol(
2751 module_key,
2752 SymbolKind::Type,
2753 name.clone(),
2754 name.clone(),
2755 ),
2756 );
2757 }
2758 }
2759
2760 let mut classes: HashMap<Symbol, CanonicalSymbol> = HashMap::new();
2761 for name in self.type_system.class_info.keys() {
2762 if !name.as_ref().starts_with("@m") {
2763 classes.insert(
2764 name.clone(),
2765 CanonicalSymbol::from_symbol(
2766 module_key,
2767 SymbolKind::Class,
2768 name.clone(),
2769 name.clone(),
2770 ),
2771 );
2772 }
2773 }
2774
2775 self.virtual_libraries.insert(
2776 PRELUDE_LIBRARY_NAME.to_string(),
2777 LibraryExports {
2778 values,
2779 types,
2780 classes,
2781 },
2782 );
2783 Ok(())
2784 }
2785
2786 pub(crate) fn virtual_library_exports(&self, library_name: &str) -> Option<LibraryExports> {
2787 self.virtual_libraries.get(library_name).cloned()
2788 }
2789
2790 fn register_prelude_typeclass_instances(&mut self) -> Result<(), EngineError> {
2791 let program =
2795 rexlang_typesystem::prelude_typeclasses_program().map_err(EngineError::Type)?;
2796 for decl in program.decls.iter() {
2797 let Decl::Instance(inst_decl) = decl else {
2798 continue;
2799 };
2800 if inst_decl.methods.is_empty() {
2801 continue;
2802 }
2803 let prepared = self
2804 .type_system
2805 .prepare_instance_decl(inst_decl)
2806 .map_err(EngineError::Type)?;
2807 self.register_typeclass_instance(inst_decl, &prepared)?;
2808 }
2809 Ok(())
2810 }
2811
2812 fn register_native(
2813 &mut self,
2814 name: Symbol,
2815 scheme: Scheme,
2816 arity: usize,
2817 func: NativeCallable<State>,
2818 gas_cost: u64,
2819 ) -> Result<(), EngineError> {
2820 let expected = type_arity(&scheme.typ);
2821 if expected != arity {
2822 return Err(EngineError::NativeArity {
2823 name: name.clone(),
2824 expected,
2825 got: arity,
2826 });
2827 }
2828 self.register_type_scheme(&name, &scheme)?;
2829 self.natives.insert(name, arity, scheme, func, gas_cost)
2830 }
2831
2832 fn register_type_scheme(
2833 &mut self,
2834 name: &Symbol,
2835 injected: &Scheme,
2836 ) -> Result<(), EngineError> {
2837 let schemes = self.type_system.env.lookup(name);
2838 match schemes {
2839 None => {
2840 self.type_system.add_value(name.as_ref(), injected.clone());
2841 Ok(())
2842 }
2843 Some(schemes) => {
2844 let has_poly = schemes
2845 .iter()
2846 .any(|s| !s.vars.is_empty() || !s.preds.is_empty());
2847 if has_poly {
2848 for existing in schemes {
2849 if scheme_accepts(&self.type_system, existing, &injected.typ)? {
2850 return Ok(());
2851 }
2852 }
2853 Err(EngineError::InvalidInjection {
2854 name: name.clone(),
2855 typ: injected.typ.to_string(),
2856 })
2857 } else {
2858 if schemes.iter().any(|s| s == injected) {
2859 return Ok(());
2860 }
2861 self.type_system
2862 .add_overload(name.as_ref(), injected.clone());
2863 Ok(())
2864 }
2865 }
2866 }
2867 }
2868
2869 fn type_check(&mut self, expr: &Expr) -> Result<TypedExpr, EngineError> {
2870 if let Some(span) = first_hole_span(expr) {
2871 return Err(EngineError::Type(TypeError::Spanned {
2872 span,
2873 error: Box::new(TypeError::UnsupportedExpr(
2874 "typed hole `?` must be filled before evaluation",
2875 )),
2876 }));
2877 }
2878 let (typed, preds, _ty) = self.type_system.infer_typed(expr)?;
2879 let (typed, preds) = default_ambiguous_types(self, typed, preds)?;
2880 self.check_predicates(&preds)?;
2881 self.check_natives(&typed)?;
2882 Ok(typed)
2883 }
2884
2885 pub(crate) fn infer_type(
2886 &mut self,
2887 expr: &Expr,
2888 gas: &mut GasMeter,
2889 ) -> Result<(Vec<Predicate>, Type), EngineError> {
2890 self.type_system
2891 .infer_with_gas(expr, gas)
2892 .map_err(EngineError::Type)
2893 }
2894
2895 fn check_predicates(&self, preds: &[Predicate]) -> Result<(), EngineError> {
2896 for pred in preds {
2897 if pred.typ.ftv().is_empty() {
2898 let ok = entails(&self.type_system.classes, &[], pred)?;
2899 if !ok {
2900 return Err(EngineError::Type(TypeError::NoInstance(
2901 pred.class.clone(),
2902 pred.typ.to_string(),
2903 )));
2904 }
2905 }
2906 }
2907 Ok(())
2908 }
2909
2910 fn check_natives(&self, expr: &TypedExpr) -> Result<(), EngineError> {
2911 enum Frame<'a> {
2912 Expr(&'a TypedExpr),
2913 Push(Symbol),
2914 PushMany(Vec<Symbol>),
2915 Pop(usize),
2916 }
2917
2918 let mut bound: Vec<Symbol> = Vec::new();
2919 let mut stack = vec![Frame::Expr(expr)];
2920 while let Some(frame) = stack.pop() {
2921 match frame {
2922 Frame::Expr(expr) => match &expr.kind {
2923 TypedExprKind::Var { name, overloads } => {
2924 if bound.iter().any(|n| n == name) {
2925 continue;
2926 }
2927 if !self.natives.has_name(name) {
2928 if self.env.get(name).is_some() {
2929 continue;
2930 }
2931 if self.type_system.class_methods.contains_key(name) {
2932 continue;
2933 }
2934 return Err(EngineError::UnknownVar(name.clone()));
2935 }
2936 if !overloads.is_empty()
2937 && expr.typ.ftv().is_empty()
2938 && !overloads.iter().any(|t| unify(t, &expr.typ).is_ok())
2939 {
2940 return Err(EngineError::MissingImpl {
2941 name: name.clone(),
2942 typ: expr.typ.to_string(),
2943 });
2944 }
2945 if expr.typ.ftv().is_empty() && !self.has_impl(name, &expr.typ) {
2946 return Err(EngineError::MissingImpl {
2947 name: name.clone(),
2948 typ: expr.typ.to_string(),
2949 });
2950 }
2951 }
2952 TypedExprKind::Tuple(elems) | TypedExprKind::List(elems) => {
2953 for elem in elems.iter().rev() {
2954 stack.push(Frame::Expr(elem));
2955 }
2956 }
2957 TypedExprKind::Dict(kvs) => {
2958 for v in kvs.values().rev() {
2959 stack.push(Frame::Expr(v));
2960 }
2961 }
2962 TypedExprKind::RecordUpdate { base, updates } => {
2963 for v in updates.values().rev() {
2964 stack.push(Frame::Expr(v));
2965 }
2966 stack.push(Frame::Expr(base));
2967 }
2968 TypedExprKind::App(f, x) => {
2969 stack.push(Frame::Expr(x));
2971 stack.push(Frame::Expr(f));
2972 }
2973 TypedExprKind::Project { expr, .. } => {
2974 stack.push(Frame::Expr(expr));
2975 }
2976 TypedExprKind::Lam { param, body } => {
2977 stack.push(Frame::Pop(1));
2978 stack.push(Frame::Expr(body));
2979 stack.push(Frame::Push(param.clone()));
2980 }
2981 TypedExprKind::Let { name, def, body } => {
2982 stack.push(Frame::Pop(1));
2983 stack.push(Frame::Expr(body));
2984 stack.push(Frame::Push(name.clone()));
2985 stack.push(Frame::Expr(def));
2986 }
2987 TypedExprKind::LetRec { bindings, body } => {
2988 if !bindings.is_empty() {
2989 stack.push(Frame::Pop(bindings.len()));
2990 stack.push(Frame::Expr(body));
2991 for (_, def) in bindings.iter().rev() {
2992 stack.push(Frame::Expr(def));
2993 }
2994 stack.push(Frame::PushMany(
2995 bindings.iter().map(|(name, _)| name.clone()).collect(),
2996 ));
2997 } else {
2998 stack.push(Frame::Expr(body));
2999 }
3000 }
3001 TypedExprKind::Ite {
3002 cond,
3003 then_expr,
3004 else_expr,
3005 } => {
3006 stack.push(Frame::Expr(else_expr));
3007 stack.push(Frame::Expr(then_expr));
3008 stack.push(Frame::Expr(cond));
3009 }
3010 TypedExprKind::Match { scrutinee, arms } => {
3011 for (pat, arm_expr) in arms.iter().rev() {
3012 let mut bindings = Vec::new();
3013 collect_pattern_bindings(pat, &mut bindings);
3014 let count = bindings.len();
3015 if count != 0 {
3016 stack.push(Frame::Pop(count));
3017 stack.push(Frame::Expr(arm_expr));
3018 stack.push(Frame::PushMany(bindings));
3019 } else {
3020 stack.push(Frame::Expr(arm_expr));
3021 }
3022 }
3023 stack.push(Frame::Expr(scrutinee));
3024 }
3025 TypedExprKind::Bool(..)
3026 | TypedExprKind::Uint(..)
3027 | TypedExprKind::Int(..)
3028 | TypedExprKind::Float(..)
3029 | TypedExprKind::String(..)
3030 | TypedExprKind::Uuid(..)
3031 | TypedExprKind::DateTime(..) => {}
3032 TypedExprKind::Hole => return Err(EngineError::UnsupportedExpr),
3033 },
3034 Frame::Push(sym) => bound.push(sym),
3035 Frame::PushMany(syms) => bound.extend(syms),
3036 Frame::Pop(count) => {
3037 bound.truncate(bound.len().saturating_sub(count));
3038 }
3039 }
3040 }
3041 Ok(())
3042 }
3043
3044 fn register_typeclass_instance(
3045 &mut self,
3046 decl: &InstanceDecl,
3047 prepared: &PreparedInstanceDecl,
3048 ) -> Result<(), EngineError> {
3049 let mut methods: HashMap<Symbol, Arc<TypedExpr>> = HashMap::new();
3050 for method in &decl.methods {
3051 let typed = self
3052 .type_system
3053 .typecheck_instance_method(prepared, method)
3054 .map_err(EngineError::Type)?;
3055 self.check_natives(&typed)?;
3056 methods.insert(method.name.clone(), Arc::new(typed));
3057 }
3058
3059 self.typeclasses.insert(
3060 prepared.class.clone(),
3061 prepared.head.clone(),
3062 self.env.clone(),
3063 methods,
3064 )?;
3065 Ok(())
3066 }
3067
3068 fn resolve_typeclass_method_impl(
3069 &self,
3070 name: &Symbol,
3071 call_type: &Type,
3072 ) -> Result<(Env, Arc<TypedExpr>, Subst), EngineError> {
3073 let info = self
3074 .type_system
3075 .class_methods
3076 .get(name)
3077 .ok_or_else(|| EngineError::UnknownVar(name.clone()))?;
3078
3079 let s_method = unify(&info.scheme.typ, call_type).map_err(EngineError::Type)?;
3080 let class_pred = info
3081 .scheme
3082 .preds
3083 .iter()
3084 .find(|p| p.class == info.class)
3085 .ok_or(EngineError::Type(TypeError::UnsupportedExpr(
3086 "method scheme missing class predicate",
3087 )))?;
3088 let param_type = class_pred.typ.apply(&s_method);
3089 if type_head_is_var(¶m_type) {
3090 return Err(EngineError::AmbiguousOverload { name: name.clone() });
3091 }
3092
3093 self.typeclasses.resolve(&info.class, name, ¶m_type)
3094 }
3095
3096 fn cached_class_method(&self, name: &Symbol, typ: &Type) -> Option<Pointer> {
3097 if !typ.ftv().is_empty() {
3098 return None;
3099 }
3100 let cache = self.typeclass_cache.lock().ok()?;
3101 cache.get(&(name.clone(), typ.clone())).cloned()
3102 }
3103
3104 fn insert_cached_class_method(&self, name: &Symbol, typ: &Type, pointer: &Pointer) {
3105 if typ.ftv().is_empty()
3106 && let Ok(mut cache) = self.typeclass_cache.lock()
3107 {
3108 cache.insert((name.clone(), typ.clone()), *pointer);
3109 }
3110 }
3111
3112 fn resolve_class_method_plan(
3113 &self,
3114 name: &Symbol,
3115 typ: &Type,
3116 ) -> Result<Result<(Env, TypedExpr), Pointer>, EngineError> {
3117 let (def_env, typed, s) = match self.resolve_typeclass_method_impl(name, typ) {
3118 Ok(res) => res,
3119 Err(EngineError::AmbiguousOverload { .. }) if is_function_type(typ) => {
3120 let (name, typ, applied, applied_types) =
3121 OverloadedFn::new(name.clone(), typ.clone()).into_parts();
3122 let pointer = self
3123 .heap
3124 .alloc_overloaded(name, typ, applied, applied_types)?;
3125 return Ok(Err(pointer));
3126 }
3127 Err(err) => return Err(err),
3128 };
3129 let specialized = typed.as_ref().apply(&s);
3130 Ok(Ok((def_env, specialized)))
3131 }
3132
3133 async fn resolve_class_method(
3134 &self,
3135 name: &Symbol,
3136 typ: &Type,
3137 gas: &mut GasMeter,
3138 ) -> Result<Pointer, EngineError> {
3139 if let Some(pointer) = self.cached_class_method(name, typ) {
3140 return Ok(pointer);
3141 }
3142
3143 let pointer = match self.resolve_class_method_plan(name, typ)? {
3144 Ok((def_env, specialized)) => {
3145 eval_typed_expr(self, &def_env, &specialized, gas).await?
3146 }
3147 Err(pointer) => pointer,
3148 };
3149
3150 if typ.ftv().is_empty() {
3151 self.insert_cached_class_method(name, typ, &pointer);
3152 }
3153 Ok(pointer)
3154 }
3155
3156 pub(crate) async fn resolve_global(
3157 &self,
3158 name: &Symbol,
3159 typ: &Type,
3160 ) -> Result<Pointer, EngineError> {
3161 if let Some(ptr) = self.env.get(name) {
3162 let value = self.heap.get(&ptr)?;
3163 match value.as_ref() {
3164 Value::Native(native) if native.arity == 0 && native.applied.is_empty() => {
3165 let mut gas = GasMeter::default();
3166 native.call_zero(self, &mut gas).await
3167 }
3168 _ => Ok(ptr),
3169 }
3170 } else if self.type_system.class_methods.contains_key(name) {
3171 let mut gas = GasMeter::default();
3172 self.resolve_class_method(name, typ, &mut gas).await
3173 } else {
3174 let mut gas = GasMeter::default();
3175 let pointer = self.resolve_native(name.as_ref(), typ, &mut gas)?;
3176 let value = self.heap.get(&pointer)?;
3177 match value.as_ref() {
3178 Value::Native(native) if native.arity == 0 && native.applied.is_empty() => {
3179 let mut gas = GasMeter::default();
3180 native.call_zero(self, &mut gas).await
3181 }
3182 _ => Ok(pointer),
3183 }
3184 }
3185 }
3186
3187 pub(crate) fn lookup_scheme(&self, name: &Symbol) -> Result<Scheme, EngineError> {
3188 let schemes = self
3189 .type_system
3190 .env
3191 .lookup(name)
3192 .ok_or_else(|| EngineError::UnknownVar(name.clone()))?;
3193 if schemes.len() != 1 {
3194 return Err(EngineError::AmbiguousOverload { name: name.clone() });
3195 }
3196 Ok(schemes[0].clone())
3197 }
3198
3199 fn has_impl(&self, name: &str, typ: &Type) -> bool {
3200 let sym_name = sym(name);
3201 self.natives
3202 .get(&sym_name)
3203 .map(|impls| impls.iter().any(|imp| impl_matches_type(imp, typ)))
3204 .unwrap_or(false)
3205 }
3206
3207 fn resolve_native_impl(
3208 &self,
3209 name: &str,
3210 typ: &Type,
3211 ) -> Result<NativeImpl<State>, EngineError> {
3212 let sym_name = sym(name);
3213 let impls = self
3214 .natives
3215 .get(&sym_name)
3216 .ok_or_else(|| EngineError::UnknownVar(sym_name.clone()))?;
3217 let matches: Vec<NativeImpl<State>> = impls
3218 .iter()
3219 .filter(|imp| impl_matches_type(imp, typ))
3220 .cloned()
3221 .collect();
3222 match matches.len() {
3223 0 => Err(EngineError::MissingImpl {
3224 name: sym_name.clone(),
3225 typ: typ.to_string(),
3226 }),
3227 1 => Ok(matches[0].clone()),
3228 _ => Err(EngineError::AmbiguousImpl {
3229 name: sym_name,
3230 typ: typ.to_string(),
3231 }),
3232 }
3233 }
3234
3235 fn native_callable(&self, id: NativeId) -> Result<NativeCallable<State>, EngineError> {
3236 self.natives
3237 .by_id(id)
3238 .map(|imp| imp.func.clone())
3239 .ok_or_else(|| EngineError::Internal(format!("unknown native id: {id}")))
3240 }
3241
3242 pub(crate) async fn call_native_impl(
3243 &self,
3244 name: &str,
3245 typ: &Type,
3246 args: &[Pointer],
3247 ) -> Result<Pointer, EngineError> {
3248 let imp = self.resolve_native_impl(name, typ)?;
3249 imp.func.call(self, typ.clone(), args).await
3250 }
3251
3252 fn resolve_native(
3253 &self,
3254 name: &str,
3255 typ: &Type,
3256 _gas: &mut GasMeter,
3257 ) -> Result<Pointer, EngineError> {
3258 let sym_name = sym(name);
3259 let impls = self
3260 .natives
3261 .get(&sym_name)
3262 .ok_or_else(|| EngineError::UnknownVar(sym_name.clone()))?;
3263 let matches: Vec<NativeImpl<State>> = impls
3264 .iter()
3265 .filter(|imp| impl_matches_type(imp, typ))
3266 .cloned()
3267 .collect();
3268 match matches.len() {
3269 0 => Err(EngineError::MissingImpl {
3270 name: sym_name.clone(),
3271 typ: typ.to_string(),
3272 }),
3273 1 => {
3274 let imp = matches[0].clone();
3275 let NativeFn {
3276 native_id,
3277 name,
3278 arity,
3279 typ,
3280 gas_cost,
3281 applied,
3282 applied_types,
3283 } = imp.to_native_fn(typ.clone());
3284 self.heap.alloc_native(
3285 native_id,
3286 name,
3287 arity,
3288 typ,
3289 gas_cost,
3290 applied,
3291 applied_types,
3292 )
3293 }
3294 _ => {
3295 if typ.ftv().is_empty() {
3296 Err(EngineError::AmbiguousImpl {
3297 name: sym_name.clone(),
3298 typ: typ.to_string(),
3299 })
3300 } else if is_function_type(typ) {
3301 let (name, typ, applied, applied_types) =
3302 OverloadedFn::new(sym_name.clone(), typ.clone()).into_parts();
3303 self.heap
3304 .alloc_overloaded(name, typ, applied, applied_types)
3305 } else {
3306 Err(EngineError::AmbiguousOverload { name: sym_name })
3307 }
3308 }
3309 }
3310 }
3311}
3312
3313fn first_hole_span(expr: &Expr) -> Option<Span> {
3314 match expr {
3315 Expr::Hole(span) => Some(*span),
3316 Expr::App(_, f, x) => first_hole_span(f).or_else(|| first_hole_span(x)),
3317 Expr::Project(_, base, _) => first_hole_span(base),
3318 Expr::Lam(_, _scope, _param, _ann, _constraints, body) => first_hole_span(body),
3319 Expr::Let(_, _var, _ann, def, body) => {
3320 first_hole_span(def).or_else(|| first_hole_span(body))
3321 }
3322 Expr::LetRec(_, bindings, body) => {
3323 for (_var, _ann, def) in bindings {
3324 if let Some(span) = first_hole_span(def) {
3325 return Some(span);
3326 }
3327 }
3328 first_hole_span(body)
3329 }
3330 Expr::Ite(_, cond, then_expr, else_expr) => first_hole_span(cond)
3331 .or_else(|| first_hole_span(then_expr))
3332 .or_else(|| first_hole_span(else_expr)),
3333 Expr::Match(_, scrutinee, arms) => {
3334 if let Some(span) = first_hole_span(scrutinee) {
3335 return Some(span);
3336 }
3337 for (_pat, arm) in arms {
3338 if let Some(span) = first_hole_span(arm) {
3339 return Some(span);
3340 }
3341 }
3342 None
3343 }
3344 Expr::Ann(_, inner, _) => first_hole_span(inner),
3345 Expr::Tuple(_, elems) | Expr::List(_, elems) => {
3346 for elem in elems {
3347 if let Some(span) = first_hole_span(elem) {
3348 return Some(span);
3349 }
3350 }
3351 None
3352 }
3353 Expr::Dict(_, kvs) => {
3354 for value in kvs.values() {
3355 if let Some(span) = first_hole_span(value) {
3356 return Some(span);
3357 }
3358 }
3359 None
3360 }
3361 Expr::RecordUpdate(_, base, kvs) => {
3362 if let Some(span) = first_hole_span(base) {
3363 return Some(span);
3364 }
3365 for value in kvs.values() {
3366 if let Some(span) = first_hole_span(value) {
3367 return Some(span);
3368 }
3369 }
3370 None
3371 }
3372 Expr::Bool(..)
3373 | Expr::Uint(..)
3374 | Expr::Int(..)
3375 | Expr::Float(..)
3376 | Expr::String(..)
3377 | Expr::Uuid(..)
3378 | Expr::DateTime(..)
3379 | Expr::Var(..) => None,
3380 }
3381}
3382
3383fn normalize_name(name: &str) -> Symbol {
3384 if let Some(stripped) = name.strip_prefix('(').and_then(|s| s.strip_suffix(')')) {
3385 let ok = stripped
3386 .chars()
3387 .all(|c| !c.is_alphanumeric() && c != '_' && !c.is_whitespace());
3388 if ok {
3389 return sym(stripped);
3390 }
3391 }
3392 sym(name)
3393}
3394
3395fn default_ambiguous_types<State: Clone + Send + Sync + 'static>(
3396 engine: &Engine<State>,
3397 typed: TypedExpr,
3398 mut preds: Vec<Predicate>,
3399) -> Result<(TypedExpr, Vec<Predicate>), EngineError> {
3400 let mut candidates = Vec::new();
3401 collect_default_candidates(&typed, &mut candidates);
3402 for ty in [
3403 Type::builtin(BuiltinTypeId::F32),
3404 Type::builtin(BuiltinTypeId::I32),
3405 Type::builtin(BuiltinTypeId::String),
3406 ] {
3407 push_unique_type(&mut candidates, ty);
3408 }
3409
3410 let mut subst = Subst::new_sync();
3411 loop {
3412 let vars: Vec<_> = preds.ftv().into_iter().collect();
3413 let mut progress = false;
3414 for tv in vars {
3415 if subst.get(&tv).is_some() {
3416 continue;
3417 }
3418 let mut relevant = Vec::new();
3419 let mut simple = true;
3420 for pred in &preds {
3421 if pred.typ.ftv().contains(&tv) {
3422 if !defaultable_class(&pred.class) {
3423 simple = false;
3424 break;
3425 }
3426 match pred.typ.as_ref() {
3427 TypeKind::Var(v) if v.id == tv => relevant.push(pred.clone()),
3428 _ => {
3429 simple = false;
3430 break;
3431 }
3432 }
3433 }
3434 }
3435 if !simple || relevant.is_empty() {
3436 continue;
3437 }
3438 if let Some(choice) = choose_default_type(engine, &relevant, &candidates)? {
3439 let mut next = Subst::new_sync();
3440 next = next.insert(tv, choice.clone());
3441 preds = preds.apply(&next);
3442 subst = compose_subst(next, subst);
3443 progress = true;
3444 }
3445 }
3446 if !progress {
3447 break;
3448 }
3449 }
3450 Ok((typed.apply(&subst), preds))
3451}
3452
3453fn defaultable_class(class: &Symbol) -> bool {
3454 matches!(
3455 class.as_ref(),
3456 "AdditiveMonoid" | "MultiplicativeMonoid" | "AdditiveGroup" | "Ring" | "Field" | "Integral"
3457 )
3458}
3459
3460fn collect_default_candidates(expr: &TypedExpr, out: &mut Vec<Type>) {
3461 let mut stack: Vec<&TypedExpr> = vec![expr];
3462 while let Some(expr) = stack.pop() {
3463 if expr.typ.ftv().is_empty()
3464 && let TypeKind::Con(tc) = expr.typ.as_ref()
3465 && tc.arity == 0
3466 {
3467 push_unique_type(out, expr.typ.clone());
3468 }
3469
3470 match &expr.kind {
3471 TypedExprKind::Tuple(elems) | TypedExprKind::List(elems) => {
3472 for elem in elems.iter().rev() {
3473 stack.push(elem);
3474 }
3475 }
3476 TypedExprKind::Dict(kvs) => {
3477 for value in kvs.values().rev() {
3478 stack.push(value);
3479 }
3480 }
3481 TypedExprKind::RecordUpdate { base, updates } => {
3482 for value in updates.values().rev() {
3483 stack.push(value);
3484 }
3485 stack.push(base);
3486 }
3487 TypedExprKind::App(f, x) => {
3488 stack.push(x);
3489 stack.push(f);
3490 }
3491 TypedExprKind::Project { expr, .. } => stack.push(expr),
3492 TypedExprKind::Lam { body, .. } => stack.push(body),
3493 TypedExprKind::Let { def, body, .. } => {
3494 stack.push(body);
3495 stack.push(def);
3496 }
3497 TypedExprKind::LetRec { bindings, body } => {
3498 stack.push(body);
3499 for (_, def) in bindings.iter().rev() {
3500 stack.push(def);
3501 }
3502 }
3503 TypedExprKind::Ite {
3504 cond,
3505 then_expr,
3506 else_expr,
3507 } => {
3508 stack.push(else_expr);
3509 stack.push(then_expr);
3510 stack.push(cond);
3511 }
3512 TypedExprKind::Match { scrutinee, arms } => {
3513 for (_, expr) in arms.iter().rev() {
3514 stack.push(expr);
3515 }
3516 stack.push(scrutinee);
3517 }
3518 TypedExprKind::Var { .. }
3519 | TypedExprKind::Bool(..)
3520 | TypedExprKind::Uint(..)
3521 | TypedExprKind::Int(..)
3522 | TypedExprKind::Float(..)
3523 | TypedExprKind::String(..)
3524 | TypedExprKind::Uuid(..)
3525 | TypedExprKind::DateTime(..)
3526 | TypedExprKind::Hole => {}
3527 }
3528 }
3529}
3530
3531fn push_unique_type(out: &mut Vec<Type>, typ: Type) {
3532 if !out.iter().any(|t| t == &typ) {
3533 out.push(typ);
3534 }
3535}
3536
3537fn choose_default_type<State: Clone + Send + Sync + 'static>(
3538 engine: &Engine<State>,
3539 preds: &[Predicate],
3540 candidates: &[Type],
3541) -> Result<Option<Type>, EngineError> {
3542 for candidate in candidates {
3543 let mut ok = true;
3544 for pred in preds {
3545 let test = Predicate::new(pred.class.clone(), candidate.clone());
3546 if !entails(&engine.type_system.classes, &[], &test)? {
3547 ok = false;
3548 break;
3549 }
3550 }
3551 if ok {
3552 return Ok(Some(candidate.clone()));
3553 }
3554 }
3555 Ok(None)
3556}
3557
3558fn scheme_accepts(ts: &TypeSystem, scheme: &Scheme, typ: &Type) -> Result<bool, EngineError> {
3559 let mut supply = TypeVarSupply::new();
3560 let (preds, scheme_ty) = instantiate(scheme, &mut supply);
3561 let subst = match unify(&scheme_ty, typ) {
3562 Ok(s) => s,
3563 Err(_) => return Ok(false),
3564 };
3565 let preds = preds.apply(&subst);
3566 for pred in preds {
3567 if pred.typ.ftv().is_empty() {
3568 let ok = entails(&ts.classes, &[], &pred)?;
3569 if !ok {
3570 return Ok(false);
3571 }
3572 }
3573 }
3574 Ok(true)
3575}
3576
3577fn is_function_type(typ: &Type) -> bool {
3578 matches!(typ.as_ref(), TypeKind::Fun(..))
3579}
3580
3581fn collect_pattern_bindings(pat: &Pattern, out: &mut Vec<Symbol>) {
3582 match pat {
3583 Pattern::Wildcard(..) => {}
3584 Pattern::Var(var) => out.push(var.name.clone()),
3585 Pattern::Named(_, _, ps) => {
3586 for p in ps {
3587 collect_pattern_bindings(p, out);
3588 }
3589 }
3590 Pattern::Tuple(_, ps) => {
3591 for p in ps {
3592 collect_pattern_bindings(p, out);
3593 }
3594 }
3595 Pattern::List(_, ps) => {
3596 for p in ps {
3597 collect_pattern_bindings(p, out);
3598 }
3599 }
3600 Pattern::Cons(_, head, tail) => {
3601 collect_pattern_bindings(head, out);
3602 collect_pattern_bindings(tail, out);
3603 }
3604 Pattern::Dict(_, fields) => {
3605 for (_key, pat) in fields {
3606 collect_pattern_bindings(pat, out);
3607 }
3608 }
3609 }
3610}
3611
3612fn type_head_and_args(typ: &Type) -> Result<(Symbol, usize, Vec<Type>), EngineError> {
3613 let mut args = Vec::new();
3614 let mut head = typ;
3615 while let TypeKind::App(f, arg) = head.as_ref() {
3616 args.push(arg.clone());
3617 head = f;
3618 }
3619 args.reverse();
3620
3621 let TypeKind::Con(con) = head.as_ref() else {
3622 return Err(EngineError::Custom(format!(
3623 "cannot build ADT declaration from non-constructor type `{typ}`"
3624 )));
3625 };
3626 if !args.is_empty() && args.len() != con.arity {
3627 return Err(EngineError::Custom(format!(
3628 "constructor `{}` expected {} type arguments but got {} in `{typ}`",
3629 con.name,
3630 con.arity,
3631 args.len()
3632 )));
3633 }
3634 Ok((con.name.clone(), con.arity, args))
3635}
3636
3637fn type_arity(typ: &Type) -> usize {
3638 let mut count = 0;
3639 let mut cur = typ;
3640 while let TypeKind::Fun(_, next) = cur.as_ref() {
3641 count += 1;
3642 cur = next;
3643 }
3644 count
3645}
3646
3647fn split_fun(typ: &Type) -> Option<(Type, Type)> {
3648 match typ.as_ref() {
3649 TypeKind::Fun(a, b) => Some((a.clone(), b.clone())),
3650 _ => None,
3651 }
3652}
3653
3654fn impl_matches_type<State: Clone + Send + Sync + 'static>(
3655 imp: &NativeImpl<State>,
3656 typ: &Type,
3657) -> bool {
3658 let mut supply = TypeVarSupply::new();
3659 let (_preds, scheme_ty) = instantiate(&imp.scheme, &mut supply);
3660 unify(&scheme_ty, typ).is_ok()
3661}
3662
3663fn value_type(heap: &Heap, value: &Value) -> Result<Type, EngineError> {
3664 let pointer_type = |pointer: &Pointer| -> Result<Type, EngineError> {
3665 let value = heap.get(pointer)?;
3666 value_type(heap, value.as_ref())
3667 };
3668
3669 match value {
3670 Value::Bool(..) => Ok(Type::builtin(BuiltinTypeId::Bool)),
3671 Value::U8(..) => Ok(Type::builtin(BuiltinTypeId::U8)),
3672 Value::U16(..) => Ok(Type::builtin(BuiltinTypeId::U16)),
3673 Value::U32(..) => Ok(Type::builtin(BuiltinTypeId::U32)),
3674 Value::U64(..) => Ok(Type::builtin(BuiltinTypeId::U64)),
3675 Value::I8(..) => Ok(Type::builtin(BuiltinTypeId::I8)),
3676 Value::I16(..) => Ok(Type::builtin(BuiltinTypeId::I16)),
3677 Value::I32(..) => Ok(Type::builtin(BuiltinTypeId::I32)),
3678 Value::I64(..) => Ok(Type::builtin(BuiltinTypeId::I64)),
3679 Value::F32(..) => Ok(Type::builtin(BuiltinTypeId::F32)),
3680 Value::F64(..) => Ok(Type::builtin(BuiltinTypeId::F64)),
3681 Value::String(..) => Ok(Type::builtin(BuiltinTypeId::String)),
3682 Value::Uuid(..) => Ok(Type::builtin(BuiltinTypeId::Uuid)),
3683 Value::DateTime(..) => Ok(Type::builtin(BuiltinTypeId::DateTime)),
3684 Value::Tuple(elems) => {
3685 let mut tys = Vec::with_capacity(elems.len());
3686 for elem in elems {
3687 tys.push(pointer_type(elem)?);
3688 }
3689 Ok(Type::tuple(tys))
3690 }
3691 Value::Array(elems) => {
3692 let first = elems
3693 .first()
3694 .ok_or_else(|| EngineError::UnknownType(sym("array")))?;
3695 let elem_ty = pointer_type(first)?;
3696 for elem in elems.iter().skip(1) {
3697 let ty = pointer_type(elem)?;
3698 if ty != elem_ty {
3699 return Err(EngineError::NativeType {
3700 expected: elem_ty.to_string(),
3701 got: ty.to_string(),
3702 });
3703 }
3704 }
3705 Ok(Type::app(Type::builtin(BuiltinTypeId::Array), elem_ty))
3706 }
3707 Value::Dict(map) => {
3708 let first = map
3709 .values()
3710 .next()
3711 .ok_or_else(|| EngineError::UnknownType(sym("dict")))?;
3712 let elem_ty = pointer_type(first)?;
3713 for val in map.values().skip(1) {
3714 let ty = pointer_type(val)?;
3715 if ty != elem_ty {
3716 return Err(EngineError::NativeType {
3717 expected: elem_ty.to_string(),
3718 got: ty.to_string(),
3719 });
3720 }
3721 }
3722 Ok(Type::app(Type::builtin(BuiltinTypeId::Dict), elem_ty))
3723 }
3724 Value::Adt(tag, args) if sym_eq(tag, "Some") && args.len() == 1 => {
3725 let inner = pointer_type(&args[0])?;
3726 Ok(Type::app(Type::builtin(BuiltinTypeId::Option), inner))
3727 }
3728 Value::Adt(tag, args) if sym_eq(tag, "None") && args.is_empty() => {
3729 Err(EngineError::UnknownType(sym("option")))
3730 }
3731 Value::Adt(tag, args) if (sym_eq(tag, "Ok") || sym_eq(tag, "Err")) && args.len() == 1 => {
3732 Err(EngineError::UnknownType(sym("result")))
3733 }
3734 Value::Adt(tag, args)
3735 if (sym_eq(tag, "Empty") || sym_eq(tag, "Cons")) && args.len() <= 2 =>
3736 {
3737 let elems = list_to_vec(heap, value)?;
3738 let first = elems
3739 .first()
3740 .ok_or_else(|| EngineError::UnknownType(sym("list")))?;
3741 let elem_ty = pointer_type(first)?;
3742 for elem in elems.iter().skip(1) {
3743 let ty = pointer_type(elem)?;
3744 if ty != elem_ty {
3745 return Err(EngineError::NativeType {
3746 expected: elem_ty.to_string(),
3747 got: ty.to_string(),
3748 });
3749 }
3750 }
3751 Ok(Type::app(Type::builtin(BuiltinTypeId::List), elem_ty))
3752 }
3753 Value::Adt(tag, _args) => Err(EngineError::UnknownType(tag.clone())),
3754 Value::Uninitialized(..) => Err(EngineError::UnknownType(sym("uninitialized"))),
3755 Value::Closure(..) => Err(EngineError::UnknownType(sym("closure"))),
3756 Value::Native(..) => Err(EngineError::UnknownType(sym("native"))),
3757 Value::Overloaded(..) => Err(EngineError::UnknownType(sym("overloaded"))),
3758 }
3759}
3760
3761fn resolve_arg_type(
3762 heap: &Heap,
3763 arg_type: Option<&Type>,
3764 arg: &Pointer,
3765) -> Result<Type, EngineError> {
3766 let infer_from_value = |ty_hint: Option<&Type>| -> Result<Type, EngineError> {
3767 let value = heap.get(arg)?;
3768 match ty_hint {
3769 Some(ty) => match value_type(heap, value.as_ref()) {
3770 Ok(val_ty) if val_ty.ftv().is_empty() => Ok(val_ty),
3771 _ => Ok(ty.clone()),
3772 },
3773 None => value_type(heap, value.as_ref()),
3774 }
3775 };
3776 match arg_type {
3777 Some(ty) if ty.ftv().is_empty() => Ok(ty.clone()),
3778 Some(ty) => infer_from_value(Some(ty)),
3779 None => infer_from_value(None),
3780 }
3781}
3782
3783pub(crate) fn binary_arg_types(typ: &Type) -> Result<(Type, Type), EngineError> {
3784 let (lhs, rest) = split_fun(typ).ok_or_else(|| EngineError::NativeType {
3785 expected: "binary function".into(),
3786 got: typ.to_string(),
3787 })?;
3788 let (rhs, _res) = split_fun(&rest).ok_or_else(|| EngineError::NativeType {
3789 expected: "binary function".into(),
3790 got: typ.to_string(),
3791 })?;
3792 Ok((lhs, rhs))
3793}
3794
3795fn project_pointer(heap: &Heap, field: &Symbol, pointer: &Pointer) -> Result<Pointer, EngineError> {
3796 let value = heap.get(pointer)?;
3797 if let Ok(index) = field.as_ref().parse::<usize>() {
3798 return match value.as_ref() {
3799 Value::Tuple(items) => {
3800 items
3801 .get(index)
3802 .cloned()
3803 .ok_or_else(|| EngineError::UnknownField {
3804 field: field.clone(),
3805 value: "tuple".into(),
3806 })
3807 }
3808 _ => Err(EngineError::UnknownField {
3809 field: field.clone(),
3810 value: heap.type_name(pointer)?.into(),
3811 }),
3812 };
3813 }
3814 match value.as_ref() {
3815 Value::Adt(_, args) if args.len() == 1 => {
3816 let inner = heap.get(&args[0])?;
3817 match inner.as_ref() {
3818 Value::Dict(map) => {
3819 map.get(field)
3820 .cloned()
3821 .ok_or_else(|| EngineError::UnknownField {
3822 field: field.clone(),
3823 value: "record".into(),
3824 })
3825 }
3826 _ => Err(EngineError::UnknownField {
3827 field: field.clone(),
3828 value: heap.type_name(&args[0])?.into(),
3829 }),
3830 }
3831 }
3832 _ => Err(EngineError::UnknownField {
3833 field: field.clone(),
3834 value: heap.type_name(pointer)?.into(),
3835 }),
3836 }
3837}
3838
3839#[async_recursion]
3840async fn eval_typed_expr<State>(
3841 engine: &Engine<State>,
3842 env: &Env,
3843 expr: &TypedExpr,
3844 gas: &mut GasMeter,
3845) -> Result<Pointer, EngineError>
3846where
3847 State: Clone + Send + Sync + 'static,
3848{
3849 check_cancelled(engine)?;
3850 let mut env = env.clone();
3851 let mut cur = expr;
3852 loop {
3853 check_cancelled(engine)?;
3854 match &cur.kind {
3855 TypedExprKind::Let { name, def, body } => {
3856 gas.charge(gas.costs.eval_node)?;
3857 let ptr = eval_typed_expr(engine, &env, def, gas).await?;
3858 env = env.extend(name.clone(), ptr);
3859 cur = body;
3860 }
3861 _ => break,
3862 }
3863 }
3864
3865 gas.charge(gas.costs.eval_node)?;
3866 match &cur.kind {
3867 TypedExprKind::Bool(v) => engine.heap.alloc_bool(*v),
3868 TypedExprKind::Uint(v) => alloc_uint_literal_as(engine, *v, &cur.typ),
3869 TypedExprKind::Int(v) => alloc_int_literal_as(engine, *v, &cur.typ),
3870 TypedExprKind::Float(v) => engine.heap.alloc_f32(*v as f32),
3871 TypedExprKind::String(v) => engine.heap.alloc_string(v.clone()),
3872 TypedExprKind::Uuid(v) => engine.heap.alloc_uuid(*v),
3873 TypedExprKind::DateTime(v) => engine.heap.alloc_datetime(*v),
3874 TypedExprKind::Hole => Err(EngineError::UnsupportedExpr),
3875 TypedExprKind::Tuple(elems) => {
3876 let mut values = Vec::with_capacity(elems.len());
3877 for elem in elems {
3878 check_cancelled(engine)?;
3879 values.push(eval_typed_expr(engine, &env, elem, gas).await?);
3880 }
3881 engine.heap.alloc_tuple(values)
3882 }
3883 TypedExprKind::List(elems) => {
3884 let mut values = Vec::with_capacity(elems.len());
3885 for elem in elems {
3886 check_cancelled(engine)?;
3887 values.push(eval_typed_expr(engine, &env, elem, gas).await?);
3888 }
3889 let mut list = engine.heap.alloc_adt(sym("Empty"), vec![])?;
3890 for value in values.into_iter().rev() {
3891 list = engine.heap.alloc_adt(sym("Cons"), vec![value, list])?;
3892 }
3893 Ok(list)
3894 }
3895 TypedExprKind::Dict(kvs) => {
3896 let mut out = BTreeMap::new();
3897 for (k, v) in kvs {
3898 check_cancelled(engine)?;
3899 out.insert(k.clone(), eval_typed_expr(engine, &env, v, gas).await?);
3900 }
3901 engine.heap.alloc_dict(out)
3902 }
3903 TypedExprKind::RecordUpdate { base, updates } => {
3904 let base_ptr = eval_typed_expr(engine, &env, base, gas).await?;
3905 let mut update_vals = BTreeMap::new();
3906 for (k, v) in updates {
3907 check_cancelled(engine)?;
3908 update_vals.insert(k.clone(), eval_typed_expr(engine, &env, v, gas).await?);
3909 }
3910
3911 let base_val = engine.heap.get(&base_ptr)?;
3912 match base_val.as_ref() {
3913 Value::Dict(map) => {
3914 let mut map = map.clone();
3915 for (k, v) in update_vals {
3916 gas.charge(gas.costs.eval_record_update_field)?;
3917 map.insert(k, v);
3918 }
3919 engine.heap.alloc_dict(map)
3920 }
3921 Value::Adt(tag, args) if args.len() == 1 => {
3922 let inner = engine.heap.get(&args[0])?;
3923 match inner.as_ref() {
3924 Value::Dict(map) => {
3925 let mut out = map.clone();
3926 for (k, v) in update_vals {
3927 gas.charge(gas.costs.eval_record_update_field)?;
3928 out.insert(k, v);
3929 }
3930 let dict = engine.heap.alloc_dict(out)?;
3931 engine.heap.alloc_adt(tag.clone(), vec![dict])
3932 }
3933 _ => Err(EngineError::UnsupportedExpr),
3934 }
3935 }
3936 _ => Err(EngineError::UnsupportedExpr),
3937 }
3938 }
3939 TypedExprKind::Var { name, .. } => {
3940 if let Some(ptr) = env.get(name) {
3941 let value = engine.heap.get(&ptr)?;
3942 match value.as_ref() {
3943 Value::Native(native) if native.arity == 0 && native.applied.is_empty() => {
3944 native.call_zero(engine, gas).await
3945 }
3946 _ => Ok(ptr),
3947 }
3948 } else if engine.type_system.class_methods.contains_key(name) {
3949 engine.resolve_class_method(name, &cur.typ, gas).await
3950 } else {
3951 let value = engine.resolve_native(name.as_ref(), &cur.typ, gas)?;
3952 match engine.heap.get(&value)?.as_ref() {
3953 Value::Native(native) if native.arity == 0 && native.applied.is_empty() => {
3954 native.call_zero(engine, gas).await
3955 }
3956 _ => Ok(value),
3957 }
3958 }
3959 }
3960 TypedExprKind::App(..) => {
3961 let mut spine: Vec<(Type, &TypedExpr)> = Vec::new();
3962 let mut head = cur;
3963 while let TypedExprKind::App(f, x) = &head.kind {
3964 check_cancelled(engine)?;
3965 spine.push((f.typ.clone(), x.as_ref()));
3966 head = f.as_ref();
3967 }
3968 spine.reverse();
3969
3970 let mut func = eval_typed_expr(engine, &env, head, gas).await?;
3971 for (func_type, arg_expr) in spine {
3972 check_cancelled(engine)?;
3973 gas.charge(gas.costs.eval_app_step)?;
3974 let arg = eval_typed_expr(engine, &env, arg_expr, gas).await?;
3975 func = apply(
3976 engine,
3977 func,
3978 arg,
3979 Some(&func_type),
3980 Some(&arg_expr.typ),
3981 gas,
3982 )
3983 .await?;
3984 }
3985 Ok(func)
3986 }
3987 TypedExprKind::Project { expr, field } => {
3988 let value = eval_typed_expr(engine, &env, expr, gas).await?;
3989 project_pointer(&engine.heap, field, &value)
3990 }
3991 TypedExprKind::Lam { param, body } => {
3992 let param_ty = split_fun(&expr.typ)
3993 .map(|(arg, _)| arg)
3994 .ok_or_else(|| EngineError::NotCallable(expr.typ.to_string()))?;
3995 engine.heap.alloc_closure(
3996 env.clone(),
3997 param.clone(),
3998 param_ty,
3999 expr.typ.clone(),
4000 Arc::new(body.as_ref().clone()),
4001 )
4002 }
4003 TypedExprKind::LetRec { bindings, body } => {
4004 let mut env_rec = env.clone();
4005 let mut slots = Vec::with_capacity(bindings.len());
4006 for (name, _) in bindings {
4007 let placeholder = engine.heap.alloc_uninitialized(name.clone())?;
4008 env_rec = env_rec.extend(name.clone(), placeholder);
4009 slots.push(placeholder);
4010 }
4011
4012 for ((_, def), slot) in bindings.iter().zip(slots.iter()) {
4013 check_cancelled(engine)?;
4014 gas.charge(gas.costs.eval_node)?;
4015 let def_ptr = eval_typed_expr(engine, &env_rec, def, gas).await?;
4016 let def_value = engine.heap.get(&def_ptr)?;
4017 engine.heap.overwrite(slot, def_value.as_ref().clone())?;
4018 }
4019
4020 eval_typed_expr(engine, &env_rec, body, gas).await
4021 }
4022 TypedExprKind::Ite {
4023 cond,
4024 then_expr,
4025 else_expr,
4026 } => {
4027 let cond_ptr = eval_typed_expr(engine, &env, cond, gas).await?;
4028 match engine.heap.pointer_as_bool(&cond_ptr) {
4029 Ok(true) => eval_typed_expr(engine, &env, then_expr, gas).await,
4030 Ok(false) => eval_typed_expr(engine, &env, else_expr, gas).await,
4031 Err(EngineError::NativeType { got, .. }) => Err(EngineError::ExpectedBool(got)),
4032 Err(err) => Err(err),
4033 }
4034 }
4035 TypedExprKind::Match { scrutinee, arms } => {
4036 let value = eval_typed_expr(engine, &env, scrutinee, gas).await?;
4037 for (pat, expr) in arms {
4038 check_cancelled(engine)?;
4039 gas.charge(gas.costs.eval_match_arm)?;
4040 if let Some(bindings) = match_pattern_ptr(&engine.heap, pat, &value) {
4041 let env = env.extend_many(bindings);
4042 return eval_typed_expr(engine, &env, expr, gas).await;
4043 }
4044 }
4045 Err(EngineError::MatchFailure)
4046 }
4047 TypedExprKind::Let { .. } => {
4048 unreachable!("let chain handled in eval_typed_expr loop")
4049 }
4050 }
4051}
4052
4053#[async_recursion]
4054pub(crate) async fn apply<State>(
4055 engine: &Engine<State>,
4056 func: Pointer,
4057 arg: Pointer,
4058 func_type: Option<&Type>,
4059 arg_type: Option<&Type>,
4060 gas: &mut GasMeter,
4061) -> Result<Pointer, EngineError>
4062where
4063 State: Clone + Send + Sync + 'static,
4064{
4065 let func_value = engine.heap.get(&func)?.as_ref().clone();
4066 match func_value {
4067 Value::Closure(Closure {
4068 env,
4069 param,
4070 param_ty,
4071 typ,
4072 body,
4073 }) => {
4074 let mut subst = Subst::new_sync();
4075 if let Some(expected) = func_type {
4076 let s_fun = unify(&typ, expected).map_err(|_| EngineError::NativeType {
4077 expected: typ.to_string(),
4078 got: expected.to_string(),
4079 })?;
4080 subst = compose_subst(s_fun, subst);
4081 }
4082 let actual_ty = resolve_arg_type(&engine.heap, arg_type, &arg)?;
4083 let param_ty = param_ty.apply(&subst);
4084 let s_arg = unify(¶m_ty, &actual_ty).map_err(|_| EngineError::NativeType {
4085 expected: param_ty.to_string(),
4086 got: actual_ty.to_string(),
4087 })?;
4088 subst = compose_subst(s_arg, subst);
4089 let env = env.extend(param, arg);
4090 let body = body.apply(&subst);
4091 eval_typed_expr(engine, &env, &body, gas).await
4092 }
4093 Value::Native(native) => native.apply(engine, arg, arg_type, gas).await,
4094 Value::Overloaded(over) => over.apply(engine, arg, func_type, arg_type, gas).await,
4095 _ => Err(EngineError::NotCallable(
4096 engine.heap.type_name(&func)?.into(),
4097 )),
4098 }
4099}
4100
4101fn match_pattern_ptr(
4102 heap: &Heap,
4103 pat: &Pattern,
4104 value: &Pointer,
4105) -> Option<HashMap<Symbol, Pointer>> {
4106 match pat {
4107 Pattern::Wildcard(..) => Some(HashMap::new()),
4108 Pattern::Var(var) => {
4109 let mut bindings = HashMap::new();
4110 bindings.insert(var.name.clone(), *value);
4111 Some(bindings)
4112 }
4113 Pattern::Named(_, name, ps) => {
4114 let v = heap.get(value).ok()?;
4115 match v.as_ref() {
4116 Value::Adt(vname, args)
4117 if vname == &name.to_dotted_symbol() && args.len() == ps.len() =>
4118 {
4119 match_patterns(heap, ps, args)
4120 }
4121 _ => None,
4122 }
4123 }
4124 Pattern::Tuple(_, ps) => {
4125 let v = heap.get(value).ok()?;
4126 match v.as_ref() {
4127 Value::Tuple(xs) if xs.len() == ps.len() => match_patterns(heap, ps, xs),
4128 _ => None,
4129 }
4130 }
4131 Pattern::List(_, ps) => {
4132 let v = heap.get(value).ok()?;
4133 let values = list_to_vec(heap, v.as_ref()).ok()?;
4134 if values.len() == ps.len() {
4135 match_patterns(heap, ps, &values)
4136 } else {
4137 None
4138 }
4139 }
4140 Pattern::Cons(_, head, tail) => {
4141 let v = heap.get(value).ok()?;
4142 match v.as_ref() {
4143 Value::Adt(tag, args) if sym_eq(tag, "Cons") && args.len() == 2 => {
4144 let mut left = match_pattern_ptr(heap, head, &args[0])?;
4145 let right = match_pattern_ptr(heap, tail, &args[1])?;
4146 left.extend(right);
4147 Some(left)
4148 }
4149 _ => None,
4150 }
4151 }
4152 Pattern::Dict(_, fields) => {
4153 let v = heap.get(value).ok()?;
4154 match v.as_ref() {
4155 Value::Dict(map) => {
4156 let mut bindings = HashMap::new();
4157 for (key, pat) in fields {
4158 let v = map.get(key)?;
4159 let sub = match_pattern_ptr(heap, pat, v)?;
4160 bindings.extend(sub);
4161 }
4162 Some(bindings)
4163 }
4164 _ => None,
4165 }
4166 }
4167 }
4168}
4169
4170fn match_patterns(
4171 heap: &Heap,
4172 patterns: &[Pattern],
4173 values: &[Pointer],
4174) -> Option<HashMap<Symbol, Pointer>> {
4175 let mut bindings = HashMap::new();
4176 for (p, v) in patterns.iter().zip(values.iter()) {
4177 let sub = match_pattern_ptr(heap, p, v)?;
4178 bindings.extend(sub);
4179 }
4180 Some(bindings)
4181}
4182
4183#[cfg(test)]
4184mod tests {
4185 use super::*;
4186 use rexlang_lexer::Token;
4187 use rexlang_parser::Parser;
4188 use rexlang_util::{GasCosts, GasMeter};
4189 use std::path::Path;
4190
4191 use crate::ReplState;
4192
4193 fn parse(code: &str) -> Arc<Expr> {
4194 let mut parser = Parser::new(Token::tokenize(code).unwrap());
4195 parser.parse_program(&mut GasMeter::default()).unwrap().expr
4196 }
4197
4198 fn parse_program(code: &str) -> rexlang_ast::expr::Program {
4199 let mut parser = Parser::new(Token::tokenize(code).unwrap());
4200 parser.parse_program(&mut GasMeter::default()).unwrap()
4201 }
4202
4203 fn strip_span(mut err: TypeError) -> TypeError {
4204 while let TypeError::Spanned { error, .. } = err {
4205 err = *error;
4206 }
4207 err
4208 }
4209
4210 fn engine_with_arith() -> Engine {
4211 Engine::with_prelude(()).unwrap()
4212 }
4213
4214 fn unlimited_gas() -> GasMeter {
4215 GasMeter::default()
4216 }
4217
4218 #[test]
4219 fn registry_markdown_lists_core_sections() {
4220 let engine = Engine::with_prelude(()).unwrap();
4221 let doc = engine.registry_markdown();
4222
4223 assert!(doc.contains("# Engine Registry"));
4224 assert!(doc.contains("## Library Index"));
4225 assert!(doc.contains("## Libraries"));
4226 assert!(doc.contains("## ADTs"));
4227 assert!(doc.contains("## Functions and Values"));
4228 assert!(doc.contains("## Type Classes"));
4229 assert!(doc.contains("## Native Implementations"));
4230 assert!(doc.contains("[`virtual:Prelude`](#library-virtual-prelude)"));
4231 assert!(doc.contains("<a id=\"library-virtual-prelude\"></a>"));
4232 assert!(doc.contains("### `virtual:Prelude`"));
4233 assert!(doc.contains("`List`"));
4234 assert!(doc.contains("`Option`"));
4235 }
4236
4237 #[test]
4238 fn library_add_adt_decls_from_types_collects_nested_unique_adts() {
4239 let mut engine = Engine::with_prelude(()).unwrap();
4240 let mut library = Library::new("acme.types");
4241 let a = Type::var(rexlang_typesystem::TypeVar::new(0, Some(sym("a"))));
4242 let types = vec![
4243 Type::fun(
4244 Type::app(Type::user_con("Foo", 1), a.clone()),
4245 Type::user_con("Bar", 0),
4246 ),
4247 Type::app(Type::user_con("Foo", 1), Type::builtin(BuiltinTypeId::I32)),
4248 ];
4249
4250 library
4251 .add_adt_decls_from_types(&mut engine, types)
4252 .unwrap();
4253
4254 assert_eq!(library.declarations.len(), 2);
4255 assert!(
4256 library
4257 .declarations
4258 .iter()
4259 .any(|d| d.starts_with("pub type Foo t0 ="))
4260 );
4261 assert!(
4262 library
4263 .declarations
4264 .iter()
4265 .any(|d| d.starts_with("pub type Bar ="))
4266 );
4267 }
4268
4269 #[test]
4270 fn library_add_adt_decls_from_types_rejects_conflicting_adts() {
4271 let mut engine = Engine::with_prelude(()).unwrap();
4272 let mut library = Library::new("acme.types");
4273 let types = vec![Type::user_con("Thing", 1), Type::user_con("Thing", 2)];
4274
4275 let err = library
4276 .add_adt_decls_from_types(&mut engine, types)
4277 .unwrap_err();
4278
4279 assert!(matches!(err, EngineError::Custom(_)));
4280 assert!(
4281 err.to_string()
4282 .contains("conflicting ADT definitions discovered in input types")
4283 );
4284 }
4285
4286 #[tokio::test]
4287 async fn repl_persists_function_definitions() {
4288 let mut gas = unlimited_gas();
4289 let mut engine = Engine::with_prelude(()).unwrap();
4290 engine.add_default_resolvers();
4291 let mut state = ReplState::new();
4292
4293 let program1 = parse_program("fn inc (x: i32) -> i32 = x + 1\ninc 1");
4294 let (v1, t1) = engine
4295 .eval_repl_program(&program1, &mut state, &mut gas)
4296 .await
4297 .unwrap();
4298 assert_eq!(t1, Type::builtin(BuiltinTypeId::I32));
4299 let expected = engine.heap.alloc_i32(2).unwrap();
4300 assert!(crate::pointer_eq(&engine.heap, &v1, &expected).unwrap());
4301
4302 let program2 = parse_program("inc 2");
4303 let (v2, t2) = engine
4304 .eval_repl_program(&program2, &mut state, &mut gas)
4305 .await
4306 .unwrap();
4307 assert_eq!(t2, Type::builtin(BuiltinTypeId::I32));
4308 let expected = engine.heap.alloc_i32(3).unwrap();
4309 assert!(crate::pointer_eq(&engine.heap, &v2, &expected).unwrap());
4310 }
4311
4312 #[tokio::test]
4313 async fn repl_persists_import_aliases() {
4314 let mut gas = unlimited_gas();
4315 let mut engine = Engine::with_prelude(()).unwrap();
4316 engine.add_default_resolvers();
4317
4318 let examples =
4320 Path::new(env!("CARGO_MANIFEST_DIR")).join("../rexlang-cli/examples/libraries_basic");
4321 engine.add_include_resolver(&examples).unwrap();
4322
4323 let mut state = ReplState::new();
4324 let program1 = parse_program("import foo.bar as Bar\n()");
4325 let (v1, t1) = engine
4326 .eval_repl_program(&program1, &mut state, &mut gas)
4327 .await
4328 .unwrap();
4329 assert_eq!(t1, Type::tuple(vec![]));
4330 let expected = engine.heap.alloc_tuple(vec![]).unwrap();
4331 assert!(crate::pointer_eq(&engine.heap, &v1, &expected).unwrap());
4332
4333 let program2 = parse_program("Bar.triple 10");
4334 let (v2, t2) = engine
4335 .eval_repl_program(&program2, &mut state, &mut gas)
4336 .await
4337 .unwrap();
4338 assert_eq!(t2, Type::builtin(BuiltinTypeId::I32));
4339 let expected = engine.heap.alloc_i32(30).unwrap();
4340 assert!(crate::pointer_eq(&engine.heap, &v2, &expected).unwrap());
4341 }
4342
4343 #[tokio::test]
4344 async fn repl_persists_imported_values() {
4345 let mut gas = unlimited_gas();
4346 let mut engine = Engine::with_prelude(()).unwrap();
4347 engine.add_default_resolvers();
4348
4349 let examples =
4351 Path::new(env!("CARGO_MANIFEST_DIR")).join("../rexlang-cli/examples/libraries_basic");
4352 engine.add_include_resolver(&examples).unwrap();
4353
4354 let mut state = ReplState::new();
4355 let program1 = parse_program("import foo.bar (triple as t)\n()");
4356 let (v1, t1) = engine
4357 .eval_repl_program(&program1, &mut state, &mut gas)
4358 .await
4359 .unwrap();
4360 assert_eq!(t1, Type::tuple(vec![]));
4361 let expected = engine.heap.alloc_tuple(vec![]).unwrap();
4362 assert!(crate::pointer_eq(&engine.heap, &v1, &expected).unwrap());
4363
4364 let program2 = parse_program("t 10");
4365 let (v2, t2) = engine
4366 .eval_repl_program(&program2, &mut state, &mut gas)
4367 .await
4368 .unwrap();
4369 assert_eq!(t2, Type::builtin(BuiltinTypeId::I32));
4370 let expected = engine.heap.alloc_i32(30).unwrap();
4371 assert!(crate::pointer_eq(&engine.heap, &v2, &expected).unwrap());
4372 }
4373
4374 #[tokio::test]
4375 async fn injected_library_can_define_pub_adt_declarations() {
4376 let mut gas = unlimited_gas();
4377 let mut engine = Engine::with_prelude(()).unwrap();
4378 engine.add_default_resolvers();
4379
4380 let mut library = Library::new("acme.status");
4381 library
4382 .add_declaration("pub type Status = Ready | Failed string")
4383 .unwrap();
4384 engine.inject_library(library).unwrap();
4385
4386 let (value, _ty) = engine
4387 .eval_snippet(
4388 r#"
4389 import acme.status (Failed)
4390 Failed "boom"
4391 "#,
4392 &mut gas,
4393 )
4394 .await
4395 .unwrap();
4396
4397 let v = engine.heap.get(&value).unwrap();
4398 match v.as_ref() {
4399 Value::Adt(tag, args) => {
4400 assert!(tag.as_ref().ends_with(".Failed"));
4401 assert_eq!(args.len(), 1);
4402 }
4403 _ => panic!("expected ADT value"),
4404 }
4405 }
4406
4407 #[tokio::test]
4408 async fn eval_can_be_cancelled_while_waiting_on_async_native() {
4409 let expr = parse("stall");
4410 let mut engine = Engine::with_prelude(()).unwrap();
4411
4412 let (started_tx, started_rx) = std::sync::mpsc::channel::<()>();
4413 let scheme = Scheme::new(vec![], vec![], Type::builtin(BuiltinTypeId::I32));
4414 engine
4415 .export_native_async_cancellable(
4416 "stall",
4417 scheme,
4418 0,
4419 move |_engine, token: CancellationToken, _, _args| {
4420 let started_tx = started_tx.clone();
4421 async move {
4422 let _ = started_tx.send(());
4423 token.cancelled().await;
4424 _engine.heap.alloc_i32(0)
4425 }
4426 .boxed()
4427 },
4428 )
4429 .unwrap();
4430
4431 let token = engine.cancellation_token();
4432 let canceller = std::thread::spawn(move || {
4433 let recv = started_rx.recv_timeout(std::time::Duration::from_secs(2));
4434 assert!(recv.is_ok(), "stall native never started");
4435 token.cancel();
4436 });
4437
4438 let mut gas = unlimited_gas();
4439 let res = engine.eval(expr.as_ref(), &mut gas).await;
4440 let joined = canceller.join();
4441 assert!(joined.is_ok(), "cancel thread panicked");
4442 assert!(matches!(res, Err(EngineError::Cancelled)));
4443 }
4444
4445 #[tokio::test]
4446 async fn eval_can_be_cancelled_while_waiting_on_non_cancellable_async_native() {
4447 let expr = parse("stall");
4448 let mut engine = Engine::with_prelude(()).unwrap();
4449
4450 let (started_tx, started_rx) = std::sync::mpsc::channel::<()>();
4451 engine
4452 .export_async("stall", move |_state: &()| {
4453 let started_tx = started_tx.clone();
4454 async move {
4455 let _ = started_tx.send(());
4456 futures::future::pending::<Result<i32, EngineError>>().await
4457 }
4458 })
4459 .unwrap();
4460
4461 let token = engine.cancellation_token();
4462 let canceller = std::thread::spawn(move || {
4463 let recv = started_rx.recv_timeout(std::time::Duration::from_secs(2));
4464 assert!(recv.is_ok(), "stall native never started");
4465 token.cancel();
4466 });
4467
4468 let mut gas = unlimited_gas();
4469 let res = engine.eval(expr.as_ref(), &mut gas).await;
4470 let joined = canceller.join();
4471 assert!(joined.is_ok(), "cancel thread panicked");
4472 assert!(matches!(res, Err(EngineError::Cancelled)));
4473 }
4474
4475 #[tokio::test]
4476 async fn native_per_impl_gas_cost_is_charged() {
4477 let expr = parse("foo");
4478 let mut engine = Engine::with_prelude(()).unwrap();
4479 let scheme = Scheme::new(vec![], vec![], Type::builtin(BuiltinTypeId::I32));
4480 engine
4481 .export_native_with_gas_cost("foo", scheme, 0, 50, |engine, _t, _args| {
4482 engine.heap.alloc_i32(1)
4483 })
4484 .unwrap();
4485
4486 let mut gas = GasMeter::new(
4487 Some(10),
4488 GasCosts {
4489 eval_node: 1,
4490 native_call_base: 1,
4491 native_call_per_arg: 0,
4492 ..GasCosts::sensible_defaults()
4493 },
4494 );
4495 let err = match engine.eval(expr.as_ref(), &mut gas).await {
4496 Ok(_) => panic!("expected out of gas"),
4497 Err(e) => e,
4498 };
4499 assert!(matches!(err, EngineError::OutOfGas(..)));
4500 }
4501
4502 #[tokio::test]
4503 async fn export_value_typed_registers_global_value() {
4504 let expr = parse("answer");
4505 let mut engine = Engine::with_prelude(()).unwrap();
4506 engine
4507 .export_value_typed("answer", Type::builtin(BuiltinTypeId::I32), Value::I32(42))
4508 .unwrap();
4509
4510 let mut gas = unlimited_gas();
4511 let (value, ty) = engine.eval(expr.as_ref(), &mut gas).await.unwrap();
4512 assert_eq!(ty, Type::builtin(BuiltinTypeId::I32));
4513 let expected = engine.heap.alloc_i32(42).unwrap();
4514 assert!(crate::pointer_eq(&engine.heap, &value, &expected).unwrap());
4515 }
4516
4517 #[tokio::test]
4518 async fn async_native_per_impl_gas_cost_is_charged() {
4519 let expr = parse("foo");
4520 let mut engine = Engine::with_prelude(()).unwrap();
4521 let scheme = Scheme::new(vec![], vec![], Type::builtin(BuiltinTypeId::I32));
4522 engine
4523 .export_native_async_with_gas_cost("foo", scheme, 0, 50, |engine, _t, _args| {
4524 async move { engine.heap.alloc_i32(1) }.boxed()
4525 })
4526 .unwrap();
4527
4528 let mut gas = GasMeter::new(
4529 Some(10),
4530 GasCosts {
4531 eval_node: 1,
4532 native_call_base: 1,
4533 native_call_per_arg: 0,
4534 ..GasCosts::sensible_defaults()
4535 },
4536 );
4537 let err = match engine.eval(expr.as_ref(), &mut gas).await {
4538 Ok(_) => panic!("expected out of gas"),
4539 Err(e) => e,
4540 };
4541 assert!(matches!(err, EngineError::OutOfGas(..)));
4542 }
4543
4544 #[tokio::test]
4545 async fn cancellable_async_native_per_impl_gas_cost_is_charged() {
4546 let expr = parse("foo");
4547 let mut engine = Engine::with_prelude(()).unwrap();
4548 let scheme = Scheme::new(vec![], vec![], Type::builtin(BuiltinTypeId::I32));
4549 engine
4550 .export_native_async_cancellable_with_gas_cost(
4551 "foo",
4552 scheme,
4553 0,
4554 50,
4555 |engine, _token: CancellationToken, _t, _args| {
4556 async move { engine.heap.alloc_i32(1) }.boxed()
4557 },
4558 )
4559 .unwrap();
4560
4561 let mut gas = GasMeter::new(
4562 Some(10),
4563 GasCosts {
4564 eval_node: 1,
4565 native_call_base: 1,
4566 native_call_per_arg: 0,
4567 ..GasCosts::sensible_defaults()
4568 },
4569 );
4570 let err = match engine.eval(expr.as_ref(), &mut gas).await {
4571 Ok(_) => panic!("expected out of gas"),
4572 Err(e) => e,
4573 };
4574 assert!(matches!(err, EngineError::OutOfGas(..)));
4575 }
4576
4577 #[tokio::test]
4578 async fn record_update_requires_known_variant_for_sum_types() {
4579 let program = parse_program(
4580 r#"
4581 type Foo = Bar { x: i32 } | Baz { x: i32 }
4582 let
4583 f = \ (foo : Foo) -> { foo with { x = 2 } }
4584 in
4585 f (Bar { x = 1 })
4586 "#,
4587 );
4588 let mut engine = engine_with_arith();
4589 engine.inject_decls(&program.decls).unwrap();
4590 let mut gas = unlimited_gas();
4591 match engine.eval(program.expr.as_ref(), &mut gas).await {
4592 Err(EngineError::Type(err)) => {
4593 let err = strip_span(err);
4594 assert!(matches!(err, TypeError::FieldNotKnown { .. }));
4595 }
4596 _ => panic!("expected type error"),
4597 }
4598 }
4599}