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