sway_core/semantic_analysis/
module.rs

1use std::{
2    collections::{HashMap, HashSet},
3    fmt::Display,
4    fs,
5    sync::Arc,
6};
7
8use graph_cycles::Cycles;
9use sway_error::{
10    error::CompileError,
11    handler::{ErrorEmitted, Handler},
12};
13use sway_types::{BaseIdent, Named, SourceId};
14
15use crate::{
16    decl_engine::{DeclEngineGet, DeclId},
17    engine_threading::{DebugWithEngines, PartialEqWithEngines, PartialEqWithEnginesContext},
18    is_ty_module_cache_up_to_date,
19    language::{
20        parsed::*,
21        ty::{self, TyAstNodeContent, TyDecl},
22        CallPath, ModName,
23    },
24    query_engine::{ModuleCacheKey, TypedModuleInfo},
25    semantic_analysis::*,
26    BuildConfig, Engines, TypeInfo,
27};
28
29use super::{
30    declaration::auto_impl::{
31        abi_encoding::AbiEncodingAutoImplContext, marker_traits::MarkerTraitsAutoImplContext,
32    },
33    symbol_collection_context::SymbolCollectionContext,
34};
35
36#[derive(Clone, Debug)]
37pub struct ModuleDepGraphEdge();
38
39impl Display for ModuleDepGraphEdge {
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        write!(f, "")
42    }
43}
44
45pub type ModuleDepGraphNodeId = petgraph::graph::NodeIndex;
46
47#[derive(Clone, Debug)]
48pub enum ModuleDepGraphNode {
49    Module {},
50    Submodule { name: ModName },
51}
52
53impl DebugWithEngines for ModuleDepGraphNode {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, _engines: &Engines) -> std::fmt::Result {
55        let text = match self {
56            ModuleDepGraphNode::Module { .. } => {
57                format!("{:?}", "Root module")
58            }
59            ModuleDepGraphNode::Submodule { name: mod_name } => {
60                format!("{:?}", mod_name.as_str())
61            }
62        };
63        f.write_str(&text)
64    }
65}
66
67// Represents an ordered graph between declaration id indexes.
68pub type ModuleDepNodeGraph = petgraph::graph::DiGraph<ModuleDepGraphNode, ModuleDepGraphEdge>;
69
70pub struct ModuleDepGraph {
71    dep_graph: ModuleDepNodeGraph,
72    root: ModuleDepGraphNodeId,
73    node_name_map: HashMap<String, ModuleDepGraphNodeId>,
74}
75
76impl ModuleDepGraph {
77    pub(crate) fn new() -> Self {
78        Self {
79            dep_graph: Default::default(),
80            root: Default::default(),
81            node_name_map: Default::default(),
82        }
83    }
84
85    pub fn add_node(&mut self, node: ModuleDepGraphNode) -> ModuleDepGraphNodeId {
86        let node_id = self.dep_graph.add_node(node.clone());
87        match node {
88            ModuleDepGraphNode::Module {} => {}
89            ModuleDepGraphNode::Submodule { name: mod_name } => {
90                self.node_name_map.insert(mod_name.to_string(), node_id);
91            }
92        };
93        node_id
94    }
95
96    pub fn add_root_node(&mut self) -> ModuleDepGraphNodeId {
97        self.root = self.add_node(super::module::ModuleDepGraphNode::Module {});
98        self.root
99    }
100
101    fn get_node_id_for_module(
102        &self,
103        mod_name: &sway_types::BaseIdent,
104    ) -> Option<ModuleDepGraphNodeId> {
105        self.node_name_map.get(&mod_name.to_string()).copied()
106    }
107
108    /// Prints out GraphViz DOT format for the dependency graph.
109    #[allow(dead_code)]
110    pub(crate) fn visualize(&self, engines: &Engines, print_graph: Option<String>) {
111        if let Some(graph_path) = print_graph {
112            use petgraph::dot::{Config, Dot};
113            let string_graph = self.dep_graph.filter_map(
114                |_idx, node| Some(format!("{:?}", engines.help_out(node))),
115                |_idx, edge| Some(format!("{}", edge)),
116            );
117
118            let output = format!(
119                "{:?}",
120                Dot::with_attr_getters(
121                    &string_graph,
122                    &[Config::NodeNoLabel, Config::EdgeNoLabel],
123                    &|_, er| format!("label = {:?}", er.weight()),
124                    &|_, nr| {
125                        let _node = &self.dep_graph[nr.0];
126                        let shape = "";
127                        let url = "".to_string();
128                        format!("{shape} label = {:?} {url}", nr.1)
129                    },
130                )
131            );
132
133            if graph_path.is_empty() {
134                tracing::info!("{output}");
135            } else {
136                let result = fs::write(graph_path.clone(), output);
137                if let Some(error) = result.err() {
138                    tracing::error!(
139                        "There was an issue while outputting module dep analysis graph to path {graph_path:?}\n{error}"
140                    );
141                }
142            }
143        }
144    }
145
146    /// Computes the ordered list by dependency, which will be used for evaluating the modules
147    /// in the correct order. We run a topological sort and cycle finding algorithm to check
148    /// for unsupported cyclic dependency cases.
149    pub(crate) fn compute_order(
150        &self,
151        handler: &Handler,
152    ) -> Result<ModuleEvaluationOrder, ErrorEmitted> {
153        // Check for dependency cycles in the graph by running the Johnson's algorithm.
154        let cycles = self.dep_graph.cycles();
155        if !cycles.is_empty() {
156            let mut modules = Vec::new();
157            for cycle in cycles.first().unwrap() {
158                let node = self.dep_graph.node_weight(*cycle).unwrap();
159                match node {
160                    ModuleDepGraphNode::Module {} => unreachable!(),
161                    ModuleDepGraphNode::Submodule { name } => modules.push(name.clone()),
162                };
163            }
164            return Err(handler.emit_err(CompileError::ModuleDepGraphCyclicReference { modules }));
165        }
166
167        // Do a topological sort to compute an ordered list of nodes.
168        let sorted = match petgraph::algo::toposort(&self.dep_graph, None) {
169            Ok(value) => value,
170            // If we were not able to toposort, this means there is likely a cycle in the module dependency graph,
171            // which we already handled above, so lets just return an empty evaluation order instead of panic'ing.
172            // module dependencies, which we have already reported.
173            Err(_) => return Err(handler.emit_err(CompileError::ModuleDepGraphEvaluationError {})),
174        };
175
176        let sorted = sorted
177            .into_iter()
178            .filter_map(|node_index| {
179                let node = self.dep_graph.node_weight(node_index);
180                match node {
181                    Some(node) => match node {
182                        ModuleDepGraphNode::Module {} => None, // root module
183                        ModuleDepGraphNode::Submodule { name: mod_name } => Some(mod_name.clone()),
184                    },
185                    None => None,
186                }
187            })
188            .rev()
189            .collect::<Vec<_>>();
190
191        Ok(sorted)
192    }
193}
194
195impl ty::TyModule {
196    /// Analyzes the given parsed module to produce a dependency graph.
197    pub fn build_dep_graph(
198        handler: &Handler,
199        parsed: &ParseModule,
200    ) -> Result<ModuleDepGraph, ErrorEmitted> {
201        let mut dep_graph = ModuleDepGraph::new();
202        dep_graph.add_root_node();
203
204        let ParseModule { submodules, .. } = parsed;
205
206        // Create graph nodes for each submodule.
207        submodules.iter().for_each(|(name, _submodule)| {
208            let sub_mod_node =
209                dep_graph.add_node(ModuleDepGraphNode::Submodule { name: name.clone() });
210            dep_graph
211                .dep_graph
212                .add_edge(dep_graph.root, sub_mod_node, ModuleDepGraphEdge {});
213        });
214
215        // Analyze submodules first in order of declaration.
216        submodules.iter().for_each(|(name, submodule)| {
217            let _ =
218                ty::TySubmodule::build_dep_graph(handler, &mut dep_graph, name.clone(), submodule);
219        });
220
221        Ok(dep_graph)
222    }
223
224    /// Collects the given parsed module to produce a module symbol map.
225    ///
226    /// Recursively collects submodules first.
227    pub fn collect(
228        handler: &Handler,
229        engines: &Engines,
230        ctx: &mut SymbolCollectionContext,
231        parsed: &ParseModule,
232    ) -> Result<(), ErrorEmitted> {
233        let ParseModule {
234            submodules,
235            tree,
236            module_eval_order,
237            attributes: _,
238            span: _,
239            hash: _,
240            ..
241        } = parsed;
242
243        // Analyze submodules first in order of evaluation previously computed by the dependency graph.
244        module_eval_order.iter().for_each(|eval_mod_name| {
245            let (name, submodule) = submodules
246                .iter()
247                .find(|(submod_name, _submodule)| eval_mod_name == submod_name)
248                .unwrap();
249            let _ = ty::TySubmodule::collect(handler, engines, ctx, name.clone(), submodule);
250        });
251
252        let _ = tree
253            .root_nodes
254            .iter()
255            .map(|node| ty::TyAstNode::collect(handler, engines, ctx, node))
256            .filter_map(|res| res.ok())
257            .collect::<Vec<_>>();
258
259        Ok(())
260    }
261
262    /// Retrieves a cached typed module if it's up to date.
263    ///
264    /// This function checks the cache for a typed module corresponding to the given source ID.
265    /// If found and up to date, it returns the cached module. Otherwise, it returns None.
266    fn get_cached_ty_module_if_up_to_date(
267        source_id: Option<&SourceId>,
268        engines: &Engines,
269        build_config: Option<&BuildConfig>,
270    ) -> Option<Arc<ty::TyModule>> {
271        let source_id = source_id?;
272
273        // Create a cache key and get the module cache
274        let path = engines.se().get_path(source_id);
275        let include_tests = build_config.is_some_and(|x| x.include_tests);
276        let key = ModuleCacheKey::new(path.clone().into(), include_tests);
277        let cache = engines.qe().module_cache.read();
278        cache.get(&key).and_then(|entry| {
279            entry.typed.as_ref().and_then(|typed| {
280                // Check if the cached module is up to date
281                let is_up_to_date = is_ty_module_cache_up_to_date(
282                    engines,
283                    &path.into(),
284                    include_tests,
285                    build_config,
286                );
287
288                // Return the cached module if it's up to date, otherwise None
289                if is_up_to_date {
290                    Some(typed.module.clone())
291                } else {
292                    None
293                }
294            })
295        })
296    }
297
298    /// Type-check the given parsed module to produce a typed module.
299    ///
300    /// Recursively type-checks submodules first.
301    pub fn type_check(
302        handler: &Handler,
303        mut ctx: TypeCheckContext,
304        engines: &Engines,
305        kind: TreeType,
306        parsed: &ParseModule,
307        build_config: Option<&BuildConfig>,
308    ) -> Result<Arc<Self>, ErrorEmitted> {
309        let ParseModule {
310            submodules,
311            tree,
312            attributes,
313            span,
314            module_eval_order,
315            ..
316        } = parsed;
317
318        // Try to get the cached root module if it's up to date
319        if let Some(module) = ty::TyModule::get_cached_ty_module_if_up_to_date(
320            parsed.span.source_id(),
321            engines,
322            build_config,
323        ) {
324            return Ok(module);
325        }
326
327        // Type-check submodules first in order of evaluation previously computed by the dependency graph.
328        let submodules_res = module_eval_order
329            .iter()
330            .map(|eval_mod_name| {
331                let (name, submodule) = submodules
332                    .iter()
333                    .find(|(submod_name, _)| eval_mod_name == submod_name)
334                    .unwrap();
335
336                // Try to get the cached submodule
337                if let Some(cached_module) = ty::TyModule::get_cached_ty_module_if_up_to_date(
338                    submodule.module.span.source_id(),
339                    engines,
340                    build_config,
341                ) {
342                    // If cached, create TySubmodule from cached module
343                    Ok::<(BaseIdent, ty::TySubmodule), ErrorEmitted>((
344                        name.clone(),
345                        ty::TySubmodule {
346                            module: cached_module,
347                            mod_name_span: submodule.mod_name_span.clone(),
348                        },
349                    ))
350                } else {
351                    // If not cached, type-check the submodule
352                    let type_checked_submodule = ty::TySubmodule::type_check(
353                        handler,
354                        ctx.by_ref(),
355                        engines,
356                        name.clone(),
357                        kind,
358                        submodule,
359                        build_config,
360                    )?;
361                    Ok((name.clone(), type_checked_submodule))
362                }
363            })
364            .collect::<Result<Vec<_>, _>>();
365
366        // TODO: Ordering should be solved across all modules prior to the beginning of type-check.
367        let ordered_nodes = node_dependencies::order_ast_nodes_by_dependency(
368            handler,
369            ctx.engines(),
370            tree.root_nodes.clone(),
371        )?;
372
373        let mut all_nodes = Self::type_check_nodes(handler, ctx.by_ref(), &ordered_nodes)?;
374        let submodules = submodules_res?;
375
376        let fallback_fn = collect_fallback_fn(&all_nodes, engines, handler)?;
377        match (&kind, &fallback_fn) {
378            (TreeType::Contract, _) | (_, None) => {}
379            (_, Some(fallback_fn)) => {
380                let fallback_fn = engines.de().get(fallback_fn);
381                return Err(handler.emit_err(CompileError::FallbackFnsAreContractOnly {
382                    span: fallback_fn.span.clone(),
383                }));
384            }
385        }
386
387        if ctx.experimental.new_encoding {
388            let main_decl = all_nodes.iter_mut().find_map(|x| match &mut x.content {
389                ty::TyAstNodeContent::Declaration(ty::TyDecl::FunctionDecl(decl)) => {
390                    let fn_decl = engines.de().get_function(&decl.decl_id);
391                    (fn_decl.name.as_str() == "main").then_some(fn_decl)
392                }
393                _ => None,
394            });
395
396            match (&kind, main_decl.is_some()) {
397                (TreeType::Predicate, true) => {
398                    let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx);
399                    if let Ok(node) = fn_generator.generate_predicate_entry(
400                        engines,
401                        main_decl.as_ref().unwrap(),
402                        handler,
403                    ) {
404                        all_nodes.push(node)
405                    }
406                }
407                (TreeType::Script, true) => {
408                    let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx);
409                    if let Ok(node) = fn_generator.generate_script_entry(
410                        engines,
411                        main_decl.as_ref().unwrap(),
412                        handler,
413                    ) {
414                        all_nodes.push(node)
415                    }
416                }
417                (TreeType::Contract, _) => {
418                    // collect all supertrait methods
419                    let contract_supertrait_fns = submodules
420                        .iter()
421                        .flat_map(|x| x.1.module.submodules_recursive())
422                        .flat_map(|x| x.1.module.contract_supertrait_fns(engines))
423                        .chain(
424                            all_nodes
425                                .iter()
426                                .flat_map(|x| x.contract_supertrait_fns(engines)),
427                        )
428                        .collect::<Vec<_>>();
429
430                    // collect all contract methods
431                    let mut contract_fns = submodules
432                        .iter()
433                        .flat_map(|x| x.1.module.submodules_recursive())
434                        .flat_map(|x| x.1.module.contract_fns(engines))
435                        .chain(all_nodes.iter().flat_map(|x| x.contract_fns(engines)))
436                        .collect::<Vec<_>>();
437
438                    // exclude all contract methods that are supertrait methods
439                    let partialeq_ctx = PartialEqWithEnginesContext::new(engines);
440                    contract_fns.retain(|method| {
441                        contract_supertrait_fns
442                            .iter()
443                            .all(|si| !PartialEqWithEngines::eq(method, si, &partialeq_ctx))
444                    });
445
446                    let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx);
447                    if let Ok(node) = fn_generator.generate_contract_entry(
448                        engines,
449                        parsed.span.source_id().map(|x| x.program_id()),
450                        &contract_fns,
451                        fallback_fn,
452                        handler,
453                    ) {
454                        all_nodes.push(node)
455                    }
456                }
457                _ => {}
458            }
459        }
460
461        #[allow(clippy::arc_with_non_send_sync)]
462        let ty_module = Arc::new(Self {
463            span: span.clone(),
464            submodules,
465            all_nodes,
466            attributes: attributes.clone(),
467        });
468
469        // Cache the ty module
470        if let Some(source_id) = span.source_id() {
471            let path = engines.se().get_path(source_id);
472            let version = build_config
473                .and_then(|config| config.lsp_mode.as_ref())
474                .and_then(|lsp| lsp.file_versions.get(&path).copied())
475                .flatten();
476
477            let include_tests = build_config.is_some_and(|x| x.include_tests);
478            let key = ModuleCacheKey::new(path.clone().into(), include_tests);
479            engines.qe().update_typed_module_cache_entry(
480                &key,
481                TypedModuleInfo {
482                    module: ty_module.clone(),
483                    version,
484                },
485            );
486        }
487
488        Ok(ty_module)
489    }
490
491    // Filter and gather impl items
492    fn get_all_impls(
493        ctx: TypeCheckContext<'_>,
494        nodes: &[AstNode],
495        predicate: fn(&ImplSelfOrTrait) -> bool,
496    ) -> HashMap<BaseIdent, HashSet<CallPath>> {
497        let engines = ctx.engines();
498        let mut impls = HashMap::<BaseIdent, HashSet<CallPath>>::new();
499
500        for node in nodes.iter() {
501            if let AstNodeContent::Declaration(Declaration::ImplSelfOrTrait(decl_id)) =
502                &node.content
503            {
504                let decl = &*engines.pe().get_impl_self_or_trait(decl_id);
505                let implementing_for = ctx.engines.te().get(decl.implementing_for.type_id);
506                let implementing_for = match &*implementing_for {
507                    TypeInfo::Struct(decl_id) => {
508                        Some(ctx.engines().de().get(decl_id).name().clone())
509                    }
510                    TypeInfo::Enum(decl) => Some(ctx.engines().de().get(decl).name().clone()),
511                    TypeInfo::Custom {
512                        qualified_call_path,
513                        ..
514                    } => Some(qualified_call_path.call_path.suffix.clone()),
515                    _ => None,
516                };
517
518                if let Some(implementing_for) = implementing_for {
519                    if predicate(decl) {
520                        impls
521                            .entry(implementing_for)
522                            .or_default()
523                            .insert(decl.trait_name.clone());
524                    }
525                }
526            }
527        }
528
529        impls
530    }
531
532    fn type_check_nodes(
533        handler: &Handler,
534        mut ctx: TypeCheckContext,
535        nodes: &[AstNode],
536    ) -> Result<Vec<ty::TyAstNode>, ErrorEmitted> {
537        let engines = ctx.engines();
538
539        // Check which structs and enums needs to have auto impl for `AbiEncode` and `AbiDecode`.
540        // We need to do this before type checking, because the impls must be right after
541        // the declarations.
542        let all_abiencode_impls = Self::get_all_impls(ctx.by_ref(), nodes, |decl| {
543            decl.trait_name.suffix.as_str() == "AbiEncode"
544        });
545
546        let mut typed_nodes = vec![];
547        for node in nodes {
548            // Check if the encoding traits are explicitly implemented.
549            let auto_impl_encoding_traits = match &node.content {
550                AstNodeContent::Declaration(Declaration::StructDeclaration(decl_id)) => {
551                    let decl = ctx.engines().pe().get_struct(decl_id);
552                    !all_abiencode_impls.contains_key(&decl.name)
553                }
554                AstNodeContent::Declaration(Declaration::EnumDeclaration(decl_id)) => {
555                    let decl = ctx.engines().pe().get_enum(decl_id);
556                    !all_abiencode_impls.contains_key(&decl.name)
557                }
558                _ => false,
559            };
560
561            let Ok(node) = ty::TyAstNode::type_check(handler, ctx.by_ref(), node) else {
562                continue;
563            };
564
565            // Auto impl encoding traits only if they are not explicitly implemented.
566            let mut generated = vec![];
567            if ctx.experimental.new_encoding {
568                if let (true, mut ctx) = (
569                    auto_impl_encoding_traits,
570                    AbiEncodingAutoImplContext::new(&mut ctx),
571                ) {
572                    match &node.content {
573                        TyAstNodeContent::Declaration(decl @ TyDecl::StructDecl(_))
574                        | TyAstNodeContent::Declaration(decl @ TyDecl::EnumDecl(_)) => {
575                            let (a, b) = ctx.generate_abi_encode_and_decode_impls(engines, decl);
576                            generated.extend(a);
577                            generated.extend(b);
578                        }
579                        _ => {}
580                    }
581                };
582            }
583
584            // Always auto impl marker traits. If an explicit implementation exists, that will be
585            // reported as an error when type-checking trait impls.
586            if ctx.experimental.error_type {
587                let mut ctx = MarkerTraitsAutoImplContext::new(&mut ctx);
588                if let TyAstNodeContent::Declaration(decl @ TyDecl::EnumDecl(_)) = &node.content {
589                    let a = ctx.generate_enum_marker_trait_impl(engines, decl);
590                    generated.extend(a);
591                }
592            }
593
594            typed_nodes.push(node);
595            typed_nodes.extend(generated);
596        }
597
598        Ok(typed_nodes)
599    }
600}
601
602fn collect_fallback_fn(
603    all_nodes: &[ty::TyAstNode],
604    engines: &Engines,
605    handler: &Handler,
606) -> Result<Option<DeclId<ty::TyFunctionDecl>>, ErrorEmitted> {
607    let mut fallback_fns = all_nodes
608        .iter()
609        .filter_map(|x| match &x.content {
610            ty::TyAstNodeContent::Declaration(ty::TyDecl::FunctionDecl(decl)) => {
611                let d = engines.de().get(&decl.decl_id);
612                d.is_fallback().then_some(decl.decl_id)
613            }
614            _ => None,
615        })
616        .collect::<Vec<_>>();
617
618    let mut last_error = None;
619    for f in fallback_fns.iter().skip(1) {
620        let decl = engines.de().get(f);
621        last_error = Some(
622            handler.emit_err(CompileError::MultipleDefinitionsOfFallbackFunction {
623                name: decl.name.clone(),
624                span: decl.span.clone(),
625            }),
626        );
627    }
628
629    if let Some(last_error) = last_error {
630        return Err(last_error);
631    }
632
633    if let Some(fallback_fn) = fallback_fns.pop() {
634        let f = engines.de().get(&fallback_fn);
635        if !f.parameters.is_empty() {
636            Err(
637                handler.emit_err(CompileError::FallbackFnsCannotHaveParameters {
638                    span: f.span.clone(),
639                }),
640            )
641        } else {
642            Ok(Some(fallback_fn))
643        }
644    } else {
645        Ok(None)
646    }
647}
648
649impl ty::TySubmodule {
650    pub fn build_dep_graph(
651        _handler: &Handler,
652        module_dep_graph: &mut ModuleDepGraph,
653        mod_name: ModName,
654        submodule: &ParseSubmodule,
655    ) -> Result<(), ErrorEmitted> {
656        let ParseSubmodule { module, .. } = submodule;
657        let sub_mod_node = module_dep_graph.get_node_id_for_module(&mod_name).unwrap();
658        for node in module.tree.root_nodes.iter() {
659            match &node.content {
660                AstNodeContent::UseStatement(use_stmt) => {
661                    if let Some(use_mod_ident) = use_stmt.call_path.first() {
662                        if let Some(mod_name_node) =
663                            module_dep_graph.get_node_id_for_module(use_mod_ident)
664                        {
665                            // Prevent adding edge loops between the same node as that will throw off
666                            // the cyclic dependency analysis.
667                            if sub_mod_node != mod_name_node {
668                                module_dep_graph.dep_graph.add_edge(
669                                    sub_mod_node,
670                                    mod_name_node,
671                                    ModuleDepGraphEdge {},
672                                );
673                            }
674                        }
675                    }
676                }
677                AstNodeContent::Declaration(_) => {}
678                AstNodeContent::Expression(_) => {}
679                AstNodeContent::IncludeStatement(_) => {}
680                AstNodeContent::Error(_, _) => {}
681            }
682        }
683        Ok(())
684    }
685
686    pub fn collect(
687        handler: &Handler,
688        engines: &Engines,
689        parent_ctx: &mut SymbolCollectionContext,
690        mod_name: ModName,
691        submodule: &ParseSubmodule,
692    ) -> Result<(), ErrorEmitted> {
693        let ParseSubmodule {
694            module,
695            mod_name_span: _,
696            visibility,
697        } = submodule;
698        parent_ctx.enter_submodule(
699            handler,
700            engines,
701            mod_name,
702            *visibility,
703            module.span.clone(),
704            |submod_ctx| ty::TyModule::collect(handler, engines, submod_ctx, module),
705        )?
706    }
707
708    pub fn type_check(
709        handler: &Handler,
710        mut parent_ctx: TypeCheckContext,
711        engines: &Engines,
712        mod_name: ModName,
713        kind: TreeType,
714        submodule: &ParseSubmodule,
715        build_config: Option<&BuildConfig>,
716    ) -> Result<Self, ErrorEmitted> {
717        let ParseSubmodule {
718            module,
719            mod_name_span,
720            visibility,
721        } = submodule;
722        parent_ctx.enter_submodule(
723            handler,
724            mod_name,
725            *visibility,
726            module.span.clone(),
727            |submod_ctx| {
728                let module_res = ty::TyModule::type_check(
729                    handler,
730                    submod_ctx,
731                    engines,
732                    kind,
733                    module,
734                    build_config,
735                );
736                module_res.map(|module| ty::TySubmodule {
737                    module,
738                    mod_name_span: mod_name_span.clone(),
739                })
740            },
741        )?
742    }
743}