]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/validity.rs
Rollup merge of #62707 - JohnTitor:add-test-for-61922, r=tmandry
[rust.git] / src / librustc_mir / interpret / validity.rs
1 use std::fmt::Write;
2 use std::ops::RangeInclusive;
3
4 use syntax_pos::symbol::{sym, Symbol};
5 use rustc::hir;
6 use rustc::ty::layout::{self, TyLayout, LayoutOf, VariantIdx};
7 use rustc::ty;
8 use rustc_data_structures::fx::FxHashSet;
9
10 use std::hash::Hash;
11
12 use super::{
13     GlobalAlloc, InterpResult, InterpError,
14     OpTy, Machine, InterpCx, ValueVisitor, MPlaceTy,
15 };
16
17 macro_rules! validation_failure {
18     ($what:expr, $where:expr, $details:expr) => {{
19         let where_ = path_format(&$where);
20         let where_ = if where_.is_empty() {
21             String::new()
22         } else {
23             format!(" at {}", where_)
24         };
25         err!(ValidationFailure(format!(
26             "encountered {}{}, but expected {}",
27             $what, where_, $details,
28         )))
29     }};
30     ($what:expr, $where:expr) => {{
31         let where_ = path_format(&$where);
32         let where_ = if where_.is_empty() {
33             String::new()
34         } else {
35             format!(" at {}", where_)
36         };
37         err!(ValidationFailure(format!(
38             "encountered {}{}",
39             $what, where_,
40         )))
41     }};
42 }
43
44 macro_rules! try_validation {
45     ($e:expr, $what:expr, $where:expr, $details:expr) => {{
46         match $e {
47             Ok(x) => x,
48             Err(_) => return validation_failure!($what, $where, $details),
49         }
50     }};
51
52     ($e:expr, $what:expr, $where:expr) => {{
53         match $e {
54             Ok(x) => x,
55             Err(_) => return validation_failure!($what, $where),
56         }
57     }}
58 }
59
60 /// We want to show a nice path to the invalid field for diagnostics,
61 /// but avoid string operations in the happy case where no error happens.
62 /// So we track a `Vec<PathElem>` where `PathElem` contains all the data we
63 /// need to later print something for the user.
64 #[derive(Copy, Clone, Debug)]
65 pub enum PathElem {
66     Field(Symbol),
67     Variant(Symbol),
68     GeneratorState(VariantIdx),
69     ClosureVar(Symbol),
70     ArrayElem(usize),
71     TupleElem(usize),
72     Deref,
73     Tag,
74     DynDowncast,
75 }
76
77 /// State for tracking recursive validation of references
78 pub struct RefTracking<T, PATH = ()> {
79     pub seen: FxHashSet<T>,
80     pub todo: Vec<(T, PATH)>,
81 }
82
83 impl<T: Copy + Eq + Hash + std::fmt::Debug, PATH: Default> RefTracking<T, PATH> {
84     pub fn empty() -> Self {
85         RefTracking {
86             seen: FxHashSet::default(),
87             todo: vec![],
88         }
89     }
90     pub fn new(op: T) -> Self {
91         let mut ref_tracking_for_consts = RefTracking {
92             seen: FxHashSet::default(),
93             todo: vec![(op, PATH::default())],
94         };
95         ref_tracking_for_consts.seen.insert(op);
96         ref_tracking_for_consts
97     }
98
99     pub fn track(&mut self, op: T, path: impl FnOnce() -> PATH) {
100         if self.seen.insert(op) {
101             trace!("Recursing below ptr {:#?}", op);
102             let path = path();
103             // Remember to come back to this later.
104             self.todo.push((op, path));
105         }
106     }
107 }
108
109 /// Format a path
110 fn path_format(path: &Vec<PathElem>) -> String {
111     use self::PathElem::*;
112
113     let mut out = String::new();
114     for elem in path.iter() {
115         match elem {
116             Field(name) => write!(out, ".{}", name),
117             Variant(name) => write!(out, ".<downcast-variant({})>", name),
118             GeneratorState(idx) => write!(out, ".<generator-state({})>", idx.index()),
119             ClosureVar(name) => write!(out, ".<closure-var({})>", name),
120             TupleElem(idx) => write!(out, ".{}", idx),
121             ArrayElem(idx) => write!(out, "[{}]", idx),
122             Deref =>
123                 // This does not match Rust syntax, but it is more readable for long paths -- and
124                 // some of the other items here also are not Rust syntax.  Actually we can't
125                 // even use the usual syntax because we are just showing the projections,
126                 // not the root.
127                 write!(out, ".<deref>"),
128             Tag => write!(out, ".<enum-tag>"),
129             DynDowncast => write!(out, ".<dyn-downcast>"),
130         }.unwrap()
131     }
132     out
133 }
134
135 // Test if a range that wraps at overflow contains `test`
136 fn wrapping_range_contains(r: &RangeInclusive<u128>, test: u128) -> bool {
137     let (lo, hi) = r.clone().into_inner();
138     if lo > hi {
139         // Wrapped
140         (..=hi).contains(&test) || (lo..).contains(&test)
141     } else {
142         // Normal
143         r.contains(&test)
144     }
145 }
146
147 // Formats such that a sentence like "expected something {}" to mean
148 // "expected something <in the given range>" makes sense.
149 fn wrapping_range_format(r: &RangeInclusive<u128>, max_hi: u128) -> String {
150     let (lo, hi) = r.clone().into_inner();
151     debug_assert!(hi <= max_hi);
152     if lo > hi {
153         format!("less or equal to {}, or greater or equal to {}", hi, lo)
154     } else if lo == hi {
155         format!("equal to {}", lo)
156     } else if lo == 0 {
157         debug_assert!(hi < max_hi, "should not be printing if the range covers everything");
158         format!("less or equal to {}", hi)
159     } else if hi == max_hi {
160         debug_assert!(lo > 0, "should not be printing if the range covers everything");
161         format!("greater or equal to {}", lo)
162     } else {
163         format!("in the range {:?}", r)
164     }
165 }
166
167 struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
168     /// The `path` may be pushed to, but the part that is present when a function
169     /// starts must not be changed!  `visit_fields` and `visit_array` rely on
170     /// this stack discipline.
171     path: Vec<PathElem>,
172     ref_tracking_for_consts: Option<&'rt mut RefTracking<
173         MPlaceTy<'tcx, M::PointerTag>,
174         Vec<PathElem>,
175     >>,
176     ecx: &'rt InterpCx<'mir, 'tcx, M>,
177 }
178
179 impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M> {
180     fn aggregate_field_path_elem(
181         &mut self,
182         layout: TyLayout<'tcx>,
183         field: usize,
184     ) -> PathElem {
185         match layout.ty.sty {
186             // generators and closures.
187             ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
188                 let mut name = None;
189                 if def_id.is_local() {
190                     let tables = self.ecx.tcx.typeck_tables_of(def_id);
191                     if let Some(upvars) = tables.upvar_list.get(&def_id) {
192                         // Sometimes the index is beyond the number of upvars (seen
193                         // for a generator).
194                         if let Some((&var_hir_id, _)) = upvars.get_index(field) {
195                             let node = self.ecx.tcx.hir().get(var_hir_id);
196                             if let hir::Node::Binding(pat) = node {
197                                 if let hir::PatKind::Binding(_, _, ident, _) = pat.node {
198                                     name = Some(ident.name);
199                                 }
200                             }
201                         }
202                     }
203                 }
204
205                 PathElem::ClosureVar(name.unwrap_or_else(|| {
206                     // Fall back to showing the field index.
207                     sym::integer(field)
208                 }))
209             }
210
211             // tuples
212             ty::Tuple(_) => PathElem::TupleElem(field),
213
214             // enums
215             ty::Adt(def, ..) if def.is_enum() => {
216                 // we might be projecting *to* a variant, or to a field *in*a variant.
217                 match layout.variants {
218                     layout::Variants::Single { index } =>
219                         // Inside a variant
220                         PathElem::Field(def.variants[index].fields[field].ident.name),
221                     _ => bug!(),
222                 }
223             }
224
225             // other ADTs
226             ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name),
227
228             // arrays/slices
229             ty::Array(..) | ty::Slice(..) => PathElem::ArrayElem(field),
230
231             // dyn traits
232             ty::Dynamic(..) => PathElem::DynDowncast,
233
234             // nothing else has an aggregate layout
235             _ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", layout.ty),
236         }
237     }
238
239     fn visit_elem(
240         &mut self,
241         new_op: OpTy<'tcx, M::PointerTag>,
242         elem: PathElem,
243     ) -> InterpResult<'tcx> {
244         // Remember the old state
245         let path_len = self.path.len();
246         // Perform operation
247         self.path.push(elem);
248         self.visit_value(new_op)?;
249         // Undo changes
250         self.path.truncate(path_len);
251         Ok(())
252     }
253 }
254
255 impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
256     for ValidityVisitor<'rt, 'mir, 'tcx, M>
257 {
258     type V = OpTy<'tcx, M::PointerTag>;
259
260     #[inline(always)]
261     fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> {
262         &self.ecx
263     }
264
265     #[inline]
266     fn visit_field(
267         &mut self,
268         old_op: OpTy<'tcx, M::PointerTag>,
269         field: usize,
270         new_op: OpTy<'tcx, M::PointerTag>
271     ) -> InterpResult<'tcx> {
272         let elem = self.aggregate_field_path_elem(old_op.layout, field);
273         self.visit_elem(new_op, elem)
274     }
275
276     #[inline]
277     fn visit_variant(
278         &mut self,
279         old_op: OpTy<'tcx, M::PointerTag>,
280         variant_id: VariantIdx,
281         new_op: OpTy<'tcx, M::PointerTag>
282     ) -> InterpResult<'tcx> {
283         let name = match old_op.layout.ty.sty {
284             ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name),
285             // Generators also have variants
286             ty::Generator(..) => PathElem::GeneratorState(variant_id),
287             _ => bug!("Unexpected type with variant: {:?}", old_op.layout.ty),
288         };
289         self.visit_elem(new_op, name)
290     }
291
292     #[inline]
293     fn visit_value(&mut self, op: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx>
294     {
295         trace!("visit_value: {:?}, {:?}", *op, op.layout);
296         // Translate some possible errors to something nicer.
297         match self.walk_value(op) {
298             Ok(()) => Ok(()),
299             Err(err) => match err.kind {
300                 InterpError::InvalidDiscriminant(val) =>
301                     validation_failure!(
302                         val, self.path, "a valid enum discriminant"
303                     ),
304                 InterpError::ReadPointerAsBytes =>
305                     validation_failure!(
306                         "a pointer", self.path, "plain (non-pointer) bytes"
307                     ),
308                 _ => Err(err),
309             }
310         }
311     }
312
313     fn visit_primitive(&mut self, value: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx>
314     {
315         let value = self.ecx.read_immediate(value)?;
316         // Go over all the primitive types
317         let ty = value.layout.ty;
318         match ty.sty {
319             ty::Bool => {
320                 let value = value.to_scalar_or_undef();
321                 try_validation!(value.to_bool(),
322                     value, self.path, "a boolean");
323             },
324             ty::Char => {
325                 let value = value.to_scalar_or_undef();
326                 try_validation!(value.to_char(),
327                     value, self.path, "a valid unicode codepoint");
328             },
329             ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
330                 // NOTE: Keep this in sync with the array optimization for int/float
331                 // types below!
332                 let size = value.layout.size;
333                 let value = value.to_scalar_or_undef();
334                 if self.ref_tracking_for_consts.is_some() {
335                     // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous
336                     try_validation!(value.to_bits(size),
337                         value, self.path, "initialized plain (non-pointer) bytes");
338                 } else {
339                     // At run-time, for now, we accept *anything* for these types, including
340                     // undef. We should fix that, but let's start low.
341                 }
342             }
343             ty::RawPtr(..) => {
344                 if self.ref_tracking_for_consts.is_some() {
345                     // Integers/floats in CTFE: For consistency with integers, we do not
346                     // accept undef.
347                     let _ptr = try_validation!(value.to_scalar_ptr(),
348                         "undefined address in raw pointer", self.path);
349                     let _meta = try_validation!(value.to_meta(),
350                         "uninitialized data in raw fat pointer metadata", self.path);
351                 } else {
352                     // Remain consistent with `usize`: Accept anything.
353                 }
354             }
355             _ if ty.is_box() || ty.is_region_ptr() => {
356                 // Handle fat pointers.
357                 // Check metadata early, for better diagnostics
358                 let ptr = try_validation!(value.to_scalar_ptr(),
359                     "undefined address in pointer", self.path);
360                 let meta = try_validation!(value.to_meta(),
361                     "uninitialized data in fat pointer metadata", self.path);
362                 let layout = self.ecx.layout_of(value.layout.ty.builtin_deref(true).unwrap().ty)?;
363                 if layout.is_unsized() {
364                     let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(layout.ty,
365                                                                           self.ecx.param_env);
366                     match tail.sty {
367                         ty::Dynamic(..) => {
368                             let vtable = meta.unwrap();
369                             try_validation!(
370                                 self.ecx.memory.check_ptr_access(
371                                     vtable,
372                                     3*self.ecx.tcx.data_layout.pointer_size, // drop, size, align
373                                     self.ecx.tcx.data_layout.pointer_align.abi,
374                                 ),
375                                 "dangling or unaligned vtable pointer or too small vtable",
376                                 self.path
377                             );
378                             try_validation!(self.ecx.read_drop_type_from_vtable(vtable),
379                                 "invalid drop fn in vtable", self.path);
380                             try_validation!(self.ecx.read_size_and_align_from_vtable(vtable),
381                                 "invalid size or align in vtable", self.path);
382                             // FIXME: More checks for the vtable.
383                         }
384                         ty::Slice(..) | ty::Str => {
385                             try_validation!(meta.unwrap().to_usize(self.ecx),
386                                 "non-integer slice length in fat pointer", self.path);
387                         }
388                         ty::Foreign(..) => {
389                             // Unsized, but not fat.
390                         }
391                         _ =>
392                             bug!("Unexpected unsized type tail: {:?}", tail),
393                     }
394                 }
395                 // Make sure this is dereferencable and all.
396                 let (size, align) = self.ecx.size_and_align_of(meta, layout)?
397                     // for the purpose of validity, consider foreign types to have
398                     // alignment and size determined by the layout (size will be 0,
399                     // alignment should take attributes into account).
400                     .unwrap_or_else(|| (layout.size, layout.align.abi));
401                 let ptr: Option<_> = match self.ecx.memory.check_ptr_access(ptr, size, align) {
402                     Ok(ptr) => ptr,
403                     Err(err) => {
404                         info!(
405                             "{:?} did not pass access check for size {:?}, align {:?}",
406                             ptr, size, align
407                         );
408                         match err.kind {
409                             InterpError::InvalidNullPointerUsage =>
410                                 return validation_failure!("NULL reference", self.path),
411                             InterpError::AlignmentCheckFailed { required, has } =>
412                                 return validation_failure!(format!("unaligned reference \
413                                     (required {} byte alignment but found {})",
414                                     required.bytes(), has.bytes()), self.path),
415                             InterpError::ReadBytesAsPointer =>
416                                 return validation_failure!(
417                                     "dangling reference (created from integer)",
418                                     self.path
419                                 ),
420                             _ =>
421                                 return validation_failure!(
422                                     "dangling reference (not entirely in bounds)",
423                                     self.path
424                                 ),
425                         }
426                     }
427                 };
428                 // Recursive checking
429                 if let Some(ref mut ref_tracking) = self.ref_tracking_for_consts {
430                     let place = self.ecx.ref_to_mplace(value)?;
431                     if let Some(ptr) = ptr { // not a ZST
432                         // Skip validation entirely for some external statics
433                         let alloc_kind = self.ecx.tcx.alloc_map.lock().get(ptr.alloc_id);
434                         if let Some(GlobalAlloc::Static(did)) = alloc_kind {
435                             // `extern static` cannot be validated as they have no body.
436                             // FIXME: Statics from other crates are also skipped.
437                             // They might be checked at a different type, but for now we
438                             // want to avoid recursing too deeply.  This is not sound!
439                             if !did.is_local() || self.ecx.tcx.is_foreign_item(did) {
440                                 return Ok(());
441                             }
442                         }
443                     }
444                     // Proceed recursively even for ZST, no reason to skip them!
445                     // `!` is a ZST and we want to validate it.
446                     // Normalize before handing `place` to tracking because that will
447                     // check for duplicates.
448                     let place = if size.bytes() > 0 {
449                         self.ecx.force_mplace_ptr(place)
450                             .expect("we already bounds-checked")
451                     } else {
452                         place
453                     };
454                     let path = &self.path;
455                     ref_tracking.track(place, || {
456                         // We need to clone the path anyway, make sure it gets created
457                         // with enough space for the additional `Deref`.
458                         let mut new_path = Vec::with_capacity(path.len() + 1);
459                         new_path.clone_from(path);
460                         new_path.push(PathElem::Deref);
461                         new_path
462                     });
463                 }
464             }
465             ty::FnPtr(_sig) => {
466                 let value = value.to_scalar_or_undef();
467                 let _fn = try_validation!(
468                     value.not_undef().and_then(|ptr| self.ecx.memory.get_fn(ptr)),
469                     value, self.path, "a function pointer"
470                 );
471                 // FIXME: Check if the signature matches
472             }
473             // This should be all the primitive types
474             _ => bug!("Unexpected primitive type {}", value.layout.ty)
475         }
476         Ok(())
477     }
478
479     fn visit_uninhabited(&mut self) -> InterpResult<'tcx>
480     {
481         validation_failure!("a value of an uninhabited type", self.path)
482     }
483
484     fn visit_scalar(
485         &mut self,
486         op: OpTy<'tcx, M::PointerTag>,
487         layout: &layout::Scalar,
488     ) -> InterpResult<'tcx> {
489         let value = self.ecx.read_scalar(op)?;
490         // Determine the allowed range
491         let (lo, hi) = layout.valid_range.clone().into_inner();
492         // `max_hi` is as big as the size fits
493         let max_hi = u128::max_value() >> (128 - op.layout.size.bits());
494         assert!(hi <= max_hi);
495         // We could also write `(hi + 1) % (max_hi + 1) == lo` but `max_hi + 1` overflows for `u128`
496         if (lo == 0 && hi == max_hi) || (hi + 1 == lo) {
497             // Nothing to check
498             return Ok(());
499         }
500         // At least one value is excluded. Get the bits.
501         let value = try_validation!(value.not_undef(),
502             value,
503             self.path,
504             format!(
505                 "something {}",
506                 wrapping_range_format(&layout.valid_range, max_hi),
507             )
508         );
509         let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) {
510             Err(ptr) => {
511                 if lo == 1 && hi == max_hi {
512                     // Only NULL is the niche.  So make sure the ptr is NOT NULL.
513                     if self.ecx.memory.ptr_may_be_null(ptr) {
514                         return validation_failure!(
515                             "a potentially NULL pointer",
516                             self.path,
517                             format!(
518                                 "something that cannot possibly fail to be {}",
519                                 wrapping_range_format(&layout.valid_range, max_hi)
520                             )
521                         );
522                     }
523                     return Ok(());
524                 } else {
525                     // Conservatively, we reject, because the pointer *could* have a bad
526                     // value.
527                     return validation_failure!(
528                         "a pointer",
529                         self.path,
530                         format!(
531                             "something that cannot possibly fail to be {}",
532                             wrapping_range_format(&layout.valid_range, max_hi)
533                         )
534                     );
535                 }
536             }
537             Ok(data) =>
538                 data
539         };
540         // Now compare. This is slightly subtle because this is a special "wrap-around" range.
541         if wrapping_range_contains(&layout.valid_range, bits) {
542             Ok(())
543         } else {
544             validation_failure!(
545                 bits,
546                 self.path,
547                 format!("something {}", wrapping_range_format(&layout.valid_range, max_hi))
548             )
549         }
550     }
551
552     fn visit_aggregate(
553         &mut self,
554         op: OpTy<'tcx, M::PointerTag>,
555         fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
556     ) -> InterpResult<'tcx> {
557         match op.layout.ty.sty {
558             ty::Str => {
559                 let mplace = op.assert_mem_place(); // strings are never immediate
560                 try_validation!(self.ecx.read_str(mplace),
561                     "uninitialized or non-UTF-8 data in str", self.path);
562             }
563             ty::Array(tys, ..) | ty::Slice(tys) if {
564                 // This optimization applies only for integer and floating point types
565                 // (i.e., types that can hold arbitrary bytes).
566                 match tys.sty {
567                     ty::Int(..) | ty::Uint(..) | ty::Float(..) => true,
568                     _ => false,
569                 }
570             } => {
571                 // bailing out for zsts is ok, since the array element type can only be int/float
572                 if op.layout.is_zst() {
573                     return Ok(());
574                 }
575                 // non-ZST array cannot be immediate, slices are never immediate
576                 let mplace = op.assert_mem_place();
577                 // This is the length of the array/slice.
578                 let len = mplace.len(self.ecx)?;
579                 // zero length slices have nothing to be checked
580                 if len == 0 {
581                     return Ok(());
582                 }
583                 // This is the element type size.
584                 let ty_size = self.ecx.layout_of(tys)?.size;
585                 // This is the size in bytes of the whole array.
586                 let size = ty_size * len;
587                 // Size is not 0, get a pointer.
588                 let ptr = self.ecx.force_ptr(mplace.ptr)?;
589
590                 // NOTE: Keep this in sync with the handling of integer and float
591                 // types above, in `visit_primitive`.
592                 // In run-time mode, we accept pointers in here.  This is actually more
593                 // permissive than a per-element check would be, e.g., we accept
594                 // an &[u8] that contains a pointer even though bytewise checking would
595                 // reject it.  However, that's good: We don't inherently want
596                 // to reject those pointers, we just do not have the machinery to
597                 // talk about parts of a pointer.
598                 // We also accept undef, for consistency with the type-based checks.
599                 match self.ecx.memory.get(ptr.alloc_id)?.check_bytes(
600                     self.ecx,
601                     ptr,
602                     size,
603                     /*allow_ptr_and_undef*/ self.ref_tracking_for_consts.is_none(),
604                 ) {
605                     // In the happy case, we needn't check anything else.
606                     Ok(()) => {},
607                     // Some error happened, try to provide a more detailed description.
608                     Err(err) => {
609                         // For some errors we might be able to provide extra information
610                         match err.kind {
611                             InterpError::ReadUndefBytes(offset) => {
612                                 // Some byte was undefined, determine which
613                                 // element that byte belongs to so we can
614                                 // provide an index.
615                                 let i = (offset.bytes() / ty_size.bytes()) as usize;
616                                 self.path.push(PathElem::ArrayElem(i));
617
618                                 return validation_failure!(
619                                     "undefined bytes", self.path
620                                 )
621                             },
622                             // Other errors shouldn't be possible
623                             _ => return Err(err),
624                         }
625                     }
626                 }
627             }
628             _ => {
629                 self.walk_aggregate(op, fields)? // default handler
630             }
631         }
632         Ok(())
633     }
634 }
635
636 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
637     /// This function checks the data at `op`. `op` is assumed to cover valid memory if it
638     /// is an indirect operand.
639     /// It will error if the bits at the destination do not match the ones described by the layout.
640     ///
641     /// `ref_tracking_for_consts` can be `None` to avoid recursive checking below references.
642     /// This also toggles between "run-time" (no recursion) and "compile-time" (with recursion)
643     /// validation (e.g., pointer values are fine in integers at runtime) and various other const
644     /// specific validation checks.
645     pub fn validate_operand(
646         &self,
647         op: OpTy<'tcx, M::PointerTag>,
648         path: Vec<PathElem>,
649         ref_tracking_for_consts: Option<&mut RefTracking<
650             MPlaceTy<'tcx, M::PointerTag>,
651             Vec<PathElem>,
652         >>,
653     ) -> InterpResult<'tcx> {
654         trace!("validate_operand: {:?}, {:?}", *op, op.layout.ty);
655
656         // Construct a visitor
657         let mut visitor = ValidityVisitor {
658             path,
659             ref_tracking_for_consts,
660             ecx: self,
661         };
662
663         // Try to cast to ptr *once* instead of all the time.
664         let op = self.force_op_ptr(op).unwrap_or(op);
665
666         // Run it
667         visitor.visit_value(op)
668     }
669 }