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::result_large_err)]
54    #[allow(clippy::too_many_arguments)]
55    pub fn type_check(
56        handler: &Handler,
57        engines: &Engines,
58        parsed: &ParseProgram,
59        collection_ctx: &mut SymbolCollectionContext,
60        mut namespace: namespace::Namespace,
61        package_name: &str,
62        build_config: Option<&BuildConfig>,
63        experimental: ExperimentalFeatures,
64    ) -> Result<Self, TypeCheckFailed> {
65        let mut ctx =
66            TypeCheckContext::from_root(&mut namespace, collection_ctx, engines, experimental)
67                .with_kind(parsed.kind);
68
69        let ParseProgram { root, kind } = parsed;
70
71        let root = ty::TyModule::type_check(
72            handler,
73            ctx.by_ref(),
74            engines,
75            parsed.kind,
76            root,
77            build_config,
78        )
79        .map_err(|error| TypeCheckFailed {
80            error,
81            root_module: None,
82            namespace: ctx.namespace.current_package_ref().clone(),
83        })?;
84
85        let experimental = ctx.experimental;
86        let (kind, declarations, configurables) =
87            Self::validate_root(handler, engines, &root, *kind, package_name, experimental)
88                .map_err(|error| TypeCheckFailed {
89                    error,
90                    root_module: Some(root.clone()),
91                    namespace: ctx.namespace.current_package_ref().clone(),
92                })?;
93
94        let mut namespace = ctx.namespace().clone();
95        Self::validate_coherence(handler, engines, &root, &mut namespace).map_err(|error| {
96            TypeCheckFailed {
97                error,
98                root_module: Some(root.clone()),
99                namespace: ctx.namespace.current_package_ref().clone(),
100            }
101        })?;
102
103        let program = TyProgram {
104            kind,
105            root_module: (*root).clone(),
106            namespace,
107            declarations,
108            configurables,
109            storage_slots: vec![],
110            logged_types: vec![],
111            messages_types: vec![],
112        };
113
114        Ok(program)
115    }
116
117    pub(crate) fn get_typed_program_with_initialized_storage_slots(
118        &mut self,
119        handler: &Handler,
120        engines: &Engines,
121        context: &mut Context,
122        md_mgr: &mut MetadataManager,
123        module: Module,
124    ) -> Result<(), ErrorEmitted> {
125        let decl_engine = engines.de();
126        match &self.kind {
127            ty::TyProgramKind::Contract { .. } => {
128                let storage_decl = self
129                    .declarations
130                    .iter()
131                    .find(|decl| matches!(decl, ty::TyDecl::StorageDecl { .. }));
132
133                // Expecting at most a single storage declaration
134                match storage_decl {
135                    Some(ty::TyDecl::StorageDecl(ty::StorageDecl { decl_id, .. })) => {
136                        let decl = decl_engine.get_storage(decl_id);
137                        let mut storage_slots = decl.get_initialized_storage_slots(
138                            handler, engines, context, md_mgr, module,
139                        )?;
140                        // Sort the slots to standardize the output. Not strictly required by the
141                        // spec.
142                        storage_slots.sort();
143                        self.storage_slots = storage_slots;
144                        Ok(())
145                    }
146                    _ => {
147                        self.storage_slots = vec![];
148                        Ok(())
149                    }
150                }
151            }
152            _ => {
153                self.storage_slots = vec![];
154                Ok(())
155            }
156        }
157    }
158}
159
160impl TypeCheckAnalysis for TyProgram {
161    fn type_check_analyze(
162        &self,
163        handler: &Handler,
164        ctx: &mut TypeCheckAnalysisContext,
165    ) -> Result<(), ErrorEmitted> {
166        for node in self.root_module.all_nodes.iter() {
167            node.type_check_analyze(handler, ctx)?;
168        }
169        Ok(())
170    }
171}
172
173impl TypeCheckFinalization for TyProgram {
174    fn type_check_finalize(
175        &mut self,
176        handler: &Handler,
177        ctx: &mut TypeCheckFinalizationContext,
178    ) -> Result<(), ErrorEmitted> {
179        handler.scope(|handler| {
180            for node in self.root_module.all_nodes.iter_mut() {
181                let _ = node.type_check_finalize(handler, ctx);
182            }
183            Ok(())
184        })
185    }
186}