Skip to main content

hir_ty/mir/
lower.rs

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