diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6ba465cb5990..7bc70b08cc76 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1093,12 +1093,20 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { use rustc::util::ppaux; use std::fmt; struct Instance<'tcx>(DefId, &'tcx subst::Substs<'tcx>); + impl<'tcx> ::std::panic::UnwindSafe for Instance<'tcx> {} + impl<'tcx> ::std::panic::RefUnwindSafe for Instance<'tcx> {} impl<'tcx> fmt::Display for Instance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[]) } } - err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); + let inst = Instance(def_id, substs); + match ::std::panic::catch_unwind(|| { + format!("inside call to {}", inst) + }) { + Ok(msg) => err.span_note(span, &msg), + Err(_) => err.span_note(span, &format!("ppaux::parameterized failed: {:?}, {:?}", def_id, substs)), + }; } err.emit(); } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 23396e0694e1..d8edad79f1eb 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -182,29 +182,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Only trait methods can have a Self parameter. let (resolved_def_id, resolved_substs) = if let Some(trait_id) = self.tcx.trait_of_item(def_id) { - self.trait_method(trait_id, def_id, substs, arg_srcs.get_mut(0))? + self.trait_method(trait_id, def_id, substs, &mut arg_srcs)? } else { (def_id, substs) }; - if fn_ty.abi == Abi::RustCall && !args.is_empty() { - arg_srcs.pop(); - let last_arg = args.last().unwrap(); - let last = self.eval_operand(last_arg)?; - let last_ty = self.operand_ty(last_arg); - let last_layout = self.type_layout(last_ty); - match (&last_ty.sty, last_layout) { - (&ty::TyTuple(fields), - &Layout::Univariant { ref variant, .. }) => { - let offsets = iter::once(0) - .chain(variant.offset_after_field.iter() - .map(|s| s.bytes())); - for (offset, ty) in offsets.zip(fields) { - let src = last.offset(offset as isize); - arg_srcs.push((src, ty)); + if fn_ty.abi == Abi::RustCall { + if let Some((last, last_ty)) = arg_srcs.pop() { + let last_layout = self.type_layout(last_ty); + match (&last_ty.sty, last_layout) { + (&ty::TyTuple(fields), + &Layout::Univariant { ref variant, .. }) => { + let offsets = iter::once(0) + .chain(variant.offset_after_field.iter() + .map(|s| s.bytes())); + for (offset, ty) in offsets.zip(fields) { + let src = last.offset(offset as isize); + arg_srcs.push((src, ty)); + } } + ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), } - ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), } } @@ -300,6 +298,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; } + "ctpop" => { + let elem_ty = substs.type_at(0); + let elem_size = self.type_size(elem_ty); + let num = self.memory.read_uint(args_ptrs[0], elem_size)?.count_ones(); + self.memory.write_uint(dest, num.into(), elem_size)?; + } + + "ctlz" => { + let elem_ty = substs.type_at(0); + let elem_size = self.type_size(elem_ty); + let num = self.memory.read_uint(args_ptrs[0], elem_size)?.leading_zeros(); + self.memory.write_uint(dest, num.into(), elem_size)?; + } + "discriminant_value" => { let ty = substs.type_at(0); let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; @@ -495,7 +507,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trait_id: DefId, def_id: DefId, substs: &'tcx Substs<'tcx>, - first_arg: Option<&mut (Pointer, Ty<'tcx>)>, + args: &mut Vec<(Pointer, Ty<'tcx>)>, ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>)> { let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs); let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref)); @@ -514,23 +526,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableClosure(vtable_closure) => Ok((vtable_closure.closure_def_id, vtable_closure.substs.func_substs)), - traits::VtableFnPointer(_fn_ty) => { - let _trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); - unimplemented!() - // let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty); - - // let method_ty = def_ty(tcx, def_id, substs); - // let fn_ptr_ty = match method_ty.sty { - // ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)), - // _ => unreachable!("expected fn item type, found {}", - // method_ty) - // }; - // Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) + traits::VtableFnPointer(vtable_fn_ptr) => { + if let ty::TyFnDef(did, ref substs, _) = vtable_fn_ptr.fn_ty.sty { + args.remove(0); + Ok((did, substs)) + } else { + bug!("VtableFnPointer did not contain a concrete function: {:?}", vtable_fn_ptr) + } } traits::VtableObject(ref data) => { let idx = self.tcx.get_vtable_index_of_object_method(data, def_id); - if let Some(&mut(first_arg, ref mut first_ty)) = first_arg { + if let Some(&mut(first_arg, ref mut first_ty)) = args.get_mut(0) { let (_, vtable) = self.get_fat_ptr(first_arg); let vtable = self.memory.read_ptr(vtable)?; let idx = idx + 3; diff --git a/tests/compiletest.rs b/tests/compiletest.rs index a401257c6ae7..bc112f406854 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -22,6 +22,8 @@ fn run_pass() { let mut config = compiletest::default_config(); config.mode = "run-pass".parse().expect("Invalid mode"); config.src_base = PathBuf::from("tests/run-pass".to_string()); + config.target_rustcflags = Some("-Dwarnings".to_string()); + config.host_rustcflags = Some("-Dwarnings".to_string()); compiletest::run_tests(&config); } @@ -55,7 +57,17 @@ fn compile_test() { compile_fail(&sysroot); run_pass(); for_all_targets(&sysroot, |target| { - for file in std::fs::read_dir("tests/run-pass").unwrap() { + let files = std::fs::read_dir("tests/run-pass").unwrap(); + let files: Box> = if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { + Box::new(files.chain(std::fs::read_dir(path).unwrap())) + } else { + Box::new(files) + }; + let mut mir_not_found = 0; + let mut crate_not_found = 0; + let mut success = 0; + let mut failed = 0; + for file in files { let file = file.unwrap(); let path = file.path(); @@ -67,7 +79,6 @@ fn compile_test() { write!(stderr.lock(), "test [miri-pass] {} ... ", path.display()).unwrap(); let mut cmd = std::process::Command::new("target/debug/miri"); cmd.arg(path); - cmd.arg("-Dwarnings"); cmd.arg(format!("--target={}", target)); let libs = Path::new(&sysroot).join("lib"); let sysroot = libs.join("rustlib").join(&target).join("lib"); @@ -75,20 +86,34 @@ fn compile_test() { cmd.env(compiletest::procsrv::dylib_env_var(), paths); match cmd.output() { - Ok(ref output) if output.status.success() => writeln!(stderr.lock(), "ok").unwrap(), + Ok(ref output) if output.status.success() => { + success += 1; + writeln!(stderr.lock(), "ok").unwrap() + }, Ok(output) => { - writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); - writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); - writeln!(stderr.lock(), "stderr: \n {}", std::str::from_utf8(&output.stderr).unwrap()).unwrap(); - panic!("some tests failed"); + let output_err = std::str::from_utf8(&output.stderr).unwrap(); + if let Some(text) = output_err.splitn(2, "thread 'main' panicked at 'no mir for `").nth(1) { + mir_not_found += 1; + let end = text.find('`').unwrap(); + writeln!(stderr.lock(), "NO MIR FOR `{}`", &text[..end]).unwrap(); + } else if let Some(text) = output_err.splitn(2, "can't find crate for `").nth(1) { + crate_not_found += 1; + let end = text.find('`').unwrap(); + writeln!(stderr.lock(), "CAN'T FIND CRATE FOR `{}`", &text[..end]).unwrap(); + } else { + failed += 1; + writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); + writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); + writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap(); + } } Err(e) => { writeln!(stderr.lock(), "FAILED: {}", e).unwrap(); - panic!("some tests failed"); + panic!("failed to execute miri"); }, } } let stderr = std::io::stderr(); - writeln!(stderr.lock(), "").unwrap(); + writeln!(stderr.lock(), "{} success, {} mir not found, {} crate not found, {} failed", success, mir_not_found, crate_not_found, failed).unwrap(); }); } diff --git a/tests/run-pass/function_pointers.rs b/tests/run-pass/function_pointers.rs index 2e75a5a3ea2a..e7368004069f 100644 --- a/tests/run-pass/function_pointers.rs +++ b/tests/run-pass/function_pointers.rs @@ -2,6 +2,10 @@ fn f() -> i32 { 42 } +fn g(i: i32) -> i32 { + i*42 +} + fn return_fn_ptr() -> fn() -> i32 { f } @@ -10,8 +14,22 @@ fn call_fn_ptr() -> i32 { return_fn_ptr()() } +fn indirect i32>(f: F) -> i32 { f() } +fn indirect_mut i32>(mut f: F) -> i32 { f() } +fn indirect_once i32>(f: F) -> i32 { f() } + +fn indirect2 i32>(f: F) -> i32 { f(10) } +fn indirect_mut2 i32>(mut f: F) -> i32 { f(10) } +fn indirect_once2 i32>(f: F) -> i32 { f(10) } + fn main() { assert_eq!(call_fn_ptr(), 42); + assert_eq!(indirect(f), 42); + assert_eq!(indirect_mut(f), 42); + assert_eq!(indirect_once(f), 42); + assert_eq!(indirect2(g), 420); + assert_eq!(indirect_mut2(g), 420); + assert_eq!(indirect_once2(g), 420); assert!(return_fn_ptr() == f); assert!(return_fn_ptr() as unsafe fn() -> i32 == f as fn() -> i32 as unsafe fn() -> i32); }