Skip to content

Commit 7d7bd9b

Browse files
committed
reduce the amount of traversal/projection code that the visitor has to implement itself
1 parent 5b5e076 commit 7d7bd9b

File tree

4 files changed

+237
-160
lines changed

4 files changed

+237
-160
lines changed

src/librustc_mir/interpret/validity.rs

Lines changed: 118 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ pub enum PathElem {
7878
TupleElem(usize),
7979
Deref,
8080
Tag,
81+
DynDowncast,
8182
}
8283

8384
/// State for tracking recursive validation of references
@@ -97,75 +98,30 @@ impl<'tcx, Tag: Copy+Eq+Hash> RefTracking<'tcx, Tag> {
9798
}
9899
}
99100

100-
// Adding a Deref and making a copy of the path to be put into the queue
101-
// always go together. This one does it with only new allocation.
102-
fn path_clone_and_deref(path: &Vec<PathElem>) -> Vec<PathElem> {
103-
let mut new_path = Vec::with_capacity(path.len()+1);
104-
new_path.clone_from(path);
105-
new_path.push(PathElem::Deref);
106-
new_path
107-
}
108-
109101
/// Format a path
110102
fn path_format(path: &Vec<PathElem>) -> String {
111103
use self::PathElem::*;
112104

113105
let mut out = String::new();
114106
for elem in path.iter() {
115107
match elem {
116-
Field(name) => write!(out, ".{}", name).unwrap(),
117-
ClosureVar(name) => write!(out, ".<closure-var({})>", name).unwrap(),
118-
TupleElem(idx) => write!(out, ".{}", idx).unwrap(),
119-
ArrayElem(idx) => write!(out, "[{}]", idx).unwrap(),
108+
Field(name) => write!(out, ".{}", name),
109+
ClosureVar(name) => write!(out, ".<closure-var({})>", name),
110+
TupleElem(idx) => write!(out, ".{}", idx),
111+
ArrayElem(idx) => write!(out, "[{}]", idx),
120112
Deref =>
121113
// This does not match Rust syntax, but it is more readable for long paths -- and
122114
// some of the other items here also are not Rust syntax. Actually we can't
123115
// even use the usual syntax because we are just showing the projections,
124116
// not the root.
125-
write!(out, ".<deref>").unwrap(),
126-
Tag => write!(out, ".<enum-tag>").unwrap(),
127-
}
117+
write!(out, ".<deref>"),
118+
Tag => write!(out, ".<enum-tag>"),
119+
DynDowncast => write!(out, ".<dyn-downcast>"),
120+
}.unwrap()
128121
}
129122
out
130123
}
131124

132-
fn aggregate_field_path_elem<'a, 'tcx>(
133-
layout: TyLayout<'tcx>,
134-
field: usize,
135-
tcx: TyCtxt<'a, 'tcx, 'tcx>,
136-
) -> PathElem {
137-
match layout.ty.sty {
138-
// generators and closures.
139-
ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
140-
if let Some(upvar) = tcx.optimized_mir(def_id).upvar_decls.get(field) {
141-
PathElem::ClosureVar(upvar.debug_name)
142-
} else {
143-
// Sometimes the index is beyond the number of freevars (seen
144-
// for a generator).
145-
PathElem::ClosureVar(Symbol::intern(&field.to_string()))
146-
}
147-
}
148-
149-
// tuples
150-
ty::Tuple(_) => PathElem::TupleElem(field),
151-
152-
// enums
153-
ty::Adt(def, ..) if def.is_enum() => {
154-
let variant = match layout.variants {
155-
layout::Variants::Single { index } => &def.variants[index],
156-
_ => bug!("aggregate_field_path_elem: got enum but not in a specific variant"),
157-
};
158-
PathElem::Field(variant.fields[field].ident.name)
159-
}
160-
161-
// other ADTs
162-
ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name),
163-
164-
// nothing else has an aggregate layout
165-
_ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", layout.ty),
166-
}
167-
}
168-
169125
fn scalar_format<Tag>(value: ScalarMaybeUndef<Tag>) -> String {
170126
match value {
171127
ScalarMaybeUndef::Undef =>
@@ -177,28 +133,97 @@ fn scalar_format<Tag>(value: ScalarMaybeUndef<Tag>) -> String {
177133
}
178134
}
179135

180-
struct ValidityVisitor<'rt, 'tcx, Tag> {
136+
struct ValidityVisitor<'rt, 'a, 'tcx, Tag> {
181137
op: OpTy<'tcx, Tag>,
182138
/// The `path` may be pushed to, but the part that is present when a function
183139
/// starts must not be changed! `visit_fields` and `visit_array` rely on
184140
/// this stack discipline.
185141
path: Vec<PathElem>,
186142
ref_tracking: Option<&'rt mut RefTracking<'tcx, Tag>>,
187143
const_mode: bool,
144+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
188145
}
189146

190-
impl<Tag: fmt::Debug> fmt::Debug for ValidityVisitor<'_, '_, Tag> {
147+
impl<Tag: fmt::Debug> fmt::Debug for ValidityVisitor<'_, '_, '_, Tag> {
191148
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192-
write!(f, "{:?} ({:?})", *self.op, self.op.layout.ty)
149+
write!(f, "{:?}, {:?}", *self.op, self.op.layout.ty)
150+
}
151+
}
152+
153+
impl<'rt, 'a, 'tcx, Tag> ValidityVisitor<'rt, 'a, 'tcx, Tag> {
154+
fn push_aggregate_field_path_elem(
155+
&mut self,
156+
layout: TyLayout<'tcx>,
157+
field: usize,
158+
) {
159+
let elem = match layout.ty.sty {
160+
// generators and closures.
161+
ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
162+
if let Some(upvar) = self.tcx.optimized_mir(def_id).upvar_decls.get(field) {
163+
PathElem::ClosureVar(upvar.debug_name)
164+
} else {
165+
// Sometimes the index is beyond the number of freevars (seen
166+
// for a generator).
167+
PathElem::ClosureVar(Symbol::intern(&field.to_string()))
168+
}
169+
}
170+
171+
// tuples
172+
ty::Tuple(_) => PathElem::TupleElem(field),
173+
174+
// enums
175+
ty::Adt(def, ..) if def.is_enum() => {
176+
let variant = match layout.variants {
177+
layout::Variants::Single { index } => &def.variants[index],
178+
_ => bug!("aggregate_field_path_elem: got enum but not in a specific variant"),
179+
};
180+
PathElem::Field(variant.fields[field].ident.name)
181+
}
182+
183+
// other ADTs
184+
ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name),
185+
186+
// arrays/slices
187+
ty::Array(..) | ty::Slice(..) => PathElem::ArrayElem(field),
188+
189+
// dyn traits
190+
ty::Dynamic(..) => PathElem::DynDowncast,
191+
192+
// nothing else has an aggregate layout
193+
_ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", layout.ty),
194+
};
195+
self.path.push(elem);
193196
}
194197
}
195198

196199
impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
197-
ValueVisitor<'a, 'mir, 'tcx, M> for ValidityVisitor<'rt, 'tcx, M::PointerTag>
200+
ValueVisitor<'a, 'mir, 'tcx, M> for ValidityVisitor<'rt, 'a, 'tcx, M::PointerTag>
198201
{
202+
type V = OpTy<'tcx, M::PointerTag>;
203+
199204
#[inline(always)]
200-
fn layout(&self) -> TyLayout<'tcx> {
201-
self.op.layout
205+
fn value(&self) -> &OpTy<'tcx, M::PointerTag> {
206+
&self.op
207+
}
208+
209+
#[inline]
210+
fn with_field(
211+
&mut self,
212+
val: Self::V,
213+
field: usize,
214+
f: impl FnOnce(&mut Self) -> EvalResult<'tcx>,
215+
) -> EvalResult<'tcx> {
216+
// Remember the old state
217+
let path_len = self.path.len();
218+
let op = self.op;
219+
// Perform operation
220+
self.push_aggregate_field_path_elem(op.layout, field);
221+
self.op = val;
222+
f(self)?;
223+
// Undo changes
224+
self.path.truncate(path_len);
225+
self.op = op;
226+
Ok(())
202227
}
203228

204229
fn downcast_enum(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>)
@@ -227,15 +252,6 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
227252
Ok(())
228253
}
229254

230-
fn downcast_dyn_trait(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>)
231-
-> EvalResult<'tcx>
232-
{
233-
// FIXME: Should we reflect this in `self.path`?
234-
let dest = self.op.to_mem_place(); // immediate trait objects are not a thing
235-
self.op = ectx.unpack_dyn_trait(dest)?.1.into();
236-
Ok(())
237-
}
238-
239255
fn visit_primitive(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
240256
-> EvalResult<'tcx>
241257
{
@@ -365,7 +381,13 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
365381
let op = place.into();
366382
if ref_tracking.seen.insert(op) {
367383
trace!("Recursing below ptr {:#?}", *op);
368-
ref_tracking.todo.push((op, path_clone_and_deref(&self.path)));
384+
// We need to clone the path anyway, make sure it gets created
385+
// with enough space for the additional `Deref`.
386+
let mut new_path = Vec::with_capacity(self.path.len()+1);
387+
new_path.clone_from(&self.path);
388+
new_path.push(PathElem::Deref);
389+
// Remember to come back to this later.
390+
ref_tracking.todo.push((op, new_path));
369391
}
370392
}
371393
}
@@ -378,12 +400,17 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
378400
// FIXME: Check if the signature matches
379401
}
380402
// This should be all the primitive types
381-
ty::Never => bug!("Uninhabited type should have been caught earlier"),
382403
_ => bug!("Unexpected primitive type {}", value.layout.ty)
383404
}
384405
Ok(())
385406
}
386407

408+
fn visit_uninhabited(&mut self, _ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
409+
-> EvalResult<'tcx>
410+
{
411+
validation_failure!("a value of an uninhabited type", self.path)
412+
}
413+
387414
fn visit_scalar(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>, layout: &layout::Scalar)
388415
-> EvalResult<'tcx>
389416
{
@@ -468,47 +495,16 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
468495
}
469496
}
470497

471-
fn visit_fields(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>, num_fields: usize)
472-
-> EvalResult<'tcx>
473-
{
474-
// Remember some stuff that will change for the recursive calls
475-
let op = self.op;
476-
let path_len = self.path.len();
477-
// Go look at all the fields
478-
for i in 0..num_fields {
479-
// Adapt our state
480-
self.op = ectx.operand_field(op, i as u64)?;
481-
self.path.push(aggregate_field_path_elem(op.layout, i, *ectx.tcx));
482-
// Recursive visit
483-
ectx.visit_value(self)?;
484-
// Restore original state
485-
self.op = op;
486-
self.path.truncate(path_len);
487-
}
488-
Ok(())
489-
}
490-
491-
fn visit_str(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
492-
-> EvalResult<'tcx>
493-
{
494-
let mplace = self.op.to_mem_place(); // strings are never immediate
495-
try_validation!(ectx.read_str(mplace),
496-
"uninitialized or non-UTF-8 data in str", self.path);
497-
Ok(())
498-
}
499-
500-
fn visit_array(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>) -> EvalResult<'tcx>
498+
fn handle_array(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>)
499+
-> EvalResult<'tcx, bool>
501500
{
502-
let mplace = if self.op.layout.is_zst() {
503-
// it's a ZST, the memory content cannot matter
504-
MPlaceTy::dangling(self.op.layout, ectx)
505-
} else {
506-
// non-ZST array/slice/str cannot be immediate
507-
self.op.to_mem_place()
508-
};
509-
match self.op.layout.ty.sty {
510-
ty::Str => bug!("Strings should be handled separately"),
511-
// Special handling for arrays/slices of builtin integer types
501+
Ok(match self.op.layout.ty.sty {
502+
ty::Str => {
503+
let mplace = self.op.to_mem_place(); // strings are never immediate
504+
try_validation!(ectx.read_str(mplace),
505+
"uninitialized or non-UTF-8 data in str", self.path);
506+
true
507+
}
512508
ty::Array(tys, ..) | ty::Slice(tys) if {
513509
// This optimization applies only for integer and floating point types
514510
// (i.e., types that can hold arbitrary bytes).
@@ -517,6 +513,13 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
517513
_ => false,
518514
}
519515
} => {
516+
let mplace = if self.op.layout.is_zst() {
517+
// it's a ZST, the memory content cannot matter
518+
MPlaceTy::dangling(self.op.layout, ectx)
519+
} else {
520+
// non-ZST array/slice/str cannot be immediate
521+
self.op.to_mem_place()
522+
};
520523
// This is the length of the array/slice.
521524
let len = mplace.len(ectx)?;
522525
// This is the element type size.
@@ -539,7 +542,7 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
539542
/*allow_ptr_and_undef*/!self.const_mode,
540543
) {
541544
// In the happy case, we needn't check anything else.
542-
Ok(()) => {},
545+
Ok(()) => true, // handled these arrays
543546
// Some error happened, try to provide a more detailed description.
544547
Err(err) => {
545548
// For some errors we might be able to provide extra information
@@ -560,26 +563,9 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
560563
}
561564
}
562565
}
563-
},
564-
_ => {
565-
// Remember some stuff that will change for the recursive calls
566-
let op = self.op;
567-
let path_len = self.path.len();
568-
// This handles the unsized case correctly as well, as well as
569-
// SIMD and all sorts of other array-like types.
570-
for (i, field) in ectx.mplace_array_fields(mplace)?.enumerate() {
571-
// Adapt our state
572-
self.op = field?.into();
573-
self.path.push(PathElem::ArrayElem(i));
574-
// Recursive visit
575-
ectx.visit_value(self)?;
576-
// Restore original state
577-
self.op = op;
578-
self.path.truncate(path_len);
579-
}
580566
}
581-
}
582-
Ok(())
567+
_ => false, // not handled
568+
})
583569
}
584570
}
585571

@@ -605,7 +591,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
605591
op,
606592
path,
607593
ref_tracking,
608-
const_mode
594+
const_mode,
595+
tcx: *self.tcx,
609596
};
610597

611598
// Run it

0 commit comments

Comments
 (0)