Skip to content
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

Miri: dispatch first on the type #62946

Merged
merged 13 commits into from
Aug 3, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1863,6 +1863,12 @@ impl<'tcx> TyS<'tcx> {
}
}

/// Tests if this is any kind of primitive pointer type (reference, raw pointer, fn pointer).
#[inline]
pub fn is_any_ptr(&self) -> bool {
self.is_region_ptr() || self.is_unsafe_ptr() || self.is_fn_ptr()
}

/// Returns `true` if this type is an `Arc<T>`.
#[inline]
pub fn is_arc(&self) -> bool {
Expand Down
15 changes: 12 additions & 3 deletions src/librustc_mir/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ use rustc_data_structures::fx::FxHashMap;
use syntax::source_map::{Span, DUMMY_SP};

use crate::interpret::{self,
PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar,
PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer,
RawConst, ConstValue,
InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup,
Allocation, AllocId, MemoryKind,
Allocation, AllocId, MemoryKind, Memory,
snapshot, RefTracking, intern_const_alloc_recursive,
};

Expand Down Expand Up @@ -397,7 +397,16 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
)
}

fn ptr_op(
fn ptr_to_int(
_mem: &Memory<'mir, 'tcx, Self>,
_ptr: Pointer,
) -> InterpResult<'tcx, u64> {
Err(
ConstEvalError::NeedsRfc("pointer-to-integer cast".to_string()).into(),
)
}

fn binary_ptr_op(
_ecx: &InterpCx<'mir, 'tcx, Self>,
_bin_op: mir::BinOp,
_left: ImmTy<'tcx>,
Expand Down
156 changes: 65 additions & 91 deletions src/librustc_mir/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,13 @@ use syntax::symbol::sym;
use rustc_apfloat::ieee::{Single, Double};
use rustc_apfloat::{Float, FloatConvert};
use rustc::mir::interpret::{
Scalar, InterpResult, Pointer, PointerArithmetic,
Scalar, InterpResult, PointerArithmetic,
};
use rustc::mir::CastKind;

use super::{InterpCx, Machine, PlaceTy, OpTy, Immediate, FnVal};
use super::{InterpCx, Machine, PlaceTy, OpTy, ImmTy, Immediate, FnVal};

impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool {
match ty.sty {
ty::RawPtr(ty::TypeAndMut { ty, .. }) |
ty::Ref(_, ty, _) => !self.type_is_sized(ty),
ty::Adt(def, _) if def.is_box() => !self.type_is_sized(ty.boxed_ty()),
_ => false,
}
}

pub fn cast(
&mut self,
src: OpTy<'tcx, M::PointerTag>,
Expand All @@ -37,40 +28,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {

Misc | Pointer(PointerCast::MutToConstPointer) => {
let src = self.read_immediate(src)?;

if self.type_is_fat_ptr(src.layout.ty) {
match (*src, self.type_is_fat_ptr(dest.layout.ty)) {
// pointers to extern types
(Immediate::Scalar(_),_) |
// slices and trait objects to other slices/trait objects
(Immediate::ScalarPair(..), true) => {
// No change to immediate
self.write_immediate(*src, dest)?;
}
// slices and trait objects to thin pointers (dropping the metadata)
(Immediate::ScalarPair(data, _), false) => {
self.write_scalar(data, dest)?;
}
}
} else {
match src.layout.variants {
layout::Variants::Single { index } => {
if let Some(discr) =
src.layout.ty.discriminant_for_variant(*self.tcx, index)
{
// Cast from a univariant enum
assert!(src.layout.is_zst());
return self.write_scalar(
Scalar::from_uint(discr.val, dest.layout.size),
dest);
}
}
layout::Variants::Multiple { .. } => {},
}

let dest_val = self.cast_scalar(src.to_scalar()?, src.layout, dest.layout)?;
self.write_scalar(dest_val, dest)?;
}
let res = self.cast_immediate(src, dest.layout)?;
self.write_immediate(res, dest)?;
}

Pointer(PointerCast::ReifyFnPointer) => {
Expand Down Expand Up @@ -126,36 +85,76 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Ok(())
}

fn cast_scalar(
fn cast_immediate(
&self,
val: Scalar<M::PointerTag>,
src_layout: TyLayout<'tcx>,
src: ImmTy<'tcx, M::PointerTag>,
dest_layout: TyLayout<'tcx>,
) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
use rustc::ty::TyKind::*;
trace!("Casting {:?}: {:?} to {:?}", val, src_layout.ty, dest_layout.ty);
trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, dest_layout.ty);

match src_layout.ty.sty {
match src.layout.ty.sty {
// Floating point
Float(FloatTy::F32) => self.cast_from_float(val.to_f32()?, dest_layout.ty),
Float(FloatTy::F64) => self.cast_from_float(val.to_f64()?, dest_layout.ty),
// Integer(-like), including fn ptr casts and casts from enums that
// are represented as integers (this excludes univariant enums, which
// are handled in `cast` directly).
_ => {
Float(FloatTy::F32) =>
return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, dest_layout.ty)?.into()),
Float(FloatTy::F64) =>
return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, dest_layout.ty)?.into()),
// The rest is integer/pointer-"like", including fn ptr casts and casts from enums that
// are represented as integers.
_ =>
assert!(
src_layout.ty.is_bool() || src_layout.ty.is_char() ||
src_layout.ty.is_enum() || src_layout.ty.is_integral() ||
src_layout.ty.is_unsafe_ptr() || src_layout.ty.is_fn_ptr() ||
src_layout.ty.is_region_ptr(),
"Unexpected cast from type {:?}", src_layout.ty
);
match val.to_bits_or_ptr(src_layout.size, self) {
Err(ptr) => self.cast_from_ptr(ptr, src_layout, dest_layout),
Ok(data) => self.cast_from_int(data, src_layout, dest_layout),
src.layout.ty.is_bool() || src.layout.ty.is_char() ||
src.layout.ty.is_enum() || src.layout.ty.is_integral() ||
src.layout.ty.is_any_ptr(),
"Unexpected cast from type {:?}", src.layout.ty
)
}

// Handle cast from a univariant (ZST) enum.
match src.layout.variants {
layout::Variants::Single { index } => {
if let Some(discr) =
src.layout.ty.discriminant_for_variant(*self.tcx, index)
{
assert!(src.layout.is_zst());
return Ok(Scalar::from_uint(discr.val, dest_layout.size).into());
}
}
layout::Variants::Multiple { .. } => {},
}

// Handle casting the metadata away from a fat pointer.
if src.layout.ty.is_unsafe_ptr() && dest_layout.ty.is_unsafe_ptr() &&
dest_layout.size != src.layout.size
{
assert_eq!(src.layout.size, 2*self.memory.pointer_size());
assert_eq!(dest_layout.size, self.memory.pointer_size());
assert!(dest_layout.ty.is_unsafe_ptr());
match *src {
Immediate::ScalarPair(data, _) =>
return Ok(data.into()),
Immediate::Scalar(..) =>
bug!(
"{:?} input to a fat-to-thin cast ({:?} -> {:?})",
*src, src.layout.ty, dest_layout.ty
),
};
}

// Handle casting any ptr to raw ptr (might be a fat ptr).
if src.layout.ty.is_any_ptr() && dest_layout.ty.is_unsafe_ptr()
{
// The only possible size-unequal case was handled above.
assert_eq!(src.layout.size, dest_layout.size);
return Ok(*src);
}

// For all remaining casts, we either
// (a) cast a raw ptr to usize, or
// (b) cast from an integer-like (including bool, char, enums).
// In both cases we want the bits.
let bits = self.force_bits(src.to_scalar()?, src.layout.size)?;
Ok(self.cast_from_int(bits, src.layout, dest_layout)?.into())
}

fn cast_from_int(
Expand Down Expand Up @@ -236,31 +235,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}

fn cast_from_ptr(
&self,
ptr: Pointer<M::PointerTag>,
src_layout: TyLayout<'tcx>,
dest_layout: TyLayout<'tcx>,
) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
use rustc::ty::TyKind::*;

match dest_layout.ty.sty {
// Casting to a reference or fn pointer is not permitted by rustc,
// no need to support it here.
RawPtr(_) => Ok(ptr.into()),
Int(_) | Uint(_) => {
let size = self.memory.pointer_size();

match self.force_bits(Scalar::Ptr(ptr), size) {
Ok(bits) => self.cast_from_int(bits, src_layout, dest_layout),
Err(_) if dest_layout.size == size => Ok(ptr.into()),
Err(e) => Err(e),
}
}
_ => bug!("invalid MIR: ptr to {:?} cast", dest_layout.ty)
}
}

fn unsize_into_ptr(
&mut self,
src: OpTy<'tcx, M::PointerTag>,
Expand Down
11 changes: 3 additions & 8 deletions src/librustc_mir/interpret/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,10 @@ pub trait Machine<'mir, 'tcx>: Sized {
def_id: DefId,
) -> InterpResult<'tcx, Cow<'tcx, Allocation>>;

/// Called for all binary operations on integer(-like) types when one operand is a pointer
/// value, and for the `Offset` operation that is inherently about pointers.
/// Called for all binary operations where the LHS has pointer type.
///
/// Returns a (value, overflowed) pair if the operation succeeded
fn ptr_op(
fn binary_ptr_op(
ecx: &InterpCx<'mir, 'tcx, Self>,
bin_op: mir::BinOp,
left: ImmTy<'tcx, Self::PointerTag>,
Expand Down Expand Up @@ -234,7 +233,6 @@ pub trait Machine<'mir, 'tcx>: Sized {
extra: Self::FrameExtra,
) -> InterpResult<'tcx>;

#[inline(always)]
fn int_to_ptr(
_mem: &Memory<'mir, 'tcx, Self>,
int: u64,
Expand All @@ -246,11 +244,8 @@ pub trait Machine<'mir, 'tcx>: Sized {
}).into())
}

#[inline(always)]
fn ptr_to_int(
_mem: &Memory<'mir, 'tcx, Self>,
_ptr: Pointer<Self::PointerTag>,
) -> InterpResult<'tcx, u64> {
throw_unsup!(ReadPointerAsBytes)
}
) -> InterpResult<'tcx, u64>;
}
33 changes: 21 additions & 12 deletions src/librustc_mir/interpret/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,21 @@ pub enum Immediate<Tag=(), Id=AllocId> {
ScalarPair(ScalarMaybeUndef<Tag, Id>, ScalarMaybeUndef<Tag, Id>),
}

impl<'tcx, Tag> Immediate<Tag> {
#[inline]
pub fn from_scalar(val: Scalar<Tag>) -> Self {
Immediate::Scalar(ScalarMaybeUndef::Scalar(val))
impl<Tag> From<ScalarMaybeUndef<Tag>> for Immediate<Tag> {
#[inline(always)]
fn from(val: ScalarMaybeUndef<Tag>) -> Self {
Immediate::Scalar(val)
}
}

impl<Tag> From<Scalar<Tag>> for Immediate<Tag> {
#[inline(always)]
fn from(val: Scalar<Tag>) -> Self {
Immediate::Scalar(val.into())
}
}

impl<'tcx, Tag> Immediate<Tag> {
pub fn new_slice(
val: Scalar<Tag>,
len: u64,
Expand Down Expand Up @@ -182,7 +191,7 @@ impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag>
{
#[inline]
pub fn from_scalar(val: Scalar<Tag>, layout: TyLayout<'tcx>) -> Self {
ImmTy { imm: Immediate::from_scalar(val), layout }
ImmTy { imm: val.into(), layout }
}

#[inline]
Expand Down Expand Up @@ -240,7 +249,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let ptr = match self.check_mplace_access(mplace, None)? {
Some(ptr) => ptr,
None => return Ok(Some(ImmTy { // zero-sized type
imm: Immediate::Scalar(Scalar::zst().into()),
imm: Scalar::zst().into(),
layout: mplace.layout,
})),
};
Expand All @@ -251,7 +260,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
.get(ptr.alloc_id)?
.read_scalar(self, ptr, mplace.layout.size)?;
Ok(Some(ImmTy {
imm: Immediate::Scalar(scalar),
imm: scalar.into(),
layout: mplace.layout,
}))
}
Expand Down Expand Up @@ -354,7 +363,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let field = field.try_into().unwrap();
let field_layout = op.layout.field(self, field)?;
if field_layout.is_zst() {
let immediate = Immediate::Scalar(Scalar::zst().into());
let immediate = Scalar::zst().into();
return Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout });
}
let offset = op.layout.fields.offset(field);
Expand All @@ -364,7 +373,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// extract fields from types with `ScalarPair` ABI
Immediate::ScalarPair(a, b) => {
let val = if offset.bytes() == 0 { a } else { b };
Immediate::Scalar(val)
Immediate::from(val)
},
Immediate::Scalar(val) =>
bug!("field access on non aggregate {:#?}, {:#?}", val, op.layout),
Expand Down Expand Up @@ -401,7 +410,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Deref => self.deref_operand(base)?.into(),
Subslice { .. } | ConstantIndex { .. } | Index(_) => if base.layout.is_zst() {
OpTy {
op: Operand::Immediate(Immediate::Scalar(Scalar::zst().into())),
op: Operand::Immediate(Scalar::zst().into()),
// the actual index doesn't matter, so we just pick a convenient one like 0
layout: base.layout.field(self, 0)?,
}
Expand All @@ -425,7 +434,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let layout = self.layout_of_local(frame, local, layout)?;
let op = if layout.is_zst() {
// Do not read from ZST, they might not be initialized
Operand::Immediate(Immediate::Scalar(Scalar::zst().into()))
Operand::Immediate(Scalar::zst().into())
} else {
frame.locals[local].access()?
};
Expand Down Expand Up @@ -556,7 +565,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Operand::Indirect(MemPlace::from_ptr(ptr, align))
},
ConstValue::Scalar(x) =>
Operand::Immediate(Immediate::Scalar(tag_scalar(x).into())),
Operand::Immediate(tag_scalar(x).into()),
ConstValue::Slice { data, start, end } => {
// We rely on mutability being set correctly in `data` to prevent writes
// where none should happen.
Expand Down
Loading