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::DbgGeneration;
35pub use build_config::{BuildConfig, BuildTarget, LspConfig, OptLevel, PrintAsm, PrintIr};
36use control_flow_analysis::ControlFlowGraph;
37pub use debug_generation::write_dwarf;
38use itertools::Itertools;
39use metadata::MetadataManager;
40use query_engine::{ModuleCacheKey, ModuleCommonInfo, ParsedModuleInfo, ProgramsCacheEntry};
41use semantic_analysis::program::TypeCheckFailed;
42use std::collections::hash_map::DefaultHasher;
43use std::collections::HashMap;
44use std::hash::{Hash, Hasher};
45use std::path::{Path, PathBuf};
46use std::sync::atomic::{AtomicBool, Ordering};
47use std::sync::Arc;
48use sway_ast::AttributeDecl;
49use sway_error::convert_parse_tree_error::ConvertParseTreeError;
50use sway_error::handler::{ErrorEmitted, Handler};
51use sway_error::warning::{CompileWarning, Warning};
52use sway_features::ExperimentalFeatures;
53use sway_ir::{
54 create_o1_pass_group, register_known_passes, Context, Kind, Module, PassGroup, PassManager,
55 PrintPassesOpts, ARG_DEMOTION_NAME, CONST_DEMOTION_NAME, DCE_NAME, FN_DEDUP_DEBUG_PROFILE_NAME,
56 FN_INLINE_NAME, GLOBALS_DCE_NAME, MEM2REG_NAME, MEMCPYOPT_NAME, MISC_DEMOTION_NAME,
57 RET_DEMOTION_NAME, SIMPLIFY_CFG_NAME, SROA_NAME,
58};
59use sway_types::span::Source;
60use sway_types::{SourceEngine, SourceLocation, Span};
61use sway_utils::{time_expr, PerformanceData, PerformanceMetric};
62use transform::{ArgsExpectValues, Attribute, AttributeKind, Attributes, ExpectedArgs};
63use types::{CollectTypesMetadata, CollectTypesMetadataContext, LogId, TypeMetadata};
64
65pub use semantic_analysis::namespace::{self, Namespace};
66pub mod types;
67
68use sway_error::error::CompileError;
69use sway_types::{ident::Ident, span, Spanned};
70pub use type_system::*;
71
72pub use language::Programs;
73use language::{lexed, parsed, ty, Visibility};
74use transform::to_parsed_lang::{self, convert_module_kind};
75
76pub mod fuel_prelude {
77 pub use fuel_vm::{self, fuel_asm, fuel_crypto, fuel_tx, fuel_types};
78}
79
80pub use engine_threading::Engines;
81
82pub fn parse(
96 src: Source,
97 handler: &Handler,
98 engines: &Engines,
99 config: Option<&BuildConfig>,
100 experimental: ExperimentalFeatures,
101) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> {
102 match config {
103 None => parse_in_memory(handler, engines, src, experimental, DbgGeneration::None),
104 Some(config) => parse_module_tree(
107 handler,
108 engines,
109 src,
110 config.canonical_root_module(),
111 None,
112 config.build_target,
113 config.dbg_generation,
114 config.include_tests,
115 experimental,
116 config.lsp_mode.as_ref(),
117 )
118 .map(
119 |ParsedModuleTree {
120 tree_type: kind,
121 lexed_module,
122 parse_module,
123 }| {
124 let lexed = lexed::LexedProgram {
125 kind,
126 root: lexed_module,
127 };
128 let parsed = parsed::ParseProgram {
129 kind,
130 root: parse_module,
131 };
132 (lexed, parsed)
133 },
134 ),
135 }
136}
137
138pub fn parse_tree_type(handler: &Handler, src: Source) -> Result<parsed::TreeType, ErrorEmitted> {
142 let experimental = ExperimentalFeatures::default();
146 sway_parse::parse_module_kind(handler, src, None, experimental)
147 .map(|kind| convert_module_kind(&kind))
148}
149
150pub(crate) fn attr_decls_to_attributes(
159 attribute_decls: &[AttributeDecl],
160 can_annotate: impl Fn(&Attribute) -> bool,
161 target_friendly_name: &'static str,
162) -> (Handler, Attributes) {
163 let handler = Handler::default();
164 for attr_decl in attribute_decls
173 .iter()
174 .filter(|attr| !attr.is_doc_comment() && attr.is_inner())
175 {
176 handler.emit_err(CompileError::Unimplemented {
177 span: attr_decl.hash_kind.span(),
178 feature: "Using inner attributes (`#!`)".to_string(),
179 help: vec![],
180 });
181 }
182
183 let attributes = Attributes::new(attribute_decls);
184
185 for attribute in attributes.unknown().filter(|attr| attr.is_outer()) {
187 handler.emit_warn(CompileWarning {
188 span: attribute.name.span(),
189 warning_content: Warning::UnknownAttribute {
190 attribute: (&attribute.name).into(),
191 known_attributes: attributes.known_attribute_names(),
192 },
193 });
194 }
195
196 for ((attribute_kind, _attribute_direction), mut attributes) in &attributes
198 .all()
199 .filter(|attr| attr.is_doc_comment() || attr.is_outer())
200 .chunk_by(|attr| (attr.kind, attr.direction))
201 {
202 if attribute_kind == AttributeKind::DocComment {
205 let first_doc_line = attributes
206 .next()
207 .expect("`chunk_by` guarantees existence of at least one element in the chunk");
208 if !can_annotate(first_doc_line) {
209 let last_doc_line = match attributes.last() {
210 Some(last_attr) => last_attr,
211 None => first_doc_line,
213 };
214 handler.emit_err(
215 ConvertParseTreeError::InvalidAttributeTarget {
216 span: Span::join(
217 first_doc_line.span.clone(),
218 &last_doc_line.span.start_span(),
219 ),
220 attribute: first_doc_line.name.clone(),
221 target_friendly_name,
222 can_only_annotate_help: first_doc_line
223 .can_only_annotate_help(target_friendly_name),
224 }
225 .into(),
226 );
227 }
228 } else {
229 for attribute in attributes {
231 if !can_annotate(attribute) {
232 handler.emit_err(
233 ConvertParseTreeError::InvalidAttributeTarget {
234 span: attribute.name.span(),
235 attribute: attribute.name.clone(),
236 target_friendly_name,
237 can_only_annotate_help: attribute
238 .can_only_annotate_help(target_friendly_name),
239 }
240 .into(),
241 );
242 }
243 }
244 }
245 }
246
247 let should_be_checked =
250 |attr: &&Attribute| !attr.is_doc_comment() && attr.is_outer() && can_annotate(attr);
251
252 for (_attribute_kind, attributes_of_kind) in
254 attributes.all_by_kind(|attr| should_be_checked(attr) && !attr.kind.allows_multiple())
255 {
256 if attributes_of_kind.len() > 1 {
257 let (last_attribute, previous_attributes) = attributes_of_kind
258 .split_last()
259 .expect("`attributes_of_kind` has more then one element");
260 handler.emit_err(
261 ConvertParseTreeError::InvalidAttributeMultiplicity {
262 last_occurrence: (&last_attribute.name).into(),
263 previous_occurrences: previous_attributes
264 .iter()
265 .map(|attr| (&attr.name).into())
266 .collect(),
267 }
268 .into(),
269 );
270 }
271 }
272
273 for attribute in attributes.all().filter(should_be_checked) {
277 let _ = attribute.check_args_multiplicity(&handler);
278 }
279
280 for attribute in attributes
286 .all()
287 .filter(|attr| should_be_checked(attr) && attr.can_have_arguments())
288 {
289 match attribute.expected_args() {
290 ExpectedArgs::None => unreachable!("`attribute` can have arguments"),
291 ExpectedArgs::Any => {}
292 ExpectedArgs::MustBeIn(expected_args) => {
293 for arg in attribute.args.iter() {
294 if !expected_args.contains(&arg.name.as_str()) {
295 handler.emit_err(
296 ConvertParseTreeError::InvalidAttributeArg {
297 attribute: attribute.name.clone(),
298 arg: (&arg.name).into(),
299 expected_args: expected_args.clone(),
300 }
301 .into(),
302 );
303 }
304 }
305 }
306 ExpectedArgs::ShouldBeIn(expected_args) => {
307 for arg in attribute.args.iter() {
308 if !expected_args.contains(&arg.name.as_str()) {
309 handler.emit_warn(CompileWarning {
310 span: arg.name.span(),
311 warning_content: Warning::UnknownAttributeArg {
312 attribute: attribute.name.clone(),
313 arg: (&arg.name).into(),
314 expected_args: expected_args.clone(),
315 },
316 });
317 }
318 }
319 }
320 }
321 }
322
323 for attribute in attributes
327 .all()
328 .filter(|attr| should_be_checked(attr) && attr.can_have_arguments())
329 {
330 fn check_value_expected(handler: &Handler, attribute: &Attribute, is_value_expected: bool) {
335 for arg in attribute.args.iter() {
336 if let ExpectedArgs::MustBeIn(expected_args) = attribute.expected_args() {
337 if !expected_args.contains(&arg.name.as_str()) {
338 continue;
339 }
340 }
341
342 if (is_value_expected && arg.value.is_none())
343 || (!is_value_expected && arg.value.is_some())
344 {
345 handler.emit_err(
346 ConvertParseTreeError::InvalidAttributeArgExpectsValue {
347 attribute: attribute.name.clone(),
348 arg: (&arg.name).into(),
349 value_span: arg.value.as_ref().map(|literal| literal.span()),
350 }
351 .into(),
352 );
353 }
354 }
355 }
356
357 match attribute.args_expect_values() {
358 ArgsExpectValues::Yes => check_value_expected(&handler, attribute, true),
359 ArgsExpectValues::No => check_value_expected(&handler, attribute, false),
360 ArgsExpectValues::Maybe => {}
361 }
362 }
363
364 (handler, attributes)
365}
366
367fn parse_in_memory(
369 handler: &Handler,
370 engines: &Engines,
371 src: Source,
372 experimental: ExperimentalFeatures,
373 dbg_generation: DbgGeneration,
374) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> {
375 let mut hasher = DefaultHasher::new();
376 src.text.hash(&mut hasher);
377 let hash = hasher.finish();
378 let module = sway_parse::parse_file(handler, src, None, experimental)?;
379
380 let (attributes_handler, attributes) = attr_decls_to_attributes(
381 &module.attributes,
382 |attr| attr.can_annotate_module_kind(),
383 module.value.kind.friendly_name(),
384 );
385 let attributes_error_emitted = handler.append(attributes_handler);
386
387 let (kind, tree) = to_parsed_lang::convert_parse_tree(
388 &mut to_parsed_lang::Context::new(BuildTarget::EVM, dbg_generation, experimental),
389 handler,
390 engines,
391 module.value.clone(),
392 )?;
393
394 match attributes_error_emitted {
395 Some(err) => Err(err),
396 None => {
397 let root = parsed::ParseModule {
398 span: span::Span::dummy(),
399 module_kind_span: module.value.kind.span(),
400 module_eval_order: vec![],
401 tree,
402 submodules: vec![],
403 attributes,
404 hash,
405 };
406 let lexed_program = lexed::LexedProgram::new(
407 kind,
408 lexed::LexedModule {
409 tree: module,
410 submodules: vec![],
411 },
412 );
413 Ok((lexed_program, parsed::ParseProgram { kind, root }))
414 }
415 }
416}
417
418pub struct Submodule {
419 name: Ident,
420 path: Arc<PathBuf>,
421 lexed: lexed::LexedSubmodule,
422 parsed: parsed::ParseSubmodule,
423}
424
425pub type Submodules = Vec<Submodule>;
427
428#[allow(clippy::too_many_arguments)]
430fn parse_submodules(
431 handler: &Handler,
432 engines: &Engines,
433 module_name: Option<&str>,
434 module: &sway_ast::Module,
435 module_dir: &Path,
436 build_target: BuildTarget,
437 dbg_generation: DbgGeneration,
438 include_tests: bool,
439 experimental: ExperimentalFeatures,
440 lsp_mode: Option<&LspConfig>,
441) -> Submodules {
442 let mut submods = Vec::with_capacity(module.submodules().count());
444 module.submodules().for_each(|submod| {
445 let submod_path = Arc::new(module_path(module_dir, module_name, submod));
448 let submod_src: Source = match std::fs::read_to_string(&*submod_path) {
449 Ok(s) => s.as_str().into(),
450 Err(e) => {
451 handler.emit_err(CompileError::FileCouldNotBeRead {
452 span: submod.name.span(),
453 file_path: submod_path.to_string_lossy().to_string(),
454 stringified_error: e.to_string(),
455 });
456 return;
457 }
458 };
459 if let Ok(ParsedModuleTree {
460 tree_type: kind,
461 lexed_module,
462 parse_module,
463 }) = parse_module_tree(
464 handler,
465 engines,
466 submod_src.clone(),
467 submod_path.clone(),
468 Some(submod.name.as_str()),
469 build_target,
470 dbg_generation,
471 include_tests,
472 experimental,
473 lsp_mode,
474 ) {
475 if !matches!(kind, parsed::TreeType::Library) {
476 let source_id = engines.se().get_source_id(submod_path.as_ref());
477 let span = span::Span::new(submod_src, 0, 0, Some(source_id)).unwrap();
478 handler.emit_err(CompileError::ImportMustBeLibrary { span });
479 return;
480 }
481
482 let parse_submodule = parsed::ParseSubmodule {
483 module: parse_module,
484 visibility: match submod.visibility {
485 Some(..) => Visibility::Public,
486 None => Visibility::Private,
487 },
488 mod_name_span: submod.name.span(),
489 };
490 let lexed_submodule = lexed::LexedSubmodule {
491 module: lexed_module,
492 };
493 let submodule = Submodule {
494 name: submod.name.clone(),
495 path: submod_path,
496 lexed: lexed_submodule,
497 parsed: parse_submodule,
498 };
499 submods.push(submodule);
500 }
501 });
502 submods
503}
504
505pub type SourceHash = u64;
506
507#[derive(Clone, Debug)]
508pub struct ParsedModuleTree {
509 pub tree_type: parsed::TreeType,
510 pub lexed_module: lexed::LexedModule,
511 pub parse_module: parsed::ParseModule,
512}
513
514#[allow(clippy::too_many_arguments)]
517fn parse_module_tree(
518 handler: &Handler,
519 engines: &Engines,
520 src: Source,
521 path: Arc<PathBuf>,
522 module_name: Option<&str>,
523 build_target: BuildTarget,
524 dbg_generation: DbgGeneration,
525 include_tests: bool,
526 experimental: ExperimentalFeatures,
527 lsp_mode: Option<&LspConfig>,
528) -> Result<ParsedModuleTree, ErrorEmitted> {
529 let query_engine = engines.qe();
530
531 let module_dir = path.parent().expect("module file has no parent directory");
533 let source_id = engines.se().get_source_id(&path.clone());
534 let module = sway_parse::parse_file(handler, src.clone(), Some(source_id), experimental)?;
535
536 let submodules = parse_submodules(
539 handler,
540 engines,
541 module_name,
542 &module.value,
543 module_dir,
544 build_target,
545 dbg_generation,
546 include_tests,
547 experimental,
548 lsp_mode,
549 );
550
551 let (attributes_handler, attributes) = attr_decls_to_attributes(
552 &module.attributes,
553 |attr| attr.can_annotate_module_kind(),
554 module.value.kind.friendly_name(),
555 );
556 let attributes_error_emitted = handler.append(attributes_handler);
557
558 let (kind, tree) = to_parsed_lang::convert_parse_tree(
560 &mut to_parsed_lang::Context::new(build_target, dbg_generation, experimental),
561 handler,
562 engines,
563 module.value.clone(),
564 )?;
565
566 if let Some(err) = attributes_error_emitted {
567 return Err(err);
568 }
569
570 let module_kind_span = module.value.kind.span();
571 let lexed_submodules = submodules
572 .iter()
573 .map(|s| (s.name.clone(), s.lexed.clone()))
574 .collect::<Vec<_>>();
575 let lexed = lexed::LexedModule {
576 tree: module,
577 submodules: lexed_submodules,
578 };
579
580 let mut hasher = DefaultHasher::new();
581 src.text.hash(&mut hasher);
582 let hash = hasher.finish();
583
584 let parsed_submodules = submodules
585 .iter()
586 .map(|s| (s.name.clone(), s.parsed.clone()))
587 .collect::<Vec<_>>();
588 let parsed = parsed::ParseModule {
589 span: span::Span::new(src, 0, 0, Some(source_id)).unwrap(),
590 module_kind_span,
591 module_eval_order: vec![],
592 tree,
593 submodules: parsed_submodules,
594 attributes,
595 hash,
596 };
597
598 let modified_time = std::fs::metadata(path.as_path())
600 .ok()
601 .and_then(|m| m.modified().ok());
602 let dependencies = submodules.into_iter().map(|s| s.path).collect::<Vec<_>>();
603 let version = lsp_mode
604 .and_then(|lsp| lsp.file_versions.get(path.as_ref()).copied())
605 .unwrap_or(None);
606
607 let common_info = ModuleCommonInfo {
608 path: path.clone(),
609 include_tests,
610 dependencies,
611 hash,
612 };
613 let parsed_info = ParsedModuleInfo {
614 modified_time,
615 version,
616 };
617 let cache_entry = ModuleCacheEntry::new(common_info, parsed_info);
618 query_engine.update_or_insert_parsed_module_cache_entry(cache_entry);
619
620 Ok(ParsedModuleTree {
621 tree_type: kind,
622 lexed_module: lexed,
623 parse_module: parsed,
624 })
625}
626
627pub(crate) fn is_ty_module_cache_up_to_date(
635 engines: &Engines,
636 path: &Arc<PathBuf>,
637 include_tests: bool,
638 build_config: Option<&BuildConfig>,
639) -> bool {
640 let cache = engines.qe().module_cache.read();
641 let key = ModuleCacheKey::new(path.clone(), include_tests);
642 cache.get(&key).is_some_and(|entry| {
643 entry.typed.as_ref().is_some_and(|typed| {
644 let cache_up_to_date = build_config
646 .and_then(|x| x.lsp_mode.as_ref())
647 .and_then(|lsp| lsp.file_versions.get(path.as_ref()))
648 .is_none_or(|version| {
649 version.is_none_or(|v| typed.version.is_some_and(|tv| v <= tv))
650 });
651
652 cache_up_to_date
654 && entry.common.dependencies.iter().all(|dep_path| {
655 is_ty_module_cache_up_to_date(engines, dep_path, include_tests, build_config)
656 })
657 })
658 })
659}
660
661pub(crate) fn is_parse_module_cache_up_to_date(
666 engines: &Engines,
667 path: &Arc<PathBuf>,
668 include_tests: bool,
669 build_config: Option<&BuildConfig>,
670) -> bool {
671 let cache = engines.qe().module_cache.read();
672 let key = ModuleCacheKey::new(path.clone(), include_tests);
673 cache.get(&key).is_some_and(|entry| {
674 let cache_up_to_date = build_config
676 .and_then(|x| x.lsp_mode.as_ref())
677 .and_then(|lsp| lsp.file_versions.get(path.as_ref()))
678 .map_or_else(
679 || {
680 let modified_time = std::fs::metadata(path.as_path())
682 .ok()
683 .and_then(|m| m.modified().ok());
684 entry.parsed.modified_time == modified_time || {
686 let src = std::fs::read_to_string(path.as_path()).unwrap();
687 let mut hasher = DefaultHasher::new();
688 src.hash(&mut hasher);
689 hasher.finish() == entry.common.hash
690 }
691 },
692 |version| {
693 version.is_none_or(|v| entry.parsed.version.is_some_and(|ev| v <= ev))
700 },
701 );
702
703 cache_up_to_date
706 && entry.common.dependencies.iter().all(|dep_path| {
707 is_parse_module_cache_up_to_date(engines, dep_path, include_tests, build_config)
708 })
709 })
710}
711
712fn module_path(
713 parent_module_dir: &Path,
714 parent_module_name: Option<&str>,
715 submod: &sway_ast::Submodule,
716) -> PathBuf {
717 if let Some(parent_name) = parent_module_name {
718 parent_module_dir
719 .join(parent_name)
720 .join(submod.name.to_string())
721 .with_extension(sway_types::constants::DEFAULT_FILE_EXTENSION)
722 } else {
723 parent_module_dir
725 .join(submod.name.to_string())
726 .with_extension(sway_types::constants::DEFAULT_FILE_EXTENSION)
727 }
728}
729
730pub fn build_module_dep_graph(
731 handler: &Handler,
732 parse_module: &mut parsed::ParseModule,
733) -> Result<(), ErrorEmitted> {
734 let module_dep_graph = ty::TyModule::build_dep_graph(handler, parse_module)?;
735 parse_module.module_eval_order = module_dep_graph.compute_order(handler)?;
736
737 for (_, submodule) in &mut parse_module.submodules {
738 build_module_dep_graph(handler, &mut submodule.module)?;
739 }
740 Ok(())
741}
742
743#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
754pub struct PanicOccurrence {
755 pub loc: SourceLocation,
756 pub log_id: Option<LogId>,
757 pub msg: Option<String>,
758}
759
760pub type PanicOccurrences = HashMap<PanicOccurrence, u64>;
762
763pub struct CompiledAsm {
764 pub finalized_asm: FinalizedAsm,
765 pub panic_occurrences: PanicOccurrences,
766}
767
768#[allow(clippy::too_many_arguments)]
769pub fn parsed_to_ast(
770 handler: &Handler,
771 engines: &Engines,
772 parse_program: &mut parsed::ParseProgram,
773 initial_namespace: namespace::Package,
774 build_config: Option<&BuildConfig>,
775 package_name: &str,
776 retrigger_compilation: Option<Arc<AtomicBool>>,
777 experimental: ExperimentalFeatures,
778) -> Result<ty::TyProgram, TypeCheckFailed> {
779 let lsp_config = build_config.map(|x| x.lsp_mode.clone()).unwrap_or_default();
780
781 build_module_dep_graph(handler, &mut parse_program.root).map_err(|error| TypeCheckFailed {
783 root_module: None,
784 namespace: initial_namespace.clone(),
785 error,
786 })?;
787
788 let collection_namespace = Namespace::new(handler, engines, initial_namespace.clone(), true)
789 .map_err(|error| TypeCheckFailed {
790 root_module: None,
791 namespace: initial_namespace.clone(),
792 error,
793 })?;
794 let mut collection_ctx =
797 ty::TyProgram::collect(handler, engines, parse_program, collection_namespace).map_err(
798 |error| TypeCheckFailed {
799 root_module: None,
800 namespace: initial_namespace.clone(),
801 error,
802 },
803 )?;
804
805 let typecheck_namespace =
806 Namespace::new(handler, engines, initial_namespace, true).map_err(|error| {
807 TypeCheckFailed {
808 root_module: None,
809 namespace: collection_ctx.namespace().current_package_ref().clone(),
810 error,
811 }
812 })?;
813 let typed_program_opt = ty::TyProgram::type_check(
815 handler,
816 engines,
817 parse_program,
818 &mut collection_ctx,
819 typecheck_namespace,
820 package_name,
821 build_config,
822 experimental,
823 );
824
825 let mut typed_program = typed_program_opt?;
826
827 check_should_abort(handler, retrigger_compilation.clone()).map_err(|error| {
828 TypeCheckFailed {
829 root_module: Some(Arc::new(typed_program.root_module.clone())),
830 namespace: typed_program.namespace.current_package_ref().clone(),
831 error,
832 }
833 })?;
834 if lsp_config.is_none() {
838 engines.pe().clear();
839 }
840
841 typed_program.check_deprecated(engines, handler);
842
843 match typed_program.check_recursive(engines, handler) {
844 Ok(()) => {}
845 Err(error) => {
846 handler.dedup();
847 return Err(TypeCheckFailed {
848 root_module: Some(Arc::new(typed_program.root_module.clone())),
849 namespace: typed_program.namespace.current_package().clone(),
850 error,
851 });
852 }
853 };
854
855 let types_metadata = if !lsp_config.as_ref().is_some_and(|lsp| lsp.optimized_build) {
857 let types_metadata_result = typed_program.collect_types_metadata(
859 handler,
860 &mut CollectTypesMetadataContext::new(engines, experimental, package_name.to_string()),
861 );
862 let types_metadata = match types_metadata_result {
863 Ok(types_metadata) => types_metadata,
864 Err(error) => {
865 handler.dedup();
866 return Err(TypeCheckFailed {
867 root_module: Some(Arc::new(typed_program.root_module.clone())),
868 namespace: typed_program.namespace.current_package().clone(),
869 error,
870 });
871 }
872 };
873
874 typed_program
875 .logged_types
876 .extend(types_metadata.iter().filter_map(|m| match m {
877 TypeMetadata::LoggedType(log_id, type_id) => Some((*log_id, *type_id)),
878 _ => None,
879 }));
880
881 typed_program
882 .messages_types
883 .extend(types_metadata.iter().filter_map(|m| match m {
884 TypeMetadata::MessageType(message_id, type_id) => Some((*message_id, *type_id)),
885 _ => None,
886 }));
887
888 let (print_graph, print_graph_url_format) = match build_config {
889 Some(cfg) => (
890 cfg.print_dca_graph.clone(),
891 cfg.print_dca_graph_url_format.clone(),
892 ),
893 None => (None, None),
894 };
895
896 check_should_abort(handler, retrigger_compilation.clone()).map_err(|error| {
897 TypeCheckFailed {
898 root_module: Some(Arc::new(typed_program.root_module.clone())),
899 namespace: typed_program.namespace.current_package_ref().clone(),
900 error,
901 }
902 })?;
903
904 let _ = perform_control_flow_analysis(
906 handler,
907 engines,
908 &typed_program,
909 print_graph,
910 print_graph_url_format,
911 );
912
913 types_metadata
914 } else {
915 vec![]
916 };
917
918 let mut ctx = Context::new(engines.se(), experimental);
920 let module = Module::new(&mut ctx, Kind::Contract);
921 if let Err(errs) = ir_generation::compile::compile_constants_for_package(
922 engines,
923 &mut ctx,
924 module,
925 &typed_program.namespace,
926 ) {
927 errs.into_iter().for_each(|err| {
928 handler.emit_err(err.clone());
929 });
930 }
931
932 let cei_analysis_warnings =
934 semantic_analysis::cei_pattern_analysis::analyze_program(engines, &typed_program);
935 for warn in cei_analysis_warnings {
936 handler.emit_warn(warn);
937 }
938
939 let mut md_mgr = MetadataManager::default();
940 typed_program
942 .get_typed_program_with_initialized_storage_slots(
943 handler,
944 engines,
945 &mut ctx,
946 &mut md_mgr,
947 module,
948 )
949 .map_err(|error: ErrorEmitted| {
950 handler.dedup();
951 TypeCheckFailed {
952 root_module: Some(Arc::new(typed_program.root_module.clone())),
953 namespace: typed_program.namespace.current_package_ref().clone(),
954 error,
955 }
956 })?;
957
958 for err in types_metadata.iter().filter_map(|m| match m {
960 TypeMetadata::UnresolvedType(name, call_site_span_opt) => {
961 Some(CompileError::UnableToInferGeneric {
962 ty: name.as_str().to_string(),
963 span: call_site_span_opt.clone().unwrap_or_else(|| name.span()),
964 })
965 }
966 _ => None,
967 }) {
968 handler.emit_err(err);
969 }
970
971 Ok(typed_program)
972}
973
974#[allow(clippy::too_many_arguments)]
975pub fn compile_to_ast(
976 handler: &Handler,
977 engines: &Engines,
978 src: Source,
979 initial_namespace: namespace::Package,
980 build_config: Option<&BuildConfig>,
981 package_name: &str,
982 retrigger_compilation: Option<Arc<AtomicBool>>,
983 experimental: ExperimentalFeatures,
984) -> Result<Programs, ErrorEmitted> {
985 check_should_abort(handler, retrigger_compilation.clone())?;
986
987 let query_engine = engines.qe();
988 let mut metrics = PerformanceData::default();
989 if let Some(config) = build_config {
990 let path = config.canonical_root_module();
991 let include_tests = config.include_tests;
992 if is_parse_module_cache_up_to_date(engines, &path, include_tests, build_config) {
994 let mut entry = query_engine.get_programs_cache_entry(&path).unwrap();
995 entry.programs.metrics.reused_programs += 1;
996
997 let (warnings, errors) = entry.handler_data;
998 let new_handler = Handler::from_parts(warnings, errors);
999 handler.append(new_handler);
1000 return Ok(entry.programs);
1001 };
1002 }
1003
1004 let parse_program_opt = time_expr!(
1006 package_name,
1007 "parse the program to a concrete syntax tree (CST)",
1008 "parse_cst",
1009 parse(src, handler, engines, build_config, experimental),
1010 build_config,
1011 metrics
1012 );
1013
1014 check_should_abort(handler, retrigger_compilation.clone())?;
1015
1016 let (lexed_program, mut parsed_program) = match parse_program_opt {
1017 Ok(modules) => modules,
1018 Err(e) => {
1019 handler.dedup();
1020 return Err(e);
1021 }
1022 };
1023
1024 if build_config.is_none_or(|config| !config.include_tests) {
1026 parsed_program.exclude_tests(engines);
1027 }
1028
1029 let program = time_expr!(
1031 package_name,
1032 "parse the concrete syntax tree (CST) to a typed AST",
1033 "parse_ast",
1034 parsed_to_ast(
1035 handler,
1036 engines,
1037 &mut parsed_program,
1038 initial_namespace,
1039 build_config,
1040 package_name,
1041 retrigger_compilation.clone(),
1042 experimental
1043 ),
1044 build_config,
1045 metrics
1046 );
1047
1048 check_should_abort(handler, retrigger_compilation.clone())?;
1049
1050 handler.dedup();
1051
1052 let programs = Programs::new(
1053 Arc::new(lexed_program),
1054 Arc::new(parsed_program),
1055 program.map(Arc::new),
1056 metrics,
1057 );
1058
1059 if let Some(config) = build_config {
1060 let path = config.canonical_root_module();
1061 let cache_entry = ProgramsCacheEntry {
1062 path,
1063 programs: programs.clone(),
1064 handler_data: handler.clone().consume(),
1065 };
1066 query_engine.insert_programs_cache_entry(cache_entry);
1067 }
1068
1069 check_should_abort(handler, retrigger_compilation.clone())?;
1070
1071 Ok(programs)
1072}
1073
1074pub fn compile_to_asm(
1077 handler: &Handler,
1078 engines: &Engines,
1079 src: Source,
1080 initial_namespace: namespace::Package,
1081 build_config: &BuildConfig,
1082 package_name: &str,
1083 experimental: ExperimentalFeatures,
1084) -> Result<CompiledAsm, ErrorEmitted> {
1085 let ast_res = compile_to_ast(
1086 handler,
1087 engines,
1088 src,
1089 initial_namespace,
1090 Some(build_config),
1091 package_name,
1092 None,
1093 experimental,
1094 )?;
1095
1096 ast_to_asm(handler, engines, &ast_res, build_config, experimental)
1097}
1098
1099pub fn ast_to_asm(
1102 handler: &Handler,
1103 engines: &Engines,
1104 programs: &Programs,
1105 build_config: &BuildConfig,
1106 experimental: ExperimentalFeatures,
1107) -> Result<CompiledAsm, ErrorEmitted> {
1108 let typed_program = match &programs.typed {
1109 Ok(typed_program) => typed_program,
1110 Err(err) => return Err(err.error),
1111 };
1112
1113 let mut panic_occurrences = PanicOccurrences::default();
1114
1115 let asm = match compile_ast_to_ir_to_asm(
1116 handler,
1117 engines,
1118 typed_program,
1119 &mut panic_occurrences,
1120 build_config,
1121 experimental,
1122 ) {
1123 Ok(res) => res,
1124 Err(err) => {
1125 handler.dedup();
1126 return Err(err);
1127 }
1128 };
1129
1130 Ok(CompiledAsm {
1131 finalized_asm: asm,
1132 panic_occurrences,
1133 })
1134}
1135
1136pub(crate) fn compile_ast_to_ir_to_asm(
1137 handler: &Handler,
1138 engines: &Engines,
1139 program: &ty::TyProgram,
1140 panic_occurrences: &mut PanicOccurrences,
1141 build_config: &BuildConfig,
1142 experimental: ExperimentalFeatures,
1143) -> Result<FinalizedAsm, ErrorEmitted> {
1144 let mut ir = match ir_generation::compile_program(
1156 program,
1157 panic_occurrences,
1158 build_config.include_tests,
1159 engines,
1160 experimental,
1161 ) {
1162 Ok(ir) => ir,
1163 Err(errors) => {
1164 let mut last = None;
1165 for e in errors {
1166 last = Some(handler.emit_err(e));
1167 }
1168 return Err(last.unwrap());
1169 }
1170 };
1171
1172 let entry_point_functions: Vec<::sway_ir::Function> = ir
1174 .module_iter()
1175 .flat_map(|module| module.function_iter(&ir))
1176 .filter(|func| func.is_entry(&ir))
1177 .collect();
1178
1179 {
1181 let mut env = ir_generation::PurityEnv::default();
1182 let mut md_mgr = metadata::MetadataManager::default();
1183 for entry_point in &entry_point_functions {
1184 check_function_purity(handler, &mut env, &ir, &mut md_mgr, entry_point);
1185 }
1186 }
1187
1188 let mut pass_mgr = PassManager::default();
1190 register_known_passes(&mut pass_mgr);
1191 let mut pass_group = PassGroup::default();
1192
1193 match build_config.optimization_level {
1194 OptLevel::Opt1 => {
1195 pass_group.append_group(create_o1_pass_group());
1196 }
1197 OptLevel::Opt0 => {
1198 pass_group.append_pass(FN_DEDUP_DEBUG_PROFILE_NAME);
1201
1202 pass_group.append_pass(FN_INLINE_NAME);
1204
1205 pass_group.append_pass(GLOBALS_DCE_NAME);
1207 pass_group.append_pass(DCE_NAME);
1208 }
1209 }
1210
1211 if build_config.build_target == BuildTarget::Fuel {
1213 pass_group.append_pass(CONST_DEMOTION_NAME);
1218 pass_group.append_pass(ARG_DEMOTION_NAME);
1219 pass_group.append_pass(RET_DEMOTION_NAME);
1220 pass_group.append_pass(MISC_DEMOTION_NAME);
1221
1222 pass_group.append_pass(MEMCPYOPT_NAME);
1224
1225 pass_group.append_pass(DCE_NAME);
1227 pass_group.append_pass(SIMPLIFY_CFG_NAME);
1228
1229 match build_config.optimization_level {
1230 OptLevel::Opt1 => {
1231 pass_group.append_pass(SROA_NAME);
1232 pass_group.append_pass(MEM2REG_NAME);
1233 pass_group.append_pass(DCE_NAME);
1234 }
1235 OptLevel::Opt0 => {}
1236 }
1237 }
1238
1239 let print_passes_opts: PrintPassesOpts = (&build_config.print_ir).into();
1241 let res =
1242 if let Err(ir_error) = pass_mgr.run_with_print(&mut ir, &pass_group, &print_passes_opts) {
1243 Err(handler.emit_err(CompileError::InternalOwned(
1244 ir_error.to_string(),
1245 span::Span::dummy(),
1246 )))
1247 } else {
1248 Ok(())
1249 };
1250 res?;
1251
1252 compile_ir_context_to_finalized_asm(handler, &ir, Some(build_config))
1253}
1254
1255#[allow(clippy::too_many_arguments)]
1257pub fn compile_to_bytecode(
1258 handler: &Handler,
1259 engines: &Engines,
1260 src: Source,
1261 initial_namespace: namespace::Package,
1262 build_config: &BuildConfig,
1263 source_map: &mut SourceMap,
1264 package_name: &str,
1265 experimental: ExperimentalFeatures,
1266) -> Result<CompiledBytecode, ErrorEmitted> {
1267 let mut asm_res = compile_to_asm(
1268 handler,
1269 engines,
1270 src,
1271 initial_namespace,
1272 build_config,
1273 package_name,
1274 experimental,
1275 )?;
1276 asm_to_bytecode(
1277 handler,
1278 &mut asm_res,
1279 source_map,
1280 engines.se(),
1281 build_config,
1282 )
1283}
1284
1285pub const PRELUDE_CONFIGURABLES_SIZE_IN_BYTES: usize = 8;
1287pub const PRELUDE_CONFIGURABLES_OFFSET_IN_BYTES: usize = 16;
1289pub const PRELUDE_SIZE_IN_BYTES: usize = 32;
1291
1292pub fn set_bytecode_configurables_offset(
1294 compiled_bytecode: &mut CompiledBytecode,
1295 md: &[u8; PRELUDE_CONFIGURABLES_SIZE_IN_BYTES],
1296) {
1297 assert!(
1298 compiled_bytecode.bytecode.len()
1299 >= PRELUDE_CONFIGURABLES_OFFSET_IN_BYTES + PRELUDE_CONFIGURABLES_SIZE_IN_BYTES
1300 );
1301 let code = &mut compiled_bytecode.bytecode;
1302 for (index, byte) in md.iter().enumerate() {
1303 code[index + PRELUDE_CONFIGURABLES_OFFSET_IN_BYTES] = *byte;
1304 }
1305}
1306
1307pub fn asm_to_bytecode(
1309 handler: &Handler,
1310 asm: &mut CompiledAsm,
1311 source_map: &mut SourceMap,
1312 source_engine: &SourceEngine,
1313 build_config: &BuildConfig,
1314) -> Result<CompiledBytecode, ErrorEmitted> {
1315 let compiled_bytecode =
1316 asm.finalized_asm
1317 .to_bytecode_mut(handler, source_map, source_engine, build_config)?;
1318 Ok(compiled_bytecode)
1319}
1320
1321fn perform_control_flow_analysis(
1324 handler: &Handler,
1325 engines: &Engines,
1326 program: &ty::TyProgram,
1327 print_graph: Option<String>,
1328 print_graph_url_format: Option<String>,
1329) -> Result<(), ErrorEmitted> {
1330 let dca_res = dead_code_analysis(handler, engines, program);
1331 let rpa_errors = return_path_analysis(engines, program);
1332 let rpa_res = handler.scope(|handler| {
1333 for err in rpa_errors {
1334 handler.emit_err(err);
1335 }
1336 Ok(())
1337 });
1338
1339 if let Ok(graph) = dca_res.clone() {
1340 graph.visualize(engines, print_graph, print_graph_url_format);
1341 }
1342 dca_res?;
1343 rpa_res
1344}
1345
1346fn dead_code_analysis<'a>(
1351 handler: &Handler,
1352 engines: &'a Engines,
1353 program: &ty::TyProgram,
1354) -> Result<ControlFlowGraph<'a>, ErrorEmitted> {
1355 let decl_engine = engines.de();
1356 let mut dead_code_graph = ControlFlowGraph::new(engines);
1357 let tree_type = program.kind.tree_type();
1358 module_dead_code_analysis(
1359 handler,
1360 engines,
1361 &program.root_module,
1362 &tree_type,
1363 &mut dead_code_graph,
1364 )?;
1365 let warnings = dead_code_graph.find_dead_code(decl_engine);
1366 for warn in warnings {
1367 handler.emit_warn(warn);
1368 }
1369 Ok(dead_code_graph)
1370}
1371
1372fn module_dead_code_analysis<'eng: 'cfg, 'cfg>(
1374 handler: &Handler,
1375 engines: &'eng Engines,
1376 module: &ty::TyModule,
1377 tree_type: &parsed::TreeType,
1378 graph: &mut ControlFlowGraph<'cfg>,
1379) -> Result<(), ErrorEmitted> {
1380 module
1381 .submodules
1382 .iter()
1383 .try_fold((), |(), (_, submodule)| {
1384 let tree_type = parsed::TreeType::Library;
1385 module_dead_code_analysis(handler, engines, &submodule.module, &tree_type, graph)
1386 })?;
1387 let res = {
1388 ControlFlowGraph::append_module_to_dead_code_graph(
1389 engines,
1390 &module.all_nodes,
1391 tree_type,
1392 graph,
1393 )
1394 .map_err(|err| handler.emit_err(err))
1395 };
1396 graph.connect_pending_entry_edges();
1397 res
1398}
1399
1400fn return_path_analysis(engines: &Engines, program: &ty::TyProgram) -> Vec<CompileError> {
1401 let mut errors = vec![];
1402 module_return_path_analysis(engines, &program.root_module, &mut errors);
1403 errors
1404}
1405
1406fn module_return_path_analysis(
1407 engines: &Engines,
1408 module: &ty::TyModule,
1409 errors: &mut Vec<CompileError>,
1410) {
1411 for (_, submodule) in &module.submodules {
1412 module_return_path_analysis(engines, &submodule.module, errors);
1413 }
1414 let graph = ControlFlowGraph::construct_return_path_graph(engines, &module.all_nodes);
1415 match graph {
1416 Ok(graph) => errors.extend(graph.analyze_return_paths(engines)),
1417 Err(mut error) => errors.append(&mut error),
1418 }
1419}
1420
1421fn check_should_abort(
1424 handler: &Handler,
1425 retrigger_compilation: Option<Arc<AtomicBool>>,
1426) -> Result<(), ErrorEmitted> {
1427 if let Some(ref retrigger_compilation) = retrigger_compilation {
1428 if retrigger_compilation.load(Ordering::SeqCst) {
1429 return Err(handler.cancel());
1430 }
1431 }
1432 Ok(())
1433}
1434
1435#[test]
1436fn test_basic_prog() {
1437 let handler = Handler::default();
1438 let engines = Engines::default();
1439 let prog = parse(
1440 r#"
1441 contract;
1442
1443 enum yo
1444 <T>
1445 where
1446 T: IsAThing
1447 {
1448 x: u32,
1449 y: MyStruct<u32>
1450 }
1451
1452 enum MyOtherSumType
1453 {
1454 x: u32,
1455 y: MyStruct<u32>
1456 }
1457 struct MyStruct<T> {
1458 field_name: u64,
1459 other_field: T,
1460 }
1461
1462
1463 fn generic_function
1464 <T>
1465 (arg1: u64,
1466 arg2: T)
1467 ->
1468 T
1469 where T: Display,
1470 T: Debug {
1471 let x: MyStruct =
1472 MyStruct
1473 {
1474 field_name:
1475 5
1476 };
1477 return
1478 match
1479 arg1
1480 {
1481 1
1482 => true,
1483 _ => { return false; },
1484 };
1485 }
1486
1487 struct MyStruct {
1488 test: string,
1489 }
1490
1491
1492
1493 use stdlib::println;
1494
1495 trait MyTrait {
1496 // interface points
1497 fn myfunc(x: int) -> unit;
1498 } {
1499 // methods
1500 fn calls_interface_fn(x: int) -> unit {
1501 // declare a byte
1502 let x = 0b10101111;
1503 let mut y = 0b11111111;
1504 self.interface_fn(x);
1505 }
1506 }
1507
1508 pub fn prints_number_five() -> u8 {
1509 let x: u8 = 5;
1510 println(x);
1511 x.to_string();
1512 let some_list = [
1513 5,
1514 10 + 3 / 2,
1515 func_app(my_args, (so_many_args))];
1516 return 5;
1517 }
1518 "#
1519 .into(),
1520 &handler,
1521 &engines,
1522 None,
1523 ExperimentalFeatures::default(),
1524 );
1525 prog.unwrap();
1526}
1527#[test]
1528fn test_parenthesized() {
1529 let handler = Handler::default();
1530 let engines = Engines::default();
1531 let prog = parse(
1532 r#"
1533 contract;
1534 pub fn some_abi_func() -> unit {
1535 let x = (5 + 6 / (1 + (2 / 1) + 4));
1536 return;
1537 }
1538 "#
1539 .into(),
1540 &handler,
1541 &engines,
1542 None,
1543 ExperimentalFeatures::default(),
1544 );
1545 prog.unwrap();
1546}
1547
1548#[test]
1549fn test_unary_ordering() {
1550 use crate::language::{self, parsed};
1551 let handler = Handler::default();
1552 let engines = Engines::default();
1553 let prog = parse(
1554 r#"
1555 script;
1556 fn main() -> bool {
1557 let a = true;
1558 let b = true;
1559 !a && b;
1560 }"#
1561 .into(),
1562 &handler,
1563 &engines,
1564 None,
1565 ExperimentalFeatures::default(),
1566 );
1567 let (.., prog) = prog.unwrap();
1568 if let parsed::AstNode {
1571 content:
1572 parsed::AstNodeContent::Declaration(parsed::Declaration::FunctionDeclaration(decl_id)),
1573 ..
1574 } = &prog.root.tree.root_nodes[0]
1575 {
1576 let fn_decl = engines.pe().get_function(decl_id);
1577 if let parsed::AstNode {
1578 content:
1579 parsed::AstNodeContent::Expression(parsed::Expression {
1580 kind:
1581 parsed::ExpressionKind::LazyOperator(parsed::LazyOperatorExpression {
1582 op, ..
1583 }),
1584 ..
1585 }),
1586 ..
1587 } = &fn_decl.body.contents[2]
1588 {
1589 assert_eq!(op, &language::LazyOp::And)
1590 } else {
1591 panic!("Was not lazy operator.")
1592 }
1593 } else {
1594 panic!("Was not ast node")
1595 };
1596}
1597
1598#[test]
1599fn test_parser_recovery() {
1600 let handler = Handler::default();
1601 let engines = Engines::default();
1602 let prog = parse(
1603 r#"
1604 script;
1605 fn main() -> bool {
1606 let
1607 let a = true;
1608 true
1609 }"#
1610 .into(),
1611 &handler,
1612 &engines,
1613 None,
1614 ExperimentalFeatures::default(),
1615 );
1616 let (_, _) = prog.unwrap();
1617 assert!(handler.has_errors());
1618 dbg!(handler);
1619}