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