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 a74ba5bc7fa..bfc75ab41dc 100644 --- a/sway-core/src/control_flow_analysis/dead_code_analysis.rs +++ b/sway-core/src/control_flow_analysis/dead_code_analysis.rs @@ -519,8 +519,7 @@ fn connect_declaration<'eng: 'cfg, 'cfg>( type_ascription, .. } = &**var_decl; - // Connect variable declaration node to its type ascription. - connect_type_id(engines, type_ascription.type_id, graph, entry_node)?; + // Connect variable declaration node to body expression. let result = connect_expression( engines, @@ -533,6 +532,14 @@ fn connect_declaration<'eng: 'cfg, 'cfg>( body.clone().span, options, ); + + if let Ok(ref vec) = result { + if !vec.is_empty() { + // Connect variable declaration node to its type ascription. + connect_type_id(engines, type_ascription.type_id, graph, entry_node)?; + } + } + // Insert variable only after connecting body.expressions // This enables: // let ptr = alloc::(0); @@ -1219,7 +1226,22 @@ fn connect_expression<'eng: 'cfg, 'cfg>( )?; } - let mut leaves = leaves.to_vec(); + let mut args_diverge = false; + for (_name, arg) in arguments { + if type_engine + .get(arg.return_type) + .is_uninhabited(engines.te(), engines.de()) + { + args_diverge = true; + } + } + + let mut param_leaves = leaves.to_vec(); + let mut leaves = if args_diverge { + vec![] + } else { + leaves.to_vec() + }; // if the parent node exists in this module, then add the monomorphized version // to the graph. @@ -1338,13 +1360,20 @@ fn connect_expression<'eng: 'cfg, 'cfg>( engines, &arg.expression, graph, - ¤t_leaf, + ¶m_leaves, exit_node, "arg eval", tree_type, span, options, )?; + + if type_engine + .get(arg.return_type) + .is_uninhabited(engines.te(), engines.de()) + { + param_leaves = vec![]; + } } options.force_struct_fields_connection = force_struct_fields_connection; @@ -1363,16 +1392,20 @@ fn connect_expression<'eng: 'cfg, 'cfg>( } } } - // the exit points get connected to an exit node for the application - if !is_external { - if let Some(exit_node) = exit_node { - graph.add_edge(fn_exit_point, exit_node, "".into()); - Ok(vec![exit_node]) + if args_diverge { + Ok(vec![]) + } else { + // the exit points get connected to an exit node for the application + if !is_external { + if let Some(exit_node) = exit_node { + graph.add_edge(fn_exit_point, exit_node, "".into()); + Ok(vec![exit_node]) + } else { + Ok(vec![fn_exit_point]) + } } else { - Ok(vec![fn_exit_point]) + Ok(vec![fn_entrypoint]) } - } else { - Ok(vec![fn_entrypoint]) } } LazyOperator { lhs, rhs, .. } => { @@ -1711,9 +1744,17 @@ fn connect_expression<'eng: 'cfg, 'cfg>( elem_type: _, contents, } => { + let mut element_diverge = false; let nodes = contents .iter() .map(|elem| { + if !element_diverge + && type_engine + .get(elem.return_type) + .is_uninhabited(engines.te(), engines.de()) + { + element_diverge = true + } connect_expression( engines, &elem.expression, @@ -1727,7 +1768,11 @@ fn connect_expression<'eng: 'cfg, 'cfg>( ) }) .collect::, _>>()?; - Ok(nodes.concat()) + if element_diverge { + Ok(vec![]) + } else { + Ok(nodes.concat()) + } } ArrayIndex { prefix, index } => { let prefix_idx = connect_expression( @@ -1843,15 +1888,21 @@ fn connect_expression<'eng: 'cfg, 'cfg>( let while_loop_exit = graph.add_node("while loop exit".to_string().into()); - // it is possible for a whole while loop to be skipped so add edge from - // beginning of while loop straight to exit - graph.add_edge( - entry, - while_loop_exit, - "condition is initially false".into(), - ); let mut leaves = vec![entry]; + if !matches!(*type_engine.get(condition.return_type), TypeInfo::Never) { + // it is possible for a whole while loop to be skipped so add edge from + // beginning of while loop straight to exit + graph.add_edge( + entry, + while_loop_exit, + "condition is initially false".into(), + ); + } else { + // As condition return type is NeverType we should not connect the remaining nodes to entry. + leaves = vec![]; + } + // handle the condition of the loop connect_expression( engines, @@ -2074,8 +2125,25 @@ fn connect_enum_instantiation<'eng: 'cfg, 'cfg>( (node_idx, node_idx) }); + let mut is_variant_unreachable = false; + if let Some(instantiator) = contents { + if engines + .te() + .get(instantiator.return_type) + .is_uninhabited(engines.te(), engines.de()) + { + is_variant_unreachable = true; + } + } + + let leaves = if is_variant_unreachable { + vec![] + } else { + leaves.to_vec() + }; + // Connects call path decl, useful for aliases. - connect_call_path_decl(engines, call_path_decl, graph, leaves)?; + connect_call_path_decl(engines, call_path_decl, graph, &leaves)?; // insert organizational nodes for instantiation of enum let enum_instantiation_entry_idx = graph.add_node("enum instantiation entry".into()); @@ -2084,7 +2152,7 @@ fn connect_enum_instantiation<'eng: 'cfg, 'cfg>( // connect to declaration node itself to show that the declaration is used graph.add_edge(enum_instantiation_entry_idx, decl_ix, "".into()); for leaf in leaves { - graph.add_edge(*leaf, enum_instantiation_entry_idx, "".into()); + graph.add_edge(leaf, enum_instantiation_entry_idx, "".into()); } // add edge from the entry of the enum instantiation to the body of the instantiation @@ -2100,15 +2168,19 @@ fn connect_enum_instantiation<'eng: 'cfg, 'cfg>( enum_decl.span.clone(), options, )?; + for leaf in instantiator_contents { graph.add_edge(leaf, enum_instantiation_exit_idx, "".into()); } } graph.add_edge(decl_ix, variant_index, "".into()); - graph.add_edge(variant_index, enum_instantiation_exit_idx, "".into()); - - Ok(vec![enum_instantiation_exit_idx]) + if !is_variant_unreachable { + graph.add_edge(variant_index, enum_instantiation_exit_idx, "".into()); + Ok(vec![enum_instantiation_exit_idx]) + } else { + Ok(vec![]) + } } /// Given a [ty::TyAstNode] that we know is not reached in the graph, construct a warning 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 ce2f2751714..c5e43be75b4 100644 --- a/sway-core/src/control_flow_analysis/flow_graph/mod.rs +++ b/sway-core/src/control_flow_analysis/flow_graph/mod.rs @@ -201,7 +201,11 @@ impl<'cfg> ControlFlowGraph<'cfg> { pub(crate) fn get_node_from_decl(&self, cfg_node: &ControlFlowGraphNode) -> Option { if let Some(ident) = cfg_node.get_decl_ident() { - self.decls.get(&ident.into()).cloned() + if !ident.span().is_dummy() { + self.decls.get(&ident.into()).cloned() + } else { + None + } } else { None } diff --git a/sway-core/src/type_system/info.rs b/sway-core/src/type_system/info.rs index 64e09511a93..d68ee4d571d 100644 --- a/sway-core/src/type_system/info.rs +++ b/sway-core/src/type_system/info.rs @@ -1014,6 +1014,7 @@ impl TypeInfo { let id_uninhabited = |id| type_engine.get(id).is_uninhabited(type_engine, decl_engine); match self { + TypeInfo::Never => true, TypeInfo::Enum(decl_ref) => decl_engine .get_enum(decl_ref) .variants @@ -1028,6 +1029,13 @@ impl TypeInfo { .iter() .any(|field_type| id_uninhabited(field_type.type_id)), TypeInfo::Array(elem_ty, length) => length.val() > 0 && id_uninhabited(elem_ty.type_id), + TypeInfo::Ptr(ty) => id_uninhabited(ty.type_id), + TypeInfo::Alias { name: _, ty } => id_uninhabited(ty.type_id), + TypeInfo::Slice(ty) => id_uninhabited(ty.type_id), + TypeInfo::Ref { + to_mutable_value: _, + referenced_type, + } => id_uninhabited(referenced_type.type_id), _ => false, } } diff --git a/sway-types/src/span.rs b/sway-types/src/span.rs index 73bff86e5c5..f71ebbf2a4d 100644 --- a/sway-types/src/span.rs +++ b/sway-types/src/span.rs @@ -209,6 +209,10 @@ impl Span { pub fn line_col(&self) -> (LineCol, LineCol) { (self.start_pos().line_col(), self.end_pos().line_col()) } + + pub fn is_dummy(&self) -> bool { + self.eq(&DUMMY_SPAN) + } } impl fmt::Debug for Span { diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index a9f9506d374..71562a7fb1b 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -299,6 +299,8 @@ impl TestContext { run_and_capture_output(|| harness::compile_to_bytes(&name, &run_config)).await; *output = out; + check_file_checker(checker, &name, output)?; + let compiled = result?; let compiled = match compiled { diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/return_in_strange_positions/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/return_in_strange_positions/src/main.sw index 29362150164..60570e9442f 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/return_in_strange_positions/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/return_in_strange_positions/src/main.sw @@ -48,7 +48,7 @@ fn in_length_2_array_first() -> u64 { fn in_length_2_array_second() -> u64 { let _ = [0, return]; - 145 // TODO: Missing unreachable warning + 145 } fn in_tuple() -> u64 { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/break_in_strange_positions/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/break_in_strange_positions/test.toml index 3e8a6c1d354..3068c5efe34 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/break_in_strange_positions/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_pass/break_in_strange_positions/test.toml @@ -1,4 +1,4 @@ category = "run" expected_result = { action = "return", value = 8193 } validate_abi = true -expected_warnings = 26 +expected_warnings = 38 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/continue_in_strange_positions/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/continue_in_strange_positions/test.toml index b914ad4ca6a..b9cc230231d 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/continue_in_strange_positions/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_pass/continue_in_strange_positions/test.toml @@ -1,4 +1,4 @@ category = "run" expected_result = { action = "return", value = 8193 } validate_abi = true -expected_warnings = 25 +expected_warnings = 36 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/test.toml index 915f7efb4b4..0ad5b84efff 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/test.toml @@ -1,4 +1,4 @@ category = "run" expected_result = { action = "return", value = 42 } validate_abi = true -expected_warnings = 27 +expected_warnings = 36 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/implicit_casting/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/implicit_casting/test.toml index 6225d0880ca..7bbc428caf0 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/implicit_casting/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/implicit_casting/test.toml @@ -2,10 +2,3 @@ category = "run" expected_result = { action = "return", value = 42 } validate_abi = true -# check: let _a: Result = Ok(5); -# nextln: $()This cast, from integer type of width sixty four to integer type of width thirty two, will lose precision. - -# check: let _b: MyStruct = MyStruct{ a:5 }; -# nextln: $()This cast, from integer type of width sixty four to integer type of width thirty two, will lose precision. - -expected_warnings = 2 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/return_in_strange_positions/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/return_in_strange_positions/src/main.sw index 2c3ec216638..ba0fcbd8ef1 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/return_in_strange_positions/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/return_in_strange_positions/src/main.sw @@ -20,34 +20,34 @@ pub enum Enum_multivariant { fn in_init() -> u64 { let _ = return 42; - 45 + 045 } fn in_array() -> u64 { let _ = [return 42, return 43]; - 145 + 1450 } // Arrays of length 1 are treated differently fn in_length_1_array() -> u64 { let _ = [return 42]; - 145 + 1451 } // The first element of an array is treated differently fn in_length_2_array_first() -> u64 { let _ = [return 42, 0]; - 145 + 1452 } // The first element of an array is treated differently fn in_length_2_array_second() -> u64 { let _ = [0, return 42]; - 145 // TODO: Missing unreachable warning + 1453 } fn in_tuple() -> u64 { @@ -90,19 +90,19 @@ fn in_while_condition() -> u64 { break; }; - 745 // TODO: Missing unreachable warning + 745 } fn in_enum() -> u64 { let _ = Enum::A((return 42, return 43)); - 845 // TODO: Missing unreachable warning + 845 } fn in_enum_multivariant() -> u64 { let _ = Enum_multivariant::B((return 42, return 43)); - 945 // TODO: Missing unreachable warning + 945 } fn helper_fun(x : u64, y : u64) -> u64 { @@ -112,7 +112,7 @@ fn helper_fun(x : u64, y : u64) -> u64 { fn in_fun_arg() -> u64 { let _ = helper_fun(return 42, return 43); - 1045 // TODO: Missing unreachable warning + 1045 } fn in_lazy_and() -> u64 { @@ -128,11 +128,11 @@ fn in_lazy_or() -> u64 { } fn in_match_scrutinee() -> u64 { - match return 42 { + let _ = match return 42 { _ => 5411, - } + }; - 1145 + 1345 } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/return_in_strange_positions/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/return_in_strange_positions/test.toml index 868bbd25b90..07f8dd2bb1d 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/return_in_strange_positions/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_pass/return_in_strange_positions/test.toml @@ -1,4 +1,87 @@ category = "run" expected_result = { action = "return", value = 8193 } validate_abi = true -expected_warnings = 17 +expected_warnings = 26 + +#check: $()pub struct S { x : u64, y : u64, } +#nextln: $()This struct field is never accessed. + +#check: $()pub struct S { x : u64, y : u64, } +#nextln: $()This struct field is never accessed. +#nextln: $()pub enum Enum + +#check: $()pub enum Enum +#nextln: $()This enum is never used. + +#check: $()pub enum Enum_multivariant +#nextln: $()This enum is never used. + +#check: $()045 +#nextln: $()This code is unreachable. + +#check: $()1450 +#nextln: $()This code is unreachable. + +#check: $()1451 +#nextln: $()This code is unreachable. + +#check: $()1452 +#nextln: $()This code is unreachable. + +#check: $()1453 +#nextln: $()This code is unreachable. + +#check: $()245 +#nextln: $()This code is unreachable. + +#check: $()345 +#nextln: $()This code is unreachable. + +#check: $()445 +#nextln: $()This code is unreachable. + +#check: $()545 +#nextln: $()This code is unreachable. + +#check: $()543 +#nextln: $()This code is unreachable. + +#check: $()345 +#nextln: $()This code is unreachable. + +#check: $()645 +#nextln: $()This code is unreachable. + +# check: $()while return 42 +# nextln: $()break; +# nextln: $()This code is unreachable. + +# check: $()745 +# nextln: $()This code is unreachable. + +#check: $()845 +#nextln: $()This code is unreachable. + +#check: $()945 +#nextln: $()This code is unreachable. + +#check: $()fn helper_fun(x : u64, y : u64) -> u64 +#nextln: $()This function is never called. + +#check: $()1045 +#nextln: $()This code is unreachable. + +#check: $()1145 +#nextln: $()This code is unreachable. + +#check: $()1245 +#nextln: $()This code is unreachable. + +#check: $()let _ = match return 42 { +#check: $()}; +#nextln: $()This code is unreachable. +#nextln: $() +#nextln: $()1345 + +#check: $()1345 +#nextln: $()This code is unreachable. \ No newline at end of file