From a30a79c5b4c3f470648adbfe3eb11575e72b2f97 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 11 Apr 2024 19:36:30 +0200 Subject: [PATCH 01/17] std: use queue-based `RwLock` on SGX --- .../src/sys/pal/sgx/libunwind_integration.rs | 46 ++++ library/std/src/sys/pal/sgx/mod.rs | 1 + library/std/src/sys/pal/sgx/waitqueue/mod.rs | 21 -- library/std/src/sys/sync/rwlock/sgx.rs | 219 ------------------ library/std/src/sys/sync/rwlock/sgx/tests.rs | 21 -- 5 files changed, 47 insertions(+), 261 deletions(-) create mode 100644 library/std/src/sys/pal/sgx/libunwind_integration.rs delete mode 100644 library/std/src/sys/sync/rwlock/sgx.rs delete mode 100644 library/std/src/sys/sync/rwlock/sgx/tests.rs diff --git a/library/std/src/sys/pal/sgx/libunwind_integration.rs b/library/std/src/sys/pal/sgx/libunwind_integration.rs new file mode 100644 index 0000000000000..debfd324c864e --- /dev/null +++ b/library/std/src/sys/pal/sgx/libunwind_integration.rs @@ -0,0 +1,46 @@ +//! The functions in this module are needed by libunwind. These symbols are named +//! in pre-link args for the target specification, so keep that in sync. + +#![cfg(not(test))] + +use crate::sys::sync::RwLock; + +// Verify that the byte pattern libunwind uses to initialize an RwLock is +// equivalent to the value of RwLock::new(). If the value changes, +// `src/UnwindRustSgx.h` in libunwind needs to be changed too. +const _: () = unsafe { + let bits_rust: usize = crate::mem::transmute(RwLock::new()); + assert!(bits_rust == 0); +}; + +const EINVAL: i32 = 22; + +#[no_mangle] +pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RwLock) -> i32 { + if p.is_null() { + return EINVAL; + } + + // We cannot differentiate between reads an writes in unlock and therefore + // always use a write-lock. Unwinding isn't really in the hot path anyway. + unsafe { (*p).write() }; + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RwLock) -> i32 { + if p.is_null() { + return EINVAL; + } + unsafe { (*p).write() }; + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RwLock) -> i32 { + if p.is_null() { + return EINVAL; + } + unsafe { (*p).write_unlock() }; + return 0; +} diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 76f930b86f2ec..d30976ec15149 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -17,6 +17,7 @@ pub mod fd; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; +mod libunwind_integration; pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/sgx/waitqueue/mod.rs b/library/std/src/sys/pal/sgx/waitqueue/mod.rs index 2d952b7ebbca3..ea49bb2803466 100644 --- a/library/std/src/sys/pal/sgx/waitqueue/mod.rs +++ b/library/std/src/sys/pal/sgx/waitqueue/mod.rs @@ -52,10 +52,6 @@ impl WaitVariable { WaitVariable { queue: WaitQueue::new(), lock: var } } - pub fn queue_empty(&self) -> bool { - self.queue.is_empty() - } - pub fn lock_var(&self) -> &T { &self.lock } @@ -98,19 +94,6 @@ impl Default for WaitQueue { } } -impl<'a, T> WaitGuard<'a, T> { - /// Returns which TCSes will be notified when this guard drops. - pub fn notified_tcs(&self) -> NotifiedTcs { - self.notified_tcs - } - - /// Drop this `WaitGuard`, after dropping another `guard`. - pub fn drop_after(self, guard: U) { - drop(guard); - drop(self); - } -} - impl<'a, T> Deref for WaitGuard<'a, T> { type Target = SpinMutexGuard<'a, WaitVariable>; @@ -141,10 +124,6 @@ impl WaitQueue { WaitQueue { inner: UnsafeList::new() } } - pub fn is_empty(&self) -> bool { - self.inner.is_empty() - } - /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait /// until a wakeup event. /// diff --git a/library/std/src/sys/sync/rwlock/sgx.rs b/library/std/src/sys/sync/rwlock/sgx.rs deleted file mode 100644 index 136dea597bb0c..0000000000000 --- a/library/std/src/sys/sync/rwlock/sgx.rs +++ /dev/null @@ -1,219 +0,0 @@ -#[cfg(test)] -mod tests; - -use crate::alloc::Layout; -use crate::num::NonZero; -use crate::sys::pal::waitqueue::{ - try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable, -}; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; - -struct AllocatedRwLock { - readers: SpinMutex>>>, - writer: SpinMutex>, -} - -pub struct RwLock { - inner: LazyBox, -} - -impl LazyInit for AllocatedRwLock { - fn init() -> Box { - Box::new(AllocatedRwLock { - readers: SpinMutex::new(WaitVariable::new(None)), - writer: SpinMutex::new(WaitVariable::new(false)), - }) - } -} - -// Check at compile time that RwLock's size and alignment matches the C definition -// in libunwind (see also `test_c_rwlock_initializer` in `tests`). -const _: () = { - let rust = Layout::new::(); - let c = Layout::new::<*mut ()>(); - assert!(rust.size() == c.size()); - assert!(rust.align() == c.align()); -}; - -impl RwLock { - pub const fn new() -> RwLock { - RwLock { inner: LazyBox::new() } - } - - #[inline] - pub fn read(&self) { - let lock = &*self.inner; - let mut rguard = lock.readers.lock(); - let wguard = lock.writer.lock(); - if *wguard.lock_var() || !wguard.queue_empty() { - // Another thread has or is waiting for the write lock, wait - drop(wguard); - WaitQueue::wait(rguard, || {}); - // Another thread has passed the lock to us - } else { - // No waiting writers, acquire the read lock - *rguard.lock_var_mut() = NonZero::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); - } - } - - #[inline] - pub unsafe fn try_read(&self) -> bool { - let lock = &*self.inner; - let mut rguard = try_lock_or_false!(lock.readers); - let wguard = try_lock_or_false!(lock.writer); - if *wguard.lock_var() || !wguard.queue_empty() { - // Another thread has or is waiting for the write lock - false - } else { - // No waiting writers, acquire the read lock - *rguard.lock_var_mut() = NonZero::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); - true - } - } - - #[inline] - pub fn write(&self) { - let lock = &*self.inner; - let rguard = lock.readers.lock(); - let mut wguard = lock.writer.lock(); - if *wguard.lock_var() || rguard.lock_var().is_some() { - // Another thread has the lock, wait - drop(rguard); - WaitQueue::wait(wguard, || {}); - // Another thread has passed the lock to us - } else { - // We are just now obtaining the lock - *wguard.lock_var_mut() = true; - } - } - - #[inline] - pub fn try_write(&self) -> bool { - let lock = &*self.inner; - let rguard = try_lock_or_false!(lock.readers); - let mut wguard = try_lock_or_false!(lock.writer); - if *wguard.lock_var() || rguard.lock_var().is_some() { - // Another thread has the lock - false - } else { - // We are just now obtaining the lock - *wguard.lock_var_mut() = true; - true - } - } - - #[inline] - unsafe fn __read_unlock( - &self, - mut rguard: SpinMutexGuard<'_, WaitVariable>>>, - wguard: SpinMutexGuard<'_, WaitVariable>, - ) { - *rguard.lock_var_mut() = NonZero::new(rguard.lock_var().unwrap().get() - 1); - if rguard.lock_var().is_some() { - // There are other active readers - } else { - if let Ok(mut wguard) = WaitQueue::notify_one(wguard) { - // A writer was waiting, pass the lock - *wguard.lock_var_mut() = true; - wguard.drop_after(rguard); - } else { - // No writers were waiting, the lock is released - rtassert!(rguard.queue_empty()); - } - } - } - - #[inline] - pub unsafe fn read_unlock(&self) { - let lock = &*self.inner; - let rguard = lock.readers.lock(); - let wguard = lock.writer.lock(); - unsafe { self.__read_unlock(rguard, wguard) }; - } - - #[inline] - unsafe fn __write_unlock( - &self, - rguard: SpinMutexGuard<'_, WaitVariable>>>, - wguard: SpinMutexGuard<'_, WaitVariable>, - ) { - match WaitQueue::notify_one(wguard) { - Err(mut wguard) => { - // No writers waiting, release the write lock - *wguard.lock_var_mut() = false; - if let Ok(mut rguard) = WaitQueue::notify_all(rguard) { - // One or more readers were waiting, pass the lock to them - if let NotifiedTcs::All { count } = rguard.notified_tcs() { - *rguard.lock_var_mut() = Some(count) - } else { - unreachable!() // called notify_all - } - rguard.drop_after(wguard); - } else { - // No readers waiting, the lock is released - } - } - Ok(wguard) => { - // There was a thread waiting for write, just pass the lock - wguard.drop_after(rguard); - } - } - } - - #[inline] - pub unsafe fn write_unlock(&self) { - let lock = &*self.inner; - let rguard = lock.readers.lock(); - let wguard = lock.writer.lock(); - unsafe { self.__write_unlock(rguard, wguard) }; - } - - // only used by __rust_rwlock_unlock below - #[inline] - #[cfg_attr(test, allow(dead_code))] - unsafe fn unlock(&self) { - let lock = &*self.inner; - let rguard = lock.readers.lock(); - let wguard = lock.writer.lock(); - if *wguard.lock_var() == true { - unsafe { self.__write_unlock(rguard, wguard) }; - } else { - unsafe { self.__read_unlock(rguard, wguard) }; - } - } -} - -// The following functions are needed by libunwind. These symbols are named -// in pre-link args for the target specification, so keep that in sync. -#[cfg(not(test))] -const EINVAL: i32 = 22; - -#[cfg(not(test))] -#[no_mangle] -pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RwLock) -> i32 { - if p.is_null() { - return EINVAL; - } - unsafe { (*p).read() }; - return 0; -} - -#[cfg(not(test))] -#[no_mangle] -pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RwLock) -> i32 { - if p.is_null() { - return EINVAL; - } - unsafe { (*p).write() }; - return 0; -} - -#[cfg(not(test))] -#[no_mangle] -pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RwLock) -> i32 { - if p.is_null() { - return EINVAL; - } - unsafe { (*p).unlock() }; - return 0; -} diff --git a/library/std/src/sys/sync/rwlock/sgx/tests.rs b/library/std/src/sys/sync/rwlock/sgx/tests.rs deleted file mode 100644 index 5fd6670afd435..0000000000000 --- a/library/std/src/sys/sync/rwlock/sgx/tests.rs +++ /dev/null @@ -1,21 +0,0 @@ -use super::*; -use crate::ptr; - -// Verify that the byte pattern libunwind uses to initialize an RwLock is -// equivalent to the value of RwLock::new(). If the value changes, -// `src/UnwindRustSgx.h` in libunwind needs to be changed too. -#[test] -fn test_c_rwlock_initializer() { - const C_RWLOCK_INIT: *mut () = ptr::null_mut(); - - // For the test to work, we need the padding/unused bytes in RwLock to be - // initialized as 0. In practice, this is the case with statics. - static RUST_RWLOCK_INIT: RwLock = RwLock::new(); - - unsafe { - // If the assertion fails, that not necessarily an issue with the value - // of C_RWLOCK_INIT. It might just be an issue with the way padding - // bytes are initialized in the test code. - assert_eq!(crate::mem::transmute_copy::<_, *mut ()>(&RUST_RWLOCK_INIT), C_RWLOCK_INIT); - }; -} From 8afee1420232e9698aef020b35aff048b9a302e9 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 11 Apr 2024 19:36:50 +0200 Subject: [PATCH 02/17] std: use queue-based `RwLock` on Xous --- library/std/src/sys/sync/rwlock/xous.rs | 74 ------------------------- 1 file changed, 74 deletions(-) delete mode 100644 library/std/src/sys/sync/rwlock/xous.rs diff --git a/library/std/src/sys/sync/rwlock/xous.rs b/library/std/src/sys/sync/rwlock/xous.rs deleted file mode 100644 index ab45b33e1f69b..0000000000000 --- a/library/std/src/sys/sync/rwlock/xous.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::sync::atomic::{AtomicIsize, Ordering::Acquire}; -use crate::thread::yield_now; - -pub struct RwLock { - /// The "mode" value indicates how many threads are waiting on this - /// Mutex. Possible values are: - /// -1: The lock is locked for writing - /// 0: The lock is unlocked - /// >=1: The lock is locked for reading - /// - /// This currently spins waiting for the lock to be freed. An - /// optimization would be to involve the ticktimer server to - /// coordinate unlocks. - mode: AtomicIsize, -} - -const RWLOCK_WRITING: isize = -1; -const RWLOCK_FREE: isize = 0; - -unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} - -impl RwLock { - #[inline] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - pub const fn new() -> RwLock { - RwLock { mode: AtomicIsize::new(RWLOCK_FREE) } - } - - #[inline] - pub unsafe fn read(&self) { - while !unsafe { self.try_read() } { - yield_now(); - } - } - - #[inline] - pub unsafe fn try_read(&self) -> bool { - self.mode - .fetch_update( - Acquire, - Acquire, - |v| if v == RWLOCK_WRITING { None } else { Some(v + 1) }, - ) - .is_ok() - } - - #[inline] - pub unsafe fn write(&self) { - while !unsafe { self.try_write() } { - yield_now(); - } - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - self.mode.compare_exchange(RWLOCK_FREE, RWLOCK_WRITING, Acquire, Acquire).is_ok() - } - - #[inline] - pub unsafe fn read_unlock(&self) { - let previous = self.mode.fetch_sub(1, Acquire); - assert!(previous != RWLOCK_FREE); - assert!(previous != RWLOCK_WRITING); - } - - #[inline] - pub unsafe fn write_unlock(&self) { - assert_eq!( - self.mode.compare_exchange(RWLOCK_WRITING, RWLOCK_FREE, Acquire, Acquire), - Ok(RWLOCK_WRITING) - ); - } -} From dbda4f91aa0cac1ed3885f1c53c7b66a578d16a1 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 11 Apr 2024 19:37:12 +0200 Subject: [PATCH 03/17] std: use queue-based `RwLock` on Windows 7 --- library/std/src/sys/sync/rwlock/mod.rs | 16 ++++----- library/std/src/sys/sync/rwlock/windows7.rs | 40 --------------------- 2 files changed, 6 insertions(+), 50 deletions(-) delete mode 100644 library/std/src/sys/sync/rwlock/windows7.rs diff --git a/library/std/src/sys/sync/rwlock/mod.rs b/library/std/src/sys/sync/rwlock/mod.rs index 675931c64bdd3..70ba6bf38ef55 100644 --- a/library/std/src/sys/sync/rwlock/mod.rs +++ b/library/std/src/sys/sync/rwlock/mod.rs @@ -12,24 +12,20 @@ cfg_if::cfg_if! { ))] { mod futex; pub use futex::RwLock; - } else if #[cfg(target_family = "unix")] { + } else if #[cfg(any( + target_family = "unix", + all(target_os = "windows", target_vendor = "win7"), + all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "xous", + ))] { mod queue; pub use queue::RwLock; - } else if #[cfg(all(target_os = "windows", target_vendor = "win7"))] { - mod windows7; - pub use windows7::RwLock; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { - mod sgx; - pub use sgx::RwLock; } else if #[cfg(target_os = "solid_asp3")] { mod solid; pub use solid::RwLock; } else if #[cfg(target_os = "teeos")] { mod teeos; pub use teeos::RwLock; - } else if #[cfg(target_os = "xous")] { - mod xous; - pub use xous::RwLock; } else { mod no_threads; pub use no_threads::RwLock; diff --git a/library/std/src/sys/sync/rwlock/windows7.rs b/library/std/src/sys/sync/rwlock/windows7.rs deleted file mode 100644 index e69415baac42b..0000000000000 --- a/library/std/src/sys/sync/rwlock/windows7.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::sys::c; - -pub struct RwLock { - inner: UnsafeCell, -} - -unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} - -impl RwLock { - #[inline] - pub const fn new() -> RwLock { - RwLock { inner: UnsafeCell::new(c::SRWLOCK_INIT) } - } - #[inline] - pub fn read(&self) { - unsafe { c::AcquireSRWLockShared(self.inner.get()) } - } - #[inline] - pub fn try_read(&self) -> bool { - unsafe { c::TryAcquireSRWLockShared(self.inner.get()) != 0 } - } - #[inline] - pub fn write(&self) { - unsafe { c::AcquireSRWLockExclusive(self.inner.get()) } - } - #[inline] - pub fn try_write(&self) -> bool { - unsafe { c::TryAcquireSRWLockExclusive(self.inner.get()) != 0 } - } - #[inline] - pub unsafe fn read_unlock(&self) { - c::ReleaseSRWLockShared(self.inner.get()) - } - #[inline] - pub unsafe fn write_unlock(&self) { - c::ReleaseSRWLockExclusive(self.inner.get()) - } -} From ff2e4ed1f18ccfbd9f7aab1f93c0873f57e0b71f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 21 Mar 2024 14:06:15 +1100 Subject: [PATCH 04/17] Factor out common code in interface tests. Replacing `mk_session` with `sess_and_cfgs`, which does a bit more of the shared stuff -- the option parsing and the `build_configuration` call. --- compiler/rustc_interface/src/tests.rs | 87 ++++++++++++--------------- 1 file changed, 39 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index d2fb65b5d4f8e..2abe35948ea00 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -25,42 +25,46 @@ use std::num::NonZero; use std::path::{Path, PathBuf}; use std::sync::Arc; -fn mk_session(matches: getopts::Matches) -> (Session, Cfg) { +fn sess_and_cfg(args: &[&'static str], f: F) +where + F: FnOnce(Session, Cfg), +{ let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); early_dcx.initialize_checked_jobserver(); - let registry = registry::Registry::new(&[]); + let matches = optgroups().parse(args).unwrap(); let sessopts = build_session_options(&mut early_dcx, &matches); - let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); - let io = CompilerIO { - input: Input::Str { name: FileName::Custom(String::new()), input: String::new() }, - output_dir: None, - output_file: None, - temps_dir, - }; - let sysroot = filesearch::materialize_sysroot(sessopts.maybe_sysroot.clone()); - let target = rustc_session::config::build_target_config(&early_dcx, &sessopts, &sysroot); - let sess = build_session( - early_dcx, - sessopts, - io, - None, - registry, - vec![], - Default::default(), - None, - target, - sysroot, - "", - None, - Arc::default(), - Default::default(), - ); - let cfg = parse_cfg(&sess.dcx(), matches.opt_strs("cfg")); - (sess, cfg) + rustc_span::create_default_session_globals_then(|| { + let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); + let io = CompilerIO { + input: Input::Str { name: FileName::Custom(String::new()), input: String::new() }, + output_dir: None, + output_file: None, + temps_dir, + }; + let sess = build_session( + early_dcx, + sessopts, + io, + None, + registry::Registry::new(&[]), + vec![], + Default::default(), + None, + target, + sysroot, + "", + None, + Arc::default(), + Default::default(), + ); + let cfg = parse_cfg(&sess.dcx(), matches.opt_strs("cfg")); + let cfg = build_configuration(&sess, cfg); + f(sess, cfg) + }); } fn new_public_extern_entry(locations: I) -> ExternEntry @@ -125,21 +129,15 @@ fn assert_non_crate_hash_different(x: &Options, y: &Options) { // When the user supplies --test we should implicitly supply --cfg test #[test] fn test_switch_implies_cfg_test() { - rustc_span::create_default_session_globals_then(|| { - let matches = optgroups().parse(&["--test".to_string()]).unwrap(); - let (sess, cfg) = mk_session(matches); - let cfg = build_configuration(&sess, cfg); + sess_and_cfg(&["--test"], |_sess, cfg| { assert!(cfg.contains(&(sym::test, None))); - }); + }) } // When the user supplies --test and --cfg test, don't implicitly add another --cfg test #[test] fn test_switch_implies_cfg_test_unless_cfg_test() { - rustc_span::create_default_session_globals_then(|| { - let matches = optgroups().parse(&["--test".to_string(), "--cfg=test".to_string()]).unwrap(); - let (sess, cfg) = mk_session(matches); - let cfg = build_configuration(&sess, cfg); + sess_and_cfg(&["--test", "--cfg=test"], |_sess, cfg| { let mut test_items = cfg.iter().filter(|&&(name, _)| name == sym::test); assert!(test_items.next().is_some()); assert!(test_items.next().is_none()); @@ -148,22 +146,15 @@ fn test_switch_implies_cfg_test_unless_cfg_test() { #[test] fn test_can_print_warnings() { - rustc_span::create_default_session_globals_then(|| { - let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap(); - let (sess, _) = mk_session(matches); + sess_and_cfg(&["-Awarnings"], |sess, _cfg| { assert!(!sess.dcx().can_emit_warnings()); }); - rustc_span::create_default_session_globals_then(|| { - let matches = - optgroups().parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]).unwrap(); - let (sess, _) = mk_session(matches); + sess_and_cfg(&["-Awarnings", "-Dwarnings"], |sess, _cfg| { assert!(sess.dcx().can_emit_warnings()); }); - rustc_span::create_default_session_globals_then(|| { - let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap(); - let (sess, _) = mk_session(matches); + sess_and_cfg(&["-Adead_code"], |sess, _cfg| { assert!(sess.dcx().can_emit_warnings()); }); } From 62c32aeeab0558123f12a9372768ba567ce43361 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 21 Mar 2024 14:17:00 +1100 Subject: [PATCH 05/17] Construct `SourceMap` at the same time as `SessionGlobals`. Currently `SourceMap` is constructed slightly later than `SessionGlobals`, and inserted. This commit changes things so they are done at the same time. Benefits: - `SessionGlobals::source_map` changes from `Lock>>` to `Option>`. It's still optional, but mutability isn't required because it's initialized at construction. - `set_source_map` is removed, simplifying `run_compiler`, which is good because that's a critical function and it's nice to make it simpler. This requires moving things around a bit, so the necessary inputs are available when `SessionGlobals` is created, in particular the `loader` and `hash_kind`, which are no longer computed by `build_session`. These inputs are captured by the new `SourceMapInputs` type, which is threaded through various places. --- compiler/rustc_hir/src/tests.rs | 2 +- compiler/rustc_interface/src/interface.rs | 94 +++++++++---------- compiler/rustc_interface/src/tests.rs | 11 ++- compiler/rustc_interface/src/util.rs | 16 +++- compiler/rustc_log/src/lib.rs | 2 +- compiler/rustc_session/src/config.rs | 12 ++- compiler/rustc_session/src/session.rs | 20 +--- compiler/rustc_span/src/lib.rs | 53 ++++------- compiler/rustc_span/src/source_map.rs | 26 +++-- .../src/doc/needless_doctest_main.rs | 2 +- 10 files changed, 120 insertions(+), 118 deletions(-) diff --git a/compiler/rustc_hir/src/tests.rs b/compiler/rustc_hir/src/tests.rs index 74b8e88a97790..571923b546293 100644 --- a/compiler/rustc_hir/src/tests.rs +++ b/compiler/rustc_hir/src/tests.rs @@ -14,7 +14,7 @@ fn def_path_hash_depends_on_crate_id() { // the crate by changing the crate disambiguator (e.g. via bumping the // crate's version number). - create_session_globals_then(Edition::Edition2024, || { + create_session_globals_then(Edition::Edition2024, None, || { let id0 = StableCrateId::new(Symbol::intern("foo"), false, vec!["1".to_string()], ""); let id1 = StableCrateId::new(Symbol::intern("foo"), false, vec!["2".to_string()], ""); diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index da2fb490a36f2..2ffb1426de48c 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -21,7 +21,7 @@ use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileN use rustc_session::filesearch::{self, sysroot_candidates}; use rustc_session::parse::ParseSess; use rustc_session::{lint, CompilerIO, EarlyDiagCtxt, Session}; -use rustc_span::source_map::FileLoader; +use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMapInputs}; use rustc_span::symbol::sym; use rustc_span::FileName; use std::path::PathBuf; @@ -336,18 +336,23 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se let early_dcx = EarlyDiagCtxt::new(config.opts.error_format); early_dcx.initialize_checked_jobserver(); + crate::callbacks::setup_callbacks(); + + let sysroot = filesearch::materialize_sysroot(config.opts.maybe_sysroot.clone()); + let target = config::build_target_config(&early_dcx, &config.opts, &sysroot); + let file_loader = config.file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); + let path_mapping = config.opts.file_path_mapping(); + let hash_kind = config.opts.unstable_opts.src_hash_algorithm(&target); + util::run_in_thread_pool_with_globals( config.opts.edition, config.opts.unstable_opts.threads, + SourceMapInputs { file_loader, path_mapping, hash_kind }, |current_gcx| { - crate::callbacks::setup_callbacks(); - + // The previous `early_dcx` can't be reused here because it doesn't + // impl `Send`. Creating a new one is fine. let early_dcx = EarlyDiagCtxt::new(config.opts.error_format); - let sysroot = filesearch::materialize_sysroot(config.opts.maybe_sysroot.clone()); - - let target = config::build_target_config(&early_dcx, &config.opts, &sysroot); - let codegen_backend = match config.make_codegen_backend { None => util::get_codegen_backend( &early_dcx, @@ -372,9 +377,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se config.opts.unstable_opts.translate_directionality_markers, ) { Ok(bundle) => bundle, - Err(e) => { - early_dcx.early_fatal(format!("failed to load fluent bundle: {e}")); - } + Err(e) => early_dcx.early_fatal(format!("failed to load fluent bundle: {e}")), }; let mut locale_resources = Vec::from(config.locale_resources); @@ -393,7 +396,6 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se config.registry.clone(), locale_resources, config.lint_caps, - config.file_loader, target, sysroot, util::rustc_version_str().unwrap_or("unknown"), @@ -440,45 +442,43 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se current_gcx, }; - rustc_span::set_source_map(compiler.sess.psess.clone_source_map(), move || { - // There are two paths out of `f`. - // - Normal exit. - // - Panic, e.g. triggered by `abort_if_errors`. - // - // We must run `finish_diagnostics` in both cases. - let res = { - // If `f` panics, `finish_diagnostics` will run during - // unwinding because of the `defer`. - let mut guar = None; - let sess_abort_guard = defer(|| { - guar = compiler.sess.finish_diagnostics(&config.registry); - }); - - let res = f(&compiler); - - // If `f` doesn't panic, `finish_diagnostics` will run - // normally when `sess_abort_guard` is dropped. - drop(sess_abort_guard); - - // If `finish_diagnostics` emits errors (e.g. stashed - // errors) we can't return an error directly, because the - // return type of this function is `R`, not `Result`. - // But we need to communicate the errors' existence to the - // caller, otherwise the caller might mistakenly think that - // no errors occurred and return a zero exit code. So we - // abort (panic) instead, similar to if `f` had panicked. - if guar.is_some() { - compiler.sess.dcx().abort_if_errors(); - } + // There are two paths out of `f`. + // - Normal exit. + // - Panic, e.g. triggered by `abort_if_errors`. + // + // We must run `finish_diagnostics` in both cases. + let res = { + // If `f` panics, `finish_diagnostics` will run during + // unwinding because of the `defer`. + let mut guar = None; + let sess_abort_guard = defer(|| { + guar = compiler.sess.finish_diagnostics(&config.registry); + }); + + let res = f(&compiler); + + // If `f` doesn't panic, `finish_diagnostics` will run + // normally when `sess_abort_guard` is dropped. + drop(sess_abort_guard); + + // If `finish_diagnostics` emits errors (e.g. stashed + // errors) we can't return an error directly, because the + // return type of this function is `R`, not `Result`. + // But we need to communicate the errors' existence to the + // caller, otherwise the caller might mistakenly think that + // no errors occurred and return a zero exit code. So we + // abort (panic) instead, similar to if `f` had panicked. + if guar.is_some() { + compiler.sess.dcx().abort_if_errors(); + } - res - }; + res + }; - let prof = compiler.sess.prof.clone(); - prof.generic_activity("drop_compiler").run(move || drop(compiler)); + let prof = compiler.sess.prof.clone(); + prof.generic_activity("drop_compiler").run(move || drop(compiler)); - res - }) + res }, ) } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 2abe35948ea00..4a5bd9a3ceb6c 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -16,6 +16,7 @@ use rustc_session::search_paths::SearchPath; use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; use rustc_session::{build_session, filesearch, getopts, CompilerIO, EarlyDiagCtxt, Session}; use rustc_span::edition::{Edition, DEFAULT_EDITION}; +use rustc_span::source_map::{RealFileLoader, SourceMapInputs}; use rustc_span::symbol::sym; use rustc_span::{FileName, SourceFileHashAlgorithm}; use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel}; @@ -36,8 +37,14 @@ where let sessopts = build_session_options(&mut early_dcx, &matches); let sysroot = filesearch::materialize_sysroot(sessopts.maybe_sysroot.clone()); let target = rustc_session::config::build_target_config(&early_dcx, &sessopts, &sysroot); + let hash_kind = sessopts.unstable_opts.src_hash_algorithm(&target); + let sm_inputs = Some(SourceMapInputs { + file_loader: Box::new(RealFileLoader) as _, + path_mapping: sessopts.file_path_mapping(), + hash_kind, + }); - rustc_span::create_default_session_globals_then(|| { + rustc_span::create_session_globals_then(DEFAULT_EDITION, sm_inputs, || { let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); let io = CompilerIO { input: Input::Str { name: FileName::Custom(String::new()), input: String::new() }, @@ -45,6 +52,7 @@ where output_file: None, temps_dir, }; + let sess = build_session( early_dcx, sessopts, @@ -53,7 +61,6 @@ where registry::Registry::new(&[]), vec![], Default::default(), - None, target, sysroot, "", diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index d0f04fccc4810..02dcfe9c8dffb 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -14,6 +14,7 @@ use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer}; use rustc_session::{filesearch, Session}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; +use rustc_span::source_map::SourceMapInputs; use rustc_span::symbol::sym; use rustc_target::spec::Target; use session::output::{categorize_crate_type, CRATE_TYPES}; @@ -65,8 +66,9 @@ fn init_stack_size() -> usize { }) } -pub(crate) fn run_in_thread_with_globals R + Send, R: Send>( +fn run_in_thread_with_globals R + Send, R: Send>( edition: Edition, + sm_inputs: SourceMapInputs, f: F, ) -> R { // The "thread pool" is a single spawned thread in the non-parallel @@ -84,7 +86,9 @@ pub(crate) fn run_in_thread_with_globals R + Send, R: S // name contains null bytes. let r = builder .spawn_scoped(s, move || { - rustc_span::create_session_globals_then(edition, || f(CurrentGcx::new())) + rustc_span::create_session_globals_then(edition, Some(sm_inputs), || { + f(CurrentGcx::new()) + }) }) .unwrap() .join(); @@ -100,15 +104,17 @@ pub(crate) fn run_in_thread_with_globals R + Send, R: S pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( edition: Edition, _threads: usize, + sm_inputs: SourceMapInputs, f: F, ) -> R { - run_in_thread_with_globals(edition, f) + run_in_thread_with_globals(edition, sm_inputs, f) } #[cfg(parallel_compiler)] pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( edition: Edition, threads: usize, + sm_inputs: SourceMapInputs, f: F, ) -> R { use rustc_data_structures::{defer, jobserver, sync::FromDyn}; @@ -120,7 +126,7 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap()); if !sync::is_dyn_thread_safe() { - return run_in_thread_with_globals(edition, |current_gcx| { + return run_in_thread_with_globals(edition, sm_inputs, |current_gcx| { // Register the thread for use with the `WorkerLocal` type. registry.register(); @@ -169,7 +175,7 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, // pool. Upon creation, each worker thread created gets a copy of the // session globals in TLS. This is possible because `SessionGlobals` impls // `Send` in the parallel compiler. - rustc_span::create_session_globals_then(edition, || { + rustc_span::create_session_globals_then(edition, Some(sm_inputs), || { rustc_span::with_session_globals(|session_globals| { let session_globals = FromDyn::from(session_globals); builder diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index e4b67cde244fb..81257f9be8823 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -17,7 +17,7 @@ //! rustc_log::init_logger(rustc_log::LoggerConfig::from_env("LOG")).unwrap(); //! //! let edition = rustc_span::edition::Edition::Edition2021; -//! rustc_span::create_session_globals_then(edition, || { +//! rustc_span::create_session_globals_then(edition, None, || { //! /* ... */ //! }); //! } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 7aca86f7169d7..c7526f43087e2 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1125,7 +1125,7 @@ impl Options { || self.unstable_opts.query_dep_graph } - pub(crate) fn file_path_mapping(&self) -> FilePathMapping { + pub fn file_path_mapping(&self) -> FilePathMapping { file_path_mapping(self.remap_path_prefix.clone(), &self.unstable_opts) } @@ -1162,6 +1162,16 @@ impl UnstableOptions { track_diagnostics: self.track_diagnostics, } } + + pub fn src_hash_algorithm(&self, target: &Target) -> SourceFileHashAlgorithm { + self.src_hash_algorithm.unwrap_or_else(|| { + if target.is_like_msvc { + SourceFileHashAlgorithm::Sha256 + } else { + SourceFileHashAlgorithm::Md5 + } + }) + } } // The type of entry function, so users can have their own entry functions diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 22ca8a3cf3ec2..ecf70e0822e9f 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -28,9 +28,9 @@ use rustc_errors::{ use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; use rustc_span::edition::Edition; -use rustc_span::source_map::{FileLoader, FilePathMapping, RealFileLoader, SourceMap}; +use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::{FileNameDisplayPreference, RealFileName}; -use rustc_span::{SourceFileHashAlgorithm, Span, Symbol}; +use rustc_span::{Span, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel}; use rustc_target::spec::{ @@ -988,7 +988,6 @@ pub fn build_session( registry: rustc_errors::registry::Registry, fluent_resources: Vec<&'static str>, driver_lint_caps: FxHashMap, - file_loader: Option>, target: Target, sysroot: PathBuf, cfg_version: &'static str, @@ -1015,24 +1014,11 @@ pub fn build_session( early_dcx.early_warn(warning) } - let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); - let hash_kind = sopts.unstable_opts.src_hash_algorithm.unwrap_or_else(|| { - if target.is_like_msvc { - SourceFileHashAlgorithm::Sha256 - } else { - SourceFileHashAlgorithm::Md5 - } - }); - let source_map = Lrc::new(SourceMap::with_file_loader_and_hash_kind( - loader, - sopts.file_path_mapping(), - hash_kind, - )); - let fallback_bundle = fallback_fluent_bundle( fluent_resources, sopts.unstable_opts.translate_directionality_markers, ); + let source_map = rustc_span::source_map::get_source_map().unwrap(); let emitter = default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle); let mut dcx = diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index c1e1175b4bd6b..f749d4eb83368 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -49,7 +49,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; mod caching_source_map_view; pub mod source_map; pub use self::caching_source_map_view::CachingSourceMapView; -use source_map::SourceMap; +use source_map::{SourceMap, SourceMapInputs}; pub mod edition; use edition::Edition; @@ -104,35 +104,35 @@ pub struct SessionGlobals { metavar_spans: Lock>, hygiene_data: Lock, - /// A reference to the source map in the `Session`. It's an `Option` - /// because it can't be initialized until `Session` is created, which - /// happens after `SessionGlobals`. `set_source_map` does the - /// initialization. - /// - /// This field should only be used in places where the `Session` is truly - /// not available, such as `::fmt`. - source_map: Lock>>, + /// The session's source map, if there is one. This field should only be + /// used in places where the `Session` is truly not available, such as + /// `::fmt`. + source_map: Option>, } impl SessionGlobals { - pub fn new(edition: Edition) -> SessionGlobals { + pub fn new(edition: Edition, sm_inputs: Option) -> SessionGlobals { SessionGlobals { symbol_interner: symbol::Interner::fresh(), span_interner: Lock::new(span_encoding::SpanInterner::default()), metavar_spans: Default::default(), hygiene_data: Lock::new(hygiene::HygieneData::new(edition)), - source_map: Lock::new(None), + source_map: sm_inputs.map(|inputs| Lrc::new(SourceMap::with_inputs(inputs))), } } } -pub fn create_session_globals_then(edition: Edition, f: impl FnOnce() -> R) -> R { +pub fn create_session_globals_then( + edition: Edition, + sm_inputs: Option, + f: impl FnOnce() -> R, +) -> R { assert!( !SESSION_GLOBALS.is_set(), "SESSION_GLOBALS should never be overwritten! \ Use another thread if you need another SessionGlobals" ); - let session_globals = SessionGlobals::new(edition); + let session_globals = SessionGlobals::new(edition, sm_inputs); SESSION_GLOBALS.set(&session_globals, f) } @@ -145,12 +145,13 @@ pub fn set_session_globals_then(session_globals: &SessionGlobals, f: impl FnO SESSION_GLOBALS.set(session_globals, f) } +/// No source map. pub fn create_session_if_not_set_then(edition: Edition, f: F) -> R where F: FnOnce(&SessionGlobals) -> R, { if !SESSION_GLOBALS.is_set() { - let session_globals = SessionGlobals::new(edition); + let session_globals = SessionGlobals::new(edition, None); SESSION_GLOBALS.set(&session_globals, || SESSION_GLOBALS.with(f)) } else { SESSION_GLOBALS.with(f) @@ -164,8 +165,9 @@ where SESSION_GLOBALS.with(f) } +/// Default edition, no source map. pub fn create_default_session_globals_then(f: impl FnOnce() -> R) -> R { - create_session_globals_then(edition::DEFAULT_EDITION, f) + create_session_globals_then(edition::DEFAULT_EDITION, None, f) } // If this ever becomes non thread-local, `decode_syntax_context` @@ -1318,25 +1320,6 @@ impl Decodable for AttrId { } } -/// Insert `source_map` into the session globals for the duration of the -/// closure's execution. -pub fn set_source_map T>(source_map: Lrc, f: F) -> T { - with_session_globals(|session_globals| { - *session_globals.source_map.borrow_mut() = Some(source_map); - }); - struct ClearSourceMap; - impl Drop for ClearSourceMap { - fn drop(&mut self) { - with_session_globals(|session_globals| { - session_globals.source_map.borrow_mut().take(); - }); - } - } - - let _guard = ClearSourceMap; - f() -} - impl fmt::Debug for Span { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Use the global `SourceMap` to print the span. If that's not @@ -1352,7 +1335,7 @@ impl fmt::Debug for Span { if SESSION_GLOBALS.is_set() { with_session_globals(|session_globals| { - if let Some(source_map) = &*session_globals.source_map.borrow() { + if let Some(source_map) = &session_globals.source_map { write!(f, "{} ({:?})", source_map.span_to_diagnostic_string(*self), self.ctxt()) } else { fallback(*self, f) diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 93d5f06a16736..e3e76caebaf00 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -167,9 +167,17 @@ struct SourceMapFiles { stable_id_to_source_file: UnhashMap>, } +/// Used to construct a `SourceMap` with `SourceMap::with_inputs`. +pub struct SourceMapInputs { + pub file_loader: Box, + pub path_mapping: FilePathMapping, + pub hash_kind: SourceFileHashAlgorithm, +} + pub struct SourceMap { files: RwLock, file_loader: IntoDynSyncSend>, + // This is used to apply the file path remapping as specified via // `--remap-path-prefix` to all `SourceFile`s allocated within this `SourceMap`. path_mapping: FilePathMapping, @@ -180,17 +188,15 @@ pub struct SourceMap { impl SourceMap { pub fn new(path_mapping: FilePathMapping) -> SourceMap { - Self::with_file_loader_and_hash_kind( - Box::new(RealFileLoader), + Self::with_inputs(SourceMapInputs { + file_loader: Box::new(RealFileLoader), path_mapping, - SourceFileHashAlgorithm::Md5, - ) + hash_kind: SourceFileHashAlgorithm::Md5, + }) } - pub fn with_file_loader_and_hash_kind( - file_loader: Box, - path_mapping: FilePathMapping, - hash_kind: SourceFileHashAlgorithm, + pub fn with_inputs( + SourceMapInputs { file_loader, path_mapping, hash_kind }: SourceMapInputs, ) -> SourceMap { SourceMap { files: Default::default(), @@ -1054,6 +1060,10 @@ impl SourceMap { } } +pub fn get_source_map() -> Option> { + with_session_globals(|session_globals| session_globals.source_map.clone()) +} + #[derive(Clone)] pub struct FilePathMapping { mapping: Vec<(PathBuf, PathBuf)>, diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs index e55a988321b34..651f2ebaee6f4 100644 --- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs +++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs @@ -38,7 +38,7 @@ pub fn check( // of all `#[test]` attributes in not ignored code examples fn check_code_sample(code: String, edition: Edition, ignore: bool) -> (bool, Vec>) { rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_globals_then(edition, || { + rustc_span::create_session_globals_then(edition, None, || { let mut test_attr_spans = vec![]; let filename = FileName::anon_source_code(&code); From 9b0ced000a7e9db6bed97046974adfa9bf3bc165 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 21 Mar 2024 14:54:24 +1100 Subject: [PATCH 06/17] Move `initialize_checked_jobserver`. Currently it's a method on `EarlyDiagCtxt`, which is not the right place for it at all -- `EarlyDiagCtxt` is used to issue diagnostics, but shouldn't be doing any of the actual checking. This commit moves it into a standalone function that takes an `EarlyDiagCtxt` as an argument, which is more sensible. This does require adding `EarlyDiagCtxt::early_struct_warn`, so a warning can be returned and then modified with a note. (And that likely explains why somebody put `initialize_checked_jobserver` into `EarlyDiagCtxt` in the first place.) --- compiler/rustc_interface/src/interface.rs | 15 ++++++++++++++- compiler/rustc_interface/src/tests.rs | 4 ++-- compiler/rustc_session/src/session.rs | 14 ++++---------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 2ffb1426de48c..c5b81dbd67919 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -5,6 +5,7 @@ use rustc_ast::{LitKind, MetaItemKind}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::defer; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::jobserver; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::Lrc; use rustc_errors::registry::Registry; @@ -323,6 +324,18 @@ pub struct Config { pub expanded_args: Vec, } +/// Initialize jobserver before getting `jobserver::client` and `build_session`. +pub(crate) fn initialize_checked_jobserver(early_dcx: &EarlyDiagCtxt) { + jobserver::initialize_checked(|err| { + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + early_dcx + .early_struct_warn(err) + .with_note("the build environment is likely misconfigured") + .emit() + }); +} + // JUSTIFICATION: before session exists, only config #[allow(rustc::bad_opt_access)] #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable @@ -334,7 +347,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se // Check jobserver before run_in_thread_pool_with_globals, which call jobserver::acquire_thread let early_dcx = EarlyDiagCtxt::new(config.opts.error_format); - early_dcx.initialize_checked_jobserver(); + initialize_checked_jobserver(&early_dcx); crate::callbacks::setup_callbacks(); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 4a5bd9a3ceb6c..6748a1fd976be 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -1,5 +1,5 @@ #![allow(rustc::bad_opt_access)] -use crate::interface::parse_cfg; +use crate::interface::{initialize_checked_jobserver, parse_cfg}; use rustc_data_structures::profiling::TimePassesFormat; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; use rustc_session::config::{ @@ -31,7 +31,7 @@ where F: FnOnce(Session, Cfg), { let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); - early_dcx.initialize_checked_jobserver(); + initialize_checked_jobserver(&early_dcx); let matches = optgroups().parse(args).unwrap(); let sessopts = build_session_options(&mut early_dcx, &matches); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index ecf70e0822e9f..8b844f340e5cb 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1397,16 +1397,10 @@ impl EarlyDiagCtxt { self.dcx.warn(msg) } - pub fn initialize_checked_jobserver(&self) { - // initialize jobserver before getting `jobserver::client` and `build_session`. - jobserver::initialize_checked(|err| { - #[allow(rustc::untranslatable_diagnostic)] - #[allow(rustc::diagnostic_outside_of_impl)] - self.dcx - .struct_warn(err) - .with_note("the build environment is likely misconfigured") - .emit() - }); + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + pub fn early_struct_warn(&self, msg: impl Into) -> Diag<'_, ()> { + self.dcx.struct_warn(msg) } } From 93544d5db3d8f0d0e5d2ea344154689008fa2ff8 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 31 Mar 2024 22:27:19 -0500 Subject: [PATCH 07/17] Match ergonomics 2024: Implement eat-one-layer --- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir_typeck/src/pat.rs | 120 +++++++++------- compiler/rustc_span/src/symbol.rs | 1 + ...feature-gate-ref_pat_eat_one_layer_2024.rs | 37 +++++ ...ure-gate-ref_pat_eat_one_layer_2024.stderr | 128 ++++++++++++++++++ .../ref_pat_eat_one_layer_2021.rs | 37 +++++ .../ref_pat_eat_one_layer_2021.stderr | 128 ++++++++++++++++++ .../ref_pat_eat_one_layer_2024.rs | 32 +++++ .../ref_pat_eat_one_layer_2024_fail.rs | 13 ++ .../ref_pat_eat_one_layer_2024_fail.stderr | 25 ++++ 10 files changed, 472 insertions(+), 51 deletions(-) create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.rs create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.stderr create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.rs create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.stderr create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index e6b19817de385..5d17675a5325c 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -571,6 +571,8 @@ declare_features! ( (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions. (unstable, raw_ref_op, "1.41.0", Some(64490)), + /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024. + (incomplete, ref_pat_eat_one_layer_2024, "CURRENT_RUSTC_VERSION", Some(123076)), /// Allows `&` and `&mut` patterns to consume match-ergonomics-inserted references. (incomplete, ref_pat_everywhere, "CURRENT_RUSTC_VERSION", Some(123076)), /// Allows using the `#[register_tool]` attribute. diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index db4bd132b7e30..ff7716ce7eae0 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -294,7 +294,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { AdjustMode::Pass => (expected, def_bm, false), AdjustMode::Reset => (expected, INITIAL_BM, false), AdjustMode::ResetAndConsumeRef(mutbl) => { - (expected, INITIAL_BM, def_bm.0 == ByRef::Yes(mutbl)) + let mutbls_match = def_bm.0 == ByRef::Yes(mutbl); + if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + if mutbls_match { + (expected, INITIAL_BM, true) + } else { + (expected, def_bm, false) + } + } else { + (expected, INITIAL_BM, mutbls_match) + } } AdjustMode::Peel => { let peeled = self.peel_off_references(pat, expected, def_bm); @@ -2056,61 +2065,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat_info: PatInfo<'tcx, '_>, consumed_inherited_ref: bool, ) -> Ty<'tcx> { - let tcx = self.tcx; - let expected = self.shallow_resolve(expected); - let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) { - Ok(()) => { - // `demand::subtype` would be good enough, but using `eqtype` turns - // out to be equally general. See (note_1) for details. - - // Take region, inner-type from expected type if we can, - // to avoid creating needless variables. This also helps with - // the bad interactions of the given hack detailed in (note_1). - debug!("check_pat_ref: expected={:?}", expected); - match *expected.kind() { - ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), - _ => { - if consumed_inherited_ref && self.tcx.features().ref_pat_everywhere { - // We already matched against a match-ergonmics inserted reference, - // so we don't need to match against a reference from the original type. - // Save this infor for use in lowering later - self.typeck_results - .borrow_mut() - .skipped_ref_pats_mut() - .insert(pat.hir_id); - (expected, expected) - } else { - let inner_ty = self.next_ty_var(TypeVariableOrigin { - param_def_id: None, - span: inner.span, - }); - let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); - debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); - let err = self.demand_eqtype_pat_diag( - pat.span, - expected, - ref_ty, - pat_info.top_info, - ); + if consumed_inherited_ref + && pat.span.at_least_rust_2024() + && self.tcx.features().ref_pat_eat_one_layer_2024 + { + self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); + self.check_pat(inner, expected, pat_info); + expected + } else { + let tcx = self.tcx; + let expected = self.shallow_resolve(expected); + let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) { + Ok(()) => { + // `demand::subtype` would be good enough, but using `eqtype` turns + // out to be equally general. See (note_1) for details. + + // Take region, inner-type from expected type if we can, + // to avoid creating needless variables. This also helps with + // the bad interactions of the given hack detailed in (note_1). + debug!("check_pat_ref: expected={:?}", expected); + match *expected.kind() { + ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), + _ => { + if consumed_inherited_ref && self.tcx.features().ref_pat_everywhere { + // We already matched against a match-ergonmics inserted reference, + // so we don't need to match against a reference from the original type. + // Save this infor for use in lowering later + self.typeck_results + .borrow_mut() + .skipped_ref_pats_mut() + .insert(pat.hir_id); + (expected, expected) + } else { + let inner_ty = self.next_ty_var(TypeVariableOrigin { + param_def_id: None, + span: inner.span, + }); + let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); + debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); + let err = self.demand_eqtype_pat_diag( + pat.span, + expected, + ref_ty, + pat_info.top_info, + ); - // Look for a case like `fn foo(&foo: u32)` and suggest - // `fn foo(foo: &u32)` - if let Some(mut err) = err { - self.borrow_pat_suggestion(&mut err, pat); - err.emit(); + // Look for a case like `fn foo(&foo: u32)` and suggest + // `fn foo(foo: &u32)` + if let Some(mut err) = err { + self.borrow_pat_suggestion(&mut err, pat); + err.emit(); + } + (ref_ty, inner_ty) } - (ref_ty, inner_ty) } } } - } - Err(guar) => { - let err = Ty::new_error(tcx, guar); - (err, err) - } - }; - self.check_pat(inner, inner_ty, pat_info); - ref_ty + Err(guar) => { + let err = Ty::new_error(tcx, guar); + (err, err) + } + }; + self.check_pat(inner, inner_ty, pat_info); + ref_ty + } } /// Create a reference type with a fresh region variable. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index bfd0f77c237b2..baf7fab3ad852 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1460,6 +1460,7 @@ symbols! { receiver, recursion_limit, reexport_test_harness_main, + ref_pat_eat_one_layer_2024, ref_pat_everywhere, ref_unwind_safe_trait, reference, diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.rs new file mode 100644 index 0000000000000..83f1ee6a77e89 --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.rs @@ -0,0 +1,37 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options + +pub fn main() { + if let Some(Some(&x)) = &Some(&Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(Some(&x)) = &Some(Some(&0)) { + let _: &u32 = x; + //~^ ERROR: mismatched types + } + if let Some(Some(&&x)) = &Some(Some(&0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(&Some(x)) = &Some(Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(Some(&x)) = &Some(&Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } +} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.stderr new file mode 100644 index 0000000000000..132fe421a18d8 --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.stderr @@ -0,0 +1,128 @@ +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:5:22 + | +LL | if let Some(Some(&x)) = &Some(&Some(0)) { + | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(Some(x)) = &Some(&Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:10:23 + | +LL | let _: &u32 = x; + | ---- ^ expected `&u32`, found integer + | | + | expected due to this + | +help: consider borrowing here + | +LL | let _: &u32 = &x; + | + + +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:13:23 + | +LL | if let Some(Some(&&x)) = &Some(Some(&0)) { + | ^^ --------------- this expression has type `&Option>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - if let Some(Some(&&x)) = &Some(Some(&0)) { +LL + if let Some(Some(&x)) = &Some(Some(&0)) { + | + +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:17:17 + | +LL | if let Some(&Some(x)) = &Some(Some(0)) { + | ^^^^^^^^ -------------- this expression has type `&Option>` + | | + | expected `Option<{integer}>`, found `&_` + | + = note: expected enum `Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:21:22 + | +LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + | ^^^^^^ ----------------------- this expression has type `&mut Option<&mut Option<{integer}>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:21:22 + | +LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL | if let Some(Some(x)) = &mut Some(&mut Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:25:22 + | +LL | if let Some(Some(&x)) = &Some(&Some(0)) { + | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(Some(x)) = &Some(&Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:29:27 + | +LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(&mut Some(x)) = &Some(&mut Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:33:23 + | +LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { + | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:33:23 + | +LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL | if let Some(&Some(x)) = &mut Some(&Some(0)) { + | ~ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.rs new file mode 100644 index 0000000000000..d28567f2859a3 --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.rs @@ -0,0 +1,37 @@ +//@ edition: 2021 +#![allow(incomplete_features)] +#![feature(ref_pat_eat_one_layer_2024)] +pub fn main() { + if let Some(Some(&x)) = &Some(&Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(Some(&x)) = &Some(Some(&0)) { + let _: &u32 = x; + //~^ ERROR: mismatched types + } + if let Some(Some(&&x)) = &Some(Some(&0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(&Some(x)) = &Some(Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(Some(&x)) = &Some(&Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } +} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.stderr new file mode 100644 index 0000000000000..28706f89c066b --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.stderr @@ -0,0 +1,128 @@ +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2021.rs:5:22 + | +LL | if let Some(Some(&x)) = &Some(&Some(0)) { + | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(Some(x)) = &Some(&Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2021.rs:10:23 + | +LL | let _: &u32 = x; + | ---- ^ expected `&u32`, found integer + | | + | expected due to this + | +help: consider borrowing here + | +LL | let _: &u32 = &x; + | + + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2021.rs:13:23 + | +LL | if let Some(Some(&&x)) = &Some(Some(&0)) { + | ^^ --------------- this expression has type `&Option>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - if let Some(Some(&&x)) = &Some(Some(&0)) { +LL + if let Some(Some(&x)) = &Some(Some(&0)) { + | + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2021.rs:17:17 + | +LL | if let Some(&Some(x)) = &Some(Some(0)) { + | ^^^^^^^^ -------------- this expression has type `&Option>` + | | + | expected `Option<{integer}>`, found `&_` + | + = note: expected enum `Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2021.rs:21:22 + | +LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + | ^^^^^^ ----------------------- this expression has type `&mut Option<&mut Option<{integer}>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/ref_pat_eat_one_layer_2021.rs:21:22 + | +LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL | if let Some(Some(x)) = &mut Some(&mut Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2021.rs:25:22 + | +LL | if let Some(Some(&x)) = &Some(&Some(0)) { + | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(Some(x)) = &Some(&Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2021.rs:29:27 + | +LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(&mut Some(x)) = &Some(&mut Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2021.rs:33:23 + | +LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { + | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/ref_pat_eat_one_layer_2021.rs:33:23 + | +LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL | if let Some(&Some(x)) = &mut Some(&Some(0)) { + | ~ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs new file mode 100644 index 0000000000000..f83784273836f --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs @@ -0,0 +1,32 @@ +//@ run-pass +//@ edition: 2024 +//@ compile-flags: -Zunstable-options +#![allow(incomplete_features)] +#![feature(ref_pat_eat_one_layer_2024)] + +pub fn main() { + if let Some(Some(&x)) = &Some(&Some(0)) { + let _: u32 = x; + } + if let Some(Some(&x)) = &Some(Some(&0)) { + let _: &u32 = x; + } + if let Some(Some(&&x)) = &Some(Some(&0)) { + let _: u32 = x; + } + if let Some(&Some(x)) = &Some(Some(0)) { + let _: u32 = x; + } + if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + let _: u32 = x; + } + if let Some(Some(&x)) = &Some(&Some(0)) { + let _: u32 = x; + } + if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + let _: u32 = x; + } + if let Some(&Some(&mut x)) = &mut Some(& Some(0)) { + let _: u32 = x; + } +} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs new file mode 100644 index 0000000000000..566196c1a323e --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs @@ -0,0 +1,13 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options +#![allow(incomplete_features)] +#![feature(ref_pat_eat_one_layer_2024)] + +pub fn main() { + if let Some(&mut Some(&_)) = &Some(&Some(0)) { + //~^ ERROR: mismatched types + } + if let Some(&Some(&_)) = &Some(&mut Some(0)) { + //~^ ERROR: mismatched types + } +} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr new file mode 100644 index 0000000000000..96a107b5c508d --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:7:17 + | +LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { + | ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | types differ in mutability + | + = note: expected reference `&Option<{integer}>` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:10:23 + | +LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { + | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. From 4cd87c463c2a58379b2efd45cdc92027f2965611 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Fri, 5 Apr 2024 21:18:05 -0400 Subject: [PATCH 08/17] Properly downgrade inherited mutability --- compiler/rustc_hir_typeck/src/pat.rs | 6 +++- .../ref_pat_eat_one_layer_2024.rs | 11 +++++++- .../ref_pat_eat_one_layer_2024_fail.rs | 7 +++++ .../ref_pat_eat_one_layer_2024_fail.stderr | 28 ++++++++++++++++++- .../ref_pat_eat_one_layer_2024_fail2.rs | 11 ++++++++ .../ref_pat_eat_one_layer_2024_fail2.stderr | 17 +++++++++++ 6 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index ff7716ce7eae0..bec177a2845a6 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -299,7 +299,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if mutbls_match { (expected, INITIAL_BM, true) } else { - (expected, def_bm, false) + let mut new_bm = def_bm; + if new_bm.0 == ByRef::Yes(Mutability::Mut) && mutbl == Mutability::Not { + new_bm.0 = ByRef::Yes(Mutability::Not); + } + (expected, new_bm, false) } } else { (expected, INITIAL_BM, mutbls_match) diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs index f83784273836f..3cf6008609ded 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs @@ -26,7 +26,16 @@ pub fn main() { if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { let _: u32 = x; } - if let Some(&Some(&mut x)) = &mut Some(& Some(0)) { + if let Some(&Some(&x)) = &mut Some(&Some(0)) { let _: u32 = x; } + if let Some(&Some(x)) = &mut Some(&Some(0)) { + let _: &u32 = x; + } + if let Some(&Some(&mut ref x)) = Some(&Some(&mut 0)) { + let _: &u32 = x; + } + if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { + let _: &u32 = x; + } } diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs index 566196c1a323e..f8385d2e0494d 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs @@ -10,4 +10,11 @@ pub fn main() { if let Some(&Some(&_)) = &Some(&mut Some(0)) { //~^ ERROR: mismatched types } + if let Some(&Some(x)) = &mut Some(&Some(0)) { + let _: &mut u32 = x; + //~^ ERROR: mismatched types + } + if let Some(&Some(&x)) = Some(&Some(&mut 0)) { + //~^ ERROR: mismatched types + } } diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr index 96a107b5c508d..8b714cfcc3ecd 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr @@ -20,6 +20,32 @@ LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { = note: expected type `{integer}` found reference `&_` -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:14:27 + | +LL | let _: &mut u32 = x; + | -------- ^ types differ in mutability + | | + | expected due to this + | + = note: expected mutable reference `&mut u32` + found reference `&{integer}` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:17:23 + | +LL | if let Some(&Some(&x)) = Some(&Some(&mut 0)) { + | ^^ ------------------- this expression has type `Option<&Option<&mut {integer}>>` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { + | ~ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs new file mode 100644 index 0000000000000..364554884073a --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs @@ -0,0 +1,11 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options +#![allow(incomplete_features)] +#![feature(ref_pat_eat_one_layer_2024)] + +pub fn main() { + if let Some(&Some(x)) = Some(&Some(&mut 0)) { + //~^ ERROR: cannot move out of a shared reference [E0507] + let _: &u32 = x; + } +} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr new file mode 100644 index 0000000000000..ccfb5c7a0c076 --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr @@ -0,0 +1,17 @@ +error[E0507]: cannot move out of a shared reference + --> $DIR/ref_pat_eat_one_layer_2024_fail2.rs:7:29 + | +LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { + | - ^^^^^^^^^^^^^^^^^^^ + | | + | data moved here + | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | +help: consider borrowing the pattern binding + | +LL | if let Some(&Some(ref x)) = Some(&Some(&mut 0)) { + | +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0507`. From b6c409723b6438a10db53f12ae6e79f530df8a1b Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Fri, 5 Apr 2024 22:07:57 -0400 Subject: [PATCH 09/17] Support `let &mut x = &&mut 0;` --- compiler/rustc_hir_typeck/src/pat.rs | 23 +++++++++----- .../ref_pat_eat_one_layer_2024.rs | 9 ++++++ .../ref_pat_eat_one_layer_2024_fail.rs | 8 +++++ .../ref_pat_eat_one_layer_2024_fail.stderr | 30 +++++++++++++++++-- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index bec177a2845a6..5930cc0698abf 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -299,18 +299,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if mutbls_match { (expected, INITIAL_BM, true) } else { - let mut new_bm = def_bm; - if new_bm.0 == ByRef::Yes(Mutability::Mut) && mutbl == Mutability::Not { - new_bm.0 = ByRef::Yes(Mutability::Not); - } - (expected, new_bm, false) + let (new_ty, new_bm) = if mutbl == Mutability::Mut { + self.peel_off_references(pat, expected, def_bm, Mutability::Not) + } else { + let new_byref = if def_bm.0 == ByRef::Yes(Mutability::Mut) { + ByRef::Yes(Mutability::Not) + } else { + def_bm.0 + }; + (expected, BindingAnnotation(new_byref, def_bm.1)) + }; + (new_ty, new_bm, false) } } else { (expected, INITIAL_BM, mutbls_match) } } AdjustMode::Peel => { - let peeled = self.peel_off_references(pat, expected, def_bm); + let peeled = self.peel_off_references(pat, expected, def_bm, Mutability::Mut); (peeled.0, peeled.1, false) } } @@ -392,6 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, mut def_bm: BindingAnnotation, + max_mutability: Mutability, ) -> (Ty<'tcx>, BindingAnnotation) { let mut expected = self.try_structurally_resolve_type(pat.span, expected); // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example, @@ -403,7 +410,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // See the examples in `ui/match-defbm*.rs`. let mut pat_adjustments = vec![]; - while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() { + while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() + && inner_mutability <= max_mutability + { debug!("inspecting {:?}", expected); debug!("current discriminant is Ref, inserting implicit deref"); diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs index 3cf6008609ded..2c426f469d84a 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs @@ -38,4 +38,13 @@ pub fn main() { if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { let _: &u32 = x; } + + let &mut x = &&mut 0; + let _: &u32 = x; + + let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&mut 0; + let _: &u32 = x; + + let &mut &mut &mut &mut x = &mut &&&&mut &&&mut &mut 0; + let _: &u32 = x; } diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs index f8385d2e0494d..9e14a524e2b71 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs @@ -17,4 +17,12 @@ pub fn main() { if let Some(&Some(&x)) = Some(&Some(&mut 0)) { //~^ ERROR: mismatched types } + + let &mut x = &&0; + //~^ ERROR: mismatched types + let _: &u32 = x; + + let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; + //~^ ERROR: mismatched types + let _: &u32 = x; } diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr index 8b714cfcc3ecd..074c5d298a553 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr @@ -4,9 +4,9 @@ error[E0308]: mismatched types LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { | ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>` | | - | types differ in mutability + | expected `Option<{integer}>`, found `&mut _` | - = note: expected reference `&Option<{integer}>` + = note: expected enum `Option<{integer}>` found mutable reference `&mut _` error[E0308]: mismatched types @@ -46,6 +46,30 @@ help: consider removing `&` from the pattern LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { | ~ -error: aborting due to 4 previous errors +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:21:9 + | +LL | let &mut x = &&0; + | ^^^^^^ --- this expression has type `&&{integer}` + | | + | expected integer, found `&mut _` + | help: to declare a mutable variable use: `mut x` + | + = note: expected type `{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:25:9 + | +LL | let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; + | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` + | | + | expected integer, found `&mut _` + | help: to declare a mutable variable use: `mut x` + | + = note: expected type `{integer}` + found mutable reference `&mut _` + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0308`. From e3945bd3a8236ea6e1f6e1e04842c56a3c689cc5 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sat, 6 Apr 2024 17:07:09 -0400 Subject: [PATCH 10/17] Ensure inherited reference is never set to `&mut` behind an `&` --- compiler/rustc_ast/src/ast.rs | 8 ++ compiler/rustc_hir_typeck/src/pat.rs | 91 +++++++++++-------- .../rustc_mir_build/src/thir/pattern/mod.rs | 2 +- .../ref_pat_eat_one_layer_2024.rs | 15 +++ .../ref_pat_eat_one_layer_2024_fail.rs | 17 ++-- .../ref_pat_eat_one_layer_2024_fail.stderr | 50 ++++++---- 6 files changed, 120 insertions(+), 63 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5b708cf4e1a55..fc7f64c739268 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -36,6 +36,7 @@ use rustc_macros::HashStable_Generic; use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; +use std::cmp; use std::fmt; use std::mem; use thin_vec::{thin_vec, ThinVec}; @@ -731,6 +732,13 @@ impl BindingAnnotation { Self::MUT_REF_MUT => "mut ref mut ", } } + + pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self { + if let ByRef::Yes(old_mutbl) = self.0 { + self.0 = ByRef::Yes(cmp::min(old_mutbl, mutbl)); + } + self + } } #[derive(Clone, Encodable, Decodable, Debug)] diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 5930cc0698abf..801c735ade38a 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -79,6 +79,7 @@ struct TopInfo<'tcx> { #[derive(Copy, Clone)] struct PatInfo<'tcx, 'a> { binding_mode: BindingAnnotation, + max_ref_mutbl: Mutability, top_info: TopInfo<'tcx>, decl_origin: Option>, @@ -160,8 +161,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { decl_origin: Option>, ) { let info = TopInfo { expected, origin_expr, span }; - let pat_info = - PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_origin, current_depth: 0 }; + let pat_info = PatInfo { + binding_mode: INITIAL_BM, + max_ref_mutbl: Mutability::Mut, + top_info: info, + decl_origin, + current_depth: 0, + }; self.check_pat(pat, expected, pat_info); } @@ -172,7 +178,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Conversely, inside this module, `check_pat_top` should never be used. #[instrument(level = "debug", skip(self, pat_info))] fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) { - let PatInfo { binding_mode: def_bm, top_info: ti, current_depth, .. } = pat_info; + let PatInfo { binding_mode: def_bm, max_ref_mutbl, top_info: ti, current_depth, .. } = + pat_info; let path_res = match &pat.kind { PatKind::Path(qpath) => Some( @@ -181,10 +188,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }; let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); - let (expected, def_bm, ref_pattern_already_consumed) = - self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode); + let (expected, def_bm, max_ref_mutbl, ref_pattern_already_consumed) = + self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode, max_ref_mutbl); let pat_info = PatInfo { binding_mode: def_bm, + max_ref_mutbl, top_info: ti, decl_origin: pat_info.decl_origin, current_depth: current_depth + 1, @@ -289,35 +297,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, def_bm: BindingAnnotation, adjust_mode: AdjustMode, - ) -> (Ty<'tcx>, BindingAnnotation, bool) { + max_ref_mutbl: Mutability, + ) -> (Ty<'tcx>, BindingAnnotation, Mutability, bool) { + if let ByRef::Yes(mutbl) = def_bm.0 { + debug_assert!(mutbl <= max_ref_mutbl); + } match adjust_mode { - AdjustMode::Pass => (expected, def_bm, false), - AdjustMode::Reset => (expected, INITIAL_BM, false), - AdjustMode::ResetAndConsumeRef(mutbl) => { - let mutbls_match = def_bm.0 == ByRef::Yes(mutbl); + AdjustMode::Pass => (expected, def_bm, max_ref_mutbl, false), + AdjustMode::Reset => (expected, INITIAL_BM, Mutability::Mut, false), + AdjustMode::ResetAndConsumeRef(ref_pat_mutbl) => { + let mutbls_match = def_bm.0 == ByRef::Yes(ref_pat_mutbl); if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + let max_ref_mutbl = cmp::min(max_ref_mutbl, ref_pat_mutbl); if mutbls_match { - (expected, INITIAL_BM, true) + debug!("consuming inherited reference"); + (expected, INITIAL_BM, max_ref_mutbl, true) } else { - let (new_ty, new_bm) = if mutbl == Mutability::Mut { - self.peel_off_references(pat, expected, def_bm, Mutability::Not) + let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut { + self.peel_off_references( + pat, + expected, + def_bm, + Mutability::Not, + max_ref_mutbl, + ) } else { - let new_byref = if def_bm.0 == ByRef::Yes(Mutability::Mut) { - ByRef::Yes(Mutability::Not) - } else { - def_bm.0 - }; - (expected, BindingAnnotation(new_byref, def_bm.1)) + (expected, def_bm.cap_ref_mutability(Mutability::Not), Mutability::Not) }; - (new_ty, new_bm, false) + (new_ty, new_bm, max_ref_mutbl, false) } } else { - (expected, INITIAL_BM, mutbls_match) + (expected, INITIAL_BM, max_ref_mutbl, mutbls_match) } } AdjustMode::Peel => { - let peeled = self.peel_off_references(pat, expected, def_bm, Mutability::Mut); - (peeled.0, peeled.1, false) + let peeled = + self.peel_off_references(pat, expected, def_bm, Mutability::Mut, max_ref_mutbl); + (peeled.0, peeled.1, peeled.2, false) } } } @@ -398,8 +414,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, mut def_bm: BindingAnnotation, - max_mutability: Mutability, - ) -> (Ty<'tcx>, BindingAnnotation) { + max_peelable_mutability: Mutability, + mut max_ref_mutability: Mutability, + ) -> (Ty<'tcx>, BindingAnnotation, Mutability) { let mut expected = self.try_structurally_resolve_type(pat.span, expected); // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example, // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches @@ -411,7 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // See the examples in `ui/match-defbm*.rs`. let mut pat_adjustments = vec![]; while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() - && inner_mutability <= max_mutability + && inner_mutability <= max_peelable_mutability { debug!("inspecting {:?}", expected); @@ -430,6 +447,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This is because a `& &mut` cannot mutate the underlying value. ByRef::Yes(Mutability::Not) => Mutability::Not, }); + + if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + max_ref_mutability = cmp::min(max_ref_mutability, inner_mutability); + def_bm = def_bm.cap_ref_mutability(max_ref_mutability); + } } if !pat_adjustments.is_empty() { @@ -440,7 +462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .insert(pat.hir_id, pat_adjustments); } - (expected, def_bm) + (expected, def_bm, max_ref_mutability) } fn check_pat_lit( @@ -1116,15 +1138,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth } = pat_info; let tcx = self.tcx; let on_error = |e| { for pat in subpats { - self.check_pat( - pat, - Ty::new_error(tcx, e), - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth }, - ); + self.check_pat(pat, Ty::new_error(tcx, e), pat_info); } }; let report_unexpected_res = |res: Res| { @@ -1169,7 +1186,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pat_ty = pat_ty.no_bound_vars().expect("expected fn type"); // Type-check the tuple struct pattern against the expected type. - let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, ti); + let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, pat_info.top_info); let had_err = if let Some(err) = diag { err.emit(); true @@ -1187,11 +1204,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { let field = &variant.fields[FieldIdx::from_usize(i)]; let field_ty = self.field_ty(subpat.span, field, args); - self.check_pat( - subpat, - field_ty, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth }, - ); + self.check_pat(subpat, field_ty, pat_info); self.tcx.check_stability( variant.fields[FieldIdx::from_usize(i)].did, diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 133cf8e334929..bcb43a0054709 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -69,7 +69,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir::PatKind::Ref(inner, _) if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) => { - self.lower_pattern_unadjusted(inner) + self.lower_pattern(inner) } _ => self.lower_pattern_unadjusted(pat), }; diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs index 2c426f469d84a..f1ac3e340e915 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs @@ -38,6 +38,21 @@ pub fn main() { if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { let _: &u32 = x; } + if let &Some(Some(x)) = &Some(&mut Some(0)) { + let _: &u32 = x; + } + if let Some(&Some(&x)) = &Some(&mut Some(0)) { + let _: u32 = x; + } + if let Some(&Some(&x)) = &Some(&Some(0)) { + let _: u32 = x; + } + if let Some(&Some(&x)) = &Some(&mut Some(0)) { + let _: u32 = x; + } + if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { + let _: u32 = x; + } let &mut x = &&mut 0; let _: &u32 = x; diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs index 9e14a524e2b71..ec091bb17467d 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs @@ -7,22 +7,27 @@ pub fn main() { if let Some(&mut Some(&_)) = &Some(&Some(0)) { //~^ ERROR: mismatched types } - if let Some(&Some(&_)) = &Some(&mut Some(0)) { + if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { //~^ ERROR: mismatched types } if let Some(&Some(x)) = &mut Some(&Some(0)) { let _: &mut u32 = x; //~^ ERROR: mismatched types } - if let Some(&Some(&x)) = Some(&Some(&mut 0)) { + if let Some(&Some(&_)) = Some(&Some(&mut 0)) { //~^ ERROR: mismatched types } + if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { + //~^ ERROR: mismatched types + } + if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { + //~^ ERROR: mismatched types + } + - let &mut x = &&0; + let &mut _= &&0; //~^ ERROR: mismatched types - let _: &u32 = x; - let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; + let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; //~^ ERROR: mismatched types - let _: &u32 = x; } diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr index 074c5d298a553..be71ee606c767 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr @@ -12,13 +12,13 @@ LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:10:23 | -LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { - | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` +LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { + | ^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` | | - | expected integer, found `&_` + | expected integer, found `&mut _` | - = note: expected type `{integer}` - found reference `&_` + = note: expected type `{integer}` + found mutable reference `&mut _` error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:14:27 @@ -34,42 +34,58 @@ LL | let _: &mut u32 = x; error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:17:23 | -LL | if let Some(&Some(&x)) = Some(&Some(&mut 0)) { +LL | if let Some(&Some(&_)) = Some(&Some(&mut 0)) { | ^^ ------------------- this expression has type `Option<&Option<&mut {integer}>>` | | | types differ in mutability | = note: expected mutable reference `&mut {integer}` found reference `&_` -help: consider removing `&` from the pattern + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:20:23 + | +LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { + | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:23:29 | -LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { - | ~ +LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { + | ^^^^^^ ------------------------- this expression has type `&Option>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:21:9 + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:28:9 | -LL | let &mut x = &&0; - | ^^^^^^ --- this expression has type `&&{integer}` +LL | let &mut _= &&0; + | ^^^^^^ --- this expression has type `&&{integer}` | | | expected integer, found `&mut _` - | help: to declare a mutable variable use: `mut x` | = note: expected type `{integer}` found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:25:9 + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:31:9 | -LL | let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; +LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` | | | expected integer, found `&mut _` - | help: to declare a mutable variable use: `mut x` | = note: expected type `{integer}` found mutable reference `&mut _` -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0308`. From 88cd821e6275ed18e493fd22201ee313154ea6a7 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 14 Apr 2024 10:52:37 -0400 Subject: [PATCH 11/17] Address review comments --- compiler/rustc_ast/src/ast.rs | 4 ++-- compiler/rustc_hir_typeck/src/pat.rs | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fc7f64c739268..334ce5e060b8d 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -734,8 +734,8 @@ impl BindingAnnotation { } pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self { - if let ByRef::Yes(old_mutbl) = self.0 { - self.0 = ByRef::Yes(cmp::min(old_mutbl, mutbl)); + if let ByRef::Yes(old_mutbl) = &mut self.0 { + *old_mutbl = cmp::min(*old_mutbl, mutbl); } self } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 801c735ade38a..28681365af547 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -447,10 +447,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This is because a `& &mut` cannot mutate the underlying value. ByRef::Yes(Mutability::Not) => Mutability::Not, }); + } - if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { - max_ref_mutability = cmp::min(max_ref_mutability, inner_mutability); - def_bm = def_bm.cap_ref_mutability(max_ref_mutability); + if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + def_bm = def_bm.cap_ref_mutability(max_ref_mutability); + if def_bm.0 == ByRef::Yes(Mutability::Not) { + max_ref_mutability = Mutability::Not; } } From 3efbe3e70ceb9825bcced29422e5f4a695bb254b Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 14 Apr 2024 11:01:00 -0400 Subject: [PATCH 12/17] Simplify `calc_default_binding_mode` --- compiler/rustc_hir_typeck/src/pat.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 28681365af547..d66d327c49a4c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -308,10 +308,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { AdjustMode::ResetAndConsumeRef(ref_pat_mutbl) => { let mutbls_match = def_bm.0 == ByRef::Yes(ref_pat_mutbl); if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { - let max_ref_mutbl = cmp::min(max_ref_mutbl, ref_pat_mutbl); if mutbls_match { debug!("consuming inherited reference"); - (expected, INITIAL_BM, max_ref_mutbl, true) + (expected, INITIAL_BM, cmp::min(max_ref_mutbl, ref_pat_mutbl), true) } else { let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut { self.peel_off_references( From 82e7773bc1ab8b89c7b8c0a107c1f5741fe0bf1e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 15 Apr 2024 18:29:32 +0000 Subject: [PATCH 13/17] Subtype predicates only exist on inference types, so we can allow them to register opaque types within them. --- compiler/rustc_infer/src/infer/mod.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 0b8061104ab43..76a40ecd5bc43 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -945,14 +945,27 @@ impl<'tcx> InferCtxt<'tcx> { (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { return Err((a_vid, b_vid)); } + // We don't silently want to constrain hidden types here, so we assert that either one side is + // an infer var, so it'll get constrained to whatever the other side is, or there are no opaque + // types involved. + // We don't expect this to actually get hit, but if it does, we now at least know how to write + // a test for it. + (_, ty::Infer(ty::TyVar(_))) => {} + (ty::Infer(ty::TyVar(_)), _) => {} + _ if (r_a, r_b).has_opaque_types() => { + span_bug!( + cause.span(), + "opaque types got hidden types registered from within subtype predicate: {r_a:?} vs {r_b:?}" + ) + } _ => {} } self.enter_forall(predicate, |ty::SubtypePredicate { a_is_expected, a, b }| { if a_is_expected { - Ok(self.at(cause, param_env).sub(DefineOpaqueTypes::No, a, b)) + Ok(self.at(cause, param_env).sub(DefineOpaqueTypes::Yes, a, b)) } else { - Ok(self.at(cause, param_env).sup(DefineOpaqueTypes::No, b, a)) + Ok(self.at(cause, param_env).sup(DefineOpaqueTypes::Yes, b, a)) } }) } From 780dfb803fe1b0ad139bac9736ce847c75ab1c81 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:20:12 +0200 Subject: [PATCH 14/17] Outline default query and hook provider function implementations --- compiler/rustc_middle/src/hooks/mod.rs | 15 +++++---- compiler/rustc_middle/src/query/plumbing.rs | 36 ++++++++++++--------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index f7ce15d0a8dcb..669265c86f5c2 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -47,12 +47,7 @@ macro_rules! declare_hooks { impl Default for Providers { fn default() -> Self { Providers { - $($name: |_, $($arg,)*| bug!( - "`tcx.{}{:?}` cannot be called as `{}` was never assigned to a provider function.\n", - stringify!($name), - ($($arg,)*), - stringify!($name), - ),)* + $($name: |_, $($arg,)*| default_hook(stringify!($name), &($($arg,)*))),* } } } @@ -84,7 +79,6 @@ declare_hooks! { /// via `mir_built` hook build_mir(key: LocalDefId) -> mir::Body<'tcx>; - /// Imports all `SourceFile`s from the given crate into the current session. /// This normally happens automatically when we decode a `Span` from /// that crate's metadata - however, the incr comp cache needs @@ -103,3 +97,10 @@ declare_hooks! { /// Will fetch a DefId from a DefPathHash for a foreign crate. hook def_path_hash_to_def_id_extern(hash: DefPathHash, stable_crate_id: StableCrateId) -> DefId; } + +#[cold] +fn default_hook(name: &str, args: &dyn std::fmt::Debug) -> ! { + bug!( + "`tcx.{name}{args:?}` cannot be called as `{name}` was never assigned to a provider function" + ) +} diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 4ce6f7747a56e..643861972c4ba 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -266,13 +266,7 @@ macro_rules! separate_provide_extern_default { () }; ([(separate_provide_extern) $($rest:tt)*][$name:ident]) => { - |_, key| bug!( - "`tcx.{}({:?})` unsupported by its crate; \ - perhaps the `{}` query was never assigned a provider function", - stringify!($name), - key, - stringify!($name), - ) + |_, key| $crate::query::plumbing::default_extern_query(stringify!($name), &key) }; ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { separate_provide_extern_default!([$($modifiers)*][$($args)*]) @@ -462,15 +456,7 @@ macro_rules! define_callbacks { impl Default for Providers { fn default() -> Self { Providers { - $($name: |_, key| bug!( - "`tcx.{}({:?})` is not supported for this key;\n\ - hint: Queries can be either made to the local crate, or the external crate. \ - This error means you tried to use it for one that's not supported.\n\ - If that's not the case, {} was likely never assigned to a provider function.\n", - stringify!($name), - key, - stringify!($name), - ),)* + $($name: |_, key| $crate::query::plumbing::default_query(stringify!($name), &key)),* } } } @@ -661,3 +647,21 @@ use super::erase::EraseType; #[derive(Copy, Clone, Debug, HashStable)] pub struct CyclePlaceholder(pub ErrorGuaranteed); + +#[cold] +pub(crate) fn default_query(name: &str, key: &dyn std::fmt::Debug) -> ! { + bug!( + "`tcx.{name}({key:?})` is not supported for this key;\n\ + hint: Queries can be either made to the local crate, or the external crate. \ + This error means you tried to use it for one that's not supported.\n\ + If that's not the case, {name} was likely never assigned to a provider function.\n", + ) +} + +#[cold] +pub(crate) fn default_extern_query(name: &str, key: &dyn std::fmt::Debug) -> ! { + bug!( + "`tcx.{name}({key:?})` unsupported by its crate; \ + perhaps the `{name}` query was never assigned a provider function", + ) +} From 864eb7fa14c25de655e03d692c5da798d3d98413 Mon Sep 17 00:00:00 2001 From: Arthur Carcano Date: Fri, 12 Apr 2024 17:47:30 +0200 Subject: [PATCH 15/17] Remove uneeded clones now that TrustedStep implies Copy This is a follow up to 11fa1764ee4819aa674ca861c5e9a8fafd7a59e6 --- library/core/src/iter/range.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 5eea764b28adc..644a169294396 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -1156,11 +1156,11 @@ impl RangeInclusiveIteratorImpl for ops::RangeInclusive { let is_iterating = self.start < self.end; Some(if is_iterating { // SAFETY: just checked precondition - let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; + let n = unsafe { Step::forward_unchecked(self.start, 1) }; mem::replace(&mut self.start, n) } else { self.exhausted = true; - self.start.clone() + self.start }) } @@ -1179,7 +1179,7 @@ impl RangeInclusiveIteratorImpl for ops::RangeInclusive { while self.start < self.end { // SAFETY: just checked precondition - let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; + let n = unsafe { Step::forward_unchecked(self.start, 1) }; let n = mem::replace(&mut self.start, n); accum = f(accum, n)?; } @@ -1187,7 +1187,7 @@ impl RangeInclusiveIteratorImpl for ops::RangeInclusive { self.exhausted = true; if self.start == self.end { - accum = f(accum, self.start.clone())?; + accum = f(accum, self.start)?; } try { accum } @@ -1201,11 +1201,11 @@ impl RangeInclusiveIteratorImpl for ops::RangeInclusive { let is_iterating = self.start < self.end; Some(if is_iterating { // SAFETY: just checked precondition - let n = unsafe { Step::backward_unchecked(self.end.clone(), 1) }; + let n = unsafe { Step::backward_unchecked(self.end, 1) }; mem::replace(&mut self.end, n) } else { self.exhausted = true; - self.end.clone() + self.end }) } @@ -1224,7 +1224,7 @@ impl RangeInclusiveIteratorImpl for ops::RangeInclusive { while self.start < self.end { // SAFETY: just checked precondition - let n = unsafe { Step::backward_unchecked(self.end.clone(), 1) }; + let n = unsafe { Step::backward_unchecked(self.end, 1) }; let n = mem::replace(&mut self.end, n); accum = f(accum, n)?; } @@ -1232,7 +1232,7 @@ impl RangeInclusiveIteratorImpl for ops::RangeInclusive { self.exhausted = true; if self.start == self.end { - accum = f(accum, self.start.clone())?; + accum = f(accum, self.start)?; } try { accum } From 10b6ca139eed124e20fb49dae5ec463a8b611ac2 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 16 Apr 2024 16:50:21 +0200 Subject: [PATCH 16/17] std: fix lint on SGX --- library/std/src/sys/pal/sgx/waitqueue/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/pal/sgx/waitqueue/mod.rs b/library/std/src/sys/pal/sgx/waitqueue/mod.rs index ea49bb2803466..f5668a9493fb5 100644 --- a/library/std/src/sys/pal/sgx/waitqueue/mod.rs +++ b/library/std/src/sys/pal/sgx/waitqueue/mod.rs @@ -64,7 +64,7 @@ impl WaitVariable { #[derive(Copy, Clone)] pub enum NotifiedTcs { Single(Tcs), - All { count: NonZero }, + All { _count: NonZero }, } /// An RAII guard that will notify a set of target threads as well as unlock @@ -232,7 +232,10 @@ impl WaitQueue { } if let Some(count) = NonZero::new(count) { - Ok(WaitGuard { mutex_guard: Some(guard), notified_tcs: NotifiedTcs::All { count } }) + Ok(WaitGuard { + mutex_guard: Some(guard), + notified_tcs: NotifiedTcs::All { _count: count }, + }) } else { Err(guard) } From a03aeca99a1679e27d7439e6030fe759eb1f6f04 Mon Sep 17 00:00:00 2001 From: Michael Baikov Date: Tue, 16 Apr 2024 10:25:17 -0400 Subject: [PATCH 17/17] Allow workproducts without object files. This pull request partially reverts changes from e16c3b4a4421 Original motivation for this assert was described with "A WorkProduct without a saved file is useless" which was true at the time but now it is possible to have work products with other types of files (llvm-ir, asm, etc) and there are bugreports for this failure: For example: https://github.com/rust-lang/rust/issues/123695 Fixes https://github.com/rust-lang/rust/issues/123234 Now existing `assert` and `.unwrap_or_else` are unified into a single check that emits slightly more user friendly error message if an object files was meant to be produced but it's missing --- compiler/rustc_codegen_ssa/src/back/write.rs | 13 ++++------- .../artifact-incr-cache-no-obj/lib.rs | 6 +++++ .../artifact-incr-cache-no-obj/rmake.rs | 23 +++++++++++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 tests/run-make/artifact-incr-cache-no-obj/lib.rs create mode 100644 tests/run-make/artifact-incr-cache-no-obj/rmake.rs diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index e7f692144ff05..c4f062405bb5c 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -907,8 +907,6 @@ fn execute_copy_from_cache_work_item( module: CachedModuleCodegen, module_config: &ModuleConfig, ) -> WorkItemResult { - assert!(module_config.emit_obj != EmitObj::None); - let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap(); let load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| { @@ -928,12 +926,6 @@ fn execute_copy_from_cache_work_item( } }; - let object = load_from_incr_comp_dir( - cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)), - module.source.saved_files.get("o").unwrap_or_else(|| { - cgcx.create_dcx().emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name }) - }), - ); let dwarf_object = module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| { let dwarf_obj_out = cgcx @@ -955,9 +947,14 @@ fn execute_copy_from_cache_work_item( } }; + let should_emit_obj = module_config.emit_obj != EmitObj::None; let assembly = load_from_incr_cache(module_config.emit_asm, OutputType::Assembly); let llvm_ir = load_from_incr_cache(module_config.emit_ir, OutputType::LlvmAssembly); let bytecode = load_from_incr_cache(module_config.emit_bc, OutputType::Bitcode); + let object = load_from_incr_cache(should_emit_obj, OutputType::Object); + if should_emit_obj && object.is_none() { + cgcx.create_dcx().emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name }) + } WorkItemResult::Finished(CompiledModule { name: module.name, diff --git a/tests/run-make/artifact-incr-cache-no-obj/lib.rs b/tests/run-make/artifact-incr-cache-no-obj/lib.rs new file mode 100644 index 0000000000000..fa4048594e369 --- /dev/null +++ b/tests/run-make/artifact-incr-cache-no-obj/lib.rs @@ -0,0 +1,6 @@ +#![crate_name = "foo"] + +#[inline(never)] +pub fn add(a: u32, b: u32) -> u32 { + a + b +} diff --git a/tests/run-make/artifact-incr-cache-no-obj/rmake.rs b/tests/run-make/artifact-incr-cache-no-obj/rmake.rs new file mode 100644 index 0000000000000..de55de2a1ee20 --- /dev/null +++ b/tests/run-make/artifact-incr-cache-no-obj/rmake.rs @@ -0,0 +1,23 @@ +// emitting an object file is not necessary if user didn't ask for one +// +// This test is similar to run-make/artifact-incr-cache but it doesn't +// require to emit an object file +// +// Fixes: rust-lang/rust#123234 + +extern crate run_make_support; + +use run_make_support::{rustc, tmp_dir}; + +fn main() { + let inc_dir = tmp_dir(); + + for _ in 0..=1 { + rustc() + .input("lib.rs") + .crate_type("lib") + .emit("asm,dep-info,link,mir,llvm-ir,llvm-bc") + .incremental(&inc_dir) + .run(); + } +}