cairo_lang_lowering/lower/
mod.rs

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