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