Skip to main content

rexlang_engine/modules/
mod.rs

1//! Module system: resolvers, loading, and import rewriting.
2
3use std::collections::{HashMap, HashSet};
4use std::hash::{Hash, Hasher};
5use std::path::{Path, PathBuf};
6use std::sync::Arc;
7
8use async_recursion::async_recursion;
9use rexlang_ast::expr::{
10    Decl, DeclareFnDecl, Expr, FnDecl, ImportClause, ImportDecl, ImportPath, InstanceDecl, NameRef,
11    Pattern, Program, Symbol, TypeConstraint, TypeDecl, TypeExpr, Var, intern,
12};
13use rexlang_lexer::Token;
14use rexlang_parser::Parser as RexParser;
15use rexlang_typesystem::{Predicate, Type};
16use rexlang_util::{GasMeter, sha256_hex};
17use uuid::Uuid;
18
19use crate::Engine;
20use crate::{EngineError, Pointer};
21
22#[cfg(not(target_arch = "wasm32"))]
23mod filesystem;
24mod resolvers;
25mod system;
26mod types;
27
28#[cfg(not(target_arch = "wasm32"))]
29pub use filesystem::{default_local_resolver, include_resolver};
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, ModuleExports, ModuleId, ModuleInstance, ModuleKey, ReplState, ResolveRequest,
37    ResolvedModule, SymbolKind,
38};
39
40pub(crate) use system::ModuleSystem;
41pub(crate) use types::module_key_for_module;
42
43use system::wrap_resolver;
44use types::{prefix_for_module, 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(module_name: &str) -> ImportDecl {
82    ImportDecl {
83        span: rexlang_lexer::span::Span::default(),
84        is_pub: false,
85        path: ImportPath::Local {
86            segments: vec![intern(module_name)],
87            sha: None,
88        },
89        alias: intern(module_name),
90        clause: Some(ImportClause::All),
91    }
92}
93
94#[derive(Default)]
95struct ImportBindings {
96    alias_exports: HashMap<Symbol, ModuleExports>,
97    imported_values: HashMap<Symbol, CanonicalSymbol>,
98}
99
100fn add_import_bindings(
101    out: &mut ImportBindings,
102    import: &ImportDecl,
103    exports: &ModuleExports,
104    forbidden_locals: &HashSet<Symbol>,
105    existing_imported: Option<&HashSet<Symbol>>,
106) -> Result<(), EngineError> {
107    let module_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::ModuleError::ImportNameConflictsWithLocal {
111                module: module_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::ModuleError::DuplicateImportedName { name: local_name }.into());
120        }
121        if out.imported_values.contains_key(&local_name) {
122            return Err(crate::ModuleError::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::ModuleError::MissingExport {
144                        module: 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, ModuleExports>,
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, ModuleExports>,
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, ModuleExports>,
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
1267fn rewrite_import_uses(
1268    program: &Program,
1269    aliases: &HashMap<Symbol, ModuleExports>,
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, ModuleExports>,
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::ModuleError::MissingExport {
1515                    module: 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, ModuleExports>,
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::ModuleError::MissingExport {
1646        module: 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, ModuleExports>,
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::ModuleError::MissingExport {
1673                    module: 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
1702fn validate_import_uses(
1703    program: &Program,
1704    aliases: &HashMap<Symbol, ModuleExports>,
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
1832fn 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 module_name in default_imports {
1884        let alias = intern(module_name);
1885        if contains_import_alias(&program.decls, &alias) {
1886            continue;
1887        }
1888        out.push(default_import_decl(module_name));
1889    }
1890    out
1891}
1892
1893fn tarjan_scc_module_ids(
1894    nodes: &[ModuleId],
1895    edges: &HashMap<ModuleId, Vec<ModuleId>>,
1896) -> Vec<Vec<ModuleId>> {
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 module cycles as units.
1906    // - We want one DFS pass with low overhead because this runs in module loading paths.
1907    #[derive(Default)]
1908    struct TarjanState {
1909        index: usize,
1910        index_of: HashMap<ModuleId, usize>,
1911        lowlink: HashMap<ModuleId, usize>,
1912        stack: Vec<ModuleId>,
1913        on_stack: HashSet<ModuleId>,
1914        components: Vec<Vec<ModuleId>>,
1915    }
1916
1917    fn strong_connect(
1918        v: &ModuleId,
1919        edges: &HashMap<ModuleId, Vec<ModuleId>>,
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
1973fn exports_from_program(program: &Program, prefix: &str, module_id: &ModuleId) -> ModuleExports {
1974    let (value_renames, type_renames, class_renames) = collect_local_renames(program, prefix);
1975    let module_key = module_key_for_module(module_id);
1976
1977    let mut values = HashMap::new();
1978    let mut types = HashMap::new();
1979    let mut classes = HashMap::new();
1980
1981    for decl in &program.decls {
1982        match decl {
1983            Decl::Fn(fd) if fd.is_pub => {
1984                if let Some(internal) = value_renames.get(&fd.name.name) {
1985                    values.insert(
1986                        fd.name.name.clone(),
1987                        CanonicalSymbol::from_symbol(
1988                            module_key,
1989                            SymbolKind::Value,
1990                            fd.name.name.clone(),
1991                            internal.clone(),
1992                        ),
1993                    );
1994                }
1995            }
1996            Decl::DeclareFn(df) if df.is_pub => {
1997                if let Some(internal) = value_renames.get(&df.name.name) {
1998                    values.insert(
1999                        df.name.name.clone(),
2000                        CanonicalSymbol::from_symbol(
2001                            module_key,
2002                            SymbolKind::Value,
2003                            df.name.name.clone(),
2004                            internal.clone(),
2005                        ),
2006                    );
2007                }
2008            }
2009            Decl::Type(td) if td.is_pub => {
2010                if let Some(internal) = type_renames.get(&td.name) {
2011                    types.insert(
2012                        td.name.clone(),
2013                        CanonicalSymbol::from_symbol(
2014                            module_key,
2015                            SymbolKind::Type,
2016                            td.name.clone(),
2017                            internal.clone(),
2018                        ),
2019                    );
2020                }
2021                for variant in &td.variants {
2022                    if let Some(internal) = value_renames.get(&variant.name) {
2023                        values.insert(
2024                            variant.name.clone(),
2025                            CanonicalSymbol::from_symbol(
2026                                module_key,
2027                                SymbolKind::Value,
2028                                variant.name.clone(),
2029                                internal.clone(),
2030                            ),
2031                        );
2032                    }
2033                }
2034            }
2035            Decl::Class(cd) if cd.is_pub => {
2036                if let Some(internal) = class_renames.get(&cd.name) {
2037                    classes.insert(
2038                        cd.name.clone(),
2039                        CanonicalSymbol::from_symbol(
2040                            module_key,
2041                            SymbolKind::Class,
2042                            cd.name.clone(),
2043                            internal.clone(),
2044                        ),
2045                    );
2046                }
2047            }
2048            Decl::Instance(..)
2049            | Decl::Import(..)
2050            | Decl::Fn(..)
2051            | Decl::DeclareFn(..)
2052            | Decl::Type(..)
2053            | Decl::Class(..) => {}
2054        }
2055    }
2056
2057    ModuleExports {
2058        values,
2059        types,
2060        classes,
2061    }
2062}
2063
2064fn parse_program_from_source(
2065    source: &str,
2066    context: Option<&ModuleId>,
2067    gas: Option<&mut GasMeter>,
2068) -> Result<Program, EngineError> {
2069    let tokens = Token::tokenize(source).map_err(|e| match context {
2070        Some(id) => EngineError::from(crate::ModuleError::LexInModule {
2071            module: id.clone(),
2072            source: e,
2073        }),
2074        None => EngineError::from(crate::ModuleError::Lex { source: e }),
2075    })?;
2076    let mut parser = RexParser::new(tokens);
2077    let program = match gas {
2078        Some(gas) => parser.parse_program(gas),
2079        None => parser.parse_program(&mut GasMeter::default()),
2080    }
2081    .map_err(|errs| match context {
2082        Some(id) => EngineError::from(crate::ModuleError::ParseInModule {
2083            module: id.clone(),
2084            errors: errs,
2085        }),
2086        None => EngineError::from(crate::ModuleError::Parse { errors: errs }),
2087    })?;
2088    if let Some(module) = context
2089        && !matches!(program.expr.as_ref(), Expr::Tuple(_, elems) if elems.is_empty())
2090    {
2091        return Err(crate::ModuleError::TopLevelExprInModule {
2092            module: module.clone(),
2093        }
2094        .into());
2095    }
2096    Ok(program)
2097}
2098
2099impl<State> Engine<State>
2100where
2101    State: Clone + Send + Sync + 'static,
2102{
2103    fn source_fingerprint(source: &str) -> String {
2104        sha256_hex(source.as_bytes())
2105    }
2106
2107    fn refresh_if_stale(&mut self, resolved: &ResolvedModule) -> Result<String, EngineError> {
2108        let next = Self::source_fingerprint(&resolved.source);
2109        if let Some(prev) = self.module_source_fingerprints.get(&resolved.id)
2110            && prev != &next
2111        {
2112            self.invalidate_module_caches(&resolved.id)?;
2113        }
2114        Ok(next)
2115    }
2116
2117    fn remove_type_level_symbols_for_module_interface(&mut self, decls: &[Decl]) {
2118        for decl in decls {
2119            match decl {
2120                Decl::Fn(fd) => {
2121                    self.type_system.env.remove(&fd.name.name);
2122                    self.type_system.declared_values.remove(&fd.name.name);
2123                }
2124                Decl::DeclareFn(df) => {
2125                    self.type_system.env.remove(&df.name.name);
2126                    self.type_system.declared_values.remove(&df.name.name);
2127                }
2128                Decl::Type(td) => {
2129                    self.type_system.adts.remove(&td.name);
2130                    for variant in &td.variants {
2131                        self.type_system.env.remove(&variant.name);
2132                        self.type_system.declared_values.remove(&variant.name);
2133                    }
2134                }
2135                Decl::Class(cd) => {
2136                    self.type_system.classes.classes.remove(&cd.name);
2137                    self.type_system.classes.instances.remove(&cd.name);
2138                    self.type_system.class_info.remove(&cd.name);
2139                    for method in &cd.methods {
2140                        self.type_system.env.remove(&method.name);
2141                        self.type_system.class_methods.remove(&method.name);
2142                    }
2143                }
2144                Decl::Import(..) | Decl::Instance(..) => {}
2145            }
2146        }
2147    }
2148
2149    fn invalidate_module_caches(&mut self, id: &ModuleId) -> Result<(), EngineError> {
2150        if let Some(prev_interface) = self.module_interface_cache.get(id).cloned() {
2151            self.remove_type_level_symbols_for_module_interface(&prev_interface);
2152        }
2153        self.modules.invalidate(id)?;
2154        self.module_exports_cache.remove(id);
2155        self.module_interface_cache.remove(id);
2156        self.module_sources.remove(id);
2157        self.module_source_fingerprints.remove(id);
2158        self.published_cycle_interfaces.remove(id);
2159        Ok(())
2160    }
2161
2162    fn load_module_types_via_scc(
2163        &mut self,
2164        root: ResolvedModule,
2165        gas: &mut GasMeter,
2166        loaded: &mut HashMap<ModuleId, ModuleExports>,
2167        loading: &mut HashSet<ModuleId>,
2168    ) -> Result<ModuleExports, EngineError> {
2169        #[derive(Clone)]
2170        struct PendingModule {
2171            resolved: ResolvedModule,
2172            program: Program,
2173            prefix: String,
2174        }
2175
2176        if let Some(exports) = loaded.get(&root.id)
2177            && !loading.contains(&root.id)
2178        {
2179            return Ok(exports.clone());
2180        }
2181
2182        let mut pending: HashMap<ModuleId, PendingModule> = HashMap::new();
2183        let mut edges: HashMap<ModuleId, Vec<ModuleId>> = HashMap::new();
2184        let mut stack = vec![root.clone()];
2185
2186        while let Some(resolved) = stack.pop() {
2187            let fingerprint = self.refresh_if_stale(&resolved)?;
2188            if pending.contains_key(&resolved.id) {
2189                continue;
2190            }
2191            if loaded.contains_key(&resolved.id) && !loading.contains(&resolved.id) {
2192                continue;
2193            }
2194
2195            let prefix = prefix_for_module(&resolved.id);
2196            let program =
2197                parse_program_from_source(&resolved.source, Some(&resolved.id), Some(&mut *gas))?;
2198            let exports = exports_from_program(&program, &prefix, &resolved.id);
2199            loaded.insert(resolved.id.clone(), exports);
2200            loading.insert(resolved.id.clone());
2201            self.module_sources
2202                .insert(resolved.id.clone(), resolved.source.clone());
2203
2204            let imports = graph_imports_for_program(&program, self.default_imports());
2205            for import_decl in imports {
2206                let spec = import_specifier(&import_decl.path);
2207                if self.virtual_module_exports(spec_base_name(&spec)).is_some() {
2208                    continue;
2209                }
2210                let imported = self.modules.resolve(ResolveRequest {
2211                    module_name: spec,
2212                    importer: Some(resolved.id.clone()),
2213                })?;
2214                edges
2215                    .entry(resolved.id.clone())
2216                    .or_default()
2217                    .push(imported.id.clone());
2218                if (loading.contains(&imported.id) || !loaded.contains_key(&imported.id))
2219                    && !pending.contains_key(&imported.id)
2220                {
2221                    stack.push(imported);
2222                }
2223            }
2224
2225            let module_id = resolved.id.clone();
2226            pending.insert(
2227                module_id.clone(),
2228                PendingModule {
2229                    resolved,
2230                    program,
2231                    prefix,
2232                },
2233            );
2234            self.module_source_fingerprints
2235                .insert(module_id, fingerprint);
2236        }
2237
2238        if pending.is_empty() {
2239            return loaded.get(&root.id).cloned().ok_or_else(|| {
2240                EngineError::Internal("missing module exports after SCC load".into())
2241            });
2242        }
2243
2244        let pending_ids: Vec<ModuleId> = pending.keys().cloned().collect();
2245        let sccs = tarjan_scc_module_ids(&pending_ids, &edges);
2246
2247        // Tarjan yields SCCs in reverse topological order of the SCC DAG, so
2248        // dependencies are processed before dependents.
2249        for component in sccs {
2250            for module_id in &component {
2251                let node = pending
2252                    .get(module_id)
2253                    .ok_or_else(|| EngineError::Internal("missing pending module node".into()))?;
2254                let rewritten = self.rewrite_program_with_imports(
2255                    &node.program,
2256                    Some(node.resolved.id.clone()),
2257                    &node.prefix,
2258                    gas,
2259                    loaded,
2260                    loading,
2261                )?;
2262                self.inject_decls(&rewritten.decls)?;
2263            }
2264            for module_id in component {
2265                loading.remove(&module_id);
2266            }
2267        }
2268
2269        loaded
2270            .get(&root.id)
2271            .cloned()
2272            .ok_or_else(|| EngineError::Internal("missing root exports after SCC load".into()))
2273    }
2274
2275    fn ensure_cycle_interfaces_published(
2276        &mut self,
2277        module_id: &ModuleId,
2278    ) -> Result<(), EngineError> {
2279        if self.published_cycle_interfaces.contains(module_id) {
2280            return Ok(());
2281        }
2282        let Some(decls) = self.module_interface_cache.get(module_id).cloned() else {
2283            return Ok(());
2284        };
2285        self.inject_decls(&decls)?;
2286        self.publish_runtime_interfaces(&decls)?;
2287        self.published_cycle_interfaces.insert(module_id.clone());
2288        Ok(())
2289    }
2290
2291    fn publish_runtime_interfaces(&mut self, decls: &[Decl]) -> Result<(), EngineError> {
2292        let mut signatures = Vec::new();
2293        for decl in decls {
2294            let Decl::DeclareFn(df) = decl else {
2295                continue;
2296            };
2297            signatures.push(df.clone());
2298        }
2299        self.publish_runtime_decl_interfaces(&signatures)
2300    }
2301
2302    #[async_recursion]
2303    async fn resolve_module_exports_from_import_decl_async(
2304        &mut self,
2305        import_decl: &ImportDecl,
2306        importer: Option<ModuleId>,
2307        gas: &mut GasMeter,
2308    ) -> Result<ModuleExports, EngineError> {
2309        let spec = import_specifier(&import_decl.path);
2310        if let Some(exports) = self.virtual_module_exports(spec_base_name(&spec)) {
2311            return Ok(exports);
2312        }
2313        let imported = self.modules.resolve(ResolveRequest {
2314            module_name: spec,
2315            importer,
2316        })?;
2317        self.refresh_if_stale(&imported)?;
2318        if let Some(exports) = self.module_exports_cache.get(&imported.id).cloned() {
2319            self.ensure_cycle_interfaces_published(&imported.id)?;
2320            return Ok(exports);
2321        }
2322        let inst = self.load_module_from_resolved(imported, gas).await?;
2323        Ok(inst.exports)
2324    }
2325
2326    async fn add_default_import_bindings(
2327        &mut self,
2328        bindings: &mut ImportBindings,
2329        decls: &[Decl],
2330        importer: Option<ModuleId>,
2331        forbidden_locals: &HashSet<Symbol>,
2332        existing_imported: Option<&HashSet<Symbol>>,
2333        gas: &mut GasMeter,
2334    ) -> Result<(), EngineError> {
2335        let existing_names: HashSet<Symbol> = existing_imported.cloned().unwrap_or_default();
2336        let default_imports = self.default_imports().to_vec();
2337        for module_name in default_imports {
2338            let alias = intern(&module_name);
2339            if contains_import_alias(decls, &alias) {
2340                continue;
2341            }
2342            let import_decl = default_import_decl(&module_name);
2343            let exports = self
2344                .resolve_module_exports_from_import_decl_async(&import_decl, importer.clone(), gas)
2345                .await?;
2346            for (local, target) in exports.values {
2347                if !forbidden_locals.contains(&local)
2348                    && !existing_names.contains(&local)
2349                    && !bindings.imported_values.contains_key(&local)
2350                {
2351                    bindings.imported_values.insert(local, target);
2352                }
2353            }
2354        }
2355        Ok(())
2356    }
2357
2358    pub fn add_resolver<F>(&mut self, name: impl Into<String>, f: F)
2359    where
2360        F: Fn(ResolveRequest) -> Result<Option<ResolvedModule>, EngineError>
2361            + Send
2362            + Sync
2363            + 'static,
2364    {
2365        self.modules.add_resolver(name, wrap_resolver(f));
2366    }
2367
2368    pub fn add_default_resolvers(&mut self) {
2369        self.modules
2370            .add_resolver("stdlib", default_stdlib_resolver());
2371
2372        #[cfg(not(target_arch = "wasm32"))]
2373        self.modules.add_resolver("local", default_local_resolver());
2374
2375        #[cfg(all(not(target_arch = "wasm32"), feature = "github-imports"))]
2376        {
2377            self.modules
2378                .add_resolver("remote", default_github_resolver());
2379        }
2380    }
2381
2382    #[cfg(not(target_arch = "wasm32"))]
2383    pub fn add_include_resolver(&mut self, root: impl AsRef<Path>) -> Result<(), EngineError> {
2384        let canon =
2385            root.as_ref()
2386                .canonicalize()
2387                .map_err(|e| crate::ModuleError::InvalidIncludeRoot {
2388                    path: root.as_ref().to_path_buf(),
2389                    source: e,
2390                })?;
2391        self.modules.add_resolver(
2392            format!("include:{}", canon.display()),
2393            include_resolver(canon),
2394        );
2395        Ok(())
2396    }
2397
2398    #[cfg(target_arch = "wasm32")]
2399    pub fn add_include_resolver(&mut self, _root: impl AsRef<Path>) -> Result<(), EngineError> {
2400        Err(EngineError::UnsupportedExpr)
2401    }
2402
2403    #[async_recursion]
2404    async fn import_bindings_for_decls(
2405        &mut self,
2406        decls: &[Decl],
2407        importer: Option<ModuleId>,
2408        forbidden_locals: &HashSet<Symbol>,
2409        existing_imported: Option<&HashSet<Symbol>>,
2410        gas: &mut GasMeter,
2411    ) -> Result<ImportBindings, EngineError> {
2412        let mut bindings = ImportBindings::default();
2413        for decl in decls {
2414            let Decl::Import(import_decl) = decl else {
2415                continue;
2416            };
2417            let exports = self
2418                .resolve_module_exports_from_import_decl_async(import_decl, importer.clone(), gas)
2419                .await?;
2420            add_import_bindings(
2421                &mut bindings,
2422                import_decl,
2423                &exports,
2424                forbidden_locals,
2425                existing_imported,
2426            )?;
2427        }
2428        self.add_default_import_bindings(
2429            &mut bindings,
2430            decls,
2431            importer,
2432            forbidden_locals,
2433            existing_imported,
2434            gas,
2435        )
2436        .await?;
2437        Ok(bindings)
2438    }
2439
2440    #[async_recursion]
2441    async fn load_module_from_resolved(
2442        &mut self,
2443        resolved: ResolvedModule,
2444        gas: &mut GasMeter,
2445    ) -> Result<ModuleInstance, EngineError> {
2446        let source_fingerprint = self.refresh_if_stale(&resolved)?;
2447        if let Some(inst) = self.modules.cached(&resolved.id)? {
2448            return Ok(inst);
2449        }
2450
2451        self.modules.mark_loading(&resolved.id)?;
2452
2453        let prefix = prefix_for_module(&resolved.id);
2454        let program = parse_program_from_source(&resolved.source, Some(&resolved.id), Some(gas))?;
2455        self.module_sources
2456            .insert(resolved.id.clone(), resolved.source.clone());
2457        let exports = exports_from_program(&program, &prefix, &resolved.id);
2458        self.module_exports_cache
2459            .insert(resolved.id.clone(), exports.clone());
2460        let qualified = qualify_program(&program, &prefix);
2461        let interfaces = interface_decls_from_program(&qualified);
2462        self.module_interface_cache
2463            .insert(resolved.id.clone(), interfaces);
2464
2465        // Resolve imports first so qualified names exist in the environment.
2466        let local_values = decl_value_names(&program.decls);
2467        let import_bindings = self
2468            .import_bindings_for_decls(
2469                &program.decls,
2470                Some(resolved.id.clone()),
2471                &local_values,
2472                None,
2473                gas,
2474            )
2475            .await?;
2476
2477        // Qualify local names, then rewrite `alias.foo` uses into internal symbols.
2478        validate_import_uses(&qualified, &import_bindings.alias_exports, None)?;
2479        let rewritten = rewrite_import_uses(
2480            &qualified,
2481            &import_bindings.alias_exports,
2482            &import_bindings.imported_values,
2483            None,
2484        );
2485
2486        self.inject_decls(&rewritten.decls)?;
2487        let init_value = self.heap.alloc_tuple(vec![])?;
2488        let init_type = Type::tuple(vec![]);
2489
2490        let inst = ModuleInstance {
2491            id: resolved.id.clone(),
2492            exports,
2493            init_value,
2494            init_type,
2495            source_fingerprint: Some(source_fingerprint.clone()),
2496        };
2497        self.modules.store_loaded(inst.clone())?;
2498        self.module_source_fingerprints
2499            .insert(resolved.id.clone(), source_fingerprint);
2500        Ok(inst)
2501    }
2502
2503    fn load_module_types_from_resolved(
2504        &mut self,
2505        resolved: ResolvedModule,
2506        gas: &mut GasMeter,
2507        loaded: &mut HashMap<ModuleId, ModuleExports>,
2508        loading: &mut HashSet<ModuleId>,
2509    ) -> Result<ModuleExports, EngineError> {
2510        if let Some(exports) = loaded.get(&resolved.id) {
2511            return Ok(exports.clone());
2512        }
2513
2514        if loading.contains(&resolved.id)
2515            && let Some(exports) = loaded.get(&resolved.id)
2516        {
2517            return Ok(exports.clone());
2518        }
2519        self.load_module_types_via_scc(resolved, gas, loaded, loading)
2520    }
2521
2522    fn resolve_module_exports_for_rewrite(
2523        &mut self,
2524        import_decl: &ImportDecl,
2525        importer: Option<ModuleId>,
2526        gas: &mut GasMeter,
2527        loaded: &mut HashMap<ModuleId, ModuleExports>,
2528        loading: &mut HashSet<ModuleId>,
2529    ) -> Result<ModuleExports, EngineError> {
2530        let spec = import_specifier(&import_decl.path);
2531        if let Some(exports) = self.virtual_module_exports(spec_base_name(&spec)) {
2532            return Ok(exports);
2533        }
2534        let imported = self.modules.resolve(ResolveRequest {
2535            module_name: spec,
2536            importer,
2537        })?;
2538        self.refresh_if_stale(&imported)?;
2539        self.load_module_types_from_resolved(imported, gas, loaded, loading)
2540    }
2541
2542    fn rewrite_program_with_imports(
2543        &mut self,
2544        program: &Program,
2545        importer: Option<ModuleId>,
2546        prefix: &str,
2547        gas: &mut GasMeter,
2548        loaded: &mut HashMap<ModuleId, ModuleExports>,
2549        loading: &mut HashSet<ModuleId>,
2550    ) -> Result<Program, EngineError> {
2551        let mut bindings = ImportBindings::default();
2552        let local_values = decl_value_names(&program.decls);
2553        for decl in &program.decls {
2554            let Decl::Import(import_decl) = decl else {
2555                continue;
2556            };
2557            let exports = self.resolve_module_exports_for_rewrite(
2558                import_decl,
2559                importer.clone(),
2560                gas,
2561                loaded,
2562                loading,
2563            )?;
2564            add_import_bindings(&mut bindings, import_decl, &exports, &local_values, None)?;
2565        }
2566
2567        let default_imports = self.default_imports().to_vec();
2568        for module_name in default_imports {
2569            let alias = intern(&module_name);
2570            if contains_import_alias(&program.decls, &alias) {
2571                continue;
2572            }
2573            let import_decl = default_import_decl(&module_name);
2574            let exports = self.resolve_module_exports_for_rewrite(
2575                &import_decl,
2576                importer.clone(),
2577                gas,
2578                loaded,
2579                loading,
2580            )?;
2581            for (local, target) in exports.values {
2582                if !local_values.contains(&local) && !bindings.imported_values.contains_key(&local)
2583                {
2584                    bindings.imported_values.insert(local, target);
2585                }
2586            }
2587        }
2588
2589        let qualified = qualify_program(program, prefix);
2590        validate_import_uses(&qualified, &bindings.alias_exports, None)?;
2591        Ok(rewrite_import_uses(
2592            &qualified,
2593            &bindings.alias_exports,
2594            &bindings.imported_values,
2595            None,
2596        ))
2597    }
2598
2599    fn read_local_module_bytes(&self, path: &Path) -> Result<(ModuleId, Vec<u8>), EngineError> {
2600        let canon = path
2601            .canonicalize()
2602            .map_err(|e| crate::ModuleError::InvalidModulePath {
2603                path: path.to_path_buf(),
2604                source: e,
2605            })?;
2606        let bytes = std::fs::read(&canon).map_err(|e| crate::ModuleError::ReadFailed {
2607            path: canon.clone(),
2608            source: e,
2609        })?;
2610        Ok((ModuleId::Local { path: canon }, bytes))
2611    }
2612
2613    fn decode_local_module_source(
2614        &self,
2615        id: &ModuleId,
2616        bytes: Vec<u8>,
2617    ) -> Result<String, EngineError> {
2618        let path = match id {
2619            ModuleId::Local { path, .. } => path.clone(),
2620            other => {
2621                return Err(EngineError::Internal(format!(
2622                    "decode_local_module_source called with non-local module id {other}"
2623                )));
2624            }
2625        };
2626        String::from_utf8(bytes).map_err(|e| {
2627            crate::ModuleError::NotUtf8 {
2628                kind: "local",
2629                path,
2630                source: e,
2631            }
2632            .into()
2633        })
2634    }
2635
2636    pub fn infer_module_file(
2637        &mut self,
2638        path: impl AsRef<Path>,
2639        gas: &mut GasMeter,
2640    ) -> Result<(Vec<Predicate>, Type), EngineError> {
2641        let (id, bytes) = self.read_local_module_bytes(path.as_ref())?;
2642        let source = self.decode_local_module_source(&id, bytes)?;
2643        self.infer_module_source(ResolvedModule { id, source }, gas)
2644    }
2645
2646    fn infer_module_source(
2647        &mut self,
2648        resolved: ResolvedModule,
2649        gas: &mut GasMeter,
2650    ) -> Result<(Vec<Predicate>, Type), EngineError> {
2651        let mut loaded: HashMap<ModuleId, ModuleExports> = HashMap::new();
2652        let mut loading: HashSet<ModuleId> = HashSet::new();
2653
2654        loading.insert(resolved.id.clone());
2655
2656        let prefix = prefix_for_module(&resolved.id);
2657        let program =
2658            parse_program_from_source(&resolved.source, Some(&resolved.id), Some(&mut *gas))?;
2659
2660        let rewritten = self.rewrite_program_with_imports(
2661            &program,
2662            Some(resolved.id.clone()),
2663            &prefix,
2664            gas,
2665            &mut loaded,
2666            &mut loading,
2667        )?;
2668        self.inject_decls(&rewritten.decls)?;
2669
2670        let (preds, ty) = self.infer_type(rewritten.expr.as_ref(), gas)?;
2671
2672        let exports = exports_from_program(&program, &prefix, &resolved.id);
2673        loaded.insert(resolved.id.clone(), exports);
2674        loading.remove(&resolved.id);
2675
2676        Ok((preds, ty))
2677    }
2678
2679    pub fn infer_snippet(
2680        &mut self,
2681        source: &str,
2682        gas: &mut GasMeter,
2683    ) -> Result<(Vec<Predicate>, Type), EngineError> {
2684        self.infer_snippet_with_gas_and_importer(source, gas, None)
2685    }
2686
2687    pub fn infer_snippet_at(
2688        &mut self,
2689        source: &str,
2690        importer_path: impl AsRef<Path>,
2691        gas: &mut GasMeter,
2692    ) -> Result<(Vec<Predicate>, Type), EngineError> {
2693        let path = importer_path.as_ref().to_path_buf();
2694        self.infer_snippet_with_gas_and_importer(source, gas, Some(path))
2695    }
2696
2697    fn infer_snippet_with_gas_and_importer(
2698        &mut self,
2699        source: &str,
2700        gas: &mut GasMeter,
2701        importer_path: Option<PathBuf>,
2702    ) -> Result<(Vec<Predicate>, Type), EngineError> {
2703        let program = parse_program_from_source(source, None, Some(&mut *gas))?;
2704
2705        let importer = importer_path.map(|p| ModuleId::Local { path: p });
2706
2707        let mut loaded: HashMap<ModuleId, ModuleExports> = HashMap::new();
2708        let mut loading: HashSet<ModuleId> = HashSet::new();
2709
2710        let prefix = format!("@snippet{}", Uuid::new_v4());
2711        let rewritten = self.rewrite_program_with_imports(
2712            &program,
2713            importer,
2714            &prefix,
2715            gas,
2716            &mut loaded,
2717            &mut loading,
2718        )?;
2719        self.inject_decls(&rewritten.decls)?;
2720        self.infer_type(rewritten.expr.as_ref(), gas)
2721    }
2722
2723    pub async fn eval_module_file(
2724        &mut self,
2725        path: impl AsRef<Path>,
2726        gas: &mut GasMeter,
2727    ) -> Result<(Pointer, Type), EngineError> {
2728        let (id, bytes) = self.read_local_module_bytes(path.as_ref())?;
2729        let source_fingerprint = sha256_hex(&bytes);
2730        if let Some(inst) = self.modules.cached(&id)? {
2731            if inst.source_fingerprint.as_deref() == Some(source_fingerprint.as_str()) {
2732                return Ok((inst.init_value, inst.init_type));
2733            }
2734            // Local module identity is path-based, so file edits must explicitly
2735            // invalidate path-keyed caches before reload.
2736            self.invalidate_module_caches(&id)?;
2737        }
2738        let source = self.decode_local_module_source(&id, bytes)?;
2739        let inst = self
2740            .load_module_from_resolved(ResolvedModule { id, source }, gas)
2741            .await?;
2742        Ok((inst.init_value, inst.init_type))
2743    }
2744
2745    pub async fn eval_module_source(
2746        &mut self,
2747        source: &str,
2748        gas: &mut GasMeter,
2749    ) -> Result<(Pointer, Type), EngineError> {
2750        let mut hasher = std::collections::hash_map::DefaultHasher::new();
2751        source.hash(&mut hasher);
2752        let id = ModuleId::Virtual(format!("<inline:{:016x}>", hasher.finish()));
2753        if let Some(inst) = self.modules.cached(&id)? {
2754            return Ok((inst.init_value, inst.init_type));
2755        }
2756        let inst = self
2757            .load_module_from_resolved(
2758                ResolvedModule {
2759                    id,
2760                    source: source.to_string(),
2761                },
2762                gas,
2763            )
2764            .await?;
2765        Ok((inst.init_value, inst.init_type))
2766    }
2767
2768    pub async fn eval_snippet(
2769        &mut self,
2770        source: &str,
2771        gas: &mut GasMeter,
2772    ) -> Result<(Pointer, Type), EngineError> {
2773        self.eval_snippet_with_gas_and_importer(source, gas, None)
2774            .await
2775    }
2776
2777    pub async fn eval_repl_program(
2778        &mut self,
2779        program: &Program,
2780        state: &mut ReplState,
2781        gas: &mut GasMeter,
2782    ) -> Result<(Pointer, Type), EngineError> {
2783        let importer = state
2784            .importer_path
2785            .as_ref()
2786            .map(|p| ModuleId::Local { path: p.clone() });
2787
2788        let mut local_values = state.defined_values.clone();
2789        local_values.extend(decl_value_names(&program.decls));
2790        let existing_imported: HashSet<Symbol> = state.imported_values.keys().cloned().collect();
2791        let import_bindings = self
2792            .import_bindings_for_decls(
2793                &program.decls,
2794                importer.clone(),
2795                &local_values,
2796                Some(&existing_imported),
2797                gas,
2798            )
2799            .await?;
2800        state.alias_exports.extend(import_bindings.alias_exports);
2801        state
2802            .imported_values
2803            .extend(import_bindings.imported_values);
2804
2805        let mut shadowed_values = state.defined_values.clone();
2806        shadowed_values.extend(decl_value_names(&program.decls));
2807
2808        validate_import_uses(program, &state.alias_exports, Some(&shadowed_values))?;
2809        let rewritten = rewrite_import_uses(
2810            program,
2811            &state.alias_exports,
2812            &state.imported_values,
2813            Some(&shadowed_values),
2814        );
2815
2816        self.inject_decls(&rewritten.decls)?;
2817        state
2818            .defined_values
2819            .extend(decl_value_names(&program.decls));
2820        self.eval(rewritten.expr.as_ref(), gas).await
2821    }
2822
2823    /// Evaluate a non-module snippet (with gas), but use `importer_path` for resolving local-relative imports.
2824    pub async fn eval_snippet_at(
2825        &mut self,
2826        source: &str,
2827        importer_path: impl AsRef<Path>,
2828        gas: &mut GasMeter,
2829    ) -> Result<(Pointer, Type), EngineError> {
2830        let path = importer_path.as_ref().to_path_buf();
2831        self.eval_snippet_with_gas_and_importer(source, gas, Some(path))
2832            .await
2833    }
2834
2835    async fn eval_snippet_with_gas_and_importer(
2836        &mut self,
2837        source: &str,
2838        gas: &mut GasMeter,
2839        importer_path: Option<PathBuf>,
2840    ) -> Result<(Pointer, Type), EngineError> {
2841        let program = parse_program_from_source(source, None, Some(&mut *gas))?;
2842
2843        let importer = importer_path.map(|p| ModuleId::Local { path: p });
2844
2845        let local_values = decl_value_names(&program.decls);
2846        let import_bindings = self
2847            .import_bindings_for_decls(&program.decls, importer.clone(), &local_values, None, gas)
2848            .await?;
2849
2850        let prefix = format!("@snippet{}", Uuid::new_v4());
2851        let qualified = qualify_program(&program, &prefix);
2852        validate_import_uses(&qualified, &import_bindings.alias_exports, None)?;
2853        let rewritten = rewrite_import_uses(
2854            &qualified,
2855            &import_bindings.alias_exports,
2856            &import_bindings.imported_values,
2857            None,
2858        );
2859
2860        self.inject_decls(&rewritten.decls)?;
2861        self.eval(rewritten.expr.as_ref(), gas).await
2862    }
2863}