flux_rustc_bridge/
mir.rs

1//! A simplified version of rust mir.
2
3use std::{fmt, rc::Rc};
4
5use flux_arc_interner::List;
6use flux_common::index::{Idx, IndexVec};
7use itertools::Itertools;
8pub use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx};
9use rustc_borrowck::consumers::{BodyWithBorrowckFacts, BorrowData, BorrowIndex};
10use rustc_data_structures::{
11    fx::FxIndexMap,
12    graph::{self, DirectedGraph, StartNode, dominators::Dominators},
13    unord::UnordMap,
14};
15use rustc_hir::def_id::DefId;
16use rustc_index::IndexSlice;
17use rustc_macros::{TyDecodable, TyEncodable};
18use rustc_middle::mir::{Promoted, VarDebugInfoContents};
19pub use rustc_middle::{
20    mir::{
21        BasicBlock, BorrowKind, FakeBorrowKind, FakeReadCause, Local, LocalKind, Location,
22        RETURN_PLACE, RawPtrKind, START_BLOCK, SourceInfo, SwitchTargets, UnOp, UnwindAction,
23    },
24    ty::{UserTypeAnnotationIndex, Variance},
25};
26use rustc_span::{Span, Symbol};
27
28use super::ty::{Const, GenericArg, GenericArgs, Region, Ty};
29use crate::{
30    def_id_to_string,
31    ty::{Binder, FnSig, region_to_string},
32};
33
34pub struct BodyRoot<'tcx> {
35    pub body: Body<'tcx>,
36    pub promoted: IndexVec<Promoted, Body<'tcx>>,
37    /// During borrow checking, `rustc` generates fresh [region variable ids] for each structurally
38    /// different position in a type. For example, given a function
39    ///
40    /// `fn foo<'a, 'b>(x: &'a S<'a>, y: &'b u32)`
41    ///
42    /// `rustc` will generate variables `?2` and `?3` for the universal regions `'a` and `'b` (the variable
43    /// `?0` correspond to `'static` and `?1` to the implicit lifetime of the function body). Additionally,
44    /// it will assign `x` type &'?4 S<'?5>` and `y` type `&'?6 u32` (together with some constraints relating
45    /// region variables). Unfortunately, we cannot recover the exact region variables rustc used.
46    ///
47    /// The exact ids picked for `'a` and `'b` are not too relevant to us, the important part is the regions
48    /// used in the types of `x` and `y`. To work around this, we generate fresh regions variables for
49    /// the function signature, different from the ones sued by rustc. To recover the correct regions, whenever
50    /// there's an assignment of a refinement type `T` to a variable with (unrefined) Rust type `S`, we _match_
51    /// both types to infer a region substitution. For this to work, we need to give a different variable id to every
52    /// position in `T`. To avoid clashes, we need to use fresh ids, so we start enumerating from the last id
53    /// generated by borrow checking.
54    ///
55    /// To do that, we replicate the [`InferCtxt`] use for mir typeck by generating region variables for every
56    /// region in the `RegionInferenceContext`. The [`InferCtxt`] is then used to generate new region variables.
57    ///
58    /// The ids generated during refinement type checking are purely instrumental and temporary, they should never
59    /// appear in a type bound in the environment.
60    ///
61    /// Besides generating ids when checking a function's body, we also need to generate fresh ids at
62    /// function calls.
63    ///
64    /// Additionally, the [`InferCtxt`] is used during type projection normalization.
65    ///
66    /// [region variable ids]: super::ty::RegionVid
67    /// [`InferCtxt`]: rustc_infer::infer::InferCtxt
68    pub infcx: rustc_infer::infer::InferCtxt<'tcx>,
69    /// The borrowck facts (`rustc_body`, `borrow_set`, `region_inference_context`, ...) backing
70    /// this body. Shared via [`Rc`] so the same stashed body can be lowered by the checker and
71    /// walked by the no-panic call-graph provider without being cloned or moved out. The lowered
72    /// [`Body`]s in `body`/`promoted` keep their own clone of this `Rc` and recover their
73    /// corresponding [`rustc_middle::mir::Body`] through it.
74    facts: Rc<BodyWithBorrowckFacts<'tcx>>,
75}
76
77impl<'tcx> BodyRoot<'tcx> {
78    pub fn body(&self) -> &Body<'tcx> {
79        &self.body
80    }
81
82    pub fn calculate_borrows_out_of_scope_at_location(
83        &self,
84    ) -> FxIndexMap<Location, Vec<BorrowIndex>> {
85        rustc_borrowck::consumers::calculate_borrows_out_of_scope_at_location(
86            self.body.rustc_body(),
87            &self.facts.region_inference_context,
88            &self.facts.borrow_set,
89        )
90    }
91
92    pub fn borrow_data(&self, idx: BorrowIndex) -> &BorrowData<'tcx> {
93        self.facts
94            .borrow_set
95            .location_map()
96            .get_index(idx.as_usize())
97            .unwrap()
98            .1
99    }
100}
101
102pub struct Body<'tcx> {
103    pub basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
104    pub local_decls: IndexVec<Local, LocalDecl>,
105    pub dominator_order_rank: IndexVec<BasicBlock, u32>,
106    /// See [`mk_fake_predecessors`]
107    fake_predecessors: IndexVec<BasicBlock, usize>,
108    pub local_names: UnordMap<Local, Symbol>,
109    /// The borrowck facts backing the whole [`BodyRoot`]. The unrefined [`rustc_middle::mir::Body`]
110    /// for *this* body (which contains region identifiers) lives inside, selected by `kind`. Access
111    /// it through [`Body::rustc_body`].
112    facts: Rc<BodyWithBorrowckFacts<'tcx>>,
113    /// Identifies which [`rustc_middle::mir::Body`] inside `facts` this body corresponds to.
114    kind: BodyKind,
115}
116
117/// Selects a particular [`rustc_middle::mir::Body`] within a [`BodyWithBorrowckFacts`].
118#[derive(Clone, Copy)]
119pub enum BodyKind {
120    /// The main body, i.e. `facts.body`.
121    Main,
122    /// The promoted constant at the given index, i.e. `facts.promoted[_]`.
123    Promoted(Promoted),
124}
125
126impl BodyKind {
127    /// The [`rustc_middle::mir::Body`] this kind selects out of `facts`.
128    pub fn select_body<'a, 'tcx>(
129        self,
130        facts: &'a BodyWithBorrowckFacts<'tcx>,
131    ) -> &'a rustc_middle::mir::Body<'tcx> {
132        match self {
133            BodyKind::Main => &facts.body,
134            BodyKind::Promoted(promoted) => &facts.promoted[promoted],
135        }
136    }
137}
138
139#[derive(Debug)]
140pub struct BasicBlockData<'tcx> {
141    pub statements: Vec<Statement<'tcx>>,
142    pub terminator: Option<Terminator<'tcx>>,
143    pub is_cleanup: bool,
144}
145
146pub type LocalDecls = IndexSlice<Local, LocalDecl>;
147
148#[derive(Clone, Debug)]
149pub struct LocalDecl {
150    pub ty: Ty,
151    pub source_info: SourceInfo,
152}
153
154pub struct Terminator<'tcx> {
155    pub kind: TerminatorKind<'tcx>,
156    pub source_info: SourceInfo,
157}
158
159#[derive(Debug)]
160pub struct CallArgs<'tcx> {
161    pub orig: rustc_middle::ty::GenericArgsRef<'tcx>,
162    pub lowered: List<GenericArg>,
163}
164
165/// An `Instance` is the resolved call-target at a particular trait-call-site
166#[derive(Debug)]
167pub struct Instance {
168    pub impl_f: DefId,
169    pub args: GenericArgs,
170}
171
172pub enum CallKind<'tcx> {
173    FnDef {
174        def_id: DefId,
175        generic_args: CallArgs<'tcx>,
176        resolved_id: DefId,
177        resolved_args: CallArgs<'tcx>,
178    },
179    FnPtr {
180        fn_sig: Binder<FnSig>,
181        operand: Operand<'tcx>,
182    },
183}
184
185#[derive(Debug)]
186pub enum TerminatorKind<'tcx> {
187    Return,
188    Call {
189        kind: CallKind<'tcx>,
190        args: Vec<Operand<'tcx>>,
191        destination: Place,
192        target: Option<BasicBlock>,
193        unwind: UnwindAction,
194    },
195    SwitchInt {
196        discr: Operand<'tcx>,
197        targets: SwitchTargets,
198    },
199    Goto {
200        target: BasicBlock,
201    },
202    Drop {
203        place: Place,
204        target: BasicBlock,
205        unwind: UnwindAction,
206    },
207    Assert {
208        cond: Operand<'tcx>,
209        expected: bool,
210        target: BasicBlock,
211        msg: AssertKind,
212    },
213    Unreachable,
214    FalseEdge {
215        real_target: BasicBlock,
216        imaginary_target: BasicBlock,
217    },
218    FalseUnwind {
219        real_target: BasicBlock,
220        unwind: UnwindAction,
221    },
222    Yield {
223        value: Operand<'tcx>,
224        resume: BasicBlock,
225        resume_arg: Place,
226        drop: Option<BasicBlock>,
227    },
228    CoroutineDrop,
229    UnwindResume,
230}
231
232#[derive(Debug)]
233pub enum AssertKind {
234    BoundsCheck,
235    RemainderByZero,
236    Overflow(BinOp),
237    DivisionByZero,
238    // OverflowNeg(O),
239    // ResumedAfterReturn(GeneratorKind),
240    // ResumedAfterPanic(GeneratorKind),
241}
242
243pub struct Statement<'tcx> {
244    pub kind: StatementKind<'tcx>,
245    pub source_info: SourceInfo,
246}
247
248#[derive(Debug)]
249pub enum NonDivergingIntrinsic<'tcx> {
250    Assume(Operand<'tcx>),
251}
252
253#[derive(Debug)]
254pub enum StatementKind<'tcx> {
255    Assign(Place, Rvalue<'tcx>),
256    SetDiscriminant(Place, VariantIdx),
257    FakeRead(Box<(FakeReadCause, Place)>),
258    AscribeUserType(Place, Variance),
259    Intrinsic(NonDivergingIntrinsic<'tcx>),
260    PlaceMention(Place),
261    Nop,
262}
263
264/// Corresponds to <https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/mir/enum.Rvalue.html>
265pub enum Rvalue<'tcx> {
266    Use(Operand<'tcx>),
267    Repeat(Operand<'tcx>, Const),
268    Ref(Region, BorrowKind, Place),
269    RawPtr(RawPtrKind, Place),
270    Cast(CastKind, Operand<'tcx>, Ty),
271    BinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>),
272    UnaryOp(UnOp, Operand<'tcx>),
273    Discriminant(Place),
274    Aggregate(AggregateKind, Vec<Operand<'tcx>>),
275    ShallowInitBox(Operand<'tcx>, Ty),
276}
277
278#[derive(Copy, Clone)]
279pub enum CastKind {
280    IntToInt,
281    FloatToInt,
282    IntToFloat,
283    FloatToFloat,
284    PtrToPtr,
285    PointerCoercion(PointerCast),
286    PointerExposeProvenance,
287    PointerWithExposedProvenance,
288}
289
290#[derive(Copy, Clone)]
291pub enum PointerCast {
292    MutToConstPointer,
293    Unsize,
294    ClosureFnPointer,
295    ReifyFnPointer,
296}
297
298#[derive(Debug)]
299pub enum AggregateKind {
300    Adt(DefId, VariantIdx, GenericArgs, Option<UserTypeAnnotationIndex>, Option<FieldIdx>),
301    Array(Ty),
302    Tuple,
303    Closure(DefId, GenericArgs),
304    Coroutine(DefId, GenericArgs),
305}
306
307#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
308pub enum BinOp {
309    Gt,
310    Ge,
311    Lt,
312    Le,
313    Eq,
314    Ne,
315    Add,
316    Sub,
317    Mul,
318    Div,
319    Rem,
320    BitAnd,
321    BitOr,
322    BitXor,
323    Shl,
324    Shr,
325}
326
327pub enum Operand<'tcx> {
328    Copy(Place),
329    Move(Place),
330    Constant(ConstOperand<'tcx>),
331}
332
333/// The representation of constants in the mir is complicated. This struct is a thin wrapper
334/// around that representation. We don't support all constants, but we ensure that at least
335/// the type of the constant is supported. Thus, we can always fallback to give a constant
336/// an unrefined type.
337pub struct ConstOperand<'tcx> {
338    /// This is the lowered type of the constant.
339    ///
340    /// NOTE: [`rustc_middle::mir::ConstOperand`] has a `user_ty` field. That type is
341    /// unrelated
342    pub ty: Ty,
343    pub span: Span,
344    pub const_: rustc_middle::mir::Const<'tcx>,
345}
346
347#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
348pub struct Place {
349    /// the "root" of the place, e.g. `_1` in `*_1.f.g.h`
350    pub local: Local,
351    /// path taken to "get" the place e.g. `*.f.g.h` in `*_1.f.g.h` (except also have derefs)
352    pub projection: Vec<PlaceElem>,
353}
354
355impl Place {
356    pub const RETURN: &'static Place = &Place { local: RETURN_PLACE, projection: vec![] };
357
358    pub fn new(local: Local, projection: Vec<PlaceElem>) -> Place {
359        Place { local, projection }
360    }
361
362    pub fn as_ref(&self) -> PlaceRef<'_> {
363        PlaceRef { local: self.local, projection: &self.projection[..] }
364    }
365
366    pub fn deref(&self) -> Self {
367        let mut projection = self.projection.clone();
368        projection.push(PlaceElem::Deref);
369        Place { local: self.local, projection }
370    }
371
372    // TODO(source-level-binders): use the bits from the `projection` too?
373    pub fn name(&self, local_names: &UnordMap<Local, Symbol>) -> Option<Symbol> {
374        if self.projection.is_empty() { local_names.get(&self.local).copied() } else { None }
375    }
376}
377
378#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
379pub enum PlaceElem {
380    Deref,
381    Field(FieldIdx),
382    Downcast(Option<Symbol>, VariantIdx),
383    Index(Local),
384    ConstantIndex {
385        /// index or -index (in Python terms), depending on from_end
386        offset: u64,
387        /// The thing being indexed must be at least this long. For arrays this
388        /// is always the exact length.
389        min_length: u64,
390        /// Counting backwards from end? This is always false when indexing an
391        /// array.
392        from_end: bool,
393    },
394}
395
396#[derive(Clone, Copy, PartialEq, Eq)]
397pub struct PlaceRef<'a> {
398    pub local: Local,
399    pub projection: &'a [PlaceElem],
400}
401
402impl<'a> PlaceRef<'a> {
403    pub fn truncate(self, i: usize) -> PlaceRef<'a> {
404        Self { local: self.local, projection: &self.projection[..i] }
405    }
406
407    pub fn to_place(self) -> Place {
408        Place { local: self.local, projection: self.projection.to_vec() }
409    }
410
411    pub fn last_projection(self) -> Option<(PlaceRef<'a>, PlaceElem)> {
412        if let [base @ .., elem] = self.projection {
413            Some((PlaceRef { local: self.local, projection: base }, *elem))
414        } else {
415            None
416        }
417    }
418}
419
420impl Terminator<'_> {
421    pub fn is_return(&self) -> bool {
422        matches!(self.kind, TerminatorKind::Return)
423    }
424}
425
426impl Statement<'_> {
427    pub fn is_nop(&self) -> bool {
428        matches!(self.kind, StatementKind::Nop)
429    }
430}
431
432impl<'tcx> BodyRoot<'tcx> {
433    pub fn new(
434        facts: Rc<BodyWithBorrowckFacts<'tcx>>,
435        infcx: rustc_infer::infer::InferCtxt<'tcx>,
436        body: Body<'tcx>,
437        promoted: IndexVec<Promoted, Body<'tcx>>,
438    ) -> Self {
439        Self { body, promoted, infcx, facts }
440    }
441}
442
443impl<'tcx> Body<'tcx> {
444    pub fn new(
445        basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
446        local_decls: IndexVec<Local, LocalDecl>,
447        facts: Rc<BodyWithBorrowckFacts<'tcx>>,
448        kind: BodyKind,
449    ) -> Self {
450        let rustc_body = kind.select_body(&facts);
451        let fake_predecessors = mk_fake_predecessors(&basic_blocks);
452
453        // The dominator rank of each node is just its index in a reverse-postorder traversal.
454        let graph = &rustc_body.basic_blocks;
455        let mut dominator_order_rank = IndexVec::from_elem_n(0, graph.num_nodes());
456        let reverse_post_order = graph::iterate::reverse_post_order(graph, graph.start_node());
457        assert_eq!(reverse_post_order.len(), graph.num_nodes());
458        for (rank, bb) in (0u32..).zip(reverse_post_order) {
459            dominator_order_rank[bb] = rank;
460        }
461        let local_names = rustc_body
462            .var_debug_info
463            .iter()
464            .flat_map(|var_debug_info| {
465                if let VarDebugInfoContents::Place(place) = var_debug_info.value {
466                    let local = place.as_local()?;
467                    Some((local, var_debug_info.name))
468                } else {
469                    None
470                }
471            })
472            .collect();
473        Self {
474            basic_blocks,
475            local_decls,
476            fake_predecessors,
477            dominator_order_rank,
478            local_names,
479            facts,
480            kind,
481        }
482    }
483
484    /// The unrefined [`rustc_middle::mir::Body`] backing this body. It still contains region
485    /// identifiers.
486    pub fn rustc_body(&self) -> &rustc_middle::mir::Body<'tcx> {
487        self.kind.select_body(&self.facts)
488    }
489
490    pub fn terminator_loc(&self, bb: BasicBlock) -> Location {
491        Location { block: bb, statement_index: self.basic_blocks[bb].statements.len() }
492    }
493
494    #[inline]
495    pub fn is_join_point(&self, bb: BasicBlock) -> bool {
496        let total_preds = self.rustc_body().basic_blocks.predecessors()[bb].len();
497        let real_preds = total_preds - self.fake_predecessors[bb];
498        // The entry block is a joint point if it has at least one predecessor because there's
499        // an implicit goto from the environment at the beginning of the function.
500        real_preds > usize::from(bb != START_BLOCK)
501    }
502
503    #[inline]
504    pub fn dominators(&self) -> &Dominators<BasicBlock> {
505        self.rustc_body().basic_blocks.dominators()
506    }
507
508    #[inline]
509    pub fn args_iter(&self) -> impl ExactSizeIterator<Item = Local> {
510        (1..self.rustc_body().arg_count + 1).map(Local::new)
511    }
512
513    #[inline]
514    pub fn vars_and_temps_iter(&self) -> impl ExactSizeIterator<Item = Local> {
515        (self.rustc_body().arg_count + 1..self.local_decls.len()).map(Local::new)
516    }
517
518    pub fn span(&self) -> Span {
519        self.rustc_body().span
520    }
521
522    #[inline]
523    pub fn return_ty(&self) -> Ty {
524        self.local_decls[RETURN_PLACE].ty.clone()
525    }
526}
527
528/// The `FalseEdge/imaginary_target` edges mess up the `is_join_point` computation which creates spurious
529/// join points that lose information e.g. in match arms, the k+1-th arm has the k-th arm as a "fake"
530/// predecessor so we lose the assumptions specific to the k+1-th arm due to a spurious join. This code
531/// corrects for this problem by computing the number of "fake" predecessors and decreasing them from
532/// the total number of "predecessors" returned by `rustc`.  The option is to recompute "predecessors"
533/// from scratch but we may miss some cases there. (see also [`is_join_point`])
534///
535/// [`is_join_point`]: crate::mir::Body::is_join_point
536fn mk_fake_predecessors(
537    basic_blocks: &IndexVec<BasicBlock, BasicBlockData>,
538) -> IndexVec<BasicBlock, usize> {
539    let mut res: IndexVec<BasicBlock, usize> = basic_blocks.iter().map(|_| 0).collect();
540
541    for bb in basic_blocks {
542        if let Some(terminator) = &bb.terminator
543            && let TerminatorKind::FalseEdge { imaginary_target, .. } = terminator.kind
544        {
545            res[imaginary_target] += 1;
546        }
547    }
548    res
549}
550
551impl fmt::Debug for Body<'_> {
552    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553        for (bb, data) in self.basic_blocks.iter_enumerated() {
554            writeln!(
555                f,
556                "{bb:?}: {{{}",
557                data.statements
558                    .iter()
559                    .filter(|stmt| !matches!(stmt.kind, StatementKind::Nop))
560                    .format_with("", |stmt, f| f(&format_args!("\n    {stmt:?};")))
561            )?;
562            if let Some(terminator) = &data.terminator {
563                writeln!(f, "    {terminator:?}")?;
564            }
565            writeln!(f, "}}\n")?;
566        }
567        Ok(())
568    }
569}
570
571impl fmt::Debug for Statement<'_> {
572    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
573        match &self.kind {
574            StatementKind::Assign(place, rvalue) => write!(f, "{place:?} = {rvalue:?}"),
575            StatementKind::Nop => write!(f, "nop"),
576            StatementKind::PlaceMention(place) => {
577                write!(f, "PlaceMention({place:?})")
578            }
579            StatementKind::SetDiscriminant(place, variant_idx) => {
580                write!(f, "discriminant({place:?}) = {variant_idx:?}")
581            }
582            StatementKind::FakeRead(box (cause, place)) => {
583                write!(f, "FakeRead({cause:?}, {place:?})")
584            }
585            StatementKind::AscribeUserType(place, variance) => {
586                write!(f, "AscribeUserType({place:?}, {variance:?})")
587            }
588            StatementKind::Intrinsic(NonDivergingIntrinsic::Assume(op)) => {
589                write!(f, "Assume({op:?})")
590            }
591        }
592    }
593}
594
595impl fmt::Debug for CallKind<'_> {
596    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
597        match self {
598            CallKind::FnDef { resolved_id, resolved_args, .. } => {
599                let fname = rustc_middle::ty::tls::with(|tcx| tcx.def_path_str(*resolved_id));
600                write!(f, "call {fname}")?;
601                if !resolved_args.lowered.is_empty() {
602                    write!(f, "<{:?}>", resolved_args.lowered.iter().format(", "))?;
603                }
604                Ok(())
605            }
606            CallKind::FnPtr { fn_sig, operand } => write!(f, "FnPtr[{operand:?}]({fn_sig:?})"),
607        }
608    }
609}
610
611impl fmt::Debug for Terminator<'_> {
612    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
613        match &self.kind {
614            TerminatorKind::Return => write!(f, "return"),
615            TerminatorKind::Unreachable => write!(f, "unreachable"),
616            TerminatorKind::Call { kind, args, destination, target, unwind, .. } => {
617                write!(
618                    f,
619                    "{destination:?} = call {kind:?}({args:?}) -> [return: {target}, unwind: {unwind:?}]",
620                    args = args.iter().format(", "),
621                    target = opt_bb_to_str(*target),
622                )
623            }
624            TerminatorKind::SwitchInt { discr, targets } => {
625                write!(
626                    f,
627                    "switchInt({discr:?}) -> [{}, otherwise: {:?}]",
628                    targets
629                        .iter()
630                        .format_with(", ", |(val, bb), f| f(&format_args!("{val:?}: {bb:?}"))),
631                    targets.otherwise()
632                )
633            }
634            TerminatorKind::Goto { target } => {
635                write!(f, "goto -> {target:?}")
636            }
637            TerminatorKind::Drop { place, target, unwind } => {
638                write!(f, "drop({place:?}) -> [{target:?}, unwind: {unwind:?}]",)
639            }
640            TerminatorKind::Assert { cond, target, expected, msg } => {
641                write!(
642                    f,
643                    "assert({cond:?} is expected to be {expected:?}, \"{msg:?}\") -> {target:?}"
644                )
645            }
646            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
647                write!(f, "falseEdge -> [real: {real_target:?}, imaginary: {imaginary_target:?}]")
648            }
649            TerminatorKind::FalseUnwind { real_target, unwind } => {
650                write!(f, "falseUnwind -> [real: {real_target:?}, cleanup: {unwind:?}]")
651            }
652            TerminatorKind::UnwindResume => write!(f, "resume"),
653            TerminatorKind::CoroutineDrop => write!(f, "generator_drop"),
654            TerminatorKind::Yield { value, resume, drop, resume_arg } => {
655                write!(
656                    f,
657                    "{resume_arg:?} = yield({value:?}) -> [resume: {resume:?}, drop: {drop:?}]"
658                )
659            }
660        }
661    }
662}
663
664impl fmt::Debug for Place {
665    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
666        write!(f, "{:?}", self.as_ref())
667    }
668}
669
670impl fmt::Debug for PlaceRef<'_> {
671    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
672        let mut p = format!("{:?}", self.local);
673        let mut need_parens = false;
674        for elem in self.projection {
675            match elem {
676                PlaceElem::Field(f) => {
677                    if need_parens {
678                        p = format!("({p}).{}", u32::from(*f));
679                        need_parens = false;
680                    } else {
681                        p = format!("{p}.{}", u32::from(*f));
682                    }
683                }
684                PlaceElem::Deref => {
685                    p = format!("*{p}");
686                    need_parens = true;
687                }
688                PlaceElem::Downcast(variant_name, variant_idx) => {
689                    if let Some(variant_name) = variant_name {
690                        p = format!("{p} as {variant_name}");
691                    } else {
692                        p = format!("{p} as {variant_idx:?}");
693                    }
694                    need_parens = true;
695                }
696                PlaceElem::Index(v) => {
697                    p = format!("{p}[{v:?}]");
698                    need_parens = false;
699                }
700                PlaceElem::ConstantIndex { offset, min_length, .. } => {
701                    p = format!("{p}[{offset:?} of {min_length:?}]");
702                    need_parens = false;
703                }
704            }
705        }
706        write!(f, "{p}")
707    }
708}
709
710impl fmt::Debug for Rvalue<'_> {
711    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
712        match self {
713            Rvalue::Use(op) => write!(f, "{op:?}"),
714            Rvalue::Ref(r, BorrowKind::Mut { .. }, place) => {
715                write!(f, "&{} mut {place:?}", region_to_string(*r))
716            }
717            Rvalue::Ref(r, BorrowKind::Shared, place) => {
718                write!(f, "&{} {place:?}", region_to_string(*r))
719            }
720            Rvalue::Ref(r, BorrowKind::Fake(FakeBorrowKind::Shallow), place) => {
721                write!(f, "&{} fake shallow {place:?}", region_to_string(*r))
722            }
723            Rvalue::Ref(r, BorrowKind::Fake(FakeBorrowKind::Deep), place) => {
724                write!(f, "&{} fake deep {place:?}", region_to_string(*r))
725            }
726            Rvalue::RawPtr(mutbl, place) => write!(f, "&raw {} {place:?}", mutbl.ptr_str()),
727            Rvalue::Discriminant(place) => write!(f, "discriminant({place:?})"),
728            Rvalue::BinaryOp(bin_op, op1, op2) => write!(f, "{bin_op:?}({op1:?}, {op2:?})"),
729            Rvalue::UnaryOp(un_op, op) => write!(f, "{un_op:?}({op:?})"),
730            Rvalue::Aggregate(AggregateKind::Adt(def_id, variant_idx, args, _, _), operands) => {
731                let (fname, variant_name) = rustc_middle::ty::tls::with(|tcx| {
732                    let variant_name = tcx.adt_def(*def_id).variant(*variant_idx).name;
733                    let fname = tcx.def_path_str(*def_id);
734                    (fname, variant_name)
735                });
736                write!(f, "{fname}::{variant_name}")?;
737                if !args.is_empty() {
738                    write!(f, "<{:?}>", args.iter().format(", "),)?;
739                }
740                if !operands.is_empty() {
741                    write!(f, "({:?})", operands.iter().format(", "))?;
742                }
743                Ok(())
744            }
745            Rvalue::Aggregate(AggregateKind::Closure(def_id, args), operands) => {
746                write!(
747                    f,
748                    "closure({}, {args:?}, {:?})",
749                    def_id_to_string(*def_id),
750                    operands.iter().format(", ")
751                )
752            }
753            Rvalue::Aggregate(AggregateKind::Coroutine(def_id, args), operands) => {
754                write!(
755                    f,
756                    "generator({}, {args:?}, {:?})",
757                    def_id_to_string(*def_id),
758                    operands.iter().format(", ")
759                )
760            }
761            Rvalue::Aggregate(AggregateKind::Array(_), args) => {
762                write!(f, "[{:?}]", args.iter().format(", "))
763            }
764            Rvalue::Aggregate(AggregateKind::Tuple, args) => {
765                write!(f, "({:?})", args.iter().format(", "))
766            }
767            Rvalue::Cast(kind, op, ty) => write!(f, "{op:?} as {ty:?} [{kind:?}]"),
768            Rvalue::Repeat(op, c) => write!(f, "[{op:?}; {c:?}]"),
769            Rvalue::ShallowInitBox(op, ty) => write!(f, "ShallowInitBox({op:?}, {ty:?})"),
770        }
771    }
772}
773
774impl fmt::Debug for PointerCast {
775    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
776        match self {
777            PointerCast::MutToConstPointer => write!(f, "MutToConstPointer"),
778            PointerCast::Unsize => write!(f, "Unsize"),
779            PointerCast::ClosureFnPointer => write!(f, "ClosureFnPointer"),
780            PointerCast::ReifyFnPointer => write!(f, "ReifyFnPointer"),
781        }
782    }
783}
784
785impl fmt::Debug for CastKind {
786    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
787        match self {
788            CastKind::IntToInt => write!(f, "IntToInt"),
789            CastKind::FloatToInt => write!(f, "FloatToInt"),
790            CastKind::IntToFloat => write!(f, "IntToFloat"),
791            CastKind::FloatToFloat => write!(f, "FloatToFloat"),
792            CastKind::PtrToPtr => write!(f, "PtrToPtr"),
793            CastKind::PointerCoercion(c) => write!(f, "Pointer({c:?})"),
794            CastKind::PointerExposeProvenance => write!(f, "PointerExposeProvenance"),
795            CastKind::PointerWithExposedProvenance => write!(f, "PointerWithExposedProvenance"),
796        }
797    }
798}
799
800impl fmt::Debug for Operand<'_> {
801    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
802        match self {
803            Self::Copy(place) => write!(f, "copy {place:?}"),
804            Self::Move(place) => write!(f, "move {place:?}"),
805            Self::Constant(c) => write!(f, "{:?}", c.const_),
806        }
807    }
808}
809
810fn opt_bb_to_str(bb: Option<BasicBlock>) -> String {
811    match bb {
812        Some(bb) => format!("{bb:?}"),
813        None => "None".to_string(),
814    }
815}