1use std::{iter, mem};
4
5use arrayvec::ArrayVec;
6use base_db::Crate;
7use hir_def::{
8 AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GenericParamId, HasModule,
9 ItemContainerId, LocalFieldId, Lookup, TraitId,
10 expr_store::{Body, ExpressionStore, HygieneId, body::Param, path::Path},
11 hir::{
12 ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ClosureKind, ExprId, ExprOrPatId,
13 LabelId, Literal, MatchArm, Pat, PatId, RecordLitField, RecordSpread,
14 },
15 item_tree::FieldsShape,
16 lang_item::LangItems,
17 resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
18 signatures::{ConstSignature, EnumSignature, FunctionSignature, StaticSignature},
19};
20use hir_expand::name::Name;
21use itertools::{EitherOrBoth, Itertools};
22use la_arena::{ArenaMap, RawIdx};
23use rustc_apfloat::Float;
24use rustc_hash::FxHashMap;
25use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _};
26use salsa::SalsaValue;
27use span::Edition;
28
29use crate::{
30 Adjust, Adjustment, AutoBorrow, CallableDefId, InferBodyId, ParamEnvAndCrate,
31 consteval::ConstEvalError,
32 db::{GeneralConstId, HirDatabase, InternedClosure, InternedClosureId},
33 generics::generics,
34 infer::{
35 CaptureSourceStack, CapturedPlace, UpvarCapture,
36 cast::CastTy,
37 closure::analysis::expr_use_visitor::{
38 Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
39 },
40 },
41 inhabitedness::is_ty_uninhabited_from,
42 layout::LayoutError,
43 method_resolution::CandidateId,
44 mir::{
45 AggregateKind, Arena, BasicBlock, BasicBlockId, BinOp, BorrowKind, CastKind, Expr,
46 FieldIndex, GenericArgs, Idx, InferenceResult, Local, LocalId, MemoryMap, MirBody, MirSpan,
47 Mutability, Operand, PlaceElem, PointerCast, Projection, ProjectionElem, Rvalue, Statement,
48 StatementKind, StoredPlace, SwitchTargets, Terminator, TerminatorKind, Ty, UnOp, VariantId,
49 return_slot,
50 },
51 next_solver::{
52 Const, DbInterner, ParamConst, ParamEnv, Region, StoredGenericArgs, StoredTy, TyKind,
53 TypingMode, UnevaluatedConst,
54 infer::{DbInternerInferExt, InferCtxt},
55 },
56};
57
58use super::{OperandKind, Place};
59
60mod as_place;
61mod pattern_matching;
62
63#[derive(Debug, Clone)]
64struct LoopBlocks {
65 begin: BasicBlockId,
66 end: Option<BasicBlockId>,
68 place: StoredPlace,
69 drop_scope_index: usize,
70}
71
72#[derive(Debug, Clone, Default)]
73struct DropScope {
74 locals: Vec<LocalId>,
76}
77
78struct MirLowerCtx<'a, 'db> {
79 result: MirBody<'db>,
80 owner: InferBodyId<'db>,
81 store_owner: ExpressionStoreOwnerId,
82 current_loop_blocks: Option<LoopBlocks>,
83 labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>,
84 discr_temp: Option<StoredPlace>,
85 db: &'db dyn HirDatabase,
86 store: &'a ExpressionStore,
87 infer: &'a InferenceResult<'db>,
88 types: &'db crate::next_solver::DefaultAny<'db>,
89 resolver: Resolver<'db>,
90 drop_scopes: Vec<DropScope>,
91 env: ParamEnv<'db>,
92 infcx: InferCtxt<'db>,
93}
94
95#[derive(Debug, Clone, PartialEq, Eq, SalsaValue)]
97pub enum MirLowerError<'db> {
98 ConstEvalError(Box<str>, Box<ConstEvalError<'db>>),
99 LayoutError(LayoutError),
100 IncompleteExpr,
101 IncompletePattern,
102 TraitFunctionDefinition(TraitId, Name),
104 UnresolvedName {
105 owner: ExpressionStoreOwnerId,
106 path: Path,
107 },
108 RecordLiteralWithoutPath,
109 UnresolvedMethod(String),
110 UnresolvedField,
111 UnsizedTemporary(StoredTy),
112 MissingFunctionDefinition(InferBodyId<'db>, ExprId),
113 HasErrors,
114 TypeError(&'static str),
116 NotSupported(String),
117 ContinueWithoutLoop,
118 BreakWithoutLoop,
119 Loop,
120 ImplementationError(String),
122 LangItemNotFound,
123 MutatingRvalue,
124 UnresolvedLabel,
125 UnresolvedUpvar(StoredPlace),
126 InaccessibleLocal,
127
128 GenericArgNotProvided(GenericParamId, StoredGenericArgs),
130}
131
132struct DropScopeToken;
134impl DropScopeToken {
135 fn pop_and_drop<'db>(
136 self,
137 ctx: &mut MirLowerCtx<'_, 'db>,
138 current: BasicBlockId,
139 span: MirSpan,
140 ) -> BasicBlockId {
141 std::mem::forget(self);
142 ctx.pop_drop_scope_internal(current, span)
143 }
144
145 fn pop_assume_dropped(self, ctx: &mut MirLowerCtx<'_, '_>) {
150 std::mem::forget(self);
151 ctx.pop_drop_scope_assume_dropped_internal();
152 }
153}
154
155impl Drop for DropScopeToken {
156 fn drop(&mut self) {}
157}
158
159macro_rules! not_supported {
171 ($it: expr) => {
172 return Err(MirLowerError::NotSupported(format!($it)))
173 };
174}
175
176macro_rules! implementation_error {
177 ($it: expr) => {{
178 ::stdx::never!("MIR lower implementation bug: {}", format!($it));
179 return Err(MirLowerError::ImplementationError(format!($it)));
180 }};
181}
182
183impl From<LayoutError> for MirLowerError<'_> {
184 fn from(value: LayoutError) -> Self {
185 MirLowerError::LayoutError(value)
186 }
187}
188
189type Result<'db, T> = std::result::Result<T, MirLowerError<'db>>;
190
191impl<'a, 'db> MirLowerCtx<'a, 'db> {
192 fn new(
193 db: &'db dyn HirDatabase,
194 owner: InferBodyId<'db>,
195 store: &'a ExpressionStore,
196 infer: &'a InferenceResult<'db>,
197 ) -> Self {
198 let mut basic_blocks = Arena::new();
199 let start_block = basic_blocks.alloc(BasicBlock {
200 statements: vec![],
201 terminator: None,
202 is_cleanup: false,
203 });
204 let locals = Arena::new();
205 let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
206 let mir = MirBody {
207 basic_blocks,
208 locals,
209 start_block,
210 binding_locals,
211 upvar_locals: FxHashMap::default(),
212 param_locals: vec![],
213 owner,
214 closures: vec![],
215 };
216 let store_owner = owner.expression_store_owner(db);
217 let resolver = owner.resolver(db);
218 let env = db.trait_environment(owner.generic_def(db));
219 let interner = DbInterner::new_with(db, resolver.krate());
220 let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
222
223 MirLowerCtx {
224 result: mir,
225 db,
226 infer,
227 store,
228 types: crate::next_solver::default_types(),
229 owner,
230 store_owner,
231 resolver,
232 current_loop_blocks: None,
233 labeled_loop_blocks: Default::default(),
234 discr_temp: None,
235 drop_scopes: vec![DropScope::default()],
236 env,
237 infcx,
238 }
239 }
240
241 #[inline]
242 fn interner(&self) -> DbInterner<'db> {
243 self.infcx.interner
244 }
245
246 #[inline]
247 fn lang_items(&self) -> &'db LangItems {
248 self.infcx.interner.lang_items()
249 }
250
251 fn temp(&mut self, ty: Ty<'db>, current: BasicBlockId, span: MirSpan) -> Result<'db, LocalId> {
252 if matches!(ty.kind(), TyKind::Slice(_) | TyKind::Dynamic(..)) {
253 return Err(MirLowerError::UnsizedTemporary(ty.store()));
254 }
255 let l = self.result.locals.alloc(Local { ty: ty.store() });
256 self.push_storage_live_for_local(l, current, span)?;
257 Ok(l)
258 }
259
260 fn lower_expr_to_some_operand(
261 &mut self,
262 expr_id: ExprId,
263 current: BasicBlockId,
264 ) -> Result<'db, Option<(Operand, BasicBlockId)>> {
265 if !self.has_adjustments(expr_id)
266 && let Expr::Literal(l) = &self.store[expr_id]
267 {
268 let ty = self.expr_ty_without_adjust(expr_id);
269 return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
270 }
271 let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else {
272 return Ok(None);
273 };
274 Ok(Some((
275 Operand { kind: OperandKind::Copy(p.store()), span: Some(expr_id.into()) },
276 current,
277 )))
278 }
279
280 fn lower_expr_to_place_with_adjust(
281 &mut self,
282 expr_id: ExprId,
283 place: Place<'db>,
284 current: BasicBlockId,
285 adjustments: &[Adjustment],
286 ) -> Result<'db, Option<BasicBlockId>> {
287 match adjustments.split_last() {
288 Some((last, rest)) => match &last.kind {
289 Adjust::NeverToAny => {
290 let temp = self.temp(self.types.types.never, current, MirSpan::Unknown)?;
291 self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest)
292 }
293 Adjust::Deref(_) => {
294 let Some((p, current)) =
295 self.lower_expr_as_place_with_adjust(current, expr_id, true, adjustments)?
296 else {
297 return Ok(None);
298 };
299 self.push_assignment(
300 current,
301 place,
302 Operand { kind: OperandKind::Copy(p.store()), span: None }.into(),
303 expr_id.into(),
304 );
305 Ok(Some(current))
306 }
307 Adjust::Borrow(AutoBorrow::Ref(m)) => self.lower_expr_to_place_with_borrow_adjust(
308 expr_id,
309 place,
310 current,
311 rest,
312 (*m).into(),
313 ),
314 Adjust::Borrow(AutoBorrow::RawPtr(m)) => {
315 self.lower_expr_to_place_with_borrow_adjust(expr_id, place, current, rest, *m)
316 }
317 Adjust::Pointer(cast) => {
318 let Some((p, current)) =
319 self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)?
320 else {
321 return Ok(None);
322 };
323 self.push_assignment(
324 current,
325 place,
326 Rvalue::Cast(
327 CastKind::PointerCoercion(*cast),
328 Operand { kind: OperandKind::Copy(p.store()), span: None },
329 last.target.clone(),
330 ),
331 expr_id.into(),
332 );
333 Ok(Some(current))
334 }
335 },
336 None => self.lower_expr_to_place_without_adjust(expr_id, place, current),
337 }
338 }
339
340 fn lower_expr_to_place_with_borrow_adjust(
341 &mut self,
342 expr_id: ExprId,
343 place: Place<'db>,
344 current: BasicBlockId,
345 rest: &[Adjustment],
346 m: Mutability,
347 ) -> Result<'db, Option<BasicBlockId>> {
348 let Some((p, current)) =
349 self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)?
350 else {
351 return Ok(None);
352 };
353 let bk = BorrowKind::from_rustc_mutability(m);
354 self.push_assignment(current, place, Rvalue::Ref(bk, p.store()), expr_id.into());
355 Ok(Some(current))
356 }
357
358 fn lower_expr_to_place(
359 &mut self,
360 expr_id: ExprId,
361 place: Place<'db>,
362 prev_block: BasicBlockId,
363 ) -> Result<'db, Option<BasicBlockId>> {
364 if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) {
365 return self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments);
366 }
367 self.lower_expr_to_place_without_adjust(expr_id, place, prev_block)
368 }
369
370 fn lower_expr_to_place_without_adjust(
371 &mut self,
372 expr_id: ExprId,
373 place: Place<'db>,
374 mut current: BasicBlockId,
375 ) -> Result<'db, Option<BasicBlockId>> {
376 match &self.store[expr_id] {
377 Expr::OffsetOf(_) => {
378 not_supported!("builtin#offset_of")
379 }
380 Expr::InlineAsm(_) => {
381 not_supported!("builtin#asm")
382 }
383 Expr::Missing => {
384 if let Some(f) = self.owner.as_function() {
385 let assoc = f.lookup(self.db);
386 if let ItemContainerId::TraitId(t) = assoc.container {
387 let name = &FunctionSignature::of(self.db, f).name;
388 return Err(MirLowerError::TraitFunctionDefinition(t, name.clone()));
389 }
390 }
391 Err(MirLowerError::IncompleteExpr)
392 }
393 Expr::Path(p) => {
394 let pr =
395 if let Some((assoc, subst)) = self.infer.assoc_resolutions_for_expr(expr_id) {
396 match assoc {
397 CandidateId::ConstId(c) => {
398 self.lower_const(c.into(), current, place, subst, expr_id.into())?;
399 return Ok(Some(current));
400 }
401 CandidateId::FunctionId(_) => {
402 return Ok(Some(current));
404 }
405 }
406 } else if let Some(variant) = self.infer.variant_resolution_for_expr(expr_id) {
407 match variant {
408 VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
409 VariantId::StructId(s) => ValueNs::StructId(s),
410 VariantId::UnionId(_) => implementation_error!("Union variant as path"),
411 }
412 } else {
413 let resolver_guard =
414 self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
415 let hygiene = self.store.expr_path_hygiene(expr_id);
416 let result = self
417 .resolver
418 .resolve_path_in_value_ns_fully(self.db, p, hygiene)
419 .ok_or_else(|| MirLowerError::UnresolvedName {
420 owner: self.owner.expression_store_owner(self.db),
421 path: p.clone(),
422 })?;
423 self.resolver.reset_to_guard(resolver_guard);
424 result
425 };
426 match pr {
427 ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => {
428 let Some((temp, current)) =
429 self.lower_expr_as_place_without_adjust(current, expr_id, false)?
430 else {
431 return Ok(None);
432 };
433 self.push_assignment(
434 current,
435 place,
436 Operand { kind: OperandKind::Copy(temp.store()), span: None }.into(),
437 expr_id.into(),
438 );
439 Ok(Some(current))
440 }
441 ValueNs::ConstId(const_id) => {
442 self.lower_const(
443 const_id.into(),
444 current,
445 place,
446 GenericArgs::empty(),
447 expr_id.into(),
448 )?;
449 Ok(Some(current))
450 }
451 ValueNs::EnumVariantId(variant_id) => {
452 let variant_fields = variant_id.fields(self.db);
453 if variant_fields.shape == FieldsShape::Unit {
454 let ty = self.infer.expr_ty(expr_id);
455 current = self.lower_enum_variant(
456 variant_id,
457 current,
458 place,
459 ty,
460 Box::new([]),
461 expr_id.into(),
462 )?;
463 }
464 Ok(Some(current))
466 }
467 ValueNs::GenericParam(p) => {
468 let def = self.owner.generic_def(self.db);
469 let generics = generics(self.db, def);
470 let index = generics.type_or_const_param_idx(p.into());
471 self.push_assignment(
472 current,
473 place,
474 Rvalue::from(Operand {
475 kind: OperandKind::Constant {
476 konst: Const::new_param(
477 self.interner(),
478 ParamConst { id: p, index },
479 )
480 .store(),
481 ty: self.db.const_param_ty(p).store(),
482 },
483 span: None,
484 }),
485 expr_id.into(),
486 );
487 Ok(Some(current))
488 }
489 ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::ImplSelf(_) => {
490 Ok(Some(current))
492 }
493 }
494 }
495 Expr::If { condition, then_branch, else_branch } => {
496 let Some((discr, current)) =
497 self.lower_expr_to_some_operand(*condition, current)?
498 else {
499 return Ok(None);
500 };
501 let start_of_then = self.new_basic_block();
502 let end_of_then = self.lower_expr_to_place(*then_branch, place, start_of_then)?;
503 let start_of_else = self.new_basic_block();
504 let end_of_else = if let Some(else_branch) = else_branch {
505 self.lower_expr_to_place(*else_branch, place, start_of_else)?
506 } else {
507 Some(start_of_else)
508 };
509 self.set_terminator(
510 current,
511 TerminatorKind::SwitchInt {
512 discr,
513 targets: SwitchTargets::static_if(1, start_of_then, start_of_else),
514 },
515 expr_id.into(),
516 );
517 Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()))
518 }
519 Expr::Let { pat, expr } => {
520 let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)?
521 else {
522 return Ok(None);
523 };
524 self.push_fake_read(current, cond_place, expr_id.into());
525 let resolver_guard =
526 self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
527 let (then_target, else_target) =
528 self.pattern_match(current, None, cond_place, *pat)?;
529 self.resolver.reset_to_guard(resolver_guard);
530 self.write_bytes_to_place(
531 then_target,
532 place,
533 Box::new([1]),
534 Ty::new_bool(self.interner()),
535 MirSpan::Unknown,
536 )?;
537 if let Some(else_target) = else_target {
538 self.write_bytes_to_place(
539 else_target,
540 place,
541 Box::new([0]),
542 Ty::new_bool(self.interner()),
543 MirSpan::Unknown,
544 )?;
545 }
546 Ok(self.merge_blocks(Some(then_target), else_target, expr_id.into()))
547 }
548 Expr::Block { id: _, statements, tail, label, unsafe_: _ } => {
549 if let Some(label) = label {
550 self.lower_loop(current, place, Some(*label), expr_id.into(), |this, begin| {
551 if let Some(current) = this.lower_block_to_place(
552 statements,
553 begin,
554 *tail,
555 place,
556 expr_id.into(),
557 )? {
558 let end = this.current_loop_end()?;
559 this.set_goto(current, end, expr_id.into());
560 }
561 Ok(())
562 })
563 } else {
564 self.lower_block_to_place(statements, current, *tail, place, expr_id.into())
565 }
566 }
567 Expr::Loop { body, label, source: _ } => {
568 self.lower_loop(current, place, *label, expr_id.into(), |this, begin| {
569 let scope = this.push_drop_scope();
570 if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? {
571 current = scope.pop_and_drop(this, current, body.into());
572 this.set_goto(current, begin, expr_id.into());
573 } else {
574 scope.pop_assume_dropped(this);
575 }
576 Ok(())
577 })
578 }
579 Expr::Call { callee, args, .. } => {
580 if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) {
581 let ty = Ty::new_fn_def(
582 self.interner(),
583 CallableDefId::FunctionId(func_id).into(),
584 generic_args,
585 );
586 let func = Operand::from_bytes(Box::default(), ty);
587 return self.lower_call_and_args(
588 func,
589 iter::once(*callee).chain(args.iter().copied()),
590 place,
591 current,
592 self.is_uninhabited(expr_id),
593 expr_id.into(),
594 );
595 }
596 let callee_ty = self.expr_ty_after_adjustments(*callee);
597 match callee_ty.kind() {
598 TyKind::FnDef(..) => {
599 let func = Operand::from_bytes(Box::default(), callee_ty);
600 self.lower_call_and_args(
601 func,
602 args.iter().copied(),
603 place,
604 current,
605 self.is_uninhabited(expr_id),
606 expr_id.into(),
607 )
608 }
609 TyKind::FnPtr(..) => {
610 let Some((func, current)) =
611 self.lower_expr_to_some_operand(*callee, current)?
612 else {
613 return Ok(None);
614 };
615 self.lower_call_and_args(
616 func,
617 args.iter().copied(),
618 place,
619 current,
620 self.is_uninhabited(expr_id),
621 expr_id.into(),
622 )
623 }
624 TyKind::Closure(_, _) => {
625 not_supported!(
626 "method resolution not emitted for closure (Are Fn traits available?)"
627 );
628 }
629 TyKind::Error(_) => {
630 Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id))
631 }
632 _ => Err(MirLowerError::TypeError("function call on bad type")),
633 }
634 }
635 Expr::MethodCall { receiver, args, method_name, .. } => {
636 let (func_id, generic_args) =
637 self.infer.method_resolution(expr_id).ok_or_else(|| {
638 MirLowerError::UnresolvedMethod(
639 method_name.display(self.db, self.edition()).to_string(),
640 )
641 })?;
642 let func = Operand::from_fn(self.db, func_id, generic_args);
643 self.lower_call_and_args(
644 func,
645 iter::once(*receiver).chain(args.iter().copied()),
646 place,
647 current,
648 self.is_uninhabited(expr_id),
649 expr_id.into(),
650 )
651 }
652 Expr::Match { expr, arms } => {
653 let Some((cond_place, mut current)) =
654 self.lower_expr_as_place(current, *expr, true)?
655 else {
656 return Ok(None);
657 };
658 self.push_fake_read(current, cond_place, expr_id.into());
659 let mut end = None;
660 let resolver_guard =
661 self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
662 for MatchArm { pat, guard, expr } in arms.iter() {
663 let (then, mut otherwise) =
664 self.pattern_match(current, None, cond_place, *pat)?;
665 let then = if let &Some(guard) = guard {
666 let next = self.new_basic_block();
667 let o = otherwise.get_or_insert_with(|| self.new_basic_block());
668 if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? {
669 self.set_terminator(
670 c,
671 TerminatorKind::SwitchInt {
672 discr,
673 targets: SwitchTargets::static_if(1, next, *o),
674 },
675 expr_id.into(),
676 );
677 }
678 next
679 } else {
680 then
681 };
682 if let Some(block) = self.lower_expr_to_place(*expr, place, then)? {
683 let r = end.get_or_insert_with(|| self.new_basic_block());
684 self.set_goto(block, *r, expr_id.into());
685 }
686 match otherwise {
687 Some(o) => current = o,
688 None => {
689 break;
692 }
693 }
694 }
695 self.resolver.reset_to_guard(resolver_guard);
696 if self.is_unterminated(current) {
697 self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into());
698 }
699 Ok(end)
700 }
701 Expr::Continue { label } => {
702 let loop_data = match label {
703 Some(l) => {
704 self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?
705 }
706 None => self
707 .current_loop_blocks
708 .as_ref()
709 .ok_or(MirLowerError::ContinueWithoutLoop)?,
710 };
711 let begin = loop_data.begin;
712 current =
713 self.drop_until_scope(loop_data.drop_scope_index, current, expr_id.into());
714 self.set_goto(current, begin, expr_id.into());
715 Ok(None)
716 }
717 &Expr::Break { expr, label } => {
718 if let Some(expr) = expr {
719 let loop_data = match label {
720 Some(l) => self
721 .labeled_loop_blocks
722 .get(&l)
723 .ok_or(MirLowerError::UnresolvedLabel)?,
724 None => self
725 .current_loop_blocks
726 .as_ref()
727 .ok_or(MirLowerError::BreakWithoutLoop)?,
728 };
729 let Some(c) =
730 self.lower_expr_to_place(expr, loop_data.place.as_ref(), current)?
731 else {
732 return Ok(None);
733 };
734 current = c;
735 }
736 let (end, drop_scope) = match label {
737 Some(l) => {
738 let loop_blocks = self
739 .labeled_loop_blocks
740 .get(&l)
741 .ok_or(MirLowerError::UnresolvedLabel)?;
742 (
743 loop_blocks.end.expect("We always generate end for labeled loops"),
744 loop_blocks.drop_scope_index,
745 )
746 }
747 None => (
748 self.current_loop_end()?,
749 self.current_loop_blocks.as_ref().unwrap().drop_scope_index,
750 ),
751 };
752 current = self.drop_until_scope(drop_scope, current, expr_id.into());
753 self.set_goto(current, end, expr_id.into());
754 Ok(None)
755 }
756 Expr::Return { expr } => {
757 if let Some(expr) = expr {
758 if let Some(c) =
759 self.lower_expr_to_place(*expr, return_slot().into(), current)?
760 {
761 current = c;
762 } else {
763 return Ok(None);
764 }
765 }
766 current = self.drop_until_scope(0, current, expr_id.into());
767 self.set_terminator(current, TerminatorKind::Return, expr_id.into());
768 Ok(None)
769 }
770 Expr::Become { .. } => not_supported!("tail-calls"),
771 Expr::Yield { .. } => not_supported!("yield"),
772 Expr::RecordLit { fields, path, spread, .. } => {
773 let spread_place = match *spread {
774 RecordSpread::Expr(it) => {
775 let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else {
776 return Ok(None);
777 };
778 current = c;
779 Some(p)
780 }
781 RecordSpread::None => None,
782 RecordSpread::FieldDefaults => not_supported!("empty record spread"),
783 };
784 let variant_id =
785 self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| {
786 MirLowerError::UnresolvedName {
787 owner: self.owner.expression_store_owner(self.db),
788 path: path.clone(),
789 }
790 })?;
791 let subst = match self.expr_ty_without_adjust(expr_id).kind() {
792 TyKind::Adt(_, s) => s,
793 _ => not_supported!("Non ADT record literal"),
794 };
795 let variant_fields = variant_id.fields(self.db);
796 match variant_id {
797 VariantId::EnumVariantId(_) | VariantId::StructId(_) => {
798 let mut operands = vec![None; variant_fields.fields().len()];
799 for RecordLitField { name, expr } in fields.iter() {
800 let field_id =
801 variant_fields.field(name).ok_or(MirLowerError::UnresolvedField)?;
802 let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)?
803 else {
804 return Ok(None);
805 };
806 current = c;
807 operands[u32::from(field_id.into_raw()) as usize] = Some(op);
808 }
809 let rvalue = Rvalue::Aggregate(
810 AggregateKind::Adt(variant_id, subst.store()),
811 match spread_place {
812 Some(sp) if let VariantId::StructId(_) = variant_id => operands
813 .into_iter()
814 .enumerate()
815 .map(|(i, it)| match it {
816 Some(it) => it,
817 None => {
818 let p = sp.project(ProjectionElem::Field(FieldIndex(
819 i as u32,
820 )));
821 Operand {
822 kind: OperandKind::Copy(p.store()),
823 span: None,
824 }
825 }
826 })
827 .collect(),
828 Some(_) => {
829 return Err(MirLowerError::TypeError(
830 "functional record update syntax requires a struct",
831 ));
832 }
833 None => operands.into_iter().collect::<Option<_>>().ok_or(
834 MirLowerError::TypeError("missing field in record literal"),
835 )?,
836 },
837 );
838 self.push_assignment(current, place, rvalue, expr_id.into());
839 Ok(Some(current))
840 }
841 VariantId::UnionId(_union_id) => {
842 let [RecordLitField { name, expr }] = fields.as_ref() else {
843 not_supported!("Union record literal with more than one field");
844 };
845 let local_id =
846 variant_fields.field(name).ok_or(MirLowerError::UnresolvedField)?;
847 let place = place.project(PlaceElem::Field(local_id.into()));
848 self.lower_expr_to_place(*expr, place, current)
849 }
850 }
851 }
852 Expr::Await { .. } => not_supported!("await"),
853 Expr::Yeet { .. } => not_supported!("yeet"),
854 &Expr::Const(id) => {
855 self.lower_expr_to_place(id, place, current)
860 }
861 Expr::Cast { expr, type_ref: _ } => {
862 let (rvalue, current) = if self.infer.coercion_casts.contains(expr) {
865 let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)?
866 else {
867 return Ok(None);
868 };
869 (Rvalue::Use(it), current)
870 } else {
871 let source_ty = self.infer.expr_ty(*expr);
872 let target_ty = self.infer.expr_ty(expr_id);
873 let (it, source_ty, current) =
874 if let Some(VariantId::EnumVariantId(variant_id)) =
875 self.infer.variant_resolution_for_expr(*expr)
876 {
877 self.lower_variant_discriminant(current, variant_id)?
878 } else if let TyKind::Adt(adt, _) = source_ty.kind()
879 && adt.is_enum()
880 {
881 let Some((enum_place, current)) =
882 self.lower_expr_as_place(current, *expr, true)?
883 else {
884 return Ok(None);
885 };
886 let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128);
887 let discr_place: Place<'db> =
888 self.temp(discr_ty, current, expr_id.into())?.into();
889
890 self.push_assignment(
891 current,
892 discr_place,
893 Rvalue::Discriminant(enum_place.store()),
894 expr_id.into(),
895 );
896 (
897 Operand {
898 kind: OperandKind::Copy(discr_place.store()),
899 span: Some(expr_id.into()),
900 },
901 discr_ty,
902 current,
903 )
904 } else {
905 let Some((it, current)) =
906 self.lower_expr_to_some_operand(*expr, current)?
907 else {
908 return Ok(None);
909 };
910 (it, source_ty, current)
911 };
912 let cast_kind = if source_ty.as_reference().is_some() {
913 CastKind::PointerCoercion(PointerCast::ArrayToPointer)
914 } else {
915 cast_kind(self.db, source_ty, target_ty)?
916 };
917 (Rvalue::Cast(cast_kind, it, target_ty.store()), current)
918 };
919 self.push_assignment(current, place, rvalue, expr_id.into());
920 Ok(Some(current))
921 }
922 Expr::Ref { expr, rawness: _, mutability } => {
923 let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
924 return Ok(None);
925 };
926 let bk = BorrowKind::from_hir_mutability(*mutability);
927 self.push_assignment(current, place, Rvalue::Ref(bk, p.store()), expr_id.into());
928 Ok(Some(current))
929 }
930 Expr::Field { .. }
931 | Expr::Index { .. }
932 | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => {
933 let Some((p, current)) =
934 self.lower_expr_as_place_without_adjust(current, expr_id, true)?
935 else {
936 return Ok(None);
937 };
938 self.push_assignment(
939 current,
940 place,
941 Operand { kind: OperandKind::Copy(p.store()), span: None }.into(),
942 expr_id.into(),
943 );
944 Ok(Some(current))
945 }
946 Expr::UnaryOp {
947 expr,
948 op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg),
949 } => {
950 let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)?
951 else {
952 return Ok(None);
953 };
954 let operation = match op {
955 hir_def::hir::UnaryOp::Not => UnOp::Not,
956 hir_def::hir::UnaryOp::Neg => UnOp::Neg,
957 _ => unreachable!(),
958 };
959 self.push_assignment(
960 current,
961 place,
962 Rvalue::UnaryOp(operation, operand),
963 expr_id.into(),
964 );
965 Ok(Some(current))
966 }
967 Expr::BinaryOp { lhs, rhs, op } => {
968 let op: BinaryOp = op.ok_or(MirLowerError::IncompleteExpr)?;
969 let is_builtin = 'b: {
970 let lhs_ty = self.expr_ty_without_adjust(*lhs);
973 let rhs_ty = self.expr_ty_without_adjust(*rhs);
974 if matches!(op, BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. }))
975 && matches!(lhs_ty.kind(), TyKind::RawPtr(..))
976 && matches!(rhs_ty.kind(), TyKind::RawPtr(..))
977 {
978 break 'b true;
979 }
980 let builtin_inequal_impls = matches!(
981 op,
982 BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr)
983 | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) }
984 );
985 matches!(
986 lhs_ty.kind(),
987 TyKind::Bool
988 | TyKind::Char
989 | TyKind::Int(_)
990 | TyKind::Uint(_)
991 | TyKind::Float(_)
992 ) && matches!(
993 rhs_ty.kind(),
994 TyKind::Bool
995 | TyKind::Char
996 | TyKind::Int(_)
997 | TyKind::Uint(_)
998 | TyKind::Float(_)
999 ) && (lhs_ty == rhs_ty || builtin_inequal_impls)
1000 };
1001 if !is_builtin
1002 && let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id)
1003 {
1004 let func = Operand::from_fn(self.db, func_id, generic_args);
1005 return self.lower_call_and_args(
1006 func,
1007 [*lhs, *rhs].into_iter(),
1008 place,
1009 current,
1010 self.is_uninhabited(expr_id),
1011 expr_id.into(),
1012 );
1013 }
1014 if let hir_def::hir::BinaryOp::Assignment { op: Some(op) } = op {
1015 let adjusts = self
1017 .infer
1018 .expr_adjustments
1019 .get(lhs)
1020 .and_then(|it| it.split_last())
1021 .map(|it| it.1)
1022 .ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?;
1023 let Some((lhs_place, current)) =
1024 self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)?
1025 else {
1026 return Ok(None);
1027 };
1028 let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)?
1029 else {
1030 return Ok(None);
1031 };
1032 let r_value = Rvalue::CheckedBinaryOp(
1033 op.into(),
1034 Operand { kind: OperandKind::Copy(lhs_place.store()), span: None },
1035 rhs_op,
1036 );
1037 self.push_assignment(current, lhs_place, r_value, expr_id.into());
1038 return Ok(Some(current));
1039 }
1040 let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)?
1041 else {
1042 return Ok(None);
1043 };
1044 if let hir_def::hir::BinaryOp::LogicOp(op) = op {
1045 let value_to_short = match op {
1046 syntax::ast::LogicOp::And => 0,
1047 syntax::ast::LogicOp::Or => 1,
1048 };
1049 let start_of_then = self.new_basic_block();
1050 self.push_assignment(
1051 start_of_then,
1052 place,
1053 lhs_op.clone().into(),
1054 expr_id.into(),
1055 );
1056 let end_of_then = Some(start_of_then);
1057 let start_of_else = self.new_basic_block();
1058 let end_of_else = self.lower_expr_to_place(*rhs, place, start_of_else)?;
1059 self.set_terminator(
1060 current,
1061 TerminatorKind::SwitchInt {
1062 discr: lhs_op,
1063 targets: SwitchTargets::static_if(
1064 value_to_short,
1065 start_of_then,
1066 start_of_else,
1067 ),
1068 },
1069 expr_id.into(),
1070 );
1071 return Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()));
1072 }
1073 let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)?
1074 else {
1075 return Ok(None);
1076 };
1077 self.push_assignment(
1078 current,
1079 place,
1080 Rvalue::CheckedBinaryOp(
1081 match op {
1082 hir_def::hir::BinaryOp::LogicOp(op) => match op {
1083 hir_def::hir::LogicOp::And => BinOp::BitAnd, hir_def::hir::LogicOp::Or => BinOp::BitOr,
1085 },
1086 hir_def::hir::BinaryOp::ArithOp(op) => BinOp::from(op),
1087 hir_def::hir::BinaryOp::CmpOp(op) => BinOp::from(op),
1088 hir_def::hir::BinaryOp::Assignment { .. } => unreachable!(), },
1090 lhs_op,
1091 rhs_op,
1092 ),
1093 expr_id.into(),
1094 );
1095 Ok(Some(current))
1096 }
1097 &Expr::Assignment { target, value } => {
1098 let Some((value, mut current)) = self.lower_expr_as_place(current, value, true)?
1099 else {
1100 return Ok(None);
1101 };
1102 self.push_fake_read(current, value, expr_id.into());
1103 let resolver_guard =
1104 self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
1105 current = self.pattern_match_assignment(current, value, target)?;
1106 self.resolver.reset_to_guard(resolver_guard);
1107 Ok(Some(current))
1108 }
1109 Expr::Closure { closure_kind: ClosureKind::Closure, .. } => {
1110 let ty = self.expr_ty_without_adjust(expr_id);
1111 let TyKind::Closure(id, _) = ty.kind() else {
1112 not_supported!("closure with non closure type");
1113 };
1114 self.result.closures.push(id.0);
1115 let closure_data = &self.infer.closures_data[&id.0.loc(self.db).expr];
1116
1117 let span = |sources: &[CaptureSourceStack]| match sources
1118 .first()
1119 .map(|it| it.final_source().unpack())
1120 {
1121 Some(ExprOrPatId::ExprId(it)) => it.into(),
1122 Some(ExprOrPatId::PatId(it)) => it.into(),
1123 None => MirSpan::Unknown,
1124 };
1125 let convert_place = |this: &mut Self, place: &HirPlace| {
1126 let (HirPlaceBase::Local(local) | HirPlaceBase::Upvar { var_id: local, .. }) =
1127 place.base
1128 else {
1129 not_supported!("non-local capture");
1130 };
1131 Ok(StoredPlace {
1132 local: this.binding_local(local)?,
1133 projection: Projection::new_from_iter(convert_closure_capture_projections(
1134 self.db, place,
1135 ))
1136 .store(),
1137 })
1138 };
1139
1140 for (place, _, sources) in &closure_data.fake_reads {
1141 let p = convert_place(self, place)?;
1142 self.push_fake_read(current, p.as_ref(), span(sources));
1143 }
1144
1145 let captures = closure_data.min_captures.values().flatten();
1146 let mut operands = vec![];
1147 for capture in captures {
1148 let p = convert_place(self, &capture.place)?;
1149 match capture.info.capture_kind {
1150 UpvarCapture::ByRef(bk) => {
1151 let tmp_ty = capture.captured_ty(self.db);
1152 let capture_span = span(&capture.info.sources);
1154 let tmp = self.temp(tmp_ty, current, capture_span)?.into();
1155 self.push_assignment(
1156 current,
1157 tmp,
1158 Rvalue::Ref(BorrowKind::from_hir(bk), p),
1159 capture_span,
1160 );
1161 operands
1162 .push(Operand { kind: OperandKind::Move(tmp.store()), span: None });
1163 }
1164 UpvarCapture::ByValue => {
1165 operands.push(Operand { kind: OperandKind::Move(p), span: None })
1166 }
1167 UpvarCapture::ByUse => not_supported!("capture by use"),
1168 }
1169 }
1170 self.push_assignment(
1171 current,
1172 place,
1173 Rvalue::Aggregate(AggregateKind::Closure(ty.store()), operands.into()),
1174 expr_id.into(),
1175 );
1176 Ok(Some(current))
1177 }
1178 Expr::Closure { closure_kind, .. } => not_supported!("{closure_kind:?} closure"),
1179 Expr::Tuple { exprs } => {
1180 let Some(values) = exprs
1181 .iter()
1182 .map(|it| {
1183 let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)? else {
1184 return Ok(None);
1185 };
1186 current = c;
1187 Ok(Some(o))
1188 })
1189 .collect::<Result<'_, Option<_>>>()?
1190 else {
1191 return Ok(None);
1192 };
1193 let r = Rvalue::Aggregate(
1194 AggregateKind::Tuple(self.expr_ty_without_adjust(expr_id).store()),
1195 values,
1196 );
1197 self.push_assignment(current, place, r, expr_id.into());
1198 Ok(Some(current))
1199 }
1200 Expr::Array(l) => match l {
1201 Array::ElementList { elements, .. } => {
1202 let elem_ty = match self.expr_ty_without_adjust(expr_id).kind() {
1203 TyKind::Array(ty, _) => ty,
1204 _ => {
1205 return Err(MirLowerError::TypeError(
1206 "Array expression with non array type",
1207 ));
1208 }
1209 };
1210 let Some(values) = elements
1211 .iter()
1212 .map(|it| {
1213 let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)?
1214 else {
1215 return Ok(None);
1216 };
1217 current = c;
1218 Ok(Some(o))
1219 })
1220 .collect::<Result<'_, Option<_>>>()?
1221 else {
1222 return Ok(None);
1223 };
1224 let r = Rvalue::Aggregate(AggregateKind::Array(elem_ty.store()), values);
1225 self.push_assignment(current, place, r, expr_id.into());
1226 Ok(Some(current))
1227 }
1228 Array::Repeat { initializer, .. } => {
1229 let Some((init, current)) =
1230 self.lower_expr_to_some_operand(*initializer, current)?
1231 else {
1232 return Ok(None);
1233 };
1234 let len = match self.expr_ty_without_adjust(expr_id).kind() {
1235 TyKind::Array(_, len) => len,
1236 _ => {
1237 return Err(MirLowerError::TypeError(
1238 "Array repeat expression with non array type",
1239 ));
1240 }
1241 };
1242 let r = Rvalue::Repeat(init, len.store());
1243 self.push_assignment(current, place, r, expr_id.into());
1244 Ok(Some(current))
1245 }
1246 },
1247 Expr::Literal(l) => {
1248 let ty = self.expr_ty_without_adjust(expr_id);
1249 let op = self.lower_literal_to_operand(ty, l)?;
1250 self.push_assignment(current, place, op.into(), expr_id.into());
1251 Ok(Some(current))
1252 }
1253 Expr::Underscore => Ok(Some(current)),
1254 Expr::IncludeBytes => not_supported!("include_bytes!()"),
1255 }
1256 }
1257
1258 fn push_field_projection(
1259 &mut self,
1260 place: &mut Place<'db>,
1261 expr_id: ExprId,
1262 ) -> Result<'db, ()> {
1263 if let Expr::Field { expr, name } = &self.store[expr_id] {
1264 if let TyKind::Tuple(tys) = self.expr_ty_after_adjustments(*expr).kind() {
1265 let index =
1266 name.as_tuple_index().ok_or(MirLowerError::TypeError("named field on tuple"))?
1267 as u32;
1268 if tys.get(index as usize).is_none() {
1269 return Err(MirLowerError::TypeError("tuple field index out of range"));
1270 }
1271 *place = place.project(ProjectionElem::Field(FieldIndex(index)));
1272 } else {
1273 let field = self
1274 .infer
1275 .field_resolution(expr_id)
1276 .ok_or(MirLowerError::UnresolvedField)?
1277 .either(|f| f.local_id.into(), |t| FieldIndex(t.index));
1278 *place = place.project(ProjectionElem::Field(field));
1279 }
1280 } else {
1281 not_supported!("")
1282 }
1283 Ok(())
1284 }
1285
1286 fn lower_literal_or_const_to_operand(
1287 &mut self,
1288 ty: Ty<'db>,
1289 loc: &ExprId,
1290 ) -> Result<'db, Operand> {
1291 match &self.store[*loc] {
1292 Expr::Literal(l) => self.lower_literal_to_operand(ty, l),
1293 Expr::Path(c) => {
1294 let unresolved_name = || MirLowerError::UnresolvedName {
1295 owner: self.owner.expression_store_owner(self.db),
1296 path: c.clone(),
1297 };
1298 let pr = self
1299 .resolver
1300 .resolve_path_in_value_ns(self.db, c, HygieneId::ROOT)
1301 .ok_or_else(unresolved_name)?;
1302 match pr {
1303 ResolveValueResult::ValueNs(v) => {
1304 if let ValueNs::ConstId(c) = v {
1305 self.lower_const_to_operand(GenericArgs::empty(), c.into())
1306 } else {
1307 not_supported!("bad path in range pattern");
1308 }
1309 }
1310 ResolveValueResult::Partial(_, _) => {
1311 not_supported!("associated constants in range pattern")
1312 }
1313 }
1314 }
1315 _ => {
1316 not_supported!("only `char` and numeric types are allowed in range patterns");
1317 }
1318 }
1319 }
1320
1321 fn lower_literal_to_operand(&mut self, ty: Ty<'db>, l: &Literal) -> Result<'db, Operand> {
1322 let size = || {
1323 self.db
1324 .layout_of_ty(
1325 ty.store(),
1326 ParamEnvAndCrate { param_env: self.env, krate: self.krate() }.store(),
1327 )
1328 .map(|it| it.size.bytes_usize())
1329 };
1330 const USIZE_SIZE: usize = size_of::<usize>();
1331 let bytes: Box<[_]> = match l {
1332 hir_def::hir::Literal::String(b) => {
1333 let b = b.as_str();
1334 let mut data = [0; { 2 * USIZE_SIZE }];
1335 data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes());
1336 data[USIZE_SIZE..].copy_from_slice(&b.len().to_le_bytes());
1337 let mm = MemoryMap::simple(b.as_bytes().into());
1338 return Ok(Operand::from_concrete_const(Box::new(data), mm, ty));
1339 }
1340 hir_def::hir::Literal::CString(b) => {
1341 let bytes = b.iter().copied().chain(iter::once(0)).collect::<Box<_>>();
1342
1343 let mut data = [0; { 2 * USIZE_SIZE }];
1344 data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes());
1345 data[USIZE_SIZE..].copy_from_slice(&bytes.len().to_le_bytes());
1346 let mm = MemoryMap::simple(bytes);
1347 return Ok(Operand::from_concrete_const(Box::new(data), mm, ty));
1348 }
1349 hir_def::hir::Literal::ByteString(b) => {
1350 let mut data = [0; { 2 * USIZE_SIZE }];
1351 data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes());
1352 data[USIZE_SIZE..].copy_from_slice(&b.len().to_le_bytes());
1353 let mm = MemoryMap::simple(b.clone());
1354 return Ok(Operand::from_concrete_const(Box::new(data), mm, ty));
1355 }
1356 hir_def::hir::Literal::Char(c) => Box::new(u32::from(*c).to_le_bytes()),
1357 hir_def::hir::Literal::Bool(b) => Box::new([*b as u8]),
1358 hir_def::hir::Literal::Int(it, _) => Box::from(&it.to_le_bytes()[0..size()?]),
1359 hir_def::hir::Literal::Uint(it, _) => Box::from(&it.to_le_bytes()[0..size()?]),
1360 hir_def::hir::Literal::Float(f, _) => match size()? {
1361 16 => Box::new(f.to_f128().to_bits().to_le_bytes()),
1362 8 => Box::new(f.to_f64().to_bits().to_le_bytes()),
1363 4 => Box::new(f.to_f32().to_bits().to_le_bytes()),
1364 2 => Box::new(u16::try_from(f.to_f16().to_bits()).unwrap().to_le_bytes()),
1365 _ => {
1366 return Err(MirLowerError::TypeError(
1367 "float with size other than 2, 4, 8 or 16 bytes",
1368 ));
1369 }
1370 },
1371 };
1372 Ok(Operand::from_concrete_const(bytes, MemoryMap::default(), ty))
1373 }
1374
1375 fn new_basic_block(&mut self) -> BasicBlockId {
1376 self.result.basic_blocks.alloc(BasicBlock::default())
1377 }
1378
1379 fn lower_const(
1380 &mut self,
1381 const_id: GeneralConstId<'db>,
1382 prev_block: BasicBlockId,
1383 place: Place<'db>,
1384 subst: GenericArgs<'db>,
1385 span: MirSpan,
1386 ) -> Result<'db, ()> {
1387 let c = self.lower_const_to_operand(subst, const_id)?;
1388 self.push_assignment(prev_block, place, c.into(), span);
1389 Ok(())
1390 }
1391
1392 fn lower_const_to_operand(
1393 &mut self,
1394 subst: GenericArgs<'db>,
1395 const_id: GeneralConstId<'db>,
1396 ) -> Result<'db, Operand> {
1397 let konst = Const::new_unevaluated(
1398 self.interner(),
1399 UnevaluatedConst { def: const_id.into(), args: subst },
1400 );
1401 let ty = match const_id {
1402 GeneralConstId::ConstId(id) => self.db.value_ty(id.into()).unwrap(),
1403 GeneralConstId::StaticId(id) => self.db.value_ty(id.into()).unwrap(),
1404 GeneralConstId::AnonConstId(id) => id.loc(self.db).ty.get(),
1405 };
1406 let ty = ty.instantiate(self.interner(), subst).skip_norm_wip();
1407 Ok(Operand {
1408 kind: OperandKind::Constant { konst: konst.store(), ty: ty.store() },
1409 span: None,
1410 })
1411 }
1412
1413 fn write_bytes_to_place(
1414 &mut self,
1415 prev_block: BasicBlockId,
1416 place: Place<'db>,
1417 cv: Box<[u8]>,
1418 ty: Ty<'db>,
1419 span: MirSpan,
1420 ) -> Result<'db, ()> {
1421 self.push_assignment(prev_block, place, Operand::from_bytes(cv, ty).into(), span);
1422 Ok(())
1423 }
1424
1425 fn lower_enum_variant(
1426 &mut self,
1427 variant_id: EnumVariantId,
1428 prev_block: BasicBlockId,
1429 place: Place<'db>,
1430 ty: Ty<'db>,
1431 fields: Box<[Operand]>,
1432 span: MirSpan,
1433 ) -> Result<'db, BasicBlockId> {
1434 let subst = match ty.kind() {
1435 TyKind::Adt(_, subst) => subst,
1436 _ => implementation_error!("Non ADT enum"),
1437 };
1438 self.push_assignment(
1439 prev_block,
1440 place,
1441 Rvalue::Aggregate(AggregateKind::Adt(variant_id.into(), subst.store()), fields),
1442 span,
1443 );
1444 Ok(prev_block)
1445 }
1446
1447 fn lower_variant_discriminant(
1448 &mut self,
1449 current: BasicBlockId,
1450 variant_id: EnumVariantId,
1451 ) -> Result<'db, (Operand, Ty<'db>, BasicBlockId)> {
1452 let discriminant = self.const_eval_discriminant(variant_id)?;
1453 let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128);
1454 let operand = Operand::from_bytes(Box::new(discriminant.to_le_bytes()), discr_ty);
1455 Ok((operand, discr_ty, current))
1456 }
1457
1458 fn lower_call_and_args(
1459 &mut self,
1460 func: Operand,
1461 args: impl Iterator<Item = ExprId>,
1462 place: Place<'db>,
1463 mut current: BasicBlockId,
1464 is_uninhabited: bool,
1465 span: MirSpan,
1466 ) -> Result<'db, Option<BasicBlockId>> {
1467 let Some(args) = args
1468 .map(|arg| {
1469 if let Some((temp, c)) = self.lower_expr_to_some_operand(arg, current)? {
1470 current = c;
1471 Ok(Some(temp))
1472 } else {
1473 Ok(None)
1474 }
1475 })
1476 .collect::<Result<'_, Option<Vec<_>>>>()?
1477 else {
1478 return Ok(None);
1479 };
1480 self.lower_call(func, args.into(), place, current, is_uninhabited, span)
1481 }
1482
1483 fn lower_call(
1484 &mut self,
1485 func: Operand,
1486 args: Box<[Operand]>,
1487 place: Place<'db>,
1488 current: BasicBlockId,
1489 is_uninhabited: bool,
1490 span: MirSpan,
1491 ) -> Result<'db, Option<BasicBlockId>> {
1492 let b = if is_uninhabited { None } else { Some(self.new_basic_block()) };
1493 self.set_terminator(
1494 current,
1495 TerminatorKind::Call {
1496 func,
1497 args,
1498 destination: place.store(),
1499 target: b,
1500 cleanup: None,
1501 from_hir_call: true,
1502 },
1503 span,
1504 );
1505 Ok(b)
1506 }
1507
1508 fn is_unterminated(&mut self, source: BasicBlockId) -> bool {
1509 self.result.basic_blocks[source].terminator.is_none()
1510 }
1511
1512 fn set_terminator(&mut self, source: BasicBlockId, terminator: TerminatorKind, span: MirSpan) {
1513 self.result.basic_blocks[source].terminator = Some(Terminator { span, kind: terminator });
1514 }
1515
1516 fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId, span: MirSpan) {
1517 self.set_terminator(source, TerminatorKind::Goto { target }, span);
1518 }
1519
1520 fn expr_ty_without_adjust(&self, e: ExprId) -> Ty<'db> {
1521 self.infer.expr_ty(e)
1522 }
1523
1524 fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty<'db> {
1525 let mut ty = None;
1526 if let Some(it) = self.infer.expr_adjustments.get(&e)
1527 && let Some(it) = it.last()
1528 {
1529 ty = Some(it.target.as_ref());
1530 }
1531 ty.unwrap_or_else(|| self.expr_ty_without_adjust(e))
1532 }
1533
1534 fn push_statement(&mut self, block: BasicBlockId, statement: Statement) {
1535 self.result.basic_blocks[block].statements.push(statement);
1536 }
1537
1538 fn push_fake_read(&mut self, block: BasicBlockId, p: Place<'db>, span: MirSpan) {
1539 self.push_statement(block, StatementKind::FakeRead(p.store()).with_span(span));
1540 }
1541
1542 fn push_assignment(
1543 &mut self,
1544 block: BasicBlockId,
1545 place: Place<'db>,
1546 rvalue: Rvalue,
1547 span: MirSpan,
1548 ) {
1549 self.push_statement(block, StatementKind::Assign(place.store(), rvalue).with_span(span));
1550 }
1551
1552 fn discr_temp_place(&mut self, current: BasicBlockId) -> Place<'db> {
1553 match &self.discr_temp {
1554 Some(it) => it.as_ref(),
1555 None => {
1556 let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128);
1558 let tmp: Place<'_> = self
1559 .temp(discr_ty, current, MirSpan::Unknown)
1560 .expect("discr_ty is never unsized")
1561 .into();
1562 self.discr_temp = Some(tmp.store());
1563 tmp
1564 }
1565 }
1566 }
1567
1568 fn lower_loop(
1569 &mut self,
1570 prev_block: BasicBlockId,
1571 place: Place<'db>,
1572 label: Option<LabelId>,
1573 span: MirSpan,
1574 f: impl FnOnce(&mut MirLowerCtx<'_, 'db>, BasicBlockId) -> Result<'db, ()>,
1575 ) -> Result<'db, Option<BasicBlockId>> {
1576 let begin = self.new_basic_block();
1577 let prev = self.current_loop_blocks.replace(LoopBlocks {
1578 begin,
1579 end: None,
1580 place: place.store(),
1581 drop_scope_index: self.drop_scopes.len(),
1582 });
1583 let prev_label = if let Some(label) = label {
1584 self.current_loop_end()?;
1588 self.labeled_loop_blocks
1589 .insert(label, self.current_loop_blocks.as_ref().unwrap().clone())
1590 } else {
1591 None
1592 };
1593 self.set_goto(prev_block, begin, span);
1594 f(self, begin)?;
1595 let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
1596 MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_owned()),
1597 )?;
1598 if let Some(prev) = prev_label {
1599 self.labeled_loop_blocks.insert(label.unwrap(), prev);
1600 }
1601 Ok(my.end)
1602 }
1603
1604 fn has_adjustments(&self, expr_id: ExprId) -> bool {
1605 !self.infer.expr_adjustments.get(&expr_id).map(|it| it.is_empty()).unwrap_or(true)
1606 }
1607
1608 fn merge_blocks(
1609 &mut self,
1610 b1: Option<BasicBlockId>,
1611 b2: Option<BasicBlockId>,
1612 span: MirSpan,
1613 ) -> Option<BasicBlockId> {
1614 match (b1, b2) {
1615 (None, None) => None,
1616 (None, Some(b)) | (Some(b), None) => Some(b),
1617 (Some(b1), Some(b2)) => {
1618 let bm = self.new_basic_block();
1619 self.set_goto(b1, bm, span);
1620 self.set_goto(b2, bm, span);
1621 Some(bm)
1622 }
1623 }
1624 }
1625
1626 fn current_loop_end(&mut self) -> Result<'db, BasicBlockId> {
1627 let r = match self
1628 .current_loop_blocks
1629 .as_mut()
1630 .ok_or(MirLowerError::ImplementationError(
1631 "Current loop access out of loop".to_owned(),
1632 ))?
1633 .end
1634 {
1635 Some(it) => it,
1636 None => {
1637 let s = self.new_basic_block();
1638 self.current_loop_blocks
1639 .as_mut()
1640 .ok_or(MirLowerError::ImplementationError(
1641 "Current loop access out of loop".to_owned(),
1642 ))?
1643 .end = Some(s);
1644 s
1645 }
1646 };
1647 Ok(r)
1648 }
1649
1650 fn is_uninhabited(&self, expr_id: ExprId) -> bool {
1651 is_ty_uninhabited_from(
1652 &self.infcx,
1653 self.infer.expr_ty(expr_id),
1654 self.owner.module(self.db),
1655 self.env,
1656 )
1657 }
1658
1659 fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<'db, ()> {
1662 let l = self.binding_local(b)?;
1663 self.push_storage_live_for_local(l, current, MirSpan::BindingId(b))
1664 }
1665
1666 fn push_storage_live_for_local(
1667 &mut self,
1668 l: LocalId,
1669 current: BasicBlockId,
1670 span: MirSpan,
1671 ) -> Result<'db, ()> {
1672 self.drop_scopes.last_mut().unwrap().locals.push(l);
1673 self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
1674 Ok(())
1675 }
1676
1677 fn lower_block_to_place(
1678 &mut self,
1679 statements: &[hir_def::hir::Statement],
1680 mut current: BasicBlockId,
1681 tail: Option<ExprId>,
1682 place: Place<'db>,
1683 span: MirSpan,
1684 ) -> Result<'db, Option<Idx<BasicBlock>>> {
1685 let scope = self.push_drop_scope();
1686 for statement in statements.iter() {
1687 match statement {
1688 hir_def::hir::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
1689 if let Some(expr_id) = initializer {
1690 let else_block;
1691 let Some((init_place, c)) =
1692 self.lower_expr_as_place(current, *expr_id, true)?
1693 else {
1694 scope.pop_assume_dropped(self);
1695 return Ok(None);
1696 };
1697 current = c;
1698 self.push_fake_read(current, init_place, span);
1699 let resolver_guard = self.resolver.update_to_inner_scope(
1702 self.db,
1703 self.store_owner,
1704 *expr_id,
1705 );
1706 (current, else_block) =
1707 self.pattern_match(current, None, init_place, *pat)?;
1708 self.resolver.reset_to_guard(resolver_guard);
1709 match (else_block, else_branch) {
1710 (None, _) => (),
1711 (Some(else_block), None) => {
1712 self.set_terminator(else_block, TerminatorKind::Unreachable, span);
1713 }
1714 (Some(else_block), Some(else_branch)) => {
1715 if let Some((_, b)) =
1716 self.lower_expr_as_place(else_block, *else_branch, true)?
1717 {
1718 self.set_terminator(b, TerminatorKind::Unreachable, span);
1719 }
1720 }
1721 }
1722 } else {
1723 let mut err = None;
1724 self.store.walk_bindings_in_pat(*pat, |b| {
1725 if let Err(e) = self.push_storage_live(b, current) {
1726 err = Some(e);
1727 }
1728 });
1729 if let Some(e) = err {
1730 return Err(e);
1731 }
1732 }
1733 }
1734 &hir_def::hir::Statement::Expr { expr, has_semi: _ } => {
1735 let scope2 = self.push_drop_scope();
1736 let Some((p, c)) = self.lower_expr_as_place(current, expr, true)? else {
1737 scope2.pop_assume_dropped(self);
1738 scope.pop_assume_dropped(self);
1739 return Ok(None);
1740 };
1741 self.push_fake_read(c, p, expr.into());
1742 current = scope2.pop_and_drop(self, c, expr.into());
1743 }
1744 hir_def::hir::Statement::Item(_) => (),
1745 }
1746 }
1747 if let Some(tail) = tail {
1748 let Some(c) = self.lower_expr_to_place(tail, place, current)? else {
1749 scope.pop_assume_dropped(self);
1750 return Ok(None);
1751 };
1752 current = c;
1753 }
1754 current = scope.pop_and_drop(self, current, span);
1755 Ok(Some(current))
1756 }
1757
1758 fn lower_params_and_bindings(
1759 &mut self,
1760 params: impl Iterator<Item = (PatId, Ty<'db>)> + Clone,
1761 self_binding: Option<(BindingId, Ty<'db>)>,
1762 pick_binding: impl Fn(BindingId) -> bool,
1763 ) -> Result<'db, BasicBlockId> {
1764 let base_param_count = self.result.param_locals.len();
1765 let self_binding = match self_binding {
1766 Some((self_binding, ty)) => {
1767 let local_id = self.result.locals.alloc(Local { ty: ty.store() });
1768 self.drop_scopes.last_mut().unwrap().locals.push(local_id);
1769 self.result.binding_locals.insert(self_binding, local_id);
1770 self.result.param_locals.push(local_id);
1771 Some(self_binding)
1772 }
1773 None => None,
1774 };
1775 self.result.param_locals.extend(params.clone().map(|(it, ty)| {
1776 let local_id = self.result.locals.alloc(Local { ty: ty.store() });
1777 self.drop_scopes.last_mut().unwrap().locals.push(local_id);
1778 if let Pat::Bind { id, subpat: None } = self.store[it]
1779 && matches!(
1780 self.store[id].mode,
1781 BindingAnnotation::Unannotated | BindingAnnotation::Mutable
1782 )
1783 {
1784 self.result.binding_locals.insert(id, local_id);
1785 }
1786 local_id
1787 }));
1788 for (id, _) in self.store.bindings() {
1790 if !pick_binding(id) {
1791 continue;
1792 }
1793 if !self.result.binding_locals.contains_idx(id) {
1794 self.result.binding_locals.insert(
1795 id,
1796 self.result.locals.alloc(Local { ty: self.infer.binding_ty(id).store() }),
1797 );
1798 }
1799 }
1800 let mut current = self.result.start_block;
1801 if let Some(self_binding) = self_binding {
1802 let local = self.result.param_locals.clone()[base_param_count];
1803 if local != self.binding_local(self_binding)? {
1804 let r = self.match_self_param(self_binding, current, local)?;
1805 if let Some(b) = r.1 {
1806 self.set_terminator(b, TerminatorKind::Unreachable, MirSpan::SelfParam);
1807 }
1808 current = r.0;
1809 }
1810 }
1811 let local_params = self
1812 .result
1813 .param_locals
1814 .clone()
1815 .into_iter()
1816 .skip(base_param_count + self_binding.is_some() as usize);
1817 for ((param, _), local) in params.zip(local_params) {
1818 if let Pat::Bind { id, .. } = self.store[param]
1819 && local == self.binding_local(id)?
1820 {
1821 continue;
1822 }
1823 let r = self.pattern_match(current, None, local.into(), param)?;
1824 if let Some(b) = r.1 {
1825 self.set_terminator(b, TerminatorKind::Unreachable, param.into());
1826 }
1827 current = r.0;
1828 }
1829 Ok(current)
1830 }
1831
1832 fn binding_local(&self, b: BindingId) -> Result<'db, LocalId> {
1833 match self.result.binding_locals.get(b) {
1834 Some(it) => Ok(*it),
1835 None => {
1836 Err(MirLowerError::InaccessibleLocal)
1839 }
1840 }
1841 }
1842
1843 fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<'db, i128> {
1844 let r = self.db.const_eval_discriminant(variant);
1845 match r {
1846 Ok(r) => Ok(r),
1847 Err(e) => {
1848 let edition = self.edition();
1849 let db = self.db;
1850 let loc = variant.lookup(db);
1851 let name = format!(
1852 "{}::{}",
1853 EnumSignature::of(db, loc.parent).name.display(db, edition),
1854 loc.parent
1855 .enum_variants(self.db)
1856 .variant_name_by_id(variant)
1857 .unwrap()
1858 .display(db, edition),
1859 );
1860 Err(MirLowerError::ConstEvalError(name.into(), Box::new(e)))
1861 }
1862 }
1863 }
1864
1865 fn edition(&self) -> Edition {
1866 self.krate().data(self.db).edition
1867 }
1868
1869 fn krate(&self) -> Crate {
1870 self.owner.krate(self.db)
1871 }
1872
1873 fn drop_until_scope(
1874 &mut self,
1875 scope_index: usize,
1876 mut current: BasicBlockId,
1877 span: MirSpan,
1878 ) -> BasicBlockId {
1879 for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() {
1880 self.emit_drop_and_storage_dead_for_scope(scope, &mut current, span);
1881 }
1882 current
1883 }
1884
1885 fn push_drop_scope(&mut self) -> DropScopeToken {
1886 self.drop_scopes.push(DropScope::default());
1887 DropScopeToken
1888 }
1889
1890 fn pop_drop_scope_assume_dropped_internal(&mut self) {
1892 self.drop_scopes.pop();
1893 }
1894
1895 fn pop_drop_scope_internal(
1897 &mut self,
1898 mut current: BasicBlockId,
1899 span: MirSpan,
1900 ) -> BasicBlockId {
1901 let scope = self.drop_scopes.pop().unwrap();
1902 self.emit_drop_and_storage_dead_for_scope(&scope, &mut current, span);
1903 current
1904 }
1905
1906 fn pop_drop_scope_assert_finished(
1907 &mut self,
1908 mut current: BasicBlockId,
1909 span: MirSpan,
1910 ) -> Result<'db, BasicBlockId> {
1911 current = self.pop_drop_scope_internal(current, span);
1912 if !self.drop_scopes.is_empty() {
1913 implementation_error!("Mismatched count between drop scope push and pops");
1914 }
1915 Ok(current)
1916 }
1917
1918 fn emit_drop_and_storage_dead_for_scope(
1919 &mut self,
1920 scope: &DropScope,
1921 current: &mut Idx<BasicBlock>,
1922 span: MirSpan,
1923 ) {
1924 for &l in scope.locals.iter().rev() {
1925 if !self.infcx.type_is_copy_modulo_regions(self.env, self.result.locals[l].ty.as_ref())
1926 {
1927 let prev = std::mem::replace(current, self.new_basic_block());
1928 self.set_terminator(
1929 prev,
1930 TerminatorKind::Drop {
1931 place: Place::from(l).store(),
1932 target: *current,
1933 unwind: None,
1934 },
1935 span,
1936 );
1937 }
1938 self.push_statement(*current, StatementKind::StorageDead(l).with_span(span));
1939 }
1940 }
1941}
1942
1943fn convert_closure_capture_projections(
1944 _db: &dyn HirDatabase,
1945 place: &HirPlace,
1946) -> impl Iterator<Item = PlaceElem> {
1947 place.projections.iter().enumerate().flat_map(|(i, proj)| {
1948 let mut projections = ArrayVec::<PlaceElem, 2>::new();
1949 match proj.kind {
1950 HirProjectionKind::Deref => projections.push(ProjectionElem::Deref),
1951 HirProjectionKind::Field { field_idx, variant_idx } => {
1952 let ty = place.ty_before_projection(i);
1953 match ty.kind() {
1954 TyKind::Tuple(_) => {
1955 projections.push(ProjectionElem::Field(FieldIndex(field_idx)))
1956 }
1957 TyKind::Adt(adt_def, _) => {
1958 if let AdtId::EnumId(enum_id) = adt_def.def_id() {
1959 let variant =
1960 enum_id.enum_variants(_db).variants[variant_idx as usize].0.into();
1961 projections.push(ProjectionElem::Downcast(variant));
1962 }
1963 let local_field_id = LocalFieldId::from_raw(RawIdx::from_u32(field_idx));
1964 projections.push(ProjectionElem::Field(local_field_id.into()));
1965 }
1966 _ => panic!("unexpected type"),
1967 }
1968 }
1969 _ => panic!("unexpected projection"),
1970 }
1971 projections.into_iter()
1972 })
1973}
1974
1975fn cast_kind<'db>(
1976 db: &'db dyn HirDatabase,
1977 source_ty: Ty<'db>,
1978 target_ty: Ty<'db>,
1979) -> Result<'db, CastKind> {
1980 let from = CastTy::from_ty(db, source_ty);
1981 let cast = CastTy::from_ty(db, target_ty);
1982 Ok(match (from, cast) {
1983 (Some(CastTy::Ptr(..) | CastTy::FnPtr), Some(CastTy::Int(_))) => {
1984 CastKind::PointerExposeAddress
1985 }
1986 (Some(CastTy::Int(_)), Some(CastTy::Ptr(..))) => CastKind::PointerFromExposedAddress,
1987 (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => CastKind::IntToInt,
1988 (Some(CastTy::FnPtr), Some(CastTy::Ptr(..))) => CastKind::FnPtrToPtr,
1989 (Some(CastTy::Float), Some(CastTy::Int(_))) => CastKind::FloatToInt,
1990 (Some(CastTy::Int(_)), Some(CastTy::Float)) => CastKind::IntToFloat,
1991 (Some(CastTy::Float), Some(CastTy::Float)) => CastKind::FloatToFloat,
1992 (Some(CastTy::Ptr(..)), Some(CastTy::Ptr(..))) => CastKind::PtrToPtr,
1993 _ => not_supported!("Unknown cast between {source_ty:?} and {target_ty:?}"),
1994 })
1995}
1996
1997#[salsa::tracked(returns(as_ref), cycle_result = mir_body_for_closure_cycle_result)]
1998pub fn mir_body_for_closure_query<'db>(
1999 db: &'db dyn HirDatabase,
2000 closure: InternedClosureId<'db>,
2001) -> Result<'db, MirBody<'db>> {
2002 let InternedClosure { owner: body_owner, expr, .. } = closure.loc(db);
2003 let store = ExpressionStore::of(db, body_owner.expression_store_owner(db));
2004 let infer = InferenceResult::of(db, body_owner);
2005 let Expr::Closure { args, body: root, .. } = &store[expr] else {
2006 implementation_error!("closure expression is not closure");
2007 };
2008 let crate::next_solver::TyKind::Closure(_, substs) = infer.expr_ty(expr).kind() else {
2009 implementation_error!("closure expression is not closure");
2010 };
2011 let kind = substs.as_closure().kind();
2012 let captures = infer.closures_data[&expr].min_captures.values().flatten();
2013 let mut ctx = MirLowerCtx::new(db, body_owner, store, infer);
2014
2015 ctx.result.locals.alloc(Local { ty: infer.expr_ty(*root).store() });
2017 let closure_local = ctx.result.locals.alloc(Local {
2018 ty: match kind {
2019 rustc_type_ir::ClosureKind::FnOnce => infer.expr_ty(expr),
2020 rustc_type_ir::ClosureKind::FnMut => Ty::new_ref(
2021 ctx.interner(),
2022 Region::error(ctx.interner()),
2023 infer.expr_ty(expr),
2024 Mutability::Mut,
2025 ),
2026 rustc_type_ir::ClosureKind::Fn => Ty::new_ref(
2027 ctx.interner(),
2028 Region::error(ctx.interner()),
2029 infer.expr_ty(expr),
2030 Mutability::Not,
2031 ),
2032 }
2033 .store(),
2034 });
2035 ctx.result.param_locals.push(closure_local);
2036 let sig = infer.closures_data[&expr].liberated_sig.get();
2037 let resolver_guard = ctx.resolver.update_to_inner_scope(db, ctx.store_owner, expr);
2038 let current = ctx.lower_params_and_bindings(
2039 args.iter().zip(sig.inputs().iter()).map(|(it, y)| (*it, *y)),
2040 None,
2041 |_| true,
2042 )?;
2043
2044 let is_by_ref_closure = match kind {
2047 rustc_type_ir::ClosureKind::Fn | rustc_type_ir::ClosureKind::FnMut => true,
2048 rustc_type_ir::ClosureKind::FnOnce => false,
2049 };
2050 let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedPlace, LocalId)>> = FxHashMap::default();
2051 for (capture_idx, capture) in captures.enumerate() {
2052 let capture_local = ctx.result.locals.alloc(Local { ty: capture.captured_ty(db).store() });
2053 ctx.push_storage_live_for_local(capture_local, current, MirSpan::Unknown)?;
2054 let mut projections = Vec::with_capacity(usize::from(is_by_ref_closure) + 1);
2055 if is_by_ref_closure {
2056 projections.push(ProjectionElem::Deref);
2057 }
2058 projections.push(ProjectionElem::Field(FieldIndex(capture_idx as u32)));
2059 let capture_param_place = StoredPlace {
2060 local: closure_local,
2061 projection: Projection::new_from_slice(&projections).store(),
2062 };
2063 let capture_local_place = StoredPlace {
2064 local: capture_local,
2065 projection: Projection::new_from_slice(&[]).store(),
2066 };
2067 let capture_local_rvalue =
2068 Rvalue::Use(Operand { kind: OperandKind::Move(capture_param_place), span: None });
2069 ctx.push_assignment(
2070 current,
2071 capture_local_place.as_ref(),
2072 capture_local_rvalue,
2073 MirSpan::Unknown,
2074 );
2075
2076 let local = capture.captured_local();
2077 let local = ctx.binding_local(local)?;
2078 upvar_map.entry(local).or_default().push((capture, capture_local));
2079
2080 ctx.result
2081 .upvar_locals
2082 .entry(capture.captured_local())
2083 .or_default()
2084 .push((capture_local, capture.place.clone()));
2085 }
2086
2087 ctx.resolver.reset_to_guard(resolver_guard);
2088 if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
2089 let current = ctx.pop_drop_scope_assert_finished(current, root.into())?;
2090 ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
2091 }
2092 let mut err = None;
2093 ctx.result.walk_places(|mir_place| {
2094 let mir_projections = mir_place.projection.as_slice();
2095 if let Some(hir_places) = upvar_map.get(&mir_place.local) {
2096 let projections = hir_places.iter().find_map(|hir_place| {
2097 let iter = mir_projections
2098 .iter()
2099 .cloned()
2100 .zip_longest(convert_closure_capture_projections(db, &hir_place.0.place))
2101 .enumerate();
2102
2103 for (idx, item) in iter {
2104 match item {
2105 EitherOrBoth::Both(mir, hir) => {
2106 if mir != hir {
2107 return None;
2109 }
2110 }
2111 EitherOrBoth::Right(_) => {
2112 return Some((mir_projections.len(), hir_place));
2119 }
2120 EitherOrBoth::Left(_) => return Some((idx, hir_place)),
2122 }
2123 }
2124 Some((hir_place.0.place.projections.len(), hir_place))
2126 });
2127 match projections {
2128 Some((skip_projections_up_to, (hir_place, upvar_local))) => {
2129 mir_place.local = *upvar_local;
2130 let maybe_deref: &[PlaceElem] =
2131 if hir_place.is_by_ref() { &[ProjectionElem::Deref] } else { &[] };
2132 mir_place.projection = Projection::new_from_iter(
2133 maybe_deref
2134 .iter()
2135 .copied()
2136 .chain(mir_projections[skip_projections_up_to..].iter().copied()),
2137 )
2138 .store();
2139 }
2140 None => err = Some(mir_place.clone()),
2141 }
2142 }
2143 });
2144 ctx.result.binding_locals = ctx
2145 .result
2146 .binding_locals
2147 .into_iter()
2148 .filter(|it| ctx.store.binding_owner(it.0) == Some(expr))
2149 .collect();
2150 if let Some(err) = err {
2151 return Err(MirLowerError::UnresolvedUpvar(err));
2152 }
2153 ctx.result.shrink_to_fit();
2154 Ok(ctx.result)
2155}
2156
2157#[salsa::tracked(returns(as_ref), cycle_result = mir_body_cycle_result)]
2158pub fn mir_body_query<'db>(
2159 db: &'db dyn HirDatabase,
2160 def: InferBodyId<'db>,
2161) -> Result<'db, MirBody<'db>> {
2162 let krate = def.krate(db);
2163 let edition = krate.data(db).edition;
2164 let detail = match def {
2165 InferBodyId::DefWithBodyId(DefWithBodyId::FunctionId(it)) => {
2166 FunctionSignature::of(db, it).name.display(db, edition).to_string()
2167 }
2168 InferBodyId::DefWithBodyId(DefWithBodyId::StaticId(it)) => {
2169 StaticSignature::of(db, it).name.display(db, edition).to_string()
2170 }
2171 InferBodyId::DefWithBodyId(DefWithBodyId::ConstId(it)) => ConstSignature::of(db, it)
2172 .name
2173 .clone()
2174 .unwrap_or_else(Name::missing)
2175 .display(db, edition)
2176 .to_string(),
2177 InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(it)) => {
2178 let loc = it.lookup(db);
2179 loc.name.display(db, edition).to_string()
2180 }
2181 InferBodyId::AnonConstId(_) => "{const}".to_owned(),
2182 };
2183 let _p = tracing::info_span!("mir_body_query", ?detail).entered();
2184 let (store, root_expr, self_param, params) = match def {
2185 InferBodyId::DefWithBodyId(def) => {
2186 let body = Body::of(db, def);
2187 (&**body, body.root_expr(), body.self_param.map(|param| param.formal), &*body.params)
2188 }
2189 InferBodyId::AnonConstId(def) => {
2190 let loc = def.loc(db);
2191 let store = ExpressionStore::of(db, loc.owner);
2192 (store, loc.expr, None, &[][..])
2193 }
2194 };
2195 let infer = InferenceResult::of(db, def);
2196 let mut result = lower_body_to_mir(db, def, store, infer, root_expr, self_param, params)?;
2197 result.shrink_to_fit();
2198 Ok(result)
2199}
2200
2201fn mir_body_cycle_result<'db>(
2202 _db: &'db dyn HirDatabase,
2203 _: salsa::Id,
2204 _def: InferBodyId<'db>,
2205) -> Result<'db, MirBody<'db>> {
2206 Err(MirLowerError::Loop)
2207}
2208
2209fn mir_body_for_closure_cycle_result<'db>(
2210 _db: &'db dyn HirDatabase,
2211 _: salsa::Id,
2212 _def: InternedClosureId<'db>,
2213) -> Result<'db, MirBody<'db>> {
2214 Err(MirLowerError::Loop)
2215}
2216
2217pub fn lower_body_to_mir<'db>(
2220 db: &'db dyn HirDatabase,
2221 owner: InferBodyId<'db>,
2222 store: &ExpressionStore,
2223 infer: &InferenceResult<'db>,
2224 root_expr: ExprId,
2225 self_param: Option<BindingId>,
2226 params: &[Param<PatId>],
2227) -> Result<'db, MirBody<'db>> {
2228 if let Some(fid) = owner.as_function() {
2230 let callable_sig = {
2231 let resolver = owner.resolver(db);
2232 let interner = DbInterner::new_with(db, resolver.krate());
2233 interner.liberate_late_bound_regions(
2234 fid.into(),
2235 db.callable_item_signature(fid.into()).instantiate_identity().skip_norm_wip(),
2236 )
2237 };
2238 let mut param_tys = callable_sig.inputs().iter().copied();
2239 let self_param = self_param.and_then(|id| Some((id, param_tys.next()?)));
2240
2241 lower_to_mir_with_store(
2242 db,
2243 owner,
2244 store,
2245 infer,
2246 root_expr,
2247 params.iter().map(|param| param.formal).zip(param_tys),
2248 self_param,
2249 )
2250 } else {
2251 lower_to_mir_with_store(db, owner, store, infer, root_expr, iter::empty(), None)
2252 }
2253}
2254
2255pub fn lower_to_mir_with_store<'db>(
2260 db: &'db dyn HirDatabase,
2261 owner: InferBodyId<'db>,
2262 store: &ExpressionStore,
2263 infer: &InferenceResult<'db>,
2264 root_expr: ExprId,
2265 params: impl Iterator<Item = (PatId, Ty<'db>)> + Clone,
2266 self_param: Option<(BindingId, Ty<'db>)>,
2267) -> Result<'db, MirBody<'db>> {
2268 if infer.has_type_mismatches() || infer.is_erroneous() {
2269 return Err(MirLowerError::HasErrors);
2270 }
2271 let mut ctx = MirLowerCtx::new(db, owner, store, infer);
2272 ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr).store() });
2274 let expected_binding_owner =
2275 if matches!(owner, InferBodyId::DefWithBodyId(_)) { None } else { Some(root_expr) };
2276 let binding_picker = |b: BindingId| ctx.store.binding_owner(b) == expected_binding_owner;
2277 let current = ctx.lower_params_and_bindings(params, self_param, binding_picker)?;
2278 if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
2279 let current = ctx.pop_drop_scope_assert_finished(current, root_expr.into())?;
2280 ctx.set_terminator(current, TerminatorKind::Return, root_expr.into());
2281 }
2282 Ok(ctx.result)
2283}