Skip to content

Adjust Miri value visitor, and doc-comment layout components #69257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 2, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/librustc_mir/interpret/intern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx
self.walk_aggregate(mplace, fields)
}

fn visit_primitive(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
fn visit_value(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
// Handle Reference types, as these are the only relocations supported by const eval.
// Raw pointers (and boxes) are handled by the `leftover_relocations` logic.
let ty = mplace.layout.ty;
Expand Down Expand Up @@ -263,8 +263,11 @@ impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx
None => self.ref_tracking.track((mplace, mutability, mode), || ()),
}
}
Ok(())
} else {
// Not a reference -- proceed recursively.
self.walk_value(mplace)
}
Ok(())
}
}

Expand Down
189 changes: 121 additions & 68 deletions src/librustc_mir/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,16 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M

// enums
ty::Adt(def, ..) if def.is_enum() => {
// we might be projecting *to* a variant, or to a field *in*a variant.
// we might be projecting *to* a variant, or to a field *in* a variant.
match layout.variants {
layout::Variants::Single { index } => {
// Inside a variant
PathElem::Field(def.variants[index].fields[field].ident.name)
}
_ => bug!(),
layout::Variants::Multiple { discr_index, .. } => {
assert_eq!(discr_index, field);
PathElem::Tag
}
}
}

Expand Down Expand Up @@ -288,62 +291,6 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M

Ok(())
}
}

impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
for ValidityVisitor<'rt, 'mir, 'tcx, M>
{
type V = OpTy<'tcx, M::PointerTag>;

#[inline(always)]
fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> {
&self.ecx
}

#[inline]
fn visit_field(
&mut self,
old_op: OpTy<'tcx, M::PointerTag>,
field: usize,
new_op: OpTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx> {
let elem = self.aggregate_field_path_elem(old_op.layout, field);
self.visit_elem(new_op, elem)
}

#[inline]
fn visit_variant(
&mut self,
old_op: OpTy<'tcx, M::PointerTag>,
variant_id: VariantIdx,
new_op: OpTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx> {
let name = match old_op.layout.ty.kind {
ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name),
// Generators also have variants
ty::Generator(..) => PathElem::GeneratorState(variant_id),
_ => bug!("Unexpected type with variant: {:?}", old_op.layout.ty),
};
self.visit_elem(new_op, name)
}

#[inline]
fn visit_value(&mut self, op: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
trace!("visit_value: {:?}, {:?}", *op, op.layout);
// Translate some possible errors to something nicer.
match self.walk_value(op) {
Ok(()) => Ok(()),
Err(err) => match err.kind {
err_ub!(InvalidDiscriminant(val)) => {
throw_validation_failure!(val, self.path, "a valid enum discriminant")
}
err_unsup!(ReadPointerAsBytes) => {
throw_validation_failure!("a pointer", self.path, "plain (non-pointer) bytes")
}
_ => Err(err),
},
}
}

fn visit_primitive(&mut self, value: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
let value = self.ecx.read_immediate(value)?;
Expand Down Expand Up @@ -415,25 +362,25 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
);
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 \
(required {} byte alignment but found {})",
"an unaligned reference \
(required {} byte alignment but found {})",
required.bytes(),
has.bytes()
),
self.path
)
}
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
),
}
Expand Down Expand Up @@ -485,16 +432,12 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
);
// FIXME: Check if the signature matches
}
// This should be all the primitive types
// This should be all the (inhabited) primitive types
_ => bug!("Unexpected primitive type {}", value.layout.ty),
}
Ok(())
}

fn visit_uninhabited(&mut self) -> InterpResult<'tcx> {
throw_validation_failure!("a value of an uninhabited type", self.path)
}

fn visit_scalar(
&mut self,
op: OpTy<'tcx, M::PointerTag>,
Expand Down Expand Up @@ -559,6 +502,116 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
)
}
}
}

impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
for ValidityVisitor<'rt, 'mir, 'tcx, M>
{
type V = OpTy<'tcx, M::PointerTag>;

#[inline(always)]
fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> {
&self.ecx
}

#[inline]
fn visit_field(
&mut self,
old_op: OpTy<'tcx, M::PointerTag>,
field: usize,
new_op: OpTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx> {
let elem = self.aggregate_field_path_elem(old_op.layout, field);
self.visit_elem(new_op, elem)
}

#[inline]
fn visit_variant(
&mut self,
old_op: OpTy<'tcx, M::PointerTag>,
variant_id: VariantIdx,
new_op: OpTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx> {
let name = match old_op.layout.ty.kind {
ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name),
// Generators also have variants
ty::Generator(..) => PathElem::GeneratorState(variant_id),
_ => bug!("Unexpected type with variant: {:?}", old_op.layout.ty),
};
self.visit_elem(new_op, name)
}

#[inline(always)]
fn visit_union(&mut self, _v: Self::V, fields: usize) -> InterpResult<'tcx> {
// Empty unions are not accepted by rustc. That's great, it means we can
// use that as a signal for detecting primitives. Make sure
// we did not miss any primitive.
assert!(fields > 0);
Ok(())
}

#[inline]
fn visit_value(&mut self, op: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
trace!("visit_value: {:?}, {:?}", *op, op.layout);

if op.layout.abi.is_uninhabited() {
// Uninhabited types do not have sensible layout, stop right here.
throw_validation_failure!(
format_args!("a value of uninhabited type {:?}", op.layout.ty),
self.path
)
}

// Check primitive types. We do this after checking for uninhabited types,
// to exclude fieldless enums (that also appear as fieldless unions here).
// Primitives can have varying layout, so we check them separately and before aggregate
// handling.
// It is CRITICAL that we get this check right, or we might be validating the wrong thing!
let primitive = match op.layout.fields {
// Primitives appear as Union with 0 fields - except for Boxes and fat pointers.
// (Fieldless enums also appear here, but they are uninhabited and thus handled above.)
layout::FieldPlacement::Union(0) => true,
_ => op.layout.ty.builtin_deref(true).is_some(),
};
if primitive {
// No need to recurse further or check scalar layout, this is a leaf type.
return self.visit_primitive(op);
}

// Recursively walk the type. Translate some possible errors to something nicer.
match self.walk_value(op) {
Ok(()) => {}
Err(err) => match err.kind {
err_ub!(InvalidDiscriminant(val)) => {
throw_validation_failure!(val, self.path, "a valid enum discriminant")
}
err_unsup!(ReadPointerAsBytes) => {
throw_validation_failure!("a pointer", self.path, "plain (non-pointer) bytes")
}
_ => return Err(err),
},
}

// *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,
// and then we want the error to point at the field -- so, first recurse,
// then check ABI.
//
// FIXME: We could avoid some redundant checks here. For newtypes wrapping
// scalars, we do the same check on every "level" (e.g., first we check
// 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)?;
}
// FIXME: Should we do something for ScalarPair? Vector?
_ => {}
}

Ok(())
}

fn visit_aggregate(
&mut self,
Expand Down
Loading