Skip to main content

rustc_utils/mir/
place.rs

1//! Utilities for [`Place`].
2
3use std::{borrow::Cow, collections::VecDeque};
4
5use log::{trace, warn};
6use rustc_abi::{FieldIdx, VariantIdx};
7use rustc_data_structures::fx::{FxHashMap as HashMap, FxHashSet as HashSet};
8use rustc_hir::def_id::DefId;
9use rustc_middle::{
10  mir::{
11    Body, HasLocalDecls, Local, Location, Mutability, Place, PlaceElem, PlaceRef,
12    ProjectionElem, RETURN_PLACE, VarDebugInfo, VarDebugInfoContents,
13    visit::{PlaceContext, Visitor},
14  },
15  ty::{self, AdtKind, Region, RegionKind, RegionVid, Ty, TyCtxt, TyKind, TypeVisitor},
16};
17use rustc_type_ir::Unnormalized;
18
19use crate::{AdtDefExt, SpanExt};
20
21/// A MIR [`Visitor`] which collects all [`Place`]s that appear in the visited object.
22#[derive(Default)]
23pub struct PlaceCollector<'tcx>(pub Vec<Place<'tcx>>);
24
25impl<'tcx> Visitor<'tcx> for PlaceCollector<'tcx> {
26  fn visit_place(
27    &mut self,
28    place: &Place<'tcx>,
29    _context: PlaceContext,
30    _location: Location,
31  ) {
32    self.0.push(*place);
33  }
34}
35
36/// Extension trait for [`Place`].
37pub trait PlaceExt<'tcx> {
38  /// Creates a new [`Place`].
39  fn make(local: Local, projection: &[PlaceElem<'tcx>], tcx: TyCtxt<'tcx>) -> Self;
40
41  /// Converts a [`PlaceRef`] into an owned [`Place`].
42  fn from_ref(place: PlaceRef<'tcx>, tcx: TyCtxt<'tcx>) -> Self;
43
44  /// Creates a new [`Place`] with an empty projection.
45  fn from_local(local: Local, tcx: TyCtxt<'tcx>) -> Self;
46
47  /// Returns true if `self` is a projection of an argument local.
48  fn is_arg(&self, body: &Body<'tcx>) -> bool;
49
50  /// Returns true if `self` cannot be resolved further to another place.
51  ///
52  /// This is true if one of the following is true:
53  /// - `self` contains no dereference (`*`) projections
54  /// - `self` is the dereference of (a projection of) an argument to `body`
55  /// - all dereferences in `self` are dereferences of a `Box`
56  fn is_direct(&self, body: &Body<'tcx>, tcx: TyCtxt<'tcx>) -> bool;
57
58  /// Returns an iterator over all prefixes of `self`'s projection that are references,
59  ///  along with the suffix of the remaining projection.
60  fn refs_in_projection(
61    self,
62    body: &Body<'tcx>,
63    tcx: TyCtxt<'tcx>,
64  ) -> impl Iterator<Item = (PlaceRef<'tcx>, &'tcx [PlaceElem<'tcx>])>;
65
66  /// Returns all possible projections of `self` that are references.
67  ///
68  /// The output data structure groups the resultant places based on the region of the references.
69  fn interior_pointers(
70    &self,
71    tcx: TyCtxt<'tcx>,
72    body: &Body<'tcx>,
73    def_id: DefId,
74  ) -> HashMap<RegionVid, Vec<(Place<'tcx>, Mutability)>>;
75
76  /// Returns all possible projections of `self` that do not go through a reference,
77  /// i.e. the set of fields directly in the structure referred by `self`.
78  fn interior_places(
79    &self,
80    tcx: TyCtxt<'tcx>,
81    body: &Body<'tcx>,
82    def_id: DefId,
83  ) -> HashSet<Place<'tcx>>;
84
85  /// Returns all possible projections of `self`.
86  fn interior_paths(
87    &self,
88    tcx: TyCtxt<'tcx>,
89    body: &Body<'tcx>,
90    def_id: DefId,
91  ) -> HashSet<Place<'tcx>>;
92
93  /// Returns a pretty representation of a place that uses debug info when available.
94  fn to_string(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> Option<String>;
95
96  /// Erases/normalizes information in a place to ensure stable comparisons between places.
97  ///
98  /// Consider a place `_1: &'1 <T as SomeTrait>::Foo[2]`.
99  ///   We might encounter this type with a different region, e.g. `&'2`.
100  ///   We might encounter this type with a more specific type for the associated type, e.g. `&'1 [i32][0]`.
101  /// To account for this variation, we normalize associated types,
102  ///   erase regions, and normalize projections.
103  fn normalize(&self, tcx: TyCtxt<'tcx>, def_id: DefId) -> Place<'tcx>;
104
105  /// Returns true if this place's base [`Local`] corresponds to code that is visible in the source.
106  fn is_source_visible(&self, tcx: TyCtxt, body: &Body) -> bool;
107}
108
109impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
110  fn make(local: Local, projection: &[PlaceElem<'tcx>], tcx: TyCtxt<'tcx>) -> Self {
111    Place {
112      local,
113      projection: tcx.mk_place_elems(projection),
114    }
115  }
116
117  fn from_ref(place: PlaceRef<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
118    Self::make(place.local, place.projection, tcx)
119  }
120
121  fn from_local(local: Local, tcx: TyCtxt<'tcx>) -> Self {
122    Place::make(local, &[], tcx)
123  }
124
125  fn is_arg(&self, body: &Body<'tcx>) -> bool {
126    let i = self.local.as_usize();
127    i > 0 && i - 1 < body.arg_count
128  }
129
130  fn is_direct(&self, body: &Body<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
131    !self.is_indirect()
132      || self.is_arg(body)
133      || self.refs_in_projection(body, tcx).next().is_none()
134  }
135
136  fn refs_in_projection(
137    self,
138    body: &Body<'tcx>,
139    tcx: TyCtxt<'tcx>,
140  ) -> impl Iterator<Item = (PlaceRef<'tcx>, &'tcx [PlaceElem<'tcx>])> {
141    self
142      .iter_projections()
143      .enumerate()
144      .filter_map(move |(i, (place_ref, elem))| match elem {
145        ProjectionElem::Deref => {
146          let ptr = PlaceRef {
147            local: self.local,
148            projection: &self.projection[.. i],
149          };
150          let after = &self.projection[i + 1 ..];
151          (!place_ref.ty(body.local_decls(), tcx).ty.is_box()).then_some((ptr, after))
152        }
153        _ => None,
154      })
155  }
156
157  fn interior_pointers(
158    &self,
159    tcx: TyCtxt<'tcx>,
160    body: &Body<'tcx>,
161    def_id: DefId,
162  ) -> HashMap<RegionVid, Vec<(Place<'tcx>, Mutability)>> {
163    let ty = self.ty(body.local_decls(), tcx).ty;
164    let mut region_collector = RegionVisitor::<RegionMemberCollector>::new(
165      tcx,
166      def_id,
167      *self,
168      if
169      /*shallow*/
170      false {
171        StoppingCondition::AfterRefs
172      } else {
173        StoppingCondition::None
174      },
175    );
176    region_collector.visit_ty(ty);
177    region_collector.into_inner().0
178  }
179
180  fn interior_places(
181    &self,
182    tcx: TyCtxt<'tcx>,
183    body: &Body<'tcx>,
184    def_id: DefId,
185  ) -> HashSet<Place<'tcx>> {
186    let ty = self.ty(body.local_decls(), tcx).ty;
187    let mut region_collector = RegionVisitor::<VisitedPlacesCollector>::new(
188      tcx,
189      def_id,
190      *self,
191      StoppingCondition::BeforeRefs,
192    );
193    region_collector.visit_ty(ty);
194    region_collector.into_inner().0
195  }
196
197  fn interior_paths(
198    &self,
199    tcx: TyCtxt<'tcx>,
200    body: &Body<'tcx>,
201    def_id: DefId,
202  ) -> HashSet<Place<'tcx>> {
203    let ty = self.ty(body.local_decls(), tcx).ty;
204    let mut region_collector = RegionVisitor::<VisitedPlacesCollector>::new(
205      tcx,
206      def_id,
207      *self,
208      StoppingCondition::None,
209    );
210    region_collector.visit_ty(ty);
211    region_collector.into_inner().0
212  }
213
214  fn to_string(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> Option<String> {
215    // Get the local's debug name from the Body's VarDebugInfo
216    let local_name = if self.local == RETURN_PLACE {
217      Cow::Borrowed("RETURN")
218    } else {
219      let get_local_name = |info: &VarDebugInfo<'tcx>| match info.value {
220        VarDebugInfoContents::Place(place) if place.local == self.local => info
221          .source_info
222          .span
223          .as_local(body.span)
224          .map(|_| info.name.to_string()),
225        _ => None,
226      };
227      let local_name = body.var_debug_info.iter().find_map(get_local_name)?;
228      Cow::Owned(local_name)
229    };
230
231    #[derive(Copy, Clone)]
232    enum ElemPosition {
233      Prefix,
234      Suffix,
235    }
236
237    // Turn each PlaceElem into a prefix (e.g. * for deref) or a suffix
238    // (e.g. .field for projection).
239    let elem_to_string = |(index, (place, elem)): (
240      usize,
241      (PlaceRef<'tcx>, PlaceElem<'tcx>),
242    )|
243     -> (ElemPosition, Cow<'static, str>) {
244      match elem {
245        ProjectionElem::Deref => (ElemPosition::Prefix, "*".into()),
246
247        ProjectionElem::Field(field, _) => {
248          let ty = place.ty(&body.local_decls, tcx).ty;
249
250          let field_name = match ty.kind() {
251            TyKind::Adt(def, _substs) => {
252              let fields = match def.adt_kind() {
253                AdtKind::Struct => &def.non_enum_variant().fields,
254                AdtKind::Enum => {
255                  let Some(PlaceElem::Downcast(_, variant_idx)) =
256                    self.projection.get(index - 1)
257                  else {
258                    unimplemented!()
259                  };
260                  &def.variant(*variant_idx).fields
261                }
262                kind => unimplemented!("{kind:?}"),
263              };
264
265              fields[field].ident(tcx).to_string()
266            }
267
268            TyKind::Tuple(_) => field.as_usize().to_string(),
269
270            TyKind::Closure(def_id, _substs) => match def_id.as_local() {
271              Some(local_def_id) => {
272                let captures = tcx.closure_captures(local_def_id);
273                captures[field.as_usize()].var_ident.to_string()
274              }
275              None => field.as_usize().to_string(),
276            },
277
278            kind => unimplemented!("{kind:?}"),
279          };
280
281          (ElemPosition::Suffix, format!(".{field_name}").into())
282        }
283        ProjectionElem::Downcast(sym, _) => {
284          let variant = sym.map(|s| s.to_string()).unwrap_or_else(|| "??".into());
285          (ElemPosition::Suffix, format!("@{variant}").into())
286        }
287
288        ProjectionElem::Index(_) => (ElemPosition::Suffix, "[_]".into()),
289        kind => unimplemented!("{kind:?}"),
290      }
291    };
292
293    let (positions, contents): (Vec<_>, Vec<_>) = self
294      .iter_projections()
295      .enumerate()
296      .map(elem_to_string)
297      .unzip();
298
299    // Combine the prefixes and suffixes into a corresponding sequence
300    let mut parts = VecDeque::from([local_name]);
301    for (i, string) in contents.into_iter().enumerate() {
302      match positions[i] {
303        ElemPosition::Prefix => {
304          parts.push_front(string);
305          if matches!(positions.get(i + 1), Some(ElemPosition::Suffix)) {
306            parts.push_front(Cow::Borrowed("("));
307            parts.push_back(Cow::Borrowed(")"));
308          }
309        }
310        ElemPosition::Suffix => parts.push_back(string),
311      }
312    }
313
314    let full = parts.make_contiguous().join("");
315    Some(full)
316  }
317
318  fn normalize(&self, tcx: TyCtxt<'tcx>, def_id: DefId) -> Place<'tcx> {
319    let place = tcx.normalize_erasing_regions(
320      tcx.typing_env_normalized_for_post_analysis(def_id),
321      Unnormalized::new_wip(*self),
322    );
323
324    let projection = place
325      .projection
326      .into_iter()
327      .filter_map(|elem| match elem {
328        // Map all indexes [i] to [0] since they should be considered equal
329        ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => {
330          Some(ProjectionElem::Index(Local::from_usize(0)))
331        }
332        // Ignore subslices, they should be treated the same as the
333        // full slice
334        ProjectionElem::Subslice { .. } => None,
335        _ => Some(elem),
336      })
337      .collect::<Vec<_>>();
338
339    Place::make(place.local, &projection, tcx)
340  }
341
342  fn is_source_visible(&self, _tcx: TyCtxt, body: &Body) -> bool {
343    let local = self.local;
344    let local_info = &body.local_decls[local];
345    let is_loc = local_info.is_user_variable();
346    let from_desugaring = local_info.from_compiler_desugaring();
347    let from_expansion = local_info.source_info.span.from_expansion();
348
349    // The assumption is that for a place to be source visible it needs to:
350    // 1. Be a local declaration.
351    // 2. Not be from a compiler desugaring.
352    // 3. Not be from a macro expansion (basically also a desugaring).
353    is_loc && !from_desugaring && !from_expansion
354  }
355}
356
357#[derive(Copy, Clone)]
358enum StoppingCondition {
359  None,
360  BeforeRefs,
361  AfterRefs,
362}
363
364trait RegionVisitorDispatcher<'tcx> {
365  fn on_visit_place(&mut self, _: Place<'tcx>) {}
366  fn on_visit_type(&mut self, _: Ty<'tcx>) {}
367  fn on_visit_region_member(&mut self, _: RegionVid, _: Place<'tcx>, _: Mutability) {}
368}
369
370#[derive(Default)]
371struct VisitedPlacesCollector<'tcx>(HashSet<Place<'tcx>>);
372
373impl<'tcx> RegionVisitorDispatcher<'tcx> for VisitedPlacesCollector<'tcx> {
374  fn on_visit_place(&mut self, place: Place<'tcx>) {
375    self.0.insert(place);
376  }
377}
378
379#[derive(Default)]
380struct RegionMemberCollector<'tcx>(HashMap<RegionVid, Vec<(Place<'tcx>, Mutability)>>);
381
382impl<'tcx> RegionVisitorDispatcher<'tcx> for RegionMemberCollector<'tcx> {
383  fn on_visit_region_member(
384    &mut self,
385    key: RegionVid,
386    place: Place<'tcx>,
387    mutbl: Mutability,
388  ) {
389    self.0.entry(key).or_default().push((place, mutbl));
390  }
391}
392
393struct RegionVisitor<'tcx, Dispatcher> {
394  tcx: TyCtxt<'tcx>,
395  def_id: DefId,
396  /// Base local of the place we are collecting regions for.
397  local: Local,
398  /// List of projections to apply to the base local in order to reach the
399  /// child place currently under consideration.
400  ///
401  /// Starts out as the projections in the input place.
402  place_stack: Vec<PlaceElem<'tcx>>,
403  /// Sequence of parent types to reach the place currently under consideration.
404  /// Correspond to the projections in `place_stack`.
405  ty_stack: Vec<Ty<'tcx>>,
406  /// Callbacks
407  dispatcher: Dispatcher,
408  stop_at: StoppingCondition,
409}
410
411impl<'tcx, Dispatcher: Default> RegionVisitor<'tcx, Dispatcher> {
412  /// Construct a new [`CollectRegions`] visitor.
413  ///
414  /// By itself the visitor only implements the traversal. Actual accumulation
415  /// of useful information is done by the `Dispatcher`.
416  fn new(
417    tcx: TyCtxt<'tcx>,
418    def_id: DefId,
419    place: Place<'tcx>,
420    stop_at: StoppingCondition,
421  ) -> Self {
422    Self {
423      tcx,
424      def_id,
425      local: place.local,
426      place_stack: place.projection.to_vec(),
427      ty_stack: Vec::new(),
428      dispatcher: Default::default(),
429      stop_at,
430    }
431  }
432
433  fn into_inner(self) -> Dispatcher {
434    self.dispatcher
435  }
436}
437
438/// Used to describe aliases of owned and raw pointers.
439pub const UNKNOWN_REGION: RegionVid = RegionVid::MAX;
440
441impl<'tcx, Dispatcher: RegionVisitorDispatcher<'tcx>> TypeVisitor<TyCtxt<'tcx>>
442  for RegionVisitor<'tcx, Dispatcher>
443{
444  fn visit_ty(&mut self, ty: Ty<'tcx>) {
445    let tcx = self.tcx;
446    if self.ty_stack.contains(&ty) {
447      return;
448    }
449
450    trace!(
451      "exploring {:?} with {ty:?}",
452      Place::make(self.local, &self.place_stack, tcx)
453    );
454
455    self.ty_stack.push(ty);
456
457    match ty.kind() {
458      _ if ty.is_box() => {
459        self.place_stack.push(ProjectionElem::Deref);
460        self.visit_ty(ty.boxed_ty().expect("Cannot unbox boxed type??"));
461        self.place_stack.pop();
462      }
463
464      TyKind::Tuple(fields) => {
465        for (i, field) in fields.iter().enumerate() {
466          self
467            .place_stack
468            .push(ProjectionElem::Field(FieldIdx::from_usize(i), field));
469          self.visit_ty(field);
470          self.place_stack.pop();
471        }
472      }
473
474      TyKind::Adt(adt_def, subst) => match adt_def.adt_kind() {
475        ty::AdtKind::Struct => {
476          for (i, field) in adt_def.all_visible_fields(self.def_id, tcx).enumerate() {
477            let ty = field.ty(tcx, subst);
478            self
479              .place_stack
480              .push(ProjectionElem::Field(FieldIdx::from_usize(i), ty));
481            self.visit_ty(ty);
482            self.place_stack.pop();
483          }
484        }
485        ty::AdtKind::Union => {
486          // unsafe, so ignore
487        }
488        ty::AdtKind::Enum => {
489          for (i, variant) in adt_def.variants().iter().enumerate() {
490            let variant_index = VariantIdx::from_usize(i);
491            let cast = PlaceElem::Downcast(
492              Some(adt_def.variant(variant_index).ident(tcx).name),
493              variant_index,
494            );
495            self.place_stack.push(cast);
496            for (j, field) in variant.fields.iter().enumerate() {
497              let ty = field.ty(tcx, subst);
498              let field = ProjectionElem::Field(FieldIdx::from_usize(j), ty);
499              self.place_stack.push(field);
500              self.visit_ty(ty);
501              self.place_stack.pop();
502            }
503            self.place_stack.pop();
504          }
505        }
506      },
507
508      TyKind::Array(elem_ty, _) | TyKind::Slice(elem_ty) => {
509        self
510          .place_stack
511          .push(ProjectionElem::Index(Local::from_usize(0)));
512        self.visit_ty(*elem_ty);
513        self.place_stack.pop();
514      }
515
516      TyKind::Ref(region, elem_ty, _) => match self.stop_at {
517        StoppingCondition::None => {
518          self.visit_region(*region);
519          self.place_stack.push(ProjectionElem::Deref);
520          self.visit_ty(*elem_ty);
521          self.place_stack.pop();
522        }
523        StoppingCondition::AfterRefs => {
524          self.visit_region(*region);
525        }
526        StoppingCondition::BeforeRefs => {}
527      },
528
529      TyKind::Closure(_, substs) | TyKind::Coroutine(_, substs) => {
530        self.visit_ty(substs.as_closure().tupled_upvars_ty());
531      }
532
533      TyKind::RawPtr(ty, _) => {
534        self.visit_region(Region::new_var(tcx, UNKNOWN_REGION));
535        self.place_stack.push(ProjectionElem::Deref);
536        self.visit_ty(*ty);
537        self.place_stack.pop();
538      }
539
540      TyKind::FnDef(..)
541      | TyKind::FnPtr(..)
542      | TyKind::Foreign(..)
543      | TyKind::Dynamic(..)
544      | TyKind::Param(..)
545      | TyKind::Never => {}
546
547      _ if ty.is_primitive_ty() => {}
548
549      _ => warn!("unimplemented {ty:?} ({:?})", ty.kind()),
550    }
551
552    // let inherent_impls = tcx.inherent_impls(self.def_id);
553    // let traits = tcx.infer_ctxt().enter(|infcx| {
554    //   let param_env = tcx.param_env(self.def_id);
555    //   self
556    //     .tcx
557    //     .all_traits()
558    //     .filter(|trait_def_id| {
559    //       let result = infcx.type_implements_trait(*trait_def_id, ty, params, param_env);
560    //       matches!(
561    //         result,
562    //         EvaluationResult::EvaluatedToOk
563    //           | EvaluationResult::EvaluatedToOkModuloRegions
564    //       )
565    //     })
566    //     .collect::<Vec<_>>()
567    // });
568
569    // let fns = inherent_impls.iter().chain(&traits).flat_map(|def_id| {
570    //   let items = tcx.associated_items(def_id).in_definition_order();
571    //   items.filter_map(|item| match item.kind {
572    //     AssocKind::Fn => Some(tcx.fn_sig(item.def_id)),
573    //     _ => None,
574    //   })
575    // });
576
577    // // for f in fns {
578    // //   f.
579    // // }
580
581    self
582      .dispatcher
583      .on_visit_place(Place::make(self.local, &self.place_stack, tcx));
584
585    self.dispatcher.on_visit_type(ty);
586
587    self.ty_stack.pop();
588  }
589
590  fn visit_region(&mut self, region: ty::Region<'tcx>) -> Self::Result {
591    trace!("visiting region {region:?}");
592    let region = match region.kind() {
593      RegionKind::ReVar(region) => region,
594      RegionKind::ReStatic => RegionVid::from_usize(0),
595      RegionKind::ReErased | RegionKind::ReLateParam(_) => {
596        return;
597      }
598      _ => unreachable!("{:?}: {:?}", self.ty_stack.first().unwrap(), region),
599    };
600
601    let mutability = if self
602      .ty_stack
603      .iter()
604      .any(|ty| ty.is_ref() && ty.ref_mutability().unwrap() == Mutability::Not)
605    {
606      Mutability::Not
607    } else {
608      Mutability::Mut
609    };
610
611    let place = Place::make(self.local, &self.place_stack, self.tcx);
612
613    self
614      .dispatcher
615      .on_visit_region_member(region, place, mutability);
616
617    // for initialization setup of Aliases::build
618    //
619    // uses `once_with` so that the place is only created when the iterator is
620    // not ignored (e.g. `Places != Ignore`)
621    self
622      .dispatcher
623      .on_visit_place(self.tcx.mk_place_deref(place));
624  }
625}
626
627#[cfg(test)]
628mod test {
629  use rustc_borrowck::consumers::BodyWithBorrowckFacts;
630  use rustc_hir::BodyId;
631  use rustc_middle::{
632    mir::{Place, PlaceElem},
633    ty::TyCtxt,
634  };
635
636  use crate::{
637    BodyExt, PlaceExt,
638    test_utils::{self, Placer, compare_sets},
639  };
640
641  #[test]
642  fn test_place_arg_direct() {
643    let input = r"
644fn foobar(x: &i32) {
645  let y = 1;
646  let z = &y;
647  let k = Box::new(*x);
648  let ref_k = &k;
649  let box_ref = Box::new(x);
650}
651";
652    test_utils::compile_body(input, |tcx, _, body_with_facts| {
653      let body = &body_with_facts.body;
654      let name_map = body.debug_info_name_map();
655      let x = Place::from_local(name_map["x"], tcx);
656      assert!(x.is_arg(body));
657      assert!(x.is_direct(body, tcx));
658      assert!(Place::make(x.local, &[PlaceElem::Deref], tcx).is_direct(body, tcx));
659
660      let y = Place::from_local(name_map["y"], tcx);
661      assert!(!y.is_arg(body));
662      assert!(y.is_direct(body, tcx));
663
664      let z = Place::from_local(name_map["z"], tcx);
665      assert!(!z.is_arg(body));
666      assert!(z.is_direct(body, tcx));
667      assert!(!Place::make(z.local, &[PlaceElem::Deref], tcx).is_direct(body, tcx));
668
669      let k = Place::from_local(name_map["k"], tcx);
670      assert!(!k.is_arg(body));
671      assert!(k.is_direct(body, tcx));
672      assert!(Place::make(k.local, &[PlaceElem::Deref], tcx).is_direct(body, tcx));
673      let deref_k = Place::make(k.local, &[PlaceElem::Deref], tcx);
674      assert!(deref_k.is_direct(body, tcx));
675      assert!(!deref_k.is_arg(body));
676      assert_eq!(deref_k.refs_in_projection(body, tcx).count(), 0);
677
678      let ref_k = Place::from_local(name_map["ref_k"], tcx);
679      assert!(!ref_k.is_arg(body));
680      assert!(k.is_direct(body, tcx));
681      let deref_ref_k =
682        Place::make(ref_k.local, &[PlaceElem::Deref, PlaceElem::Deref], tcx);
683      assert!(!deref_ref_k.is_direct(body, tcx));
684      assert_eq!(deref_ref_k.refs_in_projection(body, tcx).count(), 1);
685
686      let box_ref = Place::from_local(name_map["box_ref"], tcx);
687      assert!(!box_ref.is_arg(body));
688      assert!(!box_ref.is_indirect());
689      let box_ref_deref = Place::make(box_ref.local, &[PlaceElem::Deref], tcx);
690      assert_eq!(box_ref_deref.refs_in_projection(body, tcx).count(), 0);
691      assert!(box_ref_deref.is_direct(body, tcx));
692      let box_ref_deref_deref = box_ref_deref.project_deeper(&[PlaceElem::Deref], tcx);
693      assert_eq!(box_ref_deref_deref.refs_in_projection(body, tcx).count(), 1);
694      assert!(!box_ref_deref_deref.is_direct(body, tcx));
695    });
696  }
697
698  #[test]
699  fn test_place_to_string() {
700    let input = r"
701struct Point { x: usize, y: usize }
702fn main() {
703  let x = (0, 0);
704  let y = Some(1);
705  let z = &[Some((0, 1))];
706  let w = (&y,);
707  let p = &Point { x: 0, y: 0 };
708}";
709    test_utils::compile_body(input, |tcx, _, body_with_facts| {
710      let body = &body_with_facts.body;
711      let p = Placer::new(tcx, body);
712
713      let x = p.local("x").mk();
714      let x_1 = p.local("x").field(1).mk();
715      let y_some_0 = p.local("y").downcast(1).field(0).mk();
716      let z_deref_some_0_1 = p
717        .local("z")
718        .deref()
719        .index(0)
720        .downcast(1)
721        .field(0)
722        .field(1)
723        .mk();
724      let w_0_deref = p.local("w").field(0).deref().mk();
725      let w_0_deref_some = p.local("w").field(0).deref().downcast(1).mk();
726      let p_deref_x = p.local("p").deref().field(0).mk();
727
728      let tests = [
729        (x, "x"),
730        (x_1, "x.1"),
731        (y_some_0, "y@Some.0"),
732        (z_deref_some_0_1, "(*z)[_]@Some.0.1"),
733        (w_0_deref, "*w.0"),
734        (w_0_deref_some, "(*w.0)@Some"),
735        (p_deref_x, "(*p).x"),
736      ];
737
738      for (place, expected) in tests {
739        assert_eq!(place.to_string(tcx, body).unwrap(), expected);
740      }
741    });
742  }
743
744  #[test]
745  fn test_place_visitors() {
746    let input = r"
747fn main() {
748  let x = 0;
749  let y = (0, &x);
750}
751";
752    fn callback<'tcx>(
753      tcx: TyCtxt<'tcx>,
754      body_id: BodyId,
755      body_with_facts: &BodyWithBorrowckFacts<'tcx>,
756    ) {
757      let body = &body_with_facts.body;
758      let def_id = tcx.hir_body_owner_def_id(body_id).to_def_id();
759      let p = Placer::new(tcx, body);
760
761      let y = p.local("y").mk();
762      let y0 = p.local("y").field(0).mk();
763      let y1 = p.local("y").field(1).mk();
764      let y1_deref = p.local("y").field(1).deref().mk();
765
766      compare_sets(y.interior_paths(tcx, body, def_id), [y, y0, y1, y1_deref]);
767
768      compare_sets(y.interior_places(tcx, body, def_id), [y, y0, y1]);
769
770      compare_sets(
771        y.interior_pointers(tcx, body, def_id)
772          .into_values()
773          .flat_map(|vs| vs.into_iter().map(|(p, _)| p)),
774        [y1],
775      );
776    }
777    test_utils::compile_body(input, callback);
778  }
779}