Skip to content

Commit 828b451

Browse files
committed
flow-control: Support let-else.
commit-id:17f9f2da
1 parent 3ac61ac commit 828b451

File tree

6 files changed

+112
-71
lines changed

6 files changed

+112
-71
lines changed

crates/cairo-lang-lowering/src/lower/flow_control/create_graph.rs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use cache::Cache;
2-
use cairo_lang_semantic::{self as semantic, Condition, PatternId};
2+
use cairo_lang_semantic::{self as semantic, Condition, ExprId, PatternId};
33
use cairo_lang_syntax::node::TypedStablePtr;
4+
use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
45
use filtered_patterns::IndexAndBindings;
56
use itertools::Itertools;
67
use patterns::{CreateNodeParams, create_node_for_patterns, get_pattern};
78

89
use super::graph::{
910
ArmExpr, BooleanIf, EvaluateExpr, FlowControlGraph, FlowControlGraphBuilder, FlowControlNode,
10-
NodeId,
11+
LetElseSuccess, NodeId,
1112
};
1213
use crate::diagnostic::{LoweringDiagnosticKind, MatchDiagnostic, MatchError, MatchKind};
1314
use crate::lower::context::LoweringContext;
@@ -176,3 +177,55 @@ pub fn create_graph_expr_match<'db>(
176177

177178
graph.finalize(root, ctx)
178179
}
180+
181+
/// Creates a graph node for a let-else statement.
182+
///
183+
/// See [crate::lower::lower_let_else::lower_let_else] for more details.
184+
pub fn create_graph_expr_let_else<'db>(
185+
ctx: &mut LoweringContext<'db, '_>,
186+
pattern: PatternId,
187+
expr_id: ExprId,
188+
else_clause: ExprId,
189+
var_ids_and_stable_ptrs: Vec<(semantic::VarId<'db>, SyntaxStablePtrId<'db>)>,
190+
) -> FlowControlGraph<'db> {
191+
let mut graph = FlowControlGraphBuilder::new(MatchKind::IfLet);
192+
193+
// Add the `true` branch (the `if` block).
194+
let true_branch =
195+
graph.add_node(FlowControlNode::LetElseSuccess(LetElseSuccess { var_ids_and_stable_ptrs }));
196+
197+
// Add the `false` branch (the `else` block), if exists.
198+
let false_branch = graph.add_node(FlowControlNode::ArmExpr(ArmExpr { expr: else_clause }));
199+
200+
let expr = &ctx.function_body.arenas.exprs[expr_id];
201+
202+
// Create a variable for the expression.
203+
let expr_location = ctx.get_location(expr.stable_ptr().untyped());
204+
let expr_var = graph.new_var(expr.ty(), expr_location);
205+
206+
let match_node_id = create_node_for_patterns(
207+
CreateNodeParams {
208+
ctx,
209+
graph: &mut graph,
210+
patterns: &[Some(get_pattern(ctx, pattern))],
211+
build_node_callback: &mut |graph, pattern_indices, _path| {
212+
if let Some(index_and_bindings) = pattern_indices.first() {
213+
index_and_bindings.wrap_node(graph, true_branch)
214+
} else {
215+
false_branch
216+
}
217+
},
218+
location: expr_location,
219+
},
220+
expr_var,
221+
);
222+
223+
// Create a node for lowering `expr_id` into `expr_var` and continue to the match.
224+
let root = graph.add_node(FlowControlNode::EvaluateExpr(EvaluateExpr {
225+
expr: expr_id,
226+
var_id: expr_var,
227+
next: match_node_id,
228+
}));
229+
230+
graph.finalize(root, ctx)
231+
}

crates/cairo-lang-lowering/src/lower/flow_control/graph.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,15 @@ pub struct Downcast {
211211
pub out_of_range: NodeId,
212212
}
213213

214+
/// An arm (final node) that returns a tuple of bound variables for the let-else success arm.
215+
///
216+
/// See [crate::lower::lower_let_else::lower_let_else] for more details.
217+
#[derive(Debug)]
218+
pub struct LetElseSuccess<'db> {
219+
/// The variables to assign the result to.
220+
pub var_ids_and_stable_ptrs: Vec<(semantic::VarId<'db>, SyntaxStablePtrId<'db>)>,
221+
}
222+
214223
/// A node in the flow control graph for a match or if lowering.
215224
pub enum FlowControlNode<'db> {
216225
/// Evaluates an expression and assigns the result to a [FlowControlVar].
@@ -233,6 +242,8 @@ pub enum FlowControlNode<'db> {
233242
Upcast(Upcast),
234243
/// Downcasts a value to a smaller type.
235244
Downcast(Downcast),
245+
/// An arm (final node) that returns a tuple of bound variables for the let-else success arm.
246+
LetElseSuccess(LetElseSuccess<'db>),
236247
/// An arm (final node) that returns a unit value - `()`.
237248
UnitResult,
238249
/// Represents a node that could not be properly constructed due to an error in the code.
@@ -258,6 +269,7 @@ impl<'db> FlowControlNode<'db> {
258269
FlowControlNode::BindVar(node) => Some(node.input),
259270
FlowControlNode::Upcast(node) => Some(node.input),
260271
FlowControlNode::Downcast(node) => Some(node.input),
272+
FlowControlNode::LetElseSuccess(..) => None,
261273
FlowControlNode::UnitResult => None,
262274
FlowControlNode::Missing(_) => None,
263275
}
@@ -277,6 +289,7 @@ impl<'db> Debug for FlowControlNode<'db> {
277289
FlowControlNode::BindVar(node) => node.fmt(f),
278290
FlowControlNode::Upcast(node) => node.fmt(f),
279291
FlowControlNode::Downcast(node) => node.fmt(f),
292+
FlowControlNode::LetElseSuccess(node) => node.fmt(f),
280293
FlowControlNode::UnitResult => write!(f, "UnitResult"),
281294
FlowControlNode::Missing(_) => write!(f, "Missing"),
282295
}

crates/cairo-lang-lowering/src/lower/flow_control/lower_graph/lower_node.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ use crate::lower::context::{LoweredExpr, LoweredExprExternEnum, VarRequest};
2020
use crate::lower::external::extern_facade_expr;
2121
use crate::lower::flow_control::graph::{
2222
ArmExpr, BindVar, BooleanIf, Deconstruct, Downcast, EnumMatch, EqualsLiteral, EvaluateExpr,
23-
FlowControlNode, NodeId, Upcast, ValueMatch,
23+
FlowControlNode, LetElseSuccess, NodeId, Upcast, ValueMatch,
2424
};
25+
use crate::lower::lower_let_else::lower_success_arm_body;
2526
use crate::lower::{
2627
generators, lower_expr, lower_expr_literal_to_var_usage, match_extern_arm_ref_args_bind,
2728
match_extern_variant_arm_input_types,
@@ -57,6 +58,7 @@ pub fn lower_node(ctx: &mut LowerGraphContext<'_, '_, '_>, id: NodeId) -> Maybe<
5758
FlowControlNode::Deconstruct(node) => lower_deconstruct(ctx, id, node, builder),
5859
FlowControlNode::Upcast(node) => lower_upcast(ctx, id, node, builder),
5960
FlowControlNode::Downcast(node) => lower_downcast(ctx, id, node, builder),
61+
FlowControlNode::LetElseSuccess(node) => lower_let_else_success(ctx, id, node, builder),
6062
FlowControlNode::Missing(diag_added) => Err(*diag_added),
6163
}
6264
}
@@ -507,3 +509,18 @@ fn lower_downcast<'db>(
507509
ctx.finalize_with_match(id, builder, match_info);
508510
Ok(())
509511
}
512+
513+
/// Lowers a [LetElseSuccess] node.
514+
fn lower_let_else_success<'db>(
515+
ctx: &mut LowerGraphContext<'db, '_, '_>,
516+
id: NodeId,
517+
node: &LetElseSuccess<'db>,
518+
builder: BlockBuilder<'db>,
519+
) -> Maybe<()> {
520+
let sealed_goto_callsite =
521+
lower_success_arm_body(ctx.ctx, builder, &node.var_ids_and_stable_ptrs, ctx.location);
522+
let (builder, var_usage) = sealed_goto_callsite;
523+
524+
ctx.finalize_with_arm(id, builder, Ok(LoweredExpr::AtVariable(var_usage)))?;
525+
Ok(())
526+
}

crates/cairo-lang-lowering/src/lower/lower_let_else.rs

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
66
use cairo_lang_utils::Intern;
77
use itertools::zip_eq;
88

9-
use super::block_builder::{BlockBuilder, SealedBlockBuilder};
10-
use super::context::{LoweredExpr, LoweringContext, LoweringFlowError, VarRequest};
9+
use super::block_builder::BlockBuilder;
10+
use super::context::{LoweringContext, LoweringFlowError, VarRequest};
11+
use super::flow_control::create_graph::create_graph_expr_let_else;
12+
use super::flow_control::lower_graph::lower_graph;
1113
use super::generators;
12-
use super::lower_match::{MatchArmWrapper, lower_match_arms};
13-
use crate::diagnostic::MatchKind;
14+
use crate::VarUsage;
15+
use crate::ids::LocationId;
1416

1517
/// Lowers a let-else statement.
1618
///
@@ -34,35 +36,21 @@ pub fn lower_let_else<'db>(
3436
builder: &mut BlockBuilder<'db>,
3537
pattern_id: PatternId,
3638
expr: ExprId,
37-
lowered_expr: LoweredExpr<'db>,
3839
else_clause: ExprId,
3940
stable_ptr: &StatementPtr<'db>,
4041
) -> Result<(), LoweringFlowError<'db>> {
41-
let pattern = ctx.function_body.arenas.patterns[pattern_id].clone();
42+
let pattern = &ctx.function_body.arenas.patterns[pattern_id];
4243
let variables = pattern.variables(&ctx.function_body.arenas.patterns);
4344

44-
// Create a match expression with two arms.
45-
let patterns = &[pattern_id];
45+
// Create a list of the variables in the pattern, and their stable pointers.
4646
let var_ids_and_stable_ptrs = variables
4747
.iter()
4848
.map(|pattern_var| (VarId::Local(pattern_var.var.id), pattern_var.stable_ptr.untyped()))
4949
.collect();
50-
let arms = vec![
51-
MatchArmWrapper::LetElseSuccess(patterns, var_ids_and_stable_ptrs, stable_ptr.untyped()),
52-
MatchArmWrapper::ElseClause(else_clause),
53-
];
5450

55-
// Lower the match expression.
56-
// The result is a tuple with the values of the pattern's variables.
57-
let match_lowered = lower_match_arms(
58-
ctx,
59-
builder,
60-
expr,
61-
lowered_expr,
62-
arms,
63-
ctx.get_location(stable_ptr.untyped()),
64-
MatchKind::Match,
65-
)?;
51+
let graph =
52+
create_graph_expr_let_else(ctx, pattern_id, expr, else_clause, var_ids_and_stable_ptrs);
53+
let lowered_expr = lower_graph(ctx, builder, &graph, ctx.get_location(stable_ptr.untyped()))?;
6654

6755
// Destruct the tuple.
6856
let reqs = variables
@@ -73,7 +61,7 @@ pub fn lower_let_else<'db>(
7361
})
7462
.collect();
7563
let output_vars = generators::StructDestructure {
76-
input: match_lowered.as_var_usage(ctx, builder)?,
64+
input: lowered_expr.as_var_usage(ctx, builder)?,
7765
var_reqs: reqs,
7866
}
7967
.add(ctx, &mut builder.statements);
@@ -98,8 +86,8 @@ pub fn lower_success_arm_body<'db>(
9886
ctx: &mut LoweringContext<'db, '_>,
9987
mut builder: BlockBuilder<'db>,
10088
vars: &[(VarId<'db>, SyntaxStablePtrId<'db>)],
101-
stable_ptr: &SyntaxStablePtrId<'db>,
102-
) -> SealedBlockBuilder<'db> {
89+
location: LocationId<'db>,
90+
) -> (BlockBuilder<'db>, VarUsage<'db>) {
10391
log::trace!("Lowering success arm body");
10492

10593
let inputs: Vec<_> = vars
@@ -119,12 +107,8 @@ pub fn lower_success_arm_body<'db>(
119107

120108
let tys = inputs.iter().map(|var_usage| ctx.variables[var_usage.var_id].ty).collect();
121109
let tuple_ty = TypeLongId::Tuple(tys).intern(ctx.db);
122-
let tuple_var_usage = generators::StructConstruct {
123-
inputs,
124-
ty: tuple_ty,
125-
location: ctx.get_location(*stable_ptr),
126-
}
127-
.add(ctx, &mut builder.statements);
110+
let tuple_var_usage = generators::StructConstruct { inputs, ty: tuple_ty, location }
111+
.add(ctx, &mut builder.statements);
128112

129-
builder.goto_callsite(Some(tuple_var_usage))
113+
(builder, tuple_var_usage)
130114
}

crates/cairo-lang-lowering/src/lower/lower_match.rs

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use cairo_lang_diagnostics::{DiagnosticAdded, Maybe};
55
use cairo_lang_filesystem::db::FilesGroup;
66
use cairo_lang_filesystem::flag::Flag;
77
use cairo_lang_filesystem::ids::{FlagId, FlagLongId};
8-
use cairo_lang_semantic::{self as semantic, ConcreteEnumId, ConcreteVariant, VarId};
8+
use cairo_lang_semantic::{self as semantic, ConcreteEnumId, ConcreteVariant};
99
use cairo_lang_syntax::node::TypedStablePtr;
1010
use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
1111
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
@@ -25,7 +25,6 @@ use super::context::{
2525
handle_lowering_flow_error,
2626
};
2727
use super::lower_if::{ConditionedExpr, lower_conditioned_expr_and_seal};
28-
use super::lower_let_else::lower_success_arm_body;
2928
use super::{
3029
alloc_empty_block, generators, lower_expr_block, lower_tail_expr,
3130
lowered_expr_to_block_scope_end, recursively_call_loop_func,
@@ -60,13 +59,6 @@ pub enum MatchArmWrapper<'db, 'a> {
6059
ElseClause(semantic::ExprId),
6160
/// Default clause when else clause is not provided (if-let/while-let).
6261
DefaultClause,
63-
/// The success arm of a let-else statement. See [super::lower_let_else::lower_let_else] for
64-
/// more details.
65-
LetElseSuccess(
66-
&'a [PatternId],
67-
Vec<(VarId<'db>, SyntaxStablePtrId<'db>)>,
68-
SyntaxStablePtrId<'db>,
69-
),
7062
/// Similar to [Self::Arm], except that the expression is a conditioned expression
7163
/// (see [ConditionedExpr]).
7264
ConditionedArm(&'a [PatternId], ConditionedExpr<'db, 'a>),
@@ -82,9 +74,9 @@ impl<'db> MatchArmWrapper<'db, '_> {
8274
/// Returns the patterns of the match arm.
8375
pub fn patterns(&self) -> Option<&[PatternId]> {
8476
match self {
85-
MatchArmWrapper::Arm(patterns, _)
86-
| MatchArmWrapper::ConditionedArm(patterns, _)
87-
| MatchArmWrapper::LetElseSuccess(patterns, _, _) => Some(patterns),
77+
MatchArmWrapper::Arm(patterns, _) | MatchArmWrapper::ConditionedArm(patterns, _) => {
78+
Some(patterns)
79+
}
8880
MatchArmWrapper::ElseClause(_) | MatchArmWrapper::DefaultClause => None,
8981
}
9082
}
@@ -197,9 +189,7 @@ fn get_underscore_pattern_path_and_mark_unreachable<'db>(
197189

198190
for arm in arms.iter().skip(otherwise_variant.arm_index + 1) {
199191
match arm {
200-
MatchArmWrapper::Arm(patterns, _)
201-
| MatchArmWrapper::ConditionedArm(patterns, _)
202-
| MatchArmWrapper::LetElseSuccess(patterns, _, _) => {
192+
MatchArmWrapper::Arm(patterns, _) | MatchArmWrapper::ConditionedArm(patterns, _) => {
203193
for pattern in *patterns {
204194
let pattern_ptr = ctx.function_body.arenas.patterns[*pattern].stable_ptr();
205195
ctx.diagnostics.report(
@@ -1162,8 +1152,7 @@ trait EnumVariantScopeBuilder<'db> {
11621152
continue;
11631153
}
11641154
MatchArmWrapper::Arm(patterns, _)
1165-
| MatchArmWrapper::ConditionedArm(patterns, _)
1166-
| MatchArmWrapper::LetElseSuccess(patterns, _, _) => patterns,
1155+
| MatchArmWrapper::ConditionedArm(patterns, _) => patterns,
11671156
};
11681157
for (pattern_index, pattern) in patterns.iter().copied().enumerate() {
11691158
let pattern_path = PatternPath { arm_index, pattern_index: Some(pattern_index) };
@@ -1366,15 +1355,6 @@ trait EnumVariantScopeBuilder<'db> {
13661355
}),
13671356
);
13681357
},
1369-
MatchArmWrapper::LetElseSuccess(_,_, stable_ptr) => {
1370-
ctx.diagnostics.report(
1371-
*stable_ptr,
1372-
MatchError(MatchError {
1373-
kind: builder_context.kind,
1374-
error: MatchDiagnostic::UnreachableMatchArm,
1375-
}),
1376-
);
1377-
}
13781358
MatchArmWrapper::DefaultClause => (),
13791359
}
13801360
}
@@ -1796,12 +1776,6 @@ fn lower_arm_expr_and_seal<'db>(
17961776
lowered_expr_to_block_scope_end(ctx, subscope, block_expr)
17971777
}
17981778
(MatchArmWrapper::DefaultClause, _) => Ok(subscope.goto_callsite(None)),
1799-
(MatchArmWrapper::LetElseSuccess(_, vars, stable_ptr), MatchKind::Match) => {
1800-
Ok(lower_success_arm_body(ctx, subscope, vars, stable_ptr))
1801-
}
1802-
(MatchArmWrapper::LetElseSuccess(_, _, _), _) => {
1803-
unreachable!("Invalid MatchKind for LetElseSuccess.")
1804-
}
18051779
(MatchArmWrapper::ConditionedArm(_, expr), _) => {
18061780
lower_conditioned_expr_and_seal(ctx, subscope, expr)
18071781
}

crates/cairo-lang-lowering/src/lower/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -663,19 +663,19 @@ pub fn lower_statement<'db>(
663663
else_clause,
664664
stable_ptr,
665665
}) => {
666-
log::trace!("Lowering a let statement.");
667-
let lowered_expr = lower_expr(ctx, builder, *expr)?;
668666
if let Some(else_clause) = else_clause {
667+
log::trace!("Lowering a let-else statement.");
669668
lower_let_else::lower_let_else(
670669
ctx,
671670
builder,
672671
*pattern,
673672
*expr,
674-
lowered_expr,
675673
*else_clause,
676674
stable_ptr,
677675
)?;
678676
} else {
677+
log::trace!("Lowering a let statement.");
678+
let lowered_expr = lower_expr(ctx, builder, *expr)?;
679679
lower_single_pattern(ctx, builder, *pattern, lowered_expr, true)?;
680680
}
681681
}

0 commit comments

Comments
 (0)