reduce the amount of traversal/projection code that the visitor has to implement itself

This commit is contained in:
Ralf Jung 2018-10-31 18:44:00 +01:00
parent 5b5e076b47
commit 7d7bd9b6c2
4 changed files with 237 additions and 160 deletions

View File

@ -78,6 +78,7 @@ pub enum PathElem {
TupleElem(usize), TupleElem(usize),
Deref, Deref,
Tag, Tag,
DynDowncast,
} }
/// State for tracking recursive validation of references /// State for tracking recursive validation of references
@ -97,15 +98,6 @@ impl<'tcx, Tag: Copy+Eq+Hash> RefTracking<'tcx, Tag> {
} }
} }
// Adding a Deref and making a copy of the path to be put into the queue
// always go together. This one does it with only new allocation.
fn path_clone_and_deref(path: &Vec<PathElem>) -> Vec<PathElem> {
let mut new_path = Vec::with_capacity(path.len()+1);
new_path.clone_from(path);
new_path.push(PathElem::Deref);
new_path
}
/// Format a path /// Format a path
fn path_format(path: &Vec<PathElem>) -> String { fn path_format(path: &Vec<PathElem>) -> String {
use self::PathElem::*; use self::PathElem::*;
@ -113,31 +105,61 @@ fn path_format(path: &Vec<PathElem>) -> String {
let mut out = String::new(); let mut out = String::new();
for elem in path.iter() { for elem in path.iter() {
match elem { match elem {
Field(name) => write!(out, ".{}", name).unwrap(), Field(name) => write!(out, ".{}", name),
ClosureVar(name) => write!(out, ".<closure-var({})>", name).unwrap(), ClosureVar(name) => write!(out, ".<closure-var({})>", name),
TupleElem(idx) => write!(out, ".{}", idx).unwrap(), TupleElem(idx) => write!(out, ".{}", idx),
ArrayElem(idx) => write!(out, "[{}]", idx).unwrap(), ArrayElem(idx) => write!(out, "[{}]", idx),
Deref => Deref =>
// This does not match Rust syntax, but it is more readable for long paths -- and // This does not match Rust syntax, but it is more readable for long paths -- and
// some of the other items here also are not Rust syntax. Actually we can't // some of the other items here also are not Rust syntax. Actually we can't
// even use the usual syntax because we are just showing the projections, // even use the usual syntax because we are just showing the projections,
// not the root. // not the root.
write!(out, ".<deref>").unwrap(), write!(out, ".<deref>"),
Tag => write!(out, ".<enum-tag>").unwrap(), Tag => write!(out, ".<enum-tag>"),
} DynDowncast => write!(out, ".<dyn-downcast>"),
}.unwrap()
} }
out out
} }
fn aggregate_field_path_elem<'a, 'tcx>( fn scalar_format<Tag>(value: ScalarMaybeUndef<Tag>) -> String {
match value {
ScalarMaybeUndef::Undef =>
"uninitialized bytes".to_owned(),
ScalarMaybeUndef::Scalar(Scalar::Ptr(_)) =>
"a pointer".to_owned(),
ScalarMaybeUndef::Scalar(Scalar::Bits { bits, .. }) =>
bits.to_string(),
}
}
struct ValidityVisitor<'rt, 'a, 'tcx, Tag> {
op: OpTy<'tcx, Tag>,
/// The `path` may be pushed to, but the part that is present when a function
/// starts must not be changed! `visit_fields` and `visit_array` rely on
/// this stack discipline.
path: Vec<PathElem>,
ref_tracking: Option<&'rt mut RefTracking<'tcx, Tag>>,
const_mode: bool,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
impl<Tag: fmt::Debug> fmt::Debug for ValidityVisitor<'_, '_, '_, Tag> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}, {:?}", *self.op, self.op.layout.ty)
}
}
impl<'rt, 'a, 'tcx, Tag> ValidityVisitor<'rt, 'a, 'tcx, Tag> {
fn push_aggregate_field_path_elem(
&mut self,
layout: TyLayout<'tcx>, layout: TyLayout<'tcx>,
field: usize, field: usize,
tcx: TyCtxt<'a, 'tcx, 'tcx>, ) {
) -> PathElem { let elem = match layout.ty.sty {
match layout.ty.sty {
// generators and closures. // generators and closures.
ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
if let Some(upvar) = tcx.optimized_mir(def_id).upvar_decls.get(field) { if let Some(upvar) = self.tcx.optimized_mir(def_id).upvar_decls.get(field) {
PathElem::ClosureVar(upvar.debug_name) PathElem::ClosureVar(upvar.debug_name)
} else { } else {
// Sometimes the index is beyond the number of freevars (seen // Sometimes the index is beyond the number of freevars (seen
@ -161,44 +183,47 @@ fn aggregate_field_path_elem<'a, 'tcx>(
// other ADTs // other ADTs
ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name), ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name),
// arrays/slices
ty::Array(..) | ty::Slice(..) => PathElem::ArrayElem(field),
// dyn traits
ty::Dynamic(..) => PathElem::DynDowncast,
// nothing else has an aggregate layout // nothing else has an aggregate layout
_ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", layout.ty), _ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", layout.ty),
} };
} self.path.push(elem);
fn scalar_format<Tag>(value: ScalarMaybeUndef<Tag>) -> String {
match value {
ScalarMaybeUndef::Undef =>
"uninitialized bytes".to_owned(),
ScalarMaybeUndef::Scalar(Scalar::Ptr(_)) =>
"a pointer".to_owned(),
ScalarMaybeUndef::Scalar(Scalar::Bits { bits, .. }) =>
bits.to_string(),
}
}
struct ValidityVisitor<'rt, 'tcx, Tag> {
op: OpTy<'tcx, Tag>,
/// The `path` may be pushed to, but the part that is present when a function
/// starts must not be changed! `visit_fields` and `visit_array` rely on
/// this stack discipline.
path: Vec<PathElem>,
ref_tracking: Option<&'rt mut RefTracking<'tcx, Tag>>,
const_mode: bool,
}
impl<Tag: fmt::Debug> fmt::Debug for ValidityVisitor<'_, '_, Tag> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?} ({:?})", *self.op, self.op.layout.ty)
} }
} }
impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
ValueVisitor<'a, 'mir, 'tcx, M> for ValidityVisitor<'rt, 'tcx, M::PointerTag> ValueVisitor<'a, 'mir, 'tcx, M> for ValidityVisitor<'rt, 'a, 'tcx, M::PointerTag>
{ {
type V = OpTy<'tcx, M::PointerTag>;
#[inline(always)] #[inline(always)]
fn layout(&self) -> TyLayout<'tcx> { fn value(&self) -> &OpTy<'tcx, M::PointerTag> {
self.op.layout &self.op
}
#[inline]
fn with_field(
&mut self,
val: Self::V,
field: usize,
f: impl FnOnce(&mut Self) -> EvalResult<'tcx>,
) -> EvalResult<'tcx> {
// Remember the old state
let path_len = self.path.len();
let op = self.op;
// Perform operation
self.push_aggregate_field_path_elem(op.layout, field);
self.op = val;
f(self)?;
// Undo changes
self.path.truncate(path_len);
self.op = op;
Ok(())
} }
fn downcast_enum(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>) 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>>
Ok(()) Ok(())
} }
fn downcast_dyn_trait(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>)
-> EvalResult<'tcx>
{
// FIXME: Should we reflect this in `self.path`?
let dest = self.op.to_mem_place(); // immediate trait objects are not a thing
self.op = ectx.unpack_dyn_trait(dest)?.1.into();
Ok(())
}
fn visit_primitive(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>) fn visit_primitive(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
-> EvalResult<'tcx> -> EvalResult<'tcx>
{ {
@ -365,7 +381,13 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
let op = place.into(); let op = place.into();
if ref_tracking.seen.insert(op) { if ref_tracking.seen.insert(op) {
trace!("Recursing below ptr {:#?}", *op); trace!("Recursing below ptr {:#?}", *op);
ref_tracking.todo.push((op, path_clone_and_deref(&self.path))); // We need to clone the path anyway, make sure it gets created
// with enough space for the additional `Deref`.
let mut new_path = Vec::with_capacity(self.path.len()+1);
new_path.clone_from(&self.path);
new_path.push(PathElem::Deref);
// Remember to come back to this later.
ref_tracking.todo.push((op, new_path));
} }
} }
} }
@ -378,12 +400,17 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
// FIXME: Check if the signature matches // FIXME: Check if the signature matches
} }
// This should be all the primitive types // This should be all the primitive types
ty::Never => bug!("Uninhabited type should have been caught earlier"),
_ => bug!("Unexpected primitive type {}", value.layout.ty) _ => bug!("Unexpected primitive type {}", value.layout.ty)
} }
Ok(()) Ok(())
} }
fn visit_uninhabited(&mut self, _ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
-> EvalResult<'tcx>
{
validation_failure!("a value of an uninhabited type", self.path)
}
fn visit_scalar(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>, layout: &layout::Scalar) fn visit_scalar(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>, layout: &layout::Scalar)
-> EvalResult<'tcx> -> EvalResult<'tcx>
{ {
@ -468,47 +495,16 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
} }
} }
fn visit_fields(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>, num_fields: usize) fn handle_array(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>)
-> EvalResult<'tcx> -> EvalResult<'tcx, bool>
{
// Remember some stuff that will change for the recursive calls
let op = self.op;
let path_len = self.path.len();
// Go look at all the fields
for i in 0..num_fields {
// Adapt our state
self.op = ectx.operand_field(op, i as u64)?;
self.path.push(aggregate_field_path_elem(op.layout, i, *ectx.tcx));
// Recursive visit
ectx.visit_value(self)?;
// Restore original state
self.op = op;
self.path.truncate(path_len);
}
Ok(())
}
fn visit_str(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
-> EvalResult<'tcx>
{ {
Ok(match self.op.layout.ty.sty {
ty::Str => {
let mplace = self.op.to_mem_place(); // strings are never immediate let mplace = self.op.to_mem_place(); // strings are never immediate
try_validation!(ectx.read_str(mplace), try_validation!(ectx.read_str(mplace),
"uninitialized or non-UTF-8 data in str", self.path); "uninitialized or non-UTF-8 data in str", self.path);
Ok(()) true
} }
fn visit_array(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>) -> EvalResult<'tcx>
{
let mplace = if self.op.layout.is_zst() {
// it's a ZST, the memory content cannot matter
MPlaceTy::dangling(self.op.layout, ectx)
} else {
// non-ZST array/slice/str cannot be immediate
self.op.to_mem_place()
};
match self.op.layout.ty.sty {
ty::Str => bug!("Strings should be handled separately"),
// Special handling for arrays/slices of builtin integer types
ty::Array(tys, ..) | ty::Slice(tys) if { ty::Array(tys, ..) | ty::Slice(tys) if {
// This optimization applies only for integer and floating point types // This optimization applies only for integer and floating point types
// (i.e., types that can hold arbitrary bytes). // (i.e., types that can hold arbitrary bytes).
@ -517,6 +513,13 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
_ => false, _ => false,
} }
} => { } => {
let mplace = if self.op.layout.is_zst() {
// it's a ZST, the memory content cannot matter
MPlaceTy::dangling(self.op.layout, ectx)
} else {
// non-ZST array/slice/str cannot be immediate
self.op.to_mem_place()
};
// This is the length of the array/slice. // This is the length of the array/slice.
let len = mplace.len(ectx)?; let len = mplace.len(ectx)?;
// This is the element type size. // This is the element type size.
@ -539,7 +542,7 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
/*allow_ptr_and_undef*/!self.const_mode, /*allow_ptr_and_undef*/!self.const_mode,
) { ) {
// In the happy case, we needn't check anything else. // In the happy case, we needn't check anything else.
Ok(()) => {}, Ok(()) => true, // handled these arrays
// Some error happened, try to provide a more detailed description. // Some error happened, try to provide a more detailed description.
Err(err) => { Err(err) => {
// For some errors we might be able to provide extra information // 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>>
} }
} }
} }
},
_ => {
// Remember some stuff that will change for the recursive calls
let op = self.op;
let path_len = self.path.len();
// This handles the unsized case correctly as well, as well as
// SIMD and all sorts of other array-like types.
for (i, field) in ectx.mplace_array_fields(mplace)?.enumerate() {
// Adapt our state
self.op = field?.into();
self.path.push(PathElem::ArrayElem(i));
// Recursive visit
ectx.visit_value(self)?;
// Restore original state
self.op = op;
self.path.truncate(path_len);
} }
} _ => false, // not handled
} })
Ok(())
} }
} }
@ -605,7 +591,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
op, op,
path, path,
ref_tracking, ref_tracking,
const_mode const_mode,
tcx: *self.tcx,
}; };
// Run it // Run it

View File

@ -10,34 +10,103 @@ use rustc::mir::interpret::{
}; };
use super::{ use super::{
Machine, EvalContext, Machine, EvalContext, MPlaceTy, OpTy,
}; };
// How to traverse a value and what to do when we are at the leaves. // A thing that we can project into, and that has a layout.
// In the future, we might want to turn this into two traits, but so far the // This wouldn't have to depend on `Machine` but with the current type inference,
// only implementations we have couldn't share any code anyway. // that's just more convenient to work with (avoids repeading all the `Machine` bounds).
pub trait ValueVisitor<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: fmt::Debug { pub trait Value<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: Copy
{
// Get this value's layout. // Get this value's layout.
fn layout(&self) -> TyLayout<'tcx>; fn layout(&self) -> TyLayout<'tcx>;
// Downcast functions. These change the value as a side-effect. // Get the underlying `MPlaceTy`, or panic if there is no such thing.
fn to_mem_place(self) -> MPlaceTy<'tcx, M::PointerTag>;
// Create this from an `MPlaceTy`
fn from_mem_place(MPlaceTy<'tcx, M::PointerTag>) -> Self;
// Project to the n-th field
fn project_field(
self,
ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
field: u64,
) -> EvalResult<'tcx, Self>;
}
// Operands and places are both values
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Value<'a, 'mir, 'tcx, M>
for OpTy<'tcx, M::PointerTag>
{
#[inline(always)]
fn layout(&self) -> TyLayout<'tcx> {
self.layout
}
#[inline(always)]
fn to_mem_place(self) -> MPlaceTy<'tcx, M::PointerTag> {
self.to_mem_place()
}
#[inline(always)]
fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
mplace.into()
}
#[inline(always)]
fn project_field(
self,
ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
field: u64,
) -> EvalResult<'tcx, Self> {
ectx.operand_field(self, field)
}
}
// How to traverse a value and what to do when we are at the leaves.
pub trait ValueVisitor<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: fmt::Debug {
type V: Value<'a, 'mir, 'tcx, M>;
// There's a value in here.
fn value(&self) -> &Self::V;
// The value's layout (not meant to be overwritten).
#[inline(always)]
fn layout(&self) -> TyLayout<'tcx> {
self.value().layout()
}
// Replace the value by `val`, which must be the `field`th field of `self`,
// then call `f` and then un-do everything that might have happened to the visitor state.
// The point of this is that some visitors keep a stack of fields that we projected below,
// and this lets us avoid copying that stack; instead they will pop the stack after
// executing `f`.
fn with_field(
&mut self,
val: Self::V,
field: usize,
f: impl FnOnce(&mut Self) -> EvalResult<'tcx>,
) -> EvalResult<'tcx>;
// This is an enum, downcast it to whatever the current variant is.
// (We do this here and not in `Value` to keep error handling
// under control of th visitor.)
fn downcast_enum(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>) fn downcast_enum(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>)
-> EvalResult<'tcx>; -> EvalResult<'tcx>;
fn downcast_dyn_trait(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>)
-> EvalResult<'tcx>;
// Visit all fields of a compound. // A chance for the visitor to do special (different or more efficient) handling for some
// Just call `visit_value` if you want to go on recursively. // array types. Return `true` if the value was handled and we should return.
fn visit_fields(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>, num_fields: usize) #[inline]
-> EvalResult<'tcx>; fn handle_array(&mut self, _ectx: &EvalContext<'a, 'mir, 'tcx, M>)
// Optimized handling for arrays -- avoid computing the layout for every field. -> EvalResult<'tcx, bool>
// Also it is the value's responsibility to figure out the length. {
fn visit_array(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>) -> EvalResult<'tcx>; Ok(false)
// Special handling for strings. }
fn visit_str(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
-> EvalResult<'tcx>;
// Actions on the leaves. // Actions on the leaves.
fn visit_uninhabited(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
-> EvalResult<'tcx>;
fn visit_scalar(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>, layout: &layout::Scalar) fn visit_scalar(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>, layout: &layout::Scalar)
-> EvalResult<'tcx>; -> EvalResult<'tcx>;
fn visit_primitive(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>) fn visit_primitive(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
@ -45,7 +114,10 @@ pub trait ValueVisitor<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: fmt::Debug {
} }
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
pub fn visit_value<V: ValueVisitor<'a, 'mir, 'tcx, M>>(&mut self, v: &mut V) -> EvalResult<'tcx> { pub fn visit_value<V: ValueVisitor<'a, 'mir, 'tcx, M>>(
&mut self,
v: &mut V,
) -> EvalResult<'tcx> {
trace!("visit_value: {:?}", v); trace!("visit_value: {:?}", v);
// If this is a multi-variant layout, we have find the right one and proceed with that. // If this is a multi-variant layout, we have find the right one and proceed with that.
@ -63,7 +135,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
// If it is a trait object, switch to the actual type that was used to create it. // If it is a trait object, switch to the actual type that was used to create it.
match v.layout().ty.sty { match v.layout().ty.sty {
ty::Dynamic(..) => { ty::Dynamic(..) => {
v.downcast_dyn_trait(self)?; let dest = v.value().to_mem_place(); // immediate trait objects are not a thing
let inner = self.unpack_dyn_trait(dest)?.1;
// recurse with the inner type
return v.with_field(Value::from_mem_place(inner), 0, |v| self.visit_value(v));
}, },
_ => {}, _ => {},
}; };
@ -76,6 +151,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
// scalars, we do the same check on every "level" (e.g. first we check // scalars, we do the same check on every "level" (e.g. first we check
// MyNewtype and then the scalar in there). // MyNewtype and then the scalar in there).
match v.layout().abi { match v.layout().abi {
layout::Abi::Uninhabited => {
v.visit_uninhabited(self)?;
}
layout::Abi::Scalar(ref layout) => { layout::Abi::Scalar(ref layout) => {
v.visit_scalar(self, layout)?; v.visit_scalar(self, layout)?;
} }
@ -107,19 +185,31 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
// We can't traverse unions, their bits are allowed to be anything. // We can't traverse unions, their bits are allowed to be anything.
// The fields don't need to correspond to any bit pattern of the union's fields. // The fields don't need to correspond to any bit pattern of the union's fields.
// See https://github.com/rust-lang/rust/issues/32836#issuecomment-406875389 // See https://github.com/rust-lang/rust/issues/32836#issuecomment-406875389
Ok(())
}, },
layout::FieldPlacement::Arbitrary { ref offsets, .. } => { layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
v.visit_fields(self, offsets.len()) for i in 0..offsets.len() {
let val = v.value().project_field(self, i as u64)?;
v.with_field(val, i, |v| self.visit_value(v))?;
}
}, },
layout::FieldPlacement::Array { .. } => { layout::FieldPlacement::Array { .. } => {
match v.layout().ty.sty { if !v.handle_array(self)? {
// Strings have properties that cannot be expressed pointwise. // We still have to work!
ty::Str => v.visit_str(self), // Let's get an mplace first.
// General case -- might also be SIMD vector or so let mplace = if v.layout().is_zst() {
_ => v.visit_array(self), // it's a ZST, the memory content cannot matter
MPlaceTy::dangling(v.layout(), self)
} else {
// non-ZST array/slice/str cannot be immediate
v.value().to_mem_place()
};
// Now iterate over it.
for (i, field) in self.mplace_array_fields(mplace)?.enumerate() {
v.with_field(Value::from_mem_place(field?), i, |v| self.visit_value(v))?;
} }
} }
} }
} }
Ok(())
}
} }

View File

@ -6,7 +6,7 @@ LL | | let bad_ref: &'static u16 = unsafe { mem::transmute(0usize) };
LL | | let another_var = 13; LL | | let another_var = 13;
LL | | move || { let _ = bad_ref; let _ = another_var; } LL | | move || { let _ = bad_ref; let _ = another_var; }
LL | | }; LL | | };
| |__^ type validation failed: encountered 0 at .<deref>.<closure-var(bad_ref)>, but expected something greater or equal to 1 | |__^ type validation failed: encountered 0 at .<deref>.<dyn-downcast>.<closure-var(bad_ref)>, but expected something greater or equal to 1
| |
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior

View File

@ -66,7 +66,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/union-ub-fat-ptr.rs:116:1 --> $DIR/union-ub-fat-ptr.rs:116:1
| |
LL | const G: &Trait = &unsafe { BoolTransmute { val: 3 }.bl }; LL | const G: &Trait = &unsafe { BoolTransmute { val: 3 }.bl };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>, but expected something in the range 0..=1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>.<dyn-downcast>, but expected something in the range 0..=1
| |
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior