Skip to content

Commit f8999fb

Browse files
committed
miri value visitor: fix some wrong assumptions about layout; improve error messages
1 parent ee49e95 commit f8999fb

13 files changed

+174
-180
lines changed

src/librustc_mir/interpret/intern.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx
187187
self.walk_aggregate(mplace, fields)
188188
}
189189

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

src/librustc_mir/interpret/validity.rs

Lines changed: 117 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,16 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M
201201

202202
// enums
203203
ty::Adt(def, ..) if def.is_enum() => {
204-
// we might be projecting *to* a variant, or to a field *in*a variant.
204+
// we might be projecting *to* a variant, or to a field *in* a variant.
205205
match layout.variants {
206206
layout::Variants::Single { index } => {
207207
// Inside a variant
208208
PathElem::Field(def.variants[index].fields[field].ident.name)
209209
}
210-
_ => bug!(),
210+
layout::Variants::Multiple { discr_index, .. } => {
211+
assert_eq!(discr_index, field);
212+
PathElem::Tag
213+
}
211214
}
212215
}
213216

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

289292
Ok(())
290293
}
291-
}
292-
293-
impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
294-
for ValidityVisitor<'rt, 'mir, 'tcx, M>
295-
{
296-
type V = OpTy<'tcx, M::PointerTag>;
297-
298-
#[inline(always)]
299-
fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> {
300-
&self.ecx
301-
}
302-
303-
#[inline]
304-
fn visit_field(
305-
&mut self,
306-
old_op: OpTy<'tcx, M::PointerTag>,
307-
field: usize,
308-
new_op: OpTy<'tcx, M::PointerTag>,
309-
) -> InterpResult<'tcx> {
310-
let elem = self.aggregate_field_path_elem(old_op.layout, field);
311-
self.visit_elem(new_op, elem)
312-
}
313-
314-
#[inline]
315-
fn visit_variant(
316-
&mut self,
317-
old_op: OpTy<'tcx, M::PointerTag>,
318-
variant_id: VariantIdx,
319-
new_op: OpTy<'tcx, M::PointerTag>,
320-
) -> InterpResult<'tcx> {
321-
let name = match old_op.layout.ty.kind {
322-
ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name),
323-
// Generators also have variants
324-
ty::Generator(..) => PathElem::GeneratorState(variant_id),
325-
_ => bug!("Unexpected type with variant: {:?}", old_op.layout.ty),
326-
};
327-
self.visit_elem(new_op, name)
328-
}
329-
330-
#[inline]
331-
fn visit_value(&mut self, op: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
332-
trace!("visit_value: {:?}, {:?}", *op, op.layout);
333-
// Translate some possible errors to something nicer.
334-
match self.walk_value(op) {
335-
Ok(()) => Ok(()),
336-
Err(err) => match err.kind {
337-
err_ub!(InvalidDiscriminant(val)) => {
338-
throw_validation_failure!(val, self.path, "a valid enum discriminant")
339-
}
340-
err_unsup!(ReadPointerAsBytes) => {
341-
throw_validation_failure!("a pointer", self.path, "plain (non-pointer) bytes")
342-
}
343-
_ => Err(err),
344-
},
345-
}
346-
}
347294

348295
fn visit_primitive(&mut self, value: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
349296
let value = self.ecx.read_immediate(value)?;
@@ -421,7 +368,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
421368
throw_validation_failure!(
422369
format_args!(
423370
"unaligned reference \
424-
(required {} byte alignment but found {})",
371+
(required {} byte alignment but found {})",
425372
required.bytes(),
426373
has.bytes()
427374
),
@@ -485,16 +432,12 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
485432
);
486433
// FIXME: Check if the signature matches
487434
}
488-
// This should be all the primitive types
435+
// This should be all the (inhabited) primitive types
489436
_ => bug!("Unexpected primitive type {}", value.layout.ty),
490437
}
491438
Ok(())
492439
}
493440

494-
fn visit_uninhabited(&mut self) -> InterpResult<'tcx> {
495-
throw_validation_failure!("a value of an uninhabited type", self.path)
496-
}
497-
498441
fn visit_scalar(
499442
&mut self,
500443
op: OpTy<'tcx, M::PointerTag>,
@@ -559,6 +502,116 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
559502
)
560503
}
561504
}
505+
}
506+
507+
impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
508+
for ValidityVisitor<'rt, 'mir, 'tcx, M>
509+
{
510+
type V = OpTy<'tcx, M::PointerTag>;
511+
512+
#[inline(always)]
513+
fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> {
514+
&self.ecx
515+
}
516+
517+
#[inline]
518+
fn visit_field(
519+
&mut self,
520+
old_op: OpTy<'tcx, M::PointerTag>,
521+
field: usize,
522+
new_op: OpTy<'tcx, M::PointerTag>,
523+
) -> InterpResult<'tcx> {
524+
let elem = self.aggregate_field_path_elem(old_op.layout, field);
525+
self.visit_elem(new_op, elem)
526+
}
527+
528+
#[inline]
529+
fn visit_variant(
530+
&mut self,
531+
old_op: OpTy<'tcx, M::PointerTag>,
532+
variant_id: VariantIdx,
533+
new_op: OpTy<'tcx, M::PointerTag>,
534+
) -> InterpResult<'tcx> {
535+
let name = match old_op.layout.ty.kind {
536+
ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name),
537+
// Generators also have variants
538+
ty::Generator(..) => PathElem::GeneratorState(variant_id),
539+
_ => bug!("Unexpected type with variant: {:?}", old_op.layout.ty),
540+
};
541+
self.visit_elem(new_op, name)
542+
}
543+
544+
#[inline(always)]
545+
fn visit_union(&mut self, _v: Self::V, fields: usize) -> InterpResult<'tcx> {
546+
// Empty unions are not accepted by rustc. That's great, it means we can
547+
// use that as a signal for detecting primitives. Make sure
548+
// we did not miss any primitive.
549+
assert!(fields > 0);
550+
Ok(())
551+
}
552+
553+
#[inline]
554+
fn visit_value(&mut self, op: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
555+
trace!("visit_value: {:?}, {:?}", *op, op.layout);
556+
557+
if op.layout.abi.is_uninhabited() {
558+
// Uninhabited types do not have sensible layout, stop right here.
559+
throw_validation_failure!(
560+
format_args!("a value of uninhabited type {:?}", op.layout.ty),
561+
self.path
562+
)
563+
}
564+
565+
// Check primitive types. We do this after checking for uninhabited types,
566+
// to exclude fieldless enums (that also appear as fieldless unions here).
567+
// Primitives can have varying layout, so we check them separately and before aggregate
568+
// handling.
569+
// It is CRITICAL that we get this check right, or we might be validating the wrong thing!
570+
let primitive = match op.layout.fields {
571+
// Primitives appear as Union with 0 fields - except for Boxes and fat pointers.
572+
// (Fieldless enums also appear here, but they are uninhabited and thus handled above.)
573+
layout::FieldPlacement::Union(0) => true,
574+
_ => op.layout.ty.builtin_deref(true).is_some(),
575+
};
576+
if primitive {
577+
// No need to recurse further or check scalar layout, this is a leaf type.
578+
return self.visit_primitive(op);
579+
}
580+
581+
// Recursively walk the type. Translate some possible errors to something nicer.
582+
match self.walk_value(op) {
583+
Ok(()) => {}
584+
Err(err) => match err.kind {
585+
err_ub!(InvalidDiscriminant(val)) => {
586+
throw_validation_failure!(val, self.path, "a valid enum discriminant")
587+
}
588+
err_unsup!(ReadPointerAsBytes) => {
589+
throw_validation_failure!("a pointer", self.path, "plain (non-pointer) bytes")
590+
}
591+
_ => return Err(err),
592+
},
593+
}
594+
595+
// *After* all of this, check the ABI. We need to check the ABI to handle
596+
// types like `NonNull` where the `Scalar` info is more restrictive than what
597+
// the fields say. But in most cases, this will just propagate what the fields say,
598+
// and then we want the error to point at the field -- so, first recurse,
599+
// then check ABI.
600+
//
601+
// FIXME: We could avoid some redundant checks here. For newtypes wrapping
602+
// scalars, we do the same check on every "level" (e.g., first we check
603+
// MyNewtype and then the scalar in there).
604+
match op.layout.abi {
605+
layout::Abi::Uninhabited => unreachable!(), // checked above
606+
layout::Abi::Scalar(ref layout) => {
607+
self.visit_scalar(op, layout)?;
608+
}
609+
// FIXME: Should we do something for ScalarPair? Vector?
610+
_ => {}
611+
}
612+
613+
Ok(())
614+
}
562615

563616
fn visit_aggregate(
564617
&mut self,

0 commit comments

Comments
 (0)