Skip to main content

cairo_lang_lowering/lower/
mod.rs

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