提交 f96208ca 编写于 作者: R Ralf Jung

address nits

上级 6c78fa82
...@@ -517,7 +517,6 @@ fn hash_stable<W: StableHasherResult>(&self, ...@@ -517,7 +517,6 @@ fn hash_stable<W: StableHasherResult>(&self,
InvalidMemoryAccess | InvalidMemoryAccess |
InvalidFunctionPointer | InvalidFunctionPointer |
InvalidBool | InvalidBool |
InvalidDiscriminant |
InvalidNullPointerUsage | InvalidNullPointerUsage |
ReadPointerAsBytes | ReadPointerAsBytes |
ReadBytesAsPointer | ReadBytesAsPointer |
...@@ -550,6 +549,7 @@ fn hash_stable<W: StableHasherResult>(&self, ...@@ -550,6 +549,7 @@ fn hash_stable<W: StableHasherResult>(&self,
GeneratorResumedAfterReturn | GeneratorResumedAfterReturn |
GeneratorResumedAfterPanic | GeneratorResumedAfterPanic |
InfiniteLoop => {} InfiniteLoop => {}
InvalidDiscriminant(val) => val.hash_stable(hcx, hasher),
Panic { ref msg, ref file, line, col } => { Panic { ref msg, ref file, line, col } => {
msg.hash_stable(hcx, hasher); msg.hash_stable(hcx, hasher);
file.hash_stable(hcx, hasher); file.hash_stable(hcx, hasher);
......
...@@ -190,7 +190,7 @@ pub enum EvalErrorKind<'tcx, O> { ...@@ -190,7 +190,7 @@ pub enum EvalErrorKind<'tcx, O> {
InvalidMemoryAccess, InvalidMemoryAccess,
InvalidFunctionPointer, InvalidFunctionPointer,
InvalidBool, InvalidBool,
InvalidDiscriminant, InvalidDiscriminant(u128),
PointerOutOfBounds { PointerOutOfBounds {
ptr: Pointer, ptr: Pointer,
access: bool, access: bool,
...@@ -302,8 +302,8 @@ pub fn description(&self) -> &str { ...@@ -302,8 +302,8 @@ pub fn description(&self) -> &str {
"tried to use a function pointer after offsetting it", "tried to use a function pointer after offsetting it",
InvalidBool => InvalidBool =>
"invalid boolean value read", "invalid boolean value read",
InvalidDiscriminant => InvalidDiscriminant(..) =>
"invalid enum discriminant value read or written", "invalid enum discriminant value read",
PointerOutOfBounds { .. } => PointerOutOfBounds { .. } =>
"pointer offset outside bounds of allocation", "pointer offset outside bounds of allocation",
InvalidNullPointerUsage => InvalidNullPointerUsage =>
...@@ -488,6 +488,8 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ...@@ -488,6 +488,8 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
align {}", size.bytes(), align.abi(), size2.bytes(), align2.abi()), align {}", size.bytes(), align.abi(), size2.bytes(), align2.abi()),
Panic { ref msg, line, col, ref file } => Panic { ref msg, line, col, ref file } =>
write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col), write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col),
InvalidDiscriminant(val) =>
write!(f, "encountered invalid enum discriminant {}", val),
_ => write!(f, "{}", self.description()), _ => write!(f, "{}", self.description()),
} }
} }
......
...@@ -498,7 +498,7 @@ fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lif ...@@ -498,7 +498,7 @@ fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lif
InvalidMemoryAccess => InvalidMemoryAccess, InvalidMemoryAccess => InvalidMemoryAccess,
InvalidFunctionPointer => InvalidFunctionPointer, InvalidFunctionPointer => InvalidFunctionPointer,
InvalidBool => InvalidBool, InvalidBool => InvalidBool,
InvalidDiscriminant => InvalidDiscriminant, InvalidDiscriminant(val) => InvalidDiscriminant(val),
PointerOutOfBounds { PointerOutOfBounds {
ptr, ptr,
access, access,
......
...@@ -585,7 +585,7 @@ pub fn read_discriminant( ...@@ -585,7 +585,7 @@ pub fn read_discriminant(
.expect("tagged layout for non adt") .expect("tagged layout for non adt")
.discriminants(self.tcx.tcx) .discriminants(self.tcx.tcx)
.position(|var| var.val == real_discr) .position(|var| var.val == real_discr)
.ok_or_else(|| EvalErrorKind::InvalidDiscriminant)?; .ok_or_else(|| EvalErrorKind::InvalidDiscriminant(real_discr))?;
(real_discr, index) (real_discr, index)
}, },
layout::Variants::NicheFilling { layout::Variants::NicheFilling {
......
...@@ -48,111 +48,100 @@ pub fn binop_ignore_overflow( ...@@ -48,111 +48,100 @@ pub fn binop_ignore_overflow(
} }
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
/// Returns the result of the specified operation and whether it overflowed. fn binary_char_op(
pub fn binary_op(
&self, &self,
bin_op: mir::BinOp, bin_op: mir::BinOp,
ValTy { value: left, layout: left_layout }: ValTy<'tcx>, l: char,
ValTy { value: right, layout: right_layout }: ValTy<'tcx>, r: char,
) -> EvalResult<'tcx, (Scalar, bool)> { ) -> EvalResult<'tcx, (Scalar, bool)> {
use rustc::mir::BinOp::*; use rustc::mir::BinOp::*;
let left = left.to_scalar()?; let res = match bin_op {
let right = right.to_scalar()?; Eq => l == r,
Ne => l != r,
Lt => l < r,
Le => l <= r,
Gt => l > r,
Ge => l >= r,
_ => bug!("Invalid operation on char: {:?}", bin_op),
};
return Ok((Scalar::from_bool(res), false));
}
trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", fn binary_bool_op(
bin_op, left, left_layout.ty.sty, right, right_layout.ty.sty); &self,
bin_op: mir::BinOp,
l: bool,
r: bool,
) -> EvalResult<'tcx, (Scalar, bool)> {
use rustc::mir::BinOp::*;
// Handle non-integer operations let res = match bin_op {
if let ty::Char = left_layout.ty.sty { Eq => l == r,
assert_eq!(right_layout.ty.sty, ty::Char); Ne => l != r,
let l = left.to_char()?; Lt => l < r,
let r = right.to_char()?; Le => l <= r,
let res = match bin_op { Gt => l > r,
Eq => l == r, Ge => l >= r,
Ne => l != r, BitAnd => l & r,
Lt => l < r, BitOr => l | r,
Le => l <= r, BitXor => l ^ r,
Gt => l > r, _ => bug!("Invalid operation on bool: {:?}", bin_op),
Ge => l >= r, };
_ => bug!("Invalid operation on char: {:?}", bin_op), return Ok((Scalar::from_bool(res), false));
}; }
return Ok((Scalar::from_bool(res), false));
} fn binary_float_op(
if let ty::Bool = left_layout.ty.sty { &self,
assert_eq!(right_layout.ty.sty, ty::Bool); bin_op: mir::BinOp,
let l = left.to_bool()?; fty: FloatTy,
let r = right.to_bool()?; // passing in raw bits
let res = match bin_op { l: u128,
Eq => l == r, r: u128,
Ne => l != r, ) -> EvalResult<'tcx, (Scalar, bool)> {
Lt => l < r, use rustc::mir::BinOp::*;
Le => l <= r,
Gt => l > r, macro_rules! float_math {
Ge => l >= r, ($ty:path, $size:expr) => {{
BitAnd => l & r, let l = <$ty>::from_bits(l);
BitOr => l | r, let r = <$ty>::from_bits(r);
BitXor => l ^ r, let bitify = |res: ::rustc_apfloat::StatusAnd<$ty>| Scalar::Bits {
_ => bug!("Invalid operation on bool: {:?}", bin_op), bits: res.value.to_bits(),
}; size: $size,
return Ok((Scalar::from_bool(res), false)); };
} let val = match bin_op {
if let ty::Float(fty) = left_layout.ty.sty { Eq => Scalar::from_bool(l == r),
let l = left.to_bits(left_layout.size)?; Ne => Scalar::from_bool(l != r),
let r = right.to_bits(right_layout.size)?; Lt => Scalar::from_bool(l < r),
assert_eq!(right_layout.ty.sty, ty::Float(fty)); Le => Scalar::from_bool(l <= r),
macro_rules! float_math { Gt => Scalar::from_bool(l > r),
($ty:path, $size:expr) => {{ Ge => Scalar::from_bool(l >= r),
let l = <$ty>::from_bits(l); Add => bitify(l + r),
let r = <$ty>::from_bits(r); Sub => bitify(l - r),
let bitify = |res: ::rustc_apfloat::StatusAnd<$ty>| Scalar::Bits { Mul => bitify(l * r),
bits: res.value.to_bits(), Div => bitify(l / r),
size: $size, Rem => bitify(l % r),
}; _ => bug!("invalid float op: `{:?}`", bin_op),
let val = match bin_op { };
Eq => Scalar::from_bool(l == r), return Ok((val, false));
Ne => Scalar::from_bool(l != r), }};
Lt => Scalar::from_bool(l < r),
Le => Scalar::from_bool(l <= r),
Gt => Scalar::from_bool(l > r),
Ge => Scalar::from_bool(l >= r),
Add => bitify(l + r),
Sub => bitify(l - r),
Mul => bitify(l * r),
Div => bitify(l / r),
Rem => bitify(l % r),
_ => bug!("invalid float op: `{:?}`", bin_op),
};
return Ok((val, false));
}};
}
match fty {
FloatTy::F32 => float_math!(Single, 4),
FloatTy::F64 => float_math!(Double, 8),
}
}
// Only integers left
#[inline]
fn is_ptr<'tcx>(ty: ty::Ty<'tcx>) -> bool {
match ty.sty {
ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true,
_ => false,
}
} }
assert!(left_layout.ty.is_integral() || is_ptr(left_layout.ty)); match fty {
assert!(right_layout.ty.is_integral() || is_ptr(right_layout.ty)); FloatTy::F32 => float_math!(Single, 4),
FloatTy::F64 => float_math!(Double, 8),
// Handle operations that support pointers
if let Some(handled) =
M::try_ptr_op(self, bin_op, left, left_layout, right, right_layout)?
{
return Ok(handled);
} }
}
// From now on, everything must be bytes, no pointer values fn binary_int_op(
// (this is independent of the type) &self,
let l = left.to_bits(left_layout.size)?; bin_op: mir::BinOp,
let r = right.to_bits(right_layout.size)?; // passing in raw bits
l: u128,
left_layout: TyLayout<'tcx>,
r: u128,
right_layout: TyLayout<'tcx>,
) -> EvalResult<'tcx, (Scalar, bool)> {
use rustc::mir::BinOp::*;
// Shift ops can have an RHS with a different numeric type. // Shift ops can have an RHS with a different numeric type.
if bin_op == Shl || bin_op == Shr { if bin_op == Shl || bin_op == Shr {
...@@ -189,11 +178,11 @@ fn is_ptr<'tcx>(ty: ty::Ty<'tcx>) -> bool { ...@@ -189,11 +178,11 @@ fn is_ptr<'tcx>(ty: ty::Ty<'tcx>) -> bool {
// For the remaining ops, the types must be the same on both sides // For the remaining ops, the types must be the same on both sides
if left_layout.ty != right_layout.ty { if left_layout.ty != right_layout.ty {
let msg = format!( let msg = format!(
"unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", "unimplemented asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})",
bin_op, bin_op,
left, l,
left_layout.ty, left_layout.ty,
right, r,
right_layout.ty right_layout.ty
); );
return err!(Unimplemented(msg)); return err!(Unimplemented(msg));
...@@ -289,11 +278,10 @@ fn is_ptr<'tcx>(ty: ty::Ty<'tcx>) -> bool { ...@@ -289,11 +278,10 @@ fn is_ptr<'tcx>(ty: ty::Ty<'tcx>) -> bool {
_ => { _ => {
let msg = format!( let msg = format!(
"unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", "unimplemented binary op {:?}: {:?}, {:?} (both {:?})",
bin_op, bin_op,
left, l,
left_layout.ty, r,
right,
right_layout.ty, right_layout.ty,
); );
return err!(Unimplemented(msg)); return err!(Unimplemented(msg));
...@@ -303,6 +291,65 @@ fn is_ptr<'tcx>(ty: ty::Ty<'tcx>) -> bool { ...@@ -303,6 +291,65 @@ fn is_ptr<'tcx>(ty: ty::Ty<'tcx>) -> bool {
Ok((val, false)) Ok((val, false))
} }
/// Returns the result of the specified operation and whether it overflowed.
pub fn binary_op(
&self,
bin_op: mir::BinOp,
ValTy { value: left, layout: left_layout }: ValTy<'tcx>,
ValTy { value: right, layout: right_layout }: ValTy<'tcx>,
) -> EvalResult<'tcx, (Scalar, bool)> {
let left = left.to_scalar()?;
let right = right.to_scalar()?;
trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})",
bin_op, left, left_layout.ty.sty, right, right_layout.ty.sty);
match left_layout.ty.sty {
ty::Char => {
assert_eq!(left_layout.ty, right_layout.ty);
let l = left.to_char()?;
let r = right.to_char()?;
self.binary_char_op(bin_op, l, r)
}
ty::Bool => {
assert_eq!(left_layout.ty, right_layout.ty);
let l = left.to_bool()?;
let r = right.to_bool()?;
self.binary_bool_op(bin_op, l, r)
}
ty::Float(fty) => {
assert_eq!(left_layout.ty, right_layout.ty);
let l = left.to_bits(left_layout.size)?;
let r = right.to_bits(right_layout.size)?;
self.binary_float_op(bin_op, fty, l, r)
}
_ => {
// Must be integer(-like) types
#[inline]
fn is_ptr<'tcx>(ty: ty::Ty<'tcx>) -> bool {
match ty.sty {
ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true,
_ => false,
}
}
assert!(left_layout.ty.is_integral() || is_ptr(left_layout.ty));
assert!(right_layout.ty.is_integral() || is_ptr(right_layout.ty));
// Handle operations that support pointer values
if let Some(handled) =
M::try_ptr_op(self, bin_op, left, left_layout, right, right_layout)?
{
return Ok(handled);
}
// Everything else only works with "proper" bits
let l = left.to_bits(left_layout.size)?;
let r = right.to_bits(right_layout.size)?;
self.binary_int_op(bin_op, l, left_layout, r, right_layout)
}
}
}
pub fn unary_op( pub fn unary_op(
&self, &self,
un_op: mir::UnOp, un_op: mir::UnOp,
......
...@@ -660,7 +660,8 @@ pub fn force_allocation( ...@@ -660,7 +660,8 @@ pub fn force_allocation(
// a fake pointer? Are we even called for ZST? // a fake pointer? Are we even called for ZST?
// We need the layout of the local. We can NOT use the layout we got, // We need the layout of the local. We can NOT use the layout we got,
// that might e.g. be a downcast variant! // that might e.g. be an inner field of a struct with `Scalar` layout,
// that has different alignment than the outer field.
let local_layout = self.layout_of_local(frame, local)?; let local_layout = self.layout_of_local(frame, local)?;
let ptr = self.allocate(local_layout, MemoryKind::Stack)?; let ptr = self.allocate(local_layout, MemoryKind::Stack)?;
self.write_value_to_mplace(value, ptr)?; self.write_value_to_mplace(value, ptr)?;
...@@ -695,15 +696,11 @@ pub fn write_discriminant_index( ...@@ -695,15 +696,11 @@ pub fn write_discriminant_index(
) -> EvalResult<'tcx> { ) -> EvalResult<'tcx> {
match dest.layout.variants { match dest.layout.variants {
layout::Variants::Single { index } => { layout::Variants::Single { index } => {
if index != variant_index { assert_eq!(index, variant_index);
return err!(InvalidDiscriminant);
}
} }
layout::Variants::Tagged { ref tag, .. } => { layout::Variants::Tagged { ref tag, .. } => {
let adt_def = dest.layout.ty.ty_adt_def().unwrap(); let adt_def = dest.layout.ty.ty_adt_def().unwrap();
if variant_index >= adt_def.variants.len() { assert!(variant_index < adt_def.variants.len());
return err!(InvalidDiscriminant);
}
let discr_val = adt_def let discr_val = adt_def
.discriminant_for_variant(*self.tcx, variant_index) .discriminant_for_variant(*self.tcx, variant_index)
.val; .val;
...@@ -727,9 +724,7 @@ pub fn write_discriminant_index( ...@@ -727,9 +724,7 @@ pub fn write_discriminant_index(
niche_start, niche_start,
.. ..
} => { } => {
if variant_index >= dest.layout.ty.ty_adt_def().unwrap().variants.len() { assert!(variant_index < dest.layout.ty.ty_adt_def().unwrap().variants.len());
return err!(InvalidDiscriminant);
}
if variant_index != dataful_variant { if variant_index != dataful_variant {
let niche_dest = let niche_dest =
self.place_field(dest, 0)?; self.place_field(dest, 0)?;
......
...@@ -276,7 +276,7 @@ fn check_ty_compat<'tcx>(ty: Ty<'tcx>, real_ty: Ty<'tcx>) -> bool { ...@@ -276,7 +276,7 @@ fn check_ty_compat<'tcx>(ty: Ty<'tcx>, real_ty: Ty<'tcx>) -> bool {
} }
/// Call this function -- pushing the stack frame and initializing the arguments. /// Call this function -- pushing the stack frame and initializing the arguments.
/// `sig` is ptional in case of FnPtr/FnDef -- but mandatory for closures! /// `sig` is optional in case of FnPtr/FnDef -- but mandatory for closures!
fn eval_fn_call( fn eval_fn_call(
&mut self, &mut self,
instance: ty::Instance<'tcx>, instance: ty::Instance<'tcx>,
...@@ -462,7 +462,7 @@ fn drop_in_place( ...@@ -462,7 +462,7 @@ fn drop_in_place(
layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?, layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
}; };
let ty = self.tcx.mk_tup((&[] as &[ty::Ty<'tcx>]).iter()); // return type is () let ty = self.tcx.mk_nil(); // return type is ()
let dest = PlaceTy::null(&self, self.layout_of(ty)?); let dest = PlaceTy::null(&self, self.layout_of(ty)?);
self.eval_fn_call( self.eval_fn_call(
......
...@@ -208,10 +208,16 @@ pub fn validate_operand( ...@@ -208,10 +208,16 @@ pub fn validate_operand(
layout::Variants::Tagged { .. } => { layout::Variants::Tagged { .. } => {
let variant = match self.read_discriminant(dest) { let variant = match self.read_discriminant(dest) {
Ok(res) => res.1, Ok(res) => res.1,
Err(_) => Err(err) => match err.kind {
return validation_failure!( EvalErrorKind::InvalidDiscriminant(val) =>
"invalid enum discriminant", path return validation_failure!(
), format!("invalid enum discriminant {}", val), path
),
_ =>
return validation_failure!(
format!("non-integer enum discriminant"), path
),
}
}; };
let inner_dest = self.operand_downcast(dest, variant)?; let inner_dest = self.operand_downcast(dest, variant)?;
// Put the variant projection onto the path, as a field // Put the variant projection onto the path, as a field
......
...@@ -170,7 +170,7 @@ fn use_ecx<F, T>( ...@@ -170,7 +170,7 @@ fn use_ecx<F, T>(
| DoubleFree | DoubleFree
| InvalidFunctionPointer | InvalidFunctionPointer
| InvalidBool | InvalidBool
| InvalidDiscriminant | InvalidDiscriminant(..)
| PointerOutOfBounds { .. } | PointerOutOfBounds { .. }
| InvalidNullPointerUsage | InvalidNullPointerUsage
| MemoryLockViolation { .. } | MemoryLockViolation { .. }
......
...@@ -5,7 +5,7 @@ LL | / static FOO: (&Foo, &Bar) = unsafe {( //~ undefined behavior ...@@ -5,7 +5,7 @@ LL | / static FOO: (&Foo, &Bar) = unsafe {( //~ undefined behavior
LL | | Union { usize: &BAR }.foo, LL | | Union { usize: &BAR }.foo,
LL | | Union { usize: &BAR }.bar, LL | | Union { usize: &BAR }.bar,
LL | | )}; LL | | )};
| |___^ type validation failed: encountered invalid enum discriminant at .1.<deref> | |___^ type validation failed: encountered invalid enum discriminant 5 at .1.<deref>
| |
= 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
......
...@@ -2,7 +2,7 @@ error[E0080]: this constant likely exhibits undefined behavior ...@@ -2,7 +2,7 @@ error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/ub-enum.rs:22:1 --> $DIR/ub-enum.rs:22:1
| |
LL | const BAD_ENUM: Enum = unsafe { TransmuteEnum { a: &1 }.b }; LL | const BAD_ENUM: Enum = unsafe { TransmuteEnum { a: &1 }.b };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid enum discriminant | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer enum discriminant
| |
= 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
...@@ -10,7 +10,7 @@ error[E0080]: this constant likely exhibits undefined behavior ...@@ -10,7 +10,7 @@ error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/ub-enum.rs:35:1 --> $DIR/ub-enum.rs:35:1
| |
LL | const BAD_ENUM2 : Enum2 = unsafe { TransmuteEnum2 { a: 0 }.b }; LL | const BAD_ENUM2 : Enum2 = unsafe { TransmuteEnum2 { a: 0 }.b };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid enum discriminant | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid enum discriminant 0
| |
= 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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册