diff options
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | miri/fn_call.rs | 564 | ||||
| -rw-r--r-- | miri/lib.rs | 20 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/const_eval.rs | 44 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/machine.rs | 20 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/terminator/mod.rs | 53 |
6 files changed, 624 insertions, 78 deletions
diff --git a/Cargo.toml b/Cargo.toml index 5081cb1081b..4ee9dc974bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,3 +35,4 @@ compiletest_rs = "0.2.6" [workspace] members = ["src/librustc_mir"] +exclude = ["xargo"] diff --git a/miri/fn_call.rs b/miri/fn_call.rs new file mode 100644 index 00000000000..d8e92e7291f --- /dev/null +++ b/miri/fn_call.rs @@ -0,0 +1,564 @@ +use rustc::ty::{self, Ty}; +use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX}; +use rustc::mir; +use syntax::attr; +use syntax::abi::Abi; +use syntax::codemap::Span; + +use std::mem; + +use rustc_miri::interpret::*; + +use super::{ + TlsKey, + EvalContext, + MemoryExt, +}; + +pub trait EvalContextExt<'tcx> { + fn call_c_abi( + &mut self, + def_id: DefId, + arg_operands: &[mir::Operand<'tcx>], + dest: Lvalue<'tcx>, + dest_ty: Ty<'tcx>, + dest_block: mir::BasicBlock, + ) -> EvalResult<'tcx>; + + fn resolve_path(&self, path: &[&str]) -> EvalResult<'tcx, ty::Instance<'tcx>>; + + fn call_missing_fn( + &mut self, + instance: ty::Instance<'tcx>, + destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, + arg_operands: &[mir::Operand<'tcx>], + sig: ty::FnSig<'tcx>, + path: String, + ) -> EvalResult<'tcx>; + + fn eval_fn_call( + &mut self, + instance: ty::Instance<'tcx>, + destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, + arg_operands: &[mir::Operand<'tcx>], + span: Span, + sig: ty::FnSig<'tcx>, + ) -> EvalResult<'tcx, bool>; +} + +impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator> { + fn eval_fn_call( + &mut self, + instance: ty::Instance<'tcx>, + destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, + arg_operands: &[mir::Operand<'tcx>], + span: Span, + sig: ty::FnSig<'tcx>, + ) -> EvalResult<'tcx, bool> { + trace!("eval_fn_call: {:#?}, {:#?}", instance, destination); + + let mir = match self.load_mir(instance.def) { + Ok(mir) => mir, + Err(EvalError::NoMirFor(path)) => { + self.call_missing_fn(instance, destination, arg_operands, sig, path)?; + return Ok(true); + }, + Err(other) => return Err(other), + }; + + let (return_lvalue, return_to_block) = match destination { + Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)), + None => (Lvalue::undef(), StackPopCleanup::None), + }; + + self.push_stack_frame( + instance, + span, + mir, + return_lvalue, + return_to_block, + )?; + + Ok(false) + } + + fn call_c_abi( + &mut self, + def_id: DefId, + arg_operands: &[mir::Operand<'tcx>], + dest: Lvalue<'tcx>, + dest_ty: Ty<'tcx>, + dest_block: mir::BasicBlock, + ) -> EvalResult<'tcx> { + let name = self.tcx.item_name(def_id); + let attrs = self.tcx.get_attrs(def_id); + let link_name = attr::first_attr_value_str_by_name(&attrs, "link_name") + .unwrap_or(name) + .as_str(); + + let args_res: EvalResult<Vec<Value>> = arg_operands.iter() + .map(|arg| self.eval_operand(arg)) + .collect(); + let args = args_res?; + + let usize = self.tcx.types.usize; + + match &link_name[..] { + "malloc" => { + let size = self.value_to_primval(args[0], usize)?.to_u64()?; + if size == 0 { + self.write_null(dest, dest_ty)?; + } else { + let align = self.memory.pointer_size(); + let ptr = self.memory.allocate(size, align, Kind::C)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + } + } + + "free" => { + let ptr = args[0].into_ptr(&mut self.memory)?; + if !ptr.is_null()? { + self.memory.deallocate(ptr.to_ptr()?, None, Kind::C)?; + } + } + + "syscall" => { + match self.value_to_primval(args[0], usize)?.to_u64()? { + 511 => return Err(EvalError::Unimplemented("miri does not support random number generators".to_owned())), + id => return Err(EvalError::Unimplemented(format!("miri does not support syscall id {}", id))), + } + } + + "dlsym" => { + let _handle = args[0].into_ptr(&mut self.memory)?; + let symbol = args[1].into_ptr(&mut self.memory)?.to_ptr()?; + let symbol_name = self.memory.read_c_str(symbol)?; + let err = format!("bad c unicode symbol: {:?}", symbol_name); + let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err); + return Err(EvalError::Unimplemented(format!("miri does not support dynamically loading libraries (requested symbol: {})", symbol_name))); + } + + "__rust_maybe_catch_panic" => { + // fn __rust_maybe_catch_panic(f: fn(*mut u8), data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32 + // We abort on panic, so not much is going on here, but we still have to call the closure + let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); + let f = args[0].into_ptr(&mut self.memory)?.to_ptr()?; + let data = args[1].into_ptr(&mut self.memory)?; + let f_instance = self.memory.get_fn(f)?; + self.write_null(dest, dest_ty)?; + + // Now we make a function call. TODO: Consider making this re-usable? EvalContext::step does sth. similar for the TLS dtors, + // and of course eval_main. + let mir = self.load_mir(f_instance.def)?; + self.push_stack_frame( + f_instance, + mir.span, + mir, + Lvalue::undef(), + StackPopCleanup::Goto(dest_block), + )?; + + let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("Argument to __rust_maybe_catch_panic does not take enough arguments.".to_owned()))?; + let arg_dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_ptr(arg_dest, data, u8_ptr_ty)?; + + // We ourselves return 0 + self.write_null(dest, dest_ty)?; + + // Don't fall through + return Ok(()); + } + + "__rust_start_panic" => { + return Err(EvalError::Panic); + } + + "memcmp" => { + let left = args[0].into_ptr(&mut self.memory)?; + let right = args[1].into_ptr(&mut self.memory)?; + let n = self.value_to_primval(args[2], usize)?.to_u64()?; + + let result = { + let left_bytes = self.memory.read_bytes(left, n)?; + let right_bytes = self.memory.read_bytes(right, n)?; + + use std::cmp::Ordering::*; + match left_bytes.cmp(right_bytes) { + Less => -1i8, + Equal => 0, + Greater => 1, + } + }; + + self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?; + } + + "memrchr" => { + let ptr = args[0].into_ptr(&mut self.memory)?; + let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; + let num = self.value_to_primval(args[2], usize)?.to_u64()?; + if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { + let new_ptr = ptr.offset(num - idx as u64 - 1, &self)?; + self.write_ptr(dest, new_ptr, dest_ty)?; + } else { + self.write_null(dest, dest_ty)?; + } + } + + "memchr" => { + let ptr = args[0].into_ptr(&mut self.memory)?; + let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; + let num = self.value_to_primval(args[2], usize)?.to_u64()?; + if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { + let new_ptr = ptr.offset(idx as u64, &self)?; + self.write_ptr(dest, new_ptr, dest_ty)?; + } else { + self.write_null(dest, dest_ty)?; + } + } + + "getenv" => { + let result = { + let name_ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?; + let name = self.memory.read_c_str(name_ptr)?; + match self.machine_data.env_vars.get(name) { + Some(&var) => PrimVal::Ptr(var), + None => PrimVal::Bytes(0), + } + }; + self.write_primval(dest, result, dest_ty)?; + } + + "unsetenv" => { + let mut success = None; + { + let name_ptr = args[0].into_ptr(&mut self.memory)?; + if !name_ptr.is_null()? { + let name = self.memory.read_c_str(name_ptr.to_ptr()?)?; + if !name.is_empty() && !name.contains(&b'=') { + success = Some(self.machine_data.env_vars.remove(name)); + } + } + } + if let Some(old) = success { + if let Some(var) = old { + self.memory.deallocate(var, None, Kind::Env)?; + } + self.write_null(dest, dest_ty)?; + } else { + self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?; + } + } + + "setenv" => { + let mut new = None; + { + let name_ptr = args[0].into_ptr(&mut self.memory)?; + let value_ptr = args[1].into_ptr(&mut self.memory)?.to_ptr()?; + let value = self.memory.read_c_str(value_ptr)?; + if !name_ptr.is_null()? { + let name = self.memory.read_c_str(name_ptr.to_ptr()?)?; + if !name.is_empty() && !name.contains(&b'=') { + new = Some((name.to_owned(), value.to_owned())); + } + } + } + if let Some((name, value)) = new { + // +1 for the null terminator + let value_copy = self.memory.allocate((value.len() + 1) as u64, 1, Kind::Env)?; + self.memory.write_bytes(value_copy.into(), &value)?; + let trailing_zero_ptr = value_copy.offset(value.len() as u64, &self)?.into(); + self.memory.write_bytes(trailing_zero_ptr, &[0])?; + if let Some(var) = self.machine_data.env_vars.insert(name.to_owned(), value_copy) { + self.memory.deallocate(var, None, Kind::Env)?; + } + self.write_null(dest, dest_ty)?; + } else { + self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?; + } + } + + "write" => { + let fd = self.value_to_primval(args[0], usize)?.to_u64()?; + let buf = args[1].into_ptr(&mut self.memory)?; + let n = self.value_to_primval(args[2], usize)?.to_u64()?; + trace!("Called write({:?}, {:?}, {:?})", fd, buf, n); + let result = if fd == 1 || fd == 2 { // stdout/stderr + use std::io::{self, Write}; + + let buf_cont = self.memory.read_bytes(buf, n)?; + let res = if fd == 1 { io::stdout().write(buf_cont) } else { io::stderr().write(buf_cont) }; + match res { Ok(n) => n as isize, Err(_) => -1 } + } else { + info!("Ignored output to FD {}", fd); + n as isize // pretend it all went well + }; // now result is the value we return back to the program + self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?; + } + + "strlen" => { + let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?; + let n = self.memory.read_c_str(ptr)?.len(); + self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?; + } + + // Some things needed for sys::thread initialization to go through + "signal" | "sigaction" | "sigaltstack" => { + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + } + + "sysconf" => { + let name = self.value_to_primval(args[0], usize)?.to_u64()?; + trace!("sysconf() called with name {}", name); + // cache the sysconf integers via miri's global cache + let paths = &[ + (&["libc", "_SC_PAGESIZE"], PrimVal::Bytes(4096)), + (&["libc", "_SC_GETPW_R_SIZE_MAX"], PrimVal::from_i128(-1)), + ]; + let mut result = None; + for &(path, path_value) in paths { + if let Ok(instance) = self.resolve_path(path) { + let cid = GlobalId { instance, promoted: None }; + // compute global if not cached + let val = match self.globals.get(&cid).map(|glob| glob.value) { + Some(value) => self.value_to_primval(value, usize)?.to_u64()?, + None => eval_body_as_primval(self.tcx, instance)?.0.to_u64()?, + }; + if val == name { + result = Some(path_value); + break; + } + } + } + if let Some(result) = result { + self.write_primval(dest, result, dest_ty)?; + } else { + return Err(EvalError::Unimplemented(format!("Unimplemented sysconf name: {}", name))); + } + } + + // Hook pthread calls that go to the thread-local storage memory subsystem + "pthread_key_create" => { + let key_ptr = args[0].into_ptr(&mut self.memory)?; + + // Extract the function type out of the signature (that seems easier than constructing it ourselves...) + let dtor = match args[1].into_ptr(&mut self.memory)?.into_inner_primval() { + PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?), + PrimVal::Bytes(0) => None, + PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer), + PrimVal::Undef => return Err(EvalError::ReadUndefBytes), + }; + + // Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t. + 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 + let key = self.memory.create_tls_key(dtor) as u128; + if key_size.bits() < 128 && key >= (1u128 << key_size.bits() as u128) { + return Err(EvalError::OutOfTls); + } + // TODO: Does this need checking for alignment? + self.memory.write_uint(key_ptr.to_ptr()?, key, key_size.bytes())?; + + // Return success (0) + self.write_null(dest, dest_ty)?; + } + "pthread_key_delete" => { + // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t + let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; + self.memory.delete_tls_key(key)?; + // Return success (0) + self.write_null(dest, dest_ty)?; + } + "pthread_getspecific" => { + // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t + let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; + let ptr = self.memory.load_tls(key)?; + self.write_ptr(dest, ptr, dest_ty)?; + } + "pthread_setspecific" => { + // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t + let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; + let new_ptr = args[1].into_ptr(&mut self.memory)?; + self.memory.store_tls(key, new_ptr)?; + + // Return success (0) + self.write_null(dest, dest_ty)?; + } + + // Stub out all the other pthread calls to just return 0 + link_name if link_name.starts_with("pthread_") => { + warn!("ignoring C ABI call: {}", link_name); + self.write_null(dest, dest_ty)?; + }, + + _ => { + return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))); + } + } + + // Since we pushed no stack frame, the main loop will act + // as if the call just completed and it's returning to the + // current frame. + self.dump_local(dest); + self.goto_block(dest_block); + Ok(()) + } + + /// Get an instance for a path. + fn resolve_path(&self, path: &[&str]) -> EvalResult<'tcx, ty::Instance<'tcx>> { + let cstore = &self.tcx.sess.cstore; + + let crates = cstore.crates(); + crates.iter() + .find(|&&krate| cstore.crate_name(krate) == path[0]) + .and_then(|krate| { + let krate = DefId { + krate: *krate, + index: CRATE_DEF_INDEX, + }; + let mut items = cstore.item_children(krate, self.tcx.sess); + let mut path_it = path.iter().skip(1).peekable(); + + while let Some(segment) = path_it.next() { + for item in &mem::replace(&mut items, vec![]) { + if item.ident.name == *segment { + if path_it.peek().is_none() { + return Some(ty::Instance::mono(self.tcx, item.def.def_id())); + } + + items = cstore.item_children(item.def.def_id(), self.tcx.sess); + break; + } + } + } + None + }) + .ok_or_else(|| { + let path = path.iter() + .map(|&s| s.to_owned()) + .collect(); + EvalError::PathNotFound(path) + }) + } + + fn call_missing_fn( + &mut self, + instance: ty::Instance<'tcx>, + destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, + arg_operands: &[mir::Operand<'tcx>], + sig: ty::FnSig<'tcx>, + path: String, + ) -> EvalResult<'tcx> { + // In some cases in non-MIR libstd-mode, not having a destination is legit. Handle these early. + match &path[..] { + "std::panicking::rust_panic_with_hook" | + "std::rt::begin_panic_fmt" => return Err(EvalError::Panic), + _ => {}, + } + + let dest_ty = sig.output(); + let (dest, dest_block) = destination.ok_or_else(|| EvalError::NoMirFor(path.clone()))?; + + if sig.abi == Abi::C { + // An external C function + // TODO: That functions actually has a similar preamble to what follows here. May make sense to + // unify these two mechanisms for "hooking into missing functions". + self.call_c_abi(instance.def_id(), arg_operands, dest, dest_ty, dest_block)?; + return Ok(()); + } + + let args_res: EvalResult<Vec<Value>> = arg_operands.iter() + .map(|arg| self.eval_operand(arg)) + .collect(); + let args = args_res?; + + let usize = self.tcx.types.usize; + + match &path[..] { + // Allocators are magic. They have no MIR, even when the rest of libstd does. + "alloc::heap::::__rust_alloc" => { + let size = self.value_to_primval(args[0], usize)?.to_u64()?; + let align = self.value_to_primval(args[1], usize)?.to_u64()?; + if size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); + } + let ptr = self.memory.allocate(size, align, Kind::Rust)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + } + "alloc::heap::::__rust_alloc_zeroed" => { + let size = self.value_to_primval(args[0], usize)?.to_u64()?; + let align = self.value_to_primval(args[1], usize)?.to_u64()?; + if size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); + } + let ptr = self.memory.allocate(size, align, Kind::Rust)?; + self.memory.write_repeat(ptr.into(), 0, size)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + } + "alloc::heap::::__rust_dealloc" => { + let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?; + let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; + let align = self.value_to_primval(args[2], usize)?.to_u64()?; + if old_size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); + } + self.memory.deallocate(ptr, Some((old_size, align)), Kind::Rust)?; + } + "alloc::heap::::__rust_realloc" => { + let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?; + let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; + let old_align = self.value_to_primval(args[2], usize)?.to_u64()?; + let new_size = self.value_to_primval(args[3], usize)?.to_u64()?; + let new_align = self.value_to_primval(args[4], usize)?.to_u64()?; + if old_size == 0 || new_size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !old_align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(old_align)); + } + if !new_align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(new_align)); + } + let new_ptr = self.memory.reallocate(ptr, old_size, old_align, new_size, new_align, Kind::Rust)?; + self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; + } + + // A Rust function is missing, which means we are running with MIR missing for libstd (or other dependencies). + // Still, we can make many things mostly work by "emulating" or ignoring some functions. + "std::io::_print" => { + trace!("Ignoring output. To run programs that print, make sure you have a libstd with full MIR."); + } + "std::thread::Builder::new" => return Err(EvalError::Unimplemented("miri does not support threading".to_owned())), + "std::env::args" => return Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), + "std::panicking::panicking" | + "std::rt::panicking" => { + // we abort on panic -> `std::rt::panicking` always returns false + let bool = self.tcx.types.bool; + self.write_primval(dest, PrimVal::from_bool(false), bool)?; + } + _ => return Err(EvalError::NoMirFor(path)), + } + + // Since we pushed no stack frame, the main loop will act + // as if the call just completed and it's returning to the + // current frame. + self.dump_local(dest); + self.goto_block(dest_block); + return Ok(()); + } +} diff --git a/miri/lib.rs b/miri/lib.rs index 4482e8fcb7c..bd17ab6f2cb 100644 --- a/miri/lib.rs +++ b/miri/lib.rs @@ -17,6 +17,8 @@ use rustc::ty::{self, TyCtxt}; use rustc::hir::def_id::DefId; use rustc::mir; +use syntax::codemap::Span; + use std::collections::{ HashMap, BTreeMap, @@ -25,10 +27,10 @@ use std::collections::{ extern crate rustc_miri; pub use rustc_miri::interpret::*; -mod missing_fns; +mod fn_call; mod operator; -use missing_fns::EvalContextExt as MissingFnsEvalContextExt; +use fn_call::EvalContextExt as MissingFnsEvalContextExt; use operator::EvalContextExt as OperatorEvalContextExt; pub fn eval_main<'a, 'tcx: 'a>( @@ -272,17 +274,19 @@ impl<'a, 'tcx: 'a> MemoryExt<'tcx> for Memory<'a, 'tcx, Evaluator> { impl<'tcx> Machine<'tcx> for Evaluator { type Data = EvaluatorData; type MemoryData = MemoryData<'tcx>; + /// Returns Ok() when the function was handled, fail otherwise - fn call_missing_fn<'a>( + fn eval_fn_call<'a>( ecx: &mut EvalContext<'a, 'tcx, Self>, instance: ty::Instance<'tcx>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, arg_operands: &[mir::Operand<'tcx>], + span: Span, sig: ty::FnSig<'tcx>, - path: String, - ) -> EvalResult<'tcx> { - ecx.call_missing_fn(instance, destination, arg_operands, sig, path) + ) -> EvalResult<'tcx, bool> { + ecx.eval_fn_call(instance, destination, arg_operands, span, sig) } + fn ptr_op<'a>( ecx: &rustc_miri::interpret::EvalContext<'a, 'tcx, Self>, bin_op: mir::BinOp, @@ -293,8 +297,4 @@ impl<'tcx> Machine<'tcx> for Evaluator { ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> { ecx.ptr_op(bin_op, left, left_ty, right, right_ty) } - - fn check_non_const_fn_call(_instance: ty::Instance<'tcx>) -> EvalResult<'tcx> { - Ok(()) - } } diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index a9974e5367e..32f5a0a183e 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -3,6 +3,7 @@ use rustc::ty::{self, TyCtxt, Ty, Instance}; use rustc::mir; use syntax::ast::Mutability; +use syntax::codemap::Span; use super::{ EvalResult, EvalError, @@ -127,16 +128,39 @@ impl Error for ConstEvalError { impl<'tcx> super::Machine<'tcx> for CompileTimeFunctionEvaluator { type Data = (); type MemoryData = (); - fn call_missing_fn<'a>( - _ecx: &mut EvalContext<'a, 'tcx, Self>, - _instance: ty::Instance<'tcx>, - _destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, + fn eval_fn_call<'a>( + ecx: &mut EvalContext<'a, 'tcx, Self>, + instance: ty::Instance<'tcx>, + destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, _arg_operands: &[mir::Operand<'tcx>], + span: Span, _sig: ty::FnSig<'tcx>, - path: String, - ) -> EvalResult<'tcx> { - // some simple things like `malloc` might get accepted in the future - Err(ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path)).into()) + ) -> EvalResult<'tcx, bool> { + if !ecx.tcx.is_const_fn(instance.def_id()) { + return Err(ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into()); + } + let mir = match ecx.load_mir(instance.def) { + Ok(mir) => mir, + Err(EvalError::NoMirFor(path)) => { + // some simple things like `malloc` might get accepted in the future + return Err(ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path)).into()); + }, + Err(other) => return Err(other), + }; + let (return_lvalue, return_to_block) = match destination { + Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)), + None => (Lvalue::undef(), StackPopCleanup::None), + }; + + ecx.push_stack_frame( + instance, + span, + mir, + return_lvalue, + return_to_block, + )?; + + Ok(false) } fn ptr_op<'a>( @@ -149,8 +173,4 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeFunctionEvaluator { ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> { Err(ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into()) } - - fn check_non_const_fn_call(instance: ty::Instance<'tcx>) -> EvalResult<'tcx> { - return Err(ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into()); - } } diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 5d762c81a9b..ebf15300e8c 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -10,6 +10,7 @@ use super::{ }; use rustc::{mir, ty}; +use syntax::codemap::Span; /// Methods of this trait signifies a point where CTFE evaluation would fail /// and some use case dependent behaviour can instead be applied @@ -20,16 +21,20 @@ pub trait Machine<'tcx>: Sized { /// Additional data that can be accessed via the Memory type MemoryData; - /// Called when a function's MIR is not found. - /// This will happen for `extern "C"` functions. - fn call_missing_fn<'a>( + /// Entry point to all function calls. + /// + /// Returns Ok(true) when the function was handled completely + /// e.g. due to missing mir or + /// + /// Returns Ok(false) if a new stack frame was pushed + fn eval_fn_call<'a>( ecx: &mut EvalContext<'a, 'tcx, Self>, instance: ty::Instance<'tcx>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, arg_operands: &[mir::Operand<'tcx>], + span: Span, sig: ty::FnSig<'tcx>, - path: String, - ) -> EvalResult<'tcx>; + ) -> EvalResult<'tcx, bool>; /// Called when operating on the value of pointers. /// @@ -45,10 +50,5 @@ pub trait Machine<'tcx>: Sized { right: PrimVal, right_ty: ty::Ty<'tcx>, ) -> EvalResult<'tcx, Option<(PrimVal, bool)>>; - - /// Called when adding a frame for a function that's not `const fn` - /// - /// Const eval returns `Err`, miri returns `Ok` - fn check_non_const_fn_call(instance: ty::Instance<'tcx>) -> EvalResult<'tcx>; } diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index 779ab8c9874..21e59e9d456 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -6,7 +6,7 @@ use syntax::abi::Abi; use super::{ EvalError, EvalResult, - EvalContext, StackPopCleanup, eval_context, TyAndPacked, + EvalContext, eval_context, TyAndPacked, Lvalue, MemoryPointer, PrimVal, Value, @@ -233,7 +233,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - if self.eval_fn_call_inner( + if M::eval_fn_call( + self, instance, destination, arg_operands, @@ -276,7 +277,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } // Push the stack frame, and potentially be entirely done if the call got hooked - if self.eval_fn_call_inner( + if M::eval_fn_call( + self, instance, destination, arg_operands, @@ -369,7 +371,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - if self.eval_fn_call_inner( + if M::eval_fn_call( + self, instance, destination, arg_operands, @@ -416,48 +419,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } - /// Returns Ok(true) when the function was handled completely due to mir not being available - fn eval_fn_call_inner( - &mut self, - instance: ty::Instance<'tcx>, - destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, - arg_operands: &[mir::Operand<'tcx>], - span: Span, - sig: ty::FnSig<'tcx>, - ) -> EvalResult<'tcx, bool> { - trace!("eval_fn_call_inner: {:#?}, {:#?}", instance, destination); - - // Only trait methods can have a Self parameter. - - let mir = match self.load_mir(instance.def) { - Ok(mir) => mir, - Err(EvalError::NoMirFor(path)) => { - M::call_missing_fn(self, instance, destination, arg_operands, sig, path)?; - return Ok(true); - }, - Err(other) => return Err(other), - }; - - if !self.tcx.is_const_fn(instance.def_id()) { - M::check_non_const_fn_call(instance)?; - } - - let (return_lvalue, return_to_block) = match destination { - Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)), - None => (Lvalue::undef(), StackPopCleanup::None), - }; - - self.push_stack_frame( - instance, - span, - mir, - return_lvalue, - return_to_block, - )?; - - Ok(false) - } - pub fn read_discriminant_value(&self, adt_ptr: MemoryPointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { use rustc::ty::layout::Layout::*; let adt_layout = self.type_layout(adt_ty)?; |
