Skip to main content

rexlang_engine/libraries/
mod.rs

1//! Library system: resolvers, loading, and import rewriting.
2
3use std::collections::{HashMap, HashSet};
4use std::hash::{Hash, Hasher};
5use std::path::{Path, PathBuf};
6use std::sync::Arc;
7
8use async_recursion::async_recursion;
9use rexlang_ast::expr::{
10    Decl, DeclareFnDecl, Expr, FnDecl, ImportClause, ImportDecl, ImportPath, InstanceDecl, NameRef,
11    Pattern, Program, Symbol, TypeConstraint, TypeDecl, TypeExpr, Var, intern,
12};
13use rexlang_lexer::Token;
14use rexlang_parser::Parser as RexParser;
15use rexlang_typesystem::{Predicate, Type};
16use rexlang_util::{GasMeter, sha256_hex};
17use uuid::Uuid;
18
19use crate::Engine;
20use crate::{EngineError, Pointer};
21
22#[cfg(not(target_arch = "wasm32"))]
23mod filesystem;
24mod library;
25mod resolvers;
26mod system;
27mod types;
28
29#[cfg(not(target_arch = "wasm32"))]
30pub use filesystem::{default_local_resolver, include_resolver};
31pub use library::Library;
32#[cfg(all(not(target_arch = "wasm32"), feature = "github-imports"))]
33pub use resolvers::default_github_resolver;
34pub use resolvers::default_stdlib_resolver;
35pub use system::ResolverFn;
36pub use types::virtual_export_name;
37pub use types::{
38    CanonicalSymbol, LibraryExports, LibraryId, LibraryInstance, LibraryKey, ReplState,
39    ResolveRequest, ResolvedLibrary, SymbolKind,
40};
41
42pub(crate) use system::LibrarySystem;
43pub(crate) use types::library_key_for_library;
44
45use system::wrap_resolver;
46use types::{prefix_for_library, qualify};
47
48fn import_specifier(path: &ImportPath) -> String {
49    match path {
50        ImportPath::Local { segments, sha } => {
51            let base = segments
52                .iter()
53                .map(|s| s.as_ref())
54                .collect::<Vec<_>>()
55                .join(".");
56            if let Some(sha) = sha {
57                format!("{base}#{sha}")
58            } else {
59                base
60            }
61        }
62        ImportPath::Remote { url, sha } => {
63            if let Some(sha) = sha {
64                format!("{url}#{sha}")
65            } else {
66                url.clone()
67            }
68        }
69    }
70}
71
72fn spec_base_name(spec: &str) -> &str {
73    spec.split_once('#').map_or(spec, |(base, _)| base)
74}
75
76fn contains_import_alias(decls: &[Decl], alias: &Symbol) -> bool {
77    decls.iter().any(|decl| match decl {
78        Decl::Import(import_decl) => import_decl.alias == *alias,
79        _ => false,
80    })
81}
82
83fn default_import_decl(library_name: &str) -> ImportDecl {
84    ImportDecl {
85        span: rexlang_lexer::span::Span::default(),
86        is_pub: false,
87        path: ImportPath::Local {
88            segments: vec![intern(library_name)],
89            sha: None,
90        },
91        alias: intern(library_name),
92        clause: Some(ImportClause::All),
93    }
94}
95
96#[derive(Default)]
97struct ImportBindings {
98    alias_exports: HashMap<Symbol, LibraryExports>,
99    imported_values: HashMap<Symbol, CanonicalSymbol>,
100}
101
102fn add_import_bindings(
103    out: &mut ImportBindings,
104    import: &ImportDecl,
105    exports: &LibraryExports,
106    forbidden_locals: &HashSet<Symbol>,
107    existing_imported: Option<&HashSet<Symbol>>,
108) -> Result<(), EngineError> {
109    let library_name = import.alias.clone();
110    let mut bind_local = |local_name: Symbol, target: CanonicalSymbol| -> Result<(), EngineError> {
111        if forbidden_locals.contains(&local_name) {
112            return Err(crate::LibraryError::ImportNameConflictsWithLocal {
113                library: library_name.clone(),
114                name: local_name,
115            }
116            .into());
117        }
118        if let Some(existing) = existing_imported
119            && existing.contains(&local_name)
120        {
121            return Err(crate::LibraryError::DuplicateImportedName { name: local_name }.into());
122        }
123        if out.imported_values.contains_key(&local_name) {
124            return Err(crate::LibraryError::DuplicateImportedName { name: local_name }.into());
125        }
126        out.imported_values.insert(local_name, target);
127        Ok(())
128    };
129
130    match &import.clause {
131        None => {
132            out.alias_exports
133                .insert(import.alias.clone(), exports.clone());
134            Ok(())
135        }
136        Some(ImportClause::All) => {
137            for (export, target) in &exports.values {
138                bind_local(export.clone(), target.clone())?;
139            }
140            Ok(())
141        }
142        Some(ImportClause::Items(items)) => {
143            for item in items {
144                let Some(target) = exports.values.get(&item.name) else {
145                    return Err(crate::LibraryError::MissingExport {
146                        library: import.alias.clone(),
147                        export: item.name.clone(),
148                    }
149                    .into());
150                };
151                let local_name = item.alias.clone().unwrap_or_else(|| item.name.clone());
152                bind_local(local_name, target.clone())?;
153            }
154            Ok(())
155        }
156    }
157}
158
159fn collect_local_renames(
160    program: &Program,
161    prefix: &str,
162) -> (
163    HashMap<Symbol, Symbol>,
164    HashMap<Symbol, Symbol>,
165    HashMap<Symbol, Symbol>,
166) {
167    let mut values = HashMap::new();
168    let mut types = HashMap::new();
169    let mut classes = HashMap::new();
170
171    for decl in &program.decls {
172        match decl {
173            Decl::Fn(fd) => {
174                values.insert(fd.name.name.clone(), qualify(prefix, &fd.name.name));
175            }
176            Decl::DeclareFn(df) => {
177                values.insert(df.name.name.clone(), qualify(prefix, &df.name.name));
178            }
179            Decl::Type(td) => {
180                types.insert(td.name.clone(), qualify(prefix, &td.name));
181                for variant in &td.variants {
182                    values.insert(variant.name.clone(), qualify(prefix, &variant.name));
183                }
184            }
185            Decl::Class(cd) => {
186                classes.insert(cd.name.clone(), qualify(prefix, &cd.name));
187            }
188            Decl::Instance(..) | Decl::Import(..) => {}
189        }
190    }
191
192    (values, types, classes)
193}
194
195fn collect_pattern_bindings(pat: &Pattern, out: &mut Vec<Symbol>) {
196    match pat {
197        Pattern::Wildcard(..) => {}
198        Pattern::Var(v) => out.push(v.name.clone()),
199        Pattern::Named(_, _, args) => {
200            for arg in args {
201                collect_pattern_bindings(arg, out);
202            }
203        }
204        Pattern::Tuple(_, elems) | Pattern::List(_, elems) => {
205            for elem in elems {
206                collect_pattern_bindings(elem, out);
207            }
208        }
209        Pattern::Cons(_, head, tail) => {
210            collect_pattern_bindings(head, out);
211            collect_pattern_bindings(tail, out);
212        }
213        Pattern::Dict(_, fields) => {
214            for (_, pat) in fields {
215                collect_pattern_bindings(pat, out);
216            }
217        }
218    }
219}
220
221fn rename_type_expr(
222    ty: &TypeExpr,
223    type_renames: &HashMap<Symbol, Symbol>,
224    class_renames: &HashMap<Symbol, Symbol>,
225) -> TypeExpr {
226    match ty {
227        TypeExpr::Name(span, name) => {
228            let name_sym = name.to_dotted_symbol();
229            if let Some(new) = type_renames.get(&name_sym) {
230                TypeExpr::Name(*span, NameRef::Unqualified(new.clone()))
231            } else if let Some(new) = class_renames.get(&name_sym) {
232                TypeExpr::Name(*span, NameRef::Unqualified(new.clone()))
233            } else {
234                TypeExpr::Name(*span, name.clone())
235            }
236        }
237        TypeExpr::App(span, f, x) => TypeExpr::App(
238            *span,
239            Box::new(rename_type_expr(f, type_renames, class_renames)),
240            Box::new(rename_type_expr(x, type_renames, class_renames)),
241        ),
242        TypeExpr::Fun(span, a, b) => TypeExpr::Fun(
243            *span,
244            Box::new(rename_type_expr(a, type_renames, class_renames)),
245            Box::new(rename_type_expr(b, type_renames, class_renames)),
246        ),
247        TypeExpr::Tuple(span, elems) => TypeExpr::Tuple(
248            *span,
249            elems
250                .iter()
251                .map(|e| rename_type_expr(e, type_renames, class_renames))
252                .collect(),
253        ),
254        TypeExpr::Record(span, fields) => TypeExpr::Record(
255            *span,
256            fields
257                .iter()
258                .map(|(name, ty)| {
259                    (
260                        name.clone(),
261                        rename_type_expr(ty, type_renames, class_renames),
262                    )
263                })
264                .collect(),
265        ),
266    }
267}
268
269fn rename_constraints(
270    cs: &[TypeConstraint],
271    type_renames: &HashMap<Symbol, Symbol>,
272    class_renames: &HashMap<Symbol, Symbol>,
273) -> Vec<TypeConstraint> {
274    cs.iter()
275        .map(|c| TypeConstraint {
276            class: {
277                let class_sym = c.class.to_dotted_symbol();
278                class_renames
279                    .get(&class_sym)
280                    .cloned()
281                    .map(NameRef::Unqualified)
282                    .unwrap_or_else(|| c.class.clone())
283            },
284            typ: rename_type_expr(&c.typ, type_renames, class_renames),
285        })
286        .collect()
287}
288
289fn rename_pattern(pat: &Pattern, value_renames: &HashMap<Symbol, Symbol>) -> Pattern {
290    match pat {
291        Pattern::Wildcard(span) => Pattern::Wildcard(*span),
292        Pattern::Var(v) => Pattern::Var(v.clone()),
293        Pattern::Named(span, name, args) => Pattern::Named(
294            *span,
295            {
296                let name_sym = name.to_dotted_symbol();
297                value_renames
298                    .get(&name_sym)
299                    .cloned()
300                    .map(NameRef::Unqualified)
301                    .unwrap_or_else(|| name.clone())
302            },
303            args.iter()
304                .map(|p| rename_pattern(p, value_renames))
305                .collect(),
306        ),
307        Pattern::Tuple(span, elems) => Pattern::Tuple(
308            *span,
309            elems
310                .iter()
311                .map(|p| rename_pattern(p, value_renames))
312                .collect(),
313        ),
314        Pattern::List(span, elems) => Pattern::List(
315            *span,
316            elems
317                .iter()
318                .map(|p| rename_pattern(p, value_renames))
319                .collect(),
320        ),
321        Pattern::Cons(span, head, tail) => Pattern::Cons(
322            *span,
323            Box::new(rename_pattern(head, value_renames)),
324            Box::new(rename_pattern(tail, value_renames)),
325        ),
326        Pattern::Dict(span, fields) => Pattern::Dict(
327            *span,
328            fields
329                .iter()
330                .map(|(name, p)| (name.clone(), rename_pattern(p, value_renames)))
331                .collect(),
332        ),
333    }
334}
335
336fn rename_expr(
337    expr: &Expr,
338    bound: &mut HashSet<Symbol>,
339    value_renames: &HashMap<Symbol, Symbol>,
340    type_renames: &HashMap<Symbol, Symbol>,
341    class_renames: &HashMap<Symbol, Symbol>,
342) -> Expr {
343    match expr {
344        Expr::Bool(span, v) => Expr::Bool(*span, *v),
345        Expr::Uint(span, v) => Expr::Uint(*span, *v),
346        Expr::Int(span, v) => Expr::Int(*span, *v),
347        Expr::Float(span, v) => Expr::Float(*span, *v),
348        Expr::String(span, v) => Expr::String(*span, v.clone()),
349        Expr::Uuid(span, v) => Expr::Uuid(*span, *v),
350        Expr::DateTime(span, v) => Expr::DateTime(*span, *v),
351        Expr::Hole(span) => Expr::Hole(*span),
352        Expr::Tuple(span, elems) => Expr::Tuple(
353            *span,
354            elems
355                .iter()
356                .map(|e| {
357                    Arc::new(rename_expr(
358                        e,
359                        bound,
360                        value_renames,
361                        type_renames,
362                        class_renames,
363                    ))
364                })
365                .collect(),
366        ),
367        Expr::List(span, elems) => Expr::List(
368            *span,
369            elems
370                .iter()
371                .map(|e| {
372                    Arc::new(rename_expr(
373                        e,
374                        bound,
375                        value_renames,
376                        type_renames,
377                        class_renames,
378                    ))
379                })
380                .collect(),
381        ),
382        Expr::Dict(span, kvs) => Expr::Dict(
383            *span,
384            kvs.iter()
385                .map(|(k, v)| {
386                    (
387                        k.clone(),
388                        Arc::new(rename_expr(
389                            v,
390                            bound,
391                            value_renames,
392                            type_renames,
393                            class_renames,
394                        )),
395                    )
396                })
397                .collect(),
398        ),
399        Expr::RecordUpdate(span, base, updates) => Expr::RecordUpdate(
400            *span,
401            Arc::new(rename_expr(
402                base,
403                bound,
404                value_renames,
405                type_renames,
406                class_renames,
407            )),
408            updates
409                .iter()
410                .map(|(k, v)| {
411                    (
412                        k.clone(),
413                        Arc::new(rename_expr(
414                            v,
415                            bound,
416                            value_renames,
417                            type_renames,
418                            class_renames,
419                        )),
420                    )
421                })
422                .collect(),
423        ),
424        Expr::Var(v) => {
425            if bound.contains(&v.name) {
426                Expr::Var(v.clone())
427            } else if let Some(new) = value_renames.get(&v.name) {
428                Expr::Var(Var {
429                    span: v.span,
430                    name: new.clone(),
431                })
432            } else {
433                Expr::Var(v.clone())
434            }
435        }
436        Expr::App(span, f, x) => Expr::App(
437            *span,
438            Arc::new(rename_expr(
439                f,
440                bound,
441                value_renames,
442                type_renames,
443                class_renames,
444            )),
445            Arc::new(rename_expr(
446                x,
447                bound,
448                value_renames,
449                type_renames,
450                class_renames,
451            )),
452        ),
453        Expr::Project(span, base, field) => Expr::Project(
454            *span,
455            Arc::new(rename_expr(
456                base,
457                bound,
458                value_renames,
459                type_renames,
460                class_renames,
461            )),
462            field.clone(),
463        ),
464        Expr::Lam(span, scope, param, ann, constraints, body) => {
465            bound.insert(param.name.clone());
466            let out = Expr::Lam(
467                *span,
468                scope.clone(),
469                param.clone(),
470                ann.as_ref()
471                    .map(|t| rename_type_expr(t, type_renames, class_renames)),
472                rename_constraints(constraints, type_renames, class_renames),
473                Arc::new(rename_expr(
474                    body,
475                    bound,
476                    value_renames,
477                    type_renames,
478                    class_renames,
479                )),
480            );
481            bound.remove(&param.name);
482            out
483        }
484        Expr::Let(span, var, ann, val, body) => {
485            let renamed_val = rename_expr(val, bound, value_renames, type_renames, class_renames);
486            bound.insert(var.name.clone());
487            let renamed_body = rename_expr(body, bound, value_renames, type_renames, class_renames);
488            bound.remove(&var.name);
489            Expr::Let(
490                *span,
491                var.clone(),
492                ann.as_ref()
493                    .map(|t| rename_type_expr(t, type_renames, class_renames)),
494                Arc::new(renamed_val),
495                Arc::new(renamed_body),
496            )
497        }
498        Expr::LetRec(span, bindings, body) => {
499            let names: Vec<Symbol> = bindings
500                .iter()
501                .map(|(var, _, _)| var.name.clone())
502                .collect();
503            for name in &names {
504                bound.insert(name.clone());
505            }
506            let renamed_bindings = bindings
507                .iter()
508                .map(|(var, ann, def)| {
509                    (
510                        var.clone(),
511                        ann.as_ref()
512                            .map(|t| rename_type_expr(t, type_renames, class_renames)),
513                        Arc::new(rename_expr(
514                            def,
515                            bound,
516                            value_renames,
517                            type_renames,
518                            class_renames,
519                        )),
520                    )
521                })
522                .collect();
523            let renamed_body = Arc::new(rename_expr(
524                body,
525                bound,
526                value_renames,
527                type_renames,
528                class_renames,
529            ));
530            for name in &names {
531                bound.remove(name);
532            }
533            Expr::LetRec(*span, renamed_bindings, renamed_body)
534        }
535        Expr::Ite(span, c, t, e) => Expr::Ite(
536            *span,
537            Arc::new(rename_expr(
538                c,
539                bound,
540                value_renames,
541                type_renames,
542                class_renames,
543            )),
544            Arc::new(rename_expr(
545                t,
546                bound,
547                value_renames,
548                type_renames,
549                class_renames,
550            )),
551            Arc::new(rename_expr(
552                e,
553                bound,
554                value_renames,
555                type_renames,
556                class_renames,
557            )),
558        ),
559        Expr::Match(span, scrutinee, arms) => {
560            let scrutinee = Arc::new(rename_expr(
561                scrutinee,
562                bound,
563                value_renames,
564                type_renames,
565                class_renames,
566            ));
567            let mut renamed_arms = Vec::new();
568            for (pat, arm_expr) in arms {
569                let pat_renamed = rename_pattern(pat, value_renames);
570                let mut binds = Vec::new();
571                collect_pattern_bindings(&pat_renamed, &mut binds);
572                for b in &binds {
573                    bound.insert(b.clone());
574                }
575                let arm_expr = Arc::new(rename_expr(
576                    arm_expr,
577                    bound,
578                    value_renames,
579                    type_renames,
580                    class_renames,
581                ));
582                for b in &binds {
583                    bound.remove(b);
584                }
585                renamed_arms.push((pat_renamed, arm_expr));
586            }
587            Expr::Match(*span, scrutinee, renamed_arms)
588        }
589        Expr::Ann(span, e, t) => Expr::Ann(
590            *span,
591            Arc::new(rename_expr(
592                e,
593                bound,
594                value_renames,
595                type_renames,
596                class_renames,
597            )),
598            rename_type_expr(t, type_renames, class_renames),
599        ),
600    }
601}
602
603fn qualify_program(program: &Program, prefix: &str) -> Program {
604    let (value_renames, type_renames, class_renames) = collect_local_renames(program, prefix);
605
606    let decls = program
607        .decls
608        .iter()
609        .filter_map(|decl| match decl {
610            Decl::Import(..) => None,
611            Decl::Type(td) => {
612                let name = type_renames
613                    .get(&td.name)
614                    .cloned()
615                    .unwrap_or_else(|| td.name.clone());
616                let variants = td
617                    .variants
618                    .iter()
619                    .map(|v| rexlang_ast::expr::TypeVariant {
620                        name: value_renames
621                            .get(&v.name)
622                            .cloned()
623                            .unwrap_or_else(|| v.name.clone()),
624                        args: v
625                            .args
626                            .iter()
627                            .map(|t| rename_type_expr(t, &type_renames, &class_renames))
628                            .collect(),
629                    })
630                    .collect();
631                Some(Decl::Type(TypeDecl {
632                    span: td.span,
633                    is_pub: td.is_pub,
634                    name,
635                    params: td.params.clone(),
636                    variants,
637                }))
638            }
639            Decl::Fn(fd) => {
640                let name_sym = value_renames
641                    .get(&fd.name.name)
642                    .cloned()
643                    .unwrap_or_else(|| fd.name.name.clone());
644                let name = Var {
645                    span: fd.name.span,
646                    name: name_sym,
647                };
648                let params: Vec<(Var, TypeExpr)> = fd
649                    .params
650                    .iter()
651                    .map(|(v, ann)| {
652                        (
653                            v.clone(),
654                            rename_type_expr(ann, &type_renames, &class_renames),
655                        )
656                    })
657                    .collect();
658                let ret = rename_type_expr(&fd.ret, &type_renames, &class_renames);
659                let constraints =
660                    rename_constraints(&fd.constraints, &type_renames, &class_renames);
661                let mut bound = HashSet::new();
662                for (v, _) in &params {
663                    bound.insert(v.name.clone());
664                }
665                let body = Arc::new(rename_expr(
666                    fd.body.as_ref(),
667                    &mut bound,
668                    &value_renames,
669                    &type_renames,
670                    &class_renames,
671                ));
672                Some(Decl::Fn(FnDecl {
673                    span: fd.span,
674                    is_pub: fd.is_pub,
675                    name,
676                    params,
677                    ret,
678                    constraints,
679                    body,
680                }))
681            }
682            Decl::DeclareFn(df) => {
683                let name_sym = value_renames
684                    .get(&df.name.name)
685                    .cloned()
686                    .unwrap_or_else(|| df.name.name.clone());
687                let name = Var {
688                    span: df.name.span,
689                    name: name_sym,
690                };
691                let params: Vec<(Var, TypeExpr)> = df
692                    .params
693                    .iter()
694                    .map(|(v, ann)| {
695                        (
696                            v.clone(),
697                            rename_type_expr(ann, &type_renames, &class_renames),
698                        )
699                    })
700                    .collect();
701                let ret = rename_type_expr(&df.ret, &type_renames, &class_renames);
702                let constraints =
703                    rename_constraints(&df.constraints, &type_renames, &class_renames);
704                Some(Decl::DeclareFn(DeclareFnDecl {
705                    span: df.span,
706                    is_pub: df.is_pub,
707                    name,
708                    params,
709                    ret,
710                    constraints,
711                }))
712            }
713            Decl::Class(cd) => {
714                let name = class_renames
715                    .get(&cd.name)
716                    .cloned()
717                    .unwrap_or_else(|| cd.name.clone());
718                let supers = rename_constraints(&cd.supers, &type_renames, &class_renames);
719                let methods = cd
720                    .methods
721                    .iter()
722                    .map(|m| rexlang_ast::expr::ClassMethodSig {
723                        name: m.name.clone(),
724                        typ: rename_type_expr(&m.typ, &type_renames, &class_renames),
725                    })
726                    .collect();
727                Some(Decl::Class(rexlang_ast::expr::ClassDecl {
728                    span: cd.span,
729                    is_pub: cd.is_pub,
730                    name,
731                    params: cd.params.clone(),
732                    supers,
733                    methods,
734                }))
735            }
736            Decl::Instance(id) => {
737                let class = class_renames
738                    .get(&id.class)
739                    .cloned()
740                    .unwrap_or_else(|| id.class.clone());
741                let head = rename_type_expr(&id.head, &type_renames, &class_renames);
742                let context = rename_constraints(&id.context, &type_renames, &class_renames);
743                let mut methods = Vec::new();
744                for m in &id.methods {
745                    let mut bound = HashSet::new();
746                    let body = Arc::new(rename_expr(
747                        m.body.as_ref(),
748                        &mut bound,
749                        &value_renames,
750                        &type_renames,
751                        &class_renames,
752                    ));
753                    methods.push(rexlang_ast::expr::InstanceMethodImpl {
754                        name: m.name.clone(),
755                        body,
756                    });
757                }
758                Some(Decl::Instance(InstanceDecl {
759                    span: id.span,
760                    is_pub: id.is_pub,
761                    class,
762                    head,
763                    context,
764                    methods,
765                }))
766            }
767        })
768        .collect();
769
770    let mut bound = HashSet::new();
771    let expr = Arc::new(rename_expr(
772        program.expr.as_ref(),
773        &mut bound,
774        &value_renames,
775        &type_renames,
776        &class_renames,
777    ));
778
779    Program { decls, expr }
780}
781
782fn alias_is_visible(
783    name: &Symbol,
784    bound: &HashSet<Symbol>,
785    shadowed_values: Option<&HashSet<Symbol>>,
786) -> bool {
787    if bound.contains(name) {
788        return false;
789    }
790    match shadowed_values {
791        None => true,
792        Some(s) => !s.contains(name),
793    }
794}
795
796fn rewrite_import_uses_expr(
797    expr: &Expr,
798    bound: &mut HashSet<Symbol>,
799    aliases: &HashMap<Symbol, LibraryExports>,
800    imported_values: &HashMap<Symbol, CanonicalSymbol>,
801    shadowed_values: Option<&HashSet<Symbol>>,
802) -> Expr {
803    match expr {
804        Expr::Bool(span, v) => Expr::Bool(*span, *v),
805        Expr::Uint(span, v) => Expr::Uint(*span, *v),
806        Expr::Int(span, v) => Expr::Int(*span, *v),
807        Expr::Float(span, v) => Expr::Float(*span, *v),
808        Expr::String(span, v) => Expr::String(*span, v.clone()),
809        Expr::Uuid(span, v) => Expr::Uuid(*span, *v),
810        Expr::DateTime(span, v) => Expr::DateTime(*span, *v),
811        Expr::Hole(span) => Expr::Hole(*span),
812        Expr::Project(span, base, field) => {
813            if let Expr::Var(v) = base.as_ref()
814                && alias_is_visible(&v.name, bound, shadowed_values)
815                && let Some(exports) = aliases.get(&v.name)
816                && let Some(internal) = exports.values.get(field)
817            {
818                return Expr::Var(Var {
819                    span: *span,
820                    name: internal.symbol().clone(),
821                });
822            }
823            Expr::Project(
824                *span,
825                Arc::new(rewrite_import_uses_expr(
826                    base,
827                    bound,
828                    aliases,
829                    imported_values,
830                    shadowed_values,
831                )),
832                field.clone(),
833            )
834        }
835        Expr::Var(v) => {
836            if alias_is_visible(&v.name, bound, shadowed_values)
837                && let Some(internal) = imported_values.get(&v.name)
838            {
839                Expr::Var(Var {
840                    span: v.span,
841                    name: internal.symbol().clone(),
842                })
843            } else {
844                Expr::Var(v.clone())
845            }
846        }
847        Expr::Lam(span, scope, param, ann, constraints, body) => {
848            let ann = ann
849                .as_ref()
850                .map(|t| rewrite_import_uses_type_expr(t, bound, aliases, shadowed_values));
851            let constraints = constraints
852                .iter()
853                .map(|c| TypeConstraint {
854                    class: rewrite_import_uses_class_name(
855                        &c.class,
856                        bound,
857                        aliases,
858                        shadowed_values,
859                    ),
860                    typ: rewrite_import_uses_type_expr(&c.typ, bound, aliases, shadowed_values),
861                })
862                .collect();
863            bound.insert(param.name.clone());
864            let out = Expr::Lam(
865                *span,
866                scope.clone(),
867                param.clone(),
868                ann,
869                constraints,
870                Arc::new(rewrite_import_uses_expr(
871                    body,
872                    bound,
873                    aliases,
874                    imported_values,
875                    shadowed_values,
876                )),
877            );
878            bound.remove(&param.name);
879            out
880        }
881        Expr::Let(span, var, ann, val, body) => {
882            let val = Arc::new(rewrite_import_uses_expr(
883                val,
884                bound,
885                aliases,
886                imported_values,
887                shadowed_values,
888            ));
889            bound.insert(var.name.clone());
890            let body = Arc::new(rewrite_import_uses_expr(
891                body,
892                bound,
893                aliases,
894                imported_values,
895                shadowed_values,
896            ));
897            bound.remove(&var.name);
898            Expr::Let(
899                *span,
900                var.clone(),
901                ann.as_ref()
902                    .map(|t| rewrite_import_uses_type_expr(t, bound, aliases, shadowed_values)),
903                val,
904                body,
905            )
906        }
907        Expr::LetRec(span, bindings, body) => {
908            let anns: Vec<Option<TypeExpr>> = bindings
909                .iter()
910                .map(|(_, ann, _)| {
911                    ann.as_ref()
912                        .map(|t| rewrite_import_uses_type_expr(t, bound, aliases, shadowed_values))
913                })
914                .collect();
915            let names: Vec<Symbol> = bindings
916                .iter()
917                .map(|(var, _, _)| var.name.clone())
918                .collect();
919            for name in &names {
920                bound.insert(name.clone());
921            }
922            let bindings = bindings
923                .iter()
924                .zip(anns)
925                .map(|((var, _ann, def), ann)| {
926                    (
927                        var.clone(),
928                        ann,
929                        Arc::new(rewrite_import_uses_expr(
930                            def,
931                            bound,
932                            aliases,
933                            imported_values,
934                            shadowed_values,
935                        )),
936                    )
937                })
938                .collect();
939            let body = Arc::new(rewrite_import_uses_expr(
940                body,
941                bound,
942                aliases,
943                imported_values,
944                shadowed_values,
945            ));
946            for name in &names {
947                bound.remove(name);
948            }
949            Expr::LetRec(*span, bindings, body)
950        }
951        Expr::Match(span, scrutinee, arms) => {
952            let scrutinee = Arc::new(rewrite_import_uses_expr(
953                scrutinee,
954                bound,
955                aliases,
956                imported_values,
957                shadowed_values,
958            ));
959            let mut renamed_arms = Vec::new();
960            for (pat, arm_expr) in arms {
961                let pat = rewrite_import_uses_pattern(pat, imported_values);
962                let mut binds = Vec::new();
963                collect_pattern_bindings(&pat, &mut binds);
964                for b in &binds {
965                    bound.insert(b.clone());
966                }
967                let arm_expr = Arc::new(rewrite_import_uses_expr(
968                    arm_expr,
969                    bound,
970                    aliases,
971                    imported_values,
972                    shadowed_values,
973                ));
974                for b in &binds {
975                    bound.remove(b);
976                }
977                renamed_arms.push((pat, arm_expr));
978            }
979            Expr::Match(*span, scrutinee, renamed_arms)
980        }
981        Expr::Tuple(span, elems) => Expr::Tuple(
982            *span,
983            elems
984                .iter()
985                .map(|e| {
986                    Arc::new(rewrite_import_uses_expr(
987                        e,
988                        bound,
989                        aliases,
990                        imported_values,
991                        shadowed_values,
992                    ))
993                })
994                .collect(),
995        ),
996        Expr::List(span, elems) => Expr::List(
997            *span,
998            elems
999                .iter()
1000                .map(|e| {
1001                    Arc::new(rewrite_import_uses_expr(
1002                        e,
1003                        bound,
1004                        aliases,
1005                        imported_values,
1006                        shadowed_values,
1007                    ))
1008                })
1009                .collect(),
1010        ),
1011        Expr::Dict(span, kvs) => Expr::Dict(
1012            *span,
1013            kvs.iter()
1014                .map(|(k, v)| {
1015                    (
1016                        k.clone(),
1017                        Arc::new(rewrite_import_uses_expr(
1018                            v,
1019                            bound,
1020                            aliases,
1021                            imported_values,
1022                            shadowed_values,
1023                        )),
1024                    )
1025                })
1026                .collect(),
1027        ),
1028        Expr::RecordUpdate(span, base, updates) => Expr::RecordUpdate(
1029            *span,
1030            Arc::new(rewrite_import_uses_expr(
1031                base,
1032                bound,
1033                aliases,
1034                imported_values,
1035                shadowed_values,
1036            )),
1037            updates
1038                .iter()
1039                .map(|(k, v)| {
1040                    (
1041                        k.clone(),
1042                        Arc::new(rewrite_import_uses_expr(
1043                            v,
1044                            bound,
1045                            aliases,
1046                            imported_values,
1047                            shadowed_values,
1048                        )),
1049                    )
1050                })
1051                .collect(),
1052        ),
1053        Expr::App(span, f, x) => Expr::App(
1054            *span,
1055            Arc::new(rewrite_import_uses_expr(
1056                f,
1057                bound,
1058                aliases,
1059                imported_values,
1060                shadowed_values,
1061            )),
1062            Arc::new(rewrite_import_uses_expr(
1063                x,
1064                bound,
1065                aliases,
1066                imported_values,
1067                shadowed_values,
1068            )),
1069        ),
1070        Expr::Ite(span, c, t, e) => Expr::Ite(
1071            *span,
1072            Arc::new(rewrite_import_uses_expr(
1073                c,
1074                bound,
1075                aliases,
1076                imported_values,
1077                shadowed_values,
1078            )),
1079            Arc::new(rewrite_import_uses_expr(
1080                t,
1081                bound,
1082                aliases,
1083                imported_values,
1084                shadowed_values,
1085            )),
1086            Arc::new(rewrite_import_uses_expr(
1087                e,
1088                bound,
1089                aliases,
1090                imported_values,
1091                shadowed_values,
1092            )),
1093        ),
1094        Expr::Ann(span, e, t) => Expr::Ann(
1095            *span,
1096            Arc::new(rewrite_import_uses_expr(
1097                e,
1098                bound,
1099                aliases,
1100                imported_values,
1101                shadowed_values,
1102            )),
1103            rewrite_import_uses_type_expr(t, bound, aliases, shadowed_values),
1104        ),
1105    }
1106}
1107
1108fn rewrite_import_uses_pattern(
1109    pat: &Pattern,
1110    imported_values: &HashMap<Symbol, CanonicalSymbol>,
1111) -> Pattern {
1112    match pat {
1113        Pattern::Wildcard(span) => Pattern::Wildcard(*span),
1114        Pattern::Var(v) => Pattern::Var(v.clone()),
1115        Pattern::Named(span, name, args) => {
1116            let name = imported_values
1117                .get(&name.to_dotted_symbol())
1118                .map(|c| NameRef::Unqualified(c.symbol().clone()))
1119                .unwrap_or_else(|| name.clone());
1120            let args = args
1121                .iter()
1122                .map(|p| rewrite_import_uses_pattern(p, imported_values))
1123                .collect();
1124            Pattern::Named(*span, name, args)
1125        }
1126        Pattern::Tuple(span, elems) => Pattern::Tuple(
1127            *span,
1128            elems
1129                .iter()
1130                .map(|p| rewrite_import_uses_pattern(p, imported_values))
1131                .collect(),
1132        ),
1133        Pattern::List(span, elems) => Pattern::List(
1134            *span,
1135            elems
1136                .iter()
1137                .map(|p| rewrite_import_uses_pattern(p, imported_values))
1138                .collect(),
1139        ),
1140        Pattern::Cons(span, head, tail) => Pattern::Cons(
1141            *span,
1142            Box::new(rewrite_import_uses_pattern(head, imported_values)),
1143            Box::new(rewrite_import_uses_pattern(tail, imported_values)),
1144        ),
1145        Pattern::Dict(span, fields) => Pattern::Dict(
1146            *span,
1147            fields
1148                .iter()
1149                .map(|(name, p)| {
1150                    (
1151                        name.clone(),
1152                        rewrite_import_uses_pattern(p, imported_values),
1153                    )
1154                })
1155                .collect(),
1156        ),
1157    }
1158}
1159
1160fn rewrite_import_uses_class_name(
1161    class: &NameRef,
1162    bound: &HashSet<Symbol>,
1163    aliases: &HashMap<Symbol, LibraryExports>,
1164    shadowed_values: Option<&HashSet<Symbol>>,
1165) -> NameRef {
1166    let Some((alias_sym, member_sym)) = qualified_alias_member(class) else {
1167        return class.clone();
1168    };
1169    if !alias_is_visible(alias_sym, bound, shadowed_values) {
1170        return class.clone();
1171    }
1172    let Some(exports) = aliases.get(alias_sym) else {
1173        return class.clone();
1174    };
1175    exports
1176        .classes
1177        .get(member_sym)
1178        .map(|s| s.symbol().clone())
1179        .map(NameRef::Unqualified)
1180        .unwrap_or_else(|| class.clone())
1181}
1182
1183fn qualified_alias_member(name: &NameRef) -> Option<(&Symbol, &Symbol)> {
1184    match name {
1185        NameRef::Qualified(_, segments) if segments.len() == 2 => {
1186            Some((&segments[0], &segments[1]))
1187        }
1188        _ => None,
1189    }
1190}
1191
1192fn rewrite_import_uses_type_expr(
1193    ty: &TypeExpr,
1194    bound: &HashSet<Symbol>,
1195    aliases: &HashMap<Symbol, LibraryExports>,
1196    shadowed_values: Option<&HashSet<Symbol>>,
1197) -> TypeExpr {
1198    match ty {
1199        TypeExpr::Name(span, name) => {
1200            let Some((alias_sym, member_sym)) = qualified_alias_member(name) else {
1201                return TypeExpr::Name(*span, name.clone());
1202            };
1203            if !alias_is_visible(alias_sym, bound, shadowed_values) {
1204                return TypeExpr::Name(*span, name.clone());
1205            }
1206            let Some(exports) = aliases.get(alias_sym) else {
1207                return TypeExpr::Name(*span, name.clone());
1208            };
1209            if let Some(new) = exports.types.get(member_sym) {
1210                TypeExpr::Name(*span, NameRef::Unqualified(new.symbol().clone()))
1211            } else if let Some(new) = exports.classes.get(member_sym) {
1212                TypeExpr::Name(*span, NameRef::Unqualified(new.symbol().clone()))
1213            } else {
1214                TypeExpr::Name(*span, name.clone())
1215            }
1216        }
1217        TypeExpr::App(span, f, x) => TypeExpr::App(
1218            *span,
1219            Box::new(rewrite_import_uses_type_expr(
1220                f,
1221                bound,
1222                aliases,
1223                shadowed_values,
1224            )),
1225            Box::new(rewrite_import_uses_type_expr(
1226                x,
1227                bound,
1228                aliases,
1229                shadowed_values,
1230            )),
1231        ),
1232        TypeExpr::Fun(span, a, b) => TypeExpr::Fun(
1233            *span,
1234            Box::new(rewrite_import_uses_type_expr(
1235                a,
1236                bound,
1237                aliases,
1238                shadowed_values,
1239            )),
1240            Box::new(rewrite_import_uses_type_expr(
1241                b,
1242                bound,
1243                aliases,
1244                shadowed_values,
1245            )),
1246        ),
1247        TypeExpr::Tuple(span, elems) => TypeExpr::Tuple(
1248            *span,
1249            elems
1250                .iter()
1251                .map(|e| rewrite_import_uses_type_expr(e, bound, aliases, shadowed_values))
1252                .collect(),
1253        ),
1254        TypeExpr::Record(span, fields) => TypeExpr::Record(
1255            *span,
1256            fields
1257                .iter()
1258                .map(|(name, ty)| {
1259                    (
1260                        name.clone(),
1261                        rewrite_import_uses_type_expr(ty, bound, aliases, shadowed_values),
1262                    )
1263                })
1264                .collect(),
1265        ),
1266    }
1267}
1268
1269fn rewrite_import_uses(
1270    program: &Program,
1271    aliases: &HashMap<Symbol, LibraryExports>,
1272    imported_values: &HashMap<Symbol, CanonicalSymbol>,
1273    shadowed_values: Option<&HashSet<Symbol>>,
1274) -> Program {
1275    let decl_bound = HashSet::new();
1276    let decls = program
1277        .decls
1278        .iter()
1279        .map(|decl| match decl {
1280            Decl::Fn(fd) => {
1281                let mut bound: HashSet<Symbol> =
1282                    fd.params.iter().map(|(v, _)| v.name.clone()).collect();
1283                let body = Arc::new(rewrite_import_uses_expr(
1284                    fd.body.as_ref(),
1285                    &mut bound,
1286                    aliases,
1287                    imported_values,
1288                    shadowed_values,
1289                ));
1290                Decl::Fn(FnDecl {
1291                    span: fd.span,
1292                    is_pub: fd.is_pub,
1293                    name: fd.name.clone(),
1294                    params: fd
1295                        .params
1296                        .iter()
1297                        .map(|(v, t)| {
1298                            (
1299                                v.clone(),
1300                                rewrite_import_uses_type_expr(
1301                                    t,
1302                                    &decl_bound,
1303                                    aliases,
1304                                    shadowed_values,
1305                                ),
1306                            )
1307                        })
1308                        .collect(),
1309                    ret: rewrite_import_uses_type_expr(
1310                        &fd.ret,
1311                        &decl_bound,
1312                        aliases,
1313                        shadowed_values,
1314                    ),
1315                    constraints: fd
1316                        .constraints
1317                        .iter()
1318                        .map(|c| TypeConstraint {
1319                            class: rewrite_import_uses_class_name(
1320                                &c.class,
1321                                &decl_bound,
1322                                aliases,
1323                                shadowed_values,
1324                            ),
1325                            typ: rewrite_import_uses_type_expr(
1326                                &c.typ,
1327                                &decl_bound,
1328                                aliases,
1329                                shadowed_values,
1330                            ),
1331                        })
1332                        .collect(),
1333                    body,
1334                })
1335            }
1336            Decl::DeclareFn(df) => Decl::DeclareFn(DeclareFnDecl {
1337                span: df.span,
1338                is_pub: df.is_pub,
1339                name: df.name.clone(),
1340                params: df
1341                    .params
1342                    .iter()
1343                    .map(|(v, t)| {
1344                        (
1345                            v.clone(),
1346                            rewrite_import_uses_type_expr(t, &decl_bound, aliases, shadowed_values),
1347                        )
1348                    })
1349                    .collect(),
1350                ret: rewrite_import_uses_type_expr(&df.ret, &decl_bound, aliases, shadowed_values),
1351                constraints: df
1352                    .constraints
1353                    .iter()
1354                    .map(|c| TypeConstraint {
1355                        class: rewrite_import_uses_class_name(
1356                            &c.class,
1357                            &decl_bound,
1358                            aliases,
1359                            shadowed_values,
1360                        ),
1361                        typ: rewrite_import_uses_type_expr(
1362                            &c.typ,
1363                            &decl_bound,
1364                            aliases,
1365                            shadowed_values,
1366                        ),
1367                    })
1368                    .collect(),
1369            }),
1370            Decl::Type(td) => Decl::Type(TypeDecl {
1371                span: td.span,
1372                is_pub: td.is_pub,
1373                name: td.name.clone(),
1374                params: td.params.clone(),
1375                variants: td
1376                    .variants
1377                    .iter()
1378                    .map(|v| rexlang_ast::expr::TypeVariant {
1379                        name: v.name.clone(),
1380                        args: v
1381                            .args
1382                            .iter()
1383                            .map(|t| {
1384                                rewrite_import_uses_type_expr(
1385                                    t,
1386                                    &decl_bound,
1387                                    aliases,
1388                                    shadowed_values,
1389                                )
1390                            })
1391                            .collect(),
1392                    })
1393                    .collect(),
1394            }),
1395            Decl::Class(cd) => Decl::Class(rexlang_ast::expr::ClassDecl {
1396                span: cd.span,
1397                is_pub: cd.is_pub,
1398                name: cd.name.clone(),
1399                params: cd.params.clone(),
1400                supers: cd
1401                    .supers
1402                    .iter()
1403                    .map(|c| TypeConstraint {
1404                        class: rewrite_import_uses_class_name(
1405                            &c.class,
1406                            &decl_bound,
1407                            aliases,
1408                            shadowed_values,
1409                        ),
1410                        typ: rewrite_import_uses_type_expr(
1411                            &c.typ,
1412                            &decl_bound,
1413                            aliases,
1414                            shadowed_values,
1415                        ),
1416                    })
1417                    .collect(),
1418                methods: cd
1419                    .methods
1420                    .iter()
1421                    .map(|m| rexlang_ast::expr::ClassMethodSig {
1422                        name: m.name.clone(),
1423                        typ: rewrite_import_uses_type_expr(
1424                            &m.typ,
1425                            &decl_bound,
1426                            aliases,
1427                            shadowed_values,
1428                        ),
1429                    })
1430                    .collect(),
1431            }),
1432            Decl::Instance(inst) => {
1433                let methods = inst
1434                    .methods
1435                    .iter()
1436                    .map(|m| {
1437                        let mut bound = HashSet::new();
1438                        let body = Arc::new(rewrite_import_uses_expr(
1439                            m.body.as_ref(),
1440                            &mut bound,
1441                            aliases,
1442                            imported_values,
1443                            shadowed_values,
1444                        ));
1445                        rexlang_ast::expr::InstanceMethodImpl {
1446                            name: m.name.clone(),
1447                            body,
1448                        }
1449                    })
1450                    .collect();
1451                Decl::Instance(InstanceDecl {
1452                    span: inst.span,
1453                    is_pub: inst.is_pub,
1454                    class: rewrite_import_uses_class_name(
1455                        &NameRef::from_dotted(inst.class.as_ref()),
1456                        &decl_bound,
1457                        aliases,
1458                        shadowed_values,
1459                    )
1460                    .to_dotted_symbol(),
1461                    head: rewrite_import_uses_type_expr(
1462                        &inst.head,
1463                        &decl_bound,
1464                        aliases,
1465                        shadowed_values,
1466                    ),
1467                    context: inst
1468                        .context
1469                        .iter()
1470                        .map(|c| TypeConstraint {
1471                            class: rewrite_import_uses_class_name(
1472                                &c.class,
1473                                &decl_bound,
1474                                aliases,
1475                                shadowed_values,
1476                            ),
1477                            typ: rewrite_import_uses_type_expr(
1478                                &c.typ,
1479                                &decl_bound,
1480                                aliases,
1481                                shadowed_values,
1482                            ),
1483                        })
1484                        .collect(),
1485                    methods,
1486                })
1487            }
1488            other => other.clone(),
1489        })
1490        .collect();
1491
1492    let mut bound = HashSet::new();
1493    let expr = Arc::new(rewrite_import_uses_expr(
1494        program.expr.as_ref(),
1495        &mut bound,
1496        aliases,
1497        imported_values,
1498        shadowed_values,
1499    ));
1500    Program { decls, expr }
1501}
1502
1503fn validate_import_uses_expr(
1504    expr: &Expr,
1505    bound: &mut HashSet<Symbol>,
1506    aliases: &HashMap<Symbol, LibraryExports>,
1507    shadowed_values: Option<&HashSet<Symbol>>,
1508) -> Result<(), EngineError> {
1509    match expr {
1510        Expr::Project(_, base, field) => {
1511            if let Expr::Var(v) = base.as_ref()
1512                && alias_is_visible(&v.name, bound, shadowed_values)
1513                && let Some(exports) = aliases.get(&v.name)
1514                && !exports.values.contains_key(field)
1515            {
1516                return Err(crate::LibraryError::MissingExport {
1517                    library: v.name.clone(),
1518                    export: field.clone(),
1519                }
1520                .into());
1521            }
1522            validate_import_uses_expr(base, bound, aliases, shadowed_values)
1523        }
1524        Expr::Lam(_, _, param, ann, constraints, body) => {
1525            if let Some(ann) = ann {
1526                validate_import_uses_type_expr(ann, bound, aliases, shadowed_values)?;
1527            }
1528            for c in constraints {
1529                validate_import_uses_class_name(&c.class, bound, aliases, shadowed_values)?;
1530                validate_import_uses_type_expr(&c.typ, bound, aliases, shadowed_values)?;
1531            }
1532            bound.insert(param.name.clone());
1533            let res = validate_import_uses_expr(body, bound, aliases, shadowed_values);
1534            bound.remove(&param.name);
1535            res
1536        }
1537        Expr::Let(_, var, ann, val, body) => {
1538            if let Some(ann) = ann {
1539                validate_import_uses_type_expr(ann, bound, aliases, shadowed_values)?;
1540            }
1541            validate_import_uses_expr(val, bound, aliases, shadowed_values)?;
1542            bound.insert(var.name.clone());
1543            let res = validate_import_uses_expr(body, bound, aliases, shadowed_values);
1544            bound.remove(&var.name);
1545            res
1546        }
1547        Expr::LetRec(_, bindings, body) => {
1548            for (_, ann, _) in bindings {
1549                if let Some(ann) = ann {
1550                    validate_import_uses_type_expr(ann, bound, aliases, shadowed_values)?;
1551                }
1552            }
1553            let names: Vec<Symbol> = bindings
1554                .iter()
1555                .map(|(var, _, _)| var.name.clone())
1556                .collect();
1557            for name in &names {
1558                bound.insert(name.clone());
1559            }
1560            for (_, _ann, def) in bindings {
1561                validate_import_uses_expr(def, bound, aliases, shadowed_values)?;
1562            }
1563            let res = validate_import_uses_expr(body, bound, aliases, shadowed_values);
1564            for name in &names {
1565                bound.remove(name);
1566            }
1567            res
1568        }
1569        Expr::Match(_, scrutinee, arms) => {
1570            validate_import_uses_expr(scrutinee, bound, aliases, shadowed_values)?;
1571            for (pat, arm_expr) in arms {
1572                let mut binds = Vec::new();
1573                collect_pattern_bindings(pat, &mut binds);
1574                for b in &binds {
1575                    bound.insert(b.clone());
1576                }
1577                let res = validate_import_uses_expr(arm_expr, bound, aliases, shadowed_values);
1578                for b in &binds {
1579                    bound.remove(b);
1580                }
1581                res?;
1582            }
1583            Ok(())
1584        }
1585        Expr::Tuple(_, elems) | Expr::List(_, elems) => {
1586            for e in elems {
1587                validate_import_uses_expr(e, bound, aliases, shadowed_values)?;
1588            }
1589            Ok(())
1590        }
1591        Expr::Dict(_, kvs) => {
1592            for v in kvs.values() {
1593                validate_import_uses_expr(v, bound, aliases, shadowed_values)?;
1594            }
1595            Ok(())
1596        }
1597        Expr::RecordUpdate(_, base, updates) => {
1598            validate_import_uses_expr(base, bound, aliases, shadowed_values)?;
1599            for v in updates.values() {
1600                validate_import_uses_expr(v, bound, aliases, shadowed_values)?;
1601            }
1602            Ok(())
1603        }
1604        Expr::App(_, f, x) => {
1605            validate_import_uses_expr(f, bound, aliases, shadowed_values)?;
1606            validate_import_uses_expr(x, bound, aliases, shadowed_values)
1607        }
1608        Expr::Ite(_, c, t, e) => {
1609            validate_import_uses_expr(c, bound, aliases, shadowed_values)?;
1610            validate_import_uses_expr(t, bound, aliases, shadowed_values)?;
1611            validate_import_uses_expr(e, bound, aliases, shadowed_values)
1612        }
1613        Expr::Ann(_, e, t) => {
1614            validate_import_uses_expr(e, bound, aliases, shadowed_values)?;
1615            validate_import_uses_type_expr(t, bound, aliases, shadowed_values)
1616        }
1617        Expr::Var(..)
1618        | Expr::Bool(..)
1619        | Expr::Uint(..)
1620        | Expr::Int(..)
1621        | Expr::Float(..)
1622        | Expr::String(..)
1623        | Expr::Uuid(..)
1624        | Expr::DateTime(..)
1625        | Expr::Hole(..) => Ok(()),
1626    }
1627}
1628
1629fn validate_import_uses_class_name(
1630    class: &NameRef,
1631    bound: &HashSet<Symbol>,
1632    aliases: &HashMap<Symbol, LibraryExports>,
1633    shadowed_values: Option<&HashSet<Symbol>>,
1634) -> Result<(), EngineError> {
1635    let Some((alias_sym, member_sym)) = qualified_alias_member(class) else {
1636        return Ok(());
1637    };
1638    if !alias_is_visible(alias_sym, bound, shadowed_values) {
1639        return Ok(());
1640    }
1641    let Some(exports) = aliases.get(alias_sym) else {
1642        return Ok(());
1643    };
1644    if exports.classes.contains_key(member_sym) {
1645        return Ok(());
1646    }
1647    Err(crate::LibraryError::MissingExport {
1648        library: alias_sym.clone(),
1649        export: member_sym.clone(),
1650    }
1651    .into())
1652}
1653
1654fn validate_import_uses_type_expr(
1655    ty: &TypeExpr,
1656    bound: &HashSet<Symbol>,
1657    aliases: &HashMap<Symbol, LibraryExports>,
1658    shadowed_values: Option<&HashSet<Symbol>>,
1659) -> Result<(), EngineError> {
1660    match ty {
1661        TypeExpr::Name(_, name) => {
1662            let Some((alias_sym, member_sym)) = qualified_alias_member(name) else {
1663                return Ok(());
1664            };
1665            if !alias_is_visible(alias_sym, bound, shadowed_values) {
1666                return Ok(());
1667            }
1668            let Some(exports) = aliases.get(alias_sym) else {
1669                return Ok(());
1670            };
1671            if exports.types.contains_key(member_sym) || exports.classes.contains_key(member_sym) {
1672                Ok(())
1673            } else {
1674                Err(crate::LibraryError::MissingExport {
1675                    library: alias_sym.clone(),
1676                    export: member_sym.clone(),
1677                }
1678                .into())
1679            }
1680        }
1681        TypeExpr::App(_, f, x) => {
1682            validate_import_uses_type_expr(f, bound, aliases, shadowed_values)?;
1683            validate_import_uses_type_expr(x, bound, aliases, shadowed_values)
1684        }
1685        TypeExpr::Fun(_, a, b) => {
1686            validate_import_uses_type_expr(a, bound, aliases, shadowed_values)?;
1687            validate_import_uses_type_expr(b, bound, aliases, shadowed_values)
1688        }
1689        TypeExpr::Tuple(_, elems) => {
1690            for e in elems {
1691                validate_import_uses_type_expr(e, bound, aliases, shadowed_values)?;
1692            }
1693            Ok(())
1694        }
1695        TypeExpr::Record(_, fields) => {
1696            for (_, t) in fields {
1697                validate_import_uses_type_expr(t, bound, aliases, shadowed_values)?;
1698            }
1699            Ok(())
1700        }
1701    }
1702}
1703
1704fn validate_import_uses(
1705    program: &Program,
1706    aliases: &HashMap<Symbol, LibraryExports>,
1707    shadowed_values: Option<&HashSet<Symbol>>,
1708) -> Result<(), EngineError> {
1709    for decl in &program.decls {
1710        match decl {
1711            Decl::Fn(fd) => {
1712                for (_, t) in &fd.params {
1713                    validate_import_uses_type_expr(t, &HashSet::new(), aliases, shadowed_values)?;
1714                }
1715                validate_import_uses_type_expr(&fd.ret, &HashSet::new(), aliases, shadowed_values)?;
1716                for c in &fd.constraints {
1717                    validate_import_uses_class_name(
1718                        &c.class,
1719                        &HashSet::new(),
1720                        aliases,
1721                        shadowed_values,
1722                    )?;
1723                    validate_import_uses_type_expr(
1724                        &c.typ,
1725                        &HashSet::new(),
1726                        aliases,
1727                        shadowed_values,
1728                    )?;
1729                }
1730                let mut bound: HashSet<Symbol> =
1731                    fd.params.iter().map(|(v, _)| v.name.clone()).collect();
1732                validate_import_uses_expr(fd.body.as_ref(), &mut bound, aliases, shadowed_values)?;
1733            }
1734            Decl::DeclareFn(df) => {
1735                for (_, t) in &df.params {
1736                    validate_import_uses_type_expr(t, &HashSet::new(), aliases, shadowed_values)?;
1737                }
1738                validate_import_uses_type_expr(&df.ret, &HashSet::new(), aliases, shadowed_values)?;
1739                for c in &df.constraints {
1740                    validate_import_uses_class_name(
1741                        &c.class,
1742                        &HashSet::new(),
1743                        aliases,
1744                        shadowed_values,
1745                    )?;
1746                    validate_import_uses_type_expr(
1747                        &c.typ,
1748                        &HashSet::new(),
1749                        aliases,
1750                        shadowed_values,
1751                    )?;
1752                }
1753            }
1754            Decl::Type(td) => {
1755                for v in &td.variants {
1756                    for t in &v.args {
1757                        validate_import_uses_type_expr(
1758                            t,
1759                            &HashSet::new(),
1760                            aliases,
1761                            shadowed_values,
1762                        )?;
1763                    }
1764                }
1765            }
1766            Decl::Class(cd) => {
1767                for c in &cd.supers {
1768                    validate_import_uses_class_name(
1769                        &c.class,
1770                        &HashSet::new(),
1771                        aliases,
1772                        shadowed_values,
1773                    )?;
1774                    validate_import_uses_type_expr(
1775                        &c.typ,
1776                        &HashSet::new(),
1777                        aliases,
1778                        shadowed_values,
1779                    )?;
1780                }
1781                for m in &cd.methods {
1782                    validate_import_uses_type_expr(
1783                        &m.typ,
1784                        &HashSet::new(),
1785                        aliases,
1786                        shadowed_values,
1787                    )?;
1788                }
1789            }
1790            Decl::Instance(inst) => {
1791                validate_import_uses_class_name(
1792                    &NameRef::from_dotted(inst.class.as_ref()),
1793                    &HashSet::new(),
1794                    aliases,
1795                    shadowed_values,
1796                )?;
1797                validate_import_uses_type_expr(
1798                    &inst.head,
1799                    &HashSet::new(),
1800                    aliases,
1801                    shadowed_values,
1802                )?;
1803                for c in &inst.context {
1804                    validate_import_uses_class_name(
1805                        &c.class,
1806                        &HashSet::new(),
1807                        aliases,
1808                        shadowed_values,
1809                    )?;
1810                    validate_import_uses_type_expr(
1811                        &c.typ,
1812                        &HashSet::new(),
1813                        aliases,
1814                        shadowed_values,
1815                    )?;
1816                }
1817                for m in &inst.methods {
1818                    let mut bound = HashSet::new();
1819                    validate_import_uses_expr(
1820                        m.body.as_ref(),
1821                        &mut bound,
1822                        aliases,
1823                        shadowed_values,
1824                    )?;
1825                }
1826            }
1827            Decl::Import(..) => {}
1828        }
1829    }
1830    let mut bound = HashSet::new();
1831    validate_import_uses_expr(program.expr.as_ref(), &mut bound, aliases, shadowed_values)
1832}
1833
1834fn decl_value_names(decls: &[Decl]) -> HashSet<Symbol> {
1835    let mut out = HashSet::new();
1836    for decl in decls {
1837        match decl {
1838            Decl::Fn(fd) => {
1839                out.insert(fd.name.name.clone());
1840            }
1841            Decl::DeclareFn(df) => {
1842                out.insert(df.name.name.clone());
1843            }
1844            Decl::Type(td) => {
1845                for variant in &td.variants {
1846                    out.insert(variant.name.clone());
1847                }
1848            }
1849            Decl::Class(..) | Decl::Instance(..) | Decl::Import(..) => {}
1850        }
1851    }
1852    out
1853}
1854
1855fn interface_decls_from_program(program: &Program) -> Vec<Decl> {
1856    let mut out = Vec::new();
1857    for decl in &program.decls {
1858        match decl {
1859            Decl::Fn(fd) if fd.is_pub => out.push(Decl::DeclareFn(DeclareFnDecl {
1860                span: fd.span,
1861                is_pub: fd.is_pub,
1862                name: fd.name.clone(),
1863                params: fd.params.clone(),
1864                ret: fd.ret.clone(),
1865                constraints: fd.constraints.clone(),
1866            })),
1867            Decl::Instance(..)
1868            | Decl::Import(..)
1869            | Decl::Fn(..)
1870            | Decl::DeclareFn(..)
1871            | Decl::Type(..)
1872            | Decl::Class(..) => {}
1873        }
1874    }
1875    out
1876}
1877
1878fn graph_imports_for_program(program: &Program, default_imports: &[String]) -> Vec<ImportDecl> {
1879    let mut out = Vec::new();
1880    for decl in &program.decls {
1881        if let Decl::Import(import_decl) = decl {
1882            out.push(import_decl.clone());
1883        }
1884    }
1885    for library_name in default_imports {
1886        let alias = intern(library_name);
1887        if contains_import_alias(&program.decls, &alias) {
1888            continue;
1889        }
1890        out.push(default_import_decl(library_name));
1891    }
1892    out
1893}
1894
1895fn tarjan_scc_library_ids(
1896    nodes: &[LibraryId],
1897    edges: &HashMap<LibraryId, Vec<LibraryId>>,
1898) -> Vec<Vec<LibraryId>> {
1899    // Tarjan's SCC algorithm (linear in |V| + |E|).
1900    //
1901    // References:
1902    // - Tarjan, R. E. (1972). "Depth-first search and linear graph algorithms."
1903    //   SIAM Journal on Computing, 1(2), 146-160.
1904    // - Cormen et al. (CLRS), 3rd ed., §22.5 "Strongly connected components".
1905    //
1906    // Why Tarjan here:
1907    // - We need explicit SCC groups to process library cycles as units.
1908    // - We want one DFS pass with low overhead because this runs in library loading paths.
1909    #[derive(Default)]
1910    struct TarjanState {
1911        index: usize,
1912        index_of: HashMap<LibraryId, usize>,
1913        lowlink: HashMap<LibraryId, usize>,
1914        stack: Vec<LibraryId>,
1915        on_stack: HashSet<LibraryId>,
1916        components: Vec<Vec<LibraryId>>,
1917    }
1918
1919    fn strong_connect(
1920        v: &LibraryId,
1921        edges: &HashMap<LibraryId, Vec<LibraryId>>,
1922        st: &mut TarjanState,
1923    ) {
1924        st.index_of.insert(v.clone(), st.index);
1925        st.lowlink.insert(v.clone(), st.index);
1926        st.index += 1;
1927
1928        st.stack.push(v.clone());
1929        st.on_stack.insert(v.clone());
1930
1931        if let Some(neighbors) = edges.get(v) {
1932            for w in neighbors {
1933                if !st.index_of.contains_key(w) {
1934                    strong_connect(w, edges, st);
1935                    let lw = st.lowlink.get(w).copied();
1936                    if let (Some(lw), Some(lv)) = (lw, st.lowlink.get_mut(v)) {
1937                        *lv = (*lv).min(lw);
1938                    }
1939                } else if st.on_stack.contains(w) {
1940                    let iw = st.index_of.get(w).copied();
1941                    if let (Some(iw), Some(lv)) = (iw, st.lowlink.get_mut(v)) {
1942                        *lv = (*lv).min(iw);
1943                    }
1944                }
1945            }
1946        }
1947
1948        // Root of an SCC when lowlink(v) == index(v): pop until we get v.
1949        let is_root = st.lowlink.get(v) == st.index_of.get(v);
1950        if is_root {
1951            let mut component = Vec::new();
1952            loop {
1953                let Some(w) = st.stack.pop() else {
1954                    break;
1955                };
1956                st.on_stack.remove(&w);
1957                component.push(w.clone());
1958                if &w == v {
1959                    break;
1960                }
1961            }
1962            st.components.push(component);
1963        }
1964    }
1965
1966    let mut st = TarjanState::default();
1967    for node in nodes {
1968        if !st.index_of.contains_key(node) {
1969            strong_connect(node, edges, &mut st);
1970        }
1971    }
1972    st.components
1973}
1974
1975fn exports_from_program(program: &Program, prefix: &str, library_id: &LibraryId) -> LibraryExports {
1976    let (value_renames, type_renames, class_renames) = collect_local_renames(program, prefix);
1977    let module_key = library_key_for_library(library_id);
1978
1979    let mut values = HashMap::new();
1980    let mut types = HashMap::new();
1981    let mut classes = HashMap::new();
1982
1983    for decl in &program.decls {
1984        match decl {
1985            Decl::Fn(fd) if fd.is_pub => {
1986                if let Some(internal) = value_renames.get(&fd.name.name) {
1987                    values.insert(
1988                        fd.name.name.clone(),
1989                        CanonicalSymbol::from_symbol(
1990                            module_key,
1991                            SymbolKind::Value,
1992                            fd.name.name.clone(),
1993                            internal.clone(),
1994                        ),
1995                    );
1996                }
1997            }
1998            Decl::DeclareFn(df) if df.is_pub => {
1999                if let Some(internal) = value_renames.get(&df.name.name) {
2000                    values.insert(
2001                        df.name.name.clone(),
2002                        CanonicalSymbol::from_symbol(
2003                            module_key,
2004                            SymbolKind::Value,
2005                            df.name.name.clone(),
2006                            internal.clone(),
2007                        ),
2008                    );
2009                }
2010            }
2011            Decl::Type(td) if td.is_pub => {
2012                if let Some(internal) = type_renames.get(&td.name) {
2013                    types.insert(
2014                        td.name.clone(),
2015                        CanonicalSymbol::from_symbol(
2016                            module_key,
2017                            SymbolKind::Type,
2018                            td.name.clone(),
2019                            internal.clone(),
2020                        ),
2021                    );
2022                }
2023                for variant in &td.variants {
2024                    if let Some(internal) = value_renames.get(&variant.name) {
2025                        values.insert(
2026                            variant.name.clone(),
2027                            CanonicalSymbol::from_symbol(
2028                                module_key,
2029                                SymbolKind::Value,
2030                                variant.name.clone(),
2031                                internal.clone(),
2032                            ),
2033                        );
2034                    }
2035                }
2036            }
2037            Decl::Class(cd) if cd.is_pub => {
2038                if let Some(internal) = class_renames.get(&cd.name) {
2039                    classes.insert(
2040                        cd.name.clone(),
2041                        CanonicalSymbol::from_symbol(
2042                            module_key,
2043                            SymbolKind::Class,
2044                            cd.name.clone(),
2045                            internal.clone(),
2046                        ),
2047                    );
2048                }
2049            }
2050            Decl::Instance(..)
2051            | Decl::Import(..)
2052            | Decl::Fn(..)
2053            | Decl::DeclareFn(..)
2054            | Decl::Type(..)
2055            | Decl::Class(..) => {}
2056        }
2057    }
2058
2059    LibraryExports {
2060        values,
2061        types,
2062        classes,
2063    }
2064}
2065
2066fn parse_program_from_source(
2067    source: &str,
2068    context: Option<&LibraryId>,
2069    gas: Option<&mut GasMeter>,
2070) -> Result<Program, EngineError> {
2071    let tokens = Token::tokenize(source).map_err(|e| match context {
2072        Some(id) => EngineError::from(crate::LibraryError::LexInLibrary {
2073            library: id.clone(),
2074            source: e,
2075        }),
2076        None => EngineError::from(crate::LibraryError::Lex { source: e }),
2077    })?;
2078    let mut parser = RexParser::new(tokens);
2079    let program = match gas {
2080        Some(gas) => parser.parse_program(gas),
2081        None => parser.parse_program(&mut GasMeter::default()),
2082    }
2083    .map_err(|errs| match context {
2084        Some(id) => EngineError::from(crate::LibraryError::ParseInLibrary {
2085            library: id.clone(),
2086            errors: errs,
2087        }),
2088        None => EngineError::from(crate::LibraryError::Parse { errors: errs }),
2089    })?;
2090    if let Some(library) = context
2091        && !matches!(program.expr.as_ref(), Expr::Tuple(_, elems) if elems.is_empty())
2092    {
2093        return Err(crate::LibraryError::TopLevelExprInLibrary {
2094            library: library.clone(),
2095        }
2096        .into());
2097    }
2098    Ok(program)
2099}
2100
2101impl<State> Engine<State>
2102where
2103    State: Clone + Send + Sync + 'static,
2104{
2105    fn source_fingerprint(source: &str) -> String {
2106        sha256_hex(source.as_bytes())
2107    }
2108
2109    fn refresh_if_stale(&mut self, resolved: &ResolvedLibrary) -> Result<String, EngineError> {
2110        let next = Self::source_fingerprint(&resolved.source);
2111        if let Some(prev) = self.library_source_fingerprints.get(&resolved.id)
2112            && prev != &next
2113        {
2114            self.invalidate_library_caches(&resolved.id)?;
2115        }
2116        Ok(next)
2117    }
2118
2119    fn remove_type_level_symbols_for_library_interface(&mut self, decls: &[Decl]) {
2120        for decl in decls {
2121            match decl {
2122                Decl::Fn(fd) => {
2123                    self.type_system.env.remove(&fd.name.name);
2124                    self.type_system.declared_values.remove(&fd.name.name);
2125                }
2126                Decl::DeclareFn(df) => {
2127                    self.type_system.env.remove(&df.name.name);
2128                    self.type_system.declared_values.remove(&df.name.name);
2129                }
2130                Decl::Type(td) => {
2131                    self.type_system.adts.remove(&td.name);
2132                    for variant in &td.variants {
2133                        self.type_system.env.remove(&variant.name);
2134                        self.type_system.declared_values.remove(&variant.name);
2135                    }
2136                }
2137                Decl::Class(cd) => {
2138                    self.type_system.classes.classes.remove(&cd.name);
2139                    self.type_system.classes.instances.remove(&cd.name);
2140                    self.type_system.class_info.remove(&cd.name);
2141                    for method in &cd.methods {
2142                        self.type_system.env.remove(&method.name);
2143                        self.type_system.class_methods.remove(&method.name);
2144                    }
2145                }
2146                Decl::Import(..) | Decl::Instance(..) => {}
2147            }
2148        }
2149    }
2150
2151    fn invalidate_library_caches(&mut self, id: &LibraryId) -> Result<(), EngineError> {
2152        if let Some(prev_interface) = self.library_interface_cache.get(id).cloned() {
2153            self.remove_type_level_symbols_for_library_interface(&prev_interface);
2154        }
2155        self.modules.invalidate(id)?;
2156        self.library_exports_cache.remove(id);
2157        self.library_interface_cache.remove(id);
2158        self.library_sources.remove(id);
2159        self.library_source_fingerprints.remove(id);
2160        self.published_cycle_interfaces.remove(id);
2161        Ok(())
2162    }
2163
2164    fn load_library_types_via_scc(
2165        &mut self,
2166        root: ResolvedLibrary,
2167        gas: &mut GasMeter,
2168        loaded: &mut HashMap<LibraryId, LibraryExports>,
2169        loading: &mut HashSet<LibraryId>,
2170    ) -> Result<LibraryExports, EngineError> {
2171        #[derive(Clone)]
2172        struct PendingModule {
2173            resolved: ResolvedLibrary,
2174            program: Program,
2175            prefix: String,
2176        }
2177
2178        if let Some(exports) = loaded.get(&root.id)
2179            && !loading.contains(&root.id)
2180        {
2181            return Ok(exports.clone());
2182        }
2183
2184        let mut pending: HashMap<LibraryId, PendingModule> = HashMap::new();
2185        let mut edges: HashMap<LibraryId, Vec<LibraryId>> = HashMap::new();
2186        let mut stack = vec![root.clone()];
2187
2188        while let Some(resolved) = stack.pop() {
2189            let fingerprint = self.refresh_if_stale(&resolved)?;
2190            if pending.contains_key(&resolved.id) {
2191                continue;
2192            }
2193            if loaded.contains_key(&resolved.id) && !loading.contains(&resolved.id) {
2194                continue;
2195            }
2196
2197            let prefix = prefix_for_library(&resolved.id);
2198            let program =
2199                parse_program_from_source(&resolved.source, Some(&resolved.id), Some(&mut *gas))?;
2200            let exports = exports_from_program(&program, &prefix, &resolved.id);
2201            loaded.insert(resolved.id.clone(), exports);
2202            loading.insert(resolved.id.clone());
2203            self.library_sources
2204                .insert(resolved.id.clone(), resolved.source.clone());
2205
2206            let imports = graph_imports_for_program(&program, self.default_imports());
2207            for import_decl in imports {
2208                let spec = import_specifier(&import_decl.path);
2209                if self
2210                    .virtual_library_exports(spec_base_name(&spec))
2211                    .is_some()
2212                {
2213                    continue;
2214                }
2215                let imported = self.modules.resolve(ResolveRequest {
2216                    library_name: spec,
2217                    importer: Some(resolved.id.clone()),
2218                })?;
2219                edges
2220                    .entry(resolved.id.clone())
2221                    .or_default()
2222                    .push(imported.id.clone());
2223                if (loading.contains(&imported.id) || !loaded.contains_key(&imported.id))
2224                    && !pending.contains_key(&imported.id)
2225                {
2226                    stack.push(imported);
2227                }
2228            }
2229
2230            let library_id = resolved.id.clone();
2231            pending.insert(
2232                library_id.clone(),
2233                PendingModule {
2234                    resolved,
2235                    program,
2236                    prefix,
2237                },
2238            );
2239            self.library_source_fingerprints
2240                .insert(library_id, fingerprint);
2241        }
2242
2243        if pending.is_empty() {
2244            return loaded.get(&root.id).cloned().ok_or_else(|| {
2245                EngineError::Internal("missing library exports after SCC load".into())
2246            });
2247        }
2248
2249        let pending_ids: Vec<LibraryId> = pending.keys().cloned().collect();
2250        let sccs = tarjan_scc_library_ids(&pending_ids, &edges);
2251
2252        // Tarjan yields SCCs in reverse topological order of the SCC DAG, so
2253        // dependencies are processed before dependents.
2254        for component in sccs {
2255            for library_id in &component {
2256                let node = pending
2257                    .get(library_id)
2258                    .ok_or_else(|| EngineError::Internal("missing pending library node".into()))?;
2259                let rewritten = self.rewrite_program_with_imports(
2260                    &node.program,
2261                    Some(node.resolved.id.clone()),
2262                    &node.prefix,
2263                    gas,
2264                    loaded,
2265                    loading,
2266                )?;
2267                self.inject_decls(&rewritten.decls)?;
2268            }
2269            for library_id in component {
2270                loading.remove(&library_id);
2271            }
2272        }
2273
2274        loaded
2275            .get(&root.id)
2276            .cloned()
2277            .ok_or_else(|| EngineError::Internal("missing root exports after SCC load".into()))
2278    }
2279
2280    fn ensure_cycle_interfaces_published(
2281        &mut self,
2282        library_id: &LibraryId,
2283    ) -> Result<(), EngineError> {
2284        if self.published_cycle_interfaces.contains(library_id) {
2285            return Ok(());
2286        }
2287        let Some(decls) = self.library_interface_cache.get(library_id).cloned() else {
2288            return Ok(());
2289        };
2290        self.inject_decls(&decls)?;
2291        self.publish_runtime_interfaces(&decls)?;
2292        self.published_cycle_interfaces.insert(library_id.clone());
2293        Ok(())
2294    }
2295
2296    fn publish_runtime_interfaces(&mut self, decls: &[Decl]) -> Result<(), EngineError> {
2297        let mut signatures = Vec::new();
2298        for decl in decls {
2299            let Decl::DeclareFn(df) = decl else {
2300                continue;
2301            };
2302            signatures.push(df.clone());
2303        }
2304        self.publish_runtime_decl_interfaces(&signatures)
2305    }
2306
2307    #[async_recursion]
2308    async fn resolve_library_exports_from_import_decl_async(
2309        &mut self,
2310        import_decl: &ImportDecl,
2311        importer: Option<LibraryId>,
2312        gas: &mut GasMeter,
2313    ) -> Result<LibraryExports, EngineError> {
2314        let spec = import_specifier(&import_decl.path);
2315        if let Some(exports) = self.virtual_library_exports(spec_base_name(&spec)) {
2316            return Ok(exports);
2317        }
2318        let imported = self.modules.resolve(ResolveRequest {
2319            library_name: spec,
2320            importer,
2321        })?;
2322        self.refresh_if_stale(&imported)?;
2323        if let Some(exports) = self.library_exports_cache.get(&imported.id).cloned() {
2324            self.ensure_cycle_interfaces_published(&imported.id)?;
2325            return Ok(exports);
2326        }
2327        let inst = self.load_library_from_resolved(imported, gas).await?;
2328        Ok(inst.exports)
2329    }
2330
2331    async fn add_default_import_bindings(
2332        &mut self,
2333        bindings: &mut ImportBindings,
2334        decls: &[Decl],
2335        importer: Option<LibraryId>,
2336        forbidden_locals: &HashSet<Symbol>,
2337        existing_imported: Option<&HashSet<Symbol>>,
2338        gas: &mut GasMeter,
2339    ) -> Result<(), EngineError> {
2340        let existing_names: HashSet<Symbol> = existing_imported.cloned().unwrap_or_default();
2341        let default_imports = self.default_imports().to_vec();
2342        for library_name in default_imports {
2343            let alias = intern(&library_name);
2344            if contains_import_alias(decls, &alias) {
2345                continue;
2346            }
2347            let import_decl = default_import_decl(&library_name);
2348            let exports = self
2349                .resolve_library_exports_from_import_decl_async(&import_decl, importer.clone(), gas)
2350                .await?;
2351            for (local, target) in exports.values {
2352                if !forbidden_locals.contains(&local)
2353                    && !existing_names.contains(&local)
2354                    && !bindings.imported_values.contains_key(&local)
2355                {
2356                    bindings.imported_values.insert(local, target);
2357                }
2358            }
2359        }
2360        Ok(())
2361    }
2362
2363    pub fn add_resolver<F>(&mut self, name: impl Into<String>, f: F)
2364    where
2365        F: Fn(ResolveRequest) -> Result<Option<ResolvedLibrary>, EngineError>
2366            + Send
2367            + Sync
2368            + 'static,
2369    {
2370        self.modules.add_resolver(name, wrap_resolver(f));
2371    }
2372
2373    pub fn add_default_resolvers(&mut self) {
2374        self.modules
2375            .add_resolver("stdlib", default_stdlib_resolver());
2376
2377        #[cfg(not(target_arch = "wasm32"))]
2378        self.modules.add_resolver("local", default_local_resolver());
2379
2380        #[cfg(all(not(target_arch = "wasm32"), feature = "github-imports"))]
2381        {
2382            self.modules
2383                .add_resolver("remote", default_github_resolver());
2384        }
2385    }
2386
2387    #[cfg(not(target_arch = "wasm32"))]
2388    pub fn add_include_resolver(&mut self, root: impl AsRef<Path>) -> Result<(), EngineError> {
2389        let canon =
2390            root.as_ref()
2391                .canonicalize()
2392                .map_err(|e| crate::LibraryError::InvalidIncludeRoot {
2393                    path: root.as_ref().to_path_buf(),
2394                    source: e,
2395                })?;
2396        self.modules.add_resolver(
2397            format!("include:{}", canon.display()),
2398            include_resolver(canon),
2399        );
2400        Ok(())
2401    }
2402
2403    #[cfg(target_arch = "wasm32")]
2404    pub fn add_include_resolver(&mut self, _root: impl AsRef<Path>) -> Result<(), EngineError> {
2405        Err(EngineError::UnsupportedExpr)
2406    }
2407
2408    #[async_recursion]
2409    async fn import_bindings_for_decls(
2410        &mut self,
2411        decls: &[Decl],
2412        importer: Option<LibraryId>,
2413        forbidden_locals: &HashSet<Symbol>,
2414        existing_imported: Option<&HashSet<Symbol>>,
2415        gas: &mut GasMeter,
2416    ) -> Result<ImportBindings, EngineError> {
2417        let mut bindings = ImportBindings::default();
2418        for decl in decls {
2419            let Decl::Import(import_decl) = decl else {
2420                continue;
2421            };
2422            let exports = self
2423                .resolve_library_exports_from_import_decl_async(import_decl, importer.clone(), gas)
2424                .await?;
2425            add_import_bindings(
2426                &mut bindings,
2427                import_decl,
2428                &exports,
2429                forbidden_locals,
2430                existing_imported,
2431            )?;
2432        }
2433        self.add_default_import_bindings(
2434            &mut bindings,
2435            decls,
2436            importer,
2437            forbidden_locals,
2438            existing_imported,
2439            gas,
2440        )
2441        .await?;
2442        Ok(bindings)
2443    }
2444
2445    #[async_recursion]
2446    async fn load_library_from_resolved(
2447        &mut self,
2448        resolved: ResolvedLibrary,
2449        gas: &mut GasMeter,
2450    ) -> Result<LibraryInstance, EngineError> {
2451        let source_fingerprint = self.refresh_if_stale(&resolved)?;
2452        if let Some(inst) = self.modules.cached(&resolved.id)? {
2453            return Ok(inst);
2454        }
2455
2456        self.modules.mark_loading(&resolved.id)?;
2457
2458        let prefix = prefix_for_library(&resolved.id);
2459        let program = parse_program_from_source(&resolved.source, Some(&resolved.id), Some(gas))?;
2460        self.library_sources
2461            .insert(resolved.id.clone(), resolved.source.clone());
2462        let exports = exports_from_program(&program, &prefix, &resolved.id);
2463        self.library_exports_cache
2464            .insert(resolved.id.clone(), exports.clone());
2465        let qualified = qualify_program(&program, &prefix);
2466        let interfaces = interface_decls_from_program(&qualified);
2467        self.library_interface_cache
2468            .insert(resolved.id.clone(), interfaces);
2469
2470        // Resolve imports first so qualified names exist in the environment.
2471        let local_values = decl_value_names(&program.decls);
2472        let import_bindings = self
2473            .import_bindings_for_decls(
2474                &program.decls,
2475                Some(resolved.id.clone()),
2476                &local_values,
2477                None,
2478                gas,
2479            )
2480            .await?;
2481
2482        // Qualify local names, then rewrite `alias.foo` uses into internal symbols.
2483        validate_import_uses(&qualified, &import_bindings.alias_exports, None)?;
2484        let rewritten = rewrite_import_uses(
2485            &qualified,
2486            &import_bindings.alias_exports,
2487            &import_bindings.imported_values,
2488            None,
2489        );
2490
2491        self.inject_decls(&rewritten.decls)?;
2492        let init_value = self.heap.alloc_tuple(vec![])?;
2493        let init_type = Type::tuple(vec![]);
2494
2495        let inst = LibraryInstance {
2496            id: resolved.id.clone(),
2497            exports,
2498            init_value,
2499            init_type,
2500            source_fingerprint: Some(source_fingerprint.clone()),
2501        };
2502        self.modules.store_loaded(inst.clone())?;
2503        self.library_source_fingerprints
2504            .insert(resolved.id.clone(), source_fingerprint);
2505        Ok(inst)
2506    }
2507
2508    fn load_library_types_from_resolved(
2509        &mut self,
2510        resolved: ResolvedLibrary,
2511        gas: &mut GasMeter,
2512        loaded: &mut HashMap<LibraryId, LibraryExports>,
2513        loading: &mut HashSet<LibraryId>,
2514    ) -> Result<LibraryExports, EngineError> {
2515        if let Some(exports) = loaded.get(&resolved.id) {
2516            return Ok(exports.clone());
2517        }
2518
2519        if loading.contains(&resolved.id)
2520            && let Some(exports) = loaded.get(&resolved.id)
2521        {
2522            return Ok(exports.clone());
2523        }
2524        self.load_library_types_via_scc(resolved, gas, loaded, loading)
2525    }
2526
2527    fn resolve_library_exports_for_rewrite(
2528        &mut self,
2529        import_decl: &ImportDecl,
2530        importer: Option<LibraryId>,
2531        gas: &mut GasMeter,
2532        loaded: &mut HashMap<LibraryId, LibraryExports>,
2533        loading: &mut HashSet<LibraryId>,
2534    ) -> Result<LibraryExports, EngineError> {
2535        let spec = import_specifier(&import_decl.path);
2536        if let Some(exports) = self.virtual_library_exports(spec_base_name(&spec)) {
2537            return Ok(exports);
2538        }
2539        let imported = self.modules.resolve(ResolveRequest {
2540            library_name: spec,
2541            importer,
2542        })?;
2543        self.refresh_if_stale(&imported)?;
2544        self.load_library_types_from_resolved(imported, gas, loaded, loading)
2545    }
2546
2547    fn rewrite_program_with_imports(
2548        &mut self,
2549        program: &Program,
2550        importer: Option<LibraryId>,
2551        prefix: &str,
2552        gas: &mut GasMeter,
2553        loaded: &mut HashMap<LibraryId, LibraryExports>,
2554        loading: &mut HashSet<LibraryId>,
2555    ) -> Result<Program, EngineError> {
2556        let mut bindings = ImportBindings::default();
2557        let local_values = decl_value_names(&program.decls);
2558        for decl in &program.decls {
2559            let Decl::Import(import_decl) = decl else {
2560                continue;
2561            };
2562            let exports = self.resolve_library_exports_for_rewrite(
2563                import_decl,
2564                importer.clone(),
2565                gas,
2566                loaded,
2567                loading,
2568            )?;
2569            add_import_bindings(&mut bindings, import_decl, &exports, &local_values, None)?;
2570        }
2571
2572        let default_imports = self.default_imports().to_vec();
2573        for library_name in default_imports {
2574            let alias = intern(&library_name);
2575            if contains_import_alias(&program.decls, &alias) {
2576                continue;
2577            }
2578            let import_decl = default_import_decl(&library_name);
2579            let exports = self.resolve_library_exports_for_rewrite(
2580                &import_decl,
2581                importer.clone(),
2582                gas,
2583                loaded,
2584                loading,
2585            )?;
2586            for (local, target) in exports.values {
2587                if !local_values.contains(&local) && !bindings.imported_values.contains_key(&local)
2588                {
2589                    bindings.imported_values.insert(local, target);
2590                }
2591            }
2592        }
2593
2594        let qualified = qualify_program(program, prefix);
2595        validate_import_uses(&qualified, &bindings.alias_exports, None)?;
2596        Ok(rewrite_import_uses(
2597            &qualified,
2598            &bindings.alias_exports,
2599            &bindings.imported_values,
2600            None,
2601        ))
2602    }
2603
2604    fn read_local_library_bytes(&self, path: &Path) -> Result<(LibraryId, Vec<u8>), EngineError> {
2605        let canon = path
2606            .canonicalize()
2607            .map_err(|e| crate::LibraryError::InvalidLibraryPath {
2608                path: path.to_path_buf(),
2609                source: e,
2610            })?;
2611        let bytes = std::fs::read(&canon).map_err(|e| crate::LibraryError::ReadFailed {
2612            path: canon.clone(),
2613            source: e,
2614        })?;
2615        Ok((LibraryId::Local { path: canon }, bytes))
2616    }
2617
2618    fn decode_local_library_source(
2619        &self,
2620        id: &LibraryId,
2621        bytes: Vec<u8>,
2622    ) -> Result<String, EngineError> {
2623        let path = match id {
2624            LibraryId::Local { path, .. } => path.clone(),
2625            other => {
2626                return Err(EngineError::Internal(format!(
2627                    "decode_local_library_source called with non-local library id {other}"
2628                )));
2629            }
2630        };
2631        String::from_utf8(bytes).map_err(|e| {
2632            crate::LibraryError::NotUtf8 {
2633                kind: "local",
2634                path,
2635                source: e,
2636            }
2637            .into()
2638        })
2639    }
2640
2641    pub fn infer_library_file(
2642        &mut self,
2643        path: impl AsRef<Path>,
2644        gas: &mut GasMeter,
2645    ) -> Result<(Vec<Predicate>, Type), EngineError> {
2646        let (id, bytes) = self.read_local_library_bytes(path.as_ref())?;
2647        let source = self.decode_local_library_source(&id, bytes)?;
2648        self.infer_library_source(ResolvedLibrary { id, source }, gas)
2649    }
2650
2651    fn infer_library_source(
2652        &mut self,
2653        resolved: ResolvedLibrary,
2654        gas: &mut GasMeter,
2655    ) -> Result<(Vec<Predicate>, Type), EngineError> {
2656        let mut loaded: HashMap<LibraryId, LibraryExports> = HashMap::new();
2657        let mut loading: HashSet<LibraryId> = HashSet::new();
2658
2659        loading.insert(resolved.id.clone());
2660
2661        let prefix = prefix_for_library(&resolved.id);
2662        let program =
2663            parse_program_from_source(&resolved.source, Some(&resolved.id), Some(&mut *gas))?;
2664
2665        let rewritten = self.rewrite_program_with_imports(
2666            &program,
2667            Some(resolved.id.clone()),
2668            &prefix,
2669            gas,
2670            &mut loaded,
2671            &mut loading,
2672        )?;
2673        self.inject_decls(&rewritten.decls)?;
2674
2675        let (preds, ty) = self.infer_type(rewritten.expr.as_ref(), gas)?;
2676
2677        let exports = exports_from_program(&program, &prefix, &resolved.id);
2678        loaded.insert(resolved.id.clone(), exports);
2679        loading.remove(&resolved.id);
2680
2681        Ok((preds, ty))
2682    }
2683
2684    pub fn infer_snippet(
2685        &mut self,
2686        source: &str,
2687        gas: &mut GasMeter,
2688    ) -> Result<(Vec<Predicate>, Type), EngineError> {
2689        self.infer_snippet_with_gas_and_importer(source, gas, None)
2690    }
2691
2692    pub fn infer_snippet_at(
2693        &mut self,
2694        source: &str,
2695        importer_path: impl AsRef<Path>,
2696        gas: &mut GasMeter,
2697    ) -> Result<(Vec<Predicate>, Type), EngineError> {
2698        let path = importer_path.as_ref().to_path_buf();
2699        self.infer_snippet_with_gas_and_importer(source, gas, Some(path))
2700    }
2701
2702    fn infer_snippet_with_gas_and_importer(
2703        &mut self,
2704        source: &str,
2705        gas: &mut GasMeter,
2706        importer_path: Option<PathBuf>,
2707    ) -> Result<(Vec<Predicate>, Type), EngineError> {
2708        let program = parse_program_from_source(source, None, Some(&mut *gas))?;
2709
2710        let importer = importer_path.map(|p| LibraryId::Local { path: p });
2711
2712        let mut loaded: HashMap<LibraryId, LibraryExports> = HashMap::new();
2713        let mut loading: HashSet<LibraryId> = HashSet::new();
2714
2715        let prefix = format!("@snippet{}", Uuid::new_v4());
2716        let rewritten = self.rewrite_program_with_imports(
2717            &program,
2718            importer,
2719            &prefix,
2720            gas,
2721            &mut loaded,
2722            &mut loading,
2723        )?;
2724        self.inject_decls(&rewritten.decls)?;
2725        self.infer_type(rewritten.expr.as_ref(), gas)
2726    }
2727
2728    pub async fn eval_library_file(
2729        &mut self,
2730        path: impl AsRef<Path>,
2731        gas: &mut GasMeter,
2732    ) -> Result<(Pointer, Type), EngineError> {
2733        let (id, bytes) = self.read_local_library_bytes(path.as_ref())?;
2734        let source_fingerprint = sha256_hex(&bytes);
2735        if let Some(inst) = self.modules.cached(&id)? {
2736            if inst.source_fingerprint.as_deref() == Some(source_fingerprint.as_str()) {
2737                return Ok((inst.init_value, inst.init_type));
2738            }
2739            // Local library identity is path-based, so file edits must explicitly
2740            // invalidate path-keyed caches before reload.
2741            self.invalidate_library_caches(&id)?;
2742        }
2743        let source = self.decode_local_library_source(&id, bytes)?;
2744        let inst = self
2745            .load_library_from_resolved(ResolvedLibrary { id, source }, gas)
2746            .await?;
2747        Ok((inst.init_value, inst.init_type))
2748    }
2749
2750    pub async fn eval_library_source(
2751        &mut self,
2752        source: &str,
2753        gas: &mut GasMeter,
2754    ) -> Result<(Pointer, Type), EngineError> {
2755        let mut hasher = std::collections::hash_map::DefaultHasher::new();
2756        source.hash(&mut hasher);
2757        let id = LibraryId::Virtual(format!("<inline:{:016x}>", hasher.finish()));
2758        if let Some(inst) = self.modules.cached(&id)? {
2759            return Ok((inst.init_value, inst.init_type));
2760        }
2761        let inst = self
2762            .load_library_from_resolved(
2763                ResolvedLibrary {
2764                    id,
2765                    source: source.to_string(),
2766                },
2767                gas,
2768            )
2769            .await?;
2770        Ok((inst.init_value, inst.init_type))
2771    }
2772
2773    pub async fn eval_snippet(
2774        &mut self,
2775        source: &str,
2776        gas: &mut GasMeter,
2777    ) -> Result<(Pointer, Type), EngineError> {
2778        self.eval_snippet_with_gas_and_importer(source, gas, None)
2779            .await
2780    }
2781
2782    pub async fn eval_repl_program(
2783        &mut self,
2784        program: &Program,
2785        state: &mut ReplState,
2786        gas: &mut GasMeter,
2787    ) -> Result<(Pointer, Type), EngineError> {
2788        let importer = state
2789            .importer_path
2790            .as_ref()
2791            .map(|p| LibraryId::Local { path: p.clone() });
2792
2793        let mut local_values = state.defined_values.clone();
2794        local_values.extend(decl_value_names(&program.decls));
2795        let existing_imported: HashSet<Symbol> = state.imported_values.keys().cloned().collect();
2796        let import_bindings = self
2797            .import_bindings_for_decls(
2798                &program.decls,
2799                importer.clone(),
2800                &local_values,
2801                Some(&existing_imported),
2802                gas,
2803            )
2804            .await?;
2805        state.alias_exports.extend(import_bindings.alias_exports);
2806        state
2807            .imported_values
2808            .extend(import_bindings.imported_values);
2809
2810        let mut shadowed_values = state.defined_values.clone();
2811        shadowed_values.extend(decl_value_names(&program.decls));
2812
2813        validate_import_uses(program, &state.alias_exports, Some(&shadowed_values))?;
2814        let rewritten = rewrite_import_uses(
2815            program,
2816            &state.alias_exports,
2817            &state.imported_values,
2818            Some(&shadowed_values),
2819        );
2820
2821        self.inject_decls(&rewritten.decls)?;
2822        state
2823            .defined_values
2824            .extend(decl_value_names(&program.decls));
2825        self.eval(rewritten.expr.as_ref(), gas).await
2826    }
2827
2828    /// Evaluate a non-library snippet (with gas), but use `importer_path` for resolving local-relative imports.
2829    pub async fn eval_snippet_at(
2830        &mut self,
2831        source: &str,
2832        importer_path: impl AsRef<Path>,
2833        gas: &mut GasMeter,
2834    ) -> Result<(Pointer, Type), EngineError> {
2835        let path = importer_path.as_ref().to_path_buf();
2836        self.eval_snippet_with_gas_and_importer(source, gas, Some(path))
2837            .await
2838    }
2839
2840    async fn eval_snippet_with_gas_and_importer(
2841        &mut self,
2842        source: &str,
2843        gas: &mut GasMeter,
2844        importer_path: Option<PathBuf>,
2845    ) -> Result<(Pointer, Type), EngineError> {
2846        let program = parse_program_from_source(source, None, Some(&mut *gas))?;
2847
2848        let importer = importer_path.map(|p| LibraryId::Local { path: p });
2849
2850        let local_values = decl_value_names(&program.decls);
2851        let import_bindings = self
2852            .import_bindings_for_decls(&program.decls, importer.clone(), &local_values, None, gas)
2853            .await?;
2854
2855        let prefix = format!("@snippet{}", Uuid::new_v4());
2856        let qualified = qualify_program(&program, &prefix);
2857        validate_import_uses(&qualified, &import_bindings.alias_exports, None)?;
2858        let rewritten = rewrite_import_uses(
2859            &qualified,
2860            &import_bindings.alias_exports,
2861            &import_bindings.imported_values,
2862            None,
2863        );
2864
2865        self.inject_decls(&rewritten.decls)?;
2866        self.eval(rewritten.expr.as_ref(), gas).await
2867    }
2868}