Field(Symbol),
Variant(Symbol),
GeneratorState(VariantIdx),
- ClosureVar(Symbol),
+ CapturedVar(Symbol),
ArrayElem(usize),
TupleElem(usize),
Deref,
- Tag,
+ EnumTag,
+ GeneratorTag,
DynDowncast,
}
for elem in path.iter() {
match elem {
Field(name) => write!(out, ".{}", name),
- Variant(name) => write!(out, ".<downcast-variant({})>", name),
+ EnumTag => write!(out, ".<enum-tag>"),
+ Variant(name) => write!(out, ".<enum-variant({})>", name),
+ GeneratorTag => write!(out, ".<generator-tag>"),
GeneratorState(idx) => write!(out, ".<generator-state({})>", idx.index()),
- ClosureVar(name) => write!(out, ".<closure-var({})>", name),
+ CapturedVar(name) => write!(out, ".<captured-var({})>", name),
TupleElem(idx) => write!(out, ".{}", idx),
ArrayElem(idx) => write!(out, "[{}]", idx),
// `.<deref>` does not match Rust syntax, but it is more readable for long paths -- and
// even use the usual syntax because we are just showing the projections,
// not the root.
Deref => write!(out, ".<deref>"),
- Tag => write!(out, ".<enum-tag>"),
DynDowncast => write!(out, ".<dyn-downcast>"),
}
.unwrap()
// "expected something <in the given range>" makes sense.
fn wrapping_range_format(r: &RangeInclusive<u128>, max_hi: u128) -> String {
let (lo, hi) = r.clone().into_inner();
- debug_assert!(hi <= max_hi);
+ assert!(hi <= max_hi);
if lo > hi {
format!("less or equal to {}, or greater or equal to {}", hi, lo)
} else if lo == hi {
format!("equal to {}", lo)
} else if lo == 0 {
- debug_assert!(hi < max_hi, "should not be printing if the range covers everything");
+ assert!(hi < max_hi, "should not be printing if the range covers everything");
format!("less or equal to {}", hi)
} else if hi == max_hi {
- debug_assert!(lo > 0, "should not be printing if the range covers everything");
+ assert!(lo > 0, "should not be printing if the range covers everything");
format!("greater or equal to {}", lo)
} else {
format!("in the range {:?}", r)
impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M> {
fn aggregate_field_path_elem(&mut self, layout: TyLayout<'tcx>, field: usize) -> PathElem {
+ // First, check if we are projecting to a variant.
+ match layout.variants {
+ layout::Variants::Multiple { discr_index, .. } => {
+ if discr_index == field {
+ return match layout.ty.kind {
+ ty::Adt(def, ..) if def.is_enum() => PathElem::EnumTag,
+ ty::Generator(..) => PathElem::GeneratorTag,
+ _ => bug!("non-variant type {:?}", layout.ty),
+ };
+ }
+ }
+ layout::Variants::Single { .. } => {}
+ }
+
+ // Now we know we are projecting to a field, so figure out which one.
match layout.ty.kind {
// generators and closures.
ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
}
}
- PathElem::ClosureVar(name.unwrap_or_else(|| {
+ PathElem::CapturedVar(name.unwrap_or_else(|| {
// Fall back to showing the field index.
sym::integer(field)
}))
// Inside a variant
PathElem::Field(def.variants[index].fields[field].ident.name)
}
- layout::Variants::Multiple { discr_index, .. } => {
- assert_eq!(discr_index, field);
- PathElem::Tag
- }
+ layout::Variants::Multiple { .. } => bug!("we handled variants above"),
}
}
ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
// NOTE: Keep this in sync with the array optimization for int/float
// types below!
- let size = value.layout.size;
let value = value.to_scalar_or_undef();
if self.ref_tracking_for_consts.is_some() {
// Integers/floats in CTFE: Must be scalar bits, pointers are dangerous
- try_validation!(
- value.to_bits(size),
- value,
- self.path,
- "initialized plain (non-pointer) bytes"
- );
+ let is_bits = value.not_undef().map_or(false, |v| v.is_bits());
+ if !is_bits {
+ throw_validation_failure!(
+ value,
+ self.path,
+ "initialized plain (non-pointer) bytes"
+ )
+ }
} else {
// At run-time, for now, we accept *anything* for these types, including
// undef. We should fix that, but let's start low.
);
match err.kind {
err_unsup!(InvalidNullPointerUsage) => {
- throw_validation_failure!("NULL reference", self.path)
+ throw_validation_failure!("a NULL reference", self.path)
}
err_unsup!(AlignmentCheckFailed { required, has }) => {
throw_validation_failure!(
format_args!(
- "unaligned reference \
+ "an unaligned reference \
(required {} byte alignment but found {})",
required.bytes(),
has.bytes()
)
}
err_unsup!(ReadBytesAsPointer) => throw_validation_failure!(
- "dangling reference (created from integer)",
+ "a dangling reference (created from integer)",
self.path
),
_ => throw_validation_failure!(
- "dangling reference (not entirely in bounds)",
+ "a dangling reference (not entirely in bounds)",
self.path
),
}
fn visit_scalar(
&mut self,
op: OpTy<'tcx, M::PointerTag>,
- layout: &layout::Scalar,
+ scalar_layout: &layout::Scalar,
) -> InterpResult<'tcx> {
let value = self.ecx.read_scalar(op)?;
+ let valid_range = &scalar_layout.valid_range;
+ let (lo, hi) = valid_range.clone().into_inner();
// Determine the allowed range
- let (lo, hi) = layout.valid_range.clone().into_inner();
// `max_hi` is as big as the size fits
- let max_hi = u128::max_value() >> (128 - op.layout.size.bits());
+ let max_hi = u128::MAX >> (128 - op.layout.size.bits());
assert!(hi <= max_hi);
// We could also write `(hi + 1) % (max_hi + 1) == lo` but `max_hi + 1` overflows for `u128`
if (lo == 0 && hi == max_hi) || (hi + 1 == lo) {
value.not_undef(),
value,
self.path,
- format_args!("something {}", wrapping_range_format(&layout.valid_range, max_hi),)
+ format_args!("something {}", wrapping_range_format(valid_range, max_hi),)
);
let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) {
Err(ptr) => {
self.path,
format_args!(
"something that cannot possibly fail to be {}",
- wrapping_range_format(&layout.valid_range, max_hi)
+ wrapping_range_format(valid_range, max_hi)
)
)
}
self.path,
format_args!(
"something that cannot possibly fail to be {}",
- wrapping_range_format(&layout.valid_range, max_hi)
+ wrapping_range_format(valid_range, max_hi)
)
)
}
Ok(data) => data,
};
// Now compare. This is slightly subtle because this is a special "wrap-around" range.
- if wrapping_range_contains(&layout.valid_range, bits) {
+ if wrapping_range_contains(&valid_range, bits) {
Ok(())
} else {
throw_validation_failure!(
bits,
self.path,
- format_args!("something {}", wrapping_range_format(&layout.valid_range, max_hi))
+ format_args!("something {}", wrapping_range_format(valid_range, max_hi))
)
}
}
// *After* all of this, check the ABI. We need to check the ABI to handle
// types like `NonNull` where the `Scalar` info is more restrictive than what
- // the fields say. But in most cases, this will just propagate what the fields say,
+ // the fields say (`rustc_layout_scalar_valid_range_start`).
+ // But in most cases, this will just propagate what the fields say,
// and then we want the error to point at the field -- so, first recurse,
// then check ABI.
//
// MyNewtype and then the scalar in there).
match op.layout.abi {
layout::Abi::Uninhabited => unreachable!(), // checked above
- layout::Abi::Scalar(ref layout) => {
- self.visit_scalar(op, layout)?;
+ layout::Abi::Scalar(ref scalar_layout) => {
+ self.visit_scalar(op, scalar_layout)?;
+ }
+ layout::Abi::ScalarPair { .. } | layout::Abi::Vector { .. } => {
+ // These have fields that we already visited above, so we already checked
+ // all their scalar-level restrictions.
+ // There is also no equivalent to `rustc_layout_scalar_valid_range_start`
+ // that would make skipping them here an issue.
+ }
+ layout::Abi::Aggregate { .. } => {
+ // Nothing to do.
}
- // FIXME: Should we do something for ScalarPair? Vector?
- _ => {}
}
Ok(())