flux_rustc_bridge/
mir.rs

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