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, SymbolKind,
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
601fn 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
1853fn 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
2103impl<State> Engine<State>
2104where
2105    State: Clone + Send + Sync + 'static,
2106{
2107    fn source_fingerprint(source: &str) -> String {
2108        sha256_hex(source.as_bytes())
2109    }
2110
2111    fn refresh_if_stale(&mut self, resolved: &ResolvedLibrary) -> Result<String, EngineError> {
2112        let next = Self::source_fingerprint(&resolved.source);
2113        if let Some(prev) = self.library_source_fingerprints.get(&resolved.id)
2114            && prev != &next
2115        {
2116            self.invalidate_library_caches(&resolved.id)?;
2117        }
2118        Ok(next)
2119    }
2120
2121    fn remove_type_level_symbols_for_library_interface(&mut self, decls: &[Decl]) {
2122        for decl in decls {
2123            match decl {
2124                Decl::Fn(fd) => {
2125                    self.type_system.env.remove(&fd.name.name);
2126                    self.type_system.declared_values.remove(&fd.name.name);
2127                }
2128                Decl::DeclareFn(df) => {
2129                    self.type_system.env.remove(&df.name.name);
2130                    self.type_system.declared_values.remove(&df.name.name);
2131                }
2132                Decl::Type(td) => {
2133                    self.type_system.adts.remove(&td.name);
2134                    for variant in &td.variants {
2135                        self.type_system.env.remove(&variant.name);
2136                        self.type_system.declared_values.remove(&variant.name);
2137                    }
2138                }
2139                Decl::Class(cd) => {
2140                    self.type_system.classes.classes.remove(&cd.name);
2141                    self.type_system.classes.instances.remove(&cd.name);
2142                    self.type_system.class_info.remove(&cd.name);
2143                    for method in &cd.methods {
2144                        self.type_system.env.remove(&method.name);
2145                        self.type_system.class_methods.remove(&method.name);
2146                    }
2147                }
2148                Decl::Import(..) | Decl::Instance(..) => {}
2149            }
2150        }
2151    }
2152
2153    pub(crate) fn invalidate_library_caches(&mut self, id: &LibraryId) -> Result<(), EngineError> {
2154        if let Some(prev_interface) = self.library_interface_cache.get(id).cloned() {
2155            self.remove_type_level_symbols_for_library_interface(&prev_interface);
2156        }
2157        self.modules.invalidate(id)?;
2158        self.library_exports_cache.remove(id);
2159        self.library_interface_cache.remove(id);
2160        self.library_sources.remove(id);
2161        self.library_source_fingerprints.remove(id);
2162        self.published_cycle_interfaces.remove(id);
2163        Ok(())
2164    }
2165
2166    fn load_library_types_via_scc(
2167        &mut self,
2168        root: ResolvedLibrary,
2169        gas: &mut GasMeter,
2170        loaded: &mut HashMap<LibraryId, LibraryExports>,
2171        loading: &mut HashSet<LibraryId>,
2172    ) -> Result<LibraryExports, EngineError> {
2173        #[derive(Clone)]
2174        struct PendingModule {
2175            resolved: ResolvedLibrary,
2176            program: Program,
2177            prefix: String,
2178        }
2179
2180        if let Some(exports) = loaded.get(&root.id)
2181            && !loading.contains(&root.id)
2182        {
2183            return Ok(exports.clone());
2184        }
2185
2186        let mut pending: HashMap<LibraryId, PendingModule> = HashMap::new();
2187        let mut edges: HashMap<LibraryId, Vec<LibraryId>> = HashMap::new();
2188        let mut stack = vec![root.clone()];
2189
2190        while let Some(resolved) = stack.pop() {
2191            let fingerprint = self.refresh_if_stale(&resolved)?;
2192            if pending.contains_key(&resolved.id) {
2193                continue;
2194            }
2195            if loaded.contains_key(&resolved.id) && !loading.contains(&resolved.id) {
2196                continue;
2197            }
2198
2199            let prefix = prefix_for_library(&resolved.id);
2200            let program =
2201                parse_program_from_source(&resolved.source, Some(&resolved.id), Some(&mut *gas))?;
2202            let exports = exports_from_program(&program, &prefix, &resolved.id);
2203            loaded.insert(resolved.id.clone(), exports);
2204            loading.insert(resolved.id.clone());
2205            self.library_sources
2206                .insert(resolved.id.clone(), resolved.source.clone());
2207            let qualified = qualify_program(&program, &prefix);
2208            let interfaces = interface_decls_from_program(&qualified);
2209            self.library_interface_cache
2210                .insert(resolved.id.clone(), interfaces);
2211
2212            let imports = graph_imports_for_program(&program, self.default_imports());
2213            for import_decl in imports {
2214                let spec = import_specifier(&import_decl.path);
2215                if self
2216                    .virtual_library_exports(spec_base_name(&spec))
2217                    .is_some()
2218                {
2219                    continue;
2220                }
2221                let imported = self.modules.resolve(ResolveRequest {
2222                    library_name: spec,
2223                    importer: Some(resolved.id.clone()),
2224                })?;
2225                edges
2226                    .entry(resolved.id.clone())
2227                    .or_default()
2228                    .push(imported.id.clone());
2229                if (loading.contains(&imported.id) || !loaded.contains_key(&imported.id))
2230                    && !pending.contains_key(&imported.id)
2231                {
2232                    stack.push(imported);
2233                }
2234            }
2235
2236            let library_id = resolved.id.clone();
2237            pending.insert(
2238                library_id.clone(),
2239                PendingModule {
2240                    resolved,
2241                    program,
2242                    prefix,
2243                },
2244            );
2245            self.library_source_fingerprints
2246                .insert(library_id, fingerprint);
2247        }
2248
2249        if pending.is_empty() {
2250            return loaded.get(&root.id).cloned().ok_or_else(|| {
2251                EngineError::Internal("missing library exports after SCC load".into())
2252            });
2253        }
2254
2255        let pending_ids: Vec<LibraryId> = pending.keys().cloned().collect();
2256        let sccs = tarjan_scc_library_ids(&pending_ids, &edges);
2257
2258        // Tarjan yields SCCs in reverse topological order of the SCC DAG, so
2259        // dependencies are processed before dependents.
2260        for component in sccs {
2261            let has_cycle = component.len() > 1;
2262            if has_cycle {
2263                for library_id in &component {
2264                    self.ensure_cycle_interfaces_published(library_id)?;
2265                }
2266            }
2267            for library_id in &component {
2268                let node = pending
2269                    .get(library_id)
2270                    .ok_or_else(|| EngineError::Internal("missing pending library node".into()))?;
2271                let rewritten = self.rewrite_program_with_imports(
2272                    &node.program,
2273                    Some(node.resolved.id.clone()),
2274                    &node.prefix,
2275                    gas,
2276                    loaded,
2277                    loading,
2278                )?;
2279                self.inject_decls(&rewritten.decls)?;
2280            }
2281            for library_id in component {
2282                loading.remove(&library_id);
2283            }
2284        }
2285
2286        loaded
2287            .get(&root.id)
2288            .cloned()
2289            .ok_or_else(|| EngineError::Internal("missing root exports after SCC load".into()))
2290    }
2291
2292    fn ensure_cycle_interfaces_published(
2293        &mut self,
2294        library_id: &LibraryId,
2295    ) -> Result<(), EngineError> {
2296        if self.published_cycle_interfaces.contains(library_id) {
2297            return Ok(());
2298        }
2299        let Some(decls) = self.library_interface_cache.get(library_id).cloned() else {
2300            return Ok(());
2301        };
2302        self.inject_decls(&decls)?;
2303        self.publish_runtime_interfaces(&decls)?;
2304        self.published_cycle_interfaces.insert(library_id.clone());
2305        Ok(())
2306    }
2307
2308    fn publish_runtime_interfaces(&mut self, decls: &[Decl]) -> Result<(), EngineError> {
2309        let mut signatures = Vec::new();
2310        for decl in decls {
2311            let Decl::DeclareFn(df) = decl else {
2312                continue;
2313            };
2314            signatures.push(df.clone());
2315        }
2316        self.publish_runtime_decl_interfaces(&signatures)
2317    }
2318
2319    #[async_recursion]
2320    async fn resolve_library_exports_from_import_decl_async(
2321        &mut self,
2322        import_decl: &ImportDecl,
2323        importer: Option<LibraryId>,
2324        gas: &mut GasMeter,
2325    ) -> Result<LibraryExports, EngineError> {
2326        let spec = import_specifier(&import_decl.path);
2327        if let Some(exports) = self.virtual_library_exports(spec_base_name(&spec)) {
2328            return Ok(exports);
2329        }
2330        let imported = self.modules.resolve(ResolveRequest {
2331            library_name: spec,
2332            importer,
2333        })?;
2334        self.refresh_if_stale(&imported)?;
2335        if let Some(exports) = self.library_exports_cache.get(&imported.id).cloned() {
2336            self.ensure_cycle_interfaces_published(&imported.id)?;
2337            return Ok(exports);
2338        }
2339        let inst = self.load_library_from_resolved(imported, gas).await?;
2340        Ok(inst.exports)
2341    }
2342
2343    async fn add_default_import_bindings(
2344        &mut self,
2345        bindings: &mut ImportBindings,
2346        decls: &[Decl],
2347        importer: Option<LibraryId>,
2348        forbidden_locals: &HashSet<Symbol>,
2349        existing_imported: Option<&HashSet<Symbol>>,
2350        gas: &mut GasMeter,
2351    ) -> Result<(), EngineError> {
2352        let existing_names: HashSet<Symbol> = existing_imported.cloned().unwrap_or_default();
2353        let default_imports = self.default_imports().to_vec();
2354        for library_name in default_imports {
2355            let alias = intern(&library_name);
2356            if contains_import_alias(decls, &alias) {
2357                continue;
2358            }
2359            let import_decl = default_import_decl(&library_name);
2360            let exports = self
2361                .resolve_library_exports_from_import_decl_async(&import_decl, importer.clone(), gas)
2362                .await?;
2363            for (local, target) in exports.values {
2364                if !forbidden_locals.contains(&local)
2365                    && !existing_names.contains(&local)
2366                    && !bindings.imported_values.contains_key(&local)
2367                {
2368                    bindings.imported_values.insert(local, target);
2369                }
2370            }
2371        }
2372        Ok(())
2373    }
2374
2375    pub fn add_resolver<F>(&mut self, name: impl Into<String>, f: F)
2376    where
2377        F: Fn(ResolveRequest) -> Result<Option<ResolvedLibrary>, EngineError>
2378            + Send
2379            + Sync
2380            + 'static,
2381    {
2382        self.modules.add_resolver(name, wrap_resolver(f));
2383    }
2384
2385    pub fn add_default_resolvers(&mut self) {
2386        self.modules
2387            .add_resolver("stdlib", default_stdlib_resolver());
2388
2389        #[cfg(not(target_arch = "wasm32"))]
2390        self.modules.add_resolver("local", default_local_resolver());
2391
2392        #[cfg(all(not(target_arch = "wasm32"), feature = "github-imports"))]
2393        {
2394            self.modules
2395                .add_resolver("remote", default_github_resolver());
2396        }
2397    }
2398
2399    #[cfg(not(target_arch = "wasm32"))]
2400    pub fn add_include_resolver(&mut self, root: impl AsRef<Path>) -> Result<(), EngineError> {
2401        let canon =
2402            root.as_ref()
2403                .canonicalize()
2404                .map_err(|e| crate::LibraryError::InvalidIncludeRoot {
2405                    path: root.as_ref().to_path_buf(),
2406                    source: e,
2407                })?;
2408        self.modules.add_resolver(
2409            format!("include:{}", canon.display()),
2410            include_resolver(canon),
2411        );
2412        Ok(())
2413    }
2414
2415    #[cfg(target_arch = "wasm32")]
2416    pub fn add_include_resolver(&mut self, _root: impl AsRef<Path>) -> Result<(), EngineError> {
2417        Err(EngineError::UnsupportedExpr)
2418    }
2419
2420    #[async_recursion]
2421    pub(crate) async fn import_bindings_for_decls(
2422        &mut self,
2423        decls: &[Decl],
2424        importer: Option<LibraryId>,
2425        forbidden_locals: &HashSet<Symbol>,
2426        existing_imported: Option<&HashSet<Symbol>>,
2427        gas: &mut GasMeter,
2428    ) -> Result<ImportBindings, EngineError> {
2429        let mut bindings = ImportBindings::default();
2430        for decl in decls {
2431            let Decl::Import(import_decl) = decl else {
2432                continue;
2433            };
2434            let exports = self
2435                .resolve_library_exports_from_import_decl_async(import_decl, importer.clone(), gas)
2436                .await?;
2437            add_import_bindings(
2438                &mut bindings,
2439                import_decl,
2440                &exports,
2441                forbidden_locals,
2442                existing_imported,
2443            )?;
2444        }
2445        self.add_default_import_bindings(
2446            &mut bindings,
2447            decls,
2448            importer,
2449            forbidden_locals,
2450            existing_imported,
2451            gas,
2452        )
2453        .await?;
2454        Ok(bindings)
2455    }
2456
2457    #[async_recursion]
2458    pub(crate) async fn load_library_from_resolved(
2459        &mut self,
2460        resolved: ResolvedLibrary,
2461        gas: &mut GasMeter,
2462    ) -> Result<LibraryInstance, EngineError> {
2463        let source_fingerprint = self.refresh_if_stale(&resolved)?;
2464        if let Some(inst) = self.modules.cached(&resolved.id)? {
2465            return Ok(inst);
2466        }
2467
2468        self.modules.mark_loading(&resolved.id)?;
2469
2470        let prefix = prefix_for_library(&resolved.id);
2471        let program = parse_program_from_source(&resolved.source, Some(&resolved.id), Some(gas))?;
2472        self.library_sources
2473            .insert(resolved.id.clone(), resolved.source.clone());
2474        let exports = exports_from_program(&program, &prefix, &resolved.id);
2475        self.library_exports_cache
2476            .insert(resolved.id.clone(), exports.clone());
2477        let qualified = qualify_program(&program, &prefix);
2478        let interfaces = interface_decls_from_program(&qualified);
2479        self.library_interface_cache
2480            .insert(resolved.id.clone(), interfaces);
2481
2482        // Resolve imports first so qualified names exist in the environment.
2483        let local_values = decl_value_names(&program.decls);
2484        let import_bindings = self
2485            .import_bindings_for_decls(
2486                &program.decls,
2487                Some(resolved.id.clone()),
2488                &local_values,
2489                None,
2490                gas,
2491            )
2492            .await?;
2493
2494        // Qualify local names, then rewrite `alias.foo` uses into internal symbols.
2495        validate_import_uses(&qualified, &import_bindings.alias_exports, None)?;
2496        let rewritten = rewrite_import_uses(
2497            &qualified,
2498            &import_bindings.alias_exports,
2499            &import_bindings.imported_values,
2500            None,
2501        );
2502
2503        self.inject_decls(&rewritten.decls)?;
2504        let init_value = self.heap.alloc_tuple(vec![])?;
2505        let init_type = Type::tuple(vec![]);
2506
2507        let inst = LibraryInstance {
2508            id: resolved.id.clone(),
2509            exports,
2510            init_value,
2511            init_type,
2512            source_fingerprint: Some(source_fingerprint.clone()),
2513        };
2514        self.modules.store_loaded(inst.clone())?;
2515        self.library_source_fingerprints
2516            .insert(resolved.id.clone(), source_fingerprint);
2517        Ok(inst)
2518    }
2519
2520    fn load_library_types_from_resolved(
2521        &mut self,
2522        resolved: ResolvedLibrary,
2523        gas: &mut GasMeter,
2524        loaded: &mut HashMap<LibraryId, LibraryExports>,
2525        loading: &mut HashSet<LibraryId>,
2526    ) -> Result<LibraryExports, EngineError> {
2527        if let Some(exports) = loaded.get(&resolved.id) {
2528            return Ok(exports.clone());
2529        }
2530
2531        if loading.contains(&resolved.id)
2532            && let Some(exports) = loaded.get(&resolved.id)
2533        {
2534            return Ok(exports.clone());
2535        }
2536        self.load_library_types_via_scc(resolved, gas, loaded, loading)
2537    }
2538
2539    fn resolve_library_exports_for_rewrite(
2540        &mut self,
2541        import_decl: &ImportDecl,
2542        importer: Option<LibraryId>,
2543        gas: &mut GasMeter,
2544        loaded: &mut HashMap<LibraryId, LibraryExports>,
2545        loading: &mut HashSet<LibraryId>,
2546    ) -> Result<LibraryExports, EngineError> {
2547        let spec = import_specifier(&import_decl.path);
2548        if let Some(exports) = self.virtual_library_exports(spec_base_name(&spec)) {
2549            return Ok(exports);
2550        }
2551        let imported = self.modules.resolve(ResolveRequest {
2552            library_name: spec,
2553            importer,
2554        })?;
2555        self.refresh_if_stale(&imported)?;
2556        self.load_library_types_from_resolved(imported, gas, loaded, loading)
2557    }
2558
2559    pub(crate) fn rewrite_program_with_imports(
2560        &mut self,
2561        program: &Program,
2562        importer: Option<LibraryId>,
2563        prefix: &str,
2564        gas: &mut GasMeter,
2565        loaded: &mut HashMap<LibraryId, LibraryExports>,
2566        loading: &mut HashSet<LibraryId>,
2567    ) -> Result<Program, EngineError> {
2568        let mut bindings = ImportBindings::default();
2569        let local_values = decl_value_names(&program.decls);
2570        for decl in &program.decls {
2571            let Decl::Import(import_decl) = decl else {
2572                continue;
2573            };
2574            let exports = self.resolve_library_exports_for_rewrite(
2575                import_decl,
2576                importer.clone(),
2577                gas,
2578                loaded,
2579                loading,
2580            )?;
2581            add_import_bindings(&mut bindings, import_decl, &exports, &local_values, None)?;
2582        }
2583
2584        let default_imports = self.default_imports().to_vec();
2585        for library_name in default_imports {
2586            let alias = intern(&library_name);
2587            if contains_import_alias(&program.decls, &alias) {
2588                continue;
2589            }
2590            let import_decl = default_import_decl(&library_name);
2591            let exports = self.resolve_library_exports_for_rewrite(
2592                &import_decl,
2593                importer.clone(),
2594                gas,
2595                loaded,
2596                loading,
2597            )?;
2598            for (local, target) in exports.values {
2599                if !local_values.contains(&local) && !bindings.imported_values.contains_key(&local)
2600                {
2601                    bindings.imported_values.insert(local, target);
2602                }
2603            }
2604        }
2605
2606        let qualified = qualify_program(program, prefix);
2607        validate_import_uses(&qualified, &bindings.alias_exports, None)?;
2608        Ok(rewrite_import_uses(
2609            &qualified,
2610            &bindings.alias_exports,
2611            &bindings.imported_values,
2612            None,
2613        ))
2614    }
2615
2616    pub(crate) fn read_local_library_bytes(
2617        &self,
2618        path: &Path,
2619    ) -> Result<(LibraryId, Vec<u8>), EngineError> {
2620        let canon = path
2621            .canonicalize()
2622            .map_err(|e| crate::LibraryError::InvalidLibraryPath {
2623                path: path.to_path_buf(),
2624                source: e,
2625            })?;
2626        let bytes = std::fs::read(&canon).map_err(|e| crate::LibraryError::ReadFailed {
2627            path: canon.clone(),
2628            source: e,
2629        })?;
2630        Ok((LibraryId::Local { path: canon }, bytes))
2631    }
2632
2633    pub(crate) fn decode_local_library_source(
2634        &self,
2635        id: &LibraryId,
2636        bytes: Vec<u8>,
2637    ) -> Result<String, EngineError> {
2638        let path = match id {
2639            LibraryId::Local { path, .. } => path.clone(),
2640            other => {
2641                return Err(EngineError::Internal(format!(
2642                    "decode_local_library_source called with non-local library id {other}"
2643                )));
2644            }
2645        };
2646        String::from_utf8(bytes).map_err(|e| {
2647            crate::LibraryError::NotUtf8 {
2648                kind: "local",
2649                path,
2650                source: e,
2651            }
2652            .into()
2653        })
2654    }
2655
2656    pub fn infer_library_file(
2657        &mut self,
2658        path: impl AsRef<Path>,
2659        gas: &mut GasMeter,
2660    ) -> Result<(Vec<Predicate>, Type), CompileError> {
2661        let (id, bytes) = self.read_local_library_bytes(path.as_ref())?;
2662        let source = self.decode_local_library_source(&id, bytes)?;
2663        self.infer_library_source(ResolvedLibrary { id, source }, gas)
2664            .map_err(CompileError::from)
2665    }
2666
2667    fn infer_library_source(
2668        &mut self,
2669        resolved: ResolvedLibrary,
2670        gas: &mut GasMeter,
2671    ) -> Result<(Vec<Predicate>, Type), EngineError> {
2672        let mut loaded: HashMap<LibraryId, LibraryExports> = HashMap::new();
2673        let mut loading: HashSet<LibraryId> = HashSet::new();
2674
2675        loading.insert(resolved.id.clone());
2676
2677        let prefix = prefix_for_library(&resolved.id);
2678        let program =
2679            parse_program_from_source(&resolved.source, Some(&resolved.id), Some(&mut *gas))?;
2680
2681        let rewritten = self.rewrite_program_with_imports(
2682            &program,
2683            Some(resolved.id.clone()),
2684            &prefix,
2685            gas,
2686            &mut loaded,
2687            &mut loading,
2688        )?;
2689        self.inject_decls(&rewritten.decls)?;
2690
2691        let (preds, ty) = self.infer_type(rewritten.expr.as_ref(), gas)?;
2692
2693        let exports = exports_from_program(&program, &prefix, &resolved.id);
2694        loaded.insert(resolved.id.clone(), exports);
2695        loading.remove(&resolved.id);
2696
2697        Ok((preds, ty))
2698    }
2699
2700    pub fn infer_snippet(
2701        &mut self,
2702        source: &str,
2703        gas: &mut GasMeter,
2704    ) -> Result<(Vec<Predicate>, Type), CompileError> {
2705        self.infer_snippet_with_gas_and_importer(source, gas, None)
2706            .map_err(CompileError::from)
2707    }
2708
2709    pub fn infer_snippet_at(
2710        &mut self,
2711        source: &str,
2712        importer_path: impl AsRef<Path>,
2713        gas: &mut GasMeter,
2714    ) -> Result<(Vec<Predicate>, Type), CompileError> {
2715        let path = importer_path.as_ref().to_path_buf();
2716        self.infer_snippet_with_gas_and_importer(source, gas, Some(path))
2717            .map_err(CompileError::from)
2718    }
2719
2720    fn infer_snippet_with_gas_and_importer(
2721        &mut self,
2722        source: &str,
2723        gas: &mut GasMeter,
2724        importer_path: Option<PathBuf>,
2725    ) -> Result<(Vec<Predicate>, Type), EngineError> {
2726        let program = parse_program_from_source(source, None, Some(&mut *gas))?;
2727
2728        let importer = importer_path.map(|p| LibraryId::Local { path: p });
2729
2730        let mut loaded: HashMap<LibraryId, LibraryExports> = HashMap::new();
2731        let mut loading: HashSet<LibraryId> = HashSet::new();
2732
2733        let prefix = format!("@snippet{}", Uuid::new_v4());
2734        let rewritten = self.rewrite_program_with_imports(
2735            &program,
2736            importer,
2737            &prefix,
2738            gas,
2739            &mut loaded,
2740            &mut loading,
2741        )?;
2742        self.inject_decls(&rewritten.decls)?;
2743        self.infer_type(rewritten.expr.as_ref(), gas)
2744    }
2745}