Skip to main content

cairo_lang_lowering/lower/
mod.rs

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