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