sway_core/
ir_generation.rs

1pub(crate) mod compile;
2pub mod const_eval;
3mod convert;
4mod function;
5mod lexical_map;
6mod purity;
7pub mod storage;
8mod types;
9
10use std::{
11    collections::HashMap,
12    hash::{DefaultHasher, Hasher},
13};
14
15use sway_error::error::CompileError;
16use sway_features::ExperimentalFeatures;
17use sway_ir::{Context, Function, Kind, Module};
18use sway_types::{span::Span, Ident};
19
20pub(crate) use purity::{check_function_purity, PurityEnv};
21
22use crate::{
23    engine_threading::HashWithEngines,
24    language::ty,
25    metadata::MetadataManager,
26    types::{LogId, MessageId},
27    Engines, TypeId,
28};
29
30type FnKey = u64;
31
32/// Every compiled function needs to go through this cache for two reasons:
33/// 1 - to have its IR name unique;
34/// 2 - to avoid being compiled twice.
35#[derive(Default)]
36pub(crate) struct CompiledFunctionCache {
37    recreated_fns: HashMap<FnKey, Function>,
38}
39
40impl CompiledFunctionCache {
41    #[allow(clippy::too_many_arguments)]
42    fn ty_function_decl_to_unique_function(
43        &mut self,
44        engines: &Engines,
45        context: &mut Context,
46        module: Module,
47        md_mgr: &mut MetadataManager,
48        decl: &ty::TyFunctionDecl,
49        logged_types_map: &HashMap<TypeId, LogId>,
50        messages_types_map: &HashMap<TypeId, MessageId>,
51    ) -> Result<Function, CompileError> {
52        // The compiler inlines everything very lazily.  Function calls include the body of the
53        // callee (i.e., the callee_body arg above). Library functions are provided in an initial
54        // namespace from Forc and when the parser builds the AST (or is it during type checking?)
55        // these function bodies are embedded.
56        //
57        // Here we build little single-use instantiations of the callee and then call them.  Naming
58        // is not yet absolute so we must ensure the function names are unique.
59        //
60        // Eventually we need to Do It Properly and inline into the AST only when necessary, and
61        // compile the standard library to an actual module.
62        //
63        // Get the callee from the cache if we've already compiled it.  We can't insert it with
64        // .entry() since `compile_function()` returns a Result we need to handle.  The key to our
65        // cache, to uniquely identify a function instance, is the span and the type IDs of any
66        // args and type parameters.  It's using the Sway types rather than IR types, which would
67        // be more accurate but also more fiddly.
68
69        let mut hasher = DefaultHasher::default();
70        decl.hash(&mut hasher, engines);
71        let fn_key = hasher.finish();
72
73        let (fn_key, item) = (Some(fn_key), self.recreated_fns.get(&fn_key).copied());
74        let new_callee = match item {
75            Some(func) => func,
76            None => {
77                let callee_fn_decl = ty::TyFunctionDecl {
78                    type_parameters: Vec::new(),
79                    name: Ident::new(Span::from_string(format!(
80                        "{}_{}",
81                        decl.name,
82                        context.get_unique_id()
83                    ))),
84                    parameters: decl.parameters.clone(),
85                    ..decl.clone()
86                };
87                // Entry functions are already compiled at the top level
88                // when compiling scripts, predicates, contracts, and libraries.
89                let is_entry = false;
90                let is_original_entry = callee_fn_decl.is_main() || callee_fn_decl.is_test();
91                let new_func = compile::compile_function(
92                    engines,
93                    context,
94                    md_mgr,
95                    module,
96                    &callee_fn_decl,
97                    &decl.name,
98                    logged_types_map,
99                    messages_types_map,
100                    is_entry,
101                    is_original_entry,
102                    None,
103                    self,
104                )
105                .map_err(|mut x| x.pop().unwrap())?
106                .unwrap();
107
108                if let Some(fn_key) = fn_key {
109                    self.recreated_fns.insert(fn_key, new_func);
110                }
111
112                new_func
113            }
114        };
115
116        Ok(new_callee)
117    }
118}
119
120pub fn compile_program<'eng>(
121    program: &ty::TyProgram,
122    include_tests: bool,
123    engines: &'eng Engines,
124    experimental: ExperimentalFeatures,
125) -> Result<Context<'eng>, Vec<CompileError>> {
126    let declaration_engine = engines.de();
127
128    let test_fns = match include_tests {
129        true => program.test_fns(declaration_engine).collect(),
130        false => vec![],
131    };
132
133    let ty::TyProgram {
134        kind,
135        namespace,
136        logged_types,
137        messages_types,
138        declarations,
139        ..
140    } = program;
141
142    let logged_types = logged_types
143        .iter()
144        .map(|(log_id, type_id)| (*type_id, *log_id))
145        .collect();
146
147    let messages_types = messages_types
148        .iter()
149        .map(|(message_id, type_id)| (*type_id, *message_id))
150        .collect();
151
152    let mut ctx = Context::new(engines.se(), experimental);
153    ctx.program_kind = match kind {
154        ty::TyProgramKind::Script { .. } => Kind::Script,
155        ty::TyProgramKind::Predicate { .. } => Kind::Predicate,
156        ty::TyProgramKind::Contract { .. } => Kind::Contract,
157        ty::TyProgramKind::Library { .. } => Kind::Library,
158    };
159
160    let mut cache = CompiledFunctionCache::default();
161
162    match kind {
163        // Predicates and scripts have the same codegen, their only difference is static
164        // type-check time checks.
165        ty::TyProgramKind::Script { entry_function, .. } => compile::compile_script(
166            engines,
167            &mut ctx,
168            entry_function,
169            namespace.root_ref(),
170            &logged_types,
171            &messages_types,
172            &test_fns,
173            &mut cache,
174        ),
175        ty::TyProgramKind::Predicate { entry_function, .. } => compile::compile_predicate(
176            engines,
177            &mut ctx,
178            entry_function,
179            namespace.root_ref(),
180            &logged_types,
181            &messages_types,
182            &test_fns,
183            &mut cache,
184        ),
185        ty::TyProgramKind::Contract {
186            entry_function,
187            abi_entries,
188        } => compile::compile_contract(
189            &mut ctx,
190            entry_function.as_ref(),
191            abi_entries,
192            namespace.root_ref(),
193            declarations,
194            &logged_types,
195            &messages_types,
196            &test_fns,
197            engines,
198            &mut cache,
199        ),
200        ty::TyProgramKind::Library { .. } => compile::compile_library(
201            engines,
202            &mut ctx,
203            namespace.root_ref(),
204            &logged_types,
205            &messages_types,
206            &test_fns,
207            &mut cache,
208        ),
209    }?;
210
211    ctx.verify().map_err(|ir_error: sway_ir::IrError| {
212        vec![CompileError::InternalOwned(
213            ir_error.to_string(),
214            Span::dummy(),
215        )]
216    })
217}