sway_core/
lib.rs

1#![recursion_limit = "256"]
2
3#[macro_use]
4pub mod error;
5
6#[macro_use]
7pub mod engine_threading;
8
9pub mod abi_generation;
10pub mod asm_generation;
11mod asm_lang;
12mod build_config;
13pub mod compiler_generated;
14mod concurrent_slab;
15mod control_flow_analysis;
16mod debug_generation;
17pub mod decl_engine;
18pub mod ir_generation;
19pub mod language;
20pub mod marker_traits;
21mod metadata;
22pub mod query_engine;
23pub mod semantic_analysis;
24pub mod source_map;
25pub mod transform;
26pub mod type_system;
27
28use crate::ir_generation::check_function_purity;
29use crate::query_engine::ModuleCacheEntry;
30use crate::source_map::SourceMap;
31pub use asm_generation::from_ir::compile_ir_context_to_finalized_asm;
32use asm_generation::FinalizedAsm;
33pub use asm_generation::{CompiledBytecode, FinalizedEntry};
34pub use build_config::{BuildConfig, BuildTarget, LspConfig, OptLevel, PrintAsm, PrintIr};
35use control_flow_analysis::ControlFlowGraph;
36pub use debug_generation::write_dwarf;
37use indexmap::IndexMap;
38use metadata::MetadataManager;
39use query_engine::{ModuleCacheKey, ModuleCommonInfo, ParsedModuleInfo, ProgramsCacheEntry};
40use semantic_analysis::program::TypeCheckFailed;
41use std::collections::hash_map::DefaultHasher;
42use std::hash::{Hash, Hasher};
43use std::path::{Path, PathBuf};
44use std::sync::atomic::{AtomicBool, Ordering};
45use std::sync::Arc;
46use sway_ast::AttributeDecl;
47use sway_error::handler::{ErrorEmitted, Handler};
48use sway_features::ExperimentalFeatures;
49use sway_ir::{
50    create_o1_pass_group, register_known_passes, Context, Kind, Module, PassGroup, PassManager,
51    PrintPassesOpts, ARG_DEMOTION_NAME, CONST_DEMOTION_NAME, DCE_NAME, FN_DEDUP_DEBUG_PROFILE_NAME,
52    FN_INLINE_NAME, GLOBALS_DCE_NAME, MEM2REG_NAME, MEMCPYOPT_NAME, MISC_DEMOTION_NAME,
53    RET_DEMOTION_NAME, SIMPLIFY_CFG_NAME, SROA_NAME,
54};
55use sway_types::constants::DOC_COMMENT_ATTRIBUTE_NAME;
56use sway_types::SourceEngine;
57use sway_utils::{time_expr, PerformanceData, PerformanceMetric};
58use transform::{Attribute, AttributeArg, AttributeKind, AttributesMap};
59use types::{CollectTypesMetadata, CollectTypesMetadataContext, TypeMetadata};
60
61pub use semantic_analysis::namespace::{self, Namespace};
62pub mod types;
63
64use sway_error::error::CompileError;
65use sway_types::{ident::Ident, span, Spanned};
66pub use type_system::*;
67
68pub use language::Programs;
69use language::{lexed, parsed, ty, Visibility};
70use transform::to_parsed_lang::{self, convert_module_kind};
71
72pub mod fuel_prelude {
73    pub use fuel_vm::{self, fuel_asm, fuel_crypto, fuel_tx, fuel_types};
74}
75
76pub use engine_threading::Engines;
77
78/// Given an input `Arc<str>` and an optional [BuildConfig], parse the input into a [lexed::LexedProgram] and [parsed::ParseProgram].
79///
80/// # Example
81/// ```ignore
82/// # use sway_core::parse;
83/// # fn main() {
84///     let input = "script; fn main() -> bool { true }";
85///     let result = parse(input.into(), <_>::default(), None);
86/// # }
87/// ```
88///
89/// # Panics
90/// Panics if the parser panics.
91pub fn parse(
92    input: Arc<str>,
93    handler: &Handler,
94    engines: &Engines,
95    config: Option<&BuildConfig>,
96    experimental: ExperimentalFeatures,
97) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> {
98    match config {
99        None => parse_in_memory(handler, engines, input, experimental),
100        // When a `BuildConfig` is given,
101        // the module source may declare `dep`s that must be parsed from other files.
102        Some(config) => parse_module_tree(
103            handler,
104            engines,
105            input,
106            config.canonical_root_module(),
107            None,
108            config.build_target,
109            config.include_tests,
110            experimental,
111            config.lsp_mode.as_ref(),
112        )
113        .map(
114            |ParsedModuleTree {
115                 tree_type: kind,
116                 lexed_module,
117                 parse_module,
118             }| {
119                let lexed = lexed::LexedProgram {
120                    kind,
121                    root: lexed_module,
122                };
123                let parsed = parsed::ParseProgram {
124                    kind,
125                    root: parse_module,
126                };
127                (lexed, parsed)
128            },
129        ),
130    }
131}
132
133/// Parses the tree kind in the input provided.
134///
135/// This will lex the entire input, but parses only the module kind.
136pub fn parse_tree_type(
137    handler: &Handler,
138    input: Arc<str>,
139) -> Result<parsed::TreeType, ErrorEmitted> {
140    sway_parse::parse_module_kind(handler, input, None).map(|kind| convert_module_kind(&kind))
141}
142
143/// Convert attributes from `Annotated<Module>` to an [AttributesMap].
144fn module_attrs_to_map(
145    handler: &Handler,
146    attribute_list: &[AttributeDecl],
147) -> Result<AttributesMap, ErrorEmitted> {
148    let mut attrs_map: IndexMap<_, Vec<Attribute>> = IndexMap::new();
149    for attr_decl in attribute_list {
150        let attrs = attr_decl.attribute.get().into_iter();
151        for attr in attrs {
152            let name = attr.name.as_str();
153            if name != DOC_COMMENT_ATTRIBUTE_NAME {
154                // prevent using anything except doc comment attributes
155                handler.emit_err(CompileError::ExpectedModuleDocComment {
156                    span: attr.name.span(),
157                });
158            }
159
160            let args = attr
161                .args
162                .as_ref()
163                .map(|parens| {
164                    parens
165                        .get()
166                        .into_iter()
167                        .cloned()
168                        .map(|arg| AttributeArg {
169                            name: arg.name.clone(),
170                            value: arg.value.clone(),
171                            span: arg.span(),
172                        })
173                        .collect()
174                })
175                .unwrap_or_else(Vec::new);
176
177            let attribute = Attribute {
178                name: attr.name.clone(),
179                args,
180                span: attr_decl.span(),
181            };
182
183            if let Some(attr_kind) = match name {
184                DOC_COMMENT_ATTRIBUTE_NAME => Some(AttributeKind::DocComment),
185                _ => None,
186            } {
187                attrs_map.entry(attr_kind).or_default().push(attribute);
188            }
189        }
190    }
191    Ok(AttributesMap::new(Arc::new(attrs_map)))
192}
193
194/// When no `BuildConfig` is given, we're assumed to be parsing in-memory with no submodules.
195fn parse_in_memory(
196    handler: &Handler,
197    engines: &Engines,
198    src: Arc<str>,
199    experimental: ExperimentalFeatures,
200) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> {
201    let mut hasher = DefaultHasher::new();
202    src.hash(&mut hasher);
203    let hash = hasher.finish();
204    let module = sway_parse::parse_file(handler, src, None)?;
205
206    let (kind, tree) = to_parsed_lang::convert_parse_tree(
207        &mut to_parsed_lang::Context::new(BuildTarget::EVM, experimental),
208        handler,
209        engines,
210        module.value.clone(),
211    )?;
212    let module_kind_span = module.value.kind.span();
213    let submodules = Vec::default();
214    let attributes = module_attrs_to_map(handler, &module.attribute_list)?;
215    let root = parsed::ParseModule {
216        span: span::Span::dummy(),
217        module_kind_span,
218        module_eval_order: vec![],
219        tree,
220        submodules,
221        attributes,
222        hash,
223    };
224    let lexed_program = lexed::LexedProgram::new(
225        kind,
226        lexed::LexedModule {
227            tree: module,
228            submodules: Vec::default(),
229        },
230    );
231
232    Ok((lexed_program, parsed::ParseProgram { kind, root }))
233}
234
235pub struct Submodule {
236    name: Ident,
237    path: Arc<PathBuf>,
238    lexed: lexed::LexedSubmodule,
239    parsed: parsed::ParseSubmodule,
240}
241
242/// Contains the lexed and parsed submodules 'deps' of a module.
243pub type Submodules = Vec<Submodule>;
244
245/// Parse all dependencies `deps` as submodules.
246#[allow(clippy::too_many_arguments)]
247fn parse_submodules(
248    handler: &Handler,
249    engines: &Engines,
250    module_name: Option<&str>,
251    module: &sway_ast::Module,
252    module_dir: &Path,
253    build_target: BuildTarget,
254    include_tests: bool,
255    experimental: ExperimentalFeatures,
256    lsp_mode: Option<&LspConfig>,
257) -> Submodules {
258    // Assume the happy path, so there'll be as many submodules as dependencies, but no more.
259    let mut submods = Vec::with_capacity(module.submodules().count());
260    module.submodules().for_each(|submod| {
261        // Read the source code from the dependency.
262        // If we cannot, record as an error, but continue with other files.
263        let submod_path = Arc::new(module_path(module_dir, module_name, submod));
264        let submod_str: Arc<str> = match std::fs::read_to_string(&*submod_path) {
265            Ok(s) => Arc::from(s),
266            Err(e) => {
267                handler.emit_err(CompileError::FileCouldNotBeRead {
268                    span: submod.name.span(),
269                    file_path: submod_path.to_string_lossy().to_string(),
270                    stringified_error: e.to_string(),
271                });
272                return;
273            }
274        };
275        if let Ok(ParsedModuleTree {
276            tree_type: kind,
277            lexed_module,
278            parse_module,
279        }) = parse_module_tree(
280            handler,
281            engines,
282            submod_str.clone(),
283            submod_path.clone(),
284            Some(submod.name.as_str()),
285            build_target,
286            include_tests,
287            experimental,
288            lsp_mode,
289        ) {
290            if !matches!(kind, parsed::TreeType::Library) {
291                let source_id = engines.se().get_source_id(submod_path.as_ref());
292                let span = span::Span::new(submod_str, 0, 0, Some(source_id)).unwrap();
293                handler.emit_err(CompileError::ImportMustBeLibrary { span });
294                return;
295            }
296
297            let parse_submodule = parsed::ParseSubmodule {
298                module: parse_module,
299                visibility: match submod.visibility {
300                    Some(..) => Visibility::Public,
301                    None => Visibility::Private,
302                },
303                mod_name_span: submod.name.span(),
304            };
305            let lexed_submodule = lexed::LexedSubmodule {
306                module: lexed_module,
307            };
308            let submodule = Submodule {
309                name: submod.name.clone(),
310                path: submod_path,
311                lexed: lexed_submodule,
312                parsed: parse_submodule,
313            };
314            submods.push(submodule);
315        }
316    });
317    submods
318}
319
320pub type SourceHash = u64;
321
322#[derive(Clone, Debug)]
323pub struct ParsedModuleTree {
324    pub tree_type: parsed::TreeType,
325    pub lexed_module: lexed::LexedModule,
326    pub parse_module: parsed::ParseModule,
327}
328
329/// Given the source of the module along with its path,
330/// parse this module including all of its submodules.
331#[allow(clippy::too_many_arguments)]
332fn parse_module_tree(
333    handler: &Handler,
334    engines: &Engines,
335    src: Arc<str>,
336    path: Arc<PathBuf>,
337    module_name: Option<&str>,
338    build_target: BuildTarget,
339    include_tests: bool,
340    experimental: ExperimentalFeatures,
341    lsp_mode: Option<&LspConfig>,
342) -> Result<ParsedModuleTree, ErrorEmitted> {
343    let query_engine = engines.qe();
344
345    // Parse this module first.
346    let module_dir = path.parent().expect("module file has no parent directory");
347    let source_id = engines.se().get_source_id(&path.clone());
348    let module = sway_parse::parse_file(handler, src.clone(), Some(source_id))?;
349
350    // Parse all submodules before converting to the `ParseTree`.
351    // This always recovers on parse errors for the file itself by skipping that file.
352    let submodules = parse_submodules(
353        handler,
354        engines,
355        module_name,
356        &module.value,
357        module_dir,
358        build_target,
359        include_tests,
360        experimental,
361        lsp_mode,
362    );
363
364    // Convert from the raw parsed module to the `ParseTree` ready for type-check.
365    let (kind, tree) = to_parsed_lang::convert_parse_tree(
366        &mut to_parsed_lang::Context::new(build_target, experimental),
367        handler,
368        engines,
369        module.value.clone(),
370    )?;
371    let module_kind_span = module.value.kind.span();
372    let attributes = module_attrs_to_map(handler, &module.attribute_list)?;
373
374    let lexed_submodules = submodules
375        .iter()
376        .map(|s| (s.name.clone(), s.lexed.clone()))
377        .collect::<Vec<_>>();
378    let lexed = lexed::LexedModule {
379        tree: module,
380        submodules: lexed_submodules,
381    };
382
383    let mut hasher = DefaultHasher::new();
384    src.hash(&mut hasher);
385    let hash = hasher.finish();
386
387    let parsed_submodules = submodules
388        .iter()
389        .map(|s| (s.name.clone(), s.parsed.clone()))
390        .collect::<Vec<_>>();
391    let parsed = parsed::ParseModule {
392        span: span::Span::new(src, 0, 0, Some(source_id)).unwrap(),
393        module_kind_span,
394        module_eval_order: vec![],
395        tree,
396        submodules: parsed_submodules,
397        attributes,
398        hash,
399    };
400
401    // Let's prime the cache with the module dependency and hash data.
402    let modified_time = std::fs::metadata(path.as_path())
403        .ok()
404        .and_then(|m| m.modified().ok());
405    let dependencies = submodules.into_iter().map(|s| s.path).collect::<Vec<_>>();
406    let version = lsp_mode
407        .and_then(|lsp| lsp.file_versions.get(path.as_ref()).copied())
408        .unwrap_or(None);
409
410    let common_info = ModuleCommonInfo {
411        path: path.clone(),
412        include_tests,
413        dependencies,
414        hash,
415    };
416    let parsed_info = ParsedModuleInfo {
417        modified_time,
418        version,
419    };
420    let cache_entry = ModuleCacheEntry::new(common_info, parsed_info);
421    query_engine.update_or_insert_parsed_module_cache_entry(cache_entry);
422
423    Ok(ParsedModuleTree {
424        tree_type: kind,
425        lexed_module: lexed,
426        parse_module: parsed,
427    })
428}
429
430/// Checks if the typed module cache for a given path is up to date.
431///
432/// This function determines whether the cached typed representation of a module
433/// is still valid based on file versions and dependencies.
434///
435/// Note: This functionality is currently only supported when the compiler is
436/// initiated from the language server.
437pub(crate) fn is_ty_module_cache_up_to_date(
438    engines: &Engines,
439    path: &Arc<PathBuf>,
440    include_tests: bool,
441    build_config: Option<&BuildConfig>,
442) -> bool {
443    let cache = engines.qe().module_cache.read();
444    let key = ModuleCacheKey::new(path.clone(), include_tests);
445    cache.get(&key).is_some_and(|entry| {
446        entry.typed.as_ref().is_some_and(|typed| {
447            // Check if the cache is up to date based on file versions
448            let cache_up_to_date = build_config
449                .and_then(|x| x.lsp_mode.as_ref())
450                .and_then(|lsp| lsp.file_versions.get(path.as_ref()))
451                .is_none_or(|version| {
452                    version.map_or(true, |v| typed.version.is_some_and(|tv| v <= tv))
453                });
454
455            // If the cache is up to date, recursively check all dependencies
456            cache_up_to_date
457                && entry.common.dependencies.iter().all(|dep_path| {
458                    is_ty_module_cache_up_to_date(engines, dep_path, include_tests, build_config)
459                })
460        })
461    })
462}
463
464/// Checks if the parsed module cache for a given path is up to date.
465///
466/// This function determines whether the cached parsed representation of a module
467/// is still valid based on file versions, modification times, or content hashes.
468pub(crate) fn is_parse_module_cache_up_to_date(
469    engines: &Engines,
470    path: &Arc<PathBuf>,
471    include_tests: bool,
472    build_config: Option<&BuildConfig>,
473) -> bool {
474    let cache = engines.qe().module_cache.read();
475    let key = ModuleCacheKey::new(path.clone(), include_tests);
476    cache.get(&key).is_some_and(|entry| {
477        // Determine if the cached dependency information is still valid
478        let cache_up_to_date = build_config
479            .and_then(|x| x.lsp_mode.as_ref())
480            .and_then(|lsp| lsp.file_versions.get(path.as_ref()))
481            .map_or_else(
482                || {
483                    // If LSP mode is not active or file version is unavailable, fall back to filesystem checks.
484                    let modified_time = std::fs::metadata(path.as_path())
485                        .ok()
486                        .and_then(|m| m.modified().ok());
487                    // Check if modification time matches, or if not, compare file content hash
488                    entry.parsed.modified_time == modified_time || {
489                        let src = std::fs::read_to_string(path.as_path()).unwrap();
490                        let mut hasher = DefaultHasher::new();
491                        src.hash(&mut hasher);
492                        hasher.finish() == entry.common.hash
493                    }
494                },
495                |version| {
496                    // Determine if the parse cache is up-to-date in LSP mode:
497                    // - If there's no LSP file version (version is None), consider the cache up-to-date.
498                    // - If there is an LSP file version:
499                    //   - If there's no cached version (entry.parsed.version is None), the cache is outdated.
500                    //   - If there's a cached version, compare them: cache is up-to-date if the LSP file version
501                    //     is not greater than the cached version.
502                    version.map_or(true, |v| entry.parsed.version.is_some_and(|ev| v <= ev))
503                },
504            );
505
506        // Checks if the typed module cache for a given path is up to date// If the cache is up to date, recursively check all dependencies to make sure they have not been
507        // modified either.
508        cache_up_to_date
509            && entry.common.dependencies.iter().all(|dep_path| {
510                is_parse_module_cache_up_to_date(engines, dep_path, include_tests, build_config)
511            })
512    })
513}
514
515fn module_path(
516    parent_module_dir: &Path,
517    parent_module_name: Option<&str>,
518    submod: &sway_ast::Submodule,
519) -> PathBuf {
520    if let Some(parent_name) = parent_module_name {
521        parent_module_dir
522            .join(parent_name)
523            .join(submod.name.to_string())
524            .with_extension(sway_types::constants::DEFAULT_FILE_EXTENSION)
525    } else {
526        // top level module
527        parent_module_dir
528            .join(submod.name.to_string())
529            .with_extension(sway_types::constants::DEFAULT_FILE_EXTENSION)
530    }
531}
532
533pub fn build_module_dep_graph(
534    handler: &Handler,
535    parse_module: &mut parsed::ParseModule,
536) -> Result<(), ErrorEmitted> {
537    let module_dep_graph = ty::TyModule::build_dep_graph(handler, parse_module)?;
538    parse_module.module_eval_order = module_dep_graph.compute_order(handler)?;
539
540    for (_, submodule) in &mut parse_module.submodules {
541        build_module_dep_graph(handler, &mut submodule.module)?;
542    }
543    Ok(())
544}
545
546pub struct CompiledAsm(pub FinalizedAsm);
547
548#[allow(clippy::too_many_arguments)]
549pub fn parsed_to_ast(
550    handler: &Handler,
551    engines: &Engines,
552    parse_program: &mut parsed::ParseProgram,
553    initial_namespace: namespace::Root,
554    build_config: Option<&BuildConfig>,
555    package_name: &str,
556    retrigger_compilation: Option<Arc<AtomicBool>>,
557    experimental: ExperimentalFeatures,
558) -> Result<ty::TyProgram, TypeCheckFailed> {
559    let lsp_config = build_config.map(|x| x.lsp_mode.clone()).unwrap_or_default();
560
561    // Build the dependency graph for the submodules.
562    build_module_dep_graph(handler, &mut parse_program.root).map_err(|error| TypeCheckFailed {
563        root_module: None,
564        namespace: initial_namespace.clone(),
565        error,
566    })?;
567
568    let collection_namespace = Namespace::new(handler, engines, initial_namespace.clone(), true)
569        .map_err(|error| TypeCheckFailed {
570            root_module: None,
571            namespace: initial_namespace.clone(),
572            error,
573        })?;
574    // Collect the program symbols.
575
576    let mut collection_ctx =
577        ty::TyProgram::collect(handler, engines, parse_program, collection_namespace).map_err(
578            |error| TypeCheckFailed {
579                root_module: None,
580                namespace: initial_namespace.clone(),
581                error,
582            },
583        )?;
584
585    let typecheck_namespace =
586        Namespace::new(handler, engines, initial_namespace, true).map_err(|error| {
587            TypeCheckFailed {
588                root_module: None,
589                namespace: collection_ctx.namespace().root_ref().clone(),
590                error,
591            }
592        })?;
593    // Type check the program.
594    let typed_program_opt = ty::TyProgram::type_check(
595        handler,
596        engines,
597        parse_program,
598        &mut collection_ctx,
599        typecheck_namespace,
600        package_name,
601        build_config,
602        experimental,
603    );
604
605    let mut typed_program = typed_program_opt?;
606
607    check_should_abort(handler, retrigger_compilation.clone()).map_err(|error| {
608        TypeCheckFailed {
609            root_module: Some(Arc::new(typed_program.root_module.clone())),
610            namespace: typed_program.namespace.root_ref().clone(),
611            error,
612        }
613    })?;
614    // Only clear the parsed AST nodes if we are running a regular compilation pipeline.
615    // LSP needs these to build its token map, and they are cleared by `clear_program` as
616    // part of the LSP garbage collection functionality instead.
617    if lsp_config.is_none() {
618        engines.pe().clear();
619    }
620
621    typed_program.check_deprecated(engines, handler);
622
623    match typed_program.check_recursive(engines, handler) {
624        Ok(()) => {}
625        Err(error) => {
626            handler.dedup();
627            return Err(TypeCheckFailed {
628                root_module: Some(Arc::new(typed_program.root_module.clone())),
629                namespace: typed_program.namespace.root().clone(),
630                error,
631            });
632        }
633    };
634
635    // Skip collecting metadata if we triggered an optimised build from LSP.
636    let types_metadata = if !lsp_config.as_ref().is_some_and(|lsp| lsp.optimized_build) {
637        // Collect information about the types used in this program
638        let types_metadata_result = typed_program.collect_types_metadata(
639            handler,
640            &mut CollectTypesMetadataContext::new(engines, experimental, package_name.to_string()),
641        );
642        let types_metadata = match types_metadata_result {
643            Ok(types_metadata) => types_metadata,
644            Err(error) => {
645                handler.dedup();
646                return Err(TypeCheckFailed {
647                    root_module: Some(Arc::new(typed_program.root_module.clone())),
648                    namespace: typed_program.namespace.root().clone(),
649                    error,
650                });
651            }
652        };
653
654        typed_program
655            .logged_types
656            .extend(types_metadata.iter().filter_map(|m| match m {
657                TypeMetadata::LoggedType(log_id, type_id) => Some((*log_id, *type_id)),
658                _ => None,
659            }));
660
661        typed_program
662            .messages_types
663            .extend(types_metadata.iter().filter_map(|m| match m {
664                TypeMetadata::MessageType(message_id, type_id) => Some((*message_id, *type_id)),
665                _ => None,
666            }));
667
668        let (print_graph, print_graph_url_format) = match build_config {
669            Some(cfg) => (
670                cfg.print_dca_graph.clone(),
671                cfg.print_dca_graph_url_format.clone(),
672            ),
673            None => (None, None),
674        };
675
676        check_should_abort(handler, retrigger_compilation.clone()).map_err(|error| {
677            TypeCheckFailed {
678                root_module: Some(Arc::new(typed_program.root_module.clone())),
679                namespace: typed_program.namespace.root_ref().clone(),
680                error,
681            }
682        })?;
683
684        // Perform control flow analysis and extend with any errors.
685        let _ = perform_control_flow_analysis(
686            handler,
687            engines,
688            &typed_program,
689            print_graph,
690            print_graph_url_format,
691        );
692
693        types_metadata
694    } else {
695        vec![]
696    };
697
698    // Evaluate const declarations, to allow storage slots initialization with consts.
699    let mut ctx = Context::new(engines.se(), experimental);
700    let module = Module::new(&mut ctx, Kind::Contract);
701    if let Err(errs) = ir_generation::compile::compile_constants_for_package(
702        engines,
703        &mut ctx,
704        module,
705        typed_program.namespace.root_ref(),
706    ) {
707        errs.into_iter().for_each(|err| {
708            handler.emit_err(err.clone());
709        });
710    }
711
712    // CEI pattern analysis
713    let cei_analysis_warnings =
714        semantic_analysis::cei_pattern_analysis::analyze_program(engines, &typed_program);
715    for warn in cei_analysis_warnings {
716        handler.emit_warn(warn);
717    }
718
719    let mut md_mgr = MetadataManager::default();
720    // Check that all storage initializers can be evaluated at compile time.
721    typed_program
722        .get_typed_program_with_initialized_storage_slots(
723            handler,
724            engines,
725            &mut ctx,
726            &mut md_mgr,
727            module,
728        )
729        .map_err(|error: ErrorEmitted| {
730            handler.dedup();
731            TypeCheckFailed {
732                root_module: Some(Arc::new(typed_program.root_module.clone())),
733                namespace: typed_program.namespace.root_ref().clone(),
734                error,
735            }
736        })?;
737
738    // All unresolved types lead to compile errors.
739    for err in types_metadata.iter().filter_map(|m| match m {
740        TypeMetadata::UnresolvedType(name, call_site_span_opt) => {
741            Some(CompileError::UnableToInferGeneric {
742                ty: name.as_str().to_string(),
743                span: call_site_span_opt.clone().unwrap_or_else(|| name.span()),
744            })
745        }
746        _ => None,
747    }) {
748        handler.emit_err(err);
749    }
750
751    Ok(typed_program)
752}
753
754#[allow(clippy::too_many_arguments)]
755pub fn compile_to_ast(
756    handler: &Handler,
757    engines: &Engines,
758    input: Arc<str>,
759    initial_namespace: namespace::Root,
760    build_config: Option<&BuildConfig>,
761    package_name: &str,
762    retrigger_compilation: Option<Arc<AtomicBool>>,
763    experimental: ExperimentalFeatures,
764) -> Result<Programs, ErrorEmitted> {
765    check_should_abort(handler, retrigger_compilation.clone())?;
766
767    let query_engine = engines.qe();
768    let mut metrics = PerformanceData::default();
769    if let Some(config) = build_config {
770        let path = config.canonical_root_module();
771        let include_tests = config.include_tests;
772        // Check if we can re-use the data in the cache.
773        if is_parse_module_cache_up_to_date(engines, &path, include_tests, build_config) {
774            let mut entry = query_engine.get_programs_cache_entry(&path).unwrap();
775            entry.programs.metrics.reused_programs += 1;
776
777            let (warnings, errors) = entry.handler_data;
778            let new_handler = Handler::from_parts(warnings, errors);
779            handler.append(new_handler);
780            return Ok(entry.programs);
781        };
782    }
783
784    // Parse the program to a concrete syntax tree (CST).
785    let parse_program_opt = time_expr!(
786        package_name,
787        "parse the program to a concrete syntax tree (CST)",
788        "parse_cst",
789        parse(input, handler, engines, build_config, experimental),
790        build_config,
791        metrics
792    );
793
794    check_should_abort(handler, retrigger_compilation.clone())?;
795
796    let (lexed_program, mut parsed_program) = match parse_program_opt {
797        Ok(modules) => modules,
798        Err(e) => {
799            handler.dedup();
800            return Err(e);
801        }
802    };
803
804    // If tests are not enabled, exclude them from `parsed_program`.
805    if build_config.is_none_or(|config| !config.include_tests) {
806        parsed_program.exclude_tests(engines);
807    }
808
809    // Type check (+ other static analysis) the CST to a typed AST.
810    let program = time_expr!(
811        package_name,
812        "parse the concrete syntax tree (CST) to a typed AST",
813        "parse_ast",
814        parsed_to_ast(
815            handler,
816            engines,
817            &mut parsed_program,
818            initial_namespace,
819            build_config,
820            package_name,
821            retrigger_compilation.clone(),
822            experimental
823        ),
824        build_config,
825        metrics
826    );
827
828    check_should_abort(handler, retrigger_compilation.clone())?;
829
830    handler.dedup();
831
832    let programs = Programs::new(lexed_program, parsed_program, program, metrics);
833
834    if let Some(config) = build_config {
835        let path = config.canonical_root_module();
836        let cache_entry = ProgramsCacheEntry {
837            path,
838            programs: programs.clone(),
839            handler_data: handler.clone().consume(),
840        };
841        query_engine.insert_programs_cache_entry(cache_entry);
842    }
843
844    check_should_abort(handler, retrigger_compilation.clone())?;
845
846    Ok(programs)
847}
848
849/// Given input Sway source code, try compiling to a `CompiledAsm`,
850/// containing the asm in opcode form (not raw bytes/bytecode).
851pub fn compile_to_asm(
852    handler: &Handler,
853    engines: &Engines,
854    input: Arc<str>,
855    initial_namespace: namespace::Root,
856    build_config: &BuildConfig,
857    package_name: &str,
858    experimental: ExperimentalFeatures,
859) -> Result<CompiledAsm, ErrorEmitted> {
860    let ast_res = compile_to_ast(
861        handler,
862        engines,
863        input,
864        initial_namespace,
865        Some(build_config),
866        package_name,
867        None,
868        experimental,
869    )?;
870    ast_to_asm(handler, engines, &ast_res, build_config, experimental)
871}
872
873/// Given an AST compilation result, try compiling to a `CompiledAsm`,
874/// containing the asm in opcode form (not raw bytes/bytecode).
875pub fn ast_to_asm(
876    handler: &Handler,
877    engines: &Engines,
878    programs: &Programs,
879    build_config: &BuildConfig,
880    experimental: ExperimentalFeatures,
881) -> Result<CompiledAsm, ErrorEmitted> {
882    let typed_program = match &programs.typed {
883        Ok(typed_program) => typed_program,
884        Err(err) => return Err(err.error),
885    };
886
887    let asm =
888        match compile_ast_to_ir_to_asm(handler, engines, typed_program, build_config, experimental)
889        {
890            Ok(res) => res,
891            Err(err) => {
892                handler.dedup();
893                return Err(err);
894            }
895        };
896    Ok(CompiledAsm(asm))
897}
898
899pub(crate) fn compile_ast_to_ir_to_asm(
900    handler: &Handler,
901    engines: &Engines,
902    program: &ty::TyProgram,
903    build_config: &BuildConfig,
904    experimental: ExperimentalFeatures,
905) -> Result<FinalizedAsm, ErrorEmitted> {
906    // The IR pipeline relies on type information being fully resolved.
907    // If type information is found to still be generic or unresolved inside of
908    // IR, this is considered an internal compiler error. To resolve this situation,
909    // we need to explicitly ensure all types are resolved before going into IR.
910    //
911    // We _could_ introduce a new type here that uses TypeInfo instead of TypeId and throw away
912    // the engine, since we don't need inference for IR. That'd be a _lot_ of copy-pasted code,
913    // though, so instead, we are just going to do a pass and throw any unresolved generics as
914    // errors and then hold as a runtime invariant that none of the types will be unresolved in the
915    // IR phase.
916
917    let mut ir = match ir_generation::compile_program(
918        program,
919        build_config.include_tests,
920        engines,
921        experimental,
922    ) {
923        Ok(ir) => ir,
924        Err(errors) => {
925            let mut last = None;
926            for e in errors {
927                last = Some(handler.emit_err(e));
928            }
929            return Err(last.unwrap());
930        }
931    };
932
933    // Find all the entry points for purity checking and DCE.
934    let entry_point_functions: Vec<::sway_ir::Function> = ir
935        .module_iter()
936        .flat_map(|module| module.function_iter(&ir))
937        .filter(|func| func.is_entry(&ir))
938        .collect();
939
940    // Do a purity check on the _unoptimised_ IR.
941    {
942        let mut env = ir_generation::PurityEnv::default();
943        let mut md_mgr = metadata::MetadataManager::default();
944        for entry_point in &entry_point_functions {
945            check_function_purity(handler, &mut env, &ir, &mut md_mgr, entry_point);
946        }
947    }
948
949    // Initialize the pass manager and register known passes.
950    let mut pass_mgr = PassManager::default();
951    register_known_passes(&mut pass_mgr);
952    let mut pass_group = PassGroup::default();
953
954    match build_config.optimization_level {
955        OptLevel::Opt1 => {
956            pass_group.append_group(create_o1_pass_group());
957        }
958        OptLevel::Opt0 => {
959            // We run a function deduplication pass that only removes duplicate
960            // functions when everything, including the metadata are identical.
961            pass_group.append_pass(FN_DEDUP_DEBUG_PROFILE_NAME);
962
963            // Inlining is necessary until #4899 is resolved.
964            pass_group.append_pass(FN_INLINE_NAME);
965
966            // Do DCE so other optimizations run faster.
967            pass_group.append_pass(GLOBALS_DCE_NAME);
968            pass_group.append_pass(DCE_NAME);
969        }
970    }
971
972    // Target specific transforms should be moved into something more configured.
973    if build_config.build_target == BuildTarget::Fuel {
974        // FuelVM target specific transforms.
975        //
976        // Demote large by-value constants, arguments and return values to by-reference values
977        // using temporaries.
978        pass_group.append_pass(CONST_DEMOTION_NAME);
979        pass_group.append_pass(ARG_DEMOTION_NAME);
980        pass_group.append_pass(RET_DEMOTION_NAME);
981        pass_group.append_pass(MISC_DEMOTION_NAME);
982
983        // Convert loads and stores to mem_copies where possible.
984        pass_group.append_pass(MEMCPYOPT_NAME);
985
986        // Run a DCE and simplify-cfg to clean up any obsolete instructions.
987        pass_group.append_pass(DCE_NAME);
988        pass_group.append_pass(SIMPLIFY_CFG_NAME);
989
990        match build_config.optimization_level {
991            OptLevel::Opt1 => {
992                pass_group.append_pass(SROA_NAME);
993                pass_group.append_pass(MEM2REG_NAME);
994                pass_group.append_pass(DCE_NAME);
995            }
996            OptLevel::Opt0 => {}
997        }
998    }
999
1000    // Run the passes.
1001    let print_passes_opts: PrintPassesOpts = (&build_config.print_ir).into();
1002    let res =
1003        if let Err(ir_error) = pass_mgr.run_with_print(&mut ir, &pass_group, &print_passes_opts) {
1004            Err(handler.emit_err(CompileError::InternalOwned(
1005                ir_error.to_string(),
1006                span::Span::dummy(),
1007            )))
1008        } else {
1009            Ok(())
1010        };
1011    res?;
1012
1013    compile_ir_context_to_finalized_asm(handler, &ir, Some(build_config))
1014}
1015
1016/// Given input Sway source code, compile to [CompiledBytecode], containing the asm in bytecode form.
1017#[allow(clippy::too_many_arguments)]
1018pub fn compile_to_bytecode(
1019    handler: &Handler,
1020    engines: &Engines,
1021    input: Arc<str>,
1022    initial_namespace: namespace::Root,
1023    build_config: &BuildConfig,
1024    source_map: &mut SourceMap,
1025    package_name: &str,
1026    experimental: ExperimentalFeatures,
1027) -> Result<CompiledBytecode, ErrorEmitted> {
1028    let mut asm_res = compile_to_asm(
1029        handler,
1030        engines,
1031        input,
1032        initial_namespace,
1033        build_config,
1034        package_name,
1035        experimental,
1036    )?;
1037    asm_to_bytecode(
1038        handler,
1039        &mut asm_res,
1040        source_map,
1041        engines.se(),
1042        build_config,
1043    )
1044}
1045
1046/// Size of the prelude's CONFIGURABLES_OFFSET section, in bytes.
1047pub const PRELUDE_CONFIGURABLES_SIZE_IN_BYTES: usize = 8;
1048/// Offset (in bytes) of the CONFIGURABLES_OFFSET section in the prelude.
1049pub const PRELUDE_CONFIGURABLES_OFFSET_IN_BYTES: usize = 16;
1050/// Total size of the prelude in bytes. Instructions start right after.
1051pub const PRELUDE_SIZE_IN_BYTES: usize = 32;
1052
1053/// Given bytecode, overwrite the existing offset to configurables offset in the prelude with the given one.
1054pub fn set_bytecode_configurables_offset(
1055    compiled_bytecode: &mut CompiledBytecode,
1056    md: &[u8; PRELUDE_CONFIGURABLES_SIZE_IN_BYTES],
1057) {
1058    assert!(
1059        compiled_bytecode.bytecode.len()
1060            >= PRELUDE_CONFIGURABLES_OFFSET_IN_BYTES + PRELUDE_CONFIGURABLES_SIZE_IN_BYTES
1061    );
1062    let code = &mut compiled_bytecode.bytecode;
1063    for (index, byte) in md.iter().enumerate() {
1064        code[index + PRELUDE_CONFIGURABLES_OFFSET_IN_BYTES] = *byte;
1065    }
1066}
1067
1068/// Given the assembly (opcodes), compile to [CompiledBytecode], containing the asm in bytecode form.
1069pub fn asm_to_bytecode(
1070    handler: &Handler,
1071    asm: &mut CompiledAsm,
1072    source_map: &mut SourceMap,
1073    source_engine: &SourceEngine,
1074    build_config: &BuildConfig,
1075) -> Result<CompiledBytecode, ErrorEmitted> {
1076    let compiled_bytecode =
1077        asm.0
1078            .to_bytecode_mut(handler, source_map, source_engine, build_config)?;
1079    Ok(compiled_bytecode)
1080}
1081
1082/// Given a [ty::TyProgram], which is type-checked Sway source, construct a graph to analyze
1083/// control flow and determine if it is valid.
1084fn perform_control_flow_analysis(
1085    handler: &Handler,
1086    engines: &Engines,
1087    program: &ty::TyProgram,
1088    print_graph: Option<String>,
1089    print_graph_url_format: Option<String>,
1090) -> Result<(), ErrorEmitted> {
1091    let dca_res = dead_code_analysis(handler, engines, program);
1092    let rpa_errors = return_path_analysis(engines, program);
1093    let rpa_res = handler.scope(|handler| {
1094        for err in rpa_errors {
1095            handler.emit_err(err);
1096        }
1097        Ok(())
1098    });
1099
1100    if let Ok(graph) = dca_res.clone() {
1101        graph.visualize(engines, print_graph, print_graph_url_format);
1102    }
1103    dca_res?;
1104    rpa_res
1105}
1106
1107/// Constructs a dead code graph from all modules within the graph and then attempts to find dead
1108/// code.
1109///
1110/// Returns the graph that was used for analysis.
1111fn dead_code_analysis<'a>(
1112    handler: &Handler,
1113    engines: &'a Engines,
1114    program: &ty::TyProgram,
1115) -> Result<ControlFlowGraph<'a>, ErrorEmitted> {
1116    let decl_engine = engines.de();
1117    let mut dead_code_graph = ControlFlowGraph::new(engines);
1118    let tree_type = program.kind.tree_type();
1119    module_dead_code_analysis(
1120        handler,
1121        engines,
1122        &program.root_module,
1123        &tree_type,
1124        &mut dead_code_graph,
1125    )?;
1126    let warnings = dead_code_graph.find_dead_code(decl_engine);
1127    for warn in warnings {
1128        handler.emit_warn(warn);
1129    }
1130    Ok(dead_code_graph)
1131}
1132
1133/// Recursively collect modules into the given `ControlFlowGraph` ready for dead code analysis.
1134fn module_dead_code_analysis<'eng: 'cfg, 'cfg>(
1135    handler: &Handler,
1136    engines: &'eng Engines,
1137    module: &ty::TyModule,
1138    tree_type: &parsed::TreeType,
1139    graph: &mut ControlFlowGraph<'cfg>,
1140) -> Result<(), ErrorEmitted> {
1141    module
1142        .submodules
1143        .iter()
1144        .try_fold((), |(), (_, submodule)| {
1145            let tree_type = parsed::TreeType::Library;
1146            module_dead_code_analysis(handler, engines, &submodule.module, &tree_type, graph)
1147        })?;
1148    let res = {
1149        ControlFlowGraph::append_module_to_dead_code_graph(
1150            engines,
1151            &module.all_nodes,
1152            tree_type,
1153            graph,
1154        )
1155        .map_err(|err| handler.emit_err(err))
1156    };
1157    graph.connect_pending_entry_edges();
1158    res
1159}
1160
1161fn return_path_analysis(engines: &Engines, program: &ty::TyProgram) -> Vec<CompileError> {
1162    let mut errors = vec![];
1163    module_return_path_analysis(engines, &program.root_module, &mut errors);
1164    errors
1165}
1166
1167fn module_return_path_analysis(
1168    engines: &Engines,
1169    module: &ty::TyModule,
1170    errors: &mut Vec<CompileError>,
1171) {
1172    for (_, submodule) in &module.submodules {
1173        module_return_path_analysis(engines, &submodule.module, errors);
1174    }
1175    let graph = ControlFlowGraph::construct_return_path_graph(engines, &module.all_nodes);
1176    match graph {
1177        Ok(graph) => errors.extend(graph.analyze_return_paths(engines)),
1178        Err(mut error) => errors.append(&mut error),
1179    }
1180}
1181
1182/// Check if the retrigger compilation flag has been set to true in the language server.
1183/// If it has, there is a new compilation request, so we should abort the current compilation.
1184fn check_should_abort(
1185    handler: &Handler,
1186    retrigger_compilation: Option<Arc<AtomicBool>>,
1187) -> Result<(), ErrorEmitted> {
1188    if let Some(ref retrigger_compilation) = retrigger_compilation {
1189        if retrigger_compilation.load(Ordering::SeqCst) {
1190            return Err(handler.cancel());
1191        }
1192    }
1193    Ok(())
1194}
1195
1196#[test]
1197fn test_basic_prog() {
1198    let handler = Handler::default();
1199    let engines = Engines::default();
1200    let prog = parse(
1201        r#"
1202        contract;
1203
1204    enum yo
1205    <T>
1206    where
1207    T: IsAThing
1208    {
1209        x: u32,
1210        y: MyStruct<u32>
1211    }
1212
1213    enum  MyOtherSumType
1214    {
1215        x: u32,
1216        y: MyStruct<u32>
1217    }
1218        struct MyStruct<T> {
1219            field_name: u64,
1220            other_field: T,
1221        }
1222
1223
1224    fn generic_function
1225    <T>
1226    (arg1: u64,
1227    arg2: T)
1228    ->
1229    T
1230    where T: Display,
1231          T: Debug {
1232          let x: MyStruct =
1233          MyStruct
1234          {
1235              field_name:
1236              5
1237          };
1238          return
1239          match
1240            arg1
1241          {
1242               1
1243               => true,
1244               _ => { return false; },
1245          };
1246    }
1247
1248    struct MyStruct {
1249        test: string,
1250    }
1251
1252
1253
1254    use stdlib::println;
1255
1256    trait MyTrait {
1257        // interface points
1258        fn myfunc(x: int) -> unit;
1259        } {
1260        // methods
1261        fn calls_interface_fn(x: int) -> unit {
1262            // declare a byte
1263            let x = 0b10101111;
1264            let mut y = 0b11111111;
1265            self.interface_fn(x);
1266        }
1267    }
1268
1269    pub fn prints_number_five() -> u8 {
1270        let x: u8 = 5;
1271        println(x);
1272         x.to_string();
1273         let some_list = [
1274         5,
1275         10 + 3 / 2,
1276         func_app(my_args, (so_many_args))];
1277        return 5;
1278    }
1279    "#
1280        .into(),
1281        &handler,
1282        &engines,
1283        None,
1284        ExperimentalFeatures::default(),
1285    );
1286    prog.unwrap();
1287}
1288#[test]
1289fn test_parenthesized() {
1290    let handler = Handler::default();
1291    let engines = Engines::default();
1292    let prog = parse(
1293        r#"
1294        contract;
1295        pub fn some_abi_func() -> unit {
1296            let x = (5 + 6 / (1 + (2 / 1) + 4));
1297            return;
1298        }
1299    "#
1300        .into(),
1301        &handler,
1302        &engines,
1303        None,
1304        ExperimentalFeatures::default(),
1305    );
1306    prog.unwrap();
1307}
1308
1309#[test]
1310fn test_unary_ordering() {
1311    use crate::language::{self, parsed};
1312    let handler = Handler::default();
1313    let engines = Engines::default();
1314    let prog = parse(
1315        r#"
1316    script;
1317    fn main() -> bool {
1318        let a = true;
1319        let b = true;
1320        !a && b;
1321    }"#
1322        .into(),
1323        &handler,
1324        &engines,
1325        None,
1326        ExperimentalFeatures::default(),
1327    );
1328    let (.., prog) = prog.unwrap();
1329    // this should parse as `(!a) && b`, not `!(a && b)`. So, the top level
1330    // expression should be `&&`
1331    if let parsed::AstNode {
1332        content:
1333            parsed::AstNodeContent::Declaration(parsed::Declaration::FunctionDeclaration(decl_id)),
1334        ..
1335    } = &prog.root.tree.root_nodes[0]
1336    {
1337        let fn_decl = engines.pe().get_function(decl_id);
1338        if let parsed::AstNode {
1339            content:
1340                parsed::AstNodeContent::Expression(parsed::Expression {
1341                    kind:
1342                        parsed::ExpressionKind::LazyOperator(parsed::LazyOperatorExpression {
1343                            op, ..
1344                        }),
1345                    ..
1346                }),
1347            ..
1348        } = &fn_decl.body.contents[2]
1349        {
1350            assert_eq!(op, &language::LazyOp::And)
1351        } else {
1352            panic!("Was not lazy operator.")
1353        }
1354    } else {
1355        panic!("Was not ast node")
1356    };
1357}
1358
1359#[test]
1360fn test_parser_recovery() {
1361    let handler = Handler::default();
1362    let engines = Engines::default();
1363    let prog = parse(
1364        r#"
1365    script;
1366    fn main() -> bool {
1367        let
1368        let a = true;
1369        true
1370    }"#
1371        .into(),
1372        &handler,
1373        &engines,
1374        None,
1375        ExperimentalFeatures::default(),
1376    );
1377    let (_, _) = prog.unwrap();
1378    assert!(handler.has_errors());
1379    dbg!(handler);
1380}