From 09799d5330c89e9a245e414a148a241be5c8341d Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Wed, 1 Feb 2023 09:38:55 +0000 Subject: [PATCH 1/8] Rename friendly_name to friendly_type_name. --- forc-plugins/forc-doc/src/descriptor.rs | 14 ++++++------- forc-plugins/forc-doc/src/render.rs | 2 +- .../language/ty/declaration/declaration.rs | 21 ++++++++++--------- .../ast_node/expression/typed_expression.rs | 6 +++--- sway-lsp/src/capabilities/hover.rs | 6 +++--- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/forc-plugins/forc-doc/src/descriptor.rs b/forc-plugins/forc-doc/src/descriptor.rs index 0f36ce457c1..232d224ce5f 100644 --- a/forc-plugins/forc-doc/src/descriptor.rs +++ b/forc-plugins/forc-doc/src/descriptor.rs @@ -59,7 +59,7 @@ impl Descriptor { module_info: module_info.clone(), item_header: ItemHeader { module_info: module_info.clone(), - friendly_name: ty_decl.friendly_name(), + friendly_name: ty_decl.friendly_type_name(), item_name: item_name.clone(), }, item_body: ItemBody { @@ -91,7 +91,7 @@ impl Descriptor { module_info: module_info.clone(), item_header: ItemHeader { module_info: module_info.clone(), - friendly_name: ty_decl.friendly_name(), + friendly_name: ty_decl.friendly_type_name(), item_name: item_name.clone(), }, item_body: ItemBody { @@ -126,7 +126,7 @@ impl Descriptor { module_info: module_info.clone(), item_header: ItemHeader { module_info: module_info.clone(), - friendly_name: ty_decl.friendly_name(), + friendly_name: ty_decl.friendly_type_name(), item_name: item_name.clone(), }, item_body: ItemBody { @@ -158,7 +158,7 @@ impl Descriptor { module_info: module_info.clone(), item_header: ItemHeader { module_info: module_info.clone(), - friendly_name: ty_decl.friendly_name(), + friendly_name: ty_decl.friendly_type_name(), item_name: item_name.clone(), }, item_body: ItemBody { @@ -186,7 +186,7 @@ impl Descriptor { module_info: module_info.clone(), item_header: ItemHeader { module_info: module_info.clone(), - friendly_name: ty_decl.friendly_name(), + friendly_name: ty_decl.friendly_type_name(), item_name: item_name.clone(), }, item_body: ItemBody { @@ -243,7 +243,7 @@ impl Descriptor { module_info: module_info.clone(), item_header: ItemHeader { module_info: module_info.clone(), - friendly_name: ty_decl.friendly_name(), + friendly_name: ty_decl.friendly_type_name(), item_name: item_name.clone(), }, item_body: ItemBody { @@ -273,7 +273,7 @@ impl Descriptor { module_info: module_info.clone(), item_header: ItemHeader { module_info: module_info.clone(), - friendly_name: ty_decl.friendly_name(), + friendly_name: ty_decl.friendly_type_name(), item_name: item_name.clone(), }, item_body: ItemBody { diff --git a/forc-plugins/forc-doc/src/render.rs b/forc-plugins/forc-doc/src/render.rs index 2393c16bcef..9cfd8d65292 100644 --- a/forc-plugins/forc-doc/src/render.rs +++ b/forc-plugins/forc-doc/src/render.rs @@ -375,7 +375,7 @@ impl Renderable for ItemBody { } = self; let decl_ty = ty_decl.doc_name(); - let friendly_name = ty_decl.friendly_name(); + let friendly_name = ty_decl.friendly_type_name(); let sidebar = sidebar.render()?; let item_context = (item_context.context.is_some()) .then(|| -> Result> { item_context.render() }); diff --git a/sway-core/src/language/ty/declaration/declaration.rs b/sway-core/src/language/ty/declaration/declaration.rs index 4e9dac06a1d..b289186ee00 100644 --- a/sway-core/src/language/ty/declaration/declaration.rs +++ b/sway-core/src/language/ty/declaration/declaration.rs @@ -123,7 +123,7 @@ impl DisplayWithEngines for TyDeclaration { write!( f, "{} declaration ({})", - self.friendly_name(), + self.friendly_type_name(), match self { TyDeclaration::VariableDeclaration(decl) => { let TyVariableDeclaration { @@ -330,7 +330,7 @@ impl TyDeclaration { decl => err( vec![], vec![CompileError::DeclIsNotAnEnum { - actually: decl.friendly_name().to_string(), + actually: decl.friendly_type_name().to_string(), span: decl.span(), }], ), @@ -360,7 +360,7 @@ impl TyDeclaration { TyDeclaration::ErrorRecovery(_) => err(vec![], vec![]), decl => { errors.push(CompileError::DeclIsNotAStruct { - actually: decl.friendly_name().to_string(), + actually: decl.friendly_type_name().to_string(), span: decl.span(), }); err(warnings, errors) @@ -391,7 +391,7 @@ impl TyDeclaration { TyDeclaration::ErrorRecovery(_) => err(vec![], vec![]), decl => { errors.push(CompileError::DeclIsNotAFunction { - actually: decl.friendly_name().to_string(), + actually: decl.friendly_type_name().to_string(), span: decl.span(), }); err(warnings, errors) @@ -410,7 +410,7 @@ impl TyDeclaration { TyDeclaration::ErrorRecovery(_) => err(vec![], vec![]), decl => { errors.push(CompileError::DeclIsNotAVariable { - actually: decl.friendly_name().to_string(), + actually: decl.friendly_type_name().to_string(), span: decl.span(), }); err(warnings, errors) @@ -434,7 +434,7 @@ impl TyDeclaration { decl => err( vec![], vec![CompileError::DeclIsNotAnAbi { - actually: decl.friendly_name().to_string(), + actually: decl.friendly_type_name().to_string(), span: decl.span(), }], ), @@ -457,7 +457,7 @@ impl TyDeclaration { decl => { let errors = vec![ (CompileError::DeclIsNotAConstant { - actually: decl.friendly_name().to_string(), + actually: decl.friendly_type_name().to_string(), span: decl.span(), }), ]; @@ -466,8 +466,9 @@ impl TyDeclaration { } } - /// friendly name string used for error reporting. - pub fn friendly_name(&self) -> &'static str { + + /// friendly type name string used for error reporting. + pub fn friendly_type_name(&self) -> &'static str { use TyDeclaration::*; match self { VariableDeclaration(_) => "variable", @@ -557,7 +558,7 @@ impl TyDeclaration { errors.push(CompileError::NotAType { span: decl.span(), name: engines.help_out(decl).to_string(), - actually_is: decl.friendly_name(), + actually_is: decl.friendly_type_name(), }); return err(warnings, errors); } diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index acc9fcf1a06..2a36b8fbe6f 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -448,7 +448,7 @@ impl ty::TyExpression { Some(a) => { errors.push(CompileError::NotAVariable { name: name.clone(), - what_it_is: a.friendly_name(), + what_it_is: a.friendly_type_name(), span, }); ty::TyExpression::error(name.span(), engines) @@ -1272,7 +1272,7 @@ impl ty::TyExpression { _ => { errors.push(CompileError::NotAnAbi { span: abi_name.span(), - actually_is: abi.friendly_name(), + actually_is: abi.friendly_type_name(), }); return err(warnings, errors); } @@ -1315,7 +1315,7 @@ impl ty::TyExpression { a => { errors.push(CompileError::NotAnAbi { span: abi_name.span(), - actually_is: a.friendly_name(), + actually_is: a.friendly_type_name(), }); return err(warnings, errors); } diff --git a/sway-lsp/src/capabilities/hover.rs b/sway-lsp/src/capabilities/hover.rs index afdeec3cdc2..fb092234b00 100644 --- a/sway-lsp/src/capabilities/hover.rs +++ b/sway-lsp/src/capabilities/hover.rs @@ -143,7 +143,7 @@ fn hover_format(engines: Engines<'_>, token: &Token, ident: &Ident) -> lsp_types .map(|struct_decl| { format_visibility_hover( struct_decl.visibility, - decl.friendly_name(), + decl.friendly_type_name(), &token_name, ) }) @@ -153,7 +153,7 @@ fn hover_format(engines: Engines<'_>, token: &Token, ident: &Ident) -> lsp_types .map(|trait_decl| { format_visibility_hover( trait_decl.visibility, - decl.friendly_name(), + decl.friendly_type_name(), &token_name, ) }) @@ -163,7 +163,7 @@ fn hover_format(engines: Engines<'_>, token: &Token, ident: &Ident) -> lsp_types .map(|enum_decl| { format_visibility_hover( enum_decl.visibility, - decl.friendly_name(), + decl.friendly_type_name(), &token_name, ) }) From d40740112e025fcac533dad122549b91480781f4 Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Tue, 24 Jan 2023 16:28:40 +0000 Subject: [PATCH 2/8] Improve debugging printing for impl trait methods CFG nodes. --- .../control_flow_analysis/flow_graph/mod.rs | 4 +--- .../language/ty/declaration/declaration.rs | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/sway-core/src/control_flow_analysis/flow_graph/mod.rs b/sway-core/src/control_flow_analysis/flow_graph/mod.rs index 5c60d37e2c6..f2e7f59fcb3 100644 --- a/sway-core/src/control_flow_analysis/flow_graph/mod.rs +++ b/sway-core/src/control_flow_analysis/flow_graph/mod.rs @@ -146,9 +146,7 @@ impl<'cfg> std::fmt::Debug for ControlFlowGraphNode<'cfg> { if let Some(implementing_type) = method.implementing_type { format!( "Method {}.{}", - implementing_type - .get_decl_ident(decl_engines) - .map_or(String::from(""), |f| f.as_str().to_string()), + implementing_type.friendly_name(engines), method_name.as_str() ) } else { diff --git a/sway-core/src/language/ty/declaration/declaration.rs b/sway-core/src/language/ty/declaration/declaration.rs index b289186ee00..5ba91094029 100644 --- a/sway-core/src/language/ty/declaration/declaration.rs +++ b/sway-core/src/language/ty/declaration/declaration.rs @@ -466,6 +466,29 @@ impl TyDeclaration { } } + /// friendly type name string used for error reporting. + pub fn friendly_name(&self, engines: &Engines) -> String { + use TyDeclaration::*; + let decl_engine = engines.de(); + let type_engine = engines.te(); + match self { + ImplTrait(decl_id) => { + let decl = decl_engine + .get_impl_trait(decl_id.clone(), &Span::dummy()) + .unwrap(); + let implementing_for_type_id = type_engine.get(decl.implementing_for_type_id); + format!( + "{} for {}", + self.get_decl_ident(decl_engine) + .map_or(String::from(""), |f| f.as_str().to_string()), + implementing_for_type_id.json_abi_str(type_engine) + ) + } + _ => self + .get_decl_ident(decl_engine) + .map_or(String::from(""), |f| f.as_str().to_string()), + } + } /// friendly type name string used for error reporting. pub fn friendly_type_name(&self) -> &'static str { From ef1e8f6374831acfbfc5a01226dc16bb3efa85a0 Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Tue, 24 Jan 2023 16:57:29 +0000 Subject: [PATCH 3/8] Refactor AST node entry point analysis. --- .../dead_code_analysis.rs | 92 +++---------------- sway-core/src/language/ty/ast_node.rs | 74 ++++++++++++++- 2 files changed, 82 insertions(+), 84 deletions(-) diff --git a/sway-core/src/control_flow_analysis/dead_code_analysis.rs b/sway-core/src/control_flow_analysis/dead_code_analysis.rs index 9c40de5371b..2ee684dfdd5 100644 --- a/sway-core/src/control_flow_analysis/dead_code_analysis.rs +++ b/sway-core/src/control_flow_analysis/dead_code_analysis.rs @@ -150,100 +150,30 @@ impl<'cfg> ControlFlowGraph<'cfg> { leaves = l_leaves; } - graph.entry_points = entry_points(decl_engine, tree_type, &graph.graph)?; + graph.entry_points = collect_entry_points(decl_engine, tree_type, &graph.graph)?; Ok(()) } } /// Collect all entry points into the graph based on the tree type. -fn entry_points( +fn collect_entry_points( decl_engine: &DeclEngine, tree_type: &TreeType, graph: &flow_graph::Graph, ) -> Result, CompileError> { let mut entry_points = vec![]; - match tree_type { - TreeType::Predicate | TreeType::Script => { - // Predicates and scripts have main and test functions as entry points. - for i in graph.node_indices() { - match &graph[i] { - ControlFlowGraphNode::OrganizationalDominator(_) => continue, - ControlFlowGraphNode::ProgramNode(ty::TyAstNode { - span, - content: - ty::TyAstNodeContent::Declaration(ty::TyDeclaration::FunctionDeclaration( - decl_id, - )), - .. - }) => { - let decl = decl_engine.get_function(decl_id.clone(), span)?; - if !decl.is_entry() { - continue; - } - } - _ => continue, - }; - entry_points.push(i); - } - } - TreeType::Contract | TreeType::Library { .. } => { - for i in graph.node_indices() { - let is_entry = match &graph[i] { - ControlFlowGraphNode::OrganizationalDominator(_) => continue, - ControlFlowGraphNode::ProgramNode(ty::TyAstNode { - content: - ty::TyAstNodeContent::Declaration(ty::TyDeclaration::FunctionDeclaration( - decl_id, - )), - .. - }) => { - let decl = decl_engine.get_function(decl_id.clone(), &decl_id.span())?; - decl.visibility == Visibility::Public || decl.is_test() - } - ControlFlowGraphNode::ProgramNode(ty::TyAstNode { - content: - ty::TyAstNodeContent::Declaration(ty::TyDeclaration::TraitDeclaration( - decl_id, - )), - .. - }) => decl_engine - .get_trait(decl_id.clone(), &decl_id.span())? - .visibility - .is_public(), - ControlFlowGraphNode::ProgramNode(ty::TyAstNode { - content: - ty::TyAstNodeContent::Declaration(ty::TyDeclaration::StructDeclaration( - decl_id, - )), - .. - }) => { - let struct_decl = - decl_engine.get_struct(decl_id.clone(), &decl_id.span())?; - struct_decl.visibility == Visibility::Public - } - ControlFlowGraphNode::ProgramNode(ty::TyAstNode { - content: - ty::TyAstNodeContent::Declaration(ty::TyDeclaration::ImplTrait { .. }), - .. - }) => true, - ControlFlowGraphNode::ProgramNode(ty::TyAstNode { - content: - ty::TyAstNodeContent::Declaration(ty::TyDeclaration::ConstantDeclaration( - decl_id, - )), - .. - }) => { - let decl = decl_engine.get_constant(decl_id.clone(), &decl_id.span())?; - decl.visibility.is_public() - } - _ => continue, - }; - if is_entry { - entry_points.push(i); - } + for i in graph.node_indices() { + let is_entry = match &graph[i] { + ControlFlowGraphNode::ProgramNode(node) => { + node.is_entry_point(decl_engine, tree_type)? } + _ => false, + }; + if is_entry { + entry_points.push(i); } } + Ok(entry_points) } diff --git a/sway-core/src/language/ty/ast_node.rs b/sway-core/src/language/ty/ast_node.rs index f6dca2a654c..32a99899da5 100644 --- a/sway-core/src/language/ty/ast_node.rs +++ b/sway-core/src/language/ty/ast_node.rs @@ -1,10 +1,16 @@ use std::fmt::{self, Debug}; -use sway_types::{Ident, Span}; +use sway_error::error::CompileError; +use sway_types::{Ident, Span, Spanned}; use crate::{ - decl_engine::*, engine_threading::*, error::*, language::ty::*, transform::AttributeKind, - type_system::*, types::DeterministicallyAborts, + decl_engine::*, + engine_threading::*, + error::*, + language::{parsed::TreeType, ty::*, Visibility}, + transform::AttributeKind, + type_system::*, + types::DeterministicallyAborts, }; pub trait GetDeclIdent { @@ -195,6 +201,68 @@ impl TyAstNode { } } + pub(crate) fn is_entry_point( + &self, + decl_engine: &DeclEngine, + tree_type: &TreeType, + ) -> Result { + match tree_type { + TreeType::Predicate | TreeType::Script => { + // Predicates and scripts have main and test functions as entry points. + match self { + TyAstNode { + span, + content: + TyAstNodeContent::Declaration(TyDeclaration::FunctionDeclaration(decl_id)), + .. + } => { + let decl = decl_engine.get_function(decl_id.clone(), span)?; + Ok(decl.is_entry()) + } + _ => Ok(false), + } + } + TreeType::Contract | TreeType::Library { .. } => match self { + TyAstNode { + content: + TyAstNodeContent::Declaration(TyDeclaration::FunctionDeclaration(decl_id)), + .. + } => { + let decl = decl_engine.get_function(decl_id.clone(), &decl_id.span())?; + Ok(decl.visibility == Visibility::Public || decl.is_test()) + } + TyAstNode { + content: TyAstNodeContent::Declaration(TyDeclaration::TraitDeclaration(decl_id)), + .. + } => Ok(decl_engine + .get_trait(decl_id.clone(), &decl_id.span())? + .visibility + .is_public()), + TyAstNode { + content: + TyAstNodeContent::Declaration(TyDeclaration::StructDeclaration(decl_id)), + .. + } => { + let struct_decl = decl_engine.get_struct(decl_id.clone(), &decl_id.span())?; + Ok(struct_decl.visibility == Visibility::Public) + } + TyAstNode { + content: TyAstNodeContent::Declaration(TyDeclaration::ImplTrait { .. }), + .. + } => Ok(true), + TyAstNode { + content: + TyAstNodeContent::Declaration(TyDeclaration::ConstantDeclaration(decl_id)), + .. + } => { + let decl = decl_engine.get_constant(decl_id.clone(), &decl_id.span())?; + Ok(decl.visibility.is_public()) + } + _ => Ok(false), + }, + } + } + pub(crate) fn type_info(&self, type_engine: &TypeEngine) -> TypeInfo { // return statement should be () match &self.content { From 6508046e4c96ac4164ab95af3628989c82bfd5a6 Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Tue, 24 Jan 2023 16:58:42 +0000 Subject: [PATCH 4/8] Process entry points last when doing DCA. --- .../src/control_flow_analysis/dead_code_analysis.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sway-core/src/control_flow_analysis/dead_code_analysis.rs b/sway-core/src/control_flow_analysis/dead_code_analysis.rs index 2ee684dfdd5..774c23e36f2 100644 --- a/sway-core/src/control_flow_analysis/dead_code_analysis.rs +++ b/sway-core/src/control_flow_analysis/dead_code_analysis.rs @@ -135,7 +135,16 @@ impl<'cfg> ControlFlowGraph<'cfg> { let decl_engine = engines.de(); let mut leaves = vec![]; let exit_node = Some(graph.add_node(engines, ("Program exit".to_string()).into())); + let mut entry_points = vec![]; + let mut non_entry_points = vec![]; for ast_entrypoint in module_nodes { + if ast_entrypoint.is_entry_point(decl_engine, tree_type)? { + entry_points.push(ast_entrypoint); + } else { + non_entry_points.push(ast_entrypoint); + } + } + for ast_entrypoint in non_entry_points.into_iter().chain(entry_points) { let (l_leaves, _new_exit_node) = connect_node( engines, ast_entrypoint, From 25245c3d10d93f02a0f55e8dc040219caf758c3d Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Wed, 1 Feb 2023 09:45:25 +0000 Subject: [PATCH 5/8] Keep track of parents during function application monomorphization. --- sway-core/src/language/ty/ast_node.rs | 4 +++ .../language/ty/declaration/declaration.rs | 18 ++++++++++++ .../ast_node/expression/typed_expression.rs | 29 ++++++++++++++----- .../typed_expression/function_application.rs | 12 ++++++-- 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/sway-core/src/language/ty/ast_node.rs b/sway-core/src/language/ty/ast_node.rs index 32a99899da5..10a149dfda0 100644 --- a/sway-core/src/language/ty/ast_node.rs +++ b/sway-core/src/language/ty/ast_node.rs @@ -17,6 +17,10 @@ pub trait GetDeclIdent { fn get_decl_ident(&self, decl_engine: &DeclEngine) -> Option; } +pub trait GetDeclId { + fn get_decl_id(&self) -> Option; +} + #[derive(Clone, Debug)] pub struct TyAstNode { pub content: TyAstNodeContent, diff --git a/sway-core/src/language/ty/declaration/declaration.rs b/sway-core/src/language/ty/declaration/declaration.rs index 5ba91094029..7095d068ba3 100644 --- a/sway-core/src/language/ty/declaration/declaration.rs +++ b/sway-core/src/language/ty/declaration/declaration.rs @@ -313,6 +313,24 @@ impl GetDeclIdent for TyDeclaration { } } +impl GetDeclId for TyDeclaration { + fn get_decl_id(&self) -> Option { + match self { + TyDeclaration::VariableDeclaration(_) => todo!("not a declaration id yet"), + TyDeclaration::ConstantDeclaration(decl) => Some(decl.clone()), + TyDeclaration::FunctionDeclaration(decl) => Some(decl.clone()), + TyDeclaration::TraitDeclaration(decl) => Some(decl.clone()), + TyDeclaration::StructDeclaration(decl) => Some(decl.clone()), + TyDeclaration::EnumDeclaration(decl) => Some(decl.clone()), + TyDeclaration::ImplTrait(decl) => Some(decl.clone()), + TyDeclaration::AbiDeclaration(decl) => Some(decl.clone()), + TyDeclaration::GenericTypeForFunctionScope { .. } => None, + TyDeclaration::ErrorRecovery(_) => None, + TyDeclaration::StorageDeclaration(_decl) => None, + } + } +} + impl TyDeclaration { /// Retrieves the declaration as an enum declaration. /// diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index 2a36b8fbe6f..69c508d178a 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -19,7 +19,11 @@ pub(crate) use self::{ use crate::{ asm_lang::{virtual_ops::VirtualOp, virtual_register::VirtualRegister}, error::*, - language::{parsed::*, ty, *}, + language::{ + parsed::*, + ty::{self, GetDeclId}, + *, + }, semantic_analysis::*, transform::to_parsed_lang::type_name_to_type_info_opt, type_system::*, @@ -484,7 +488,7 @@ impl ty::TyExpression { ); // check that the decl is a function decl - let function_decl = check!( + let _ = check!( unknown_decl.expect_function(decl_engine, &span), return err(warnings, errors), warnings, @@ -493,7 +497,7 @@ impl ty::TyExpression { instantiate_function_application( ctx, - function_decl, + unknown_decl.get_decl_id().unwrap(), call_path_binding, Some(arguments), span, @@ -1107,14 +1111,24 @@ impl ty::TyExpression { // Check if this could be a function let mut function_probe_warnings = Vec::new(); let mut function_probe_errors = Vec::new(); - let maybe_function = { + + let maybe_function_decl = { let mut call_path_binding = unknown_call_path_binding.clone(); TypeBinding::type_check_with_ident(&mut call_path_binding, ctx.by_ref()) - .flat_map(|unknown_decl| unknown_decl.expect_function(decl_engine, &span)) .ok(&mut function_probe_warnings, &mut function_probe_errors) .map(|func_decl| (func_decl, call_path_binding)) }; + let maybe_function = { + maybe_function_decl.clone().and_then(|f| { + let (fn_decl, call_path_binding) = f; + fn_decl + .expect_function(decl_engine, &span) + .ok(&mut function_probe_warnings, &mut function_probe_errors) + .map(|func_decl| (func_decl, call_path_binding)) + }) + }; + // Check if this could be an enum let mut enum_probe_warnings = vec![]; let mut enum_probe_errors = vec![]; @@ -1169,11 +1183,12 @@ impl ty::TyExpression { errors ) } - (false, Some((func_decl, call_path_binding)), None, None) => { + (false, Some((_func_decl, call_path_binding)), None, None) => { warnings.append(&mut function_probe_warnings); errors.append(&mut function_probe_errors); + let decl_id = maybe_function_decl.and_then(|d| d.0.get_decl_id()).unwrap(); check!( - instantiate_function_application(ctx, func_decl, call_path_binding, args, span), + instantiate_function_application(ctx, decl_id, call_path_binding, args, span), return err(warnings, errors), warnings, errors diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs index 2a47487cb75..fd00a3fcff4 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs @@ -1,5 +1,5 @@ use crate::{ - decl_engine::ReplaceDecls, + decl_engine::{DeclId, ReplaceDecls}, error::*, language::{ty, *}, semantic_analysis::{ast_node::*, TypeCheckContext}, @@ -11,7 +11,7 @@ use sway_types::Spanned; #[allow(clippy::too_many_arguments)] pub(crate) fn instantiate_function_application( mut ctx: TypeCheckContext, - mut function_decl: ty::TyFunctionDeclaration, + function_decl_id: DeclId, call_path_binding: TypeBinding, arguments: Option>, span: Span, @@ -22,6 +22,10 @@ pub(crate) fn instantiate_function_application( let decl_engine = ctx.decl_engine; let engines = ctx.engines(); + let mut function_decl = decl_engine + .get_function(function_decl_id.clone(), &span) + .unwrap(); + if arguments.is_none() { errors.push(CompileError::MissingParenthesesForFunction { method_name: call_path_binding.inner.suffix.clone(), @@ -86,7 +90,9 @@ pub(crate) fn instantiate_function_application( ); function_decl.replace_decls(&decl_mapping, engines); let return_type = function_decl.return_type; - let new_decl_id = decl_engine.insert(function_decl); + let new_decl_id = decl_engine + .insert(function_decl) + .with_parent(decl_engine, function_decl_id); let exp = ty::TyExpression { expression: ty::TyExpressionVariant::FunctionApplication { From d0b6de5b0d3a6db8841a37ecc5cf897532e93a0d Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Tue, 24 Jan 2023 19:01:07 +0000 Subject: [PATCH 6/8] Fix DCA for generic functions. Closes https://github.com/FuelLabs/sway/issues/3671. --- .../analyze_return_paths.rs | 6 +-- .../dead_code_analysis.rs | 54 ++++++++++++++----- .../flow_graph/namespace.rs | 27 +++++++--- .../src/language/ty/declaration/function.rs | 19 +++++++ .../dca/generic_fn_trait_contraint/Forc.lock | 3 ++ .../dca/generic_fn_trait_contraint/Forc.toml | 6 +++ .../generic_fn_trait_contraint/src/main.sw | 18 +++++++ .../dca/generic_fn_trait_contraint/test.toml | 5 ++ 8 files changed, 115 insertions(+), 23 deletions(-) create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/test.toml diff --git a/sway-core/src/control_flow_analysis/analyze_return_paths.rs b/sway-core/src/control_flow_analysis/analyze_return_paths.rs index 3ea6f9b5cc9..c383c00e286 100644 --- a/sway-core/src/control_flow_analysis/analyze_return_paths.rs +++ b/sway-core/src/control_flow_analysis/analyze_return_paths.rs @@ -38,7 +38,7 @@ impl<'cfg> ControlFlowGraph<'cfg> { pub(crate) fn analyze_return_paths(&self, engines: Engines<'_>) -> Vec { let mut errors = vec![]; for ( - name, + (name, _sig), FunctionNamespaceEntry { entry_point, exit_point, @@ -305,9 +305,7 @@ fn connect_typed_fn_decl<'eng: 'cfg, 'cfg>( .to_typeinfo(fn_decl.return_type, &fn_decl.return_type_span) .unwrap_or_else(|_| TypeInfo::Tuple(Vec::new())), }; - graph - .namespace - .insert_function(fn_decl.name.clone(), namespace_entry); + graph.namespace.insert_function(fn_decl, namespace_entry); Ok(()) } diff --git a/sway-core/src/control_flow_analysis/dead_code_analysis.rs b/sway-core/src/control_flow_analysis/dead_code_analysis.rs index 774c23e36f2..73f70b25b01 100644 --- a/sway-core/src/control_flow_analysis/dead_code_analysis.rs +++ b/sway-core/src/control_flow_analysis/dead_code_analysis.rs @@ -152,9 +152,7 @@ impl<'cfg> ControlFlowGraph<'cfg> { &leaves, exit_node, tree_type, - NodeConnectionOptions { - force_struct_fields_connection: false, - }, + NodeConnectionOptions::default(), )?; leaves = l_leaves; @@ -188,7 +186,7 @@ fn collect_entry_points( /// This struct is used to pass node connection further down the tree as /// we are processing AST nodes. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] struct NodeConnectionOptions { /// When this is enabled, connect struct fields to the struct itself, /// thus making all struct fields considered as being used in the graph. @@ -689,9 +687,7 @@ fn connect_typed_fn_decl<'eng: 'cfg, 'cfg>( return_type: ty, }; - graph - .namespace - .insert_function(fn_decl.name.clone(), namespace_entry); + graph.namespace.insert_function(fn_decl, namespace_entry); connect_fn_params_struct_enums(engines, fn_decl, graph, entry_node)?; Ok(()) @@ -827,11 +823,45 @@ fn connect_expression<'eng: 'cfg, 'cfg>( } => { let fn_decl = decl_engine.get_function(function_decl_id.clone(), &expression_span)?; let mut is_external = false; + + // in the case of monomorphized functions, first check if we already have a node for + // it in the namespace. if not then we need to check to see if the namespace contains + // the decl id parents (the original generic non monomorphized decl id). + let mut exists = false; + let parents = decl_engine.find_all_parents(engines, function_decl_id.clone()); + for parent in parents { + if let Ok(parent) = decl_engine.get_function(parent.clone(), &expression_span) { + exists |= graph.namespace.get_function(&parent).is_some(); + } + } + // find the function in the namespace - let (fn_entrypoint, fn_exit_point) = graph - .namespace - .get_function(&fn_decl.name) - .cloned() + let fn_namespace_entry = graph.namespace.get_function(&fn_decl).cloned(); + + let mut leaves = leaves.to_vec(); + + // if the parent node exists in this module, then add the monomorphized version + // to the graph. + if fn_namespace_entry.is_none() && exists { + let (l_leaves, _new_exit_node) = connect_node( + engines, + &ty::TyAstNode { + content: ty::TyAstNodeContent::Declaration( + ty::TyDeclaration::FunctionDeclaration(function_decl_id.clone()), + ), + span: expression_span.clone(), + }, + graph, + &leaves, + exit_node, + tree_type, + NodeConnectionOptions::default(), + )?; + + leaves = l_leaves; + } + + let (fn_entrypoint, fn_exit_point) = fn_namespace_entry .map( |FunctionNamespaceEntry { entry_point, @@ -863,7 +893,7 @@ fn connect_expression<'eng: 'cfg, 'cfg>( } for leaf in leaves { - graph.add_edge(*leaf, fn_entrypoint, label.into()); + graph.add_edge(leaf, fn_entrypoint, label.into()); } // save the existing options value to restore after handling the arguments diff --git a/sway-core/src/control_flow_analysis/flow_graph/namespace.rs b/sway-core/src/control_flow_analysis/flow_graph/namespace.rs index 8b71586c9c7..1418b601daa 100644 --- a/sway-core/src/control_flow_analysis/flow_graph/namespace.rs +++ b/sway-core/src/control_flow_analysis/flow_graph/namespace.rs @@ -1,6 +1,9 @@ use super::{EntryPoint, ExitPoint}; use crate::{ - language::{ty, CallPath}, + language::{ + ty::{self, TyFunctionDeclaration, TyFunctionSig}, + CallPath, + }, type_system::TypeInfo, Ident, }; @@ -32,7 +35,7 @@ pub(crate) struct StructNamespaceEntry { /// of scope at this point, as that would have been caught earlier and aborted the compilation /// process. pub struct ControlFlowNamespace { - pub(crate) function_namespace: HashMap, + pub(crate) function_namespace: HashMap<(IdentUnique, TyFunctionSig), FunctionNamespaceEntry>, pub(crate) enum_namespace: HashMap)>, pub(crate) trait_namespace: HashMap, /// This is a mapping from trait name to method names and their node indexes @@ -45,13 +48,23 @@ pub struct ControlFlowNamespace { } impl ControlFlowNamespace { - pub(crate) fn get_function(&self, ident: &Ident) -> Option<&FunctionNamespaceEntry> { - let ident: IdentUnique = ident.into(); - self.function_namespace.get(&ident) + pub(crate) fn get_function( + &self, + fn_decl: &TyFunctionDeclaration, + ) -> Option<&FunctionNamespaceEntry> { + let ident: IdentUnique = fn_decl.name.clone().into(); + self.function_namespace + .get(&(ident, TyFunctionSig::from_fn_decl(fn_decl))) } - pub(crate) fn insert_function(&mut self, ident: Ident, entry: FunctionNamespaceEntry) { + pub(crate) fn insert_function( + &mut self, + fn_decl: &ty::TyFunctionDeclaration, + entry: FunctionNamespaceEntry, + ) { + let ident = &fn_decl.name; let ident: IdentUnique = ident.into(); - self.function_namespace.insert(ident, entry); + self.function_namespace + .insert((ident, TyFunctionSig::from_fn_decl(fn_decl)), entry); } pub(crate) fn get_constant(&self, ident: &Ident) -> Option<&NodeIndex> { self.const_namespace.get(ident) diff --git a/sway-core/src/language/ty/declaration/function.rs b/sway-core/src/language/ty/declaration/function.rs index c1831376051..2c7cc9e224f 100644 --- a/sway-core/src/language/ty/declaration/function.rs +++ b/sway-core/src/language/ty/declaration/function.rs @@ -372,3 +372,22 @@ impl TyFunctionParameter { self.name.as_str() == "self" } } + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct TyFunctionSig { + pub return_type: TypeId, + pub parameters: Vec, +} + +impl TyFunctionSig { + pub fn from_fn_decl(fn_decl: &TyFunctionDeclaration) -> Self { + Self { + return_type: fn_decl.return_type, + parameters: fn_decl + .parameters + .iter() + .map(|p| p.type_id) + .collect::>(), + } + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/Forc.lock new file mode 100644 index 00000000000..5b775562f00 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = 'impl_trait_single' +source = 'member' diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/Forc.toml new file mode 100644 index 00000000000..bba3d7ba22a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/Forc.toml @@ -0,0 +1,6 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "impl_trait_single" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/src/main.sw new file mode 100644 index 00000000000..c39ebe16f31 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/src/main.sw @@ -0,0 +1,18 @@ +script; + +pub trait MyEq { + fn my_eq(self, other: Self); +} + +impl MyEq for u64 { + fn my_eq(self, other: Self) { + } +} + +fn test_my_eq(x: T, y: T) where T: MyEq { + x.my_eq(y) +} + +fn main() { + test_my_eq(42, 42); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/test.toml new file mode 100644 index 00000000000..50bf5bf84ce --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/dca/generic_fn_trait_contraint/test.toml @@ -0,0 +1,5 @@ +category = "compile" + +# not: $()This declaration is never used + +# not: $()This trait is never implemented From 24c98b66881a206e71df77ef20b854f83dd63a76 Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Mon, 6 Feb 2023 14:17:24 +0000 Subject: [PATCH 7/8] Improve doc comments for friendly_name/friendly_type_name. --- sway-core/src/language/ty/declaration/declaration.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sway-core/src/language/ty/declaration/declaration.rs b/sway-core/src/language/ty/declaration/declaration.rs index 7095d068ba3..3e48af45a14 100644 --- a/sway-core/src/language/ty/declaration/declaration.rs +++ b/sway-core/src/language/ty/declaration/declaration.rs @@ -484,7 +484,8 @@ impl TyDeclaration { } } - /// friendly type name string used for error reporting. + /// friendly name string used for error reporting, + /// which consists of the the identifier for the declaration. pub fn friendly_name(&self, engines: &Engines) -> String { use TyDeclaration::*; let decl_engine = engines.de(); @@ -508,7 +509,8 @@ impl TyDeclaration { } } - /// friendly type name string used for error reporting. + /// friendly type name string used for error reporting, + /// which consists of the type name of the declaration AST node. pub fn friendly_type_name(&self) -> &'static str { use TyDeclaration::*; match self { From 46bcc7bb59ce9f026767d258f5ba868fc5a10d86 Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Mon, 6 Feb 2023 14:34:19 +0000 Subject: [PATCH 8/8] Clean up function matching code in typed_expression.rs. --- .../ast_node/expression/typed_expression.rs | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index 69c508d178a..e0726f76202 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -1112,21 +1112,15 @@ impl ty::TyExpression { let mut function_probe_warnings = Vec::new(); let mut function_probe_errors = Vec::new(); - let maybe_function_decl = { + let maybe_function = { let mut call_path_binding = unknown_call_path_binding.clone(); TypeBinding::type_check_with_ident(&mut call_path_binding, ctx.by_ref()) .ok(&mut function_probe_warnings, &mut function_probe_errors) - .map(|func_decl| (func_decl, call_path_binding)) - }; - - let maybe_function = { - maybe_function_decl.clone().and_then(|f| { - let (fn_decl, call_path_binding) = f; - fn_decl - .expect_function(decl_engine, &span) - .ok(&mut function_probe_warnings, &mut function_probe_errors) - .map(|func_decl| (func_decl, call_path_binding)) - }) + .and_then(|decl| { + decl.expect_function(decl_engine, &span) + .ok(&mut function_probe_warnings, &mut function_probe_errors) + .map(|_s| (decl.get_decl_id().unwrap(), call_path_binding)) + }) }; // Check if this could be an enum @@ -1183,10 +1177,9 @@ impl ty::TyExpression { errors ) } - (false, Some((_func_decl, call_path_binding)), None, None) => { + (false, Some((decl_id, call_path_binding)), None, None) => { warnings.append(&mut function_probe_warnings); errors.append(&mut function_probe_errors); - let decl_id = maybe_function_decl.and_then(|d| d.0.get_decl_id()).unwrap(); check!( instantiate_function_application(ctx, decl_id, call_path_binding, args, span), return err(warnings, errors),