sway_core/semantic_analysis/
program.rs

1use std::sync::Arc;
2
3use crate::{
4    language::{
5        parsed::ParseProgram,
6        ty::{self, TyModule, TyProgram},
7    },
8    metadata::MetadataManager,
9    semantic_analysis::{
10        namespace::{self, Package},
11        TypeCheckContext,
12    },
13    BuildConfig, Engines,
14};
15use sway_error::handler::{ErrorEmitted, Handler};
16use sway_features::ExperimentalFeatures;
17use sway_ir::{Context, Module};
18
19use super::{
20    symbol_collection_context::SymbolCollectionContext, TypeCheckAnalysis,
21    TypeCheckAnalysisContext, TypeCheckFinalization, TypeCheckFinalizationContext,
22};
23
24#[derive(Clone, Debug)]
25pub struct TypeCheckFailed {
26    pub root_module: Option<Arc<TyModule>>,
27    pub namespace: Package,
28    pub error: ErrorEmitted,
29}
30
31impl TyProgram {
32    /// Collects the given parsed program to produce a symbol maps.
33    ///
34    /// The given `initial_namespace` acts as an initial state for each module within this program.
35    /// It should contain a submodule for each library package dependency.
36    pub fn collect(
37        handler: &Handler,
38        engines: &Engines,
39        parsed: &ParseProgram,
40        namespace: namespace::Namespace,
41    ) -> Result<SymbolCollectionContext, ErrorEmitted> {
42        let mut ctx = SymbolCollectionContext::new(namespace);
43        let ParseProgram { root, kind: _ } = parsed;
44
45        ty::TyModule::collect(handler, engines, &mut ctx, root)?;
46        Ok(ctx)
47    }
48
49    /// Type-check the given parsed program to produce a typed program.
50    ///
51    /// The given `namespace` acts as an initial state for each module within this program.
52    /// It should contain a submodule for each library package dependency.
53    #[allow(clippy::too_many_arguments)]
54    pub fn type_check(
55        handler: &Handler,
56        engines: &Engines,
57        parsed: &ParseProgram,
58        collection_ctx: &mut SymbolCollectionContext,
59        mut namespace: namespace::Namespace,
60        package_name: &str,
61        build_config: Option<&BuildConfig>,
62        experimental: ExperimentalFeatures,
63    ) -> Result<Self, TypeCheckFailed> {
64        let mut ctx =
65            TypeCheckContext::from_root(&mut namespace, collection_ctx, engines, experimental)
66                .with_kind(parsed.kind);
67
68        let ParseProgram { root, kind } = parsed;
69
70        let root = ty::TyModule::type_check(
71            handler,
72            ctx.by_ref(),
73            engines,
74            parsed.kind,
75            root,
76            build_config,
77        )
78        .map_err(|error| TypeCheckFailed {
79            error,
80            root_module: None,
81            namespace: ctx.namespace.current_package_ref().clone(),
82        })?;
83
84        let experimental = ctx.experimental;
85        let (kind, declarations, configurables) =
86            Self::validate_root(handler, engines, &root, *kind, package_name, experimental)
87                .map_err(|error| TypeCheckFailed {
88                    error,
89                    root_module: Some(root.clone()),
90                    namespace: ctx.namespace.current_package_ref().clone(),
91                })?;
92
93        let mut namespace = ctx.namespace().clone();
94        // TODO: Currently this exposes an LSP engines re-use bug which causes a difference
95        // in behaviour when traversing the root namespace. Disable this for now under LSP.
96        let skip_coherence_checks = build_config
97            .is_some_and(|config| config.lsp_mode.as_ref().is_some_and(|_lsp_mode| true));
98        if !skip_coherence_checks {
99            Self::validate_coherence(handler, engines, &root, &mut namespace).map_err(|error| {
100                TypeCheckFailed {
101                    error,
102                    root_module: Some(root.clone()),
103                    namespace: ctx.namespace.current_package_ref().clone(),
104                }
105            })?;
106        }
107
108        let program = TyProgram {
109            kind,
110            root_module: (*root).clone(),
111            namespace,
112            declarations,
113            configurables,
114            storage_slots: vec![],
115            logged_types: vec![],
116            messages_types: vec![],
117        };
118
119        Ok(program)
120    }
121
122    pub(crate) fn get_typed_program_with_initialized_storage_slots(
123        &mut self,
124        handler: &Handler,
125        engines: &Engines,
126        context: &mut Context,
127        md_mgr: &mut MetadataManager,
128        module: Module,
129    ) -> Result<(), ErrorEmitted> {
130        let decl_engine = engines.de();
131        match &self.kind {
132            ty::TyProgramKind::Contract { .. } => {
133                let storage_decl = self
134                    .declarations
135                    .iter()
136                    .find(|decl| matches!(decl, ty::TyDecl::StorageDecl { .. }));
137
138                // Expecting at most a single storage declaration
139                match storage_decl {
140                    Some(ty::TyDecl::StorageDecl(ty::StorageDecl { decl_id, .. })) => {
141                        let decl = decl_engine.get_storage(decl_id);
142                        let mut storage_slots = decl.get_initialized_storage_slots(
143                            handler, engines, context, md_mgr, module,
144                        )?;
145                        // Sort the slots to standardize the output. Not strictly required by the
146                        // spec.
147                        storage_slots.sort();
148                        self.storage_slots = storage_slots;
149                        Ok(())
150                    }
151                    _ => {
152                        self.storage_slots = vec![];
153                        Ok(())
154                    }
155                }
156            }
157            _ => {
158                self.storage_slots = vec![];
159                Ok(())
160            }
161        }
162    }
163}
164
165impl TypeCheckAnalysis for TyProgram {
166    fn type_check_analyze(
167        &self,
168        handler: &Handler,
169        ctx: &mut TypeCheckAnalysisContext,
170    ) -> Result<(), ErrorEmitted> {
171        for node in self.root_module.all_nodes.iter() {
172            node.type_check_analyze(handler, ctx)?;
173        }
174        Ok(())
175    }
176}
177
178impl TypeCheckFinalization for TyProgram {
179    fn type_check_finalize(
180        &mut self,
181        handler: &Handler,
182        ctx: &mut TypeCheckFinalizationContext,
183    ) -> Result<(), ErrorEmitted> {
184        handler.scope(|handler| {
185            for node in self.root_module.all_nodes.iter_mut() {
186                let _ = node.type_check_finalize(handler, ctx);
187            }
188            Ok(())
189        })
190    }
191}