diff --git a/src/eval_context.rs b/src/eval_context.rs index 0ed8e5dc495..b2cd1a665d3 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -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) => { diff --git a/src/lvalue.rs b/src/lvalue.rs index ce0651bf129..1d796d254d7 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -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) } diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 93dfe408e31..77606195425 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -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)?, }; diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index e65c7eda121..0ca12ee5063 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -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)?; } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 3c254734b32..c2131d6627e 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -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 diff --git a/tests/run-pass/call_drop_on_array_elements.rs b/tests/run-pass/call_drop_on_array_elements.rs index 80dd63de5e9..68dbf61da4e 100644 --- a/tests/run-pass/call_drop_on_array_elements.rs +++ b/tests/run-pass/call_drop_on_array_elements.rs @@ -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); } diff --git a/tests/run-pass/call_drop_on_zst_array_elements.rs b/tests/run-pass/call_drop_on_zst_array_elements.rs new file mode 100644 index 00000000000..1887130fdd8 --- /dev/null +++ b/tests/run-pass/call_drop_on_zst_array_elements.rs @@ -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); +}