Skip to content

Commit ecb0c78

Browse files
authored
semantic: Support let-else. (#7854)
1 parent eb8c5dc commit ecb0c78

File tree

16 files changed

+287
-17
lines changed

16 files changed

+287
-17
lines changed

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,9 +648,19 @@ pub fn lower_statement(
648648
x.as_var_usage(ctx, builder)?;
649649
}
650650
}
651-
semantic::Statement::Let(semantic::StatementLet { pattern, expr, stable_ptr: _ }) => {
651+
semantic::Statement::Let(semantic::StatementLet {
652+
pattern,
653+
expr,
654+
else_clause,
655+
stable_ptr,
656+
}) => {
652657
log::trace!("Lowering a let statement.");
653658
let lowered_expr = lower_expr(ctx, builder, *expr)?;
659+
if else_clause.is_some() {
660+
return Err(LoweringFlowError::Failed(
661+
ctx.diagnostics.report(stable_ptr.untyped(), Unsupported),
662+
));
663+
}
654664
lower_single_pattern(ctx, builder, *pattern, lowered_expr)?
655665
}
656666
semantic::Statement::Continue(semantic::StatementContinue { stable_ptr }) => {

crates/cairo-lang-lowering/src/test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ cairo_lang_test_utils::test_file_test!(
4242
arm_pattern_destructure :"arm_pattern_destructure",
4343
if_ :"if",
4444
implicits :"implicits",
45+
let_else :"let_else",
4546
logical_operator :"logical_operator",
4647
loop_ :"loop",
4748
match_ :"match",
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//! > let else
2+
3+
//! > test_runner_name
4+
test_function_lowering(expect_diagnostics: true)
5+
6+
//! > function
7+
fn foo(a: MyEnum) {
8+
let MyEnum::A(x) = a else {
9+
bar(0);
10+
return;
11+
};
12+
bar(x);
13+
}
14+
15+
extern fn bar(x: felt252) nopanic;
16+
17+
//! > function_name
18+
foo
19+
20+
//! > module_code
21+
enum MyEnum {
22+
A: felt252,
23+
B: felt252,
24+
}
25+
26+
//! > semantic_diagnostics
27+
28+
//! > lowering_diagnostics
29+
error: Unsupported feature.
30+
--> lib.cairo:6:5-9:6
31+
let MyEnum::A(x) = a else {
32+
_____^
33+
| ...
34+
| };
35+
|______^
36+
37+
//! > lowering_flat
38+
<Failed lowering function - run with RUST_LOG=warn (or less) to see diagnostics>

crates/cairo-lang-semantic/src/diagnostic.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,11 @@ impl DiagnosticEntry for SemanticDiagnostic {
10801080
SemanticDiagnosticKind::UserDefinedInlineMacrosDisabled => {
10811081
"User defined inline macros are disabled in the current crate.".into()
10821082
}
1083-
SemanticDiagnosticKind::LetElseNotSupported => "Let else is not supported yet.".into(),
1083+
SemanticDiagnosticKind::NonNeverLetElseType => concat!(
1084+
"`else` clause of `let...else` must exit the scope. ",
1085+
"Consider using `return`, `continue`, ..."
1086+
)
1087+
.into(),
10841088
}
10851089
}
10861090
fn location(&self, db: &Self::DbType) -> DiagnosticLocation {
@@ -1520,7 +1524,7 @@ pub enum SemanticDiagnosticKind {
15201524
PatternMissingArgs(ast::ExprPath),
15211525
UndefinedMacroPlaceholder(String),
15221526
UserDefinedInlineMacrosDisabled,
1523-
LetElseNotSupported,
1527+
NonNeverLetElseType,
15241528
}
15251529

15261530
/// The kind of an expression with multiple possible return types.

crates/cairo-lang-semantic/src/expr/compute.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3883,8 +3883,22 @@ pub fn compute_statement_semantic(
38833883
};
38843884
let rhs_expr_id = rhs_expr.id;
38853885

3886-
if !matches!(let_syntax.let_else_clause(db), ast::OptionLetElseClause::Empty(_)) {
3887-
return Err(ctx.diagnostics.report(let_syntax.stable_ptr(db), LetElseNotSupported));
3886+
let else_clause = match let_syntax.let_else_clause(db) {
3887+
ast::OptionLetElseClause::Empty(_) => None,
3888+
ast::OptionLetElseClause::LetElseClause(else_clause) => {
3889+
let else_block_syntax = else_clause.else_block(db);
3890+
let else_block_stable_ptr = else_block_syntax.stable_ptr(db);
3891+
3892+
let else_block =
3893+
compute_expr_semantic(ctx, &ast::Expr::Block(else_block_syntax));
3894+
3895+
if else_block.ty() != never_ty(db) {
3896+
// Report the error, but continue processing.
3897+
ctx.diagnostics.report(else_block_stable_ptr, NonNeverLetElseType);
3898+
}
3899+
3900+
Some(else_block.id)
3901+
}
38883902
};
38893903

38903904
let pattern = compute_pattern_semantic(
@@ -3911,6 +3925,7 @@ pub fn compute_statement_semantic(
39113925
semantic::Statement::Let(semantic::StatementLet {
39123926
pattern: pattern.id,
39133927
expr: rhs_expr_id,
3928+
else_clause,
39143929
stable_ptr: syntax.stable_ptr(db),
39153930
})
39163931
}

crates/cairo-lang-semantic/src/expr/objects.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ pub struct StatementExpr {
9191
pub struct StatementLet {
9292
pub pattern: PatternId,
9393
pub expr: ExprId,
94+
pub else_clause: Option<ExprId>,
9495
#[hide_field_debug_with_db]
9596
#[dont_rewrite]
9697
pub stable_ptr: ast::StatementPtr,

crates/cairo-lang-semantic/src/expr/semantic_test_data/closure

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ Block(
100100
expr: Var(
101101
ParamId(test::a),
102102
),
103+
else_clause: None,
103104
},
104105
),
105106
],
@@ -133,6 +134,7 @@ Block(
133134
ty: {[email protected]:6:13: 6:16},
134135
},
135136
),
137+
else_clause: None,
136138
},
137139
),
138140
Let(
@@ -154,6 +156,7 @@ Block(
154156
ty: core::integer::u128,
155157
},
156158
),
159+
else_clause: None,
157160
},
158161
),
159162
],

crates/cairo-lang-semantic/src/expr/semantic_test_data/for

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ For(
4141
ty: core::array::Array::<core::felt252>,
4242
},
4343
),
44+
else_clause: None,
4445
},
4546
),
4647
Expr(

crates/cairo-lang-semantic/src/expr/semantic_test_data/inline_macros

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ FunctionCall(
3232
ty: core::array::Array::<core::felt252>,
3333
},
3434
),
35+
else_clause: None,
3536
},
3637
),
3738
Expr(

crates/cairo-lang-semantic/src/expr/semantic_test_data/let_else

Lines changed: 109 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! > Test let-else statement
22

33
//! > test_runner_name
4-
test_expr_semantics(expect_diagnostics: true)
4+
test_expr_semantics(expect_diagnostics: false)
55

66
//! > function_body
77
let a = MyEnum::A(9);
@@ -37,6 +37,109 @@ Block(
3737
ty: (),
3838
},
3939
),
40+
else_clause: None,
41+
},
42+
),
43+
Let(
44+
StatementLet {
45+
pattern: EnumVariant(
46+
PatternEnumVariant {
47+
variant: MyEnum::A,
48+
inner_pattern: Some(
49+
Variable(
50+
x,
51+
),
52+
),
53+
ty: test::MyEnum,
54+
},
55+
),
56+
expr: Var(
57+
LocalVarId(test::a),
58+
),
59+
else_clause: Some(
60+
Block(
61+
ExprBlock {
62+
statements: [
63+
Return(
64+
StatementReturn {
65+
expr_option: Some(
66+
Var(
67+
LocalVarId(test::x),
68+
),
69+
),
70+
},
71+
),
72+
],
73+
tail: None,
74+
ty: core::never,
75+
},
76+
),
77+
),
78+
},
79+
),
80+
],
81+
tail: Some(
82+
Var(
83+
LocalVarId(test::x),
84+
),
85+
),
86+
ty: core::felt252,
87+
},
88+
)
89+
90+
//! > expected_diagnostics
91+
92+
//! > ==========================================================================
93+
94+
//! > Test let-else statement with empty else block
95+
96+
//! > test_runner_name
97+
test_expr_semantics(expect_diagnostics: true)
98+
99+
//! > function_body
100+
let a = MyEnum::A(9);
101+
102+
//! > expr_code
103+
{
104+
let MyEnum::A(x) = a else {};
105+
x
106+
}
107+
108+
//! > module_code
109+
enum MyEnum {
110+
A: felt252,
111+
B: felt252,
112+
}
113+
114+
//! > expected_semantics
115+
Block(
116+
ExprBlock {
117+
statements: [
118+
Let(
119+
StatementLet {
120+
pattern: EnumVariant(
121+
PatternEnumVariant {
122+
variant: MyEnum::A,
123+
inner_pattern: Some(
124+
Variable(
125+
x,
126+
),
127+
),
128+
ty: test::MyEnum,
129+
},
130+
),
131+
expr: Var(
132+
LocalVarId(test::a),
133+
),
134+
else_clause: Some(
135+
Block(
136+
ExprBlock {
137+
statements: [],
138+
tail: None,
139+
ty: (),
140+
},
141+
),
142+
),
40143
},
41144
),
42145
],
@@ -45,15 +148,12 @@ Block(
45148
LocalVarId(test::x),
46149
),
47150
),
48-
ty: (),
151+
ty: core::felt252,
49152
},
50153
)
51154

52155
//! > expected_diagnostics
53-
error: Let else is not supported yet.
54-
--> lib.cairo:8:5-11:6
55-
let MyEnum::A(x) = a else {
56-
_____^
57-
| ...
58-
| };
59-
|______^
156+
error: `else` clause of `let...else` must exit the scope. Consider using `return`, `continue`, ...
157+
--> lib.cairo:7:31
158+
let MyEnum::A(x) = a else {};
159+
^^

0 commit comments

Comments
 (0)