From 382f9a7a3d15580c15531428f39bf47f55093d42 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 31 Mar 2019 14:39:03 -0700 Subject: [PATCH 1/9] wasi: Load arguments via syscalls This commit switches the wasi target to loading CLI arguments via the syscalls provided by wasi rather than through the argc/argv passed to the main function. While serving the same purpose it's hoped that using syscalls will make us a bit more portable (less reliance from libstd on an external C library) as well as avoiding the need for a lock! --- src/libstd/sys/wasi/args.rs | 50 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/libstd/sys/wasi/args.rs b/src/libstd/sys/wasi/args.rs index 20558a8042db4..9c8e59e4fb5e1 100644 --- a/src/libstd/sys/wasi/args.rs +++ b/src/libstd/sys/wasi/args.rs @@ -1,30 +1,15 @@ -use crate::any::Any; use crate::ffi::CStr; +use crate::io; +use crate::sys::cvt_wasi; use crate::ffi::OsString; use crate::marker::PhantomData; use crate::os::wasi::ffi::OsStringExt; -use crate::ptr; use crate::vec; -static mut ARGC: isize = 0; -static mut ARGV: *const *const u8 = ptr::null(); - -#[cfg(not(target_feature = "atomics"))] -pub unsafe fn args_lock() -> impl Any { - // No need for a lock if we're single-threaded, but this function will need - // to get implemented for multi-threaded scenarios -} - -pub unsafe fn init(argc: isize, argv: *const *const u8) { - let _guard = args_lock(); - ARGC = argc; - ARGV = argv; +pub unsafe fn init(_argc: isize, _argv: *const *const u8) { } pub unsafe fn cleanup() { - let _guard = args_lock(); - ARGC = 0; - ARGV = ptr::null(); } pub struct Args { @@ -34,18 +19,31 @@ pub struct Args { /// Returns the command line arguments pub fn args() -> Args { + maybe_args().unwrap_or_else(|_| { + Args { + iter: Vec::new().into_iter(), + _dont_send_or_sync_me: PhantomData + } + }) +} + +fn maybe_args() -> io::Result { unsafe { - let _guard = args_lock(); - let args = (0..ARGC) - .map(|i| { - let cstr = CStr::from_ptr(*ARGV.offset(i) as *const libc::c_char); - OsStringExt::from_vec(cstr.to_bytes().to_vec()) - }) + let (mut argc, mut argv_buf_size) = (0, 0); + cvt_wasi(libc::__wasi_args_sizes_get(&mut argc, &mut argv_buf_size))?; + + let mut argc = vec![0 as *mut libc::c_char; argc]; + let mut argv_buf = vec![0; argv_buf_size]; + cvt_wasi(libc::__wasi_args_get(argc.as_mut_ptr(), argv_buf.as_mut_ptr()))?; + + let args = argc.into_iter() + .map(|ptr| CStr::from_ptr(ptr).to_bytes().to_vec()) + .map(|bytes| OsString::from_vec(bytes)) .collect::>(); - Args { + Ok(Args { iter: args.into_iter(), _dont_send_or_sync_me: PhantomData, - } + }) } } From 60f6cbd0028c61bcca181318f48cdf0c6be61231 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 31 Mar 2019 14:56:56 -0700 Subject: [PATCH 2/9] wasi: Use raw syscalls for stdio I've since learned that the mapping between libc fds and wasi fds are expected to be one-to-one, so we can use the raw syscalls for writing to stdout/stderr and reading from stdin! This should help ensure that we don't depend on a C library too unnecessarily. --- src/libstd/sys/wasi/stdio.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/libstd/sys/wasi/stdio.rs b/src/libstd/sys/wasi/stdio.rs index f6a4958897d9b..192947886668f 100644 --- a/src/libstd/sys/wasi/stdio.rs +++ b/src/libstd/sys/wasi/stdio.rs @@ -1,6 +1,7 @@ -use crate::io; +use crate::io::{self, IoVec, IoVecMut}; use crate::libc; -use crate::sys::cvt; +use crate::mem::ManuallyDrop; +use crate::sys::fd::WasiFd; pub struct Stdin; pub struct Stdout; @@ -12,10 +13,8 @@ impl Stdin { } pub fn read(&self, data: &mut [u8]) -> io::Result { - let amt = cvt(unsafe { - libc::read(libc::STDIN_FILENO, data.as_mut_ptr() as *mut _, data.len()) - })?; - Ok(amt as usize) + ManuallyDrop::new(unsafe { WasiFd::from_raw(libc::STDIN_FILENO as u32) }) + .read(&mut [IoVecMut::new(data)]) } } @@ -25,10 +24,8 @@ impl Stdout { } pub fn write(&self, data: &[u8]) -> io::Result { - let amt = cvt(unsafe { - libc::write(libc::STDOUT_FILENO, data.as_ptr() as *const _, data.len()) - })?; - Ok(amt as usize) + ManuallyDrop::new(unsafe { WasiFd::from_raw(libc::STDOUT_FILENO as u32) }) + .write(&[IoVec::new(data)]) } pub fn flush(&self) -> io::Result<()> { @@ -42,10 +39,8 @@ impl Stderr { } pub fn write(&self, data: &[u8]) -> io::Result { - let amt = cvt(unsafe { - libc::write(libc::STDERR_FILENO, data.as_ptr() as *const _, data.len()) - })?; - Ok(amt as usize) + ManuallyDrop::new(unsafe { WasiFd::from_raw(libc::STDERR_FILENO as u32) }) + .write(&[IoVec::new(data)]) } pub fn flush(&self) -> io::Result<()> { From 32a76844c47924a1d230f647db3bafb53c3e20ea Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 1 Apr 2019 05:29:17 -0700 Subject: [PATCH 3/9] wasi: Implement `error_string` to get readable errors This routes the `error_string` API to `strerror` in libc which should have more human readable descriptions. --- src/libstd/sys/wasi/os.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/libstd/sys/wasi/os.rs b/src/libstd/sys/wasi/os.rs index 6d4d6aae61b9f..822ea02a11b89 100644 --- a/src/libstd/sys/wasi/os.rs +++ b/src/libstd/sys/wasi/os.rs @@ -27,8 +27,21 @@ pub fn errno() -> i32 { unsafe { errno as i32 } } -pub fn error_string(_errno: i32) -> String { - "operation failed".to_string() +pub fn error_string(errno: i32) -> String { + extern { + fn strerror_r(errnum: libc::c_int, buf: *mut libc::c_char, + buflen: libc::size_t) -> libc::c_int; + } + + let mut buf = [0 as libc::c_char; 1024]; + + let p = buf.as_mut_ptr(); + unsafe { + if strerror_r(errno as libc::c_int, p, buf.len()) < 0 { + panic!("strerror_r failure"); + } + str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() + } } pub fn getcwd() -> io::Result { From fb575c0ea9fdda046285d527a9063255e08507d4 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 2 Apr 2019 11:30:36 -0700 Subject: [PATCH 4/9] Never return uninhabited values at all Functions with uninhabited return values are already marked `noreturn`, but we were still generating return instructions for this. When running with `C passes=lint`, LLVM prints: Unusual: Return statement in function with noreturn attribute The LLVM manual makes a stronger statement about `noreturn` though: > This produces undefined behavior at runtime if the function ever does dynamically return. We now mark such return values with a new `IgnoreMode::Uninhabited`, and emit an `abort` anywhere that would have returned. --- src/librustc_codegen_llvm/abi.rs | 9 +++++-- src/librustc_codegen_ssa/mir/block.rs | 6 +++++ src/librustc_codegen_ssa/mir/mod.rs | 3 ++- src/librustc_target/abi/call/mod.rs | 2 ++ src/test/codegen/noreturn-uninhabited.rs | 32 ++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 src/test/codegen/noreturn-uninhabited.rs diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs index 3a0d9e1334cf6..6a80051569f6a 100644 --- a/src/librustc_codegen_llvm/abi.rs +++ b/src/librustc_codegen_llvm/abi.rs @@ -518,7 +518,11 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> { let arg_of = |ty: Ty<'tcx>, arg_idx: Option| { let is_return = arg_idx.is_none(); let mut arg = mk_arg_type(ty, arg_idx); - if arg.layout.is_zst() { + if is_return && arg.layout.abi.is_uninhabited() { + // Functions with uninhabited return values are marked `noreturn`, + // so we don't actually need to do anything with the value. + arg.mode = PassMode::Ignore(IgnoreMode::Uninhabited); + } else if arg.layout.is_zst() { // For some forsaken reason, x86_64-pc-windows-gnu // doesn't ignore zero-sized struct arguments. // The same is true for s390x-unknown-linux-gnu @@ -677,7 +681,8 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> { ); let llreturn_ty = match self.ret.mode { - PassMode::Ignore(IgnoreMode::Zst) => cx.type_void(), + PassMode::Ignore(IgnoreMode::Zst) + | PassMode::Ignore(IgnoreMode::Uninhabited) => cx.type_void(), PassMode::Ignore(IgnoreMode::CVarArgs) => bug!("`va_list` should never be a return type"), PassMode::Direct(_) | PassMode::Pair(..) => { diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index 53e8f7ed88b4e..76357078c4514 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -244,6 +244,12 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } + PassMode::Ignore(IgnoreMode::Uninhabited) => { + bx.abort(); + bx.unreachable(); + return; + } + PassMode::Ignore(IgnoreMode::CVarArgs) => { bug!("C-variadic arguments should never be the return type"); } diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs index 91aa9bcc7808b..0b3476c44e2ce 100644 --- a/src/librustc_codegen_ssa/mir/mod.rs +++ b/src/librustc_codegen_ssa/mir/mod.rs @@ -521,7 +521,8 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( PassMode::Ignore(IgnoreMode::Zst) => { return local(OperandRef::new_zst(bx, arg.layout)); } - PassMode::Ignore(IgnoreMode::CVarArgs) => {} + PassMode::Ignore(IgnoreMode::CVarArgs) + | PassMode::Ignore(IgnoreMode::Uninhabited) => {} PassMode::Direct(_) => { let llarg = bx.get_param(llarg_idx); bx.set_value_name(llarg, &name); diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs index fbbd120f934be..8cbe99e3d8c76 100644 --- a/src/librustc_target/abi/call/mod.rs +++ b/src/librustc_target/abi/call/mod.rs @@ -29,6 +29,8 @@ pub enum IgnoreMode { CVarArgs, /// A zero-sized type. Zst, + /// An uninhabited type. + Uninhabited, } #[derive(Clone, Copy, PartialEq, Eq, Debug)] diff --git a/src/test/codegen/noreturn-uninhabited.rs b/src/test/codegen/noreturn-uninhabited.rs new file mode 100644 index 0000000000000..1b65da9f2877a --- /dev/null +++ b/src/test/codegen/noreturn-uninhabited.rs @@ -0,0 +1,32 @@ +// compile-flags: -g -C no-prepopulate-passes +// ignore-tidy-linelength + +#![crate_type = "lib"] + +#[derive(Clone, Copy)] +pub enum EmptyEnum {} + +#[no_mangle] +pub fn empty(x: &EmptyEnum) -> EmptyEnum { + // CHECK: @empty({{.*}}) unnamed_addr #0 + // CHECK-NOT: ret void + // CHECK: call void @llvm.trap() + // CHECK: unreachable + *x +} + +pub struct Foo(String, EmptyEnum); + +#[no_mangle] +pub fn foo(x: String, y: &EmptyEnum) -> Foo { + // CHECK: @foo({{.*}}) unnamed_addr #0 + // CHECK-NOT: ret %Foo + // CHECK: call void @llvm.trap() + // CHECK: unreachable + Foo(x, *y) +} + +// CHECK: attributes #0 = {{{.*}} noreturn {{.*}}} + +// CHECK: DISubprogram(name: "empty", {{.*}} DIFlagNoReturn +// CHECK: DISubprogram(name: "foo", {{.*}} DIFlagNoReturn From 7eb2efdb125039f51a339eb3c204e717bb25aff6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 2 Apr 2019 13:38:51 -0700 Subject: [PATCH 5/9] std: Upgrade `compiler_builtins` to fix wasi linkage Turns out we needed to exclude a number of math functions on the `wasm32-unknown-wasi` target, and this was fixed in 0.1.9 of compiler-builtins and this is pulling in the fix to libstd's own build. --- Cargo.lock | 32 ++++++++++++++++---------------- src/libstd/Cargo.toml | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c071a2e11d269..086058a8761d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,7 +17,7 @@ dependencies = [ name = "alloc" version = "0.0.0" dependencies = [ - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -106,7 +106,7 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-std-workspace-core 1.0.0", ] @@ -464,7 +464,7 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", @@ -750,7 +750,7 @@ name = "dlmalloc" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-std-workspace-core 1.0.0", ] @@ -916,7 +916,7 @@ name = "fortanix-sgx-abi" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-std-workspace-core 1.0.0", ] @@ -1768,7 +1768,7 @@ dependencies = [ name = "panic_abort" version = "0.0.0" dependencies = [ - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1778,7 +1778,7 @@ name = "panic_unwind" version = "0.0.0" dependencies = [ "alloc 0.0.0", - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "unwind 0.0.0", @@ -1963,7 +1963,7 @@ name = "profiler_builtins" version = "0.0.0" dependencies = [ "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", ] @@ -2498,7 +2498,7 @@ name = "rustc-demangle" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-std-workspace-core 1.0.0", ] @@ -2598,7 +2598,7 @@ dependencies = [ "alloc 0.0.0", "build_helper 0.1.0", "cmake 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", ] @@ -2825,7 +2825,7 @@ dependencies = [ "alloc 0.0.0", "build_helper 0.1.0", "cmake 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", ] @@ -2887,7 +2887,7 @@ dependencies = [ "alloc 0.0.0", "build_helper 0.1.0", "cmake 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", ] @@ -3008,7 +3008,7 @@ dependencies = [ "alloc 0.0.0", "build_helper 0.1.0", "cmake 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", ] @@ -3275,7 +3275,7 @@ dependencies = [ "alloc 0.0.0", "backtrace-sys 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", "dlmalloc 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "fortanix-sgx-abi 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3848,7 +3848,7 @@ dependencies = [ name = "unwind" version = "0.0.0" dependencies = [ - "compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4044,7 +4044,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc" "checksum commoncrypto 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d056a8586ba25a1e4d61cb090900e495952c7886786fc55f909ab2f819b69007" "checksum commoncrypto-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fed34f46747aa73dfaa578069fd8279d2818ade2b55f38f22a9401c7f4083e2" -"checksum compiler_builtins 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "a28c3898d0c57b26fa6f92de141ba665fa5ac5179f795db06db408be84302395" +"checksum compiler_builtins 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d7de11892d9f9f1bc76d43011c8233d27d58300d629dc9dfb51b6626ef7f6077" "checksum compiletest_rs 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "56c799b1f7142badf3b047b4c1f2074cc96b6b784fb2432f2ed9c87da0a03749" "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "checksum core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4e2640d6d0bf22e82bed1b73c6aef8d5dd31e5abe6666c57e6d45e2649f4f887" diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 9ac03adfc2778..b2b9c94ea4fe4 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -19,7 +19,7 @@ panic_unwind = { path = "../libpanic_unwind", optional = true } panic_abort = { path = "../libpanic_abort" } core = { path = "../libcore" } libc = { version = "0.2.51", default-features = false, features = ['rustc-dep-of-std'] } -compiler_builtins = { version = "0.1.8" } +compiler_builtins = { version = "0.1.9" } profiler_builtins = { path = "../libprofiler_builtins", optional = true } unwind = { path = "../libunwind" } rustc-demangle = { version = "0.1.10", features = ['rustc-dep-of-std'] } From ab3b65737380d2c2a15a868452026df49c58131a Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 3 Apr 2019 10:54:07 +0200 Subject: [PATCH 6/9] Updated the documentation of core::hints::spin_loop and core::sync::spin_loop_hint --- src/libcore/hint.rs | 27 ++++++++++++++++++++------- src/libcore/sync/atomic.rs | 27 ++++++++++++++++++++------- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/libcore/hint.rs b/src/libcore/hint.rs index d1ccc148654ca..7d2006e03c409 100644 --- a/src/libcore/hint.rs +++ b/src/libcore/hint.rs @@ -50,15 +50,28 @@ pub unsafe fn unreachable_unchecked() -> ! { intrinsics::unreachable() } -/// Save power or switch hyperthreads in a busy-wait spin-loop. +/// Signals the processor that it is entering a busy-wait spin-loop. /// -/// This function is deliberately more primitive than -/// [`std::thread::yield_now`](../../std/thread/fn.yield_now.html) and -/// does not directly yield to the system's scheduler. -/// In some cases it might be useful to use a combination of both functions. -/// Careful benchmarking is advised. +/// Upon receiving spin-loop signal the processor can optimize its behavior by, for example, saving +/// power or switching hyper-threads. /// -/// On some platforms this function may not do anything at all. +/// This function is different than [`std::thread::yield_now`] which directly yields to the +/// system's scheduler, whereas `spin_loop` only signals the processor that it is entering a +/// busy-wait spin-loop without yielding control to the system's scheduler. +/// +/// Using a busy-wait spin-loop with `spin_loop` is ideally used in situations where a +/// contended lock is held by another thread executed on a different CPU and where the waiting +/// times are relatively small. Because entering busy-wait spin-loop does not trigger the system's +/// scheduler, no overhead for switching threads occurs. However, if the thread holding the +/// contended lock is running on the same CPU, the spin-loop is likely to occupy an entire CPU slice +/// before switching to the thread that holds the lock. If the contending lock is held by a thread +/// on the same CPU or if the waiting times for acquiring the lock are longer, it is often better to +/// use [`std::thread::yield_now`]. +/// +/// **Note**: On platforms that do not support receiving spin-loop hints this function does not +/// do anything at all. +/// +/// [`std::thread::yield_now`]: ../../../std/thread/fn.yield_now.html #[inline] #[unstable(feature = "renamed_spin_loop", issue = "55002")] pub fn spin_loop() { diff --git a/src/libcore/sync/atomic.rs b/src/libcore/sync/atomic.rs index 04a49d253015f..26b59969e18af 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs @@ -124,15 +124,28 @@ use fmt; use hint::spin_loop; -/// Save power or switch hyperthreads in a busy-wait spin-loop. +/// Signals the processor that it is entering a busy-wait spin-loop. /// -/// This function is deliberately more primitive than -/// [`std::thread::yield_now`](../../../std/thread/fn.yield_now.html) and -/// does not directly yield to the system's scheduler. -/// In some cases it might be useful to use a combination of both functions. -/// Careful benchmarking is advised. +/// Upon receiving spin-loop signal the processor can optimize its behavior by, for example, saving +/// power or switching hyper-threads. /// -/// On some platforms this function may not do anything at all. +/// This function is different than [`std::thread::yield_now`] which directly yields to the +/// system's scheduler, whereas `spin_loop_hint` only signals the processor that it is entering a +/// busy-wait spin-loop without yielding control to the system's scheduler. +/// +/// Using a busy-wait spin-loop with `spin_loop_hint` is ideally used in situations where a +/// contended lock is held by another thread executed on a different CPU and where the waiting +/// times are relatively small. Because entering busy-wait spin-loop does not trigger the system's +/// scheduler, no overhead for switching threads occurs. However, if the thread holding the +/// contended lock is running on the same CPU, the spin-loop is likely to occupy an entire CPU slice +/// before switching to the thread that holds the lock. If the contending lock is held by a thread +/// on the same CPU or if the waiting times for acquiring the lock are longer, it is often better to +/// use [`std::thread::yield_now`]. +/// +/// **Note**: On platforms that do not support receiving spin-loop hints this function does not +/// do anything at all. +/// +/// [`std::thread::yield_now`]: ../../../std/thread/fn.yield_now.html #[inline] #[stable(feature = "spin_loop_hint", since = "1.24.0")] pub fn spin_loop_hint() { From becee90cfd9f174ed050c1d4b366fd13d89f1913 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 3 Apr 2019 15:47:38 +0200 Subject: [PATCH 7/9] Updated the reference in core::hint::spin_loop to the correct relative path. --- src/libcore/hint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/hint.rs b/src/libcore/hint.rs index 7d2006e03c409..d43e6c49f4c99 100644 --- a/src/libcore/hint.rs +++ b/src/libcore/hint.rs @@ -71,7 +71,7 @@ pub unsafe fn unreachable_unchecked() -> ! { /// **Note**: On platforms that do not support receiving spin-loop hints this function does not /// do anything at all. /// -/// [`std::thread::yield_now`]: ../../../std/thread/fn.yield_now.html +/// [`std::thread::yield_now`]: ../../std/thread/fn.yield_now.html #[inline] #[unstable(feature = "renamed_spin_loop", issue = "55002")] pub fn spin_loop() { From c0d68d2769b9f783982bf63d3bb0a4cd202ca02e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 3 Apr 2019 07:30:08 -0700 Subject: [PATCH 8/9] Include trailing comma in multiline Debug representation This commit changes the behavior of Formatter::debug_struct, debug_tuple, debug_list, debug_set, and debug_map to render trailing commas in {:#?} mode, which is the dominant style in modern Rust code. Before: Language { name: "Rust", trailing_commas: false } After: Language { name: "Rust", trailing_commas: true, } --- src/libcore/fmt/builders.rs | 90 +++++-------- src/libcore/tests/fmt/builders.rs | 52 ++++---- .../escape-argument-callee.stderr | 2 +- .../escape-argument.stderr | 2 +- .../escape-upvar-nested.stderr | 4 +- .../escape-upvar-ref.stderr | 2 +- ...pagate-approximated-fail-no-postdom.stderr | 2 +- .../propagate-approximated-ref.stderr | 2 +- ...er-to-static-comparing-against-free.stderr | 4 +- ...oximated-shorter-to-static-no-bound.stderr | 2 +- ...mated-shorter-to-static-wrong-bound.stderr | 2 +- .../propagate-approximated-val.stderr | 2 +- .../propagate-despite-same-free-region.stderr | 2 +- ...ail-to-approximate-longer-no-bounds.stderr | 2 +- ...-to-approximate-longer-wrong-bounds.stderr | 2 +- .../propagate-from-trait-match.stderr | 4 +- .../return-wrong-bound-region.stderr | 2 +- .../projection-no-regions-closure.stderr | 16 +-- .../projection-one-region-closure.stderr | 16 +-- ...tion-one-region-trait-bound-closure.stderr | 20 +-- ...e-region-trait-bound-static-closure.stderr | 20 +-- ...tion-two-region-trait-bound-closure.stderr | 32 ++--- ...ram-closure-approximate-lower-bound.stderr | 8 +- ...m-closure-outlives-from-return-type.stderr | 4 +- ...-closure-outlives-from-where-clause.stderr | 16 +-- .../dollar-crate-issue-57089.stdout | 40 +++--- src/test/ui/proc-macro/dollar-crate.stdout | 120 +++++++++--------- .../dbg-macro-expected-behavior.rs | 8 +- 28 files changed, 226 insertions(+), 252 deletions(-) diff --git a/src/libcore/fmt/builders.rs b/src/libcore/fmt/builders.rs index 45994c2b4f0f0..df3852973b8dd 100644 --- a/src/libcore/fmt/builders.rs +++ b/src/libcore/fmt/builders.rs @@ -11,7 +11,7 @@ impl<'a> PadAdapter<'a> { fmt.wrap_buf(move |buf| { *slot = Some(PadAdapter { buf, - on_newline: false, + on_newline: true, }); slot.as_mut().unwrap() }) @@ -128,22 +128,21 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { #[stable(feature = "debug_builders", since = "1.2.0")] pub fn field(&mut self, name: &str, value: &dyn fmt::Debug) -> &mut DebugStruct<'a, 'b> { self.result = self.result.and_then(|_| { - let prefix = if self.has_fields { - "," - } else { - " {" - }; - if self.is_pretty() { + if !self.has_fields { + self.fmt.write_str(" {\n")?; + } let mut slot = None; let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot); - writer.write_str(prefix)?; - writer.write_str("\n")?; writer.write_str(name)?; writer.write_str(": ")?; - value.fmt(&mut writer) + value.fmt(&mut writer)?; + writer.write_str(",\n") } else { - write!(self.fmt, "{} {}: ", prefix, name)?; + let prefix = if self.has_fields { ", " } else { " { " }; + self.fmt.write_str(prefix)?; + self.fmt.write_str(name)?; + self.fmt.write_str(": ")?; value.fmt(self.fmt) } }); @@ -184,7 +183,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { if self.has_fields { self.result = self.result.and_then(|_| { if self.is_pretty() { - self.fmt.write_str("\n}") + self.fmt.write_str("}") } else { self.fmt.write_str(" }") } @@ -275,21 +274,17 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { #[stable(feature = "debug_builders", since = "1.2.0")] pub fn field(&mut self, value: &dyn fmt::Debug) -> &mut DebugTuple<'a, 'b> { self.result = self.result.and_then(|_| { - let (prefix, space) = if self.fields > 0 { - (",", " ") - } else { - ("(", "") - }; - if self.is_pretty() { + if self.fields == 0 { + self.fmt.write_str("(\n")?; + } let mut slot = None; let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot); - writer.write_str(prefix)?; - writer.write_str("\n")?; - value.fmt(&mut writer) + value.fmt(&mut writer)?; + writer.write_str(",\n") } else { + let prefix = if self.fields == 0 { "(" } else { ", " }; self.fmt.write_str(prefix)?; - self.fmt.write_str(space)?; value.fmt(self.fmt) } }); @@ -326,10 +321,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { pub fn finish(&mut self) -> fmt::Result { if self.fields > 0 { self.result = self.result.and_then(|_| { - if self.is_pretty() { - self.fmt.write_str("\n")?; - } - if self.fields == 1 && self.empty_name { + if self.fields == 1 && self.empty_name && !self.is_pretty() { self.fmt.write_str(",")?; } self.fmt.write_str(")") @@ -353,14 +345,13 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> { fn entry(&mut self, entry: &dyn fmt::Debug) { self.result = self.result.and_then(|_| { if self.is_pretty() { + if !self.has_fields { + self.fmt.write_str("\n")?; + } let mut slot = None; let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot); - writer.write_str(if self.has_fields { - ",\n" - } else { - "\n" - })?; - entry.fmt(&mut writer) + entry.fmt(&mut writer)?; + writer.write_str(",\n") } else { if self.has_fields { self.fmt.write_str(", ")? @@ -372,15 +363,6 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> { self.has_fields = true; } - pub fn finish(&mut self) { - let prefix = if self.is_pretty() && self.has_fields { - "\n" - } else { - "" - }; - self.result = self.result.and_then(|_| self.fmt.write_str(prefix)); - } - fn is_pretty(&self) -> bool { self.fmt.alternate() } @@ -421,7 +403,7 @@ pub struct DebugSet<'a, 'b: 'a> { } pub fn debug_set_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>) -> DebugSet<'a, 'b> { - let result = write!(fmt, "{{"); + let result = fmt.write_str("{"); DebugSet { inner: DebugInner { fmt, @@ -519,7 +501,6 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn finish(&mut self) -> fmt::Result { - self.inner.finish(); self.inner.result.and_then(|_| self.inner.fmt.write_str("}")) } } @@ -559,7 +540,7 @@ pub struct DebugList<'a, 'b: 'a> { } pub fn debug_list_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>) -> DebugList<'a, 'b> { - let result = write!(fmt, "["); + let result = fmt.write_str("["); DebugList { inner: DebugInner { fmt, @@ -657,7 +638,6 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn finish(&mut self) -> fmt::Result { - self.inner.finish(); self.inner.result.and_then(|_| self.inner.fmt.write_str("]")) } } @@ -699,7 +679,7 @@ pub struct DebugMap<'a, 'b: 'a> { } pub fn debug_map_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>) -> DebugMap<'a, 'b> { - let result = write!(fmt, "{{"); + let result = fmt.write_str("{"); DebugMap { fmt, result, @@ -734,16 +714,15 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { pub fn entry(&mut self, key: &dyn fmt::Debug, value: &dyn fmt::Debug) -> &mut DebugMap<'a, 'b> { self.result = self.result.and_then(|_| { if self.is_pretty() { + if !self.has_fields { + self.fmt.write_str("\n")?; + } let mut slot = None; let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot); - writer.write_str(if self.has_fields { - ",\n" - } else { - "\n" - })?; key.fmt(&mut writer)?; writer.write_str(": ")?; - value.fmt(&mut writer) + value.fmt(&mut writer)?; + writer.write_str(",\n") } else { if self.has_fields { self.fmt.write_str(", ")? @@ -818,12 +797,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn finish(&mut self) -> fmt::Result { - let prefix = if self.is_pretty() && self.has_fields { - "\n" - } else { - "" - }; - self.result.and_then(|_| write!(self.fmt, "{}}}", prefix)) + self.result.and_then(|_| self.fmt.write_str("}")) } fn is_pretty(&self) -> bool { diff --git a/src/libcore/tests/fmt/builders.rs b/src/libcore/tests/fmt/builders.rs index fd7192cc15119..e4b75fe1fa265 100644 --- a/src/libcore/tests/fmt/builders.rs +++ b/src/libcore/tests/fmt/builders.rs @@ -30,7 +30,7 @@ mod debug_struct { assert_eq!("Foo { bar: true }", format!("{:?}", Foo)); assert_eq!( "Foo { - bar: true + bar: true, }", format!("{:#?}", Foo)); } @@ -52,7 +52,7 @@ mod debug_struct { assert_eq!( "Foo { bar: true, - baz: 10/20 + baz: 10/20, }", format!("{:#?}", Foo)); } @@ -87,9 +87,9 @@ mod debug_struct { "Bar { foo: Foo { bar: true, - baz: 10/20 + baz: 10/20, }, - hello: \"world\" + hello: \"world\", }", format!("{:#?}", Bar)); } @@ -127,7 +127,7 @@ mod debug_tuple { assert_eq!("Foo(true)", format!("{:?}", Foo)); assert_eq!( "Foo( - true + true, )", format!("{:#?}", Foo)); } @@ -149,7 +149,7 @@ mod debug_tuple { assert_eq!( "Foo( true, - 10/20 + 10/20, )", format!("{:#?}", Foo)); } @@ -184,9 +184,9 @@ mod debug_tuple { "Bar( Foo( true, - 10/20 + 10/20, ), - \"world\" + \"world\", )", format!("{:#?}", Bar)); } @@ -224,7 +224,7 @@ mod debug_map { assert_eq!("{\"bar\": true}", format!("{:?}", Foo)); assert_eq!( "{ - \"bar\": true + \"bar\": true, }", format!("{:#?}", Foo)); } @@ -246,7 +246,7 @@ mod debug_map { assert_eq!( "{ \"bar\": true, - 10: 10/20 + 10: 10/20, }", format!("{:#?}", Foo)); } @@ -282,12 +282,12 @@ mod debug_map { "{ \"foo\": { \"bar\": true, - 10: 10/20 + 10: 10/20, }, { \"bar\": true, - 10: 10/20 - }: \"world\" + 10: 10/20, + }: \"world\", }", format!("{:#?}", Bar)); } @@ -325,7 +325,7 @@ mod debug_set { assert_eq!("{true}", format!("{:?}", Foo)); assert_eq!( "{ - true + true, }", format!("{:#?}", Foo)); } @@ -347,7 +347,7 @@ mod debug_set { assert_eq!( "{ true, - 10/20 + 10/20, }", format!("{:#?}", Foo)); } @@ -382,9 +382,9 @@ mod debug_set { "{ { true, - 10/20 + 10/20, }, - \"world\" + \"world\", }", format!("{:#?}", Bar)); } @@ -422,7 +422,7 @@ mod debug_list { assert_eq!("[true]", format!("{:?}", Foo)); assert_eq!( "[ - true + true, ]", format!("{:#?}", Foo)); } @@ -444,7 +444,7 @@ mod debug_list { assert_eq!( "[ true, - 10/20 + 10/20, ]", format!("{:#?}", Foo)); } @@ -479,9 +479,9 @@ mod debug_list { "[ [ true, - 10/20 + 10/20, ], - \"world\" + \"world\", ]", format!("{:#?}", Bar)); } @@ -513,31 +513,31 @@ fn test_formatting_parameters_are_forwarded() { assert_eq!(format!("{:#03?}", struct_), " Foo { bar: 1024, - baz: 007 + baz: 007, } ".trim()); assert_eq!(format!("{:#03?}", tuple), " ( 1024, - 007 + 007, ) ".trim()); assert_eq!(format!("{:#03?}", list), " [ 1024, - 007 + 007, ] ".trim()); assert_eq!(format!("{:#03?}", map), r#" { "bar": 1024, - "baz": 007 + "baz": 007, } "#.trim()); assert_eq!(format!("{:#03?}", set), " { 007, - 1024 + 1024, } ".trim()); } diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr index 46de13dbbbd9b..20041389b3c38 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -6,7 +6,7 @@ LL | let mut closure = expect_sig(|p, y| *p = y); | = note: defining type: DefId(0/1:9 ~ escape_argument_callee[317d]::test[0]::{{closure}}[0]) with closure substs [ i16, - for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't0)) i32)) + for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't0)) i32)), ] error: lifetime may not live long enough diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr index d47b326f48ed5..b08ec9539318a 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -6,7 +6,7 @@ LL | let mut closure = expect_sig(|p, y| *p = y); | = note: defining type: DefId(0/1:9 ~ escape_argument[317d]::test[0]::{{closure}}[0]) with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) i32)) + for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) i32)), ] note: No external requirements diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr index dec4a6b811fcd..7178b22bb5f2d 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr @@ -8,7 +8,7 @@ LL | let mut closure1 = || p = &y; i16, extern "rust-call" fn(()), &'_#1r mut &'_#2r i32, - &'_#3r i32 + &'_#3r i32, ] = note: number of external vids: 4 = note: where '_#3r: '_#2r @@ -27,7 +27,7 @@ LL | | }; i16, extern "rust-call" fn(()), &'_#1r mut &'_#2r i32, - &'_#3r i32 + &'_#3r i32, ] = note: number of external vids: 4 = note: where '_#3r: '_#2r diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr index 55ede6ed4aae9..d129f945f252a 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr @@ -8,7 +8,7 @@ LL | let mut closure = || p = &y; i16, extern "rust-call" fn(()), &'_#1r mut &'_#2r i32, - &'_#3r i32 + &'_#3r i32, ] = note: number of external vids: 4 = note: where '_#3r: '_#2r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index 7eb4d96fc5f75..4db1f04077043 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -10,7 +10,7 @@ LL | | }, | = note: defining type: DefId(0/1:20 ~ propagate_approximated_fail_no_postdom[317d]::supply[0]::{{closure}}[0]) with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>)) + for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>)), ] = note: late-bound region is '_#4r = note: late-bound region is '_#5r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr index a1a1024bccd5b..7dedae715bea0 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -11,7 +11,7 @@ LL | | }); | = note: defining type: DefId(0/1:18 ~ propagate_approximated_ref[317d]::supply[0]::{{closure}}[0]) with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) u32>)) + for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) u32>)), ] = note: late-bound region is '_#3r = note: late-bound region is '_#4r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index ad2a5cae62e00..b2d7fd8df6d33 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -10,7 +10,7 @@ LL | | }) | = note: defining type: DefId(0/1:12 ~ propagate_approximated_shorter_to_static_comparing_against_free[317d]::case1[0]::{{closure}}[0]) with closure substs [ i32, - for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>)) + for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>)), ] error[E0521]: borrowed data escapes outside of closure @@ -48,7 +48,7 @@ LL | | }) | = note: defining type: DefId(0/1:13 ~ propagate_approximated_shorter_to_static_comparing_against_free[317d]::case2[0]::{{closure}}[0]) with closure substs [ i32, - for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>)) + for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>)), ] = note: number of external vids: 2 = note: where '_#1r: '_#0r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index f5167c2197b3e..e30e2dfee6301 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -12,7 +12,7 @@ LL | | }); | = note: defining type: DefId(0/1:18 ~ propagate_approximated_shorter_to_static_no_bound[317d]::supply[0]::{{closure}}[0]) with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't2)) u32>)) + for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't2)) u32>)), ] = note: late-bound region is '_#2r = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index b4e1b0d224769..ec608590a7104 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -12,7 +12,7 @@ LL | | }); | = note: defining type: DefId(0/1:18 ~ propagate_approximated_shorter_to_static_wrong_bound[317d]::supply[0]::{{closure}}[0]) with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) u32>)) + for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) u32>)), ] = note: late-bound region is '_#3r = note: late-bound region is '_#4r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr index 6ab2104a51c65..223c29f596922 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -11,7 +11,7 @@ LL | | }); | = note: defining type: DefId(0/1:18 ~ propagate_approximated_val[317d]::test[0]::{{closure}}[0]) with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>)) + for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>)), ] = note: late-bound region is '_#3r = note: late-bound region is '_#4r diff --git a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr index 79f0df9a6888e..d618b4d06a1d8 100644 --- a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr @@ -10,7 +10,7 @@ LL | | }, | = note: defining type: DefId(0/1:16 ~ propagate_despite_same_free_region[317d]::supply[0]::{{closure}}[0]) with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>)) + for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>)), ] = note: late-bound region is '_#3r = note: number of external vids: 4 diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index f8b6bfa003b32..07fb4d0d5e3ae 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -11,7 +11,7 @@ LL | | }); | = note: defining type: DefId(0/1:18 ~ propagate_fail_to_approximate_longer_no_bounds[317d]::supply[0]::{{closure}}[0]) with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>)) + for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>)), ] = note: late-bound region is '_#2r = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index 7e7429405fa06..a0744c27db72c 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -11,7 +11,7 @@ LL | | }); | = note: defining type: DefId(0/1:18 ~ propagate_fail_to_approximate_longer_wrong_bounds[317d]::supply[0]::{{closure}}[0]) with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) u32>)) + for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 't1)) u32>)), ] = note: late-bound region is '_#3r = note: late-bound region is '_#4r diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr index c2bbd8138142d..282246f81666b 100644 --- a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr @@ -15,7 +15,7 @@ LL | | }); '_#1r, T, i32, - extern "rust-call" fn((T,)) + extern "rust-call" fn((T,)), ] = note: number of external vids: 2 = note: where T: '_#1r @@ -34,7 +34,7 @@ LL | | } | = note: defining type: DefId(0/0:6 ~ propagate_from_trait_match[317d]::supply[0]) with substs [ '_#1r, - T + T, ] error[E0309]: the parameter type `T` may not live long enough diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr index d5bfa3f9894d0..c5645413c77cd 100644 --- a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr +++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr @@ -6,7 +6,7 @@ LL | expect_sig(|a, b| b); // ought to return `a` | = note: defining type: DefId(0/1:9 ~ return_wrong_bound_region[317d]::test[0]::{{closure}}[0]) with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) i32, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) i32)) -> &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) i32 + for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) i32, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) i32)) -> &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 'r)) i32, ] error: lifetime may not live long enough diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr index a7679aac219fd..cda1f7d36311c 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr @@ -8,7 +8,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) '_#1r, T, i32, - extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn Anything + '_#2r)> + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn Anything + '_#2r)>, ] = note: number of external vids: 3 = note: where ::Item: '_#2r @@ -27,7 +27,7 @@ LL | | } | = note: defining type: DefId(0/0:6 ~ projection_no_regions_closure[317d]::no_region[0]) with substs [ '_#1r, - T + T, ] error[E0309]: the associated type `::Item` may not live long enough @@ -48,7 +48,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) '_#1r, T, i32, - extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn Anything + '_#2r)> + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn Anything + '_#2r)>, ] = note: number of external vids: 3 = note: where ::Item: '_#2r @@ -66,7 +66,7 @@ LL | | } | = note: defining type: DefId(0/0:7 ~ projection_no_regions_closure[317d]::correct_region[0]) with substs [ '_#1r, - T + T, ] note: External requirements @@ -80,7 +80,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) '_#2r, T, i32, - extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn Anything + '_#3r)> + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn Anything + '_#3r)>, ] = note: number of external vids: 4 = note: where ::Item: '_#3r @@ -100,7 +100,7 @@ LL | | } = note: defining type: DefId(0/0:8 ~ projection_no_regions_closure[317d]::wrong_region[0]) with substs [ '_#1r, '_#2r, - T + T, ] error[E0309]: the associated type `::Item` may not live long enough @@ -122,7 +122,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) '_#2r, T, i32, - extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn Anything + '_#3r)> + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn Anything + '_#3r)>, ] = note: number of external vids: 4 = note: where ::Item: '_#3r @@ -142,7 +142,7 @@ LL | | } = note: defining type: DefId(0/0:9 ~ projection_no_regions_closure[317d]::outlives_region[0]) with substs [ '_#1r, '_#2r, - T + T, ] error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr index 7ba7164b35b84..9d716006500b7 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -8,7 +8,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#1r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), ] = note: late-bound region is '_#3r = note: number of external vids: 4 @@ -29,7 +29,7 @@ LL | | } | = note: defining type: DefId(0/0:8 ~ projection_one_region_closure[317d]::no_relationships_late[0]) with substs [ '_#1r, - T + T, ] error[E0309]: the parameter type `T` may not live long enough @@ -62,7 +62,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#2r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), ] = note: number of external vids: 4 = note: where T: '_#3r @@ -83,7 +83,7 @@ LL | | } = note: defining type: DefId(0/0:9 ~ projection_one_region_closure[317d]::no_relationships_early[0]) with substs [ '_#1r, '_#2r, - T + T, ] error[E0309]: the parameter type `T` may not live long enough @@ -116,7 +116,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#2r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), ] = note: number of external vids: 4 = note: where >::AssocType: '_#3r @@ -136,7 +136,7 @@ LL | | } = note: defining type: DefId(0/0:10 ~ projection_one_region_closure[317d]::projection_outlives[0]) with substs [ '_#1r, '_#2r, - T + T, ] note: External requirements @@ -150,7 +150,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#2r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), ] = note: number of external vids: 4 = note: where T: '_#3r @@ -171,7 +171,7 @@ LL | | } = note: defining type: DefId(0/0:11 ~ projection_one_region_closure[317d]::elements_outlive[0]) with substs [ '_#1r, '_#2r, - T + T, ] error: aborting due to 4 previous errors diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr index 63ead49adfcb4..0fa4060137a3f 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr @@ -8,7 +8,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#1r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), ] = note: late-bound region is '_#3r = note: number of external vids: 4 @@ -28,7 +28,7 @@ LL | | } | = note: defining type: DefId(0/0:8 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_late[0]) with substs [ '_#1r, - T + T, ] error: lifetime may not live long enough @@ -53,7 +53,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#2r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), ] = note: number of external vids: 4 = note: where '_#2r: '_#3r @@ -73,7 +73,7 @@ LL | | } = note: defining type: DefId(0/0:9 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_early[0]) with substs [ '_#1r, '_#2r, - T + T, ] error: lifetime may not live long enough @@ -98,7 +98,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#2r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), ] = note: number of external vids: 4 = note: where >::AssocType: '_#3r @@ -118,7 +118,7 @@ LL | | } = note: defining type: DefId(0/0:10 ~ projection_one_region_trait_bound_closure[317d]::projection_outlives[0]) with substs [ '_#1r, '_#2r, - T + T, ] note: External requirements @@ -132,7 +132,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#2r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), ] = note: number of external vids: 4 = note: where '_#2r: '_#3r @@ -152,7 +152,7 @@ LL | | } = note: defining type: DefId(0/0:11 ~ projection_one_region_trait_bound_closure[317d]::elements_outlive[0]) with substs [ '_#1r, '_#2r, - T + T, ] note: External requirements @@ -165,7 +165,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#1r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), ] = note: number of external vids: 3 = note: where '_#1r: '_#2r @@ -184,7 +184,7 @@ LL | | } | = note: defining type: DefId(0/0:12 ~ projection_one_region_trait_bound_closure[317d]::one_region[0]) with substs [ '_#1r, - T + T, ] error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr index 61f8132cf490c..f616a7feae3f0 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr @@ -8,7 +8,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#1r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), ] = note: late-bound region is '_#3r @@ -25,7 +25,7 @@ LL | | } | = note: defining type: DefId(0/0:8 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_late[0]) with substs [ '_#1r, - T + T, ] note: No external requirements @@ -39,7 +39,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#2r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), ] note: No external requirements @@ -57,7 +57,7 @@ LL | | } = note: defining type: DefId(0/0:9 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_early[0]) with substs [ '_#1r, '_#2r, - T + T, ] note: No external requirements @@ -71,7 +71,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#2r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), ] note: No external requirements @@ -89,7 +89,7 @@ LL | | } = note: defining type: DefId(0/0:10 ~ projection_one_region_trait_bound_static_closure[317d]::projection_outlives[0]) with substs [ '_#1r, '_#2r, - T + T, ] note: No external requirements @@ -103,7 +103,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#2r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), ] note: No external requirements @@ -121,7 +121,7 @@ LL | | } = note: defining type: DefId(0/0:11 ~ projection_one_region_trait_bound_static_closure[317d]::elements_outlive[0]) with substs [ '_#1r, '_#2r, - T + T, ] note: No external requirements @@ -134,7 +134,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#1r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), ] note: No external requirements @@ -151,6 +151,6 @@ LL | | } | = note: defining type: DefId(0/0:12 ~ projection_one_region_trait_bound_static_closure[317d]::one_region[0]) with substs [ '_#1r, - T + T, ] diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr index f5daae286709e..b761b031444da 100644 --- a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr @@ -9,7 +9,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#2r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), ] = note: late-bound region is '_#4r = note: number of external vids: 5 @@ -30,7 +30,7 @@ LL | | } = note: defining type: DefId(0/0:8 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_late[0]) with substs [ '_#1r, '_#2r, - T + T, ] error[E0309]: the associated type `>::AssocType` may not live long enough @@ -53,7 +53,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#3r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)), ] = note: number of external vids: 5 = note: where >::AssocType: '_#4r @@ -74,7 +74,7 @@ LL | | } '_#1r, '_#2r, '_#3r, - T + T, ] error[E0309]: the associated type `>::AssocType` may not live long enough @@ -97,7 +97,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#3r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)), ] = note: number of external vids: 5 = note: where >::AssocType: '_#4r @@ -118,7 +118,7 @@ LL | | } '_#1r, '_#2r, '_#3r, - T + T, ] note: External requirements @@ -133,7 +133,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#3r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)), ] = note: number of external vids: 5 = note: where >::AssocType: '_#4r @@ -154,7 +154,7 @@ LL | | } '_#1r, '_#2r, '_#3r, - T + T, ] note: External requirements @@ -169,7 +169,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#3r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)), ] = note: number of external vids: 5 = note: where >::AssocType: '_#4r @@ -190,7 +190,7 @@ LL | | } '_#1r, '_#2r, '_#3r, - T + T, ] note: External requirements @@ -203,7 +203,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#1r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), ] = note: late-bound region is '_#3r = note: number of external vids: 4 @@ -223,7 +223,7 @@ LL | | } | = note: defining type: DefId(0/0:13 ~ projection_two_region_trait_bound_closure[317d]::two_regions[0]) with substs [ '_#1r, - T + T, ] error: lifetime may not live long enough @@ -248,7 +248,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#2r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), ] = note: number of external vids: 4 = note: where >::AssocType: '_#3r @@ -268,7 +268,7 @@ LL | | } = note: defining type: DefId(0/0:14 ~ projection_two_region_trait_bound_closure[317d]::two_regions_outlive[0]) with substs [ '_#1r, '_#2r, - T + T, ] note: External requirements @@ -281,7 +281,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); '_#1r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), ] = note: number of external vids: 3 = note: where >::AssocType: '_#2r @@ -300,7 +300,7 @@ LL | | } | = note: defining type: DefId(0/0:15 ~ projection_two_region_trait_bound_closure[317d]::one_region[0]) with substs [ '_#1r, - T + T, ] error: aborting due to 3 previous errors diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr index 41c2916dca3b7..6f3071becfd0e 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr @@ -7,7 +7,7 @@ LL | twice(cell, value, |a, b| invoke(a, b)); = note: defining type: DefId(0/1:14 ~ ty_param_closure_approximate_lower_bound[317d]::generic[0]::{{closure}}[0]) with closure substs [ T, i16, - for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) T)) + for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) T)), ] = note: number of external vids: 2 = note: where T: '_#1r @@ -22,7 +22,7 @@ LL | | } | |_^ | = note: defining type: DefId(0/0:5 ~ ty_param_closure_approximate_lower_bound[317d]::generic[0]) with substs [ - T + T, ] note: External requirements @@ -34,7 +34,7 @@ LL | twice(cell, value, |a, b| invoke(a, b)); = note: defining type: DefId(0/1:17 ~ ty_param_closure_approximate_lower_bound[317d]::generic_fail[0]::{{closure}}[0]) with closure substs [ T, i16, - for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) T)) + for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0:0), 's)) T)), ] = note: late-bound region is '_#2r = note: number of external vids: 3 @@ -50,7 +50,7 @@ LL | | } | |_^ | = note: defining type: DefId(0/0:6 ~ ty_param_closure_approximate_lower_bound[317d]::generic_fail[0]) with substs [ - T + T, ] error[E0309]: the parameter type `T` may not live long enough diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr index 6a201b8523ad3..cdb715762031f 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr @@ -8,7 +8,7 @@ LL | with_signature(x, |y| y) '_#1r, T, i32, - extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn std::fmt::Debug + '_#2r)> + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn std::fmt::Debug + '_#2r)>, ] = note: number of external vids: 3 = note: where T: '_#2r @@ -27,7 +27,7 @@ LL | | } | = note: defining type: DefId(0/0:5 ~ ty_param_closure_outlives_from_return_type[317d]::no_region[0]) with substs [ '_#1r, - T + T, ] error[E0309]: the parameter type `T` may not live long enough diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr index 4ddc54717fbb1..68798a335f9df 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr @@ -14,7 +14,7 @@ LL | | }) = note: defining type: DefId(0/1:16 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]::{{closure}}[0]) with closure substs [ T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#1r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#1r ()>, T)), ] = note: late-bound region is '_#2r = note: number of external vids: 3 @@ -33,7 +33,7 @@ LL | | } | |_^ | = note: defining type: DefId(0/0:6 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]) with substs [ - T + T, ] error[E0309]: the parameter type `T` may not live long enough @@ -68,7 +68,7 @@ LL | | }) '_#1r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), ] = note: number of external vids: 3 = note: where T: '_#2r @@ -87,7 +87,7 @@ LL | | } | = note: defining type: DefId(0/0:7 ~ ty_param_closure_outlives_from_where_clause[317d]::correct_region[0]) with substs [ '_#1r, - T + T, ] note: External requirements @@ -105,7 +105,7 @@ LL | | }) '_#1r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), ] = note: late-bound region is '_#3r = note: number of external vids: 4 @@ -125,7 +125,7 @@ LL | | } | = note: defining type: DefId(0/0:8 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]) with substs [ '_#1r, - T + T, ] error[E0309]: the parameter type `T` may not live long enough @@ -156,7 +156,7 @@ LL | | }) '_#2r, T, i32, - extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), ] = note: number of external vids: 4 = note: where T: '_#3r @@ -176,7 +176,7 @@ LL | | } = note: defining type: DefId(0/0:9 ~ ty_param_closure_outlives_from_where_clause[317d]::outlives_region[0]) with substs [ '_#1r, '_#2r, - T + T, ] error: aborting due to 2 previous errors diff --git a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout index 09340988c8968..618380d7f0bee 100644 --- a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout +++ b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout @@ -2,79 +2,79 @@ PROC MACRO INPUT (PRETTY-PRINTED): struct M ( $crate :: S ) ; PROC MACRO INPUT: TokenStream [ Ident { ident: "struct", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Ident { ident: "M", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ':', spacing: Joint, - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ':', spacing: Alone, - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Ident { ident: "S", - span: #2 bytes(LO..HI) - } + span: #2 bytes(LO..HI), + }, ], - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ';', spacing: Alone, - span: #2 bytes(LO..HI) - } + span: #2 bytes(LO..HI), + }, ] ATTRIBUTE INPUT (PRETTY-PRINTED): struct A(crate::S); ATTRIBUTE INPUT: TokenStream [ Ident { ident: "struct", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Ident { ident: "A", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ':', spacing: Joint, - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ':', spacing: Alone, - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Ident { ident: "S", - span: #2 bytes(LO..HI) - } + span: #2 bytes(LO..HI), + }, ], - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ';', spacing: Alone, - span: #2 bytes(LO..HI) - } + span: #2 bytes(LO..HI), + }, ] diff --git a/src/test/ui/proc-macro/dollar-crate.stdout b/src/test/ui/proc-macro/dollar-crate.stdout index c47b3603f41c0..454da53943054 100644 --- a/src/test/ui/proc-macro/dollar-crate.stdout +++ b/src/test/ui/proc-macro/dollar-crate.stdout @@ -2,239 +2,239 @@ PROC MACRO INPUT (PRETTY-PRINTED): struct M ( $crate :: S ) ; PROC MACRO INPUT: TokenStream [ Ident { ident: "struct", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Ident { ident: "M", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ':', spacing: Joint, - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ':', spacing: Alone, - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Ident { ident: "S", - span: #2 bytes(LO..HI) - } + span: #2 bytes(LO..HI), + }, ], - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ';', spacing: Alone, - span: #2 bytes(LO..HI) - } + span: #2 bytes(LO..HI), + }, ] ATTRIBUTE INPUT (PRETTY-PRINTED): struct A(crate::S); ATTRIBUTE INPUT: TokenStream [ Ident { ident: "struct", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Ident { ident: "A", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ':', spacing: Joint, - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ':', spacing: Alone, - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Ident { ident: "S", - span: #2 bytes(LO..HI) - } + span: #2 bytes(LO..HI), + }, ], - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ';', spacing: Alone, - span: #2 bytes(LO..HI) - } + span: #2 bytes(LO..HI), + }, ] DERIVE INPUT (PRETTY-PRINTED): struct D(crate::S); DERIVE INPUT: TokenStream [ Ident { ident: "struct", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Ident { ident: "D", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ':', spacing: Joint, - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ':', spacing: Alone, - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Ident { ident: "S", - span: #2 bytes(LO..HI) - } + span: #2 bytes(LO..HI), + }, ], - span: #2 bytes(LO..HI) + span: #2 bytes(LO..HI), }, Punct { ch: ';', spacing: Alone, - span: #2 bytes(LO..HI) - } + span: #2 bytes(LO..HI), + }, ] PROC MACRO INPUT (PRETTY-PRINTED): struct M ( $crate :: S ) ; PROC MACRO INPUT: TokenStream [ Ident { ident: "struct", - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Ident { ident: "M", - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Punct { ch: ':', spacing: Joint, - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Punct { ch: ':', spacing: Alone, - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Ident { ident: "S", - span: #10 bytes(LO..HI) - } + span: #10 bytes(LO..HI), + }, ], - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Punct { ch: ';', spacing: Alone, - span: #10 bytes(LO..HI) - } + span: #10 bytes(LO..HI), + }, ] ATTRIBUTE INPUT (PRETTY-PRINTED): struct A(::dollar_crate_external::S); ATTRIBUTE INPUT: TokenStream [ Ident { ident: "struct", - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Ident { ident: "A", - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Punct { ch: ':', spacing: Joint, - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Punct { ch: ':', spacing: Alone, - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Ident { ident: "S", - span: #10 bytes(LO..HI) - } + span: #10 bytes(LO..HI), + }, ], - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Punct { ch: ';', spacing: Alone, - span: #10 bytes(LO..HI) - } + span: #10 bytes(LO..HI), + }, ] DERIVE INPUT (PRETTY-PRINTED): struct D(::dollar_crate_external::S); DERIVE INPUT: TokenStream [ Ident { ident: "struct", - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Ident { ident: "D", - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Punct { ch: ':', spacing: Joint, - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Punct { ch: ':', spacing: Alone, - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Ident { ident: "S", - span: #10 bytes(LO..HI) - } + span: #10 bytes(LO..HI), + }, ], - span: #10 bytes(LO..HI) + span: #10 bytes(LO..HI), }, Punct { ch: ';', spacing: Alone, - span: #10 bytes(LO..HI) - } + span: #10 bytes(LO..HI), + }, ] diff --git a/src/test/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs b/src/test/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs index 67f7f80a9e2da..f1b73ee115db1 100644 --- a/src/test/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs +++ b/src/test/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs @@ -64,22 +64,22 @@ fn validate_stderr(stderr: Vec) { ":28] Point{x: 42, y: 24,} = Point {", " x: 42,", - " y: 24", + " y: 24,", "}", ":29] b = Point {", " x: 42,", - " y: 24", + " y: 24,", "}", ":37]", ":41] &a = NoCopy(", - " 1337", + " 1337,", ")", ":41] dbg!(& a) = NoCopy(", - " 1337", + " 1337,", ")", ":46] f(&42) = 42", From 61b487ca8be1d8667a82c1357dc2729cfe56186d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 29 Mar 2019 15:57:14 -0700 Subject: [PATCH 9/9] wasi: Fill out `std::fs` module for WASI This commit fills out the `std::fs` module and implementation for WASI. Not all APIs are implemented, such as permissions-related ones and `canonicalize`, but all others APIs have been implemented and very lightly tested so far. We'll eventually want to run a more exhaustive test suite! For now the highlights of this commit are: * The `std::fs::File` type is now backed by `WasiFd`, a raw WASI file descriptor. * All APIs in `std::fs` (except permissions/canonicalize) have implementations for the WASI target. * A suite of unstable extension traits were added to `std::os::wasi::fs`. These traits expose the raw filesystem functionality of WASI, namely `*at` syscalls (opening a file relative to an already opened one, for example). Additionally metadata only available on wasi is exposed through these traits. Perhaps one of the most notable parts is the implementation of path-taking APIs. WASI actually has no fundamental API that just takes a path, but rather everything is relative to a previously opened file descriptor. To allow existing APIs to work (that only take a path) WASI has a few syscalls to learn about "pre opened" file descriptors by the runtime. We use these to build a map of existing directory names to file descriptors, and then when using a path we try to anchor it at an already-opened file. This support is very rudimentary though and is intended to be shared with C since it's likely to be so tricky. For now though the C library doesn't expose quite an API for us to use, so we implement it for now and will swap it out as soon as one is available. --- src/libstd/fs.rs | 8 + src/libstd/sys/redox/fs.rs | 42 +- src/libstd/sys/unix/fs.rs | 23 +- src/libstd/sys/wasi/ext/ffi.rs | 57 +-- src/libstd/sys/wasi/ext/fs.rs | 412 ++++++++++++++++++++ src/libstd/sys/wasi/ext/io.rs | 87 +++++ src/libstd/sys/wasi/ext/mod.rs | 8 + src/libstd/sys/wasi/fd.rs | 34 +- src/libstd/sys/wasi/fs.rs | 682 ++++++++++++++++++++++++++------- src/libstd/sys/wasi/process.rs | 4 +- src/libstd/sys/wasi/time.rs | 4 + src/libstd/sys_common/fs.rs | 41 ++ src/libstd/sys_common/mod.rs | 1 + 13 files changed, 1141 insertions(+), 262 deletions(-) create mode 100644 src/libstd/sys/wasi/ext/fs.rs create mode 100644 src/libstd/sys/wasi/ext/io.rs create mode 100644 src/libstd/sys_common/fs.rs diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 705dc8f40b5a0..29f4c78e27b48 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -881,6 +881,10 @@ impl OpenOptions { } } +impl AsInner for OpenOptions { + fn as_inner(&self) -> &fs_imp::OpenOptions { &self.0 } +} + impl AsInnerMut for OpenOptions { fn as_inner_mut(&mut self) -> &mut fs_imp::OpenOptions { &mut self.0 } } @@ -1104,6 +1108,10 @@ impl AsInner for Metadata { fn as_inner(&self) -> &fs_imp::FileAttr { &self.0 } } +impl FromInner for Metadata { + fn from_inner(attr: fs_imp::FileAttr) -> Metadata { Metadata(attr) } +} + impl Permissions { /// Returns `true` if these permissions describe a readonly (unwritable) file. /// diff --git a/src/libstd/sys/redox/fs.rs b/src/libstd/sys/redox/fs.rs index 3ef9925705fb8..ebefbc942103c 100644 --- a/src/libstd/sys/redox/fs.rs +++ b/src/libstd/sys/redox/fs.rs @@ -2,7 +2,7 @@ use crate::os::unix::prelude::*; use crate::ffi::{OsString, OsStr}; use crate::fmt; -use crate::io::{self, Error, ErrorKind, SeekFrom}; +use crate::io::{self, Error, SeekFrom}; use crate::path::{Path, PathBuf}; use crate::sync::Arc; use crate::sys::fd::FileDesc; @@ -10,6 +10,9 @@ use crate::sys::time::SystemTime; use crate::sys::{cvt, syscall}; use crate::sys_common::{AsInner, FromInner}; +pub use crate::sys_common::fs::copy; +pub use crate::sys_common::fs::remove_dir_all; + pub struct File(FileDesc); #[derive(Clone)] @@ -392,27 +395,6 @@ pub fn rmdir(p: &Path) -> io::Result<()> { Ok(()) } -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - let filetype = lstat(path)?.file_type(); - if filetype.is_symlink() { - unlink(path) - } else { - remove_dir_all_recursive(path) - } -} - -fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { - for child in readdir(path)? { - let child = child?; - if child.file_type()?.is_dir() { - remove_dir_all_recursive(&child.path())?; - } else { - unlink(&child.path())?; - } - } - rmdir(path) -} - pub fn readlink(p: &Path) -> io::Result { let fd = cvt(syscall::open(p.to_str().unwrap(), syscall::O_CLOEXEC | syscall::O_SYMLINK | syscall::O_RDONLY))?; @@ -455,19 +437,3 @@ pub fn canonicalize(p: &Path) -> io::Result { let file = File(FileDesc::new(fd)); file.path() } - -pub fn copy(from: &Path, to: &Path) -> io::Result { - use crate::fs::{File, set_permissions}; - if !from.is_file() { - return Err(Error::new(ErrorKind::InvalidInput, - "the source path is not an existing regular file")) - } - - let mut reader = File::open(from)?; - let mut writer = File::create(to)?; - let perm = reader.metadata()?.permissions(); - - let ret = io::copy(&mut reader, &mut writer)?; - set_permissions(to, perm)?; - Ok(ret) -} diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index a36dae2f5a19d..dc3dcb5817c05 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -36,6 +36,8 @@ use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, off_t as off64_t, target_os = "fuchsia")))] use libc::{readdir_r as readdir64_r}; +pub use crate::sys_common::fs::remove_dir_all; + pub struct File(FileDesc); #[derive(Clone)] @@ -734,27 +736,6 @@ pub fn rmdir(p: &Path) -> io::Result<()> { Ok(()) } -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - let filetype = lstat(path)?.file_type(); - if filetype.is_symlink() { - unlink(path) - } else { - remove_dir_all_recursive(path) - } -} - -fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { - for child in readdir(path)? { - let child = child?; - if child.file_type()?.is_dir() { - remove_dir_all_recursive(&child.path())?; - } else { - unlink(&child.path())?; - } - } - rmdir(path) -} - pub fn readlink(p: &Path) -> io::Result { let c_path = cstr(p)?; let p = c_path.as_ptr(); diff --git a/src/libstd/sys/wasi/ext/ffi.rs b/src/libstd/sys/wasi/ext/ffi.rs index 07b93dd143f8f..f71f316d1ba10 100644 --- a/src/libstd/sys/wasi/ext/ffi.rs +++ b/src/libstd/sys/wasi/ext/ffi.rs @@ -2,60 +2,5 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::ffi::{OsStr, OsString}; -use crate::mem; -use crate::sys::os_str::Buf; -use crate::sys_common::{FromInner, IntoInner, AsInner}; - -/// WASI-specific extensions to [`OsString`]. -/// -/// [`OsString`]: ../../../../std/ffi/struct.OsString.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait OsStringExt { - /// Creates an `OsString` from a byte vector. - #[stable(feature = "rust1", since = "1.0.0")] - fn from_vec(vec: Vec) -> Self; - - /// Yields the underlying byte vector of this `OsString`. - #[stable(feature = "rust1", since = "1.0.0")] - fn into_vec(self) -> Vec; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl OsStringExt for OsString { - fn from_vec(vec: Vec) -> OsString { - FromInner::from_inner(Buf { inner: vec }) - } - fn into_vec(self) -> Vec { - self.into_inner().inner - } -} - -/// WASI-specific extensions to [`OsStr`]. -/// -/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html #[stable(feature = "rust1", since = "1.0.0")] -pub trait OsStrExt { - #[stable(feature = "rust1", since = "1.0.0")] - /// Creates an [`OsStr`] from a byte slice. - /// - /// [`OsStr`]: ../../../ffi/struct.OsStr.html - fn from_bytes(slice: &[u8]) -> &Self; - - /// Gets the underlying byte view of the [`OsStr`] slice. - /// - /// [`OsStr`]: ../../../ffi/struct.OsStr.html - #[stable(feature = "rust1", since = "1.0.0")] - fn as_bytes(&self) -> &[u8]; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl OsStrExt for OsStr { - fn from_bytes(slice: &[u8]) -> &OsStr { - unsafe { mem::transmute(slice) } - } - fn as_bytes(&self) -> &[u8] { - &self.as_inner().inner - } -} - +pub use crate::sys_common::os_str_bytes::*; diff --git a/src/libstd/sys/wasi/ext/fs.rs b/src/libstd/sys/wasi/ext/fs.rs new file mode 100644 index 0000000000000..53f415c78219e --- /dev/null +++ b/src/libstd/sys/wasi/ext/fs.rs @@ -0,0 +1,412 @@ +//! WASI-specific extensions to primitives in the `std::fs` module. + +#![unstable(feature = "wasi_ext", issue = "0")] + +use crate::fs::{self, File, Metadata, OpenOptions}; +use crate::io::{self, IoVec, IoVecMut}; +use crate::os::wasi::ffi::OsStrExt; +use crate::path::{Path, PathBuf}; +use crate::sys_common::{AsInner, AsInnerMut, FromInner}; + +/// WASI-specific extensions to [`File`]. +/// +/// [`File`]: ../../../../std/fs/struct.File.html +pub trait FileExt { + /// Reads a number of bytes starting from a given offset. + /// + /// Returns the number of bytes read. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// Note that similar to [`File::read_vectored`], it is not an error to + /// return with a short read. + /// + /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read_vectored + fn read_at(&self, bufs: &mut [IoVecMut<'_>], offset: u64) -> io::Result; + + /// Writes a number of bytes starting from a given offset. + /// + /// Returns the number of bytes written. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// When writing beyond the end of the file, the file is appropriately + /// extended and the intermediate bytes are initialized with the value 0. + /// + /// Note that similar to [`File::write_vectored`], it is not an error to return a + /// short write. + /// + /// [`File::write`]: ../../../../std/fs/struct.File.html#method.write_vectored + fn write_at(&self, bufs: &[IoVec<'_>], offset: u64) -> io::Result; + + /// Returns the current position within the file. + /// + /// This corresponds to the `__wasi_fd_tell` syscall and is similar to + /// `seek` where you offset 0 bytes from the current position. + fn tell(&self) -> io::Result; + + /// Adjust the flags associated with this file. + /// + /// This corresponds to the `__wasi_fd_fdstat_set_flags` syscall. + fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>; + + /// Adjust the rights associated with this file. + /// + /// This corresponds to the `__wasi_fd_fdstat_set_rights` syscall. + fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>; + + /// Provide file advisory information on a file descriptor. + /// + /// This corresponds to the `__wasi_fd_advise` syscall. + fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>; + + /// Force the allocation of space in a file. + /// + /// This corresponds to the `__wasi_fd_allocate` syscall. + fn allocate(&self, offset: u64, len: u64) -> io::Result<()>; + + /// Create a directory. + /// + /// This corresponds to the `__wasi_path_create_directory` syscall. + fn create_directory>(&self, dir: P) -> io::Result<()>; + + /// Read the contents of a symbolic link. + /// + /// This corresponds to the `__wasi_path_readlink` syscall. + fn read_link>(&self, path: P) -> io::Result; + + /// Return the attributes of a file or directory. + /// + /// This corresponds to the `__wasi_path_filestat_get` syscall. + fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result; + + /// Unlink a file. + /// + /// This corresponds to the `__wasi_path_unlink_file` syscall. + fn remove_file>(&self, path: P) -> io::Result<()>; + + /// Remove a directory. + /// + /// This corresponds to the `__wasi_path_remove_directory` syscall. + fn remove_directory>(&self, path: P) -> io::Result<()>; +} + +// FIXME: bind __wasi_fd_fdstat_get - need to define a custom return type +// FIXME: bind __wasi_fd_readdir - can't return `ReadDir` since we only have entry name +// FIXME: bind __wasi_fd_filestat_set_times maybe? - on crates.io for unix +// FIXME: bind __wasi_path_filestat_set_times maybe? - on crates.io for unix +// FIXME: bind __wasi_poll_oneoff maybe? - probably should wait for I/O to settle +// FIXME: bind __wasi_random_get maybe? - on crates.io for unix + +impl FileExt for fs::File { + fn read_at(&self, bufs: &mut [IoVecMut<'_>], offset: u64) -> io::Result { + self.as_inner().fd().pread(bufs, offset) + } + + fn write_at(&self, bufs: &[IoVec<'_>], offset: u64) -> io::Result { + self.as_inner().fd().pwrite(bufs, offset) + } + + fn tell(&self) -> io::Result { + self.as_inner().fd().tell() + } + + fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> { + self.as_inner().fd().set_flags(flags) + } + + fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> { + self.as_inner().fd().set_rights(rights, inheriting) + } + + fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> { + self.as_inner().fd().advise(offset, len, advice) + } + + fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { + self.as_inner().fd().allocate(offset, len) + } + + fn create_directory>(&self, dir: P) -> io::Result<()> { + self.as_inner() + .fd() + .create_directory(dir.as_ref().as_os_str().as_bytes()) + } + + fn read_link>(&self, path: P) -> io::Result { + self.as_inner().read_link(path.as_ref()) + } + + fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result { + let m = self.as_inner().metadata_at(lookup_flags, path.as_ref())?; + Ok(FromInner::from_inner(m)) + } + + fn remove_file>(&self, path: P) -> io::Result<()> { + self.as_inner() + .fd() + .unlink_file(path.as_ref().as_os_str().as_bytes()) + } + + fn remove_directory>(&self, path: P) -> io::Result<()> { + self.as_inner() + .fd() + .remove_directory(path.as_ref().as_os_str().as_bytes()) + } +} + +/// WASI-specific extensions to [`fs::OpenOptions`]. +/// +/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html +pub trait OpenOptionsExt { + /// Pass custom `dirflags` argument to `__wasi_path_open`. + /// + /// This option configures the `dirflags` argument to the + /// `__wasi_path_open` syscall which `OpenOptions` will eventually call. The + /// `dirflags` argument configures how the file is looked up, currently + /// primarily affecting whether symlinks are followed or not. + /// + /// By default this value is `__WASI_LOOKUP_SYMLINK_FOLLOW`, or symlinks are + /// followed. You can call this method with 0 to disable following symlinks + fn lookup_flags(&mut self, flags: u32) -> &mut Self; + + /// Indicates whether `OpenOptions` must open a directory or not. + /// + /// This method will configure whether the `__WASI_O_DIRECTORY` flag is + /// passed when opening a file. When passed it will require that the opened + /// path is a directory. + /// + /// This option is by default `false` + fn directory(&mut self, dir: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_DSYNC` is passed in the `fs_flags` + /// field of `__wasi_path_open`. + /// + /// This option is by default `false` + fn dsync(&mut self, dsync: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_NONBLOCK` is passed in the `fs_flags` + /// field of `__wasi_path_open`. + /// + /// This option is by default `false` + fn nonblock(&mut self, nonblock: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_RSYNC` is passed in the `fs_flags` + /// field of `__wasi_path_open`. + /// + /// This option is by default `false` + fn rsync(&mut self, rsync: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_SYNC` is passed in the `fs_flags` + /// field of `__wasi_path_open`. + /// + /// This option is by default `false` + fn sync(&mut self, sync: bool) -> &mut Self; + + /// Indicates the value that should be passed in for the `fs_rights_base` + /// parameter of `__wasi_path_open`. + /// + /// This option defaults based on the `read` and `write` configuration of + /// this `OpenOptions` builder. If this method is called, however, the + /// exact mask passed in will be used instead. + fn fs_rights_base(&mut self, rights: u64) -> &mut Self; + + /// Indicates the value that should be passed in for the + /// `fs_rights_inheriting` parameter of `__wasi_path_open`. + /// + /// The default for this option is the same value as what will be passed + /// for the `fs_rights_base` parameter but if this method is called then + /// the specified value will be used instead. + fn fs_rights_inheriting(&mut self, rights: u64) -> &mut Self; + + /// Open a file or directory. + /// + /// This corresponds to the `__wasi_path_open` syscall. + fn open_at>(&self, file: &File, path: P) -> io::Result; +} + +impl OpenOptionsExt for OpenOptions { + fn lookup_flags(&mut self, flags: u32) -> &mut OpenOptions { + self.as_inner_mut().lookup_flags(flags); + self + } + + fn directory(&mut self, dir: bool) -> &mut OpenOptions { + self.as_inner_mut().directory(dir); + self + } + + fn dsync(&mut self, enabled: bool) -> &mut OpenOptions { + self.as_inner_mut().dsync(enabled); + self + } + + fn nonblock(&mut self, enabled: bool) -> &mut OpenOptions { + self.as_inner_mut().nonblock(enabled); + self + } + + fn rsync(&mut self, enabled: bool) -> &mut OpenOptions { + self.as_inner_mut().rsync(enabled); + self + } + + fn sync(&mut self, enabled: bool) -> &mut OpenOptions { + self.as_inner_mut().sync(enabled); + self + } + + fn fs_rights_base(&mut self, rights: u64) -> &mut OpenOptions { + self.as_inner_mut().fs_rights_base(rights); + self + } + + fn fs_rights_inheriting(&mut self, rights: u64) -> &mut OpenOptions { + self.as_inner_mut().fs_rights_inheriting(rights); + self + } + + fn open_at>(&self, file: &File, path: P) -> io::Result { + let inner = file.as_inner().open_at(path.as_ref(), self.as_inner())?; + Ok(File::from_inner(inner)) + } +} + +/// WASI-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html +pub trait MetadataExt { + /// Returns the `st_dev` field of the internal `__wasi_filestat_t` + fn dev(&self) -> u64; + /// Returns the `st_ino` field of the internal `__wasi_filestat_t` + fn ino(&self) -> u64; + /// Returns the `st_nlink` field of the internal `__wasi_filestat_t` + fn nlink(&self) -> u32; + /// Returns the `st_atim` field of the internal `__wasi_filestat_t` + fn atim(&self) -> u64; + /// Returns the `st_mtim` field of the internal `__wasi_filestat_t` + fn mtim(&self) -> u64; + /// Returns the `st_ctim` field of the internal `__wasi_filestat_t` + fn ctim(&self) -> u64; +} + +impl MetadataExt for fs::Metadata { + fn dev(&self) -> u64 { + self.as_inner().as_wasi().st_dev + } + fn ino(&self) -> u64 { + self.as_inner().as_wasi().st_ino + } + fn nlink(&self) -> u32 { + self.as_inner().as_wasi().st_nlink + } + fn atim(&self) -> u64 { + self.as_inner().as_wasi().st_atim + } + fn mtim(&self) -> u64 { + self.as_inner().as_wasi().st_mtim + } + fn ctim(&self) -> u64 { + self.as_inner().as_wasi().st_ctim + } +} + +/// WASI-specific extensions for [`FileType`]. +/// +/// Adds support for special WASI file types such as block/character devices, +/// pipes, and sockets. +/// +/// [`FileType`]: ../../../../std/fs/struct.FileType.html +pub trait FileTypeExt { + /// Returns `true` if this file type is a block device. + fn is_block_device(&self) -> bool; + /// Returns `true` if this file type is a character device. + fn is_character_device(&self) -> bool; + /// Returns `true` if this file type is a socket datagram. + fn is_socket_dgram(&self) -> bool; + /// Returns `true` if this file type is a socket stream. + fn is_socket_stream(&self) -> bool; +} + +impl FileTypeExt for fs::FileType { + fn is_block_device(&self) -> bool { + self.as_inner().bits() == libc::__WASI_FILETYPE_BLOCK_DEVICE + } + fn is_character_device(&self) -> bool { + self.as_inner().bits() == libc::__WASI_FILETYPE_CHARACTER_DEVICE + } + fn is_socket_dgram(&self) -> bool { + self.as_inner().bits() == libc::__WASI_FILETYPE_SOCKET_DGRAM + } + fn is_socket_stream(&self) -> bool { + self.as_inner().bits() == libc::__WASI_FILETYPE_SOCKET_STREAM + } +} + +/// WASI-specific extension methods for [`fs::DirEntry`]. +/// +/// [`fs::DirEntry`]: ../../../../std/fs/struct.DirEntry.html +pub trait DirEntryExt { + /// Returns the underlying `d_ino` field of the `__wasi_dirent_t` + fn ino(&self) -> u64; +} + +impl DirEntryExt for fs::DirEntry { + fn ino(&self) -> u64 { + self.as_inner().ino() + } +} + +/// Create a hard link. +/// +/// This corresponds to the `__wasi_path_link` syscall. +pub fn link, U: AsRef>( + old_fd: &File, + old_flags: u32, + old_path: P, + new_fd: &File, + new_path: U, +) -> io::Result<()> { + old_fd.as_inner().fd().link( + old_flags, + old_path.as_ref().as_os_str().as_bytes(), + new_fd.as_inner().fd(), + new_path.as_ref().as_os_str().as_bytes(), + ) +} + +/// Rename a file or directory. +/// +/// This corresponds to the `__wasi_path_rename` syscall. +pub fn rename, U: AsRef>( + old_fd: &File, + old_path: P, + new_fd: &File, + new_path: U, +) -> io::Result<()> { + old_fd.as_inner().fd().rename( + old_path.as_ref().as_os_str().as_bytes(), + new_fd.as_inner().fd(), + new_path.as_ref().as_os_str().as_bytes(), + ) +} + +/// Create a symbolic link. +/// +/// This corresponds to the `__wasi_path_symlink` syscall. +pub fn symlink, U: AsRef>( + old_path: P, + fd: &File, + new_path: U, +) -> io::Result<()> { + fd.as_inner().fd().symlink( + old_path.as_ref().as_os_str().as_bytes(), + new_path.as_ref().as_os_str().as_bytes(), + ) +} diff --git a/src/libstd/sys/wasi/ext/io.rs b/src/libstd/sys/wasi/ext/io.rs new file mode 100644 index 0000000000000..cf75a96d28c1a --- /dev/null +++ b/src/libstd/sys/wasi/ext/io.rs @@ -0,0 +1,87 @@ +//! WASI-specific extensions to general I/O primitives + +#![unstable(feature = "wasi_ext", issue = "0")] + +use crate::fs; +use crate::io; +use crate::sys; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +/// Raw file descriptors. +pub type RawFd = u32; + +/// A trait to extract the raw WASI file descriptor from an underlying +/// object. +pub trait AsRawFd { + /// Extracts the raw file descriptor. + /// + /// This method does **not** pass ownership of the raw file descriptor + /// to the caller. The descriptor is only guaranteed to be valid while + /// the original object has not yet been destroyed. + fn as_raw_fd(&self) -> RawFd; +} + +/// A trait to express the ability to construct an object from a raw file +/// descriptor. +pub trait FromRawFd { + /// Constructs a new instance of `Self` from the given raw file + /// descriptor. + /// + /// This function **consumes ownership** of the specified file + /// descriptor. The returned object will take responsibility for closing + /// it when the object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + unsafe fn from_raw_fd(fd: RawFd) -> Self; +} + +/// A trait to express the ability to consume an object and acquire ownership of +/// its raw file descriptor. +pub trait IntoRawFd { + /// Consumes this object, returning the raw underlying file descriptor. + /// + /// This function **transfers ownership** of the underlying file descriptor + /// to the caller. Callers are then the unique owners of the file descriptor + /// and must close the descriptor once it's no longer needed. + fn into_raw_fd(self) -> RawFd; +} + +impl AsRawFd for fs::File { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().as_raw() + } +} + +impl FromRawFd for fs::File { + unsafe fn from_raw_fd(fd: RawFd) -> fs::File { + fs::File::from_inner(sys::fs::File::from_inner(fd)) + } +} + +impl IntoRawFd for fs::File { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +impl AsRawFd for io::Stdin { + fn as_raw_fd(&self) -> RawFd { + libc::STDIN_FILENO as u32 + } +} + +impl AsRawFd for io::Stdout { + fn as_raw_fd(&self) -> RawFd { + libc::STDOUT_FILENO as u32 + } +} + +impl AsRawFd for io::Stderr { + fn as_raw_fd(&self) -> RawFd { + libc::STDERR_FILENO as u32 + } +} diff --git a/src/libstd/sys/wasi/ext/mod.rs b/src/libstd/sys/wasi/ext/mod.rs index 877b9ed89d81e..1c24b244b8cd0 100644 --- a/src/libstd/sys/wasi/ext/mod.rs +++ b/src/libstd/sys/wasi/ext/mod.rs @@ -1,4 +1,6 @@ pub mod ffi; +pub mod fs; +pub mod io; /// A prelude for conveniently writing platform-specific code. /// @@ -7,4 +9,10 @@ pub mod ffi; pub mod prelude { #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use crate::sys::ext::ffi::{OsStringExt, OsStrExt}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::sys::ext::fs::{FileExt, DirEntryExt, MetadataExt, OpenOptionsExt}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::sys::ext::fs::FileTypeExt; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::sys::ext::io::{AsRawFd, IntoRawFd, FromRawFd}; } diff --git a/src/libstd/sys/wasi/fd.rs b/src/libstd/sys/wasi/fd.rs index 29e1880b8f135..0b68b6f4d9c72 100644 --- a/src/libstd/sys/wasi/fd.rs +++ b/src/libstd/sys/wasi/fd.rs @@ -6,6 +6,7 @@ use crate::net::Shutdown; use crate::sys::cvt_wasi; use libc::{self, c_char, c_void}; +#[derive(Debug)] pub struct WasiFd { fd: libc::__wasi_fd_t, } @@ -52,6 +53,16 @@ impl WasiFd { WasiFd { fd } } + pub fn into_raw(self) -> libc::__wasi_fd_t { + let ret = self.fd; + mem::forget(self); + ret + } + + pub fn as_raw(&self) -> libc::__wasi_fd_t { + self.fd + } + pub fn datasync(&self) -> io::Result<()> { cvt_wasi(unsafe { libc::__wasi_fd_datasync(self.fd) }) } @@ -123,7 +134,7 @@ impl WasiFd { cvt_wasi(unsafe { libc::__wasi_fd_allocate(self.fd, offset, len) }) } - pub fn crate_directory(&self, path: &[u8]) -> io::Result<()> { + pub fn create_directory(&self, path: &[u8]) -> io::Result<()> { cvt_wasi(unsafe { libc::__wasi_path_create_directory(self.fd, path.as_ptr() as *const c_char, path.len()) }) @@ -217,7 +228,9 @@ impl WasiFd { }) } - // FIXME: __wasi_fd_filestat_get + pub fn filestat_get(&self, buf: *mut libc::__wasi_filestat_t) -> io::Result<()> { + cvt_wasi(unsafe { libc::__wasi_fd_filestat_get(self.fd, buf) }) + } pub fn filestat_set_times( &self, @@ -232,7 +245,22 @@ impl WasiFd { cvt_wasi(unsafe { libc::__wasi_fd_filestat_set_size(self.fd, size) }) } - // FIXME: __wasi_path_filestat_get + pub fn path_filestat_get( + &self, + flags: LookupFlags, + path: &[u8], + buf: *mut libc::__wasi_filestat_t, + ) -> io::Result<()> { + cvt_wasi(unsafe { + libc::__wasi_path_filestat_get( + self.fd, + flags, + path.as_ptr() as *const c_char, + path.len(), + buf, + ) + }) + } pub fn path_filestat_set_times( &self, diff --git a/src/libstd/sys/wasi/fs.rs b/src/libstd/sys/wasi/fs.rs index 68c8e9356a89a..7b1c2bd79cc16 100644 --- a/src/libstd/sys/wasi/fs.rs +++ b/src/libstd/sys/wasi/fs.rs @@ -1,138 +1,144 @@ -use crate::ffi::OsString; +use crate::collections::HashMap; +use crate::ffi::{OsStr, OsString}; use crate::fmt; -use crate::hash::{Hash, Hasher}; -use crate::io::{self, SeekFrom}; +use crate::io::{self, IoVec, IoVecMut, SeekFrom}; +use crate::iter; +use crate::mem::{self, ManuallyDrop}; +use crate::os::wasi::ffi::{OsStrExt, OsStringExt}; use crate::path::{Path, PathBuf}; +use crate::ptr; +use crate::sync::atomic::{AtomicPtr, Ordering::SeqCst}; +use crate::sync::Arc; +use crate::sys::fd::{DirCookie, WasiFd}; use crate::sys::time::SystemTime; -use crate::sys::{unsupported, Void}; +use crate::sys::{cvt_wasi, unsupported}; +use crate::sys_common::FromInner; -pub struct File(Void); +pub use crate::sys_common::fs::copy; +pub use crate::sys_common::fs::remove_dir_all; -pub struct FileAttr(Void); +pub struct File { + fd: WasiFd, +} + +#[derive(Clone)] +pub struct FileAttr { + meta: libc::__wasi_filestat_t, +} -pub struct ReadDir(Void); +pub struct ReadDir { + inner: Arc, + cookie: Option, + buf: Vec, + offset: usize, + cap: usize, +} -pub struct DirEntry(Void); +struct ReadDirInner { + root: PathBuf, + dir: File, +} + +pub struct DirEntry { + meta: libc::__wasi_dirent_t, + name: Vec, + inner: Arc, +} -#[derive(Clone, Debug)] -pub struct OpenOptions { } +#[derive(Clone, Debug, Default)] +pub struct OpenOptions { + read: bool, + write: bool, + dirflags: libc::__wasi_lookupflags_t, + fdflags: libc::__wasi_fdflags_t, + oflags: libc::__wasi_oflags_t, + rights_base: Option, + rights_inheriting: Option, +} -pub struct FilePermissions(Void); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + readonly: bool, +} -pub struct FileType(Void); +#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] +pub struct FileType { + bits: libc::__wasi_filetype_t, +} #[derive(Debug)] -pub struct DirBuilder { } +pub struct DirBuilder {} impl FileAttr { + fn zero() -> FileAttr { + FileAttr { + meta: unsafe { mem::zeroed() }, + } + } + pub fn size(&self) -> u64 { - match self.0 {} + self.meta.st_size } pub fn perm(&self) -> FilePermissions { - match self.0 {} + // not currently implemented in wasi yet + FilePermissions { readonly: false } } pub fn file_type(&self) -> FileType { - match self.0 {} + FileType { + bits: self.meta.st_filetype, + } } pub fn modified(&self) -> io::Result { - match self.0 {} + Ok(SystemTime::from_wasi_timestamp(self.meta.st_mtim)) } pub fn accessed(&self) -> io::Result { - match self.0 {} + Ok(SystemTime::from_wasi_timestamp(self.meta.st_atim)) } pub fn created(&self) -> io::Result { - match self.0 {} + Ok(SystemTime::from_wasi_timestamp(self.meta.st_ctim)) } -} -impl Clone for FileAttr { - fn clone(&self) -> FileAttr { - match self.0 {} + pub fn as_wasi(&self) -> &libc::__wasi_filestat_t { + &self.meta } } impl FilePermissions { pub fn readonly(&self) -> bool { - match self.0 {} - } - - pub fn set_readonly(&mut self, _readonly: bool) { - match self.0 {} - } -} - -impl Clone for FilePermissions { - fn clone(&self) -> FilePermissions { - match self.0 {} - } -} - -impl PartialEq for FilePermissions { - fn eq(&self, _other: &FilePermissions) -> bool { - match self.0 {} + self.readonly } -} - -impl Eq for FilePermissions { -} -impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + pub fn set_readonly(&mut self, readonly: bool) { + self.readonly = readonly; } } impl FileType { pub fn is_dir(&self) -> bool { - match self.0 {} + self.bits == libc::__WASI_FILETYPE_DIRECTORY } pub fn is_file(&self) -> bool { - match self.0 {} + self.bits == libc::__WASI_FILETYPE_REGULAR_FILE } pub fn is_symlink(&self) -> bool { - match self.0 {} - } -} - -impl Clone for FileType { - fn clone(&self) -> FileType { - match self.0 {} - } -} - -impl Copy for FileType {} - -impl PartialEq for FileType { - fn eq(&self, _other: &FileType) -> bool { - match self.0 {} - } -} - -impl Eq for FileType { -} - -impl Hash for FileType { - fn hash(&self, _h: &mut H) { - match self.0 {} + self.bits == libc::__WASI_FILETYPE_SYMBOLIC_LINK } -} -impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + pub fn bits(&self) -> libc::__wasi_filetype_t { + self.bits } } impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ReadDir").finish() } } @@ -140,155 +146,547 @@ impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { - match self.0 {} + loop { + // If we've reached the capacity of our buffer then we need to read + // some more from the OS, otherwise we pick up at our old offset. + let offset = if self.offset == self.cap { + let cookie = self.cookie.take()?; + match self.inner.dir.fd.readdir(&mut self.buf, cookie) { + Ok(bytes) => self.cap = bytes, + Err(e) => return Some(Err(e)), + } + self.offset = 0; + self.cookie = Some(cookie); + + // If we didn't actually read anything, this is in theory the + // end of the directory. + if self.cap == 0 { + self.cookie = None; + return None; + } + + 0 + } else { + self.offset + }; + let data = &self.buf[offset..self.cap]; + + // If we're not able to read a directory entry then that means it + // must have been truncated at the end of the buffer, so reset our + // offset so we can go back and reread into the buffer, picking up + // where we last left off. + let dirent_size = mem::size_of::(); + if data.len() < dirent_size { + assert!(self.cookie.is_some()); + assert!(self.buf.len() >= dirent_size); + self.offset = self.cap; + continue; + } + let (dirent, data) = data.split_at(dirent_size); + let dirent = + unsafe { ptr::read_unaligned(dirent.as_ptr() as *const libc::__wasi_dirent_t) }; + + // If the file name was truncated, then we need to reinvoke + // `readdir` so we truncate our buffer to start over and reread this + // descriptor. Note that if our offset is 0 that means the file name + // is massive and we need a bigger buffer. + if data.len() < dirent.d_namlen as usize { + if offset == 0 { + let amt_to_add = self.buf.capacity(); + self.buf.extend(iter::repeat(0).take(amt_to_add)); + } + assert!(self.cookie.is_some()); + self.offset = self.cap; + continue; + } + self.cookie = Some(dirent.d_next); + self.offset = offset + dirent_size + dirent.d_namlen as usize; + + let name = &data[..(dirent.d_namlen as usize)]; + + // These names are skipped on all other platforms, so let's skip + // them here too + if name == b"." || name == b".." { + continue; + } + + return Some(Ok(DirEntry { + meta: dirent, + name: name.to_vec(), + inner: self.inner.clone(), + })); + } } } impl DirEntry { pub fn path(&self) -> PathBuf { - match self.0 {} + let name = OsStr::from_bytes(&self.name); + self.inner.root.join(name) } pub fn file_name(&self) -> OsString { - match self.0 {} + OsString::from_vec(self.name.clone()) } pub fn metadata(&self) -> io::Result { - match self.0 {} + metadata_at(&self.inner.dir.fd, 0, OsStr::from_bytes(&self.name).as_ref()) } pub fn file_type(&self) -> io::Result { - match self.0 {} + Ok(FileType { + bits: self.meta.d_type, + }) + } + + pub fn ino(&self) -> libc::__wasi_inode_t { + self.meta.d_ino } } impl OpenOptions { pub fn new() -> OpenOptions { - OpenOptions { } + let mut base = OpenOptions::default(); + base.dirflags = libc::__WASI_LOOKUP_SYMLINK_FOLLOW; + return base; + } + + pub fn read(&mut self, read: bool) { + self.read = read; + } + + pub fn write(&mut self, write: bool) { + self.write = write; + } + + pub fn truncate(&mut self, truncate: bool) { + self.oflag(libc::__WASI_O_TRUNC, truncate); + } + + pub fn create(&mut self, create: bool) { + self.oflag(libc::__WASI_O_CREAT, create); + } + + pub fn create_new(&mut self, create_new: bool) { + self.oflag(libc::__WASI_O_EXCL, create_new); + self.oflag(libc::__WASI_O_CREAT, create_new); + } + + pub fn directory(&mut self, directory: bool) { + self.oflag(libc::__WASI_O_DIRECTORY, directory); + } + + fn oflag(&mut self, bit: libc::__wasi_oflags_t, set: bool) { + if set { + self.oflags |= bit; + } else { + self.oflags &= !bit; + } + } + + pub fn append(&mut self, set: bool) { + self.fdflag(libc::__WASI_FDFLAG_APPEND, set); + } + + pub fn dsync(&mut self, set: bool) { + self.fdflag(libc::__WASI_FDFLAG_DSYNC, set); + } + + pub fn nonblock(&mut self, set: bool) { + self.fdflag(libc::__WASI_FDFLAG_NONBLOCK, set); + } + + pub fn rsync(&mut self, set: bool) { + self.fdflag(libc::__WASI_FDFLAG_RSYNC, set); + } + + pub fn sync(&mut self, set: bool) { + self.fdflag(libc::__WASI_FDFLAG_SYNC, set); + } + + fn fdflag(&mut self, bit: libc::__wasi_fdflags_t, set: bool) { + if set { + self.fdflags |= bit; + } else { + self.fdflags &= !bit; + } + } + + pub fn fs_rights_base(&mut self, rights: libc::__wasi_rights_t) { + self.rights_base = Some(rights); + } + + pub fn fs_rights_inheriting(&mut self, rights: libc::__wasi_rights_t) { + self.rights_inheriting = Some(rights); + } + + fn rights_base(&self) -> libc::__wasi_rights_t { + if let Some(rights) = self.rights_base { + return rights; + } + + // If rights haven't otherwise been specified try to pick a reasonable + // set. This can always be overridden by users via extension traits, and + // implementations may give us fewer rights silently than we ask for. So + // given that, just look at `read` and `write` and bucket permissions + // based on that. + let mut base = 0; + if self.read { + base |= libc::__WASI_RIGHT_FD_READ; + base |= libc::__WASI_RIGHT_FD_READDIR; + } + if self.write { + base |= libc::__WASI_RIGHT_FD_WRITE; + base |= libc::__WASI_RIGHT_FD_DATASYNC; + base |= libc::__WASI_RIGHT_FD_ALLOCATE; + base |= libc::__WASI_RIGHT_FD_FILESTAT_SET_SIZE; + } + + // FIXME: some of these should probably be read-only or write-only... + base |= libc::__WASI_RIGHT_FD_ADVISE; + base |= libc::__WASI_RIGHT_FD_FDSTAT_SET_FLAGS; + base |= libc::__WASI_RIGHT_FD_FILESTAT_SET_TIMES; + base |= libc::__WASI_RIGHT_FD_SEEK; + base |= libc::__WASI_RIGHT_FD_SYNC; + base |= libc::__WASI_RIGHT_FD_TELL; + base |= libc::__WASI_RIGHT_PATH_CREATE_DIRECTORY; + base |= libc::__WASI_RIGHT_PATH_CREATE_FILE; + base |= libc::__WASI_RIGHT_PATH_FILESTAT_GET; + base |= libc::__WASI_RIGHT_PATH_LINK_SOURCE; + base |= libc::__WASI_RIGHT_PATH_LINK_TARGET; + base |= libc::__WASI_RIGHT_PATH_OPEN; + base |= libc::__WASI_RIGHT_PATH_READLINK; + base |= libc::__WASI_RIGHT_PATH_REMOVE_DIRECTORY; + base |= libc::__WASI_RIGHT_PATH_RENAME_SOURCE; + base |= libc::__WASI_RIGHT_PATH_RENAME_TARGET; + base |= libc::__WASI_RIGHT_PATH_SYMLINK; + base |= libc::__WASI_RIGHT_PATH_UNLINK_FILE; + base |= libc::__WASI_RIGHT_POLL_FD_READWRITE; + + return base; } - pub fn read(&mut self, _read: bool) { } - pub fn write(&mut self, _write: bool) { } - pub fn append(&mut self, _append: bool) { } - pub fn truncate(&mut self, _truncate: bool) { } - pub fn create(&mut self, _create: bool) { } - pub fn create_new(&mut self, _create_new: bool) { } + fn rights_inheriting(&self) -> libc::__wasi_rights_t { + self.rights_inheriting.unwrap_or_else(|| self.rights_base()) + } + + pub fn lookup_flags(&mut self, flags: libc::__wasi_lookupflags_t) { + self.dirflags = flags; + } } impl File { - pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { - unsupported() + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + let (dir, file) = open_parent(path)?; + open_at(&dir, file, opts) + } + + pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result { + open_at(&self.fd, path, opts) } pub fn file_attr(&self) -> io::Result { - match self.0 {} + let mut ret = FileAttr::zero(); + self.fd.filestat_get(&mut ret.meta)?; + Ok(ret) + } + + pub fn metadata_at( + &self, + flags: libc::__wasi_lookupflags_t, + path: &Path, + ) -> io::Result { + metadata_at(&self.fd, flags, path) } pub fn fsync(&self) -> io::Result<()> { - match self.0 {} + self.fd.sync() } pub fn datasync(&self) -> io::Result<()> { - match self.0 {} + self.fd.datasync() + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + self.fd.filestat_set_size(size) } - pub fn truncate(&self, _size: u64) -> io::Result<()> { - match self.0 {} + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.read_vectored(&mut [IoVecMut::new(buf)]) } - pub fn read(&self, _buf: &mut [u8]) -> io::Result { - match self.0 {} + pub fn read_vectored(&self, bufs: &mut [IoVecMut<'_>]) -> io::Result { + self.fd.read(bufs) } - pub fn write(&self, _buf: &[u8]) -> io::Result { - match self.0 {} + pub fn write(&self, buf: &[u8]) -> io::Result { + self.write_vectored(&[IoVec::new(buf)]) + } + + pub fn write_vectored(&self, bufs: &[IoVec<'_>]) -> io::Result { + self.fd.write(bufs) } pub fn flush(&self) -> io::Result<()> { - match self.0 {} + Ok(()) } - pub fn seek(&self, _pos: SeekFrom) -> io::Result { - match self.0 {} + pub fn seek(&self, pos: SeekFrom) -> io::Result { + self.fd.seek(pos) } pub fn duplicate(&self) -> io::Result { - match self.0 {} + // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup + unsupported() } pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - match self.0 {} + // Permissions haven't been fully figured out in wasi yet, so this is + // likely temporary + unsupported() } - pub fn diverge(&self) -> ! { - match self.0 {} + pub fn fd(&self) -> &WasiFd { + &self.fd } -} -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder { } + pub fn into_fd(self) -> WasiFd { + self.fd } - pub fn mkdir(&self, _p: &Path) -> io::Result<()> { - unsupported() + pub fn read_link(&self, file: &Path) -> io::Result { + read_link(&self.fd, file) } } -impl fmt::Debug for File { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} +impl FromInner for File { + fn from_inner(fd: u32) -> File { + unsafe { + File { + fd: WasiFd::from_raw(fd), + } + } } } -pub fn readdir(_p: &Path) -> io::Result { - unsupported() -} +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder {} + } -pub fn unlink(_p: &Path) -> io::Result<()> { - unsupported() + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let (dir, file) = open_parent(p)?; + dir.create_directory(file.as_os_str().as_bytes()) + } } -pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("File") + .field("fd", &self.fd.as_raw()) + .finish() + } +} + +pub fn readdir(p: &Path) -> io::Result { + let mut opts = OpenOptions::new(); + opts.directory(true); + opts.read(true); + let dir = File::open(p, &opts)?; + Ok(ReadDir { + cookie: Some(0), + buf: vec![0; 128], + offset: 0, + cap: 0, + inner: Arc::new(ReadDirInner { + dir, + root: p.to_path_buf(), + }), + }) +} + +pub fn unlink(p: &Path) -> io::Result<()> { + let (dir, file) = open_parent(p)?; + dir.unlink_file(file.as_os_str().as_bytes()) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + let (old, old_file) = open_parent(old)?; + let (new, new_file) = open_parent(new)?; + old.rename( + old_file.as_os_str().as_bytes(), + &new, + new_file.as_os_str().as_bytes(), + ) +} + +pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + // Permissions haven't been fully figured out in wasi yet, so this is + // likely temporary unsupported() } -pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { - match perm.0 {} +pub fn rmdir(p: &Path) -> io::Result<()> { + let (dir, file) = open_parent(p)?; + dir.remove_directory(file.as_os_str().as_bytes()) } -pub fn rmdir(_p: &Path) -> io::Result<()> { - unsupported() +pub fn readlink(p: &Path) -> io::Result { + let (dir, file) = open_parent(p)?; + read_link(&dir, file) } -pub fn remove_dir_all(_path: &Path) -> io::Result<()> { - unsupported() +fn read_link(fd: &WasiFd, file: &Path) -> io::Result { + // Try to get a best effort initial capacity for the vector we're going to + // fill. Note that if it's not a symlink we don't use a file to avoid + // allocating gigabytes if you read_link a huge movie file by accident. + // Additionally we add 1 to the initial size so if it doesn't change until + // when we call `readlink` the returned length will be less than the + // capacity, guaranteeing that we got all the data. + let meta = metadata_at(fd, 0, file)?; + let initial_size = if meta.file_type().is_symlink() { + (meta.size() as usize).saturating_add(1) + } else { + 1 // this'll fail in just a moment + }; + + // Now that we have an initial guess of how big to make our buffer, call + // `readlink` in a loop until it fails or reports it filled fewer bytes than + // we asked for, indicating we got everything. + let file = file.as_os_str().as_bytes(); + let mut destination = vec![0u8; initial_size]; + loop { + let len = fd.readlink(file, &mut destination)?; + if len < destination.len() { + destination.truncate(len); + destination.shrink_to_fit(); + return Ok(PathBuf::from(OsString::from_vec(destination))); + } + let amt_to_add = destination.len(); + destination.extend(iter::repeat(0).take(amt_to_add)); + } } -pub fn readlink(_p: &Path) -> io::Result { - unsupported() +pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { + let (dst, dst_file) = open_parent(dst)?; + dst.symlink(src.as_os_str().as_bytes(), dst_file.as_os_str().as_bytes()) } -pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> { - unsupported() +pub fn link(src: &Path, dst: &Path) -> io::Result<()> { + let (src, src_file) = open_parent(src)?; + let (dst, dst_file) = open_parent(dst)?; + src.link( + libc::__WASI_LOOKUP_SYMLINK_FOLLOW, + src_file.as_os_str().as_bytes(), + &dst, + dst_file.as_os_str().as_bytes(), + ) } -pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { - unsupported() +pub fn stat(p: &Path) -> io::Result { + let (dir, file) = open_parent(p)?; + metadata_at(&dir, libc::__WASI_LOOKUP_SYMLINK_FOLLOW, file) } -pub fn stat(_p: &Path) -> io::Result { - unsupported() +pub fn lstat(p: &Path) -> io::Result { + let (dir, file) = open_parent(p)?; + metadata_at(&dir, 0, file) } -pub fn lstat(_p: &Path) -> io::Result { - unsupported() +fn metadata_at( + fd: &WasiFd, + flags: libc::__wasi_lookupflags_t, + path: &Path, +) -> io::Result { + let mut ret = FileAttr::zero(); + fd.path_filestat_get(flags, path.as_os_str().as_bytes(), &mut ret.meta)?; + Ok(ret) } pub fn canonicalize(_p: &Path) -> io::Result { + // This seems to not be in wasi's API yet, and we may need to end up + // emulating it ourselves. For now just return an error. unsupported() } -pub fn copy(_from: &Path, _to: &Path) -> io::Result { - unsupported() +fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result { + let fd = fd.open( + opts.dirflags, + path.as_os_str().as_bytes(), + opts.oflags, + opts.rights_base(), + opts.rights_inheriting(), + opts.fdflags, + )?; + Ok(File { fd }) +} + +// FIXME: we shouldn't implement this. It'd be much better to share this between +// libc (the wasi-sysroot) and Rust as the logic here is likely far more tricky +// than what we're executing below. For now this is a stopgap to enable this +// module, but we should add an official API in upstream wasi-libc which looks +// like this. +// +// In the meantime this is highly unlikely to be correct. It allows some basic +// testing but is not at all robust. +fn open_parent(p: &Path) -> io::Result<(&'static WasiFd, &Path)> { + let map = preopened_map(); + for ancestor in p.ancestors() { + if let Some(fd) = map.get(ancestor) { + let tail = p.strip_prefix(ancestor).unwrap(); + let tail = if tail == Path::new("") { + ".".as_ref() + } else { + tail + }; + return Ok((fd, tail)) + } + } + let msg = format!("failed to find a preopened file descriptor to open {:?}", p); + return Err(io::Error::new(io::ErrorKind::Other, msg)); + + type Preopened = HashMap>; + fn preopened_map() -> &'static Preopened { + static PTR: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + unsafe { + let ptr = PTR.load(SeqCst); + if !ptr.is_null() { + return &*ptr; + } + + let mut map = Box::new(HashMap::new()); + for fd in 3.. { + let mut buf = mem::zeroed(); + if cvt_wasi(libc::__wasi_fd_prestat_get(fd, &mut buf)).is_err() { + break; + } + if buf.pr_type != libc::__WASI_PREOPENTYPE_DIR { + continue; + } + let len = buf.u.dir.pr_name_len; + let mut v = vec![0u8; len]; + let res = cvt_wasi(libc::__wasi_fd_prestat_dir_name( + fd, + v.as_mut_ptr() as *mut i8, + v.len(), + )); + if res.is_err() { + continue; + } + let path = PathBuf::from(OsString::from_vec(v)); + map.insert(path, ManuallyDrop::new(WasiFd::from_raw(fd))); + } + let ptr = Box::into_raw(map); + match PTR.compare_exchange(ptr::null_mut(), ptr, SeqCst, SeqCst) { + Ok(_) => &*ptr, + + // If we lost the race for initialization clean up the map we + // made and just use the one that's already there + Err(other) => { + drop(Box::from_raw(ptr)); + &*other + } + } + } + } } diff --git a/src/libstd/sys/wasi/process.rs b/src/libstd/sys/wasi/process.rs index a02e009d95356..788b829f4bac9 100644 --- a/src/libstd/sys/wasi/process.rs +++ b/src/libstd/sys/wasi/process.rs @@ -67,8 +67,8 @@ impl From for Stdio { } impl From for Stdio { - fn from(file: File) -> Stdio { - file.diverge() + fn from(_file: File) -> Stdio { + panic!("unsupported") } } diff --git a/src/libstd/sys/wasi/time.rs b/src/libstd/sys/wasi/time.rs index e1b92e7c5a705..3f14c80928c67 100644 --- a/src/libstd/sys/wasi/time.rs +++ b/src/libstd/sys/wasi/time.rs @@ -57,6 +57,10 @@ impl SystemTime { SystemTime(current_time(libc::__WASI_CLOCK_REALTIME)) } + pub fn from_wasi_timestamp(ts: libc::__wasi_timestamp_t) -> SystemTime { + SystemTime(Duration::from_nanos(ts)) + } + pub fn sub_time(&self, other: &SystemTime) -> Result { self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) diff --git a/src/libstd/sys_common/fs.rs b/src/libstd/sys_common/fs.rs new file mode 100644 index 0000000000000..7152fcd215c9a --- /dev/null +++ b/src/libstd/sys_common/fs.rs @@ -0,0 +1,41 @@ +#![allow(dead_code)] // not used on all platforms + +use crate::path::Path; +use crate::fs; +use crate::io::{self, Error, ErrorKind}; + +pub fn copy(from: &Path, to: &Path) -> io::Result { + if !from.is_file() { + return Err(Error::new(ErrorKind::InvalidInput, + "the source path is not an existing regular file")) + } + + let mut reader = fs::File::open(from)?; + let mut writer = fs::File::create(to)?; + let perm = reader.metadata()?.permissions(); + + let ret = io::copy(&mut reader, &mut writer)?; + fs::set_permissions(to, perm)?; + Ok(ret) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + let filetype = fs::symlink_metadata(path)?.file_type(); + if filetype.is_symlink() { + fs::remove_file(path) + } else { + remove_dir_all_recursive(path) + } +} + +fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { + for child in fs::read_dir(path)? { + let child = child?; + if child.file_type()?.is_dir() { + remove_dir_all_recursive(&child.path())?; + } else { + fs::remove_file(&child.path())?; + } + } + fs::remove_dir(path) +} diff --git a/src/libstd/sys_common/mod.rs b/src/libstd/sys_common/mod.rs index 883ab34f07c58..c911d772bf0fc 100644 --- a/src/libstd/sys_common/mod.rs +++ b/src/libstd/sys_common/mod.rs @@ -52,6 +52,7 @@ pub mod util; pub mod wtf8; pub mod bytestring; pub mod process; +pub mod fs; cfg_if! { if #[cfg(any(target_os = "cloudabi",