From 0e24ad537be4d47686f3b9e3e6623664bce7cbc2 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 4 Jan 2022 14:51:39 +0100 Subject: [PATCH 01/28] Implement RFC 3151: Scoped threads. --- library/std/src/thread/mod.rs | 96 ++++++++++++++++------ library/std/src/thread/scoped.rs | 132 +++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 26 deletions(-) create mode 100644 library/std/src/thread/scoped.rs diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index c799b64c05b2d..0125545a3dbd4 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -180,6 +180,12 @@ use crate::time::Duration; #[macro_use] mod local; +#[unstable(feature = "scoped_threads", issue = "none")] +mod scoped; + +#[unstable(feature = "scoped_threads", issue = "none")] +pub use scoped::{scope, Scope, ScopedJoinHandle}; + #[stable(feature = "rust1", since = "1.0.0")] pub use self::local::{AccessError, LocalKey}; @@ -446,6 +452,20 @@ impl Builder { F: FnOnce() -> T, F: Send + 'a, T: Send + 'a, + { + Ok(JoinHandle(unsafe { self.spawn_unchecked_(f, None) }?)) + } + + unsafe fn spawn_unchecked_<'a, 'scope, F, T>( + self, + f: F, + scope_data: Option<&'scope scoped::ScopeData>, + ) -> io::Result> + where + F: FnOnce() -> T, + F: Send + 'a, + T: Send + 'a, + 'scope: 'a, { let Builder { name, stack_size } = self; @@ -456,7 +476,8 @@ impl Builder { })); let their_thread = my_thread.clone(); - let my_packet: Arc>>> = Arc::new(UnsafeCell::new(None)); + let my_packet: Arc> = + Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None) }); let their_packet = my_packet.clone(); let output_capture = crate::io::set_output_capture(None); @@ -480,10 +501,14 @@ impl Builder { // closure (it is an Arc<...>) and `my_packet` will be stored in the // same `JoinInner` as this closure meaning the mutation will be // safe (not modify it and affect a value far away). - unsafe { *their_packet.get() = Some(try_result) }; + unsafe { *their_packet.result.get() = Some(try_result) }; }; - Ok(JoinHandle(JoinInner { + if let Some(scope_data) = scope_data { + scope_data.increment_n_running_threads(); + } + + Ok(JoinInner { // SAFETY: // // `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed @@ -506,8 +531,8 @@ impl Builder { )? }, thread: my_thread, - packet: Packet(my_packet), - })) + packet: my_packet, + }) } } @@ -1239,34 +1264,53 @@ impl fmt::Debug for Thread { #[stable(feature = "rust1", since = "1.0.0")] pub type Result = crate::result::Result>; -// This packet is used to communicate the return value between the spawned thread -// and the rest of the program. Memory is shared through the `Arc` within and there's -// no need for a mutex here because synchronization happens with `join()` (the -// caller will never read this packet until the thread has exited). +// This packet is used to communicate the return value between the spawned +// thread and the rest of the program. It is shared through an `Arc` and +// there's no need for a mutex here because synchronization happens with `join()` +// (the caller will never read this packet until the thread has exited). // -// This packet itself is then stored into a `JoinInner` which in turns is placed -// in `JoinHandle` and `JoinGuard`. Due to the usage of `UnsafeCell` we need to -// manually worry about impls like Send and Sync. The type `T` should -// already always be Send (otherwise the thread could not have been created) and -// this type is inherently Sync because no methods take &self. Regardless, -// however, we add inheriting impls for Send/Sync to this type to ensure it's -// Send/Sync and that future modifications will still appropriately classify it. -struct Packet(Arc>>>); - -unsafe impl Send for Packet {} -unsafe impl Sync for Packet {} +// An Arc to the packet is stored into a `JoinInner` which in turns is placed +// in `JoinHandle`. Due to the usage of `UnsafeCell` we need to manually worry +// about impls like Send and Sync. The type `T` should already always be Send +// (otherwise the thread could not have been created) and this type is +// inherently Sync because no methods take &self. Regardless, however, we add +// inheriting impls for Send/Sync to this type to ensure it's Send/Sync and +// that future modifications will still appropriately classify it. +struct Packet<'scope, T> { + scope: Option<&'scope scoped::ScopeData>, + result: UnsafeCell>>, +} + +unsafe impl<'scope, T: Send> Send for Packet<'scope, T> {} +unsafe impl<'scope, T: Sync> Sync for Packet<'scope, T> {} + +impl<'scope, T> Drop for Packet<'scope, T> { + fn drop(&mut self) { + if let Some(scope) = self.scope { + // If this packet was for a thread that ran in a scope, the thread + // panicked, and nobody consumed the panic payload, we put the + // panic payload in the scope so it can re-throw it, if it didn't + // already capture any panic yet. + if let Some(Err(e)) = self.result.get_mut().take() { + scope.panic_payload.lock().unwrap().get_or_insert(e); + } + // Book-keeping so the scope knows when it's done. + scope.decrement_n_running_threads(); + } + } +} /// Inner representation for JoinHandle -struct JoinInner { +struct JoinInner<'scope, T> { native: imp::Thread, thread: Thread, - packet: Packet, + packet: Arc>, } -impl JoinInner { +impl<'scope, T> JoinInner<'scope, T> { fn join(mut self) -> Result { self.native.join(); - Arc::get_mut(&mut self.packet.0).unwrap().get_mut().take().unwrap() + Arc::get_mut(&mut self.packet).unwrap().result.get_mut().take().unwrap() } } @@ -1333,7 +1377,7 @@ impl JoinInner { /// [`thread::Builder::spawn`]: Builder::spawn /// [`thread::spawn`]: spawn #[stable(feature = "rust1", since = "1.0.0")] -pub struct JoinHandle(JoinInner); +pub struct JoinHandle(JoinInner<'static, T>); #[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] unsafe impl Send for JoinHandle {} @@ -1407,7 +1451,7 @@ impl JoinHandle { /// function has returned, but before the thread itself has stopped running. #[unstable(feature = "thread_is_running", issue = "90470")] pub fn is_running(&self) -> bool { - Arc::strong_count(&self.0.packet.0) > 1 + Arc::strong_count(&self.0.packet) > 1 } } diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs new file mode 100644 index 0000000000000..8e9a43e05bef1 --- /dev/null +++ b/library/std/src/thread/scoped.rs @@ -0,0 +1,132 @@ +use super::{current, park, Builder, JoinInner, Result, Thread}; +use crate::any::Any; +use crate::fmt; +use crate::io; +use crate::marker::PhantomData; +use crate::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::Mutex; + +/// TODO: documentation +pub struct Scope<'env> { + data: ScopeData, + env: PhantomData<&'env ()>, +} + +/// TODO: documentation +pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>); + +pub(super) struct ScopeData { + n_running_threads: AtomicUsize, + main_thread: Thread, + pub(super) panic_payload: Mutex>>, +} + +impl ScopeData { + pub(super) fn increment_n_running_threads(&self) { + // We check for 'overflow' with usize::MAX / 2, to make sure there's no + // chance it overflows to 0, which would result in unsoundness. + if self.n_running_threads.fetch_add(1, Ordering::Relaxed) == usize::MAX / 2 { + // This can only reasonably happen by mem::forget()'ing many many ScopedJoinHandles. + self.decrement_n_running_threads(); + panic!("too many running threads in thread scope"); + } + } + pub(super) fn decrement_n_running_threads(&self) { + if self.n_running_threads.fetch_sub(1, Ordering::Release) == 1 { + self.main_thread.unpark(); + } + } +} + +/// TODO: documentation +pub fn scope<'env, F, T>(f: F) -> T +where + F: FnOnce(&Scope<'env>) -> T, +{ + let mut scope = Scope { + data: ScopeData { + n_running_threads: AtomicUsize::new(0), + main_thread: current(), + panic_payload: Mutex::new(None), + }, + env: PhantomData, + }; + + // Run `f`, but catch panics so we can make sure to wait for all the threads to join. + let result = catch_unwind(AssertUnwindSafe(|| f(&scope))); + + // Wait until all the threads are finished. + while scope.data.n_running_threads.load(Ordering::Acquire) != 0 { + park(); + } + + // Throw any panic from `f` or from any panicked thread, or the return value of `f` otherwise. + match result { + Err(e) => { + // `f` itself panicked. + resume_unwind(e); + } + Ok(result) => { + if let Some(panic_payload) = scope.data.panic_payload.get_mut().unwrap().take() { + // A thread panicked. + resume_unwind(panic_payload); + } else { + // Nothing panicked. + result + } + } + } +} + +impl<'env> Scope<'env> { + /// TODO: documentation + pub fn spawn<'scope, F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T> + where + F: FnOnce(&Scope<'env>) -> T + Send + 'env, + T: Send + 'env, + { + Builder::new().spawn_scoped(self, f).expect("failed to spawn thread") + } +} + +impl Builder { + fn spawn_scoped<'scope, 'env, F, T>( + self, + scope: &'scope Scope<'env>, + f: F, + ) -> io::Result> + where + F: FnOnce(&Scope<'env>) -> T + Send + 'env, + T: Send + 'env, + { + Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(|| f(scope), Some(&scope.data)) }?)) + } +} + +impl<'scope, T> ScopedJoinHandle<'scope, T> { + /// TODO + pub fn join(self) -> Result { + self.0.join() + } + + /// TODO + pub fn thread(&self) -> &Thread { + &self.0.thread + } +} + +impl<'env> fmt::Debug for Scope<'env> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Scope") + .field("n_running_threads", &self.data.n_running_threads.load(Ordering::Relaxed)) + .field("panic_payload", &self.data.panic_payload) + .finish_non_exhaustive() + } +} + +impl<'scope, T> fmt::Debug for ScopedJoinHandle<'scope, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ScopedJoinHandle").finish_non_exhaustive() + } +} From cc699e1b62573fef34e2521a6ada0975e79b0459 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 4 Jan 2022 14:52:55 +0100 Subject: [PATCH 02/28] Add ScopedJoinHandle::is_running(). --- library/std/src/thread/scoped.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 8e9a43e05bef1..0b65c682c56b3 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -5,7 +5,7 @@ use crate::io; use crate::marker::PhantomData; use crate::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::Mutex; +use crate::sync::{Arc, Mutex}; /// TODO: documentation pub struct Scope<'env> { @@ -114,6 +114,15 @@ impl<'scope, T> ScopedJoinHandle<'scope, T> { pub fn thread(&self) -> &Thread { &self.0.thread } + + /// Checks if the the associated thread is still running its main function. + /// + /// This might return `false` for a brief moment after the thread's main + /// function has returned, but before the thread itself has stopped running. + #[unstable(feature = "thread_is_running", issue = "90470")] + pub fn is_running(&self) -> bool { + Arc::strong_count(&self.0.packet) > 1 + } } impl<'env> fmt::Debug for Scope<'env> { From da33da161bf01093bc87e0cdff317d40bd648974 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 4 Jan 2022 15:57:09 +0100 Subject: [PATCH 03/28] Add documentation for scoped threads. --- library/std/src/thread/scoped.rs | 186 +++++++++++++++++++++++++++++-- 1 file changed, 175 insertions(+), 11 deletions(-) diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 0b65c682c56b3..8f050b72a4104 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -7,13 +7,17 @@ use crate::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sync::{Arc, Mutex}; -/// TODO: documentation +/// A scope to spawn scoped threads in. +/// +/// See [`scope`] for details. pub struct Scope<'env> { data: ScopeData, env: PhantomData<&'env ()>, } -/// TODO: documentation +/// An owned permission to join on a scoped thread (block on its termination). +/// +/// See [`Scope::spawn`] for details. pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>); pub(super) struct ScopeData { @@ -39,7 +43,52 @@ impl ScopeData { } } -/// TODO: documentation +/// Create a scope for spawning scoped threads. +/// +/// The function passed to `scope` will be provided a [`Scope`] object, +/// through which scoped threads can be [spawned][`Scope::spawn`]. +/// +/// Unlike non-scoped threads, scoped threads can non-`'static` data, +/// as the scope guarantees all threads will be joined at the end of the scope. +/// +/// All threads spawned within the scope that haven't been manually joined +/// will be automatically joined before this function returns. +/// +/// # Panics +/// +/// If any of the automatically joined threads panicked, this function will panic. +/// +/// If you want to handle panics from spawned threads, +/// [`join`][ScopedJoinHandle::join] them before the end of the scope. +/// +/// # Example +/// +/// ``` +/// #![feature(scoped_threads)] +/// use std::thread; +/// +/// let mut a = vec![1, 2, 3]; +/// let mut x = 0; +/// +/// thread::scope(|s| { +/// s.spawn(|_| { +/// println!("hello from the first scoped thread"); +/// // We can borrow `a` here. +/// dbg!(&a); +/// }); +/// s.spawn(|_| { +/// println!("hello from the second scoped thread"); +/// // We can even mutably borrow `x` here, +/// // because no other threads are using it. +/// x += a[0] + a[2]; +/// }); +/// println!("hello from the main thread"); +/// }); +/// +/// // After the scope, we can modify and access our variables again: +/// a.push(4); +/// assert_eq!(x, a.len()); +/// ``` pub fn scope<'env, F, T>(f: F) -> T where F: FnOnce(&Scope<'env>) -> T, @@ -80,7 +129,30 @@ where } impl<'env> Scope<'env> { - /// TODO: documentation + /// Spawns a new thread within a scope, returning a [`ScopedJoinHandle`] for it. + /// + /// Unlike non-scoped threads, threads spawned with this function may + /// borrow non-`'static` data from the outside the scope. See [`scope`] for + /// details. + /// + /// The join handle provides a [`join`] method that can be used to join the spawned + /// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing + /// the panic payload. + /// + /// If the join handle is dropped, the spawned thread will implicitly joined at the + /// end of the scope. In that case, if the spawned thread panics, [`scope`] will + /// panic after all threads are joined. + /// + /// This call will create a thread using default parameters of [`Builder`]. + /// If you want to specify the stack size or the name of the thread, use + /// [`Builder::spawn_scoped`] instead. + /// + /// # Panics + /// + /// Panics if the OS fails to create a thread; use [`Builder::spawn`] + /// to recover from such errors. + /// + /// [`join`]: ScopedJoinHandle::join pub fn spawn<'scope, F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T> where F: FnOnce(&Scope<'env>) -> T + Send + 'env, @@ -91,7 +163,54 @@ impl<'env> Scope<'env> { } impl Builder { - fn spawn_scoped<'scope, 'env, F, T>( + /// Spawns a new scoped thread using the settings set through this `Builder`. + /// + /// Unlike [`Scope::spawn`], this method yields an [`io::Result`] to + /// capture any failure to create the thread at the OS level. + /// + /// [`io::Result`]: crate::io::Result + /// + /// # Panics + /// + /// Panics if a thread name was set and it contained null bytes. + /// + /// # Example + /// + /// ``` + /// #![feature(scoped_threads)] + /// use std::thread; + /// + /// let mut a = vec![1, 2, 3]; + /// let mut x = 0; + /// + /// thread::scope(|s| { + /// thread::Builder::new() + /// .name("first".to_string()) + /// .spawn_scoped(s, |_| + /// { + /// println!("hello from the {:?} scoped thread", thread::current().name()); + /// // We can borrow `a` here. + /// dbg!(&a); + /// }) + /// .unwrap(); + /// thread::Builder::new() + /// .name("second".to_string()) + /// .spawn_scoped(s, |_| + /// { + /// println!("hello from the {:?} scoped thread", thread::current().name()); + /// // We can even mutably borrow `x` here, + /// // because no other threads are using it. + /// x += a[0] + a[2]; + /// }) + /// .unwrap(); + /// println!("hello from the main thread"); + /// }); + /// + /// // After the scope, we can modify and access our variables again: + /// a.push(4); + /// assert_eq!(x, a.len()); + /// ``` + pub fn spawn_scoped<'scope, 'env, F, T>( self, scope: &'scope Scope<'env>, f: F, @@ -105,16 +224,61 @@ impl Builder { } impl<'scope, T> ScopedJoinHandle<'scope, T> { - /// TODO - pub fn join(self) -> Result { - self.0.join() - } - - /// TODO + /// Extracts a handle to the underlying thread. + /// + /// # Examples + /// + /// ``` + /// #![feature(scoped_threads)] + /// #![feature(thread_is_running)] + /// + /// use std::thread; + /// + /// thread::scope(|s| { + /// let t = s.spawn(|_| { + /// println!("hello"); + /// }); + /// println!("thread id: {:?}", t.thread().id()); + /// }); + /// ``` + #[must_use] pub fn thread(&self) -> &Thread { &self.0.thread } + /// Waits for the associated thread to finish. + /// + /// This function will return immediately if the associated thread has already finished. + /// + /// In terms of [atomic memory orderings], the completion of the associated + /// thread synchronizes with this function returning. + /// In other words, all operations performed by that thread + /// [happen before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses) + /// all operations that happen after `join` returns. + /// + /// If the associated thread panics, [`Err`] is returned with the panic payload. + /// + /// [atomic memory orderings]: crate::sync::atomic + /// + /// # Examples + /// + /// ``` + /// #![feature(scoped_threads)] + /// #![feature(thread_is_running)] + /// + /// use std::thread; + /// + /// thread::scope(|s| { + /// let t = s.spawn(|_| { + /// panic!("oh no"); + /// }); + /// assert!(t.join().is_err()); + /// }); + /// ``` + pub fn join(self) -> Result { + self.0.join() + } + /// Checks if the the associated thread is still running its main function. /// /// This might return `false` for a brief moment after the thread's main From f5217792ed0dbf132174250ea81ca2e3dd7b60f1 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 4 Jan 2022 16:10:14 +0100 Subject: [PATCH 04/28] Simplify panicking mechanism of thread::scope. It now panic!()s on its own, rather than resume_unwind'ing the panic payload from the thread. Using resume_unwind skips the panic_handler, meaning that the main thread would never have a panic handler run, which can get confusing. --- library/std/src/thread/mod.rs | 13 ++++------- library/std/src/thread/scoped.rs | 40 ++++++++++++++------------------ 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 0125545a3dbd4..88dabfa8d22b8 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1286,16 +1286,13 @@ unsafe impl<'scope, T: Sync> Sync for Packet<'scope, T> {} impl<'scope, T> Drop for Packet<'scope, T> { fn drop(&mut self) { + // Book-keeping so the scope knows when it's done. if let Some(scope) = self.scope { // If this packet was for a thread that ran in a scope, the thread - // panicked, and nobody consumed the panic payload, we put the - // panic payload in the scope so it can re-throw it, if it didn't - // already capture any panic yet. - if let Some(Err(e)) = self.result.get_mut().take() { - scope.panic_payload.lock().unwrap().get_or_insert(e); - } - // Book-keeping so the scope knows when it's done. - scope.decrement_n_running_threads(); + // panicked, and nobody consumed the panic payload, we make sure + // the scope function will panic. + let unhandled_panic = matches!(self.result.get_mut(), Some(Err(_))); + scope.decrement_n_running_threads(unhandled_panic); } } } diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 8f050b72a4104..f9b056dcd4658 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -1,11 +1,10 @@ use super::{current, park, Builder, JoinInner, Result, Thread}; -use crate::any::Any; use crate::fmt; use crate::io; use crate::marker::PhantomData; use crate::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::{Arc, Mutex}; +use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use crate::sync::Arc; /// A scope to spawn scoped threads in. /// @@ -22,8 +21,8 @@ pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>); pub(super) struct ScopeData { n_running_threads: AtomicUsize, + a_thread_panicked: AtomicBool, main_thread: Thread, - pub(super) panic_payload: Mutex>>, } impl ScopeData { @@ -32,11 +31,14 @@ impl ScopeData { // chance it overflows to 0, which would result in unsoundness. if self.n_running_threads.fetch_add(1, Ordering::Relaxed) == usize::MAX / 2 { // This can only reasonably happen by mem::forget()'ing many many ScopedJoinHandles. - self.decrement_n_running_threads(); + self.decrement_n_running_threads(false); panic!("too many running threads in thread scope"); } } - pub(super) fn decrement_n_running_threads(&self) { + pub(super) fn decrement_n_running_threads(&self, panic: bool) { + if panic { + self.a_thread_panicked.store(true, Ordering::Relaxed); + } if self.n_running_threads.fetch_sub(1, Ordering::Release) == 1 { self.main_thread.unpark(); } @@ -89,15 +91,16 @@ impl ScopeData { /// a.push(4); /// assert_eq!(x, a.len()); /// ``` +#[track_caller] pub fn scope<'env, F, T>(f: F) -> T where F: FnOnce(&Scope<'env>) -> T, { - let mut scope = Scope { + let scope = Scope { data: ScopeData { n_running_threads: AtomicUsize::new(0), main_thread: current(), - panic_payload: Mutex::new(None), + a_thread_panicked: AtomicBool::new(false), }, env: PhantomData, }; @@ -110,21 +113,11 @@ where park(); } - // Throw any panic from `f` or from any panicked thread, or the return value of `f` otherwise. + // Throw any panic from `f`, or the return value of `f` if no thread panicked. match result { - Err(e) => { - // `f` itself panicked. - resume_unwind(e); - } - Ok(result) => { - if let Some(panic_payload) = scope.data.panic_payload.get_mut().unwrap().take() { - // A thread panicked. - resume_unwind(panic_payload); - } else { - // Nothing panicked. - result - } - } + Err(e) => resume_unwind(e), + Ok(_) if scope.data.a_thread_panicked.load(Ordering::Relaxed) => panic!("a thread panicked"), + Ok(result) => result, } } @@ -293,7 +286,8 @@ impl<'env> fmt::Debug for Scope<'env> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Scope") .field("n_running_threads", &self.data.n_running_threads.load(Ordering::Relaxed)) - .field("panic_payload", &self.data.panic_payload) + .field("a_thread_panicked", &self.data.a_thread_panicked) + .field("main_thread", &self.data.main_thread) .finish_non_exhaustive() } } From 4300bea0c25ae5dabf6865d333568eede555856f Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 4 Jan 2022 16:32:39 +0100 Subject: [PATCH 05/28] Formatting. --- library/std/src/thread/scoped.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index f9b056dcd4658..0db938b9f392a 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -116,7 +116,9 @@ where // Throw any panic from `f`, or the return value of `f` if no thread panicked. match result { Err(e) => resume_unwind(e), - Ok(_) if scope.data.a_thread_panicked.load(Ordering::Relaxed) => panic!("a thread panicked"), + Ok(_) if scope.data.a_thread_panicked.load(Ordering::Relaxed) => { + panic!("a thread panicked") + } Ok(result) => result, } } From c5cb2def06c210a99a7299cfed67826b22452cf8 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 4 Jan 2022 16:57:16 +0100 Subject: [PATCH 06/28] Fix variance of thread::Scope. --- library/std/src/thread/scoped.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 0db938b9f392a..cb5f48e5107d4 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -11,7 +11,7 @@ use crate::sync::Arc; /// See [`scope`] for details. pub struct Scope<'env> { data: ScopeData, - env: PhantomData<&'env ()>, + env: PhantomData<&'env mut &'env ()>, } /// An owned permission to join on a scoped thread (block on its termination). From 2c8cc70ea045e3d22ccb3eacb83bb748c717abc7 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 4 Jan 2022 15:58:29 +0000 Subject: [PATCH 07/28] Use > rather than == for overflow check in scoped threads. Co-authored-by: Jacob Lifshay --- library/std/src/thread/scoped.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index cb5f48e5107d4..cc44eccc0a094 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -29,7 +29,7 @@ impl ScopeData { pub(super) fn increment_n_running_threads(&self) { // We check for 'overflow' with usize::MAX / 2, to make sure there's no // chance it overflows to 0, which would result in unsoundness. - if self.n_running_threads.fetch_add(1, Ordering::Relaxed) == usize::MAX / 2 { + if self.n_running_threads.fetch_add(1, Ordering::Relaxed) > usize::MAX / 2 { // This can only reasonably happen by mem::forget()'ing many many ScopedJoinHandles. self.decrement_n_running_threads(false); panic!("too many running threads in thread scope"); From 09e6665ababf2c3ee258af1f151f7f375626b4f3 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 4 Jan 2022 17:05:12 +0100 Subject: [PATCH 08/28] Fix typo in documentation. --- library/std/src/thread/scoped.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index cc44eccc0a094..35e3dba54115d 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -50,7 +50,7 @@ impl ScopeData { /// The function passed to `scope` will be provided a [`Scope`] object, /// through which scoped threads can be [spawned][`Scope::spawn`]. /// -/// Unlike non-scoped threads, scoped threads can non-`'static` data, +/// Unlike non-scoped threads, scoped threads can borrow non-`'static` data, /// as the scope guarantees all threads will be joined at the end of the scope. /// /// All threads spawned within the scope that haven't been manually joined From c429ade7604b7417021ae39c63e3c7f26014c3a3 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 4 Jan 2022 17:38:52 +0000 Subject: [PATCH 09/28] Fix typo in is_running() docs. Co-authored-by: Mattias Buelens <649348+MattiasBuelens@users.noreply.github.com> --- library/std/src/thread/mod.rs | 2 +- library/std/src/thread/scoped.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 88dabfa8d22b8..1fdde17d0fd41 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1442,7 +1442,7 @@ impl JoinHandle { self.0.join() } - /// Checks if the the associated thread is still running its main function. + /// Checks if the associated thread is still running its main function. /// /// This might return `false` for a brief moment after the thread's main /// function has returned, but before the thread itself has stopped running. diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 35e3dba54115d..cda86a48b2430 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -274,7 +274,7 @@ impl<'scope, T> ScopedJoinHandle<'scope, T> { self.0.join() } - /// Checks if the the associated thread is still running its main function. + /// Checks if the associated thread is still running its main function. /// /// This might return `false` for a brief moment after the thread's main /// function has returned, but before the thread itself has stopped running. From 5b5746f08138f880cf21798402a65cd39d17cf68 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 4 Jan 2022 18:43:23 +0000 Subject: [PATCH 10/28] Fix typo in Scope::spawn docs. Co-authored-by: deltragon --- library/std/src/thread/scoped.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index cda86a48b2430..701850b42dcf6 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -144,7 +144,7 @@ impl<'env> Scope<'env> { /// /// # Panics /// - /// Panics if the OS fails to create a thread; use [`Builder::spawn`] + /// Panics if the OS fails to create a thread; use [`Builder::spawn_scoped`] /// to recover from such errors. /// /// [`join`]: ScopedJoinHandle::join From a9efbaf3a500a8670491bdc18012a7ebfbc1c122 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 5 Jan 2022 11:24:42 +0100 Subject: [PATCH 11/28] Rename n_running_threads to num_running_threads. --- library/std/src/thread/mod.rs | 4 ++-- library/std/src/thread/scoped.rs | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 1fdde17d0fd41..e5e9638aaa8c5 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -505,7 +505,7 @@ impl Builder { }; if let Some(scope_data) = scope_data { - scope_data.increment_n_running_threads(); + scope_data.increment_num_running_threads(); } Ok(JoinInner { @@ -1292,7 +1292,7 @@ impl<'scope, T> Drop for Packet<'scope, T> { // panicked, and nobody consumed the panic payload, we make sure // the scope function will panic. let unhandled_panic = matches!(self.result.get_mut(), Some(Err(_))); - scope.decrement_n_running_threads(unhandled_panic); + scope.decrement_num_running_threads(unhandled_panic); } } } diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 701850b42dcf6..b2fe3c3185ed3 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -20,26 +20,26 @@ pub struct Scope<'env> { pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>); pub(super) struct ScopeData { - n_running_threads: AtomicUsize, + num_running_threads: AtomicUsize, a_thread_panicked: AtomicBool, main_thread: Thread, } impl ScopeData { - pub(super) fn increment_n_running_threads(&self) { + pub(super) fn increment_num_running_threads(&self) { // We check for 'overflow' with usize::MAX / 2, to make sure there's no // chance it overflows to 0, which would result in unsoundness. - if self.n_running_threads.fetch_add(1, Ordering::Relaxed) > usize::MAX / 2 { + if self.num_running_threads.fetch_add(1, Ordering::Relaxed) > usize::MAX / 2 { // This can only reasonably happen by mem::forget()'ing many many ScopedJoinHandles. - self.decrement_n_running_threads(false); + self.decrement_num_running_threads(false); panic!("too many running threads in thread scope"); } } - pub(super) fn decrement_n_running_threads(&self, panic: bool) { + pub(super) fn decrement_num_running_threads(&self, panic: bool) { if panic { self.a_thread_panicked.store(true, Ordering::Relaxed); } - if self.n_running_threads.fetch_sub(1, Ordering::Release) == 1 { + if self.num_running_threads.fetch_sub(1, Ordering::Release) == 1 { self.main_thread.unpark(); } } @@ -98,7 +98,7 @@ where { let scope = Scope { data: ScopeData { - n_running_threads: AtomicUsize::new(0), + num_running_threads: AtomicUsize::new(0), main_thread: current(), a_thread_panicked: AtomicBool::new(false), }, @@ -109,7 +109,7 @@ where let result = catch_unwind(AssertUnwindSafe(|| f(&scope))); // Wait until all the threads are finished. - while scope.data.n_running_threads.load(Ordering::Acquire) != 0 { + while scope.data.num_running_threads.load(Ordering::Acquire) != 0 { park(); } @@ -287,7 +287,7 @@ impl<'scope, T> ScopedJoinHandle<'scope, T> { impl<'env> fmt::Debug for Scope<'env> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Scope") - .field("n_running_threads", &self.data.n_running_threads.load(Ordering::Relaxed)) + .field("num_running_threads", &self.data.num_running_threads.load(Ordering::Relaxed)) .field("a_thread_panicked", &self.data.a_thread_panicked) .field("main_thread", &self.data.main_thread) .finish_non_exhaustive() From 5bd5781823629235c1a96950a4487263d52cee6a Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 5 Jan 2022 11:25:09 +0100 Subject: [PATCH 12/28] Fix missing .load() in Scope's Debug impl. --- library/std/src/thread/scoped.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index b2fe3c3185ed3..8c64a6e6201d2 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -288,7 +288,7 @@ impl<'env> fmt::Debug for Scope<'env> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Scope") .field("num_running_threads", &self.data.num_running_threads.load(Ordering::Relaxed)) - .field("a_thread_panicked", &self.data.a_thread_panicked) + .field("a_thread_panicked", &self.data.a_thread_panicked.load(Ordering::Relaxed)) .field("main_thread", &self.data.main_thread) .finish_non_exhaustive() } From aa9c0881efb116c20c9550ba26150a2060a276f0 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 5 Jan 2022 12:13:33 +0100 Subject: [PATCH 13/28] Note the invariance over 'env in Scope<'env>. --- library/std/src/thread/scoped.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 8c64a6e6201d2..2bc93549dab26 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -11,6 +11,8 @@ use crate::sync::Arc; /// See [`scope`] for details. pub struct Scope<'env> { data: ScopeData, + /// Invariance over 'env, to make sure 'env cannot shrink, + /// which is necessary for soundness. env: PhantomData<&'env mut &'env ()>, } From 4cb73704e2f0be18f44c295a9b8435e015419dda Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 5 Jan 2022 11:17:11 +0000 Subject: [PATCH 14/28] Mention *scoped* thread in panic message. Co-authored-by: Amanieu d'Antras --- library/std/src/thread/scoped.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 2bc93549dab26..c1b501f3aff7e 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -119,7 +119,7 @@ where match result { Err(e) => resume_unwind(e), Ok(_) if scope.data.a_thread_panicked.load(Ordering::Relaxed) => { - panic!("a thread panicked") + panic!("a scoped thread panicked") } Ok(result) => result, } From bc8cef194b7754e61aecb27bd5fe15fbe6ee026e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Sat, 22 Jan 2022 12:12:12 +0100 Subject: [PATCH 15/28] rustc_mir_itertools: Avoid needless `collect` with itertools --- compiler/rustc_mir_transform/src/coverage/debug.rs | 5 +---- compiler/rustc_mir_transform/src/coverage/graph.rs | 10 ++-------- compiler/rustc_mir_transform/src/coverage/spans.rs | 7 ++----- compiler/rustc_mir_transform/src/coverage/tests.rs | 5 +---- .../src/function_item_references.rs | 3 ++- 5 files changed, 8 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs index c61ee6f7e6cb7..ce8b187a74443 100644 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ b/compiler/rustc_mir_transform/src/coverage/debug.rs @@ -111,6 +111,7 @@ use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; use super::spans::CoverageSpan; +use itertools::Itertools; use rustc_middle::mir::create_dump_file; use rustc_middle::mir::generic_graphviz::GraphvizWriter; use rustc_middle::mir::spanview::{self, SpanViewable}; @@ -739,7 +740,6 @@ pub(super) fn dump_coverage_graphviz<'tcx>( ) } }) - .collect::>() .join("\n ") )); } @@ -768,7 +768,6 @@ fn bcb_to_string_sections<'tcx>( .map(|expression| { format!("Intermediate {}", debug_counters.format_counter(expression)) }) - .collect::>() .join("\n"), ); } @@ -783,7 +782,6 @@ fn bcb_to_string_sections<'tcx>( covspan.format(tcx, mir_body) ) }) - .collect::>() .join("\n"), ); } @@ -793,7 +791,6 @@ fn bcb_to_string_sections<'tcx>( dependency_counters .iter() .map(|counter| debug_counters.format_counter(counter)) - .collect::>() .join(" \n"), )); } diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index a25402a1ff9f5..c0ad1b81d81f5 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -1,5 +1,6 @@ use super::Error; +use itertools::Itertools; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph::dominators::{self, Dominators}; use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; @@ -422,14 +423,7 @@ impl BasicCoverageBlockData { } pub fn id(&self) -> String { - format!( - "@{}", - self.basic_blocks - .iter() - .map(|bb| bb.index().to_string()) - .collect::>() - .join(ID_SEPARATOR) - ) + format!("@{}", self.basic_blocks.iter().map(|bb| bb.index().to_string()).join(ID_SEPARATOR)) } } diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index a9161580bc681..237cb1e11053a 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,6 +1,7 @@ use super::debug::term_type; use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB}; +use itertools::Itertools; use rustc_data_structures::graph::WithNumNodes; use rustc_middle::mir::spanview::source_range_no_file; use rustc_middle::mir::{ @@ -169,11 +170,7 @@ impl CoverageSpan { CoverageStatement::Statement(bb, _, index) => (bb, index), CoverageStatement::Terminator(bb, _) => (bb, usize::MAX), }); - sorted_coverage_statements - .iter() - .map(|covstmt| covstmt.format(tcx, mir_body)) - .collect::>() - .join("\n") + sorted_coverage_statements.iter().map(|covstmt| covstmt.format(tcx, mir_body)).join("\n") } /// If the span is part of a macro, returns the macro name symbol. diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index b9c79d4cf2db8..62ea2538ff0d8 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -31,6 +31,7 @@ use super::spans; use coverage_test_macros::let_bcb; +use itertools::Itertools; use rustc_data_structures::graph::WithNumNodes; use rustc_data_structures::graph::WithSuccessors; use rustc_index::vec::{Idx, IndexVec}; @@ -232,11 +233,9 @@ fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) { mir_body .successors(bb) .map(|successor| { format!(" {:?} -> {:?};", bb, successor) }) - .collect::>() .join("\n") ) }) - .collect::>() .join("\n") ); } @@ -262,11 +261,9 @@ fn print_coverage_graphviz( basic_coverage_blocks .successors(bcb) .map(|successor| { format!(" {:?} -> {:?};", bcb, successor) }) - .collect::>() .join("\n") ) }) - .collect::>() .join("\n") ); } diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index f364a332a788c..450e0c31e982b 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_middle::mir::visit::Visitor; @@ -197,7 +198,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { let ident = self.tcx.item_name(fn_id).to_ident_string(); let ty_params = fn_substs.types().map(|ty| format!("{}", ty)); let const_params = fn_substs.consts().map(|c| format!("{}", c)); - let params = ty_params.chain(const_params).collect::>().join(", "); + let params = ty_params.chain(const_params).join(", "); let num_args = fn_sig.inputs().map_bound(|inputs| inputs.len()).skip_binder(); let variadic = if fn_sig.c_variadic() { ", ..." } else { "" }; let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" }; From e572c5a3d53e2ca608aa38d8d1a07a0e91c524a9 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sat, 22 Jan 2022 16:02:18 +0100 Subject: [PATCH 16/28] Simplify Send/Sync of std::thread::Packet. --- library/std/src/thread/mod.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index e5e9638aaa8c5..8f16259b87016 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1270,18 +1270,16 @@ pub type Result = crate::result::Result>; // (the caller will never read this packet until the thread has exited). // // An Arc to the packet is stored into a `JoinInner` which in turns is placed -// in `JoinHandle`. Due to the usage of `UnsafeCell` we need to manually worry -// about impls like Send and Sync. The type `T` should already always be Send -// (otherwise the thread could not have been created) and this type is -// inherently Sync because no methods take &self. Regardless, however, we add -// inheriting impls for Send/Sync to this type to ensure it's Send/Sync and -// that future modifications will still appropriately classify it. +// in `JoinHandle`. struct Packet<'scope, T> { scope: Option<&'scope scoped::ScopeData>, result: UnsafeCell>>, } -unsafe impl<'scope, T: Send> Send for Packet<'scope, T> {} +// Due to the usage of `UnsafeCell` we need to manually implement Sync. +// The type `T` should already always be Send (otherwise the thread could not +// have been created) and the Packet is Sync because all access to the +// `UnsafeCell` synchronized (by the `join()` boundary), and `ScopeData` is Sync. unsafe impl<'scope, T: Sync> Sync for Packet<'scope, T> {} impl<'scope, T> Drop for Packet<'scope, T> { From 12cc7d9e1552068e224fd484a7dcc92a5d337110 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sat, 22 Jan 2022 16:03:23 +0100 Subject: [PATCH 17/28] Add tracking issue number for scoped_threads. --- library/std/src/thread/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 8f16259b87016..fdf0e9faba48e 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -180,10 +180,10 @@ use crate::time::Duration; #[macro_use] mod local; -#[unstable(feature = "scoped_threads", issue = "none")] +#[unstable(feature = "scoped_threads", issue = "93203")] mod scoped; -#[unstable(feature = "scoped_threads", issue = "none")] +#[unstable(feature = "scoped_threads", issue = "93203")] pub use scoped::{scope, Scope, ScopedJoinHandle}; #[stable(feature = "rust1", since = "1.0.0")] From 465c405418409cdd1d4c1537c44849effd01501d Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sat, 22 Jan 2022 17:15:08 +0100 Subject: [PATCH 18/28] Add test for thread::Scope invariance. --- library/std/src/thread/scoped.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index c1b501f3aff7e..9dd7c15fc5922 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -13,6 +13,19 @@ pub struct Scope<'env> { data: ScopeData, /// Invariance over 'env, to make sure 'env cannot shrink, /// which is necessary for soundness. + /// + /// Without invariance, this would compile fine but be unsound: + /// + /// ```compile_fail + /// #![feature(scoped_threads)] + /// + /// std::thread::scope(|s| { + /// s.spawn(|s| { + /// let a = String::from("abcd"); + /// s.spawn(|_| println!("{:?}", a)); // might run after `a` is dropped + /// }); + /// }); + /// ``` env: PhantomData<&'env mut &'env ()>, } From cbb0fffe59ff17e322b0b29d73381975abe039c1 Mon Sep 17 00:00:00 2001 From: Caio Date: Sat, 22 Jan 2022 17:45:45 -0300 Subject: [PATCH 19/28] Fix let_chains and if_let_guard feature flags --- compiler/rustc_parse/src/parser/expr.rs | 20 +++++- .../ui/rfc-2294-if-let-guard/feature-gate.rs | 13 ++-- .../rfc-2294-if-let-guard/feature-gate.stderr | 72 ++++++++++++++----- .../ui/rfc-2497-if-let-chains/issue-93150.rs | 8 +++ .../rfc-2497-if-let-chains/issue-93150.stderr | 22 ++++++ 5 files changed, 112 insertions(+), 23 deletions(-) create mode 100644 src/test/ui/rfc-2497-if-let-chains/issue-93150.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/issue-93150.stderr diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 192e87b4c01f7..26284728ff2c6 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2383,6 +2383,17 @@ impl<'a> Parser<'a> { } pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { + fn check_let_expr(expr: &Expr) -> (bool, bool) { + match expr.kind { + ExprKind::Binary(_, ref lhs, ref rhs) => { + let lhs_rslt = check_let_expr(lhs); + let rhs_rslt = check_let_expr(rhs); + (lhs_rslt.0 || rhs_rslt.0, false) + } + ExprKind::Let(..) => (true, true), + _ => (false, true), + } + } let attrs = self.parse_outer_attributes()?; self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; @@ -2390,9 +2401,12 @@ impl<'a> Parser<'a> { let guard = if this.eat_keyword(kw::If) { let if_span = this.prev_token.span; let cond = this.parse_expr()?; - if let ExprKind::Let(..) = cond.kind { - // Remove the last feature gating of a `let` expression since it's stable. - this.sess.gated_spans.ungate_last(sym::let_chains, cond.span); + let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond); + if has_let_expr { + if does_not_have_bin_op { + // Remove the last feature gating of a `let` expression since it's stable. + this.sess.gated_spans.ungate_last(sym::let_chains, cond.span); + } let span = if_span.to(cond.span); this.sess.gated_spans.gate(sym::if_let_guard, span); } diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs index 34d2d84da934f..4a36515b99128 100644 --- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs +++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs @@ -14,10 +14,12 @@ fn _if_let_guard() { //~^ ERROR `let` expressions in this position are unstable () if true && let 0 = 1 => {} - //~^ ERROR `let` expressions in this position are unstable + //~^ ERROR `if let` guards are experimental + //~| ERROR `let` expressions in this position are unstable () if let 0 = 1 && true => {} - //~^ ERROR `let` expressions in this position are unstable + //~^ ERROR `if let` guards are experimental + //~| ERROR `let` expressions in this position are unstable () if (let 0 = 1) && true => {} //~^ ERROR `let` expressions in this position are unstable @@ -30,14 +32,17 @@ fn _if_let_guard() { //~| ERROR `let` expressions in this position are unstable () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} - //~^ ERROR `let` expressions in this position are unstable + //~^ ERROR `if let` guards are experimental + //~| ERROR `let` expressions in this position are unstable //~| ERROR `let` expressions in this position are unstable //~| ERROR `let` expressions in this position are unstable //~| ERROR `let` expressions in this position are unstable //~| ERROR `let` expressions in this position are unstable () if let Range { start: _, end: _ } = (true..true) && false => {} - //~^ ERROR `let` expressions in this position are unstable + //~^ ERROR `if let` guards are experimental + //~| ERROR `let` expressions in this position are unstable + _ => {} } } diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr index 0cda6ba9a9927..8d93fb87f7ad1 100644 --- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr +++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `let` - --> $DIR/feature-gate.rs:64:15 + --> $DIR/feature-gate.rs:69:15 | LL | macro_rules! use_expr { | --------------------- when calling this macro @@ -18,7 +18,47 @@ LL | () if let 0 = 1 => {} = help: you can write `if matches!(, )` instead of `if let = ` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:60:12 + --> $DIR/feature-gate.rs:16:12 + | +LL | () if true && let 0 = 1 => {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #51114 for more information + = help: add `#![feature(if_let_guard)]` to the crate attributes to enable + = help: you can write `if matches!(, )` instead of `if let = ` + +error[E0658]: `if let` guards are experimental + --> $DIR/feature-gate.rs:20:12 + | +LL | () if let 0 = 1 && true => {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #51114 for more information + = help: add `#![feature(if_let_guard)]` to the crate attributes to enable + = help: you can write `if matches!(, )` instead of `if let = ` + +error[E0658]: `if let` guards are experimental + --> $DIR/feature-gate.rs:34:12 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #51114 for more information + = help: add `#![feature(if_let_guard)]` to the crate attributes to enable + = help: you can write `if matches!(, )` instead of `if let = ` + +error[E0658]: `if let` guards are experimental + --> $DIR/feature-gate.rs:42:12 + | +LL | () if let Range { start: _, end: _ } = (true..true) && false => {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #51114 for more information + = help: add `#![feature(if_let_guard)]` to the crate attributes to enable + = help: you can write `if matches!(, )` instead of `if let = ` + +error[E0658]: `if let` guards are experimental + --> $DIR/feature-gate.rs:65:12 | LL | () if let 0 = 1 => {} | ^^^^^^^^^^^^ @@ -55,7 +95,7 @@ LL | () if true && let 0 = 1 => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:19:15 + --> $DIR/feature-gate.rs:20:15 | LL | () if let 0 = 1 && true => {} | ^^^^^^^^^ @@ -64,7 +104,7 @@ LL | () if let 0 = 1 && true => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:22:16 + --> $DIR/feature-gate.rs:24:16 | LL | () if (let 0 = 1) && true => {} | ^^^^^^^^^ @@ -73,7 +113,7 @@ LL | () if (let 0 = 1) && true => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:25:24 + --> $DIR/feature-gate.rs:27:24 | LL | () if true && (let 0 = 1) => {} | ^^^^^^^^^ @@ -82,7 +122,7 @@ LL | () if true && (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:28:16 + --> $DIR/feature-gate.rs:30:16 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ @@ -91,7 +131,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:28:31 + --> $DIR/feature-gate.rs:30:31 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ @@ -100,7 +140,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:32:15 + --> $DIR/feature-gate.rs:34:15 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -109,7 +149,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:32:28 + --> $DIR/feature-gate.rs:34:28 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -118,7 +158,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:32:42 + --> $DIR/feature-gate.rs:34:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -127,7 +167,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:32:55 + --> $DIR/feature-gate.rs:34:55 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -136,7 +176,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:32:68 + --> $DIR/feature-gate.rs:34:68 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -145,7 +185,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:39:15 + --> $DIR/feature-gate.rs:42:15 | LL | () if let Range { start: _, end: _ } = (true..true) && false => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +194,7 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:54:16 + --> $DIR/feature-gate.rs:59:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^^^^^^^ @@ -163,7 +203,7 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:56:16 + --> $DIR/feature-gate.rs:61:16 | LL | use_expr!((let 0 = 1)); | ^^^^^^^^^ @@ -171,6 +211,6 @@ LL | use_expr!((let 0 = 1)); = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable -error: aborting due to 19 previous errors +error: aborting due to 23 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/rfc-2497-if-let-chains/issue-93150.rs b/src/test/ui/rfc-2497-if-let-chains/issue-93150.rs new file mode 100644 index 0000000000000..f90b9ab0d40f0 --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/issue-93150.rs @@ -0,0 +1,8 @@ +fn main() { + match true { + _ if let true = true && true => {} + //~^ ERROR `if let` guards are + //~| ERROR `let` expressions in this + _ => {} + } +} diff --git a/src/test/ui/rfc-2497-if-let-chains/issue-93150.stderr b/src/test/ui/rfc-2497-if-let-chains/issue-93150.stderr new file mode 100644 index 0000000000000..b25f299a2190f --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/issue-93150.stderr @@ -0,0 +1,22 @@ +error[E0658]: `if let` guards are experimental + --> $DIR/issue-93150.rs:3:11 + | +LL | _ if let true = true && true => {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #51114 for more information + = help: add `#![feature(if_let_guard)]` to the crate attributes to enable + = help: you can write `if matches!(, )` instead of `if let = ` + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/issue-93150.rs:3:14 + | +LL | _ if let true = true && true => {} + | ^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. From 4ff7e6e3e385f5205731737f756b5db8de7d9a93 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 22 Jan 2022 20:04:44 -0800 Subject: [PATCH 20/28] Normalize field access types during borrowck --- compiler/rustc_borrowck/src/type_check/mod.rs | 1 + .../generic-associated-types/issue-93141.rs | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/test/ui/generic-associated-types/issue-93141.rs diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index b6f5f4998a643..73103643e3e16 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -758,6 +758,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { }, ProjectionElem::Field(field, fty) => { let fty = self.sanitize_type(place, fty); + let fty = self.cx.normalize(fty, location); match self.field_ty(place, base, field, location) { Ok(ty) => { let ty = self.cx.normalize(ty, location); diff --git a/src/test/ui/generic-associated-types/issue-93141.rs b/src/test/ui/generic-associated-types/issue-93141.rs new file mode 100644 index 0000000000000..39ca77d13db99 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-93141.rs @@ -0,0 +1,25 @@ +// check-pass + +#![feature(generic_associated_types)] + +pub trait Fooey: Sized { + type Context<'c> where Self: 'c; +} + +pub struct Handle(Option Fn(&mut E::Context<'c>)>>); + +fn tuple() -> (Option,) { (Option::None,) } + +pub struct FooImpl {} +impl Fooey for FooImpl { + type Context<'c> = &'c (); +} + +impl FooImpl { + pub fn fail1() -> Handle { + let (tx,) = tuple(); + Handle(tx) + } +} + +fn main() {} From 19809ed76d97104fced81b22f51d34475228b305 Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Thu, 20 Jan 2022 17:44:50 -0500 Subject: [PATCH 21/28] Add preliminary support for inline assembly for msp430. --- compiler/rustc_codegen_gcc/src/asm.rs | 3 + compiler/rustc_codegen_llvm/src/asm.rs | 6 + compiler/rustc_target/src/asm/mod.rs | 25 +++ compiler/rustc_target/src/asm/msp430.rs | 81 +++++++++ .../asm-experimental-arch.md | 17 +- src/test/assembly/asm/msp430-types.rs | 158 ++++++++++++++++++ 6 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 compiler/rustc_target/src/asm/msp430.rs create mode 100644 src/test/assembly/asm/msp430-types.rs diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index b4213da6e0504..8a74c4c07e0cf 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -560,6 +560,7 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister { InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => unimplemented!(), InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => unimplemented!(), InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => unimplemented!(), + InlineAsmRegClass::Msp430(_) => unimplemented!(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => unimplemented!(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => unimplemented!(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => unimplemented!(), @@ -622,6 +623,7 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(), + InlineAsmRegClass::Msp430(_) => unimplemented!(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(), @@ -729,6 +731,7 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option InlineAsmRegClass::Bpf(_) => unimplemented!(), InlineAsmRegClass::Hexagon(_) => unimplemented!(), InlineAsmRegClass::Mips(_) => unimplemented!(), + InlineAsmRegClass::Msp430(_) => unimplemented!(), InlineAsmRegClass::Nvptx(_) => unimplemented!(), InlineAsmRegClass::PowerPC(_) => unimplemented!(), InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 8b696dc6fba69..e22bec249513d 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -232,6 +232,9 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { InlineAsmArch::SpirV => {} InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {} InlineAsmArch::Bpf => {} + InlineAsmArch::Msp430 => { + constraints.push("~{sr}".to_string()); + } } } if !options.contains(InlineAsmOptions::NOMEM) { @@ -580,6 +583,7 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => "e", InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r", InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => "r", InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") } @@ -666,6 +670,7 @@ fn modifier_to_llvm( }, InlineAsmRegClass::Avr(_) => None, InlineAsmRegClass::S390x(_) => None, + InlineAsmRegClass::Msp430(_) => None, InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") } @@ -734,6 +739,7 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => cx.type_i16(), InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(), + InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(), InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 6b82bb337e6c1..8046267a59da6 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -152,6 +152,7 @@ mod avr; mod bpf; mod hexagon; mod mips; +mod msp430; mod nvptx; mod powerpc; mod riscv; @@ -166,6 +167,7 @@ pub use avr::{AvrInlineAsmReg, AvrInlineAsmRegClass}; pub use bpf::{BpfInlineAsmReg, BpfInlineAsmRegClass}; pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass}; pub use mips::{MipsInlineAsmReg, MipsInlineAsmRegClass}; +pub use msp430::{Msp430InlineAsmReg, Msp430InlineAsmRegClass}; pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass}; pub use powerpc::{PowerPCInlineAsmReg, PowerPCInlineAsmRegClass}; pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; @@ -194,6 +196,7 @@ pub enum InlineAsmArch { Wasm64, Bpf, Avr, + Msp430, } impl FromStr for InlineAsmArch { @@ -219,6 +222,7 @@ impl FromStr for InlineAsmArch { "wasm64" => Ok(Self::Wasm64), "bpf" => Ok(Self::Bpf), "avr" => Ok(Self::Avr), + "msp430" => Ok(Self::Msp430), _ => Err(()), } } @@ -250,6 +254,7 @@ pub enum InlineAsmReg { Wasm(WasmInlineAsmReg), Bpf(BpfInlineAsmReg), Avr(AvrInlineAsmReg), + Msp430(Msp430InlineAsmReg), // Placeholder for invalid register constraints for the current target Err, } @@ -267,6 +272,7 @@ impl InlineAsmReg { Self::S390x(r) => r.name(), Self::Bpf(r) => r.name(), Self::Avr(r) => r.name(), + Self::Msp430(r) => r.name(), Self::Err => "", } } @@ -283,6 +289,7 @@ impl InlineAsmReg { Self::S390x(r) => InlineAsmRegClass::S390x(r.reg_class()), Self::Bpf(r) => InlineAsmRegClass::Bpf(r.reg_class()), Self::Avr(r) => InlineAsmRegClass::Avr(r.reg_class()), + Self::Msp430(r) => InlineAsmRegClass::Msp430(r.reg_class()), Self::Err => InlineAsmRegClass::Err, } } @@ -336,6 +343,9 @@ impl InlineAsmReg { InlineAsmArch::Avr => { Self::Avr(AvrInlineAsmReg::parse(arch, target_features, target, name)?) } + InlineAsmArch::Msp430 => { + Self::Msp430(Msp430InlineAsmReg::parse(arch, target_features, target, name)?) + } }) } @@ -358,6 +368,7 @@ impl InlineAsmReg { Self::S390x(r) => r.emit(out, arch, modifier), Self::Bpf(r) => r.emit(out, arch, modifier), Self::Avr(r) => r.emit(out, arch, modifier), + Self::Msp430(r) => r.emit(out, arch, modifier), Self::Err => unreachable!("Use of InlineAsmReg::Err"), } } @@ -374,6 +385,7 @@ impl InlineAsmReg { Self::S390x(_) => cb(self), Self::Bpf(r) => r.overlapping_regs(|r| cb(Self::Bpf(r))), Self::Avr(r) => r.overlapping_regs(|r| cb(Self::Avr(r))), + Self::Msp430(_) => cb(self), Self::Err => unreachable!("Use of InlineAsmReg::Err"), } } @@ -405,6 +417,7 @@ pub enum InlineAsmRegClass { Wasm(WasmInlineAsmRegClass), Bpf(BpfInlineAsmRegClass), Avr(AvrInlineAsmRegClass), + Msp430(Msp430InlineAsmRegClass), // Placeholder for invalid register constraints for the current target Err, } @@ -425,6 +438,7 @@ impl InlineAsmRegClass { Self::Wasm(r) => r.name(), Self::Bpf(r) => r.name(), Self::Avr(r) => r.name(), + Self::Msp430(r) => r.name(), Self::Err => rustc_span::symbol::sym::reg, } } @@ -447,6 +461,7 @@ impl InlineAsmRegClass { Self::Wasm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Wasm), Self::Bpf(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Bpf), Self::Avr(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Avr), + Self::Msp430(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Msp430), Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -476,6 +491,7 @@ impl InlineAsmRegClass { Self::Wasm(r) => r.suggest_modifier(arch, ty), Self::Bpf(r) => r.suggest_modifier(arch, ty), Self::Avr(r) => r.suggest_modifier(arch, ty), + Self::Msp430(r) => r.suggest_modifier(arch, ty), Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -501,6 +517,7 @@ impl InlineAsmRegClass { Self::Wasm(r) => r.default_modifier(arch), Self::Bpf(r) => r.default_modifier(arch), Self::Avr(r) => r.default_modifier(arch), + Self::Msp430(r) => r.default_modifier(arch), Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -525,6 +542,7 @@ impl InlineAsmRegClass { Self::Wasm(r) => r.supported_types(arch), Self::Bpf(r) => r.supported_types(arch), Self::Avr(r) => r.supported_types(arch), + Self::Msp430(r) => r.supported_types(arch), Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -554,6 +572,7 @@ impl InlineAsmRegClass { } InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmRegClass::parse(arch, name)?), InlineAsmArch::Avr => Self::Avr(AvrInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmRegClass::parse(arch, name)?), }) } @@ -574,6 +593,7 @@ impl InlineAsmRegClass { Self::Wasm(r) => r.valid_modifiers(arch), Self::Bpf(r) => r.valid_modifiers(arch), Self::Avr(r) => r.valid_modifiers(arch), + Self::Msp430(r) => r.valid_modifiers(arch), Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -764,6 +784,11 @@ pub fn allocatable_registers( avr::fill_reg_map(arch, target_features, target, &mut map); map } + InlineAsmArch::Msp430 => { + let mut map = msp430::regclass_map(); + msp430::fill_reg_map(arch, target_features, target, &mut map); + map + } } } diff --git a/compiler/rustc_target/src/asm/msp430.rs b/compiler/rustc_target/src/asm/msp430.rs new file mode 100644 index 0000000000000..a27d6390a72e8 --- /dev/null +++ b/compiler/rustc_target/src/asm/msp430.rs @@ -0,0 +1,81 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; + +def_reg_class! { + Msp430 Msp430InlineAsmRegClass { + reg, + } +} + +impl Msp430InlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option)] { + match (self, arch) { + (Self::reg, _) => types! { _: I8, I16; }, + } + } +} + +// The reserved registers are taken from: +// https://github.com/llvm/llvm-project/blob/36cb29cbbe1b22dcd298ad65e1fabe899b7d7249/llvm/lib/Target/MSP430/MSP430RegisterInfo.cpp#L73. +def_regs! { + Msp430 Msp430InlineAsmReg Msp430InlineAsmRegClass { + r5: reg = ["r5"], + r6: reg = ["r6"], + r7: reg = ["r7"], + r8: reg = ["r8"], + r9: reg = ["r9"], + r10: reg = ["r10"], + r11: reg = ["r11"], + r12: reg = ["r12"], + r13: reg = ["r13"], + r14: reg = ["r14"], + r15: reg = ["r15"], + + #error = ["r0", "pc"] => + "the program counter cannot be used as an operand for inline asm", + #error = ["r1", "sp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["r2", "sr"] => + "the status register cannot be used as an operand for inline asm", + #error = ["r3", "cg"] => + "the constant generator cannot be used as an operand for inline asm", + #error = ["r4", "fp"] => + "the frame pointer cannot be used as an operand for inline asm", + } +} + +impl Msp430InlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option, + ) -> fmt::Result { + out.write_str(self.name()) + } +} diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md index ec97eaa8b2b5b..37fd67447c134 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md @@ -15,6 +15,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect - BPF - SPIR-V - AVR +- MSP430 ## Register classes @@ -39,6 +40,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | AVR | `reg_pair` | `r3r2` .. `r25r24`, `X`, `Z` | `r` | | AVR | `reg_iw` | `r25r24`, `X`, `Z` | `w` | | AVR | `reg_ptr` | `X`, `Z` | `e` | +| MSP430 | `reg` | `r[0-15]` | `r` | > **Notes**: > - NVPTX doesn't have a fixed register set, so named registers are not supported. @@ -67,6 +69,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | BPF | `wreg` | `alu32` | `i8` `i16` `i32` | | AVR | `reg`, `reg_upper` | None | `i8` | | AVR | `reg_pair`, `reg_iw`, `reg_ptr` | None | `i16` | +| MSP430 | `reg` | None | `i8`, `i16` | ## Register aliases @@ -80,13 +83,22 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | AVR | `XL` | `r26` | | AVR | `ZH` | `r31` | | AVR | `ZL` | `r30` | +| MSP430 | `r0` | `pc` | +| MSP430 | `r1` | `sp` | +| MSP430 | `r2` | `sr` | +| MSP430 | `r3` | `cg` | +| MSP430 | `r4` | `fp` | + +> **Notes**: +> - TI does not mandate a frame pointer for MSP430, but toolchains are allowed + to use one; LLVM uses `r4`. ## Unsupported registers | Architecture | Unsupported register | Reason | | ------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. | -| All | `fr` (Hexagon), `$fp` (MIPS), `Y` (AVR) | The frame pointer cannot be used as an input or output. | +| All | `fr` (Hexagon), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430) | The frame pointer cannot be used as an input or output. | | All | `r19` (Hexagon) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. | | MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. | | MIPS | `$1` or `$at` | Reserved for assembler. | @@ -95,6 +107,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | MIPS | `$ra` | Return address cannot be used as inputs or outputs. | | Hexagon | `lr` | This is the link register which cannot be used as an input or output. | | AVR | `r0`, `r1`, `r1r0` | Due to an issue in LLVM, the `r0` and `r1` registers cannot be used as inputs or outputs. If modified, they must be restored to their original values before the end of the block. | +|MSP430 | `r0`, `r2`, `r3` | These are the program counter, status register, and constant generator respectively. Neither the status register nor constant generator can be written to. | ## Template modifiers @@ -115,3 +128,5 @@ This feature tracks `asm!` and `global_asm!` support for the following architect These flags registers must be restored upon exiting the asm block if the `preserves_flags` option is set: - AVR - The status register `SREG`. +- MSP430 + - The status register `r2`. diff --git a/src/test/assembly/asm/msp430-types.rs b/src/test/assembly/asm/msp430-types.rs new file mode 100644 index 0000000000000..6cfb86e276e23 --- /dev/null +++ b/src/test/assembly/asm/msp430-types.rs @@ -0,0 +1,158 @@ +// min-llvm-version: 13.0 +// assembly-output: emit-asm +// compile-flags: --target msp430-none-elf +// needs-llvm-components: msp430 + +#![feature(no_core, lang_items, rustc_attrs, asm_sym, asm_experimental_arch, asm_const)] +#![crate_type = "rlib"] +#![no_core] +#![allow(non_camel_case_types)] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! concat { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +type ptr = *const i16; + +impl Copy for i8 {} +impl Copy for i16 {} +impl Copy for i32 {} +impl Copy for i64 {} +impl Copy for ptr {} + +macro_rules! check { + ($func:ident $ty:ident $class:ident) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + let y; + asm!("mov {}, {}", lateout($class) y, in($class) x); + y + } + }; +} + +macro_rules! checkb { + ($func:ident $ty:ident $class:ident) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + let y; + asm!("mov.b {}, {}", lateout($class) y, in($class) x); + y + } + }; +} + +macro_rules! check_reg { + ($func:ident $ty:ident $reg:tt) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + let y; + asm!(concat!("mov ", $reg, ", ", $reg), lateout($reg) y, in($reg) x); + y + } + }; +} + +macro_rules! check_regb { + ($func:ident $ty:ident $reg:tt) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + let y; + asm!(concat!("mov.b ", $reg, ", ", $reg), lateout($reg) y, in($reg) x); + y + } + }; +} + +extern "C" { + fn extern_func(); + static extern_static: i8; +} + +// CHECK-LABEL: sym_fn +// CHECK: ;APP +// CHECK: call extern_func +// CHECK: ;NO_APP +#[no_mangle] +pub unsafe fn sym_fn() { + asm!("call {}", sym extern_func); +} + +// CHECK-LABEL: sym_static +// CHECK: ;APP +// CHECK: mov.b extern_static, r{{[0-9]+}} +// CHECK: ;NO_APP +#[no_mangle] +pub unsafe fn sym_static() -> i8 { + let y; + asm!("mov.b {1}, {0}", lateout(reg) y, sym extern_static); + y +} + +// CHECK-LABEL: add_const: +// CHECK: ;APP +// CHECK: add.b #5, r{{[0-9]+}} +// CHECK: ;NO_APP +#[no_mangle] +pub unsafe fn add_const() -> i8 { + let y; + asm!("add.b #{number}, {}", out(reg) y, number = const 5); + y +} + +// CHECK-LABEL: mov_postincrement: +// CHECK: ;APP +// CHECK: mov @r5+, r{{[0-9]+}} +// CHECK: ;NO_APP +#[no_mangle] +pub unsafe fn mov_postincrement(mut x: *const i16) -> (i16, *const i16) { + let y; + asm!("mov @r5+, {0}", out(reg) y, inlateout("r5") x); + (y, x) +} + +// CHECK-LABEL: reg_i8: +// CHECK: ;APP +// CHECK: mov r{{[0-9]+}}, r{{[0-9]+}} +// CHECK: ;NO_APP +check!(reg_i8 i8 reg); + +// CHECK-LABEL: reg_i16: +// CHECK: ;APP +// CHECK: mov r{{[0-9]+}}, r{{[0-9]+}} +// CHECK: ;NO_APP +check!(reg_i16 i16 reg); + +// CHECK-LABEL: reg_i8b: +// CHECK: ;APP +// CHECK: mov.b r{{[0-9]+}}, r{{[0-9]+}} +// CHECK: ;NO_APP +checkb!(reg_i8b i8 reg); + +// CHECK-LABEL: r5_i8: +// CHECK: ;APP +// CHECK: mov r5, r5 +// CHECK: ;NO_APP +check_reg!(r5_i8 i8 "r5"); + +// CHECK-LABEL: r5_i16: +// CHECK: ;APP +// CHECK: mov r5, r5 +// CHECK: ;NO_APP +check_reg!(r5_i16 i16 "r5"); + +// CHECK-LABEL: r5_i8b: +// CHECK: ;APP +// CHECK: mov.b r5, r5 +// CHECK: ;NO_APP +check_regb!(r5_i8b i8 "r5"); From cf382de0cc268e6af8d37d31323bf36d0099ade3 Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 23 Jan 2022 00:11:13 -0600 Subject: [PATCH 22/28] Remove DiagnosticBuilder.quiet --- compiler/rustc_errors/src/lib.rs | 17 +---------------- compiler/rustc_session/src/session.rs | 4 ---- src/librustdoc/html/render/span_map.rs | 12 +++++++----- 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index a681298301acb..06b9729a7fd9f 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -445,9 +445,6 @@ struct HandlerInner { deduplicated_warn_count: usize, future_breakage_diagnostics: Vec, - - /// If set to `true`, no warning or error will be emitted. - quiet: bool, } /// A key denoting where from a diagnostic was stashed. @@ -563,19 +560,10 @@ impl Handler { emitted_diagnostics: Default::default(), stashed_diagnostics: Default::default(), future_breakage_diagnostics: Vec::new(), - quiet: false, }), } } - pub fn with_disabled_diagnostic T>(&self, f: F) -> T { - let prev = self.inner.borrow_mut().quiet; - self.inner.borrow_mut().quiet = true; - let ret = f(); - self.inner.borrow_mut().quiet = prev; - ret - } - // This is here to not allow mutation of flags; // as of this writing it's only used in tests in librustc_middle. pub fn can_emit_warnings(&self) -> bool { @@ -946,7 +934,7 @@ impl HandlerInner { } fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) { - if diagnostic.cancelled() || self.quiet { + if diagnostic.cancelled() { return; } @@ -1170,9 +1158,6 @@ impl HandlerInner { } fn delay_as_bug(&mut self, diagnostic: Diagnostic) { - if self.quiet { - return; - } if self.flags.report_delayed_bugs { self.emit_diagnostic(&diagnostic); } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 730e79a56470e..9bcdd7f3da6b3 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -476,10 +476,6 @@ impl Session { &self.parse_sess.span_diagnostic } - pub fn with_disabled_diagnostic T>(&self, f: F) -> T { - self.parse_sess.span_diagnostic.with_disabled_diagnostic(f) - } - /// Analogous to calling methods on the given `DiagnosticBuilder`, but /// deduplicates on lint ID, span (if any), and message for this `Session` fn diag_once<'a, 'b>( diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 586f34cd2c814..731e18b1eec2c 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -134,11 +134,13 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { if let Some(hir_id) = segment.hir_id { let hir = self.tcx.hir(); let body_id = hir.enclosing_body_owner(hir_id); - let typeck_results = self.tcx.sess.with_disabled_diagnostic(|| { - self.tcx.typeck_body( - hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"), - ) - }); + // FIXME: this is showing error messages for parts of the code that are not + // compiled (because of cfg)! + // + // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352 + let typeck_results = self.tcx.typeck_body( + hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"), + ); if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) { self.matches.insert( segment.ident.span, From 452aa81770d0f2aa05e874122cc15cc73e51235c Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 28 Sep 2021 00:28:49 +0300 Subject: [PATCH 23/28] rustc_lint: Remove some redundant fields from `EarlyContext` Use consistent function parameter order for early context construction and early linting Rename some functions to make it clear that they do not necessarily work on the whole crate --- compiler/rustc_interface/src/passes.rs | 22 +++---- compiler/rustc_lint/src/builtin.rs | 14 ++--- compiler/rustc_lint/src/context.rs | 25 ++------ compiler/rustc_lint/src/early.rs | 64 ++++++++------------ compiler/rustc_lint/src/levels.rs | 33 +++++----- compiler/rustc_lint/src/lib.rs | 2 +- compiler/rustc_lint/src/non_ascii_idents.rs | 2 +- compiler/rustc_lint/src/nonstandard_style.rs | 2 +- 8 files changed, 70 insertions(+), 94 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 33bf670f570f8..93d1976eb1e39 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -236,19 +236,19 @@ pub fn register_plugins<'a>( fn pre_expansion_lint( sess: &Session, lint_store: &LintStore, - krate: &ast::Crate, crate_attrs: &[ast::Attribute], - crate_name: &str, + check_node: &ast::Crate, + node_name: &str, ) { - sess.prof.generic_activity_with_arg("pre_AST_expansion_lint_checks", crate_name).run(|| { - rustc_lint::check_ast_crate( + sess.prof.generic_activity_with_arg("pre_AST_expansion_lint_checks", node_name).run(|| { + rustc_lint::check_ast_node( sess, + true, lint_store, - krate, crate_attrs, - true, None, rustc_lint::BuiltinCombinedPreExpansionLintPass::new(), + check_node, ); }); } @@ -265,7 +265,7 @@ pub fn configure_and_expand( resolver: &mut Resolver<'_>, ) -> Result { tracing::trace!("configure_and_expand"); - pre_expansion_lint(sess, lint_store, &krate, &krate.attrs, crate_name); + pre_expansion_lint(sess, lint_store, &krate.attrs, &krate, crate_name); rustc_builtin_macros::register_builtin_macros(resolver); krate = sess.time("crate_injection", || { @@ -324,7 +324,7 @@ pub fn configure_and_expand( let crate_attrs = krate.attrs.clone(); let extern_mod_loaded = |ident: Ident, attrs, items, span| { let krate = ast::Crate { attrs, items, span, id: DUMMY_NODE_ID, is_placeholder: false }; - pre_expansion_lint(sess, lint_store, &krate, &crate_attrs, ident.name.as_str()); + pre_expansion_lint(sess, lint_store, &crate_attrs, &krate, ident.name.as_str()); (krate.attrs, krate.items) }; let mut ecx = ExtCtxt::new(sess, cfg, resolver, Some(&extern_mod_loaded)); @@ -499,14 +499,14 @@ pub fn lower_to_hir<'res, 'tcx>( ); sess.time("early_lint_checks", || { - rustc_lint::check_ast_crate( + rustc_lint::check_ast_node( sess, + false, lint_store, - &krate, &krate.attrs, - false, Some(std::mem::take(resolver.lint_buffer())), rustc_lint::BuiltinCombinedEarlyLintPass::new(), + &krate, ) }); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 65385d4c7a12f..24d295ee9fbf6 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -912,7 +912,7 @@ declare_lint_pass!( impl EarlyLintPass for AnonymousParameters { fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) { - if cx.sess.edition() != Edition::Edition2015 { + if cx.sess().edition() != Edition::Edition2015 { // This is a hard error in future editions; avoid linting and erroring return; } @@ -921,7 +921,7 @@ impl EarlyLintPass for AnonymousParameters { if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind { if ident.name == kw::Empty { cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |lint| { - let ty_snip = cx.sess.source_map().span_to_snippet(arg.ty.span); + let ty_snip = cx.sess().source_map().span_to_snippet(arg.ty.span); let (ty_snip, appl) = if let Ok(ref snip) = ty_snip { (snip.as_str(), Applicability::MachineApplicable) @@ -1775,7 +1775,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { }; if join.edition() >= Edition::Edition2021 { let mut err = - rustc_errors::struct_span_err!(cx.sess, pat.span, E0783, "{}", msg,); + rustc_errors::struct_span_err!(cx.sess(), pat.span, E0783, "{}", msg,); err.span_suggestion( pat.span, suggestion, @@ -1799,7 +1799,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { let replace = "..=".to_owned(); if join.edition() >= Edition::Edition2021 { let mut err = - rustc_errors::struct_span_err!(cx.sess, pat.span, E0783, "{}", msg,); + rustc_errors::struct_span_err!(cx.sess(), pat.span, E0783, "{}", msg,); err.span_suggestion_short( join, suggestion, @@ -1983,7 +1983,7 @@ impl KeywordIdents { UnderMacro(under_macro): UnderMacro, ident: Ident, ) { - let next_edition = match cx.sess.edition() { + let next_edition = match cx.sess().edition() { Edition::Edition2015 => { match ident.name { kw::Async | kw::Await | kw::Try => Edition::Edition2018, @@ -2011,7 +2011,7 @@ impl KeywordIdents { }; // Don't lint `r#foo`. - if cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&ident.span) { + if cx.sess().parse_sess.raw_identifier_spans.borrow().contains(&ident.span) { return; } @@ -2379,7 +2379,7 @@ declare_lint_pass!( impl EarlyLintPass for IncompleteFeatures { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { - let features = cx.sess.features_untracked(); + let features = cx.sess().features_untracked(); features .declared_lang_features .iter() diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 69c376c6169ba..d762ee2acd2c8 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -553,20 +553,9 @@ pub struct LateContext<'tcx> { pub only_module: bool, } -/// Context for lint checking of the AST, after expansion, before lowering to -/// HIR. +/// Context for lint checking of the AST, after expansion, before lowering to HIR. pub struct EarlyContext<'a> { - /// Type context we're checking in. - pub sess: &'a Session, - - /// The crate being checked. - pub krate: &'a ast::Crate, - pub builder: LintLevelsBuilder<'a>, - - /// The store of registered lints and the lint levels. - pub lint_store: &'a LintStore, - pub buffered: LintBuffer, } @@ -801,18 +790,14 @@ pub trait LintContext: Sized { } impl<'a> EarlyContext<'a> { - pub fn new( + pub(crate) fn new( sess: &'a Session, + warn_about_weird_lints: bool, lint_store: &'a LintStore, - krate: &'a ast::Crate, crate_attrs: &'a [ast::Attribute], buffered: LintBuffer, - warn_about_weird_lints: bool, ) -> EarlyContext<'a> { EarlyContext { - sess, - krate, - lint_store, builder: LintLevelsBuilder::new(sess, warn_about_weird_lints, lint_store, crate_attrs), buffered, } @@ -851,11 +836,11 @@ impl LintContext for EarlyContext<'_> { /// Gets the overall compiler `Session` object. fn sess(&self) -> &Session { - &self.sess + &self.builder.sess() } fn lints(&self) -> &LintStore { - &*self.lint_store + self.builder.lint_store() } fn lookup>( diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 0bba66d383869..9753bbfe75291 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -57,7 +57,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { F: FnOnce(&mut Self), { let is_crate_node = id == ast::CRATE_NODE_ID; - let push = self.context.builder.push(attrs, &self.context.lint_store, is_crate_node); + let push = self.context.builder.push(attrs, is_crate_node); self.check_id(id); self.enter_attrs(attrs); f(self); @@ -325,48 +325,36 @@ macro_rules! early_lint_pass_impl { crate::early_lint_methods!(early_lint_pass_impl, []); -fn early_lint_crate( +fn early_lint_node( sess: &Session, + warn_about_weird_lints: bool, lint_store: &LintStore, - krate: &ast::Crate, crate_attrs: &[ast::Attribute], - pass: T, buffered: LintBuffer, - warn_about_weird_lints: bool, + pass: impl EarlyLintPass, + check_node: &ast::Crate, ) -> LintBuffer { let mut cx = EarlyContextAndPass { - context: EarlyContext::new( - sess, - lint_store, - krate, - crate_attrs, - buffered, - warn_about_weird_lints, - ), + context: EarlyContext::new(sess, warn_about_weird_lints, lint_store, crate_attrs, buffered), pass, }; - // Visit the whole crate. - cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| { - // since the root module isn't visited as an item (because it isn't an - // item), warn for it here. - run_early_pass!(cx, check_crate, krate); - - ast_visit::walk_crate(cx, krate); - - run_early_pass!(cx, check_crate_post, krate); + cx.with_lint_attrs(ast::CRATE_NODE_ID, &check_node.attrs, |cx| { + run_early_pass!(cx, check_crate, check_node); + ast_visit::walk_crate(cx, check_node); + run_early_pass!(cx, check_crate_post, check_node); }); cx.context.buffered } -pub fn check_ast_crate( +pub fn check_ast_node( sess: &Session, + pre_expansion: bool, lint_store: &LintStore, - krate: &ast::Crate, crate_attrs: &[ast::Attribute], - pre_expansion: bool, lint_buffer: Option, - builtin_lints: T, + builtin_lints: impl EarlyLintPass, + check_node: &ast::Crate, ) { let passes = if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes }; @@ -374,39 +362,39 @@ pub fn check_ast_crate( let mut buffered = lint_buffer.unwrap_or_default(); if !sess.opts.debugging_opts.no_interleave_lints { - buffered = early_lint_crate( + buffered = early_lint_node( sess, + pre_expansion, lint_store, - krate, crate_attrs, - builtin_lints, buffered, - pre_expansion, + builtin_lints, + check_node, ); if !passes.is_empty() { - buffered = early_lint_crate( + buffered = early_lint_node( sess, + false, lint_store, - krate, crate_attrs, - EarlyLintPassObjects { lints: &mut passes[..] }, buffered, - false, + EarlyLintPassObjects { lints: &mut passes[..] }, + check_node, ); } } else { for (i, pass) in passes.iter_mut().enumerate() { buffered = sess.prof.extra_verbose_generic_activity("run_lint", pass.name()).run(|| { - early_lint_crate( + early_lint_node( sess, + pre_expansion && i == 0, lint_store, - krate, crate_attrs, - EarlyLintPassObjects { lints: slice::from_mut(pass) }, buffered, - pre_expansion && i == 0, + EarlyLintPassObjects { lints: slice::from_mut(pass) }, + check_node, ) }); } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 6e95708b17fe4..8e6ae2c60fd8f 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -29,12 +29,12 @@ fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap { let store = unerased_lint_store(tcx); let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID); let levels = LintLevelsBuilder::new(tcx.sess, false, &store, crate_attrs); - let mut builder = LintLevelMapBuilder { levels, tcx, store }; + let mut builder = LintLevelMapBuilder { levels, tcx }; let krate = tcx.hir().krate(); builder.levels.id_to_set.reserve(krate.owners.len() + 1); - let push = builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), &store, true); + let push = builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), true); builder.levels.register_id(hir::CRATE_HIR_ID); tcx.hir().walk_toplevel_module(&mut builder); builder.levels.pop(push); @@ -78,6 +78,14 @@ impl<'s> LintLevelsBuilder<'s> { builder } + pub(crate) fn sess(&self) -> &Session { + self.sess + } + + pub(crate) fn lint_store(&self) -> &LintStore { + self.store + } + fn process_command_line(&mut self, sess: &Session, store: &LintStore) { let mut specs = FxHashMap::default(); self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid); @@ -217,12 +225,7 @@ impl<'s> LintLevelsBuilder<'s> { /// `#[allow]` /// /// Don't forget to call `pop`! - pub(crate) fn push( - &mut self, - attrs: &[ast::Attribute], - store: &LintStore, - is_crate_node: bool, - ) -> BuilderPush { + pub(crate) fn push(&mut self, attrs: &[ast::Attribute], is_crate_node: bool) -> BuilderPush { let mut specs = FxHashMap::default(); let sess = self.sess; let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input"); @@ -310,7 +313,8 @@ impl<'s> LintLevelsBuilder<'s> { }; let tool_name = tool_ident.map(|ident| ident.name); let name = pprust::path_to_string(&meta_item.path); - let lint_result = store.check_lint_name(sess, &name, tool_name, self.crate_attrs); + let lint_result = + self.store.check_lint_name(sess, &name, tool_name, self.crate_attrs); match &lint_result { CheckLintNameResult::Ok(ids) => { let src = LintLevelSource::Node( @@ -459,7 +463,7 @@ impl<'s> LintLevelsBuilder<'s> { // Ignore any errors or warnings that happen because the new name is inaccurate // NOTE: `new_name` already includes the tool name, so we don't have to add it again. if let CheckLintNameResult::Ok(ids) = - store.check_lint_name(sess, &new_name, None, self.crate_attrs) + self.store.check_lint_name(sess, &new_name, None, self.crate_attrs) { let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason); for &id in ids { @@ -576,20 +580,19 @@ pub fn is_known_lint_tool(m_item: Symbol, sess: &Session, attrs: &[ast::Attribut .any(|name| name == m_item) } -struct LintLevelMapBuilder<'a, 'tcx> { +struct LintLevelMapBuilder<'tcx> { levels: LintLevelsBuilder<'tcx>, tcx: TyCtxt<'tcx>, - store: &'a LintStore, } -impl LintLevelMapBuilder<'_, '_> { +impl LintLevelMapBuilder<'_> { fn with_lint_attrs(&mut self, id: hir::HirId, f: F) where F: FnOnce(&mut Self), { let is_crate_hir = id == hir::CRATE_HIR_ID; let attrs = self.tcx.hir().attrs(id); - let push = self.levels.push(attrs, self.store, is_crate_hir); + let push = self.levels.push(attrs, is_crate_hir); if push.changed { self.levels.register_id(id); } @@ -598,7 +601,7 @@ impl LintLevelMapBuilder<'_, '_> { } } -impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'_, 'tcx> { +impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> { type NestedFilter = nested_filter::All; fn nested_visit_map(&mut self) -> Self::Map { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index dde47c1e4c43b..c36a233df9185 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -96,7 +96,7 @@ use unused::*; pub use builtin::SoftLints; pub use context::{CheckLintNameResult, FindLintError, LintStore}; pub use context::{EarlyContext, LateContext, LintContext}; -pub use early::check_ast_crate; +pub use early::check_ast_node; pub use late::check_crate; pub use passes::{EarlyLintPass, LateLintPass}; pub use rustc_session::lint::Level::{self, *}; diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index a570206f1eef7..2dd6dbd67a8d2 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -166,7 +166,7 @@ impl EarlyLintPass for NonAsciiIdents { } let mut has_non_ascii_idents = false; - let symbols = cx.sess.parse_sess.symbol_gallery.symbols.lock(); + let symbols = cx.sess().parse_sess.symbol_gallery.symbols.lock(); // Sort by `Span` so that error messages make sense with respect to the // order of identifier locations in the code. diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index be7756b0f28f6..f73388c675ee2 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -164,7 +164,7 @@ impl EarlyLintPass for NonCamelCaseTypes { let has_repr_c = it .attrs .iter() - .any(|attr| attr::find_repr_attrs(&cx.sess, attr).contains(&attr::ReprC)); + .any(|attr| attr::find_repr_attrs(cx.sess(), attr).contains(&attr::ReprC)); if has_repr_c { return; From 51b23386114ba7eba5dcbb5cbb7995575d7c0d8b Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 29 Sep 2021 01:17:54 +0300 Subject: [PATCH 24/28] rustc_lint: Reuse the set of registered tools from resolver --- compiler/rustc_expand/src/base.rs | 5 ++- compiler/rustc_interface/src/passes.rs | 19 +++++------ compiler/rustc_lint/src/context.rs | 32 ++++++++++++------- compiler/rustc_lint/src/early.rs | 19 +++++++---- compiler/rustc_lint/src/levels.rs | 32 ++++++------------- compiler/rustc_lint/src/lib.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 3 ++ compiler/rustc_resolve/src/lib.rs | 6 ++-- compiler/rustc_resolve/src/macros.rs | 6 +++- .../internal_lints/metadata_collector.rs | 2 +- 10 files changed, 70 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 07b5e20b2ddc5..ce08d5d7f2fa0 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -8,7 +8,7 @@ use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream}; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, Stability}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{self, Lrc}; use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported}; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; @@ -920,6 +920,9 @@ pub trait ResolverExpand { /// we generated proc macros harnesses, so that we can map /// HIR proc macros items back to their harness items. fn declare_proc_macro(&mut self, id: NodeId); + + /// Tools registered with `#![register_tool]` and used by tool attributes and lints. + fn registered_tools(&self) -> &FxHashSet; } #[derive(Clone, Default)] diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 93d1976eb1e39..6ac73d06868b7 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -11,7 +11,7 @@ use rustc_data_structures::parallel; use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{Applicability, ErrorReported, PResult}; -use rustc_expand::base::ExtCtxt; +use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE}; use rustc_hir::Crate; use rustc_lint::LintStore; @@ -20,7 +20,7 @@ use rustc_metadata::{encode_metadata, EncodedMetadata}; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; use rustc_middle::ty::query::{ExternProviders, Providers}; -use rustc_middle::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt}; +use rustc_middle::ty::{self, GlobalCtxt, RegisteredTools, ResolverOutputs, TyCtxt}; use rustc_mir_build as mir_build; use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_attr}; use rustc_passes::{self, hir_stats, layout_test}; @@ -236,7 +236,7 @@ pub fn register_plugins<'a>( fn pre_expansion_lint( sess: &Session, lint_store: &LintStore, - crate_attrs: &[ast::Attribute], + registered_tools: &RegisteredTools, check_node: &ast::Crate, node_name: &str, ) { @@ -245,7 +245,7 @@ fn pre_expansion_lint( sess, true, lint_store, - crate_attrs, + registered_tools, None, rustc_lint::BuiltinCombinedPreExpansionLintPass::new(), check_node, @@ -265,7 +265,7 @@ pub fn configure_and_expand( resolver: &mut Resolver<'_>, ) -> Result { tracing::trace!("configure_and_expand"); - pre_expansion_lint(sess, lint_store, &krate.attrs, &krate, crate_name); + pre_expansion_lint(sess, lint_store, resolver.registered_tools(), &krate, crate_name); rustc_builtin_macros::register_builtin_macros(resolver); krate = sess.time("crate_injection", || { @@ -321,10 +321,10 @@ pub fn configure_and_expand( ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string()) }; - let crate_attrs = krate.attrs.clone(); + let registered_tools = resolver.registered_tools().clone(); let extern_mod_loaded = |ident: Ident, attrs, items, span| { let krate = ast::Crate { attrs, items, span, id: DUMMY_NODE_ID, is_placeholder: false }; - pre_expansion_lint(sess, lint_store, &crate_attrs, &krate, ident.name.as_str()); + pre_expansion_lint(sess, lint_store, ®istered_tools, &krate, ident.name.as_str()); (krate.attrs, krate.items) }; let mut ecx = ExtCtxt::new(sess, cfg, resolver, Some(&extern_mod_loaded)); @@ -499,12 +499,13 @@ pub fn lower_to_hir<'res, 'tcx>( ); sess.time("early_lint_checks", || { + let lint_buffer = Some(std::mem::take(resolver.lint_buffer())); rustc_lint::check_ast_node( sess, false, lint_store, - &krate.attrs, - Some(std::mem::take(resolver.lint_buffer())), + resolver.registered_tools(), + lint_buffer, rustc_lint::BuiltinCombinedEarlyLintPass::new(), &krate, ) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d762ee2acd2c8..35c0abd73ba86 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -16,10 +16,9 @@ use self::TargetLint::*; -use crate::levels::{is_known_lint_tool, LintLevelsBuilder}; +use crate::levels::LintLevelsBuilder; use crate::passes::{EarlyLintPassObject, LateLintPassObject}; -use ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; -use rustc_ast as ast; +use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync; use rustc_errors::{struct_span_err, Applicability, SuggestionStyle}; @@ -32,13 +31,14 @@ use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::middle::stability; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt}; +use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools, Ty, TyCtxt}; use rustc_serialize::json::Json; use rustc_session::lint::{BuiltinLintDiagnostics, ExternDepSpec}; use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; use rustc_session::Session; use rustc_span::lev_distance::find_best_match_for_name; -use rustc_span::{symbol::Symbol, BytePos, MultiSpan, Span, DUMMY_SP}; +use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; use rustc_target::abi; use tracing::debug; @@ -313,7 +313,7 @@ impl LintStore { sess: &Session, lint_name: &str, level: Level, - crate_attrs: &[ast::Attribute], + registered_tools: &RegisteredTools, ) { let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name); if lint_name_only == crate::WARNINGS.name_lower() && level == Level::ForceWarn { @@ -326,7 +326,7 @@ impl LintStore { ) .emit(); } - let db = match self.check_lint_name(sess, lint_name_only, tool_name, crate_attrs) { + let db = match self.check_lint_name(lint_name_only, tool_name, registered_tools) { CheckLintNameResult::Ok(_) => None, CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)), CheckLintNameResult::NoLint(suggestion) => { @@ -397,13 +397,16 @@ impl LintStore { /// printing duplicate warnings. pub fn check_lint_name( &self, - sess: &Session, lint_name: &str, tool_name: Option, - crate_attrs: &[ast::Attribute], + registered_tools: &RegisteredTools, ) -> CheckLintNameResult<'_> { if let Some(tool_name) = tool_name { - if !is_known_lint_tool(tool_name, sess, crate_attrs) { + // FIXME: rustc and rustdoc are considered tools for lints, but not for attributes. + if tool_name != sym::rustc + && tool_name != sym::rustdoc + && !registered_tools.contains(&Ident::with_dummy_span(tool_name)) + { return CheckLintNameResult::NoTool; } } @@ -794,11 +797,16 @@ impl<'a> EarlyContext<'a> { sess: &'a Session, warn_about_weird_lints: bool, lint_store: &'a LintStore, - crate_attrs: &'a [ast::Attribute], + registered_tools: &'a RegisteredTools, buffered: LintBuffer, ) -> EarlyContext<'a> { EarlyContext { - builder: LintLevelsBuilder::new(sess, warn_about_weird_lints, lint_store, crate_attrs), + builder: LintLevelsBuilder::new( + sess, + warn_about_weird_lints, + lint_store, + registered_tools, + ), buffered, } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 9753bbfe75291..6cf67922be72b 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -19,6 +19,7 @@ use crate::passes::{EarlyLintPass, EarlyLintPassObject}; use rustc_ast as ast; use rustc_ast::visit as ast_visit; use rustc_ast::AstLike; +use rustc_middle::ty::RegisteredTools; use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_session::Session; use rustc_span::symbol::Ident; @@ -329,13 +330,19 @@ fn early_lint_node( sess: &Session, warn_about_weird_lints: bool, lint_store: &LintStore, - crate_attrs: &[ast::Attribute], + registered_tools: &RegisteredTools, buffered: LintBuffer, pass: impl EarlyLintPass, check_node: &ast::Crate, ) -> LintBuffer { let mut cx = EarlyContextAndPass { - context: EarlyContext::new(sess, warn_about_weird_lints, lint_store, crate_attrs, buffered), + context: EarlyContext::new( + sess, + warn_about_weird_lints, + lint_store, + registered_tools, + buffered, + ), pass, }; @@ -351,7 +358,7 @@ pub fn check_ast_node( sess: &Session, pre_expansion: bool, lint_store: &LintStore, - crate_attrs: &[ast::Attribute], + registered_tools: &RegisteredTools, lint_buffer: Option, builtin_lints: impl EarlyLintPass, check_node: &ast::Crate, @@ -366,7 +373,7 @@ pub fn check_ast_node( sess, pre_expansion, lint_store, - crate_attrs, + registered_tools, buffered, builtin_lints, check_node, @@ -377,7 +384,7 @@ pub fn check_ast_node( sess, false, lint_store, - crate_attrs, + registered_tools, buffered, EarlyLintPassObjects { lints: &mut passes[..] }, check_node, @@ -391,7 +398,7 @@ pub fn check_ast_node( sess, pre_expansion && i == 0, lint_store, - crate_attrs, + registered_tools, buffered, EarlyLintPassObjects { lints: slice::from_mut(pass) }, check_node, diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 8e6ae2c60fd8f..080a38bae669c 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -14,7 +14,7 @@ use rustc_middle::lint::{ COMMAND_LINE, }; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::lint::{ builtin::{self, FORBIDDEN_LINT_GROUPS}, Level, Lint, LintId, @@ -27,8 +27,8 @@ use tracing::debug; fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap { let store = unerased_lint_store(tcx); - let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID); - let levels = LintLevelsBuilder::new(tcx.sess, false, &store, crate_attrs); + let levels = + LintLevelsBuilder::new(tcx.sess, false, &store, &tcx.resolutions(()).registered_tools); let mut builder = LintLevelMapBuilder { levels, tcx }; let krate = tcx.hir().krate(); @@ -49,7 +49,7 @@ pub struct LintLevelsBuilder<'s> { cur: LintStackIndex, warn_about_weird_lints: bool, store: &'s LintStore, - crate_attrs: &'s [ast::Attribute], + registered_tools: &'s RegisteredTools, } pub struct BuilderPush { @@ -62,7 +62,7 @@ impl<'s> LintLevelsBuilder<'s> { sess: &'s Session, warn_about_weird_lints: bool, store: &'s LintStore, - crate_attrs: &'s [ast::Attribute], + registered_tools: &'s RegisteredTools, ) -> Self { let mut builder = LintLevelsBuilder { sess, @@ -71,7 +71,7 @@ impl<'s> LintLevelsBuilder<'s> { id_to_set: Default::default(), warn_about_weird_lints, store, - crate_attrs, + registered_tools, }; builder.process_command_line(sess, store); assert_eq!(builder.sets.list.len(), 1); @@ -91,7 +91,7 @@ impl<'s> LintLevelsBuilder<'s> { self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid); for &(ref lint_name, level) in &sess.opts.lint_opts { - store.check_lint_name_cmdline(sess, &lint_name, level, self.crate_attrs); + store.check_lint_name_cmdline(sess, &lint_name, level, self.registered_tools); let orig_level = level; let lint_flag_val = Symbol::intern(lint_name); @@ -314,7 +314,7 @@ impl<'s> LintLevelsBuilder<'s> { let tool_name = tool_ident.map(|ident| ident.name); let name = pprust::path_to_string(&meta_item.path); let lint_result = - self.store.check_lint_name(sess, &name, tool_name, self.crate_attrs); + self.store.check_lint_name(&name, tool_name, self.registered_tools); match &lint_result { CheckLintNameResult::Ok(ids) => { let src = LintLevelSource::Node( @@ -463,7 +463,7 @@ impl<'s> LintLevelsBuilder<'s> { // Ignore any errors or warnings that happen because the new name is inaccurate // NOTE: `new_name` already includes the tool name, so we don't have to add it again. if let CheckLintNameResult::Ok(ids) = - self.store.check_lint_name(sess, &new_name, None, self.crate_attrs) + self.store.check_lint_name(&new_name, None, self.registered_tools) { let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason); for &id in ids { @@ -566,20 +566,6 @@ impl<'s> LintLevelsBuilder<'s> { } } -pub fn is_known_lint_tool(m_item: Symbol, sess: &Session, attrs: &[ast::Attribute]) -> bool { - if [sym::clippy, sym::rustc, sym::rustdoc].contains(&m_item) { - return true; - } - // Look for registered tools - // NOTE: does no error handling; error handling is done by rustc_resolve. - sess.filter_by_name(attrs, sym::register_tool) - .filter_map(|attr| attr.meta_item_list()) - .flatten() - .filter_map(|nested_meta| nested_meta.ident()) - .map(|ident| ident.name) - .any(|name| name == m_item) -} - struct LintLevelMapBuilder<'tcx> { levels: LintLevelsBuilder<'tcx>, tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index c36a233df9185..a87f2b2768d67 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -96,7 +96,7 @@ use unused::*; pub use builtin::SoftLints; pub use context::{CheckLintNameResult, FindLintError, LintStore}; pub use context::{EarlyContext, LateContext, LintContext}; -pub use early::check_ast_node; +pub use early::{check_ast_node, EarlyCheckNode}; pub use late::check_crate; pub use passes::{EarlyLintPass, LateLintPass}; pub use rustc_session::lint::Level::{self, *}; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 8ed1533436ed1..64c00c353ca1b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -119,6 +119,8 @@ mod sty; // Data types +pub type RegisteredTools = FxHashSet; + #[derive(Debug)] pub struct ResolverOutputs { pub definitions: rustc_hir::definitions::Definitions, @@ -141,6 +143,7 @@ pub struct ResolverOutputs { /// Mapping from ident span to path span for paths that don't exist as written, but that /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. pub confused_type_with_std_module: FxHashMap, + pub registered_tools: RegisteredTools, } #[derive(Clone, Copy, Debug)] diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index f5b2ba8fd72eb..f6625ac021b30 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -53,7 +53,7 @@ use rustc_middle::metadata::ModChild; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::span_bug; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, DefIdTree, MainDefinition, ResolverOutputs}; +use rustc_middle::ty::{self, DefIdTree, MainDefinition, RegisteredTools, ResolverOutputs}; use rustc_query_system::ich::StableHashingContext; use rustc_session::cstore::{CrateStore, MetadataLoaderDyn}; use rustc_session::lint; @@ -989,7 +989,7 @@ pub struct Resolver<'a> { macro_names: FxHashSet, builtin_macros: FxHashMap, registered_attrs: FxHashSet, - registered_tools: FxHashSet, + registered_tools: RegisteredTools, macro_use_prelude: FxHashMap>, all_macros: FxHashMap, macro_map: FxHashMap>, @@ -1487,6 +1487,7 @@ impl<'a> Resolver<'a> { trait_impls: self.trait_impls, proc_macros, confused_type_with_std_module, + registered_tools: self.registered_tools, } } @@ -1511,6 +1512,7 @@ impl<'a> Resolver<'a> { trait_impls: self.trait_impls.clone(), proc_macros, confused_type_with_std_module: self.confused_type_with_std_module.clone(), + registered_tools: self.registered_tools.clone(), } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 52685ec697ce8..82807e2d0a2c5 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -23,7 +23,7 @@ use rustc_hir::def::{self, DefKind, NonMacroAttrKind}; use rustc_hir::def_id::{CrateNum, LocalDefId}; use rustc_hir::PrimTy; use rustc_middle::middle::stability; -use rustc_middle::ty; +use rustc_middle::ty::{self, RegisteredTools}; use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK}; use rustc_session::lint::builtin::{SOFT_UNSTABLE, UNUSED_MACROS}; use rustc_session::lint::BuiltinLintDiagnostics; @@ -447,6 +447,10 @@ impl<'a> ResolverExpand for Resolver<'a> { fn declare_proc_macro(&mut self, id: NodeId) { self.proc_macros.push(id) } + + fn registered_tools(&self) -> &RegisteredTools { + &self.registered_tools + } } impl<'a> Resolver<'a> { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 5ee3146eaab12..512c39389c1bd 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -580,7 +580,7 @@ fn get_lint_group_and_level_or_lint( ) -> Option<(String, &'static str)> { let result = cx .lint_store - .check_lint_name(cx.sess(), lint_name, Some(sym::clippy), &[]); + .check_lint_name(lint_name, Some(sym::clippy), &[]); if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { if let Some(group) = get_lint_group(cx, lint_lst[0]) { if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) { From 9c70b6d11b8337102f2d88e2ebab4c42944797e5 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 4 Dec 2021 23:09:15 +0800 Subject: [PATCH 25/28] Update clippy --- .../clippy/clippy_lints/src/approx_const.rs | 2 +- .../clippy/clippy_lints/src/as_conversions.rs | 4 ++-- .../src/disallowed_script_idents.rs | 4 ++-- .../clippy_lints/src/else_if_without_else.rs | 4 ++-- .../clippy/clippy_lints/src/formatting.rs | 6 +++--- .../clippy/clippy_lints/src/from_over_into.rs | 2 +- .../clippy_lints/src/index_refutable_slice.rs | 2 +- .../src/items_after_statements.rs | 6 +++--- .../src/literal_representation.rs | 6 +++--- .../clippy_lints/src/manual_non_exhaustive.rs | 6 +++--- .../clippy/clippy_lints/src/manual_strip.rs | 2 +- src/tools/clippy/clippy_lints/src/matches.rs | 2 +- .../clippy/clippy_lints/src/mem_replace.rs | 2 +- .../clippy/clippy_lints/src/misc_early/mod.rs | 4 ++-- .../clippy_lints/src/missing_const_for_fn.rs | 2 +- .../clippy/clippy_lints/src/module_style.rs | 4 ++-- .../clippy_lints/src/non_expressive_names.rs | 6 +++--- .../clippy/clippy_lints/src/octal_escapes.rs | 4 ++-- src/tools/clippy/clippy_lints/src/ranges.rs | 2 +- .../src/redundant_closure_call.rs | 4 ++-- .../clippy/clippy_lints/src/redundant_else.rs | 4 ++-- .../clippy_lints/src/redundant_field_names.rs | 4 ++-- .../src/single_char_lifetime_names.rs | 4 ++-- .../src/single_component_path_imports.rs | 4 ++-- src/tools/clippy/clippy_lints/src/use_self.rs | 2 +- src/tools/clippy/clippy_lints/src/write.rs | 6 +++--- src/tools/clippy/clippy_utils/src/lib.rs | 20 +++++-------------- 27 files changed, 54 insertions(+), 64 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index 5061c9d1eaf6f..e109ee0009ee0 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol; diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs index 53704da1046bc..88b91d589074d 100644 --- a/src/tools/clippy/clippy_lints/src/as_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Expr, ExprKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -48,7 +48,7 @@ declare_lint_pass!(AsConversions => [AS_CONVERSIONS]); impl EarlyLintPass for AsConversions { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if in_external_macro(cx.sess, expr.span) { + if in_external_macro(cx.sess(), expr.span) { return; } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs index 3c3f3631849e5..0c27c3f9255f2 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; -use rustc_lint::{EarlyContext, EarlyLintPass, Level}; +use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use unicode_script::{Script, UnicodeScript}; @@ -72,7 +72,7 @@ impl EarlyLintPass for DisallowedScriptIdents { return; } - let symbols = cx.sess.parse_sess.symbol_gallery.symbols.lock(); + let symbols = cx.sess().parse_sess.symbol_gallery.symbols.lock(); // Sort by `Span` so that error messages make sense with respect to the // order of identifier locations in the code. let mut symbols: Vec<_> = symbols.iter().collect(); diff --git a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs index 92c56c762aad6..0b9f54231c59b 100644 --- a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs +++ b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Expr, ExprKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -50,7 +50,7 @@ declare_lint_pass!(ElseIfWithoutElse => [ELSE_IF_WITHOUT_ELSE]); impl EarlyLintPass for ElseIfWithoutElse { fn check_expr(&mut self, cx: &EarlyContext<'_>, mut item: &Expr) { - if in_external_macro(cx.sess, item.span) { + if in_external_macro(cx.sess(), item.span) { return; } diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs index 3e85c8a9c8071..ae18f8081bcc8 100644 --- a/src/tools/clippy/clippy_lints/src/formatting.rs +++ b/src/tools/clippy/clippy_lints/src/formatting.rs @@ -3,7 +3,7 @@ use clippy_utils::differing_macro_contexts; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -207,7 +207,7 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::If(_, then, Some(else_)) = &expr.kind; if is_block(else_) || is_if(else_); if !differing_macro_contexts(then.span, else_.span); - if !then.span.from_expansion() && !in_external_macro(cx.sess, expr.span); + if !then.span.from_expansion() && !in_external_macro(cx.sess(), expr.span); // workaround for rust-lang/rust#43081 if expr.span.lo().0 != 0 && expr.span.hi().0 != 0; @@ -259,7 +259,7 @@ fn has_unary_equivalent(bin_op: BinOpKind) -> bool { } fn indentation(cx: &EarlyContext<'_>, span: Span) -> usize { - cx.sess.source_map().lookup_char_pos(span.lo()).col.0 + cx.sess().source_map().lookup_char_pos(span.lo()).col.0 } /// Implementation of the `POSSIBLE_MISSING_COMMA` lint for array diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index 5ece2cc5ac4f7..c2f52605151ed 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{meets_msrv, msrvs}; use if_chain::if_chain; use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 4615122bbf9e5..667652106987a 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; use rustc_semver::RustcVersion; diff --git a/src/tools/clippy/clippy_lints/src/items_after_statements.rs b/src/tools/clippy/clippy_lints/src/items_after_statements.rs index b118d3c8b8727..cdefe627efdaa 100644 --- a/src/tools/clippy/clippy_lints/src/items_after_statements.rs +++ b/src/tools/clippy/clippy_lints/src/items_after_statements.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{Block, ItemKind, StmtKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -55,7 +55,7 @@ declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]); impl EarlyLintPass for ItemsAfterStatements { fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) { - if in_external_macro(cx.sess, item.span) { + if in_external_macro(cx.sess(), item.span) { return; } @@ -69,7 +69,7 @@ impl EarlyLintPass for ItemsAfterStatements { // lint on all further items for stmt in stmts { if let StmtKind::Item(ref it) = *stmt { - if in_external_macro(cx.sess, it.span) { + if in_external_macro(cx.sess(), it.span) { return; } if let ItemKind::MacroDef(..) = it.kind { diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 130543bbbee80..b7430f49229ae 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -7,7 +7,7 @@ use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use std::iter; @@ -225,7 +225,7 @@ impl_lint_pass!(LiteralDigitGrouping => [ impl EarlyLintPass for LiteralDigitGrouping { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if in_external_macro(cx.sess, expr.span) { + if in_external_macro(cx.sess(), expr.span) { return; } @@ -418,7 +418,7 @@ impl_lint_pass!(DecimalLiteralRepresentation => [DECIMAL_LITERAL_REPRESENTATION] impl EarlyLintPass for DecimalLiteralRepresentation { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if in_external_macro(cx.sess, expr.span) { + if in_external_macro(cx.sess(), expr.span) { return; } diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 63a72d4fddeb0..33d1bb2985f43 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -5,7 +5,7 @@ use clippy_utils::{meets_msrv, msrvs}; use if_chain::if_chain; use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; @@ -116,7 +116,7 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants |diag| { if_chain! { if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)); - let header_span = cx.sess.source_map().span_until_char(item.span, '{'); + let header_span = cx.sess().source_map().span_until_char(item.span, '{'); if let Some(snippet) = snippet_opt(cx, header_span); then { diag.span_suggestion( @@ -149,7 +149,7 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"), }; - cx.sess.source_map().span_until_char(item.span, delimiter) + cx.sess().source_map().span_until_char(item.span, delimiter) } let fields = data.fields(); diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index c814e013c631c..aacabf303a702 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -9,7 +9,7 @@ use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::BinOpKind; use rustc_hir::{BorrowKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs index 411a797b6cb53..dfb450c8848ad 100644 --- a/src/tools/clippy/clippy_lints/src/matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches.rs @@ -25,7 +25,7 @@ use rustc_hir::{ Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind, }; use rustc_hir::{HirIdMap, HirIdSet}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty, TyS, VariantDef}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index 7fc39f17232fd..a184806d021bc 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -6,7 +6,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs index 6e09e25109fbb..d955fad7d41a2 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs @@ -12,7 +12,7 @@ use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Expr, ExprKind, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -342,7 +342,7 @@ impl EarlyLintPass for MiscEarlyLints { } fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if in_external_macro(cx.sess, expr.span) { + if in_external_macro(cx.sess(), expr.span) { return; } diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index 77849e1800f6b..bad9e0be82e61 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -5,7 +5,7 @@ use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, msr use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index 3b65f80cba20a..b8dfe99688066 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -80,9 +80,9 @@ impl EarlyLintPass for ModStyle { return; } - let files = cx.sess.source_map().files(); + let files = cx.sess().source_map().files(); - let trim_to_src = if let RealFileName::LocalPath(p) = &cx.sess.opts.working_dir { + let trim_to_src = if let RealFileName::LocalPath(p) = &cx.sess().opts.working_dir { p.to_string_lossy() } else { return; diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index 39a37e3e378ed..0d0c88b02c78b 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -3,7 +3,7 @@ use rustc_ast::ast::{ self, Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, Pat, PatKind, }; use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -356,7 +356,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> { impl EarlyLintPass for NonExpressiveNames { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if in_external_macro(cx.sess, item.span) { + if in_external_macro(cx.sess(), item.span) { return; } @@ -371,7 +371,7 @@ impl EarlyLintPass for NonExpressiveNames { } fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) { - if in_external_macro(cx.sess, item.span) { + if in_external_macro(cx.sess(), item.span) { return; } diff --git a/src/tools/clippy/clippy_lints/src/octal_escapes.rs b/src/tools/clippy/clippy_lints/src/octal_escapes.rs index e0da12f77fcc7..d81481ade044d 100644 --- a/src/tools/clippy/clippy_lints/src/octal_escapes.rs +++ b/src/tools/clippy/clippy_lints/src/octal_escapes.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::token::{Lit, LitKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -51,7 +51,7 @@ declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]); impl EarlyLintPass for OctalEscapes { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if in_external_macro(cx.sess, expr.span) { + if in_external_macro(cx.sess(), expr.span) { return; } diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 52d47e6d97861..027ab70014fc0 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -8,7 +8,7 @@ use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index 0c77cf5e77dd2..5a25008e95e5b 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -8,7 +8,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit as hir_visit; use rustc_hir::intravisit::Visitor as HirVisitor; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -62,7 +62,7 @@ impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor { impl EarlyLintPass for RedundantClosureCall { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if in_external_macro(cx.sess, expr.span) { + if in_external_macro(cx.sess(), expr.span) { return; } if_chain! { diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs index 93dbe936d5841..73088ce1a87e7 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_else.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_ast::visit::{walk_expr, Visitor}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -46,7 +46,7 @@ declare_lint_pass!(RedundantElse => [REDUNDANT_ELSE]); impl EarlyLintPass for RedundantElse { fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &Stmt) { - if in_external_macro(cx.sess, stmt.span) { + if in_external_macro(cx.sess(), stmt.span) { return; } // Only look at expressions that are a whole statement diff --git a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs index 0dea4a784b217..40a62fd6d2013 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -55,7 +55,7 @@ impl EarlyLintPass for RedundantFieldNames { return; } - if in_external_macro(cx.sess, expr.span) { + if in_external_macro(cx.sess(), expr.span) { return; } if let ExprKind::Struct(ref se) = expr.kind { diff --git a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs index ee82666b5affe..aa306a630c467 100644 --- a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs +++ b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{GenericParam, GenericParamKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -43,7 +43,7 @@ declare_lint_pass!(SingleCharLifetimeNames => [SINGLE_CHAR_LIFETIME_NAMES]); impl EarlyLintPass for SingleCharLifetimeNames { fn check_generic_param(&mut self, ctx: &EarlyContext<'_>, param: &GenericParam) { - if in_external_macro(ctx.sess, param.ident.span) { + if in_external_macro(ctx.sess(), param.ident.span) { return; } diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs index 28d32203da9d7..961cdb317e76c 100644 --- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{edition::Edition, symbol::kw, Span, Symbol}; @@ -37,7 +37,7 @@ declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS] impl EarlyLintPass for SingleComponentPathImports { fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { - if cx.sess.opts.edition < Edition::Edition2018 { + if cx.sess().opts.edition < Edition::Edition2018 { return; } check_mod(cx, &krate.items); diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 6c5a5fe1434f8..be20282b3b88c 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -11,7 +11,7 @@ use rustc_hir::{ intravisit::{walk_inf, walk_ty, Visitor}, Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Path, QPath, TyKind, }; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index f9add927b49b8..b0044695ea8a8 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -9,7 +9,7 @@ use rustc_ast::token::{self, LitKind}; use rustc_ast::tokenstream::TokenStream; use rustc_errors::Applicability; use rustc_lexer::unescape::{self, EscapeError}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, Symbol}; @@ -290,7 +290,7 @@ impl EarlyLintPass for Write { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { fn is_build_script(cx: &EarlyContext<'_>) -> bool { // Cargo sets the crate name for build scripts to `build_script_build` - cx.sess + cx.sess() .opts .crate_name .as_ref() @@ -529,7 +529,7 @@ impl Write { /// ``` #[allow(clippy::too_many_lines)] fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option, Option) { - let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None); + let mut parser = parser::Parser::new(&cx.sess().parse_sess, tts, false, None); let expr = if is_write { match parser .parse_expr() diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 8386aaeaf4449..9233903e98a00 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -117,25 +117,15 @@ pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool #[macro_export] macro_rules! extract_msrv_attr { - (LateContext) => { - extract_msrv_attr!(@LateContext, ()); - }; - (EarlyContext) => { - extract_msrv_attr!(@EarlyContext); - }; - (@$context:ident$(, $call:tt)?) => { + ($context:ident) => { fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) { - use $crate::get_unique_inner_attr; - match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") { + let sess = rustc_lint::LintContext::sess(cx); + match $crate::get_unique_inner_attr(sess, attrs, "msrv") { Some(msrv_attr) => { if let Some(msrv) = msrv_attr.value_str() { - self.msrv = $crate::parse_msrv( - &msrv.to_string(), - Some(cx.sess$($call)?), - Some(msrv_attr.span), - ); + self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span)); } else { - cx.sess$($call)?.span_err(msrv_attr.span, "bad clippy attribute"); + sess.span_err(msrv_attr.span, "bad clippy attribute"); } }, _ => (), From 05cd75504b6bfce9e8ea9cc57dcef1120b98acde Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 7 Dec 2021 18:28:12 +0800 Subject: [PATCH 26/28] rustc_lint: Stop creating a fake `ast::Crate` for running early lints Add a trait generalizing over the crate root and freshly loaded modules instead This also makes node IDs used for pre-expansion linting more precise --- compiler/rustc_expand/src/base.rs | 5 +- compiler/rustc_expand/src/expand.rs | 7 ++- compiler/rustc_interface/src/passes.rs | 25 +++++---- compiler/rustc_lint/src/early.rs | 72 +++++++++++++++++++++----- compiler/rustc_lint/src/levels.rs | 2 +- 5 files changed, 85 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index ce08d5d7f2fa0..dcda42265514a 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -959,8 +959,9 @@ pub struct ExpansionData { pub is_trailing_mac: bool, } -type OnExternModLoaded<'a> = - Option<&'a dyn Fn(Ident, Vec, Vec>, Span) -> (Vec, Vec>)>; +type OnExternModLoaded<'a> = Option< + &'a dyn Fn(NodeId, Vec, Vec>, Symbol) -> (Vec, Vec>), +>; /// One of these is made during expansion and incrementally updated as we go; /// when a macro expansion occurs, the resulting nodes have the `backtrace() diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 7604a464be2ff..ac637b7c7ed37 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1108,7 +1108,12 @@ impl InvocationCollectorNode for P { ); if let Some(extern_mod_loaded) = ecx.extern_mod_loaded { - (attrs, items) = extern_mod_loaded(ident, attrs, items, inner_span); + (attrs, items) = extern_mod_loaded( + ecx.current_expansion.lint_node_id, + attrs, + items, + ident.name, + ); } *mod_kind = ModKind::Loaded(items, Inline::No, inner_span); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 6ac73d06868b7..a1b38b6fb9038 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -3,7 +3,7 @@ use crate::proc_macro_decls; use crate::util; use rustc_ast::mut_visit::MutVisitor; -use rustc_ast::{self as ast, visit, DUMMY_NODE_ID}; +use rustc_ast::{self as ast, visit}; use rustc_borrowck as mir_borrowck; use rustc_codegen_ssa::back::link::emit_metadata; use rustc_codegen_ssa::traits::CodegenBackend; @@ -14,7 +14,7 @@ use rustc_errors::{Applicability, ErrorReported, PResult}; use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE}; use rustc_hir::Crate; -use rustc_lint::LintStore; +use rustc_lint::{EarlyCheckNode, LintStore}; use rustc_metadata::creader::CStore; use rustc_metadata::{encode_metadata, EncodedMetadata}; use rustc_middle::arena::Arena; @@ -34,7 +34,7 @@ use rustc_session::lint; use rustc_session::output::{filename_for_input, filename_for_metadata}; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; -use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::symbol::{sym, Symbol}; use rustc_span::{FileName, MultiSpan}; use rustc_trait_selection::traits; use rustc_typeck as typeck; @@ -233,11 +233,11 @@ pub fn register_plugins<'a>( Ok((krate, lint_store)) } -fn pre_expansion_lint( +fn pre_expansion_lint<'a>( sess: &Session, lint_store: &LintStore, registered_tools: &RegisteredTools, - check_node: &ast::Crate, + check_node: impl EarlyCheckNode<'a>, node_name: &str, ) { sess.prof.generic_activity_with_arg("pre_AST_expansion_lint_checks", node_name).run(|| { @@ -322,10 +322,15 @@ pub fn configure_and_expand( }; let registered_tools = resolver.registered_tools().clone(); - let extern_mod_loaded = |ident: Ident, attrs, items, span| { - let krate = ast::Crate { attrs, items, span, id: DUMMY_NODE_ID, is_placeholder: false }; - pre_expansion_lint(sess, lint_store, ®istered_tools, &krate, ident.name.as_str()); - (krate.attrs, krate.items) + let extern_mod_loaded = |node_id, attrs: Vec<_>, items: Vec<_>, name: Symbol| { + pre_expansion_lint( + sess, + lint_store, + ®istered_tools, + (node_id, &*attrs, &*items), + name.as_str(), + ); + (attrs, items) }; let mut ecx = ExtCtxt::new(sess, cfg, resolver, Some(&extern_mod_loaded)); @@ -507,7 +512,7 @@ pub fn lower_to_hir<'res, 'tcx>( resolver.registered_tools(), lint_buffer, rustc_lint::BuiltinCombinedEarlyLintPass::new(), - &krate, + &*krate, ) }); diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 6cf67922be72b..1b2c88867d49d 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -16,9 +16,10 @@ use crate::context::{EarlyContext, LintContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; -use rustc_ast as ast; -use rustc_ast::visit as ast_visit; +use rustc_ast::ptr::P; +use rustc_ast::visit::{self as ast_visit, Visitor}; use rustc_ast::AstLike; +use rustc_ast::{self as ast, walk_list}; use rustc_middle::ty::RegisteredTools; use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_session::Session; @@ -32,7 +33,7 @@ macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({ $cx.pass.$f(&$cx.context, $($args),*); }) } -struct EarlyContextAndPass<'a, T: EarlyLintPass> { +pub struct EarlyContextAndPass<'a, T: EarlyLintPass> { context: EarlyContext<'a>, pass: T, } @@ -326,14 +327,65 @@ macro_rules! early_lint_pass_impl { crate::early_lint_methods!(early_lint_pass_impl, []); -fn early_lint_node( +/// Early lints work on different nodes - either on the crate root, or on freshly loaded modules. +/// This trait generalizes over those nodes. +pub trait EarlyCheckNode<'a>: Copy { + fn id(self) -> ast::NodeId; + fn attrs<'b>(self) -> &'b [ast::Attribute] + where + 'a: 'b; + fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>) + where + 'a: 'b; +} + +impl<'a> EarlyCheckNode<'a> for &'a ast::Crate { + fn id(self) -> ast::NodeId { + ast::CRATE_NODE_ID + } + fn attrs<'b>(self) -> &'b [ast::Attribute] + where + 'a: 'b, + { + &self.attrs + } + fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>) + where + 'a: 'b, + { + run_early_pass!(cx, check_crate, self); + ast_visit::walk_crate(cx, self); + run_early_pass!(cx, check_crate_post, self); + } +} + +impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P]) { + fn id(self) -> ast::NodeId { + self.0 + } + fn attrs<'b>(self) -> &'b [ast::Attribute] + where + 'a: 'b, + { + self.1 + } + fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>) + where + 'a: 'b, + { + walk_list!(cx, visit_attribute, self.1); + walk_list!(cx, visit_item, self.2); + } +} + +fn early_lint_node<'a>( sess: &Session, warn_about_weird_lints: bool, lint_store: &LintStore, registered_tools: &RegisteredTools, buffered: LintBuffer, pass: impl EarlyLintPass, - check_node: &ast::Crate, + check_node: impl EarlyCheckNode<'a>, ) -> LintBuffer { let mut cx = EarlyContextAndPass { context: EarlyContext::new( @@ -346,22 +398,18 @@ fn early_lint_node( pass, }; - cx.with_lint_attrs(ast::CRATE_NODE_ID, &check_node.attrs, |cx| { - run_early_pass!(cx, check_crate, check_node); - ast_visit::walk_crate(cx, check_node); - run_early_pass!(cx, check_crate_post, check_node); - }); + cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx)); cx.context.buffered } -pub fn check_ast_node( +pub fn check_ast_node<'a>( sess: &Session, pre_expansion: bool, lint_store: &LintStore, registered_tools: &RegisteredTools, lint_buffer: Option, builtin_lints: impl EarlyLintPass, - check_node: &ast::Crate, + check_node: impl EarlyCheckNode<'a>, ) { let passes = if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes }; diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 080a38bae669c..8afbd462c146b 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -5,7 +5,7 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::{intravisit, HirId, CRATE_HIR_ID}; +use rustc_hir::{intravisit, HirId}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::LevelAndSource; use rustc_middle::lint::LintDiagnosticBuilder; From 67cccaff4858eef5e78793f5a123ac33e5cf3704 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 11 Dec 2021 15:32:48 +0800 Subject: [PATCH 27/28] expand: Pass everything by reference to pre-expansion lint callback --- compiler/rustc_expand/src/base.rs | 28 ++++++++++++++-------- compiler/rustc_expand/src/expand.rs | 14 ++++++----- compiler/rustc_interface/src/passes.rs | 33 ++++++++++++++++---------- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index dcda42265514a..258320aeb636a 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -925,6 +925,20 @@ pub trait ResolverExpand { fn registered_tools(&self) -> &FxHashSet; } +pub trait LintStoreExpand { + fn pre_expansion_lint( + &self, + sess: &Session, + registered_tools: &FxHashSet, + node_id: NodeId, + attrs: &[Attribute], + items: &[P], + name: &str, + ); +} + +type LintStoreExpandDyn<'a> = Option<&'a (dyn LintStoreExpand + 'a)>; + #[derive(Clone, Default)] pub struct ModuleData { /// Path to the module starting from the crate name, like `my_crate::foo::bar`. @@ -959,10 +973,6 @@ pub struct ExpansionData { pub is_trailing_mac: bool, } -type OnExternModLoaded<'a> = Option< - &'a dyn Fn(NodeId, Vec, Vec>, Symbol) -> (Vec, Vec>), ->; - /// One of these is made during expansion and incrementally updated as we go; /// when a macro expansion occurs, the resulting nodes have the `backtrace() /// -> expn_data` of their expansion context stored into their span. @@ -977,10 +987,8 @@ pub struct ExtCtxt<'a> { /// (or during eager expansion, but that's a hack). pub force_mode: bool, pub expansions: FxHashMap>, - /// Called directly after having parsed an external `mod foo;` in expansion. - /// - /// `Ident` is the module name. - pub(super) extern_mod_loaded: OnExternModLoaded<'a>, + /// Used for running pre-expansion lints on freshly loaded modules. + pub(super) lint_store: LintStoreExpandDyn<'a>, /// When we 'expand' an inert attribute, we leave it /// in the AST, but insert it here so that we know /// not to expand it again. @@ -992,14 +1000,14 @@ impl<'a> ExtCtxt<'a> { sess: &'a Session, ecfg: expand::ExpansionConfig<'a>, resolver: &'a mut dyn ResolverExpand, - extern_mod_loaded: OnExternModLoaded<'a>, + lint_store: LintStoreExpandDyn<'a>, ) -> ExtCtxt<'a> { ExtCtxt { sess, ecfg, reduced_recursion_limit: None, resolver, - extern_mod_loaded, + lint_store, root_path: PathBuf::new(), current_expansion: ExpansionData { id: LocalExpnId::ROOT, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index ac637b7c7ed37..9a4daa6d7500a 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1097,7 +1097,7 @@ impl InvocationCollectorNode for P { ModKind::Unloaded => { // We have an outline `mod foo;` so we need to parse the file. let old_attrs_len = attrs.len(); - let ParsedExternalMod { mut items, inner_span, file_path, dir_path, dir_ownership } = + let ParsedExternalMod { items, inner_span, file_path, dir_path, dir_ownership } = parse_external_mod( &ecx.sess, ident, @@ -1107,12 +1107,14 @@ impl InvocationCollectorNode for P { &mut attrs, ); - if let Some(extern_mod_loaded) = ecx.extern_mod_loaded { - (attrs, items) = extern_mod_loaded( + if let Some(lint_store) = ecx.lint_store { + lint_store.pre_expansion_lint( + ecx.sess, + ecx.resolver.registered_tools(), ecx.current_expansion.lint_node_id, - attrs, - items, - ident.name, + &attrs, + &items, + ident.name.as_str(), ); } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index a1b38b6fb9038..be31eb89f1b92 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -11,7 +11,7 @@ use rustc_data_structures::parallel; use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{Applicability, ErrorReported, PResult}; -use rustc_expand::base::{ExtCtxt, ResolverExpand}; +use rustc_expand::base::{ExtCtxt, LintStoreExpand, ResolverExpand}; use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE}; use rustc_hir::Crate; use rustc_lint::{EarlyCheckNode, LintStore}; @@ -253,6 +253,23 @@ fn pre_expansion_lint<'a>( }); } +// Cannot implement directly for `LintStore` due to trait coherence. +struct LintStoreExpandImpl<'a>(&'a LintStore); + +impl LintStoreExpand for LintStoreExpandImpl<'_> { + fn pre_expansion_lint( + &self, + sess: &Session, + registered_tools: &RegisteredTools, + node_id: ast::NodeId, + attrs: &[ast::Attribute], + items: &[rustc_ast::ptr::P], + name: &str, + ) { + pre_expansion_lint(sess, self.0, registered_tools, (node_id, attrs, items), name); + } +} + /// Runs the "early phases" of the compiler: initial `cfg` processing, loading compiler plugins, /// syntax expansion, secondary `cfg` expansion, synthesis of a test /// harness if one is to be provided, injection of a dependency on the @@ -321,18 +338,8 @@ pub fn configure_and_expand( ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string()) }; - let registered_tools = resolver.registered_tools().clone(); - let extern_mod_loaded = |node_id, attrs: Vec<_>, items: Vec<_>, name: Symbol| { - pre_expansion_lint( - sess, - lint_store, - ®istered_tools, - (node_id, &*attrs, &*items), - name.as_str(), - ); - (attrs, items) - }; - let mut ecx = ExtCtxt::new(sess, cfg, resolver, Some(&extern_mod_loaded)); + let lint_store = LintStoreExpandImpl(lint_store); + let mut ecx = ExtCtxt::new(sess, cfg, resolver, Some(&lint_store)); // Expand macros now! let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate)); From 4a74ace3c99e21e544224599636e3e1ae7024da0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 22 Jan 2022 20:36:05 -0800 Subject: [PATCH 28/28] Liberate late bound regions when collecting GAT substs in wfcheck --- compiler/rustc_typeck/src/check/wfcheck.rs | 19 ++++++++++++++++--- .../generic-associated-types/issue-92954.rs | 10 ++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/generic-associated-types/issue-92954.rs diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 606a2d6a24e59..aa1a521d53274 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -312,7 +312,7 @@ fn check_gat_where_clauses( // of the function signature. In our example, the GAT in the return // type is `::Item<'a>`, so 'a and Self are arguments. let (regions, types) = - GATSubstCollector::visit(trait_item.def_id.to_def_id(), sig.output()); + GATSubstCollector::visit(tcx, trait_item.def_id.to_def_id(), sig.output()); // If both regions and types are empty, then this GAT isn't in the // return type, and we shouldn't try to do clause analysis @@ -602,6 +602,7 @@ fn resolve_regions_with_wf_tys<'tcx>( /// the two vectors, `regions` and `types` (depending on their kind). For each /// parameter `Pi` also track the index `i`. struct GATSubstCollector<'tcx> { + tcx: TyCtxt<'tcx>, gat: DefId, // Which region appears and which parameter index its subsituted for regions: FxHashSet<(ty::Region<'tcx>, usize)>, @@ -611,11 +612,16 @@ struct GATSubstCollector<'tcx> { impl<'tcx> GATSubstCollector<'tcx> { fn visit>( + tcx: TyCtxt<'tcx>, gat: DefId, t: T, ) -> (FxHashSet<(ty::Region<'tcx>, usize)>, FxHashSet<(Ty<'tcx>, usize)>) { - let mut visitor = - GATSubstCollector { gat, regions: FxHashSet::default(), types: FxHashSet::default() }; + let mut visitor = GATSubstCollector { + tcx, + gat, + regions: FxHashSet::default(), + types: FxHashSet::default(), + }; t.visit_with(&mut visitor); (visitor.regions, visitor.types) } @@ -624,6 +630,13 @@ impl<'tcx> GATSubstCollector<'tcx> { impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> { type BreakTy = !; + fn visit_binder>( + &mut self, + t: &ty::Binder<'tcx, T>, + ) -> ControlFlow { + self.tcx.liberate_late_bound_regions(self.gat, t.clone()).visit_with(self) + } + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { match t.kind() { ty::Projection(p) if p.item_def_id == self.gat => { diff --git a/src/test/ui/generic-associated-types/issue-92954.rs b/src/test/ui/generic-associated-types/issue-92954.rs new file mode 100644 index 0000000000000..95c090ff4e906 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-92954.rs @@ -0,0 +1,10 @@ +// check-pass + +#![feature(generic_associated_types)] + +pub trait Foo { + type Assoc<'c>; + fn function() -> for<'x> fn(Self::Assoc<'x>); +} + +fn main() {}