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