Skip to main content

cairo_lang_lowering/lower/
mod.rs

1use std::vec;
2
3use block_builder::BlockBuilder;
4use cairo_lang_debug::DebugWithDb;
5use cairo_lang_defs::diagnostic_utils::StableLocation;
6use cairo_lang_diagnostics::{Diagnostics, Maybe};
7use cairo_lang_semantic::corelib::{
8    ErrorPropagationType, get_enum_concrete_variant, try_get_ty_by_name,
9    unwrap_error_propagation_type,
10};
11use cairo_lang_semantic::db::SemanticGroup;
12use cairo_lang_semantic::items::functions::{GenericFunctionId, ImplGenericFunctionId};
13use cairo_lang_semantic::items::imp::ImplLongId;
14use cairo_lang_semantic::usage::MemberPath;
15use cairo_lang_semantic::{
16    ConcreteFunction, ConcreteTraitLongId, ExprVar, LocalVariable, VarId, corelib,
17};
18use cairo_lang_syntax::node::TypedStablePtr;
19use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
20use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
21use cairo_lang_utils::unordered_hash_map::{Entry, UnorderedHashMap};
22use cairo_lang_utils::{Intern, LookupIntern, extract_matches, try_extract_matches};
23use defs::ids::TopLevelLanguageElementId;
24use itertools::{Itertools, chain, izip, zip_eq};
25use num_bigint::{BigInt, Sign};
26use num_traits::ToPrimitive;
27use refs::ClosureInfo;
28use semantic::corelib::{
29    core_submodule, get_core_function_id, get_core_ty_by_name, get_function_id, never_ty, unit_ty,
30};
31use semantic::items::constant::{ConstValue, value_as_const_value};
32use semantic::types::{peel_snapshots, wrap_in_snapshots};
33use semantic::{
34    ExprFunctionCallArg, ExprId, ExprPropagateError, ExprVarMemberPath, GenericArgumentId,
35    MatchArmSelector, SemanticDiagnostic, TypeLongId,
36};
37use {cairo_lang_defs as defs, cairo_lang_semantic as semantic};
38
39use self::block_builder::SealedBlockBuilder;
40use self::context::{
41    EncapsulatingLoweringContext, LoweredExpr, LoweredExprExternEnum, LoweringContext,
42    LoweringFlowError, lowering_flow_error_to_sealed_block,
43};
44use self::external::{extern_facade_expr, extern_facade_return_tys};
45use self::logical_op::lower_logical_op;
46use self::lower_if::lower_expr_if;
47use self::lower_match::lower_expr_match;
48use crate::blocks::Blocks;
49use crate::db::LoweringGroup;
50use crate::diagnostic::LoweringDiagnosticKind::{self, *};
51use crate::diagnostic::{LoweringDiagnosticsBuilder, MatchDiagnostic, MatchError, MatchKind};
52use crate::ids::{
53    FunctionLongId, FunctionWithBodyId, FunctionWithBodyLongId, GeneratedFunction,
54    GeneratedFunctionKey, LocationId, SemanticFunctionIdEx, Signature, parameter_as_member_path,
55};
56use crate::lower::context::{LoopContext, LoopEarlyReturnInfo, LoweringResult, VarRequest};
57use crate::lower::generators::StructDestructure;
58use crate::lower::lower_match::MatchArmWrapper;
59use crate::{
60    BlockId, Lowered, MatchArm, MatchEnumInfo, MatchExternInfo, MatchInfo, VarUsage, VariableId,
61};
62
63mod block_builder;
64pub mod context;
65mod external;
66pub mod generators;
67mod logical_op;
68mod lower_if;
69mod lower_let_else;
70mod lower_match;
71pub mod refs;
72
73#[cfg(test)]
74mod generated_test;
75
76#[cfg(test)]
77mod specialized_test;
78
79/// Lowering of a function together with extra generated functions.
80#[derive(Clone, Debug, PartialEq, Eq)]
81pub struct MultiLowering {
82    pub main_lowering: Lowered,
83    pub generated_lowerings: OrderedHashMap<GeneratedFunctionKey, Lowered>,
84}
85
86/// Lowers a semantic free function.
87pub fn lower_semantic_function(
88    db: &dyn LoweringGroup,
89    semantic_function_id: defs::ids::FunctionWithBodyId,
90) -> Maybe<MultiLowering> {
91    let declaration_diagnostics = db.function_declaration_diagnostics(semantic_function_id);
92    check_error_free_or_warn(db, declaration_diagnostics, semantic_function_id, "declaration")?;
93    let body_diagnostics = db.function_body_diagnostics(semantic_function_id);
94    check_error_free_or_warn(db, body_diagnostics, semantic_function_id, "body")?;
95
96    let mut encapsulating_ctx = EncapsulatingLoweringContext::new(db, semantic_function_id)?;
97    let function_id = FunctionWithBodyLongId::Semantic(semantic_function_id).intern(db);
98    let signature = db.function_with_body_signature(semantic_function_id)?;
99
100    // TODO(spapini): Build semantic_defs in semantic model.
101    for semantic_var in &signature.params {
102        encapsulating_ctx.semantic_defs.insert(
103            semantic::VarId::Param(semantic_var.id),
104            semantic::Binding::Param(semantic_var.clone()),
105        );
106    }
107
108    let block_expr_id = encapsulating_ctx.function_body.body_expr;
109    let main_lowering = lower_function(
110        &mut encapsulating_ctx,
111        function_id,
112        Signature::from_semantic(db, signature),
113        block_expr_id,
114    )?;
115    Ok(MultiLowering { main_lowering, generated_lowerings: encapsulating_ctx.lowerings })
116}
117
118/// Lowers a function into [Lowered].
119pub fn lower_function(
120    encapsulating_ctx: &mut EncapsulatingLoweringContext<'_>,
121    function_id: FunctionWithBodyId,
122    signature: Signature,
123    block_expr_id: semantic::ExprId,
124) -> Maybe<Lowered> {
125    log::trace!("Lowering a free function.");
126    let return_type = signature.return_type;
127    let mut ctx = LoweringContext::new(encapsulating_ctx, function_id, signature, return_type)?;
128
129    // Fetch body block expr.
130    let semantic_block =
131        extract_matches!(&ctx.function_body.arenas.exprs[block_expr_id], semantic::Expr::Block)
132            .clone();
133
134    // Initialize builder.
135    let root_block_id = alloc_empty_block(&mut ctx);
136    let mut builder = BlockBuilder::root(root_block_id);
137
138    let parameters = ctx
139        .signature
140        .params
141        .clone()
142        .into_iter()
143        .map(|param| {
144            let location = ctx.get_location(param.stable_ptr().untyped());
145            let var = ctx.new_var(VarRequest { ty: param.ty(), location });
146            // TODO(spapini): Introduce member paths, not just base variables.
147            let param_var = extract_matches!(param, ExprVarMemberPath::Var);
148            builder.put_semantic(param_var.var, var);
149            var
150        })
151        .collect_vec();
152
153    let root_ok = {
154        let maybe_sealed_block = lower_block(&mut ctx, builder, &semantic_block);
155        maybe_sealed_block.and_then(|block_sealed| {
156            wrap_sealed_block_as_function(
157                &mut ctx,
158                block_sealed,
159                semantic_block.stable_ptr.untyped(),
160            )?;
161            Ok(root_block_id)
162        })
163    };
164    let blocks = root_ok
165        .map(|_| ctx.blocks.build().expect("Root block must exist."))
166        .unwrap_or_else(Blocks::new_errored);
167    Ok(Lowered {
168        diagnostics: ctx.diagnostics.build(),
169        variables: ctx.variables.variables,
170        blocks,
171        signature: ctx.signature.clone(),
172        parameters,
173    })
174}
175
176/// Lowers an expression of type [semantic::ExprFor].
177pub fn lower_for_loop(
178    ctx: &mut LoweringContext<'_, '_>,
179    builder: &mut BlockBuilder,
180    loop_expr: semantic::ExprFor,
181    loop_expr_id: semantic::ExprId,
182) -> LoweringResult<LoweredExpr> {
183    let db = ctx.db;
184    let for_location = ctx.get_location(loop_expr.stable_ptr.untyped());
185    let next_semantic_signature =
186        db.concrete_function_signature(loop_expr.next_function_id).unwrap();
187    let into_iter = builder.get_ref(ctx, &loop_expr.into_iter_member_path).unwrap();
188    let next_call = generators::Call {
189        function: loop_expr.next_function_id.lowered(db),
190        inputs: vec![into_iter],
191        coupon_input: None,
192        extra_ret_tys: vec![next_semantic_signature.params.first().unwrap().ty],
193        ret_tys: vec![next_semantic_signature.return_type],
194        location: for_location,
195    }
196    .add(ctx, &mut builder.statements);
197    let next_iterator = next_call.extra_outputs.first().unwrap();
198    let next_value = next_call.returns.first().unwrap();
199    let ErrorPropagationType::Option { some_variant, none_variant } =
200        unwrap_error_propagation_type(db, ctx.variables[next_value.var_id].ty)
201            .expect("Expected Option type for next function return.")
202    else {
203        unreachable!("Return type for next function must be Option.")
204    };
205    let next_value_type = some_variant.ty;
206    builder.update_ref(ctx, &loop_expr.into_iter_member_path, next_iterator.var_id);
207    let unit_ty = corelib::unit_ty(db);
208    let some_block: cairo_lang_semantic::ExprBlock =
209        extract_matches!(&ctx.function_body.arenas.exprs[loop_expr.body], semantic::Expr::Block)
210            .clone();
211    let mut some_subscope = create_subscope(ctx, builder);
212    let some_subscope_block_id = some_subscope.block_id;
213    let some_var_id = ctx.new_var(VarRequest {
214        ty: next_value_type,
215        location: ctx.get_location(some_block.stable_ptr.untyped()),
216    });
217    let variant_expr = LoweredExpr::AtVariable(VarUsage {
218        var_id: some_var_id,
219        location: ctx.get_location(some_block.stable_ptr.untyped()),
220    });
221    let lowered_pattern =
222        lower_single_pattern(ctx, &mut some_subscope, loop_expr.pattern, variant_expr);
223    let sealed_some = match lowered_pattern {
224        Ok(_) => {
225            let block_expr = (|| {
226                lower_expr_block(ctx, &mut some_subscope, &some_block)?;
227                recursively_call_loop_func(
228                    ctx,
229                    &mut some_subscope,
230                    loop_expr_id,
231                    loop_expr.stable_ptr.untyped(),
232                )
233            })();
234            lowered_expr_to_block_scope_end(ctx, some_subscope, block_expr)
235        }
236        Err(err) => lowering_flow_error_to_sealed_block(ctx, some_subscope.clone(), err),
237    }
238    .map_err(LoweringFlowError::Failed)?;
239
240    let none_subscope = create_subscope(ctx, builder);
241    let none_var_id = ctx.new_var(VarRequest {
242        ty: unit_ty,
243        location: ctx.get_location(some_block.stable_ptr.untyped()),
244    });
245    let none_subscope_block_id = none_subscope.block_id;
246    let sealed_none = return_a_unit(ctx, none_subscope, for_location, false)?;
247
248    let match_info = MatchInfo::Enum(MatchEnumInfo {
249        concrete_enum_id: some_variant.concrete_enum_id,
250        input: *next_value,
251        arms: vec![
252            MatchArm {
253                arm_selector: MatchArmSelector::VariantId(some_variant),
254                block_id: some_subscope_block_id,
255                var_ids: vec![some_var_id],
256            },
257            MatchArm {
258                arm_selector: MatchArmSelector::VariantId(none_variant),
259                block_id: none_subscope_block_id,
260                var_ids: vec![none_var_id],
261            },
262        ],
263        location: for_location,
264    });
265    builder.merge_and_end_with_match(ctx, match_info, vec![sealed_some, sealed_none], for_location)
266}
267
268/// Lowers an expression of type [semantic::ExprWhile].
269pub fn lower_while_loop(
270    ctx: &mut LoweringContext<'_, '_>,
271    builder: &mut BlockBuilder,
272    loop_expr: semantic::ExprWhile,
273    loop_expr_id: semantic::ExprId,
274) -> LoweringResult<LoweredExpr> {
275    let while_location = ctx.get_location(loop_expr.stable_ptr.untyped());
276    let semantic_condition = match &loop_expr.condition {
277        semantic::Condition::BoolExpr(semantic_condition) => *semantic_condition,
278        semantic::Condition::Let(match_expr, patterns) => {
279            return (|| {
280                let ret_var = lower_expr_while_let(
281                    ctx,
282                    builder,
283                    &loop_expr,
284                    *match_expr,
285                    patterns,
286                    MatchKind::WhileLet(loop_expr_id, loop_expr.stable_ptr.untyped()),
287                )?
288                .as_var_usage(ctx, builder)?;
289
290                lower_return(ctx, builder, ret_var, while_location, false)
291            })();
292        }
293    };
294    let condition = lower_expr_to_var_usage(ctx, builder, semantic_condition)?;
295    let db = ctx.db;
296    let unit_ty = corelib::unit_ty(db);
297
298    // Main block.
299    let mut subscope_main = create_subscope(ctx, builder);
300    let block_main_id = subscope_main.block_id;
301    let main_block =
302        extract_matches!(&ctx.function_body.arenas.exprs[loop_expr.body], semantic::Expr::Block)
303            .clone();
304    let main_block_var_id = ctx.new_var(VarRequest {
305        ty: unit_ty,
306        location: ctx.get_location(main_block.stable_ptr.untyped()),
307    });
308
309    let block_expr = (|| {
310        lower_expr_block(ctx, &mut subscope_main, &main_block)?;
311        recursively_call_loop_func(
312            ctx,
313            &mut subscope_main,
314            loop_expr_id,
315            loop_expr.stable_ptr.untyped(),
316        )
317    })();
318    let block_main = lowered_expr_to_block_scope_end(ctx, subscope_main, block_expr)
319        .map_err(LoweringFlowError::Failed)?;
320
321    // Empty else block.
322    let subscope_else = create_subscope(ctx, builder);
323    let block_else_id = subscope_else.block_id;
324    let else_block_input_var_id = ctx.new_var(VarRequest { ty: unit_ty, location: while_location });
325    let block_else = return_a_unit(ctx, subscope_else, while_location, false)?;
326
327    let match_info = MatchInfo::Enum(MatchEnumInfo {
328        concrete_enum_id: corelib::core_bool_enum(db),
329        input: condition,
330        arms: vec![
331            MatchArm {
332                arm_selector: MatchArmSelector::VariantId(corelib::false_variant(db)),
333                block_id: block_else_id,
334                var_ids: vec![else_block_input_var_id],
335            },
336            MatchArm {
337                arm_selector: MatchArmSelector::VariantId(corelib::true_variant(db)),
338                block_id: block_main_id,
339                var_ids: vec![main_block_var_id],
340            },
341        ],
342        location: while_location,
343    });
344    builder.merge_and_end_with_match(ctx, match_info, vec![block_main, block_else], while_location)
345}
346
347/// Lowers an expression of type if where the condition is of type [semantic::Condition::Let].
348pub fn lower_expr_while_let(
349    ctx: &mut LoweringContext<'_, '_>,
350    builder: &mut BlockBuilder,
351    loop_expr: &semantic::ExprWhile,
352    matched_expr: semantic::ExprId,
353    patterns: &[semantic::PatternId],
354    match_type: MatchKind,
355) -> LoweringResult<LoweredExpr> {
356    log::trace!("Lowering a match expression: {:?}", loop_expr.debug(&ctx.expr_formatter));
357    let location = ctx.get_location(loop_expr.stable_ptr.untyped());
358    let lowered_expr = lower_expr(ctx, builder, matched_expr)?;
359
360    let ty = ctx.function_body.arenas.exprs[matched_expr].ty();
361
362    if corelib::numeric_upcastable_to_felt252(ctx.db, ty) {
363        return Err(LoweringFlowError::Failed(ctx.diagnostics.report(
364            loop_expr.stable_ptr.untyped(),
365            LoweringDiagnosticKind::MatchError(MatchError {
366                kind: match_type,
367                error: MatchDiagnostic::UnsupportedNumericInLetCondition,
368            }),
369        )));
370    }
371
372    let arms = vec![MatchArmWrapper::Arm(patterns, loop_expr.body), MatchArmWrapper::DefaultClause];
373
374    lower_match::lower_match_arms(
375        ctx,
376        builder,
377        matched_expr,
378        lowered_expr,
379        arms,
380        location,
381        match_type,
382    )
383}
384
385/// Lowers a loop inner function into [Lowered].
386/// Similar to `lower_function`, but adds a recursive call.
387// TODO(spapini): Unite with `lower_function`.
388pub fn lower_loop_function(
389    encapsulating_ctx: &mut EncapsulatingLoweringContext<'_>,
390    function_id: FunctionWithBodyId,
391    loop_signature: Signature,
392    loop_ctx: LoopContext,
393    return_type: semantic::TypeId,
394) -> Maybe<Lowered> {
395    let loop_expr_id = loop_ctx.loop_expr_id;
396    let mut ctx =
397        LoweringContext::new(encapsulating_ctx, function_id, loop_signature, return_type)?;
398    let old_loop_ctx = ctx.current_loop_ctx.replace(loop_ctx);
399
400    // Initialize builder.
401    let root_block_id = alloc_empty_block(&mut ctx);
402    let mut builder = BlockBuilder::root(root_block_id);
403
404    let snapped_params = ctx.usages.usages[&loop_expr_id].snap_usage.clone();
405    let parameters = ctx
406        .signature
407        .params
408        .clone()
409        .into_iter()
410        .map(|param| {
411            let location = ctx.get_location(param.stable_ptr().untyped());
412            let var = ctx.new_var(VarRequest { ty: param.ty(), location });
413            if snapped_params.contains_key::<MemberPath>(&(&param).into()) {
414                builder.introduce_snap((&param).into(), var)
415            } else {
416                builder.introduce((&param).into(), var);
417            }
418            var
419        })
420        .collect_vec();
421
422    let root_ok = (|| {
423        let (block_expr, stable_ptr) = match ctx.function_body.arenas.exprs[loop_expr_id].clone() {
424            semantic::Expr::Loop(semantic::ExprLoop { body, stable_ptr, .. }) => {
425                // Fetch body block expr.
426                let semantic_block =
427                    extract_matches!(&ctx.function_body.arenas.exprs[body], semantic::Expr::Block)
428                        .clone();
429
430                let block_expr = (|| {
431                    lower_expr_block(&mut ctx, &mut builder, &semantic_block)?;
432                    recursively_call_loop_func(
433                        &mut ctx,
434                        &mut builder,
435                        loop_expr_id,
436                        stable_ptr.untyped(),
437                    )
438                })();
439                (block_expr, stable_ptr)
440            }
441
442            semantic::Expr::While(while_expr) => {
443                let stable_ptr = while_expr.stable_ptr;
444                let location = ctx.get_location(stable_ptr.untyped());
445                let block_expr = (|| {
446                    let ret_var =
447                        lower_while_loop(&mut ctx, &mut builder, while_expr, loop_expr_id)?
448                            .as_var_usage(&mut ctx, &mut builder)?;
449
450                    lower_return(&mut ctx, &mut builder, ret_var, location, false)
451                })();
452                (block_expr, stable_ptr)
453            }
454
455            semantic::Expr::For(for_expr) => {
456                let stable_ptr: cairo_lang_syntax::node::ast::ExprPtr = for_expr.stable_ptr;
457                let block_expr: Result<LoweredExpr, LoweringFlowError> =
458                    lower_for_loop(&mut ctx, &mut builder, for_expr, loop_expr_id);
459                (block_expr, stable_ptr)
460            }
461            _ => unreachable!("Loop expression must be either loop, while or for."),
462        };
463
464        let block_sealed = lowered_expr_to_block_scope_end(&mut ctx, builder, block_expr)?;
465        wrap_sealed_block_as_function(&mut ctx, block_sealed, stable_ptr.untyped())?;
466
467        Ok(root_block_id)
468    })();
469    ctx.current_loop_ctx = old_loop_ctx;
470
471    let blocks = root_ok
472        .map(|_| ctx.blocks.build().expect("Root block must exist."))
473        .unwrap_or_else(Blocks::new_errored);
474    Ok(Lowered {
475        diagnostics: ctx.diagnostics.build(),
476        variables: ctx.variables.variables,
477        blocks,
478        signature: ctx.signature.clone(),
479        parameters,
480    })
481}
482
483/// Wraps `block_sealed` as the root block of a function.
484fn wrap_sealed_block_as_function(
485    ctx: &mut LoweringContext<'_, '_>,
486    block_sealed: SealedBlockBuilder,
487    stable_ptr: SyntaxStablePtrId,
488) -> Maybe<()> {
489    let SealedBlockBuilder::GotoCallsite { mut builder, expr } = block_sealed else {
490        return Ok(());
491    };
492    let location = ctx.get_location(stable_ptr);
493    match &expr {
494        Some(expr) if ctx.variables[expr.var_id].ty == never_ty(ctx.db) => {
495            // If the expression is of type never, then the block is unreachable, so add a match on
496            // never to make it a viable block end.
497            let semantic::TypeLongId::Concrete(semantic::ConcreteTypeId::Enum(concrete_enum_id)) =
498                ctx.variables[expr.var_id].ty.lookup_intern(ctx.db)
499            else {
500                unreachable!("Never type must be a concrete enum.");
501            };
502            builder.unreachable_match(
503                ctx,
504                MatchInfo::Enum(MatchEnumInfo {
505                    concrete_enum_id,
506                    input: *expr,
507                    arms: vec![],
508                    location,
509                }),
510            );
511            Ok(())
512        }
513        _ => {
514            // Convert to a return.
515            let var_usage = expr.unwrap_or_else(|| {
516                generators::StructConstruct { inputs: vec![], ty: unit_ty(ctx.db), location }
517                    .add(ctx, &mut builder.statements)
518            });
519            builder.ret(ctx, var_usage, location)
520        }
521    }
522}
523
524/// Lowers a semantic block.
525fn lower_block(
526    ctx: &mut LoweringContext<'_, '_>,
527    mut builder: BlockBuilder,
528    semantic_block: &semantic::ExprBlock,
529) -> Maybe<SealedBlockBuilder> {
530    let block_expr = lower_expr_block(ctx, &mut builder, semantic_block);
531    lowered_expr_to_block_scope_end(ctx, builder, block_expr)
532}
533
534/// Lowers a semantic block.
535fn lower_expr_block(
536    ctx: &mut LoweringContext<'_, '_>,
537    builder: &mut BlockBuilder,
538    expr_block: &semantic::ExprBlock,
539) -> LoweringResult<LoweredExpr> {
540    log::trace!("Lowering a block.");
541    for (i, stmt_id) in expr_block.statements.iter().enumerate() {
542        let stmt = ctx.function_body.arenas.statements[*stmt_id].clone();
543        let Err(err) = lower_statement(ctx, builder, &stmt) else {
544            continue;
545        };
546        if err.is_unreachable() {
547            let stmt_ptr = |id| ctx.function_body.arenas.statements[id].stable_ptr().untyped();
548            let tail_ptr =
549                expr_block.tail.map(|id| ctx.function_body.arenas.exprs[id].stable_ptr().untyped());
550            // If flow is not reachable anymore, no need to continue emitting statements.
551            if let Some(start_ptr) =
552                expr_block.statements.get(i + 1).copied().map(stmt_ptr).or(tail_ptr)
553            {
554                let end_ptr = tail_ptr
555                    .or_else(|| expr_block.statements.last().copied().map(stmt_ptr))
556                    .unwrap();
557                // Emit diagnostic for the rest of the block with unreachable.
558                ctx.diagnostics.report(start_ptr, Unreachable { block_end_ptr: end_ptr });
559            }
560        }
561        return Err(err);
562    }
563    // Determine correct block end.
564    let location = ctx.get_location(expr_block.stable_ptr.untyped());
565    expr_block
566        .tail
567        .map(|expr| lower_expr(ctx, builder, expr))
568        .unwrap_or_else(|| Ok(LoweredExpr::Tuple { exprs: vec![], location }))
569}
570
571/// Lowers an expression that is either a complete block, or the end (tail expression) of a
572/// block.
573pub fn lower_tail_expr(
574    ctx: &mut LoweringContext<'_, '_>,
575    mut builder: BlockBuilder,
576    expr: semantic::ExprId,
577) -> Maybe<SealedBlockBuilder> {
578    log::trace!("Lowering a tail expression.");
579    let lowered_expr = lower_expr(ctx, &mut builder, expr);
580    lowered_expr_to_block_scope_end(ctx, builder, lowered_expr)
581}
582
583/// Converts [`LoweringResult<LoweredExpr>`] into `BlockScopeEnd`.
584pub fn lowered_expr_to_block_scope_end(
585    ctx: &mut LoweringContext<'_, '_>,
586    mut builder: BlockBuilder,
587    lowered_expr: LoweringResult<LoweredExpr>,
588) -> Maybe<SealedBlockBuilder> {
589    Ok(match lowered_expr {
590        Ok(LoweredExpr::Tuple { exprs, .. }) if exprs.is_empty() => builder.goto_callsite(None),
591        Ok(lowered_expr) => match lowered_expr.as_var_usage(ctx, &mut builder) {
592            Ok(var) => builder.goto_callsite(Some(var)),
593            Err(err) => lowering_flow_error_to_sealed_block(ctx, builder, err)?,
594        },
595        Err(err) => lowering_flow_error_to_sealed_block(ctx, builder, err)?,
596    })
597}
598
599/// Generates the lowering for a return `ret_expr`
600/// in the case where we are inside a loop `is_early_return` indicates if this is a normal return
601/// or an early return.
602pub fn lower_return(
603    ctx: &mut LoweringContext<'_, '_>,
604    builder: &mut BlockBuilder,
605    mut ret_var: VarUsage,
606    location: LocationId,
607    is_early_return: bool,
608) -> Result<LoweredExpr, LoweringFlowError> {
609    if let Some(LoopContext {
610        early_return_info: Some(LoopEarlyReturnInfo { normal_return_variant, early_return_variant }),
611        ..
612    }) = &ctx.current_loop_ctx
613    {
614        let variant = if is_early_return { early_return_variant } else { normal_return_variant };
615
616        ret_var = generators::EnumConstruct { input: ret_var, variant: *variant, location }
617            .add(ctx, &mut builder.statements);
618    }
619
620    Err(LoweringFlowError::Return(ret_var, location))
621}
622
623/// Generates lowering to return a unit.
624pub fn return_a_unit(
625    ctx: &mut LoweringContext<'_, '_>,
626    mut builder: BlockBuilder,
627    location: LocationId,
628    is_early_return: bool,
629) -> Result<SealedBlockBuilder, LoweringFlowError> {
630    let ret_var = LoweredExpr::Tuple { exprs: vec![], location }.as_var_usage(ctx, &mut builder)?;
631
632    let ret_expr = lower_return(ctx, &mut builder, ret_var, location, is_early_return);
633    lowered_expr_to_block_scope_end(ctx, builder, ret_expr).map_err(LoweringFlowError::Failed)
634}
635
636/// Lowers a semantic statement.
637pub fn lower_statement(
638    ctx: &mut LoweringContext<'_, '_>,
639    builder: &mut BlockBuilder,
640    stmt: &semantic::Statement,
641) -> Result<(), LoweringFlowError> {
642    match stmt {
643        semantic::Statement::Expr(semantic::StatementExpr { expr, stable_ptr: _ }) => {
644            log::trace!("Lowering an expression statement.");
645            let lowered_expr = lower_expr(ctx, builder, *expr)?;
646            // The LoweredExpr must be evaluated now to push/bring back variables in case it is
647            // LoweredExpr::ExternEnum.
648            if let LoweredExpr::ExternEnum(x) = lowered_expr {
649                x.as_var_usage(ctx, builder)?;
650            }
651        }
652        semantic::Statement::Let(semantic::StatementLet {
653            pattern,
654            expr,
655            else_clause,
656            stable_ptr,
657        }) => {
658            log::trace!("Lowering a let statement.");
659            let lowered_expr = lower_expr(ctx, builder, *expr)?;
660            if let Some(else_clause) = else_clause {
661                lower_let_else::lower_let_else(
662                    ctx,
663                    builder,
664                    pattern,
665                    expr,
666                    lowered_expr,
667                    else_clause,
668                    stable_ptr,
669                )?;
670            } else {
671                lower_single_pattern(ctx, builder, *pattern, lowered_expr)?;
672            }
673        }
674        semantic::Statement::Continue(semantic::StatementContinue { stable_ptr }) => {
675            log::trace!("Lowering a continue statement.");
676            recursively_call_loop_func(
677                ctx,
678                builder,
679                ctx.current_loop_ctx.as_ref().unwrap().loop_expr_id,
680                stable_ptr.untyped(),
681            )?;
682            return Ok(());
683        }
684        semantic::Statement::Return(semantic::StatementReturn { expr_option, stable_ptr })
685        | semantic::Statement::Break(semantic::StatementBreak { expr_option, stable_ptr }) => {
686            log::trace!("Lowering a return | break statement.");
687            let location = ctx.get_location(stable_ptr.untyped());
688            let ret_var = match expr_option {
689                None => {
690                    LoweredExpr::Tuple { exprs: vec![], location }.as_var_usage(ctx, builder)?
691                }
692                Some(expr) => lower_expr_to_var_usage(ctx, builder, *expr)?,
693            };
694
695            lower_return(
696                ctx,
697                builder,
698                ret_var,
699                location,
700                matches!(stmt, semantic::Statement::Return(_)),
701            )?;
702        }
703        semantic::Statement::Item(_) => {}
704    }
705    Ok(())
706}
707
708// TODO(spapini): Separate match pattern from non-match (single) patterns in the semantic
709// model.
710/// Lowers a single-pattern (pattern that does not appear in a match. This includes structs,
711/// tuples, variables, etc...
712/// Adds the bound variables to the builder.
713/// Note that single patterns are the only way to bind new local variables in the semantic
714/// model.
715fn lower_single_pattern(
716    ctx: &mut LoweringContext<'_, '_>,
717    builder: &mut BlockBuilder,
718    pattern_id: semantic::PatternId,
719    lowered_expr: LoweredExpr,
720) -> Result<(), LoweringFlowError> {
721    log::trace!("Lowering a single pattern.");
722    let pattern = &ctx.function_body.arenas.patterns[pattern_id];
723    match pattern {
724        semantic::Pattern::Literal(_)
725        | semantic::Pattern::StringLiteral(_)
726        | semantic::Pattern::EnumVariant(_) => {
727            return Err(LoweringFlowError::Failed(
728                ctx.diagnostics.report(pattern.stable_ptr(), UnsupportedPattern),
729            ));
730        }
731        semantic::Pattern::Variable(semantic::PatternVariable {
732            name: _,
733            var: sem_var,
734            stable_ptr,
735        }) => {
736            let sem_var = semantic::Binding::LocalVar(sem_var.clone());
737            let stable_ptr = *stable_ptr;
738            // Deposit the owned variable in the semantic variables store.
739            let var = lowered_expr.as_var_usage(ctx, builder)?.var_id;
740            // Override variable location.
741            ctx.variables.variables[var].location = ctx.get_location(stable_ptr.untyped());
742            builder.put_semantic(sem_var.id(), var);
743            // TODO(spapini): Build semantic_defs in semantic model.
744            ctx.semantic_defs.insert(sem_var.id(), sem_var);
745        }
746        semantic::Pattern::Struct(structure) => {
747            let members = ctx
748                .db
749                .concrete_struct_members(structure.concrete_struct_id)
750                .map_err(LoweringFlowError::Failed)?;
751            let mut required_members = UnorderedHashMap::<_, _>::from_iter(
752                structure.field_patterns.iter().map(|(member, pattern)| (member.id, *pattern)),
753            );
754            let n_snapshots = structure.n_snapshots;
755            let stable_ptr = structure.stable_ptr.untyped();
756            let generator = generators::StructDestructure {
757                input: lowered_expr.as_var_usage(ctx, builder)?,
758                var_reqs: members
759                    .iter()
760                    .map(|(_, member)| VarRequest {
761                        ty: wrap_in_snapshots(ctx.db, member.ty, n_snapshots),
762                        location: ctx.get_location(
763                            required_members
764                                .get(&member.id)
765                                .map(|pattern| {
766                                    ctx.function_body.arenas.patterns[*pattern]
767                                        .stable_ptr()
768                                        .untyped()
769                                })
770                                .unwrap_or_else(|| stable_ptr),
771                        ),
772                    })
773                    .collect(),
774            };
775            for (var_id, (_, member)) in
776                izip!(generator.add(ctx, &mut builder.statements), members.iter())
777            {
778                if let Some(member_pattern) = required_members.remove(&member.id) {
779                    let stable_ptr = ctx.function_body.arenas.patterns[member_pattern].stable_ptr();
780                    lower_single_pattern(
781                        ctx,
782                        builder,
783                        member_pattern,
784                        LoweredExpr::AtVariable(VarUsage {
785                            var_id,
786                            location: ctx.get_location(stable_ptr.untyped()),
787                        }),
788                    )?;
789                }
790            }
791        }
792        semantic::Pattern::Tuple(semantic::PatternTuple {
793            field_patterns: patterns, ty, ..
794        })
795        | semantic::Pattern::FixedSizeArray(semantic::PatternFixedSizeArray {
796            elements_patterns: patterns,
797            ty,
798            ..
799        }) => {
800            let patterns = patterns.clone();
801            lower_tuple_like_pattern_helper(ctx, builder, lowered_expr, &patterns, *ty)?;
802        }
803        semantic::Pattern::Otherwise(pattern) => {
804            let stable_ptr = pattern.stable_ptr.untyped();
805            let var = lowered_expr.as_var_usage(ctx, builder)?.var_id;
806            ctx.variables.variables[var].location = ctx.get_location(stable_ptr);
807        }
808        semantic::Pattern::Missing(_) => unreachable!("Missing pattern in semantic model."),
809    }
810    Ok(())
811}
812
813/// A helper function to handle patterns of tuples or fixed size arrays.
814fn lower_tuple_like_pattern_helper(
815    ctx: &mut LoweringContext<'_, '_>,
816    builder: &mut BlockBuilder,
817    lowered_expr: LoweredExpr,
818    patterns: &[semantic::PatternId],
819    ty: semantic::TypeId,
820) -> LoweringResult<()> {
821    let outputs = match lowered_expr {
822        LoweredExpr::Tuple { exprs, .. } => exprs,
823        LoweredExpr::FixedSizeArray { exprs, .. } => exprs,
824        _ => {
825            let (n_snapshots, long_type_id) = peel_snapshots(ctx.db, ty);
826            let tys = match long_type_id {
827                TypeLongId::Tuple(tys) => tys,
828                TypeLongId::FixedSizeArray { type_id, size } => {
829                    let size = size
830                        .lookup_intern(ctx.db)
831                        .into_int()
832                        .expect("Expected ConstValue::Int for size")
833                        .to_usize()
834                        .unwrap();
835                    vec![type_id; size]
836                }
837                _ => unreachable!("Tuple-like pattern must be a tuple or fixed size array."),
838            };
839            let reqs = patterns
840                .iter()
841                .zip_eq(tys)
842                .map(|(pattern, ty)| VarRequest {
843                    ty: wrap_in_snapshots(ctx.db, ty, n_snapshots),
844                    location: ctx.get_location(
845                        ctx.function_body.arenas.patterns[*pattern].stable_ptr().untyped(),
846                    ),
847                })
848                .collect();
849            generators::StructDestructure {
850                input: lowered_expr.as_var_usage(ctx, builder)?,
851                var_reqs: reqs,
852            }
853            .add(ctx, &mut builder.statements)
854            .into_iter()
855            .map(|var_id| {
856                LoweredExpr::AtVariable(VarUsage {
857                    var_id,
858                    location: ctx.variables[var_id].location,
859                })
860            })
861            .collect()
862        }
863    };
864    for (var, pattern) in zip_eq(outputs, patterns) {
865        lower_single_pattern(ctx, builder, *pattern, var)?;
866    }
867    Ok(())
868}
869
870/// Lowers a semantic expression to a VarUsage.
871///
872/// For example, if we have the code:
873/// foo(a + b)
874///
875/// then `a + b` will be assigned  variable and a VarUsage object whose origin
876/// is the location of the `a + b` expression.
877fn lower_expr_to_var_usage(
878    ctx: &mut LoweringContext<'_, '_>,
879    builder: &mut BlockBuilder,
880    expr_id: semantic::ExprId,
881) -> LoweringResult<VarUsage> {
882    lower_expr(ctx, builder, expr_id)?.as_var_usage(ctx, builder)
883}
884
885/// Lowers a semantic expression.
886fn lower_expr(
887    ctx: &mut LoweringContext<'_, '_>,
888    builder: &mut BlockBuilder,
889    expr_id: semantic::ExprId,
890) -> LoweringResult<LoweredExpr> {
891    let expr = ctx.function_body.arenas.exprs[expr_id].clone();
892    match &expr {
893        semantic::Expr::Constant(expr) => lower_expr_constant(ctx, expr, builder),
894        semantic::Expr::Tuple(expr) => lower_expr_tuple(ctx, expr, builder),
895        semantic::Expr::Snapshot(expr) => lower_expr_snapshot(ctx, expr, builder),
896        semantic::Expr::Desnap(expr) => lower_expr_desnap(ctx, expr, builder),
897        semantic::Expr::Assignment(expr) => lower_expr_assignment(ctx, expr, builder),
898        semantic::Expr::LogicalOperator(expr) => lower_logical_op(ctx, builder, expr),
899        semantic::Expr::Block(expr) => lower_expr_block(ctx, builder, expr),
900        semantic::Expr::FunctionCall(expr) => lower_expr_function_call(ctx, expr, builder),
901        semantic::Expr::Match(expr) => lower_expr_match(ctx, expr, builder),
902        semantic::Expr::If(expr) => lower_expr_if(ctx, builder, expr),
903        semantic::Expr::Loop(_) | semantic::Expr::While(_) | semantic::Expr::For(_) => {
904            lower_expr_loop(ctx, builder, expr_id)
905        }
906        semantic::Expr::Var(expr) => {
907            let member_path = ExprVarMemberPath::Var(expr.clone());
908            log::trace!("Lowering a variable: {:?}", expr.debug(&ctx.expr_formatter));
909            Ok(LoweredExpr::Member(member_path, ctx.get_location(expr.stable_ptr.untyped())))
910        }
911        semantic::Expr::Literal(expr) => lower_expr_literal(ctx, expr, builder),
912        semantic::Expr::StringLiteral(expr) => lower_expr_string_literal(ctx, expr, builder),
913        semantic::Expr::MemberAccess(expr) => lower_expr_member_access(ctx, expr, builder),
914        semantic::Expr::StructCtor(expr) => lower_expr_struct_ctor(ctx, expr, builder),
915        semantic::Expr::EnumVariantCtor(expr) => lower_expr_enum_ctor(ctx, expr, builder),
916        semantic::Expr::FixedSizeArray(expr) => lower_expr_fixed_size_array(ctx, expr, builder),
917        semantic::Expr::ExprClosure(expr) => lower_expr_closure(ctx, expr, expr_id, builder),
918        semantic::Expr::PropagateError(expr) => lower_expr_error_propagate(ctx, expr, builder),
919        semantic::Expr::Missing(semantic::ExprMissing { diag_added, .. }) => {
920            Err(LoweringFlowError::Failed(*diag_added))
921        }
922    }
923}
924
925fn lower_expr_literal(
926    ctx: &mut LoweringContext<'_, '_>,
927    expr: &semantic::ExprLiteral,
928    builder: &mut BlockBuilder,
929) -> LoweringResult<LoweredExpr> {
930    log::trace!("Lowering a literal: {:?}", expr.debug(&ctx.expr_formatter));
931    lower_expr_literal_helper(ctx, expr.stable_ptr.untyped(), expr.ty, &expr.value, builder)
932}
933
934/// Lowers a semantic expression that is a literal, possibly including a negation.
935fn lower_expr_literal_helper(
936    ctx: &mut LoweringContext<'_, '_>,
937    stable_ptr: SyntaxStablePtrId,
938    ty: semantic::TypeId,
939    value: &BigInt,
940    builder: &mut BlockBuilder,
941) -> LoweringResult<LoweredExpr> {
942    let value = value_as_const_value(ctx.db, ty, value)
943        .map_err(|err| {
944            ctx.diagnostics.report(stable_ptr, LoweringDiagnosticKind::LiteralError(err))
945        })
946        .unwrap_or_else(ConstValue::Missing);
947    let location = ctx.get_location(stable_ptr);
948    Ok(LoweredExpr::AtVariable(
949        generators::Const { value, ty, location }.add(ctx, &mut builder.statements),
950    ))
951}
952
953fn lower_expr_string_literal(
954    ctx: &mut LoweringContext<'_, '_>,
955    expr: &semantic::ExprStringLiteral,
956    builder: &mut BlockBuilder,
957) -> LoweringResult<LoweredExpr> {
958    log::trace!("Lowering a string literal: {:?}", expr.debug(&ctx.expr_formatter));
959    let db = ctx.db;
960
961    // Get all the relevant types from the corelib.
962    let bytes31_ty = get_core_ty_by_name(db, "bytes31".into(), vec![]);
963    let data_array_ty =
964        get_core_ty_by_name(db, "Array".into(), vec![GenericArgumentId::Type(bytes31_ty)]);
965    let byte_array_ty = get_core_ty_by_name(db, "ByteArray".into(), vec![]);
966
967    let array_submodule = core_submodule(db, "array");
968    let data_array_new_function = FunctionLongId::Semantic(get_function_id(
969        db,
970        array_submodule,
971        "array_new".into(),
972        vec![GenericArgumentId::Type(bytes31_ty)],
973    ))
974    .intern(ctx.db);
975    let data_array_append_function = FunctionLongId::Semantic(get_function_id(
976        db,
977        array_submodule,
978        "array_append".into(),
979        vec![GenericArgumentId::Type(bytes31_ty)],
980    ))
981    .intern(ctx.db);
982
983    // Emit lowering statements to build the ByteArray struct components.
984    let mut data_array_usage =
985        build_empty_data_array(ctx, builder, expr, data_array_new_function, data_array_ty);
986    let remainder = add_chunks_to_data_array(
987        ctx,
988        builder,
989        expr,
990        bytes31_ty,
991        &mut data_array_usage,
992        data_array_append_function,
993        data_array_ty,
994    );
995    let (pending_word_usage, pending_word_len_usage) =
996        add_pending_word(ctx, builder, expr, remainder);
997
998    // Emit the lowering statement for creating the ByteArray struct.
999    let byte_array_usage = generators::StructConstruct {
1000        inputs: vec![data_array_usage, pending_word_usage, pending_word_len_usage],
1001        ty: byte_array_ty,
1002        location: ctx.get_location(expr.stable_ptr.untyped()),
1003    }
1004    .add(ctx, &mut builder.statements);
1005
1006    Ok(LoweredExpr::AtVariable(byte_array_usage))
1007}
1008
1009/// Emits lowering statements to build an empty data array.
1010fn build_empty_data_array(
1011    ctx: &mut LoweringContext<'_, '_>,
1012    builder: &mut BlockBuilder,
1013    expr: &semantic::ExprStringLiteral,
1014    data_array_new_function: crate::ids::FunctionId,
1015    data_array_ty: semantic::TypeId,
1016) -> VarUsage {
1017    generators::Call {
1018        function: data_array_new_function,
1019        inputs: vec![],
1020        coupon_input: None,
1021        extra_ret_tys: vec![],
1022        ret_tys: vec![data_array_ty],
1023        location: ctx.get_location(expr.stable_ptr.untyped()),
1024    }
1025    .add(ctx, &mut builder.statements)
1026    .returns[0]
1027}
1028
1029/// Emits lowering statements to add 31-byte words to the given data array.
1030fn add_chunks_to_data_array<'a>(
1031    ctx: &mut LoweringContext<'_, '_>,
1032    builder: &mut BlockBuilder,
1033    expr: &'a semantic::ExprStringLiteral,
1034    bytes31_ty: semantic::TypeId,
1035    data_array_usage: &mut VarUsage,
1036    data_array_append_function: crate::ids::FunctionId,
1037    data_array_ty: semantic::TypeId,
1038) -> &'a [u8] {
1039    let expr_stable_ptr = expr.stable_ptr.untyped();
1040
1041    let chunks = expr.value.as_bytes().chunks_exact(31);
1042    let remainder = chunks.remainder();
1043    for chunk in chunks {
1044        let chunk_usage = generators::Const {
1045            value: ConstValue::Int(BigInt::from_bytes_be(Sign::Plus, chunk), bytes31_ty),
1046            ty: bytes31_ty,
1047            location: ctx.get_location(expr_stable_ptr),
1048        }
1049        .add(ctx, &mut builder.statements);
1050
1051        *data_array_usage = generators::Call {
1052            function: data_array_append_function,
1053            inputs: vec![*data_array_usage, chunk_usage],
1054            coupon_input: None,
1055            extra_ret_tys: vec![data_array_ty],
1056            ret_tys: vec![],
1057            location: ctx.get_location(expr_stable_ptr),
1058        }
1059        .add(ctx, &mut builder.statements)
1060        .extra_outputs[0];
1061    }
1062    remainder
1063}
1064
1065/// Emits lowering statements to set variables for the pending word of the
1066/// ByteArray.
1067fn add_pending_word(
1068    ctx: &mut LoweringContext<'_, '_>,
1069    builder: &mut BlockBuilder,
1070    expr: &semantic::ExprStringLiteral,
1071    pending_word_bytes: &[u8],
1072) -> (VarUsage, VarUsage) {
1073    let expr_stable_ptr = expr.stable_ptr.untyped();
1074
1075    let u32_ty = ctx.db.core_info().u32;
1076    let felt252_ty = ctx.db.core_info().felt252;
1077
1078    let pending_word_usage = generators::Const {
1079        value: ConstValue::Int(BigInt::from_bytes_be(Sign::Plus, pending_word_bytes), felt252_ty),
1080        ty: felt252_ty,
1081        location: ctx.get_location(expr_stable_ptr),
1082    }
1083    .add(ctx, &mut builder.statements);
1084
1085    let pending_word_len = expr.value.len() % 31;
1086    let pending_word_len_usage = generators::Const {
1087        value: ConstValue::Int(pending_word_len.into(), u32_ty),
1088        ty: u32_ty,
1089        location: ctx.get_location(expr_stable_ptr),
1090    }
1091    .add(ctx, &mut builder.statements);
1092    (pending_word_usage, pending_word_len_usage)
1093}
1094
1095fn lower_expr_constant(
1096    ctx: &mut LoweringContext<'_, '_>,
1097    expr: &semantic::ExprConstant,
1098    builder: &mut BlockBuilder,
1099) -> LoweringResult<LoweredExpr> {
1100    log::trace!("Lowering a constant: {:?}", expr.debug(&ctx.expr_formatter));
1101    let value = expr.const_value_id.lookup_intern(ctx.db);
1102    let ty = expr.ty;
1103
1104    let location = ctx.get_location(expr.stable_ptr.untyped());
1105    Ok(LoweredExpr::AtVariable(
1106        generators::Const { value, ty, location }.add(ctx, &mut builder.statements),
1107    ))
1108}
1109
1110/// Lowers an expression of type [semantic::ExprTuple].
1111fn lower_expr_tuple(
1112    ctx: &mut LoweringContext<'_, '_>,
1113    expr: &semantic::ExprTuple,
1114    builder: &mut BlockBuilder,
1115) -> LoweringResult<LoweredExpr> {
1116    log::trace!("Lowering a tuple: {:?}", expr.debug(&ctx.expr_formatter));
1117    let location = ctx.get_location(expr.stable_ptr.untyped());
1118    let inputs = expr
1119        .items
1120        .iter()
1121        .map(|arg_expr_id| lower_expr(ctx, builder, *arg_expr_id))
1122        .collect::<Result<Vec<_>, _>>()?;
1123    Ok(LoweredExpr::Tuple { exprs: inputs, location })
1124}
1125
1126/// Lowers an expression of type [semantic::ExprFixedSizeArray]
1127fn lower_expr_fixed_size_array(
1128    ctx: &mut LoweringContext<'_, '_>,
1129    expr: &semantic::ExprFixedSizeArray,
1130    builder: &mut BlockBuilder,
1131) -> Result<LoweredExpr, LoweringFlowError> {
1132    log::trace!("Lowering a fixed size array: {:?}", expr.debug(&ctx.expr_formatter));
1133    let location = ctx.get_location(expr.stable_ptr.untyped());
1134    let exprs = match &expr.items {
1135        semantic::FixedSizeArrayItems::Items(items) => items
1136            .iter()
1137            .map(|arg_expr_id| lower_expr(ctx, builder, *arg_expr_id))
1138            .collect::<Result<Vec<_>, _>>()?,
1139        semantic::FixedSizeArrayItems::ValueAndSize(value, size) => {
1140            let lowered_value = lower_expr(ctx, builder, *value)?;
1141            let var_usage = lowered_value.as_var_usage(ctx, builder)?;
1142            let size = size
1143                .lookup_intern(ctx.db)
1144                .into_int()
1145                .expect("Expected ConstValue::Int for size")
1146                .to_usize()
1147                .unwrap();
1148            if size == 0 {
1149                return Err(LoweringFlowError::Failed(
1150                    ctx.diagnostics
1151                        .report(expr.stable_ptr.untyped(), EmptyRepeatedElementFixedSizeArray),
1152                ));
1153            }
1154            // If there are multiple elements, the type must be copyable as we copy the var `size`
1155            // times.
1156            if size > 1 && ctx.variables[var_usage.var_id].info.copyable.is_err() {
1157                {
1158                    return Err(LoweringFlowError::Failed(
1159                        ctx.diagnostics.report(expr.stable_ptr.0, FixedSizeArrayNonCopyableType),
1160                    ));
1161                }
1162            }
1163            let expr = LoweredExpr::AtVariable(var_usage);
1164            vec![expr; size]
1165        }
1166    };
1167    Ok(LoweredExpr::FixedSizeArray { exprs, location, ty: expr.ty })
1168}
1169
1170/// Lowers an expression of type [semantic::ExprSnapshot].
1171fn lower_expr_snapshot(
1172    ctx: &mut LoweringContext<'_, '_>,
1173    expr: &semantic::ExprSnapshot,
1174    builder: &mut BlockBuilder,
1175) -> LoweringResult<LoweredExpr> {
1176    log::trace!("Lowering a snapshot: {:?}", expr.debug(&ctx.expr_formatter));
1177    let location = ctx.get_location(expr.stable_ptr.untyped());
1178    let expr = Box::new(lower_expr(ctx, builder, expr.inner)?);
1179    Ok(LoweredExpr::Snapshot { expr, location })
1180}
1181
1182/// Lowers an expression of type [semantic::ExprDesnap].
1183fn lower_expr_desnap(
1184    ctx: &mut LoweringContext<'_, '_>,
1185    expr: &semantic::ExprDesnap,
1186    builder: &mut BlockBuilder,
1187) -> LoweringResult<LoweredExpr> {
1188    log::trace!("Lowering a desnap: {:?}", expr.debug(&ctx.expr_formatter));
1189    let location = ctx.get_location(expr.stable_ptr.untyped());
1190    let expr = lower_expr(ctx, builder, expr.inner)?;
1191    if let LoweredExpr::Snapshot { expr, .. } = &expr {
1192        return Ok(expr.as_ref().clone());
1193    }
1194    let input = expr.as_var_usage(ctx, builder)?;
1195
1196    Ok(LoweredExpr::AtVariable(
1197        generators::Desnap { input, location }.add(ctx, &mut builder.statements),
1198    ))
1199}
1200
1201/// Lowers an expression of type [semantic::ExprFunctionCall].
1202fn lower_expr_function_call(
1203    ctx: &mut LoweringContext<'_, '_>,
1204    expr: &semantic::ExprFunctionCall,
1205    builder: &mut BlockBuilder,
1206) -> LoweringResult<LoweredExpr> {
1207    log::trace!("Lowering a function call expression: {:?}", expr.debug(&ctx.expr_formatter));
1208    let location = ctx.get_location(expr.stable_ptr.untyped());
1209
1210    // TODO(spapini): Use the correct stable pointer.
1211    let arg_inputs = lower_exprs_to_var_usages(ctx, &expr.args, builder)?;
1212    let ref_args_iter = expr
1213        .args
1214        .iter()
1215        .filter_map(|arg| try_extract_matches!(arg, ExprFunctionCallArg::Reference));
1216    let ref_tys = ref_args_iter.clone().map(|ref_arg| ref_arg.ty()).collect();
1217
1218    let coupon_input = if let Some(coupon_arg) = expr.coupon_arg {
1219        Some(lower_expr_to_var_usage(ctx, builder, coupon_arg)?)
1220    } else {
1221        None
1222    };
1223
1224    // If the function is panic(), do something special.
1225    if expr.function == get_core_function_id(ctx.db, "panic".into(), vec![]) {
1226        let [input] = <[_; 1]>::try_from(arg_inputs).ok().unwrap();
1227        return Err(LoweringFlowError::Panic(input, location));
1228    }
1229
1230    // The following is relevant only to extern functions.
1231    if expr.function.try_get_extern_function_id(ctx.db).is_some() {
1232        if let semantic::TypeLongId::Concrete(semantic::ConcreteTypeId::Enum(concrete_enum_id)) =
1233            expr.ty.lookup_intern(ctx.db)
1234        {
1235            let lowered_expr = LoweredExprExternEnum {
1236                function: expr.function,
1237                concrete_enum_id,
1238                inputs: arg_inputs,
1239                member_paths: ref_args_iter.cloned().collect(),
1240                location,
1241            };
1242
1243            // It is still unknown whether we directly match on this enum result, or store it to a
1244            // variable. Thus we can't perform the call. Performing it and pushing/bringing-back
1245            // variables are done on the 2 places where this result is used:
1246            // 1. [lower_optimized_extern_match]
1247            // 2. [context::LoweredExprExternEnum::var]
1248            return Ok(LoweredExpr::ExternEnum(lowered_expr));
1249        }
1250    }
1251
1252    let (ref_outputs, res) = perform_function_call(
1253        ctx,
1254        builder,
1255        FunctionCallInfo {
1256            function: expr.function,
1257            inputs: arg_inputs,
1258            coupon_input,
1259            extra_ret_tys: ref_tys,
1260            ret_ty: expr.ty,
1261        },
1262        location,
1263    )?;
1264
1265    // Rebind the ref variables.
1266    for (ref_arg, output_var) in zip_eq(ref_args_iter, ref_outputs) {
1267        builder.update_ref(ctx, ref_arg, output_var.var_id);
1268    }
1269
1270    Ok(res)
1271}
1272
1273/// Information required for [perform_function_call].
1274struct FunctionCallInfo {
1275    function: semantic::FunctionId,
1276    inputs: Vec<VarUsage>,
1277    coupon_input: Option<VarUsage>,
1278    extra_ret_tys: Vec<semantic::TypeId>,
1279    ret_ty: semantic::TypeId,
1280}
1281
1282/// Creates a LoweredExpr for a function call, taking into consideration external function facades:
1283/// For external functions, sometimes the high level signature doesn't exactly correspond to the
1284/// external function returned variables / branches.
1285fn perform_function_call(
1286    ctx: &mut LoweringContext<'_, '_>,
1287    builder: &mut BlockBuilder,
1288    function_call_info: FunctionCallInfo,
1289    location: LocationId,
1290) -> Result<(Vec<VarUsage>, LoweredExpr), LoweringFlowError> {
1291    let FunctionCallInfo { function, inputs, coupon_input, extra_ret_tys, ret_ty } =
1292        function_call_info;
1293
1294    // If the function is not extern, simply call it.
1295    if function.try_get_extern_function_id(ctx.db).is_none() {
1296        let call_result = generators::Call {
1297            function: function.lowered(ctx.db),
1298            inputs,
1299            coupon_input,
1300            extra_ret_tys,
1301            ret_tys: vec![ret_ty],
1302            location,
1303        }
1304        .add(ctx, &mut builder.statements);
1305
1306        if ret_ty == never_ty(ctx.db) {
1307            // If the function returns never, the control flow is not allowed to continue.
1308            // This special case is required because without it the following code:
1309            // ```
1310            //    let res: felt252 = match a {
1311            //        true => 1,
1312            //        false => never_returns()
1313            //    };
1314            // ```
1315            // would try to assign never to res, which is not allowed.
1316
1317            return Err(LoweringFlowError::Match(MatchInfo::Enum(MatchEnumInfo {
1318                concrete_enum_id: extract_matches!(
1319                    extract_matches!(ret_ty.lookup_intern(ctx.db), semantic::TypeLongId::Concrete),
1320                    semantic::ConcreteTypeId::Enum
1321                ),
1322                input: VarUsage { var_id: call_result.returns[0].var_id, location },
1323                arms: vec![],
1324                location,
1325            })));
1326        }
1327
1328        let res = LoweredExpr::AtVariable(call_result.returns.into_iter().next().unwrap());
1329        return Ok((call_result.extra_outputs, res));
1330    };
1331
1332    // Extern function.
1333    assert!(coupon_input.is_none(), "Extern functions cannot have a __coupon__ argument.");
1334    let ret_tys = extern_facade_return_tys(ctx, ret_ty);
1335    let call_result = generators::Call {
1336        function: function.lowered(ctx.db),
1337        inputs,
1338        coupon_input: None,
1339        extra_ret_tys,
1340        ret_tys,
1341        location,
1342    }
1343    .add(ctx, &mut builder.statements);
1344
1345    Ok((
1346        call_result.extra_outputs,
1347        extern_facade_expr(
1348            ctx,
1349            ret_ty,
1350            call_result.returns.into_iter().map(|var_usage| var_usage.var_id).collect_vec(),
1351            location,
1352        ),
1353    ))
1354}
1355
1356/// Lowers an expression of type [semantic::ExprLoop].
1357fn lower_expr_loop(
1358    ctx: &mut LoweringContext<'_, '_>,
1359    builder: &mut BlockBuilder,
1360    loop_expr_id: ExprId,
1361) -> LoweringResult<LoweredExpr> {
1362    let (stable_ptr, return_type) = match ctx.function_body.arenas.exprs[loop_expr_id].clone() {
1363        semantic::Expr::Loop(semantic::ExprLoop { stable_ptr, ty, .. }) => (stable_ptr, ty),
1364        semantic::Expr::While(semantic::ExprWhile { stable_ptr, ty, .. }) => (stable_ptr, ty),
1365        semantic::Expr::For(semantic::ExprFor {
1366            stable_ptr,
1367            ty,
1368            into_iter,
1369            expr_id,
1370            into_iter_member_path,
1371            ..
1372        }) => {
1373            let semantic_db: &dyn SemanticGroup = ctx.db;
1374            let var_id = lower_expr(ctx, builder, expr_id)?.as_var_usage(ctx, builder)?;
1375            let into_iter_call = generators::Call {
1376                function: into_iter.lowered(ctx.db),
1377                inputs: vec![var_id],
1378                coupon_input: None,
1379                extra_ret_tys: vec![],
1380                ret_tys: vec![
1381                    semantic_db.concrete_function_signature(into_iter).unwrap().return_type,
1382                ],
1383                location: ctx.get_location(stable_ptr.untyped()),
1384            }
1385            .add(ctx, &mut builder.statements);
1386            let into_iter_var = into_iter_call.returns.into_iter().next().unwrap();
1387            let sem_var = LocalVariable {
1388                ty: semantic_db.concrete_function_signature(into_iter).unwrap().return_type,
1389                is_mut: true,
1390                id: extract_matches!(into_iter_member_path.base_var(), VarId::Local),
1391            };
1392            builder.put_semantic(into_iter_member_path.base_var(), into_iter_var.var_id);
1393
1394            ctx.semantic_defs
1395                .insert(into_iter_member_path.base_var(), semantic::Binding::LocalVar(sem_var));
1396
1397            (stable_ptr, ty)
1398        }
1399        _ => unreachable!("Loop expression must be either loop, while or for."),
1400    };
1401
1402    let semantic_db = ctx.db;
1403
1404    let usage = &ctx.usages.usages[&loop_expr_id];
1405    let has_normal_return = return_type != never_ty(semantic_db);
1406
1407    let (loop_return_ty, early_return_info) = if !has_normal_return {
1408        // If the loop does not have a normal return, `LoopResult` is not used
1409        // but we need to override the return type of the loop to match the function.
1410        (ctx.return_type, None)
1411    } else if !usage.has_early_return {
1412        (return_type, None)
1413    } else {
1414        let generic_args =
1415            vec![GenericArgumentId::Type(return_type), GenericArgumentId::Type(ctx.return_type)];
1416
1417        let internal_module = core_submodule(semantic_db, "internal");
1418        let ret_ty = try_get_ty_by_name(
1419            semantic_db,
1420            internal_module,
1421            "LoopResult".into(),
1422            generic_args.clone(),
1423        )
1424        .unwrap();
1425        (
1426            ret_ty,
1427            Some(LoopEarlyReturnInfo {
1428                normal_return_variant: get_enum_concrete_variant(
1429                    semantic_db,
1430                    internal_module,
1431                    "LoopResult",
1432                    generic_args.clone(),
1433                    "Normal",
1434                ),
1435                early_return_variant: get_enum_concrete_variant(
1436                    semantic_db,
1437                    internal_module,
1438                    "LoopResult",
1439                    generic_args,
1440                    "EarlyReturn",
1441                ),
1442            }),
1443        )
1444    };
1445
1446    // Determine signature.
1447    let params = usage
1448        .usage
1449        .iter()
1450        .map(|(_, expr)| expr.clone())
1451        .chain(usage.snap_usage.iter().map(|(_, expr)| match expr {
1452            ExprVarMemberPath::Var(var) => {
1453                ExprVarMemberPath::Var(ExprVar { ty: wrap_in_snapshots(ctx.db, var.ty, 1), ..*var })
1454            }
1455            ExprVarMemberPath::Member { parent, member_id, stable_ptr, concrete_struct_id, ty } => {
1456                ExprVarMemberPath::Member {
1457                    parent: parent.clone(),
1458                    member_id: *member_id,
1459                    stable_ptr: *stable_ptr,
1460                    concrete_struct_id: *concrete_struct_id,
1461                    ty: wrap_in_snapshots(ctx.db, *ty, 1),
1462                }
1463            }
1464        }))
1465        .collect_vec();
1466    let extra_rets = usage.changes.iter().map(|(_, expr)| expr.clone()).collect_vec();
1467
1468    let loop_location = ctx.get_location(stable_ptr.untyped());
1469    let loop_signature = Signature {
1470        params,
1471        extra_rets,
1472        return_type: loop_return_ty,
1473        implicits: vec![],
1474        panicable: ctx.signature.panicable,
1475        location: loop_location,
1476    };
1477
1478    // Get the function id.
1479    let function = FunctionWithBodyLongId::Generated {
1480        parent: ctx.semantic_function_id,
1481        key: GeneratedFunctionKey::Loop(stable_ptr),
1482    }
1483    .intern(ctx.db);
1484
1485    // Generate the function.
1486    let encapsulating_ctx = ctx.encapsulating_ctx.take().unwrap();
1487    let loop_ctx = LoopContext { loop_expr_id, early_return_info: early_return_info.clone() };
1488    let lowered = lower_loop_function(
1489        encapsulating_ctx,
1490        function,
1491        loop_signature.clone(),
1492        loop_ctx,
1493        ctx.return_type,
1494    )
1495    .map_err(LoweringFlowError::Failed)?;
1496    // TODO(spapini): Recursive call.
1497    encapsulating_ctx.lowerings.insert(GeneratedFunctionKey::Loop(stable_ptr), lowered);
1498    ctx.encapsulating_ctx = Some(encapsulating_ctx);
1499    let call_loop_expr = call_loop_func_ex(
1500        ctx,
1501        loop_signature,
1502        builder,
1503        loop_expr_id,
1504        stable_ptr.untyped(),
1505        |ctx, builder, param| {
1506            if let Some(var) = builder.get_snap_ref(ctx, param) {
1507                return Some(var);
1508            };
1509            let input = builder.get_ref(ctx, param)?;
1510            let location = ctx.get_location(param.stable_ptr().untyped());
1511            let (original, snapped) =
1512                generators::Snapshot { input, location }.add(ctx, &mut builder.statements);
1513            builder.update_ref(ctx, param, original);
1514            Some(VarUsage { var_id: snapped, location })
1515        },
1516    )?;
1517
1518    let Some(LoopEarlyReturnInfo { normal_return_variant, early_return_variant }) =
1519        early_return_info
1520    else {
1521        if !has_normal_return {
1522            let ret_var_usage = call_loop_expr.as_var_usage(ctx, builder)?;
1523            return Err(LoweringFlowError::Return(ret_var_usage, loop_location));
1524        }
1525
1526        return Ok(call_loop_expr);
1527    };
1528
1529    let loop_res = call_loop_expr.as_var_usage(ctx, builder)?;
1530
1531    let normal_return_subscope = create_subscope(ctx, builder);
1532    let normal_return_subscope_block_id = normal_return_subscope.block_id;
1533    let normal_return_var_id = ctx.new_var(VarRequest { ty: return_type, location: loop_location });
1534    let sealed_normal_return = lowered_expr_to_block_scope_end(
1535        ctx,
1536        normal_return_subscope,
1537        Ok(LoweredExpr::AtVariable(VarUsage {
1538            var_id: normal_return_var_id,
1539            location: loop_location,
1540        })),
1541    )
1542    .map_err(LoweringFlowError::Failed)?;
1543
1544    let mut early_return_subscope = create_subscope(ctx, builder);
1545    let early_return_var_id =
1546        ctx.new_var(VarRequest { ty: ctx.signature.return_type, location: loop_location });
1547    let early_return_subscope_block_id = early_return_subscope.block_id;
1548    let ret_expr = lower_return(
1549        ctx,
1550        &mut early_return_subscope,
1551        VarUsage { var_id: early_return_var_id, location: loop_location },
1552        loop_location,
1553        true,
1554    );
1555    let sealed_early_return = lowered_expr_to_block_scope_end(ctx, early_return_subscope, ret_expr)
1556        .map_err(LoweringFlowError::Failed)?;
1557
1558    let match_info = MatchInfo::Enum(MatchEnumInfo {
1559        concrete_enum_id: normal_return_variant.concrete_enum_id,
1560        input: loop_res,
1561        arms: vec![
1562            MatchArm {
1563                arm_selector: MatchArmSelector::VariantId(normal_return_variant),
1564                block_id: normal_return_subscope_block_id,
1565                var_ids: vec![normal_return_var_id],
1566            },
1567            MatchArm {
1568                arm_selector: MatchArmSelector::VariantId(early_return_variant),
1569                block_id: early_return_subscope_block_id,
1570                var_ids: vec![early_return_var_id],
1571            },
1572        ],
1573        location: loop_location,
1574    });
1575
1576    builder.merge_and_end_with_match(
1577        ctx,
1578        match_info,
1579        vec![sealed_normal_return, sealed_early_return],
1580        loop_location,
1581    )
1582}
1583
1584/// Adds a call to an inner loop-generated function from the loop function itself.
1585fn recursively_call_loop_func(
1586    ctx: &mut LoweringContext<'_, '_>,
1587    builder: &mut BlockBuilder,
1588    loop_expr_id: ExprId,
1589    stable_ptr: SyntaxStablePtrId,
1590) -> LoweringResult<LoweredExpr> {
1591    let loop_res = call_loop_func_ex(
1592        ctx,
1593        ctx.signature.clone(),
1594        builder,
1595        loop_expr_id,
1596        stable_ptr,
1597        |ctx, builder, param| builder.get_snap_ref(ctx, param),
1598    )?
1599    .as_var_usage(ctx, builder)?;
1600    Err(LoweringFlowError::Return(loop_res, ctx.get_location(stable_ptr)))
1601}
1602
1603/// Adds a call to an inner loop-generated function.
1604fn call_loop_func_ex(
1605    ctx: &mut LoweringContext<'_, '_>,
1606    loop_signature: Signature,
1607    builder: &mut BlockBuilder,
1608    loop_expr_id: ExprId,
1609    stable_ptr: SyntaxStablePtrId,
1610    handle_snap: impl Fn(
1611        &mut LoweringContext<'_, '_>,
1612        &mut BlockBuilder,
1613        &ExprVarMemberPath,
1614    ) -> Option<VarUsage>,
1615) -> LoweringResult<LoweredExpr> {
1616    let location = ctx.get_location(stable_ptr);
1617    let loop_stable_ptr = ctx.function_body.arenas.exprs[loop_expr_id].stable_ptr();
1618    // Call it.
1619    let function = FunctionLongId::Generated(GeneratedFunction {
1620        parent: ctx.concrete_function_id.base_semantic_function(ctx.db),
1621        key: GeneratedFunctionKey::Loop(loop_stable_ptr),
1622    })
1623    .intern(ctx.db);
1624    let inputs = loop_signature
1625        .params
1626        .into_iter()
1627        .map(|param| {
1628            builder
1629                .get_ref(ctx, &param)
1630                .and_then(|var| (ctx.variables[var.var_id].ty == param.ty()).then_some(var))
1631                .or_else(|| {
1632                    let var = handle_snap(ctx, builder, &param)?;
1633                    (ctx.variables[var.var_id].ty == param.ty()).then_some(var)
1634                })
1635                .ok_or_else(|| {
1636                    // TODO(TomerStaskware): make sure this is unreachable and remove
1637                    // `MemberPathLoop` diagnostic.
1638                    LoweringFlowError::Failed(
1639                        ctx.diagnostics.report(param.stable_ptr(), MemberPathLoop),
1640                    )
1641                })
1642        })
1643        .collect::<LoweringResult<Vec<_>>>()?;
1644    let extra_ret_tys = loop_signature.extra_rets.iter().map(|path| path.ty()).collect_vec();
1645    let call_result = generators::Call {
1646        function,
1647        inputs,
1648        coupon_input: None,
1649        extra_ret_tys,
1650        ret_tys: vec![loop_signature.return_type],
1651        location,
1652    }
1653    .add(ctx, &mut builder.statements);
1654
1655    // Rebind the ref variables.
1656    for (ref_arg, output_var) in zip_eq(&loop_signature.extra_rets, call_result.extra_outputs) {
1657        builder.update_ref(ctx, ref_arg, output_var.var_id);
1658    }
1659
1660    Ok(LoweredExpr::AtVariable(call_result.returns.into_iter().next().unwrap()))
1661}
1662
1663/// Lowers a sequence of expressions and return them all. If the flow ended in the middle,
1664/// propagates that flow error without returning any variable.
1665fn lower_exprs_to_var_usages(
1666    ctx: &mut LoweringContext<'_, '_>,
1667    args: &[semantic::ExprFunctionCallArg],
1668    builder: &mut BlockBuilder,
1669) -> Result<Vec<VarUsage>, LoweringFlowError> {
1670    // Since value expressions may depends on the same variables as the references, which must be
1671    // variables, all expressions must be evaluated before using the references for binding into the
1672    // call.
1673    // TODO(orizi): Consider changing this to disallow taking a reference and then using the
1674    // variable, while still allowing `arr.append(arr.len())`.
1675    let mut value_iter = args
1676        .iter()
1677        .filter_map(|arg| try_extract_matches!(arg, ExprFunctionCallArg::Value))
1678        .map(|arg_expr_id| lower_expr_to_var_usage(ctx, builder, *arg_expr_id))
1679        .collect::<Result<Vec<_>, _>>()?
1680        .into_iter();
1681    Ok(args
1682        .iter()
1683        .map(|arg| match arg {
1684            semantic::ExprFunctionCallArg::Reference(ref_arg) => {
1685                builder.get_ref(ctx, ref_arg).unwrap()
1686            }
1687            semantic::ExprFunctionCallArg::Value(_) => value_iter.next().unwrap(),
1688        })
1689        .collect())
1690}
1691
1692/// Lowers an expression of type [semantic::ExprEnumVariantCtor].
1693fn lower_expr_enum_ctor(
1694    ctx: &mut LoweringContext<'_, '_>,
1695    expr: &semantic::ExprEnumVariantCtor,
1696    builder: &mut BlockBuilder,
1697) -> LoweringResult<LoweredExpr> {
1698    log::trace!(
1699        "Started lowering of an enum c'tor expression: {:?}",
1700        expr.debug(&ctx.expr_formatter)
1701    );
1702    let location = ctx.get_location(expr.stable_ptr.untyped());
1703    Ok(LoweredExpr::AtVariable(
1704        generators::EnumConstruct {
1705            input: lower_expr_to_var_usage(ctx, builder, expr.value_expr)?,
1706            variant: expr.variant,
1707            location,
1708        }
1709        .add(ctx, &mut builder.statements),
1710    ))
1711}
1712
1713/// Lowers an expression of type [semantic::ExprMemberAccess].
1714fn lower_expr_member_access(
1715    ctx: &mut LoweringContext<'_, '_>,
1716    expr: &semantic::ExprMemberAccess,
1717    builder: &mut BlockBuilder,
1718) -> LoweringResult<LoweredExpr> {
1719    log::trace!("Lowering a member-access expression: {:?}", expr.debug(&ctx.expr_formatter));
1720    if let Some(member_path) = &expr.member_path {
1721        return Ok(LoweredExpr::Member(
1722            member_path.clone(),
1723            ctx.get_location(expr.stable_ptr.untyped()),
1724        ));
1725    }
1726    let location = ctx.get_location(expr.stable_ptr.untyped());
1727    let members = ctx
1728        .db
1729        .concrete_struct_members(expr.concrete_struct_id)
1730        .map_err(LoweringFlowError::Failed)?;
1731    let member_idx =
1732        members.iter().position(|(_, member)| member.id == expr.member).ok_or_else(|| {
1733            LoweringFlowError::Failed(
1734                ctx.diagnostics.report(expr.stable_ptr.untyped(), UnexpectedError),
1735            )
1736        })?;
1737    Ok(LoweredExpr::AtVariable(
1738        generators::StructMemberAccess {
1739            input: lower_expr_to_var_usage(ctx, builder, expr.expr)?,
1740            member_tys: members
1741                .iter()
1742                .map(|(_, member)| wrap_in_snapshots(ctx.db, member.ty, expr.n_snapshots))
1743                .collect(),
1744            member_idx,
1745            location,
1746        }
1747        .add(ctx, &mut builder.statements),
1748    ))
1749}
1750
1751/// Lowers an expression of type [semantic::ExprStructCtor].
1752fn lower_expr_struct_ctor(
1753    ctx: &mut LoweringContext<'_, '_>,
1754    expr: &semantic::ExprStructCtor,
1755    builder: &mut BlockBuilder,
1756) -> LoweringResult<LoweredExpr> {
1757    log::trace!("Lowering a struct c'tor expression: {:?}", expr.debug(&ctx.expr_formatter));
1758    let location = ctx.get_location(expr.stable_ptr.untyped());
1759    let members = ctx
1760        .db
1761        .concrete_struct_members(expr.concrete_struct_id)
1762        .map_err(LoweringFlowError::Failed)?;
1763    let mut member_expr_usages =
1764        UnorderedHashMap::<_, _>::from_iter(expr.members.iter().map(|(id, expr)| {
1765            let usage = lower_expr_to_var_usage(ctx, builder, *expr);
1766            (*id, usage)
1767        }));
1768    if members.len() != member_expr_usages.len() {
1769        // Semantic model should have made sure base struct exist if some members are missing.
1770        let base_struct = lower_expr(ctx, builder, expr.base_struct.unwrap())?;
1771        if let LoweredExpr::Member(path, location) = base_struct {
1772            for (_, member) in members.iter() {
1773                let Entry::Vacant(entry) = member_expr_usages.entry(member.id) else {
1774                    continue;
1775                };
1776                let member_path = ExprVarMemberPath::Member {
1777                    parent: Box::new(path.clone()),
1778                    member_id: member.id,
1779                    stable_ptr: path.stable_ptr(),
1780                    concrete_struct_id: expr.concrete_struct_id,
1781                    ty: member.ty,
1782                };
1783                entry.insert(Ok(
1784                    LoweredExpr::Member(member_path, location).as_var_usage(ctx, builder)?
1785                ));
1786            }
1787        } else {
1788            for (base_member, (_, member)) in izip!(
1789                StructDestructure {
1790                    input: base_struct.as_var_usage(ctx, builder)?,
1791                    var_reqs: members
1792                        .iter()
1793                        .map(|(_, member)| VarRequest { ty: member.ty, location })
1794                        .collect(),
1795                }
1796                .add(ctx, &mut builder.statements),
1797                members.iter()
1798            ) {
1799                match member_expr_usages.entry(member.id) {
1800                    Entry::Occupied(_) => {}
1801                    Entry::Vacant(entry) => {
1802                        entry.insert(Ok(VarUsage { var_id: base_member, location }));
1803                    }
1804                }
1805            }
1806        }
1807    }
1808    Ok(LoweredExpr::AtVariable(
1809        generators::StructConstruct {
1810            inputs: members
1811                .iter()
1812                .map(|(_, member)| member_expr_usages.remove(&member.id).unwrap())
1813                .collect::<Result<Vec<_>, _>>()?,
1814            ty: expr.ty,
1815            location,
1816        }
1817        .add(ctx, &mut builder.statements),
1818    ))
1819}
1820
1821/// Adds the lowering for the destruct or panic_destruct of a capture variable.
1822fn add_capture_destruct_impl(
1823    ctx: &mut LoweringContext<'_, '_>,
1824    capture_var_usage: VarUsage,
1825    closure_info: &ClosureInfo,
1826    location: StableLocation,
1827) -> Maybe<()> {
1828    let capture_ty_info = &ctx.variables.variables[capture_var_usage.var_id].info;
1829    // Skipping generation for the case of `Drop`.
1830    let Some(Ok(impl_id)) = [&capture_ty_info.destruct_impl, &capture_ty_info.panic_destruct_impl]
1831        .iter()
1832        .find(|infer_result| infer_result.is_ok())
1833    else {
1834        return Ok(());
1835    };
1836
1837    let db = ctx.db;
1838    let concrete_trait = impl_id.concrete_trait(db)?;
1839
1840    let trait_functions = db.trait_functions(concrete_trait.trait_id(db))?;
1841
1842    assert_eq!(trait_functions.len(), 1);
1843    let trait_function = *trait_functions.values().next().unwrap();
1844
1845    let generic_function = GenericFunctionId::Impl(ImplGenericFunctionId {
1846        impl_id: *impl_id,
1847        function: trait_function,
1848    });
1849
1850    let function = semantic::FunctionLongId {
1851        function: ConcreteFunction { generic_function, generic_args: vec![] },
1852    }
1853    .intern(ctx.db);
1854
1855    let signature = Signature::from_semantic(ctx.db, db.concrete_function_signature(function)?);
1856
1857    let func_key = GeneratedFunctionKey::TraitFunc(trait_function, location);
1858    let function_id =
1859        FunctionWithBodyLongId::Generated { parent: ctx.semantic_function_id, key: func_key }
1860            .intern(ctx.db);
1861
1862    let location_id = LocationId::from_stable_location(ctx.db, location);
1863
1864    let encapsulating_ctx = ctx.encapsulating_ctx.take().unwrap();
1865    let return_type = signature.return_type;
1866    let lowered_impl_res = get_destruct_lowering(
1867        LoweringContext::new(encapsulating_ctx, function_id, signature, return_type)?,
1868        location_id,
1869        closure_info,
1870    );
1871    // Restore the encapsulating context before unwrapping the result.
1872    ctx.encapsulating_ctx = Some(encapsulating_ctx);
1873    ctx.lowerings.insert(func_key, lowered_impl_res?);
1874    Ok(())
1875}
1876
1877/// Returns the lowering of the destruct function of a capture variable.
1878fn get_destruct_lowering(
1879    mut ctx: LoweringContext<'_, '_>,
1880    location_id: LocationId,
1881    closure_info: &ClosureInfo,
1882) -> Maybe<Lowered> {
1883    let root_block_id = alloc_empty_block(&mut ctx);
1884    let mut builder = BlockBuilder::root(root_block_id);
1885
1886    let parameters = ctx
1887        .signature
1888        .params
1889        .clone()
1890        .into_iter()
1891        .map(|param| {
1892            let var = ctx.new_var(VarRequest { ty: param.ty(), location: location_id });
1893            builder.introduce((&param).into(), var);
1894            var
1895        })
1896        .collect_vec();
1897
1898    builder.destructure_closure(&mut ctx, location_id, parameters[0], closure_info);
1899    let var_usage =
1900        generators::StructConstruct { inputs: vec![], ty: unit_ty(ctx.db), location: location_id }
1901            .add(&mut ctx, &mut builder.statements);
1902    builder.ret(&mut ctx, var_usage, location_id)?;
1903    let lowered_impl = Lowered {
1904        diagnostics: ctx.diagnostics.build(),
1905        variables: ctx.variables.variables,
1906        blocks: ctx.blocks.build().unwrap(),
1907        signature: ctx.signature,
1908        parameters,
1909    };
1910    Ok(lowered_impl)
1911}
1912
1913/// Adds the lowering for the closure function.
1914fn add_closure_call_function(
1915    encapsulated_ctx: &mut LoweringContext<'_, '_>,
1916    expr: &semantic::ExprClosure,
1917    closure_info: &ClosureInfo,
1918    trait_id: cairo_lang_defs::ids::TraitId,
1919) -> Maybe<()> {
1920    let db: &dyn SemanticGroup = encapsulated_ctx.db;
1921    let closure_ty = extract_matches!(expr.ty.lookup_intern(db), TypeLongId::Closure);
1922    let expr_location = encapsulated_ctx.get_location(expr.stable_ptr.untyped());
1923    let parameters_ty = TypeLongId::Tuple(closure_ty.param_tys.clone()).intern(db);
1924    let concrete_trait = ConcreteTraitLongId {
1925        trait_id,
1926        generic_args: vec![
1927            GenericArgumentId::Type(expr.ty),
1928            GenericArgumentId::Type(parameters_ty),
1929        ],
1930    }
1931    .intern(db);
1932    let Ok(impl_id) = semantic::types::get_impl_at_context(
1933        db,
1934        encapsulated_ctx.variables.lookup_context.clone(),
1935        concrete_trait,
1936        None,
1937    ) else {
1938        // If the impl doesn't exist, there won't be a call to the call-function, so we don't need
1939        // to generate it.
1940        return Ok(());
1941    };
1942    if !matches!(impl_id.lookup_intern(db), ImplLongId::GeneratedImpl(_)) {
1943        // If the impl is not generated, we don't need to generate a lowering for it.
1944        return Ok(());
1945    }
1946
1947    let trait_function: cairo_lang_defs::ids::TraitFunctionId = db
1948        .trait_function_by_name(trait_id, "call".into())
1949        .unwrap()
1950        .expect("Call function must exist for an Fn trait.");
1951
1952    let generic_function =
1953        GenericFunctionId::Impl(ImplGenericFunctionId { impl_id, function: trait_function });
1954    let function = semantic::FunctionLongId {
1955        function: ConcreteFunction { generic_function, generic_args: vec![] },
1956    }
1957    .intern(db);
1958    let function_with_body_id = FunctionWithBodyLongId::Generated {
1959        parent: encapsulated_ctx.semantic_function_id,
1960        key: GeneratedFunctionKey::TraitFunc(trait_function, closure_ty.wrapper_location),
1961    }
1962    .intern(encapsulated_ctx.db);
1963    let signature =
1964        Signature::from_semantic(encapsulated_ctx.db, db.concrete_function_signature(function)?);
1965
1966    let return_type = signature.return_type;
1967    let mut ctx =
1968        LoweringContext::new(encapsulated_ctx, function_with_body_id, signature, return_type)?;
1969
1970    let root_block_id = alloc_empty_block(&mut ctx);
1971    let mut builder = BlockBuilder::root(root_block_id);
1972
1973    let info = ctx.db.core_info();
1974    let (closure_param_var_id, closure_var) = if trait_id == info.fn_once_trt {
1975        // If the closure is FnOnce, the closure is passed by value.
1976        let closure_param_var = ctx.new_var(VarRequest { ty: expr.ty, location: expr_location });
1977        let closure_var = VarUsage { var_id: closure_param_var, location: expr_location };
1978        (closure_param_var, closure_var)
1979    } else {
1980        // If the closure is Fn the closure argument will be a snapshot, so we need to desnap it.
1981        let closure_param_var = ctx
1982            .new_var(VarRequest { ty: wrap_in_snapshots(db, expr.ty, 1), location: expr_location });
1983
1984        let closure_var = generators::Desnap {
1985            input: VarUsage { var_id: closure_param_var, location: expr_location },
1986            location: expr_location,
1987        }
1988        .add(&mut ctx, &mut builder.statements);
1989        (closure_param_var, closure_var)
1990    };
1991    let parameters: Vec<VariableId> = [
1992        closure_param_var_id,
1993        ctx.new_var(VarRequest { ty: parameters_ty, location: expr_location }),
1994    ]
1995    .into();
1996    let captured_vars = generators::StructDestructure {
1997        input: closure_var,
1998        var_reqs: chain!(closure_info.members.iter(), closure_info.snapshots.iter())
1999            .map(|(_, ty)| VarRequest { ty: *ty, location: expr_location })
2000            .collect_vec(),
2001    }
2002    .add(&mut ctx, &mut builder.statements);
2003    for (i, (param, _)) in closure_info.members.iter().enumerate() {
2004        builder.introduce(param.clone(), captured_vars[i]);
2005    }
2006    for (i, (param, _)) in closure_info.snapshots.iter().enumerate() {
2007        builder.introduce_snap(param.clone(), captured_vars[i + closure_info.members.len()]);
2008    }
2009    let param_vars = generators::StructDestructure {
2010        input: VarUsage { var_id: parameters[1], location: expr_location },
2011        var_reqs: closure_ty
2012            .param_tys
2013            .iter()
2014            .map(|ty| VarRequest { ty: *ty, location: expr_location })
2015            .collect_vec(),
2016    }
2017    .add(&mut ctx, &mut builder.statements);
2018    for (param_var, param) in param_vars.into_iter().zip(expr.params.iter()) {
2019        builder.introduce((&parameter_as_member_path(param.clone())).into(), param_var);
2020        ctx.semantic_defs
2021            .insert(semantic::VarId::Param(param.id), semantic::Binding::Param(param.clone()));
2022    }
2023    let lowered_expr = lower_expr(&mut ctx, &mut builder, expr.body);
2024    let maybe_sealed_block = lowered_expr_to_block_scope_end(&mut ctx, builder, lowered_expr);
2025    let root_ok = maybe_sealed_block.and_then(|block_sealed| {
2026        wrap_sealed_block_as_function(&mut ctx, block_sealed, expr.stable_ptr.untyped())?;
2027        Ok(root_block_id)
2028    });
2029    let blocks = root_ok
2030        .map(|_| ctx.blocks.build().expect("Root block must exist."))
2031        .unwrap_or_else(Blocks::new_errored);
2032
2033    let lowered = Lowered {
2034        diagnostics: ctx.diagnostics.build(),
2035        variables: ctx.variables.variables,
2036        blocks,
2037        signature: ctx.signature.clone(),
2038        parameters,
2039    };
2040    encapsulated_ctx.lowerings.insert(
2041        GeneratedFunctionKey::TraitFunc(trait_function, closure_ty.wrapper_location),
2042        lowered,
2043    );
2044    Ok(())
2045}
2046
2047/// Lowers an expression of type [semantic::ExprClosure].
2048fn lower_expr_closure(
2049    ctx: &mut LoweringContext<'_, '_>,
2050    expr: &semantic::ExprClosure,
2051    expr_id: semantic::ExprId,
2052    builder: &mut BlockBuilder,
2053) -> LoweringResult<LoweredExpr> {
2054    log::trace!("Lowering a closure expression: {:?}", expr.debug(&ctx.expr_formatter));
2055
2056    let usage = ctx.usages.usages[&expr_id].clone();
2057    let (capture_var_usage, closure_info) = builder.capture(ctx, usage, expr);
2058    let closure_variable = LoweredExpr::AtVariable(capture_var_usage);
2059    let closure_ty = extract_matches!(expr.ty.lookup_intern(ctx.db), TypeLongId::Closure);
2060    let _ = add_capture_destruct_impl(
2061        ctx,
2062        capture_var_usage,
2063        &closure_info,
2064        closure_ty.wrapper_location,
2065    );
2066    add_closure_call_function(
2067        ctx,
2068        expr,
2069        &closure_info,
2070        if ctx.variables[capture_var_usage.var_id].info.copyable.is_ok() {
2071            ctx.db.core_info().fn_trt
2072        } else {
2073            ctx.db.core_info().fn_once_trt
2074        },
2075    )
2076    .map_err(LoweringFlowError::Failed)?;
2077
2078    Ok(closure_variable)
2079}
2080
2081/// Lowers an expression of type [semantic::ExprPropagateError].
2082fn lower_expr_error_propagate(
2083    ctx: &mut LoweringContext<'_, '_>,
2084    expr: &semantic::ExprPropagateError,
2085    builder: &mut BlockBuilder,
2086) -> LoweringResult<LoweredExpr> {
2087    log::trace!(
2088        "Started lowering of an error-propagate expression: {:?}",
2089        expr.debug(&ctx.expr_formatter)
2090    );
2091    let location = ctx.get_location(expr.stable_ptr.untyped());
2092    let lowered_expr = lower_expr(ctx, builder, expr.inner)?;
2093    let ExprPropagateError { ok_variant, err_variant, func_err_variant, .. } = expr;
2094    if let LoweredExpr::ExternEnum(extern_enum) = lowered_expr {
2095        return lower_optimized_extern_error_propagate(
2096            ctx,
2097            builder,
2098            extern_enum,
2099            ok_variant,
2100            err_variant,
2101            func_err_variant,
2102            location,
2103        );
2104    }
2105
2106    let match_input = lowered_expr.as_var_usage(ctx, builder)?;
2107    // Ok arm.
2108    let subscope_ok = create_subscope(ctx, builder);
2109    let block_ok_id = subscope_ok.block_id;
2110    let expr_var = ctx.new_var(VarRequest { ty: ok_variant.ty, location });
2111    let sealed_block_ok = subscope_ok.goto_callsite(Some(VarUsage { var_id: expr_var, location }));
2112
2113    // Err arm.
2114    let mut subscope_err = create_subscope(ctx, builder);
2115    let block_err_id = subscope_err.block_id;
2116    let err_value = ctx.new_var(VarRequest { ty: err_variant.ty, location });
2117    let err_res = generators::EnumConstruct {
2118        input: VarUsage { var_id: err_value, location },
2119        variant: *func_err_variant,
2120        location,
2121    }
2122    .add(ctx, &mut subscope_err.statements);
2123    let ret_expr = lower_return(ctx, &mut subscope_err, err_res, location, true);
2124    let sealed_block_err = lowered_expr_to_block_scope_end(ctx, subscope_err, ret_expr)
2125        .map_err(LoweringFlowError::Failed)?;
2126
2127    // Merge blocks.
2128    let match_info = MatchInfo::Enum(MatchEnumInfo {
2129        concrete_enum_id: ok_variant.concrete_enum_id,
2130        input: match_input,
2131        arms: vec![
2132            MatchArm {
2133                arm_selector: MatchArmSelector::VariantId(*ok_variant),
2134                block_id: block_ok_id,
2135                var_ids: vec![expr_var],
2136            },
2137            MatchArm {
2138                arm_selector: MatchArmSelector::VariantId(*err_variant),
2139                block_id: block_err_id,
2140                var_ids: vec![err_value],
2141            },
2142        ],
2143        location,
2144    });
2145    builder.merge_and_end_with_match(
2146        ctx,
2147        match_info,
2148        vec![sealed_block_ok, sealed_block_err],
2149        location,
2150    )
2151}
2152
2153/// Lowers an error propagation expression on a LoweredExpr::ExternEnum lowered expression.
2154fn lower_optimized_extern_error_propagate(
2155    ctx: &mut LoweringContext<'_, '_>,
2156    builder: &mut BlockBuilder,
2157    extern_enum: LoweredExprExternEnum,
2158    ok_variant: &semantic::ConcreteVariant,
2159    err_variant: &semantic::ConcreteVariant,
2160    func_err_variant: &semantic::ConcreteVariant,
2161    location: LocationId,
2162) -> LoweringResult<LoweredExpr> {
2163    log::trace!("Started lowering of an optimized error-propagate expression.");
2164
2165    // Ok arm.
2166    let mut subscope_ok = create_subscope(ctx, builder);
2167    let block_ok_id = subscope_ok.block_id;
2168    let input_tys = match_extern_variant_arm_input_types(ctx, ok_variant.ty, &extern_enum);
2169    let mut input_vars: Vec<VariableId> =
2170        input_tys.into_iter().map(|ty| ctx.new_var(VarRequest { ty, location })).collect();
2171    let block_ok_input_vars = input_vars.clone();
2172    match_extern_arm_ref_args_bind(ctx, &mut input_vars, &extern_enum, &mut subscope_ok);
2173    let expr = extern_facade_expr(ctx, ok_variant.ty, input_vars, location)
2174        .as_var_usage(ctx, &mut subscope_ok)?;
2175    let sealed_block_ok = subscope_ok.goto_callsite(Some(expr));
2176
2177    // Err arm.
2178    let mut subscope_err = create_subscope(ctx, builder);
2179    let block_err_id = subscope_err.block_id;
2180    let input_tys = match_extern_variant_arm_input_types(ctx, err_variant.ty, &extern_enum);
2181    let mut input_vars: Vec<VariableId> =
2182        input_tys.into_iter().map(|ty| ctx.new_var(VarRequest { ty, location })).collect();
2183    let block_err_input_vars = input_vars.clone();
2184
2185    match_extern_arm_ref_args_bind(ctx, &mut input_vars, &extern_enum, &mut subscope_err);
2186    let expr = extern_facade_expr(ctx, err_variant.ty, input_vars, location);
2187    let input = expr.as_var_usage(ctx, &mut subscope_err)?;
2188    let err_res = generators::EnumConstruct { input, variant: *func_err_variant, location }
2189        .add(ctx, &mut subscope_err.statements);
2190
2191    let ret_expr = lower_return(ctx, &mut subscope_err, err_res, location, true);
2192    let sealed_block_err = lowered_expr_to_block_scope_end(ctx, subscope_err, ret_expr)
2193        .map_err(LoweringFlowError::Failed)?;
2194
2195    // Merge.
2196    let match_info = MatchInfo::Extern(MatchExternInfo {
2197        function: extern_enum.function.lowered(ctx.db),
2198        inputs: extern_enum.inputs,
2199        arms: vec![
2200            MatchArm {
2201                arm_selector: MatchArmSelector::VariantId(*ok_variant),
2202                block_id: block_ok_id,
2203                var_ids: block_ok_input_vars,
2204            },
2205            MatchArm {
2206                arm_selector: MatchArmSelector::VariantId(*err_variant),
2207                block_id: block_err_id,
2208                var_ids: block_err_input_vars,
2209            },
2210        ],
2211        location,
2212    });
2213    builder.merge_and_end_with_match(
2214        ctx,
2215        match_info,
2216        vec![sealed_block_ok, sealed_block_err],
2217        location,
2218    )
2219}
2220
2221/// Returns the input types for an extern match variant arm.
2222fn match_extern_variant_arm_input_types(
2223    ctx: &mut LoweringContext<'_, '_>,
2224    ty: semantic::TypeId,
2225    extern_enum: &LoweredExprExternEnum,
2226) -> Vec<semantic::TypeId> {
2227    let variant_input_tys = extern_facade_return_tys(ctx, ty);
2228    let ref_tys = extern_enum.member_paths.iter().map(|ref_arg| ref_arg.ty());
2229    chain!(ref_tys, variant_input_tys.into_iter()).collect()
2230}
2231
2232/// Binds input references and implicits when matching on extern functions.
2233fn match_extern_arm_ref_args_bind(
2234    ctx: &mut LoweringContext<'_, '_>,
2235    arm_inputs: &mut Vec<VariableId>,
2236    extern_enum: &LoweredExprExternEnum,
2237    subscope: &mut BlockBuilder,
2238) {
2239    let ref_outputs: Vec<_> = arm_inputs.drain(0..extern_enum.member_paths.len()).collect();
2240    // Bind the ref parameters.
2241    for (ref_arg, output_var) in zip_eq(&extern_enum.member_paths, ref_outputs) {
2242        subscope.update_ref(ctx, ref_arg, output_var);
2243    }
2244}
2245
2246/// Lowers an expression of type [semantic::ExprAssignment].
2247fn lower_expr_assignment(
2248    ctx: &mut LoweringContext<'_, '_>,
2249    expr: &semantic::ExprAssignment,
2250    builder: &mut BlockBuilder,
2251) -> LoweringResult<LoweredExpr> {
2252    log::trace!(
2253        "Started lowering of an assignment expression: {:?}",
2254        expr.debug(&ctx.expr_formatter)
2255    );
2256    let location = ctx.get_location(expr.stable_ptr.untyped());
2257    let var = lower_expr(ctx, builder, expr.rhs)?.as_var_usage(ctx, builder)?.var_id;
2258    builder.update_ref(ctx, &expr.ref_arg, var);
2259    Ok(LoweredExpr::Tuple { exprs: vec![], location })
2260}
2261
2262/// Allocates and empty block in `ctx`.
2263fn alloc_empty_block(ctx: &mut LoweringContext<'_, '_>) -> BlockId {
2264    ctx.blocks.alloc_empty()
2265}
2266
2267/// Creates a new subscope of the given builder, with an empty block.
2268fn create_subscope(ctx: &mut LoweringContext<'_, '_>, builder: &BlockBuilder) -> BlockBuilder {
2269    builder.child_block_builder(alloc_empty_block(ctx))
2270}
2271
2272/// Calls `.check_error_free()` and warns (in log) if there are errors.
2273fn check_error_free_or_warn(
2274    db: &dyn LoweringGroup,
2275    diagnostics: Diagnostics<SemanticDiagnostic>,
2276    semantic_function_id: defs::ids::FunctionWithBodyId,
2277    diagnostics_description: &str,
2278) -> Maybe<()> {
2279    let declaration_error_free = diagnostics.check_error_free();
2280    declaration_error_free.inspect_err(|_| {
2281        log::warn!(
2282            "Function `{function_path}` has semantic diagnostics in its \
2283             {diagnostics_description}:\n{diagnostics_format}",
2284            function_path = semantic_function_id.full_path(db),
2285            diagnostics_format = diagnostics.format(db.upcast())
2286        );
2287    })
2288}