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    Cast(CastKind, Operand, Ty),
211    BinaryOp(BinOp, Operand, Operand),
212    NullaryOp(NullOp, Ty),
213    UnaryOp(UnOp, Operand),
214    Discriminant(Place),
215    Aggregate(AggregateKind, Vec<Operand>),
216    ShallowInitBox(Operand, Ty),
217}
218
219#[derive(Copy, Clone)]
220pub enum CastKind {
221    IntToInt,
222    FloatToInt,
223    IntToFloat,
224    PtrToPtr,
225    PointerCoercion(PointerCast),
226    PointerExposeProvenance,
227    PointerWithExposedProvenance,
228}
229
230#[derive(Copy, Clone)]
231pub enum PointerCast {
232    MutToConstPointer,
233    Unsize,
234    ClosureFnPointer,
235    ReifyFnPointer,
236}
237
238#[derive(Debug)]
239pub enum AggregateKind {
240    Adt(DefId, VariantIdx, GenericArgs, Option<UserTypeAnnotationIndex>, Option<FieldIdx>),
241    Array(Ty),
242    Tuple,
243    Closure(DefId, GenericArgs),
244    Coroutine(DefId, GenericArgs),
245}
246
247#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
248pub enum BinOp {
249    Gt,
250    Ge,
251    Lt,
252    Le,
253    Eq,
254    Ne,
255    Add,
256    Sub,
257    Mul,
258    Div,
259    Rem,
260    BitAnd,
261    BitOr,
262    BitXor,
263    Shl,
264    Shr,
265}
266
267#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
268pub enum NullOp {
269    SizeOf,
270    AlignOf,
271}
272
273pub enum Operand {
274    Copy(Place),
275    Move(Place),
276    Constant(Constant),
277}
278
279#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
280pub struct Place {
281    /// the "root" of the place, e.g. `_1` in `*_1.f.g.h`
282    pub local: Local,
283    /// path taken to "get" the place e.g. `*.f.g.h` in `*_1.f.g.h` (except also have derefs)
284    pub projection: Vec<PlaceElem>,
285}
286
287impl Place {
288    pub const RETURN: &'static Place = &Place { local: RETURN_PLACE, projection: vec![] };
289
290    pub fn new(local: Local, projection: Vec<PlaceElem>) -> Place {
291        Place { local, projection }
292    }
293
294    pub fn as_ref(&self) -> PlaceRef<'_> {
295        PlaceRef { local: self.local, projection: &self.projection[..] }
296    }
297
298    pub fn deref(&self) -> Self {
299        let mut projection = self.projection.clone();
300        projection.push(PlaceElem::Deref);
301        Place { local: self.local, projection }
302    }
303}
304
305#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
306pub enum PlaceElem {
307    Deref,
308    Field(FieldIdx),
309    Downcast(Option<Symbol>, VariantIdx),
310    Index(Local),
311    ConstantIndex {
312        /// index or -index (in Python terms), depending on from_end
313        offset: u64,
314        /// The thing being indexed must be at least this long. For arrays this
315        /// is always the exact length.
316        min_length: u64,
317        /// Counting backwards from end? This is always false when indexing an
318        /// array.
319        from_end: bool,
320    },
321}
322
323#[derive(Clone, Copy, PartialEq, Eq)]
324pub struct PlaceRef<'a> {
325    pub local: Local,
326    pub projection: &'a [PlaceElem],
327}
328
329impl<'a> PlaceRef<'a> {
330    pub fn truncate(self, i: usize) -> PlaceRef<'a> {
331        Self { local: self.local, projection: &self.projection[..i] }
332    }
333
334    pub fn to_place(self) -> Place {
335        Place { local: self.local, projection: self.projection.to_vec() }
336    }
337
338    pub fn last_projection(self) -> Option<(PlaceRef<'a>, PlaceElem)> {
339        if let [base @ .., elem] = self.projection {
340            Some((PlaceRef { local: self.local, projection: base }, *elem))
341        } else {
342            None
343        }
344    }
345}
346
347pub enum Constant {
348    Int(i128, IntTy),
349    Uint(u128, UintTy),
350    Float(u128, FloatTy),
351    Bool(bool),
352    Str(Symbol),
353    Char(char),
354    Unit,
355    Param(ParamConst, Ty),
356    /// General catch-all for constants of a given Ty
357    Opaque(Ty),
358    /// Better than opaque -- we track `DefId` so we can get the actual refinement index
359    Unevaluated(Ty, DefId),
360}
361
362impl Terminator<'_> {
363    pub fn is_return(&self) -> bool {
364        matches!(self.kind, TerminatorKind::Return)
365    }
366}
367
368impl Statement {
369    pub fn is_nop(&self) -> bool {
370        matches!(self.kind, StatementKind::Nop)
371    }
372}
373
374impl<'tcx> Body<'tcx> {
375    pub fn new(
376        basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
377        local_decls: IndexVec<Local, LocalDecl>,
378        body_with_facts: BodyWithBorrowckFacts<'tcx>,
379        infcx: rustc_infer::infer::InferCtxt<'tcx>,
380    ) -> Self {
381        let fake_predecessors = mk_fake_predecessors(&basic_blocks);
382
383        // The dominator rank of each node is just its index in a reverse-postorder traversal.
384        let graph = &body_with_facts.body.basic_blocks;
385        let mut dominator_order_rank = IndexVec::from_elem_n(0, graph.num_nodes());
386        let reverse_post_order = graph::iterate::reverse_post_order(graph, graph.start_node());
387        assert_eq!(reverse_post_order.len(), graph.num_nodes());
388        for (rank, bb) in (0u32..).zip(reverse_post_order) {
389            dominator_order_rank[bb] = rank;
390        }
391        let local_names = body_with_facts
392            .body
393            .var_debug_info
394            .iter()
395            .flat_map(|var_debug_info| {
396                if let VarDebugInfoContents::Place(place) = var_debug_info.value {
397                    let local = place.as_local()?;
398                    Some((local, var_debug_info.name))
399                } else {
400                    None
401                }
402            })
403            .collect();
404        Self {
405            basic_blocks,
406            local_decls,
407            infcx,
408            fake_predecessors,
409            body_with_facts,
410            dominator_order_rank,
411            local_names,
412        }
413    }
414
415    pub fn def_id(&self) -> DefId {
416        self.inner().source.def_id()
417    }
418
419    pub fn span(&self) -> Span {
420        self.body_with_facts.body.span
421    }
422
423    pub fn inner(&self) -> &mir::Body<'tcx> {
424        &self.body_with_facts.body
425    }
426
427    #[inline]
428    pub fn args_iter(&self) -> impl ExactSizeIterator<Item = Local> {
429        (1..self.body_with_facts.body.arg_count + 1).map(Local::new)
430    }
431
432    #[inline]
433    pub fn vars_and_temps_iter(&self) -> impl ExactSizeIterator<Item = Local> {
434        (self.body_with_facts.body.arg_count + 1..self.local_decls.len()).map(Local::new)
435    }
436
437    #[inline]
438    pub fn is_join_point(&self, bb: BasicBlock) -> bool {
439        let total_preds = self.body_with_facts.body.basic_blocks.predecessors()[bb].len();
440        let real_preds = total_preds - self.fake_predecessors[bb];
441        // The entry block is a joint point if it has at least one predecessor because there's
442        // an implicit goto from the environment at the beginning of the function.
443        real_preds > usize::from(bb != START_BLOCK)
444    }
445
446    #[inline]
447    pub fn dominators(&self) -> &Dominators<BasicBlock> {
448        self.body_with_facts.body.basic_blocks.dominators()
449    }
450
451    pub fn terminator_loc(&self, bb: BasicBlock) -> Location {
452        Location { block: bb, statement_index: self.basic_blocks[bb].statements.len() }
453    }
454
455    pub fn calculate_borrows_out_of_scope_at_location(
456        &self,
457    ) -> FxIndexMap<Location, Vec<BorrowIndex>> {
458        rustc_borrowck::consumers::calculate_borrows_out_of_scope_at_location(
459            &self.body_with_facts.body,
460            &self.body_with_facts.region_inference_context,
461            &self.body_with_facts.borrow_set,
462        )
463    }
464
465    pub fn borrow_data(&self, idx: BorrowIndex) -> &BorrowData<'tcx> {
466        self.body_with_facts
467            .borrow_set
468            .location_map()
469            .get_index(idx.as_usize())
470            .unwrap()
471            .1
472    }
473
474    pub fn rustc_body(&self) -> &mir::Body<'tcx> {
475        &self.body_with_facts.body
476    }
477
478    pub fn local_kind(&self, local: Local) -> LocalKind {
479        self.body_with_facts.body.local_kind(local)
480    }
481}
482
483/// The `FalseEdge/imaginary_target` edges mess up the `is_join_point` computation which creates spurious
484/// join points that lose information e.g. in match arms, the k+1-th arm has the k-th arm as a "fake"
485/// predecessor so we lose the assumptions specific to the k+1-th arm due to a spurious join. This code
486/// corrects for this problem by computing the number of "fake" predecessors and decreasing them from
487/// the total number of "predecessors" returned by `rustc`.  The option is to recompute "predecessors"
488/// from scratch but we may miss some cases there. (see also [`is_join_point`])
489///
490/// [`is_join_point`]: crate::mir::Body::is_join_point
491fn mk_fake_predecessors(
492    basic_blocks: &IndexVec<BasicBlock, BasicBlockData>,
493) -> IndexVec<BasicBlock, usize> {
494    let mut res: IndexVec<BasicBlock, usize> = basic_blocks.iter().map(|_| 0).collect();
495
496    for bb in basic_blocks {
497        if let Some(terminator) = &bb.terminator
498            && let TerminatorKind::FalseEdge { imaginary_target, .. } = terminator.kind
499        {
500            res[imaginary_target] += 1;
501        }
502    }
503    res
504}
505
506impl fmt::Debug for Body<'_> {
507    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508        for (bb, data) in self.basic_blocks.iter_enumerated() {
509            writeln!(
510                f,
511                "{bb:?}: {{{}",
512                data.statements
513                    .iter()
514                    .filter(|stmt| !matches!(stmt.kind, StatementKind::Nop))
515                    .format_with("", |stmt, f| f(&format_args!("\n    {stmt:?};")))
516            )?;
517            if let Some(terminator) = &data.terminator {
518                writeln!(f, "    {terminator:?}")?;
519            }
520            writeln!(f, "}}\n")?;
521        }
522        Ok(())
523    }
524}
525
526impl fmt::Debug for Statement {
527    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
528        match &self.kind {
529            StatementKind::Assign(place, rvalue) => write!(f, "{place:?} = {rvalue:?}"),
530            StatementKind::Nop => write!(f, "nop"),
531            StatementKind::PlaceMention(place) => {
532                write!(f, "PlaceMention({place:?})")
533            }
534            StatementKind::SetDiscriminant(place, variant_idx) => {
535                write!(f, "discriminant({place:?}) = {variant_idx:?}")
536            }
537            StatementKind::FakeRead(box (cause, place)) => {
538                write!(f, "FakeRead({cause:?}, {place:?})")
539            }
540            StatementKind::AscribeUserType(place, variance) => {
541                write!(f, "AscribeUserType({place:?}, {variance:?})")
542            }
543            StatementKind::Intrinsic(NonDivergingIntrinsic::Assume(op)) => {
544                write!(f, "Assume({op:?})")
545            }
546        }
547    }
548}
549
550impl fmt::Debug for CallKind<'_> {
551    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
552        match self {
553            CallKind::FnDef { resolved_id, resolved_args, .. } => {
554                let fname = rustc_middle::ty::tls::with(|tcx| tcx.def_path_str(*resolved_id));
555                write!(f, "call {fname}")?;
556                if !resolved_args.lowered.is_empty() {
557                    write!(f, "<{:?}>", resolved_args.lowered.iter().format(", "))?;
558                }
559                Ok(())
560            }
561            CallKind::FnPtr { fn_sig, operand } => write!(f, "FnPtr[{operand:?}]({fn_sig:?})"),
562        }
563    }
564}
565
566impl fmt::Debug for Terminator<'_> {
567    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
568        match &self.kind {
569            TerminatorKind::Return => write!(f, "return"),
570            TerminatorKind::Unreachable => write!(f, "unreachable"),
571            TerminatorKind::Call { kind, args, destination, target, unwind, .. } => {
572                write!(
573                    f,
574                    "{destination:?} = call {kind:?}({args:?}) -> [return: {target}, unwind: {unwind:?}]",
575                    args = args.iter().format(", "),
576                    target = opt_bb_to_str(*target),
577                )
578            }
579            TerminatorKind::SwitchInt { discr, targets } => {
580                write!(
581                    f,
582                    "switchInt({discr:?}) -> [{}, otherwise: {:?}]",
583                    targets
584                        .iter()
585                        .format_with(", ", |(val, bb), f| f(&format_args!("{val:?}: {bb:?}"))),
586                    targets.otherwise()
587                )
588            }
589            TerminatorKind::Goto { target } => {
590                write!(f, "goto -> {target:?}")
591            }
592            TerminatorKind::Drop { place, target, unwind } => {
593                write!(f, "drop({place:?}) -> [{target:?}, unwind: {unwind:?}]",)
594            }
595            TerminatorKind::Assert { cond, target, expected, msg } => {
596                write!(
597                    f,
598                    "assert({cond:?} is expected to be {expected:?}, \"{msg:?}\") -> {target:?}"
599                )
600            }
601            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
602                write!(f, "falseEdge -> [real: {real_target:?}, imaginary: {imaginary_target:?}]")
603            }
604            TerminatorKind::FalseUnwind { real_target, unwind } => {
605                write!(f, "falseUnwind -> [real: {real_target:?}, cleanup: {unwind:?}]")
606            }
607            TerminatorKind::UnwindResume => write!(f, "resume"),
608            TerminatorKind::CoroutineDrop => write!(f, "generator_drop"),
609            TerminatorKind::Yield { value, resume, drop, resume_arg } => {
610                write!(
611                    f,
612                    "{resume_arg:?} = yield({value:?}) -> [resume: {resume:?}, drop: {drop:?}]"
613                )
614            }
615        }
616    }
617}
618
619impl fmt::Debug for Place {
620    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
621        write!(f, "{:?}", self.as_ref())
622    }
623}
624
625impl fmt::Debug for PlaceRef<'_> {
626    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
627        let mut p = format!("{:?}", self.local);
628        let mut need_parens = false;
629        for elem in self.projection {
630            match elem {
631                PlaceElem::Field(f) => {
632                    if need_parens {
633                        p = format!("({p}).{}", u32::from(*f));
634                        need_parens = false;
635                    } else {
636                        p = format!("{p}.{}", u32::from(*f));
637                    }
638                }
639                PlaceElem::Deref => {
640                    p = format!("*{p}");
641                    need_parens = true;
642                }
643                PlaceElem::Downcast(variant_name, variant_idx) => {
644                    if let Some(variant_name) = variant_name {
645                        p = format!("{p} as {variant_name}");
646                    } else {
647                        p = format!("{p} as {variant_idx:?}");
648                    }
649                    need_parens = true;
650                }
651                PlaceElem::Index(v) => {
652                    p = format!("{p}[{v:?}]");
653                    need_parens = false;
654                }
655                PlaceElem::ConstantIndex { offset, min_length, .. } => {
656                    p = format!("{p}[{offset:?} of {min_length:?}]");
657                    need_parens = false;
658                }
659            }
660        }
661        write!(f, "{p}")
662    }
663}
664
665impl fmt::Debug for Rvalue {
666    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
667        match self {
668            Rvalue::Use(op) => write!(f, "{op:?}"),
669            Rvalue::Ref(r, BorrowKind::Mut { .. }, place) => {
670                write!(f, "&{} mut {place:?}", region_to_string(*r))
671            }
672            Rvalue::Ref(r, BorrowKind::Shared, place) => {
673                write!(f, "&{} {place:?}", region_to_string(*r))
674            }
675            Rvalue::Ref(r, BorrowKind::Fake(FakeBorrowKind::Shallow), place) => {
676                write!(f, "&{} fake shallow {place:?}", region_to_string(*r))
677            }
678            Rvalue::Ref(r, BorrowKind::Fake(FakeBorrowKind::Deep), place) => {
679                write!(f, "&{} fake deep {place:?}", region_to_string(*r))
680            }
681            Rvalue::RawPtr(mutbl, place) => write!(f, "&raw {} {place:?}", mutbl.ptr_str()),
682            Rvalue::Discriminant(place) => write!(f, "discriminant({place:?})"),
683            Rvalue::BinaryOp(bin_op, op1, op2) => write!(f, "{bin_op:?}({op1:?}, {op2:?})"),
684            Rvalue::NullaryOp(null_op, ty) => write!(f, "{null_op:?}({ty:?})"),
685            Rvalue::UnaryOp(un_op, op) => write!(f, "{un_op:?}({op:?})"),
686            Rvalue::Aggregate(AggregateKind::Adt(def_id, variant_idx, args, _, _), operands) => {
687                let (fname, variant_name) = rustc_middle::ty::tls::with(|tcx| {
688                    let variant_name = tcx.adt_def(*def_id).variant(*variant_idx).name;
689                    let fname = tcx.def_path_str(*def_id);
690                    (fname, variant_name)
691                });
692                write!(f, "{fname}::{variant_name}")?;
693                if !args.is_empty() {
694                    write!(f, "<{:?}>", args.iter().format(", "),)?;
695                }
696                if !operands.is_empty() {
697                    write!(f, "({:?})", operands.iter().format(", "))?;
698                }
699                Ok(())
700            }
701            Rvalue::Aggregate(AggregateKind::Closure(def_id, args), operands) => {
702                write!(
703                    f,
704                    "closure({}, {args:?}, {:?})",
705                    def_id_to_string(*def_id),
706                    operands.iter().format(", ")
707                )
708            }
709            Rvalue::Aggregate(AggregateKind::Coroutine(def_id, args), operands) => {
710                write!(
711                    f,
712                    "generator({}, {args:?}, {:?})",
713                    def_id_to_string(*def_id),
714                    operands.iter().format(", ")
715                )
716            }
717            Rvalue::Aggregate(AggregateKind::Array(_), args) => {
718                write!(f, "[{:?}]", args.iter().format(", "))
719            }
720            Rvalue::Aggregate(AggregateKind::Tuple, args) => {
721                write!(f, "({:?})", args.iter().format(", "))
722            }
723            Rvalue::Cast(kind, op, ty) => write!(f, "{op:?} as {ty:?} [{kind:?}]"),
724            Rvalue::Repeat(op, c) => write!(f, "[{op:?}; {c:?}]"),
725            Rvalue::ShallowInitBox(op, ty) => write!(f, "ShallowInitBox({op:?}, {ty:?})"),
726        }
727    }
728}
729
730impl fmt::Debug for PointerCast {
731    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
732        match self {
733            PointerCast::MutToConstPointer => write!(f, "MutToConstPointer"),
734            PointerCast::Unsize => write!(f, "Unsize"),
735            PointerCast::ClosureFnPointer => write!(f, "ClosureFnPointer"),
736            PointerCast::ReifyFnPointer => write!(f, "ReifyFnPointer"),
737        }
738    }
739}
740
741impl fmt::Debug for CastKind {
742    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
743        match self {
744            CastKind::IntToInt => write!(f, "IntToInt"),
745            CastKind::FloatToInt => write!(f, "FloatToInt"),
746            CastKind::IntToFloat => write!(f, "IntToFloat"),
747            CastKind::PtrToPtr => write!(f, "PtrToPtr"),
748            CastKind::PointerCoercion(c) => write!(f, "Pointer({c:?})"),
749            CastKind::PointerExposeProvenance => write!(f, "PointerExposeProvenance"),
750            CastKind::PointerWithExposedProvenance => write!(f, "PointerWithExposedProvenance"),
751        }
752    }
753}
754
755impl fmt::Debug for Operand {
756    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
757        match self {
758            Self::Copy(place) => write!(f, "copy {place:?}"),
759            Self::Move(place) => write!(f, "move {place:?}"),
760            Self::Constant(c) => write!(f, "{c:?}"),
761        }
762    }
763}
764
765impl fmt::Debug for Constant {
766    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
767        match self {
768            Constant::Int(n, int_ty) => write!(f, "{n}{}", int_ty.name_str()),
769            Constant::Uint(n, uint_ty) => write!(f, "{n}{}", uint_ty.name_str()),
770            Constant::Float(bits, float_ty) => write!(f, "{bits}{}", float_ty.name_str()),
771            Constant::Bool(b) => write!(f, "{b}"),
772            Constant::Unit => write!(f, "()"),
773            Constant::Str(s) => write!(f, "\"{s:?}\""),
774            Constant::Char(c) => write!(f, "\'{c}\'"),
775            Constant::Opaque(ty) => write!(f, "<opaque {ty:?}>"),
776            Constant::Param(p, _) => write!(f, "{p:?}"),
777            Constant::Unevaluated(ty, def_id) => write!(f, "<uneval {ty:?} from {def_id:?}>"),
778        }
779    }
780}
781
782fn opt_bb_to_str(bb: Option<BasicBlock>) -> String {
783    match bb {
784        Some(bb) => format!("{bb:?}"),
785        None => "None".to_string(),
786    }
787}