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