Skip to main content

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