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