From 9e27e8f1cedce641394dfd1958b23f932cad40b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Matos?= Date: Tue, 12 Dec 2023 08:51:56 +0000 Subject: [PATCH] Implement order-independent module evaluation (#5300) ## Description This PR implements a new module evaluation system to allow order-independent mod statements when importing modules. It supports these sort of mod patterns to compile: `a.sw`: ```rust library; use ::b::*; pub fn a() { b::b(); } ``` `b.sw`: ```rust library; pub fn b() {} ``` `lib.sw`: ```rust library; mod a; mod b; fn main() -> u32 { b::b(); 0 } ``` To make this work, we now analyze the submodule structure first, creating a dependency graph that we later use to inform the module evaluation order. We also now check for cyclic dependencies using the same graph, and report them when they are found. Closes https://github.com/FuelLabs/sway/issues/409. ## Checklist - [x] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [x] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [x] I have added tests that prove my fix is effective or that my feature works. - [x] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers. --- sway-core/src/semantic_analysis/module.rs | 251 +++++++++++++++++- sway-core/src/semantic_analysis/program.rs | 7 +- sway-error/src/error.rs | 12 + .../module_cyclic_reference/Forc.lock | 3 + .../module_cyclic_reference/Forc.toml | 6 + .../module_cyclic_reference/src/a.sw | 5 + .../module_cyclic_reference/src/b.sw | 5 + .../module_cyclic_reference/src/lib.sw | 9 + .../module_cyclic_reference/test.toml | 4 + .../should_pass/language/module_dep/Forc.lock | 3 + .../should_pass/language/module_dep/Forc.toml | 6 + .../should_pass/language/module_dep/src/a.sw | 5 + .../should_pass/language/module_dep/src/b.sw | 3 + .../language/module_dep/src/lib.sw | 8 + .../should_pass/language/module_dep/test.toml | 4 + .../language/module_dep_multiple/Forc.lock | 3 + .../language/module_dep_multiple/Forc.toml | 6 + .../language/module_dep_multiple/src/a.sw | 5 + .../language/module_dep_multiple/src/b.sw | 3 + .../language/module_dep_multiple/src/c.sw | 5 + .../language/module_dep_multiple/src/lib.sw | 9 + .../language/module_dep_multiple/test.toml | 4 + .../language/module_dep_self/Forc.lock | 3 + .../language/module_dep_self/Forc.toml | 6 + .../language/module_dep_self/src/a.sw | 5 + .../language/module_dep_self/src/lib.sw | 8 + .../language/module_dep_self/test.toml | 4 + 27 files changed, 386 insertions(+), 6 deletions(-) create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/src/a.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/src/b.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/src/lib.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/test.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/src/a.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/src/b.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/src/lib.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/test.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/a.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/b.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/c.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/lib.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/test.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/src/a.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/src/lib.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/test.toml diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index d39a7b64a46..12975c1890c 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -1,11 +1,207 @@ -use sway_error::handler::{ErrorEmitted, Handler}; +use std::{collections::HashMap, fmt::Display, fs}; + +use graph_cycles::Cycles; +use sway_error::{ + error::CompileError, + handler::{ErrorEmitted, Handler}, +}; use crate::{ + engine_threading::DebugWithEngines, language::{parsed::*, ty, ModName}, semantic_analysis::*, + Engines, }; +#[derive(Clone, Debug)] +pub struct ModuleDepGraphEdge(); + +impl Display for ModuleDepGraphEdge { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "") + } +} + +pub type ModuleDepGraphNodeId = petgraph::graph::NodeIndex; + +#[derive(Clone, Debug)] +pub enum ModuleDepGraphNode { + Module {}, + Submodule { name: ModName }, +} + +impl DebugWithEngines for ModuleDepGraphNode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>, _engines: &Engines) -> std::fmt::Result { + let text = match self { + ModuleDepGraphNode::Module { .. } => { + format!("{:?}", "Root module") + } + ModuleDepGraphNode::Submodule { name: mod_name } => { + format!("{:?}", mod_name.as_str()) + } + }; + f.write_str(&text) + } +} + +// Represents an ordered graph between declaration id indexes. +pub type ModuleDepNodeGraph = petgraph::graph::DiGraph; + +pub struct ModuleDepGraph { + dep_graph: ModuleDepNodeGraph, + root: ModuleDepGraphNodeId, + node_name_map: HashMap, +} + +pub type ModuleEvaluationOrder = Vec; + +impl ModuleDepGraph { + pub(crate) fn new() -> Self { + Self { + dep_graph: Default::default(), + root: Default::default(), + node_name_map: Default::default(), + } + } + + pub fn add_node(&mut self, node: ModuleDepGraphNode) -> ModuleDepGraphNodeId { + let node_id = self.dep_graph.add_node(node.clone()); + match node { + ModuleDepGraphNode::Module {} => {} + ModuleDepGraphNode::Submodule { name: mod_name } => { + self.node_name_map.insert(mod_name.to_string(), node_id); + } + }; + node_id + } + + pub fn add_root_node(&mut self) -> ModuleDepGraphNodeId { + self.root = self.add_node(super::module::ModuleDepGraphNode::Module {}); + self.root + } + + fn get_node_id_for_module( + &self, + mod_name: &sway_types::BaseIdent, + ) -> Option { + self.node_name_map.get(&mod_name.to_string()).copied() + } + + /// Prints out GraphViz DOT format for the dependency graph. + #[allow(dead_code)] + pub(crate) fn visualize(&self, engines: &Engines, print_graph: Option) { + if let Some(graph_path) = print_graph { + use petgraph::dot::{Config, Dot}; + let string_graph = self.dep_graph.filter_map( + |_idx, node| Some(format!("{:?}", engines.help_out(node))), + |_idx, edge| Some(format!("{}", edge)), + ); + + let output = format!( + "{:?}", + Dot::with_attr_getters( + &string_graph, + &[Config::NodeNoLabel, Config::EdgeNoLabel], + &|_, er| format!("label = {:?}", er.weight()), + &|_, nr| { + let _node = &self.dep_graph[nr.0]; + let shape = ""; + let url = "".to_string(); + format!("{shape} label = {:?} {url}", nr.1) + }, + ) + ); + + if graph_path.is_empty() { + tracing::info!("{output}"); + } else { + let result = fs::write(graph_path.clone(), output); + if let Some(error) = result.err() { + tracing::error!( + "There was an issue while outputing module dep analysis graph to path {graph_path:?}\n{error}" + ); + } + } + } + } + + /// Computes the ordered list by dependency, which will be used for evaluating the modules + /// in the correct order. We run a topological sort and cycle finding algorithm to check + /// for unsupported cyclic dependency cases. + pub(crate) fn compute_order( + &self, + handler: &Handler, + ) -> Result { + // Check for dependency cycles in the graph by running the Johnson's algorithm. + let cycles = self.dep_graph.cycles(); + if !cycles.is_empty() { + let mut modules = Vec::new(); + for cycle in cycles.first().unwrap() { + let node = self.dep_graph.node_weight(*cycle).unwrap(); + match node { + ModuleDepGraphNode::Module {} => unreachable!(), + ModuleDepGraphNode::Submodule { name } => modules.push(name.clone()), + }; + } + return Err(handler.emit_err(CompileError::ModuleDepGraphCyclicReference { modules })); + } + + // Do a topological sort to compute an ordered list of nodes. + let sorted = match petgraph::algo::toposort(&self.dep_graph, None) { + Ok(value) => value, + // If we were not able to toposort, this means there is likely a cycle in the module dependency graph, + // which we already handled above, so lets just return an empty evaluation order instead of panic'ing. + // module dependencies, which we have already reported. + Err(_) => return Err(handler.emit_err(CompileError::ModuleDepGraphEvaluationError {})), + }; + + let sorted = sorted + .into_iter() + .filter_map(|node_index| { + let node = self.dep_graph.node_weight(node_index); + match node { + Some(node) => match node { + ModuleDepGraphNode::Module {} => None, // root module + ModuleDepGraphNode::Submodule { name: mod_name } => Some(mod_name.clone()), + }, + None => None, + } + }) + .rev() + .collect::>(); + + Ok(sorted) + } +} + impl ty::TyModule { + /// Analyzes the given parsed module to produce a dependency graph. + pub fn analyze( + handler: &Handler, + parsed: &ParseModule, + ) -> Result { + let mut dep_graph = ModuleDepGraph::new(); + dep_graph.add_root_node(); + + let ParseModule { submodules, .. } = parsed; + + // Create graph nodes for each submodule. + submodules.iter().for_each(|(name, _submodule)| { + let sub_mod_node = + dep_graph.add_node(ModuleDepGraphNode::Submodule { name: name.clone() }); + dep_graph + .dep_graph + .add_edge(dep_graph.root, sub_mod_node, ModuleDepGraphEdge {}); + }); + + // Analyze submodules first in order of declaration. + submodules.iter().for_each(|(name, submodule)| { + let _ = ty::TySubmodule::analyze(handler, &mut dep_graph, name.clone(), submodule); + }); + + Ok(dep_graph) + } + /// Type-check the given parsed module to produce a typed module. /// /// Recursively type-checks submodules first. @@ -13,6 +209,7 @@ impl ty::TyModule { handler: &Handler, mut ctx: TypeCheckContext, parsed: &ParseModule, + module_eval_order: ModuleEvaluationOrder, ) -> Result { let ParseModule { submodules, @@ -23,10 +220,14 @@ impl ty::TyModule { .. } = parsed; - // Type-check submodules first in order of declaration. - let submodules_res = submodules + // Type-check submodules first in order of evaluation previously computed by the dependency graph. + let submodules_res = module_eval_order .iter() - .map(|(name, submodule)| { + .map(|eval_mod_name| { + let (name, submodule) = submodules + .iter() + .find(|(submod_name, _submodule)| eval_mod_name == submod_name) + .unwrap(); Ok(( name.clone(), ty::TySubmodule::type_check(handler, ctx.by_ref(), name.clone(), submodule)?, @@ -70,6 +271,43 @@ impl ty::TyModule { } impl ty::TySubmodule { + pub fn analyze( + _handler: &Handler, + module_dep_graph: &mut ModuleDepGraph, + mod_name: ModName, + submodule: &ParseSubmodule, + ) -> Result<(), ErrorEmitted> { + let ParseSubmodule { module, .. } = submodule; + let sub_mod_node = module_dep_graph.get_node_id_for_module(&mod_name).unwrap(); + for node in module.tree.root_nodes.iter() { + match &node.content { + AstNodeContent::UseStatement(use_stmt) => { + if let Some(use_mod_ident) = use_stmt.call_path.first() { + if let Some(mod_name_node) = + module_dep_graph.get_node_id_for_module(use_mod_ident) + { + // Prevent adding edge loops between the same node as that will throw off + // the cyclic dependency analysis. + if sub_mod_node != mod_name_node { + module_dep_graph.dep_graph.add_edge( + sub_mod_node, + mod_name_node, + ModuleDepGraphEdge {}, + ); + } + } + } + } + AstNodeContent::Declaration(_) => {} + AstNodeContent::Expression(_) => {} + AstNodeContent::ImplicitReturnExpression(_) => {} + AstNodeContent::IncludeStatement(_) => {} + AstNodeContent::Error(_, _) => {} + } + } + Ok(()) + } + pub fn type_check( handler: &Handler, parent_ctx: TypeCheckContext, @@ -81,8 +319,11 @@ impl ty::TySubmodule { mod_name_span, visibility, } = submodule; + let modules_dep_graph = ty::TyModule::analyze(handler, module)?; + let module_eval_order = modules_dep_graph.compute_order(handler)?; parent_ctx.enter_submodule(mod_name, *visibility, module.span.clone(), |submod_ctx| { - let module_res = ty::TyModule::type_check(handler, submod_ctx, module); + let module_res = + ty::TyModule::type_check(handler, submod_ctx, module, module_eval_order); module_res.map(|module| ty::TySubmodule { module, mod_name_span: mod_name_span.clone(), diff --git a/sway-core/src/semantic_analysis/program.rs b/sway-core/src/semantic_analysis/program.rs index 1215573511b..b795ca8f9e9 100644 --- a/sway-core/src/semantic_analysis/program.rs +++ b/sway-core/src/semantic_analysis/program.rs @@ -34,7 +34,12 @@ impl TyProgram { let ctx = TypeCheckContext::from_root(&mut namespace, engines).with_kind(parsed.kind.clone()); let ParseProgram { root, kind } = parsed; - ty::TyModule::type_check(handler, ctx, root).and_then(|root| { + + // Analyze the dependency order for the submodules. + let modules_dep_graph = ty::TyModule::analyze(handler, root)?; + let module_eval_order = modules_dep_graph.compute_order(handler)?; + + ty::TyModule::type_check(handler, ctx, root, module_eval_order).and_then(|root| { let res = Self::validate_root(handler, engines, &root, kind.clone(), package_name); res.map(|(kind, declarations, configurables)| Self { kind, diff --git a/sway-error/src/error.rs b/sway-error/src/error.rs index 1b6bdc35253..c0baf598058 100644 --- a/sway-error/src/error.rs +++ b/sway-error/src/error.rs @@ -28,6 +28,16 @@ impl fmt::Display for InterfaceName { // this type. #[derive(Error, Debug, Clone, PartialEq, Eq, Hash)] pub enum CompileError { + #[error( + "There was an error while evaluating the evaluation order for the module dependency graph." + )] + ModuleDepGraphEvaluationError {}, + #[error("A cyclic reference was found between the modules: {}.", + modules.iter().map(|ident| ident.as_str().to_string()) + .collect::>() + .join(", "))] + ModuleDepGraphCyclicReference { modules: Vec }, + #[error("Variable \"{var_name}\" does not exist in this scope.")] UnknownVariable { var_name: Ident, span: Span }, #[error("Identifier \"{name}\" was used as a variable, but it is actually a {what_it_is}.")] @@ -756,6 +766,8 @@ impl Spanned for CompileError { fn span(&self) -> Span { use CompileError::*; match self { + ModuleDepGraphEvaluationError { .. } => Span::dummy(), + ModuleDepGraphCyclicReference { .. } => Span::dummy(), UnknownVariable { span, .. } => span.clone(), NotAVariable { span, .. } => span.clone(), Unimplemented(_, span) => span.clone(), diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/Forc.lock new file mode 100644 index 00000000000..3ca4100b58e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = "module_cyclic_reference" +source = "member" diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/Forc.toml new file mode 100644 index 00000000000..071e8731642 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/Forc.toml @@ -0,0 +1,6 @@ +[project] +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +name = "module_cyclic_reference" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/src/a.sw b/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/src/a.sw new file mode 100644 index 00000000000..027b0b4591f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/src/a.sw @@ -0,0 +1,5 @@ +library; + +use ::b::*; + +pub fn a() { b(); } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/src/b.sw b/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/src/b.sw new file mode 100644 index 00000000000..94f7106c44e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/src/b.sw @@ -0,0 +1,5 @@ +library; + +use ::a::*; + +pub fn b() {} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/src/lib.sw new file mode 100644 index 00000000000..a6fdbea5bed --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/src/lib.sw @@ -0,0 +1,9 @@ +library; + +mod a; +mod b; + +fn main() -> u32 { + a::a(); + 0 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/test.toml new file mode 100644 index 00000000000..49ec8385f11 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/module_cyclic_reference/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: $()A cyclic reference was found between the modules: a, b. + diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/Forc.lock new file mode 100644 index 00000000000..f046cd46a65 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = "module_dep" +source = "member" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/Forc.toml new file mode 100644 index 00000000000..da3a54c801b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/Forc.toml @@ -0,0 +1,6 @@ +[project] +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +name = "module_dep" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/src/a.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/src/a.sw new file mode 100644 index 00000000000..027b0b4591f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/src/a.sw @@ -0,0 +1,5 @@ +library; + +use ::b::*; + +pub fn a() { b(); } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/src/b.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/src/b.sw new file mode 100644 index 00000000000..2fca48e94d7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/src/b.sw @@ -0,0 +1,3 @@ +library; + +pub fn b() {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/src/lib.sw new file mode 100644 index 00000000000..e59f07dba62 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/src/lib.sw @@ -0,0 +1,8 @@ +library; + +mod a; +mod b; + +fn main() -> u32 { + 1 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/test.toml new file mode 100644 index 00000000000..31f8791b861 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep/test.toml @@ -0,0 +1,4 @@ +category = "compile" +expected_result = { action = "return", value = 0 } +validate_abi = false +expected_warnings = 1 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/Forc.lock new file mode 100644 index 00000000000..7e0c9e25d45 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = "module_dep_multiple" +source = "member" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/Forc.toml new file mode 100644 index 00000000000..2ff21bc4786 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/Forc.toml @@ -0,0 +1,6 @@ +[project] +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +name = "module_dep_multiple" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/a.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/a.sw new file mode 100644 index 00000000000..027b0b4591f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/a.sw @@ -0,0 +1,5 @@ +library; + +use ::b::*; + +pub fn a() { b(); } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/b.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/b.sw new file mode 100644 index 00000000000..2fca48e94d7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/b.sw @@ -0,0 +1,3 @@ +library; + +pub fn b() {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/c.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/c.sw new file mode 100644 index 00000000000..b5dcece4ec4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/c.sw @@ -0,0 +1,5 @@ +library; + +use ::a::*; + +pub fn c() { a(); } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/lib.sw new file mode 100644 index 00000000000..06ceb2f6e8b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/src/lib.sw @@ -0,0 +1,9 @@ +library; + +mod c; +mod a; +mod b; + +fn main() -> u32 { + 1 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/test.toml new file mode 100644 index 00000000000..31f8791b861 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_multiple/test.toml @@ -0,0 +1,4 @@ +category = "compile" +expected_result = { action = "return", value = 0 } +validate_abi = false +expected_warnings = 1 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/Forc.lock new file mode 100644 index 00000000000..53fa7aed12e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = "module_dep_self" +source = "member" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/Forc.toml new file mode 100644 index 00000000000..4763ca4519c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/Forc.toml @@ -0,0 +1,6 @@ +[project] +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +name = "module_dep_self" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/src/a.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/src/a.sw new file mode 100644 index 00000000000..9b8a2ca020b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/src/a.sw @@ -0,0 +1,5 @@ +library; + +use ::a::*; + +pub fn a() { } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/src/lib.sw new file mode 100644 index 00000000000..6449e22ede5 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/src/lib.sw @@ -0,0 +1,8 @@ +library; + +mod a; + +fn main() -> u32 { + a::a(); + 0 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/test.toml new file mode 100644 index 00000000000..31f8791b861 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/module_dep_self/test.toml @@ -0,0 +1,4 @@ +category = "compile" +expected_result = { action = "return", value = 0 } +validate_abi = false +expected_warnings = 1