1use 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_infer::infer::TyCtxtInferExt;
10use rustc_middle::{
11 mir::{
12 Body, HasLocalDecls, Local, Location, Mutability, Place, PlaceElem, PlaceRef,
13 ProjectionElem, RETURN_PLACE, VarDebugInfo, VarDebugInfoContents,
14 visit::{PlaceContext, Visitor},
15 },
16 traits::ObligationCause,
17 ty::{self, AdtKind, Region, RegionKind, RegionVid, Ty, TyCtxt, TyKind, TypeVisitor},
18};
19use rustc_trait_selection::traits::NormalizeExt;
20use rustc_type_ir::TypingMode;
21
22use crate::{AdtDefExt, SpanExt};
23
24#[derive(Default)]
26pub struct PlaceCollector<'tcx>(pub Vec<Place<'tcx>>);
27
28impl<'tcx> Visitor<'tcx> for PlaceCollector<'tcx> {
29 fn visit_place(
30 &mut self,
31 place: &Place<'tcx>,
32 _context: PlaceContext,
33 _location: Location,
34 ) {
35 self.0.push(*place);
36 }
37}
38
39pub trait PlaceExt<'tcx> {
41 fn make(local: Local, projection: &[PlaceElem<'tcx>], tcx: TyCtxt<'tcx>) -> Self;
43
44 fn from_ref(place: PlaceRef<'tcx>, tcx: TyCtxt<'tcx>) -> Self;
46
47 fn from_local(local: Local, tcx: TyCtxt<'tcx>) -> Self;
49
50 fn is_arg(&self, body: &Body<'tcx>) -> bool;
52
53 fn is_direct(&self, body: &Body<'tcx>, tcx: TyCtxt<'tcx>) -> bool;
60
61 fn refs_in_projection(
64 self,
65 body: &Body<'tcx>,
66 tcx: TyCtxt<'tcx>,
67 ) -> impl Iterator<Item = (PlaceRef<'tcx>, &'tcx [PlaceElem<'tcx>])>;
68
69 fn interior_pointers(
73 &self,
74 tcx: TyCtxt<'tcx>,
75 body: &Body<'tcx>,
76 def_id: DefId,
77 ) -> HashMap<RegionVid, Vec<(Place<'tcx>, Mutability)>>;
78
79 fn interior_places(
82 &self,
83 tcx: TyCtxt<'tcx>,
84 body: &Body<'tcx>,
85 def_id: DefId,
86 ) -> HashSet<Place<'tcx>>;
87
88 fn interior_paths(
90 &self,
91 tcx: TyCtxt<'tcx>,
92 body: &Body<'tcx>,
93 def_id: DefId,
94 ) -> HashSet<Place<'tcx>>;
95
96 fn to_string(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> Option<String>;
98
99 fn normalize(&self, tcx: TyCtxt<'tcx>, def_id: DefId) -> Place<'tcx>;
107
108 fn is_source_visible(&self, tcx: TyCtxt, body: &Body) -> bool;
110}
111
112impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
113 fn make(local: Local, projection: &[PlaceElem<'tcx>], tcx: TyCtxt<'tcx>) -> Self {
114 Place {
115 local,
116 projection: tcx.mk_place_elems(projection),
117 }
118 }
119
120 fn from_ref(place: PlaceRef<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
121 Self::make(place.local, place.projection, tcx)
122 }
123
124 fn from_local(local: Local, tcx: TyCtxt<'tcx>) -> Self {
125 Place::make(local, &[], tcx)
126 }
127
128 fn is_arg(&self, body: &Body<'tcx>) -> bool {
129 let i = self.local.as_usize();
130 i > 0 && i - 1 < body.arg_count
131 }
132
133 fn is_direct(&self, body: &Body<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
134 !self.is_indirect()
135 || self.is_arg(body)
136 || self.refs_in_projection(body, tcx).next().is_none()
137 }
138
139 fn refs_in_projection(
140 self,
141 body: &Body<'tcx>,
142 tcx: TyCtxt<'tcx>,
143 ) -> impl Iterator<Item = (PlaceRef<'tcx>, &'tcx [PlaceElem<'tcx>])> {
144 self
145 .iter_projections()
146 .enumerate()
147 .filter_map(move |(i, (place_ref, elem))| match elem {
148 ProjectionElem::Deref => {
149 let ptr = PlaceRef {
150 local: self.local,
151 projection: &self.projection[.. i],
152 };
153 let after = &self.projection[i + 1 ..];
154 (!place_ref.ty(body.local_decls(), tcx).ty.is_box()).then_some((ptr, after))
155 }
156 _ => None,
157 })
158 }
159
160 fn interior_pointers(
161 &self,
162 tcx: TyCtxt<'tcx>,
163 body: &Body<'tcx>,
164 def_id: DefId,
165 ) -> HashMap<RegionVid, Vec<(Place<'tcx>, Mutability)>> {
166 let ty = self.ty(body.local_decls(), tcx).ty;
167 let mut region_collector = RegionVisitor::<RegionMemberCollector>::new(
168 tcx,
169 def_id,
170 *self,
171 if
172 false {
174 StoppingCondition::AfterRefs
175 } else {
176 StoppingCondition::None
177 },
178 );
179 region_collector.visit_ty(ty);
180 region_collector.into_inner().0
181 }
182
183 fn interior_places(
184 &self,
185 tcx: TyCtxt<'tcx>,
186 body: &Body<'tcx>,
187 def_id: DefId,
188 ) -> HashSet<Place<'tcx>> {
189 let ty = self.ty(body.local_decls(), tcx).ty;
190 let mut region_collector = RegionVisitor::<VisitedPlacesCollector>::new(
191 tcx,
192 def_id,
193 *self,
194 StoppingCondition::BeforeRefs,
195 );
196 region_collector.visit_ty(ty);
197 region_collector.into_inner().0
198 }
199
200 fn interior_paths(
201 &self,
202 tcx: TyCtxt<'tcx>,
203 body: &Body<'tcx>,
204 def_id: DefId,
205 ) -> HashSet<Place<'tcx>> {
206 let ty = self.ty(body.local_decls(), tcx).ty;
207 let mut region_collector = RegionVisitor::<VisitedPlacesCollector>::new(
208 tcx,
209 def_id,
210 *self,
211 StoppingCondition::None,
212 );
213 region_collector.visit_ty(ty);
214 region_collector.into_inner().0
215 }
216
217 fn to_string(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> Option<String> {
218 let local_name = if self.local == RETURN_PLACE {
220 Cow::Borrowed("RETURN")
221 } else {
222 let get_local_name = |info: &VarDebugInfo<'tcx>| match info.value {
223 VarDebugInfoContents::Place(place) if place.local == self.local => info
224 .source_info
225 .span
226 .as_local(body.span)
227 .map(|_| info.name.to_string()),
228 _ => None,
229 };
230 let local_name = body.var_debug_info.iter().find_map(get_local_name)?;
231 Cow::Owned(local_name)
232 };
233
234 #[derive(Copy, Clone)]
235 enum ElemPosition {
236 Prefix,
237 Suffix,
238 }
239
240 let elem_to_string = |(index, (place, elem)): (
243 usize,
244 (PlaceRef<'tcx>, PlaceElem<'tcx>),
245 )|
246 -> (ElemPosition, Cow<'static, str>) {
247 match elem {
248 ProjectionElem::Deref => (ElemPosition::Prefix, "*".into()),
249
250 ProjectionElem::Field(field, _) => {
251 let ty = place.ty(&body.local_decls, tcx).ty;
252
253 let field_name = match ty.kind() {
254 TyKind::Adt(def, _substs) => {
255 let fields = match def.adt_kind() {
256 AdtKind::Struct => &def.non_enum_variant().fields,
257 AdtKind::Enum => {
258 let Some(PlaceElem::Downcast(_, variant_idx)) =
259 self.projection.get(index - 1)
260 else {
261 unimplemented!()
262 };
263 &def.variant(*variant_idx).fields
264 }
265 kind => unimplemented!("{kind:?}"),
266 };
267
268 fields[field].ident(tcx).to_string()
269 }
270
271 TyKind::Tuple(_) => field.as_usize().to_string(),
272
273 TyKind::Closure(def_id, _substs) => match def_id.as_local() {
274 Some(local_def_id) => {
275 let captures = tcx.closure_captures(local_def_id);
276 captures[field.as_usize()].var_ident.to_string()
277 }
278 None => field.as_usize().to_string(),
279 },
280
281 kind => unimplemented!("{kind:?}"),
282 };
283
284 (ElemPosition::Suffix, format!(".{field_name}").into())
285 }
286 ProjectionElem::Downcast(sym, _) => {
287 let variant = sym.map(|s| s.to_string()).unwrap_or_else(|| "??".into());
288 (ElemPosition::Suffix, format!("@{variant}",).into())
289 }
290
291 ProjectionElem::Index(_) => (ElemPosition::Suffix, "[_]".into()),
292 kind => unimplemented!("{kind:?}"),
293 }
294 };
295
296 let (positions, contents): (Vec<_>, Vec<_>) = self
297 .iter_projections()
298 .enumerate()
299 .map(elem_to_string)
300 .unzip();
301
302 let mut parts = VecDeque::from([local_name]);
304 for (i, string) in contents.into_iter().enumerate() {
305 match positions[i] {
306 ElemPosition::Prefix => {
307 parts.push_front(string);
308 if matches!(positions.get(i + 1), Some(ElemPosition::Suffix)) {
309 parts.push_front(Cow::Borrowed("("));
310 parts.push_back(Cow::Borrowed(")"));
311 }
312 }
313 ElemPosition::Suffix => parts.push_back(string),
314 }
315 }
316
317 let full = parts.make_contiguous().join("");
318 Some(full)
319 }
320
321 fn normalize(&self, tcx: TyCtxt<'tcx>, def_id: DefId) -> Place<'tcx> {
322 let param_env = tcx.param_env(def_id);
323 let place = tcx.erase_regions(*self);
324 let infcx = tcx.infer_ctxt().build(TypingMode::post_borrowck_analysis(
325 tcx,
326 def_id.expect_local(),
327 ));
328 let place = infcx
329 .at(&ObligationCause::dummy(), param_env)
330 .normalize(place)
331 .value;
332
333 let projection = place
334 .projection
335 .into_iter()
336 .filter_map(|elem| match elem {
337 ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => {
339 Some(ProjectionElem::Index(Local::from_usize(0)))
340 }
341 ProjectionElem::Subslice { .. } => None,
344 _ => Some(elem),
345 })
346 .collect::<Vec<_>>();
347
348 Place::make(place.local, &projection, tcx)
349 }
350
351 fn is_source_visible(&self, _tcx: TyCtxt, body: &Body) -> bool {
352 let local = self.local;
353 let local_info = &body.local_decls[local];
354 let is_loc = local_info.is_user_variable();
355 let from_desugaring = local_info.from_compiler_desugaring();
356 let from_expansion = local_info.source_info.span.from_expansion();
357
358 is_loc && !from_desugaring && !from_expansion
363 }
364}
365
366#[derive(Copy, Clone)]
367enum StoppingCondition {
368 None,
369 BeforeRefs,
370 AfterRefs,
371}
372
373trait RegionVisitorDispatcher<'tcx> {
374 fn on_visit_place(&mut self, _: Place<'tcx>) {}
375 fn on_visit_type(&mut self, _: Ty<'tcx>) {}
376 fn on_visit_region_member(&mut self, _: RegionVid, _: Place<'tcx>, _: Mutability) {}
377}
378
379#[derive(Default)]
380struct VisitedPlacesCollector<'tcx>(HashSet<Place<'tcx>>);
381
382impl<'tcx> RegionVisitorDispatcher<'tcx> for VisitedPlacesCollector<'tcx> {
383 fn on_visit_place(&mut self, place: Place<'tcx>) {
384 self.0.insert(place);
385 }
386}
387
388#[derive(Default)]
389struct RegionMemberCollector<'tcx>(HashMap<RegionVid, Vec<(Place<'tcx>, Mutability)>>);
390
391impl<'tcx> RegionVisitorDispatcher<'tcx> for RegionMemberCollector<'tcx> {
392 fn on_visit_region_member(
393 &mut self,
394 key: RegionVid,
395 place: Place<'tcx>,
396 mutbl: Mutability,
397 ) {
398 self.0.entry(key).or_default().push((place, mutbl));
399 }
400}
401
402struct RegionVisitor<'tcx, Dispatcher> {
403 tcx: TyCtxt<'tcx>,
404 def_id: DefId,
405 local: Local,
407 place_stack: Vec<PlaceElem<'tcx>>,
412 ty_stack: Vec<Ty<'tcx>>,
415 dispatcher: Dispatcher,
417 stop_at: StoppingCondition,
418}
419
420impl<'tcx, Dispatcher: Default> RegionVisitor<'tcx, Dispatcher> {
421 fn new(
426 tcx: TyCtxt<'tcx>,
427 def_id: DefId,
428 place: Place<'tcx>,
429 stop_at: StoppingCondition,
430 ) -> Self {
431 Self {
432 tcx,
433 def_id,
434 local: place.local,
435 place_stack: place.projection.to_vec(),
436 ty_stack: Vec::new(),
437 dispatcher: Default::default(),
438 stop_at,
439 }
440 }
441
442 fn into_inner(self) -> Dispatcher {
443 self.dispatcher
444 }
445}
446
447pub const UNKNOWN_REGION: RegionVid = RegionVid::MAX;
449
450impl<'tcx, Dispatcher: RegionVisitorDispatcher<'tcx>> TypeVisitor<TyCtxt<'tcx>>
451 for RegionVisitor<'tcx, Dispatcher>
452{
453 fn visit_ty(&mut self, ty: Ty<'tcx>) {
454 let tcx = self.tcx;
455 if self.ty_stack.contains(&ty) {
456 return;
457 }
458
459 trace!(
460 "exploring {:?} with {ty:?}",
461 Place::make(self.local, &self.place_stack, tcx)
462 );
463
464 self.ty_stack.push(ty);
465
466 match ty.kind() {
467 _ if ty.is_box() => {
468 self.place_stack.push(ProjectionElem::Deref);
469 self.visit_ty(ty.boxed_ty().expect("Cannot unbox boxed type??"));
470 self.place_stack.pop();
471 }
472
473 TyKind::Tuple(fields) => {
474 for (i, field) in fields.iter().enumerate() {
475 self
476 .place_stack
477 .push(ProjectionElem::Field(FieldIdx::from_usize(i), field));
478 self.visit_ty(field);
479 self.place_stack.pop();
480 }
481 }
482
483 TyKind::Adt(adt_def, subst) => match adt_def.adt_kind() {
484 ty::AdtKind::Struct => {
485 for (i, field) in adt_def.all_visible_fields(self.def_id, tcx).enumerate() {
486 let ty = field.ty(tcx, subst);
487 self
488 .place_stack
489 .push(ProjectionElem::Field(FieldIdx::from_usize(i), ty));
490 self.visit_ty(ty);
491 self.place_stack.pop();
492 }
493 }
494 ty::AdtKind::Union => {
495 }
497 ty::AdtKind::Enum => {
498 for (i, variant) in adt_def.variants().iter().enumerate() {
499 let variant_index = VariantIdx::from_usize(i);
500 let cast = PlaceElem::Downcast(
501 Some(adt_def.variant(variant_index).ident(tcx).name),
502 variant_index,
503 );
504 self.place_stack.push(cast);
505 for (j, field) in variant.fields.iter().enumerate() {
506 let ty = field.ty(tcx, subst);
507 let field = ProjectionElem::Field(FieldIdx::from_usize(j), ty);
508 self.place_stack.push(field);
509 self.visit_ty(ty);
510 self.place_stack.pop();
511 }
512 self.place_stack.pop();
513 }
514 }
515 },
516
517 TyKind::Array(elem_ty, _) | TyKind::Slice(elem_ty) => {
518 self
519 .place_stack
520 .push(ProjectionElem::Index(Local::from_usize(0)));
521 self.visit_ty(*elem_ty);
522 self.place_stack.pop();
523 }
524
525 TyKind::Ref(region, elem_ty, _) => match self.stop_at {
526 StoppingCondition::None => {
527 self.visit_region(*region);
528 self.place_stack.push(ProjectionElem::Deref);
529 self.visit_ty(*elem_ty);
530 self.place_stack.pop();
531 }
532 StoppingCondition::AfterRefs => {
533 self.visit_region(*region);
534 }
535 StoppingCondition::BeforeRefs => {}
536 },
537
538 TyKind::Closure(_, substs) | TyKind::Coroutine(_, substs) => {
539 self.visit_ty(substs.as_closure().tupled_upvars_ty());
540 }
541
542 TyKind::RawPtr(ty, _) => {
543 self.visit_region(Region::new_var(tcx, UNKNOWN_REGION));
544 self.place_stack.push(ProjectionElem::Deref);
545 self.visit_ty(*ty);
546 self.place_stack.pop();
547 }
548
549 TyKind::FnDef(..)
550 | TyKind::FnPtr(..)
551 | TyKind::Foreign(..)
552 | TyKind::Dynamic(..)
553 | TyKind::Param(..)
554 | TyKind::Never => {}
555
556 _ if ty.is_primitive_ty() => {}
557
558 _ => warn!("unimplemented {ty:?} ({:?})", ty.kind()),
559 }
560
561 self
591 .dispatcher
592 .on_visit_place(Place::make(self.local, &self.place_stack, tcx));
593
594 self.dispatcher.on_visit_type(ty);
595
596 self.ty_stack.pop();
597 }
598
599 fn visit_region(&mut self, region: ty::Region<'tcx>) -> Self::Result {
600 trace!("visiting region {region:?}");
601 let region = match region.kind() {
602 RegionKind::ReVar(region) => region,
603 RegionKind::ReStatic => RegionVid::from_usize(0),
604 RegionKind::ReErased | RegionKind::ReLateParam(_) => {
605 return;
606 }
607 _ => unreachable!("{:?}: {:?}", self.ty_stack.first().unwrap(), region),
608 };
609
610 let mutability = if self
611 .ty_stack
612 .iter()
613 .any(|ty| ty.is_ref() && ty.ref_mutability().unwrap() == Mutability::Not)
614 {
615 Mutability::Not
616 } else {
617 Mutability::Mut
618 };
619
620 let place = Place::make(self.local, &self.place_stack, self.tcx);
621
622 self
623 .dispatcher
624 .on_visit_region_member(region, place, mutability);
625
626 self
631 .dispatcher
632 .on_visit_place(self.tcx.mk_place_deref(place));
633 }
634}
635
636#[cfg(test)]
637mod test {
638 use rustc_borrowck::consumers::BodyWithBorrowckFacts;
639 use rustc_hir::BodyId;
640 use rustc_middle::{
641 mir::{Place, PlaceElem},
642 ty::TyCtxt,
643 };
644
645 use crate::{
646 BodyExt, PlaceExt,
647 test_utils::{self, Placer, compare_sets},
648 };
649
650 #[test]
651 fn test_place_arg_direct() {
652 let input = r"
653fn foobar(x: &i32) {
654 let y = 1;
655 let z = &y;
656 let k = Box::new(*x);
657 let ref_k = &k;
658 let box_ref = Box::new(x);
659}
660";
661 test_utils::compile_body(input, |tcx, _, body_with_facts| {
662 let body = &body_with_facts.body;
663 let name_map = body.debug_info_name_map();
664 let x = Place::from_local(name_map["x"], tcx);
665 assert!(x.is_arg(body));
666 assert!(x.is_direct(body, tcx));
667 assert!(Place::make(x.local, &[PlaceElem::Deref], tcx).is_direct(body, tcx));
668
669 let y = Place::from_local(name_map["y"], tcx);
670 assert!(!y.is_arg(body));
671 assert!(y.is_direct(body, tcx));
672
673 let z = Place::from_local(name_map["z"], tcx);
674 assert!(!z.is_arg(body));
675 assert!(z.is_direct(body, tcx));
676 assert!(!Place::make(z.local, &[PlaceElem::Deref], tcx).is_direct(body, tcx));
677
678 let k = Place::from_local(name_map["k"], tcx);
679 assert!(!k.is_arg(body));
680 assert!(k.is_direct(body, tcx));
681 assert!(Place::make(k.local, &[PlaceElem::Deref], tcx).is_direct(body, tcx));
682 let deref_k = Place::make(k.local, &[PlaceElem::Deref], tcx);
683 assert!(deref_k.is_direct(body, tcx));
684 assert!(!deref_k.is_arg(body));
685 assert_eq!(deref_k.refs_in_projection(body, tcx).count(), 0);
686
687 let ref_k = Place::from_local(name_map["ref_k"], tcx);
688 assert!(!ref_k.is_arg(body));
689 assert!(k.is_direct(body, tcx));
690 let deref_ref_k =
691 Place::make(ref_k.local, &[PlaceElem::Deref, PlaceElem::Deref], tcx);
692 assert!(!deref_ref_k.is_direct(body, tcx));
693 assert_eq!(deref_ref_k.refs_in_projection(body, tcx).count(), 1);
694
695 let box_ref = Place::from_local(name_map["box_ref"], tcx);
696 assert!(!box_ref.is_arg(body));
697 assert!(!box_ref.is_indirect());
698 let box_ref_deref = Place::make(box_ref.local, &[PlaceElem::Deref], tcx);
699 assert_eq!(box_ref_deref.refs_in_projection(body, tcx).count(), 0);
700 assert!(box_ref_deref.is_direct(body, tcx));
701 let box_ref_deref_deref = box_ref_deref.project_deeper(&[PlaceElem::Deref], tcx);
702 assert_eq!(box_ref_deref_deref.refs_in_projection(body, tcx).count(), 1);
703 assert!(!box_ref_deref_deref.is_direct(body, tcx));
704 });
705 }
706
707 #[test]
708 fn test_place_to_string() {
709 let input = r"
710struct Point { x: usize, y: usize }
711fn main() {
712 let x = (0, 0);
713 let y = Some(1);
714 let z = &[Some((0, 1))];
715 let w = (&y,);
716 let p = &Point { x: 0, y: 0 };
717}";
718 test_utils::compile_body(input, |tcx, _, body_with_facts| {
719 let body = &body_with_facts.body;
720 let p = Placer::new(tcx, body);
721
722 let x = p.local("x").mk();
723 let x_1 = p.local("x").field(1).mk();
724 let y_some_0 = p.local("y").downcast(1).field(0).mk();
725 let z_deref_some_0_1 = p
726 .local("z")
727 .deref()
728 .index(0)
729 .downcast(1)
730 .field(0)
731 .field(1)
732 .mk();
733 let w_0_deref = p.local("w").field(0).deref().mk();
734 let w_0_deref_some = p.local("w").field(0).deref().downcast(1).mk();
735 let p_deref_x = p.local("p").deref().field(0).mk();
736
737 let tests = [
738 (x, "x"),
739 (x_1, "x.1"),
740 (y_some_0, "y@Some.0"),
741 (z_deref_some_0_1, "(*z)[_]@Some.0.1"),
742 (w_0_deref, "*w.0"),
743 (w_0_deref_some, "(*w.0)@Some"),
744 (p_deref_x, "(*p).x"),
745 ];
746
747 for (place, expected) in tests {
748 assert_eq!(place.to_string(tcx, body).unwrap(), expected);
749 }
750 });
751 }
752
753 #[test]
754 fn test_place_visitors() {
755 let input = r"
756fn main() {
757 let x = 0;
758 let y = (0, &x);
759}
760";
761 fn callback<'tcx>(
762 tcx: TyCtxt<'tcx>,
763 body_id: BodyId,
764 body_with_facts: &BodyWithBorrowckFacts<'tcx>,
765 ) {
766 let body = &body_with_facts.body;
767 let def_id = tcx.hir_body_owner_def_id(body_id).to_def_id();
768 let p = Placer::new(tcx, body);
769
770 let y = p.local("y").mk();
771 let y0 = p.local("y").field(0).mk();
772 let y1 = p.local("y").field(1).mk();
773 let y1_deref = p.local("y").field(1).deref().mk();
774
775 compare_sets(y.interior_paths(tcx, body, def_id), [y, y0, y1, y1_deref]);
776
777 compare_sets(y.interior_places(tcx, body, def_id), [y, y0, y1]);
778
779 compare_sets(
780 y.interior_pointers(tcx, body, def_id)
781 .into_values()
782 .flat_map(|vs| vs.into_iter().map(|(p, _)| p)),
783 [y1],
784 );
785 }
786 test_utils::compile_body(input, callback);
787 }
788}