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| {
556                    let path = tcx.def_path(*resolved_id);
557                    path.data.iter().join("::")
558                });
559                write!(f, "call {fname}")?;
560                if !resolved_args.lowered.is_empty() {
561                    write!(f, "<{:?}>", resolved_args.lowered.iter().format(", "))?;
562                }
563                Ok(())
564            }
565            CallKind::FnPtr { fn_sig, operand } => write!(f, "FnPtr[{operand:?}]({fn_sig:?})"),
566        }
567    }
568}
569
570impl fmt::Debug for Terminator<'_> {
571    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
572        match &self.kind {
573            TerminatorKind::Return => write!(f, "return"),
574            TerminatorKind::Unreachable => write!(f, "unreachable"),
575            TerminatorKind::Call { kind, args, destination, target, unwind, .. } => {
576                write!(
577                    f,
578                    "{destination:?} = call {kind:?}({args:?}) -> [return: {target}, unwind: {unwind:?}]",
579                    args = args.iter().format(", "),
580                    target = opt_bb_to_str(*target),
581                )
582            }
583            TerminatorKind::SwitchInt { discr, targets } => {
584                write!(
585                    f,
586                    "switchInt({discr:?}) -> [{}, otherwise: {:?}]",
587                    targets
588                        .iter()
589                        .format_with(", ", |(val, bb), f| f(&format_args!("{val:?}: {bb:?}"))),
590                    targets.otherwise()
591                )
592            }
593            TerminatorKind::Goto { target } => {
594                write!(f, "goto -> {target:?}")
595            }
596            TerminatorKind::Drop { place, target, unwind } => {
597                write!(f, "drop({place:?}) -> [{target:?}, unwind: {unwind:?}]",)
598            }
599            TerminatorKind::Assert { cond, target, expected, msg } => {
600                write!(
601                    f,
602                    "assert({cond:?} is expected to be {expected:?}, \"{msg:?}\") -> {target:?}"
603                )
604            }
605            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
606                write!(f, "falseEdge -> [real: {real_target:?}, imaginary: {imaginary_target:?}]")
607            }
608            TerminatorKind::FalseUnwind { real_target, unwind } => {
609                write!(f, "falseUnwind -> [real: {real_target:?}, cleanup: {unwind:?}]")
610            }
611            TerminatorKind::UnwindResume => write!(f, "resume"),
612            TerminatorKind::CoroutineDrop => write!(f, "generator_drop"),
613            TerminatorKind::Yield { value, resume, drop, resume_arg } => {
614                write!(
615                    f,
616                    "{resume_arg:?} = yield({value:?}) -> [resume: {resume:?}, drop: {drop:?}]"
617                )
618            }
619        }
620    }
621}
622
623impl fmt::Debug for Place {
624    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
625        write!(f, "{:?}", self.as_ref())
626    }
627}
628
629impl fmt::Debug for PlaceRef<'_> {
630    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
631        let mut p = format!("{:?}", self.local);
632        let mut need_parens = false;
633        for elem in self.projection {
634            match elem {
635                PlaceElem::Field(f) => {
636                    if need_parens {
637                        p = format!("({p}).{}", u32::from(*f));
638                        need_parens = false;
639                    } else {
640                        p = format!("{p}.{}", u32::from(*f));
641                    }
642                }
643                PlaceElem::Deref => {
644                    p = format!("*{p}");
645                    need_parens = true;
646                }
647                PlaceElem::Downcast(variant_name, variant_idx) => {
648                    if let Some(variant_name) = variant_name {
649                        p = format!("{p} as {variant_name}");
650                    } else {
651                        p = format!("{p} as {variant_idx:?}");
652                    }
653                    need_parens = true;
654                }
655                PlaceElem::Index(v) => {
656                    p = format!("{p}[{v:?}]");
657                    need_parens = false;
658                }
659                PlaceElem::ConstantIndex { offset, min_length, .. } => {
660                    p = format!("{p}[{offset:?} of {min_length:?}]");
661                    need_parens = false;
662                }
663            }
664        }
665        write!(f, "{p}")
666    }
667}
668
669impl fmt::Debug for Rvalue {
670    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
671        match self {
672            Rvalue::Use(op) => write!(f, "{op:?}"),
673            Rvalue::Ref(r, BorrowKind::Mut { .. }, place) => {
674                write!(f, "&{} mut {place:?}", region_to_string(*r))
675            }
676            Rvalue::Ref(r, BorrowKind::Shared, place) => {
677                write!(f, "&{} {place:?}", region_to_string(*r))
678            }
679            Rvalue::Ref(r, BorrowKind::Fake(FakeBorrowKind::Shallow), place) => {
680                write!(f, "&{} fake shallow {place:?}", region_to_string(*r))
681            }
682            Rvalue::Ref(r, BorrowKind::Fake(FakeBorrowKind::Deep), place) => {
683                write!(f, "&{} fake deep {place:?}", region_to_string(*r))
684            }
685            Rvalue::RawPtr(mutbl, place) => write!(f, "&raw {} {place:?}", mutbl.ptr_str()),
686            Rvalue::Discriminant(place) => write!(f, "discriminant({place:?})"),
687            Rvalue::BinaryOp(bin_op, op1, op2) => write!(f, "{bin_op:?}({op1:?}, {op2:?})"),
688            Rvalue::NullaryOp(null_op, ty) => write!(f, "{null_op:?}({ty:?})"),
689            Rvalue::UnaryOp(un_op, op) => write!(f, "{un_op:?}({op:?})"),
690            Rvalue::Aggregate(AggregateKind::Adt(def_id, variant_idx, args, _, _), operands) => {
691                let (fname, variant_name) = rustc_middle::ty::tls::with(|tcx| {
692                    let variant_name = tcx.adt_def(*def_id).variant(*variant_idx).name;
693                    let fname = tcx.def_path(*def_id).data.iter().join("::");
694                    (fname, variant_name)
695                });
696                write!(f, "{fname}::{variant_name}")?;
697                if !args.is_empty() {
698                    write!(f, "<{:?}>", args.iter().format(", "),)?;
699                }
700                if !operands.is_empty() {
701                    write!(f, "({:?})", operands.iter().format(", "))?;
702                }
703                Ok(())
704            }
705            Rvalue::Aggregate(AggregateKind::Closure(def_id, args), operands) => {
706                write!(
707                    f,
708                    "closure({}, {args:?}, {:?})",
709                    def_id_to_string(*def_id),
710                    operands.iter().format(", ")
711                )
712            }
713            Rvalue::Aggregate(AggregateKind::Coroutine(def_id, args), operands) => {
714                write!(
715                    f,
716                    "generator({}, {args:?}, {:?})",
717                    def_id_to_string(*def_id),
718                    operands.iter().format(", ")
719                )
720            }
721            Rvalue::Aggregate(AggregateKind::Array(_), args) => {
722                write!(f, "[{:?}]", args.iter().format(", "))
723            }
724            Rvalue::Aggregate(AggregateKind::Tuple, args) => {
725                write!(f, "({:?})", args.iter().format(", "))
726            }
727            Rvalue::Len(place) => write!(f, "Len({place:?})"),
728            Rvalue::Cast(kind, op, ty) => write!(f, "{op:?} as {ty:?} [{kind:?}]"),
729            Rvalue::Repeat(op, c) => write!(f, "[{op:?}; {c:?}]"),
730            Rvalue::ShallowInitBox(op, ty) => write!(f, "ShallowInitBox({op:?}, {ty:?})"),
731        }
732    }
733}
734
735impl fmt::Debug for PointerCast {
736    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
737        match self {
738            PointerCast::MutToConstPointer => write!(f, "MutToConstPointer"),
739            PointerCast::Unsize => write!(f, "Unsize"),
740            PointerCast::ClosureFnPointer => write!(f, "ClosureFnPointer"),
741            PointerCast::ReifyFnPointer => write!(f, "ReifyFnPointer"),
742        }
743    }
744}
745
746impl fmt::Debug for CastKind {
747    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
748        match self {
749            CastKind::IntToInt => write!(f, "IntToInt"),
750            CastKind::FloatToInt => write!(f, "FloatToInt"),
751            CastKind::IntToFloat => write!(f, "IntToFloat"),
752            CastKind::PtrToPtr => write!(f, "PtrToPtr"),
753            CastKind::PointerCoercion(c) => write!(f, "Pointer({c:?})"),
754            CastKind::PointerExposeProvenance => write!(f, "PointerExposeProvenance"),
755            CastKind::PointerWithExposedProvenance => write!(f, "PointerWithExposedProvenance"),
756        }
757    }
758}
759
760impl fmt::Debug for Operand {
761    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
762        match self {
763            Self::Copy(place) => write!(f, "copy {place:?}"),
764            Self::Move(place) => write!(f, "move {place:?}"),
765            Self::Constant(c) => write!(f, "{c:?}"),
766        }
767    }
768}
769
770impl fmt::Debug for Constant {
771    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
772        match self {
773            Constant::Int(n, int_ty) => write!(f, "{n}{}", int_ty.name_str()),
774            Constant::Uint(n, uint_ty) => write!(f, "{n}{}", uint_ty.name_str()),
775            Constant::Float(bits, float_ty) => write!(f, "{bits}{}", float_ty.name_str()),
776            Constant::Bool(b) => write!(f, "{b}"),
777            Constant::Unit => write!(f, "()"),
778            Constant::Str(s) => write!(f, "\"{s:?}\""),
779            Constant::Char(c) => write!(f, "\'{c}\'"),
780            Constant::Opaque(ty) => write!(f, "<opaque {ty:?}>"),
781            Constant::Param(p, _) => write!(f, "{p:?}"),
782            Constant::Unevaluated(ty, def_id) => write!(f, "<uneval {ty:?} from {def_id:?}>"),
783        }
784    }
785}
786
787fn opt_bb_to_str(bb: Option<BasicBlock>) -> String {
788    match bb {
789        Some(bb) => format!("{bb:?}"),
790        None => "None".to_string(),
791    }
792}