Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix DCA for generic functions #3878

Merged
merged 9 commits into from
Feb 7, 2023
14 changes: 7 additions & 7 deletions forc-plugins/forc-doc/src/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion forc-plugins/forc-doc/src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Box<dyn RenderBox>> { item_context.render() });
Expand Down
6 changes: 2 additions & 4 deletions sway-core/src/control_flow_analysis/analyze_return_paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl<'cfg> ControlFlowGraph<'cfg> {
pub(crate) fn analyze_return_paths(&self, engines: Engines<'_>) -> Vec<CompileError> {
let mut errors = vec![];
for (
name,
(name, _sig),
FunctionNamespaceEntry {
entry_point,
exit_point,
Expand Down Expand Up @@ -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(())
}

Expand Down
155 changes: 62 additions & 93 deletions sway-core/src/control_flow_analysis/dead_code_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,121 +135,58 @@ 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,
graph,
&leaves,
exit_node,
tree_type,
NodeConnectionOptions {
force_struct_fields_connection: false,
},
NodeConnectionOptions::default(),
)?;

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<Vec<flow_graph::EntryPoint>, 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)
}

/// 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.
Expand Down Expand Up @@ -750,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(())
Expand Down Expand Up @@ -888,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,
Expand Down Expand Up @@ -924,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
Expand Down
4 changes: 1 addition & 3 deletions sway-core/src/control_flow_analysis/flow_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
27 changes: 20 additions & 7 deletions sway-core/src/control_flow_analysis/flow_graph/namespace.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use super::{EntryPoint, ExitPoint};
use crate::{
language::{ty, CallPath},
language::{
ty::{self, TyFunctionDeclaration, TyFunctionSig},
CallPath,
},
type_system::TypeInfo,
Ident,
};
Expand Down Expand Up @@ -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<IdentUnique, FunctionNamespaceEntry>,
pub(crate) function_namespace: HashMap<(IdentUnique, TyFunctionSig), FunctionNamespaceEntry>,
pub(crate) enum_namespace: HashMap<IdentUnique, (NodeIndex, HashMap<Ident, NodeIndex>)>,
pub(crate) trait_namespace: HashMap<CallPath, NodeIndex>,
/// This is a mapping from trait name to method names and their node indexes
Expand All @@ -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)
Expand Down
Loading