Skip to content

Commit

Permalink
Fix DCA for used struct/enum function parameters. (FuelLabs#2468)
Browse files Browse the repository at this point in the history
This fixes DCA of struct/enum types that are used as function
parameters, but otherwise not instantiated in the module.

This is done by introducing a new graph edge between struct/enum
parameters and their respective struct/enum declarations.

Closes FuelLabs#2425.

Co-authored-by: Mohammad Fawaz <[email protected]>
  • Loading branch information
tritao and mohammadfawaz authored Aug 10, 2022
1 parent c7113d3 commit 6d18807
Show file tree
Hide file tree
Showing 36 changed files with 220 additions and 21 deletions.
40 changes: 39 additions & 1 deletion sway-core/src/control_flow_analysis/dead_code_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,14 +577,18 @@ fn connect_enum_declaration(
graph: &mut ControlFlowGraph,
entry_node: NodeIndex,
) {
graph
.namespace
.insert_enum(enum_decl.name.clone(), entry_node);

// keep a mapping of each variant
for variant in &enum_decl.variants {
let variant_index = graph.add_node(ControlFlowGraphNode::from_enum_variant(
variant,
enum_decl.visibility != Visibility::Private,
));

graph.namespace.insert_enum(
graph.namespace.insert_enum_variant(
enum_decl.name.clone(),
entry_node,
variant.name.clone(),
Expand Down Expand Up @@ -630,6 +634,40 @@ fn connect_typed_fn_decl(
graph
.namespace
.insert_function(fn_decl.name.clone(), namespace_entry);

connect_fn_params_struct_enums(fn_decl, graph, entry_node)?;
Ok(())
}

// Searches for any structs or enums types referenced by the function
// parameters from the passed function declaration and connects their
// corresponding struct/enum declaration to the function entry node, thus
// making sure they are considered used by the DCA pass.
fn connect_fn_params_struct_enums(
fn_decl: &TypedFunctionDeclaration,
graph: &mut ControlFlowGraph,
fn_decl_entry_node: NodeIndex,
) -> Result<(), CompileError> {
for fn_param in &fn_decl.parameters {
let ty = resolve_type(fn_param.type_id, &fn_param.type_span)?;
match ty {
TypeInfo::Enum { name, .. } => {
let ty_index = match graph.namespace.find_enum(&name) {
Some(ix) => *ix,
None => graph.add_node(format!("External enum {}", name.as_str()).into()),
};
graph.add_edge(fn_decl_entry_node, ty_index, "".into());
}
TypeInfo::Struct { name, .. } => {
let ty_index = match graph.namespace.find_struct_decl(name.as_str()) {
Some(ix) => *ix,
None => graph.add_node(format!("External struct {}", name.as_str()).into()),
};
graph.add_edge(fn_decl_entry_node, ty_index, "".into());
}
_ => {}
}
}
Ok(())
}

Expand Down
9 changes: 8 additions & 1 deletion sway-core/src/control_flow_analysis/flow_graph/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ impl ControlFlowNamespace {
pub(crate) fn insert_constant(&mut self, const_name: Ident, declaration_node: NodeIndex) {
self.const_namespace.insert(const_name, declaration_node);
}
pub(crate) fn insert_enum(
pub(crate) fn insert_enum(&mut self, enum_name: Ident, enum_decl_index: NodeIndex) {
self.enum_namespace
.insert(enum_name, (enum_decl_index, HashMap::new()));
}
pub(crate) fn insert_enum_variant(
&mut self,
enum_name: Ident,
enum_decl_index: NodeIndex,
Expand All @@ -77,6 +81,9 @@ impl ControlFlowNamespace {
}
}
}
pub(crate) fn find_enum(&self, enum_name: &Ident) -> Option<&NodeIndex> {
self.enum_namespace.get(enum_name).map(|f| &f.0)
}
pub(crate) fn find_enum_variant_index(
&self,
enum_name: &Ident,
Expand Down
2 changes: 1 addition & 1 deletion sway-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ impl fmt::Display for Warning {
"This trait implementation overrides another one that was previously defined."
),
DeadDeclaration => write!(f, "This declaration is never used."),
DeadStructDeclaration => write!(f, "This struct is never instantiated."),
DeadStructDeclaration => write!(f, "This struct is never used."),
DeadFunctionDeclaration => write!(f, "This function is never called."),
UnreachableCode => write!(f, "This code is unreachable."),
DeadEnumVariant { variant_name } => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[[package]]
name = 'dca_constant_decl_expr'
name = 'constant_decl_expr'
source = 'root'
dependencies = []
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
license = "Apache-2.0"
name = "dca_constant_decl_expr"
name = "constant_decl_expr"
entry = "main.sw"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
category = "compile"
validate_abi = true

# not: const C1 = 66;
# not: -------------- This declaration is never used.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[[package]]
name = 'abi_fn_params'
source = 'root'
dependencies = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
license = "Apache-2.0"
name = "abi_fn_params"
entry = "main.sw"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
contract;

struct S {
}

enum E {
}

abi MyContract {
fn get_struct(s: S) -> S;
fn get_enum(e: E) -> E;
}

impl MyContract for Contract {
fn get_struct(s: S) -> S {
s
}

fn get_enum(e: E) -> E {
e
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
category = "compile"

# not: $()This struct is never used.
# not: $()This declaration is never used.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[[package]]
name = 'fn_params_free'
source = 'root'
dependencies = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
license = "Apache-2.0"
name = "fn_params_free"
entry = "main.sw"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
library foo;

struct S {}

pub fn free_fn(s: S) -> S {
s
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
category = "compile"

# not: $()This struct is never used.
# not: $()This declaration is never used.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[[package]]
name = 'fn_params_impl'
source = 'root'
dependencies = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
license = "Apache-2.0"
name = "fn_params_impl"
entry = "main.sw"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
library foo;

struct S {}

pub struct F {}

impl F {
pub fn free_fn(s: S) -> S {
s
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
category = "compile"

# not: $()This struct is never used.
# not: $()This declaration is never used.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[[package]]
name = 'fn_params_trait'
source = 'root'
dependencies = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
license = "Apache-2.0"
name = "fn_params_trait"
entry = "main.sw"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
library foo;

struct S {}

pub struct F {}

trait T {
fn bar(t: S) -> S; // pub by default
}

impl T for F {
fn bar(s: S) -> S {
s
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
category = "compile"

# not: $()This struct is never used.
# not: $()This declaration is never used.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[[package]]
name = 'unused_pub_free_fn'
source = 'root'
dependencies = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
license = "Apache-2.0"
name = "unused_pub_free_fn"
entry = "main.sw"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
library foo;

pub fn free_fn() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
category = "compile"

# not: $()This function is never called.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[[package]]
name = 'unused_free_fn'
source = 'root'
dependencies = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
license = "Apache-2.0"
name = "unused_free_fn"
entry = "main.sw"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
script;

fn free_fn() {}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
category = "compile"

# check: $()fn free_fn() {}
# nextln: $()This function is never called.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[[package]]
name = 'unused_struct'
source = 'root'
dependencies = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
license = "Apache-2.0"
name = "unused_struct"
entry = "main.sw"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
script;

struct S {}

fn main() {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
category = "compile"

# check: $()struct S {}
# nextln: $()This struct is never used.

This file was deleted.

0 comments on commit 6d18807

Please sign in to comment.