1use cairo_lang_debug::DebugWithDb;
2use cairo_lang_defs::diagnostic_utils::StableLocation;
3use cairo_lang_diagnostics::{Diagnostics, Maybe};
4use cairo_lang_filesystem::ids::SmolStrId;
5use cairo_lang_semantic::corelib::{
6 CorelibSemantic, ErrorPropagationType, bounded_int_ty, get_enum_concrete_variant,
7 try_get_ty_by_name, unwrap_error_propagation_type, validate_literal,
8};
9use cairo_lang_semantic::expr::compute::unwrap_pattern_type;
10use cairo_lang_semantic::items::constant::ConstValueId;
11use cairo_lang_semantic::items::function_with_body::FunctionWithBodySemantic;
12use cairo_lang_semantic::items::functions::{
13 FunctionsSemantic, GenericFunctionId, ImplGenericFunctionId,
14};
15use cairo_lang_semantic::items::imp::ImplLongId;
16use cairo_lang_semantic::items::structure::StructSemantic;
17use cairo_lang_semantic::items::trt::TraitSemantic;
18use cairo_lang_semantic::usage::MemberPath;
19use cairo_lang_semantic::{
20 ConcreteFunction, ConcreteTraitLongId, ExprVar, LocalVariable, VarId, corelib,
21};
22use cairo_lang_syntax::node::TypedStablePtr;
23use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
24use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
25use cairo_lang_utils::unordered_hash_map::{Entry, UnorderedHashMap};
26use cairo_lang_utils::{Intern, extract_matches};
27use context::handle_lowering_flow_error;
28use defs::ids::TopLevelLanguageElementId;
29use flow_control::create_graph::{
30 create_graph_expr_if, create_graph_expr_match, create_graph_expr_while_let,
31};
32use flow_control::lower_graph::lower_graph;
33use itertools::{Itertools, chain, izip, zip_eq};
34use num_bigint::{BigInt, Sign};
35use num_traits::ToPrimitive;
36use refs::ClosureInfo;
37use salsa::Database;
38use semantic::corelib::{
39 core_submodule, get_core_function_id, get_core_ty_by_name, get_function_id, never_ty, unit_ty,
40};
41use semantic::items::constant::ConstValue;
42use semantic::types::wrap_in_snapshots;
43use semantic::{
44 ExprFunctionCallArg, ExprId, ExprPropagateError, ExprVarMemberPath, GenericArgumentId,
45 MatchArmSelector, SemanticDiagnostic, TypeLongId,
46};
47use {cairo_lang_defs as defs, cairo_lang_semantic as semantic};
48
49use self::block_builder::{BlockBuilder, SealedBlockBuilder, SealedGotoCallsite};
50use self::context::{
51 EncapsulatingLoweringContext, LoweredExpr, LoweredExprExternEnum, LoweringContext,
52 LoweringFlowError,
53};
54use self::external::{extern_facade_expr, extern_facade_return_tys};
55use self::logical_op::lower_logical_op;
56use crate::blocks::Blocks;
57use crate::diagnostic::LoweringDiagnosticKind::{self, *};
58use crate::diagnostic::LoweringDiagnosticsBuilder;
59use crate::ids::{
60 EnrichedSemanticSignature, FunctionLongId, FunctionWithBodyId, FunctionWithBodyLongId,
61 GeneratedFunction, GeneratedFunctionKey, LocationId, SemanticFunctionIdEx,
62 parameter_as_member_path,
63};
64use crate::lower::context::{LoopContext, LoopEarlyReturnInfo, LoweringResult, RefArg, VarRequest};
65use crate::lower::generators::StructDestructure;
66use crate::{
67 BlockId, Lowered, MatchArm, MatchEnumInfo, MatchExternInfo, MatchInfo, VarUsage, VariableId,
68};
69
70mod block_builder;
71pub mod context;
72mod external;
73mod flow_control;
74pub mod generators;
75mod logical_op;
76mod lower_let_else;
77pub mod refs;
78
79#[cfg(test)]
80mod test_utils;
81
82#[cfg(test)]
83mod block_builder_test;
84
85#[cfg(test)]
86mod generated_test;
87
88#[cfg(test)]
89mod specialized_test;
90
91#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
93pub struct MultiLowering<'db> {
94 pub main_lowering: Lowered<'db>,
95 pub generated_lowerings: OrderedHashMap<GeneratedFunctionKey<'db>, Lowered<'db>>,
96}
97
98pub fn lower_semantic_function<'db>(
100 db: &'db dyn Database,
101 semantic_function_id: defs::ids::FunctionWithBodyId<'db>,
102) -> Maybe<MultiLowering<'db>> {
103 let declaration_diagnostics = db.function_declaration_diagnostics(semantic_function_id);
104 check_error_free_or_warn(db, declaration_diagnostics, semantic_function_id, "declaration")?;
105 let body_diagnostics = db.function_body_diagnostics(semantic_function_id);
106 check_error_free_or_warn(db, body_diagnostics, semantic_function_id, "body")?;
107
108 let mut encapsulating_ctx = EncapsulatingLoweringContext::new(db, semantic_function_id)?;
109 let function_id = FunctionWithBodyLongId::Semantic(semantic_function_id).intern(db);
110 let signature = db.function_with_body_signature(semantic_function_id)?;
111
112 for semantic_var in &signature.params {
114 encapsulating_ctx.semantic_defs.insert(
115 semantic::VarId::Param(semantic_var.id),
116 semantic::Binding::Param(semantic_var.clone()),
117 );
118 }
119
120 let block_expr_id = encapsulating_ctx.function_body.body_expr;
121 let main_lowering = lower_function(
122 &mut encapsulating_ctx,
123 function_id,
124 EnrichedSemanticSignature::from_semantic(db, signature),
125 block_expr_id,
126 )?;
127 Ok(MultiLowering { main_lowering, generated_lowerings: encapsulating_ctx.lowerings })
128}
129
130pub fn lower_function<'db>(
132 encapsulating_ctx: &mut EncapsulatingLoweringContext<'db>,
133 function_id: FunctionWithBodyId<'db>,
134 signature: EnrichedSemanticSignature<'db>,
135 block_expr_id: semantic::ExprId,
136) -> Maybe<Lowered<'db>> {
137 log::trace!("Lowering a free function.");
138 let return_type = signature.return_type;
139 let mut ctx = LoweringContext::new(encapsulating_ctx, function_id, signature, return_type)?;
140
141 let semantic_block =
143 extract_matches!(&ctx.function_body.arenas.exprs[block_expr_id], semantic::Expr::Block)
144 .clone();
145
146 let root_block_id = alloc_empty_block(&mut ctx);
148 let mut builder = BlockBuilder::root(root_block_id);
149
150 let parameters = ctx
151 .signature
152 .params
153 .clone()
154 .into_iter()
155 .map(|param| {
156 let location = ctx.get_location(param.stable_ptr().untyped());
157 let var = ctx.new_var(VarRequest { ty: param.ty(), location });
158 let param_var = extract_matches!(param, ExprVarMemberPath::Var);
160 builder.put_semantic(param_var.var, var);
161 var
162 })
163 .collect_vec();
164
165 let root_ok = {
166 let maybe_sealed_block = lower_block(&mut ctx, builder, &semantic_block);
167 maybe_sealed_block.and_then(|block_sealed| {
168 wrap_sealed_block_as_function(
169 &mut ctx,
170 block_sealed,
171 semantic_block.stable_ptr.untyped(),
172 )?;
173 Ok(root_block_id)
174 })
175 };
176 let blocks = root_ok
177 .map(|_| ctx.blocks.build().expect("Root block must exist."))
178 .unwrap_or_else(Blocks::new_errored);
179 Ok(Lowered {
180 diagnostics: ctx.diagnostics.build(),
181 variables: ctx.variables.variables,
182 blocks,
183 signature: ctx.signature.into(),
184 parameters,
185 })
186}
187
188pub fn lower_for_loop<'db, 'mt>(
190 ctx: &mut LoweringContext<'db, 'mt>,
191 builder: &mut BlockBuilder<'db>,
192 loop_expr: semantic::ExprFor<'db>,
193 loop_expr_id: semantic::ExprId,
194) -> LoweringResult<'db, LoweredExpr<'db>> {
195 let db = ctx.db;
196 let for_location = ctx.get_location(loop_expr.stable_ptr.untyped());
197 let next_semantic_signature =
198 db.concrete_function_signature(loop_expr.next_function_id).unwrap();
199 let into_iter = builder.get_ref(ctx, &loop_expr.into_iter_member_path).unwrap();
200 let next_call = generators::Call {
201 function: loop_expr.next_function_id.lowered(db),
202 inputs: vec![into_iter],
203 coupon_input: None,
204 extra_ret_tys: vec![next_semantic_signature.params.first().unwrap().ty],
205 ret_tys: vec![next_semantic_signature.return_type],
206 location: for_location,
207 }
208 .add(ctx, &mut builder.statements);
209 let next_iterator = next_call.extra_outputs.first().unwrap();
210 let next_value = next_call.returns.first().unwrap();
211 let ErrorPropagationType::Option { some_variant, none_variant } =
212 unwrap_error_propagation_type(db, ctx.variables[next_value.var_id].ty)
213 .expect("Expected Option type for next function return.")
214 else {
215 unreachable!("Return type for next function must be Option.")
216 };
217 let next_value_type = some_variant.ty;
218 builder.update_ref(ctx, &loop_expr.into_iter_member_path, next_iterator.var_id);
219 let unit_ty = corelib::unit_ty(db);
220 let some_block: cairo_lang_semantic::ExprBlock<'_> =
221 extract_matches!(&ctx.function_body.arenas.exprs[loop_expr.body], semantic::Expr::Block)
222 .clone();
223 let mut some_subscope = create_subscope(ctx, builder);
224 let some_subscope_block_id = some_subscope.block_id;
225 let some_var_id = ctx.new_var(VarRequest {
226 ty: next_value_type,
227 location: ctx.get_location(some_block.stable_ptr.untyped()),
228 });
229 let variant_expr = LoweredExpr::AtVariable(VarUsage {
230 var_id: some_var_id,
231 location: ctx.get_location(some_block.stable_ptr.untyped()),
232 });
233 let lowered_pattern =
234 lower_single_pattern(ctx, &mut some_subscope, loop_expr.pattern, variant_expr);
235 let sealed_some = match lowered_pattern {
236 Ok(_) => {
237 let block_expr = (|| {
238 lower_expr_block(ctx, &mut some_subscope, &some_block)?;
239 recursively_call_loop_func(
240 ctx,
241 &mut some_subscope,
242 loop_expr_id,
243 loop_expr.stable_ptr.untyped(),
244 )
245 })();
246 lowered_expr_to_block_scope_end(ctx, some_subscope, block_expr)
247 }
248 Err(err) => handle_lowering_flow_error(ctx, some_subscope.clone(), err).map(|_| None),
249 }
250 .map_err(LoweringFlowError::Failed)?;
251
252 let none_subscope = create_subscope(ctx, builder);
253 let none_var_id = ctx.new_var(VarRequest {
254 ty: unit_ty,
255 location: ctx.get_location(some_block.stable_ptr.untyped()),
256 });
257 let none_subscope_block_id = none_subscope.block_id;
258 let sealed_none = return_a_unit(ctx, none_subscope, for_location, false)?;
259
260 let match_info = MatchInfo::Enum(MatchEnumInfo {
261 concrete_enum_id: some_variant.concrete_enum_id,
262 input: *next_value,
263 arms: vec![
264 MatchArm {
265 arm_selector: MatchArmSelector::VariantId(some_variant),
266 block_id: some_subscope_block_id,
267 var_ids: vec![some_var_id],
268 },
269 MatchArm {
270 arm_selector: MatchArmSelector::VariantId(none_variant),
271 block_id: none_subscope_block_id,
272 var_ids: vec![none_var_id],
273 },
274 ],
275 location: for_location,
276 });
277 builder.merge_and_end_with_match(ctx, match_info, vec![sealed_some, sealed_none], for_location)
278}
279
280pub fn lower_while_loop<'db>(
282 ctx: &mut LoweringContext<'db, '_>,
283 builder: &mut BlockBuilder<'db>,
284 loop_expr: semantic::ExprWhile<'db>,
285 loop_expr_id: semantic::ExprId,
286) -> LoweringResult<'db, LoweredExpr<'db>> {
287 let while_location = ctx.get_location(loop_expr.stable_ptr.untyped());
288 let semantic_condition = match &loop_expr.condition {
289 semantic::Condition::BoolExpr(semantic_condition) => *semantic_condition,
290 semantic::Condition::Let(match_expr, patterns) => {
291 return (|| {
292 let ret_var = lower_expr_while_let(
293 ctx,
294 builder,
295 &loop_expr,
296 *match_expr,
297 patterns,
298 loop_expr_id,
299 loop_expr.stable_ptr.untyped(),
300 )?
301 .as_var_usage(ctx, builder)?;
302
303 lower_return(ctx, builder, ret_var, while_location, false)
304 })();
305 }
306 };
307 let condition = lower_expr_to_var_usage(ctx, builder, semantic_condition)?;
308 let db = ctx.db;
309 let unit_ty = corelib::unit_ty(db);
310
311 let mut subscope_main = create_subscope(ctx, builder);
313 let block_main_id = subscope_main.block_id;
314 let main_block =
315 extract_matches!(&ctx.function_body.arenas.exprs[loop_expr.body], semantic::Expr::Block)
316 .clone();
317 let main_block_var_id = ctx.new_var(VarRequest {
318 ty: unit_ty,
319 location: ctx.get_location(main_block.stable_ptr.untyped()),
320 });
321
322 let block_expr = (|| {
323 lower_expr_block(ctx, &mut subscope_main, &main_block)?;
324 recursively_call_loop_func(
325 ctx,
326 &mut subscope_main,
327 loop_expr_id,
328 loop_expr.stable_ptr.untyped(),
329 )
330 })();
331 let block_main = lowered_expr_to_block_scope_end(ctx, subscope_main, block_expr)
332 .map_err(LoweringFlowError::Failed)?;
333
334 let subscope_else = create_subscope(ctx, builder);
336 let block_else_id = subscope_else.block_id;
337 let else_block_input_var_id = ctx.new_var(VarRequest { ty: unit_ty, location: while_location });
338 let block_else = return_a_unit(ctx, subscope_else, while_location, false)?;
339
340 let match_info = MatchInfo::Enum(MatchEnumInfo {
341 concrete_enum_id: corelib::core_bool_enum(db),
342 input: condition,
343 arms: vec![
344 MatchArm {
345 arm_selector: MatchArmSelector::VariantId(corelib::false_variant(db)),
346 block_id: block_else_id,
347 var_ids: vec![else_block_input_var_id],
348 },
349 MatchArm {
350 arm_selector: MatchArmSelector::VariantId(corelib::true_variant(db)),
351 block_id: block_main_id,
352 var_ids: vec![main_block_var_id],
353 },
354 ],
355 location: while_location,
356 });
357 builder.merge_and_end_with_match(ctx, match_info, vec![block_main, block_else], while_location)
358}
359
360pub fn lower_expr_while_let<'db>(
362 ctx: &mut LoweringContext<'db, '_>,
363 builder: &mut BlockBuilder<'db>,
364 loop_expr: &semantic::ExprWhile<'db>,
365 matched_expr: semantic::ExprId,
366 patterns: &[semantic::PatternId],
367 loop_expr_id: semantic::ExprId,
368 loop_stable_ptr: SyntaxStablePtrId<'db>,
369) -> LoweringResult<'db, LoweredExpr<'db>> {
370 log::trace!("Lowering a match expression: {:?}", loop_expr.debug(&ctx.expr_formatter));
371 let location = ctx.get_location(loop_expr.stable_ptr.untyped());
372
373 let graph = create_graph_expr_while_let(
374 ctx,
375 patterns,
376 matched_expr,
377 loop_expr.body,
378 loop_expr_id,
379 loop_stable_ptr,
380 );
381 lower_graph(ctx, builder, &graph, location)
382}
383
384pub fn lower_loop_function<'db>(
388 encapsulating_ctx: &mut EncapsulatingLoweringContext<'db>,
389 function_id: FunctionWithBodyId<'db>,
390 loop_signature: EnrichedSemanticSignature<'db>,
391 loop_ctx: LoopContext<'db>,
392 return_type: semantic::TypeId<'db>,
393) -> Maybe<Lowered<'db>> {
394 let loop_expr_id = loop_ctx.loop_expr_id;
395 let mut ctx =
396 LoweringContext::new(encapsulating_ctx, function_id, loop_signature, return_type)?;
397 let old_loop_ctx = ctx.current_loop_ctx.replace(loop_ctx);
398
399 let root_block_id = alloc_empty_block(&mut ctx);
401 let mut builder = BlockBuilder::root(root_block_id);
402
403 let snapped_params = ctx.usages.usages[&loop_expr_id].snap_usage.clone();
404 let parameters = ctx
405 .signature
406 .params
407 .clone()
408 .into_iter()
409 .map(|param| {
410 let location = ctx.get_location(param.stable_ptr().untyped());
411 let var = ctx.new_var(VarRequest { ty: param.ty(), location });
412 if snapped_params.contains_key::<MemberPath<'_>>(&(¶m).into()) {
413 ctx.snapped_semantics.insert((¶m).into(), var);
414 } else {
415 builder.introduce((¶m).into(), var);
416 }
417 var
418 })
419 .collect_vec();
420
421 let root_ok = (|| {
422 let (block_expr, stable_ptr) = match ctx.function_body.arenas.exprs[loop_expr_id].clone() {
423 semantic::Expr::Loop(semantic::ExprLoop { body, stable_ptr, .. }) => {
424 let semantic_block =
426 extract_matches!(&ctx.function_body.arenas.exprs[body], semantic::Expr::Block)
427 .clone();
428
429 let block_expr = (|| {
430 lower_expr_block(&mut ctx, &mut builder, &semantic_block)?;
431 recursively_call_loop_func(
432 &mut ctx,
433 &mut builder,
434 loop_expr_id,
435 stable_ptr.untyped(),
436 )
437 })();
438 (block_expr, stable_ptr)
439 }
440
441 semantic::Expr::While(while_expr) => {
442 let stable_ptr = while_expr.stable_ptr;
443 let location = ctx.get_location(stable_ptr.untyped());
444 let block_expr = (|| {
445 let ret_var =
446 lower_while_loop(&mut ctx, &mut builder, while_expr, loop_expr_id)?
447 .as_var_usage(&mut ctx, &mut builder)?;
448
449 lower_return(&mut ctx, &mut builder, ret_var, location, false)
450 })();
451 (block_expr, stable_ptr)
452 }
453
454 semantic::Expr::For(for_expr) => {
455 let stable_ptr: cairo_lang_syntax::node::ast::ExprPtr<'_> = for_expr.stable_ptr;
456 let block_expr: Result<LoweredExpr<'_>, LoweringFlowError<'_>> =
457 lower_for_loop(&mut ctx, &mut builder, for_expr, loop_expr_id);
458 (block_expr, stable_ptr)
459 }
460 _ => unreachable!("Loop expression must be either loop, while or for."),
461 };
462
463 let ctx_ref = &mut ctx;
464 let block_sealed = lowered_expr_to_block_scope_end(ctx_ref, builder, block_expr)?;
465 wrap_sealed_block_as_function(ctx_ref, 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.into(),
479 parameters,
480 })
481}
482
483fn wrap_sealed_block_as_function<'db>(
485 ctx: &mut LoweringContext<'db, '_>,
486 block_sealed: SealedBlockBuilder<'db>,
487 stable_ptr: SyntaxStablePtrId<'db>,
488) -> Maybe<()> {
489 let Some(SealedGotoCallsite { 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.long(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: *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<'db, 'mt>(
526 ctx: &mut LoweringContext<'db, 'mt>,
527 mut builder: BlockBuilder<'db>,
528 semantic_block: &semantic::ExprBlock<'db>,
529) -> Maybe<SealedBlockBuilder<'db>> {
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<'db, 'mt>(
536 ctx: &mut LoweringContext<'db, 'mt>,
537 builder: &mut BlockBuilder<'db>,
538 expr_block: &semantic::ExprBlock<'db>,
539) -> LoweringResult<'db, LoweredExpr<'db>> {
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<'db>(
574 ctx: &mut LoweringContext<'db, '_>,
575 mut builder: BlockBuilder<'db>,
576 expr: semantic::ExprId,
577) -> Maybe<SealedBlockBuilder<'db>> {
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<'db>(
585 ctx: &mut LoweringContext<'db, '_>,
586 mut builder: BlockBuilder<'db>,
587 lowered_expr: LoweringResult<'db, LoweredExpr<'db>>,
588) -> Maybe<SealedBlockBuilder<'db>> {
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) => handle_lowering_flow_error(ctx, builder, err).map(|_| None)?,
594 },
595 Err(err) => handle_lowering_flow_error(ctx, builder, err).map(|_| None)?,
596 })
597}
598
599pub fn lower_return<'db>(
603 ctx: &mut LoweringContext<'db, '_>,
604 builder: &mut BlockBuilder<'db>,
605 mut ret_var: VarUsage<'db>,
606 location: LocationId<'db>,
607 is_early_return: bool,
608) -> LoweringResult<'db, LoweredExpr<'db>> {
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<'db>(
625 ctx: &mut LoweringContext<'db, '_>,
626 mut builder: BlockBuilder<'db>,
627 location: LocationId<'db>,
628 is_early_return: bool,
629) -> LoweringResult<'db, SealedBlockBuilder<'db>> {
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<'db>(
638 ctx: &mut LoweringContext<'db, '_>,
639 builder: &mut BlockBuilder<'db>,
640 stmt: &semantic::Statement<'db>,
641) -> LoweringResult<'db, ()> {
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 if let Some(else_clause) = else_clause {
659 log::trace!("Lowering a let-else statement.");
660 lower_let_else::lower_let_else(
661 ctx,
662 builder,
663 *pattern,
664 *expr,
665 *else_clause,
666 stable_ptr,
667 )?;
668 } else {
669 log::trace!("Lowering a let statement.");
670 let lowered_expr = lower_expr(ctx, builder, *expr)?;
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<'db>(
716 ctx: &mut LoweringContext<'db, '_>,
717 builder: &mut BlockBuilder<'db>,
718 pattern_id: semantic::PatternId,
719 lowered_expr: LoweredExpr<'db>,
720) -> LoweringResult<'db, ()> {
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(|(pattern, member)| (member.id, *pattern)),
753 );
754 let wrapping_info = structure.wrapping_info;
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: wrapping_info.wrap(ctx.db, member.ty),
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<'db>(
815 ctx: &mut LoweringContext<'db, '_>,
816 builder: &mut BlockBuilder<'db>,
817 lowered_expr: LoweredExpr<'db>,
818 patterns: &[semantic::PatternId],
819 ty: semantic::TypeId<'db>,
820) -> LoweringResult<'db, ()> {
821 let outputs = match lowered_expr {
822 LoweredExpr::Tuple { exprs, .. } => exprs,
823 LoweredExpr::FixedSizeArray { exprs, .. } => exprs,
824 _ => {
825 let (long_type_id, wrapping_info) = unwrap_pattern_type(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 .long(ctx.db)
831 .to_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: wrapping_info.wrap(ctx.db, ty),
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<'db>(
878 ctx: &mut LoweringContext<'db, '_>,
879 builder: &mut BlockBuilder<'db>,
880 expr_id: semantic::ExprId,
881) -> LoweringResult<'db, VarUsage<'db>> {
882 lower_expr(ctx, builder, expr_id)?.as_var_usage(ctx, builder)
883}
884
885fn lower_expr<'db>(
887 ctx: &mut LoweringContext<'db, '_>,
888 builder: &mut BlockBuilder<'db>,
889 expr_id: semantic::ExprId,
890) -> LoweringResult<'db, LoweredExpr<'db>> {
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.db));
909 Ok(LoweredExpr::MemberPath(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<'db>(
927 ctx: &mut LoweringContext<'db, '_>,
928 expr: &semantic::ExprNumericLiteral<'db>,
929 builder: &mut BlockBuilder<'db>,
930) -> LoweringResult<'db, LoweredExpr<'db>> {
931 log::trace!("Lowering a literal: {:?}", expr.debug(&ctx.expr_formatter));
932 Ok(LoweredExpr::AtVariable(lower_expr_literal_to_var_usage(
933 ctx,
934 expr.stable_ptr.untyped(),
935 expr.ty,
936 &expr.value,
937 builder,
938 )))
939}
940
941fn lower_expr_literal_to_var_usage<'db>(
943 ctx: &mut LoweringContext<'db, '_>,
944 stable_ptr: SyntaxStablePtrId<'db>,
945 ty: semantic::TypeId<'db>,
946 value: &BigInt,
947 builder: &mut BlockBuilder<'db>,
948) -> VarUsage<'db> {
949 let value = if let Err(err) = validate_literal(ctx.db, ty, value) {
950 ConstValue::Missing(
951 ctx.diagnostics.report(stable_ptr, LoweringDiagnosticKind::LiteralError(err)),
952 )
953 .intern(ctx.db)
954 } else {
955 ConstValueId::from_int(ctx.db, ty, value)
956 };
957 let location = ctx.get_location(stable_ptr);
958 generators::Const { value, ty, location }.add(ctx, &mut builder.statements)
959}
960
961fn lower_expr_string_literal<'db>(
962 ctx: &mut LoweringContext<'db, '_>,
963 expr: &semantic::ExprStringLiteral<'db>,
964 builder: &mut BlockBuilder<'db>,
965) -> LoweringResult<'db, LoweredExpr<'db>> {
966 log::trace!("Lowering a string literal: {:?}", expr.debug(&ctx.expr_formatter));
967 let db = ctx.db;
968
969 let bytes31_ty = get_core_ty_by_name(db, SmolStrId::from(db, "bytes31"), vec![]);
971 let data_array_ty = get_core_ty_by_name(
972 db,
973 SmolStrId::from(db, "Array"),
974 vec![GenericArgumentId::Type(bytes31_ty)],
975 );
976 let byte_array_ty = get_core_ty_by_name(db, SmolStrId::from(db, "ByteArray"), vec![]);
977
978 let array_submodule = core_submodule(db, SmolStrId::from(db, "array"));
979 let data_array_new_function = FunctionLongId::Semantic(get_function_id(
980 db,
981 array_submodule,
982 SmolStrId::from(db, "array_new"),
983 vec![GenericArgumentId::Type(bytes31_ty)],
984 ))
985 .intern(db);
986 let data_array_append_function = FunctionLongId::Semantic(get_function_id(
987 db,
988 array_submodule,
989 SmolStrId::from(db, "array_append"),
990 vec![GenericArgumentId::Type(bytes31_ty)],
991 ))
992 .intern(db);
993
994 let mut data_array_usage =
996 build_empty_data_array(ctx, builder, expr, data_array_new_function, data_array_ty);
997 let remainder = add_chunks_to_data_array(
998 ctx,
999 builder,
1000 expr,
1001 bytes31_ty,
1002 &mut data_array_usage,
1003 data_array_append_function,
1004 data_array_ty,
1005 );
1006 let (pending_word_usage, pending_word_len_usage) =
1007 add_pending_word(ctx, builder, expr, remainder);
1008
1009 let byte_array_usage = generators::StructConstruct {
1011 inputs: vec![data_array_usage, pending_word_usage, pending_word_len_usage],
1012 ty: byte_array_ty,
1013 location: ctx.get_location(expr.stable_ptr.untyped()),
1014 }
1015 .add(ctx, &mut builder.statements);
1016
1017 Ok(LoweredExpr::AtVariable(byte_array_usage))
1018}
1019
1020fn build_empty_data_array<'db>(
1022 ctx: &mut LoweringContext<'db, '_>,
1023 builder: &mut BlockBuilder<'db>,
1024 expr: &semantic::ExprStringLiteral<'db>,
1025 data_array_new_function: crate::ids::FunctionId<'db>,
1026 data_array_ty: semantic::TypeId<'db>,
1027) -> VarUsage<'db> {
1028 generators::Call {
1029 function: data_array_new_function,
1030 inputs: vec![],
1031 coupon_input: None,
1032 extra_ret_tys: vec![],
1033 ret_tys: vec![data_array_ty],
1034 location: ctx.get_location(expr.stable_ptr.untyped()),
1035 }
1036 .add(ctx, &mut builder.statements)
1037 .returns[0]
1038}
1039
1040fn add_chunks_to_data_array<'db, 'r>(
1042 ctx: &mut LoweringContext<'db, '_>,
1043 builder: &mut BlockBuilder<'db>,
1044 expr: &'r semantic::ExprStringLiteral<'db>,
1045 bytes31_ty: semantic::TypeId<'db>,
1046 data_array_usage: &mut VarUsage<'db>,
1047 data_array_append_function: crate::ids::FunctionId<'db>,
1048 data_array_ty: semantic::TypeId<'db>,
1049) -> &'r [u8] {
1050 let expr_stable_ptr = expr.stable_ptr.untyped();
1051
1052 let chunks = expr.value.as_bytes().chunks_exact(31);
1053 let remainder = chunks.remainder();
1054 for chunk in chunks {
1055 let chunk_usage = generators::Const {
1056 value: ConstValue::Int(BigInt::from_bytes_be(Sign::Plus, chunk), bytes31_ty)
1057 .intern(ctx.db),
1058 ty: bytes31_ty,
1059 location: ctx.get_location(expr_stable_ptr),
1060 }
1061 .add(ctx, &mut builder.statements);
1062
1063 *data_array_usage = generators::Call {
1064 function: data_array_append_function,
1065 inputs: vec![*data_array_usage, chunk_usage],
1066 coupon_input: None,
1067 extra_ret_tys: vec![data_array_ty],
1068 ret_tys: vec![],
1069 location: ctx.get_location(expr_stable_ptr),
1070 }
1071 .add(ctx, &mut builder.statements)
1072 .extra_outputs[0];
1073 }
1074 remainder
1075}
1076
1077fn add_pending_word<'db>(
1080 ctx: &mut LoweringContext<'db, '_>,
1081 builder: &mut BlockBuilder<'db>,
1082 expr: &semantic::ExprStringLiteral<'db>,
1083 pending_word_bytes: &[u8],
1084) -> (VarUsage<'db>, VarUsage<'db>) {
1085 let expr_stable_ptr = expr.stable_ptr.untyped();
1086
1087 let pending_word_len_ty = bounded_int_ty(ctx.db, BigInt::ZERO, 30.into());
1088 let felt252_ty = ctx.db.core_info().felt252;
1089
1090 let pending_word_usage = generators::Const {
1091 value: ConstValue::Int(BigInt::from_bytes_be(Sign::Plus, pending_word_bytes), felt252_ty)
1092 .intern(ctx.db),
1093 ty: felt252_ty,
1094 location: ctx.get_location(expr_stable_ptr),
1095 }
1096 .add(ctx, &mut builder.statements);
1097
1098 let pending_word_len = expr.value.len() % 31;
1099 let pending_word_len_usage = generators::Const {
1100 value: ConstValue::Int(pending_word_len.into(), pending_word_len_ty).intern(ctx.db),
1101 ty: pending_word_len_ty,
1102 location: ctx.get_location(expr_stable_ptr),
1103 }
1104 .add(ctx, &mut builder.statements);
1105 (pending_word_usage, pending_word_len_usage)
1106}
1107
1108fn lower_expr_constant<'db>(
1109 ctx: &mut LoweringContext<'db, '_>,
1110 expr: &semantic::ExprConstant<'db>,
1111 builder: &mut BlockBuilder<'db>,
1112) -> LoweringResult<'db, LoweredExpr<'db>> {
1113 log::trace!("Lowering a constant: {:?}", expr.debug(&ctx.expr_formatter));
1114 let location = ctx.get_location(expr.stable_ptr.untyped());
1115 Ok(LoweredExpr::AtVariable(
1116 generators::Const { value: expr.const_value_id, ty: expr.ty, location }
1117 .add(ctx, &mut builder.statements),
1118 ))
1119}
1120
1121fn lower_expr_tuple<'db>(
1123 ctx: &mut LoweringContext<'db, '_>,
1124 expr: &semantic::ExprTuple<'db>,
1125 builder: &mut BlockBuilder<'db>,
1126) -> LoweringResult<'db, LoweredExpr<'db>> {
1127 log::trace!("Lowering a tuple: {:?}", expr.debug(&ctx.expr_formatter));
1128 let location = ctx.get_location(expr.stable_ptr.untyped());
1129 let inputs = expr
1130 .items
1131 .iter()
1132 .map(|arg_expr_id| lower_expr(ctx, builder, *arg_expr_id))
1133 .collect::<Result<Vec<_>, _>>()?;
1134 Ok(LoweredExpr::Tuple { exprs: inputs, location })
1135}
1136
1137fn lower_expr_fixed_size_array<'db>(
1139 ctx: &mut LoweringContext<'db, '_>,
1140 expr: &semantic::ExprFixedSizeArray<'db>,
1141 builder: &mut BlockBuilder<'db>,
1142) -> LoweringResult<'db, LoweredExpr<'db>> {
1143 log::trace!("Lowering a fixed size array: {:?}", expr.debug(&ctx.expr_formatter));
1144 let location = ctx.get_location(expr.stable_ptr.untyped());
1145 let exprs = match &expr.items {
1146 semantic::FixedSizeArrayItems::Items(items) => items
1147 .iter()
1148 .map(|arg_expr_id| lower_expr(ctx, builder, *arg_expr_id))
1149 .collect::<Result<Vec<_>, _>>()?,
1150 semantic::FixedSizeArrayItems::ValueAndSize(value, size) => {
1151 let lowered_value = lower_expr(ctx, builder, *value)?;
1152 let var_usage = lowered_value.as_var_usage(ctx, builder)?;
1153 let size = size
1154 .long(ctx.db)
1155 .to_int()
1156 .expect("Expected ConstValue::Int for size")
1157 .to_usize()
1158 .unwrap();
1159 if size == 0 {
1160 return Err(LoweringFlowError::Failed(
1161 ctx.diagnostics
1162 .report(expr.stable_ptr.untyped(), EmptyRepeatedElementFixedSizeArray),
1163 ));
1164 }
1165 if size > 1 && ctx.variables[var_usage.var_id].info.copyable.is_err() {
1168 {
1169 return Err(LoweringFlowError::Failed(
1170 ctx.diagnostics.report(expr.stable_ptr.0, FixedSizeArrayNonCopyableType),
1171 ));
1172 }
1173 }
1174 let expr = LoweredExpr::AtVariable(var_usage);
1175 vec![expr; size]
1176 }
1177 };
1178 Ok(LoweredExpr::FixedSizeArray { exprs, location, ty: expr.ty })
1179}
1180
1181fn lower_expr_snapshot<'db>(
1183 ctx: &mut LoweringContext<'db, '_>,
1184 expr: &semantic::ExprSnapshot<'db>,
1185 builder: &mut BlockBuilder<'db>,
1186) -> LoweringResult<'db, LoweredExpr<'db>> {
1187 log::trace!("Lowering a snapshot: {:?}", expr.debug(&ctx.expr_formatter));
1188 let mut snap_count = 1;
1190 let mut inner = expr.inner;
1191 while let semantic::Expr::Snapshot(next) = &ctx.function_body.arenas.exprs[inner] {
1192 snap_count += 1;
1193 inner = next.inner;
1194 }
1195 while let semantic::Expr::Desnap(next) = &ctx.function_body.arenas.exprs[inner]
1197 && snap_count > 0
1198 {
1199 snap_count -= 1;
1200 inner = next.inner;
1201 }
1202 let mut result = lower_expr(ctx, builder, inner)?;
1204 let mut expr = expr;
1205 for _ in 0..snap_count {
1207 let location = ctx.get_location(expr.stable_ptr.untyped());
1208 result = LoweredExpr::Snapshot { expr: result.into(), location };
1209 if let semantic::Expr::Snapshot(next) = &ctx.function_body.arenas.exprs[expr.inner] {
1210 expr = next;
1211 } else {
1212 break;
1215 };
1216 }
1217 Ok(result)
1218}
1219
1220fn lower_expr_desnap<'db>(
1222 ctx: &mut LoweringContext<'db, '_>,
1223 expr: &semantic::ExprDesnap<'db>,
1224 builder: &mut BlockBuilder<'db>,
1225) -> LoweringResult<'db, LoweredExpr<'db>> {
1226 log::trace!("Lowering a desnap: {:?}", expr.debug(&ctx.expr_formatter));
1227 let location = ctx.get_location(expr.stable_ptr.untyped());
1228 let expr = lower_expr(ctx, builder, expr.inner)?;
1229 if let LoweredExpr::Snapshot { expr, .. } = &expr {
1230 return Ok(expr.as_ref().clone());
1231 }
1232 let input = expr.as_var_usage(ctx, builder)?;
1233
1234 Ok(LoweredExpr::AtVariable(
1235 generators::Desnap { input, location }.add(ctx, &mut builder.statements),
1236 ))
1237}
1238
1239fn lower_expr_if<'db>(
1241 ctx: &mut LoweringContext<'db, '_>,
1242 builder: &mut BlockBuilder<'db>,
1243 expr: &semantic::ExprIf<'db>,
1244) -> LoweringResult<'db, LoweredExpr<'db>> {
1245 let graph = create_graph_expr_if(ctx, expr);
1246 lower_graph(ctx, builder, &graph, ctx.get_location(expr.stable_ptr.untyped()))
1247}
1248
1249fn lower_expr_match<'db>(
1251 ctx: &mut LoweringContext<'db, '_>,
1252 expr: &semantic::ExprMatch<'db>,
1253 builder: &mut BlockBuilder<'db>,
1254) -> LoweringResult<'db, LoweredExpr<'db>> {
1255 log::trace!("Lowering a match expression: {:?}", expr.debug(&ctx.expr_formatter));
1256 let graph = create_graph_expr_match(ctx, expr);
1257 lower_graph(ctx, builder, &graph, ctx.get_location(expr.stable_ptr.untyped()))
1258}
1259
1260fn lower_expr_function_call<'db>(
1262 ctx: &mut LoweringContext<'db, '_>,
1263 expr: &semantic::ExprFunctionCall<'db>,
1264 builder: &mut BlockBuilder<'db>,
1265) -> LoweringResult<'db, LoweredExpr<'db>> {
1266 log::trace!("Lowering a function call expression: {:?}", expr.debug(&ctx.expr_formatter));
1267 let location = ctx.get_location(expr.stable_ptr.untyped());
1268
1269 let arg_inputs = lower_exprs_to_var_usages(ctx, &expr.args, builder)?;
1271 let ref_args = expr
1272 .args
1273 .iter()
1274 .filter_map(|arg| match arg {
1275 ExprFunctionCallArg::Value(_) => None,
1276 ExprFunctionCallArg::Reference(ref_arg) => Some(RefArg::Ref(ref_arg.clone())),
1277 ExprFunctionCallArg::TempReference(expr_id) => {
1278 Some(RefArg::Temp(ctx.function_body.arenas.exprs[*expr_id].ty()))
1279 }
1280 })
1281 .collect_vec();
1282 let extra_ret_tys = ref_args.iter().map(|ref_arg| ref_arg.ty()).collect();
1283
1284 let coupon_input = if let Some(coupon_arg) = expr.coupon_arg {
1285 Some(lower_expr_to_var_usage(ctx, builder, coupon_arg)?)
1286 } else {
1287 None
1288 };
1289
1290 if expr.function == get_core_function_id(ctx.db, SmolStrId::from(ctx.db, "panic"), vec![]) {
1292 let [input] = <[_; 1]>::try_from(arg_inputs).ok().unwrap();
1293 return Err(LoweringFlowError::Panic(input, location));
1294 }
1295
1296 if expr.function.try_get_extern_function_id(ctx.db).is_some()
1298 && let semantic::TypeLongId::Concrete(semantic::ConcreteTypeId::Enum(concrete_enum_id)) =
1299 expr.ty.long(ctx.db)
1300 {
1301 let lowered_expr = LoweredExprExternEnum {
1302 function: expr.function,
1303 concrete_enum_id: *concrete_enum_id,
1304 inputs: arg_inputs,
1305 ref_args,
1306 location,
1307 };
1308
1309 return Ok(LoweredExpr::ExternEnum(lowered_expr));
1315 }
1316
1317 let (ref_outputs, res) = perform_function_call(
1318 ctx,
1319 builder,
1320 FunctionCallInfo {
1321 function: expr.function,
1322 inputs: arg_inputs,
1323 coupon_input,
1324 extra_ret_tys,
1325 ret_ty: expr.ty,
1326 },
1327 location,
1328 )?;
1329
1330 for (ref_arg, output_var) in zip_eq(ref_args, ref_outputs) {
1332 if let RefArg::Ref(ref_arg) = ref_arg {
1333 builder.update_ref(ctx, &ref_arg, output_var.var_id);
1334 }
1335 }
1336
1337 Ok(res)
1338}
1339
1340struct FunctionCallInfo<'db> {
1342 function: semantic::FunctionId<'db>,
1343 inputs: Vec<VarUsage<'db>>,
1344 coupon_input: Option<VarUsage<'db>>,
1345 extra_ret_tys: Vec<semantic::TypeId<'db>>,
1346 ret_ty: semantic::TypeId<'db>,
1347}
1348
1349fn perform_function_call<'db>(
1353 ctx: &mut LoweringContext<'db, '_>,
1354 builder: &mut BlockBuilder<'db>,
1355 function_call_info: FunctionCallInfo<'db>,
1356 location: LocationId<'db>,
1357) -> LoweringResult<'db, (Vec<VarUsage<'db>>, LoweredExpr<'db>)> {
1358 let FunctionCallInfo { function, inputs, coupon_input, extra_ret_tys, ret_ty } =
1359 function_call_info;
1360
1361 let Some(extern_function_id) = function.try_get_extern_function_id(ctx.db) else {
1363 let call_result = generators::Call {
1364 function: function.lowered(ctx.db),
1365 inputs,
1366 coupon_input,
1367 extra_ret_tys,
1368 ret_tys: vec![ret_ty],
1369 location,
1370 }
1371 .add(ctx, &mut builder.statements);
1372
1373 if ret_ty == never_ty(ctx.db) {
1374 return Err(LoweringFlowError::Match(MatchInfo::Enum(MatchEnumInfo {
1385 concrete_enum_id: *extract_matches!(
1386 extract_matches!(ret_ty.long(ctx.db), semantic::TypeLongId::Concrete),
1387 semantic::ConcreteTypeId::Enum
1388 ),
1389 input: VarUsage { var_id: call_result.returns[0].var_id, location },
1390 arms: vec![],
1391 location,
1392 })));
1393 }
1394
1395 let res = LoweredExpr::AtVariable(call_result.returns.into_iter().next().unwrap());
1396 return Ok((call_result.extra_outputs, res));
1397 };
1398
1399 assert!(coupon_input.is_none(), "Extern functions cannot have a __coupon__ argument.");
1401
1402 let info = ctx.db.core_info();
1405 if extern_function_id == info.into_box {
1406 assert!(extra_ret_tys.is_empty(), "into_box should not have extra return types");
1407 let input = inputs.into_iter().exactly_one().expect("into_box expects exactly one input");
1408 let res = generators::IntoBox { input, location }.add(ctx, &mut builder.statements);
1409 return Ok((vec![], LoweredExpr::AtVariable(res)));
1410 }
1411 if extern_function_id == info.unbox {
1412 assert!(extra_ret_tys.is_empty(), "unbox should not have extra return types");
1413 let input = inputs.into_iter().exactly_one().expect("unbox expects exactly one input");
1414 let res = generators::Unbox { input, location }.add(ctx, &mut builder.statements);
1415 return Ok((vec![], LoweredExpr::AtVariable(res)));
1416 }
1417
1418 let ret_tys = extern_facade_return_tys(ctx.db, &ret_ty).to_vec();
1419 let call_result = generators::Call {
1420 function: function.lowered(ctx.db),
1421 inputs,
1422 coupon_input: None,
1423 extra_ret_tys,
1424 ret_tys,
1425 location,
1426 }
1427 .add(ctx, &mut builder.statements);
1428
1429 Ok((
1430 call_result.extra_outputs,
1431 extern_facade_expr(
1432 ctx,
1433 ret_ty,
1434 call_result.returns.into_iter().map(|var_usage| var_usage.var_id),
1435 location,
1436 ),
1437 ))
1438}
1439
1440fn lower_expr_loop<'db>(
1442 ctx: &mut LoweringContext<'db, '_>,
1443 builder: &mut BlockBuilder<'db>,
1444 loop_expr_id: ExprId,
1445) -> LoweringResult<'db, LoweredExpr<'db>> {
1446 let db = ctx.db;
1447 let (stable_ptr, return_type) = match ctx.function_body.arenas.exprs[loop_expr_id].clone() {
1448 semantic::Expr::Loop(semantic::ExprLoop { stable_ptr, ty, .. }) => (stable_ptr, ty),
1449 semantic::Expr::While(semantic::ExprWhile { stable_ptr, ty, .. }) => (stable_ptr, ty),
1450 semantic::Expr::For(semantic::ExprFor {
1451 stable_ptr,
1452 ty,
1453 into_iter,
1454 expr_id,
1455 into_iter_member_path,
1456 ..
1457 }) => {
1458 let var_id = lower_expr(ctx, builder, expr_id)?.as_var_usage(ctx, builder)?;
1459 let into_iter_call = generators::Call {
1460 function: into_iter.lowered(db),
1461 inputs: vec![var_id],
1462 coupon_input: None,
1463 extra_ret_tys: vec![],
1464 ret_tys: vec![db.concrete_function_signature(into_iter).unwrap().return_type],
1465 location: ctx.get_location(stable_ptr.untyped()),
1466 }
1467 .add(ctx, &mut builder.statements);
1468 let into_iter_var = into_iter_call.returns.into_iter().next().unwrap();
1469 let sem_var = LocalVariable {
1470 ty: db.concrete_function_signature(into_iter).unwrap().return_type,
1471 is_mut: true,
1472 id: extract_matches!(into_iter_member_path.base_var(), VarId::Local),
1473 allow_unused: true, };
1475 builder.put_semantic(into_iter_member_path.base_var(), into_iter_var.var_id);
1476
1477 ctx.semantic_defs
1478 .insert(into_iter_member_path.base_var(), semantic::Binding::LocalVar(sem_var));
1479
1480 (stable_ptr, ty)
1481 }
1482 _ => unreachable!("Loop expression must be either loop, while or for."),
1483 };
1484
1485 let usage = &ctx.usages.usages[&loop_expr_id];
1486 let has_normal_return = return_type != never_ty(db);
1487
1488 let (loop_return_ty, early_return_info) = if !has_normal_return {
1489 (ctx.return_type, None)
1492 } else if !usage.has_early_return {
1493 (return_type, None)
1494 } else {
1495 let generic_args =
1496 vec![GenericArgumentId::Type(return_type), GenericArgumentId::Type(ctx.return_type)];
1497
1498 let internal_module = core_submodule(db, SmolStrId::from(db, "internal"));
1499 let ret_ty = try_get_ty_by_name(
1500 db,
1501 internal_module,
1502 SmolStrId::from(db, "LoopResult"),
1503 generic_args.clone(),
1504 )
1505 .unwrap();
1506 (
1507 ret_ty,
1508 Some(LoopEarlyReturnInfo {
1509 normal_return_variant: get_enum_concrete_variant(
1510 db,
1511 internal_module,
1512 SmolStrId::from(db, "LoopResult"),
1513 generic_args.clone(),
1514 SmolStrId::from(db, "Normal"),
1515 ),
1516 early_return_variant: get_enum_concrete_variant(
1517 db,
1518 internal_module,
1519 SmolStrId::from(db, "LoopResult"),
1520 generic_args,
1521 SmolStrId::from(db, "EarlyReturn"),
1522 ),
1523 }),
1524 )
1525 };
1526
1527 let params = usage
1529 .usage
1530 .iter()
1531 .map(|(_, expr)| expr.clone())
1532 .chain(usage.snap_usage.iter().map(|(_, expr)| match expr {
1533 ExprVarMemberPath::Var(var) => {
1534 ExprVarMemberPath::Var(ExprVar { ty: wrap_in_snapshots(db, var.ty, 1), ..*var })
1535 }
1536 ExprVarMemberPath::Member { parent, member_id, stable_ptr, concrete_struct_id, ty } => {
1537 ExprVarMemberPath::Member {
1538 parent: parent.clone(),
1539 member_id: *member_id,
1540 stable_ptr: *stable_ptr,
1541 concrete_struct_id: *concrete_struct_id,
1542 ty: wrap_in_snapshots(db, *ty, 1),
1543 }
1544 }
1545 }))
1546 .collect_vec();
1547 let extra_rets = usage.changes.iter().map(|(_, expr)| expr.clone()).collect_vec();
1548
1549 let loop_location = ctx.get_location(stable_ptr.untyped());
1550 let loop_signature = EnrichedSemanticSignature {
1551 params,
1552 extra_rets,
1553 return_type: loop_return_ty,
1554 implicits: vec![],
1555 panicable: ctx.signature.panicable,
1556 location: loop_location,
1557 };
1558
1559 let function = FunctionWithBodyLongId::Generated {
1561 parent: ctx.semantic_function_id,
1562 key: GeneratedFunctionKey::Loop(stable_ptr),
1563 }
1564 .intern(db);
1565
1566 let encapsulating_ctx = ctx.encapsulating_ctx.take().unwrap();
1568 let loop_ctx = LoopContext { loop_expr_id, early_return_info: early_return_info.clone() };
1569 let lowered = lower_loop_function(
1570 encapsulating_ctx,
1571 function,
1572 loop_signature.clone(),
1573 loop_ctx,
1574 ctx.return_type,
1575 )
1576 .map_err(LoweringFlowError::Failed)?;
1577 encapsulating_ctx.lowerings.insert(GeneratedFunctionKey::Loop(stable_ptr), lowered);
1579 ctx.encapsulating_ctx = Some(encapsulating_ctx);
1580 let call_loop_expr = call_loop_func_ex(
1581 ctx,
1582 loop_signature,
1583 builder,
1584 loop_expr_id,
1585 stable_ptr.untyped(),
1586 |ctx, builder, param| {
1587 if let Some(var) = builder.get_snap_ref(ctx, param) {
1588 return Some(var);
1589 };
1590 let input = builder.get_ref(ctx, param)?;
1591 let location = ctx.get_location(param.stable_ptr().untyped());
1592 let (original, snapped) =
1593 generators::Snapshot { input, location }.add(ctx, &mut builder.statements);
1594 builder.update_ref(ctx, param, original);
1595 Some(VarUsage { var_id: snapped, location })
1596 },
1597 )?;
1598
1599 let Some(LoopEarlyReturnInfo { normal_return_variant, early_return_variant }) =
1600 early_return_info
1601 else {
1602 if !has_normal_return {
1603 let ret_var_usage = call_loop_expr.as_var_usage(ctx, builder)?;
1604 return Err(LoweringFlowError::Return(ret_var_usage, loop_location));
1605 }
1606
1607 return Ok(call_loop_expr);
1608 };
1609
1610 let loop_res = call_loop_expr.as_var_usage(ctx, builder)?;
1611
1612 let normal_return_subscope = create_subscope(ctx, builder);
1613 let normal_return_subscope_block_id = normal_return_subscope.block_id;
1614 let normal_return_var_id = ctx.new_var(VarRequest { ty: return_type, location: loop_location });
1615 let sealed_normal_return = lowered_expr_to_block_scope_end(
1616 ctx,
1617 normal_return_subscope,
1618 Ok(LoweredExpr::AtVariable(VarUsage {
1619 var_id: normal_return_var_id,
1620 location: loop_location,
1621 })),
1622 )
1623 .map_err(LoweringFlowError::Failed)?;
1624
1625 let mut early_return_subscope = create_subscope(ctx, builder);
1626 let early_return_var_id =
1627 ctx.new_var(VarRequest { ty: ctx.signature.return_type, location: loop_location });
1628 let early_return_subscope_block_id = early_return_subscope.block_id;
1629 let ret_expr = lower_return(
1630 ctx,
1631 &mut early_return_subscope,
1632 VarUsage { var_id: early_return_var_id, location: loop_location },
1633 loop_location,
1634 true,
1635 );
1636 let sealed_early_return = lowered_expr_to_block_scope_end(ctx, early_return_subscope, ret_expr)
1637 .map_err(LoweringFlowError::Failed)?;
1638
1639 let match_info = MatchInfo::Enum(MatchEnumInfo {
1640 concrete_enum_id: normal_return_variant.concrete_enum_id,
1641 input: loop_res,
1642 arms: vec![
1643 MatchArm {
1644 arm_selector: MatchArmSelector::VariantId(normal_return_variant),
1645 block_id: normal_return_subscope_block_id,
1646 var_ids: vec![normal_return_var_id],
1647 },
1648 MatchArm {
1649 arm_selector: MatchArmSelector::VariantId(early_return_variant),
1650 block_id: early_return_subscope_block_id,
1651 var_ids: vec![early_return_var_id],
1652 },
1653 ],
1654 location: loop_location,
1655 });
1656
1657 builder.merge_and_end_with_match(
1658 ctx,
1659 match_info,
1660 vec![sealed_normal_return, sealed_early_return],
1661 loop_location,
1662 )
1663}
1664
1665fn recursively_call_loop_func<'db>(
1667 ctx: &mut LoweringContext<'db, '_>,
1668 builder: &mut BlockBuilder<'db>,
1669 loop_expr_id: ExprId,
1670 stable_ptr: SyntaxStablePtrId<'db>,
1671) -> LoweringResult<'db, LoweredExpr<'db>> {
1672 let loop_res = call_loop_func_ex(
1673 ctx,
1674 ctx.signature.clone(),
1675 builder,
1676 loop_expr_id,
1677 stable_ptr,
1678 |ctx, builder, param| builder.get_snap_ref(ctx, param),
1679 )?
1680 .as_var_usage(ctx, builder)?;
1681 Err(LoweringFlowError::Return(loop_res, ctx.get_location(stable_ptr)))
1682}
1683
1684fn call_loop_func_ex<'db>(
1686 ctx: &mut LoweringContext<'db, '_>,
1687 loop_signature: EnrichedSemanticSignature<'db>,
1688 builder: &mut BlockBuilder<'db>,
1689 loop_expr_id: ExprId,
1690 stable_ptr: SyntaxStablePtrId<'db>,
1691 handle_snap: impl Fn(
1692 &mut LoweringContext<'db, '_>,
1693 &mut BlockBuilder<'db>,
1694 &ExprVarMemberPath<'db>,
1695 ) -> Option<VarUsage<'db>>,
1696) -> LoweringResult<'db, LoweredExpr<'db>> {
1697 let location = ctx.get_location(stable_ptr);
1698 let loop_stable_ptr = ctx.function_body.arenas.exprs[loop_expr_id].stable_ptr();
1699 let function = FunctionLongId::Generated(GeneratedFunction {
1701 parent: ctx.concrete_function_id.base_semantic_function(ctx.db),
1702 key: GeneratedFunctionKey::Loop(loop_stable_ptr),
1703 })
1704 .intern(ctx.db);
1705 let inputs = loop_signature
1706 .params
1707 .into_iter()
1708 .map(|param| {
1709 if let Some(var) = builder.get_ref_of_type(ctx, ¶m, param.ty()) {
1710 return Ok(var);
1711 }
1712
1713 if let Some(var) = handle_snap(ctx, builder, ¶m)
1714 && ctx.variables[var.var_id].ty == param.ty()
1715 {
1716 return Ok(var);
1717 }
1718
1719 Err(LoweringFlowError::Failed(
1722 ctx.diagnostics.report(param.stable_ptr(), MemberPathLoop),
1723 ))
1724 })
1725 .collect::<LoweringResult<'db, Vec<_>>>()?;
1726 let extra_ret_tys = loop_signature.extra_rets.iter().map(|path| path.ty()).collect_vec();
1727 let call_result = generators::Call {
1728 function,
1729 inputs,
1730 coupon_input: None,
1731 extra_ret_tys,
1732 ret_tys: vec![loop_signature.return_type],
1733 location,
1734 }
1735 .add(ctx, &mut builder.statements);
1736
1737 for (ref_arg, output_var) in zip_eq(&loop_signature.extra_rets, call_result.extra_outputs) {
1739 builder.update_ref(ctx, ref_arg, output_var.var_id);
1740 }
1741
1742 Ok(LoweredExpr::AtVariable(call_result.returns.into_iter().next().unwrap()))
1743}
1744
1745fn lower_exprs_to_var_usages<'db>(
1748 ctx: &mut LoweringContext<'db, '_>,
1749 args: &[semantic::ExprFunctionCallArg<'db>],
1750 builder: &mut BlockBuilder<'db>,
1751) -> LoweringResult<'db, Vec<VarUsage<'db>>> {
1752 let mut value_iter = args
1758 .iter()
1759 .filter_map(|arg| match arg {
1760 ExprFunctionCallArg::Value(expr_id) | ExprFunctionCallArg::TempReference(expr_id) => {
1761 Some(lower_expr_to_var_usage(ctx, builder, *expr_id))
1762 }
1763 ExprFunctionCallArg::Reference(_) => None,
1764 })
1765 .collect::<Result<Vec<_>, _>>()?
1766 .into_iter();
1767 Ok(args
1768 .iter()
1769 .map(|arg| match arg {
1770 semantic::ExprFunctionCallArg::Reference(arg) => builder.get_ref(ctx, arg).unwrap(),
1771 ExprFunctionCallArg::Value(_) | ExprFunctionCallArg::TempReference(_) => {
1772 value_iter.next().unwrap()
1773 }
1774 })
1775 .collect())
1776}
1777
1778fn lower_expr_enum_ctor<'db>(
1780 ctx: &mut LoweringContext<'db, '_>,
1781 expr: &semantic::ExprEnumVariantCtor<'db>,
1782 builder: &mut BlockBuilder<'db>,
1783) -> LoweringResult<'db, LoweredExpr<'db>> {
1784 log::trace!(
1785 "Started lowering of an enum c'tor expression: {:?}",
1786 expr.debug(&ctx.expr_formatter)
1787 );
1788 let location = ctx.get_location(expr.stable_ptr.untyped());
1789 Ok(LoweredExpr::AtVariable(
1790 generators::EnumConstruct {
1791 input: lower_expr_to_var_usage(ctx, builder, expr.value_expr)?,
1792 variant: expr.variant,
1793 location,
1794 }
1795 .add(ctx, &mut builder.statements),
1796 ))
1797}
1798
1799fn lower_expr_member_access<'db>(
1801 ctx: &mut LoweringContext<'db, '_>,
1802 expr: &semantic::ExprMemberAccess<'db>,
1803 builder: &mut BlockBuilder<'db>,
1804) -> LoweringResult<'db, LoweredExpr<'db>> {
1805 log::trace!("Lowering a member-access expression: {:?}", expr.debug(&ctx.expr_formatter));
1806 if let Some(member_path) = &expr.member_path {
1807 return Ok(LoweredExpr::MemberPath(
1808 member_path.clone(),
1809 ctx.get_location(expr.stable_ptr.untyped()),
1810 ));
1811 }
1812 let location = ctx.get_location(expr.stable_ptr.untyped());
1813 let members = ctx
1814 .db
1815 .concrete_struct_members(expr.concrete_struct_id)
1816 .map_err(LoweringFlowError::Failed)?;
1817 let member_idx =
1818 members.iter().position(|(_, member)| member.id == expr.member).ok_or_else(|| {
1819 LoweringFlowError::Failed(
1820 ctx.diagnostics.report(expr.stable_ptr.untyped(), UnexpectedError),
1821 )
1822 })?;
1823 Ok(LoweredExpr::AtVariable(
1824 generators::StructMemberAccess {
1825 input: lower_expr_to_var_usage(ctx, builder, expr.expr)?,
1826 member_tys: members
1827 .iter()
1828 .map(|(_, member)| wrap_in_snapshots(ctx.db, member.ty, expr.n_snapshots))
1829 .collect(),
1830 member_idx,
1831 location,
1832 }
1833 .add(ctx, &mut builder.statements),
1834 ))
1835}
1836
1837fn lower_expr_struct_ctor<'db>(
1839 ctx: &mut LoweringContext<'db, '_>,
1840 expr: &semantic::ExprStructCtor<'db>,
1841 builder: &mut BlockBuilder<'db>,
1842) -> LoweringResult<'db, LoweredExpr<'db>> {
1843 log::trace!("Lowering a struct c'tor expression: {:?}", expr.debug(&ctx.expr_formatter));
1844 let location = ctx.get_location(expr.stable_ptr.untyped());
1845 let members = ctx
1846 .db
1847 .concrete_struct_members(expr.concrete_struct_id)
1848 .map_err(LoweringFlowError::Failed)?;
1849 let mut member_expr_usages =
1850 UnorderedHashMap::<_, _>::from_iter(expr.members.iter().map(|(expr, id)| {
1851 let usage = lower_expr_to_var_usage(ctx, builder, *expr);
1852 (*id, usage)
1853 }));
1854 if members.len() != member_expr_usages.len() {
1855 let base_struct = lower_expr(ctx, builder, expr.base_struct.unwrap())?;
1857 if let LoweredExpr::MemberPath(path, location) = base_struct {
1858 for (_, member) in members.iter() {
1859 let Entry::Vacant(entry) = member_expr_usages.entry(member.id) else {
1860 continue;
1861 };
1862 let member_path = ExprVarMemberPath::Member {
1863 parent: Box::new(path.clone()),
1864 member_id: member.id,
1865 stable_ptr: path.stable_ptr(),
1866 concrete_struct_id: expr.concrete_struct_id,
1867 ty: member.ty,
1868 };
1869 entry
1870 .insert(Ok(LoweredExpr::MemberPath(member_path, location)
1871 .as_var_usage(ctx, builder)?));
1872 }
1873 } else {
1874 for (base_member, (_, member)) in izip!(
1875 StructDestructure {
1876 input: base_struct.as_var_usage(ctx, builder)?,
1877 var_reqs: members
1878 .iter()
1879 .map(|(_, member)| VarRequest { ty: member.ty, location })
1880 .collect(),
1881 }
1882 .add(ctx, &mut builder.statements),
1883 members.iter()
1884 ) {
1885 match member_expr_usages.entry(member.id) {
1886 Entry::Occupied(_) => {}
1887 Entry::Vacant(entry) => {
1888 entry.insert(Ok(VarUsage { var_id: base_member, location }));
1889 }
1890 }
1891 }
1892 }
1893 }
1894 Ok(LoweredExpr::AtVariable(
1895 generators::StructConstruct {
1896 inputs: members
1897 .iter()
1898 .map(|(_, member)| member_expr_usages.remove(&member.id).unwrap())
1899 .collect::<Result<Vec<_>, _>>()?,
1900 ty: expr.ty,
1901 location,
1902 }
1903 .add(ctx, &mut builder.statements),
1904 ))
1905}
1906
1907fn add_capture_destruct_impl<'db>(
1909 ctx: &mut LoweringContext<'db, '_>,
1910 capture_var_usage: VarUsage<'db>,
1911 closure_info: &ClosureInfo<'db>,
1912 location: StableLocation<'db>,
1913) -> Maybe<()> {
1914 let capture_ty_info = &ctx.variables.variables[capture_var_usage.var_id].info;
1915 let Some(Ok(impl_id)) = [&capture_ty_info.destruct_impl, &capture_ty_info.panic_destruct_impl]
1917 .iter()
1918 .find(|infer_result| infer_result.is_ok())
1919 else {
1920 return Ok(());
1921 };
1922
1923 let db = ctx.db;
1924 let concrete_trait = impl_id.concrete_trait(db)?;
1925
1926 let trait_functions = db.trait_functions(concrete_trait.trait_id(db))?;
1927
1928 assert_eq!(trait_functions.len(), 1);
1929 let trait_function = *trait_functions.values().next().unwrap();
1930
1931 let generic_function = GenericFunctionId::Impl(ImplGenericFunctionId {
1932 impl_id: *impl_id,
1933 function: trait_function,
1934 });
1935
1936 let function = semantic::FunctionLongId {
1937 function: ConcreteFunction { generic_function, generic_args: vec![] },
1938 }
1939 .intern(db);
1940
1941 let signature =
1942 EnrichedSemanticSignature::from_semantic(db, db.concrete_function_signature(function)?);
1943
1944 let func_key = GeneratedFunctionKey::TraitFunc(trait_function, location);
1945 let function_id =
1946 FunctionWithBodyLongId::Generated { parent: ctx.semantic_function_id, key: func_key }
1947 .intern(db);
1948
1949 let location_id = LocationId::from_stable_location(db, location);
1950
1951 let encapsulating_ctx = ctx.encapsulating_ctx.take().unwrap();
1952 let return_type = signature.return_type;
1953 let lowered_impl_res = get_destruct_lowering(
1954 LoweringContext::new(encapsulating_ctx, function_id, signature, return_type)?,
1955 location_id,
1956 closure_info,
1957 );
1958 ctx.encapsulating_ctx = Some(encapsulating_ctx);
1960 ctx.lowerings.insert(func_key, lowered_impl_res?);
1961 Ok(())
1962}
1963
1964fn get_destruct_lowering<'db>(
1966 mut ctx: LoweringContext<'db, '_>,
1967 location_id: LocationId<'db>,
1968 closure_info: &ClosureInfo<'db>,
1969) -> Maybe<Lowered<'db>> {
1970 let root_block_id = alloc_empty_block(&mut ctx);
1971 let mut builder = BlockBuilder::root(root_block_id);
1972
1973 let parameters = ctx
1974 .signature
1975 .params
1976 .clone()
1977 .into_iter()
1978 .map(|param| {
1979 let var = ctx.new_var(VarRequest { ty: param.ty(), location: location_id });
1980 builder.introduce((¶m).into(), var);
1981 var
1982 })
1983 .collect_vec();
1984
1985 builder.destructure_closure(&mut ctx, location_id, parameters[0], closure_info);
1986 let var_usage =
1987 generators::StructConstruct { inputs: vec![], ty: unit_ty(ctx.db), location: location_id }
1988 .add(&mut ctx, &mut builder.statements);
1989 builder.ret(&mut ctx, var_usage, location_id)?;
1990 let lowered_impl = Lowered {
1991 diagnostics: ctx.diagnostics.build(),
1992 variables: ctx.variables.variables,
1993 blocks: ctx.blocks.build().unwrap(),
1994 signature: ctx.signature.into(),
1995 parameters,
1996 };
1997 Ok(lowered_impl)
1998}
1999
2000fn add_closure_call_function<'db>(
2002 encapsulated_ctx: &mut LoweringContext<'db, '_>,
2003 expr: &semantic::ExprClosure<'db>,
2004 closure_info: &ClosureInfo<'db>,
2005 trait_id: cairo_lang_defs::ids::TraitId<'db>,
2006) -> Maybe<()> {
2007 let db = encapsulated_ctx.db;
2008 let closure_ty = extract_matches!(expr.ty.long(db), TypeLongId::Closure);
2009 let expr_location = encapsulated_ctx.get_location(expr.stable_ptr.untyped());
2010 let parameters_ty = TypeLongId::Tuple(closure_ty.param_tys.clone()).intern(db);
2011 let concrete_trait = ConcreteTraitLongId {
2012 trait_id,
2013 generic_args: vec![
2014 GenericArgumentId::Type(expr.ty),
2015 GenericArgumentId::Type(parameters_ty),
2016 ],
2017 }
2018 .intern(db);
2019 let Ok(impl_id) = semantic::types::get_impl_at_context(
2020 db,
2021 encapsulated_ctx.variables.lookup_context,
2022 concrete_trait,
2023 None,
2024 ) else {
2025 return Ok(());
2028 };
2029 if !matches!(impl_id.long(db), ImplLongId::GeneratedImpl(_)) {
2030 return Ok(());
2032 }
2033
2034 let trait_function: cairo_lang_defs::ids::TraitFunctionId<'_> = db
2035 .trait_function_by_name(trait_id, SmolStrId::from(db, "call"))
2036 .unwrap()
2037 .expect("Call function must exist for an Fn trait.");
2038
2039 let generic_function =
2040 GenericFunctionId::Impl(ImplGenericFunctionId { impl_id, function: trait_function });
2041 let function = semantic::FunctionLongId {
2042 function: ConcreteFunction { generic_function, generic_args: vec![] },
2043 }
2044 .intern(db);
2045 let function_with_body_id = FunctionWithBodyLongId::Generated {
2046 parent: encapsulated_ctx.semantic_function_id,
2047 key: GeneratedFunctionKey::TraitFunc(trait_function, closure_ty.params_location),
2048 }
2049 .intern(db);
2050 let signature =
2051 EnrichedSemanticSignature::from_semantic(db, db.concrete_function_signature(function)?);
2052
2053 let return_type = signature.return_type;
2054 let mut ctx =
2055 LoweringContext::new(encapsulated_ctx, function_with_body_id, signature, return_type)?;
2056
2057 let root_block_id = alloc_empty_block(&mut ctx);
2058 let mut builder = BlockBuilder::root(root_block_id);
2059
2060 let info = db.core_info();
2061 let (closure_param_var_id, closure_var) = if trait_id == info.fn_once_trt {
2062 let closure_param_var = ctx.new_var(VarRequest { ty: expr.ty, location: expr_location });
2064 let closure_var = VarUsage { var_id: closure_param_var, location: expr_location };
2065 (closure_param_var, closure_var)
2066 } else {
2067 let closure_param_var = ctx
2069 .new_var(VarRequest { ty: wrap_in_snapshots(db, expr.ty, 1), location: expr_location });
2070
2071 let closure_var = generators::Desnap {
2072 input: VarUsage { var_id: closure_param_var, location: expr_location },
2073 location: expr_location,
2074 }
2075 .add(&mut ctx, &mut builder.statements);
2076 (closure_param_var, closure_var)
2077 };
2078 let parameters: Vec<VariableId> = [
2079 closure_param_var_id,
2080 ctx.new_var(VarRequest { ty: parameters_ty, location: expr_location }),
2081 ]
2082 .into();
2083 let captured_vars = generators::StructDestructure {
2084 input: closure_var,
2085 var_reqs: chain!(closure_info.members.iter(), closure_info.snapshots.iter())
2086 .map(|(_, ty)| VarRequest { ty: *ty, location: expr_location })
2087 .collect_vec(),
2088 }
2089 .add(&mut ctx, &mut builder.statements);
2090 for (i, (param, _)) in closure_info.members.iter().enumerate() {
2091 builder.introduce(param.clone(), captured_vars[i]);
2092 }
2093 for (i, (param, _)) in closure_info.snapshots.iter().enumerate() {
2094 ctx.snapped_semantics.insert(param.clone(), captured_vars[i + closure_info.members.len()]);
2095 }
2096 let param_vars = generators::StructDestructure {
2097 input: VarUsage { var_id: parameters[1], location: expr_location },
2098 var_reqs: closure_ty
2099 .param_tys
2100 .iter()
2101 .map(|ty| VarRequest { ty: *ty, location: expr_location })
2102 .collect_vec(),
2103 }
2104 .add(&mut ctx, &mut builder.statements);
2105 for (param_var, param) in param_vars.into_iter().zip(expr.params.iter()) {
2106 builder.introduce((¶meter_as_member_path(param.clone())).into(), param_var);
2107 ctx.semantic_defs
2108 .insert(semantic::VarId::Param(param.id), semantic::Binding::Param(param.clone()));
2109 }
2110 let lowered_expr = lower_expr(&mut ctx, &mut builder, expr.body);
2111 let maybe_sealed_block = lowered_expr_to_block_scope_end(&mut ctx, builder, lowered_expr);
2112 let root_ok = maybe_sealed_block.and_then(|block_sealed| {
2113 wrap_sealed_block_as_function(&mut ctx, block_sealed, expr.stable_ptr.untyped())?;
2114 Ok(root_block_id)
2115 });
2116 let blocks = root_ok
2117 .map(|_| ctx.blocks.build().expect("Root block must exist."))
2118 .unwrap_or_else(Blocks::new_errored);
2119
2120 let lowered = Lowered {
2121 diagnostics: ctx.diagnostics.build(),
2122 variables: ctx.variables.variables,
2123 blocks,
2124 signature: ctx.signature.into(),
2125 parameters,
2126 };
2127 encapsulated_ctx.lowerings.insert(
2128 GeneratedFunctionKey::TraitFunc(trait_function, closure_ty.params_location),
2129 lowered,
2130 );
2131 Ok(())
2132}
2133
2134fn lower_expr_closure<'db>(
2136 ctx: &mut LoweringContext<'db, '_>,
2137 expr: &semantic::ExprClosure<'db>,
2138 expr_id: semantic::ExprId,
2139 builder: &mut BlockBuilder<'db>,
2140) -> LoweringResult<'db, LoweredExpr<'db>> {
2141 log::trace!("Lowering a closure expression: {:?}", expr.debug(&ctx.expr_formatter));
2142
2143 let usage = ctx.usages.usages[&expr_id].clone();
2144 let (capture_var_usage, closure_info) = builder.capture(ctx, usage, expr);
2145 let closure_variable = LoweredExpr::AtVariable(capture_var_usage);
2146 let closure_ty = extract_matches!(expr.ty.long(ctx.db), TypeLongId::Closure);
2147 let _ = add_capture_destruct_impl(
2148 ctx,
2149 capture_var_usage,
2150 &closure_info,
2151 closure_ty.params_location,
2152 );
2153 add_closure_call_function(
2154 ctx,
2155 expr,
2156 &closure_info,
2157 if ctx.variables[capture_var_usage.var_id].info.copyable.is_ok() {
2158 ctx.db.core_info().fn_trt
2159 } else {
2160 ctx.db.core_info().fn_once_trt
2161 },
2162 )
2163 .map_err(LoweringFlowError::Failed)?;
2164
2165 Ok(closure_variable)
2166}
2167
2168fn lower_expr_error_propagate<'db>(
2170 ctx: &mut LoweringContext<'db, '_>,
2171 expr: &semantic::ExprPropagateError<'db>,
2172 builder: &mut BlockBuilder<'db>,
2173) -> LoweringResult<'db, LoweredExpr<'db>> {
2174 log::trace!(
2175 "Started lowering of an error-propagate expression: {:?}",
2176 expr.debug(&ctx.expr_formatter)
2177 );
2178 let location = ctx.get_location(expr.stable_ptr.untyped());
2179 let lowered_expr = lower_expr(ctx, builder, expr.inner)?;
2180 let ExprPropagateError { ok_variant, err_variant, func_err_variant, .. } = expr;
2181 if let LoweredExpr::ExternEnum(extern_enum) = lowered_expr {
2182 return lower_optimized_extern_error_propagate(
2183 ctx,
2184 builder,
2185 extern_enum,
2186 ok_variant,
2187 err_variant,
2188 func_err_variant,
2189 location,
2190 );
2191 }
2192
2193 let match_input = lowered_expr.as_var_usage(ctx, builder)?;
2194 let subscope_ok = create_subscope(ctx, builder);
2196 let block_ok_id = subscope_ok.block_id;
2197 let expr_var = ctx.new_var(VarRequest { ty: ok_variant.ty, location });
2198 let sealed_block_ok = subscope_ok.goto_callsite(Some(VarUsage { var_id: expr_var, location }));
2199
2200 let mut subscope_err = create_subscope(ctx, builder);
2202 let block_err_id = subscope_err.block_id;
2203 let err_value = ctx.new_var(VarRequest { ty: err_variant.ty, location });
2204 let err_res = generators::EnumConstruct {
2205 input: VarUsage { var_id: err_value, location },
2206 variant: *func_err_variant,
2207 location,
2208 }
2209 .add(ctx, &mut subscope_err.statements);
2210 let ret_expr = lower_return(ctx, &mut subscope_err, err_res, location, true);
2211 let sealed_block_err = lowered_expr_to_block_scope_end(ctx, subscope_err, ret_expr)
2212 .map_err(LoweringFlowError::Failed)?;
2213
2214 let match_info = MatchInfo::Enum(MatchEnumInfo {
2216 concrete_enum_id: ok_variant.concrete_enum_id,
2217 input: match_input,
2218 arms: vec![
2219 MatchArm {
2220 arm_selector: MatchArmSelector::VariantId(*ok_variant),
2221 block_id: block_ok_id,
2222 var_ids: vec![expr_var],
2223 },
2224 MatchArm {
2225 arm_selector: MatchArmSelector::VariantId(*err_variant),
2226 block_id: block_err_id,
2227 var_ids: vec![err_value],
2228 },
2229 ],
2230 location,
2231 });
2232 builder.merge_and_end_with_match(
2233 ctx,
2234 match_info,
2235 vec![sealed_block_ok, sealed_block_err],
2236 location,
2237 )
2238}
2239
2240fn lower_optimized_extern_error_propagate<'db>(
2242 ctx: &mut LoweringContext<'db, '_>,
2243 builder: &mut BlockBuilder<'db>,
2244 extern_enum: LoweredExprExternEnum<'db>,
2245 ok_variant: &semantic::ConcreteVariant<'db>,
2246 err_variant: &semantic::ConcreteVariant<'db>,
2247 func_err_variant: &semantic::ConcreteVariant<'db>,
2248 location: LocationId<'db>,
2249) -> LoweringResult<'db, LoweredExpr<'db>> {
2250 log::trace!("Started lowering of an optimized error-propagate expression.");
2251
2252 let mut subscope_ok = create_subscope(ctx, builder);
2254 let block_ok_id = subscope_ok.block_id;
2255 let block_ok_input_vars: Vec<VariableId> =
2256 match_extern_variant_arm_input_types(ctx.db, &ok_variant.ty, &extern_enum)
2257 .map(|ty| ctx.new_var(VarRequest { ty, location }))
2258 .collect();
2259 let returns =
2260 match_extern_arm_ref_args_bind(ctx, &block_ok_input_vars, &extern_enum, &mut subscope_ok);
2261 let expr = extern_facade_expr(ctx, ok_variant.ty, returns.iter().copied(), location)
2262 .as_var_usage(ctx, &mut subscope_ok)?;
2263 let sealed_block_ok = subscope_ok.goto_callsite(Some(expr));
2264
2265 let mut subscope_err = create_subscope(ctx, builder);
2267 let block_err_id = subscope_err.block_id;
2268 let block_err_input_vars: Vec<VariableId> =
2269 match_extern_variant_arm_input_types(ctx.db, &err_variant.ty, &extern_enum)
2270 .map(|ty| ctx.new_var(VarRequest { ty, location }))
2271 .collect();
2272
2273 let returns =
2274 match_extern_arm_ref_args_bind(ctx, &block_err_input_vars, &extern_enum, &mut subscope_err);
2275 let expr = extern_facade_expr(ctx, err_variant.ty, returns.iter().copied(), location);
2276 let input = expr.as_var_usage(ctx, &mut subscope_err)?;
2277 let err_res = generators::EnumConstruct { input, variant: *func_err_variant, location }
2278 .add(ctx, &mut subscope_err.statements);
2279
2280 let ret_expr = lower_return(ctx, &mut subscope_err, err_res, location, true);
2281 let sealed_block_err = lowered_expr_to_block_scope_end(ctx, subscope_err, ret_expr)
2282 .map_err(LoweringFlowError::Failed)?;
2283
2284 let match_info = MatchInfo::Extern(MatchExternInfo {
2286 function: extern_enum.function.lowered(ctx.db),
2287 inputs: extern_enum.inputs,
2288 arms: vec![
2289 MatchArm {
2290 arm_selector: MatchArmSelector::VariantId(*ok_variant),
2291 block_id: block_ok_id,
2292 var_ids: block_ok_input_vars,
2293 },
2294 MatchArm {
2295 arm_selector: MatchArmSelector::VariantId(*err_variant),
2296 block_id: block_err_id,
2297 var_ids: block_err_input_vars,
2298 },
2299 ],
2300 location,
2301 });
2302 builder.merge_and_end_with_match(
2303 ctx,
2304 match_info,
2305 vec![sealed_block_ok, sealed_block_err],
2306 location,
2307 )
2308}
2309
2310fn match_extern_variant_arm_input_types<'db>(
2312 db: &'db dyn salsa::Database,
2313 ty: &semantic::TypeId<'db>,
2314 extern_enum: &LoweredExprExternEnum<'db>,
2315) -> impl Iterator<Item = semantic::TypeId<'db>> {
2316 chain!(
2317 extern_enum.ref_args.iter().map(|ref_arg| ref_arg.ty()),
2318 extern_facade_return_tys(db, ty).iter().copied()
2319 )
2320}
2321
2322fn match_extern_arm_ref_args_bind<'db, 'vars>(
2325 ctx: &mut LoweringContext<'db, '_>,
2326 arm_inputs: &'vars [VariableId],
2327 extern_enum: &LoweredExprExternEnum<'db>,
2328 subscope: &mut BlockBuilder<'db>,
2329) -> &'vars [VariableId] {
2330 let (ref_outputs, others) = arm_inputs.split_at(extern_enum.ref_args.len());
2331 for (ref_arg, output_var) in zip_eq(&extern_enum.ref_args, ref_outputs) {
2333 if let RefArg::Ref(ref_arg) = ref_arg {
2334 subscope.update_ref(ctx, ref_arg, *output_var);
2335 }
2336 }
2337 others
2338}
2339
2340fn lower_expr_assignment<'db>(
2342 ctx: &mut LoweringContext<'db, '_>,
2343 expr: &semantic::ExprAssignment<'db>,
2344 builder: &mut BlockBuilder<'db>,
2345) -> LoweringResult<'db, LoweredExpr<'db>> {
2346 log::trace!(
2347 "Started lowering of an assignment expression: {:?}",
2348 expr.debug(&ctx.expr_formatter)
2349 );
2350 let location = ctx.get_location(expr.stable_ptr.untyped());
2351 let var = lower_expr(ctx, builder, expr.rhs)?.as_var_usage(ctx, builder)?.var_id;
2352 builder.update_ref(ctx, &expr.ref_arg, var);
2353 Ok(LoweredExpr::Tuple { exprs: vec![], location })
2354}
2355
2356fn alloc_empty_block<'db>(ctx: &mut LoweringContext<'db, '_>) -> BlockId {
2358 ctx.blocks.alloc_empty()
2359}
2360
2361fn create_subscope<'db>(
2363 ctx: &mut LoweringContext<'db, '_>,
2364 builder: &BlockBuilder<'db>,
2365) -> BlockBuilder<'db> {
2366 builder.child_block_builder(alloc_empty_block(ctx))
2367}
2368
2369fn check_error_free_or_warn<'db>(
2371 db: &dyn Database,
2372 diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>,
2373 semantic_function_id: defs::ids::FunctionWithBodyId<'db>,
2374 diagnostics_description: &str,
2375) -> Maybe<()> {
2376 let declaration_error_free = diagnostics.check_error_free();
2377 declaration_error_free.inspect_err(|_| {
2378 log::warn!(
2379 "Function `{function_path}` has semantic diagnostics in its \
2380 {diagnostics_description}:\n{diagnostics_format}",
2381 function_path = semantic_function_id.full_path(db),
2382 diagnostics_format = diagnostics.format(db)
2383 );
2384 })
2385}