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