Skip to main content

hir_ty/mir/
lower.rs

1//! This module generates a polymorphic MIR from a hir body
2
3use 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    /// `None` for loops that are not terminating
67    end: Option<BasicBlockId>,
68    place: StoredPlace,
69    drop_scope_index: usize,
70}
71
72#[derive(Debug, Clone, Default)]
73struct DropScope {
74    /// locals, in order of definition (so we should run drop glues in reverse order)
75    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// FIXME: Make this smaller, its stored in database queries
96#[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    /// Trying to lower a trait function, instead of an implementation
103    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    /// This should never happen. Type mismatch should catch everything.
115    TypeError(&'static str),
116    NotSupported(String),
117    ContinueWithoutLoop,
118    BreakWithoutLoop,
119    Loop,
120    /// Something that should never happen and is definitely a bug, but we don't want to panic if it happened
121    ImplementationError(String),
122    LangItemNotFound,
123    MutatingRvalue,
124    UnresolvedLabel,
125    UnresolvedUpvar(StoredPlace),
126    InaccessibleLocal,
127
128    // monomorphization errors:
129    GenericArgNotProvided(GenericParamId, StoredGenericArgs),
130}
131
132/// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves.
133struct 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    /// It is useful when we want a drop scope is syntactically closed, but we don't want to execute any drop
146    /// code. Either when the control flow is diverging (so drop code doesn't reached) or when drop is handled
147    /// for us (for example a block that ended with a return statement. Return will drop everything, so the block shouldn't
148    /// do anything)
149    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
159// Uncomment this to make `DropScopeToken` a drop bomb. Unfortunately we can't do this in release, since
160// in cases that mir lowering fails, we don't handle (and don't need to handle) drop scopes so it will be
161// actually reached. `pop_drop_scope_assert_finished` will also detect this case, but doesn't show useful
162// stack trace.
163//
164// impl Drop for DropScopeToken {
165//     fn drop(&mut self) {
166//         never!("Drop scope doesn't popped");
167//     }
168// }
169
170macro_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        // FIXME(next-solver): Is `non_body_analysis()` correct here? Don't we want to reveal opaque types defined by this body?
221        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                                // FnDefs are zero sized, no action is needed.
403                                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                        // Otherwise its a tuple like enum, treated like a zero sized function, so no action is needed
465                        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                        // It's probably a unit struct or a zero sized function, so no action is needed.
491                        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                            // The current pattern was irrefutable, so there is no need to generate code
690                            // for the rest of patterns
691                            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                // Inline const blocks (`const { .. }`) are stored with their inner expression in
856                // the same body (see inference, which infers the inner expression directly), so we
857                // lower that expression in place. Const-ness is irrelevant here: MIR evaluation
858                // already runs in a const context.
859                self.lower_expr_to_place(id, place, current)
860            }
861            Expr::Cast { expr, type_ref: _ } => {
862                // Since we don't have THIR, this is the "zipped" version of [rustc's HIR lowering](https://github.com/rust-lang/rust/blob/e71f9529121ca8f687e4b725e3c9adc3f1ebab4d/compiler/rustc_mir_build/src/thir/cx/expr.rs#L165-L178)
863                // and [THIR lowering as RValue](https://github.com/rust-lang/rust/blob/a4601859ae3875732797873612d424976d9e3dd0/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs#L193-L313)
864                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                    // Without adjust here is a hack. We assume that we know every possible adjustment
971                    // for binary operator, and use without adjust to simplify our conditions.
972                    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                    // last adjustment is `&mut` which we don't want it.
1016                    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, // FIXME: make these short circuit
1084                                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!(), // handled above
1089                        },
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                            // FIXME: Handle more than one span.
1153                            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                // FIXME: rustc's ty is dependent on the adt type, maybe we need to do that as well
1557                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            // We should generate the end now, to make sure that it wouldn't change later. It is
1585            // bad as we may emit end (unnecessary unreachable block) for unterminating loop, but
1586            // it should not affect correctness.
1587            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    /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and
1660    /// `Drop` in the appropriated places.
1661    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                        // Using the initializer for the resolver scope is good enough for us, as it cannot create new declarations
1700                        // and has all declarations of the `let`.
1701                        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        // and then rest of bindings
1789        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                // FIXME: It should never happens, but currently it will happen in some cases, not sure when exactly.
1837                // never!("Using inaccessible local for binding is always a bug");
1838                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    /// Don't call directly
1891    fn pop_drop_scope_assume_dropped_internal(&mut self) {
1892        self.drop_scopes.pop();
1893    }
1894
1895    /// Don't call directly
1896    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    // 0 is return local
2016    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    // Push local for every upvar in the closure. rustc doesn't do that, but we have to so we have locals
2045    // to associate with upvars for borrowck.
2046    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                                // Not this place.
2108                                return None;
2109                            }
2110                        }
2111                        EitherOrBoth::Right(_) => {
2112                            // FIXME: This can happen in fake reads. I believe this is a bug. So we change the fake read's meaning.
2113                            // never!(
2114                            //     "mir upvar place shorter than hir upvar place; this should not happen, \
2115                            //         capture analysis should have picked the shorter place"
2116                            // );
2117                            // return None;
2118                            return Some((mir_projections.len(), hir_place));
2119                        }
2120                        // This place, but truncated.
2121                        EitherOrBoth::Left(_) => return Some((idx, hir_place)),
2122                    }
2123                }
2124                // Exactly this place.
2125                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
2217/// Extracts params from `body.params`/`body.self_param` and the callable signature,
2218/// then delegates to [`lower_to_mir_with_store`].
2219pub 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    // Extract params and self_param only when lowering the body's root expression for a function.
2229    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
2255/// # Parameters
2256/// - `is_root`: `true` when `root_expr` is the body's top-level expression (picks
2257///   bindings with no owner); `false` when lowering an inline const or anonymous
2258///   const (picks bindings owned by `root_expr`).
2259pub 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    // 0 is return local
2273    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}