Merge branch 'master' into master
This commit is contained in:
commit
6dff6e84d7
@ -1 +1 @@
|
||||
nightly-2018-08-30
|
||||
nightly-2018-09-01
|
||||
|
@ -305,7 +305,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
||||
};
|
||||
|
||||
self.write_scalar(
|
||||
Scalar::from_i32(result),
|
||||
Scalar::from_int(result, Size::from_bits(32)),
|
||||
dest,
|
||||
)?;
|
||||
}
|
||||
@ -346,7 +346,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
||||
let name = self.memory.read_c_str(name_ptr)?;
|
||||
match self.machine.env_vars.get(name) {
|
||||
Some(&var) => Scalar::Ptr(var),
|
||||
None => Scalar::null(self.memory.pointer_size()),
|
||||
None => Scalar::ptr_null(*self.tcx),
|
||||
}
|
||||
};
|
||||
self.write_scalar(result, dest)?;
|
||||
@ -446,7 +446,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
||||
|
||||
// Some things needed for sys::thread initialization to go through
|
||||
"signal" | "sigaction" | "sigaltstack" => {
|
||||
self.write_scalar(Scalar::null(dest.layout.size), dest)?;
|
||||
self.write_scalar(Scalar::from_int(0, dest.layout.size), dest)?;
|
||||
}
|
||||
|
||||
"sysconf" => {
|
||||
@ -729,6 +729,6 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
||||
}
|
||||
|
||||
fn write_null(&mut self, dest: PlaceTy<'tcx>) -> EvalResult<'tcx> {
|
||||
self.write_scalar(Scalar::null(dest.layout.size), dest)
|
||||
self.write_scalar(Scalar::from_int(0, dest.layout.size), dest)
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,5 @@
|
||||
use rustc::ty::layout::Size;
|
||||
|
||||
use super::{Scalar, ScalarMaybeUndef, EvalResult};
|
||||
|
||||
pub trait ScalarExt {
|
||||
fn null(size: Size) -> Self;
|
||||
fn from_i32(i: i32) -> Self;
|
||||
fn from_uint(i: impl Into<u128>, ptr_size: Size) -> Self;
|
||||
fn from_int(i: impl Into<i128>, ptr_size: Size) -> Self;
|
||||
fn from_f32(f: f32) -> Self;
|
||||
fn from_f64(f: f64) -> Self;
|
||||
fn is_null(self) -> bool;
|
||||
}
|
||||
|
||||
pub trait FalibleScalarExt {
|
||||
/// HACK: this function just extracts all bits if `defined != 0`
|
||||
/// Mainly used for args of C-functions and we should totally correctly fetch the size
|
||||
@ -19,39 +7,6 @@ pub trait FalibleScalarExt {
|
||||
fn to_bytes(self) -> EvalResult<'static, u128>;
|
||||
}
|
||||
|
||||
impl ScalarExt for Scalar {
|
||||
fn null(size: Size) -> Self {
|
||||
Scalar::Bits { bits: 0, size: size.bytes() as u8 }
|
||||
}
|
||||
|
||||
fn from_i32(i: i32) -> Self {
|
||||
Scalar::Bits { bits: i as u32 as u128, size: 4 }
|
||||
}
|
||||
|
||||
fn from_uint(i: impl Into<u128>, size: Size) -> Self {
|
||||
Scalar::Bits { bits: i.into(), size: size.bytes() as u8 }
|
||||
}
|
||||
|
||||
fn from_int(i: impl Into<i128>, size: Size) -> Self {
|
||||
Scalar::Bits { bits: i.into() as u128, size: size.bytes() as u8 }
|
||||
}
|
||||
|
||||
fn from_f32(f: f32) -> Self {
|
||||
Scalar::Bits { bits: f.to_bits() as u128, size: 4 }
|
||||
}
|
||||
|
||||
fn from_f64(f: f64) -> Self {
|
||||
Scalar::Bits { bits: f.to_bits() as u128, size: 8 }
|
||||
}
|
||||
|
||||
fn is_null(self) -> bool {
|
||||
match self {
|
||||
Scalar::Bits { bits, .. } => bits == 0,
|
||||
Scalar::Ptr(_) => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FalibleScalarExt for Scalar {
|
||||
fn to_bytes(self) -> EvalResult<'static, u128> {
|
||||
match self {
|
||||
|
@ -2,12 +2,12 @@ use rustc::mir;
|
||||
use rustc::ty::layout::{self, LayoutOf, Size};
|
||||
use rustc::ty;
|
||||
|
||||
use rustc::mir::interpret::{EvalResult, Scalar, ScalarMaybeUndef};
|
||||
use rustc::mir::interpret::{EvalResult, Scalar, ScalarMaybeUndef, PointerArithmetic};
|
||||
use rustc_mir::interpret::{
|
||||
PlaceTy, EvalContext, OpTy, Value
|
||||
};
|
||||
|
||||
use super::{ScalarExt, FalibleScalarExt, OperatorEvalContextExt};
|
||||
use super::{FalibleScalarExt, OperatorEvalContextExt};
|
||||
|
||||
pub trait EvalContextExt<'tcx> {
|
||||
fn call_intrinsic(
|
||||
@ -116,11 +116,11 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
|
||||
_ if intrinsic_name.starts_with("atomic_cxchg") => {
|
||||
let ptr = self.ref_to_mplace(self.read_value(args[0])?)?;
|
||||
let expect_old = self.read_value(args[1])?; // read as value for the sake of `binary_op()`
|
||||
let expect_old = self.read_value(args[1])?; // read as value for the sake of `binary_op_val()`
|
||||
let new = self.read_scalar(args[2])?;
|
||||
let old = self.read_value(ptr.into())?; // read as value for the sake of `binary_op()`
|
||||
// binary_op will bail if either of them is not a scalar
|
||||
let (eq, _) = self.binary_op(mir::BinOp::Eq, old, expect_old)?;
|
||||
let old = self.read_value(ptr.into())?; // read as value for the sake of `binary_op_val()`
|
||||
// binary_op_val will bail if either of them is not a scalar
|
||||
let (eq, _) = self.binary_op_val(mir::BinOp::Eq, old, expect_old)?;
|
||||
let res = Value::ScalarPair(old.to_scalar_or_undef(), eq.into());
|
||||
self.write_value(res, dest)?; // old value is returned
|
||||
// update ptr depending on comparison
|
||||
@ -167,8 +167,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
_ => bug!(),
|
||||
};
|
||||
// FIXME: what do atomics do on overflow?
|
||||
let (val, _) = self.binary_op(op, old, rhs)?;
|
||||
self.write_scalar(val, ptr.into())?;
|
||||
self.binop_ignore_overflow(op, old, rhs, ptr.into())?;
|
||||
}
|
||||
|
||||
"breakpoint" => unimplemented!(), // halt miri
|
||||
@ -204,8 +203,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
|
||||
"sinf32" | "fabsf32" | "cosf32" | "sqrtf32" | "expf32" | "exp2f32" | "logf32" |
|
||||
"log10f32" | "log2f32" | "floorf32" | "ceilf32" | "truncf32" => {
|
||||
let f = self.read_scalar(args[0])?.to_bytes()?;
|
||||
let f = f32::from_bits(f as u32);
|
||||
let f = self.read_scalar(args[0])?.to_f32()?;
|
||||
let f = match intrinsic_name {
|
||||
"sinf32" => f.sin(),
|
||||
"fabsf32" => f.abs(),
|
||||
@ -226,8 +224,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
|
||||
"sinf64" | "fabsf64" | "cosf64" | "sqrtf64" | "expf64" | "exp2f64" | "logf64" |
|
||||
"log10f64" | "log2f64" | "floorf64" | "ceilf64" | "truncf64" => {
|
||||
let f = self.read_scalar(args[0])?.to_bytes()?;
|
||||
let f = f64::from_bits(f as u64);
|
||||
let f = self.read_scalar(args[0])?.to_f64()?;
|
||||
let f = match intrinsic_name {
|
||||
"sinf64" => f.sin(),
|
||||
"fabsf64" => f.abs(),
|
||||
@ -257,8 +254,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
"frem_fast" => mir::BinOp::Rem,
|
||||
_ => bug!(),
|
||||
};
|
||||
let result = self.binary_op(op, a, b)?;
|
||||
self.write_scalar(result.0, dest)?;
|
||||
self.binop_ignore_overflow(op, a, b, dest)?;
|
||||
}
|
||||
|
||||
"exact_div" => {
|
||||
@ -267,11 +263,10 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
let a = self.read_value(args[0])?;
|
||||
let b = self.read_value(args[1])?;
|
||||
// check x % y != 0
|
||||
if !self.binary_op(mir::BinOp::Rem, a, b)?.0.is_null() {
|
||||
if !self.binary_op_val(mir::BinOp::Rem, a, b)?.0.is_null() {
|
||||
return err!(ValidationFailure(format!("exact_div: {:?} cannot be divided by {:?}", a, b)));
|
||||
}
|
||||
let result = self.binary_op(mir::BinOp::Div, a, b)?;
|
||||
self.write_scalar(result.0, dest)?;
|
||||
self.binop_ignore_overflow(mir::BinOp::Div, a, b, dest)?;
|
||||
},
|
||||
|
||||
"likely" | "unlikely" | "forget" => {}
|
||||
@ -282,12 +277,12 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
if !dest.layout.is_zst() { // notzhing to do for ZST
|
||||
match dest.layout.abi {
|
||||
layout::Abi::Scalar(ref s) => {
|
||||
let x = Scalar::null(s.value.size(&self));
|
||||
let x = Scalar::from_int(0, s.value.size(&self));
|
||||
self.write_value(Value::Scalar(x.into()), dest)?;
|
||||
}
|
||||
layout::Abi::ScalarPair(ref s1, ref s2) => {
|
||||
let x = Scalar::null(s1.value.size(&self));
|
||||
let y = Scalar::null(s2.value.size(&self));
|
||||
let x = Scalar::from_int(0, s1.value.size(&self));
|
||||
let y = Scalar::from_int(0, s2.value.size(&self));
|
||||
self.write_value(Value::ScalarPair(x.into(), y.into()), dest)?;
|
||||
}
|
||||
_ => {
|
||||
@ -304,7 +299,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
let ty = substs.type_at(0);
|
||||
let layout = self.layout_of(ty)?;
|
||||
let align = layout.align.pref();
|
||||
let ptr_size = self.memory.pointer_size();
|
||||
let ptr_size = self.pointer_size();
|
||||
let align_val = Scalar::from_uint(align as u128, ptr_size);
|
||||
self.write_scalar(align_val, dest)?;
|
||||
}
|
||||
@ -365,10 +360,8 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
}
|
||||
|
||||
"powf32" => {
|
||||
let f = self.read_scalar(args[0])?.to_bits(Size::from_bits(32))?;
|
||||
let f = f32::from_bits(f as u32);
|
||||
let f2 = self.read_scalar(args[1])?.to_bits(Size::from_bits(32))?;
|
||||
let f2 = f32::from_bits(f2 as u32);
|
||||
let f = self.read_scalar(args[0])?.to_f32()?;
|
||||
let f2 = self.read_scalar(args[1])?.to_f32()?;
|
||||
self.write_scalar(
|
||||
Scalar::from_f32(f.powf(f2)),
|
||||
dest,
|
||||
@ -376,10 +369,8 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
}
|
||||
|
||||
"powf64" => {
|
||||
let f = self.read_scalar(args[0])?.to_bits(Size::from_bits(64))?;
|
||||
let f = f64::from_bits(f as u64);
|
||||
let f2 = self.read_scalar(args[1])?.to_bits(Size::from_bits(64))?;
|
||||
let f2 = f64::from_bits(f2 as u64);
|
||||
let f = self.read_scalar(args[0])?.to_f64()?;
|
||||
let f2 = self.read_scalar(args[1])?.to_f64()?;
|
||||
self.write_scalar(
|
||||
Scalar::from_f64(f.powf(f2)),
|
||||
dest,
|
||||
@ -387,12 +378,9 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
}
|
||||
|
||||
"fmaf32" => {
|
||||
let a = self.read_scalar(args[0])?.to_bits(Size::from_bits(32))?;
|
||||
let a = f32::from_bits(a as u32);
|
||||
let b = self.read_scalar(args[1])?.to_bits(Size::from_bits(32))?;
|
||||
let b = f32::from_bits(b as u32);
|
||||
let c = self.read_scalar(args[2])?.to_bits(Size::from_bits(32))?;
|
||||
let c = f32::from_bits(c as u32);
|
||||
let a = self.read_scalar(args[0])?.to_f32()?;
|
||||
let b = self.read_scalar(args[1])?.to_f32()?;
|
||||
let c = self.read_scalar(args[2])?.to_f32()?;
|
||||
self.write_scalar(
|
||||
Scalar::from_f32(a * b + c),
|
||||
dest,
|
||||
@ -400,12 +388,9 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
}
|
||||
|
||||
"fmaf64" => {
|
||||
let a = self.read_scalar(args[0])?.to_bits(Size::from_bits(64))?;
|
||||
let a = f64::from_bits(a as u64);
|
||||
let b = self.read_scalar(args[1])?.to_bits(Size::from_bits(64))?;
|
||||
let b = f64::from_bits(b as u64);
|
||||
let c = self.read_scalar(args[2])?.to_bits(Size::from_bits(64))?;
|
||||
let c = f64::from_bits(c as u64);
|
||||
let a = self.read_scalar(args[0])?.to_f64()?;
|
||||
let b = self.read_scalar(args[1])?.to_f64()?;
|
||||
let c = self.read_scalar(args[2])?.to_f64()?;
|
||||
self.write_scalar(
|
||||
Scalar::from_f64(a * b + c),
|
||||
dest,
|
||||
@ -413,8 +398,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
}
|
||||
|
||||
"powif32" => {
|
||||
let f = self.read_scalar(args[0])?.to_bits(Size::from_bits(32))?;
|
||||
let f = f32::from_bits(f as u32);
|
||||
let f = self.read_scalar(args[0])?.to_f32()?;
|
||||
let i = self.read_scalar(args[1])?.to_i32()?;
|
||||
self.write_scalar(
|
||||
Scalar::from_f32(f.powi(i)),
|
||||
@ -423,8 +407,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
}
|
||||
|
||||
"powif64" => {
|
||||
let f = self.read_scalar(args[0])?.to_bits(Size::from_bits(64))?;
|
||||
let f = f64::from_bits(f as u64);
|
||||
let f = self.read_scalar(args[0])?.to_f64()?;
|
||||
let i = self.read_scalar(args[1])?.to_i32()?;
|
||||
self.write_scalar(
|
||||
Scalar::from_f64(f.powi(i)),
|
||||
@ -435,7 +418,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
"size_of_val" => {
|
||||
let mplace = self.ref_to_mplace(self.read_value(args[0])?)?;
|
||||
let (size, _) = self.size_and_align_of_mplace(mplace)?;
|
||||
let ptr_size = self.memory.pointer_size();
|
||||
let ptr_size = self.pointer_size();
|
||||
self.write_scalar(
|
||||
Scalar::from_uint(size.bytes() as u128, ptr_size),
|
||||
dest,
|
||||
@ -446,7 +429,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
"align_of_val" => {
|
||||
let mplace = self.ref_to_mplace(self.read_value(args[0])?)?;
|
||||
let (_, align) = self.size_and_align_of_mplace(mplace)?;
|
||||
let ptr_size = self.memory.pointer_size();
|
||||
let ptr_size = self.pointer_size();
|
||||
self.write_scalar(
|
||||
Scalar::from_uint(align.abi(), ptr_size),
|
||||
dest,
|
||||
|
@ -46,7 +46,7 @@ use tls::EvalContextExt as TlsEvalContextExt;
|
||||
use memory::MemoryKind as MiriMemoryKind;
|
||||
use locks::LockInfo;
|
||||
use range_map::RangeMap;
|
||||
use helpers::{ScalarExt, FalibleScalarExt};
|
||||
use helpers::FalibleScalarExt;
|
||||
|
||||
pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
@ -304,14 +304,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
|
||||
ecx.call_intrinsic(instance, args, dest)
|
||||
}
|
||||
|
||||
fn try_ptr_op<'a>(
|
||||
fn ptr_op<'a>(
|
||||
ecx: &rustc_mir::interpret::EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
bin_op: mir::BinOp,
|
||||
left: Scalar,
|
||||
left_layout: TyLayout<'tcx>,
|
||||
right: Scalar,
|
||||
right_layout: TyLayout<'tcx>,
|
||||
) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
|
||||
) -> EvalResult<'tcx, (Scalar, bool)> {
|
||||
ecx.ptr_op(bin_op, left, left_layout, right, right_layout)
|
||||
}
|
||||
|
||||
|
171
src/operator.rs
171
src/operator.rs
@ -1,5 +1,4 @@
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::layout::{TyLayout, Primitive};
|
||||
use rustc::ty::{Ty, layout::TyLayout};
|
||||
use rustc::mir;
|
||||
|
||||
use super::*;
|
||||
@ -12,7 +11,7 @@ pub trait EvalContextExt<'tcx> {
|
||||
left_layout: TyLayout<'tcx>,
|
||||
right: Scalar,
|
||||
right_layout: TyLayout<'tcx>,
|
||||
) -> EvalResult<'tcx, Option<(Scalar, bool)>>;
|
||||
) -> EvalResult<'tcx, (Scalar, bool)>;
|
||||
|
||||
fn ptr_int_arithmetic(
|
||||
&self,
|
||||
@ -22,6 +21,13 @@ pub trait EvalContextExt<'tcx> {
|
||||
signed: bool,
|
||||
) -> EvalResult<'tcx, (Scalar, bool)>;
|
||||
|
||||
fn ptr_eq(
|
||||
&self,
|
||||
left: Scalar,
|
||||
right: Scalar,
|
||||
size: Size,
|
||||
) -> EvalResult<'tcx, bool>;
|
||||
|
||||
fn pointer_offset_inbounds(
|
||||
&self,
|
||||
ptr: Scalar,
|
||||
@ -38,38 +44,14 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
left_layout: TyLayout<'tcx>,
|
||||
right: Scalar,
|
||||
right_layout: TyLayout<'tcx>,
|
||||
) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
|
||||
trace!("ptr_op: {:?} {:?} {:?}", left, bin_op, right);
|
||||
|
||||
) -> EvalResult<'tcx, (Scalar, bool)> {
|
||||
use rustc::mir::BinOp::*;
|
||||
use rustc::ty::layout::Integer::*;
|
||||
let usize = Primitive::Int(match self.memory.pointer_size().bytes() {
|
||||
1 => I8,
|
||||
2 => I16,
|
||||
4 => I32,
|
||||
8 => I64,
|
||||
16 => I128,
|
||||
_ => unreachable!(),
|
||||
}, /*signed*/ false);
|
||||
let isize = Primitive::Int(match self.memory.pointer_size().bytes() {
|
||||
1 => I8,
|
||||
2 => I16,
|
||||
4 => I32,
|
||||
8 => I64,
|
||||
16 => I128,
|
||||
_ => unreachable!(),
|
||||
}, /*signed*/ true);
|
||||
let left_kind = match left_layout.abi {
|
||||
ty::layout::Abi::Scalar(ref scalar) => scalar.value,
|
||||
_ => Err(EvalErrorKind::TypeNotPrimitive(left_layout.ty))?,
|
||||
};
|
||||
let right_kind = match right_layout.abi {
|
||||
ty::layout::Abi::Scalar(ref scalar) => scalar.value,
|
||||
_ => Err(EvalErrorKind::TypeNotPrimitive(right_layout.ty))?,
|
||||
};
|
||||
|
||||
trace!("ptr_op: {:?} {:?} {:?}", left, bin_op, right);
|
||||
debug_assert!(left.is_ptr() || right.is_ptr() || bin_op == Offset);
|
||||
|
||||
match bin_op {
|
||||
Offset => {
|
||||
assert!(left_kind == Primitive::Pointer && right_kind == usize);
|
||||
let pointee_ty = left_layout.ty
|
||||
.builtin_deref(true)
|
||||
.expect("Offset called on non-ptr type")
|
||||
@ -77,40 +59,19 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
let ptr = self.pointer_offset_inbounds(
|
||||
left,
|
||||
pointee_ty,
|
||||
right.to_bits(self.memory.pointer_size())? as i64,
|
||||
right.to_isize(self)?,
|
||||
)?;
|
||||
Ok(Some((ptr, false)))
|
||||
Ok((ptr, false))
|
||||
}
|
||||
// These work on anything
|
||||
Eq if left_kind == right_kind => {
|
||||
let result = match (left, right) {
|
||||
(Scalar::Bits { .. }, Scalar::Bits { .. }) => {
|
||||
left.to_bits(left_layout.size)? == right.to_bits(right_layout.size)?
|
||||
},
|
||||
// FIXME: Test if both allocations are still live *or* if they are in the same allocation? (same for Ne below)
|
||||
(Scalar::Ptr(left), Scalar::Ptr(right)) => left == right,
|
||||
// FIXME: We should probably error out when comparing anything but NULL with a pointer (same for Ne below)
|
||||
_ => false,
|
||||
};
|
||||
Ok(Some((Scalar::from_bool(result), false)))
|
||||
}
|
||||
Ne if left_kind == right_kind => {
|
||||
let result = match (left, right) {
|
||||
(Scalar::Bits { .. }, Scalar::Bits { .. }) => {
|
||||
left.to_bits(left_layout.size)? != right.to_bits(right_layout.size)?
|
||||
},
|
||||
(Scalar::Ptr(left), Scalar::Ptr(right)) => left != right,
|
||||
_ => true,
|
||||
};
|
||||
Ok(Some((Scalar::from_bool(result), false)))
|
||||
}
|
||||
// These need both pointers to be in the same allocation
|
||||
Lt | Le | Gt | Ge | Sub
|
||||
if left_kind == right_kind &&
|
||||
(left_kind == Primitive::Pointer || left_kind == usize || left_kind == isize) &&
|
||||
left.is_ptr() && right.is_ptr() => {
|
||||
let left = left.to_ptr()?;
|
||||
let right = right.to_ptr()?;
|
||||
Eq =>
|
||||
Ok((Scalar::from_bool(self.ptr_eq(left, right, left_layout.size)?), false)),
|
||||
Ne =>
|
||||
Ok((Scalar::from_bool(!self.ptr_eq(left, right, left_layout.size)?), false)),
|
||||
// These need both to be pointer, and fail if they are not in the same location
|
||||
Lt | Le | Gt | Ge | Sub if left.is_ptr() && right.is_ptr() => {
|
||||
let left = left.to_ptr().expect("we checked is_ptr");
|
||||
let right = right.to_ptr().expect("we checked is_ptr");
|
||||
if left.alloc_id == right.alloc_id {
|
||||
let res = match bin_op {
|
||||
Lt => left.offset < right.offset,
|
||||
@ -118,51 +79,95 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
|
||||
Gt => left.offset > right.offset,
|
||||
Ge => left.offset >= right.offset,
|
||||
Sub => {
|
||||
// subtract the offsets
|
||||
let left_offset = Scalar::from_uint(left.offset.bytes(), self.memory.pointer_size());
|
||||
let right_offset = Scalar::from_uint(right.offset.bytes(), self.memory.pointer_size());
|
||||
let layout = self.layout_of(self.tcx.types.usize)?;
|
||||
return self.binary_op(
|
||||
Sub,
|
||||
ValTy { value: Value::Scalar(left_offset.into()), layout },
|
||||
ValTy { value: Value::Scalar(right_offset.into()), layout },
|
||||
).map(Some)
|
||||
left_offset, layout,
|
||||
right_offset, layout,
|
||||
)
|
||||
}
|
||||
_ => bug!("We already established it has to be one of these operators."),
|
||||
};
|
||||
Ok(Some((Scalar::from_bool(res), false)))
|
||||
Ok((Scalar::from_bool(res), false))
|
||||
} else {
|
||||
// Both are pointers, but from different allocations.
|
||||
err!(InvalidPointerMath)
|
||||
}
|
||||
}
|
||||
// These work if the left operand is a pointer, the right an integer
|
||||
Add | BitAnd | Sub | Rem
|
||||
if left_kind == right_kind && (left_kind == usize || left_kind == isize) &&
|
||||
left.is_ptr() && right.is_bits() => {
|
||||
// These work if the left operand is a pointer, and the right an integer
|
||||
Add | BitAnd | Sub | Rem if left.is_ptr() && right.is_bits() => {
|
||||
// Cast to i128 is fine as we checked the kind to be ptr-sized
|
||||
self.ptr_int_arithmetic(
|
||||
bin_op,
|
||||
left.to_ptr()?,
|
||||
right.to_bits(self.memory.pointer_size())?,
|
||||
left_kind == isize,
|
||||
).map(Some)
|
||||
left.to_ptr().expect("we checked is_ptr"),
|
||||
right.to_bits(self.memory.pointer_size()).expect("we checked is_bits"),
|
||||
right_layout.abi.is_signed(),
|
||||
)
|
||||
}
|
||||
// Commutative operators also work if the integer is on the left
|
||||
Add | BitAnd
|
||||
if left_kind == right_kind && (left_kind == usize || left_kind == isize) &&
|
||||
left.is_bits() && right.is_ptr() => {
|
||||
Add | BitAnd if left.is_bits() && right.is_ptr() => {
|
||||
// This is a commutative operation, just swap the operands
|
||||
self.ptr_int_arithmetic(
|
||||
bin_op,
|
||||
right.to_ptr()?,
|
||||
left.to_bits(self.memory.pointer_size())?,
|
||||
left_kind == isize,
|
||||
).map(Some)
|
||||
right.to_ptr().expect("we checked is_ptr"),
|
||||
left.to_bits(self.memory.pointer_size()).expect("we checked is_bits"),
|
||||
left_layout.abi.is_signed(),
|
||||
)
|
||||
}
|
||||
_ => Ok(None),
|
||||
// Nothing else works
|
||||
_ => err!(InvalidPointerMath),
|
||||
}
|
||||
}
|
||||
|
||||
fn ptr_eq(
|
||||
&self,
|
||||
left: Scalar,
|
||||
right: Scalar,
|
||||
size: Size,
|
||||
) -> EvalResult<'tcx, bool> {
|
||||
Ok(match (left, right) {
|
||||
(Scalar::Bits { .. }, Scalar::Bits { .. }) =>
|
||||
left.to_bits(size)? == right.to_bits(size)?,
|
||||
(Scalar::Ptr(left), Scalar::Ptr(right)) => {
|
||||
// Comparison illegal if one of them is out-of-bounds, *unless* they
|
||||
// are in the same allocation.
|
||||
if left.alloc_id == right.alloc_id {
|
||||
left.offset == right.offset
|
||||
} else {
|
||||
// This accepts one-past-the end. So technically there is still
|
||||
// some non-determinism that we do not fully rule out when two
|
||||
// allocations sit right next to each other. The C/C++ standards are
|
||||
// somewhat fuzzy about this case, so I think for now this check is
|
||||
// "good enough".
|
||||
self.memory.check_bounds(left, false)?;
|
||||
self.memory.check_bounds(right, false)?;
|
||||
// Two live in-bounds pointers, we can compare across allocations
|
||||
left == right
|
||||
}
|
||||
}
|
||||
// Comparing ptr and integer
|
||||
(Scalar::Ptr(ptr), Scalar::Bits { bits, size }) |
|
||||
(Scalar::Bits { bits, size }, Scalar::Ptr(ptr)) => {
|
||||
assert_eq!(size as u64, self.pointer_size().bytes());
|
||||
|
||||
if bits == 0 {
|
||||
// Nothing equals 0, not even dangling pointers. Ideally we would
|
||||
// require them to be in-bounds of their (possilby dead) allocation,
|
||||
// but with the allocation gonew e cannot check that.
|
||||
false
|
||||
} else {
|
||||
// Live pointers cannot equal an integer, but again do not
|
||||
// allow comparing dead pointers.
|
||||
self.memory.check_bounds(ptr, false)?;
|
||||
false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn ptr_int_arithmetic(
|
||||
&self,
|
||||
bin_op: mir::BinOp,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rustc::{ty, mir};
|
||||
|
||||
use super::{TlsKey, TlsEntry, EvalResult, EvalErrorKind, Scalar, ScalarExt, Memory, Evaluator,
|
||||
use super::{TlsKey, TlsEntry, EvalResult, EvalErrorKind, Scalar, Memory, Evaluator,
|
||||
Place, StackPopCleanup, EvalContext};
|
||||
|
||||
pub trait MemoryExt<'tcx> {
|
||||
@ -22,11 +22,10 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
|
||||
fn create_tls_key(&mut self, dtor: Option<ty::Instance<'tcx>>) -> TlsKey {
|
||||
let new_key = self.data.next_thread_local;
|
||||
self.data.next_thread_local += 1;
|
||||
let ptr_size = self.pointer_size();
|
||||
self.data.thread_local.insert(
|
||||
new_key,
|
||||
TlsEntry {
|
||||
data: Scalar::null(ptr_size).into(),
|
||||
data: Scalar::ptr_null(*self.tcx).into(),
|
||||
dtor,
|
||||
},
|
||||
);
|
||||
@ -89,7 +88,6 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
|
||||
) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> {
|
||||
use std::collections::Bound::*;
|
||||
|
||||
let ptr_size = self.pointer_size();
|
||||
let thread_local = &mut self.data.thread_local;
|
||||
let start = match key {
|
||||
Some(key) => Excluded(key),
|
||||
@ -101,7 +99,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
|
||||
if !data.is_null() {
|
||||
if let Some(dtor) = dtor {
|
||||
let ret = Some((dtor, *data, key));
|
||||
*data = Scalar::null(ptr_size);
|
||||
*data = Scalar::ptr_null(*self.tcx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,5 @@ fn main() {
|
||||
std::mem::transmute::<fn(), fn(i32)>(f)
|
||||
};
|
||||
|
||||
g(42) //~ ERROR constant evaluation error
|
||||
//~^ NOTE tried to call a function with sig fn() through a function pointer of type fn(i32)
|
||||
g(42) //~ ERROR tried to call a function with incorrect number of arguments
|
||||
}
|
||||
|
@ -5,6 +5,5 @@ fn main() {
|
||||
std::mem::transmute::<fn((i32,i32)), fn(i32)>(f)
|
||||
};
|
||||
|
||||
g(42) //~ ERROR constant evaluation error
|
||||
//~^ NOTE tried to call a function with sig fn((i32, i32)) through a function pointer of type fn(i32)
|
||||
g(42) //~ ERROR tried to call a function with argument of type (i32, i32) passing data of type i32
|
||||
}
|
||||
|
10
tests/compile-fail/cast_fn_ptr3.rs
Normal file
10
tests/compile-fail/cast_fn_ptr3.rs
Normal file
@ -0,0 +1,10 @@
|
||||
fn main() {
|
||||
fn f(_ : (i32,i32)) {}
|
||||
|
||||
let g = unsafe {
|
||||
std::mem::transmute::<fn((i32,i32)), fn()>(f)
|
||||
};
|
||||
|
||||
g() //~ ERROR tried to call a function with incorrect number of arguments
|
||||
}
|
||||
|
9
tests/compile-fail/cast_fn_ptr4.rs
Normal file
9
tests/compile-fail/cast_fn_ptr4.rs
Normal file
@ -0,0 +1,9 @@
|
||||
fn main() {
|
||||
fn f(_ : *const [i32]) {}
|
||||
|
||||
let g = unsafe {
|
||||
std::mem::transmute::<fn(*const [i32]), fn(*const i32)>(f)
|
||||
};
|
||||
|
||||
g(&42 as *const i32) //~ ERROR tried to call a function with argument of type *const [i32] passing data of type *const i32
|
||||
}
|
@ -3,6 +3,5 @@ fn main() {
|
||||
let y = &x;
|
||||
let z = &y as *const &i32 as *const usize;
|
||||
let ptr_bytes = unsafe { *z }; // the actual deref is fine, because we read the entire pointer at once
|
||||
let _ = ptr_bytes / 432; //~ ERROR constant evaluation error
|
||||
//~^ NOTE tried to access part of a pointer value as raw bytes
|
||||
let _ = ptr_bytes / 432; //~ ERROR invalid arithmetic on pointers that would leak base addresses
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ fn main() {
|
||||
let bytes = [0i8, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
let one = bytes.as_ptr().wrapping_offset(1);
|
||||
let three = bytes.as_ptr().wrapping_offset(3);
|
||||
let res = (one as usize) | (three as usize); //~ ERROR constant evaluation error
|
||||
//~^ NOTE a raw memory access tried to access part of a pointer value as raw bytes
|
||||
let res = (one as usize) | (three as usize); //~ ERROR invalid arithmetic on pointers that would leak base addresses
|
||||
println!("{}", res);
|
||||
}
|
||||
|
@ -3,7 +3,5 @@ fn main() {
|
||||
assert_eq!(c, 'x');
|
||||
assert!('a' < 'z');
|
||||
assert!('1' < '9');
|
||||
assert_eq!(std::char::from_u32('x' as u32).unwrap(), 'x');
|
||||
// FIXME:
|
||||
// assert_eq!(std::char::from_u32('x' as u32), Some('x'));
|
||||
assert_eq!(std::char::from_u32('x' as u32), Some('x'));
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ static FOO: fn() = || { assert_ne!(42, 43) };
|
||||
#[allow(const_err)]
|
||||
static BAR: fn(i32, i32) = |a, b| { assert_ne!(a, b) };
|
||||
|
||||
// use to first make the closure FnOnce() before making it fn()
|
||||
fn magic<F: FnOnce()>(f: F) -> F { f }
|
||||
|
||||
fn main() {
|
||||
FOO();
|
||||
BAR(44, 45);
|
||||
@ -11,4 +14,7 @@ fn main() {
|
||||
unsafe { bar(46, 47) };
|
||||
let boo: &Fn(i32, i32) = &BAR;
|
||||
boo(48, 49);
|
||||
|
||||
let f = magic(||{}) as fn();
|
||||
f();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user