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