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#[derive(Clone, Debug, PartialEq, Eq)]
78pub struct MultiLowering {
79 pub main_lowering: FlatLowered,
80 pub generated_lowerings: OrderedHashMap<GeneratedFunctionKey, FlatLowered>,
81}
82
83pub 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 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
115pub 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 let semantic_block =
127 extract_matches!(&ctx.function_body.arenas.exprs[block_expr_id], semantic::Expr::Block)
128 .clone();
129
130 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 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
172pub 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 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
271pub 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 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 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 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
353pub 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
415pub fn lower_loop_function(
419 encapsulating_ctx: &mut EncapsulatingLoweringContext<'_>,
420 function_id: FunctionWithBodyId,
421 loop_signature: Signature,
422 loop_expr_id: semantic::ExprId,
423 snapped_params: &OrderedHashMap<MemberPath, semantic::ExprVarMemberPath>,
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 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>(&(¶m).into()) {
441 builder.update_snap_ref(¶m, var)
442 } else {
443 builder.semantics.introduce((¶m).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 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 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
506fn 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 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 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
551fn 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
561fn 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 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 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 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
597pub 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
609pub 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
625pub 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 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
676fn 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 let var = lowered_expr.as_var_usage(ctx, builder)?.var_id;
706 ctx.variables.variables[var].location = ctx.get_location(stable_ptr.untyped());
708 builder.put_semantic(sem_var.id(), var);
709 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
773fn 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
835fn 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
850fn 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
899fn 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 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 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 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
974fn 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
994fn 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
1030fn 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
1075fn 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
1091fn 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 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
1135fn 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 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
1170fn 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
1189fn 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 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 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 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 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 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
1266struct 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
1275fn 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 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 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 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
1349fn 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 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 let function = FunctionWithBodyLongId::Generated {
1431 parent: ctx.semantic_function_id,
1432 key: GeneratedFunctionKey::Loop(loop_expr_id),
1433 }
1434 .intern(ctx.db);
1435
1436 let snap_usage = ctx.usages.usages[&loop_expr_id].snap_usage.clone();
1437
1438 let encapsulating_ctx = std::mem::take(&mut ctx.encapsulating_ctx).unwrap();
1440 let lowered = lower_loop_function(
1441 encapsulating_ctx,
1442 function,
1443 loop_signature.clone(),
1444 loop_expr_id,
1445 &snap_usage,
1446 )
1447 .map_err(LoweringFlowError::Failed)?;
1448 encapsulating_ctx.lowerings.insert(GeneratedFunctionKey::Loop(loop_expr_id), lowered);
1450 ctx.encapsulating_ctx = Some(encapsulating_ctx);
1451 let old_loop_expr_id = std::mem::replace(&mut ctx.current_loop_expr_id, Some(loop_expr_id));
1452 for snapshot_param in snap_usage.values() {
1453 if let Some(input) = builder.get_ref(ctx, snapshot_param) {
1456 let (original, snapped) = generators::Snapshot {
1457 input,
1458 location: ctx.get_location(snapshot_param.stable_ptr().untyped()),
1459 }
1460 .add(ctx, &mut builder.statements);
1461 builder.update_ref(ctx, snapshot_param, original);
1463 builder.update_snap_ref(snapshot_param, snapped);
1464 }
1465 }
1466 let call = call_loop_func(ctx, loop_signature, builder, loop_expr_id, stable_ptr.untyped());
1467
1468 ctx.current_loop_expr_id = old_loop_expr_id;
1469 call
1470}
1471
1472fn call_loop_func(
1474 ctx: &mut LoweringContext<'_, '_>,
1475 loop_signature: Signature,
1476 builder: &mut BlockBuilder,
1477 loop_expr_id: ExprId,
1478 stable_ptr: SyntaxStablePtrId,
1479) -> LoweringResult<LoweredExpr> {
1480 let location = ctx.get_location(stable_ptr);
1481
1482 let function = FunctionLongId::Generated(GeneratedFunction {
1484 parent: ctx.concrete_function_id.base_semantic_function(ctx.db),
1485 key: GeneratedFunctionKey::Loop(loop_expr_id),
1486 })
1487 .intern(ctx.db);
1488 let inputs = loop_signature
1489 .params
1490 .into_iter()
1491 .map(|param| {
1492 builder
1493 .get_ref(ctx, ¶m)
1494 .and_then(|var| (ctx.variables[var.var_id].ty == param.ty()).then_some(var))
1495 .or_else(|| {
1496 let var = builder.get_snap_ref(ctx, ¶m)?;
1497 (ctx.variables[var.var_id].ty == param.ty()).then_some(var)
1498 })
1499 .ok_or_else(|| {
1500 LoweringFlowError::Failed(
1503 ctx.diagnostics.report(param.stable_ptr(), MemberPathLoop),
1504 )
1505 })
1506 })
1507 .collect::<LoweringResult<Vec<_>>>()?;
1508 let extra_ret_tys = loop_signature.extra_rets.iter().map(|path| path.ty()).collect_vec();
1509 let call_result = generators::Call {
1510 function,
1511 inputs,
1512 coupon_input: None,
1513 extra_ret_tys,
1514 ret_tys: vec![loop_signature.return_type],
1515 location,
1516 }
1517 .add(ctx, &mut builder.statements);
1518
1519 for (ref_arg, output_var) in zip_eq(&loop_signature.extra_rets, call_result.extra_outputs) {
1521 builder.update_ref(ctx, ref_arg, output_var.var_id);
1522 }
1523
1524 Ok(LoweredExpr::AtVariable(call_result.returns.into_iter().next().unwrap()))
1525}
1526
1527fn lower_exprs_to_var_usages(
1530 ctx: &mut LoweringContext<'_, '_>,
1531 args: &[semantic::ExprFunctionCallArg],
1532 builder: &mut BlockBuilder,
1533) -> Result<Vec<VarUsage>, LoweringFlowError> {
1534 let mut value_iter = args
1540 .iter()
1541 .filter_map(|arg| try_extract_matches!(arg, ExprFunctionCallArg::Value))
1542 .map(|arg_expr_id| lower_expr_to_var_usage(ctx, builder, *arg_expr_id))
1543 .collect::<Result<Vec<_>, _>>()?
1544 .into_iter();
1545 Ok(args
1546 .iter()
1547 .map(|arg| match arg {
1548 semantic::ExprFunctionCallArg::Reference(ref_arg) => {
1549 builder.get_ref(ctx, ref_arg).unwrap()
1550 }
1551 semantic::ExprFunctionCallArg::Value(_) => value_iter.next().unwrap(),
1552 })
1553 .collect())
1554}
1555
1556fn lower_expr_enum_ctor(
1558 ctx: &mut LoweringContext<'_, '_>,
1559 expr: &semantic::ExprEnumVariantCtor,
1560 builder: &mut BlockBuilder,
1561) -> LoweringResult<LoweredExpr> {
1562 log::trace!(
1563 "Started lowering of an enum c'tor expression: {:?}",
1564 expr.debug(&ctx.expr_formatter)
1565 );
1566 let location = ctx.get_location(expr.stable_ptr.untyped());
1567 Ok(LoweredExpr::AtVariable(
1568 generators::EnumConstruct {
1569 input: lower_expr_to_var_usage(ctx, builder, expr.value_expr)?,
1570 variant: expr.variant.clone(),
1571 location,
1572 }
1573 .add(ctx, &mut builder.statements),
1574 ))
1575}
1576
1577fn lower_expr_member_access(
1579 ctx: &mut LoweringContext<'_, '_>,
1580 expr: &semantic::ExprMemberAccess,
1581 builder: &mut BlockBuilder,
1582) -> LoweringResult<LoweredExpr> {
1583 log::trace!("Lowering a member-access expression: {:?}", expr.debug(&ctx.expr_formatter));
1584 let location = ctx.get_location(expr.stable_ptr.untyped());
1585 let members = ctx
1586 .db
1587 .concrete_struct_members(expr.concrete_struct_id)
1588 .map_err(LoweringFlowError::Failed)?;
1589 let member_idx =
1590 members.iter().position(|(_, member)| member.id == expr.member).ok_or_else(|| {
1591 LoweringFlowError::Failed(
1592 ctx.diagnostics.report(expr.stable_ptr.untyped(), UnexpectedError),
1593 )
1594 })?;
1595 if let Some(member_path) = &expr.member_path {
1596 return Ok(LoweredExpr::Member(
1597 member_path.clone(),
1598 ctx.get_location(expr.stable_ptr.untyped()),
1599 ));
1600 }
1601 Ok(LoweredExpr::AtVariable(
1602 generators::StructMemberAccess {
1603 input: lower_expr_to_var_usage(ctx, builder, expr.expr)?,
1604 member_tys: members
1605 .iter()
1606 .map(|(_, member)| wrap_in_snapshots(ctx.db.upcast(), member.ty, expr.n_snapshots))
1607 .collect(),
1608 member_idx,
1609 location,
1610 }
1611 .add(ctx, &mut builder.statements),
1612 ))
1613}
1614
1615fn lower_expr_struct_ctor(
1617 ctx: &mut LoweringContext<'_, '_>,
1618 expr: &semantic::ExprStructCtor,
1619 builder: &mut BlockBuilder,
1620) -> LoweringResult<LoweredExpr> {
1621 log::trace!("Lowering a struct c'tor expression: {:?}", expr.debug(&ctx.expr_formatter));
1622 let location = ctx.get_location(expr.stable_ptr.untyped());
1623 let members = ctx
1624 .db
1625 .concrete_struct_members(expr.concrete_struct_id)
1626 .map_err(LoweringFlowError::Failed)?;
1627 let mut member_expr_usages =
1628 UnorderedHashMap::<_, _>::from_iter(expr.members.iter().map(|(id, expr)| {
1629 let usage = lower_expr_to_var_usage(ctx, builder, *expr);
1630 (*id, usage)
1631 }));
1632 if members.len() != member_expr_usages.len() {
1633 let base_struct = lower_expr(ctx, builder, expr.base_struct.unwrap())?;
1635 if let LoweredExpr::Member(path, location) = base_struct {
1636 for (_, member) in members.iter() {
1637 let Entry::Vacant(entry) = member_expr_usages.entry(member.id) else {
1638 continue;
1639 };
1640 let member_path = ExprVarMemberPath::Member {
1641 parent: Box::new(path.clone()),
1642 member_id: member.id,
1643 stable_ptr: path.stable_ptr(),
1644 concrete_struct_id: expr.concrete_struct_id,
1645 ty: member.ty,
1646 };
1647 entry.insert(Ok(
1648 LoweredExpr::Member(member_path, location).as_var_usage(ctx, builder)?
1649 ));
1650 }
1651 } else {
1652 for (base_member, (_, member)) in izip!(
1653 StructDestructure {
1654 input: base_struct.as_var_usage(ctx, builder)?,
1655 var_reqs: members
1656 .iter()
1657 .map(|(_, member)| VarRequest { ty: member.ty, location })
1658 .collect(),
1659 }
1660 .add(ctx, &mut builder.statements),
1661 members.iter()
1662 ) {
1663 match member_expr_usages.entry(member.id) {
1664 Entry::Occupied(_) => {}
1665 Entry::Vacant(entry) => {
1666 entry.insert(Ok(VarUsage { var_id: base_member, location }));
1667 }
1668 }
1669 }
1670 }
1671 }
1672 Ok(LoweredExpr::AtVariable(
1673 generators::StructConstruct {
1674 inputs: members
1675 .iter()
1676 .map(|(_, member)| member_expr_usages.remove(&member.id).unwrap())
1677 .collect::<Result<Vec<_>, _>>()?,
1678 ty: expr.ty,
1679 location,
1680 }
1681 .add(ctx, &mut builder.statements),
1682 ))
1683}
1684
1685fn add_capture_destruct_impl(
1687 ctx: &mut LoweringContext<'_, '_>,
1688 capture_var_usage: VarUsage,
1689 closure_info: &ClosureInfo,
1690 location: StableLocation,
1691) -> Maybe<()> {
1692 let capture_var = &ctx.variables.variables[capture_var_usage.var_id];
1693 let Some(Ok(impl_id)) = [&capture_var.destruct_impl, &capture_var.panic_destruct_impl]
1695 .iter()
1696 .find(|infer_result| infer_result.is_ok())
1697 else {
1698 return Ok(());
1699 };
1700
1701 let semantic_db = ctx.db.upcast();
1702 let concrete_trait = impl_id.concrete_trait(semantic_db)?;
1703
1704 let trait_functions = semantic_db.trait_functions(concrete_trait.trait_id(semantic_db))?;
1705
1706 assert_eq!(trait_functions.len(), 1);
1707 let trait_function = *trait_functions.values().next().unwrap();
1708
1709 let generic_function = GenericFunctionId::Impl(ImplGenericFunctionId {
1710 impl_id: *impl_id,
1711 function: trait_function,
1712 });
1713
1714 let function = semantic::FunctionLongId {
1715 function: ConcreteFunction { generic_function, generic_args: vec![] },
1716 }
1717 .intern(ctx.db);
1718
1719 let signature =
1720 Signature::from_semantic(ctx.db, semantic_db.concrete_function_signature(function)?);
1721
1722 let func_key = GeneratedFunctionKey::TraitFunc(function, location);
1723 let function_id =
1724 FunctionWithBodyLongId::Generated { parent: ctx.semantic_function_id, key: func_key }
1725 .intern(ctx.db);
1726
1727 let location_id = LocationId::from_stable_location(ctx.db, location);
1728
1729 let encapsulating_ctx = std::mem::take(&mut ctx.encapsulating_ctx).unwrap();
1730 let lowered_impl_res = get_destruct_lowering(
1731 LoweringContext::new(encapsulating_ctx, function_id, signature)?,
1732 location_id,
1733 closure_info,
1734 );
1735 ctx.encapsulating_ctx = Some(encapsulating_ctx);
1737 ctx.lowerings.insert(func_key, lowered_impl_res?);
1738 Ok(())
1739}
1740
1741fn get_destruct_lowering(
1743 mut ctx: LoweringContext<'_, '_>,
1744 location_id: LocationId,
1745 closure_info: &ClosureInfo,
1746) -> Maybe<FlatLowered> {
1747 let root_block_id = alloc_empty_block(&mut ctx);
1748 let mut builder = BlockBuilder::root(&mut ctx, root_block_id);
1749
1750 let parameters = ctx
1751 .signature
1752 .params
1753 .clone()
1754 .into_iter()
1755 .map(|param| {
1756 let var = ctx.new_var(VarRequest { ty: param.ty(), location: location_id });
1757 builder.semantics.introduce((¶m).into(), var);
1758 var
1759 })
1760 .collect_vec();
1761
1762 builder.destructure_closure(&mut ctx, location_id, parameters[0], closure_info);
1763 let var_usage = generators::StructConstruct {
1764 inputs: vec![],
1765 ty: unit_ty(ctx.db.upcast()),
1766 location: location_id,
1767 }
1768 .add(&mut ctx, &mut builder.statements);
1769 builder.ret(&mut ctx, var_usage, location_id)?;
1770 let lowered_impl = FlatLowered {
1771 diagnostics: ctx.diagnostics.build(),
1772 variables: ctx.variables.variables,
1773 blocks: ctx.blocks.build().unwrap(),
1774 signature: ctx.signature,
1775 parameters,
1776 };
1777 Ok(lowered_impl)
1778}
1779
1780fn add_closure_call_function(
1782 encapsulated_ctx: &mut LoweringContext<'_, '_>,
1783 expr: &semantic::ExprClosure,
1784 closure_info: &ClosureInfo,
1785 trait_id: cairo_lang_defs::ids::TraitId,
1786) -> Maybe<()> {
1787 let semantic_db: &dyn SemanticGroup = encapsulated_ctx.db.upcast();
1788 let closure_ty = extract_matches!(expr.ty.lookup_intern(semantic_db), TypeLongId::Closure);
1789 let expr_location = encapsulated_ctx.get_location(expr.stable_ptr.untyped());
1790 let parameters_ty = TypeLongId::Tuple(closure_ty.param_tys.clone()).intern(semantic_db);
1791 let concrete_trait = ConcreteTraitLongId {
1792 trait_id,
1793 generic_args: vec![
1794 GenericArgumentId::Type(expr.ty),
1795 GenericArgumentId::Type(parameters_ty),
1796 ],
1797 }
1798 .intern(semantic_db);
1799 let Ok(impl_id) = semantic::types::get_impl_at_context(
1800 semantic_db,
1801 encapsulated_ctx.variables.lookup_context.clone(),
1802 concrete_trait,
1803 None,
1804 ) else {
1805 return Ok(());
1808 };
1809 if !matches!(impl_id.lookup_intern(semantic_db), ImplLongId::GeneratedImpl(_)) {
1810 return Ok(());
1812 }
1813
1814 let trait_function: cairo_lang_defs::ids::TraitFunctionId = semantic_db
1815 .trait_function_by_name(trait_id, "call".into())
1816 .unwrap()
1817 .expect("Call function must exist for an Fn trait.");
1818
1819 let generic_function =
1820 GenericFunctionId::Impl(ImplGenericFunctionId { impl_id, function: trait_function });
1821 let function = semantic::FunctionLongId {
1822 function: ConcreteFunction { generic_function, generic_args: vec![] },
1823 }
1824 .intern(semantic_db);
1825 let function_with_body_id = FunctionWithBodyLongId::Generated {
1826 parent: encapsulated_ctx.semantic_function_id,
1827 key: GeneratedFunctionKey::TraitFunc(function, closure_ty.wrapper_location),
1828 }
1829 .intern(encapsulated_ctx.db);
1830 let signature = Signature::from_semantic(
1831 encapsulated_ctx.db,
1832 semantic_db.concrete_function_signature(function)?,
1833 );
1834
1835 let mut ctx = LoweringContext::new(encapsulated_ctx, function_with_body_id, signature)?;
1836
1837 let root_block_id = alloc_empty_block(&mut ctx);
1838 let mut builder = BlockBuilder::root(&mut ctx, root_block_id);
1839
1840 let (closure_param_var_id, closure_var) = if trait_id
1841 == semantic::corelib::fn_once_trait(semantic_db)
1842 {
1843 let closure_param_var = ctx.new_var(VarRequest { ty: expr.ty, location: expr_location });
1845 let closure_var = VarUsage { var_id: closure_param_var, location: expr_location };
1846 (closure_param_var, closure_var)
1847 } else {
1848 let closure_param_var = ctx.new_var(VarRequest {
1850 ty: wrap_in_snapshots(semantic_db, expr.ty, 1),
1851 location: expr_location,
1852 });
1853
1854 let closure_var = generators::Desnap {
1855 input: VarUsage { var_id: closure_param_var, location: expr_location },
1856 location: expr_location,
1857 }
1858 .add(&mut ctx, &mut builder.statements);
1859 (closure_param_var, closure_var)
1860 };
1861 let parameters: Vec<VariableId> = [
1862 closure_param_var_id,
1863 ctx.new_var(VarRequest { ty: parameters_ty, location: expr_location }),
1864 ]
1865 .into();
1866 let captured_vars = generators::StructDestructure {
1867 input: closure_var,
1868 var_reqs: chain!(closure_info.members.iter(), closure_info.snapshots.iter())
1869 .map(|(_, ty)| VarRequest { ty: *ty, location: expr_location })
1870 .collect_vec(),
1871 }
1872 .add(&mut ctx, &mut builder.statements);
1873 for (i, (param, _)) in closure_info.members.iter().enumerate() {
1874 builder.semantics.introduce(param.clone(), captured_vars[i]);
1875 }
1876 for (i, (param, _)) in closure_info.snapshots.iter().enumerate() {
1877 builder
1878 .snapped_semantics
1879 .insert(param.clone(), captured_vars[i + closure_info.members.len()]);
1880 }
1881 let param_vars = generators::StructDestructure {
1882 input: VarUsage { var_id: parameters[1], location: expr_location },
1883 var_reqs: closure_ty
1884 .param_tys
1885 .iter()
1886 .map(|ty| VarRequest { ty: *ty, location: expr_location })
1887 .collect_vec(),
1888 }
1889 .add(&mut ctx, &mut builder.statements);
1890 for (param_var, param) in param_vars.into_iter().zip(expr.params.iter()) {
1891 builder.semantics.introduce((¶meter_as_member_path(param.clone())).into(), param_var);
1892 }
1893 let lowered_expr = lower_expr(&mut ctx, &mut builder, expr.body);
1894 let maybe_sealed_block = lowered_expr_to_block_scope_end(&mut ctx, builder, lowered_expr);
1895 let root_ok = maybe_sealed_block.and_then(|block_sealed| {
1896 wrap_sealed_block_as_function(&mut ctx, block_sealed, expr.stable_ptr.untyped())?;
1897 Ok(root_block_id)
1898 });
1899 let blocks = root_ok
1900 .map(|_| ctx.blocks.build().expect("Root block must exist."))
1901 .unwrap_or_else(FlatBlocks::new_errored);
1902
1903 let lowered = FlatLowered {
1904 diagnostics: ctx.diagnostics.build(),
1905 variables: ctx.variables.variables,
1906 blocks,
1907 signature: ctx.signature.clone(),
1908 parameters,
1909 };
1910 encapsulated_ctx
1911 .lowerings
1912 .insert(GeneratedFunctionKey::TraitFunc(function, closure_ty.wrapper_location), lowered);
1913 Ok(())
1914}
1915
1916fn lower_expr_closure(
1918 ctx: &mut LoweringContext<'_, '_>,
1919 expr: &semantic::ExprClosure,
1920 expr_id: semantic::ExprId,
1921 builder: &mut BlockBuilder,
1922) -> LoweringResult<LoweredExpr> {
1923 log::trace!("Lowering a closure expression: {:?}", expr.debug(&ctx.expr_formatter));
1924
1925 let usage = ctx.usages.usages[&expr_id].clone();
1926 let capture_var_usage = builder.capture(ctx, usage.clone(), expr);
1927 let closure_variable = LoweredExpr::AtVariable(capture_var_usage);
1928 let closure_ty = extract_matches!(expr.ty.lookup_intern(ctx.db), TypeLongId::Closure);
1929 let _ = add_capture_destruct_impl(
1930 ctx,
1931 capture_var_usage,
1932 builder.semantics.closures.get(&capture_var_usage.var_id).unwrap(),
1933 closure_ty.wrapper_location,
1934 );
1935 add_closure_call_function(
1936 ctx,
1937 expr,
1938 builder.semantics.closures.get(&capture_var_usage.var_id).unwrap(),
1939 if ctx.variables[capture_var_usage.var_id].copyable.is_ok() {
1940 semantic::corelib::fn_trait(ctx.db.upcast())
1941 } else {
1942 semantic::corelib::fn_once_trait(ctx.db.upcast())
1943 },
1944 )
1945 .map_err(LoweringFlowError::Failed)?;
1946
1947 Ok(closure_variable)
1948}
1949
1950fn lower_expr_error_propagate(
1952 ctx: &mut LoweringContext<'_, '_>,
1953 expr: &semantic::ExprPropagateError,
1954 builder: &mut BlockBuilder,
1955) -> LoweringResult<LoweredExpr> {
1956 log::trace!(
1957 "Started lowering of an error-propagate expression: {:?}",
1958 expr.debug(&ctx.expr_formatter)
1959 );
1960 let location = ctx.get_location(expr.stable_ptr.untyped());
1961 let lowered_expr = lower_expr(ctx, builder, expr.inner)?;
1962 let ExprPropagateError { ok_variant, err_variant, func_err_variant, .. } = expr;
1963 if let LoweredExpr::ExternEnum(extern_enum) = lowered_expr {
1964 return lower_optimized_extern_error_propagate(
1965 ctx,
1966 builder,
1967 extern_enum,
1968 ok_variant,
1969 err_variant,
1970 func_err_variant,
1971 location,
1972 );
1973 }
1974
1975 let match_input = lowered_expr.as_var_usage(ctx, builder)?;
1976 let subscope_ok = create_subscope_with_bound_refs(ctx, builder);
1978 let block_ok_id = subscope_ok.block_id;
1979 let expr_var = ctx.new_var(VarRequest { ty: ok_variant.ty, location });
1980 let sealed_block_ok = subscope_ok.goto_callsite(Some(VarUsage { var_id: expr_var, location }));
1981
1982 let mut subscope_err = create_subscope_with_bound_refs(ctx, builder);
1984 let block_err_id = subscope_err.block_id;
1985 let err_value = ctx.new_var(VarRequest { ty: err_variant.ty, location });
1986 let err_res = generators::EnumConstruct {
1987 input: VarUsage { var_id: err_value, location },
1988 variant: func_err_variant.clone(),
1989 location,
1990 }
1991 .add(ctx, &mut subscope_err.statements);
1992 subscope_err.ret(ctx, err_res, location).map_err(LoweringFlowError::Failed)?;
1993 let sealed_block_err = SealedBlockBuilder::Ends(block_err_id);
1994
1995 let match_info = MatchInfo::Enum(MatchEnumInfo {
1997 concrete_enum_id: ok_variant.concrete_enum_id,
1998 input: match_input,
1999 arms: vec![
2000 MatchArm {
2001 arm_selector: MatchArmSelector::VariantId(ok_variant.clone()),
2002 block_id: block_ok_id,
2003 var_ids: vec![expr_var],
2004 },
2005 MatchArm {
2006 arm_selector: MatchArmSelector::VariantId(err_variant.clone()),
2007 block_id: block_err_id,
2008 var_ids: vec![err_value],
2009 },
2010 ],
2011 location,
2012 });
2013 builder.merge_and_end_with_match(
2014 ctx,
2015 match_info,
2016 vec![sealed_block_ok, sealed_block_err],
2017 location,
2018 )
2019}
2020
2021fn lower_optimized_extern_error_propagate(
2023 ctx: &mut LoweringContext<'_, '_>,
2024 builder: &mut BlockBuilder,
2025 extern_enum: LoweredExprExternEnum,
2026 ok_variant: &semantic::ConcreteVariant,
2027 err_variant: &semantic::ConcreteVariant,
2028 func_err_variant: &semantic::ConcreteVariant,
2029 location: LocationId,
2030) -> LoweringResult<LoweredExpr> {
2031 log::trace!("Started lowering of an optimized error-propagate expression.");
2032
2033 let mut subscope_ok = create_subscope(ctx, builder);
2035 let block_ok_id = subscope_ok.block_id;
2036 let input_tys = match_extern_variant_arm_input_types(ctx, ok_variant.ty, &extern_enum);
2037 let mut input_vars: Vec<VariableId> =
2038 input_tys.into_iter().map(|ty| ctx.new_var(VarRequest { ty, location })).collect();
2039 let block_ok_input_vars = input_vars.clone();
2040 match_extern_arm_ref_args_bind(ctx, &mut input_vars, &extern_enum, &mut subscope_ok);
2041 let expr = extern_facade_expr(ctx, ok_variant.ty, input_vars, location)
2042 .as_var_usage(ctx, &mut subscope_ok)?;
2043 let sealed_block_ok = subscope_ok.goto_callsite(Some(expr));
2044
2045 let mut subscope_err = create_subscope(ctx, builder);
2047 let block_err_id = subscope_err.block_id;
2048 let input_tys = match_extern_variant_arm_input_types(ctx, err_variant.ty, &extern_enum);
2049 let mut input_vars: Vec<VariableId> =
2050 input_tys.into_iter().map(|ty| ctx.new_var(VarRequest { ty, location })).collect();
2051 let block_err_input_vars = input_vars.clone();
2052
2053 match_extern_arm_ref_args_bind(ctx, &mut input_vars, &extern_enum, &mut subscope_err);
2054 let expr = extern_facade_expr(ctx, err_variant.ty, input_vars, location);
2055 let input = expr.as_var_usage(ctx, &mut subscope_err)?;
2056 let err_res = generators::EnumConstruct { input, variant: func_err_variant.clone(), location }
2057 .add(ctx, &mut subscope_err.statements);
2058 subscope_err.ret(ctx, err_res, location).map_err(LoweringFlowError::Failed)?;
2059 let sealed_block_err = SealedBlockBuilder::Ends(block_err_id);
2060
2061 let match_info = MatchInfo::Extern(MatchExternInfo {
2063 function: extern_enum.function.lowered(ctx.db),
2064 inputs: extern_enum.inputs,
2065 arms: vec![
2066 MatchArm {
2067 arm_selector: MatchArmSelector::VariantId(ok_variant.clone()),
2068 block_id: block_ok_id,
2069 var_ids: block_ok_input_vars,
2070 },
2071 MatchArm {
2072 arm_selector: MatchArmSelector::VariantId(err_variant.clone()),
2073 block_id: block_err_id,
2074 var_ids: block_err_input_vars,
2075 },
2076 ],
2077 location,
2078 });
2079 builder.merge_and_end_with_match(
2080 ctx,
2081 match_info,
2082 vec![sealed_block_ok, sealed_block_err],
2083 location,
2084 )
2085}
2086
2087fn match_extern_variant_arm_input_types(
2089 ctx: &mut LoweringContext<'_, '_>,
2090 ty: semantic::TypeId,
2091 extern_enum: &LoweredExprExternEnum,
2092) -> Vec<semantic::TypeId> {
2093 let variant_input_tys = extern_facade_return_tys(ctx, ty);
2094 let ref_tys = extern_enum.member_paths.iter().map(|ref_arg| ref_arg.ty());
2095 chain!(ref_tys, variant_input_tys.into_iter()).collect()
2096}
2097
2098fn match_extern_arm_ref_args_bind(
2100 ctx: &mut LoweringContext<'_, '_>,
2101 arm_inputs: &mut Vec<VariableId>,
2102 extern_enum: &LoweredExprExternEnum,
2103 subscope: &mut BlockBuilder,
2104) {
2105 let ref_outputs: Vec<_> = arm_inputs.drain(0..extern_enum.member_paths.len()).collect();
2106 for (ref_arg, output_var) in zip_eq(&extern_enum.member_paths, ref_outputs) {
2108 subscope.update_ref(ctx, ref_arg, output_var);
2109 }
2110}
2111
2112fn lower_expr_assignment(
2114 ctx: &mut LoweringContext<'_, '_>,
2115 expr: &semantic::ExprAssignment,
2116 builder: &mut BlockBuilder,
2117) -> LoweringResult<LoweredExpr> {
2118 log::trace!(
2119 "Started lowering of an assignment expression: {:?}",
2120 expr.debug(&ctx.expr_formatter)
2121 );
2122 let location = ctx.get_location(expr.stable_ptr.untyped());
2123 let var = lower_expr(ctx, builder, expr.rhs)?.as_var_usage(ctx, builder)?.var_id;
2124 builder.update_ref(ctx, &expr.ref_arg, var);
2125 Ok(LoweredExpr::Tuple { exprs: vec![], location })
2126}
2127
2128fn alloc_empty_block(ctx: &mut LoweringContext<'_, '_>) -> BlockId {
2130 ctx.blocks.alloc_empty()
2131}
2132
2133fn create_subscope_with_bound_refs(
2135 ctx: &mut LoweringContext<'_, '_>,
2136 builder: &BlockBuilder,
2137) -> BlockBuilder {
2138 builder.child_block_builder(alloc_empty_block(ctx))
2139}
2140
2141fn create_subscope(ctx: &mut LoweringContext<'_, '_>, builder: &BlockBuilder) -> BlockBuilder {
2143 builder.child_block_builder(alloc_empty_block(ctx))
2144}
2145
2146fn check_error_free_or_warn(
2148 db: &dyn LoweringGroup,
2149 diagnostics: Diagnostics<SemanticDiagnostic>,
2150 semantic_function_id: defs::ids::FunctionWithBodyId,
2151 diagnostics_description: &str,
2152) -> Maybe<()> {
2153 let declaration_error_free = diagnostics.check_error_free();
2154 declaration_error_free.inspect_err(|_| {
2155 log::warn!(
2156 "Function `{function_path}` has semantic diagnostics in its \
2157 {diagnostics_description}:\n{diagnostics_format}",
2158 function_path = semantic_function_id.full_path(db.upcast()),
2159 diagnostics_format = diagnostics.format(db.upcast())
2160 );
2161 })
2162}