diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 92bac85e56e..a8d29f78758 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -71,6 +71,9 @@ pub struct Frame<'a, 'tcx: 'a> { /// A pointer for writing the return value of the current call if it's not a diverging call. pub return_ptr: Option, + /// The block to return to when returning from the current stack frame + pub return_to_block: Option, + /// The list of locals for the current function, stored in order as /// `[arguments..., variables..., temporaries...]`. The variables begin at `self.var_offset` /// and the temporaries at `self.temp_offset`. @@ -305,6 +308,7 @@ pub fn push_stack_frame( mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, return_ptr: Option, + return_to_block: Option, ) -> EvalResult<'tcx, ()> { let arg_tys = mir.arg_decls.iter().map(|a| a.ty); let var_tys = mir.var_decls.iter().map(|v| v.ty); @@ -325,6 +329,7 @@ pub fn push_stack_frame( mir: mir.clone(), block: mir::START_BLOCK, return_ptr: return_ptr, + return_to_block: return_to_block, locals: locals?, var_offset: num_args, temp_offset: num_args + num_vars, @@ -342,7 +347,10 @@ pub fn push_stack_frame( fn pop_stack_frame(&mut self) { ::log_settings::settings().indentation -= 1; - let _frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); + let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); + if let Some(target) = frame.return_to_block { + self.goto_block(target); + } // TODO(solson): Deallocate local variables. } @@ -961,7 +969,7 @@ pub fn eval_main<'a, 'tcx: 'a>( let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs) .expect("should at least be able to allocate space for the main function's return value"); - ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr)) + ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr), None) .expect("could not allocate first stack frame"); if mir.arg_decls.len() == 2 { diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 3175d2a16a3..3684f525ba6 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -87,8 +87,6 @@ fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx, ()> { } fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx, ()> { - // after a terminator we go to a new block - self.frame_mut().stmt = 0; trace!("{:?}", terminator.kind); self.eval_terminator(terminator)?; if !self.stack.is_empty() { @@ -125,7 +123,7 @@ fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span self.try(|this| { let ptr = this.ecx.alloc_ret_ptr(mir.return_ty, substs)?; this.ecx.statics.insert(cid.clone(), ptr); - this.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr)) + this.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr), None) }); } fn try EvalResult<'tcx, ()>>(&mut self, f: F) { @@ -170,7 +168,7 @@ fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Loca let return_ptr = this.ecx.alloc_ret_ptr(return_ty, cid.substs)?; let mir = CachedMir::Owned(Rc::new(mir)); this.ecx.statics.insert(cid.clone(), return_ptr); - this.ecx.push_stack_frame(this.def_id, constant.span, mir, this.substs, Some(return_ptr)) + this.ecx.push_stack_frame(this.def_id, constant.span, mir, this.substs, Some(return_ptr), None) }); } } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index bdf8a5a946b..36e2c05f4f3 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -15,6 +15,12 @@ use memory::{Pointer, FunctionDefinition}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { + + pub(super) fn goto_block(&mut self, target: mir::BasicBlock) { + self.frame_mut().block = target; + self.frame_mut().stmt = 0; + } + pub(super) fn eval_terminator( &mut self, terminator: &mir::Terminator<'tcx>, @@ -23,14 +29,12 @@ pub(super) fn eval_terminator( match terminator.kind { Return => self.pop_stack_frame(), - Goto { target } => { - self.frame_mut().block = target; - }, + Goto { target } => self.goto_block(target), If { ref cond, targets: (then_target, else_target) } => { let cond_ptr = self.eval_operand(cond)?; let cond_val = self.memory.read_bool(cond_ptr)?; - self.frame_mut().block = if cond_val { then_target } else { else_target }; + self.goto_block(if cond_val { then_target } else { else_target }); } SwitchInt { ref discr, ref values, ref targets, .. } => { @@ -59,7 +63,7 @@ pub(super) fn eval_terminator( } } - self.frame_mut().block = target_block; + self.goto_block(target_block); } Switch { ref discr, ref targets, adt_def } => { @@ -70,19 +74,16 @@ pub(super) fn eval_terminator( .position(|v| discr_val == v.disr_val.to_u64_unchecked()); match matching { - Some(i) => { - self.frame_mut().block = targets[i]; - }, + Some(i) => self.goto_block(targets[i]), None => return Err(EvalError::InvalidDiscriminant), } } Call { ref func, ref args, ref destination, .. } => { - let mut return_ptr = None; - if let Some((ref lv, target)) = *destination { - self.frame_mut().block = target; - return_ptr = Some(self.eval_lvalue(lv)?.to_ptr()); - } + let destination = match *destination { + Some((ref lv, target)) => Some((self.eval_lvalue(lv)?.to_ptr(), target)), + None => None, + }; let func_ty = self.operand_ty(func); match func_ty.sty { @@ -93,11 +94,11 @@ pub(super) fn eval_terminator( if fn_ty != bare_fn_ty { return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty)); } - self.eval_fn_call(def_id, substs, bare_fn_ty, return_ptr, args, + self.eval_fn_call(def_id, substs, bare_fn_ty, destination, args, terminator.source_info.span)? }, ty::TyFnDef(def_id, substs, fn_ty) => { - self.eval_fn_call(def_id, substs, fn_ty, return_ptr, args, + self.eval_fn_call(def_id, substs, fn_ty, destination, args, terminator.source_info.span)? } @@ -109,13 +110,13 @@ pub(super) fn eval_terminator( let ptr = self.eval_lvalue(location)?.to_ptr(); let ty = self.lvalue_ty(location); self.drop(ptr, ty)?; - self.frame_mut().block = target; + self.goto_block(target); } Assert { ref cond, expected, ref msg, target, .. } => { let cond_ptr = self.eval_operand(cond)?; if expected == self.memory.read_bool(cond_ptr)? { - self.frame_mut().block = target; + self.goto_block(target); } else { return match *msg { mir::AssertMessage::BoundsCheck { ref len, ref index } => { @@ -143,7 +144,7 @@ fn eval_fn_call( def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy, - return_ptr: Option, + destination: Option<(Pointer, mir::BasicBlock)>, args: &[mir::Operand<'tcx>], span: Span, ) -> EvalResult<'tcx, ()> { @@ -152,14 +153,19 @@ fn eval_fn_call( Abi::RustIntrinsic => { let ty = fn_ty.sig.0.output; let layout = self.type_layout(ty); - let ret = return_ptr.unwrap(); - self.call_intrinsic(def_id, substs, args, ret, layout) + let (ret, target) = destination.unwrap(); + self.call_intrinsic(def_id, substs, args, ret, layout)?; + self.goto_block(target); + Ok(()) } Abi::C => { let ty = fn_ty.sig.0.output; let size = self.type_size(ty); - self.call_c_abi(def_id, args, return_ptr.unwrap(), size) + let (ret, target) = destination.unwrap(); + self.call_c_abi(def_id, args, ret, size)?; + self.goto_block(target); + Ok(()) } Abi::Rust | Abi::RustCall => { @@ -203,7 +209,11 @@ fn eval_fn_call( } let mir = self.load_mir(resolved_def_id); - self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr)?; + let (return_ptr, return_to_block) = match destination { + Some((ptr, block)) => (Some(ptr), Some(block)), + None => (None, None), + }; + self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr, return_to_block)?; for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { let dest = self.frame().locals[i]; diff --git a/tests/compile-fail/cast_fn_ptr.rs b/tests/compile-fail/cast_fn_ptr.rs index f35aad87270..e9b2536a700 100644 --- a/tests/compile-fail/cast_fn_ptr.rs +++ b/tests/compile-fail/cast_fn_ptr.rs @@ -1,9 +1,9 @@ fn main() { fn f() {} - let g = unsafe { //~ ERROR tried to call a function of type + let g = unsafe { std::mem::transmute::(f) }; - g(42) + g(42) //~ ERROR tried to call a function of type } diff --git a/tests/compile-fail/execute_memory.rs b/tests/compile-fail/execute_memory.rs index c7d25a66315..8d3c9df0320 100644 --- a/tests/compile-fail/execute_memory.rs +++ b/tests/compile-fail/execute_memory.rs @@ -4,7 +4,6 @@ fn main() { let x = box 42; unsafe { let f = std::mem::transmute::, fn()>(x); - //~^ ERROR: tried to treat a memory pointer as a function pointer - f() + f() //~ ERROR: tried to treat a memory pointer as a function pointer } } diff --git a/tests/compile-fail/oom.rs b/tests/compile-fail/oom.rs index d3911a65f2f..be56240af47 100644 --- a/tests/compile-fail/oom.rs +++ b/tests/compile-fail/oom.rs @@ -6,6 +6,6 @@ fn bar() { assert_eq!(x, 6); } -fn main() { //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 0 byte memory - bar(); +fn main() { + bar(); //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 0 byte memory }