remove our array drop glue and use rustc's instead; implement the new Offset and SizeOf operators
This commit is contained in:
parent
14848b3bad
commit
31cf66d0e8
7 changed files with 52 additions and 216 deletions
|
@ -4,7 +4,6 @@ use std::fmt::Write;
|
|||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::map::definitions::DefPathData;
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc_const_math::{ConstInt, ConstUsize};
|
||||
use rustc::mir;
|
||||
use rustc::traits::Reveal;
|
||||
use rustc::ty::layout::{self, Layout, Size};
|
||||
|
@ -15,7 +14,6 @@ use rustc_data_structures::indexed_vec::Idx;
|
|||
use syntax::codemap::{self, DUMMY_SP, Span};
|
||||
use syntax::ast;
|
||||
use syntax::abi::Abi;
|
||||
use syntax::symbol::Symbol;
|
||||
|
||||
use error::{EvalError, EvalResult};
|
||||
use lvalue::{Global, GlobalId, Lvalue, LvalueExtra};
|
||||
|
@ -43,9 +41,6 @@ pub struct EvalContext<'a, 'tcx: 'a> {
|
|||
/// This prevents infinite loops and huge computations from freezing up const eval.
|
||||
/// Remove once halting problem is solved.
|
||||
pub(crate) steps_remaining: u64,
|
||||
|
||||
/// Drop glue for arrays and slices
|
||||
pub(crate) seq_drop_glue: &'tcx mir::Mir<'tcx>,
|
||||
}
|
||||
|
||||
/// A stack frame.
|
||||
|
@ -127,180 +122,6 @@ impl Default for ResourceLimits {
|
|||
|
||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self {
|
||||
// Register array drop glue code
|
||||
let source_info = mir::SourceInfo {
|
||||
span: DUMMY_SP,
|
||||
scope: mir::ARGUMENT_VISIBILITY_SCOPE
|
||||
};
|
||||
// i = 0; len = Len(*a0); goto head;
|
||||
let start_block = mir::BasicBlockData {
|
||||
statements: vec![
|
||||
mir::Statement {
|
||||
source_info,
|
||||
kind: mir::StatementKind::Assign(
|
||||
mir::Lvalue::Local(mir::Local::new(2)),
|
||||
mir::Rvalue::Use(mir::Operand::Constant(Box::new(mir::Constant {
|
||||
span: DUMMY_SP,
|
||||
ty: tcx.types.usize,
|
||||
literal: mir::Literal::Value {
|
||||
value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(0, tcx.sess.target.uint_type).unwrap())),
|
||||
},
|
||||
})))
|
||||
)
|
||||
},
|
||||
mir::Statement {
|
||||
source_info,
|
||||
kind: mir::StatementKind::Assign(
|
||||
mir::Lvalue::Local(mir::Local::new(3)),
|
||||
mir::Rvalue::Len(mir::Lvalue::Projection(Box::new(mir::LvalueProjection {
|
||||
base: mir::Lvalue::Local(mir::Local::new(1)),
|
||||
elem: mir::ProjectionElem::Deref,
|
||||
}))),
|
||||
)
|
||||
},
|
||||
],
|
||||
terminator: Some(mir::Terminator {
|
||||
source_info: source_info,
|
||||
kind: mir::TerminatorKind::Goto { target: mir::BasicBlock::new(1) },
|
||||
}),
|
||||
is_cleanup: false
|
||||
};
|
||||
// head: done = i == len; switch done { 1 => ret, 0 => loop }
|
||||
let head = mir::BasicBlockData {
|
||||
statements: vec![
|
||||
mir::Statement {
|
||||
source_info,
|
||||
kind: mir::StatementKind::Assign(
|
||||
mir::Lvalue::Local(mir::Local::new(4)),
|
||||
mir::Rvalue::BinaryOp(
|
||||
mir::BinOp::Eq,
|
||||
mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2))),
|
||||
mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(3))),
|
||||
)
|
||||
)
|
||||
},
|
||||
],
|
||||
terminator: Some(mir::Terminator {
|
||||
source_info: source_info,
|
||||
kind: mir::TerminatorKind::SwitchInt {
|
||||
targets: vec![
|
||||
mir::BasicBlock::new(2),
|
||||
mir::BasicBlock::new(4),
|
||||
],
|
||||
discr: mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(4))),
|
||||
switch_ty: tcx.types.bool,
|
||||
values: vec![ConstInt::U8(0)].into(),
|
||||
},
|
||||
}),
|
||||
is_cleanup: false
|
||||
};
|
||||
// loop: drop (*a0)[i]; goto inc;
|
||||
let loop_ = mir::BasicBlockData {
|
||||
statements: Vec::new(),
|
||||
terminator: Some(mir::Terminator {
|
||||
source_info: source_info,
|
||||
kind: mir::TerminatorKind::Drop {
|
||||
target: mir::BasicBlock::new(3),
|
||||
unwind: None,
|
||||
location: mir::Lvalue::Projection(Box::new(
|
||||
mir::LvalueProjection {
|
||||
base: mir::Lvalue::Projection(Box::new(
|
||||
mir::LvalueProjection {
|
||||
base: mir::Lvalue::Local(mir::Local::new(1)),
|
||||
elem: mir::ProjectionElem::Deref,
|
||||
}
|
||||
)),
|
||||
elem: mir::ProjectionElem::Index(mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2)))),
|
||||
}
|
||||
)),
|
||||
},
|
||||
}),
|
||||
is_cleanup: false
|
||||
};
|
||||
// inc: i++; goto head;
|
||||
let inc = mir::BasicBlockData {
|
||||
statements: vec![
|
||||
mir::Statement {
|
||||
source_info,
|
||||
kind: mir::StatementKind::Assign(
|
||||
mir::Lvalue::Local(mir::Local::new(2)),
|
||||
mir::Rvalue::BinaryOp(
|
||||
mir::BinOp::Add,
|
||||
mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2))),
|
||||
mir::Operand::Constant(Box::new(mir::Constant {
|
||||
span: DUMMY_SP,
|
||||
ty: tcx.types.usize,
|
||||
literal: mir::Literal::Value {
|
||||
value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(1, tcx.sess.target.uint_type).unwrap())),
|
||||
},
|
||||
})),
|
||||
)
|
||||
)
|
||||
},
|
||||
],
|
||||
terminator: Some(mir::Terminator {
|
||||
source_info: source_info,
|
||||
kind: mir::TerminatorKind::Goto { target: mir::BasicBlock::new(1) },
|
||||
}),
|
||||
is_cleanup: false
|
||||
};
|
||||
// ret: return;
|
||||
let ret = mir::BasicBlockData {
|
||||
statements: Vec::new(),
|
||||
terminator: Some(mir::Terminator {
|
||||
source_info: source_info,
|
||||
kind: mir::TerminatorKind::Return,
|
||||
}),
|
||||
is_cleanup: false
|
||||
};
|
||||
let locals = vec![
|
||||
mir::LocalDecl {
|
||||
mutability: mir::Mutability::Mut,
|
||||
ty: tcx.mk_nil(),
|
||||
name: None,
|
||||
source_info,
|
||||
is_user_variable: false,
|
||||
},
|
||||
mir::LocalDecl {
|
||||
mutability: mir::Mutability::Mut,
|
||||
ty: tcx.mk_mut_ptr(tcx.mk_slice(tcx.mk_param(0, Symbol::intern("T")))),
|
||||
name: None,
|
||||
source_info,
|
||||
is_user_variable: false,
|
||||
},
|
||||
mir::LocalDecl {
|
||||
mutability: mir::Mutability::Mut,
|
||||
ty: tcx.types.usize,
|
||||
name: None,
|
||||
source_info,
|
||||
is_user_variable: false,
|
||||
},
|
||||
mir::LocalDecl {
|
||||
mutability: mir::Mutability::Mut,
|
||||
ty: tcx.types.usize,
|
||||
name: None,
|
||||
source_info,
|
||||
is_user_variable: false,
|
||||
},
|
||||
mir::LocalDecl {
|
||||
mutability: mir::Mutability::Mut,
|
||||
ty: tcx.types.bool,
|
||||
name: None,
|
||||
source_info,
|
||||
is_user_variable: false,
|
||||
},
|
||||
];
|
||||
let seq_drop_glue = mir::Mir::new(
|
||||
vec![start_block, head, loop_, inc, ret].into_iter().collect(),
|
||||
Vec::new().into_iter().collect(), // vis scopes
|
||||
Vec::new().into_iter().collect(), // promoted
|
||||
tcx.mk_nil(), // return type
|
||||
locals.into_iter().collect(),
|
||||
1, // arg_count
|
||||
Vec::new(), // upvars
|
||||
DUMMY_SP,
|
||||
);
|
||||
let seq_drop_glue = tcx.alloc_mir(seq_drop_glue);
|
||||
EvalContext {
|
||||
tcx,
|
||||
memory: Memory::new(&tcx.data_layout, limits.memory_size),
|
||||
|
@ -308,7 +129,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
stack: Vec::new(),
|
||||
stack_limit: limits.stack_limit,
|
||||
steps_remaining: limits.step_limit,
|
||||
seq_drop_glue: seq_drop_glue,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -631,6 +451,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
self.write_value(value, dest, dest_ty)?;
|
||||
}
|
||||
|
||||
BinaryOp(mir::BinOp::Offset, ref left, ref right) => {
|
||||
let pointer_ty = self.operand_ty(left);
|
||||
let pointee_ty = pointer_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty;
|
||||
// FIXME: assuming here that type size is < i64::max_value()
|
||||
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
|
||||
let offset = self.eval_operand_to_primval(right)?.to_i128()? as i64;
|
||||
|
||||
let ptr = self.eval_operand_to_primval(left)?.to_ptr()?;
|
||||
let result_ptr = ptr.signed_offset(offset * pointee_size);
|
||||
self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?;
|
||||
}
|
||||
|
||||
BinaryOp(bin_op, ref left, ref right) => {
|
||||
// ignore overflow bit, rustc inserts check branches for us
|
||||
self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)?;
|
||||
|
@ -823,8 +655,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
|
||||
}
|
||||
|
||||
NullaryOp(mir::NullOp::SizeOf, _ty) => {
|
||||
unimplemented!()
|
||||
NullaryOp(mir::NullOp::SizeOf, ty) => {
|
||||
let size = self.type_size(ty)?.expect("SizeOf nullary MIR operator called for unsized type");
|
||||
self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?;
|
||||
}
|
||||
|
||||
Cast(kind, ref operand, cast_ty) => {
|
||||
|
|
|
@ -362,7 +362,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let n_ptr = self.eval_operand(operand)?;
|
||||
let usize = self.tcx.types.usize;
|
||||
let n = self.value_to_primval(n_ptr, usize)?.to_u64()?;
|
||||
assert!(n < len);
|
||||
assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len);
|
||||
let ptr = base_ptr.offset(n * elem_size);
|
||||
(ptr, LvalueExtra::None)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use rustc::mir;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::subst::Kind;
|
||||
use syntax::codemap::Span;
|
||||
|
||||
use error::EvalResult;
|
||||
|
@ -21,7 +20,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
};
|
||||
self.drop(val, instance, ty, span)
|
||||
}
|
||||
pub(crate) fn drop(&mut self, mut arg: Value, mut instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> {
|
||||
pub(crate) fn drop(&mut self, arg: Value, mut instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> {
|
||||
trace!("drop: {:#?}, {:?}, {:?}", arg, ty.sty, instance.def);
|
||||
|
||||
if let ty::InstanceDef::DropGlue(_, None) = instance.def {
|
||||
|
@ -44,23 +43,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
None => return Ok(()),
|
||||
}
|
||||
},
|
||||
ty::TyArray(elem, n) => {
|
||||
instance.substs = self.tcx.mk_substs([
|
||||
Kind::from(elem),
|
||||
].iter().cloned());
|
||||
let ptr = match arg {
|
||||
Value::ByVal(PrimVal::Ptr(src_ptr)) => src_ptr,
|
||||
_ => bug!("expected thin ptr, got {:?}", arg),
|
||||
};
|
||||
arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(n as u128));
|
||||
self.seq_drop_glue
|
||||
},
|
||||
ty::TySlice(elem) => {
|
||||
instance.substs = self.tcx.mk_substs([
|
||||
Kind::from(elem),
|
||||
].iter().cloned());
|
||||
self.seq_drop_glue
|
||||
},
|
||||
_ => self.load_mir(instance.def)?,
|
||||
};
|
||||
|
||||
|
|
|
@ -360,11 +360,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
"size_of" => {
|
||||
let ty = substs.type_at(0);
|
||||
// FIXME: change the `box_free` lang item to take `T: ?Sized` and have it use the
|
||||
// `size_of_val` intrinsic, then change this back to
|
||||
// .expect("size_of intrinsic called on unsized value")
|
||||
// see https://github.com/rust-lang/rust/pull/37708
|
||||
let size = self.type_size(ty)?.unwrap_or(!0) as u128;
|
||||
let size = self.type_size(ty)?.expect("size_of intrinsic called on unsized value") as u128;
|
||||
self.write_primval(dest, PrimVal::from_u128(size), dest_ty)?;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use rustc::hir::def_id::DefId;
|
||||
use rustc::mir;
|
||||
use rustc::ty::{self, TypeVariants, Ty, TypeAndMut};
|
||||
use rustc::ty::{self, TypeVariants, Ty};
|
||||
use rustc::ty::layout::Layout;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::attr;
|
||||
|
@ -730,12 +730,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let dtor = if dtor_ptr.is_null_ptr() { None } else { Some(self.memory.get_fn(dtor_ptr.alloc_id)?) };
|
||||
|
||||
// Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t.
|
||||
let key_size = match self.operand_ty(&arg_operands[0]).sty {
|
||||
TypeVariants::TyRawPtr(TypeAndMut { ty, .. }) => {
|
||||
let layout = self.type_layout(ty)?;
|
||||
layout.size(&self.tcx.data_layout)
|
||||
}
|
||||
_ => return Err(EvalError::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned()))
|
||||
let key_type = self.operand_ty(&arg_operands[0]).builtin_deref(true, ty::LvaluePreference::NoPreference)
|
||||
.ok_or(EvalError::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned()))?.ty;
|
||||
let key_size = {
|
||||
let layout = self.type_layout(key_type)?;
|
||||
layout.size(&self.tcx.data_layout)
|
||||
};
|
||||
|
||||
// Create key and write it into the memory where key_ptr wants it
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
struct Bar;
|
||||
struct Bar(i32); // ZSTs are tested separately
|
||||
|
||||
static mut DROP_COUNT: usize = 0;
|
||||
|
||||
|
@ -9,8 +9,13 @@ impl Drop for Bar {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
let b = [Bar, Bar, Bar, Bar];
|
||||
let b = [Bar(0), Bar(0), Bar(0), Bar(0)];
|
||||
assert_eq!(unsafe { DROP_COUNT }, 0);
|
||||
drop(b);
|
||||
assert_eq!(unsafe { DROP_COUNT }, 4);
|
||||
|
||||
// check empty case
|
||||
let b : [Bar; 0] = [];
|
||||
drop(b);
|
||||
assert_eq!(unsafe { DROP_COUNT }, 4);
|
||||
}
|
||||
|
|
21
tests/run-pass/call_drop_on_zst_array_elements.rs
Normal file
21
tests/run-pass/call_drop_on_zst_array_elements.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
struct Bar;
|
||||
|
||||
static mut DROP_COUNT: usize = 0;
|
||||
|
||||
impl Drop for Bar {
|
||||
fn drop(&mut self) {
|
||||
unsafe { DROP_COUNT += 1; }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let b = [Bar, Bar, Bar, Bar];
|
||||
assert_eq!(unsafe { DROP_COUNT }, 0);
|
||||
drop(b);
|
||||
assert_eq!(unsafe { DROP_COUNT }, 4);
|
||||
|
||||
// check empty case
|
||||
let b : [Bar; 0] = [];
|
||||
drop(b);
|
||||
assert_eq!(unsafe { DROP_COUNT }, 4);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue