diff --git a/crates/cairo-lang-lowering/src/borrow_check/test_data/borrow_check b/crates/cairo-lang-lowering/src/borrow_check/test_data/borrow_check index 8498fea8afa..9f7f83ae292 100644 --- a/crates/cairo-lang-lowering/src/borrow_check/test_data/borrow_check +++ b/crates/cairo-lang-lowering/src/borrow_check/test_data/borrow_check @@ -995,20 +995,41 @@ End: blk1: Statements: - (v2: ()) <- test::foo(v1{`x`}) + (v2: core::option::Option::) <- Option::Some(v1{`maybe_consume(x)`}) End: - Goto(blk3, {v2 -> v3}) + Goto(blk3, {v2 -> v5}) blk2: Statements: - (v4: ()) <- struct_construct() + (v3: ()) <- struct_construct() + (v4: core::option::Option::) <- Option::None(v3{`maybe_consume(x)`}) End: - Goto(blk3, {v4 -> v3}) + Goto(blk3, {v4 -> v5}) blk3: Statements: End: - Return(v3) + Match(match_enum(v5{`maybe_consume(x)`}) { + Option::Some(v6) => blk4, + Option::None(v7) => blk5, + }) + +blk4: +Statements: + (v8: ()) <- test::foo(v6{`x`}) +End: + Goto(blk6, {v8 -> v9}) + +blk5: +Statements: + (v10: ()) <- struct_construct() +End: + Goto(blk6, {v10 -> v9}) + +blk6: +Statements: +End: + Return(v9) //! > lowering_diagnostics error: Variable not dropped. diff --git a/crates/cairo-lang-lowering/src/diagnostic.rs b/crates/cairo-lang-lowering/src/diagnostic.rs index ba287310350..ac88498556f 100644 --- a/crates/cairo-lang-lowering/src/diagnostic.rs +++ b/crates/cairo-lang-lowering/src/diagnostic.rs @@ -173,7 +173,7 @@ impl<'db> MatchError<'db> { "Unreachable pattern arm.".into() } (MatchDiagnostic::UnreachableMatchArm, MatchKind::IfLet) => { - "Unreachable else clause.".into() + "Unreachable clause.".into() } (MatchDiagnostic::UnreachableMatchArm, MatchKind::WhileLet(_, _)) => { unreachable!("While-let does not have two arms.") diff --git a/crates/cairo-lang-lowering/src/lower/flow_control/create_graph.rs b/crates/cairo-lang-lowering/src/lower/flow_control/create_graph.rs index b9f7b38e196..97dd28932a7 100644 --- a/crates/cairo-lang-lowering/src/lower/flow_control/create_graph.rs +++ b/crates/cairo-lang-lowering/src/lower/flow_control/create_graph.rs @@ -9,6 +9,7 @@ use super::graph::{ ArmExpr, BooleanIf, EvaluateExpr, FlowControlGraph, FlowControlGraphBuilder, FlowControlNode, NodeId, }; +use crate::diagnostic::MatchKind; use crate::lower::context::LoweringContext; mod cache; @@ -17,10 +18,10 @@ mod patterns; /// Creates a graph node for [semantic::ExprIf]. pub fn create_graph_expr_if<'db>( - ctx: &LoweringContext<'db, '_>, + ctx: &mut LoweringContext<'db, '_>, expr: &semantic::ExprIf<'db>, ) -> FlowControlGraph<'db> { - let mut graph = FlowControlGraphBuilder::default(); + let mut graph = FlowControlGraphBuilder::new(MatchKind::IfLet); // Add the `true` branch (the `if` block). let true_branch = graph.add_node(FlowControlNode::ArmExpr(ArmExpr { expr: expr.if_block })); @@ -102,16 +103,16 @@ pub fn create_graph_expr_if<'db>( } } - graph.finalize(current_node) + graph.finalize(current_node, ctx) } /// Creates a graph node for [semantic::ExprMatch]. #[allow(dead_code)] pub fn create_graph_expr_match<'db>( - ctx: &LoweringContext<'db, '_>, + ctx: &mut LoweringContext<'db, '_>, expr: &semantic::ExprMatch<'db>, ) -> FlowControlGraph<'db> { - let mut graph = FlowControlGraphBuilder::default(); + let mut graph = FlowControlGraphBuilder::new(MatchKind::Match); let matched_expr = &ctx.function_body.arenas.exprs[expr.matched_expr]; let matched_expr_location = ctx.get_location(matched_expr.stable_ptr().untyped()); @@ -164,5 +165,5 @@ pub fn create_graph_expr_match<'db>( next: match_node_id, })); - graph.finalize(root) + graph.finalize(root, ctx) } diff --git a/crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs b/crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs index 954cf5fa063..857ab8d5fc7 100644 --- a/crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs +++ b/crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs @@ -302,7 +302,9 @@ fn create_node_for_tuple_inner<'db>( ) } -/// Creates a node for matching over numeric values, using [EqualsLiteral] nodes. +/// Creates a node for matching over numeric values using a combination of [EnumMatch] and +/// [BooleanIf] nodes. +// TODO(lior): Fix doc if needed. fn create_node_for_value<'db>( params: CreateNodeParams<'db, '_, '_>, input_var: FlowControlVar, diff --git a/crates/cairo-lang-lowering/src/lower/flow_control/graph.rs b/crates/cairo-lang-lowering/src/lower/flow_control/graph.rs index 0f59c2c6f23..47bc4963c2e 100644 --- a/crates/cairo-lang-lowering/src/lower/flow_control/graph.rs +++ b/crates/cairo-lang-lowering/src/lower/flow_control/graph.rs @@ -21,12 +21,18 @@ use std::fmt::Debug; +use cairo_lang_diagnostics::DiagnosticAdded; use cairo_lang_semantic::{self as semantic, ConcreteVariant, PatternVariable}; use cairo_lang_syntax::node::ast::ExprPtr; +use cairo_lang_syntax::node::ids::SyntaxStablePtrId; use cairo_lang_utils::unordered_hash_set::UnorderedHashSet; use itertools::Itertools; +use crate::diagnostic::{ + LoweringDiagnosticKind, LoweringDiagnostics, LoweringDiagnosticsBuilder, MatchKind, +}; use crate::ids::LocationId; +use crate::lower::context::LoweringContext; /// Represents a variable in the flow control graph. #[derive(Clone, Copy, PartialEq, Eq, Hash)] @@ -228,6 +234,9 @@ pub struct FlowControlGraph<'db> { var_locations: Vec>, /// The pattern variables used by the [BindVar] nodes in the graph. pattern_vars: Vec>, + /// The kind of the expression being lowered. + /// This is used for diagnostic reporting. + kind: MatchKind<'db>, } impl<'db> FlowControlGraph<'db> { /// Returns the root node of the graph. @@ -245,6 +254,11 @@ impl<'db> FlowControlGraph<'db> { pub fn node(&self, id: NodeId) -> &FlowControlNode<'db> { &self.nodes[id.0] } + + /// Returns the kind of the expression being lowered. + pub fn kind(&self) -> MatchKind<'db> { + self.kind + } } impl<'db> Debug for FlowControlGraph<'db> { @@ -260,9 +274,27 @@ impl<'db> Debug for FlowControlGraph<'db> { pub struct FlowControlGraphBuilder<'db> { graph: FlowControlGraph<'db>, used_vars: UnorderedHashSet, + /// Current emitted diagnostics. + diagnostics: LoweringDiagnostics<'db>, } impl<'db> FlowControlGraphBuilder<'db> { + /// Constructs a new [FlowControlGraphBuilder]. + pub fn new(kind: MatchKind<'db>) -> Self { + let graph = FlowControlGraph { + nodes: Vec::new(), + var_types: Vec::new(), + var_locations: Vec::new(), + pattern_vars: Vec::new(), + kind, + }; + Self { + graph, + used_vars: UnorderedHashSet::default(), + diagnostics: LoweringDiagnostics::default(), + } + } + /// Adds a new node to the graph. Returns the new node's id. pub fn add_node(&mut self, node: FlowControlNode<'db>) -> NodeId { // Mark the input variable (if exists) as used. @@ -280,8 +312,15 @@ impl<'db> FlowControlGraphBuilder<'db> { } /// Finalizes the graph and returns the final [FlowControlGraph]. - pub fn finalize(self, root: NodeId) -> FlowControlGraph<'db> { + /// + /// Adds the reported diagnostics to the context. + pub fn finalize( + self, + root: NodeId, + ctx: &mut LoweringContext<'db, '_>, + ) -> FlowControlGraph<'db> { assert_eq!(root.0, self.graph.size() - 1, "The root must be the last node."); + ctx.diagnostics.extend(self.diagnostics.build()); self.graph } @@ -308,16 +347,14 @@ impl<'db> FlowControlGraphBuilder<'db> { pub fn var_ty(&self, input_var: FlowControlVar) -> semantic::TypeId<'db> { self.graph.var_types[input_var.0] } -} -impl<'db> Default for FlowControlGraphBuilder<'db> { - fn default() -> Self { - let graph = FlowControlGraph { - nodes: Vec::new(), - var_types: Vec::new(), - var_locations: Vec::new(), - pattern_vars: Vec::new(), - }; - Self { graph, used_vars: UnorderedHashSet::default() } + /// Reports a diagnostic. + #[expect(dead_code)] + pub fn report( + &mut self, + stable_ptr: impl Into>, + kind: LoweringDiagnosticKind<'db>, + ) -> DiagnosticAdded { + self.diagnostics.report(stable_ptr, kind) } } diff --git a/crates/cairo-lang-lowering/src/lower/flow_control/graph_test.rs b/crates/cairo-lang-lowering/src/lower/flow_control/graph_test.rs index 2045fd87e6b..f73c2df8d33 100644 --- a/crates/cairo-lang-lowering/src/lower/flow_control/graph_test.rs +++ b/crates/cairo-lang-lowering/src/lower/flow_control/graph_test.rs @@ -59,7 +59,7 @@ fn test_create_graph( let mut encapsulating_ctx = create_encapsulating_ctx(db, test_function.function_id, &test_function.signature); - let ctx = create_lowering_context( + let mut ctx = create_lowering_context( db, test_function.function_id, &test_function.signature, @@ -67,27 +67,32 @@ fn test_create_graph( ); let graph = match &expr { - semantic::Expr::If(expr) => create_graph_expr_if(&ctx, expr), - semantic::Expr::Match(expr) => create_graph_expr_match(&ctx, expr), + semantic::Expr::If(expr) => create_graph_expr_if(&mut ctx, expr), + semantic::Expr::Match(expr) => create_graph_expr_match(&mut ctx, expr), _ => { panic!("Unsupported expression: {:?}", expr.debug(&expr_formatter)); } }; - let error = verify_diagnostics_expectation(args, &semantic_diagnostics); - // Lower the graph. - let lowered_str = if args.get("skip_lowering").unwrap_or(&"false".into()) == "true" { - "".into() - } else { - let lowered = lower_graph_as_function(ctx, expr_id, &graph); - formatted_lowered(db, Some(&lowered)) - }; + let (lowered_str, lowering_diagnostics) = + if args.get("skip_lowering").unwrap_or(&"false".into()) == "true" { + ("".into(), ctx.diagnostics.build().format(db)) + } else { + let lowered = lower_graph_as_function(ctx, expr_id, &graph); + (formatted_lowered(db, Some(&lowered)), lowered.diagnostics.format(db)) + }; + + let error = verify_diagnostics_expectation( + args, + &format!("{semantic_diagnostics}{lowering_diagnostics}"), + ); TestRunnerResult { outputs: OrderedHashMap::from([ ("graph".into(), format!("{graph:?}")), ("semantic_diagnostics".into(), semantic_diagnostics), + ("lowering_diagnostics".into(), lowering_diagnostics), ("lowered".into(), lowered_str), ]), error, diff --git a/crates/cairo-lang-lowering/src/lower/flow_control/lower_graph.rs b/crates/cairo-lang-lowering/src/lower/flow_control/lower_graph.rs index cd8a5097f86..ac74bf5ebe8 100644 --- a/crates/cairo-lang-lowering/src/lower/flow_control/lower_graph.rs +++ b/crates/cairo-lang-lowering/src/lower/flow_control/lower_graph.rs @@ -6,12 +6,12 @@ use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; use super::graph::{FlowControlGraph, FlowControlVar, NodeId}; use crate::ids::LocationId; use crate::lower::block_builder::{ - BlockBuilder, SealedBlockBuilder, SealedGotoCallsite, merge_block_builders, - merge_sealed_block_builders, + BlockBuilder, SealedGotoCallsite, merge_block_builders, merge_sealed_block_builders, }; use crate::lower::context::{ LoweredExpr, LoweringContext, LoweringFlowError, LoweringResult, handle_lowering_flow_error, }; +use crate::lower::lowered_expr_to_block_scope_end; use crate::{BlockEnd, BlockId, MatchInfo, VarUsage}; mod lower_node; @@ -42,6 +42,18 @@ pub fn lower_graph<'db, 'mt>( res } +/// Information about the final result of the lowering of the graph. +enum LowerGraphResult<'db> { + /// The result is the given expression. + Expr(LoweredExpr<'db>), + /// The graph is finalized using the given [MatchInfo] on the sealed blocks from all the arms. + /// In this case, the final [LoweringResult] may be `Ok` or [LoweringFlowError::Match] (if + /// all arms panic or return). + Match(MatchInfo<'db>), + /// The flow does not continue to the callsite (for example, if all the arms panic or return). + Error(LoweringFlowError<'db>), +} + /// Helper struct for the lowering of a flow control graph. struct LowerGraphContext<'db, 'mt, 'a> { /// The lowering context. @@ -50,7 +62,7 @@ struct LowerGraphContext<'db, 'mt, 'a> { graph: &'a FlowControlGraph<'db>, /// The [BlockBuilder] for the result of the lowering, and the [MatchInfo] for its /// finalization. - result: Option<(BlockBuilder<'db>, LoweringResult<'db, MatchInfo<'db>>)>, + result: Option<(BlockBuilder<'db>, LowerGraphResult<'db>)>, /// A map from [NodeId] to all the [BlockBuilder]s that lead to it. /// When a node is visited, it creates a [BlockBuilder] for each of its child nodes /// (see [Self::assign_child_block_id]) and adds it to the map. @@ -160,7 +172,7 @@ impl<'mt, 'db, 'a> LowerGraphContext<'db, 'mt, 'a> { info: MatchInfo<'db>, ) { if id == self.effective_root { - self.result = Some((builder, Ok(info))); + self.result = Some((builder, LowerGraphResult::Match(info))); } else { builder.finalize(self.ctx, BlockEnd::Match { info }); } @@ -175,23 +187,34 @@ impl<'mt, 'db, 'a> LowerGraphContext<'db, 'mt, 'a> { err: LoweringFlowError<'db>, ) -> Maybe<()> { if id == self.effective_root { - self.result = Some((builder, Err(err))); + self.result = Some((builder, LowerGraphResult::Error(err))); } else { handle_lowering_flow_error(self.ctx, builder, err)?; } Ok(()) } - /// Adds a sealed block to the context. - /// - /// The block is an arm's block that was already sealed and should be merged with the other - /// arms. - fn add_sealed_block(&mut self, sealed_block: SealedBlockBuilder<'db>) { - // If sealed_block is `None`, ignore it as it doesn't return to the callsite and therefore - // should not be merged. - if let Some(sealed_block) = sealed_block { - self.sealed_blocks.push(sealed_block); + /// Finalizes an arm block that ends with the given `lowering_result`. + fn finalize_with_arm( + &mut self, + id: NodeId, + builder: BlockBuilder<'db>, + lowering_result: LoweringResult<'db, LoweredExpr<'db>>, + ) -> Maybe<()> { + if id == self.effective_root { + let lower_graph_result = match lowering_result { + Ok(lowered_expr) => LowerGraphResult::Expr(lowered_expr), + Err(err) => LowerGraphResult::Error(err), + }; + self.result = Some((builder, lower_graph_result)); + } else { + let sealed_block = lowered_expr_to_block_scope_end(self.ctx, builder, lowering_result)?; + if let Some(sealed_block) = sealed_block { + self.sealed_blocks.push(sealed_block); + } } + + Ok(()) } /// Finalizes the lowering of the graph. @@ -202,7 +225,7 @@ impl<'mt, 'db, 'a> LowerGraphContext<'db, 'mt, 'a> { let (builder, match_info) = self.result.take().unwrap(); match match_info { - Ok(match_info) => { + LowerGraphResult::Match(match_info) => { if let Some((new_builder, lowered_expr)) = merge_sealed_block_builders( self.ctx, self.sealed_blocks, @@ -215,7 +238,8 @@ impl<'mt, 'db, 'a> LowerGraphContext<'db, 'mt, 'a> { (Err(LoweringFlowError::Match(match_info)), builder) } } - Err(err) => (Err(err), builder), + LowerGraphResult::Error(err) => (Err(err), builder), + LowerGraphResult::Expr(lowered_expr) => (Ok(lowered_expr), builder), } } } diff --git a/crates/cairo-lang-lowering/src/lower/flow_control/lower_graph/lower_node.rs b/crates/cairo-lang-lowering/src/lower/flow_control/lower_graph/lower_node.rs index 9c5b7377972..bc3dc5552a4 100644 --- a/crates/cairo-lang-lowering/src/lower/flow_control/lower_graph/lower_node.rs +++ b/crates/cairo-lang-lowering/src/lower/flow_control/lower_graph/lower_node.rs @@ -6,6 +6,9 @@ use cairo_lang_syntax::node::TypedStablePtr; use itertools::zip_eq; use super::LowerGraphContext; +use crate::diagnostic::{ + LoweringDiagnosticKind, LoweringDiagnosticsBuilder, MatchDiagnostic, MatchError, +}; use crate::ids::SemanticFunctionIdEx; use crate::lower::block_builder::BlockBuilder; use crate::lower::context::{LoweredExpr, VarRequest}; @@ -14,22 +17,33 @@ use crate::lower::flow_control::graph::{ FlowControlNode, NodeId, }; use crate::lower::{ - generators, lower_expr_literal_to_var_usage, lower_expr_to_var_usage, lower_tail_expr, - lowered_expr_to_block_scope_end, + generators, lower_expr, lower_expr_literal_to_var_usage, lower_expr_to_var_usage, }; use crate::{MatchArm, MatchEnumInfo, MatchExternInfo, MatchInfo, VarUsage}; /// Lowers the node with the given [NodeId]. pub fn lower_node(ctx: &mut LowerGraphContext<'_, '_, '_>, id: NodeId) -> Maybe<()> { let Some(builder) = ctx.get_builder_if_reachable(id) else { + // If an [ArmExpr] node is unreachable, report an error. + // TODO(lior): If the main branch is unreachable, report an proper error. + if let FlowControlNode::ArmExpr(node) = ctx.graph.node(id) { + let stable_ptr = ctx.ctx.function_body.arenas.exprs[node.expr].stable_ptr(); + + let match_error = LoweringDiagnosticKind::MatchError(MatchError { + kind: ctx.graph.kind(), + error: MatchDiagnostic::UnreachableMatchArm, + }); + ctx.ctx.diagnostics.report(stable_ptr, match_error); + } + return Ok(()); }; match ctx.graph.node(id) { FlowControlNode::EvaluateExpr(node) => lower_evaluate_expr(ctx, id, node, builder), FlowControlNode::BooleanIf(node) => lower_boolean_if(ctx, id, node, builder), - FlowControlNode::ArmExpr(node) => lower_arm_expr(ctx, node, builder), - FlowControlNode::UnitResult => lower_unit_result(ctx, builder), + FlowControlNode::ArmExpr(node) => lower_arm_expr(ctx, id, node, builder), + FlowControlNode::UnitResult => lower_unit_result(ctx, id, builder), FlowControlNode::EnumMatch(node) => lower_enum_match(ctx, id, node, builder), FlowControlNode::EqualsLiteral(node) => lower_equals_literal(ctx, id, node, builder), FlowControlNode::BindVar(node) => lower_bind_var(ctx, id, node, builder), @@ -99,26 +113,26 @@ fn lower_boolean_if<'db>( /// Lowers an [ArmExpr] node. fn lower_arm_expr<'db>( ctx: &mut LowerGraphContext<'db, '_, '_>, + id: NodeId, node: &ArmExpr, - builder: BlockBuilder<'db>, + mut builder: BlockBuilder<'db>, ) -> Maybe<()> { - let sealed_block = lower_tail_expr(ctx.ctx, builder, node.expr)?; - ctx.add_sealed_block(sealed_block); + let lowered_expr = lower_expr(ctx.ctx, &mut builder, node.expr); + ctx.finalize_with_arm(id, builder, lowered_expr)?; Ok(()) } /// Lowers a `UnitResult` node. fn lower_unit_result<'db>( ctx: &mut LowerGraphContext<'db, '_, '_>, + id: NodeId, builder: BlockBuilder<'db>, ) -> Maybe<()> { - let sealed_block = lowered_expr_to_block_scope_end( - ctx.ctx, + ctx.finalize_with_arm( + id, builder, Ok(LoweredExpr::Tuple { exprs: vec![], location: ctx.location }), )?; - ctx.add_sealed_block(sealed_block); - Ok(()) } diff --git a/crates/cairo-lang-lowering/src/lower/flow_control/test_data/if b/crates/cairo-lang-lowering/src/lower/flow_control/test_data/if index 8feec369853..1d78f4b5901 100644 --- a/crates/cairo-lang-lowering/src/lower/flow_control/test_data/if +++ b/crates/cairo-lang-lowering/src/lower/flow_control/test_data/if @@ -21,6 +21,8 @@ Root: 3 //! > semantic_diagnostics +//! > lowering_diagnostics + //! > lowered Parameters: v0: core::bool blk0 (root): @@ -74,6 +76,8 @@ Root: 3 //! > semantic_diagnostics +//! > lowering_diagnostics + //! > lowered Parameters: v0: core::bool blk0 (root): @@ -103,85 +107,207 @@ End: //! > ========================================================================== -//! > Test let chain +//! > Test let chain with modified variables //! > test_runner_name test_create_graph(expect_diagnostics: false) //! > function_code fn foo(mut x: felt252, mut y: felt252) -> felt252 { - if let Some(_) = Some(x) && let Some(_) = Some(y) { - x + y + if let Some(z) = Some(modify(ref x)) && let Some(_) = Some(modify(ref y)) { + x + y + z } else { x + y } } +//! > module_code +extern fn modify(ref x: felt252) -> felt252 nopanic; + //! > graph -Root: 5 -0 ArmExpr { expr: ExprId(7) } -1 ArmExpr { expr: ExprId(11) } +Root: 6 +0 ArmExpr { expr: ExprId(11) } +1 ArmExpr { expr: ExprId(15) } 2 EnumMatch { matched_var: v0, variants: (NodeId(0), v1), (NodeId(1), v2)} -3 EvaluateExpr { expr: ExprId(3), var_id: v0, next: NodeId(2) } -4 EnumMatch { matched_var: v3, variants: (NodeId(3), v4), (NodeId(1), v5)} -5 EvaluateExpr { expr: ExprId(1), var_id: v3, next: NodeId(4) } +3 EvaluateExpr { expr: ExprId(5), var_id: v0, next: NodeId(2) } +4 BindVar { input: v4, output: PatternVarId(0), next: NodeId(3) } +5 EnumMatch { matched_var: v3, variants: (NodeId(4), v4), (NodeId(1), v5)} +6 EvaluateExpr { expr: ExprId(2), var_id: v3, next: NodeId(5) } //! > semantic_diagnostics +//! > lowering_diagnostics + //! > lowered Parameters: v0: core::felt252, v1: core::felt252 blk0 (root): Statements: - (v2: core::option::Option::) <- Option::Some(v0) + (v3: core::felt252, v2: core::felt252) <- test::modify(v0) + (v4: core::option::Option::) <- Option::Some(v2) End: - Match(match_enum(v2) { - Option::Some(v3) => blk1, - Option::None(v4) => blk2, + Match(match_enum(v4) { + Option::Some(v5) => blk1, + Option::None(v6) => blk2, }) blk1: Statements: - (v5: core::option::Option::) <- Option::Some(v1) + (v8: core::felt252, v7: core::felt252) <- test::modify(v1) + (v9: core::option::Option::) <- Option::Some(v7) End: - Match(match_enum(v5) { - Option::Some(v6) => blk3, - Option::None(v7) => blk4, + Match(match_enum(v9) { + Option::Some(v10) => blk3, + Option::None(v11) => blk4, }) blk2: Statements: End: - Goto(blk5, {}) + Goto(blk5, {v1 -> v12}) blk3: Statements: - (v9: core::felt252) <- core::Felt252Add::add(v0, v1) + (v14: core::felt252) <- core::Felt252Add::add(v3, v8) + (v15: core::felt252) <- core::Felt252Add::add(v14, v5) End: - Goto(blk6, {v9 -> v10}) + Goto(blk6, {v8 -> v17, v15 -> v16}) blk4: Statements: End: - Goto(blk5, {}) + Goto(blk5, {v8 -> v12}) blk5: Statements: - (v8: core::felt252) <- core::Felt252Add::add(v0, v1) + (v13: core::felt252) <- core::Felt252Add::add(v3, v12) End: - Goto(blk6, {v8 -> v10}) + Goto(blk6, {v12 -> v17, v13 -> v16}) blk6: Statements: End: - Return(v10) + Return(v16) //! > ========================================================================== -//! > Test if with panic in condition +//! > Test let chain with struct deconstruction //! > test_runner_name test_create_graph(expect_diagnostics: false) +//! > function_code +fn foo(mut x: felt252, mut y: MyStruct) -> (MyStruct, bool) { + if let Some(_) = Some(modify(ref x)) + && let Some(_) = Some(modify(ref y.a)) + && let Some(_) = Some(modify(ref y.b)) { + (y, true) + } else { + (y, false) + } +} + +//! > module_code +struct MyStruct { + a: felt252, + b: felt252, +} + +extern fn modify(ref x: felt252) -> felt252 nopanic; + +//! > graph +Root: 7 +0 ArmExpr { expr: ExprId(15) } +1 ArmExpr { expr: ExprId(20) } +2 EnumMatch { matched_var: v0, variants: (NodeId(0), v1), (NodeId(1), v2)} +3 EvaluateExpr { expr: ExprId(10), var_id: v0, next: NodeId(2) } +4 EnumMatch { matched_var: v3, variants: (NodeId(3), v4), (NodeId(1), v5)} +5 EvaluateExpr { expr: ExprId(6), var_id: v3, next: NodeId(4) } +6 EnumMatch { matched_var: v6, variants: (NodeId(5), v7), (NodeId(1), v8)} +7 EvaluateExpr { expr: ExprId(2), var_id: v6, next: NodeId(6) } + +//! > semantic_diagnostics + +//! > lowering_diagnostics + +//! > lowered +Parameters: v0: core::felt252, v1: test::MyStruct +blk0 (root): +Statements: + (v3: core::felt252, v2: core::felt252) <- test::modify(v0) + (v4: core::option::Option::) <- Option::Some(v2) +End: + Match(match_enum(v4) { + Option::Some(v5) => blk1, + Option::None(v6) => blk2, + }) + +blk1: +Statements: + (v7: core::felt252, v8: core::felt252) <- struct_destructure(v1) + (v10: core::felt252, v9: core::felt252) <- test::modify(v7) + (v11: core::option::Option::) <- Option::Some(v9) +End: + Match(match_enum(v11) { + Option::Some(v12) => blk3, + Option::None(v13) => blk4, + }) + +blk2: +Statements: + (v21: core::felt252, v22: core::felt252) <- struct_destructure(v1) +End: + Goto(blk7, {v21 -> v19, v22 -> v20}) + +blk3: +Statements: + (v15: core::felt252, v14: core::felt252) <- test::modify(v8) + (v16: core::option::Option::) <- Option::Some(v14) +End: + Match(match_enum(v16) { + Option::Some(v17) => blk5, + Option::None(v18) => blk6, + }) + +blk4: +Statements: +End: + Goto(blk7, {v10 -> v19, v8 -> v20}) + +blk5: +Statements: + (v27: ()) <- struct_construct() + (v28: core::bool) <- bool::True(v27) + (v29: test::MyStruct) <- struct_construct(v10, v15) + (v30: (test::MyStruct, core::bool)) <- struct_construct(v29, v28) +End: + Goto(blk8, {v29 -> v32, v30 -> v31}) + +blk6: +Statements: +End: + Goto(blk7, {v10 -> v19, v15 -> v20}) + +blk7: +Statements: + (v23: ()) <- struct_construct() + (v24: core::bool) <- bool::False(v23) + (v25: test::MyStruct) <- struct_construct(v19, v20) + (v26: (test::MyStruct, core::bool)) <- struct_construct(v25, v24) +End: + Goto(blk8, {v25 -> v32, v26 -> v31}) + +blk8: +Statements: +End: + Return(v31) + +//! > ========================================================================== + +//! > Test if with panic in condition + +//! > test_runner_name +test_create_graph(expect_diagnostics: true) + //! > function_code fn foo() -> felt252 { if panic!() { @@ -202,6 +328,23 @@ Root: 3 //! > semantic_diagnostics +//! > lowering_diagnostics +warning: Unreachable clause. + --> lib.cairo:4:12-6:5 + } else { + ____________^ +| 1 +| } +|_____^ + +warning: Unreachable clause. + --> lib.cairo:2:17-4:5 + if panic!() { + _________________^ +| 0 +| } else { +|_____^ + //! > lowered Parameters: blk0 (root): @@ -243,6 +386,8 @@ Root: 3 //! > semantic_diagnostics +//! > lowering_diagnostics + //! > lowered Parameters: blk0 (root): @@ -305,6 +450,8 @@ Root: 3 //! > semantic_diagnostics +//! > lowering_diagnostics + //! > lowered Parameters: blk0 (root): @@ -363,6 +510,8 @@ Root: 4 //! > semantic_diagnostics +//! > lowering_diagnostics + //! > lowered Parameters: v0: core::option::Option::> blk0 (root): @@ -407,3 +556,119 @@ blk6: Statements: End: Return(v7) + +//! > ========================================================================== + +//! > Test if let-chain with panic in condition + +//! > test_runner_name +test_create_graph(expect_diagnostics: true) + +//! > function_code +fn foo() -> felt252 { + if let Some(_) = Some(0) && panic!() { + 0 + } else { + 1 + } +} + +//! > module_code + +//! > graph +Root: 5 +0 ArmExpr { expr: ExprId(7) } +1 ArmExpr { expr: ExprId(9) } +2 BooleanIf { condition_var: v0, true_branch: NodeId(0), false_branch: NodeId(1) } +3 EvaluateExpr { expr: ExprId(5), var_id: v0, next: NodeId(2) } +4 EnumMatch { matched_var: v1, variants: (NodeId(3), v2), (NodeId(1), v3)} +5 EvaluateExpr { expr: ExprId(1), var_id: v1, next: NodeId(4) } + +//! > semantic_diagnostics + +//! > lowering_diagnostics +warning: Unreachable clause. + --> lib.cairo:2:42-4:5 + if let Some(_) = Some(0) && panic!() { + __________________________________________^ +| 0 +| } else { +|_____^ + +//! > lowered +Parameters: +blk0 (root): +Statements: + (v0: core::felt252) <- 0 + (v1: core::option::Option::) <- Option::Some(v0) +End: + Match(match_enum(v1) { + Option::Some(v2) => blk1, + Option::None(v3) => blk2, + }) + +blk1: +Statements: + (v4: core::array::Array::) <- core::array::array_new::() + (v5: core::felt252) <- 0 + (v6: core::integer::u32) <- 0 + (v7: core::byte_array::ByteArray) <- struct_construct(v4, v5, v6) + (v8: core::byte_array::ByteArray, v9: @core::byte_array::ByteArray) <- snapshot(v7) + (v10: core::never) <- core::panics::panic_with_byte_array(v9) +End: + Match(match_enum(v10) { + }) + +blk2: +Statements: + (v11: core::felt252) <- 1 +End: + Goto(blk3, {v11 -> v12}) + +blk3: +Statements: +End: + Return(v12) + +//! > ========================================================================== + +//! > Unreachable else clause. + +//! > test_runner_name +test_create_graph(expect_diagnostics: true) + +//! > function_code +fn foo(x: Option) -> felt252 { + if let Some(_) | None = x { + 0 + } else { + 1 + } +} + +//! > module_code + +//! > graph +Root: 2 +0 ArmExpr { expr: ExprId(2) } +1 ArmExpr { expr: ExprId(4) } +2 EvaluateExpr { expr: ExprId(0), var_id: v0, next: NodeId(0) } + +//! > semantic_diagnostics + +//! > lowering_diagnostics +warning: Unreachable clause. + --> lib.cairo:4:12-6:5 + } else { + ____________^ +| 1 +| } +|_____^ + +//! > lowered +Parameters: v0: core::option::Option:: +blk0 (root): +Statements: + (v1: core::felt252) <- 0 +End: + Return(v1) diff --git a/crates/cairo-lang-lowering/src/lower/flow_control/test_data/match b/crates/cairo-lang-lowering/src/lower/flow_control/test_data/match index b4c328757a4..45cf5e2d019 100644 --- a/crates/cairo-lang-lowering/src/lower/flow_control/test_data/match +++ b/crates/cairo-lang-lowering/src/lower/flow_control/test_data/match @@ -1,14 +1,15 @@ //! > Simple match //! > test_runner_name -test_create_graph(expect_diagnostics: false) +test_create_graph(expect_diagnostics: true) //! > function_code fn foo(color: Color) -> felt252 { match color { Color::Red | Color::Green(_) => 1, Color::Red(_) | Color::Blue => 2, - _ => 3, + Color::Blue | Color::Green => 3, + _ => 4, } } @@ -22,15 +23,22 @@ enum Color { } //! > graph -Root: 4 +Root: 5 0 ArmExpr { expr: ExprId(1) } 1 ArmExpr { expr: ExprId(2) } 2 ArmExpr { expr: ExprId(3) } -3 EnumMatch { matched_var: v0, variants: (NodeId(0), v1), (NodeId(0), v2), (NodeId(1), v3), (NodeId(2), v4), (NodeId(2), v5)} -4 EvaluateExpr { expr: ExprId(0), var_id: v0, next: NodeId(3) } +3 ArmExpr { expr: ExprId(4) } +4 EnumMatch { matched_var: v0, variants: (NodeId(0), v1), (NodeId(0), v2), (NodeId(1), v3), (NodeId(3), v4), (NodeId(3), v5)} +5 EvaluateExpr { expr: ExprId(0), var_id: v0, next: NodeId(4) } //! > semantic_diagnostics +//! > lowering_diagnostics +warning: Unreachable pattern arm. + --> lib.cairo:12:39 + Color::Blue | Color::Green => 3, + ^ + //! > lowered Parameters: v0: test::Color blk0 (root): @@ -72,7 +80,7 @@ End: blk6: Statements: - (v6: core::felt252) <- 3 + (v6: core::felt252) <- 4 End: Goto(blk8, {v6 -> v9}) @@ -124,6 +132,8 @@ Root: 7 //! > semantic_diagnostics +//! > lowering_diagnostics + //! > lowered Parameters: v0: test::Color blk0 (root): @@ -204,6 +214,8 @@ Root: 6 //! > semantic_diagnostics +//! > lowering_diagnostics + //! > lowered Parameters: v0: (test::Color, test::Color) blk0 (root): @@ -273,7 +285,7 @@ End: //! > Tuple inside enum //! > test_runner_name -test_create_graph(expect_diagnostics: false, skip_lowering: true) +test_create_graph(expect_diagnostics: false) //! > function_code fn foo(color: Option<(Color, Color)>) -> felt252 { @@ -303,7 +315,67 @@ Root: 7 //! > semantic_diagnostics +//! > lowering_diagnostics + //! > lowered +Parameters: v0: core::option::Option::<(test::Color, test::Color)> +blk0 (root): +Statements: +End: + Match(match_enum(v0) { + Option::Some(v1) => blk1, + Option::None(v2) => blk2, + }) + +blk1: +Statements: + (v3: test::Color, v4: test::Color) <- struct_destructure(v1) +End: + Match(match_enum(v3) { + Color::Red(v5) => blk3, + Color::Green(v6) => blk4, + }) + +blk2: +Statements: +End: + Goto(blk7, {}) + +blk3: +Statements: +End: + Goto(blk7, {}) + +blk4: +Statements: +End: + Match(match_enum(v4) { + Color::Red(v7) => blk5, + Color::Green(v8) => blk6, + }) + +blk5: +Statements: + (v10: core::felt252) <- 2 +End: + Goto(blk8, {v10 -> v12}) + +blk6: +Statements: + (v9: core::felt252) <- 3 +End: + Goto(blk8, {v9 -> v12}) + +blk7: +Statements: + (v11: core::felt252) <- 1 +End: + Goto(blk8, {v11 -> v12}) + +blk8: +Statements: +End: + Return(v12) //! > ========================================================================== @@ -344,6 +416,8 @@ Root: 10 //! > semantic_diagnostics +//! > lowering_diagnostics + //! > lowered //! > ========================================================================== @@ -381,6 +455,8 @@ Root: 4 //! > semantic_diagnostics +//! > lowering_diagnostics + //! > lowered //! > ========================================================================== @@ -411,6 +487,8 @@ Root: 6 //! > semantic_diagnostics +//! > lowering_diagnostics + //! > lowered Parameters: v0: core::felt252 blk0 (root): @@ -472,3 +550,436 @@ blk8: Statements: End: Return(v10) + +//! > ========================================================================== + +//! > Numeric match with tuples and bindings + +//! > test_runner_name +test_create_graph(expect_diagnostics: false) + +//! > function_code +fn foo(x: felt252, y: felt252) -> felt252 { + match (x, y) { + (1, 3) => 0, + (1, 4) | (2, 3) => 1, + (1, 5) => 2, + (3, _) => 3, + (x, 0) => x, + _ => 4, + } +} + +//! > graph +Root: 18 +0 ArmExpr { expr: ExprId(3) } +1 ArmExpr { expr: ExprId(4) } +2 ArmExpr { expr: ExprId(5) } +3 ArmExpr { expr: ExprId(6) } +4 ArmExpr { expr: ExprId(7) } +5 ArmExpr { expr: ExprId(8) } +6 BindVar { input: v1, output: PatternVarId(0), next: NodeId(4) } +7 EqualsLiteral { input: v2, literal: 0, true_branch: NodeId(6), false_branch: NodeId(5) } +8 EqualsLiteral { input: v1, literal: 3, true_branch: NodeId(3), false_branch: NodeId(7) } +9 EqualsLiteral { input: v2, literal: 0, true_branch: NodeId(6), false_branch: NodeId(5) } +10 EqualsLiteral { input: v2, literal: 3, true_branch: NodeId(1), false_branch: NodeId(9) } +11 EqualsLiteral { input: v1, literal: 2, true_branch: NodeId(10), false_branch: NodeId(8) } +12 EqualsLiteral { input: v2, literal: 0, true_branch: NodeId(6), false_branch: NodeId(5) } +13 EqualsLiteral { input: v2, literal: 5, true_branch: NodeId(2), false_branch: NodeId(12) } +14 EqualsLiteral { input: v2, literal: 4, true_branch: NodeId(1), false_branch: NodeId(13) } +15 EqualsLiteral { input: v2, literal: 3, true_branch: NodeId(0), false_branch: NodeId(14) } +16 EqualsLiteral { input: v1, literal: 1, true_branch: NodeId(15), false_branch: NodeId(11) } +17 Deconstruct { input: v0, outputs: [v1, v2], next: NodeId(16) } +18 EvaluateExpr { expr: ExprId(2), var_id: v0, next: NodeId(17) } + +//! > semantic_diagnostics + +//! > lowering_diagnostics + +//! > lowered +Parameters: v0: core::felt252, v1: core::felt252 +blk0 (root): +Statements: + (v2: (core::felt252, core::felt252)) <- struct_construct(v0, v1) + (v3: core::felt252, v4: core::felt252) <- struct_destructure(v2) + (v5: core::felt252) <- 1 + (v6: core::felt252) <- core::Felt252Sub::sub(v3, v5) +End: + Match(match core::felt252_is_zero(v6) { + IsZeroResult::Zero => blk1, + IsZeroResult::NonZero(v7) => blk2, + }) + +blk1: +Statements: + (v8: core::felt252) <- 3 + (v9: core::felt252) <- core::Felt252Sub::sub(v4, v8) +End: + Match(match core::felt252_is_zero(v9) { + IsZeroResult::Zero => blk3, + IsZeroResult::NonZero(v10) => blk4, + }) + +blk2: +Statements: + (v18: core::felt252) <- 2 + (v19: core::felt252) <- core::Felt252Sub::sub(v3, v18) +End: + Match(match core::felt252_is_zero(v19) { + IsZeroResult::Zero => blk11, + IsZeroResult::NonZero(v20) => blk12, + }) + +blk3: +Statements: + (v33: core::felt252) <- 0 +End: + Goto(blk24, {v33 -> v34}) + +blk4: +Statements: + (v11: core::felt252) <- 4 + (v12: core::felt252) <- core::Felt252Sub::sub(v4, v11) +End: + Match(match core::felt252_is_zero(v12) { + IsZeroResult::Zero => blk5, + IsZeroResult::NonZero(v13) => blk6, + }) + +blk5: +Statements: +End: + Goto(blk23, {}) + +blk6: +Statements: + (v14: core::felt252) <- 5 + (v15: core::felt252) <- core::Felt252Sub::sub(v4, v14) +End: + Match(match core::felt252_is_zero(v15) { + IsZeroResult::Zero => blk7, + IsZeroResult::NonZero(v16) => blk8, + }) + +blk7: +Statements: + (v31: core::felt252) <- 2 +End: + Goto(blk24, {v31 -> v34}) + +blk8: +Statements: +End: + Match(match core::felt252_is_zero(v4) { + IsZeroResult::Zero => blk9, + IsZeroResult::NonZero(v17) => blk10, + }) + +blk9: +Statements: +End: + Goto(blk21, {}) + +blk10: +Statements: +End: + Goto(blk22, {}) + +blk11: +Statements: + (v21: core::felt252) <- 3 + (v22: core::felt252) <- core::Felt252Sub::sub(v4, v21) +End: + Match(match core::felt252_is_zero(v22) { + IsZeroResult::Zero => blk13, + IsZeroResult::NonZero(v23) => blk14, + }) + +blk12: +Statements: + (v25: core::felt252) <- 3 + (v26: core::felt252) <- core::Felt252Sub::sub(v3, v25) +End: + Match(match core::felt252_is_zero(v26) { + IsZeroResult::Zero => blk17, + IsZeroResult::NonZero(v27) => blk18, + }) + +blk13: +Statements: +End: + Goto(blk23, {}) + +blk14: +Statements: +End: + Match(match core::felt252_is_zero(v4) { + IsZeroResult::Zero => blk15, + IsZeroResult::NonZero(v24) => blk16, + }) + +blk15: +Statements: +End: + Goto(blk21, {}) + +blk16: +Statements: +End: + Goto(blk22, {}) + +blk17: +Statements: + (v30: core::felt252) <- 3 +End: + Goto(blk24, {v30 -> v34}) + +blk18: +Statements: +End: + Match(match core::felt252_is_zero(v4) { + IsZeroResult::Zero => blk19, + IsZeroResult::NonZero(v28) => blk20, + }) + +blk19: +Statements: +End: + Goto(blk21, {}) + +blk20: +Statements: +End: + Goto(blk22, {}) + +blk21: +Statements: +End: + Goto(blk24, {v3 -> v34}) + +blk22: +Statements: + (v29: core::felt252) <- 4 +End: + Goto(blk24, {v29 -> v34}) + +blk23: +Statements: + (v32: core::felt252) <- 1 +End: + Goto(blk24, {v32 -> v34}) + +blk24: +Statements: +End: + Return(v34) + +//! > ========================================================================== + +//! > Pattern binding + +//! > test_runner_name +test_create_graph(expect_diagnostics: false) + +//! > function_code +fn foo(color: @(Color, Color)) -> felt252 { + match color { + (Color::Red(x), _) | (_, Color::Red(x)) => *x, + _ => 0, + } +} + +//! > module_code +enum Color { + Red: felt252, + Green, + Blue, +} + +//! > graph +Root: 7 +0 ArmExpr { expr: ExprId(2) } +1 ArmExpr { expr: ExprId(3) } +2 BindVar { input: v3, output: PatternVarId(0), next: NodeId(0) } +3 BindVar { input: v8, output: PatternVarId(2), next: NodeId(0) } +4 EnumMatch { matched_var: v2, variants: (NodeId(3), v8), (NodeId(1), v9), (NodeId(1), v10)} +5 EnumMatch { matched_var: v1, variants: (NodeId(2), v3), (NodeId(4), v7), (NodeId(4), v11)} +6 Deconstruct { input: v0, outputs: [v1, v2], next: NodeId(5) } +7 EvaluateExpr { expr: ExprId(0), var_id: v0, next: NodeId(6) } + +//! > semantic_diagnostics + +//! > lowering_diagnostics + +//! > lowered +Parameters: v0: @(test::Color, test::Color) +blk0 (root): +Statements: + (v1: @test::Color, v2: @test::Color) <- struct_destructure(v0) +End: + Match(match_enum(v1) { + Color::Red(v3) => blk1, + Color::Green(v4) => blk2, + Color::Blue(v5) => blk3, + }) + +blk1: +Statements: +End: + Goto(blk9, {v3 -> v10}) + +blk2: +Statements: +End: + Goto(blk4, {}) + +blk3: +Statements: +End: + Goto(blk4, {}) + +blk4: +Statements: +End: + Match(match_enum(v2) { + Color::Red(v6) => blk5, + Color::Green(v7) => blk6, + Color::Blue(v8) => blk7, + }) + +blk5: +Statements: +End: + Goto(blk9, {v6 -> v10}) + +blk6: +Statements: +End: + Goto(blk8, {}) + +blk7: +Statements: +End: + Goto(blk8, {}) + +blk8: +Statements: + (v9: core::felt252) <- 0 +End: + Goto(blk10, {v9 -> v12}) + +blk9: +Statements: + (v11: core::felt252) <- desnap(v10) +End: + Goto(blk10, {v11 -> v12}) + +blk10: +Statements: +End: + Return(v12) + +//! > ========================================================================== + +//! > Pattern binding 2 + +//! > test_runner_name +test_create_graph(expect_diagnostics: false) + +//! > function_code +fn foo(color: (Color, Color)) -> Color { + match color { + (Color::Red, x) | (x, Color::Red) => x, + _ => Color::Green, + } +} + +//! > module_code +enum Color { + Red, + Green, + Blue, +} + +//! > graph +Root: 7 +0 ArmExpr { expr: ExprId(1) } +1 ArmExpr { expr: ExprId(3) } +2 BindVar { input: v2, output: PatternVarId(1), next: NodeId(0) } +3 BindVar { input: v1, output: PatternVarId(0), next: NodeId(0) } +4 EnumMatch { matched_var: v2, variants: (NodeId(3), v8), (NodeId(1), v9), (NodeId(1), v10)} +5 EnumMatch { matched_var: v1, variants: (NodeId(2), v3), (NodeId(4), v7), (NodeId(4), v11)} +6 Deconstruct { input: v0, outputs: [v1, v2], next: NodeId(5) } +7 EvaluateExpr { expr: ExprId(0), var_id: v0, next: NodeId(6) } + +//! > semantic_diagnostics + +//! > lowering_diagnostics + +//! > lowered +Parameters: v0: (test::Color, test::Color) +blk0 (root): +Statements: + (v1: test::Color, v2: test::Color) <- struct_destructure(v0) +End: + Match(match_enum(v1) { + Color::Red(v3) => blk1, + Color::Green(v4) => blk2, + Color::Blue(v5) => blk3, + }) + +blk1: +Statements: +End: + Goto(blk9, {v2 -> v11}) + +blk2: +Statements: +End: + Goto(blk4, {}) + +blk3: +Statements: +End: + Goto(blk4, {}) + +blk4: +Statements: +End: + Match(match_enum(v2) { + Color::Red(v6) => blk5, + Color::Green(v7) => blk6, + Color::Blue(v8) => blk7, + }) + +blk5: +Statements: +End: + Goto(blk9, {v1 -> v11}) + +blk6: +Statements: +End: + Goto(blk8, {}) + +blk7: +Statements: +End: + Goto(blk8, {}) + +blk8: +Statements: + (v9: ()) <- struct_construct() + (v10: test::Color) <- Color::Green(v9) +End: + Goto(blk10, {v10 -> v12}) + +blk9: +Statements: +End: + Goto(blk10, {v11 -> v12}) + +blk10: +Statements: +End: + Return(v12) diff --git a/crates/cairo-lang-lowering/src/lower/lower_if.rs b/crates/cairo-lang-lowering/src/lower/lower_if.rs index 2bb3d4c59fd..77a313a4c83 100644 --- a/crates/cairo-lang-lowering/src/lower/lower_if.rs +++ b/crates/cairo-lang-lowering/src/lower/lower_if.rs @@ -47,7 +47,7 @@ pub fn lower_expr_if<'db>( builder: &mut BlockBuilder<'db>, expr: &semantic::ExprIf<'db>, ) -> LoweringResult<'db, LoweredExpr<'db>> { - if expr.conditions.len() == 1 && matches!(expr.conditions[0], Condition::BoolExpr(_)) { + if expr.conditions.len() == 1 { let graph = create_graph_expr_if(ctx, expr); return lower_graph(ctx, builder, &graph, ctx.get_location(expr.stable_ptr.untyped())); } diff --git a/crates/cairo-lang-lowering/src/test_data/if b/crates/cairo-lang-lowering/src/test_data/if index 9077dbcca4c..73f4c1e8f9d 100644 --- a/crates/cairo-lang-lowering/src/test_data/if +++ b/crates/cairo-lang-lowering/src/test_data/if @@ -453,7 +453,7 @@ End: Match(match test::a() { MyEnum::A(v10) => blk4, MyEnum::B => blk7, - MyEnum::C => blk10, + MyEnum::C => blk8, }) blk4: @@ -467,68 +467,42 @@ End: blk5: Statements: End: - Goto(blk13, {}) + Goto(blk9, {}) blk6: Statements: (v13: core::felt252) <- core::felt252_add(v0, v10) End: - Goto(blk14, {v13 -> v14}) + Goto(blk10, {v13 -> v14}) blk7: Statements: End: - Match(match_enum(v7) { - bool::False(v15) => blk8, - bool::True(v16) => blk9, - }) + Goto(blk9, {}) blk8: Statements: End: - Goto(blk13, {}) + Goto(blk9, {}) blk9: Statements: End: - Goto(blk13, {}) + Goto(blk10, {v0 -> v14}) blk10: Statements: + (v15: core::felt252) <- 1 + (v16: core::felt252) <- core::felt252_add(v14, v15) End: - Match(match_enum(v7) { - bool::False(v17) => blk11, - bool::True(v18) => blk12, - }) - -blk11: -Statements: -End: - Goto(blk13, {}) - -blk12: -Statements: -End: - Goto(blk13, {}) - -blk13: -Statements: -End: - Goto(blk14, {v0 -> v14}) - -blk14: -Statements: - (v19: core::felt252) <- 1 - (v20: core::felt252) <- core::felt252_add(v14, v19) -End: - Return(v20) + Return(v16) //! > ========================================================================== -//! > Test if let unsupported tuple +//! > Test if let enum and numeric tuple //! > test_runner_name -test_function_lowering(expect_diagnostics: true) +test_function_lowering(expect_diagnostics: false) //! > function fn foo() -> felt252 { @@ -554,20 +528,69 @@ extern fn a() -> MyEnum nopanic; //! > semantic_diagnostics //! > lowering_diagnostics -error: Unsupported value in if-let. Currently, if-let on tuples only supports enums as tuple members. - --> lib.cairo:9:32 - if let (MyEnum::A(x), 3) = (a(), 3) { - ^^^^^^^^ //! > lowering_flat - +Parameters: +blk0 (root): +Statements: + (v0: core::felt252) <- 0 +End: + Match(match test::a() { + MyEnum::A(v1) => blk1, + MyEnum::B => blk4, + MyEnum::C => blk5, + }) + +blk1: +Statements: + (v2: core::felt252) <- 3 + (v3: core::felt252) <- 3 + (v4: core::felt252) <- core::felt252_sub(v2, v3) +End: + Match(match core::felt252_is_zero(v4) { + IsZeroResult::Zero => blk2, + IsZeroResult::NonZero(v5) => blk3, + }) + +blk2: +Statements: + (v6: core::felt252) <- core::felt252_add(v0, v1) +End: + Goto(blk7, {v6 -> v7}) + +blk3: +Statements: +End: + Goto(blk6, {}) + +blk4: +Statements: +End: + Goto(blk6, {}) + +blk5: +Statements: +End: + Goto(blk6, {}) + +blk6: +Statements: +End: + Goto(blk7, {v0 -> v7}) + +blk7: +Statements: + (v8: core::felt252) <- 1 + (v9: core::felt252) <- core::felt252_add(v7, v8) +End: + Return(v9) //! > ========================================================================== //! > Test if let numeric //! > test_runner_name -test_function_lowering(expect_diagnostics: true) +test_function_lowering(expect_diagnostics: false) //! > function fn foo() -> felt252 { @@ -587,13 +610,17 @@ foo //! > semantic_diagnostics //! > lowering_diagnostics -error: Numeric values are not supported in if-let conditions. - --> lib.cairo:3:16 - if let x = y { - ^ //! > lowering_flat - +Parameters: +blk0 (root): +Statements: + (v0: core::felt252) <- 0 + (v1: core::felt252) <- core::felt252_add(v0, v0) + (v2: core::felt252) <- 1 + (v3: core::felt252) <- core::felt252_add(v1, v2) +End: + Return(v3) //! > ========================================================================== @@ -616,6 +643,7 @@ fn foo(a: MyEnum) -> felt252 { foo //! > module_code +#[derive(Drop)] enum MyEnum { A: felt252, B, @@ -631,36 +659,12 @@ Parameters: v0: test::MyEnum blk0 (root): Statements: (v1: core::felt252) <- 0 + (v2: core::felt252) <- 5 + (v3: core::felt252) <- core::felt252_add(v1, v2) + (v4: core::felt252) <- 1 + (v5: core::felt252) <- core::felt252_add(v3, v4) End: - Match(match_enum(v0) { - MyEnum::A(v2) => blk1, - MyEnum::B(v3) => blk2, - MyEnum::C(v4) => blk3, - }) - -blk1: -Statements: -End: - Goto(blk4, {}) - -blk2: -Statements: -End: - Goto(blk4, {}) - -blk3: -Statements: -End: - Goto(blk4, {}) - -blk4: -Statements: - (v5: core::felt252) <- 5 - (v6: core::felt252) <- core::felt252_add(v1, v5) - (v7: core::felt252) <- 1 - (v8: core::felt252) <- core::felt252_add(v6, v7) -End: - Return(v8) + Return(v5) //! > ========================================================================== @@ -685,6 +689,7 @@ fn foo(a: MyEnum) -> felt252 { foo //! > module_code +#[derive(Drop)] enum MyEnum { A: felt252, B, @@ -694,8 +699,8 @@ enum MyEnum { //! > semantic_diagnostics //! > lowering_diagnostics -warning: Unreachable else clause. - --> lib.cairo:10:12-12:5 +warning: Unreachable clause. + --> lib.cairo:11:12-13:5 } else { ____________^ | y = y + 8; @@ -707,36 +712,12 @@ Parameters: v0: test::MyEnum blk0 (root): Statements: (v1: core::felt252) <- 0 + (v2: core::felt252) <- 5 + (v3: core::felt252) <- core::felt252_add(v1, v2) + (v4: core::felt252) <- 1 + (v5: core::felt252) <- core::felt252_add(v3, v4) End: - Match(match_enum(v0) { - MyEnum::A(v2) => blk1, - MyEnum::B(v3) => blk2, - MyEnum::C(v4) => blk3, - }) - -blk1: -Statements: -End: - Goto(blk4, {}) - -blk2: -Statements: -End: - Goto(blk4, {}) - -blk3: -Statements: -End: - Goto(blk4, {}) - -blk4: -Statements: - (v5: core::felt252) <- 5 - (v6: core::felt252) <- core::felt252_add(v1, v5) - (v7: core::felt252) <- 1 - (v8: core::felt252) <- core::felt252_add(v6, v7) -End: - Return(v8) + Return(v5) //! > ========================================================================== @@ -761,6 +742,7 @@ fn foo(a: MyStruct) -> felt252 { foo //! > module_code +#[derive(Drop)] struct MyStruct { A: felt252, B: felt252, @@ -769,13 +751,25 @@ struct MyStruct { //! > semantic_diagnostics //! > lowering_diagnostics -error: Unsupported type in if-let. Type: `test::MyStruct`. - --> lib.cairo:7:16 - if let _ = a { - ^ +warning: Unreachable clause. + --> lib.cairo:10:12-12:5 + } else { + ____________^ +| y = y + 8; +| } +|_____^ //! > lowering_flat - +Parameters: v0: test::MyStruct +blk0 (root): +Statements: + (v1: core::felt252) <- 0 + (v2: core::felt252) <- 5 + (v3: core::felt252) <- core::felt252_add(v1, v2) + (v4: core::felt252) <- 1 + (v5: core::felt252) <- core::felt252_add(v3, v4) +End: + Return(v5) //! > ========================================================================== @@ -821,27 +815,9 @@ enum MyEnum { Parameters: v0: test::MyEnum blk0 (root): Statements: + (v1: core::felt252) <- 2 End: - Match(match_enum(v0) { - MyEnum::A(v1) => blk1, - MyEnum::B(v2) => blk2, - }) - -blk1: -Statements: -End: - Goto(blk3, {}) - -blk2: -Statements: -End: - Goto(blk3, {}) - -blk3: -Statements: - (v3: core::felt252) <- 2 -End: - Return(v3) + Return(v1) //! > ========================================================================== @@ -870,7 +846,7 @@ foo //! > semantic_diagnostics //! > lowering_diagnostics -warning: Unreachable else clause. +warning: Unreachable clause. --> lib.cairo:5:12-7:5 } else { ____________^ @@ -883,30 +859,12 @@ Parameters: v0: core::option::Option:: blk0 (root): Statements: (v1: core::felt252) <- 0 + (v2: core::felt252) <- 5 + (v3: core::felt252) <- core::felt252_add(v1, v2) + (v4: core::felt252) <- 1 + (v5: core::felt252) <- core::felt252_add(v3, v4) End: - Match(match_enum(v0) { - Option::Some(v2) => blk1, - Option::None(v3) => blk2, - }) - -blk1: -Statements: -End: - Goto(blk3, {}) - -blk2: -Statements: -End: - Goto(blk3, {}) - -blk3: -Statements: - (v4: core::felt252) <- 5 - (v5: core::felt252) <- core::felt252_add(v1, v4) - (v6: core::felt252) <- 1 - (v7: core::felt252) <- core::felt252_add(v5, v6) -End: - Return(v7) + Return(v5) //! > ==========================================================================