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