From 16ffc5a0bc51faead6a0dde63401a5e1c6f977c2 Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Thu, 1 Feb 2024 00:00:04 +0000 Subject: [PATCH] Refactor Declaration::ImplicitReturnExpression as Expression::ImplicitReturn. --- .../analyze_return_paths.rs | 5 +- .../dead_code_analysis.rs | 61 ++++++------------- sway-core/src/ir_generation/const_eval.rs | 50 +++++++++------ sway-core/src/ir_generation/function.rs | 28 ++++++--- .../src/language/parsed/expression/mod.rs | 6 ++ sway-core/src/language/parsed/mod.rs | 6 -- sway-core/src/language/ty/ast_node.rs | 39 +----------- .../src/language/ty/expression/expression.rs | 5 +- .../ty/expression/expression_variant.rs | 20 +++--- .../semantic_analysis/ast_node/code_block.rs | 10 ++- .../match_expression/typed/instantiate.rs | 48 ++++++++------- .../typed/typed_match_branch.rs | 35 ++++++----- .../typed/typed_match_expression.rs | 11 +++- .../ast_node/expression/typed_expression.rs | 22 ++++++- .../src/semantic_analysis/ast_node/mod.rs | 56 ++++++++++------- .../semantic_analysis/cei_pattern_analysis.rs | 10 ++- .../src/semantic_analysis/coins_analysis.rs | 1 + sway-core/src/semantic_analysis/module.rs | 1 - .../semantic_analysis/node_dependencies.rs | 5 +- .../to_parsed_lang/convert_parse_tree.rs | 18 ++++-- sway-lsp/src/traverse/parsed_tree.rs | 5 +- sway-lsp/src/traverse/typed_tree.rs | 7 ++- sway-lsp/src/utils/debug.rs | 3 +- 23 files changed, 242 insertions(+), 210 deletions(-) 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 1d797419fad..29735addb68 100644 --- a/sway-core/src/control_flow_analysis/analyze_return_paths.rs +++ b/sway-core/src/control_flow_analysis/analyze_return_paths.rs @@ -148,7 +148,10 @@ fn connect_node<'eng: 'cfg, 'cfg>( expression: ty::TyExpressionVariant::Return(..), .. }) - | ty::TyAstNodeContent::ImplicitReturnExpression(_) => { + | ty::TyAstNodeContent::Expression(ty::TyExpression { + expression: ty::TyExpressionVariant::ImplicitReturn(..), + .. + }) => { let this_index = graph.add_node(ControlFlowGraphNode::from_node(node)); for leaf_ix in leaves { graph.add_edge(*leaf_ix, this_index, "".into()); 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 f93ad83a4a4..8ba5a03dc1b 100644 --- a/sway-core/src/control_flow_analysis/dead_code_analysis.rs +++ b/sway-core/src/control_flow_analysis/dead_code_analysis.rs @@ -353,34 +353,6 @@ fn connect_node<'eng: 'cfg, 'cfg>( // let mut graph = graph.clone(); let span = node.span.clone(); Ok(match &node.content { - ty::TyAstNodeContent::ImplicitReturnExpression(expr) => { - let this_index = graph.add_node(ControlFlowGraphNode::from_node_with_parent( - node, - options.parent_node, - )); - for leaf_ix in leaves { - graph.add_edge(*leaf_ix, this_index, "".into()); - } - // evaluate the expression - - let return_contents = connect_expression( - engines, - &expr.expression, - graph, - &[this_index], - exit_node, - "", - tree_type, - expr.span.clone(), - options, - )?; - - // connect return to the exit node - if let Some(exit_node) = exit_node { - graph.add_edge(this_index, exit_node, "return".into()); - } - (return_contents, None) - } ty::TyAstNodeContent::Expression(ty::TyExpression { expression: expr_variant, span, @@ -408,7 +380,10 @@ fn connect_node<'eng: 'cfg, 'cfg>( span.clone(), options, )?, - exit_node, + match expr_variant { + ty::TyExpressionVariant::ImplicitReturn(_) => None, + _ => exit_node, + }, ) } ty::TyAstNodeContent::SideEffect(_) => (leaves.to_vec(), exit_node), @@ -1832,7 +1807,7 @@ fn connect_expression<'eng: 'cfg, 'cfg>( options, ) } - Return(exp) => { + ImplicitReturn(exp) | Return(exp) => { let this_index = graph.add_node("return entry".into()); for leaf in leaves { graph.add_edge(*leaf, this_index, "".into()); @@ -1848,15 +1823,19 @@ fn connect_expression<'eng: 'cfg, 'cfg>( exp.span.clone(), options, )?; - // TODO: is this right? Shouldn't we connect the return_contents leaves to the exit - // node? - for leaf in return_contents { - graph.add_edge(this_index, leaf, "".into()); - } - if let Some(exit_node) = exit_node { - graph.add_edge(this_index, exit_node, "return".into()); + if let Return(_) = expr_variant { + // TODO: is this right? Shouldn't we connect the return_contents leaves to the exit + // node? + for leaf in return_contents { + graph.add_edge(this_index, leaf, "".into()); + } + if let Some(exit_node) = exit_node { + graph.add_edge(this_index, exit_node, "return".into()); + Ok(vec![]) + } + } else { + Ok(return_contents) } - Ok(vec![]) } Ref(exp) | Deref(exp) => connect_expression( engines, @@ -2130,10 +2109,7 @@ fn construct_dead_code_warning_from_node( // Otherwise, this is unreachable. ty::TyAstNode { span, - content: - ty::TyAstNodeContent::ImplicitReturnExpression(_) - | ty::TyAstNodeContent::Expression(_) - | ty::TyAstNodeContent::SideEffect(_), + content: ty::TyAstNodeContent::Expression(_) | ty::TyAstNodeContent::SideEffect(_), } => CompileWarning { span: span.clone(), warning_content: Warning::UnreachableCode, @@ -2304,7 +2280,6 @@ fn allow_dead_code_ast_node(decl_engine: &DeclEngine, node: &ty::TyAstNode) -> b ty::TyDecl::StorageDecl { .. } => false, }, ty::TyAstNodeContent::Expression(_) => false, - ty::TyAstNodeContent::ImplicitReturnExpression(_) => false, ty::TyAstNodeContent::SideEffect(_) => false, ty::TyAstNodeContent::Error(_, _) => false, } diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index c75badc7358..a157da8c085 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -565,6 +565,17 @@ fn const_eval_typed_expr( }) } }, + ty::TyExpressionVariant::ImplicitReturn(e) => { + if let Ok(Some(constant)) = + const_eval_typed_expr(lookup, known_consts, e, allow_configurables) + { + Some(constant) + } else { + return Err(ConstEvalError::CannotBeEvaluatedToConst { + span: e.span.clone(), + }); + } + } // we could allow non-local control flow in pure functions, but it would // require some more work and at this point it's not clear if it is too useful // for constant initializers -- the user can always refactor their pure functions @@ -719,26 +730,29 @@ fn const_eval_codeblock( } } ty::TyAstNodeContent::Declaration(_) => Ok(None), - ty::TyAstNodeContent::Expression(e) => { - if const_eval_typed_expr(lookup, known_consts, e, allow_configurables).is_err() { - Err(ConstEvalError::CannotBeEvaluatedToConst { - span: e.span.clone(), - }) - } else { - Ok(None) + ty::TyAstNodeContent::Expression(e) => match e.expression { + ty::TyExpressionVariant::ImplicitReturn(_) => { + if let Ok(Some(constant)) = + const_eval_typed_expr(lookup, known_consts, e, allow_configurables) + { + Ok(Some(constant)) + } else { + Err(ConstEvalError::CannotBeEvaluatedToConst { + span: e.span.clone(), + }) + } } - } - ty::TyAstNodeContent::ImplicitReturnExpression(e) => { - if let Ok(Some(constant)) = - const_eval_typed_expr(lookup, known_consts, e, allow_configurables) - { - Ok(Some(constant)) - } else { - Err(ConstEvalError::CannotBeEvaluatedToConst { - span: e.span.clone(), - }) + _ => { + if const_eval_typed_expr(lookup, known_consts, e, allow_configurables).is_err() + { + Err(ConstEvalError::CannotBeEvaluatedToConst { + span: e.span.clone(), + }) + } else { + Ok(None) + } } - } + }, ty::TyAstNodeContent::SideEffect(_) => Err(ConstEvalError::CannotBeEvaluatedToConst { span: ast_node.span.clone(), }), diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index 7a5e137ad12..1cffc0d5105 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -240,18 +240,22 @@ impl<'eng> FnCompiler<'eng> { ty::TyDecl::TraitTypeDecl { .. } => unexpected_decl("trait type"), }, ty::TyAstNodeContent::Expression(te) => { - // An expression with an ignored return value... I assume. - let value = self.compile_expression_to_value(context, md_mgr, te)?; - // Terminating values should end the compilation of the block - if value.is_terminator { - Ok(Some(value)) - } else { - Ok(None) + match &te.expression { + TyExpressionVariant::ImplicitReturn(exp) => self + .compile_expression_to_value(context, md_mgr, exp) + .map(Some), + _ => { + // An expression with an ignored return value... I assume. + let value = self.compile_expression_to_value(context, md_mgr, te)?; + // Terminating values should end the compilation of the block + if value.is_terminator { + Ok(Some(value)) + } else { + Ok(None) + } + } } } - ty::TyAstNodeContent::ImplicitReturnExpression(te) => self - .compile_expression_to_value(context, md_mgr, te) - .map(Some), // a side effect can be () because it just impacts the type system/namespacing. // There should be no new IR generated. ty::TyAstNodeContent::SideEffect(_) => Ok(None), @@ -618,6 +622,10 @@ impl<'eng> FnCompiler<'eng> { ty::TyExpressionVariant::Reassignment(reassignment) => { self.compile_reassignment(context, md_mgr, reassignment, span_md_idx) } + ty::TyExpressionVariant::ImplicitReturn(_exp) => { + // TODO: This is currently handled at the top-level handler, `compile_ast_node`. + todo!(); + } ty::TyExpressionVariant::Return(exp) => { self.compile_return(context, md_mgr, exp, span_md_idx) } diff --git a/sway-core/src/language/parsed/expression/mod.rs b/sway-core/src/language/parsed/expression/mod.rs index e20cfa23540..b9a691844d9 100644 --- a/sway-core/src/language/parsed/expression/mod.rs +++ b/sway-core/src/language/parsed/expression/mod.rs @@ -307,6 +307,12 @@ pub enum ExpressionKind { Break, Continue, Reassignment(ReassignmentExpression), + /// An implicit return expression is different from a [Expression::Return] because + /// it is not a control flow item. Therefore it is a different variant. + /// + /// An implicit return expression is an [Expression] at the end of a code block which has no + /// semicolon, denoting that it is the [Expression] to be returned from that block. + ImplicitReturn(Box), Return(Box), Ref(Box), Deref(Box), diff --git a/sway-core/src/language/parsed/mod.rs b/sway-core/src/language/parsed/mod.rs index 1793f997f98..e2d39843886 100644 --- a/sway-core/src/language/parsed/mod.rs +++ b/sway-core/src/language/parsed/mod.rs @@ -52,12 +52,6 @@ pub enum AstNodeContent { Declaration(Declaration), /// Any type of expression, of which there are quite a few. See [Expression] for more details. Expression(Expression), - /// An implicit return expression is different from a [Expression::Return] because - /// it is not a control flow item. Therefore it is a different variant. - /// - /// An implicit return expression is an [Expression] at the end of a code block which has no - /// semicolon, denoting that it is the [Expression] to be returned from that block. - ImplicitReturnExpression(Expression), /// A statement of the form `mod foo::bar;` which imports/includes another source file. IncludeStatement(IncludeStatement), /// A malformed statement. diff --git a/sway-core/src/language/ty/ast_node.rs b/sway-core/src/language/ty/ast_node.rs index c7704b43b2b..c1f004c415f 100644 --- a/sway-core/src/language/ty/ast_node.rs +++ b/sway-core/src/language/ty/ast_node.rs @@ -54,7 +54,6 @@ impl DebugWithEngines for TyAstNode { match &self.content { Declaration(typed_decl) => DebugWithEngines::fmt(typed_decl, f, engines), Expression(exp) => DebugWithEngines::fmt(exp, f, engines), - ImplicitReturnExpression(exp) => write!(f, "return {:?}", engines.help_out(exp)), SideEffect(_) => f.write_str(""), Error(_, _) => f.write_str("error"), } @@ -64,9 +63,6 @@ impl DebugWithEngines for TyAstNode { impl SubstTypes for TyAstNode { fn subst_inner(&mut self, type_mapping: &TypeSubstMap, engines: &Engines) { match self.content { - TyAstNodeContent::ImplicitReturnExpression(ref mut exp) => { - exp.subst(type_mapping, engines) - } TyAstNodeContent::Declaration(ref mut decl) => decl.subst(type_mapping, engines), TyAstNodeContent::Expression(ref mut expr) => expr.subst(type_mapping, engines), TyAstNodeContent::SideEffect(_) => (), @@ -83,9 +79,6 @@ impl ReplaceDecls for TyAstNode { ctx: &mut TypeCheckContext, ) -> Result<(), ErrorEmitted> { match self.content { - TyAstNodeContent::ImplicitReturnExpression(ref mut exp) => { - exp.replace_decls(decl_mapping, handler, ctx) - } TyAstNodeContent::Declaration(TyDecl::VariableDecl(ref mut decl)) => { decl.body.replace_decls(decl_mapping, handler, ctx) } @@ -102,9 +95,6 @@ impl ReplaceDecls for TyAstNode { impl UpdateConstantExpression for TyAstNode { fn update_constant_expression(&mut self, engines: &Engines, implementing_type: &TyDecl) { match self.content { - TyAstNodeContent::ImplicitReturnExpression(ref mut expr) => { - expr.update_constant_expression(engines, implementing_type) - } TyAstNodeContent::Declaration(_) => {} TyAstNodeContent::Expression(ref mut expr) => { expr.update_constant_expression(engines, implementing_type) @@ -150,9 +140,7 @@ impl DeterministicallyAborts for TyAstNode { use TyAstNodeContent::*; match &self.content { Declaration(_) => false, - Expression(exp) | ImplicitReturnExpression(exp) => { - exp.deterministically_aborts(decl_engine, check_call_body) - } + Expression(exp) => exp.deterministically_aborts(decl_engine, check_call_body), SideEffect(_) => false, Error(_, _) => false, } @@ -172,7 +160,6 @@ impl TyAstNode { /// _only_ for explicit returns. pub(crate) fn gather_return_statements(&self) -> Vec<&TyExpression> { match &self.content { - TyAstNodeContent::ImplicitReturnExpression(ref exp) => exp.gather_return_statements(), // assignments and reassignments can happen during control flow and can abort TyAstNodeContent::Declaration(TyDecl::VariableDecl(decl)) => { decl.body.gather_return_statements() @@ -189,8 +176,7 @@ impl TyAstNode { TyAstNodeContent::Declaration(decl) => decl.visibility(decl_engine).is_public(), TyAstNodeContent::Expression(_) | TyAstNodeContent::SideEffect(_) - | TyAstNodeContent::Error(_, _) - | TyAstNodeContent::ImplicitReturnExpression(_) => false, + | TyAstNodeContent::Error(_, _) => false, } } @@ -325,9 +311,6 @@ impl TyAstNode { TyAstNodeContent::Expression(TyExpression { return_type, .. }) => { (*type_engine.get(*return_type)).clone() } - TyAstNodeContent::ImplicitReturnExpression(TyExpression { return_type, .. }) => { - (*type_engine.get(*return_type)).clone() - } TyAstNodeContent::SideEffect(_) => TypeInfo::Tuple(Vec::new()), TyAstNodeContent::Error(_, error) => TypeInfo::ErrorRecovery(*error), } @@ -395,9 +378,6 @@ impl TyAstNode { TyAstNodeContent::Expression(node) => { node.check_deprecated(engines, handler, allow_deprecated); } - TyAstNodeContent::ImplicitReturnExpression(node) => { - node.check_deprecated(engines, handler, allow_deprecated); - } TyAstNodeContent::SideEffect(_) | TyAstNodeContent::Error(_, _) => {} } } @@ -438,7 +418,6 @@ impl TyAstNode { | TyDecl::TypeAliasDecl(_) => {} }, TyAstNodeContent::Expression(_node) => {} - TyAstNodeContent::ImplicitReturnExpression(_node) => {} TyAstNodeContent::SideEffect(_) | TyAstNodeContent::Error(_, _) => {} }; Ok(()) @@ -450,7 +429,6 @@ impl TyAstNode { pub enum TyAstNodeContent { Declaration(TyDecl), Expression(TyExpression), - ImplicitReturnExpression(TyExpression), // a no-op node used for something that just issues a side effect, like an import statement. SideEffect(TySideEffect), Error(Box<[Span]>, ErrorEmitted), @@ -462,9 +440,6 @@ impl PartialEqWithEngines for TyAstNodeContent { match (self, other) { (Self::Declaration(x), Self::Declaration(y)) => x.eq(y, engines), (Self::Expression(x), Self::Expression(y)) => x.eq(y, engines), - (Self::ImplicitReturnExpression(x), Self::ImplicitReturnExpression(y)) => { - x.eq(y, engines) - } (Self::SideEffect(_), Self::SideEffect(_)) => true, _ => false, } @@ -479,7 +454,7 @@ impl HashWithEngines for TyAstNodeContent { Declaration(decl) => { decl.hash(state, engines); } - Expression(exp) | ImplicitReturnExpression(exp) => { + Expression(exp) => { exp.hash(state, engines); } SideEffect(effect) => { @@ -499,9 +474,6 @@ impl TypeCheckAnalysis for TyAstNodeContent { match self { TyAstNodeContent::Declaration(node) => node.type_check_analyze(handler, ctx)?, TyAstNodeContent::Expression(node) => node.type_check_analyze(handler, ctx)?, - TyAstNodeContent::ImplicitReturnExpression(node) => { - node.type_check_analyze(handler, ctx)? - } TyAstNodeContent::SideEffect(_) => {} TyAstNodeContent::Error(_, _) => {} } @@ -518,9 +490,6 @@ impl TypeCheckFinalization for TyAstNodeContent { match self { TyAstNodeContent::Declaration(node) => node.type_check_finalize(handler, ctx)?, TyAstNodeContent::Expression(node) => node.type_check_finalize(handler, ctx)?, - TyAstNodeContent::ImplicitReturnExpression(node) => { - node.type_check_finalize(handler, ctx)? - } TyAstNodeContent::SideEffect(_) => {} TyAstNodeContent::Error(_, _) => {} } @@ -538,7 +507,6 @@ impl CollectTypesMetadata for TyAstNodeContent { match self { Declaration(decl) => decl.collect_types_metadata(handler, ctx), Expression(expr) => expr.collect_types_metadata(handler, ctx), - ImplicitReturnExpression(expr) => expr.collect_types_metadata(handler, ctx), SideEffect(_) => Ok(vec![]), Error(_, _) => Ok(vec![]), } @@ -550,7 +518,6 @@ impl GetDeclIdent for TyAstNodeContent { match self { TyAstNodeContent::Declaration(decl) => decl.get_decl_ident(), TyAstNodeContent::Expression(_expr) => None, //expr.get_decl_ident(), - TyAstNodeContent::ImplicitReturnExpression(_expr) => None, //expr.get_decl_ident(), TyAstNodeContent::SideEffect(_) => None, TyAstNodeContent::Error(_, _) => None, } diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index c82dc35f75a..b0eba04c1bc 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -291,7 +291,9 @@ impl CollectTypesMetadata for TyExpression { res.append(&mut content.collect_types_metadata(handler, ctx)?); } } - Return(exp) => res.append(&mut exp.collect_types_metadata(handler, ctx)?), + ImplicitReturn(exp) | Return(exp) => { + res.append(&mut exp.collect_types_metadata(handler, ctx)?) + } Ref(exp) | Deref(exp) => res.append(&mut exp.collect_types_metadata(handler, ctx)?), // storage access can never be generic // variable expressions don't ever have return types themselves, they're stored in @@ -397,6 +399,7 @@ impl DeterministicallyAborts for TyExpression { Reassignment(reassignment) => reassignment .rhs .deterministically_aborts(decl_engine, check_call_body), + ImplicitReturn(exp) => exp.deterministically_aborts(decl_engine, check_call_body), // TODO: Is this correct? // I'm not sure what this function is supposed to do exactly. It's called // "deterministically_aborts" which I thought meant it checks for an abort/panic, but diff --git a/sway-core/src/language/ty/expression/expression_variant.rs b/sway-core/src/language/ty/expression/expression_variant.rs index cc4d22432c1..834bfeeddab 100644 --- a/sway-core/src/language/ty/expression/expression_variant.rs +++ b/sway-core/src/language/ty/expression/expression_variant.rs @@ -142,6 +142,7 @@ pub enum TyExpressionVariant { Break, Continue, Reassignment(Box), + ImplicitReturn(Box), Return(Box), Ref(Box), Deref(Box), @@ -600,7 +601,7 @@ impl HashWithEngines for TyExpressionVariant { Self::Reassignment(exp) => { exp.hash(state, engines); } - Self::Return(exp) => { + Self::ImplicitReturn(exp) | Self::Return(exp) => { exp.hash(state, engines); } Self::Ref(exp) | Self::Deref(exp) => { @@ -749,7 +750,7 @@ impl SubstTypes for TyExpressionVariant { Break => (), Continue => (), Reassignment(reassignment) => reassignment.subst(type_mapping, engines), - Return(stmt) => stmt.subst(type_mapping, engines), + ImplicitReturn(expr) | Return(expr) => expr.subst(type_mapping, engines), Ref(exp) | Deref(exp) => exp.subst(type_mapping, engines), } } @@ -897,7 +898,9 @@ impl ReplaceDecls for TyExpressionVariant { Reassignment(reassignment) => { reassignment.replace_decls(decl_mapping, handler, ctx)? } - Return(stmt) => stmt.replace_decls(decl_mapping, handler, ctx)?, + ImplicitReturn(expr) | Return(expr) => { + expr.replace_decls(decl_mapping, handler, ctx)? + } Ref(exp) | Deref(exp) => exp.replace_decls(decl_mapping, handler, ctx)?, } @@ -1010,7 +1013,7 @@ impl TypeCheckAnalysis for TyExpressionVariant { TyExpressionVariant::Reassignment(node) => { node.type_check_analyze(handler, ctx)?; } - TyExpressionVariant::Return(node) => { + TyExpressionVariant::ImplicitReturn(node) | TyExpressionVariant::Return(node) => { node.type_check_analyze(handler, ctx)?; } TyExpressionVariant::Ref(exp) | TyExpressionVariant::Deref(exp) => { @@ -1148,7 +1151,7 @@ impl TypeCheckFinalization for TyExpressionVariant { } node.type_check_finalize(handler, ctx)?; } - TyExpressionVariant::Return(node) => { + TyExpressionVariant::ImplicitReturn(node) | TyExpressionVariant::Return(node) => { node.type_check_finalize(handler, ctx)?; } TyExpressionVariant::Ref(exp) | TyExpressionVariant::Deref(exp) => { @@ -1255,7 +1258,9 @@ impl UpdateConstantExpression for TyExpressionVariant { Reassignment(reassignment) => { reassignment.update_constant_expression(engines, implementing_type) } - Return(stmt) => stmt.update_constant_expression(engines, implementing_type), + ImplicitReturn(expr) | Return(expr) => { + expr.update_constant_expression(engines, implementing_type) + } Ref(exp) | Deref(exp) => exp.update_constant_expression(engines, implementing_type), } } @@ -1416,7 +1421,7 @@ impl DebugWithEngines for TyExpressionVariant { } format!("reassignment to {place}") } - TyExpressionVariant::Return(exp) => { + TyExpressionVariant::ImplicitReturn(exp) | TyExpressionVariant::Return(exp) => { format!("return {:?}", engines.help_out(&**exp)) } TyExpressionVariant::Ref(exp) => { @@ -1535,6 +1540,7 @@ impl TyExpressionVariant { .collect(), TyExpressionVariant::EnumTag { exp } => exp.gather_return_statements(), TyExpressionVariant::UnsafeDowncast { exp, .. } => exp.gather_return_statements(), + TyExpressionVariant::ImplicitReturn(exp) => exp.gather_return_statements(), TyExpressionVariant::Return(exp) => { vec![exp] } diff --git a/sway-core/src/semantic_analysis/ast_node/code_block.rs b/sway-core/src/semantic_analysis/ast_node/code_block.rs index e3a2e5f8381..7a604e34e4e 100644 --- a/sway-core/src/semantic_analysis/ast_node/code_block.rs +++ b/sway-core/src/semantic_analysis/ast_node/code_block.rs @@ -41,7 +41,10 @@ impl ty::TyCodeBlock { .contents .iter() .find_map(|x| match &x.content { - TyAstNodeContent::ImplicitReturnExpression(expr) => Some(Some(expr.span.clone())), + TyAstNodeContent::Expression(ty::TyExpression { + expression: ty::TyExpressionVariant::ImplicitReturn(expr), + .. + }) => Some(Some(expr.span.clone())), _ => None, }) .flatten(); @@ -61,8 +64,9 @@ impl ty::TyCodeBlock { match node { ty::TyAstNode { content: - ty::TyAstNodeContent::ImplicitReturnExpression(ty::TyExpression { - ref return_type, + ty::TyAstNodeContent::Expression(ty::TyExpression { + expression: ty::TyExpressionVariant::ImplicitReturn(_expr), + return_type, .. }), .. diff --git a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/instantiate.rs b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/instantiate.rs index b5468d91e8c..3248fe9d6c1 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/instantiate.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/instantiate.rs @@ -106,14 +106,19 @@ impl Instantiate { /// Instantiates a [ty::TyExpressionVariant::CodeBlock] with a single /// [ty::TyAstNodeContent::ImplicitReturnExpression] that returns the `value`. pub(super) fn code_block_with_implicit_return_u64(&self, value: u64) -> ty::TyExpression { + let ret_expr = ty::TyExpression { + expression: ty::TyExpressionVariant::Literal(Literal::U64(value)), + return_type: self.u64_type, + span: self.dummy_span(), + }; ty::TyExpression { expression: ty::TyExpressionVariant::CodeBlock(ty::TyCodeBlock { whole_block_span: self.dummy_span(), contents: vec![ty::TyAstNode { - content: ty::TyAstNodeContent::ImplicitReturnExpression(ty::TyExpression { - expression: ty::TyExpressionVariant::Literal(Literal::U64(value)), - return_type: self.u64_type, - span: self.dummy_span(), + content: ty::TyAstNodeContent::Expression(ty::TyExpression { + return_type: ret_expr.return_type, + span: ret_expr.span.clone(), + expression: ty::TyExpressionVariant::ImplicitReturn(Box::new(ret_expr)), }), span: self.dummy_span(), }], @@ -129,27 +134,28 @@ impl Instantiate { &self, revert_code: u64, ) -> ty::TyExpression { + let ret_expr = ty::TyExpression { + expression: ty::TyExpressionVariant::IntrinsicFunction(ty::TyIntrinsicFunctionKind { + kind: sway_ast::Intrinsic::Revert, + arguments: vec![ty::TyExpression { + expression: ty::TyExpressionVariant::Literal(Literal::U64(revert_code)), + return_type: self.u64_type, + span: self.dummy_span(), + }], + type_arguments: vec![], + span: self.dummy_span(), + }), + return_type: self.revert_type, + span: self.dummy_span(), + }; ty::TyExpression { expression: ty::TyExpressionVariant::CodeBlock(ty::TyCodeBlock { whole_block_span: self.dummy_span(), contents: vec![ty::TyAstNode { - content: ty::TyAstNodeContent::ImplicitReturnExpression(ty::TyExpression { - expression: ty::TyExpressionVariant::IntrinsicFunction( - ty::TyIntrinsicFunctionKind { - kind: sway_ast::Intrinsic::Revert, - arguments: vec![ty::TyExpression { - expression: ty::TyExpressionVariant::Literal(Literal::U64( - revert_code, - )), - return_type: self.u64_type, - span: self.dummy_span(), - }], - type_arguments: vec![], - span: self.dummy_span(), - }, - ), - return_type: self.revert_type, - span: self.dummy_span(), + content: ty::TyAstNodeContent::Expression(ty::TyExpression { + return_type: ret_expr.return_type, + span: ret_expr.span.clone(), + expression: ty::TyExpressionVariant::ImplicitReturn(Box::new(ret_expr)), }), span: self.dummy_span(), }], diff --git a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_branch.rs b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_branch.rs index 51521d76283..521126bd389 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_branch.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_branch.rs @@ -13,7 +13,7 @@ use crate::{ }, language::{ parsed::MatchBranch, - ty::{self, MatchBranchCondition, MatchedOrVariantIndexVars}, + ty::{self, MatchBranchCondition, MatchedOrVariantIndexVars, TyExpression}, }, semantic_analysis::*, types::DeterministicallyAborts, @@ -130,15 +130,17 @@ impl ty::TyMatchBranch { expression: typed_result_expression_variant, return_type: typed_result_return_type, span: typed_result_span, - } = typed_result; + } = typed_result.clone(); match typed_result_expression_variant { ty::TyExpressionVariant::CodeBlock(ty::TyCodeBlock { mut contents, .. }) => { code_block_contents.append(&mut contents); } - typed_result_expression_variant => { + _ => { code_block_contents.push(ty::TyAstNode { - content: ty::TyAstNodeContent::ImplicitReturnExpression(ty::TyExpression { - expression: typed_result_expression_variant, + content: ty::TyAstNodeContent::Expression(TyExpression { + expression: ty::TyExpressionVariant::ImplicitReturn(Box::new( + typed_result.clone(), + )), return_type: typed_result_return_type, span: typed_result_span.clone(), }), @@ -638,16 +640,21 @@ fn instantiate_branch_condition_result_var_declarations_and_matched_or_variant_i } // Add the implicit return tuple that captures the values of the variables. + let ret_expr = ty::TyExpression { + expression: ty::TyExpressionVariant::Tuple { + fields: vars_in_alternative + .into_iter() + .map(|(_, exp)| exp) + .collect(), + }, + return_type: tuple_type, + span: instantiate.dummy_span(), + }; code_block_contents.push(ty::TyAstNode { - content: ty::TyAstNodeContent::ImplicitReturnExpression(ty::TyExpression { - expression: ty::TyExpressionVariant::Tuple { - fields: vars_in_alternative - .into_iter() - .map(|(_, exp)| exp) - .collect(), - }, - return_type: tuple_type, - span: instantiate.dummy_span(), + content: ty::TyAstNodeContent::Expression(ty::TyExpression { + return_type: ret_expr.return_type, + span: ret_expr.span.clone(), + expression: ty::TyExpressionVariant::ImplicitReturn(Box::new(ret_expr)), }), span: instantiate.dummy_span(), }); diff --git a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs index 7b35a40f90f..53702c8d931 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs @@ -3,7 +3,10 @@ use sway_types::{Span, Spanned}; use crate::{ compiler_generated::INVALID_DESUGARED_MATCHED_EXPRESSION_SIGNAL, - language::{parsed::*, ty}, + language::{ + parsed::*, + ty::{self, TyExpression}, + }, semantic_analysis::{ ast_node::expression::typed_expression::instantiate_if_expression, expression::match_expression::typed::instantiate::Instantiate, TypeCheckContext, @@ -150,7 +153,11 @@ impl ty::TyMatchExpression { } code_block_contents.push(ty::TyAstNode { - content: ty::TyAstNodeContent::ImplicitReturnExpression(if_exp), + content: ty::TyAstNodeContent::Expression(TyExpression { + return_type: if_exp.return_type, + span: if_exp.span.clone(), + expression: ty::TyExpressionVariant::ImplicitReturn(Box::new(if_exp)), + }), span: result_span.clone(), }); 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 9c92c817db6..c856dfc0684 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 @@ -145,6 +145,7 @@ impl ty::TyExpression { let engines = ctx.engines(); let expr_span = expr.span(); let span = expr_span.clone(); + let should_unify = !matches!(expr.kind, ExpressionKind::ImplicitReturn(_)); let res = match expr.kind { // We've already emitted an error for the `::Error` case. ExpressionKind::Error(_, err) => Ok(ty::TyExpression::error(err, span, engines)), @@ -376,6 +377,21 @@ impl ty::TyExpression { ExpressionKind::Reassignment(ReassignmentExpression { lhs, rhs }) => { Self::type_check_reassignment(handler, ctx.by_ref(), lhs, *rhs, span) } + ExpressionKind::ImplicitReturn(expr) => { + let ctx = ctx + .by_ref() + .with_help_text("Implicit return must match up with block's type."); + let expr_span = expr.span(); + let expr = ty::TyExpression::type_check(handler, ctx, *expr) + .unwrap_or_else(|err| ty::TyExpression::error(err, expr_span, engines)); + + let typed_expr = ty::TyExpression { + return_type: expr.return_type, + expression: ty::TyExpressionVariant::ImplicitReturn(Box::new(expr)), + span, + }; + Ok(typed_expr) + } ExpressionKind::Return(expr) => { let ctx = ctx // we use "unknown" here because return statements do not @@ -413,8 +429,10 @@ impl ty::TyExpression { Err(e) => return Err(e), }; - // if the return type cannot be cast into the annotation type then it is a type error - ctx.unify_with_type_annotation(handler, typed_expression.return_type, &expr_span); + if should_unify { + // if the return type cannot be cast into the annotation type then it is a type error + ctx.unify_with_type_annotation(handler, typed_expression.return_type, &expr_span); + } // The annotation may result in a cast, which is handled in the type engine. typed_expression.return_type = ctx diff --git a/sway-core/src/semantic_analysis/ast_node/mod.rs b/sway-core/src/semantic_analysis/ast_node/mod.rs index 28db7d698e9..fbcbb8cf8c0 100644 --- a/sway-core/src/semantic_analysis/ast_node/mod.rs +++ b/sway-core/src/semantic_analysis/ast_node/mod.rs @@ -142,41 +142,51 @@ impl ty::TyAstNode { ty::TyAstNodeContent::Declaration(ty::TyDecl::type_check(handler, ctx, decl)?) } AstNodeContent::Expression(expr) => { - let ctx = ctx - .with_type_annotation(type_engine.insert(engines, TypeInfo::Unknown, None)) - .with_help_text(""); + let mut ctx = ctx.with_help_text(""); + match expr.kind { + ExpressionKind::ImplicitReturn(_) => { + // Do not use any type annotation with implicit returns as that + // will later cause type inference errors when matching implicit block + // types. + } + _ => { + ctx = ctx.with_type_annotation(type_engine.insert( + engines, + TypeInfo::Unknown, + None, + )); + } + } let inner = ty::TyExpression::type_check(handler, ctx, expr.clone()) .unwrap_or_else(|err| ty::TyExpression::error(err, expr.span(), engines)); ty::TyAstNodeContent::Expression(inner) } - AstNodeContent::ImplicitReturnExpression(expr) => { - let ctx = - ctx.with_help_text("Implicit return must match up with block's type."); - let typed_expr = ty::TyExpression::type_check(handler, ctx, expr.clone()) - .unwrap_or_else(|err| ty::TyExpression::error(err, expr.span(), engines)); - ty::TyAstNodeContent::ImplicitReturnExpression(typed_expr) - } AstNodeContent::Error(spans, err) => ty::TyAstNodeContent::Error(spans, err), }, span: node.span, }; if let ty::TyAstNode { - content: ty::TyAstNodeContent::Expression(ty::TyExpression { .. }), + content: ty::TyAstNodeContent::Expression(ty::TyExpression { expression, .. }), .. - } = node + } = node.clone() { - if !node - .type_info(type_engine) - .can_safely_ignore(type_engine, decl_engine) - { - handler.emit_warn(CompileWarning { - warning_content: Warning::UnusedReturnValue { - r#type: engines.help_out(node.type_info(type_engine)).to_string(), - }, - span: node.span.clone(), - }) - }; + match expression { + ty::TyExpressionVariant::ImplicitReturn(_) => {} + _ => { + if !node + .type_info(type_engine) + .can_safely_ignore(type_engine, decl_engine) + { + handler.emit_warn(CompileWarning { + warning_content: Warning::UnusedReturnValue { + r#type: engines.help_out(node.type_info(type_engine)).to_string(), + }, + span: node.span.clone(), + }) + }; + } + } } Ok(node) diff --git a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs index a27af3209ac..fab9805e56d 100644 --- a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs +++ b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs @@ -178,8 +178,7 @@ fn analyze_code_block_entry( ty::TyAstNodeContent::Declaration(decl) => { analyze_codeblock_decl(engines, decl, block_name, warnings) } - ty::TyAstNodeContent::Expression(expr) - | ty::TyAstNodeContent::ImplicitReturnExpression(expr) => { + ty::TyAstNodeContent::Expression(expr) => { analyze_expression(engines, expr, block_name, warnings) } ty::TyAstNodeContent::SideEffect(_) | ty::TyAstNodeContent::Error(_, _) => HashSet::new(), @@ -303,6 +302,7 @@ fn analyze_expression( } StructFieldAccess { prefix: expr, .. } | TupleElemAccess { prefix: expr, .. } + | ImplicitReturn(expr) | Return(expr) | EnumTag { exp: expr } | UnsafeDowncast { exp: expr, .. } @@ -473,10 +473,7 @@ fn warn_after_interaction( fn effects_of_codeblock_entry(engines: &Engines, ast_node: &ty::TyAstNode) -> HashSet { match &ast_node.content { ty::TyAstNodeContent::Declaration(decl) => effects_of_codeblock_decl(engines, decl), - ty::TyAstNodeContent::Expression(expr) - | ty::TyAstNodeContent::ImplicitReturnExpression(expr) => { - effects_of_expression(engines, expr) - } + ty::TyAstNodeContent::Expression(expr) => effects_of_expression(engines, expr), ty::TyAstNodeContent::SideEffect(_) | ty::TyAstNodeContent::Error(_, _) => HashSet::new(), } } @@ -558,6 +555,7 @@ fn effects_of_expression(engines: &Engines, expr: &ty::TyExpression) -> HashSet< | TupleElemAccess { prefix: expr, .. } | EnumTag { exp: expr } | UnsafeDowncast { exp: expr, .. } + | ImplicitReturn(expr) | Return(expr) | Ref(expr) | Deref(expr) => effects_of_expression(engines, expr), diff --git a/sway-core/src/semantic_analysis/coins_analysis.rs b/sway-core/src/semantic_analysis/coins_analysis.rs index c32b8bce544..65473a099e7 100644 --- a/sway-core/src/semantic_analysis/coins_analysis.rs +++ b/sway-core/src/semantic_analysis/coins_analysis.rs @@ -81,6 +81,7 @@ pub fn possibly_nonzero_u64_expression( | Break | Continue | Reassignment(_) + | ImplicitReturn(_) | Return(_) | Ref(_) | Deref(_) => true, diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 6b2f4bd0d06..6d5ec8a2196 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -386,7 +386,6 @@ impl ty::TySubmodule { } AstNodeContent::Declaration(_) => {} AstNodeContent::Expression(_) => {} - AstNodeContent::ImplicitReturnExpression(_) => {} AstNodeContent::IncludeStatement(_) => {} AstNodeContent::Error(_, _) => {} } diff --git a/sway-core/src/semantic_analysis/node_dependencies.rs b/sway-core/src/semantic_analysis/node_dependencies.rs index 2e2df9b490e..d6728ac363e 100644 --- a/sway-core/src/semantic_analysis/node_dependencies.rs +++ b/sway-core/src/semantic_analysis/node_dependencies.rs @@ -659,7 +659,9 @@ impl Dependencies { ExpressionKind::Reassignment(reassignment) => { self.gather_from_expr(engines, &reassignment.rhs) } - ExpressionKind::Return(expr) => self.gather_from_expr(engines, expr), + ExpressionKind::ImplicitReturn(expr) | ExpressionKind::Return(expr) => { + self.gather_from_expr(engines, expr) + } ExpressionKind::Ref(expr) | ExpressionKind::Deref(expr) => { self.gather_from_expr(engines, expr) } @@ -693,7 +695,6 @@ impl Dependencies { fn gather_from_node(self, engines: &Engines, node: &AstNode) -> Self { match &node.content { AstNodeContent::Expression(expr) => self.gather_from_expr(engines, expr), - AstNodeContent::ImplicitReturnExpression(expr) => self.gather_from_expr(engines, expr), AstNodeContent::Declaration(decl) => self.gather_from_decl(engines, decl), // No deps from these guys. diff --git a/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs b/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs index 1b0c65abf6c..9164b4fa098 100644 --- a/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs +++ b/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs @@ -1626,7 +1626,10 @@ fn expr_to_ast_node( let expression = expr_to_expression(context, handler, engines, expr)?; if !is_statement { AstNode { - content: AstNodeContent::ImplicitReturnExpression(expression), + content: AstNodeContent::Expression(Expression { + kind: ExpressionKind::ImplicitReturn(Box::new(expression)), + span: span.clone(), + }), span, } } else { @@ -2867,11 +2870,14 @@ fn match_expr_to_expression( span: span.clone(), }, AstNode { - content: AstNodeContent::ImplicitReturnExpression(Expression { - kind: ExpressionKind::Match(MatchExpression { - value: Box::new(var_decl_exp), - branches, - }), + content: AstNodeContent::Expression(Expression { + kind: ExpressionKind::ImplicitReturn(Box::new(Expression { + kind: ExpressionKind::Match(MatchExpression { + value: Box::new(var_decl_exp), + branches, + }), + span: span.clone(), + })), span: span.clone(), }), span: span.clone(), diff --git a/sway-lsp/src/traverse/parsed_tree.rs b/sway-lsp/src/traverse/parsed_tree.rs index 3ea1937d784..1339ce3c94c 100644 --- a/sway-lsp/src/traverse/parsed_tree.rs +++ b/sway-lsp/src/traverse/parsed_tree.rs @@ -106,8 +106,7 @@ impl Parse for AstNode { fn parse(&self, ctx: &ParseContext) { match &self.content { AstNodeContent::Declaration(declaration) => declaration.parse(ctx), - AstNodeContent::Expression(expression) - | AstNodeContent::ImplicitReturnExpression(expression) => { + AstNodeContent::Expression(expression) => { expression.parse(ctx); } AstNodeContent::UseStatement(use_statement) => use_statement.parse(ctx), @@ -342,7 +341,7 @@ impl Parse for Expression { ExpressionKind::Reassignment(reassignment) => { reassignment.parse(ctx); } - ExpressionKind::Return(expr) => { + ExpressionKind::ImplicitReturn(expr) | ExpressionKind::Return(expr) => { expr.parse(ctx); } ExpressionKind::Ref(expr) | ExpressionKind::Deref(expr) => { diff --git a/sway-lsp/src/traverse/typed_tree.rs b/sway-lsp/src/traverse/typed_tree.rs index f20c5e4014b..5f323cd887e 100644 --- a/sway-lsp/src/traverse/typed_tree.rs +++ b/sway-lsp/src/traverse/typed_tree.rs @@ -64,8 +64,7 @@ impl Parse for ty::TyAstNode { fn parse(&self, ctx: &ParseContext) { match &self.content { ty::TyAstNodeContent::Declaration(declaration) => declaration.parse(ctx), - ty::TyAstNodeContent::Expression(expression) - | ty::TyAstNodeContent::ImplicitReturnExpression(expression) => expression.parse(ctx), + ty::TyAstNodeContent::Expression(expression) => expression.parse(ctx), ty::TyAstNodeContent::SideEffect(side_effect) => side_effect.parse(ctx), ty::TyAstNodeContent::Error(_, _) => {} }; @@ -573,7 +572,9 @@ impl Parse for ty::TyExpression { ty::TyExpressionVariant::Reassignment(reassignment) => { reassignment.parse(ctx); } - ty::TyExpressionVariant::Return(exp) => exp.parse(ctx), + ty::TyExpressionVariant::ImplicitReturn(exp) | ty::TyExpressionVariant::Return(exp) => { + exp.parse(ctx) + } ty::TyExpressionVariant::Ref(exp) | ty::TyExpressionVariant::Deref(exp) => { exp.parse(ctx) } diff --git a/sway-lsp/src/utils/debug.rs b/sway-lsp/src/utils/debug.rs index af5c90116ae..889b24f0492 100644 --- a/sway-lsp/src/utils/debug.rs +++ b/sway-lsp/src/utils/debug.rs @@ -112,8 +112,7 @@ pub(crate) fn print_decl_engine_types( } _ => format!("{declaration:#?}"), }, - ty::TyAstNodeContent::Expression(expression) - | ty::TyAstNodeContent::ImplicitReturnExpression(expression) => { + ty::TyAstNodeContent::Expression(expression) => { format!("{expression:#?}") } ty::TyAstNodeContent::SideEffect(side_effect) => format!("{side_effect:#?}"),