diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 78ba841d89f4b..b6f7abed6f3c9 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -934,9 +934,12 @@ pub fn version_at_macro_invocation( } fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) { - let groups = if verbose { config::rustc_optgroups() } else { config::rustc_short_optgroups() }; let mut options = getopts::Options::new(); - for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) { + for option in config::rustc_optgroups() + .iter() + .filter(|x| verbose || !x.is_verbose_help_only) + .filter(|x| include_unstable_options || x.is_stable()) + { option.apply(&mut options); } let message = "Usage: rustc [OPTIONS] INPUT"; diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 41cbca89af5ad..44721bd889a0c 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1398,9 +1398,25 @@ pub enum OptionKind { } pub struct RustcOptGroup { - apply: Box &mut getopts::Options>, + /// The "primary" name for this option. Normally equal to `long_name`, + /// except for options that don't have a long name, in which case + /// `short_name` is used. + /// + /// This is needed when interacting with `getopts` in some situations, + /// because if an option has both forms, that library treats the long name + /// as primary and the short name as an alias. pub name: &'static str, stability: OptionStability, + kind: OptionKind, + + short_name: &'static str, + long_name: &'static str, + desc: &'static str, + value_hint: &'static str, + + /// If true, this option should not be printed by `rustc --help`, but + /// should still be printed by `rustc --help -v`. + pub is_verbose_help_only: bool, } impl RustcOptGroup { @@ -1409,7 +1425,13 @@ impl RustcOptGroup { } pub fn apply(&self, options: &mut getopts::Options) { - (self.apply)(options); + let &Self { short_name, long_name, desc, value_hint, .. } = self; + match self.kind { + OptionKind::Opt => options.optopt(short_name, long_name, desc, value_hint), + OptionKind::Multi => options.optmulti(short_name, long_name, desc, value_hint), + OptionKind::Flag => options.optflag(short_name, long_name, desc), + OptionKind::FlagMulti => options.optflagmulti(short_name, long_name, desc), + }; } } @@ -1419,31 +1441,22 @@ pub fn make_opt( short_name: &'static str, long_name: &'static str, desc: &'static str, - hint: &'static str, + value_hint: &'static str, ) -> RustcOptGroup { + // "Flag" options don't have a value, and therefore don't have a value hint. + match kind { + OptionKind::Opt | OptionKind::Multi => {} + OptionKind::Flag | OptionKind::FlagMulti => assert_eq!(value_hint, ""), + } RustcOptGroup { name: cmp::max_by_key(short_name, long_name, |s| s.len()), stability, - apply: match kind { - OptionKind::Opt => Box::new(move |opts: &mut getopts::Options| { - opts.optopt(short_name, long_name, desc, hint) - }), - OptionKind::Multi => Box::new(move |opts: &mut getopts::Options| { - opts.optmulti(short_name, long_name, desc, hint) - }), - OptionKind::Flag => { - assert_eq!(hint, ""); - Box::new(move |opts: &mut getopts::Options| { - opts.optflag(short_name, long_name, desc) - }) - } - OptionKind::FlagMulti => { - assert_eq!(hint, ""); - Box::new(move |opts: &mut getopts::Options| { - opts.optflagmulti(short_name, long_name, desc) - }) - } - }, + kind, + short_name, + long_name, + desc, + value_hint, + is_verbose_help_only: false, } } @@ -1454,16 +1467,15 @@ The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE ) }); -/// Returns the "short" subset of the rustc command line options, -/// including metadata for each option, such as whether the option is -/// part of the stable long-term interface for rustc. -pub fn rustc_short_optgroups() -> Vec { +/// Returns all rustc command line options, including metadata for +/// each option, such as whether the option is stable. +pub fn rustc_optgroups() -> Vec { use OptionKind::{Flag, FlagMulti, Multi, Opt}; - use OptionStability::Stable; + use OptionStability::{Stable, Unstable}; use self::make_opt as opt; - vec![ + let mut options = vec![ opt(Stable, Flag, "h", "help", "Display this message", ""), opt( Stable, @@ -1550,21 +1562,11 @@ pub fn rustc_short_optgroups() -> Vec { opt(Stable, Multi, "C", "codegen", "Set a codegen option", "OPT[=VALUE]"), opt(Stable, Flag, "V", "version", "Print version info and exit", ""), opt(Stable, Flag, "v", "verbose", "Use verbose output", ""), - ] -} - -/// Returns all rustc command line options, including metadata for -/// each option, such as whether the option is part of the stable -/// long-term interface for rustc. -pub fn rustc_optgroups() -> Vec { - use OptionKind::{Multi, Opt}; - use OptionStability::{Stable, Unstable}; - - use self::make_opt as opt; + ]; - let mut opts = rustc_short_optgroups(); - // FIXME: none of these descriptions are actually used - opts.extend(vec![ + // Options in this list are hidden from `rustc --help` by default, but are + // shown by `rustc --help -v`. + let verbose_only = [ opt( Stable, Multi, @@ -1590,9 +1592,9 @@ pub fn rustc_optgroups() -> Vec { "", "color", "Configure coloring of output: - auto = colorize, if output goes to a tty (default); - always = always colorize output; - never = never colorize output", + auto = colorize, if output goes to a tty (default); + always = always colorize output; + never = never colorize output", "auto|always|never", ), opt( @@ -1612,8 +1614,13 @@ pub fn rustc_optgroups() -> Vec { "FROM=TO", ), opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "VAR=VALUE"), - ]); - opts + ]; + options.extend(verbose_only.into_iter().map(|mut opt| { + opt.is_verbose_help_only = true; + opt + })); + + options } pub fn get_cmd_lint_options( diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 3079c8b1d905a..76fdb1a4ffd30 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -624,6 +624,223 @@ impl File { self.inner.datasync() } + /// Acquire an exclusive advisory lock on the file. Blocks until the lock can be acquired. + /// + /// This acquires an exclusive advisory lock; no other file handle to this file may acquire + /// another lock. + /// + /// If this file handle, or a clone of it, already holds an advisory lock the exact behavior is + /// unspecified and platform dependent, including the possibility that it will deadlock. + /// However, if this method returns, then an exclusive lock is held. + /// + /// If the file not open for writing, it is unspecified whether this function returns an error. + /// + /// Note, this is an advisory lock meant to interact with [`lock_shared`], [`try_lock`], + /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] + /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` flag, + /// and the `LockFileEx` function on Windows with the `LOCKFILE_EXCLUSIVE_LOCK` flag. Note that, + /// this [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + /// + /// [`lock_shared`]: File::lock_shared + /// [`try_lock`]: File::try_lock + /// [`try_lock_shared`]: File::try_lock_shared + /// [`unlock`]: File::unlock + /// [`read`]: Read::read + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_lock)] + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("foo.txt")?; + /// f.lock()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_lock", issue = "130994")] + pub fn lock(&self) -> io::Result<()> { + self.inner.lock() + } + + /// Acquire a shared advisory lock on the file. Blocks until the lock can be acquired. + /// + /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but + /// none may hold an exclusive lock. + /// + /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is + /// unspecified and platform dependent, including the possibility that it will deadlock. + /// However, if this method returns, then a shared lock is held. + /// + /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`], + /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] + /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` flag, + /// and the `LockFileEx` function on Windows. Note that, this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + /// + /// [`lock`]: File::lock + /// [`try_lock`]: File::try_lock + /// [`try_lock_shared`]: File::try_lock_shared + /// [`unlock`]: File::unlock + /// [`read`]: Read::read + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_lock)] + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("foo.txt")?; + /// f.lock_shared()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_lock", issue = "130994")] + pub fn lock_shared(&self) -> io::Result<()> { + self.inner.lock_shared() + } + + /// Acquire an exclusive advisory lock on the file. Returns `Ok(false)` if the file is locked. + /// + /// This acquires an exclusive advisory lock; no other file handle to this file may acquire + /// another lock. + /// + /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is + /// unspecified and platform dependent, including the possibility that it will deadlock. + /// However, if this method returns, then an exclusive lock is held. + /// + /// If the file not open for writing, it is unspecified whether this function returns an error. + /// + /// Note, this is an advisory lock meant to interact with [`lock`], [`lock_shared`], + /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] + /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` and + /// `LOCK_NB` flags, and the `LockFileEx` function on Windows with the `LOCKFILE_EXCLUSIVE_LOCK` + /// and `LOCKFILE_FAIL_IMMEDIATELY` flags. Note that, this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + /// + /// [`lock`]: File::lock + /// [`lock_shared`]: File::lock_shared + /// [`try_lock_shared`]: File::try_lock_shared + /// [`unlock`]: File::unlock + /// [`read`]: Read::read + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_lock)] + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("foo.txt")?; + /// f.try_lock()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_lock", issue = "130994")] + pub fn try_lock(&self) -> io::Result { + self.inner.try_lock() + } + + /// Acquire a shared advisory lock on the file. + /// Returns `Ok(false)` if the file is exclusively locked. + /// + /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but + /// none may hold an exclusive lock. + /// + /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is + /// unspecified and platform dependent, including the possibility that it will deadlock. + /// However, if this method returns, then a shared lock is held. + /// + /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`], + /// [`try_lock`], and [`unlock`]. Its interactions with other methods, such as [`read`] + /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` and + /// `LOCK_NB` flags, and the `LockFileEx` function on Windows with the + /// `LOCKFILE_FAIL_IMMEDIATELY` flag. Note that, this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + /// + /// [`lock`]: File::lock + /// [`lock_shared`]: File::lock_shared + /// [`try_lock`]: File::try_lock + /// [`unlock`]: File::unlock + /// [`read`]: Read::read + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_lock)] + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("foo.txt")?; + /// f.try_lock_shared()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_lock", issue = "130994")] + pub fn try_lock_shared(&self) -> io::Result { + self.inner.try_lock_shared() + } + + /// Release all locks on the file. + /// + /// All remaining locks are released when the file handle, and all clones of it, are dropped. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_UN` flag, + /// and the `UnlockFile` function on Windows. Note that, this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_lock)] + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("foo.txt")?; + /// f.lock()?; + /// f.unlock()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_lock", issue = "130994")] + pub fn unlock(&self) -> io::Result<()> { + self.inner.unlock() + } + /// Truncates or extends the underlying file, updating the size of /// this file to become `size`. /// diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 0672fe6f7718a..05efed6b5dfc6 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -203,6 +203,124 @@ fn file_test_io_seek_and_write() { assert!(read_str == final_msg); } +#[test] +fn file_lock_multiple_shared() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_lock_multiple_shared_test.txt"); + let f1 = check!(File::create(filename)); + let f2 = check!(OpenOptions::new().write(true).open(filename)); + + // Check that we can acquire concurrent shared locks + check!(f1.lock_shared()); + check!(f2.lock_shared()); + check!(f1.unlock()); + check!(f2.unlock()); + assert!(check!(f1.try_lock_shared())); + assert!(check!(f2.try_lock_shared())); +} + +#[test] +fn file_lock_blocking() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_lock_blocking_test.txt"); + let f1 = check!(File::create(filename)); + let f2 = check!(OpenOptions::new().write(true).open(filename)); + + // Check that shared locks block exclusive locks + check!(f1.lock_shared()); + assert!(!check!(f2.try_lock())); + check!(f1.unlock()); + + // Check that exclusive locks block shared locks + check!(f1.lock()); + assert!(!check!(f2.try_lock_shared())); +} + +#[test] +fn file_lock_drop() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_lock_dup_test.txt"); + let f1 = check!(File::create(filename)); + let f2 = check!(OpenOptions::new().write(true).open(filename)); + + // Check that locks are released when the File is dropped + check!(f1.lock_shared()); + assert!(!check!(f2.try_lock())); + drop(f1); + assert!(check!(f2.try_lock())); +} + +#[test] +fn file_lock_dup() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_lock_dup_test.txt"); + let f1 = check!(File::create(filename)); + let f2 = check!(OpenOptions::new().write(true).open(filename)); + + // Check that locks are not dropped if the File has been cloned + check!(f1.lock_shared()); + assert!(!check!(f2.try_lock())); + let cloned = check!(f1.try_clone()); + drop(f1); + assert!(!check!(f2.try_lock())); + drop(cloned) +} + +#[test] +#[cfg(windows)] +fn file_lock_double_unlock() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_lock_double_unlock_test.txt"); + let f1 = check!(File::create(filename)); + let f2 = check!(OpenOptions::new().write(true).open(filename)); + + // On Windows a file handle may acquire both a shared and exclusive lock. + // Check that both are released by unlock() + check!(f1.lock()); + check!(f1.lock_shared()); + assert!(!check!(f2.try_lock())); + check!(f1.unlock()); + assert!(check!(f2.try_lock())); +} + +#[test] +#[cfg(windows)] +fn file_lock_blocking_async() { + use crate::thread::{sleep, spawn}; + const FILE_FLAG_OVERLAPPED: u32 = 0x40000000; + + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_lock_blocking_async.txt"); + let f1 = check!(File::create(filename)); + let f2 = + check!(OpenOptions::new().custom_flags(FILE_FLAG_OVERLAPPED).write(true).open(filename)); + + check!(f1.lock()); + + // Ensure that lock() is synchronous when the file is opened for asynchronous IO + let t = spawn(move || { + check!(f2.lock()); + }); + sleep(Duration::from_secs(1)); + assert!(!t.is_finished()); + check!(f1.unlock()); + t.join().unwrap(); + + // Ensure that lock_shared() is synchronous when the file is opened for asynchronous IO + let f2 = + check!(OpenOptions::new().custom_flags(FILE_FLAG_OVERLAPPED).write(true).open(filename)); + check!(f1.lock()); + + // Ensure that lock() is synchronous when the file is opened for asynchronous IO + let t = spawn(move || { + check!(f2.lock_shared()); + }); + sleep(Duration::from_secs(1)); + assert!(!t.is_finished()); + check!(f1.unlock()); + t.join().unwrap(); +} + #[test] fn file_test_io_seek_shakedown() { // 01234567890123 diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index 70f6981f7175b..17d15ed2e5045 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -364,6 +364,26 @@ impl File { self.fsync() } + pub fn lock(&self) -> io::Result<()> { + unsupported() + } + + pub fn lock_shared(&self) -> io::Result<()> { + unsupported() + } + + pub fn try_lock(&self) -> io::Result { + unsupported() + } + + pub fn try_lock_shared(&self) -> io::Result { + unsupported() + } + + pub fn unlock(&self) -> io::Result<()> { + unsupported() + } + pub fn truncate(&self, _size: u64) -> io::Result<()> { Err(Error::from_raw_os_error(22)) } diff --git a/library/std/src/sys/pal/solid/fs.rs b/library/std/src/sys/pal/solid/fs.rs index bce9aa6d99cd1..776a96ff3b7ba 100644 --- a/library/std/src/sys/pal/solid/fs.rs +++ b/library/std/src/sys/pal/solid/fs.rs @@ -350,6 +350,26 @@ impl File { self.flush() } + pub fn lock(&self) -> io::Result<()> { + unsupported() + } + + pub fn lock_shared(&self) -> io::Result<()> { + unsupported() + } + + pub fn try_lock(&self) -> io::Result { + unsupported() + } + + pub fn try_lock_shared(&self) -> io::Result { + unsupported() + } + + pub fn unlock(&self) -> io::Result<()> { + unsupported() + } + pub fn truncate(&self, _size: u64) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index f1f843a5f7ae7..be2363862ee2c 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -1254,6 +1254,43 @@ impl File { } } + pub fn lock(&self) -> io::Result<()> { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?; + return Ok(()); + } + + pub fn lock_shared(&self) -> io::Result<()> { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?; + return Ok(()); + } + + pub fn try_lock(&self) -> io::Result { + let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) }); + if let Err(ref err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + return Ok(false); + } + } + result?; + return Ok(true); + } + + pub fn try_lock_shared(&self) -> io::Result { + let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) }); + if let Err(ref err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + return Ok(false); + } + } + result?; + return Ok(true); + } + + pub fn unlock(&self) -> io::Result<()> { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?; + return Ok(()); + } + pub fn truncate(&self, size: u64) -> io::Result<()> { let size: off64_t = size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; diff --git a/library/std/src/sys/pal/unsupported/fs.rs b/library/std/src/sys/pal/unsupported/fs.rs index 474c9fe97d18d..9585ec24f687d 100644 --- a/library/std/src/sys/pal/unsupported/fs.rs +++ b/library/std/src/sys/pal/unsupported/fs.rs @@ -198,6 +198,26 @@ impl File { self.0 } + pub fn lock(&self) -> io::Result<()> { + self.0 + } + + pub fn lock_shared(&self) -> io::Result<()> { + self.0 + } + + pub fn try_lock(&self) -> io::Result { + self.0 + } + + pub fn try_lock_shared(&self) -> io::Result { + self.0 + } + + pub fn unlock(&self) -> io::Result<()> { + self.0 + } + pub fn truncate(&self, _size: u64) -> io::Result<()> { self.0 } diff --git a/library/std/src/sys/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs index 59ecc45b06a1f..3296c762cca2b 100644 --- a/library/std/src/sys/pal/wasi/fs.rs +++ b/library/std/src/sys/pal/wasi/fs.rs @@ -420,6 +420,26 @@ impl File { self.fd.datasync() } + pub fn lock(&self) -> io::Result<()> { + unsupported() + } + + pub fn lock_shared(&self) -> io::Result<()> { + unsupported() + } + + pub fn try_lock(&self) -> io::Result { + unsupported() + } + + pub fn try_lock_shared(&self) -> io::Result { + unsupported() + } + + pub fn unlock(&self) -> io::Result<()> { + unsupported() + } + pub fn truncate(&self, size: u64) -> io::Result<()> { self.fd.filestat_set_size(size) } diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index 192c95fd203c6..248ce3c9ff624 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2351,6 +2351,9 @@ Windows.Win32.Storage.FileSystem.GetFinalPathNameByHandleW Windows.Win32.Storage.FileSystem.GetFullPathNameW Windows.Win32.Storage.FileSystem.GetTempPathW Windows.Win32.Storage.FileSystem.INVALID_FILE_ATTRIBUTES +Windows.Win32.Storage.FileSystem.LOCKFILE_EXCLUSIVE_LOCK +Windows.Win32.Storage.FileSystem.LOCKFILE_FAIL_IMMEDIATELY +Windows.Win32.Storage.FileSystem.LockFileEx Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE_CALLBACK_REASON Windows.Win32.Storage.FileSystem.MAXIMUM_REPARSE_DATA_BUFFER_SIZE @@ -2396,6 +2399,7 @@ Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAG_DIRECTORY Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAGS Windows.Win32.Storage.FileSystem.SYNCHRONIZE Windows.Win32.Storage.FileSystem.TRUNCATE_EXISTING +Windows.Win32.Storage.FileSystem.UnlockFile Windows.Win32.Storage.FileSystem.VOLUME_NAME_DOS Windows.Win32.Storage.FileSystem.VOLUME_NAME_GUID Windows.Win32.Storage.FileSystem.VOLUME_NAME_NONE diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 52444c2c009ed..19925e59dfe9c 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -65,6 +65,7 @@ windows_targets::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinit windows_targets::link!("kernel32.dll" "system" fn InitOnceComplete(lpinitonce : *mut INIT_ONCE, dwflags : u32, lpcontext : *const core::ffi::c_void) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn InitializeProcThreadAttributeList(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST, dwattributecount : u32, dwflags : u32, lpsize : *mut usize) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn LocalFree(hmem : HLOCAL) -> HLOCAL); +windows_targets::link!("kernel32.dll" "system" fn LockFileEx(hfile : HANDLE, dwflags : LOCK_FILE_FLAGS, dwreserved : u32, nnumberofbytestolocklow : u32, nnumberofbytestolockhigh : u32, lpoverlapped : *mut OVERLAPPED) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn MoveFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, dwflags : MOVE_FILE_FLAGS) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn MultiByteToWideChar(codepage : u32, dwflags : MULTI_BYTE_TO_WIDE_CHAR_FLAGS, lpmultibytestr : PCSTR, cbmultibyte : i32, lpwidecharstr : PWSTR, cchwidechar : i32) -> i32); windows_targets::link!("kernel32.dll" "system" fn QueryPerformanceCounter(lpperformancecount : *mut i64) -> BOOL); @@ -96,6 +97,7 @@ windows_targets::link!("kernel32.dll" "system" fn TlsGetValue(dwtlsindex : u32) windows_targets::link!("kernel32.dll" "system" fn TlsSetValue(dwtlsindex : u32, lptlsvalue : *const core::ffi::c_void) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockExclusive(srwlock : *mut SRWLOCK) -> BOOLEAN); windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockShared(srwlock : *mut SRWLOCK) -> BOOLEAN); +windows_targets::link!("kernel32.dll" "system" fn UnlockFile(hfile : HANDLE, dwfileoffsetlow : u32, dwfileoffsethigh : u32, nnumberofbytestounlocklow : u32, nnumberofbytestounlockhigh : u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn UpdateProcThreadAttribute(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST, dwflags : u32, attribute : usize, lpvalue : *const core::ffi::c_void, cbsize : usize, lppreviousvalue : *mut core::ffi::c_void, lpreturnsize : *const usize) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn WaitForMultipleObjects(ncount : u32, lphandles : *const HANDLE, bwaitall : BOOL, dwmilliseconds : u32) -> WAIT_EVENT); windows_targets::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT); @@ -2730,6 +2732,9 @@ pub struct LINGER { pub l_onoff: u16, pub l_linger: u16, } +pub const LOCKFILE_EXCLUSIVE_LOCK: LOCK_FILE_FLAGS = 2u32; +pub const LOCKFILE_FAIL_IMMEDIATELY: LOCK_FILE_FLAGS = 1u32; +pub type LOCK_FILE_FLAGS = u32; pub type LPOVERLAPPED_COMPLETION_ROUTINE = Option< unsafe extern "system" fn( dwerrorcode: u32, diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index 5a9bfccc1fabb..07e4f93a37956 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -346,6 +346,120 @@ impl File { self.fsync() } + fn acquire_lock(&self, flags: c::LOCK_FILE_FLAGS) -> io::Result<()> { + unsafe { + let mut overlapped: c::OVERLAPPED = mem::zeroed(); + let event = c::CreateEventW(ptr::null_mut(), c::FALSE, c::FALSE, ptr::null()); + if event.is_null() { + return Err(io::Error::last_os_error()); + } + overlapped.hEvent = event; + let lock_result = cvt(c::LockFileEx( + self.handle.as_raw_handle(), + flags, + 0, + u32::MAX, + u32::MAX, + &mut overlapped, + )); + + let final_result = match lock_result { + Ok(_) => Ok(()), + Err(err) => { + if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) { + // Wait for the lock to be acquired, and get the lock operation status. + // This can happen asynchronously, if the file handle was opened for async IO + let mut bytes_transferred = 0; + cvt(c::GetOverlappedResult( + self.handle.as_raw_handle(), + &mut overlapped, + &mut bytes_transferred, + c::TRUE, + )) + .map(|_| ()) + } else { + Err(err) + } + } + }; + c::CloseHandle(overlapped.hEvent); + final_result + } + } + + pub fn lock(&self) -> io::Result<()> { + self.acquire_lock(c::LOCKFILE_EXCLUSIVE_LOCK) + } + + pub fn lock_shared(&self) -> io::Result<()> { + self.acquire_lock(0) + } + + pub fn try_lock(&self) -> io::Result { + let result = cvt(unsafe { + let mut overlapped = mem::zeroed(); + c::LockFileEx( + self.handle.as_raw_handle(), + c::LOCKFILE_EXCLUSIVE_LOCK | c::LOCKFILE_FAIL_IMMEDIATELY, + 0, + u32::MAX, + u32::MAX, + &mut overlapped, + ) + }); + + match result { + Ok(_) => Ok(true), + Err(err) + if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) + || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => + { + Ok(false) + } + Err(err) => Err(err), + } + } + + pub fn try_lock_shared(&self) -> io::Result { + let result = cvt(unsafe { + let mut overlapped = mem::zeroed(); + c::LockFileEx( + self.handle.as_raw_handle(), + c::LOCKFILE_FAIL_IMMEDIATELY, + 0, + u32::MAX, + u32::MAX, + &mut overlapped, + ) + }); + + match result { + Ok(_) => Ok(true), + Err(err) + if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) + || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => + { + Ok(false) + } + Err(err) => Err(err), + } + } + + pub fn unlock(&self) -> io::Result<()> { + // Unlock the handle twice because LockFileEx() allows a file handle to acquire + // both an exclusive and shared lock, in which case the documentation states that: + // "...two unlock operations are necessary to unlock the region; the first unlock operation + // unlocks the exclusive lock, the second unlock operation unlocks the shared lock" + cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) })?; + let result = + cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) }); + match result { + Ok(_) => Ok(()), + Err(err) if err.raw_os_error() == Some(c::ERROR_NOT_LOCKED as i32) => Ok(()), + Err(err) => Err(err), + } + } + pub fn truncate(&self, size: u64) -> io::Result<()> { let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 }; api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index cd198c425c008..3d4b89a363e36 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -82,7 +82,7 @@ pub(crate) fn get_clippy_rules_in_order( { item.iter().for_each(|v| { let rule = format!("{prefix}{v}"); - let position = all_args.iter().position(|t| t == &rule).unwrap(); + let position = all_args.iter().position(|t| t == &rule || t == v).unwrap(); result.push((position, rule)); }); } diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 1f02757682c22..e4ce64e2bc176 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -323,6 +323,23 @@ fn order_of_clippy_rules() { assert_eq!(expected, actual); } +#[test] +fn clippy_rule_separate_prefix() { + let args = + vec!["clippy".to_string(), "-A clippy:all".to_string(), "-W clippy::style".to_string()]; + let config = Config::parse(Flags::parse(&args)); + + let actual = match &config.cmd { + crate::Subcommand::Clippy { allow, deny, warn, forbid, .. } => { + get_clippy_rules_in_order(&args, &allow, &deny, &warn, &forbid) + } + _ => panic!("invalid subcommand"), + }; + + let expected = vec!["-A clippy:all".to_string(), "-W clippy::style".to_string()]; + assert_eq!(expected, actual); +} + #[test] fn verbose_tests_default_value() { let config = Config::parse(Flags::parse(&["build".into(), "compiler".into()])); diff --git a/tests/ui/lifetimes/temporary-lifetime-extension.edition2021.run.stdout b/tests/ui/lifetimes/temporary-lifetime-extension.edition2021.run.stdout new file mode 100644 index 0000000000000..8643722158985 --- /dev/null +++ b/tests/ui/lifetimes/temporary-lifetime-extension.edition2021.run.stdout @@ -0,0 +1 @@ +("Hello", 1) [(("Hello", 1),)] "Hello" "Hello" "Hello" ("Hello", 1) ("Hello", 1) ("Hello", 1) diff --git a/tests/ui/lifetimes/temporary-lifetime-extension.edition2024.run.stdout b/tests/ui/lifetimes/temporary-lifetime-extension.edition2024.run.stdout new file mode 100644 index 0000000000000..8643722158985 --- /dev/null +++ b/tests/ui/lifetimes/temporary-lifetime-extension.edition2024.run.stdout @@ -0,0 +1 @@ +("Hello", 1) [(("Hello", 1),)] "Hello" "Hello" "Hello" ("Hello", 1) ("Hello", 1) ("Hello", 1) diff --git a/tests/ui/lifetimes/temporary-lifetime-extension.rs b/tests/ui/lifetimes/temporary-lifetime-extension.rs index 1ecef2f3d04df..d03027cf4a321 100644 --- a/tests/ui/lifetimes/temporary-lifetime-extension.rs +++ b/tests/ui/lifetimes/temporary-lifetime-extension.rs @@ -1,4 +1,21 @@ -//@ check-pass +// This is a test for the new temporary lifetime behaviour as implemented for RFC 3606. +// In essence, with #3606 we can write the following variable initialisation without +// a borrow checking error because the temporary lifetime is automatically extended. +// ```rust +// let x = if condition() { +// &something() +// } else { +// &something_else() +// }; +// ``` +// More details can be found in https://github.com/rust-lang/rfcs/pull/3606 + +//@ run-pass +//@ check-run-results +//@ revisions: edition2021 edition2024 +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 +//@ [edition2024] compile-flags: -Z unstable-options fn temp() -> (String, i32) { (String::from("Hello"), 1) @@ -13,11 +30,7 @@ fn main() { let _ = 123; &(*temp().0)[..] }; - let f = if true { - &temp() - } else { - &temp() - }; + let f = if true { &temp() } else { &temp() }; let g = match true { true => &temp(), false => {