about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-11-11 19:25:06 +0000
committerbors <bors@rust-lang.org>2024-11-11 19:25:06 +0000
commit81eef2d362a6f03db6f8928f82d94298d31eb81b (patch)
tree86fd6dcc1953873fed4c126604d42fab8a63e744
parentde27914e8e6c5f8a02698a8ed9b318df17f2f621 (diff)
parentbcd85e5434a4eac6ce75cc5daf0334c7d7f38229 (diff)
downloadrust-81eef2d362a6f03db6f8928f82d94298d31eb81b.tar.gz
rust-81eef2d362a6f03db6f8928f82d94298d31eb81b.zip
Auto merge of #132902 - matthiaskrgr:rollup-43qgg3t, r=matthiaskrgr
Rollup of 4 pull requests

Successful merges:

 - #129627 (Ensure that tail expr receive lifetime extension)
 - #130999 (Implement file_lock feature)
 - #132873 (handle separate prefixes in clippy rules)
 - #132891 (Remove `rustc_session::config::rustc_short_optgroups`)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs7
-rw-r--r--compiler/rustc_session/src/config.rs103
-rw-r--r--library/std/src/fs.rs217
-rw-r--r--library/std/src/fs/tests.rs118
-rw-r--r--library/std/src/sys/pal/hermit/fs.rs20
-rw-r--r--library/std/src/sys/pal/solid/fs.rs20
-rw-r--r--library/std/src/sys/pal/unix/fs.rs37
-rw-r--r--library/std/src/sys/pal/unsupported/fs.rs20
-rw-r--r--library/std/src/sys/pal/wasi/fs.rs20
-rw-r--r--library/std/src/sys/pal/windows/c/bindings.txt4
-rw-r--r--library/std/src/sys/pal/windows/c/windows_sys.rs5
-rw-r--r--library/std/src/sys/pal/windows/fs.rs114
-rw-r--r--src/bootstrap/src/core/build_steps/clippy.rs2
-rw-r--r--src/bootstrap/src/core/config/tests.rs17
-rw-r--r--tests/ui/lifetimes/temporary-lifetime-extension.edition2021.run.stdout1
-rw-r--r--tests/ui/lifetimes/temporary-lifetime-extension.edition2024.run.stdout1
-rw-r--r--tests/ui/lifetimes/temporary-lifetime-extension.rs25
17 files changed, 674 insertions, 57 deletions
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 78ba841d89f..b6f7abed6f3 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 41cbca89af5..44721bd889a 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<dyn Fn(&mut getopts::Options) -> &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<RustcOptGroup> {
+/// Returns all rustc command line options, including metadata for
+/// each option, such as whether the option is stable.
+pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
     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<RustcOptGroup> {
         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<RustcOptGroup> {
-    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<RustcOptGroup> {
             "",
             "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<RustcOptGroup> {
             "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 3079c8b1d90..76fdb1a4ffd 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<bool> {
+        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<bool> {
+        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 0672fe6f771..05efed6b5df 100644
--- a/library/std/src/fs/tests.rs
+++ b/library/std/src/fs/tests.rs
@@ -204,6 +204,124 @@ fn file_test_io_seek_and_write() {
 }
 
 #[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
     let initial_msg = "qwer-asdf-zxcv";
diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs
index 70f6981f717..17d15ed2e50 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<bool> {
+        unsupported()
+    }
+
+    pub fn try_lock_shared(&self) -> io::Result<bool> {
+        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 bce9aa6d99c..776a96ff3b7 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<bool> {
+        unsupported()
+    }
+
+    pub fn try_lock_shared(&self) -> io::Result<bool> {
+        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 f1f843a5f7a..be2363862ee 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<bool> {
+        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<bool> {
+        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 474c9fe97d1..9585ec24f68 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<bool> {
+        self.0
+    }
+
+    pub fn try_lock_shared(&self) -> io::Result<bool> {
+        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 59ecc45b06a..3296c762cca 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<bool> {
+        unsupported()
+    }
+
+    pub fn try_lock_shared(&self) -> io::Result<bool> {
+        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 192c95fd203..248ce3c9ff6 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 52444c2c009..19925e59dfe 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 5a9bfccc1fa..07e4f93a379 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<bool> {
+        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<bool> {
+        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 cd198c425c0..3d4b89a363e 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 1f02757682c..e4ce64e2bc1 100644
--- a/src/bootstrap/src/core/config/tests.rs
+++ b/src/bootstrap/src/core/config/tests.rs
@@ -324,6 +324,23 @@ fn order_of_clippy_rules() {
 }
 
 #[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()]));
     assert_eq!(config.verbose_tests, false);
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 00000000000..86437221589
--- /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 00000000000..86437221589
--- /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 1ecef2f3d04..d03027cf4a3 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 => {