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