- rustup toolchain uninstall beta
- rustup update
# Install "master" toolchain
- - cargo install rustup-toolchain-install-master & exit 0
+ - cargo install rustup-toolchain-install-master
# We need to install cargo here as well or else the DLL search path inside `cargo run`
# will be for the wrong toolchain. (On Unix, `./miri` takes care of this, but not here.)
- rustup-toolchain-install-master -f -n master %RUSTC_HASH% -c rust-src -c rustc-dev -c cargo
- rustup toolchain uninstall beta
- rustup update
# Install "master" toolchain
-- cargo install rustup-toolchain-install-master || echo "rustup-toolchain-install-master already installed"
+- cargo install rustup-toolchain-install-master
- travis_retry rustup-toolchain-install-master -f -n master $RUSTC_HASH -c rust-src -c rustc-dev
- rustup default master
- rustc --version
## Using Miri
-Install Miri via `rustup`:
+Install Miri on Rust nightly via `rustup`:
```sh
-rustup component add miri
+rustup +nightly component add miri
```
If `rustup` says the `miri` component is unavailable, that's because not all
-6250d56355d72264ece721e8d0dc95b16a6824b1
+b5e21dbb5cabdaaadc47a4d8e3f59979dcad2871
// different values into a struct.
fn write_packed_immediates(
&mut self,
- place: &MPlaceTy<'tcx, Tag>,
+ place: MPlaceTy<'tcx, Tag>,
imms: &[ImmTy<'tcx, Tag>],
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}
+ "fstat$INODE64" => {
+ let result = this.fstat(args[0], args[1])?;
+ this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
+ }
+
"clock_gettime" => {
let result = this.clock_gettime(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
this.write_null(dest)?;
}
+ "posix_fadvise" => {
+ // fadvise is only informational, we can ignore it.
+ this.write_null(dest)?;
+ }
+
"mmap" => {
// This is a horrible hack, but since the guard page mechanism calls mmap and expects a particular return value, we just give it that value.
let addr = this.read_scalar(args[0])?.not_undef()?;
this.stat_or_lstat(false, path_op, buf_op)
}
+ fn fstat(
+ &mut self,
+ fd_op: OpTy<'tcx, Tag>,
+ buf_op: OpTy<'tcx, Tag>,
+ ) -> InterpResult<'tcx, i32> {
+ let this = self.eval_context_mut();
+
+ this.check_no_isolation("fstat")?;
+
+ if this.tcx.sess.target.target.target_os.to_lowercase() != "macos" {
+ throw_unsup_format!("The `fstat` shim is only available for `macos` targets.")
+ }
+
+ let fd = this.read_scalar(fd_op)?.to_i32()?;
+
+ let metadata = match FileMetadata::from_fd(this, fd)? {
+ Some(metadata) => metadata,
+ None => return Ok(-1),
+ };
+ stat_macos_write_buf(this, metadata, buf_op)
+ }
+
fn stat_or_lstat(
&mut self,
follow_symlink: bool,
let path_scalar = this.read_scalar(path_op)?.not_undef()?;
let path: PathBuf = this.read_os_str_from_c_str(path_scalar)?.into();
- let buf = this.deref_operand(buf_op)?;
-
- let metadata = match FileMetadata::new(this, path, follow_symlink)? {
+ let metadata = match FileMetadata::from_path(this, path, follow_symlink)? {
Some(metadata) => metadata,
None => return Ok(-1),
};
-
- let mode: u16 = metadata.mode.to_u16()?;
-
- let (access_sec, access_nsec) = metadata.accessed.unwrap_or((0, 0));
- let (created_sec, created_nsec) = metadata.created.unwrap_or((0, 0));
- let (modified_sec, modified_nsec) = metadata.modified.unwrap_or((0, 0));
-
- let dev_t_layout = this.libc_ty_layout("dev_t")?;
- let mode_t_layout = this.libc_ty_layout("mode_t")?;
- let nlink_t_layout = this.libc_ty_layout("nlink_t")?;
- let ino_t_layout = this.libc_ty_layout("ino_t")?;
- let uid_t_layout = this.libc_ty_layout("uid_t")?;
- let gid_t_layout = this.libc_ty_layout("gid_t")?;
- let time_t_layout = this.libc_ty_layout("time_t")?;
- let long_layout = this.libc_ty_layout("c_long")?;
- let off_t_layout = this.libc_ty_layout("off_t")?;
- let blkcnt_t_layout = this.libc_ty_layout("blkcnt_t")?;
- let blksize_t_layout = this.libc_ty_layout("blksize_t")?;
- let uint32_t_layout = this.libc_ty_layout("uint32_t")?;
-
- // We need to add 32 bits of padding after `st_rdev` if we are on a 64-bit platform.
- let pad_layout = if this.tcx.sess.target.ptr_width == 64 {
- uint32_t_layout
- } else {
- this.layout_of(this.tcx.mk_unit())?
- };
-
- let imms = [
- immty_from_uint_checked(0u128, dev_t_layout)?, // st_dev
- immty_from_uint_checked(mode, mode_t_layout)?, // st_mode
- immty_from_uint_checked(0u128, nlink_t_layout)?, // st_nlink
- immty_from_uint_checked(0u128, ino_t_layout)?, // st_ino
- immty_from_uint_checked(0u128, uid_t_layout)?, // st_uid
- immty_from_uint_checked(0u128, gid_t_layout)?, // st_gid
- immty_from_uint_checked(0u128, dev_t_layout)?, // st_rdev
- immty_from_uint_checked(0u128, pad_layout)?, // padding for 64-bit targets
- immty_from_uint_checked(access_sec, time_t_layout)?, // st_atime
- immty_from_uint_checked(access_nsec, long_layout)?, // st_atime_nsec
- immty_from_uint_checked(modified_sec, time_t_layout)?, // st_mtime
- immty_from_uint_checked(modified_nsec, long_layout)?, // st_mtime_nsec
- immty_from_uint_checked(0u128, time_t_layout)?, // st_ctime
- immty_from_uint_checked(0u128, long_layout)?, // st_ctime_nsec
- immty_from_uint_checked(created_sec, time_t_layout)?, // st_birthtime
- immty_from_uint_checked(created_nsec, long_layout)?, // st_birthtime_nsec
- immty_from_uint_checked(metadata.size, off_t_layout)?, // st_size
- immty_from_uint_checked(0u128, blkcnt_t_layout)?, // st_blocks
- immty_from_uint_checked(0u128, blksize_t_layout)?, // st_blksize
- immty_from_uint_checked(0u128, uint32_t_layout)?, // st_flags
- immty_from_uint_checked(0u128, uint32_t_layout)?, // st_gen
- ];
-
- this.write_packed_immediates(&buf, &imms)?;
-
- Ok(0)
+ stat_macos_write_buf(this, metadata, buf_op)
}
fn statx(
this.read_scalar(flags_op)?.to_machine_isize(&*this.tcx)?.try_into().map_err(|e| {
err_unsup_format!("Failed to convert pointer sized operand to integer: {}", e)
})?;
+ let empty_path_flag = flags & this.eval_libc("AT_EMPTY_PATH")?.to_i32()? != 0;
// `dirfd` should be a `c_int` but the `syscall` function provides an `isize`.
let dirfd: i32 =
this.read_scalar(dirfd_op)?.to_machine_isize(&*this.tcx)?.try_into().map_err(|e| {
err_unsup_format!("Failed to convert pointer sized operand to integer: {}", e)
})?;
- // we only support interpreting `path` as an absolute directory or as a directory relative
- // to `dirfd` when the latter is `AT_FDCWD`. The behavior of `statx` with a relative path
- // and a directory file descriptor other than `AT_FDCWD` is specified but it cannot be
- // tested from `libstd`. If you found this error, please open an issue reporting it.
- if !(path.is_absolute() || dirfd == this.eval_libc_i32("AT_FDCWD")?) {
+ // We only support:
+ // * interpreting `path` as an absolute directory,
+ // * interpreting `path` as a path relative to `dirfd` when the latter is `AT_FDCWD`, or
+ // * interpreting `dirfd` as any file descriptor when `path` is empty and AT_EMPTY_PATH is
+ // set.
+ // Other behaviors cannot be tested from `libstd` and thus are not implemented. If you
+ // found this error, please open an issue reporting it.
+ if !(
+ path.is_absolute() ||
+ dirfd == this.eval_libc_i32("AT_FDCWD")? ||
+ (path.as_os_str().is_empty() && empty_path_flag)
+ ) {
throw_unsup_format!(
- "Using statx with a relative path and a file descriptor different from `AT_FDCWD` is not supported"
+ "Using statx is only supported with absolute paths, relative paths with the file \
+ descriptor `AT_FDCWD`, and empty paths with the `AT_EMPTY_PATH` flag set and any \
+ file descriptor"
)
}
// symbolic links.
let follow_symlink = flags & this.eval_libc("AT_SYMLINK_NOFOLLOW")?.to_i32()? == 0;
- let metadata = match FileMetadata::new(this, path, follow_symlink)? {
+ // If the path is empty, and the AT_EMPTY_PATH flag is set, we query the open file
+ // represented by dirfd, whether it's a directory or otherwise.
+ let metadata = if path.as_os_str().is_empty() && empty_path_flag {
+ FileMetadata::from_fd(this, dirfd)?
+ } else {
+ FileMetadata::from_path(this, path, follow_symlink)?
+ };
+ let metadata = match metadata {
Some(metadata) => metadata,
None => return Ok(-1),
};
immty_from_uint_checked(0u128, __u64_layout)?, // stx_dev_minor
];
- this.write_packed_immediates(&statxbuf_place, &imms)?;
+ this.write_packed_immediates(statxbuf_place, &imms)?;
Ok(0)
}
}
impl FileMetadata {
- fn new<'tcx, 'mir>(
+ fn from_path<'tcx, 'mir>(
ecx: &mut MiriEvalContext<'mir, 'tcx>,
path: PathBuf,
follow_symlink: bool
std::fs::symlink_metadata(path)
};
+ FileMetadata::from_meta(ecx, metadata)
+ }
+
+ fn from_fd<'tcx, 'mir>(
+ ecx: &mut MiriEvalContext<'mir, 'tcx>,
+ fd: i32,
+ ) -> InterpResult<'tcx, Option<FileMetadata>> {
+ let option = ecx.machine.file_handler.handles.get(&fd);
+ let handle = match option {
+ Some(handle) => handle,
+ None => return ecx.handle_not_found().map(|_: i32| None),
+ };
+ let metadata = handle.file.metadata();
+
+ FileMetadata::from_meta(ecx, metadata)
+ }
+
+ fn from_meta<'tcx, 'mir>(
+ ecx: &mut MiriEvalContext<'mir, 'tcx>,
+ metadata: Result<std::fs::Metadata, std::io::Error>,
+ ) -> InterpResult<'tcx, Option<FileMetadata>> {
let metadata = match metadata {
Ok(metadata) => metadata,
Err(e) => {
Ok(Some(FileMetadata { mode, size, created, accessed, modified }))
}
}
+
+fn stat_macos_write_buf<'tcx, 'mir>(
+ ecx: &mut MiriEvalContext<'mir, 'tcx>,
+ metadata: FileMetadata,
+ buf_op: OpTy<'tcx, Tag>,
+) -> InterpResult<'tcx, i32> {
+ let mode: u16 = metadata.mode.to_u16()?;
+
+ let (access_sec, access_nsec) = metadata.accessed.unwrap_or((0, 0));
+ let (created_sec, created_nsec) = metadata.created.unwrap_or((0, 0));
+ let (modified_sec, modified_nsec) = metadata.modified.unwrap_or((0, 0));
+
+ let dev_t_layout = ecx.libc_ty_layout("dev_t")?;
+ let mode_t_layout = ecx.libc_ty_layout("mode_t")?;
+ let nlink_t_layout = ecx.libc_ty_layout("nlink_t")?;
+ let ino_t_layout = ecx.libc_ty_layout("ino_t")?;
+ let uid_t_layout = ecx.libc_ty_layout("uid_t")?;
+ let gid_t_layout = ecx.libc_ty_layout("gid_t")?;
+ let time_t_layout = ecx.libc_ty_layout("time_t")?;
+ let long_layout = ecx.libc_ty_layout("c_long")?;
+ let off_t_layout = ecx.libc_ty_layout("off_t")?;
+ let blkcnt_t_layout = ecx.libc_ty_layout("blkcnt_t")?;
+ let blksize_t_layout = ecx.libc_ty_layout("blksize_t")?;
+ let uint32_t_layout = ecx.libc_ty_layout("uint32_t")?;
+
+ // We need to add 32 bits of padding after `st_rdev` if we are on a 64-bit platform.
+ let pad_layout = if ecx.tcx.sess.target.ptr_width == 64 {
+ uint32_t_layout
+ } else {
+ ecx.layout_of(ecx.tcx.mk_unit())?
+ };
+
+ let imms = [
+ immty_from_uint_checked(0u128, dev_t_layout)?, // st_dev
+ immty_from_uint_checked(mode, mode_t_layout)?, // st_mode
+ immty_from_uint_checked(0u128, nlink_t_layout)?, // st_nlink
+ immty_from_uint_checked(0u128, ino_t_layout)?, // st_ino
+ immty_from_uint_checked(0u128, uid_t_layout)?, // st_uid
+ immty_from_uint_checked(0u128, gid_t_layout)?, // st_gid
+ immty_from_uint_checked(0u128, dev_t_layout)?, // st_rdev
+ immty_from_uint_checked(0u128, pad_layout)?, // padding for 64-bit targets
+ immty_from_uint_checked(access_sec, time_t_layout)?, // st_atime
+ immty_from_uint_checked(access_nsec, long_layout)?, // st_atime_nsec
+ immty_from_uint_checked(modified_sec, time_t_layout)?, // st_mtime
+ immty_from_uint_checked(modified_nsec, long_layout)?, // st_mtime_nsec
+ immty_from_uint_checked(0u128, time_t_layout)?, // st_ctime
+ immty_from_uint_checked(0u128, long_layout)?, // st_ctime_nsec
+ immty_from_uint_checked(created_sec, time_t_layout)?, // st_birthtime
+ immty_from_uint_checked(created_nsec, long_layout)?, // st_birthtime_nsec
+ immty_from_uint_checked(metadata.size, off_t_layout)?, // st_size
+ immty_from_uint_checked(0u128, blkcnt_t_layout)?, // st_blocks
+ immty_from_uint_checked(0u128, blksize_t_layout)?, // st_blksize
+ immty_from_uint_checked(0u128, uint32_t_layout)?, // st_flags
+ immty_from_uint_checked(0u128, uint32_t_layout)?, // st_gen
+ ];
+
+ let buf = ecx.deref_operand(buf_op)?;
+ ecx.write_packed_immediates(buf, &imms)?;
+
+ Ok(0)
+}
this.write_scalar(Scalar::from_uint(align.bytes(), ptr_size), dest)?;
}
- "unchecked_div" => {
- let l = this.read_immediate(args[0])?;
- let r = this.read_immediate(args[1])?;
- let rval = r.to_scalar()?.to_bits(args[1].layout.size)?;
- if rval == 0 {
- throw_ub_format!("Division by 0 in unchecked_div");
- }
- this.binop_ignore_overflow(mir::BinOp::Div, l, r, dest)?;
- }
-
- "unchecked_rem" => {
- let l = this.read_immediate(args[0])?;
- let r = this.read_immediate(args[1])?;
- let rval = r.to_scalar()?.to_bits(args[1].layout.size)?;
- if rval == 0 {
- throw_ub_format!("Division by 0 in unchecked_rem");
- }
- this.binop_ignore_overflow(mir::BinOp::Rem, l, r, dest)?;
- }
-
- #[rustfmt::skip]
- | "unchecked_add"
- | "unchecked_sub"
- | "unchecked_mul"
- => {
- let l = this.read_immediate(args[0])?;
- let r = this.read_immediate(args[1])?;
- let op = match intrinsic_name {
- "unchecked_add" => mir::BinOp::Add,
- "unchecked_sub" => mir::BinOp::Sub,
- "unchecked_mul" => mir::BinOp::Mul,
- _ => bug!(),
- };
- let (res, overflowed, _ty) = this.overflowing_binary_op(op, l, r)?;
- if overflowed {
- throw_ub_format!("Overflowing arithmetic in {}", intrinsic_name);
- }
- this.write_scalar(res, dest)?;
- }
-
"uninit" => {
// Check fast path: we don't want to force an allocation in case the destination is a simple value,
// but we also do not want to create a new allocation with 0s and then copy that over.
immty_from_int_checked(tv_nsec, this.libc_ty_layout("c_long")?)?,
];
- this.write_packed_immediates(&tp, &imms)?;
+ this.write_packed_immediates(tp, &imms)?;
Ok(0)
}
immty_from_int_checked(tv_usec, this.libc_ty_layout("suseconds_t")?)?,
];
- this.write_packed_immediates(&tv, &imms)?;
+ this.write_packed_immediates(tv, &imms)?;
Ok(0)
}
extern crate alloc;
use alloc::alloc::Global;
-use std::alloc::*;
+use std::alloc::{AllocRef, Layout};
// error-pattern: incorrect alloc info: expected size 1 and align 2, got size 1 and align 1
extern crate alloc;
use alloc::alloc::Global;
-use std::alloc::*;
+use std::alloc::{AllocRef, Layout};
// error-pattern: incorrect alloc info: expected size 2 and align 1, got size 1 and align 1
extern crate alloc;
use alloc::alloc::Global;
-use std::alloc::*;
+use std::alloc::{AllocRef, Layout};
// error-pattern: tried to deallocate dangling pointer
use std::intrinsics::*;
-//error-pattern: Division by 0 in unchecked_div
-
fn main() {
unsafe {
- let _n = unchecked_div(1i64, 0);
+ let _n = unchecked_div(1i64, 0); //~ERROR dividing by zero
}
}
--- /dev/null
+#![feature(core_intrinsics)]
+
+use std::intrinsics::*;
+
+fn main() {
+ unsafe {
+ let _n = unchecked_rem(3u32, 0); //~ ERROR calculating the remainder with a divisor of zero
+ }
+}
+++ /dev/null
-#![feature(core_intrinsics)]
-
-use std::intrinsics::*;
-
-//error-pattern: Division by 0 in unchecked_rem
-
-fn main() {
- unsafe {
- let _n = unchecked_rem(3u32, 0);
- }
-}
fn next(&mut self) -> Option<Self::Item> {
let me = unsafe { Pin::new_unchecked(&mut self.0) };
- match me.resume() {
+ match me.resume(()) {
GeneratorState::Yielded(x) => Some(x),
GeneratorState::Complete(_) => None,
}
extern crate alloc;
use alloc::alloc::Global;
-use std::alloc::*;
+use std::alloc::{AllocRef, Layout};
// error-pattern: incorrect alloc info: expected size 2 and align 1, got size 1 and align 1
extern crate alloc;
use alloc::alloc::Global;
-use std::alloc::*;
+use std::alloc::{AllocRef, Layout};
fn main() {
unsafe {
extern crate alloc;
use alloc::alloc::Global;
-use std::alloc::*;
+use std::alloc::{AllocRef, Layout};
// error-pattern: dangling pointer was dereferenced
--- /dev/null
+// Make sure that creating a raw ptr next to a shared ref works
+// but the shared ref still gets invalidated when the raw ptr is used for writing.
+
+fn main() { unsafe {
+ use std::mem;
+ let x = &mut 0;
+ let y1: &i32 = mem::transmute(&*x); // launder lifetimes
+ let y2 = x as *mut _;
+ let _val = *y2;
+ let _val = *y1;
+ *y2 += 1;
+ let _fail = *y1; //~ ERROR borrow stack
+} }
#![feature(core_intrinsics)]
fn main() {
// MAX overflow
- unsafe { std::intrinsics::unchecked_add(40000u16, 30000); } //~ ERROR Overflowing arithmetic in unchecked_add
+ unsafe { std::intrinsics::unchecked_add(40000u16, 30000); } //~ ERROR Overflow executing `unchecked_add`
}
#![feature(core_intrinsics)]
fn main() {
// MIN overflow
- unsafe { std::intrinsics::unchecked_add(-30000i16, -8000); } //~ ERROR Overflowing arithmetic in unchecked_add
+ unsafe { std::intrinsics::unchecked_add(-30000i16, -8000); } //~ ERROR Overflow executing `unchecked_add`
}
--- /dev/null
+#![feature(core_intrinsics)]
+fn main() {
+ // MIN/-1 cannot be represented
+ unsafe { std::intrinsics::unchecked_div(i16::min_value(), -1); } //~ ERROR Overflow executing `unchecked_div`
+}
#![feature(core_intrinsics)]
fn main() {
// MAX overflow
- unsafe { std::intrinsics::unchecked_mul(300u16, 250u16); } //~ ERROR Overflowing arithmetic in unchecked_mul
+ unsafe { std::intrinsics::unchecked_mul(300u16, 250u16); } //~ ERROR Overflow executing `unchecked_mul`
}
#![feature(core_intrinsics)]
fn main() {
// MIN overflow
- unsafe { std::intrinsics::unchecked_mul(1_000_000_000i32, -4); } //~ ERROR Overflowing arithmetic in unchecked_mul
+ unsafe { std::intrinsics::unchecked_mul(1_000_000_000i32, -4); } //~ ERROR Overflow executing `unchecked_mul`
}
#![feature(core_intrinsics)]
fn main() {
// MIN overflow
- unsafe { std::intrinsics::unchecked_sub(14u32, 22); } //~ ERROR Overflowing arithmetic in unchecked_sub
+ unsafe { std::intrinsics::unchecked_sub(14u32, 22); } //~ ERROR Overflow executing `unchecked_sub`
}
#![feature(core_intrinsics)]
fn main() {
// MAX overflow
- unsafe { std::intrinsics::unchecked_sub(30000i16, -7000); } //~ ERROR Overflowing arithmetic in unchecked_sub
+ unsafe { std::intrinsics::unchecked_sub(30000i16, -7000); } //~ ERROR Overflow executing `unchecked_sub`
}
let mut file = File::create(&path).unwrap();
// Writing 0 bytes should not change the file contents.
file.write(&mut []).unwrap();
+ assert_eq!(file.metadata().unwrap().len(), 0);
file.write(bytes).unwrap();
+ assert_eq!(file.metadata().unwrap().len(), bytes.len() as u64);
// Test opening, reading and closing a file.
let mut file = File::open(&path).unwrap();
let mut contents = Vec::new();
#![feature(generators, generator_trait, never_type)]
-use std::ops::{GeneratorState, Generator};
+use std::ops::{GeneratorState::{self, *}, Generator};
use std::pin::Pin;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::fmt::Debug;
-fn finish<T>(mut amt: usize, mut t: T) -> T::Return
- where T: Generator<Yield = usize>
-{
- // We are not moving the `t` around until it gets dropped, so this is okay.
- let mut t = unsafe { Pin::new_unchecked(&mut t) };
- loop {
- match t.as_mut().resume() {
- GeneratorState::Yielded(y) => amt -= y,
- GeneratorState::Complete(ret) => {
- assert_eq!(amt, 0);
- return ret
+fn basic() {
+ fn finish<T>(mut amt: usize, mut t: T) -> T::Return
+ where T: Generator<Yield = usize>
+ {
+ // We are not moving the `t` around until it gets dropped, so this is okay.
+ let mut t = unsafe { Pin::new_unchecked(&mut t) };
+ loop {
+ match t.as_mut().resume(()) {
+ GeneratorState::Yielded(y) => amt -= y,
+ GeneratorState::Complete(ret) => {
+ assert_eq!(amt, 0);
+ return ret
+ }
}
}
}
-}
-enum Never {}
-fn never() -> Never {
- panic!()
-}
+ enum Never {}
+ fn never() -> Never {
+ panic!()
+ }
-fn main() {
finish(1, || yield 1);
finish(3, || {
let _x: (String, !) = (String::new(), { yield 2; return });
});
}
+
+fn smoke_resume_arg() {
+ fn drain<G: Generator<R, Yield = Y> + Unpin, R, Y>(
+ gen: &mut G,
+ inout: Vec<(R, GeneratorState<Y, G::Return>)>,
+ ) where
+ Y: Debug + PartialEq,
+ G::Return: Debug + PartialEq,
+ {
+ let mut gen = Pin::new(gen);
+
+ for (input, out) in inout {
+ assert_eq!(gen.as_mut().resume(input), out);
+ }
+ }
+
+ static DROPS: AtomicUsize = AtomicUsize::new(0);
+
+ #[derive(Debug, PartialEq)]
+ struct DropMe;
+
+ impl Drop for DropMe {
+ fn drop(&mut self) {
+ DROPS.fetch_add(1, Ordering::SeqCst);
+ }
+ }
+
+ fn expect_drops<T>(expected_drops: usize, f: impl FnOnce() -> T) -> T {
+ DROPS.store(0, Ordering::SeqCst);
+
+ let res = f();
+
+ let actual_drops = DROPS.load(Ordering::SeqCst);
+ assert_eq!(actual_drops, expected_drops);
+ res
+ }
+
+ drain(
+ &mut |mut b| {
+ while b != 0 {
+ b = yield (b + 1);
+ }
+ -1
+ },
+ vec![(1, Yielded(2)), (-45, Yielded(-44)), (500, Yielded(501)), (0, Complete(-1))],
+ );
+
+ expect_drops(2, || drain(&mut |a| yield a, vec![(DropMe, Yielded(DropMe))]));
+
+ expect_drops(6, || {
+ drain(
+ &mut |a| yield yield a,
+ vec![(DropMe, Yielded(DropMe)), (DropMe, Yielded(DropMe)), (DropMe, Complete(DropMe))],
+ )
+ });
+
+ #[allow(unreachable_code)]
+ expect_drops(2, || drain(&mut |a| yield return a, vec![(DropMe, Complete(DropMe))]));
+
+ expect_drops(2, || {
+ drain(
+ &mut |a: DropMe| {
+ if false { yield () } else { a }
+ },
+ vec![(DropMe, Complete(DropMe))],
+ )
+ });
+
+ expect_drops(4, || {
+ drain(
+ #[allow(unused_assignments, unused_variables)]
+ &mut |mut a: DropMe| {
+ a = yield;
+ a = yield;
+ a = yield;
+ },
+ vec![
+ (DropMe, Yielded(())),
+ (DropMe, Yielded(())),
+ (DropMe, Yielded(())),
+ (DropMe, Complete(())),
+ ],
+ )
+ });
+}
+
+fn main() {
+ basic();
+ smoke_resume_arg();
+}
#![feature(allocator_api)]
use std::ptr::NonNull;
-use std::alloc::{Global, Alloc, Layout, System};
+use std::alloc::{Global, AllocRef, Layout, System};
use std::slice;
-fn check_alloc<T: Alloc>(mut allocator: T) { unsafe {
+fn check_alloc<T: AllocRef>(mut allocator: T) { unsafe {
for &align in &[4, 8, 16, 32] {
let layout = Layout::from_size_align(20, align).unwrap();
}
} }
-fn check_align_requests<T: Alloc>(mut allocator: T) {
+fn check_align_requests<T: AllocRef>(mut allocator: T) {
for &size in &[2, 8, 64] { // size less than and bigger than alignment
for &align in &[4, 8, 16, 32] { // Be sure to cover less than and bigger than `MIN_ALIGN` for all architectures
let iterations = 32;
--- /dev/null
+// ignore-windows: No libc on Windows
+// compile-flags: -Zmiri-disable-isolation
+
+#![feature(rustc_private)]
+
+#[allow(unused)] // necessary on macos due to conditional compilation
+extern crate libc;
+
+#[cfg(not(target_os = "macos"))]
+fn test_posix_fadvise() {
+ use std::convert::TryInto;
+ use std::env::temp_dir;
+ use std::fs::{File, remove_file};
+ use std::io::Write;
+ use std::os::unix::io::AsRawFd;
+
+ let path = temp_dir().join("miri_test_libc.txt");
+ // Cleanup before test
+ remove_file(&path).ok();
+
+ // Set up an open file
+ let mut file = File::create(&path).unwrap();
+ let bytes = b"Hello, World!\n";
+ file.write(bytes).unwrap();
+
+ // Test calling posix_fadvise on a file.
+ let result = unsafe {
+ libc::posix_fadvise(
+ file.as_raw_fd(),
+ 0,
+ bytes.len().try_into().unwrap(),
+ libc::POSIX_FADV_DONTNEED,
+ )
+ };
+ drop(file);
+ remove_file(&path).unwrap();
+ assert_eq!(result, 0);
+}
+
+fn main() {
+ #[cfg(not(target_os = "macos"))]
+ test_posix_fadvise();
+}
// Make sure that coercing &mut T to *const T produces a writeable pointer.
fn direct_mut_to_const_raw() {
- // FIXME: This is currently disabled, waiting on a fix for <https://github.com/rust-lang/rust/issues/56604>
+ // TODO: This is currently disabled, waiting on a decision on <https://github.com/rust-lang/rust/issues/56604>
/*let x = &mut 0;
let y: *const i32 = x;
unsafe { *(y as *mut i32) = 1; }
// Make sure that we can create two raw pointers from a mutable reference and use them both.
fn two_raw() { unsafe {
let x = &mut 0;
- // Given the implicit reborrows, the only reason this currently works is that we
- // do not track raw pointers: The creation of `y2` reborrows `x` and thus pops
- // `y1` off the stack.
let y1 = x as *mut _;
let y2 = x as *mut _;
*y1 += 2;
} }
// Make sure that creating a *mut does not invalidate existing shared references.
-fn shr_and_raw() { /* unsafe {
+fn shr_and_raw() { unsafe {
use std::mem;
- // FIXME: This is currently disabled because "as *mut _" incurs a reborrow.
let x = &mut 0;
let y1: &i32 = mem::transmute(&*x); // launder lifetimes
let y2 = x as *mut _;
let _val = *y1;
*y2 += 1;
- // TODO: Once this works, add compile-fail test that tries to read from y1 again.
-} */ }
+} }
fn disjoint_mutable_subborrows() {
struct Foo {
let b = unsafe{ borrow_field_b(ptr) };
b.push(4);
a.push_str(" world");
- dbg!(a,b);
+ eprintln!("{:?} {:?}", a, b);
}
-[$DIR/stacked-borrows.rs:168] a = "hello world"
-[$DIR/stacked-borrows.rs:168] b = [
- 0,
- 1,
- 2,
- 4,
-]
+"hello world" [0, 1, 2, 4]
() => (core::panic::Location::caller());
}
+fn test_fn_ptr() {
+ fn pass_to_ptr_call<T>(f: fn(T), x: T) {
+ f(x);
+ }
+
+ #[track_caller]
+ fn tracked_unit(_: ()) {
+ let expected_line = line!() - 1;
+ let location = std::panic::Location::caller();
+ assert_eq!(location.file(), file!());
+ assert_eq!(location.line(), expected_line, "call shims report location as fn definition");
+ }
+
+ pass_to_ptr_call(tracked_unit, ());
+}
+
fn main() {
let location = Location::caller();
+ let expected_line = line!() - 1;
assert_eq!(location.file(), file!());
- assert_eq!(location.line(), 23);
+ assert_eq!(location.line(), expected_line);
assert_eq!(location.column(), 20);
let tracked = tracked();
+ let expected_line = line!() - 1;
assert_eq!(tracked.file(), file!());
- assert_eq!(tracked.line(), 28);
+ assert_eq!(tracked.line(), expected_line);
assert_eq!(tracked.column(), 19);
let nested = nested_intrinsic();
// `Location::caller()` in a macro should behave similarly to `file!` and `line!`,
// i.e. point to where the macro was invoked, instead of the macro itself.
let inmacro = caller_location_from_macro!();
+ let expected_line = line!() - 1;
assert_eq!(inmacro.file(), file!());
- assert_eq!(inmacro.line(), 45);
+ assert_eq!(inmacro.line(), expected_line);
assert_eq!(inmacro.column(), 19);
let intrinsic = core::intrinsics::caller_location();
+ let expected_line = line!() - 1;
assert_eq!(intrinsic.file(), file!());
- assert_eq!(intrinsic.line(), 50);
+ assert_eq!(intrinsic.line(), expected_line);
assert_eq!(intrinsic.column(), 21);
+
+ test_fn_ptr();
}