1use std::{
2 collections::{HashMap, HashSet},
3 fmt::Display,
4 fs,
5 sync::Arc,
6};
7
8use graph_cycles::Cycles;
9use sway_error::{
10 error::CompileError,
11 handler::{ErrorEmitted, Handler},
12};
13use sway_types::{BaseIdent, Named, SourceId};
14
15use crate::{
16 decl_engine::{DeclEngineGet, DeclId},
17 engine_threading::{DebugWithEngines, PartialEqWithEngines, PartialEqWithEnginesContext},
18 is_ty_module_cache_up_to_date,
19 language::{
20 parsed::*,
21 ty::{self, TyAstNodeContent, TyDecl},
22 CallPath, ModName,
23 },
24 query_engine::{ModuleCacheKey, TypedModuleInfo},
25 semantic_analysis::*,
26 BuildConfig, Engines, TypeInfo,
27};
28
29use super::{
30 declaration::auto_impl::{
31 abi_encoding::AbiEncodingAutoImplContext, marker_traits::MarkerTraitsAutoImplContext,
32 },
33 symbol_collection_context::SymbolCollectionContext,
34};
35
36#[derive(Clone, Debug)]
37pub struct ModuleDepGraphEdge();
38
39impl Display for ModuleDepGraphEdge {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 write!(f, "")
42 }
43}
44
45pub type ModuleDepGraphNodeId = petgraph::graph::NodeIndex;
46
47#[derive(Clone, Debug)]
48pub enum ModuleDepGraphNode {
49 Module {},
50 Submodule { name: ModName },
51}
52
53impl DebugWithEngines for ModuleDepGraphNode {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, _engines: &Engines) -> std::fmt::Result {
55 let text = match self {
56 ModuleDepGraphNode::Module { .. } => {
57 format!("{:?}", "Root module")
58 }
59 ModuleDepGraphNode::Submodule { name: mod_name } => {
60 format!("{:?}", mod_name.as_str())
61 }
62 };
63 f.write_str(&text)
64 }
65}
66
67pub type ModuleDepNodeGraph = petgraph::graph::DiGraph<ModuleDepGraphNode, ModuleDepGraphEdge>;
69
70pub struct ModuleDepGraph {
71 dep_graph: ModuleDepNodeGraph,
72 root: ModuleDepGraphNodeId,
73 node_name_map: HashMap<String, ModuleDepGraphNodeId>,
74}
75
76impl ModuleDepGraph {
77 pub(crate) fn new() -> Self {
78 Self {
79 dep_graph: Default::default(),
80 root: Default::default(),
81 node_name_map: Default::default(),
82 }
83 }
84
85 pub fn add_node(&mut self, node: ModuleDepGraphNode) -> ModuleDepGraphNodeId {
86 let node_id = self.dep_graph.add_node(node.clone());
87 match node {
88 ModuleDepGraphNode::Module {} => {}
89 ModuleDepGraphNode::Submodule { name: mod_name } => {
90 self.node_name_map.insert(mod_name.to_string(), node_id);
91 }
92 };
93 node_id
94 }
95
96 pub fn add_root_node(&mut self) -> ModuleDepGraphNodeId {
97 self.root = self.add_node(super::module::ModuleDepGraphNode::Module {});
98 self.root
99 }
100
101 fn get_node_id_for_module(
102 &self,
103 mod_name: &sway_types::BaseIdent,
104 ) -> Option<ModuleDepGraphNodeId> {
105 self.node_name_map.get(&mod_name.to_string()).copied()
106 }
107
108 #[allow(dead_code)]
110 pub(crate) fn visualize(&self, engines: &Engines, print_graph: Option<String>) {
111 if let Some(graph_path) = print_graph {
112 use petgraph::dot::{Config, Dot};
113 let string_graph = self.dep_graph.filter_map(
114 |_idx, node| Some(format!("{:?}", engines.help_out(node))),
115 |_idx, edge| Some(format!("{}", edge)),
116 );
117
118 let output = format!(
119 "{:?}",
120 Dot::with_attr_getters(
121 &string_graph,
122 &[Config::NodeNoLabel, Config::EdgeNoLabel],
123 &|_, er| format!("label = {:?}", er.weight()),
124 &|_, nr| {
125 let _node = &self.dep_graph[nr.0];
126 let shape = "";
127 let url = "".to_string();
128 format!("{shape} label = {:?} {url}", nr.1)
129 },
130 )
131 );
132
133 if graph_path.is_empty() {
134 tracing::info!("{output}");
135 } else {
136 let result = fs::write(graph_path.clone(), output);
137 if let Some(error) = result.err() {
138 tracing::error!(
139 "There was an issue while outputting module dep analysis graph to path {graph_path:?}\n{error}"
140 );
141 }
142 }
143 }
144 }
145
146 pub(crate) fn compute_order(
150 &self,
151 handler: &Handler,
152 ) -> Result<ModuleEvaluationOrder, ErrorEmitted> {
153 let cycles = self.dep_graph.cycles();
155 if !cycles.is_empty() {
156 let mut modules = Vec::new();
157 for cycle in cycles.first().unwrap() {
158 let node = self.dep_graph.node_weight(*cycle).unwrap();
159 match node {
160 ModuleDepGraphNode::Module {} => unreachable!(),
161 ModuleDepGraphNode::Submodule { name } => modules.push(name.clone()),
162 };
163 }
164 return Err(handler.emit_err(CompileError::ModuleDepGraphCyclicReference { modules }));
165 }
166
167 let sorted = match petgraph::algo::toposort(&self.dep_graph, None) {
169 Ok(value) => value,
170 Err(_) => return Err(handler.emit_err(CompileError::ModuleDepGraphEvaluationError {})),
174 };
175
176 let sorted = sorted
177 .into_iter()
178 .filter_map(|node_index| {
179 let node = self.dep_graph.node_weight(node_index);
180 match node {
181 Some(node) => match node {
182 ModuleDepGraphNode::Module {} => None, ModuleDepGraphNode::Submodule { name: mod_name } => Some(mod_name.clone()),
184 },
185 None => None,
186 }
187 })
188 .rev()
189 .collect::<Vec<_>>();
190
191 Ok(sorted)
192 }
193}
194
195impl ty::TyModule {
196 pub fn build_dep_graph(
198 handler: &Handler,
199 parsed: &ParseModule,
200 ) -> Result<ModuleDepGraph, ErrorEmitted> {
201 let mut dep_graph = ModuleDepGraph::new();
202 dep_graph.add_root_node();
203
204 let ParseModule { submodules, .. } = parsed;
205
206 submodules.iter().for_each(|(name, _submodule)| {
208 let sub_mod_node =
209 dep_graph.add_node(ModuleDepGraphNode::Submodule { name: name.clone() });
210 dep_graph
211 .dep_graph
212 .add_edge(dep_graph.root, sub_mod_node, ModuleDepGraphEdge {});
213 });
214
215 submodules.iter().for_each(|(name, submodule)| {
217 let _ =
218 ty::TySubmodule::build_dep_graph(handler, &mut dep_graph, name.clone(), submodule);
219 });
220
221 Ok(dep_graph)
222 }
223
224 pub fn collect(
228 handler: &Handler,
229 engines: &Engines,
230 ctx: &mut SymbolCollectionContext,
231 parsed: &ParseModule,
232 ) -> Result<(), ErrorEmitted> {
233 let ParseModule {
234 submodules,
235 tree,
236 module_eval_order,
237 attributes: _,
238 span: _,
239 hash: _,
240 ..
241 } = parsed;
242
243 module_eval_order.iter().for_each(|eval_mod_name| {
245 let (name, submodule) = submodules
246 .iter()
247 .find(|(submod_name, _submodule)| eval_mod_name == submod_name)
248 .unwrap();
249 let _ = ty::TySubmodule::collect(handler, engines, ctx, name.clone(), submodule);
250 });
251
252 let _ = tree
253 .root_nodes
254 .iter()
255 .map(|node| ty::TyAstNode::collect(handler, engines, ctx, node))
256 .filter_map(|res| res.ok())
257 .collect::<Vec<_>>();
258
259 Ok(())
260 }
261
262 fn get_cached_ty_module_if_up_to_date(
267 source_id: Option<&SourceId>,
268 engines: &Engines,
269 build_config: Option<&BuildConfig>,
270 ) -> Option<Arc<ty::TyModule>> {
271 let source_id = source_id?;
272
273 let path = engines.se().get_path(source_id);
275 let include_tests = build_config.is_some_and(|x| x.include_tests);
276 let key = ModuleCacheKey::new(path.clone().into(), include_tests);
277 let cache = engines.qe().module_cache.read();
278 cache.get(&key).and_then(|entry| {
279 entry.typed.as_ref().and_then(|typed| {
280 let is_up_to_date = is_ty_module_cache_up_to_date(
282 engines,
283 &path.into(),
284 include_tests,
285 build_config,
286 );
287
288 if is_up_to_date {
290 Some(typed.module.clone())
291 } else {
292 None
293 }
294 })
295 })
296 }
297
298 pub fn type_check(
302 handler: &Handler,
303 mut ctx: TypeCheckContext,
304 engines: &Engines,
305 kind: TreeType,
306 parsed: &ParseModule,
307 build_config: Option<&BuildConfig>,
308 ) -> Result<Arc<Self>, ErrorEmitted> {
309 let ParseModule {
310 submodules,
311 tree,
312 attributes,
313 span,
314 module_eval_order,
315 ..
316 } = parsed;
317
318 if let Some(module) = ty::TyModule::get_cached_ty_module_if_up_to_date(
320 parsed.span.source_id(),
321 engines,
322 build_config,
323 ) {
324 return Ok(module);
325 }
326
327 let submodules_res = module_eval_order
329 .iter()
330 .map(|eval_mod_name| {
331 let (name, submodule) = submodules
332 .iter()
333 .find(|(submod_name, _)| eval_mod_name == submod_name)
334 .unwrap();
335
336 if let Some(cached_module) = ty::TyModule::get_cached_ty_module_if_up_to_date(
338 submodule.module.span.source_id(),
339 engines,
340 build_config,
341 ) {
342 Ok::<(BaseIdent, ty::TySubmodule), ErrorEmitted>((
344 name.clone(),
345 ty::TySubmodule {
346 module: cached_module,
347 mod_name_span: submodule.mod_name_span.clone(),
348 },
349 ))
350 } else {
351 let type_checked_submodule = ty::TySubmodule::type_check(
353 handler,
354 ctx.by_ref(),
355 engines,
356 name.clone(),
357 kind,
358 submodule,
359 build_config,
360 )?;
361 Ok((name.clone(), type_checked_submodule))
362 }
363 })
364 .collect::<Result<Vec<_>, _>>();
365
366 let ordered_nodes = node_dependencies::order_ast_nodes_by_dependency(
368 handler,
369 ctx.engines(),
370 tree.root_nodes.clone(),
371 )?;
372
373 let mut all_nodes = Self::type_check_nodes(handler, ctx.by_ref(), &ordered_nodes)?;
374 let submodules = submodules_res?;
375
376 let fallback_fn = collect_fallback_fn(&all_nodes, engines, handler)?;
377 match (&kind, &fallback_fn) {
378 (TreeType::Contract, _) | (_, None) => {}
379 (_, Some(fallback_fn)) => {
380 let fallback_fn = engines.de().get(fallback_fn);
381 return Err(handler.emit_err(CompileError::FallbackFnsAreContractOnly {
382 span: fallback_fn.span.clone(),
383 }));
384 }
385 }
386
387 if ctx.experimental.new_encoding {
388 let main_decl = all_nodes.iter_mut().find_map(|x| match &mut x.content {
389 ty::TyAstNodeContent::Declaration(ty::TyDecl::FunctionDecl(decl)) => {
390 let fn_decl = engines.de().get_function(&decl.decl_id);
391 (fn_decl.name.as_str() == "main").then_some(fn_decl)
392 }
393 _ => None,
394 });
395
396 match (&kind, main_decl.is_some()) {
397 (TreeType::Predicate, true) => {
398 let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx);
399 if let Ok(node) = fn_generator.generate_predicate_entry(
400 engines,
401 main_decl.as_ref().unwrap(),
402 handler,
403 ) {
404 all_nodes.push(node)
405 }
406 }
407 (TreeType::Script, true) => {
408 let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx);
409 if let Ok(node) = fn_generator.generate_script_entry(
410 engines,
411 main_decl.as_ref().unwrap(),
412 handler,
413 ) {
414 all_nodes.push(node)
415 }
416 }
417 (TreeType::Contract, _) => {
418 let contract_supertrait_fns = submodules
420 .iter()
421 .flat_map(|x| x.1.module.submodules_recursive())
422 .flat_map(|x| x.1.module.contract_supertrait_fns(engines))
423 .chain(
424 all_nodes
425 .iter()
426 .flat_map(|x| x.contract_supertrait_fns(engines)),
427 )
428 .collect::<Vec<_>>();
429
430 let mut contract_fns = submodules
432 .iter()
433 .flat_map(|x| x.1.module.submodules_recursive())
434 .flat_map(|x| x.1.module.contract_fns(engines))
435 .chain(all_nodes.iter().flat_map(|x| x.contract_fns(engines)))
436 .collect::<Vec<_>>();
437
438 let partialeq_ctx = PartialEqWithEnginesContext::new(engines);
440 contract_fns.retain(|method| {
441 contract_supertrait_fns
442 .iter()
443 .all(|si| !PartialEqWithEngines::eq(method, si, &partialeq_ctx))
444 });
445
446 let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx);
447 if let Ok(node) = fn_generator.generate_contract_entry(
448 engines,
449 parsed.span.source_id().map(|x| x.program_id()),
450 &contract_fns,
451 fallback_fn,
452 handler,
453 ) {
454 all_nodes.push(node)
455 }
456 }
457 _ => {}
458 }
459 }
460
461 #[allow(clippy::arc_with_non_send_sync)]
462 let ty_module = Arc::new(Self {
463 span: span.clone(),
464 submodules,
465 all_nodes,
466 attributes: attributes.clone(),
467 });
468
469 if let Some(source_id) = span.source_id() {
471 let path = engines.se().get_path(source_id);
472 let version = build_config
473 .and_then(|config| config.lsp_mode.as_ref())
474 .and_then(|lsp| lsp.file_versions.get(&path).copied())
475 .flatten();
476
477 let include_tests = build_config.is_some_and(|x| x.include_tests);
478 let key = ModuleCacheKey::new(path.clone().into(), include_tests);
479 engines.qe().update_typed_module_cache_entry(
480 &key,
481 TypedModuleInfo {
482 module: ty_module.clone(),
483 version,
484 },
485 );
486 }
487
488 Ok(ty_module)
489 }
490
491 fn get_all_impls(
493 ctx: TypeCheckContext<'_>,
494 nodes: &[AstNode],
495 predicate: fn(&ImplSelfOrTrait) -> bool,
496 ) -> HashMap<BaseIdent, HashSet<CallPath>> {
497 let engines = ctx.engines();
498 let mut impls = HashMap::<BaseIdent, HashSet<CallPath>>::new();
499
500 for node in nodes.iter() {
501 if let AstNodeContent::Declaration(Declaration::ImplSelfOrTrait(decl_id)) =
502 &node.content
503 {
504 let decl = &*engines.pe().get_impl_self_or_trait(decl_id);
505 let implementing_for = ctx.engines.te().get(decl.implementing_for.type_id);
506 let implementing_for = match &*implementing_for {
507 TypeInfo::Struct(decl_id) => {
508 Some(ctx.engines().de().get(decl_id).name().clone())
509 }
510 TypeInfo::Enum(decl) => Some(ctx.engines().de().get(decl).name().clone()),
511 TypeInfo::Custom {
512 qualified_call_path,
513 ..
514 } => Some(qualified_call_path.call_path.suffix.clone()),
515 _ => None,
516 };
517
518 if let Some(implementing_for) = implementing_for {
519 if predicate(decl) {
520 impls
521 .entry(implementing_for)
522 .or_default()
523 .insert(decl.trait_name.clone());
524 }
525 }
526 }
527 }
528
529 impls
530 }
531
532 fn type_check_nodes(
533 handler: &Handler,
534 mut ctx: TypeCheckContext,
535 nodes: &[AstNode],
536 ) -> Result<Vec<ty::TyAstNode>, ErrorEmitted> {
537 let engines = ctx.engines();
538
539 let all_abiencode_impls = Self::get_all_impls(ctx.by_ref(), nodes, |decl| {
543 decl.trait_name.suffix.as_str() == "AbiEncode"
544 });
545
546 let mut typed_nodes = vec![];
547 for node in nodes {
548 let auto_impl_encoding_traits = match &node.content {
550 AstNodeContent::Declaration(Declaration::StructDeclaration(decl_id)) => {
551 let decl = ctx.engines().pe().get_struct(decl_id);
552 !all_abiencode_impls.contains_key(&decl.name)
553 }
554 AstNodeContent::Declaration(Declaration::EnumDeclaration(decl_id)) => {
555 let decl = ctx.engines().pe().get_enum(decl_id);
556 !all_abiencode_impls.contains_key(&decl.name)
557 }
558 _ => false,
559 };
560
561 let Ok(node) = ty::TyAstNode::type_check(handler, ctx.by_ref(), node) else {
562 continue;
563 };
564
565 let mut generated = vec![];
567 if ctx.experimental.new_encoding {
568 if let (true, mut ctx) = (
569 auto_impl_encoding_traits,
570 AbiEncodingAutoImplContext::new(&mut ctx),
571 ) {
572 match &node.content {
573 TyAstNodeContent::Declaration(decl @ TyDecl::StructDecl(_))
574 | TyAstNodeContent::Declaration(decl @ TyDecl::EnumDecl(_)) => {
575 let (a, b) = ctx.generate_abi_encode_and_decode_impls(engines, decl);
576 generated.extend(a);
577 generated.extend(b);
578 }
579 _ => {}
580 }
581 };
582 }
583
584 if ctx.experimental.error_type {
587 let mut ctx = MarkerTraitsAutoImplContext::new(&mut ctx);
588 if let TyAstNodeContent::Declaration(decl @ TyDecl::EnumDecl(_)) = &node.content {
589 let a = ctx.generate_enum_marker_trait_impl(engines, decl);
590 generated.extend(a);
591 }
592 }
593
594 typed_nodes.push(node);
595 typed_nodes.extend(generated);
596 }
597
598 Ok(typed_nodes)
599 }
600}
601
602fn collect_fallback_fn(
603 all_nodes: &[ty::TyAstNode],
604 engines: &Engines,
605 handler: &Handler,
606) -> Result<Option<DeclId<ty::TyFunctionDecl>>, ErrorEmitted> {
607 let mut fallback_fns = all_nodes
608 .iter()
609 .filter_map(|x| match &x.content {
610 ty::TyAstNodeContent::Declaration(ty::TyDecl::FunctionDecl(decl)) => {
611 let d = engines.de().get(&decl.decl_id);
612 d.is_fallback().then_some(decl.decl_id)
613 }
614 _ => None,
615 })
616 .collect::<Vec<_>>();
617
618 let mut last_error = None;
619 for f in fallback_fns.iter().skip(1) {
620 let decl = engines.de().get(f);
621 last_error = Some(
622 handler.emit_err(CompileError::MultipleDefinitionsOfFallbackFunction {
623 name: decl.name.clone(),
624 span: decl.span.clone(),
625 }),
626 );
627 }
628
629 if let Some(last_error) = last_error {
630 return Err(last_error);
631 }
632
633 if let Some(fallback_fn) = fallback_fns.pop() {
634 let f = engines.de().get(&fallback_fn);
635 if !f.parameters.is_empty() {
636 Err(
637 handler.emit_err(CompileError::FallbackFnsCannotHaveParameters {
638 span: f.span.clone(),
639 }),
640 )
641 } else {
642 Ok(Some(fallback_fn))
643 }
644 } else {
645 Ok(None)
646 }
647}
648
649impl ty::TySubmodule {
650 pub fn build_dep_graph(
651 _handler: &Handler,
652 module_dep_graph: &mut ModuleDepGraph,
653 mod_name: ModName,
654 submodule: &ParseSubmodule,
655 ) -> Result<(), ErrorEmitted> {
656 let ParseSubmodule { module, .. } = submodule;
657 let sub_mod_node = module_dep_graph.get_node_id_for_module(&mod_name).unwrap();
658 for node in module.tree.root_nodes.iter() {
659 match &node.content {
660 AstNodeContent::UseStatement(use_stmt) => {
661 if let Some(use_mod_ident) = use_stmt.call_path.first() {
662 if let Some(mod_name_node) =
663 module_dep_graph.get_node_id_for_module(use_mod_ident)
664 {
665 if sub_mod_node != mod_name_node {
668 module_dep_graph.dep_graph.add_edge(
669 sub_mod_node,
670 mod_name_node,
671 ModuleDepGraphEdge {},
672 );
673 }
674 }
675 }
676 }
677 AstNodeContent::Declaration(_) => {}
678 AstNodeContent::Expression(_) => {}
679 AstNodeContent::IncludeStatement(_) => {}
680 AstNodeContent::Error(_, _) => {}
681 }
682 }
683 Ok(())
684 }
685
686 pub fn collect(
687 handler: &Handler,
688 engines: &Engines,
689 parent_ctx: &mut SymbolCollectionContext,
690 mod_name: ModName,
691 submodule: &ParseSubmodule,
692 ) -> Result<(), ErrorEmitted> {
693 let ParseSubmodule {
694 module,
695 mod_name_span: _,
696 visibility,
697 } = submodule;
698 parent_ctx.enter_submodule(
699 handler,
700 engines,
701 mod_name,
702 *visibility,
703 module.span.clone(),
704 |submod_ctx| ty::TyModule::collect(handler, engines, submod_ctx, module),
705 )?
706 }
707
708 pub fn type_check(
709 handler: &Handler,
710 mut parent_ctx: TypeCheckContext,
711 engines: &Engines,
712 mod_name: ModName,
713 kind: TreeType,
714 submodule: &ParseSubmodule,
715 build_config: Option<&BuildConfig>,
716 ) -> Result<Self, ErrorEmitted> {
717 let ParseSubmodule {
718 module,
719 mod_name_span,
720 visibility,
721 } = submodule;
722 parent_ctx.enter_submodule(
723 handler,
724 mod_name,
725 *visibility,
726 module.span.clone(),
727 |submod_ctx| {
728 let module_res = ty::TyModule::type_check(
729 handler,
730 submod_ctx,
731 engines,
732 kind,
733 module,
734 build_config,
735 );
736 module_res.map(|module| ty::TySubmodule {
737 module,
738 mod_name_span: mod_name_span.clone(),
739 })
740 },
741 )?
742 }
743}