From aca631fb9b444fbce3e52a092289518363c0af50 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 8 Jan 2024 13:14:30 -0500 Subject: [PATCH 01/62] Increase visibility of `join_path` and `split_paths` Add some crosslinking among `std::env` pages. Also add aliases to help anyone searching for `PATH`. --- library/std/src/env.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 30ac0512348..e662b26316b 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -258,6 +258,9 @@ fn _var(key: &OsStr) -> Result { /// None => println!("{key} is not defined in the environment.") /// } /// ``` +/// +/// If expecting a delimited variable (such as `PATH`), [`split_paths`] +/// can be used to separate items. #[must_use] #[stable(feature = "env", since = "1.0.0")] pub fn var_os>(key: K) -> Option { @@ -441,6 +444,16 @@ pub struct SplitPaths<'a> { /// Returns an iterator over the paths contained in `unparsed`. The iterator /// element type is [`PathBuf`]. /// +/// On most Unix platforms, the separator is `:` and on Windows it is `;`. This +/// also performs unquoting on Windows. +/// +/// [`join_paths`] can be used to recombine elements. +/// +/// # Panics +/// +/// This will panic on systems where theere is no delimited `PATH` variable, +/// such as UEFI. +/// /// # Examples /// /// ``` @@ -456,6 +469,7 @@ pub struct SplitPaths<'a> { /// None => println!("{key} is not defined in the environment.") /// } /// ``` +#[doc(alias = "PATH")] #[stable(feature = "env", since = "1.0.0")] pub fn split_paths + ?Sized>(unparsed: &T) -> SplitPaths<'_> { SplitPaths { inner: os_imp::split_paths(unparsed.as_ref()) } @@ -496,7 +510,8 @@ pub struct JoinPathsError { /// /// Returns an [`Err`] (containing an error message) if one of the input /// [`Path`]s contains an invalid character for constructing the `PATH` -/// variable (a double quote on Windows or a colon on Unix). +/// variable (a double quote on Windows or a colon on Unix), or if the system +/// does not have a `PATH`-like variable (e.g. UEFI or WASI). /// /// # Examples /// @@ -550,6 +565,7 @@ pub struct JoinPathsError { /// ``` /// /// [`env::split_paths()`]: split_paths +#[doc(alias = "PATH")] #[stable(feature = "env", since = "1.0.0")] pub fn join_paths(paths: I) -> Result where From d51e703534c49b5b658a954fe6d387c33ba0c5e3 Mon Sep 17 00:00:00 2001 From: CKingX Date: Thu, 8 Feb 2024 17:15:11 -0800 Subject: [PATCH 02/62] As Windows 10 requires certain features like CMPXCHG16B and a few others and Rust plans to set Windows 10 as the minimum supported OS for target x86_64-pc-windows-msvc, I have added the cmpxchg16b and sse3 feature (as CPUs that meet the Windows 10 64-bit requirement also support SSE3. See https://walbourn.github.io/directxmath-sse3-and-ssse3/ ) --- compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs index 357261073a8..e3db187e7eb 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs @@ -3,6 +3,7 @@ use crate::spec::{base, SanitizerSet, Target}; pub fn target() -> Target { let mut base = base::windows_msvc::opts(); base.cpu = "x86-64".into(); + base.features = "+cmpxchg16b,+sse3".into(); base.plt_by_default = false; base.max_atomic_width = Some(64); base.supported_sanitizers = SanitizerSet::ADDRESS; From d6766e2bc8eb6cfd695f2c9e931c7a712da474ff Mon Sep 17 00:00:00 2001 From: CKingX Date: Fri, 9 Feb 2024 07:59:38 -0800 Subject: [PATCH 03/62] Update x86_64_pc_windows_msvc.rs Fixed a bug where adding CMPXCHG16B would fail due to different names in Rustc and LLVM --- .../rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs index e3db187e7eb..f7dc60df0f4 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs @@ -3,7 +3,7 @@ use crate::spec::{base, SanitizerSet, Target}; pub fn target() -> Target { let mut base = base::windows_msvc::opts(); base.cpu = "x86-64".into(); - base.features = "+cmpxchg16b,+sse3".into(); + base.features = "+cx16,+sse3".into(); base.plt_by_default = false; base.max_atomic_width = Some(64); base.supported_sanitizers = SanitizerSet::ADDRESS; From fcb06f7ca2a20d030fdd5f9c53b684557f17c00c Mon Sep 17 00:00:00 2001 From: CKingX Date: Fri, 9 Feb 2024 09:19:59 -0800 Subject: [PATCH 04/62] Update x86_64_pc_windows_msvc.rs As CMPXCHG16B is supported, I updated the max atomic width to 128-bits from 64-bits --- .../rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs index f7dc60df0f4..e72e97db775 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs @@ -5,7 +5,7 @@ pub fn target() -> Target { base.cpu = "x86-64".into(); base.features = "+cx16,+sse3".into(); base.plt_by_default = false; - base.max_atomic_width = Some(64); + base.max_atomic_width = Some(128); base.supported_sanitizers = SanitizerSet::ADDRESS; Target { From abeac8fbc1cac4bad60094c64179366a802ec949 Mon Sep 17 00:00:00 2001 From: CKingX Date: Fri, 9 Feb 2024 12:25:17 -0800 Subject: [PATCH 05/62] Update x86_64_uwp_windows_gnu.rs Updated x86_64-uwp-windows-gnu to use CMPXCHG16B and SSE3 --- .../rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs index c2981ddbad6..ee95c67496d 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs @@ -3,6 +3,7 @@ use crate::spec::{base, Cc, LinkerFlavor, Lld, Target}; pub fn target() -> Target { let mut base = base::windows_uwp_gnu::opts(); base.cpu = "x86-64".into(); + base.features = "+cx16,+sse3".into(); base.plt_by_default = false; // Use high-entropy 64 bit address space for ASLR base.add_pre_link_args( @@ -10,7 +11,7 @@ pub fn target() -> Target { &["-m", "i386pep", "--high-entropy-va"], ); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64", "-Wl,--high-entropy-va"]); - base.max_atomic_width = Some(64); + base.max_atomic_width = Some(128); Target { llvm_target: "x86_64-pc-windows-gnu".into(), From 1c6dda72774d19e79d14b7a51c047414b3126b7b Mon Sep 17 00:00:00 2001 From: Chiragroop Date: Fri, 9 Feb 2024 12:54:38 -0800 Subject: [PATCH 06/62] Possibly removed merge policy --- .../rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs | 3 ++- .../rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs | 3 ++- .../rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs index 9e964d248bf..d16e5905eac 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs @@ -3,6 +3,7 @@ use crate::spec::{base, Cc, LinkerFlavor, Lld, Target}; pub fn target() -> Target { let mut base = base::windows_gnu::opts(); base.cpu = "x86-64".into(); + base.features = "+cx16,+sse3".into(); base.plt_by_default = false; // Use high-entropy 64 bit address space for ASLR base.add_pre_link_args( @@ -10,7 +11,7 @@ pub fn target() -> Target { &["-m", "i386pep", "--high-entropy-va"], ); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64", "-Wl,--high-entropy-va"]); - base.max_atomic_width = Some(64); + base.max_atomic_width = Some(128); base.linker = Some("x86_64-w64-mingw32-gcc".into()); Target { diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs index 1facf9450cd..ea7f7df8120 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs @@ -3,9 +3,10 @@ use crate::spec::{base, Cc, LinkerFlavor, Lld, Target}; pub fn target() -> Target { let mut base = base::windows_gnullvm::opts(); base.cpu = "x86-64".into(); + base.features = "+cx16,+sse3".into(); base.plt_by_default = false; base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); - base.max_atomic_width = Some(64); + base.max_atomic_width = Some(128); base.linker = Some("x86_64-w64-mingw32-clang".into()); Target { diff --git a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs index 3f0702c7ad6..acdf969375e 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs @@ -3,8 +3,9 @@ use crate::spec::{base, Target}; pub fn target() -> Target { let mut base = base::windows_uwp_msvc::opts(); base.cpu = "x86-64".into(); + base.features = "+cx16,+sse3".into(); base.plt_by_default = false; - base.max_atomic_width = Some(64); + base.max_atomic_width = Some(128); Target { llvm_target: "x86_64-pc-windows-msvc".into(), From 0de367748c41af843b4989d4039269ac2092c903 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 10 Feb 2024 18:59:47 -0800 Subject: [PATCH 07/62] Fix typo Co-authored-by: Benjamin Peter <145429680+benjamin-nw@users.noreply.github.com> --- library/std/src/env.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/env.rs b/library/std/src/env.rs index e662b26316b..a46ddfd9491 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -451,7 +451,7 @@ pub struct SplitPaths<'a> { /// /// # Panics /// -/// This will panic on systems where theere is no delimited `PATH` variable, +/// This will panic on systems where there is no delimited `PATH` variable, /// such as UEFI. /// /// # Examples From 376c7b98921b427699b99fc36496cb81ed40c946 Mon Sep 17 00:00:00 2001 From: CKingX Date: Tue, 13 Feb 2024 12:08:30 -0800 Subject: [PATCH 08/62] Added sahf feature to windows targets --- compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs | 2 +- .../rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs | 2 +- .../rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs | 2 +- .../rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs | 2 +- .../rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs index d16e5905eac..d6b44e7cfd4 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs @@ -3,7 +3,7 @@ use crate::spec::{base, Cc, LinkerFlavor, Lld, Target}; pub fn target() -> Target { let mut base = base::windows_gnu::opts(); base.cpu = "x86-64".into(); - base.features = "+cx16,+sse3".into(); + base.features = "+cx16,+sse3,+sahf".into(); base.plt_by_default = false; // Use high-entropy 64 bit address space for ASLR base.add_pre_link_args( diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs index ea7f7df8120..b84a9a5a8a1 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs @@ -3,7 +3,7 @@ use crate::spec::{base, Cc, LinkerFlavor, Lld, Target}; pub fn target() -> Target { let mut base = base::windows_gnullvm::opts(); base.cpu = "x86-64".into(); - base.features = "+cx16,+sse3".into(); + base.features = "+cx16,+sse3,+sahf".into(); base.plt_by_default = false; base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(128); diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs index e72e97db775..51807bdba5d 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs @@ -3,7 +3,7 @@ use crate::spec::{base, SanitizerSet, Target}; pub fn target() -> Target { let mut base = base::windows_msvc::opts(); base.cpu = "x86-64".into(); - base.features = "+cx16,+sse3".into(); + base.features = "+cx16,+sse3,+sahf".into(); base.plt_by_default = false; base.max_atomic_width = Some(128); base.supported_sanitizers = SanitizerSet::ADDRESS; diff --git a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs index ee95c67496d..b37d33601e6 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs @@ -3,7 +3,7 @@ use crate::spec::{base, Cc, LinkerFlavor, Lld, Target}; pub fn target() -> Target { let mut base = base::windows_uwp_gnu::opts(); base.cpu = "x86-64".into(); - base.features = "+cx16,+sse3".into(); + base.features = "+cx16,+sse3,+sahf".into(); base.plt_by_default = false; // Use high-entropy 64 bit address space for ASLR base.add_pre_link_args( diff --git a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs index acdf969375e..fe2b4c8e92d 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs @@ -3,7 +3,7 @@ use crate::spec::{base, Target}; pub fn target() -> Target { let mut base = base::windows_uwp_msvc::opts(); base.cpu = "x86-64".into(); - base.features = "+cx16,+sse3".into(); + base.features = "+cx16,+sse3,+sahf".into(); base.plt_by_default = false; base.max_atomic_width = Some(128); From 2d25c3b3697728ea42eb26f79e6f80907470ca22 Mon Sep 17 00:00:00 2001 From: CKingX Date: Mon, 19 Feb 2024 21:59:13 -0800 Subject: [PATCH 09/62] Updated test to account for added previous features (thanks erikdesjardins!) --- tests/codegen/sse42-implies-crc32.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen/sse42-implies-crc32.rs b/tests/codegen/sse42-implies-crc32.rs index 56079d32a8d..f84d390b4e4 100644 --- a/tests/codegen/sse42-implies-crc32.rs +++ b/tests/codegen/sse42-implies-crc32.rs @@ -12,4 +12,4 @@ pub unsafe fn crc32sse(v: u8) -> u32 { _mm_crc32_u8(out, v) } -// CHECK: attributes #0 {{.*"target-features"="\+sse4.2,\+crc32"}} +// CHECK: attributes #0 {{.*"target-features"=".*\+sse4.2,\+crc32"}} From 2aa8a1d45cd65c0bb8b3ae7cbf78bfb27440b1ec Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 28 Mar 2023 12:10:53 +0200 Subject: [PATCH 10/62] std: make `ReentrantLock` public --- library/std/src/io/stdio.rs | 45 ++- library/std/src/sync/mod.rs | 5 +- library/std/src/sync/reentrant_lock.rs | 320 ++++++++++++++++++ .../sync/{remutex => reentrant_lock}/tests.rs | 34 +- library/std/src/sync/remutex.rs | 178 ---------- 5 files changed, 375 insertions(+), 207 deletions(-) create mode 100644 library/std/src/sync/reentrant_lock.rs rename library/std/src/sync/{remutex => reentrant_lock}/tests.rs (53%) delete mode 100644 library/std/src/sync/remutex.rs diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 261b570dee7..455de6d98ba 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -11,8 +11,9 @@ use crate::fs::File; use crate::io::{ self, BorrowedCursor, BufReader, IoSlice, IoSliceMut, LineWriter, Lines, SpecReadByte, }; +use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sync::atomic::{AtomicBool, Ordering}; -use crate::sync::{Arc, Mutex, MutexGuard, OnceLock, ReentrantMutex, ReentrantMutexGuard}; +use crate::sync::{Arc, Mutex, MutexGuard, OnceLock, ReentrantLock, ReentrantLockGuard}; use crate::sys::stdio; type LocalStream = Arc>>; @@ -545,7 +546,7 @@ pub struct Stdout { // FIXME: this should be LineWriter or BufWriter depending on the state of // stdout (tty or not). Note that if this is not line buffered it // should also flush-on-panic or some form of flush-on-abort. - inner: &'static ReentrantMutex>>, + inner: &'static ReentrantLock>>, } /// A locked reference to the [`Stdout`] handle. @@ -567,10 +568,10 @@ pub struct Stdout { #[must_use = "if unused stdout will immediately unlock"] #[stable(feature = "rust1", since = "1.0.0")] pub struct StdoutLock<'a> { - inner: ReentrantMutexGuard<'a, RefCell>>, + inner: ReentrantLockGuard<'a, RefCell>>, } -static STDOUT: OnceLock>>> = OnceLock::new(); +static STDOUT: OnceLock>>> = OnceLock::new(); /// Constructs a new handle to the standard output of the current process. /// @@ -624,7 +625,7 @@ static STDOUT: OnceLock>>> = OnceLo pub fn stdout() -> Stdout { Stdout { inner: STDOUT - .get_or_init(|| ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))), + .get_or_init(|| ReentrantLock::new(RefCell::new(LineWriter::new(stdout_raw())))), } } @@ -635,7 +636,7 @@ pub fn cleanup() { let mut initialized = false; let stdout = STDOUT.get_or_init(|| { initialized = true; - ReentrantMutex::new(RefCell::new(LineWriter::with_capacity(0, stdout_raw()))) + ReentrantLock::new(RefCell::new(LineWriter::with_capacity(0, stdout_raw()))) }); if !initialized { @@ -678,6 +679,12 @@ impl Stdout { } } +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for Stdout {} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl RefUnwindSafe for Stdout {} + #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Stdout { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -737,6 +744,12 @@ impl Write for &Stdout { } } +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for StdoutLock<'_> {} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl RefUnwindSafe for StdoutLock<'_> {} + #[stable(feature = "rust1", since = "1.0.0")] impl Write for StdoutLock<'_> { fn write(&mut self, buf: &[u8]) -> io::Result { @@ -786,7 +799,7 @@ impl fmt::Debug for StdoutLock<'_> { /// standard library or via raw Windows API calls, will fail. #[stable(feature = "rust1", since = "1.0.0")] pub struct Stderr { - inner: &'static ReentrantMutex>, + inner: &'static ReentrantLock>, } /// A locked reference to the [`Stderr`] handle. @@ -808,7 +821,7 @@ pub struct Stderr { #[must_use = "if unused stderr will immediately unlock"] #[stable(feature = "rust1", since = "1.0.0")] pub struct StderrLock<'a> { - inner: ReentrantMutexGuard<'a, RefCell>, + inner: ReentrantLockGuard<'a, RefCell>, } /// Constructs a new handle to the standard error of the current process. @@ -862,8 +875,8 @@ pub fn stderr() -> Stderr { // Note that unlike `stdout()` we don't use `at_exit` here to register a // destructor. Stderr is not buffered, so there's no need to run a // destructor for flushing the buffer - static INSTANCE: ReentrantMutex> = - ReentrantMutex::new(RefCell::new(stderr_raw())); + static INSTANCE: ReentrantLock> = + ReentrantLock::new(RefCell::new(stderr_raw())); Stderr { inner: &INSTANCE } } @@ -898,6 +911,12 @@ impl Stderr { } } +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for Stderr {} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl RefUnwindSafe for Stderr {} + #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Stderr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -957,6 +976,12 @@ impl Write for &Stderr { } } +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for StderrLock<'_> {} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl RefUnwindSafe for StderrLock<'_> {} + #[stable(feature = "rust1", since = "1.0.0")] impl Write for StderrLock<'_> { fn write(&mut self, buf: &[u8]) -> io::Result { diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index f6a7c0a9f75..31def978b0f 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -180,7 +180,8 @@ pub use self::lazy_lock::LazyLock; #[stable(feature = "once_cell", since = "1.70.0")] pub use self::once_lock::OnceLock; -pub(crate) use self::remutex::{ReentrantMutex, ReentrantMutexGuard}; +#[unstable(feature = "reentrant_lock", issue = "121440")] +pub use self::reentrant_lock::{ReentrantLock, ReentrantLockGuard}; pub mod mpsc; @@ -192,5 +193,5 @@ mod mutex; pub(crate) mod once; mod once_lock; mod poison; -mod remutex; +mod reentrant_lock; mod rwlock; diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs new file mode 100644 index 00000000000..9a44998ebf6 --- /dev/null +++ b/library/std/src/sync/reentrant_lock.rs @@ -0,0 +1,320 @@ +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + +use crate::cell::UnsafeCell; +use crate::fmt; +use crate::ops::Deref; +use crate::panic::{RefUnwindSafe, UnwindSafe}; +use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed}; +use crate::sys::locks as sys; + +/// A re-entrant mutual exclusion lock +/// +/// This lock will block *other* threads waiting for the lock to become +/// available. The thread which has already locked the mutex can lock it +/// multiple times without blocking, preventing a common source of deadlocks. +/// +/// # Examples +/// +/// Allow recursively calling a function needing synchronization from within +/// a callback (this is how [`StdoutLock`](crate::io::StdoutLock) is currently +/// implemented): +/// +/// ``` +/// #![feature(reentrant_lock)] +/// +/// use std::cell::RefCell; +/// use std::sync::ReentrantLock; +/// +/// pub struct Log { +/// data: RefCell, +/// } +/// +/// impl Log { +/// pub fn append(&self, msg: &str) { +/// self.data.borrow_mut().push_str(msg); +/// } +/// } +/// +/// static LOG: ReentrantLock = ReentrantLock::new(Log { data: RefCell::new(String::new()) }); +/// +/// pub fn with_log(f: impl FnOnce(&Log) -> R) -> R { +/// let log = LOG.lock(); +/// f(&*log) +/// } +/// +/// with_log(|log| { +/// log.append("Hello"); +/// with_log(|log| log.append(" there!")); +/// }); +/// ``` +/// +// # Implementation details +// +// The 'owner' field tracks which thread has locked the mutex. +// +// We use current_thread_unique_ptr() as the thread identifier, +// which is just the address of a thread local variable. +// +// If `owner` is set to the identifier of the current thread, +// we assume the mutex is already locked and instead of locking it again, +// we increment `lock_count`. +// +// When unlocking, we decrement `lock_count`, and only unlock the mutex when +// it reaches zero. +// +// `lock_count` is protected by the mutex and only accessed by the thread that has +// locked the mutex, so needs no synchronization. +// +// `owner` can be checked by other threads that want to see if they already +// hold the lock, so needs to be atomic. If it compares equal, we're on the +// same thread that holds the mutex and memory access can use relaxed ordering +// since we're not dealing with multiple threads. If it's not equal, +// synchronization is left to the mutex, making relaxed memory ordering for +// the `owner` field fine in all cases. +#[unstable(feature = "reentrant_lock", issue = "121440")] +pub struct ReentrantLock { + mutex: sys::Mutex, + owner: AtomicUsize, + lock_count: UnsafeCell, + data: T, +} + +#[unstable(feature = "reentrant_lock", issue = "121440")] +unsafe impl Send for ReentrantLock {} +#[unstable(feature = "reentrant_lock", issue = "121440")] +unsafe impl Sync for ReentrantLock {} + +// Because of the `UnsafeCell`, these traits are not implemented automatically +#[unstable(feature = "reentrant_lock", issue = "121440")] +impl UnwindSafe for ReentrantLock {} +#[unstable(feature = "reentrant_lock", issue = "121440")] +impl RefUnwindSafe for ReentrantLock {} + +/// An RAII implementation of a "scoped lock" of a re-entrant lock. When this +/// structure is dropped (falls out of scope), the lock will be unlocked. +/// +/// The data protected by the mutex can be accessed through this guard via its +/// [`Deref`] implementation. +/// +/// This structure is created by the [`lock`](ReentrantLock::lock) method on +/// [`ReentrantLock`]. +/// +/// # Mutability +/// +/// Unlike [`MutexGuard`](super::MutexGuard), `ReentrantLockGuard` does not +/// implement [`DerefMut`](crate::ops::DerefMut), because implementation of +/// the trait would violate Rust’s reference aliasing rules. Use interior +/// mutability (usually [`RefCell`](crate::cell::RefCell)) in order to mutate +/// the guarded data. +#[must_use = "if unused the ReentrantLock will immediately unlock"] +#[unstable(feature = "reentrant_lock", issue = "121440")] +pub struct ReentrantLockGuard<'a, T: ?Sized + 'a> { + lock: &'a ReentrantLock, +} + +#[unstable(feature = "reentrant_lock", issue = "121440")] +impl !Send for ReentrantLockGuard<'_, T> {} + +#[unstable(feature = "reentrant_lock", issue = "121440")] +impl ReentrantLock { + /// Creates a new re-entrant lock in an unlocked state ready for use. + /// + /// # Examples + /// + /// ``` + /// #![feature(reentrant_lock)] + /// use std::sync::ReentrantLock; + /// + /// let lock = ReentrantLock::new(0); + /// ``` + pub const fn new(t: T) -> ReentrantLock { + ReentrantLock { + mutex: sys::Mutex::new(), + owner: AtomicUsize::new(0), + lock_count: UnsafeCell::new(0), + data: t, + } + } + + /// Consumes this lock, returning the underlying data. + /// + /// # Examples + /// + /// ``` + /// #![feature(reentrant_lock)] + /// + /// use std::sync::ReentrantLock; + /// + /// let lock = ReentrantLock::new(0); + /// assert_eq!(lock.into_inner(), 0); + /// ``` + pub fn into_inner(self) -> T { + self.data + } +} + +#[unstable(feature = "reentrant_lock", issue = "121440")] +impl ReentrantLock { + /// Acquires the lock, blocking the current thread until it is able to do + /// so. + /// + /// This function will block the caller until it is available to acquire + /// the lock. Upon returning, the thread is the only thread with the lock + /// held. When the thread calling this method already holds the lock, the + /// call succeeds without blocking. + /// + /// # Examples + /// + /// ``` + /// #![feature(reentrant_lock)] + /// use std::cell::Cell; + /// use std::sync::{Arc, ReentrantLock}; + /// use std::thread; + /// + /// let lock = Arc::new(ReentrantLock::new(Cell::new(0))); + /// let c_lock = Arc::clone(&lock); + /// + /// thread::spawn(move || { + /// c_lock.lock().set(10); + /// }).join().expect("thread::spawn failed"); + /// assert_eq!(lock.lock().get(), 10); + /// ``` + pub fn lock(&self) -> ReentrantLockGuard<'_, T> { + let this_thread = current_thread_unique_ptr(); + // Safety: We only touch lock_count when we own the lock. + unsafe { + if self.owner.load(Relaxed) == this_thread { + self.increment_lock_count().expect("lock count overflow in reentrant mutex"); + } else { + self.mutex.lock(); + self.owner.store(this_thread, Relaxed); + debug_assert_eq!(*self.lock_count.get(), 0); + *self.lock_count.get() = 1; + } + } + ReentrantLockGuard { lock: self } + } + + /// Returns a mutable reference to the underlying data. + /// + /// Since this call borrows the `ReentrantLock` mutably, no actual locking + /// needs to take place -- the mutable borrow statically guarantees no locks + /// exist. + /// + /// # Examples + /// + /// ``` + /// #![feature(reentrant_lock)] + /// use std::sync::ReentrantLock; + /// + /// let mut lock = ReentrantLock::new(0); + /// *lock.get_mut() = 10; + /// assert_eq!(*lock.lock(), 10); + /// ``` + pub fn get_mut(&mut self) -> &mut T { + &mut self.data + } + + /// Attempts to acquire this lock. + /// + /// If the lock could not be acquired at this time, then `None` is returned. + /// Otherwise, an RAII guard is returned. + /// + /// This function does not block. + pub(crate) fn try_lock(&self) -> Option> { + let this_thread = current_thread_unique_ptr(); + // Safety: We only touch lock_count when we own the lock. + unsafe { + if self.owner.load(Relaxed) == this_thread { + self.increment_lock_count()?; + Some(ReentrantLockGuard { lock: self }) + } else if self.mutex.try_lock() { + self.owner.store(this_thread, Relaxed); + debug_assert_eq!(*self.lock_count.get(), 0); + *self.lock_count.get() = 1; + Some(ReentrantLockGuard { lock: self }) + } else { + None + } + } + } + + unsafe fn increment_lock_count(&self) -> Option<()> { + *self.lock_count.get() = (*self.lock_count.get()).checked_add(1)?; + Some(()) + } +} + +#[unstable(feature = "reentrant_lock", issue = "121440")] +impl fmt::Debug for ReentrantLock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("ReentrantLock"); + match self.try_lock() { + Some(v) => d.field("data", &&*v), + None => d.field("data", &format_args!("")), + }; + d.finish_non_exhaustive() + } +} + +#[unstable(feature = "reentrant_lock", issue = "121440")] +impl Default for ReentrantLock { + fn default() -> Self { + Self::new(T::default()) + } +} + +#[unstable(feature = "reentrant_lock", issue = "121440")] +impl From for ReentrantLock { + fn from(t: T) -> Self { + Self::new(t) + } +} + +#[unstable(feature = "reentrant_lock", issue = "121440")] +impl Deref for ReentrantLockGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + &self.lock.data + } +} + +#[unstable(feature = "reentrant_lock", issue = "121440")] +impl fmt::Debug for ReentrantLockGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[unstable(feature = "reentrant_lock", issue = "121440")] +impl fmt::Display for ReentrantLockGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[unstable(feature = "reentrant_lock", issue = "121440")] +impl Drop for ReentrantLockGuard<'_, T> { + #[inline] + fn drop(&mut self) { + // Safety: We own the lock. + unsafe { + *self.lock.lock_count.get() -= 1; + if *self.lock.lock_count.get() == 0 { + self.lock.owner.store(0, Relaxed); + self.lock.mutex.unlock(); + } + } + } +} + +/// Get an address that is unique per running thread. +/// +/// This can be used as a non-null usize-sized ID. +pub(crate) fn current_thread_unique_ptr() -> usize { + // Use a non-drop type to make sure it's still available during thread destruction. + thread_local! { static X: u8 = const { 0 } } + X.with(|x| <*const _>::addr(x)) +} diff --git a/library/std/src/sync/remutex/tests.rs b/library/std/src/sync/reentrant_lock/tests.rs similarity index 53% rename from library/std/src/sync/remutex/tests.rs rename to library/std/src/sync/reentrant_lock/tests.rs index fc553081d42..d4c1d440c61 100644 --- a/library/std/src/sync/remutex/tests.rs +++ b/library/std/src/sync/reentrant_lock/tests.rs @@ -1,17 +1,17 @@ -use super::{ReentrantMutex, ReentrantMutexGuard}; +use super::{ReentrantLock, ReentrantLockGuard}; use crate::cell::RefCell; use crate::sync::Arc; use crate::thread; #[test] fn smoke() { - let m = ReentrantMutex::new(()); + let l = ReentrantLock::new(()); { - let a = m.lock(); + let a = l.lock(); { - let b = m.lock(); + let b = l.lock(); { - let c = m.lock(); + let c = l.lock(); assert_eq!(*c, ()); } assert_eq!(*b, ()); @@ -22,15 +22,15 @@ fn smoke() { #[test] fn is_mutex() { - let m = Arc::new(ReentrantMutex::new(RefCell::new(0))); - let m2 = m.clone(); - let lock = m.lock(); + let l = Arc::new(ReentrantLock::new(RefCell::new(0))); + let l2 = l.clone(); + let lock = l.lock(); let child = thread::spawn(move || { - let lock = m2.lock(); + let lock = l2.lock(); assert_eq!(*lock.borrow(), 4950); }); for i in 0..100 { - let lock = m.lock(); + let lock = l.lock(); *lock.borrow_mut() += i; } drop(lock); @@ -39,20 +39,20 @@ fn is_mutex() { #[test] fn trylock_works() { - let m = Arc::new(ReentrantMutex::new(())); - let m2 = m.clone(); - let _lock = m.try_lock(); - let _lock2 = m.try_lock(); + let l = Arc::new(ReentrantLock::new(())); + let l2 = l.clone(); + let _lock = l.try_lock(); + let _lock2 = l.try_lock(); thread::spawn(move || { - let lock = m2.try_lock(); + let lock = l2.try_lock(); assert!(lock.is_none()); }) .join() .unwrap(); - let _lock3 = m.try_lock(); + let _lock3 = l.try_lock(); } -pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell>); +pub struct Answer<'a>(pub ReentrantLockGuard<'a, RefCell>); impl Drop for Answer<'_> { fn drop(&mut self) { *self.0.borrow_mut() = 42; diff --git a/library/std/src/sync/remutex.rs b/library/std/src/sync/remutex.rs deleted file mode 100644 index 0ced48d10b7..00000000000 --- a/library/std/src/sync/remutex.rs +++ /dev/null @@ -1,178 +0,0 @@ -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests; - -use crate::cell::UnsafeCell; -use crate::ops::Deref; -use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed}; -use crate::sys::locks as sys; - -/// A reentrant mutual exclusion -/// -/// This mutex will block *other* threads waiting for the lock to become -/// available. The thread which has already locked the mutex can lock it -/// multiple times without blocking, preventing a common source of deadlocks. -/// -/// This is used by stdout().lock() and friends. -/// -/// ## Implementation details -/// -/// The 'owner' field tracks which thread has locked the mutex. -/// -/// We use current_thread_unique_ptr() as the thread identifier, -/// which is just the address of a thread local variable. -/// -/// If `owner` is set to the identifier of the current thread, -/// we assume the mutex is already locked and instead of locking it again, -/// we increment `lock_count`. -/// -/// When unlocking, we decrement `lock_count`, and only unlock the mutex when -/// it reaches zero. -/// -/// `lock_count` is protected by the mutex and only accessed by the thread that has -/// locked the mutex, so needs no synchronization. -/// -/// `owner` can be checked by other threads that want to see if they already -/// hold the lock, so needs to be atomic. If it compares equal, we're on the -/// same thread that holds the mutex and memory access can use relaxed ordering -/// since we're not dealing with multiple threads. If it's not equal, -/// synchronization is left to the mutex, making relaxed memory ordering for -/// the `owner` field fine in all cases. -pub struct ReentrantMutex { - mutex: sys::Mutex, - owner: AtomicUsize, - lock_count: UnsafeCell, - data: T, -} - -unsafe impl Send for ReentrantMutex {} -unsafe impl Sync for ReentrantMutex {} - -impl UnwindSafe for ReentrantMutex {} -impl RefUnwindSafe for ReentrantMutex {} - -/// An RAII implementation of a "scoped lock" of a mutex. When this structure is -/// dropped (falls out of scope), the lock will be unlocked. -/// -/// The data protected by the mutex can be accessed through this guard via its -/// Deref implementation. -/// -/// # Mutability -/// -/// Unlike `MutexGuard`, `ReentrantMutexGuard` does not implement `DerefMut`, -/// because implementation of the trait would violate Rust’s reference aliasing -/// rules. Use interior mutability (usually `RefCell`) in order to mutate the -/// guarded data. -#[must_use = "if unused the ReentrantMutex will immediately unlock"] -pub struct ReentrantMutexGuard<'a, T: 'a> { - lock: &'a ReentrantMutex, -} - -impl !Send for ReentrantMutexGuard<'_, T> {} - -impl ReentrantMutex { - /// Creates a new reentrant mutex in an unlocked state. - pub const fn new(t: T) -> ReentrantMutex { - ReentrantMutex { - mutex: sys::Mutex::new(), - owner: AtomicUsize::new(0), - lock_count: UnsafeCell::new(0), - data: t, - } - } - - /// Acquires a mutex, blocking the current thread until it is able to do so. - /// - /// This function will block the caller until it is available to acquire the mutex. - /// Upon returning, the thread is the only thread with the mutex held. When the thread - /// calling this method already holds the lock, the call shall succeed without - /// blocking. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return failure if the mutex would otherwise be - /// acquired. - pub fn lock(&self) -> ReentrantMutexGuard<'_, T> { - let this_thread = current_thread_unique_ptr(); - // Safety: We only touch lock_count when we own the lock. - unsafe { - if self.owner.load(Relaxed) == this_thread { - self.increment_lock_count(); - } else { - self.mutex.lock(); - self.owner.store(this_thread, Relaxed); - debug_assert_eq!(*self.lock_count.get(), 0); - *self.lock_count.get() = 1; - } - } - ReentrantMutexGuard { lock: self } - } - - /// Attempts to acquire this lock. - /// - /// If the lock could not be acquired at this time, then `Err` is returned. - /// Otherwise, an RAII guard is returned. - /// - /// This function does not block. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return failure if the mutex would otherwise be - /// acquired. - pub fn try_lock(&self) -> Option> { - let this_thread = current_thread_unique_ptr(); - // Safety: We only touch lock_count when we own the lock. - unsafe { - if self.owner.load(Relaxed) == this_thread { - self.increment_lock_count(); - Some(ReentrantMutexGuard { lock: self }) - } else if self.mutex.try_lock() { - self.owner.store(this_thread, Relaxed); - debug_assert_eq!(*self.lock_count.get(), 0); - *self.lock_count.get() = 1; - Some(ReentrantMutexGuard { lock: self }) - } else { - None - } - } - } - - unsafe fn increment_lock_count(&self) { - *self.lock_count.get() = (*self.lock_count.get()) - .checked_add(1) - .expect("lock count overflow in reentrant mutex"); - } -} - -impl Deref for ReentrantMutexGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - &self.lock.data - } -} - -impl Drop for ReentrantMutexGuard<'_, T> { - #[inline] - fn drop(&mut self) { - // Safety: We own the lock. - unsafe { - *self.lock.lock_count.get() -= 1; - if *self.lock.lock_count.get() == 0 { - self.lock.owner.store(0, Relaxed); - self.lock.mutex.unlock(); - } - } - } -} - -/// Get an address that is unique per running thread. -/// -/// This can be used as a non-null usize-sized ID. -pub fn current_thread_unique_ptr() -> usize { - // Use a non-drop type to make sure it's still available during thread destruction. - thread_local! { static X: u8 = const { 0 } } - X.with(|x| <*const _>::addr(x)) -} From 2fc091f510ce3b41de0c3c076b587badb687c458 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sat, 24 Feb 2024 13:56:14 -0300 Subject: [PATCH 11/62] Use volatile to make `p_thread_callback` used --- .../src/sys/pal/windows/thread_local_key.rs | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/library/std/src/sys/pal/windows/thread_local_key.rs b/library/std/src/sys/pal/windows/thread_local_key.rs index 5eee4a9667b..81741cc5f78 100644 --- a/library/std/src/sys/pal/windows/thread_local_key.rs +++ b/library/std/src/sys/pal/windows/thread_local_key.rs @@ -1,7 +1,7 @@ use crate::cell::UnsafeCell; use crate::ptr; use crate::sync::atomic::{ - AtomicBool, AtomicPtr, AtomicU32, + AtomicPtr, AtomicU32, Ordering::{AcqRel, Acquire, Relaxed, Release}, }; use crate::sys::c; @@ -9,10 +9,6 @@ use crate::sys::c; #[cfg(test)] mod tests; -/// An optimization hint. The compiler is often smart enough to know if an atomic -/// is never set and can remove dead code based on that fact. -static HAS_DTORS: AtomicBool = AtomicBool::new(false); - // Using a per-thread list avoids the problems in synchronizing global state. #[thread_local] #[cfg(target_thread_local)] @@ -24,12 +20,11 @@ static DESTRUCTORS: crate::cell::RefCell dtors.push((t, dtor)), Err(_) => rtabort!("global allocator may not use TLS"), } - - HAS_DTORS.store(true, Relaxed); } #[inline(never)] // See comment above @@ -130,6 +125,7 @@ impl StaticKey { #[cold] unsafe fn init(&'static self) -> Key { if self.dtor.is_some() { + dtors_used(); let mut pending = c::FALSE; let r = c::InitOnceBeginInitialize(self.once.get(), 0, &mut pending, ptr::null_mut()); assert_eq!(r, c::TRUE); @@ -215,7 +211,6 @@ unsafe fn register_dtor(key: &'static StaticKey) { Err(new) => head = new, } } - HAS_DTORS.store(true, Release); } // ------------------------------------------------------------------------- @@ -281,17 +276,16 @@ unsafe fn register_dtor(key: &'static StaticKey) { // the address of the symbol to ensure it sticks around. #[link_section = ".CRT$XLB"] -#[allow(dead_code, unused_variables)] -#[used] // we don't want LLVM eliminating this symbol for any reason, and -// when the symbol makes it to the linker the linker will take over pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::LPVOID) = on_tls_callback; -#[allow(dead_code, unused_variables)] -unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID) { - if !HAS_DTORS.load(Acquire) { - return; - } +fn dtors_used() { + // we don't want LLVM eliminating p_thread_callback when destructors are used. + // when the symbol makes it to the linker the linker will take over + unsafe { crate::intrinsics::volatile_load(&p_thread_callback) }; +} + +unsafe extern "system" fn on_tls_callback(_h: c::LPVOID, dwReason: c::DWORD, _pv: c::LPVOID) { if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH { #[cfg(not(target_thread_local))] run_dtors(); @@ -313,7 +307,7 @@ unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: unsafe fn reference_tls_used() {} } -#[allow(dead_code)] // actually called below +#[cfg(not(target_thread_local))] unsafe fn run_dtors() { for _ in 0..5 { let mut any_run = false; From ad4c4f4654bf78bb51638e58a9c418afcdd6ee15 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sat, 24 Feb 2024 14:09:22 -0300 Subject: [PATCH 12/62] Remove _tls_used trickery unless needed --- library/std/src/sys/pal/windows/thread_local_key.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/library/std/src/sys/pal/windows/thread_local_key.rs b/library/std/src/sys/pal/windows/thread_local_key.rs index 81741cc5f78..1d9cb316a45 100644 --- a/library/std/src/sys/pal/windows/thread_local_key.rs +++ b/library/std/src/sys/pal/windows/thread_local_key.rs @@ -295,16 +295,13 @@ unsafe extern "system" fn on_tls_callback(_h: c::LPVOID, dwReason: c::DWORD, _pv // See comments above for what this is doing. Note that we don't need this // trickery on GNU windows, just on MSVC. - reference_tls_used(); - #[cfg(target_env = "msvc")] - unsafe fn reference_tls_used() { + #[cfg(all(target_env = "msvc", not(target_thread_local)))] + { extern "C" { static _tls_used: u8; } crate::intrinsics::volatile_load(&_tls_used); } - #[cfg(not(target_env = "msvc"))] - unsafe fn reference_tls_used() {} } #[cfg(not(target_thread_local))] From 205319d962b3467e44927150faad462d7313fd64 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 20 Feb 2024 05:19:11 +0100 Subject: [PATCH 13/62] Skip unnecessary comparison with half-open ranges --- compiler/rustc_middle/src/thir.rs | 16 ------ .../rustc_mir_build/src/build/matches/test.rs | 54 +++++++++++-------- 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index e1e5d68148f..7ffa88afa15 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -958,22 +958,6 @@ impl<'tcx> PatRangeBoundary<'tcx> { Self::NegInfinity | Self::PosInfinity => None, } } - #[inline] - pub fn to_const(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> mir::Const<'tcx> { - match self { - Self::Finite(value) => value, - Self::NegInfinity => { - // Unwrap is ok because the type is known to be numeric. - let c = ty.numeric_min_val(tcx).unwrap(); - mir::Const::from_ty_const(c, tcx) - } - Self::PosInfinity => { - // Unwrap is ok because the type is known to be numeric. - let c = ty.numeric_max_val(tcx).unwrap(); - mir::Const::from_ty_const(c, tcx) - } - } - } pub fn eval_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u128 { match self { Self::Finite(value) => value.eval_bits(tcx, param_env), diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 1c97de58863..1b6994966d1 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -291,33 +291,41 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } TestKind::Range(ref range) => { - let lower_bound_success = self.cfg.start_new_block(); - - // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons. - // FIXME: skip useless comparison when the range is half-open. - let lo = range.lo.to_const(range.ty, self.tcx); - let hi = range.hi.to_const(range.ty, self.tcx); - let lo = self.literal_operand(test.span, lo); - let hi = self.literal_operand(test.span, hi); - let val = Operand::Copy(place); - let [success, fail] = *target_blocks else { bug!("`TestKind::Range` should have two target blocks"); }; - self.compare( - block, - lower_bound_success, - fail, - source_info, - BinOp::Le, - lo, - val.clone(), - ); - let op = match range.end { - RangeEnd::Included => BinOp::Le, - RangeEnd::Excluded => BinOp::Lt, + // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons. + let val = Operand::Copy(place); + + let intermediate_block = if !range.lo.is_finite() { + block + } else if !range.hi.is_finite() { + success + } else { + self.cfg.start_new_block() }; - self.compare(lower_bound_success, success, fail, source_info, op, val, hi); + + if let Some(lo) = range.lo.as_finite() { + let lo = self.literal_operand(test.span, lo); + self.compare( + block, + intermediate_block, + fail, + source_info, + BinOp::Le, + lo, + val.clone(), + ); + }; + + if let Some(hi) = range.hi.as_finite() { + let hi = self.literal_operand(test.span, hi); + let op = match range.end { + RangeEnd::Included => BinOp::Le, + RangeEnd::Excluded => BinOp::Lt, + }; + self.compare(intermediate_block, success, fail, source_info, op, val, hi); + } } TestKind::Len { len, op } => { From 7c6960e289c627bc5533c03769ff45b03a141be9 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 27 Feb 2024 17:23:06 +0100 Subject: [PATCH 14/62] Document invariant in `thir::PatRange` --- compiler/rustc_middle/src/thir.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 7ffa88afa15..ee11a75529b 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -815,7 +815,9 @@ pub enum PatKind<'tcx> { /// The boundaries must be of the same type and that type must be numeric. #[derive(Clone, Debug, PartialEq, HashStable, TypeVisitable)] pub struct PatRange<'tcx> { + /// Must not be `PosInfinity`. pub lo: PatRangeBoundary<'tcx>, + /// Must not be `NegInfinity`. pub hi: PatRangeBoundary<'tcx>, #[type_visitable(ignore)] pub end: RangeEnd, From 5df9593f1ad13eac4ca1da281332c8147937cd34 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 27 Feb 2024 17:39:32 +0100 Subject: [PATCH 15/62] Prevent inclusion of whitespace character after macro_rules ident --- src/librustdoc/html/highlight.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 1cdc792a819..aa5998876d9 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -136,6 +136,7 @@ fn can_merge(class1: Option, class2: Option, text: &str) -> bool { match (class1, class2) { (Some(c1), Some(c2)) => c1.is_equal_to(c2), (Some(Class::Ident(_)), None) | (None, Some(Class::Ident(_))) => true, + (Some(Class::Macro(_)), _) => false, (Some(_), None) | (None, Some(_)) => text.trim().is_empty(), (None, None) => true, } From 5d59d0c7d725fe259b652bb9e9d17e6cbe1919af Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Tue, 23 Jan 2024 19:00:10 -0700 Subject: [PATCH 16/62] have `String` use `SliceIndex` impls from `str` --- library/alloc/src/lib.rs | 1 + library/alloc/src/string.rs | 104 ++++++------------------------------ 2 files changed, 16 insertions(+), 89 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 45e93feb6c5..28695ade5bf 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -152,6 +152,7 @@ #![feature(set_ptr_value)] #![feature(sized_type_properties)] #![feature(slice_from_ptr_range)] +#![feature(slice_index_methods)] #![feature(slice_ptr_get)] #![feature(slice_ptr_len)] #![feature(slice_range)] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 6dadbc8e364..25991a91498 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -54,7 +54,7 @@ use core::ops::Add; use core::ops::AddAssign; #[cfg(not(no_global_oom_handling))] use core::ops::Bound::{Excluded, Included, Unbounded}; -use core::ops::{self, Index, IndexMut, Range, RangeBounds}; +use core::ops::{self, Range, RangeBounds}; use core::ptr; use core::slice; use core::str::pattern::Pattern; @@ -2433,100 +2433,26 @@ impl AddAssign<&str> for String { } #[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index> for String { - type Output = str; +impl ops::Index for String +where + I: slice::SliceIndex, +{ + type Output = I::Output; #[inline] - fn index(&self, index: ops::Range) -> &str { - &self[..][index] + fn index(&self, index: I) -> &I::Output { + index.index(self.as_str()) } } + #[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index> for String { - type Output = str; - +impl ops::IndexMut for String +where + I: slice::SliceIndex, +{ #[inline] - fn index(&self, index: ops::RangeTo) -> &str { - &self[..][index] - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index> for String { - type Output = str; - - #[inline] - fn index(&self, index: ops::RangeFrom) -> &str { - &self[..][index] - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index for String { - type Output = str; - - #[inline] - fn index(&self, _index: ops::RangeFull) -> &str { - unsafe { str::from_utf8_unchecked(&self.vec) } - } -} -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl ops::Index> for String { - type Output = str; - - #[inline] - fn index(&self, index: ops::RangeInclusive) -> &str { - Index::index(&**self, index) - } -} -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl ops::Index> for String { - type Output = str; - - #[inline] - fn index(&self, index: ops::RangeToInclusive) -> &str { - Index::index(&**self, index) - } -} - -#[stable(feature = "derefmut_for_string", since = "1.3.0")] -impl ops::IndexMut> for String { - #[inline] - fn index_mut(&mut self, index: ops::Range) -> &mut str { - &mut self[..][index] - } -} -#[stable(feature = "derefmut_for_string", since = "1.3.0")] -impl ops::IndexMut> for String { - #[inline] - fn index_mut(&mut self, index: ops::RangeTo) -> &mut str { - &mut self[..][index] - } -} -#[stable(feature = "derefmut_for_string", since = "1.3.0")] -impl ops::IndexMut> for String { - #[inline] - fn index_mut(&mut self, index: ops::RangeFrom) -> &mut str { - &mut self[..][index] - } -} -#[stable(feature = "derefmut_for_string", since = "1.3.0")] -impl ops::IndexMut for String { - #[inline] - fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str { - unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) } - } -} -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl ops::IndexMut> for String { - #[inline] - fn index_mut(&mut self, index: ops::RangeInclusive) -> &mut str { - IndexMut::index_mut(&mut **self, index) - } -} -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl ops::IndexMut> for String { - #[inline] - fn index_mut(&mut self, index: ops::RangeToInclusive) -> &mut str { - IndexMut::index_mut(&mut **self, index) + fn index_mut(&mut self, index: I) -> &mut I::Output { + index.index_mut(self.as_mut_str()) } } From a6727bad88c45ef05617731aaf42a370e7dcf5e3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Feb 2024 23:43:10 +0000 Subject: [PATCH 17/62] Support `{async closure}: Fn` in new solver --- .../src/solve/assembly/structural_traits.rs | 74 ++++++++++++++++++- tests/ui/async-await/async-closures/is-fn.rs | 24 ++++++ tests/ui/async-await/async-closures/once.rs | 6 +- 3 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 tests/ui/async-await/async-closures/is-fn.rs diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index 3b902dd80f5..f65f5f9c070 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -281,7 +281,79 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( } // Coroutine-closures don't implement `Fn` traits the normal way. - ty::CoroutineClosure(..) => Err(NoSolution), + // Instead, they always implement `FnOnce`, but only implement + // `FnMut`/`Fn` if they capture no upvars, since those may borrow + // from the closure. + ty::CoroutineClosure(def_id, args) => { + let args = args.as_coroutine_closure(); + let kind_ty = args.kind_ty(); + let sig = args.coroutine_closure_sig().skip_binder(); + + let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() { + if !closure_kind.extends(goal_kind) { + return Err(NoSolution); + } + + // If `Fn`/`FnMut`, we only implement this goal if we + // have no captures. + let no_borrows = match args.tupled_upvars_ty().kind() { + ty::Tuple(tys) => tys.is_empty(), + ty::Error(_) => false, + _ => bug!("tuple_fields called on non-tuple"), + }; + if closure_kind != ty::ClosureKind::FnOnce && !no_borrows { + return Err(NoSolution); + } + + sig.to_coroutine_given_kind_and_upvars( + tcx, + args.parent_args(), + tcx.coroutine_for_closure(def_id), + goal_kind, + // No captures by ref, so this doesn't matter. + tcx.lifetimes.re_static, + args.tupled_upvars_ty(), + args.coroutine_captures_by_ref_ty(), + ) + } else { + // Closure kind is not yet determined, so we return ambiguity unless + // the expected kind is `FnOnce` as that is always implemented. + if goal_kind != ty::ClosureKind::FnOnce { + return Ok(None); + } + + let async_fn_kind_trait_def_id = + tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); + let upvars_projection_def_id = tcx + .associated_items(async_fn_kind_trait_def_id) + .filter_by_name_unhygienic(sym::Upvars) + .next() + .unwrap() + .def_id; + let tupled_upvars_ty = Ty::new_projection( + tcx, + upvars_projection_def_id, + [ + ty::GenericArg::from(kind_ty), + Ty::from_closure_kind(tcx, goal_kind).into(), + // No captures by ref, so this doesn't matter. + tcx.lifetimes.re_static.into(), + sig.tupled_inputs_ty.into(), + args.tupled_upvars_ty().into(), + args.coroutine_captures_by_ref_ty().into(), + ], + ); + sig.to_coroutine( + tcx, + args.parent_args(), + Ty::from_closure_kind(tcx, goal_kind), + tcx.coroutine_for_closure(def_id), + tupled_upvars_ty, + ) + }; + + Ok(Some(args.coroutine_closure_sig().rebind((sig.tupled_inputs_ty, coroutine_ty)))) + } ty::Bool | ty::Char diff --git a/tests/ui/async-await/async-closures/is-fn.rs b/tests/ui/async-await/async-closures/is-fn.rs new file mode 100644 index 00000000000..64cc28e425f --- /dev/null +++ b/tests/ui/async-await/async-closures/is-fn.rs @@ -0,0 +1,24 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ build-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +#![feature(async_closure)] + +use std::future::Future; + +extern crate block_on; + +// Check that closures that don't capture any state may implement `Fn`. + +fn main() { + block_on::block_on(async { + async fn call_once(x: impl FnOnce(&'static str) -> F) -> F::Output { + x("hello, world").await + } + call_once(async |x: &'static str| { + println!("hello, {x}"); + }).await + }); +} diff --git a/tests/ui/async-await/async-closures/once.rs b/tests/ui/async-await/async-closures/once.rs index 55e4cedb267..761df3de444 100644 --- a/tests/ui/async-await/async-closures/once.rs +++ b/tests/ui/async-await/async-closures/once.rs @@ -1,6 +1,8 @@ //@ aux-build:block-on.rs //@ edition:2021 //@ build-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver #![feature(async_closure)] @@ -8,11 +10,11 @@ use std::future::Future; extern crate block_on; -struct NoCopy; +// Check that async closures always implement `FnOnce` fn main() { block_on::block_on(async { - async fn call_once(x: impl Fn(&'static str) -> F) -> F::Output { + async fn call_once(x: impl FnOnce(&'static str) -> F) -> F::Output { x("hello, world").await } call_once(async |x: &'static str| { From 118730b9a3895331c218bfd50457aabcd37b589b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 27 Feb 2024 15:43:42 +0000 Subject: [PATCH 18/62] Add a couple helpers, make return types less confusing --- .../src/solve/assembly/structural_traits.rs | 189 +++++++++++------- .../src/solve/normalizes_to/mod.rs | 81 ++++---- .../src/solve/trait_goals.rs | 18 +- 3 files changed, 172 insertions(+), 116 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index f65f5f9c070..af533d8db71 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -305,15 +305,14 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( return Err(NoSolution); } - sig.to_coroutine_given_kind_and_upvars( + coroutine_closure_to_certain_coroutine( tcx, - args.parent_args(), - tcx.coroutine_for_closure(def_id), goal_kind, // No captures by ref, so this doesn't matter. tcx.lifetimes.re_static, - args.tupled_upvars_ty(), - args.coroutine_captures_by_ref_ty(), + def_id, + args, + sig, ) } else { // Closure kind is not yet determined, so we return ambiguity unless @@ -322,33 +321,13 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( return Ok(None); } - let async_fn_kind_trait_def_id = - tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); - let upvars_projection_def_id = tcx - .associated_items(async_fn_kind_trait_def_id) - .filter_by_name_unhygienic(sym::Upvars) - .next() - .unwrap() - .def_id; - let tupled_upvars_ty = Ty::new_projection( + coroutine_closure_to_ambiguous_coroutine( tcx, - upvars_projection_def_id, - [ - ty::GenericArg::from(kind_ty), - Ty::from_closure_kind(tcx, goal_kind).into(), - // No captures by ref, so this doesn't matter. - tcx.lifetimes.re_static.into(), - sig.tupled_inputs_ty.into(), - args.tupled_upvars_ty().into(), - args.coroutine_captures_by_ref_ty().into(), - ], - ); - sig.to_coroutine( - tcx, - args.parent_args(), - Ty::from_closure_kind(tcx, goal_kind), - tcx.coroutine_for_closure(def_id), - tupled_upvars_ty, + goal_kind, // No captures by ref, so this doesn't matter. + tcx.lifetimes.re_static, + def_id, + args, + sig, ) }; @@ -385,6 +364,19 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( } } +/// Relevant types for an async callable, including its inputs, output, +/// and the return type you get from awaiting the output. +#[derive(Copy, Clone, Debug, TypeVisitable, TypeFoldable)] +pub(in crate::solve) struct AsyncCallableRelevantTypes<'tcx> { + pub tupled_inputs_ty: Ty<'tcx>, + /// Type returned by calling the closure + /// i.e. `f()`. + pub output_coroutine_ty: Ty<'tcx>, + /// Type returned by `await`ing the output + /// i.e. `f().await`. + pub coroutine_return_ty: Ty<'tcx>, +} + // Returns a binder of the tupled inputs types, output type, and coroutine type // from a builtin coroutine-closure type. If we don't yet know the closure kind of // the coroutine-closure, emit an additional trait predicate for `AsyncFnKindHelper` @@ -395,8 +387,10 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc self_ty: Ty<'tcx>, goal_kind: ty::ClosureKind, env_region: ty::Region<'tcx>, -) -> Result<(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Vec>), NoSolution> -{ +) -> Result< + (ty::Binder<'tcx, AsyncCallableRelevantTypes<'tcx>>, Vec>), + NoSolution, +> { match *self_ty.kind() { ty::CoroutineClosure(def_id, args) => { let args = args.as_coroutine_closure(); @@ -407,24 +401,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc if !closure_kind.extends(goal_kind) { return Err(NoSolution); } - sig.to_coroutine_given_kind_and_upvars( - tcx, - args.parent_args(), - tcx.coroutine_for_closure(def_id), - goal_kind, - env_region, - args.tupled_upvars_ty(), - args.coroutine_captures_by_ref_ty(), + + coroutine_closure_to_certain_coroutine( + tcx, goal_kind, env_region, def_id, args, sig, ) } else { - let async_fn_kind_trait_def_id = - tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); - let upvars_projection_def_id = tcx - .associated_items(async_fn_kind_trait_def_id) - .filter_by_name_unhygienic(sym::Upvars) - .next() - .unwrap() - .def_id; // When we don't know the closure kind (and therefore also the closure's upvars, // which are computed at the same time), we must delay the computation of the // generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait @@ -435,38 +416,23 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc nested.push( ty::TraitRef::new( tcx, - async_fn_kind_trait_def_id, + tcx.require_lang_item(LangItem::AsyncFnKindHelper, None), [kind_ty, Ty::from_closure_kind(tcx, goal_kind)], ) .to_predicate(tcx), ); - let tupled_upvars_ty = Ty::new_projection( - tcx, - upvars_projection_def_id, - [ - ty::GenericArg::from(kind_ty), - Ty::from_closure_kind(tcx, goal_kind).into(), - env_region.into(), - sig.tupled_inputs_ty.into(), - args.tupled_upvars_ty().into(), - args.coroutine_captures_by_ref_ty().into(), - ], - ); - sig.to_coroutine( - tcx, - args.parent_args(), - Ty::from_closure_kind(tcx, goal_kind), - tcx.coroutine_for_closure(def_id), - tupled_upvars_ty, + + coroutine_closure_to_ambiguous_coroutine( + tcx, goal_kind, env_region, def_id, args, sig, ) }; Ok(( - args.coroutine_closure_sig().rebind(( - sig.tupled_inputs_ty, - sig.return_ty, - coroutine_ty, - )), + args.coroutine_closure_sig().rebind(AsyncCallableRelevantTypes { + tupled_inputs_ty: sig.tupled_inputs_ty, + output_coroutine_ty: coroutine_ty, + coroutine_return_ty: sig.return_ty, + }), nested, )) } @@ -490,7 +456,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc .def_id; let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]); Ok(( - bound_sig.rebind((Ty::new_tup(tcx, sig.inputs()), sig.output(), future_output_ty)), + bound_sig.rebind(AsyncCallableRelevantTypes { + tupled_inputs_ty: Ty::new_tup(tcx, sig.inputs()), + output_coroutine_ty: sig.output(), + coroutine_return_ty: future_output_ty, + }), nested, )) } @@ -541,7 +511,14 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc .unwrap() .def_id; let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]); - Ok((bound_sig.rebind((sig.inputs()[0], sig.output(), future_output_ty)), nested)) + Ok(( + bound_sig.rebind(AsyncCallableRelevantTypes { + tupled_inputs_ty: sig.inputs()[0], + output_coroutine_ty: sig.output(), + coroutine_return_ty: future_output_ty, + }), + nested, + )) } ty::Bool @@ -574,6 +551,68 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc } } +/// Given a coroutine-closure, project to its returned coroutine when we are *certain* +/// that the closure's kind is compatible with the goal. +fn coroutine_closure_to_certain_coroutine<'tcx>( + tcx: TyCtxt<'tcx>, + goal_kind: ty::ClosureKind, + goal_region: ty::Region<'tcx>, + def_id: DefId, + args: ty::CoroutineClosureArgs<'tcx>, + sig: ty::CoroutineClosureSignature<'tcx>, +) -> Ty<'tcx> { + sig.to_coroutine_given_kind_and_upvars( + tcx, + args.parent_args(), + tcx.coroutine_for_closure(def_id), + goal_kind, + goal_region, + args.tupled_upvars_ty(), + args.coroutine_captures_by_ref_ty(), + ) +} + +/// Given a coroutine-closure, project to its returned coroutine when we are *not certain* +/// that the closure's kind is compatible with the goal, and therefore also don't know +/// yet what the closure's upvars are. +/// +/// Note that we do not also push a `AsyncFnKindHelper` goal here. +fn coroutine_closure_to_ambiguous_coroutine<'tcx>( + tcx: TyCtxt<'tcx>, + goal_kind: ty::ClosureKind, + goal_region: ty::Region<'tcx>, + def_id: DefId, + args: ty::CoroutineClosureArgs<'tcx>, + sig: ty::CoroutineClosureSignature<'tcx>, +) -> Ty<'tcx> { + let async_fn_kind_trait_def_id = tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); + let upvars_projection_def_id = tcx + .associated_items(async_fn_kind_trait_def_id) + .filter_by_name_unhygienic(sym::Upvars) + .next() + .unwrap() + .def_id; + let tupled_upvars_ty = Ty::new_projection( + tcx, + upvars_projection_def_id, + [ + ty::GenericArg::from(args.kind_ty()), + Ty::from_closure_kind(tcx, goal_kind).into(), + goal_region.into(), + sig.tupled_inputs_ty.into(), + args.tupled_upvars_ty().into(), + args.coroutine_captures_by_ref_ty().into(), + ], + ); + sig.to_coroutine( + tcx, + args.parent_args(), + Ty::from_closure_kind(tcx, goal_kind), + tcx.coroutine_for_closure(def_id), + tupled_upvars_ty, + ) +} + /// Assemble a list of predicates that would be present on a theoretical /// user impl for an object type. These predicates must be checked any time /// we assemble a built-in object candidate for an object type, since they diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index aa8cc3667cd..3aba5c85abc 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -1,5 +1,6 @@ use crate::traits::{check_args_compatible, specialization_graph}; +use super::assembly::structural_traits::AsyncCallableRelevantTypes; use super::assembly::{self, structural_traits, Candidate}; use super::{EvalCtxt, GoalSource}; use rustc_hir::def::DefKind; @@ -392,46 +393,56 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { goal_kind, env_region, )?; - let output_is_sized_pred = - tupled_inputs_and_output_and_coroutine.map_bound(|(_, output, _)| { - ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output]) - }); + let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound( + |AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| { + ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output_ty]) + }, + ); let pred = tupled_inputs_and_output_and_coroutine - .map_bound(|(inputs, output, coroutine)| { - let (projection_ty, term) = match tcx.item_name(goal.predicate.def_id()) { - sym::CallOnceFuture => ( - ty::AliasTy::new( - tcx, - goal.predicate.def_id(), - [goal.predicate.self_ty(), inputs], + .map_bound( + |AsyncCallableRelevantTypes { + tupled_inputs_ty, + output_coroutine_ty, + coroutine_return_ty, + }| { + let (projection_ty, term) = match tcx.item_name(goal.predicate.def_id()) { + sym::CallOnceFuture => ( + ty::AliasTy::new( + tcx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), tupled_inputs_ty], + ), + output_coroutine_ty.into(), ), - coroutine.into(), - ), - sym::CallMutFuture | sym::CallFuture => ( - ty::AliasTy::new( - tcx, - goal.predicate.def_id(), - [ - ty::GenericArg::from(goal.predicate.self_ty()), - inputs.into(), - env_region.into(), - ], + sym::CallMutFuture | sym::CallFuture => ( + ty::AliasTy::new( + tcx, + goal.predicate.def_id(), + [ + ty::GenericArg::from(goal.predicate.self_ty()), + tupled_inputs_ty.into(), + env_region.into(), + ], + ), + output_coroutine_ty.into(), ), - coroutine.into(), - ), - sym::Output => ( - ty::AliasTy::new( - tcx, - goal.predicate.def_id(), - [ty::GenericArg::from(goal.predicate.self_ty()), inputs.into()], + sym::Output => ( + ty::AliasTy::new( + tcx, + goal.predicate.def_id(), + [ + ty::GenericArg::from(goal.predicate.self_ty()), + tupled_inputs_ty.into(), + ], + ), + coroutine_return_ty.into(), ), - output.into(), - ), - name => bug!("no such associated type: {name}"), - }; - ty::ProjectionPredicate { projection_ty, term } - }) + name => bug!("no such associated type: {name}"), + }; + ty::ProjectionPredicate { projection_ty, term } + }, + ) .to_predicate(tcx); // A built-in `AsyncFn` impl only holds if the output is sized. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 73bf66f6689..eba6ba3f7b0 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -2,6 +2,7 @@ use crate::traits::supertrait_def_ids; +use super::assembly::structural_traits::AsyncCallableRelevantTypes; use super::assembly::{self, structural_traits, Candidate}; use super::{EvalCtxt, GoalSource, SolverMode}; use rustc_data_structures::fx::FxIndexSet; @@ -327,14 +328,19 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // This region doesn't matter because we're throwing away the coroutine type tcx.lifetimes.re_static, )?; - let output_is_sized_pred = - tupled_inputs_and_output_and_coroutine.map_bound(|(_, output, _)| { - ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output]) - }); + let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound( + |AsyncCallableRelevantTypes { output_coroutine_ty, .. }| { + ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output_coroutine_ty]) + }, + ); let pred = tupled_inputs_and_output_and_coroutine - .map_bound(|(inputs, _, _)| { - ty::TraitRef::new(tcx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) + .map_bound(|AsyncCallableRelevantTypes { tupled_inputs_ty, .. }| { + ty::TraitRef::new( + tcx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), tupled_inputs_ty], + ) }) .to_predicate(tcx); // A built-in `AsyncFn` impl only holds if the output is sized. From 4c0016a01f15d207ae1a430fd601098289654bb9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Feb 2024 22:49:20 +0000 Subject: [PATCH 19/62] Don't emit higher-ranked Future obligations when confirm async Fn goals --- .../src/traits/select/confirmation.rs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index d316149731e..b8733bab27b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -923,14 +923,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { [self_ty, Ty::new_tup(tcx, sig.inputs())], ) }); + // We must additionally check that the return type impls `Future`. + + // FIXME(async_closures): Investigate this before stabilization. + // We instantiate this binder eagerly because the `confirm_future_candidate` + // method doesn't support higher-ranked futures, which the `AsyncFn` + // traits expressly allow the user to write. To fix this correctly, + // we'd need to instantiate trait bounds before we get to selection, + // like the new trait solver does. let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); + let placeholder_output_ty = self.infcx.enter_forall_and_leak_universe(sig.output()); nested.push(obligation.with( tcx, - sig.map_bound(|sig| { - ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]) - }), + ty::TraitRef::new(tcx, future_trait_def_id, [placeholder_output_ty]), )); + (trait_ref, Ty::from_closure_kind(tcx, ty::ClosureKind::Fn)) } ty::Closure(_, args) => { @@ -943,14 +951,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { [self_ty, sig.inputs()[0]], ) }); + // We must additionally check that the return type impls `Future`. + // See FIXME in last branch for why we instantiate the binder eagerly. let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); + let placeholder_output_ty = self.infcx.enter_forall_and_leak_universe(sig.output()); nested.push(obligation.with( tcx, - sig.map_bound(|sig| { - ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]) - }), + ty::TraitRef::new(tcx, future_trait_def_id, [placeholder_output_ty]), )); + (trait_ref, args.kind_ty()) } _ => bug!("expected callable type for AsyncFn candidate"), From 2252ff730285decb81837db51a922608b2f036cc Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Feb 2024 22:53:45 +0000 Subject: [PATCH 20/62] Also support `fnptr(): async Fn` in codegen --- compiler/rustc_mir_transform/src/shim.rs | 6 +++- .../async-fn/higher-ranked-async-fn.rs | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/ui/async-await/async-fn/higher-ranked-async-fn.rs diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 75613a2c555..733e2f93b25 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -37,7 +37,11 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' } ty::InstanceDef::FnPtrShim(def_id, ty) => { let trait_ = tcx.trait_of_item(def_id).unwrap(); - let adjustment = match tcx.fn_trait_kind_from_def_id(trait_) { + // Supports `Fn` or `async Fn` traits. + let adjustment = match tcx + .fn_trait_kind_from_def_id(trait_) + .or_else(|| tcx.async_fn_trait_kind_from_def_id(trait_)) + { Some(ty::ClosureKind::FnOnce) => Adjustment::Identity, Some(ty::ClosureKind::Fn) => Adjustment::Deref { source: DerefSource::ImmRef }, Some(ty::ClosureKind::FnMut) => Adjustment::Deref { source: DerefSource::MutRef }, diff --git a/tests/ui/async-await/async-fn/higher-ranked-async-fn.rs b/tests/ui/async-await/async-fn/higher-ranked-async-fn.rs new file mode 100644 index 00000000000..5680c057737 --- /dev/null +++ b/tests/ui/async-await/async-fn/higher-ranked-async-fn.rs @@ -0,0 +1,31 @@ +//@ aux-build:block-on.rs +//@ edition:2018 +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ build-pass (since it ICEs during mono) + +#![feature(async_closure)] + +extern crate block_on; + +use std::future::Future; + +async fn f(arg: &i32) {} + +async fn func(f: F) +where + F: async for<'a> Fn(&'a i32), +{ + let x: i32 = 0; + f(&x).await; +} + +fn main() { + block_on::block_on(async { + // Function + func(f).await; + + // Regular closure (doesn't capture) + func(|x: &i32| async {}); + }); +} From c8e3f35eb631c35bc7aabf358b7ca5c2a7fbfd1e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Feb 2024 23:06:38 +0000 Subject: [PATCH 21/62] Flesh out a few more tests --- .../async-closures/async-fn-once-for-async-fn.rs | 10 +++++++--- .../async-await/async-closures/move-is-async-fn.rs | 10 +++++++--- tests/ui/async-await/async-closures/refd.rs | 13 ++++++++++--- tests/ui/async-await/async-fn/project.rs | 12 ++++++++++++ 4 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 tests/ui/async-await/async-fn/project.rs diff --git a/tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs b/tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs index 9c3b458cd3a..be436465315 100644 --- a/tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs +++ b/tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs @@ -8,11 +8,15 @@ extern crate block_on; fn main() { block_on::block_on(async { - let x = async || {}; - async fn needs_async_fn_once(x: impl async FnOnce()) { x().await; } - needs_async_fn_once(x).await; + + needs_async_fn_once(async || {}).await; + + needs_async_fn_once(|| async {}).await; + + async fn foo() {} + needs_async_fn_once(foo).await; }); } diff --git a/tests/ui/async-await/async-closures/move-is-async-fn.rs b/tests/ui/async-await/async-closures/move-is-async-fn.rs index 0ccd137266d..79e2298f609 100644 --- a/tests/ui/async-await/async-closures/move-is-async-fn.rs +++ b/tests/ui/async-await/async-closures/move-is-async-fn.rs @@ -2,7 +2,7 @@ //@ edition:2021 //@ build-pass -#![feature(async_closure)] +#![feature(async_closure, async_fn_traits)] extern crate block_on; @@ -15,7 +15,11 @@ fn main() { c().await; c().await; - fn is_static(_: T) {} - is_static(c); + fn is_static(_: &T) {} + is_static(&c); + + // Check that `<{async fn} as AsyncFnOnce>::CallOnceFuture` owns its captures. + fn call_once(f: F) -> F::CallOnceFuture { f() } + is_static(&call_once(c)); }); } diff --git a/tests/ui/async-await/async-closures/refd.rs b/tests/ui/async-await/async-closures/refd.rs index 1d9bc1a601b..0b8d3d7aff5 100644 --- a/tests/ui/async-await/async-closures/refd.rs +++ b/tests/ui/async-await/async-closures/refd.rs @@ -2,8 +2,6 @@ //@ edition:2021 //@ build-pass -// check that `&{async-closure}` implements `AsyncFn`. - #![feature(async_closure)] extern crate block_on; @@ -13,6 +11,15 @@ struct NoCopy; fn main() { block_on::block_on(async { async fn call_once(x: impl async Fn()) { x().await } - call_once(&async || {}).await + + // check that `&{async-closure}` implements `async Fn`. + call_once(&async || {}).await; + + // check that `&{closure}` implements `async Fn`. + call_once(&|| async {}).await; + + // check that `&fndef` implements `async Fn`. + async fn foo() {} + call_once(&foo).await; }); } diff --git a/tests/ui/async-await/async-fn/project.rs b/tests/ui/async-await/async-fn/project.rs new file mode 100644 index 00000000000..302564bb951 --- /dev/null +++ b/tests/ui/async-await/async-fn/project.rs @@ -0,0 +1,12 @@ +//@ edition:2018 +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +#![feature(async_closure, unboxed_closures, async_fn_traits)] + +fn project>(_: F) -> Option { None } + +fn main() { + let x: Option = project(|| async { 1i32 }); +} From 8f7b921f521c6440ceb98127fb3c00b7fee67b2c Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Tue, 27 Feb 2024 11:42:02 -0800 Subject: [PATCH 22/62] CFI: Don't compress user-defined builtin types Doesn't compress user-defined builtin types (see https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-builtin and https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). --- .../src/typeid/typeid_itanium_cxx_abi.rs | 17 ++++++++++--- ...fi-emit-type-metadata-attr-cfi-encoding.rs | 25 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 0d7b9afab5e..42d9b519c14 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -546,8 +546,20 @@ fn encode_ty<'tcx>( if let Some(cfi_encoding) = tcx.get_attr(def_id, sym::cfi_encoding) { // Use user-defined CFI encoding for type if let Some(value_str) = cfi_encoding.value_str() { - if !value_str.to_string().trim().is_empty() { - s.push_str(value_str.to_string().trim()); + let value_str = value_str.to_string(); + let str = value_str.trim(); + if !str.is_empty() { + s.push_str(str); + // Don't compress user-defined builtin types (see + // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-builtin and + // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). + let builtin_types = [ + "v", "w", "b", "c", "a", "h", "s", "t", "i", "j", "l", "m", "x", "y", + "n", "o", "f", "d", "e", "g", "z", + ]; + if !builtin_types.contains(&str) { + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + } } else { #[allow( rustc::diagnostic_outside_of_impl, @@ -563,7 +575,6 @@ fn encode_ty<'tcx>( } else { bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind()); } - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); } else if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() { // For cross-language LLVM CFI support, the encoding must be compatible at the FFI // boundary. For instance: diff --git a/tests/codegen/sanitizer/cfi-emit-type-metadata-attr-cfi-encoding.rs b/tests/codegen/sanitizer/cfi-emit-type-metadata-attr-cfi-encoding.rs index f15ca30b7e3..be4af9b7962 100644 --- a/tests/codegen/sanitizer/cfi-emit-type-metadata-attr-cfi-encoding.rs +++ b/tests/codegen/sanitizer/cfi-emit-type-metadata-attr-cfi-encoding.rs @@ -18,6 +18,13 @@ extern { #[repr(transparent)] pub struct Type3(i32); +#[cfi_encoding = "i"] +pub struct Type4(i32); + +#[cfi_encoding = "j"] +#[repr(transparent)] +pub struct Type5(u32); + pub fn foo0(_: Type1) { } // CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo1(_: Type1, _: Type1) { } @@ -36,6 +43,18 @@ pub fn foo7(_: *mut Type3, _: *mut Type3) { } // CHECK: define{{.*}}foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo8(_: *mut Type3, _: *mut Type3, _: *mut Type3) { } // CHECK: define{{.*}}foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo9(_: Type4) { } +// CHECK: define{{.*}}foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo10(_: Type4, _: Type4) { } +// CHECK: define{{.*}}foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo11(_: Type4, _: Type4, _: Type4) { } +// CHECK: define{{.*}}foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo12(_: Type5) { } +// CHECK: define{{.*}}foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo13(_: Type5, _: Type5) { } +// CHECK: define{{.*}}foo13{{.*}}!type ![[TYPE13:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo14(_: Type5, _: Type5, _: Type5) { } +// CHECK: define{{.*}}foo14{{.*}}!type ![[TYPE14:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFv3FooE"} // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFv3FooS_E"} @@ -46,3 +65,9 @@ pub fn foo8(_: *mut Type3, _: *mut Type3, _: *mut Type3) { } // CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvP3BazE"} // CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvP3BazS0_E"} // CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvP3BazS0_S0_E"} +// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFviE"} +// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFviiE"} +// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFviiiE"} +// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvjE"} +// CHECK: ![[TYPE13]] = !{i64 0, !"_ZTSFvjjE"} +// CHECK: ![[TYPE14]] = !{i64 0, !"_ZTSFvjjjE"} From 951f2d9ae2c3252e8076aebd54a519be83fcbab2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 28 Feb 2024 15:49:48 +1100 Subject: [PATCH 23/62] Use `LitKind::Err` for floats with empty exponents. This prevents a follow-up type error in a test, which seems fine. --- compiler/rustc_parse/src/lexer/mod.rs | 6 ++++-- .../clippy/tests/ui/crashes/ice-10912.rs | 2 -- .../clippy/tests/ui/crashes/ice-10912.stderr | 11 +--------- ...nicode-confusable-in-float-literal-expt.rs | 1 - ...de-confusable-in-float-literal-expt.stderr | 21 +------------------ 5 files changed, 6 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index dc9f5bad765..4523276970d 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -501,9 +501,11 @@ impl<'sess, 'src> StringReader<'sess, 'src> { (kind, self.symbol_from_to(start, end)) } rustc_lexer::LiteralKind::Float { base, empty_exponent } => { + let mut kind = token::Float; if empty_exponent { let span = self.mk_sp(start, self.pos); - self.dcx().emit_err(errors::EmptyExponentFloat { span }); + let guar = self.dcx().emit_err(errors::EmptyExponentFloat { span }); + kind = token::Err(guar); } let base = match base { Base::Hexadecimal => Some("hexadecimal"), @@ -515,7 +517,7 @@ impl<'sess, 'src> StringReader<'sess, 'src> { let span = self.mk_sp(start, end); self.dcx().emit_err(errors::FloatLiteralUnsupportedBase { span, base }); } - (token::Float, self.symbol_from_to(start, end)) + (kind, self.symbol_from_to(start, end)) } } } diff --git a/src/tools/clippy/tests/ui/crashes/ice-10912.rs b/src/tools/clippy/tests/ui/crashes/ice-10912.rs index 8dfce194221..1d689e1d008 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-10912.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-10912.rs @@ -2,7 +2,5 @@ //@no-rustfix fn f2() -> impl Sized { && 3.14159265358979323846E } //~^ ERROR: expected at least one digit in exponent -//~| ERROR: long literal lacking separators -//~| NOTE: `-D clippy::unreadable-literal` implied by `-D warnings` fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-10912.stderr b/src/tools/clippy/tests/ui/crashes/ice-10912.stderr index cc80354c7c6..c697e54679f 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-10912.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-10912.stderr @@ -4,14 +4,5 @@ error: expected at least one digit in exponent LL | fn f2() -> impl Sized { && 3.14159265358979323846E } | ^^^^^^^^^^^^^^^^^^^^^^^ -error: long literal lacking separators - --> tests/ui/crashes/ice-10912.rs:3:28 - | -LL | fn f2() -> impl Sized { && 3.14159265358979323846E } - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider: `3.141_592_653_589_793_238_46` - | - = note: `-D clippy::unreadable-literal` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unreadable_literal)]` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.rs b/tests/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.rs index 66d562d2eb5..5c2c3b8ec61 100644 --- a/tests/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.rs +++ b/tests/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.rs @@ -1,6 +1,5 @@ const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e−11; // m³⋅kg⁻¹⋅s⁻² //~^ ERROR expected at least one digit in exponent //~| ERROR unknown start of token: \u{2212} -//~| ERROR cannot subtract `{integer}` from `{float}` fn main() {} diff --git a/tests/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr b/tests/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr index 44bdbb93ff5..4b3d429c750 100644 --- a/tests/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr +++ b/tests/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr @@ -15,24 +15,5 @@ help: Unicode character '−' (Minus Sign) looks like '-' (Minus/Hyphen), but it LL | const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e-11; // m³⋅kg⁻¹⋅s⁻² | ~ -error[E0277]: cannot subtract `{integer}` from `{float}` - --> $DIR/issue-49746-unicode-confusable-in-float-literal-expt.rs:1:53 - | -LL | const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e−11; // m³⋅kg⁻¹⋅s⁻² - | ^ no implementation for `{float} - {integer}` - | - = help: the trait `Sub<{integer}>` is not implemented for `{float}` - = help: the following other types implement trait `Sub`: - - > - - > - - > - - > - and 48 others +error: aborting due to 2 previous errors -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0277`. From 79766098a469c72a24464e8157f58b3c6272f3d2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 28 Feb 2024 16:29:41 +1100 Subject: [PATCH 24/62] Reformat `float-field.rs` test. - Put every literal in its own braces, rather than just some of them, for maximal error recovery. - Add a blank line between every case, for readability. --- tests/ui/parser/float-field.rs | 69 +++++++--- tests/ui/parser/float-field.stderr | 202 ++++++++++++++--------------- 2 files changed, 150 insertions(+), 121 deletions(-) diff --git a/tests/ui/parser/float-field.rs b/tests/ui/parser/float-field.rs index eaa7465dc4d..e074b0567a3 100644 --- a/tests/ui/parser/float-field.rs +++ b/tests/ui/parser/float-field.rs @@ -3,60 +3,89 @@ struct S(u8, (u8, u8)); fn main() { let s = S(0, (0, 0)); - s.1e1; //~ ERROR no field `1e1` on type `S` - s.1.; //~ ERROR unexpected token: `;` - s.1.1; - s.1.1e1; //~ ERROR no field `1e1` on type `(u8, u8)` + { s.1e1; } //~ ERROR no field `1e1` on type `S` + + { s.1.; } //~ ERROR unexpected token: `;` + + { s.1.1; } + + { s.1.1e1; } //~ ERROR no field `1e1` on type `(u8, u8)` + { s.1e+; } //~ ERROR unexpected token: `1e+` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+` //~| ERROR expected at least one digit in exponent + { s.1e-; } //~ ERROR unexpected token: `1e-` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-` //~| ERROR expected at least one digit in exponent + { s.1e+1; } //~ ERROR unexpected token: `1e+1` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+1` + { s.1e-1; } //~ ERROR unexpected token: `1e-1` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-1` + { s.1.1e+1; } //~ ERROR unexpected token: `1.1e+1` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e+1` + { s.1.1e-1; } //~ ERROR unexpected token: `1.1e-1` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e-1` - s.0x1e1; //~ ERROR no field `0x1e1` on type `S` - s.0x1.; //~ ERROR no field `0x1` on type `S` - //~| ERROR hexadecimal float literal is not supported - //~| ERROR unexpected token: `;` - s.0x1.1; //~ ERROR no field `0x1` on type `S` - //~| ERROR hexadecimal float literal is not supported - s.0x1.1e1; //~ ERROR no field `0x1` on type `S` - //~| ERROR hexadecimal float literal is not supported + + { s.0x1e1; } //~ ERROR no field `0x1e1` on type `S` + + { s.0x1.; } //~ ERROR no field `0x1` on type `S` + //~| ERROR hexadecimal float literal is not supported + //~| ERROR unexpected token: `;` + + { s.0x1.1; } //~ ERROR no field `0x1` on type `S` + //~| ERROR hexadecimal float literal is not supported + + { s.0x1.1e1; } //~ ERROR no field `0x1` on type `S` + //~| ERROR hexadecimal float literal is not supported + { s.0x1e+; } //~ ERROR expected expression, found `;` + { s.0x1e-; } //~ ERROR expected expression, found `;` - s.0x1e+1; //~ ERROR no field `0x1e` on type `S` - s.0x1e-1; //~ ERROR no field `0x1e` on type `S` + + { s.0x1e+1; } //~ ERROR no field `0x1e` on type `S` + + { s.0x1e-1; } //~ ERROR no field `0x1e` on type `S` + { s.0x1.1e+1; } //~ ERROR unexpected token: `0x1.1e+1` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1e+1` //~| ERROR hexadecimal float literal is not supported + { s.0x1.1e-1; } //~ ERROR unexpected token: `0x1.1e-1` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1e-1` //~| ERROR hexadecimal float literal is not supported - s.1e1f32; //~ ERROR no field `1e1` on type `S` - //~| ERROR suffixes on a tuple index are invalid - s.1.f32; //~ ERROR no field `f32` on type `(u8, u8)` - s.1.1f32; //~ ERROR suffixes on a tuple index are invalid - s.1.1e1f32; //~ ERROR no field `1e1` on type `(u8, u8)` - //~| ERROR suffixes on a tuple index are invalid + + { s.1e1f32; } //~ ERROR no field `1e1` on type `S` + //~| ERROR suffixes on a tuple index are invalid + + { s.1.f32; } //~ ERROR no field `f32` on type `(u8, u8)` + + { s.1.1f32; } //~ ERROR suffixes on a tuple index are invalid + + { s.1.1e1f32; } //~ ERROR no field `1e1` on type `(u8, u8)` + //~| ERROR suffixes on a tuple index are invalid + { s.1e+f32; } //~ ERROR unexpected token: `1e+f32` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+f32` //~| ERROR expected at least one digit in exponent + { s.1e-f32; } //~ ERROR unexpected token: `1e-f32` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-f32` //~| ERROR expected at least one digit in exponent + { s.1e+1f32; } //~ ERROR unexpected token: `1e+1f32` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+1f32` + { s.1e-1f32; } //~ ERROR unexpected token: `1e-1f32` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-1f32` + { s.1.1e+1f32; } //~ ERROR unexpected token: `1.1e+1f32` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e+1f32` + { s.1.1e-1f32; } //~ ERROR unexpected token: `1.1e-1f32` //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e-1f32` } diff --git a/tests/ui/parser/float-field.stderr b/tests/ui/parser/float-field.stderr index d67d270ef7b..0606e321ef4 100644 --- a/tests/ui/parser/float-field.stderr +++ b/tests/ui/parser/float-field.stderr @@ -1,348 +1,348 @@ error: expected at least one digit in exponent - --> $DIR/float-field.rs:10:9 + --> $DIR/float-field.rs:14:9 | LL | { s.1e+; } | ^^^ error: expected at least one digit in exponent - --> $DIR/float-field.rs:13:9 + --> $DIR/float-field.rs:18:9 | LL | { s.1e-; } | ^^^ -error: hexadecimal float literal is not supported - --> $DIR/float-field.rs:25:7 - | -LL | s.0x1.; - | ^^^^ - -error: hexadecimal float literal is not supported - --> $DIR/float-field.rs:28:7 - | -LL | s.0x1.1; - | ^^^^^ - -error: hexadecimal float literal is not supported - --> $DIR/float-field.rs:30:7 - | -LL | s.0x1.1e1; - | ^^^^^^^ - error: hexadecimal float literal is not supported --> $DIR/float-field.rs:36:9 | +LL | { s.0x1.; } + | ^^^^ + +error: hexadecimal float literal is not supported + --> $DIR/float-field.rs:40:9 + | +LL | { s.0x1.1; } + | ^^^^^ + +error: hexadecimal float literal is not supported + --> $DIR/float-field.rs:43:9 + | +LL | { s.0x1.1e1; } + | ^^^^^^^ + +error: hexadecimal float literal is not supported + --> $DIR/float-field.rs:54:9 + | LL | { s.0x1.1e+1; } | ^^^^^^^^ error: hexadecimal float literal is not supported - --> $DIR/float-field.rs:39:9 + --> $DIR/float-field.rs:58:9 | LL | { s.0x1.1e-1; } | ^^^^^^^^ error: expected at least one digit in exponent - --> $DIR/float-field.rs:48:9 + --> $DIR/float-field.rs:72:9 | LL | { s.1e+f32; } | ^^^^^^ error: expected at least one digit in exponent - --> $DIR/float-field.rs:51:9 + --> $DIR/float-field.rs:76:9 | LL | { s.1e-f32; } | ^^^^^^ error: unexpected token: `;` - --> $DIR/float-field.rs:7:9 + --> $DIR/float-field.rs:8:11 | -LL | s.1.; - | ^ +LL | { s.1.; } + | ^ error: unexpected token: `1e+` - --> $DIR/float-field.rs:10:9 + --> $DIR/float-field.rs:14:9 | LL | { s.1e+; } | ^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+` - --> $DIR/float-field.rs:10:9 + --> $DIR/float-field.rs:14:9 | LL | { s.1e+; } | ^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1e-` - --> $DIR/float-field.rs:13:9 + --> $DIR/float-field.rs:18:9 | LL | { s.1e-; } | ^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-` - --> $DIR/float-field.rs:13:9 + --> $DIR/float-field.rs:18:9 | LL | { s.1e-; } | ^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1e+1` - --> $DIR/float-field.rs:16:9 + --> $DIR/float-field.rs:22:9 | LL | { s.1e+1; } | ^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+1` - --> $DIR/float-field.rs:16:9 + --> $DIR/float-field.rs:22:9 | LL | { s.1e+1; } | ^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1e-1` - --> $DIR/float-field.rs:18:9 + --> $DIR/float-field.rs:25:9 | LL | { s.1e-1; } | ^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-1` - --> $DIR/float-field.rs:18:9 + --> $DIR/float-field.rs:25:9 | LL | { s.1e-1; } | ^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1.1e+1` - --> $DIR/float-field.rs:20:9 + --> $DIR/float-field.rs:28:9 | LL | { s.1.1e+1; } | ^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e+1` - --> $DIR/float-field.rs:20:9 + --> $DIR/float-field.rs:28:9 | LL | { s.1.1e+1; } | ^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1.1e-1` - --> $DIR/float-field.rs:22:9 + --> $DIR/float-field.rs:31:9 | LL | { s.1.1e-1; } | ^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e-1` - --> $DIR/float-field.rs:22:9 + --> $DIR/float-field.rs:31:9 | LL | { s.1.1e-1; } | ^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `;` - --> $DIR/float-field.rs:25:11 + --> $DIR/float-field.rs:36:13 | -LL | s.0x1.; - | ^ +LL | { s.0x1.; } + | ^ error: expected expression, found `;` - --> $DIR/float-field.rs:32:14 + --> $DIR/float-field.rs:46:14 | LL | { s.0x1e+; } | ^ expected expression error: expected expression, found `;` - --> $DIR/float-field.rs:33:14 + --> $DIR/float-field.rs:48:14 | LL | { s.0x1e-; } | ^ expected expression error: unexpected token: `0x1.1e+1` - --> $DIR/float-field.rs:36:9 + --> $DIR/float-field.rs:54:9 | LL | { s.0x1.1e+1; } | ^^^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1e+1` - --> $DIR/float-field.rs:36:9 + --> $DIR/float-field.rs:54:9 | LL | { s.0x1.1e+1; } | ^^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `0x1.1e-1` - --> $DIR/float-field.rs:39:9 + --> $DIR/float-field.rs:58:9 | LL | { s.0x1.1e-1; } | ^^^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1e-1` - --> $DIR/float-field.rs:39:9 + --> $DIR/float-field.rs:58:9 | LL | { s.0x1.1e-1; } | ^^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: suffixes on a tuple index are invalid - --> $DIR/float-field.rs:42:7 + --> $DIR/float-field.rs:62:9 | -LL | s.1e1f32; - | ^^^^^^ invalid suffix `f32` +LL | { s.1e1f32; } + | ^^^^^^ invalid suffix `f32` error: suffixes on a tuple index are invalid - --> $DIR/float-field.rs:45:7 + --> $DIR/float-field.rs:67:9 | -LL | s.1.1f32; - | ^^^^^^ invalid suffix `f32` +LL | { s.1.1f32; } + | ^^^^^^ invalid suffix `f32` error: suffixes on a tuple index are invalid - --> $DIR/float-field.rs:46:7 + --> $DIR/float-field.rs:69:9 | -LL | s.1.1e1f32; - | ^^^^^^^^ invalid suffix `f32` +LL | { s.1.1e1f32; } + | ^^^^^^^^ invalid suffix `f32` error: unexpected token: `1e+f32` - --> $DIR/float-field.rs:48:9 + --> $DIR/float-field.rs:72:9 | LL | { s.1e+f32; } | ^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+f32` - --> $DIR/float-field.rs:48:9 + --> $DIR/float-field.rs:72:9 | LL | { s.1e+f32; } | ^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1e-f32` - --> $DIR/float-field.rs:51:9 + --> $DIR/float-field.rs:76:9 | LL | { s.1e-f32; } | ^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-f32` - --> $DIR/float-field.rs:51:9 + --> $DIR/float-field.rs:76:9 | LL | { s.1e-f32; } | ^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1e+1f32` - --> $DIR/float-field.rs:54:9 + --> $DIR/float-field.rs:80:9 | LL | { s.1e+1f32; } | ^^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+1f32` - --> $DIR/float-field.rs:54:9 + --> $DIR/float-field.rs:80:9 | LL | { s.1e+1f32; } | ^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1e-1f32` - --> $DIR/float-field.rs:56:9 + --> $DIR/float-field.rs:83:9 | LL | { s.1e-1f32; } | ^^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-1f32` - --> $DIR/float-field.rs:56:9 + --> $DIR/float-field.rs:83:9 | LL | { s.1e-1f32; } | ^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1.1e+1f32` - --> $DIR/float-field.rs:58:9 + --> $DIR/float-field.rs:86:9 | LL | { s.1.1e+1f32; } | ^^^^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e+1f32` - --> $DIR/float-field.rs:58:9 + --> $DIR/float-field.rs:86:9 | LL | { s.1.1e+1f32; } | ^^^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1.1e-1f32` - --> $DIR/float-field.rs:60:9 + --> $DIR/float-field.rs:89:9 | LL | { s.1.1e-1f32; } | ^^^^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e-1f32` - --> $DIR/float-field.rs:60:9 + --> $DIR/float-field.rs:89:9 | LL | { s.1.1e-1f32; } | ^^^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error[E0609]: no field `1e1` on type `S` - --> $DIR/float-field.rs:6:7 + --> $DIR/float-field.rs:6:9 | -LL | s.1e1; - | ^^^ unknown field +LL | { s.1e1; } + | ^^^ unknown field | = note: available fields are: `0`, `1` error[E0609]: no field `1e1` on type `(u8, u8)` - --> $DIR/float-field.rs:9:9 + --> $DIR/float-field.rs:12:11 | -LL | s.1.1e1; - | ^^^ unknown field +LL | { s.1.1e1; } + | ^^^ unknown field error[E0609]: no field `0x1e1` on type `S` - --> $DIR/float-field.rs:24:7 + --> $DIR/float-field.rs:34:9 | -LL | s.0x1e1; - | ^^^^^ unknown field +LL | { s.0x1e1; } + | ^^^^^ unknown field | = note: available fields are: `0`, `1` error[E0609]: no field `0x1` on type `S` - --> $DIR/float-field.rs:25:7 + --> $DIR/float-field.rs:36:9 | -LL | s.0x1.; - | ^^^ unknown field +LL | { s.0x1.; } + | ^^^ unknown field | = note: available fields are: `0`, `1` error[E0609]: no field `0x1` on type `S` - --> $DIR/float-field.rs:28:7 + --> $DIR/float-field.rs:40:9 | -LL | s.0x1.1; - | ^^^ unknown field +LL | { s.0x1.1; } + | ^^^ unknown field | = note: available fields are: `0`, `1` error[E0609]: no field `0x1` on type `S` - --> $DIR/float-field.rs:30:7 + --> $DIR/float-field.rs:43:9 | -LL | s.0x1.1e1; - | ^^^ unknown field +LL | { s.0x1.1e1; } + | ^^^ unknown field | = note: available fields are: `0`, `1` error[E0609]: no field `0x1e` on type `S` - --> $DIR/float-field.rs:34:7 + --> $DIR/float-field.rs:50:9 | -LL | s.0x1e+1; - | ^^^^ unknown field +LL | { s.0x1e+1; } + | ^^^^ unknown field | = note: available fields are: `0`, `1` error[E0609]: no field `0x1e` on type `S` - --> $DIR/float-field.rs:35:7 + --> $DIR/float-field.rs:52:9 | -LL | s.0x1e-1; - | ^^^^ unknown field +LL | { s.0x1e-1; } + | ^^^^ unknown field | = note: available fields are: `0`, `1` error[E0609]: no field `1e1` on type `S` - --> $DIR/float-field.rs:42:7 + --> $DIR/float-field.rs:62:9 | -LL | s.1e1f32; - | ^^^^^^ unknown field +LL | { s.1e1f32; } + | ^^^^^^ unknown field | = note: available fields are: `0`, `1` error[E0609]: no field `f32` on type `(u8, u8)` - --> $DIR/float-field.rs:44:9 + --> $DIR/float-field.rs:65:11 | -LL | s.1.f32; - | ^^^ unknown field +LL | { s.1.f32; } + | ^^^ unknown field error[E0609]: no field `1e1` on type `(u8, u8)` - --> $DIR/float-field.rs:46:7 + --> $DIR/float-field.rs:69:9 | -LL | s.1.1e1f32; - | ^^^^^^^^ unknown field +LL | { s.1.1e1f32; } + | ^^^^^^^^ unknown field error: aborting due to 55 previous errors From 840c8d3243761ac7d3976239aeba6f866f9df1eb Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 28 Feb 2024 16:30:20 +1100 Subject: [PATCH 25/62] Use `LitKind::Err` for floats with unsupported bases. This slightly changes error messages in `float-field.rs`, but nothing of real importance. --- compiler/rustc_parse/src/lexer/mod.rs | 4 +- tests/ui/parser/float-field.rs | 16 ++-- tests/ui/parser/float-field.stderr | 124 ++++++++++++++------------ 3 files changed, 77 insertions(+), 67 deletions(-) diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 4523276970d..15ba80be1e6 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -515,7 +515,9 @@ impl<'sess, 'src> StringReader<'sess, 'src> { }; if let Some(base) = base { let span = self.mk_sp(start, end); - self.dcx().emit_err(errors::FloatLiteralUnsupportedBase { span, base }); + let guar = + self.dcx().emit_err(errors::FloatLiteralUnsupportedBase { span, base }); + kind = token::Err(guar) } (kind, self.symbol_from_to(start, end)) } diff --git a/tests/ui/parser/float-field.rs b/tests/ui/parser/float-field.rs index e074b0567a3..59fefee26aa 100644 --- a/tests/ui/parser/float-field.rs +++ b/tests/ui/parser/float-field.rs @@ -33,15 +33,17 @@ fn main() { { s.0x1e1; } //~ ERROR no field `0x1e1` on type `S` - { s.0x1.; } //~ ERROR no field `0x1` on type `S` - //~| ERROR hexadecimal float literal is not supported - //~| ERROR unexpected token: `;` + { s.0x1.; } //~ ERROR hexadecimal float literal is not supported + //~| ERROR unexpected token: `0x1.` + //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.` - { s.0x1.1; } //~ ERROR no field `0x1` on type `S` - //~| ERROR hexadecimal float literal is not supported + { s.0x1.1; } //~ ERROR hexadecimal float literal is not supported + //~| ERROR unexpected token: `0x1.1` + //~| expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1` - { s.0x1.1e1; } //~ ERROR no field `0x1` on type `S` - //~| ERROR hexadecimal float literal is not supported + { s.0x1.1e1; } //~ ERROR hexadecimal float literal is not supported + //~| ERROR unexpected token: `0x1.1e1` + //~| expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1e1` { s.0x1e+; } //~ ERROR expected expression, found `;` diff --git a/tests/ui/parser/float-field.stderr b/tests/ui/parser/float-field.stderr index 0606e321ef4..0cc1b0767dc 100644 --- a/tests/ui/parser/float-field.stderr +++ b/tests/ui/parser/float-field.stderr @@ -23,31 +23,31 @@ LL | { s.0x1.1; } | ^^^^^ error: hexadecimal float literal is not supported - --> $DIR/float-field.rs:43:9 + --> $DIR/float-field.rs:44:9 | LL | { s.0x1.1e1; } | ^^^^^^^ error: hexadecimal float literal is not supported - --> $DIR/float-field.rs:54:9 + --> $DIR/float-field.rs:56:9 | LL | { s.0x1.1e+1; } | ^^^^^^^^ error: hexadecimal float literal is not supported - --> $DIR/float-field.rs:58:9 + --> $DIR/float-field.rs:60:9 | LL | { s.0x1.1e-1; } | ^^^^^^^^ error: expected at least one digit in exponent - --> $DIR/float-field.rs:72:9 + --> $DIR/float-field.rs:74:9 | LL | { s.1e+f32; } | ^^^^^^ error: expected at least one digit in exponent - --> $DIR/float-field.rs:76:9 + --> $DIR/float-field.rs:78:9 | LL | { s.1e-f32; } | ^^^^^^ @@ -130,134 +130,164 @@ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e-1` LL | { s.1.1e-1; } | ^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator -error: unexpected token: `;` - --> $DIR/float-field.rs:36:13 +error: unexpected token: `0x1.` + --> $DIR/float-field.rs:36:9 | LL | { s.0x1.; } - | ^ + | ^^^^ + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.` + --> $DIR/float-field.rs:36:9 + | +LL | { s.0x1.; } + | ^^^^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: unexpected token: `0x1.1` + --> $DIR/float-field.rs:40:9 + | +LL | { s.0x1.1; } + | ^^^^^ + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1` + --> $DIR/float-field.rs:40:9 + | +LL | { s.0x1.1; } + | ^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: unexpected token: `0x1.1e1` + --> $DIR/float-field.rs:44:9 + | +LL | { s.0x1.1e1; } + | ^^^^^^^ + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1e1` + --> $DIR/float-field.rs:44:9 + | +LL | { s.0x1.1e1; } + | ^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: expected expression, found `;` - --> $DIR/float-field.rs:46:14 + --> $DIR/float-field.rs:48:14 | LL | { s.0x1e+; } | ^ expected expression error: expected expression, found `;` - --> $DIR/float-field.rs:48:14 + --> $DIR/float-field.rs:50:14 | LL | { s.0x1e-; } | ^ expected expression error: unexpected token: `0x1.1e+1` - --> $DIR/float-field.rs:54:9 + --> $DIR/float-field.rs:56:9 | LL | { s.0x1.1e+1; } | ^^^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1e+1` - --> $DIR/float-field.rs:54:9 + --> $DIR/float-field.rs:56:9 | LL | { s.0x1.1e+1; } | ^^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `0x1.1e-1` - --> $DIR/float-field.rs:58:9 + --> $DIR/float-field.rs:60:9 | LL | { s.0x1.1e-1; } | ^^^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1e-1` - --> $DIR/float-field.rs:58:9 + --> $DIR/float-field.rs:60:9 | LL | { s.0x1.1e-1; } | ^^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: suffixes on a tuple index are invalid - --> $DIR/float-field.rs:62:9 + --> $DIR/float-field.rs:64:9 | LL | { s.1e1f32; } | ^^^^^^ invalid suffix `f32` error: suffixes on a tuple index are invalid - --> $DIR/float-field.rs:67:9 + --> $DIR/float-field.rs:69:9 | LL | { s.1.1f32; } | ^^^^^^ invalid suffix `f32` error: suffixes on a tuple index are invalid - --> $DIR/float-field.rs:69:9 + --> $DIR/float-field.rs:71:9 | LL | { s.1.1e1f32; } | ^^^^^^^^ invalid suffix `f32` error: unexpected token: `1e+f32` - --> $DIR/float-field.rs:72:9 + --> $DIR/float-field.rs:74:9 | LL | { s.1e+f32; } | ^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+f32` - --> $DIR/float-field.rs:72:9 + --> $DIR/float-field.rs:74:9 | LL | { s.1e+f32; } | ^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1e-f32` - --> $DIR/float-field.rs:76:9 + --> $DIR/float-field.rs:78:9 | LL | { s.1e-f32; } | ^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-f32` - --> $DIR/float-field.rs:76:9 + --> $DIR/float-field.rs:78:9 | LL | { s.1e-f32; } | ^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1e+1f32` - --> $DIR/float-field.rs:80:9 + --> $DIR/float-field.rs:82:9 | LL | { s.1e+1f32; } | ^^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+1f32` - --> $DIR/float-field.rs:80:9 + --> $DIR/float-field.rs:82:9 | LL | { s.1e+1f32; } | ^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1e-1f32` - --> $DIR/float-field.rs:83:9 + --> $DIR/float-field.rs:85:9 | LL | { s.1e-1f32; } | ^^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-1f32` - --> $DIR/float-field.rs:83:9 + --> $DIR/float-field.rs:85:9 | LL | { s.1e-1f32; } | ^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1.1e+1f32` - --> $DIR/float-field.rs:86:9 + --> $DIR/float-field.rs:88:9 | LL | { s.1.1e+1f32; } | ^^^^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e+1f32` - --> $DIR/float-field.rs:86:9 + --> $DIR/float-field.rs:88:9 | LL | { s.1.1e+1f32; } | ^^^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator error: unexpected token: `1.1e-1f32` - --> $DIR/float-field.rs:89:9 + --> $DIR/float-field.rs:91:9 | LL | { s.1.1e-1f32; } | ^^^^^^^^^ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e-1f32` - --> $DIR/float-field.rs:89:9 + --> $DIR/float-field.rs:91:9 | LL | { s.1.1e-1f32; } | ^^^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator @@ -284,32 +314,8 @@ LL | { s.0x1e1; } | = note: available fields are: `0`, `1` -error[E0609]: no field `0x1` on type `S` - --> $DIR/float-field.rs:36:9 - | -LL | { s.0x1.; } - | ^^^ unknown field - | - = note: available fields are: `0`, `1` - -error[E0609]: no field `0x1` on type `S` - --> $DIR/float-field.rs:40:9 - | -LL | { s.0x1.1; } - | ^^^ unknown field - | - = note: available fields are: `0`, `1` - -error[E0609]: no field `0x1` on type `S` - --> $DIR/float-field.rs:43:9 - | -LL | { s.0x1.1e1; } - | ^^^ unknown field - | - = note: available fields are: `0`, `1` - error[E0609]: no field `0x1e` on type `S` - --> $DIR/float-field.rs:50:9 + --> $DIR/float-field.rs:52:9 | LL | { s.0x1e+1; } | ^^^^ unknown field @@ -317,7 +323,7 @@ LL | { s.0x1e+1; } = note: available fields are: `0`, `1` error[E0609]: no field `0x1e` on type `S` - --> $DIR/float-field.rs:52:9 + --> $DIR/float-field.rs:54:9 | LL | { s.0x1e-1; } | ^^^^ unknown field @@ -325,7 +331,7 @@ LL | { s.0x1e-1; } = note: available fields are: `0`, `1` error[E0609]: no field `1e1` on type `S` - --> $DIR/float-field.rs:62:9 + --> $DIR/float-field.rs:64:9 | LL | { s.1e1f32; } | ^^^^^^ unknown field @@ -333,17 +339,17 @@ LL | { s.1e1f32; } = note: available fields are: `0`, `1` error[E0609]: no field `f32` on type `(u8, u8)` - --> $DIR/float-field.rs:65:11 + --> $DIR/float-field.rs:67:11 | LL | { s.1.f32; } | ^^^ unknown field error[E0609]: no field `1e1` on type `(u8, u8)` - --> $DIR/float-field.rs:69:9 + --> $DIR/float-field.rs:71:9 | LL | { s.1.1e1f32; } | ^^^^^^^^ unknown field -error: aborting due to 55 previous errors +error: aborting due to 57 previous errors For more information about this error, try `rustc --explain E0609`. From 12b991d9f8e969a3db6e41c53d0f3fe5fd69ad8d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 28 Feb 2024 13:26:36 +0100 Subject: [PATCH 26/62] Don't panic when encountering unexpected constructor --- compiler/rustc_pattern_analysis/src/constructor.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 483986969d1..b1f910b947a 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -940,7 +940,7 @@ impl ConstructorSet { } ConstructorSet::Variants { variants, non_exhaustive } => { let mut seen_set = index::IdxSet::new_empty(variants.len()); - for idx in seen.iter().map(|c| c.as_variant().unwrap()) { + for idx in seen.iter().filter_map(|c| c.as_variant()) { seen_set.insert(idx); } let mut skipped_a_hidden_variant = false; @@ -969,7 +969,7 @@ impl ConstructorSet { ConstructorSet::Bool => { let mut seen_false = false; let mut seen_true = false; - for b in seen.iter().map(|ctor| ctor.as_bool().unwrap()) { + for b in seen.iter().filter_map(|ctor| ctor.as_bool()) { if b { seen_true = true; } else { @@ -989,7 +989,7 @@ impl ConstructorSet { } ConstructorSet::Integers { range_1, range_2 } => { let seen_ranges: Vec<_> = - seen.iter().map(|ctor| *ctor.as_int_range().unwrap()).collect(); + seen.iter().filter_map(|ctor| ctor.as_int_range()).copied().collect(); for (seen, splitted_range) in range_1.split(seen_ranges.iter().cloned()) { match seen { Presence::Unseen => missing.push(IntRange(splitted_range)), @@ -1006,7 +1006,7 @@ impl ConstructorSet { } } ConstructorSet::Slice { array_len, subtype_is_empty } => { - let seen_slices = seen.iter().map(|c| c.as_slice().unwrap()); + let seen_slices = seen.iter().filter_map(|c| c.as_slice()); let base_slice = Slice::new(*array_len, VarLen(0, 0)); for (seen, splitted_slice) in base_slice.split(seen_slices) { let ctor = Slice(splitted_slice); From 11f948952f6f268bc41ab10b7dd4c43624fd1774 Mon Sep 17 00:00:00 2001 From: surechen Date: Wed, 28 Feb 2024 22:45:43 +0800 Subject: [PATCH 27/62] Changing some attributes to only_local. Modified according to https://github.com/rust-lang/compiler-team/issues/505. --- compiler/rustc_feature/src/builtin_attrs.rs | 65 +++++++++++++++------ 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 682363ed19d..d423beade40 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -536,16 +536,19 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), gated!( rustc_allow_const_fn_unstable, Normal, - template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, + template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, @only_local: true, "rustc_allow_const_fn_unstable side-steps feature gating and stability checks" ), gated!( allow_internal_unsafe, Normal, template!(Word), WarnFollowing, - "allow_internal_unsafe side-steps the unsafe_code lint", + @only_local: true, "allow_internal_unsafe side-steps the unsafe_code lint", + ), + rustc_attr!( + rustc_allowed_through_unstable_modules, Normal, template!(Word), + WarnFollowing, @only_local: true, + "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \ + through unstable paths" ), - rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing, - "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \ - through unstable paths"), // ========================================================================== // Internal attributes: Type system related: @@ -553,7 +556,8 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ gated!(fundamental, Normal, template!(Word), WarnFollowing, experimental!(fundamental)), gated!( - may_dangle, Normal, template!(Word), WarnFollowing, dropck_eyepatch, + may_dangle, Normal, template!(Word), WarnFollowing, + @only_local: true, dropck_eyepatch, "`may_dangle` has unstable semantics and may be removed in the future", ), @@ -561,31 +565,51 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Internal attributes: Runtime related: // ========================================================================== - rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), - rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), - rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), - rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), - rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), - gated!( - default_lib_allocator, Normal, template!(Word), WarnFollowing, allocator_internals, - experimental!(default_lib_allocator), + rustc_attr!( + rustc_allocator, Normal, template!(Word), WarnFollowing, + @only_local: true, IMPL_DETAIL + ), + rustc_attr!( + rustc_nounwind, Normal, template!(Word), WarnFollowing, + @only_local: true, IMPL_DETAIL + ), + rustc_attr!( + rustc_reallocator, Normal, template!(Word), WarnFollowing, + @only_local: true, IMPL_DETAIL + ), + rustc_attr!( + rustc_deallocator, Normal, template!(Word), WarnFollowing, + @only_local: true, IMPL_DETAIL + ), + rustc_attr!( + rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, + @only_local: true, IMPL_DETAIL ), gated!( - needs_allocator, Normal, template!(Word), WarnFollowing, allocator_internals, - experimental!(needs_allocator), + default_lib_allocator, Normal, template!(Word), WarnFollowing, + @only_local: true, allocator_internals, experimental!(default_lib_allocator), + ), + gated!( + needs_allocator, Normal, template!(Word), WarnFollowing, + @only_local: true, allocator_internals, experimental!(needs_allocator), + ), + gated!( + panic_runtime, Normal, template!(Word), WarnFollowing, + @only_local: true, experimental!(panic_runtime) ), - gated!(panic_runtime, Normal, template!(Word), WarnFollowing, experimental!(panic_runtime)), gated!( needs_panic_runtime, Normal, template!(Word), WarnFollowing, - experimental!(needs_panic_runtime) + @only_local: true, experimental!(needs_panic_runtime) ), gated!( compiler_builtins, Normal, template!(Word), WarnFollowing, + @only_local: true, "the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \ which contains compiler-rt intrinsics and will never be stable", ), gated!( profiler_runtime, Normal, template!(Word), WarnFollowing, + @only_local: true, "the `#[profiler_runtime]` attribute is used to identify the `profiler_builtins` crate \ which contains the profiler runtime and will never be stable", ), @@ -611,7 +635,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(Word, List: "name, /*opt*/ attributes(name1, name2, ...)"), ErrorFollowing, IMPL_DETAIL, ), - rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + rustc_attr!( + rustc_proc_macro_decls, Normal, template!(Word), WarnFollowing, + @only_local: true, INTERNAL_UNSTABLE + ), rustc_attr!( rustc_macro_transparency, Normal, template!(NameValueStr: "transparent|semitransparent|opaque"), ErrorFollowing, From 632d26aeff1a943d4791ddad3025af61e0ff2256 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 27 Feb 2024 17:40:12 +0100 Subject: [PATCH 28/62] Add regression test for inclusion of whitespace characters in rustdoc highlighting --- .../html/highlight/fixtures/sample.html | 2 +- tests/rustdoc/source-code-highlight.rs | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/rustdoc/source-code-highlight.rs diff --git a/src/librustdoc/html/highlight/fixtures/sample.html b/src/librustdoc/html/highlight/fixtures/sample.html index aa735e81597..773afd5c2cc 100644 --- a/src/librustdoc/html/highlight/fixtures/sample.html +++ b/src/librustdoc/html/highlight/fixtures/sample.html @@ -32,7 +32,7 @@ } } -macro_rules! bar { +macro_rules! bar { ($foo:tt) => {}; } diff --git a/tests/rustdoc/source-code-highlight.rs b/tests/rustdoc/source-code-highlight.rs new file mode 100644 index 00000000000..0a1be791ec2 --- /dev/null +++ b/tests/rustdoc/source-code-highlight.rs @@ -0,0 +1,29 @@ +// We need this option to be enabled for the `foo` macro declaration to ensure +// that the link on the ident is not including whitespace characters. + +//@ compile-flags: -Zunstable-options --generate-link-to-definition +#![crate_name = "foo"] + +// @has 'src/foo/source-code-highlight.rs.html' + +// @hasraw - 'foo' +#[macro_export] +macro_rules! foo { + () => {} +} + +// @hasraw - 'foo!' +foo! {} + +// @hasraw - 'f' +#[rustfmt::skip] +pub fn f () {} +// @hasraw - 'Bar' +// @hasraw - 'Bar' +// @hasraw - 'u32' +#[rustfmt::skip] +pub struct Bar ( u32 ); +// @hasraw - 'Foo' +pub enum Foo { + A, +} From 5cdbe83af82e0e48a05bc87ff41ef381e45391dc Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 27 Feb 2024 23:30:07 +0000 Subject: [PATCH 29/62] Opportunistically resolve regions when processing region outlives obligations --- .../src/infer/outlives/obligations.rs | 10 +++++++++- .../impl-implied-bounds-compatibility.rs | 2 +- .../impl-implied-bounds-compatibility.stderr | 20 ++++--------------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index c0a99e5cc41..8dd3a1f40cc 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -62,6 +62,7 @@ use crate::infer::outlives::components::{push_outlives_components, Component}; use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::outlives::verify::VerifyBoundCx; +use crate::infer::resolve::OpportunisticRegionResolver; use crate::infer::{ self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, UndoLog, VerifyBound, }; @@ -69,7 +70,9 @@ use crate::traits::{ObligationCause, ObligationCauseCode}; use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::{self, GenericArgsRef, Region, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{ + self, GenericArgsRef, Region, Ty, TyCtxt, TypeFoldable as _, TypeVisitableExt, +}; use rustc_middle::ty::{GenericArgKind, PolyTypeOutlivesPredicate}; use rustc_span::DUMMY_SP; use smallvec::smallvec; @@ -176,6 +179,11 @@ impl<'tcx> InferCtxt<'tcx> { .map_err(|NoSolution| (outlives, origin.clone()))? .no_bound_vars() .expect("started with no bound vars, should end with no bound vars"); + // `TypeOutlives` is structural, so we should try to opportunistically resolve all + // region vids before processing regions, so we have a better chance to match clauses + // in our param-env. + let (sup_type, sub_region) = + (sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self)); debug!(?sup_type, ?sub_region, ?origin); diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.rs b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.rs index 7f3817b326a..5e3d21eb26a 100644 --- a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.rs +++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.rs @@ -10,7 +10,7 @@ pub trait MessageListenersInterface { impl<'a> MessageListenersInterface for MessageListeners<'a> { fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { - //~^ ERROR cannot infer an appropriate lifetime for lifetime parameter 'b in generic type due to conflicting requirements + //~^ ERROR in type `&'a MessageListeners<'_>`, reference has a longer lifetime than the data it references self } } diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr index 6a412eb5e4b..e0cf15d0d1f 100644 --- a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr +++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr @@ -1,27 +1,15 @@ -error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 'b in generic type due to conflicting requirements +error[E0491]: in type `&'a MessageListeners<'_>`, reference has a longer lifetime than the data it references --> $DIR/impl-implied-bounds-compatibility.rs:12:5 | LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: first, the lifetime cannot outlive the lifetime `'c` as defined here... - --> $DIR/impl-implied-bounds-compatibility.rs:12:5 - | -LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...so that the method type is compatible with trait - --> $DIR/impl-implied-bounds-compatibility.rs:12:5 - | -LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `fn(&'c MessageListeners<'_>) -> &'c MessageListeners<'c>` - found `fn(&MessageListeners<'_>) -> &'a MessageListeners<'_>` -note: but, the lifetime must be valid for the lifetime `'a` as defined here... +note: the pointer is valid for the lifetime `'a` as defined here --> $DIR/impl-implied-bounds-compatibility.rs:11:6 | LL | impl<'a> MessageListenersInterface for MessageListeners<'a> { | ^^ -note: ...so that the reference type `&'a MessageListeners<'_>` does not outlive the data it points at +note: but the referenced data is only valid for the lifetime `'c` as defined here --> $DIR/impl-implied-bounds-compatibility.rs:12:5 | LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { @@ -29,4 +17,4 @@ LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0495`. +For more information about this error, try `rustc --explain E0491`. From 75e15f7cf4e55ed9572781930ee8babcaa256a47 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 28 Feb 2024 16:09:06 +0000 Subject: [PATCH 30/62] Deeply normalize obligations in refining_impl_trait --- .../src/check/compare_impl_item/refine.rs | 14 ++++++++----- .../src/traits/engine.rs | 9 +++++++++ .../impl-trait/in-trait/refine-normalize.rs | 20 +++++++++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 tests/ui/impl-trait/in-trait/refine-normalize.rs diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index b5e69b8e376..29dc434ab45 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -136,11 +136,15 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>( // 1. Project the RPITIT projections from the trait to the opaques on the impl, // which means that they don't need to be mapped manually. // - // 2. Project any other projections that show up in the bound. That makes sure that - // we don't consider `tests/ui/async-await/in-trait/async-associated-types.rs` - // to be refining. - let (trait_bounds, impl_bounds) = - ocx.normalize(&ObligationCause::dummy(), param_env, (trait_bounds, impl_bounds)); + // 2. Deeply normalize any other projections that show up in the bound. That makes sure + // that we don't consider `tests/ui/async-await/in-trait/async-associated-types.rs` + // or `tests/ui/impl-trait/in-trait/refine-normalize.rs` to be refining. + let Ok((trait_bounds, impl_bounds)) = + ocx.deeply_normalize(&ObligationCause::dummy(), param_env, (trait_bounds, impl_bounds)) + else { + tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (selection)"); + return; + }; // Since we've normalized things, we need to resolve regions, since we'll // possibly have introduced region vars during projection. We don't expect diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 1aaadf6cf04..e789e9c2b6e 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -107,6 +107,15 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { self.register_infer_ok_obligations(infer_ok) } + pub fn deeply_normalize>>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: T, + ) -> Result>> { + self.infcx.at(cause, param_env).deeply_normalize(value, &mut **self.engine.borrow_mut()) + } + /// Makes `expected <: actual`. pub fn eq_exp( &self, diff --git a/tests/ui/impl-trait/in-trait/refine-normalize.rs b/tests/ui/impl-trait/in-trait/refine-normalize.rs new file mode 100644 index 00000000000..95f2cda6a74 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/refine-normalize.rs @@ -0,0 +1,20 @@ +//@ check-pass +//@ edition: 2021 +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +#![deny(refining_impl_trait)] + +pub trait Foo { + type Item; + + fn hello() -> impl Iterator; +} + +impl Foo for () { + type Item = (); + + fn hello() -> impl Iterator { [()].into_iter() } +} + +fn main() {} From be01e28dceb5e8e32bb1c97f3be5c5488eed8f4f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 6 Feb 2024 02:35:59 +0100 Subject: [PATCH 31/62] Push down the decision to skip fields --- compiler/rustc_pattern_analysis/src/rustc.rs | 38 +++++++++++--------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 5b62731e202..1b33ef5472e 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -165,13 +165,14 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { &self, ty: RevealedTy<'tcx>, variant: &'tcx VariantDef, - ) -> impl Iterator)> + Captures<'p> + Captures<'_> { + ) -> impl Iterator, bool)> + Captures<'p> + Captures<'_> + { let cx = self; let ty::Adt(adt, args) = ty.kind() else { bug!() }; - // Whether we must not match the fields of this variant exhaustively. + // Whether we must avoid matching the fields of this variant exhaustively. let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did().is_local(); - variant.fields.iter().enumerate().filter_map(move |(i, field)| { + variant.fields.iter().enumerate().map(move |(i, field)| { let ty = field.ty(cx.tcx, args); // `field.ty()` doesn't normalize after instantiating. let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty); @@ -180,12 +181,9 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { || cx.tcx.features().min_exhaustive_patterns) && cx.is_uninhabited(ty); - if is_uninhabited && (!is_visible || is_non_exhaustive) { - None - } else { - let ty = cx.reveal_opaque_ty(ty); - Some((FieldIdx::new(i), ty)) - } + let skip = is_uninhabited && (!is_visible || is_non_exhaustive); + let ty = cx.reveal_opaque_ty(ty); + (FieldIdx::new(i), ty, skip) }) } @@ -229,7 +227,10 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { } else { let variant = &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); - let tys = cx.list_variant_nonhidden_fields(ty, variant).map(|(_, ty)| ty); + let tys = cx + .list_variant_nonhidden_fields(ty, variant) + .filter(|(_, _, skip)| !skip) + .map(|(_, ty, _)| ty); cx.dropless_arena.alloc_from_iter(tys) } } @@ -276,7 +277,9 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { } else { let variant = &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); - self.list_variant_nonhidden_fields(ty, variant).count() + self.list_variant_nonhidden_fields(ty, variant) + .filter(|(_, _, skip)| !skip) + .count() } } _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"), @@ -523,12 +526,14 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { // For each field in the variant, we store the relevant index into `self.fields` if any. let mut field_id_to_id: Vec> = (0..variant.fields.len()).map(|_| None).collect(); - let tys = cx.list_variant_nonhidden_fields(ty, variant).enumerate().map( - |(i, (field, ty))| { + let tys = cx + .list_variant_nonhidden_fields(ty, variant) + .filter(|(_, _, skip)| !skip) + .enumerate() + .map(|(i, (field, ty, _))| { field_id_to_id[field.index()] = Some(i); ty - }, - ); + }); fields = tys.map(|ty| DeconstructedPat::wildcard(ty)).collect(); for pat in subpatterns { if let Some(i) = field_id_to_id[pat.field.index()] { @@ -778,8 +783,9 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { let variant = &adt_def.variant(variant_index); let subpatterns = cx .list_variant_nonhidden_fields(*pat.ty(), variant) + .filter(|(_, _, skip)| !skip) .zip(subpatterns) - .map(|((field, _ty), pattern)| FieldPat { field, pattern }) + .map(|((field, _ty, _), pattern)| FieldPat { field, pattern }) .collect(); if adt_def.is_enum() { From ab06037269da9c5fc83083fb7d9d9638294e3d63 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 6 Feb 2024 02:44:48 +0100 Subject: [PATCH 32/62] Push the decision to skip fields further down --- compiler/rustc_pattern_analysis/src/lib.rs | 10 +++++++--- compiler/rustc_pattern_analysis/src/pat.rs | 8 ++++++-- compiler/rustc_pattern_analysis/src/rustc.rs | 16 +++++++++------- .../rustc_pattern_analysis/src/usefulness.rs | 6 ++++-- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 164dc36b679..0b4bc77a976 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -82,6 +82,10 @@ use crate::usefulness::{compute_match_usefulness, ValidityConstraint}; pub trait Captures<'a> {} impl<'a, T: ?Sized> Captures<'a> for T {} +/// `bool` newtype that indicates whether we should skip this field during analysis. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct SkipField(pub bool); + /// Context that provides type information about constructors. /// /// Most of the crate is parameterized on a type that implements this trait. @@ -105,13 +109,13 @@ pub trait TypeCx: Sized + fmt::Debug { /// The number of fields for this constructor. fn ctor_arity(&self, ctor: &Constructor, ty: &Self::Ty) -> usize; - /// The types of the fields for this constructor. The result must have a length of - /// `ctor_arity()`. + /// The types of the fields for this constructor. The result must contain `ctor_arity()`-many + /// fields that are not skipped. fn ctor_sub_tys<'a>( &'a self, ctor: &'a Constructor, ty: &'a Self::Ty, - ) -> impl Iterator + ExactSizeIterator + Captures<'a>; + ) -> impl Iterator + ExactSizeIterator + Captures<'a>; /// The set of all the constructors for `ty`. /// diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index d9b2b31643d..3e89a894419 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -5,7 +5,7 @@ use std::fmt; use smallvec::{smallvec, SmallVec}; use crate::constructor::{Constructor, Slice, SliceKind}; -use crate::TypeCx; +use crate::{SkipField, TypeCx}; use self::Constructor::*; @@ -300,7 +300,11 @@ impl WitnessPat { /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. pub(crate) fn wild_from_ctor(cx: &Cx, ctor: Constructor, ty: Cx::Ty) -> Self { - let fields = cx.ctor_sub_tys(&ctor, &ty).map(|ty| Self::wildcard(ty)).collect(); + let fields = cx + .ctor_sub_tys(&ctor, &ty) + .filter(|(_, SkipField(skip))| !skip) + .map(|(ty, _)| Self::wildcard(ty)) + .collect(); Self::new(ctor, fields, ty) } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 1b33ef5472e..8d55dcf2056 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -18,7 +18,7 @@ use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; use crate::constructor::{ IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility, }; -use crate::{errors, Captures, TypeCx}; +use crate::{errors, Captures, SkipField, TypeCx}; use crate::constructor::Constructor::*; @@ -208,12 +208,15 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { &'a self, ctor: &'a Constructor<'p, 'tcx>, ty: RevealedTy<'tcx>, - ) -> impl Iterator> + ExactSizeIterator + Captures<'a> { + ) -> impl Iterator, SkipField)> + ExactSizeIterator + Captures<'a> + { fn reveal_and_alloc<'a, 'tcx>( cx: &'a RustcMatchCheckCtxt<'_, 'tcx>, iter: impl Iterator>, - ) -> &'a [RevealedTy<'tcx>] { - cx.dropless_arena.alloc_from_iter(iter.map(|ty| cx.reveal_opaque_ty(ty))) + ) -> &'a [(RevealedTy<'tcx>, SkipField)] { + cx.dropless_arena.alloc_from_iter( + iter.map(|ty| cx.reveal_opaque_ty(ty)).map(|ty| (ty, SkipField(false))), + ) } let cx = self; let slice = match ctor { @@ -229,8 +232,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); let tys = cx .list_variant_nonhidden_fields(ty, variant) - .filter(|(_, _, skip)| !skip) - .map(|(_, ty, _)| ty); + .map(|(_, ty, skip)| (ty, SkipField(skip))); cx.dropless_arena.alloc_from_iter(tys) } } @@ -872,7 +874,7 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { &'a self, ctor: &'a crate::constructor::Constructor, ty: &'a Self::Ty, - ) -> impl Iterator + ExactSizeIterator + Captures<'a> { + ) -> impl Iterator + ExactSizeIterator + Captures<'a> { self.ctor_sub_tys(ctor, *ty) } fn ctors_for_ty( diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index f672051be5a..ec9f3bb0db9 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -716,7 +716,7 @@ use std::fmt; use crate::constructor::{Constructor, ConstructorSet, IntRange}; use crate::pat::{DeconstructedPat, PatId, PatOrWild, WitnessPat}; -use crate::{Captures, MatchArm, TypeCx}; +use crate::{Captures, MatchArm, SkipField, TypeCx}; use self::ValidityConstraint::*; @@ -833,7 +833,9 @@ impl PlaceInfo { ) -> impl Iterator + ExactSizeIterator + Captures<'a> { let ctor_sub_tys = cx.ctor_sub_tys(ctor, &self.ty); let ctor_sub_validity = self.validity.specialize(ctor); - ctor_sub_tys.map(move |ty| PlaceInfo { + // Collect to keep the `ExactSizeIterator` bound. This is a temporary measure. + let tmp: Vec<_> = ctor_sub_tys.filter(|(_, SkipField(skip))| !skip).collect(); + tmp.into_iter().map(move |(ty, _)| PlaceInfo { ty, validity: ctor_sub_validity, is_scrutinee: false, From 4f7f06777bf67212aa960017ced0b911ab54bbf8 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 6 Feb 2024 02:53:37 +0100 Subject: [PATCH 33/62] Add special `Skip` constructor --- .../rustc_pattern_analysis/src/constructor.rs | 7 +++++ compiler/rustc_pattern_analysis/src/pat.rs | 4 ++- compiler/rustc_pattern_analysis/src/rustc.rs | 26 ++++--------------- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 483986969d1..26b0f5fc45d 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -688,6 +688,10 @@ pub enum Constructor { /// Fake extra constructor for constructors that are not seen in the matrix, as explained at the /// top of the file. Missing, + /// Fake extra constructor that indicates that we should skip the column entirely. This is used + /// when a private field is empty, so that we don't observe its emptiness. Only used for + /// specialization. + Skip, } impl Clone for Constructor { @@ -709,6 +713,7 @@ impl Clone for Constructor { Constructor::NonExhaustive => Constructor::NonExhaustive, Constructor::Hidden => Constructor::Hidden, Constructor::Missing => Constructor::Missing, + Constructor::Skip => Constructor::Skip, } } } @@ -763,6 +768,8 @@ impl Constructor { } // Wildcards cover anything (_, Wildcard) => true, + // `Skip` skips everything. + (Skip, _) => true, // Only a wildcard pattern can match these special constructors. (Missing { .. } | NonExhaustive | Hidden, _) => false, diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 3e89a894419..642ab74b8b9 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -84,6 +84,8 @@ impl DeconstructedPat { match (&self.ctor, other_ctor) { // Return a wildcard for each field of `other_ctor`. (Wildcard, _) => wildcard_sub_tys(), + // Skip this column. + (_, Skip) => smallvec![], // The only non-trivial case: two slices of different arity. `other_slice` is // guaranteed to have a larger arity, so we fill the middle part with enough // wildcards to reach the length of the new, larger slice. @@ -192,7 +194,7 @@ impl fmt::Debug for DeconstructedPat { } Ok(()) } - Wildcard | Missing { .. } | NonExhaustive | Hidden => write!(f, "_ : {:?}", pat.ty()), + Wildcard | Missing | NonExhaustive | Hidden | Skip => write!(f, "_ : {:?}", pat.ty()), } } } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 8d55dcf2056..3b68dd503ad 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -249,16 +249,8 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { } _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), }, - Bool(..) - | IntRange(..) - | F32Range(..) - | F64Range(..) - | Str(..) - | Opaque(..) - | NonExhaustive - | Hidden - | Missing { .. } - | Wildcard => &[], + Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..) + | NonExhaustive | Hidden | Missing | Skip | Wildcard => &[], Or => { bug!("called `Fields::wildcards` on an `Or` ctor") } @@ -288,16 +280,8 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { }, Ref => 1, Slice(slice) => slice.arity(), - Bool(..) - | IntRange(..) - | F32Range(..) - | F64Range(..) - | Str(..) - | Opaque(..) - | NonExhaustive - | Hidden - | Missing { .. } - | Wildcard => 0, + Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..) + | NonExhaustive | Hidden | Missing | Skip | Wildcard => 0, Or => bug!("The `Or` constructor doesn't have a fixed arity"), } } @@ -838,7 +822,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { } } &Str(value) => PatKind::Constant { value }, - Wildcard | NonExhaustive | Hidden => PatKind::Wild, + Wildcard | NonExhaustive | Hidden | Skip => PatKind::Wild, Missing { .. } => bug!( "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, `Missing` should have been processed in `apply_constructors`" From ea381663900b90c0f78c6a64cd5e0b1876047714 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 6 Feb 2024 03:50:22 +0100 Subject: [PATCH 34/62] Don't filter out skipped fields --- compiler/rustc_pattern_analysis/src/lib.rs | 3 +-- compiler/rustc_pattern_analysis/src/pat.rs | 5 ---- compiler/rustc_pattern_analysis/src/rustc.rs | 15 ++++------- .../rustc_pattern_analysis/src/usefulness.rs | 27 +++++++++++++++---- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 0b4bc77a976..e58e322a70d 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -109,8 +109,7 @@ pub trait TypeCx: Sized + fmt::Debug { /// The number of fields for this constructor. fn ctor_arity(&self, ctor: &Constructor, ty: &Self::Ty) -> usize; - /// The types of the fields for this constructor. The result must contain `ctor_arity()`-many - /// fields that are not skipped. + /// The types of the fields for this constructor. The result must contain `ctor_arity()` fields. fn ctor_sub_tys<'a>( &'a self, ctor: &'a Constructor, diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 642ab74b8b9..8e1c22b92c8 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -23,11 +23,6 @@ impl PatId { /// Values and patterns can be represented as a constructor applied to some fields. This represents /// a pattern in this form. A `DeconstructedPat` will almost always come from user input; the only /// exception are some `Wildcard`s introduced during pattern lowering. -/// -/// Note that the number of fields may not match the fields declared in the original struct/variant. -/// This happens if a private or `non_exhaustive` field is uninhabited, because the code mustn't -/// observe that it is uninhabited. In that case that field is not included in `fields`. Care must -/// be taken when converting to/from `thir::Pat`. pub struct DeconstructedPat { ctor: Constructor, fields: Vec>, diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 3b68dd503ad..4e90d1a7406 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -271,9 +271,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { } else { let variant = &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); - self.list_variant_nonhidden_fields(ty, variant) - .filter(|(_, _, skip)| !skip) - .count() + self.list_variant_nonhidden_fields(ty, variant).count() } } _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"), @@ -512,14 +510,12 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { // For each field in the variant, we store the relevant index into `self.fields` if any. let mut field_id_to_id: Vec> = (0..variant.fields.len()).map(|_| None).collect(); - let tys = cx - .list_variant_nonhidden_fields(ty, variant) - .filter(|(_, _, skip)| !skip) - .enumerate() - .map(|(i, (field, ty, _))| { + let tys = cx.list_variant_nonhidden_fields(ty, variant).enumerate().map( + |(i, (field, ty, _))| { field_id_to_id[field.index()] = Some(i); ty - }); + }, + ); fields = tys.map(|ty| DeconstructedPat::wildcard(ty)).collect(); for pat in subpatterns { if let Some(i) = field_id_to_id[pat.field.index()] { @@ -769,7 +765,6 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { let variant = &adt_def.variant(variant_index); let subpatterns = cx .list_variant_nonhidden_fields(*pat.ty(), variant) - .filter(|(_, _, skip)| !skip) .zip(subpatterns) .map(|((field, _ty, _), pattern)| FieldPat { field, pattern }) .collect(); diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index ec9f3bb0db9..99d1f46e804 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -817,6 +817,9 @@ impl fmt::Display for ValidityConstraint { struct PlaceInfo { /// The type of the place. ty: Cx::Ty, + /// Whether we must skip this field during analysis. This is used when a private field is empty, + /// so that we don't observe its emptiness. + skip: SkipField, /// Whether the place is known to contain valid data. validity: ValidityConstraint, /// Whether the place is the scrutinee itself or a subplace of it. @@ -833,10 +836,9 @@ impl PlaceInfo { ) -> impl Iterator + ExactSizeIterator + Captures<'a> { let ctor_sub_tys = cx.ctor_sub_tys(ctor, &self.ty); let ctor_sub_validity = self.validity.specialize(ctor); - // Collect to keep the `ExactSizeIterator` bound. This is a temporary measure. - let tmp: Vec<_> = ctor_sub_tys.filter(|(_, SkipField(skip))| !skip).collect(); - tmp.into_iter().map(move |(ty, _)| PlaceInfo { + ctor_sub_tys.map(move |(ty, skip)| PlaceInfo { ty, + skip, validity: ctor_sub_validity, is_scrutinee: false, }) @@ -858,6 +860,11 @@ impl PlaceInfo { where Cx: 'a, { + if matches!(self.skip, SkipField(true)) { + // Skip the whole column + return Ok((smallvec![Constructor::Skip], vec![])); + } + let ctors_for_ty = cx.ctors_for_ty(&self.ty)?; // We treat match scrutinees of type `!` or `EmptyEnum` differently. @@ -916,7 +923,12 @@ impl PlaceInfo { impl Clone for PlaceInfo { fn clone(&self) -> Self { - Self { ty: self.ty.clone(), validity: self.validity, is_scrutinee: self.is_scrutinee } + Self { + ty: self.ty.clone(), + skip: self.skip, + validity: self.validity, + is_scrutinee: self.is_scrutinee, + } } } @@ -1123,7 +1135,12 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> { scrut_ty: Cx::Ty, scrut_validity: ValidityConstraint, ) -> Self { - let place_info = PlaceInfo { ty: scrut_ty, validity: scrut_validity, is_scrutinee: true }; + let place_info = PlaceInfo { + ty: scrut_ty, + skip: SkipField(false), + validity: scrut_validity, + is_scrutinee: true, + }; let mut matrix = Matrix { rows: Vec::with_capacity(arms.len()), place_info: smallvec![place_info], From 39441e4cdd46c61be6b86e4bfe352d1e7d5af6fb Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 6 Feb 2024 03:12:21 +0100 Subject: [PATCH 35/62] Simplify --- compiler/rustc_pattern_analysis/src/rustc.rs | 80 +++++++++----------- 1 file changed, 34 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 4e90d1a7406..4f5f0383890 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -10,7 +10,7 @@ use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef}; +use rustc_middle::ty::{self, FieldDef, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef}; use rustc_session::lint; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; @@ -158,32 +158,19 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { } } - // In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide - // uninhabited fields in order not to reveal the uninhabitedness of the whole variant. - // This lists the fields we keep along with their types. - pub(crate) fn list_variant_nonhidden_fields( + pub(crate) fn variant_sub_tys( &self, ty: RevealedTy<'tcx>, variant: &'tcx VariantDef, - ) -> impl Iterator, bool)> + Captures<'p> + Captures<'_> + ) -> impl Iterator)> + Captures<'p> + Captures<'_> { - let cx = self; - let ty::Adt(adt, args) = ty.kind() else { bug!() }; - // Whether we must avoid matching the fields of this variant exhaustively. - let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did().is_local(); - - variant.fields.iter().enumerate().map(move |(i, field)| { - let ty = field.ty(cx.tcx, args); + let ty::Adt(_, args) = ty.kind() else { bug!() }; + variant.fields.iter().map(move |field| { + let ty = field.ty(self.tcx, args); // `field.ty()` doesn't normalize after instantiating. - let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); - let is_uninhabited = (cx.tcx.features().exhaustive_patterns - || cx.tcx.features().min_exhaustive_patterns) - && cx.is_uninhabited(ty); - - let skip = is_uninhabited && (!is_visible || is_non_exhaustive); - let ty = cx.reveal_opaque_ty(ty); - (FieldIdx::new(i), ty, skip) + let ty = self.tcx.normalize_erasing_regions(self.param_env, ty); + let ty = self.reveal_opaque_ty(ty); + (field, ty) }) } @@ -230,9 +217,21 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { } else { let variant = &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); - let tys = cx - .list_variant_nonhidden_fields(ty, variant) - .map(|(_, ty, skip)| (ty, SkipField(skip))); + + // In the cases of either a `#[non_exhaustive]` field list or a non-public + // field, we skip uninhabited fields in order not to reveal the + // uninhabitedness of the whole variant. + let is_non_exhaustive = + variant.is_field_list_non_exhaustive() && !adt.did().is_local(); + let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| { + let is_visible = + adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); + let is_uninhabited = (cx.tcx.features().exhaustive_patterns + || cx.tcx.features().min_exhaustive_patterns) + && cx.is_uninhabited(*ty); + let skip = is_uninhabited && (!is_visible || is_non_exhaustive); + (ty, SkipField(skip)) + }); cx.dropless_arena.alloc_from_iter(tys) } } @@ -269,9 +268,8 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { // patterns. If we're here we can assume this is a box pattern. 1 } else { - let variant = - &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); - self.list_variant_nonhidden_fields(ty, variant).count() + let variant_idx = RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt); + adt.variant(variant_idx).fields.len() } } _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"), @@ -507,20 +505,12 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { }; let variant = &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); - // For each field in the variant, we store the relevant index into `self.fields` if any. - let mut field_id_to_id: Vec> = - (0..variant.fields.len()).map(|_| None).collect(); - let tys = cx.list_variant_nonhidden_fields(ty, variant).enumerate().map( - |(i, (field, ty, _))| { - field_id_to_id[field.index()] = Some(i); - ty - }, - ); - fields = tys.map(|ty| DeconstructedPat::wildcard(ty)).collect(); + fields = cx + .variant_sub_tys(ty, variant) + .map(|(_, ty)| DeconstructedPat::wildcard(ty)) + .collect(); for pat in subpatterns { - if let Some(i) = field_id_to_id[pat.field.index()] { - fields[i] = self.lower_pat(&pat.pattern); - } + fields[pat.field.index()] = self.lower_pat(&pat.pattern); } } _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty), @@ -762,11 +752,9 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { ty::Adt(adt_def, args) => { let variant_index = RustcMatchCheckCtxt::variant_index_for_adt(&pat.ctor(), *adt_def); - let variant = &adt_def.variant(variant_index); - let subpatterns = cx - .list_variant_nonhidden_fields(*pat.ty(), variant) - .zip(subpatterns) - .map(|((field, _ty, _), pattern)| FieldPat { field, pattern }) + let subpatterns = subpatterns + .enumerate() + .map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern }) .collect(); if adt_def.is_enum() { From c918893b63022c1d810a71e8b7fa211b6aecd782 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 28 Feb 2024 17:56:01 +0100 Subject: [PATCH 36/62] Rename `Skip` to `PrivateUninhabited` --- .../rustc_pattern_analysis/src/constructor.rs | 13 +++++------ compiler/rustc_pattern_analysis/src/lib.rs | 7 +++--- compiler/rustc_pattern_analysis/src/pat.rs | 10 ++++---- compiler/rustc_pattern_analysis/src/rustc.rs | 23 +++++++++++-------- .../rustc_pattern_analysis/src/usefulness.rs | 18 +++++++-------- 5 files changed, 38 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 26b0f5fc45d..1286022fe4d 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -688,10 +688,9 @@ pub enum Constructor { /// Fake extra constructor for constructors that are not seen in the matrix, as explained at the /// top of the file. Missing, - /// Fake extra constructor that indicates that we should skip the column entirely. This is used - /// when a private field is empty, so that we don't observe its emptiness. Only used for - /// specialization. - Skip, + /// Fake extra constructor that indicates and empty field that is private. When we encounter one + /// we skip the column entirely so we don't observe its emptiness. Only used for specialization. + PrivateUninhabited, } impl Clone for Constructor { @@ -713,7 +712,7 @@ impl Clone for Constructor { Constructor::NonExhaustive => Constructor::NonExhaustive, Constructor::Hidden => Constructor::Hidden, Constructor::Missing => Constructor::Missing, - Constructor::Skip => Constructor::Skip, + Constructor::PrivateUninhabited => Constructor::PrivateUninhabited, } } } @@ -768,8 +767,8 @@ impl Constructor { } // Wildcards cover anything (_, Wildcard) => true, - // `Skip` skips everything. - (Skip, _) => true, + // `PrivateUninhabited` skips everything. + (PrivateUninhabited, _) => true, // Only a wildcard pattern can match these special constructors. (Missing { .. } | NonExhaustive | Hidden, _) => false, diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index e58e322a70d..d4b38d260e7 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -82,9 +82,10 @@ use crate::usefulness::{compute_match_usefulness, ValidityConstraint}; pub trait Captures<'a> {} impl<'a, T: ?Sized> Captures<'a> for T {} -/// `bool` newtype that indicates whether we should skip this field during analysis. +/// `bool` newtype that indicates whether this is a privately uninhabited field that we should skip +/// during analysis. #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct SkipField(pub bool); +pub struct PrivateUninhabitedField(pub bool); /// Context that provides type information about constructors. /// @@ -114,7 +115,7 @@ pub trait TypeCx: Sized + fmt::Debug { &'a self, ctor: &'a Constructor, ty: &'a Self::Ty, - ) -> impl Iterator + ExactSizeIterator + Captures<'a>; + ) -> impl Iterator + ExactSizeIterator + Captures<'a>; /// The set of all the constructors for `ty`. /// diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 8e1c22b92c8..decbfa5c0cf 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -5,7 +5,7 @@ use std::fmt; use smallvec::{smallvec, SmallVec}; use crate::constructor::{Constructor, Slice, SliceKind}; -use crate::{SkipField, TypeCx}; +use crate::{PrivateUninhabitedField, TypeCx}; use self::Constructor::*; @@ -80,7 +80,7 @@ impl DeconstructedPat { // Return a wildcard for each field of `other_ctor`. (Wildcard, _) => wildcard_sub_tys(), // Skip this column. - (_, Skip) => smallvec![], + (_, PrivateUninhabited) => smallvec![], // The only non-trivial case: two slices of different arity. `other_slice` is // guaranteed to have a larger arity, so we fill the middle part with enough // wildcards to reach the length of the new, larger slice. @@ -189,7 +189,9 @@ impl fmt::Debug for DeconstructedPat { } Ok(()) } - Wildcard | Missing | NonExhaustive | Hidden | Skip => write!(f, "_ : {:?}", pat.ty()), + Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => { + write!(f, "_ : {:?}", pat.ty()) + } } } } @@ -299,7 +301,7 @@ impl WitnessPat { pub(crate) fn wild_from_ctor(cx: &Cx, ctor: Constructor, ty: Cx::Ty) -> Self { let fields = cx .ctor_sub_tys(&ctor, &ty) - .filter(|(_, SkipField(skip))| !skip) + .filter(|(_, PrivateUninhabitedField(skip))| !skip) .map(|(ty, _)| Self::wildcard(ty)) .collect(); Self::new(ctor, fields, ty) diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 4f5f0383890..7a0562e12f1 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -18,7 +18,7 @@ use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; use crate::constructor::{ IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility, }; -use crate::{errors, Captures, SkipField, TypeCx}; +use crate::{errors, Captures, PrivateUninhabitedField, TypeCx}; use crate::constructor::Constructor::*; @@ -195,14 +195,16 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { &'a self, ctor: &'a Constructor<'p, 'tcx>, ty: RevealedTy<'tcx>, - ) -> impl Iterator, SkipField)> + ExactSizeIterator + Captures<'a> - { + ) -> impl Iterator, PrivateUninhabitedField)> + + ExactSizeIterator + + Captures<'a> { fn reveal_and_alloc<'a, 'tcx>( cx: &'a RustcMatchCheckCtxt<'_, 'tcx>, iter: impl Iterator>, - ) -> &'a [(RevealedTy<'tcx>, SkipField)] { + ) -> &'a [(RevealedTy<'tcx>, PrivateUninhabitedField)] { cx.dropless_arena.alloc_from_iter( - iter.map(|ty| cx.reveal_opaque_ty(ty)).map(|ty| (ty, SkipField(false))), + iter.map(|ty| cx.reveal_opaque_ty(ty)) + .map(|ty| (ty, PrivateUninhabitedField(false))), ) } let cx = self; @@ -230,7 +232,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { || cx.tcx.features().min_exhaustive_patterns) && cx.is_uninhabited(*ty); let skip = is_uninhabited && (!is_visible || is_non_exhaustive); - (ty, SkipField(skip)) + (ty, PrivateUninhabitedField(skip)) }); cx.dropless_arena.alloc_from_iter(tys) } @@ -249,7 +251,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), }, Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..) - | NonExhaustive | Hidden | Missing | Skip | Wildcard => &[], + | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => &[], Or => { bug!("called `Fields::wildcards` on an `Or` ctor") } @@ -277,7 +279,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { Ref => 1, Slice(slice) => slice.arity(), Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..) - | NonExhaustive | Hidden | Missing | Skip | Wildcard => 0, + | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => 0, Or => bug!("The `Or` constructor doesn't have a fixed arity"), } } @@ -805,7 +807,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { } } &Str(value) => PatKind::Constant { value }, - Wildcard | NonExhaustive | Hidden | Skip => PatKind::Wild, + Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild, Missing { .. } => bug!( "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, `Missing` should have been processed in `apply_constructors`" @@ -841,7 +843,8 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { &'a self, ctor: &'a crate::constructor::Constructor, ty: &'a Self::Ty, - ) -> impl Iterator + ExactSizeIterator + Captures<'a> { + ) -> impl Iterator + ExactSizeIterator + Captures<'a> + { self.ctor_sub_tys(ctor, *ty) } fn ctors_for_ty( diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 99d1f46e804..bbe02f94c0a 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -716,7 +716,7 @@ use std::fmt; use crate::constructor::{Constructor, ConstructorSet, IntRange}; use crate::pat::{DeconstructedPat, PatId, PatOrWild, WitnessPat}; -use crate::{Captures, MatchArm, SkipField, TypeCx}; +use crate::{Captures, MatchArm, PrivateUninhabitedField, TypeCx}; use self::ValidityConstraint::*; @@ -817,9 +817,9 @@ impl fmt::Display for ValidityConstraint { struct PlaceInfo { /// The type of the place. ty: Cx::Ty, - /// Whether we must skip this field during analysis. This is used when a private field is empty, + /// Whether the place is a private uninhabited field. If so we skip this field during analysis /// so that we don't observe its emptiness. - skip: SkipField, + private_uninhabited: bool, /// Whether the place is known to contain valid data. validity: ValidityConstraint, /// Whether the place is the scrutinee itself or a subplace of it. @@ -836,9 +836,9 @@ impl PlaceInfo { ) -> impl Iterator + ExactSizeIterator + Captures<'a> { let ctor_sub_tys = cx.ctor_sub_tys(ctor, &self.ty); let ctor_sub_validity = self.validity.specialize(ctor); - ctor_sub_tys.map(move |(ty, skip)| PlaceInfo { + ctor_sub_tys.map(move |(ty, PrivateUninhabitedField(private_uninhabited))| PlaceInfo { ty, - skip, + private_uninhabited, validity: ctor_sub_validity, is_scrutinee: false, }) @@ -860,9 +860,9 @@ impl PlaceInfo { where Cx: 'a, { - if matches!(self.skip, SkipField(true)) { + if self.private_uninhabited { // Skip the whole column - return Ok((smallvec![Constructor::Skip], vec![])); + return Ok((smallvec![Constructor::PrivateUninhabited], vec![])); } let ctors_for_ty = cx.ctors_for_ty(&self.ty)?; @@ -925,7 +925,7 @@ impl Clone for PlaceInfo { fn clone(&self) -> Self { Self { ty: self.ty.clone(), - skip: self.skip, + private_uninhabited: self.private_uninhabited, validity: self.validity, is_scrutinee: self.is_scrutinee, } @@ -1137,7 +1137,7 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> { ) -> Self { let place_info = PlaceInfo { ty: scrut_ty, - skip: SkipField(false), + private_uninhabited: false, validity: scrut_validity, is_scrutinee: true, }; From b4a07244a1dcc11fe48f233c12097960fcfebed7 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 21 Feb 2024 12:46:23 -0800 Subject: [PATCH 37/62] Clarify the usage example for hexagon-unknown-none-elf The C wrapper program represents a typical use case (linking C libraries with Rust libraries) but it was not made explicit how this was supposed to work in the usage example. Also: correct a table alignment error for hexagon-unknown-none-elf on the general platform support doc. --- src/doc/rustc/src/platform-support.md | 2 +- .../hexagon-unknown-none-elf.md | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 80d9dc85f16..25c28711330 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -286,7 +286,7 @@ target | std | host | notes `bpfel-unknown-none` | * | | BPF (little endian) `csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian) `csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) -[`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | Bare Hexagon (v60+, HVX) +[`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | | Bare Hexagon (v60+, HVX) `hexagon-unknown-linux-musl` | ? | | `i386-apple-ios` | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI] [`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI] diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md b/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md index 3ac1d2c2460..a0e26b798ac 100644 --- a/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md +++ b/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md @@ -41,6 +41,8 @@ target = ["", "hexagon-unknown-none-elf"] cc = "hexagon-unknown-none-elf-clang" cxx = "hexagon-unknown-none-elf-clang++" linker = "hexagon-unknown-none-elf-clang" +ranlib = "hexagon-unknown-none-elf-ranlib" +ar = "hexagon-unknown-none-elf-ar" llvm-libunwind = 'in-tree' ``` @@ -142,7 +144,7 @@ ${cc} --target=hexagon-unknown-none-elf -o testit \ ${g0_lib_path}/init.o \ -L${sdk_libs}/${q6_arch}/ \ -L${sdk_libs}/ \ - testit.c \ + wrap.c \ target/hexagon-unknown-none-elf/${build_cfg}/libdemo1_hexagon.rlib \ target/hexagon-unknown-none-elf/${build_cfg}/deps/libcore-*.rlib \ target/hexagon-unknown-none-elf/${build_cfg}/deps/libcompiler_builtins-*.rlib \ @@ -217,7 +219,18 @@ fn rust_eh_personality() {} ``` -Next, save the script below as `build.sh` and edit it to suit your +Next, create a C program as an entry point, save the content below as +`wrap.c`: + +```C +int hello(); + +int main() { + hello(); +} +``` + +Then, save the script below as `build.sh` and edit it to suit your environment. The script below will build a shared object against the QuRT RTOS which is suitable for emulation or on-device testing when loaded via the fastrpc-shell. @@ -248,7 +261,7 @@ ${cc} --target=hexagon-unknown-none-elf -o testit.so \ -Wl,--wrap=realloc \ -Wl,--wrap=memalign \ -m${q6_arch} \ - testit.c \ + wrap.c \ target/hexagon-unknown-none-elf/${build_cfg}/libdemo2_hexagon.rlib \ target/hexagon-unknown-none-elf/${build_cfg}/deps/libcore-*.rlib \ target/hexagon-unknown-none-elf/${build_cfg}/deps/libcompiler_builtins-*.rlib \ From cd4773968827c820c02a1878df9447e59b7df371 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 21 Feb 2024 12:48:51 -0800 Subject: [PATCH 38/62] Add a platform doc for hexagon-unknown-linux-musl --- src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 2 +- .../hexagon-unknown-linux-musl.md | 102 ++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 1f9307203bc..f8bfb55e4ca 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -39,6 +39,7 @@ - [\*-unknown-fuchsia](platform-support/fuchsia.md) - [\*-kmc-solid_\*](platform-support/kmc-solid.md) - [csky-unknown-linux-gnuabiv2\*](platform-support/csky-unknown-linux-gnuabiv2.md) + - [hexagon-unknown-linux-musl](platform-support/hexagon-unknown-linux-musl.md) - [hexagon-unknown-none-elf](platform-support/hexagon-unknown-none-elf.md) - [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md) - [loongarch\*-unknown-none\*](platform-support/loongarch-none.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 25c28711330..65cffeae499 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -287,7 +287,7 @@ target | std | host | notes `csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian) `csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) [`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | | Bare Hexagon (v60+, HVX) -`hexagon-unknown-linux-musl` | ? | | +[`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux `i386-apple-ios` | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI] [`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI] [`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86, restricted to Pentium diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md new file mode 100644 index 00000000000..c1372726a35 --- /dev/null +++ b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md @@ -0,0 +1,102 @@ +# `hexagon-unknown-linux-musl` + +**Tier: 3** + +Target for cross-compiling Linux user-mode applications targeting the Hexagon +DSP architecture. + +| Target | Descriptions | +| ------------------------ | ----------------------------------------- | +| hexagon-unknown-linux-musl | Hexagon 32-bit Linux | + +## Target maintainers + +- [Brian Cain](https://github.com/androm3da), `bcain@quicinc.com` + +## Requirements +The target is cross-compiled. This target supports `std`. By default, code +generated with this target should run on Hexagon DSP hardware. + +- `-Ctarget-cpu=hexagonv73` adds support for instructions defined up to Hexagon V73. + +Binaries can be run using QEMU user emulation. On Debian-based systems, it should be +sufficient to install the package `qemu-user-static` to be able to run simple static +binaries: + +```text +# apt install qemu-user-static +# qemu-hexagon-static ./hello +``` + +In order to build linux programs with Rust, you will require a linker capable +of targeting hexagon. You can use `clang`/`lld` from the [hexagon toolchain +using exclusively public open source repos](https://github.com/quic/toolchain_for_hexagon/releases). + +Also included in that toolchain is the C library that can be used when creating +dynamically linked executables. + +```text +# /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/qemu-hexagon -L /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr/ ./hello +``` + +## Building the target +Because it is Tier 3, rust does not yet ship pre-compiled artifacts for this +target. + +Therefore, you can build Rust with support for the target by adding it to the +target list in `config.toml`, a sample configuration is shown below. + +```toml +[build] +target = [ "hexagon-unknown-linux-musl"] + +[target.hexagon-unknown-linux-musl] + +cc = "hexagon-unknown-linux-musl-clang" +cxx = "hexagon-unknown-linux-musl-clang++" +linker = "hexagon-unknown-linux-musl-clang" +ar = "hexagon-unknown-linux-musl-ar" +ranlib = "hexagon-unknown-linux-musl-ranlib" +musl-root = "/opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr" +llvm-libunwind = 'in-tree' +qemu-rootfs = "/opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr" +``` + + +## Testing + +Currently there is no support to run the rustc test suite for this target. + + +## Building Rust programs + +Download and install the hexagon open source toolchain from https://github.com/quic/toolchain_for_hexagon/releases + +The following `.cargo/config` is needed inside any project directory to build +for the Hexagon Linux target: + +```toml +[build] +target = "hexagon-unknown-linux-musl" + +[target.hexagon-unknown-linux-musl] +linker = "hexagon-unknown-linux-musl-clang" +ar = "hexagon-unknown-linux-musl-ar" +runner = "qemu-hexagon -L /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr" +``` + +Edit the "runner" in `.cargo/config` to point to the path to your toolchain's +C library. + +```text +... +runner = "qemu-hexagon -L /path/to/my/inst/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr" +... +``` + +Build/run your rust program with `qemu-hexagon` in your `PATH`: + +```text +export PATH=/path/to/my/inst/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/:$PATH +cargo run -Zbuild-std -Zbuild-std-features=llvm-libunwind +``` From 3726cbb5fefe28c922f5c265231a3d00bca198d5 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 28 Feb 2024 23:01:56 +0100 Subject: [PATCH 39/62] add platform-specific function to get the error number for HermitOS Extending `std` to get the last error number for HermitOS. HermitOS is a tier 3 platform and this PR changes only files, wich are related to the tier 3 platform. --- Cargo.lock | 4 ++-- library/std/Cargo.toml | 2 +- library/std/src/sys/pal/hermit/os.rs | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d216f26b61..04e6139b7e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1640,9 +1640,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 20f4310603a..d2804b4d20a 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -48,7 +48,7 @@ dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] } fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public = true } [target.'cfg(target_os = "hermit")'.dependencies] -hermit-abi = { version = "0.3.2", features = ['rustc-dep-of-std'], public = true } +hermit-abi = { version = "0.3.9", features = ['rustc-dep-of-std'], public = true } [target.'cfg(target_os = "wasi")'.dependencies] wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index a54536aecb8..645f0dc1e31 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -14,15 +14,15 @@ use crate::sys::unsupported; use crate::vec; pub fn errno() -> i32 { - 0 + unsafe { abi::get_errno() } } -pub fn error_string(_errno: i32) -> String { - "operation successful".to_string() +pub fn error_string(errno: i32) -> String { + abi::error_string(errno).to_string() } pub fn getcwd() -> io::Result { - unsupported() + Ok(PathBuf::from("/")) } pub fn chdir(_: &path::Path) -> io::Result<()> { @@ -188,7 +188,7 @@ pub fn unsetenv(k: &OsStr) -> io::Result<()> { } pub fn temp_dir() -> PathBuf { - panic!("no filesystem on hermit") + PathBuf::from("/tmp") } pub fn home_dir() -> Option { From 55129453c6cfed8884e58be1f7435d4e35a77f0d Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Wed, 28 Feb 2024 14:56:36 -0800 Subject: [PATCH 40/62] Implement unwind safety for Condvar Closes #118009 This commit adds unwind safety to Condvar. Previously, only select platforms implemented unwind safety through auto traits. Known by this committer: Linux was unwind safe, but Mac and Windows are not before this change. --- library/std/src/panic.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 3728d5b64b8..3d576af681e 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -6,7 +6,7 @@ use crate::any::Any; use crate::collections; use crate::panicking; use crate::sync::atomic::{AtomicU8, Ordering}; -use crate::sync::{Mutex, RwLock}; +use crate::sync::{Condvar, Mutex, RwLock}; use crate::thread::Result; #[doc(hidden)] @@ -67,11 +67,15 @@ pub fn panic_any(msg: M) -> ! { impl UnwindSafe for Mutex {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl UnwindSafe for RwLock {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for Condvar {} #[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] impl RefUnwindSafe for Mutex {} #[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] impl RefUnwindSafe for RwLock {} +#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] +impl RefUnwindSafe for Condvar {} // https://github.com/rust-lang/rust/issues/62301 #[stable(feature = "hashbrown", since = "1.36.0")] From c4ec196c7edf3f6d193b8b8da49ead7ceffa9799 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 26 Feb 2024 16:54:53 +1100 Subject: [PATCH 41/62] Don't cancel stashed `OpaqueHiddenTypeMismatch` errors. This gives one extra error message on one test, but is necessary to fix bigger problems caused by the cancellation of stashed errors. (Note: why not just avoid stashing altogether? Because that resulted in additional output changes.) --- compiler/rustc_middle/src/ty/mod.rs | 6 +++++- .../different_defining_uses_never_type-2.rs | 1 + .../different_defining_uses_never_type-2.stderr | 14 +++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index d97f0e4c321..9089d992cd5 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -852,7 +852,11 @@ impl<'tcx> OpaqueHiddenType<'tcx> { .dcx() .steal_diagnostic(tcx.def_span(opaque_def_id), StashKey::OpaqueHiddenTypeMismatch) { - diag.cancel(); + // We used to cancel here for slightly better error messages, but + // cancelling stashed diagnostics is no longer allowed because it + // causes problems when tracking whether errors have actually + // occurred. + diag.emit(); } (self.ty, other.ty).error_reported()?; // Found different concrete types for the opaque type. diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.rs b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.rs index b2842df150a..4b5f455e381 100644 --- a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.rs +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.rs @@ -11,6 +11,7 @@ fn foo<'a, 'b>() -> Tait<'a> { } let x: Tait<'a> = (); x + //~^ ERROR concrete type differs from previous defining opaque type use } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.stderr b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.stderr index e5cee49cf29..6f5be5467f7 100644 --- a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.stderr +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.stderr @@ -1,3 +1,15 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/different_defining_uses_never_type-2.rs:13:5 + | +LL | x + | ^ expected `i32`, got `()` + | +note: previous use here + --> $DIR/different_defining_uses_never_type-2.rs:8:31 + | +LL | let y: Tait<'b> = 1i32; + | ^^^^ + error: concrete type differs from previous defining opaque type use --> $DIR/different_defining_uses_never_type-2.rs:8:31 | @@ -10,5 +22,5 @@ note: previous use here LL | if { return } { | ^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors From ec25d6db53768a4bb68f621be3584941ac3fe416 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 26 Feb 2024 17:22:24 +1100 Subject: [PATCH 42/62] Don't cancel stashed `TraitMissingMethod` errors. This gives one extra error message on two tests, but is necessary to fix bigger problems caused by the cancellation of stashed errors. (Note: why not just avoid stashing altogether? Because that resulted in additional output changes.) --- compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs | 11 ++++------- tests/ui/resolve/issue-111312.rs | 4 +++- tests/ui/resolve/issue-111312.stderr | 16 ++++++++++++++-- tests/ui/resolve/issue-111727.rs | 4 +++- tests/ui/resolve/issue-111727.stderr | 16 ++++++++++++++-- 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index a5892dea1a5..1e6467deb01 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -879,17 +879,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - // emit or cancel the diagnostic for bare traits + // Emit the diagnostic for bare traits. (We used to cancel for slightly better + // error messages, but cancelling stashed diagnostics is no longer allowed because + // it causes problems when tracking whether errors have actually occurred.) if span.edition().at_least_rust_2021() && let Some(diag) = self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { - if trait_missing_method { - // cancel the diag for bare traits when meeting `MyTrait::missing_method` - diag.cancel(); - } else { - diag.emit(); - } + diag.emit(); } if item_name.name != kw::Empty { diff --git a/tests/ui/resolve/issue-111312.rs b/tests/ui/resolve/issue-111312.rs index 68fc8573dde..79c6f67dadd 100644 --- a/tests/ui/resolve/issue-111312.rs +++ b/tests/ui/resolve/issue-111312.rs @@ -7,5 +7,7 @@ trait Has { trait HasNot {} fn main() { - HasNot::has(); //~ ERROR + HasNot::has(); + //~^ ERROR trait objects must include the `dyn` keyword + //~| ERROR no function or associated item named `has` found for trait `HasNot` } diff --git a/tests/ui/resolve/issue-111312.stderr b/tests/ui/resolve/issue-111312.stderr index 7e7ef22ae61..431802ead30 100644 --- a/tests/ui/resolve/issue-111312.stderr +++ b/tests/ui/resolve/issue-111312.stderr @@ -1,3 +1,14 @@ +error[E0782]: trait objects must include the `dyn` keyword + --> $DIR/issue-111312.rs:10:5 + | +LL | HasNot::has(); + | ^^^^^^ + | +help: add `dyn` keyword before this trait + | +LL | ::has(); + | ++++ + + error[E0599]: no function or associated item named `has` found for trait `HasNot` --> $DIR/issue-111312.rs:10:13 | @@ -10,6 +21,7 @@ note: `Has` defines an item `has` LL | trait Has { | ^^^^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0599`. +Some errors have detailed explanations: E0599, E0782. +For more information about an error, try `rustc --explain E0599`. diff --git a/tests/ui/resolve/issue-111727.rs b/tests/ui/resolve/issue-111727.rs index 740037fe434..fcab924b809 100644 --- a/tests/ui/resolve/issue-111727.rs +++ b/tests/ui/resolve/issue-111727.rs @@ -1,5 +1,7 @@ //@ edition: 2021 fn main() { - std::any::Any::create(); //~ ERROR + std::any::Any::create(); + //~^ ERROR trait objects must include the `dyn` keyword + //~| ERROR no function or associated item named `create` found for trait `Any` } diff --git a/tests/ui/resolve/issue-111727.stderr b/tests/ui/resolve/issue-111727.stderr index b58168d0e75..1ef5a1a1d5e 100644 --- a/tests/ui/resolve/issue-111727.stderr +++ b/tests/ui/resolve/issue-111727.stderr @@ -1,9 +1,21 @@ +error[E0782]: trait objects must include the `dyn` keyword + --> $DIR/issue-111727.rs:4:5 + | +LL | std::any::Any::create(); + | ^^^^^^^^^^^^^ + | +help: add `dyn` keyword before this trait + | +LL | ::create(); + | ++++ + + error[E0599]: no function or associated item named `create` found for trait `Any` --> $DIR/issue-111727.rs:4:20 | LL | std::any::Any::create(); | ^^^^^^ function or associated item not found in `Any` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0599`. +Some errors have detailed explanations: E0599, E0782. +For more information about an error, try `rustc --explain E0599`. From 3fbdec4937a95a5af04186a799dc3b07840a4599 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 28 Feb 2024 08:48:19 +1100 Subject: [PATCH 43/62] Add a comment about how `IntoDiagnostic` should be impl'd. --- compiler/rustc_errors/src/diagnostic.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 01f36ad6a78..0cf519d2029 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -108,6 +108,25 @@ impl EmissionGuarantee for rustc_span::fatal_error::FatalError { /// Trait implemented by error types. This is rarely implemented manually. Instead, use /// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic]. +/// +/// When implemented manually, it should be generic over the emission +/// guarantee, i.e.: +/// ```ignore (fragment) +/// impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for Foo { ... } +/// ``` +/// rather than being specific: +/// ```ignore (fragment) +/// impl<'a> IntoDiagnostic<'a> for Bar { ... } // the default type param is `ErrorGuaranteed` +/// impl<'a> IntoDiagnostic<'a, ()> for Baz { ... } +/// ``` +/// There are two reasons for this. +/// - A diagnostic like `Foo` *could* be emitted at any level -- `level` is +/// passed in to `into_diagnostic` from outside. Even if in practice it is +/// always emitted at a single level, we let the diagnostic creation/emission +/// site determine the level (by using `create_err`, `emit_warn`, etc.) +/// rather than the `IntoDiagnostic` impl. +/// - Derived impls are always generic, and it's good for the hand-written +/// impls to be consistent with them. #[rustc_diagnostic_item = "IntoDiagnostic"] pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> { /// Write out as a diagnostic out of `DiagCtxt`. From 199be469ec37b30c876069762e20f531f1f12f22 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 28 Feb 2024 09:43:23 +1100 Subject: [PATCH 44/62] Refactor `DiagCtxtInner::flush_delayed`. This commit: - Moves the ICE file create/open outside the loop. (Redoing it on every loop iteration works, but is really weird.) - Moves the explanatory note emission above the loop, which removes the need for the `enumerate` call. - Introduces a `decorate` local. --- compiler/rustc_errors/src/lib.rs | 42 ++++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index bc338b01d8b..2652feff62a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1498,14 +1498,26 @@ impl DiagCtxtInner { let bugs: Vec<_> = std::mem::take(&mut self.delayed_bugs).into_iter().map(|(b, _)| b).collect(); - // If backtraces are enabled, also print the query stack let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0"); - for (i, bug) in bugs.into_iter().enumerate() { - if let Some(file) = self.ice_file.as_ref() - && let Ok(mut out) = std::fs::File::options().create(true).append(true).open(file) - { - let _ = write!( - &mut out, + let decorate = backtrace || self.ice_file.is_none(); + let mut out = self + .ice_file + .as_ref() + .and_then(|file| std::fs::File::options().create(true).append(true).open(file).ok()); + + // Put the overall explanation before the `DelayedBug`s, to frame them + // better (e.g. separate warnings from them). Also, use notes, which + // don't count as errors, to avoid possibly triggering + // `-Ztreat-err-as-bug`, which we don't want. + let note1 = "no errors encountered even though delayed bugs were created"; + let note2 = "those delayed bugs will now be shown as internal compiler errors"; + self.emit_diagnostic(DiagInner::new(Note, note1)); + self.emit_diagnostic(DiagInner::new(Note, note2)); + + for bug in bugs { + if let Some(out) = &mut out { + _ = write!( + out, "delayed bug: {}\n{}\n", bug.inner .messages @@ -1516,21 +1528,9 @@ impl DiagCtxtInner { ); } - if i == 0 { - // Put the overall explanation before the `DelayedBug`s, to - // frame them better (e.g. separate warnings from them). Also, - // make it a note so it doesn't count as an error, because that - // could trigger `-Ztreat-err-as-bug`, which we don't want. - let note1 = "no errors encountered even though delayed bugs were created"; - let note2 = "those delayed bugs will now be shown as internal compiler errors"; - self.emit_diagnostic(DiagInner::new(Note, note1)); - self.emit_diagnostic(DiagInner::new(Note, note2)); - } + let mut bug = if decorate { bug.decorate(self) } else { bug.inner }; - let mut bug = - if backtrace || self.ice_file.is_none() { bug.decorate(self) } else { bug.inner }; - - // "Undelay" the delayed bugs (into plain `Bug`s). + // "Undelay" the delayed bugs into plain bugs. if bug.level != DelayedBug { // NOTE(eddyb) not panicking here because we're already producing // an ICE, and the more information the merrier. From 260ae701405f1278202de219bcdd0d60e8060da9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 26 Feb 2024 15:21:01 +1100 Subject: [PATCH 45/62] Overhaul how stashed diagnostics work, again. Stashed errors used to be counted as errors, but could then be cancelled, leading to `ErrorGuaranteed` soundness holes. #120828 changed that, closing the soundness hole. But it introduced other difficulties because you sometimes have to account for pending stashed errors when making decisions about whether errors have occured/will occur and it's easy to overlook these. This commit aims for a middle ground. - Stashed errors (not warnings) are counted immediately as emitted errors, avoiding the possibility of forgetting to consider them. - The ability to cancel (or downgrade) stashed errors is eliminated, by disallowing the use of `steal_diagnostic` with errors, and introducing the more restrictive methods `try_steal_{modify,replace}_and_emit_err` that can be used instead. Other things: - `DiagnosticBuilder::stash` and `DiagCtxt::stash_diagnostic` now both return `Option`, which enables the removal of two `delayed_bug` calls and one `Ty::new_error_with_message` call. This is possible because we store error guarantees in `DiagCtxt::stashed_diagnostics`. - Storing the guarantees also saves us having to maintain a counter. - Calls to the `stashed_err_count` method are no longer necessary alongside calls to `has_errors`, which is a nice simplification, and eliminates two more `span_delayed_bug` calls and one FIXME comment. - Tests are added for three of the four fixed PRs mentioned below. - `issue-121108.rs`'s output improved slightly, omitting a non-useful error message. Fixes #121451. Fixes #121477. Fixes #121504. Fixes #121508. --- compiler/rustc_errors/src/diagnostic.rs | 8 +- compiler/rustc_errors/src/lib.rs | 211 +++++++++++++----- .../rustc_hir_analysis/src/astconv/lint.rs | 12 +- .../rustc_hir_analysis/src/check/check.rs | 3 +- .../rustc_hir_analysis/src/collect/type_of.rs | 22 +- compiler/rustc_hir_typeck/src/callee.rs | 17 +- compiler/rustc_hir_typeck/src/expr.rs | 45 ++-- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 22 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 76 +++---- .../rustc_hir_typeck/src/method/suggest.rs | 100 ++++----- compiler/rustc_hir_typeck/src/writeback.rs | 4 - compiler/rustc_infer/src/infer/at.rs | 1 - compiler/rustc_infer/src/infer/mod.rs | 15 -- compiler/rustc_middle/src/ty/mod.rs | 20 +- compiler/rustc_middle/src/ty/region.rs | 4 +- compiler/rustc_middle/src/ty/sty.rs | 4 +- compiler/rustc_parse/src/parser/expr.rs | 29 +-- compiler/rustc_passes/src/check_attr.rs | 20 +- .../rustc_query_system/src/query/plumbing.rs | 3 +- compiler/rustc_session/src/parse.rs | 3 +- .../error_reporting/type_err_ctxt_ext.rs | 18 +- .../ui/crashes/unreachable-array-or-slice.rs | 8 + .../crashes/unreachable-array-or-slice.stderr | 9 + tests/ui/foreign/stashed-issue-121451.rs | 4 + tests/ui/foreign/stashed-issue-121451.stderr | 9 + .../impl-trait/stashed-diag-issue-121504.rs | 13 ++ .../stashed-diag-issue-121504.stderr | 9 + tests/ui/lowering/issue-121108.rs | 2 +- tests/ui/lowering/issue-121108.stderr | 10 +- 29 files changed, 406 insertions(+), 295 deletions(-) create mode 100644 src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.rs create mode 100644 src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.stderr create mode 100644 tests/ui/foreign/stashed-issue-121451.rs create mode 100644 tests/ui/foreign/stashed-issue-121451.stderr create mode 100644 tests/ui/impl-trait/stashed-diag-issue-121504.rs create mode 100644 tests/ui/impl-trait/stashed-diag-issue-121504.stderr diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 01f36ad6a78..207c616cefc 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1289,11 +1289,9 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { drop(self); } - /// Stashes diagnostic for possible later improvement in a different, - /// later stage of the compiler. The diagnostic can be accessed with - /// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`]. - pub fn stash(mut self, span: Span, key: StashKey) { - self.dcx.stash_diagnostic(span, key, self.take_diag()); + /// See `DiagCtxt::stash_diagnostic` for details. + pub fn stash(mut self, span: Span, key: StashKey) -> Option { + self.dcx.stash_diagnostic(span, key, self.take_diag()) } /// Delay emission of this diagnostic as a bug. diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index bc338b01d8b..60bda9f4c94 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -434,10 +434,6 @@ struct DiagCtxtInner { /// The delayed bugs and their error guarantees. delayed_bugs: Vec<(DelayedDiagInner, ErrorGuaranteed)>, - /// The number of stashed errors. Unlike the other counts, this can go up - /// and down, so it doesn't guarantee anything. - stashed_err_count: usize, - /// The error count shown to the user at the end. deduplicated_err_count: usize, /// The warning count shown to the user at the end. @@ -475,7 +471,7 @@ struct DiagCtxtInner { /// add more information). All stashed diagnostics must be emitted with /// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped, /// otherwise an assertion failure will occur. - stashed_diagnostics: FxIndexMap<(Span, StashKey), DiagInner>, + stashed_diagnostics: FxIndexMap<(Span, StashKey), (DiagInner, Option)>, future_breakage_diagnostics: Vec, @@ -561,8 +557,14 @@ impl Drop for DiagCtxtInner { fn drop(&mut self) { // Any stashed diagnostics should have been handled by // `emit_stashed_diagnostics` by now. + // + // Important: it is sound to produce an `ErrorGuaranteed` when stashing + // errors because they are guaranteed to have been emitted by here. assert!(self.stashed_diagnostics.is_empty()); + // Important: it is sound to produce an `ErrorGuaranteed` when emitting + // delayed bugs because they are guaranteed to be emitted here if + // necessary. if self.err_guars.is_empty() { self.flush_delayed() } @@ -615,7 +617,6 @@ impl DiagCtxt { err_guars: Vec::new(), lint_err_guars: Vec::new(), delayed_bugs: Vec::new(), - stashed_err_count: 0, deduplicated_err_count: 0, deduplicated_warn_count: 0, emitter, @@ -676,7 +677,6 @@ impl DiagCtxt { err_guars, lint_err_guars, delayed_bugs, - stashed_err_count, deduplicated_err_count, deduplicated_warn_count, emitter: _, @@ -699,7 +699,6 @@ impl DiagCtxt { *err_guars = Default::default(); *lint_err_guars = Default::default(); *delayed_bugs = Default::default(); - *stashed_err_count = 0; *deduplicated_err_count = 0; *deduplicated_warn_count = 0; *must_produce_diag = false; @@ -715,39 +714,111 @@ impl DiagCtxt { *fulfilled_expectations = Default::default(); } - /// Stash a given diagnostic with the given `Span` and [`StashKey`] as the key. - /// Retrieve a stashed diagnostic with `steal_diagnostic`. - pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: DiagInner) { - let mut inner = self.inner.borrow_mut(); - - let key = (span.with_parent(None), key); - - if diag.is_error() { - if diag.is_lint.is_none() { - inner.stashed_err_count += 1; - } - } + /// Stashes a diagnostic for possible later improvement in a different, + /// later stage of the compiler. Possible actions depend on the diagnostic + /// level: + /// - Level::Error: immediately counted as an error that has occurred, because it + /// is guaranteed to be emitted eventually. Can be later accessed with the + /// provided `span` and `key` through + /// [`DiagCtxt::try_steal_modify_and_emit_err`] or + /// [`DiagCtxt::try_steal_replace_and_emit_err`]. These do not allow + /// cancellation or downgrading of the error. Returns + /// `Some(ErrorGuaranteed)`. + /// - Level::Warning and lower (i.e. !is_error()): can be accessed with the + /// provided `span` and `key` through [`DiagCtxt::steal_non_err()`]. This + /// allows cancelling and downgrading of the diagnostic. Returns `None`. + /// - Others: not allowed, will trigger a panic. + pub fn stash_diagnostic( + &self, + span: Span, + key: StashKey, + diag: DiagInner, + ) -> Option { + let guar = if diag.level() == Level::Error { + // This `unchecked_error_guaranteed` is valid. It is where the + // `ErrorGuaranteed` for stashed errors originates. See + // `DiagCtxtInner::drop`. + #[allow(deprecated)] + Some(ErrorGuaranteed::unchecked_error_guaranteed()) + } else if !diag.is_error() { + None + } else { + self.span_bug(span, format!("invalid level in `stash_diagnostic`: {}", diag.level)); + }; // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic // if/when we have a more robust macro-friendly replacement for `(span, key)` as a key. // See the PR for a discussion. - inner.stashed_diagnostics.insert(key, diag); + let key = (span.with_parent(None), key); + self.inner.borrow_mut().stashed_diagnostics.insert(key, (diag, guar)); + + guar } - /// Steal a previously stashed diagnostic with the given `Span` and [`StashKey`] as the key. - pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option> { - let mut inner = self.inner.borrow_mut(); + /// Steal a previously stashed non-error diagnostic with the given `Span` + /// and [`StashKey`] as the key. Panics if the found diagnostic is an + /// error. + pub fn steal_non_err(&self, span: Span, key: StashKey) -> Option> { let key = (span.with_parent(None), key); // FIXME(#120456) - is `swap_remove` correct? - let diag = inner.stashed_diagnostics.swap_remove(&key)?; - if diag.is_error() { - if diag.is_lint.is_none() { - inner.stashed_err_count -= 1; - } - } + let (diag, guar) = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key)?; + assert!(!diag.is_error()); + assert!(guar.is_none()); Some(Diag::new_diagnostic(self, diag)) } + /// Steals a previously stashed error with the given `Span` and + /// [`StashKey`] as the key, modifies it, and emits it. Returns `None` if + /// no matching diagnostic is found. Panics if the found diagnostic's level + /// isn't `Level::Error`. + pub fn try_steal_modify_and_emit_err( + &self, + span: Span, + key: StashKey, + mut modify_err: F, + ) -> Option + where + F: FnMut(&mut Diag<'_>), + { + let key = (span.with_parent(None), key); + // FIXME(#120456) - is `swap_remove` correct? + let err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key); + err.map(|(err, guar)| { + // The use of `::` is safe because level is `Level::Error`. + assert_eq!(err.level, Level::Error); + assert!(guar.is_some()); + let mut err = Diag::::new_diagnostic(self, err); + modify_err(&mut err); + assert_eq!(err.level, Level::Error); + err.emit() + }) + } + + /// Steals a previously stashed error with the given `Span` and + /// [`StashKey`] as the key, cancels it if found, and emits `new_err`. + /// Panics if the found diagnostic's level isn't `Level::Error`. + pub fn try_steal_replace_and_emit_err( + &self, + span: Span, + key: StashKey, + new_err: Diag<'_>, + ) -> ErrorGuaranteed { + let key = (span.with_parent(None), key); + // FIXME(#120456) - is `swap_remove` correct? + let old_err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key); + match old_err { + Some((old_err, guar)) => { + assert_eq!(old_err.level, Level::Error); + assert!(guar.is_some()); + // Because `old_err` has already been counted, it can only be + // safely cancelled because the `new_err` supplants it. + Diag::::new_diagnostic(self, old_err).cancel(); + } + None => {} + }; + new_err.emit() + } + pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool { self.inner.borrow().stashed_diagnostics.get(&(span.with_parent(None), key)).is_some() } @@ -757,41 +828,40 @@ impl DiagCtxt { self.inner.borrow_mut().emit_stashed_diagnostics() } - /// This excludes lint errors, delayed bugs and stashed errors. + /// This excludes lint errors, and delayed bugs. #[inline] pub fn err_count_excluding_lint_errs(&self) -> usize { - self.inner.borrow().err_guars.len() + let inner = self.inner.borrow(); + inner.err_guars.len() + + inner + .stashed_diagnostics + .values() + .filter(|(diag, guar)| guar.is_some() && diag.is_lint.is_none()) + .count() } - /// This excludes delayed bugs and stashed errors. + /// This excludes delayed bugs. #[inline] pub fn err_count(&self) -> usize { let inner = self.inner.borrow(); - inner.err_guars.len() + inner.lint_err_guars.len() + inner.err_guars.len() + + inner.lint_err_guars.len() + + inner.stashed_diagnostics.values().filter(|(_diag, guar)| guar.is_some()).count() } - /// This excludes normal errors, lint errors, and delayed bugs. Unless - /// absolutely necessary, avoid using this. It's dubious because stashed - /// errors can later be cancelled, so the presence of a stashed error at - /// some point of time doesn't guarantee anything -- there are no - /// `ErrorGuaranteed`s here. - pub fn stashed_err_count(&self) -> usize { - self.inner.borrow().stashed_err_count - } - - /// This excludes lint errors, delayed bugs, and stashed errors. Unless - /// absolutely necessary, prefer `has_errors` to this method. + /// This excludes lint errors and delayed bugs. Unless absolutely + /// necessary, prefer `has_errors` to this method. pub fn has_errors_excluding_lint_errors(&self) -> Option { self.inner.borrow().has_errors_excluding_lint_errors() } - /// This excludes delayed bugs and stashed errors. + /// This excludes delayed bugs. pub fn has_errors(&self) -> Option { self.inner.borrow().has_errors() } - /// This excludes stashed errors. Unless absolutely necessary, prefer - /// `has_errors` to this method. + /// This excludes nothing. Unless absolutely necessary, prefer `has_errors` + /// to this method. pub fn has_errors_or_delayed_bugs(&self) -> Option { self.inner.borrow().has_errors_or_delayed_bugs() } @@ -876,10 +946,10 @@ impl DiagCtxt { } } - /// This excludes delayed bugs and stashed errors. Used for early aborts - /// after errors occurred -- e.g. because continuing in the face of errors is - /// likely to lead to bad results, such as spurious/uninteresting - /// additional errors -- when returning an error `Result` is difficult. + /// This excludes delayed bugs. Used for early aborts after errors occurred + /// -- e.g. because continuing in the face of errors is likely to lead to + /// bad results, such as spurious/uninteresting additional errors -- when + /// returning an error `Result` is difficult. pub fn abort_if_errors(&self) { if self.has_errors().is_some() { FatalError.raise(); @@ -963,7 +1033,7 @@ impl DiagCtxt { inner .stashed_diagnostics .values_mut() - .for_each(|diag| diag.update_unstable_expectation_id(unstable_to_stable)); + .for_each(|(diag, _guar)| diag.update_unstable_expectation_id(unstable_to_stable)); inner .future_breakage_diagnostics .iter_mut() @@ -1270,12 +1340,8 @@ impl DiagCtxtInner { fn emit_stashed_diagnostics(&mut self) -> Option { let mut guar = None; let has_errors = !self.err_guars.is_empty(); - for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() { - if diag.is_error() { - if diag.is_lint.is_none() { - self.stashed_err_count -= 1; - } - } else { + for (_, (diag, _guar)) in std::mem::take(&mut self.stashed_diagnostics).into_iter() { + if !diag.is_error() { // Unless they're forced, don't flush stashed warnings when // there are errors, to avoid causing warning overload. The // stash would've been stolen already if it were important. @@ -1334,7 +1400,8 @@ impl DiagCtxtInner { } else { let backtrace = std::backtrace::Backtrace::capture(); // This `unchecked_error_guaranteed` is valid. It is where the - // `ErrorGuaranteed` for delayed bugs originates. + // `ErrorGuaranteed` for delayed bugs originates. See + // `DiagCtxtInner::drop`. #[allow(deprecated)] let guar = ErrorGuaranteed::unchecked_error_guaranteed(); self.delayed_bugs @@ -1446,11 +1513,31 @@ impl DiagCtxtInner { } fn has_errors_excluding_lint_errors(&self) -> Option { - self.err_guars.get(0).copied() + self.err_guars.get(0).copied().or_else(|| { + if let Some((_diag, guar)) = self + .stashed_diagnostics + .values() + .find(|(diag, guar)| guar.is_some() && diag.is_lint.is_none()) + { + *guar + } else { + None + } + }) } fn has_errors(&self) -> Option { - self.has_errors_excluding_lint_errors().or_else(|| self.lint_err_guars.get(0).copied()) + self.err_guars.get(0).copied().or_else(|| self.lint_err_guars.get(0).copied()).or_else( + || { + if let Some((_diag, guar)) = + self.stashed_diagnostics.values().find(|(_diag, guar)| guar.is_some()) + { + *guar + } else { + None + } + }, + ) } fn has_errors_or_delayed_bugs(&self) -> Option { diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs index fb5f3426cea..227254b4cc8 100644 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs @@ -1,5 +1,5 @@ use rustc_ast::TraitObjectSyntax; -use rustc_errors::{codes::*, Diag, EmissionGuarantee, StashKey}; +use rustc_errors::{codes::*, Diag, EmissionGuarantee, Level, StashKey}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability}; @@ -237,7 +237,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } // check if the impl trait that we are considering is a impl of a local trait self.maybe_lint_blanket_trait_impl(self_ty, &mut diag); - diag.stash(self_ty.span, StashKey::TraitMissingMethod); + match diag.level() { + Level::Error => { + diag.stash(self_ty.span, StashKey::TraitMissingMethod); + } + Level::DelayedBug => { + diag.emit(); + } + _ => unreachable!(), + } } else { let msg = "trait objects without an explicit `dyn` are deprecated"; tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, msg, |lint| { diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 81e84860b82..96bebda5828 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1350,8 +1350,7 @@ fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalD let ty = tcx.type_of(def_id).instantiate_identity(); if ty.references_error() { // If there is already another error, do not emit an error for not using a type parameter. - // Without the `stashed_err_count` part this can fail (#120856). - assert!(tcx.dcx().has_errors().is_some() || tcx.dcx().stashed_err_count() > 0); + assert!(tcx.dcx().has_errors().is_some()); return; } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index c0128afe2bf..417f0fceaa8 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -596,10 +596,11 @@ fn infer_placeholder_type<'a>( // then the user may have written e.g. `const A = 42;`. // In this case, the parser has stashed a diagnostic for // us to improve in typeck so we do that now. - match tcx.dcx().steal_diagnostic(span, StashKey::ItemNoType) { - Some(mut err) => { + let guar = tcx + .dcx() + .try_steal_modify_and_emit_err(span, StashKey::ItemNoType, |err| { if !ty.references_error() { - // Only suggest adding `:` if it was missing (and suggested by parsing diagnostic) + // Only suggest adding `:` if it was missing (and suggested by parsing diagnostic). let colon = if span == item_ident.span.shrink_to_hi() { ":" } else { "" }; // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type. @@ -622,12 +623,8 @@ fn infer_placeholder_type<'a>( )); } } - - err.emit(); - // diagnostic stashing loses the information of whether something is a hard error. - Ty::new_error_with_message(tcx, span, "ItemNoType is a hard error") - } - None => { + }) + .unwrap_or_else(|| { let mut diag = bad_placeholder(tcx, vec![span], kind); if !ty.references_error() { @@ -645,10 +642,9 @@ fn infer_placeholder_type<'a>( )); } } - - Ty::new_error(tcx, diag.emit()) - } - } + diag.emit() + }); + Ty::new_error(tcx, guar) } fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) { diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index bb6d1ecae02..b87d71e3533 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -484,12 +484,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind && let [segment] = path.segments - && let Some(mut diag) = - self.dcx().steal_diagnostic(segment.ident.span, StashKey::CallIntoMethod) { - // Try suggesting `foo(a)` -> `a.foo()` if possible. - self.suggest_call_as_method(&mut diag, segment, arg_exprs, call_expr, expected); - diag.emit(); + self.dcx().try_steal_modify_and_emit_err( + segment.ident.span, + StashKey::CallIntoMethod, + |err| { + // Try suggesting `foo(a)` -> `a.foo()` if possible. + self.suggest_call_as_method( + err, segment, arg_exprs, call_expr, expected, + ); + }, + ); } let err = self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs); @@ -601,7 +606,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// and suggesting the fix if the method probe is successful. fn suggest_call_as_method( &self, - diag: &mut Diag<'_, ()>, + diag: &mut Diag<'_>, segment: &'tcx hir::PathSegment<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], call_expr: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 2b7bce3f485..054be89a7c4 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1460,18 +1460,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length { let span = self.tcx.hir().span(hir_id); - match self.dcx().steal_diagnostic(span, StashKey::UnderscoreForArrayLengths) { - Some(mut err) => { + self.dcx().try_steal_modify_and_emit_err( + span, + StashKey::UnderscoreForArrayLengths, + |err| { err.span_suggestion( span, "consider specifying the array length", array_len, Applicability::MaybeIncorrect, ); - err.emit(); - } - None => (), - } + }, + ); } } @@ -1751,11 +1751,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (_, diag) = self.demand_coerce_diag(field.expr, ty, field_type, None, AllowTwoPhase::No); - if let Some(mut diag) = diag { + if let Some(diag) = diag { if idx == ast_fields.len() - 1 { if remaining_fields.is_empty() { - self.suggest_fru_from_range(field, variant, args, &mut diag); - diag.emit(); + self.suggest_fru_from_range_and_emit(field, variant, args, diag); } else { diag.stash(field.span, StashKey::MaybeFruTypo); } @@ -1986,20 +1985,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}")); if let Some(last) = ast_fields.last() { - self.suggest_fru_from_range(last, variant, args, &mut err); + self.suggest_fru_from_range_and_emit(last, variant, args, err); + } else { + err.emit(); } - - err.emit(); } /// If the last field is a range literal, but it isn't supposed to be, then they probably /// meant to use functional update syntax. - fn suggest_fru_from_range( + fn suggest_fru_from_range_and_emit( &self, last_expr_field: &hir::ExprField<'tcx>, variant: &ty::VariantDef, args: GenericArgsRef<'tcx>, - err: &mut Diag<'_>, + mut err: Diag<'_>, ) { // I don't use 'is_range_literal' because only double-sided, half-open ranges count. if let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [range_start, range_end], _) = @@ -2012,16 +2011,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|adt| adt.did()) != range_def_id { - // Suppress any range expr type mismatches - if let Some(diag) = - self.dcx().steal_diagnostic(last_expr_field.span, StashKey::MaybeFruTypo) - { - diag.delay_as_bug(); - } - // Use a (somewhat arbitrary) filtering heuristic to avoid printing // expressions that are either too long, or have control character - //such as newlines in them. + // such as newlines in them. let expr = self .tcx .sess @@ -2043,6 +2035,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.dcx(), TypeMismatchFruTypo { expr_span: range_start.span, fru_span, expr }, ); + + // Suppress any range expr type mismatches + self.dcx().try_steal_replace_and_emit_err( + last_expr_field.span, + StashKey::MaybeFruTypo, + err, + ); + } else { + err.emit(); } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 1e6467deb01..dd44fdd8893 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -848,11 +848,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id) .map(|r| { // lint bare trait if the method is found in the trait - if span.edition().at_least_rust_2021() - && let Some(diag) = - self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) - { - diag.emit(); + if span.edition().at_least_rust_2021() { + self.dcx().try_steal_modify_and_emit_err( + qself.span, + StashKey::TraitMissingMethod, + |_err| {}, + ); } r }) @@ -882,11 +883,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Emit the diagnostic for bare traits. (We used to cancel for slightly better // error messages, but cancelling stashed diagnostics is no longer allowed because // it causes problems when tracking whether errors have actually occurred.) - if span.edition().at_least_rust_2021() - && let Some(diag) = - self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) - { - diag.emit(); + if span.edition().at_least_rust_2021() { + self.dcx().try_steal_modify_and_emit_err( + qself.span, + StashKey::TraitMissingMethod, + |_err| {}, + ); } if item_name.name != kw::Empty { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 61c52422d19..1311cc8968a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1941,53 +1941,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { errors_causecode: Vec<(Span, ObligationCauseCode<'tcx>)>, ) { for (span, code) in errors_causecode { - let Some(mut diag) = self.dcx().steal_diagnostic(span, StashKey::MaybeForgetReturn) - else { - continue; - }; - - if let Some(fn_sig) = self.body_fn_sig() - && let ExprBindingObligation(_, _, hir_id, ..) = code - && !fn_sig.output().is_unit() - { - let mut block_num = 0; - let mut found_semi = false; - for (_, node) in self.tcx.hir().parent_iter(hir_id) { - match node { - hir::Node::Stmt(stmt) => { - if let hir::StmtKind::Semi(expr) = stmt.kind { - let expr_ty = self.typeck_results.borrow().expr_ty(expr); - let return_ty = fn_sig.output(); - if !matches!(expr.kind, hir::ExprKind::Ret(..)) - && self.can_coerce(expr_ty, return_ty) - { - found_semi = true; + self.dcx().try_steal_modify_and_emit_err(span, StashKey::MaybeForgetReturn, |err| { + if let Some(fn_sig) = self.body_fn_sig() + && let ExprBindingObligation(_, _, hir_id, ..) = code + && !fn_sig.output().is_unit() + { + let mut block_num = 0; + let mut found_semi = false; + for (_, node) in self.tcx.hir().parent_iter(hir_id) { + match node { + hir::Node::Stmt(stmt) => { + if let hir::StmtKind::Semi(expr) = stmt.kind { + let expr_ty = self.typeck_results.borrow().expr_ty(expr); + let return_ty = fn_sig.output(); + if !matches!(expr.kind, hir::ExprKind::Ret(..)) + && self.can_coerce(expr_ty, return_ty) + { + found_semi = true; + } } } - } - hir::Node::Block(_block) => { - if found_semi { - block_num += 1; + hir::Node::Block(_block) => { + if found_semi { + block_num += 1; + } } - } - hir::Node::Item(item) => { - if let hir::ItemKind::Fn(..) = item.kind { - break; + hir::Node::Item(item) => { + if let hir::ItemKind::Fn(..) = item.kind { + break; + } } + _ => {} } - _ => {} + } + if block_num > 1 && found_semi { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "you might have meant to return this to infer its type parameters", + "return ", + Applicability::MaybeIncorrect, + ); } } - if block_num > 1 && found_semi { - diag.span_suggestion_verbose( - span.shrink_to_lo(), - "you might have meant to return this to infer its type parameters", - "return ", - Applicability::MaybeIncorrect, - ); - } - } - diag.emit(); + }); } } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 28771ae40f5..e5b0d0ae0da 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2194,59 +2194,59 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let [seg1, seg2] = segs else { return; }; - let Some(mut diag) = - self.dcx().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod) - else { - return; - }; - - let map = self.infcx.tcx.hir(); - let body_id = self.tcx.hir().body_owned_by(self.body_id); - let body = map.body(body_id); - struct LetVisitor<'a> { - result: Option<&'a hir::Expr<'a>>, - ident_name: Symbol, - } - - // FIXME: This really should be taking scoping, etc into account. - impl<'v> Visitor<'v> for LetVisitor<'v> { - fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { - if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind - && let Binding(_, _, ident, ..) = pat.kind - && ident.name == self.ident_name - { - self.result = *init; - } else { - hir::intravisit::walk_stmt(self, ex); + self.dcx().try_steal_modify_and_emit_err( + seg1.ident.span, + StashKey::CallAssocMethod, + |err| { + let map = self.infcx.tcx.hir(); + let body_id = self.tcx.hir().body_owned_by(self.body_id); + let body = map.body(body_id); + struct LetVisitor<'a> { + result: Option<&'a hir::Expr<'a>>, + ident_name: Symbol, } - } - } - let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name }; - visitor.visit_body(body); + // FIXME: This really should be taking scoping, etc into account. + impl<'v> Visitor<'v> for LetVisitor<'v> { + fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { + if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind + && let Binding(_, _, ident, ..) = pat.kind + && ident.name == self.ident_name + { + self.result = *init; + } else { + hir::intravisit::walk_stmt(self, ex); + } + } + } - if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id) - && let Some(expr) = visitor.result - && let Some(self_ty) = self.node_ty_opt(expr.hir_id) - { - let probe = self.lookup_probe_for_diagnostic( - seg2.ident, - self_ty, - call_expr, - ProbeScope::TraitsInScope, - None, - ); - if probe.is_ok() { - let sm = self.infcx.tcx.sess.source_map(); - diag.span_suggestion_verbose( - sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(), - "you may have meant to call an instance method", - ".", - Applicability::MaybeIncorrect, - ); - } - } - diag.emit(); + let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name }; + visitor.visit_body(body); + + if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id) + && let Some(expr) = visitor.result + && let Some(self_ty) = self.node_ty_opt(expr.hir_id) + { + let probe = self.lookup_probe_for_diagnostic( + seg2.ident, + self_ty, + call_expr, + ProbeScope::TraitsInScope, + None, + ); + if probe.is_ok() { + let sm = self.infcx.tcx.sess.source_map(); + err.span_suggestion_verbose( + sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':') + .unwrap(), + "you may have meant to call an instance method", + ".", + Applicability::MaybeIncorrect, + ); + } + } + }, + ); } /// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()` diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index ed102a7fac0..0740a3fd3e9 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -772,10 +772,6 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { fn report_error(&self, p: impl Into>) -> ErrorGuaranteed { if let Some(guar) = self.fcx.dcx().has_errors() { guar - } else if self.fcx.dcx().stashed_err_count() > 0 { - // Without this case we sometimes get uninteresting and extraneous - // "type annotations needed" errors. - self.fcx.dcx().delayed_bug("error in Resolver") } else { self.fcx .err_ctxt() diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index cc09a094688..a086d89b933 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -87,7 +87,6 @@ impl<'tcx> InferCtxt<'tcx> { reported_signature_mismatch: self.reported_signature_mismatch.clone(), tainted_by_errors: self.tainted_by_errors.clone(), err_count_on_creation: self.err_count_on_creation, - stashed_err_count_on_creation: self.stashed_err_count_on_creation, universe: self.universe.clone(), intercrate, next_trait_solver: self.next_trait_solver, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index d7e16488508..6f52ded3551 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -306,12 +306,6 @@ pub struct InferCtxt<'tcx> { // FIXME(matthewjasper) Merge into `tainted_by_errors` err_count_on_creation: usize, - /// Track how many errors were stashed when this infcx is created. - /// Used for the same purpose as `err_count_on_creation`, even - /// though it's weaker because the count can go up and down. - // FIXME(matthewjasper) Merge into `tainted_by_errors` - stashed_err_count_on_creation: usize, - /// What is the innermost universe we have created? Starts out as /// `UniverseIndex::root()` but grows from there as we enter /// universal quantifiers. @@ -717,7 +711,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> { reported_signature_mismatch: Default::default(), tainted_by_errors: Cell::new(None), err_count_on_creation: tcx.dcx().err_count_excluding_lint_errs(), - stashed_err_count_on_creation: tcx.dcx().stashed_err_count(), universe: Cell::new(ty::UniverseIndex::ROOT), intercrate, next_trait_solver, @@ -1274,14 +1267,6 @@ impl<'tcx> InferCtxt<'tcx> { let guar = self.dcx().has_errors().unwrap(); self.set_tainted_by_errors(guar); Some(guar) - } else if self.dcx().stashed_err_count() > self.stashed_err_count_on_creation { - // Errors stashed since this infcx was made. Not entirely reliable - // because the count of stashed errors can go down. But without - // this case we get a moderate number of uninteresting and - // extraneous "type annotations needed" errors. - let guar = self.dcx().delayed_bug("tainted_by_errors: stashed bug awaiting emission"); - self.set_tainted_by_errors(guar); - Some(guar) } else { None } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 9089d992cd5..6fdb03c0bab 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -847,17 +847,15 @@ impl<'tcx> OpaqueHiddenType<'tcx> { opaque_def_id: LocalDefId, tcx: TyCtxt<'tcx>, ) -> Result, ErrorGuaranteed> { - if let Some(diag) = tcx - .sess - .dcx() - .steal_diagnostic(tcx.def_span(opaque_def_id), StashKey::OpaqueHiddenTypeMismatch) - { - // We used to cancel here for slightly better error messages, but - // cancelling stashed diagnostics is no longer allowed because it - // causes problems when tracking whether errors have actually - // occurred. - diag.emit(); - } + // We used to cancel here for slightly better error messages, but + // cancelling stashed diagnostics is no longer allowed because it + // causes problems when tracking whether errors have actually + // occurred. + tcx.sess.dcx().try_steal_modify_and_emit_err( + tcx.def_span(opaque_def_id), + StashKey::OpaqueHiddenTypeMismatch, + |_err| {}, + ); (self.ty, other.ty).error_reported()?; // Found different concrete types for the opaque type. let sub_diag = if self.span == other.span { diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index b206727f051..51a4a9f411c 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -91,8 +91,8 @@ impl<'tcx> Region<'tcx> { /// Constructs a `RegionKind::ReError` region. #[track_caller] - pub fn new_error(tcx: TyCtxt<'tcx>, reported: ErrorGuaranteed) -> Region<'tcx> { - tcx.intern_region(ty::ReError(reported)) + pub fn new_error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Region<'tcx> { + tcx.intern_region(ty::ReError(guar)) } /// Constructs a `RegionKind::ReError` region and registers a delayed bug to ensure it gets diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 27c78d18d19..06be8191dc4 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1528,8 +1528,8 @@ impl<'tcx> Ty<'tcx> { } /// Constructs a `TyKind::Error` type with current `ErrorGuaranteed` - pub fn new_error(tcx: TyCtxt<'tcx>, reported: ErrorGuaranteed) -> Ty<'tcx> { - Ty::new(tcx, Error(reported)) + pub fn new_error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Ty<'tcx> { + Ty::new(tcx, Error(guar)) } /// Constructs a `TyKind::Error` type and registers a `span_delayed_bug` to ensure it gets used. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f5a7bfd42ff..316a9c4f8df 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1762,24 +1762,25 @@ impl<'a> Parser<'a> { err: impl FnOnce(&Self) -> Diag<'a>, ) -> L { assert!(could_be_unclosed_char_literal(ident)); - if let Some(diag) = self.dcx().steal_diagnostic(ident.span, StashKey::LifetimeIsChar) { - diag.with_span_suggestion_verbose( - ident.span.shrink_to_hi(), - "add `'` to close the char literal", - "'", - Applicability::MaybeIncorrect, - ) - .emit(); - } else { - err(self) - .with_span_suggestion_verbose( + self.dcx() + .try_steal_modify_and_emit_err(ident.span, StashKey::LifetimeIsChar, |err| { + err.span_suggestion_verbose( ident.span.shrink_to_hi(), "add `'` to close the char literal", "'", Applicability::MaybeIncorrect, - ) - .emit(); - } + ); + }) + .unwrap_or_else(|| { + err(self) + .with_span_suggestion_verbose( + ident.span.shrink_to_hi(), + "add `'` to close the char literal", + "'", + Applicability::MaybeIncorrect, + ) + .emit() + }); let name = ident.without_first_quote().name; mk_lit_char(name, ident.span) } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c12d35ec73c..04c0cf7436e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2520,14 +2520,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { if attr.style == AttrStyle::Inner { for attr_to_check in ATTRS_TO_CHECK { if attr.has_name(*attr_to_check) { - if let AttrKind::Normal(ref p) = attr.kind - && let Some(diag) = tcx.dcx().steal_diagnostic( - p.item.path.span, - StashKey::UndeterminedMacroResolution, - ) - { - diag.cancel(); - } let item = tcx .hir() .items() @@ -2537,7 +2529,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { span: item.ident.span, kind: item.kind.descr(), }); - tcx.dcx().emit_err(errors::InvalidAttrAtCrateLevel { + let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel { span: attr.span, sugg_span: tcx .sess @@ -2553,6 +2545,16 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { name: *attr_to_check, item, }); + + if let AttrKind::Normal(ref p) = attr.kind { + tcx.dcx().try_steal_replace_and_emit_err( + p.item.path.span, + StashKey::UndeterminedMacroResolution, + err, + ); + } else { + err.emit(); + } } } } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index e4c596b10b8..40517407ee6 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -149,8 +149,7 @@ where let guar = if let Some(root) = cycle_error.cycle.first() && let Some(span) = root.query.span { - error.stash(span, StashKey::Cycle); - qcx.dep_context().sess().dcx().span_delayed_bug(span, "delayed cycle error") + error.stash(span, StashKey::Cycle).unwrap() } else { error.emit() }; diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 752bb05f3d7..1b79786c1b8 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -105,8 +105,7 @@ pub fn feature_err_issue( // Cancel an earlier warning for this same error, if it exists. if let Some(span) = span.primary_span() { - if let Some(err) = sess.parse_sess.dcx.steal_diagnostic(span, StashKey::EarlySyntaxWarning) - { + if let Some(err) = sess.parse_sess.dcx.steal_non_err(span, StashKey::EarlySyntaxWarning) { err.cancel() } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index dcbb63f00f7..d9e49f1eb0a 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -889,7 +889,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => self.report_opaque_type_auto_trait_leakage( + SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => return self.report_opaque_type_auto_trait_leakage( &obligation, def_id, ), @@ -2252,8 +2252,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ErrorCode::E0282, false, ); - err.stash(span, StashKey::MaybeForgetReturn); - return self.dcx().delayed_bug("stashed error never reported"); + return err.stash(span, StashKey::MaybeForgetReturn).unwrap(); } Some(e) => return e, } @@ -2766,7 +2765,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.suggest_unsized_bound_if_applicable(err, obligation); if let Some(span) = err.span.primary_span() && let Some(mut diag) = - self.tcx.dcx().steal_diagnostic(span, StashKey::AssociatedTypeSuggestion) + self.tcx.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion) && let Ok(ref mut s1) = err.suggestions && let Ok(ref mut s2) = diag.suggestions { @@ -3291,7 +3290,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, def_id: DefId, - ) -> Diag<'tcx> { + ) -> ErrorGuaranteed { let name = match self.tcx.opaque_type_origin(def_id.expect_local()) { hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { "opaque type".to_string() @@ -3318,12 +3317,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } }; - if let Some(diag) = self.dcx().steal_diagnostic(self.tcx.def_span(def_id), StashKey::Cycle) - { - diag.cancel(); - } - - err + self.note_obligation_cause(&mut err, &obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + self.dcx().try_steal_replace_and_emit_err(self.tcx.def_span(def_id), StashKey::Cycle, err) } fn report_signature_mismatch_error( diff --git a/src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.rs b/src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.rs new file mode 100644 index 00000000000..b56abccbd41 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.rs @@ -0,0 +1,8 @@ +struct Foo(isize, isize, isize, isize); + +pub fn main() { + let Self::anything_here_kills_it(a, b, ..) = Foo(5, 5, 5, 5); + match [5, 5, 5, 5] { + [..] => { } + } +} diff --git a/src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.stderr b/src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.stderr new file mode 100644 index 00000000000..9e0d3b934b8 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.stderr @@ -0,0 +1,9 @@ +error[E0433]: failed to resolve: `Self` is only available in impls, traits, and type definitions + --> tests/ui/crashes/unreachable-array-or-slice.rs:4:9 + | +LL | let Self::anything_here_kills_it(a, b, ..) = Foo(5, 5, 5, 5); + | ^^^^ `Self` is only available in impls, traits, and type definitions + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/foreign/stashed-issue-121451.rs b/tests/ui/foreign/stashed-issue-121451.rs new file mode 100644 index 00000000000..97a4af37475 --- /dev/null +++ b/tests/ui/foreign/stashed-issue-121451.rs @@ -0,0 +1,4 @@ +extern "C" fn _f() -> libc::uintptr_t {} +//~^ ERROR failed to resolve: use of undeclared crate or module `libc` + +fn main() {} diff --git a/tests/ui/foreign/stashed-issue-121451.stderr b/tests/ui/foreign/stashed-issue-121451.stderr new file mode 100644 index 00000000000..440d98d6f46 --- /dev/null +++ b/tests/ui/foreign/stashed-issue-121451.stderr @@ -0,0 +1,9 @@ +error[E0433]: failed to resolve: use of undeclared crate or module `libc` + --> $DIR/stashed-issue-121451.rs:1:23 + | +LL | extern "C" fn _f() -> libc::uintptr_t {} + | ^^^^ use of undeclared crate or module `libc` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/impl-trait/stashed-diag-issue-121504.rs b/tests/ui/impl-trait/stashed-diag-issue-121504.rs new file mode 100644 index 00000000000..4ac8ffe8931 --- /dev/null +++ b/tests/ui/impl-trait/stashed-diag-issue-121504.rs @@ -0,0 +1,13 @@ +//@ edition: 2021 + +trait MyTrait { + async fn foo(self) -> (Self, i32); +} + +impl MyTrait for xyz::T { //~ ERROR failed to resolve: use of undeclared crate or module `xyz` + async fn foo(self, key: i32) -> (u32, i32) { + (self, key) + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/stashed-diag-issue-121504.stderr b/tests/ui/impl-trait/stashed-diag-issue-121504.stderr new file mode 100644 index 00000000000..6a881dc7f9f --- /dev/null +++ b/tests/ui/impl-trait/stashed-diag-issue-121504.stderr @@ -0,0 +1,9 @@ +error[E0433]: failed to resolve: use of undeclared crate or module `xyz` + --> $DIR/stashed-diag-issue-121504.rs:7:18 + | +LL | impl MyTrait for xyz::T { + | ^^^ use of undeclared crate or module `xyz` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/lowering/issue-121108.rs b/tests/ui/lowering/issue-121108.rs index 6b2dd24e4a8..6dfb7e00821 100644 --- a/tests/ui/lowering/issue-121108.rs +++ b/tests/ui/lowering/issue-121108.rs @@ -3,7 +3,7 @@ use std::ptr::addr_of; const UNINHABITED_VARIANT: () = unsafe { - let v = *addr_of!(data).cast(); //~ ERROR cannot determine resolution for the macro `addr_of` + let v = *addr_of!(data).cast(); }; fn main() {} diff --git a/tests/ui/lowering/issue-121108.stderr b/tests/ui/lowering/issue-121108.stderr index c2c5746d6f1..e4942e8cb07 100644 --- a/tests/ui/lowering/issue-121108.stderr +++ b/tests/ui/lowering/issue-121108.stderr @@ -13,13 +13,5 @@ LL - #![derive(Clone, Copy)] LL + #[derive(Clone, Copy)] | -error: cannot determine resolution for the macro `addr_of` - --> $DIR/issue-121108.rs:6:14 - | -LL | let v = *addr_of!(data).cast(); - | ^^^^^^^ - | - = note: import resolution is stuck, try simplifying macro imports - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error From b4e9f93eb47fa7405749e74d2c17c2add245a877 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 27 Feb 2024 15:02:54 +1100 Subject: [PATCH 46/62] Mark some once-again-unreachable paths as unreachable. This undoes the changes from #121482 and #121586, now that stashed errors will trigger more early aborts. --- compiler/rustc_lint/src/array_into_iter.rs | 14 +++++--------- compiler/rustc_privacy/src/lib.rs | 5 +---- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs index 993b1d739a1..3a5c585366a 100644 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ b/compiler/rustc_lint/src/array_into_iter.rs @@ -70,15 +70,11 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { // Check if the method call actually calls the libcore // `IntoIterator::into_iter`. - let trait_id = cx - .typeck_results() - .type_dependent_def_id(expr.hir_id) - .and_then(|did| cx.tcx.trait_of_item(did)); - if trait_id.is_none() - || !cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id.unwrap()) - { - return; - } + let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); + match cx.tcx.trait_of_item(def_id) { + Some(trait_id) if cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id) => {} + _ => return, + }; // As this is a method call expression, we have at least one argument. let receiver_ty = cx.typeck_results().expr_ty(receiver_arg); diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 1c6bd887128..9d8a9f5fce3 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -988,10 +988,7 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { if let hir::ExprKind::Struct(qpath, fields, ref base) = expr.kind { let res = self.typeck_results().qpath_res(qpath, expr.hir_id); - let Some(adt) = self.typeck_results().expr_ty(expr).ty_adt_def() else { - self.tcx.dcx().span_delayed_bug(expr.span, "no adt_def for expression"); - return; - }; + let adt = self.typeck_results().expr_ty(expr).ty_adt_def().unwrap(); let variant = adt.variant_of_res(res); if let Some(base) = *base { // If the expression uses FRU we need to make sure all the unmentioned fields From 9aff357e5398320962137a8885b924231904ef08 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 27 Feb 2024 15:52:29 +1100 Subject: [PATCH 47/62] Stop miri if delayed bugs are present. Seems wise, since it shouldn't proceed in that case. --- src/tools/miri/src/bin/miri.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 8a7133fea43..281a32b77c5 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -68,7 +68,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { queries: &'tcx rustc_interface::Queries<'tcx>, ) -> Compilation { queries.global_ctxt().unwrap().enter(|tcx| { - if tcx.sess.dcx().has_errors().is_some() { + if tcx.sess.dcx().has_errors_or_delayed_bugs().is_some() { tcx.dcx().fatal("miri cannot be run on programs that fail compilation"); } From 82961c0abcd7b9ea73fb87fd049e2e853abd5787 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 28 Feb 2024 11:00:27 +1100 Subject: [PATCH 48/62] Reinstate `emit_stashed_diagnostics` in `DiagCtxtInner::drop`. I removed it in #121206 because I thought thought it wasn't necessary. But then I had to add an `emit_stashed_diagnostics` call elsewhere in rustfmt to avoid the assertion failure (which took two attempts to get right, #121487 and #121615), and now there's an assertion failure in clippy as well (https://github.com/rust-lang/rust-clippy/issues/12364). So this commit just reinstates the call in `DiagCtxtInner::drop`. It also reverts the rustfmt changes from #121487 and #121615, though it keeps the tests added for those PRs. --- compiler/rustc_errors/src/lib.rs | 10 ++++++---- src/tools/rustfmt/src/formatting.rs | 21 ++++++--------------- src/tools/rustfmt/src/parse/session.rs | 8 +------- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 60bda9f4c94..186ec41f7cd 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -555,12 +555,14 @@ pub struct DiagCtxtFlags { impl Drop for DiagCtxtInner { fn drop(&mut self) { - // Any stashed diagnostics should have been handled by - // `emit_stashed_diagnostics` by now. + // For tools using `interface::run_compiler` (e.g. rustc, rustdoc) + // stashed diagnostics will have already been emitted. But for others + // that don't use `interface::run_compiler` (e.g. rustfmt, some clippy + // lints) this fallback is necessary. // // Important: it is sound to produce an `ErrorGuaranteed` when stashing - // errors because they are guaranteed to have been emitted by here. - assert!(self.stashed_diagnostics.is_empty()); + // errors because they are guaranteed to be emitted here or earlier. + self.emit_stashed_diagnostics(); // Important: it is sound to produce an `ErrorGuaranteed` when emitting // delayed bugs because they are guaranteed to be emitted here if diff --git a/src/tools/rustfmt/src/formatting.rs b/src/tools/rustfmt/src/formatting.rs index 323ae83fe6e..1c64921b1a6 100644 --- a/src/tools/rustfmt/src/formatting.rs +++ b/src/tools/rustfmt/src/formatting.rs @@ -109,7 +109,7 @@ fn format_project( let main_file = input.file_name(); let input_is_stdin = main_file == FileName::Stdin; - let mut parse_session = ParseSess::new(config)?; + let parse_session = ParseSess::new(config)?; if config.skip_children() && parse_session.ignore_file(&main_file) { return Ok(FormatReport::new()); } @@ -118,20 +118,11 @@ fn format_project( let mut report = FormatReport::new(); let directory_ownership = input.to_directory_ownership(); - // rustfmt doesn't use `run_compiler` like other tools, so it must emit any - // stashed diagnostics itself, otherwise the `DiagCtxt` will assert when - // dropped. The final result here combines the parsing result and the - // `emit_stashed_diagnostics` result. - let parse_res = Parser::parse_crate(input, &parse_session); - let stashed_res = parse_session.emit_stashed_diagnostics(); - let krate = match (parse_res, stashed_res) { - (Ok(krate), None) => krate, - (parse_res, _) => { - // Surface parse error via Session (errors are merged there from report). - let forbid_verbose = match parse_res { - Err(e) if e != ParserError::ParsePanicError => true, - _ => input_is_stdin, - }; + let krate = match Parser::parse_crate(input, &parse_session) { + Ok(krate) => krate, + // Surface parse error via Session (errors are merged there from report) + Err(e) => { + let forbid_verbose = input_is_stdin || e != ParserError::ParsePanicError; should_emit_verbose(forbid_verbose, config, || { eprintln!("The Rust parser panicked"); }); diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index e33f1ca755c..f5defe63c13 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -5,9 +5,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use rustc_data_structures::sync::{IntoDynSyncSend, Lrc}; use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter}; use rustc_errors::translation::Translate; -use rustc_errors::{ - ColorConfig, Diag, DiagCtxt, DiagInner, ErrorGuaranteed, Level as DiagnosticLevel, -}; +use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel}; use rustc_session::parse::ParseSess as RawParseSess; use rustc_span::{ source_map::{FilePathMapping, SourceMap}, @@ -230,10 +228,6 @@ impl ParseSess { self.ignore_path_set.as_ref().is_match(path) } - pub(crate) fn emit_stashed_diagnostics(&mut self) -> Option { - self.parse_sess.dcx.emit_stashed_diagnostics() - } - pub(crate) fn set_silent_emitter(&mut self) { self.parse_sess.dcx = DiagCtxt::with_emitter(silent_emitter()); } From 9dfe5ef8b5496f5749223ef127d701563af4fe30 Mon Sep 17 00:00:00 2001 From: sisungo Date: Thu, 29 Feb 2024 13:16:24 +0800 Subject: [PATCH 49/62] Fix typo in `rustc_passes/messages.ftl` Line 190 contains unpaired parentheses. --- compiler/rustc_passes/messages.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 648ef9d51de..c223b847528 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -187,7 +187,7 @@ passes_doc_attr_not_crate_level = `#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute passes_doc_cfg_hide_takes_list = - `#[doc(cfg_hide(...)]` takes a list of attributes + `#[doc(cfg_hide(...))]` takes a list of attributes passes_doc_expect_str = doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")] From 9c6a0766be95fc197f4f30764a794eb4d50b649a Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Thu, 29 Feb 2024 00:56:31 -0500 Subject: [PATCH 50/62] document potential memory leak in unbounded channel --- library/std/src/sync/mpmc/list.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs index a1b275112a1..b6bae7dc50c 100644 --- a/library/std/src/sync/mpmc/list.rs +++ b/library/std/src/sync/mpmc/list.rs @@ -547,6 +547,9 @@ impl Channel { } let mut head = self.head.index.load(Ordering::Acquire); + // the channel may be uninitialized, so we have to swap to avoid overwriting any sender's attempts + // to initalize the first block before noticing that the receivers disconnected. late allocations + // will be deallocated by the sender in Drop let mut block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel); // If we're going to be dropping messages we need to synchronize with initialization From 1850ba7f548d13b7849bb8686728a113c0e6fb09 Mon Sep 17 00:00:00 2001 From: r0cky Date: Thu, 29 Feb 2024 14:00:08 +0800 Subject: [PATCH 51/62] Remove unused diagnostic struct --- compiler/rustc_builtin_macros/src/errors.rs | 21 ----------- compiler/rustc_hir_analysis/src/errors.rs | 7 ---- compiler/rustc_parse/src/errors.rs | 8 ----- compiler/rustc_resolve/src/errors.rs | 39 --------------------- 4 files changed, 75 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 23d2da128e5..6546a35734c 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -136,27 +136,6 @@ pub(crate) struct BenchSig { pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(builtin_macros_test_arg_non_lifetime)] -pub(crate) struct TestArgNonLifetime { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag(builtin_macros_should_panic)] -pub(crate) struct ShouldPanic { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag(builtin_macros_test_args)] -pub(crate) struct TestArgs { - #[primary_span] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag(builtin_macros_alloc_must_statics)] pub(crate) struct AllocMustStatics { diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index ccad3b66d6b..5330260fbf5 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1004,13 +1004,6 @@ pub(crate) struct StaticSpecialize { pub span: Span, } -#[derive(Diagnostic)] -#[diag(hir_analysis_missing_tilde_const)] -pub(crate) struct MissingTildeConst { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] pub(crate) enum DropImplPolarity { #[diag(hir_analysis_drop_impl_negative)] diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 6c506a8efe0..91dbf0321d2 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -557,14 +557,6 @@ pub(crate) struct CatchAfterTry { pub span: Span, } -#[derive(Diagnostic)] -#[diag(parse_gen_fn)] -#[help] -pub(crate) struct GenFn { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(parse_comma_after_base_struct)] #[note] diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index adc4cd911a7..6f23f531996 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -7,32 +7,6 @@ use rustc_span::{ use crate::{late::PatternSource, Res}; -#[derive(Diagnostic)] -#[diag(resolve_parent_module_reset_for_binding, code = E0637)] -pub(crate) struct ParentModuleResetForBinding; - -#[derive(Diagnostic)] -#[diag(resolve_ampersand_used_without_explicit_lifetime_name, code = E0637)] -#[note] -pub(crate) struct AmpersandUsedWithoutExplicitLifetimeName(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag(resolve_underscore_lifetime_name_cannot_be_used_here, code = E0637)] -#[note] -pub(crate) struct UnderscoreLifetimeNameCannotBeUsedHere(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag(resolve_crate_may_not_be_imported)] -pub(crate) struct CrateMayNotBeImported(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag(resolve_crate_root_imports_must_be_named_explicitly)] -pub(crate) struct CrateRootNamesMustBeNamedExplicitly(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag(resolve_crate_root_imports_must_be_named_explicitly)] -pub(crate) struct ResolutionError(#[primary_span] pub(crate) Span); - #[derive(Diagnostic)] #[diag(resolve_generic_params_from_outer_item, code = E0401)] pub(crate) struct GenericParamsFromOuterItem { @@ -467,19 +441,6 @@ pub(crate) struct UnreachableLabelSubLabelUnreachable { pub(crate) ident_span: Span, } -#[derive(Diagnostic)] -#[diag(resolve_trait_impl_mismatch)] -pub(crate) struct TraitImplMismatch { - #[primary_span] - #[label] - pub(crate) span: Span, - pub(crate) name: Symbol, - pub(crate) kind: String, - #[label(resolve_label_trait_item)] - pub(crate) trait_item_span: Span, - pub(crate) trait_path: String, -} - #[derive(Diagnostic)] #[diag(resolve_invalid_asm_sym)] #[help] From 7c9fa952c3be1f7d0872fe9cd5f1f4a0327771b9 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Thu, 29 Feb 2024 01:33:02 -0500 Subject: [PATCH 52/62] fix typos Co-authored-by: Ralf Jung --- library/std/src/sync/mpmc/list.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs index b6bae7dc50c..9e7148c716c 100644 --- a/library/std/src/sync/mpmc/list.rs +++ b/library/std/src/sync/mpmc/list.rs @@ -547,9 +547,9 @@ impl Channel { } let mut head = self.head.index.load(Ordering::Acquire); - // the channel may be uninitialized, so we have to swap to avoid overwriting any sender's attempts - // to initalize the first block before noticing that the receivers disconnected. late allocations - // will be deallocated by the sender in Drop + // The channel may be uninitialized, so we have to swap to avoid overwriting any sender's attempts + // to initalize the first block before noticing that the receivers disconnected. Late allocations + // will be deallocated by the sender in Drop. let mut block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel); // If we're going to be dropping messages we need to synchronize with initialization From da37c8f1f2ed24bc06667bf13aedd63b4aad2b9f Mon Sep 17 00:00:00 2001 From: sisungo Date: Thu, 29 Feb 2024 14:59:10 +0800 Subject: [PATCH 53/62] Fix tests that are affected by this change --- tests/rustdoc-ui/lints/doc_cfg_hide.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr index b7e8870fdf5..ca6a14a42b8 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr @@ -18,7 +18,7 @@ help: to apply to the crate, use an inner attribute LL | #![doc(cfg_hide(doc))] | + -error: `#[doc(cfg_hide(...)]` takes a list of attributes +error: `#[doc(cfg_hide(...))]` takes a list of attributes --> $DIR/doc_cfg_hide.rs:4:8 | LL | #![doc(cfg_hide = "test")] @@ -27,7 +27,7 @@ LL | #![doc(cfg_hide = "test")] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 -error: `#[doc(cfg_hide(...)]` takes a list of attributes +error: `#[doc(cfg_hide(...))]` takes a list of attributes --> $DIR/doc_cfg_hide.rs:6:8 | LL | #![doc(cfg_hide)] From c54d92c56e6f3380bb43e306d62535e24a45d211 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 29 Feb 2024 07:58:29 +0100 Subject: [PATCH 54/62] bootstrap/format: send larger batches to rustfmt --- src/bootstrap/src/core/build_steps/format.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index 69c8792b031..700c3ee4fda 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -279,8 +279,8 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) { let thread = std::thread::spawn(move || { let mut children = VecDeque::new(); while let Ok(path) = rx.recv() { - // try getting a few more paths from the channel to amortize the overhead of spawning processes - let paths: Vec<_> = rx.try_iter().take(7).chain(std::iter::once(path)).collect(); + // try getting more paths from the channel to amortize the overhead of spawning processes + let paths: Vec<_> = rx.try_iter().take(63).chain(std::iter::once(path)).collect(); let child = rustfmt(&src, &rustfmt_path, paths.as_slice(), check); children.push_back(child); From ad4c932ac4d877f782986461f77c074134204a0d Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Wed, 28 Feb 2024 16:13:28 +0000 Subject: [PATCH 55/62] Restore the standard library review rotation to its former glory --- triagebot.toml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index e084b555464..802079f496e 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -673,6 +673,12 @@ compiler = [ libs = [ "@cuviper", "@Mark-Simulacrum", + "@m-ou-se", + "@Amanieu", + "@Nilstrieb", + "@workingjubilee", + "@joboet", + "@jhpratt", ] bootstrap = [ "@Mark-Simulacrum", @@ -810,7 +816,7 @@ project-stable-mir = [ "/compiler/rustc_type_ir" = ["compiler", "types"] "/compiler/stable_mir" = ["project-stable-mir"] "/library/alloc" = ["libs"] -"/library/core" = ["libs"] +"/library/core" = ["libs", "@scottmcm"] "/library/panic_abort" = ["libs"] "/library/panic_unwind" = ["libs"] "/library/proc_macro" = ["@petrochenkov"] From f2d9bfb35fc1692c3cb9dba2e5be084fe61489fa Mon Sep 17 00:00:00 2001 From: klensy Date: Thu, 29 Feb 2024 13:05:43 +0300 Subject: [PATCH 56/62] fix clap warnings Run 'cargo check --features clap/deprecated' and fix warnings --- src/bootstrap/src/core/builder.rs | 10 ++++----- src/bootstrap/src/core/config/flags.rs | 28 +++++++++++++------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index e169cba3c13..8223a80c931 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -642,22 +642,22 @@ impl<'a> ShouldRun<'a> { #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] pub enum Kind { - #[clap(alias = "b")] + #[value(alias = "b")] Build, - #[clap(alias = "c")] + #[value(alias = "c")] Check, Clippy, Fix, Format, - #[clap(alias = "t")] + #[value(alias = "t")] Test, Bench, - #[clap(alias = "d")] + #[value(alias = "d")] Doc, Clean, Dist, Install, - #[clap(alias = "r")] + #[value(alias = "r")] Run, Setup, Suggest, diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 8af454001a6..7262b785ee0 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -31,7 +31,7 @@ pub enum Warnings { /// Deserialized version of all flags for this compile. #[derive(Debug, Parser)] -#[clap( +#[command( override_usage = "x.py [options] [...]", disable_help_subcommand(true), about = "", @@ -118,7 +118,7 @@ pub struct Flags { // This overrides the deny-warnings configuration option, // which passes -Dwarnings to the compiler invocations. #[arg(global(true), long)] - #[clap(value_enum, default_value_t=Warnings::Default, value_name = "deny|warn")] + #[arg(value_enum, default_value_t=Warnings::Default, value_name = "deny|warn")] /// if value is deny, will deny warnings /// if value is warn, will emit warnings /// otherwise, use the default configured behaviour @@ -132,7 +132,7 @@ pub struct Flags { pub json_output: bool, #[arg(global(true), long, value_name = "STYLE")] - #[clap(value_enum, default_value_t = Color::Auto)] + #[arg(value_enum, default_value_t = Color::Auto)] /// whether to use color in cargo and rustc output pub color: Color, @@ -188,7 +188,7 @@ impl Flags { let it = std::iter::once(&first).chain(args.iter()); // We need to check for ` -h -v`, in which case we list the paths #[derive(Parser)] - #[clap(disable_help_flag(true))] + #[command(disable_help_flag(true))] struct HelpVerboseOnly { #[arg(short, long)] help: bool, @@ -218,7 +218,7 @@ impl Flags { #[derive(Debug, Clone, Default, clap::Subcommand)] pub enum Subcommand { - #[clap(aliases = ["b"], long_about = "\n + #[command(aliases = ["b"], long_about = "\n Arguments: This subcommand accepts a number of paths to directories to the crates and/or artifacts to compile. For example, for a quick build of a usable @@ -233,7 +233,7 @@ pub enum Subcommand { /// Compile either the compiler or libraries #[default] Build, - #[clap(aliases = ["c"], long_about = "\n + #[command(aliases = ["c"], long_about = "\n Arguments: This subcommand accepts a number of paths to directories to the crates and/or artifacts to compile. For example: @@ -246,7 +246,7 @@ pub enum Subcommand { all_targets: bool, }, /// Run Clippy (uses rustup/cargo-installed clippy binary) - #[clap(long_about = "\n + #[command(long_about = "\n Arguments: This subcommand accepts a number of paths to directories to the crates and/or artifacts to run clippy against. For example: @@ -273,14 +273,14 @@ pub enum Subcommand { forbid: Vec, }, /// Run cargo fix - #[clap(long_about = "\n + #[command(long_about = "\n Arguments: This subcommand accepts a number of paths to directories to the crates and/or artifacts to run `cargo fix` against. For example: ./x.py fix library/core ./x.py fix library/core library/proc_macro")] Fix, - #[clap( + #[command( name = "fmt", long_about = "\n Arguments: @@ -295,7 +295,7 @@ pub enum Subcommand { #[arg(long)] check: bool, }, - #[clap(aliases = ["d"], long_about = "\n + #[command(aliases = ["d"], long_about = "\n Arguments: This subcommand accepts a number of paths to directories of documentation to build. For example: @@ -316,7 +316,7 @@ pub enum Subcommand { /// render the documentation in JSON format in addition to the usual HTML format json: bool, }, - #[clap(aliases = ["t"], long_about = "\n + #[command(aliases = ["t"], long_about = "\n Arguments: This subcommand accepts a number of paths to test directories that should be compiled and run. For example: @@ -400,7 +400,7 @@ pub enum Subcommand { Dist, /// Install distribution artifacts Install, - #[clap(aliases = ["r"], long_about = "\n + #[command(aliases = ["r"], long_about = "\n Arguments: This subcommand accepts a number of paths to tools to build and run. For example: @@ -413,7 +413,7 @@ pub enum Subcommand { args: Vec, }, /// Set up the environment for development - #[clap(long_about = format!( + #[command(long_about = format!( "\n x.py setup creates a `config.toml` which changes the defaults for x.py itself, as well as setting up a git pre-push hook, VS Code config and toolchain link. @@ -434,7 +434,7 @@ Arguments: profile: Option, }, /// Suggest a subset of tests to run, based on modified files - #[clap(long_about = "\n")] + #[command(long_about = "\n")] Suggest { /// run suggested tests #[arg(long)] From b5307f5d95a03c55f9ebbf00d7c1c876848f5622 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Thu, 29 Feb 2024 11:58:13 +0100 Subject: [PATCH 57/62] Document the precision of `f64` methods --- library/std/src/f64.rs | 184 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 88f992b3957..7385576c337 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -31,6 +31,8 @@ pub use core::f64::{ impl f64 { /// Returns the largest integer less than or equal to `self`. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -52,6 +54,8 @@ impl f64 { /// Returns the smallest integer greater than or equal to `self`. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -73,6 +77,8 @@ impl f64 { /// Returns the nearest integer to `self`. If a value is half-way between two /// integers, round away from `0.0`. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -99,6 +105,8 @@ impl f64 { /// Returns the nearest integer to a number. Rounds half-way cases to the number /// with an even least significant digit. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -123,6 +131,8 @@ impl f64 { /// Returns the integer part of `self`. /// This means that non-integer numbers are always truncated towards zero. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -145,6 +155,8 @@ impl f64 { /// Returns the fractional part of `self`. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -166,6 +178,8 @@ impl f64 { /// Computes the absolute value of `self`. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -249,6 +263,12 @@ impl f64 { /// this is not always true, and will be heavily dependant on designing /// algorithms with specific target hardware in mind. /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// /// # Examples /// /// ``` @@ -276,6 +296,11 @@ impl f64 { /// In other words, the result is `self / rhs` rounded to the integer `n` /// such that `self >= n * rhs`. /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// /// # Examples /// /// ``` @@ -309,6 +334,11 @@ impl f64 { /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` /// approximately. /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// /// # Examples /// /// ``` @@ -337,6 +367,10 @@ impl f64 { /// It might have a different sequence of rounding operations than `powf`, /// so the results are not guaranteed to agree. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -355,6 +389,10 @@ impl f64 { /// Raises a number to a floating point power. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -375,6 +413,12 @@ impl f64 { /// /// Returns NaN if `self` is a negative number other than `-0.0`. /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// /// # Examples /// /// ``` @@ -398,6 +442,10 @@ impl f64 { /// Returns `e^(self)`, (the exponential function). /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -420,6 +468,10 @@ impl f64 { /// Returns `2^(self)`. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -440,6 +492,10 @@ impl f64 { /// Returns the natural logarithm of the number. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -466,6 +522,10 @@ impl f64 { /// `self.log2()` can produce more accurate results for base 2, and /// `self.log10()` can produce more accurate results for base 10. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -486,6 +546,10 @@ impl f64 { /// Returns the base 2 logarithm of the number. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -506,6 +570,10 @@ impl f64 { /// Returns the base 10 logarithm of the number. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -529,6 +597,12 @@ impl f64 { /// * If `self <= other`: `0.0` /// * Else: `self - other` /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `fdim` from libc on Unix and + /// Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -561,6 +635,12 @@ impl f64 { /// Returns the cube root of a number. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `cbrt` from libc on Unix and + /// Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -584,6 +664,12 @@ impl f64 { /// right-angle triangle with other sides having length `x.abs()` and /// `y.abs()`. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `hypot` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -605,6 +691,10 @@ impl f64 { /// Computes the sine of a number (in radians). /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -624,6 +714,10 @@ impl f64 { /// Computes the cosine of a number (in radians). /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -643,6 +737,12 @@ impl f64 { /// Computes the tangent of a number (in radians). /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `tan` from libc on Unix and + /// Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -663,6 +763,12 @@ impl f64 { /// the range [-pi/2, pi/2] or NaN if the number is outside the range /// [-1, 1]. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `asin` from libc on Unix and + /// Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -686,6 +792,12 @@ impl f64 { /// the range [0, pi] or NaN if the number is outside the range /// [-1, 1]. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `acos` from libc on Unix and + /// Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -708,6 +820,12 @@ impl f64 { /// Computes the arctangent of a number. Return value is in radians in the /// range [-pi/2, pi/2]; /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `atan` from libc on Unix and + /// Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -734,6 +852,12 @@ impl f64 { /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `atan2` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -764,6 +888,12 @@ impl f64 { /// Simultaneously computes the sine and cosine of the number, `x`. Returns /// `(sin(x), cos(x))`. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `(f64::sin(x), + /// f64::cos(x))`. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -787,6 +917,12 @@ impl f64 { /// Returns `e^(self) - 1` in a way that is accurate even if the /// number is close to zero. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `expm1` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -809,6 +945,12 @@ impl f64 { /// Returns `ln(1+n)` (natural logarithm) more accurately than if /// the operations were performed separately. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `log1p` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -831,6 +973,12 @@ impl f64 { /// Hyperbolic sine function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `sinh` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -854,6 +1002,12 @@ impl f64 { /// Hyperbolic cosine function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `cosh` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -877,6 +1031,12 @@ impl f64 { /// Hyperbolic tangent function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `tanh` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -900,6 +1060,10 @@ impl f64 { /// Inverse hyperbolic sine function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -923,6 +1087,10 @@ impl f64 { /// Inverse hyperbolic cosine function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -948,6 +1116,10 @@ impl f64 { /// Inverse hyperbolic tangent function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -969,6 +1141,12 @@ impl f64 { /// Gamma function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `tgamma` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -991,6 +1169,12 @@ impl f64 { /// /// The integer part of the tuple indicates the sign of the gamma function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `lgamma_r` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` From 3770cf7abf2c62667e8b197be1bea3665e75a8f1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 29 Feb 2024 12:06:51 +0100 Subject: [PATCH 58/62] Improve suggestion to rename type starting with underscore to make it more obvious what is actually suggested --- compiler/rustc_resolve/src/diagnostics.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 222dd69dbc4..6e8499be9e8 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1571,9 +1571,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { { // When the suggested binding change would be from `x` to `_x`, suggest changing the // original binding definition instead. (#60164) - (span, snippet, ", consider changing it") + let post = format!(", consider renaming `{}` into `{snippet}`", suggestion.candidate); + (span, snippet, post) } else { - (span, suggestion.candidate.to_string(), "") + (span, suggestion.candidate.to_string(), String::new()) }; let msg = match suggestion.target { SuggestionTarget::SimilarlyNamed => format!( From 451fd9815393b1b1ed8f28f27929645b31550a3f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 29 Feb 2024 12:07:35 +0100 Subject: [PATCH 59/62] Update UI test checking suggestion message to rename type starting with underscore --- tests/ui/suggestions/silenced-binding-typo.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/suggestions/silenced-binding-typo.stderr b/tests/ui/suggestions/silenced-binding-typo.stderr index 8dbd94208d6..c362d00c713 100644 --- a/tests/ui/suggestions/silenced-binding-typo.stderr +++ b/tests/ui/suggestions/silenced-binding-typo.stderr @@ -4,7 +4,7 @@ error[E0425]: cannot find value `x` in this scope LL | let _y = x; | ^ | -help: a local variable with a similar name exists, consider changing it +help: a local variable with a similar name exists, consider renaming `_x` into `x` | LL | let x = 42; | ~ From 7400f22d9285fdfdefa7c27741ff648616f4c006 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Thu, 29 Feb 2024 12:38:21 +0100 Subject: [PATCH 60/62] Document which methods on `f32` are precise Same as #118217 but for `f32`. --- library/std/src/f32.rs | 184 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index b60d7a72411..6ec389400ae 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -31,6 +31,8 @@ pub use core::f32::{ impl f32 { /// Returns the largest integer less than or equal to `self`. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -52,6 +54,8 @@ impl f32 { /// Returns the smallest integer greater than or equal to `self`. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -73,6 +77,8 @@ impl f32 { /// Returns the nearest integer to `self`. If a value is half-way between two /// integers, round away from `0.0`. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -99,6 +105,8 @@ impl f32 { /// Returns the nearest integer to a number. Rounds half-way cases to the number /// with an even least significant digit. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -123,6 +131,8 @@ impl f32 { /// Returns the integer part of `self`. /// This means that non-integer numbers are always truncated towards zero. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -145,6 +155,8 @@ impl f32 { /// Returns the fractional part of `self`. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -166,6 +178,8 @@ impl f32 { /// Computes the absolute value of `self`. /// + /// This function always returns the precise result. + /// /// # Examples /// /// ``` @@ -249,6 +263,12 @@ impl f32 { /// this is not always true, and will be heavily dependant on designing /// algorithms with specific target hardware in mind. /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// /// # Examples /// /// ``` @@ -276,6 +296,11 @@ impl f32 { /// In other words, the result is `self / rhs` rounded to the integer `n` /// such that `self >= n * rhs`. /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// /// # Examples /// /// ``` @@ -309,6 +334,11 @@ impl f32 { /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` /// approximately. /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// /// # Examples /// /// ``` @@ -337,6 +367,10 @@ impl f32 { /// It might have a different sequence of rounding operations than `powf`, /// so the results are not guaranteed to agree. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -355,6 +389,10 @@ impl f32 { /// Raises a number to a floating point power. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -375,6 +413,12 @@ impl f32 { /// /// Returns NaN if `self` is a negative number other than `-0.0`. /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// /// # Examples /// /// ``` @@ -398,6 +442,10 @@ impl f32 { /// Returns `e^(self)`, (the exponential function). /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -420,6 +468,10 @@ impl f32 { /// Returns `2^(self)`. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -440,6 +492,10 @@ impl f32 { /// Returns the natural logarithm of the number. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -466,6 +522,10 @@ impl f32 { /// `self.log2()` can produce more accurate results for base 2, and /// `self.log10()` can produce more accurate results for base 10. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -486,6 +546,10 @@ impl f32 { /// Returns the base 2 logarithm of the number. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -506,6 +570,10 @@ impl f32 { /// Returns the base 10 logarithm of the number. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -529,6 +597,12 @@ impl f32 { /// * If `self <= other`: `0.0` /// * Else: `self - other` /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `fdimf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -561,6 +635,12 @@ impl f32 { /// Returns the cube root of a number. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `cbrtf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -584,6 +664,12 @@ impl f32 { /// right-angle triangle with other sides having length `x.abs()` and /// `y.abs()`. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `hypotf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -605,6 +691,10 @@ impl f32 { /// Computes the sine of a number (in radians). /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -624,6 +714,10 @@ impl f32 { /// Computes the cosine of a number (in radians). /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -643,6 +737,12 @@ impl f32 { /// Computes the tangent of a number (in radians). /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `tanf` from libc on Unix and + /// Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -663,6 +763,12 @@ impl f32 { /// the range [-pi/2, pi/2] or NaN if the number is outside the range /// [-1, 1]. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `asinf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -686,6 +792,12 @@ impl f32 { /// the range [0, pi] or NaN if the number is outside the range /// [-1, 1]. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `acosf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -708,6 +820,12 @@ impl f32 { /// Computes the arctangent of a number. Return value is in radians in the /// range [-pi/2, pi/2]; /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `atanf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -734,6 +852,12 @@ impl f32 { /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `atan2f` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -764,6 +888,12 @@ impl f32 { /// Simultaneously computes the sine and cosine of the number, `x`. Returns /// `(sin(x), cos(x))`. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `(f32::sin(x), + /// f32::cos(x))`. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -787,6 +917,12 @@ impl f32 { /// Returns `e^(self) - 1` in a way that is accurate even if the /// number is close to zero. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `expm1f` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -809,6 +945,12 @@ impl f32 { /// Returns `ln(1+n)` (natural logarithm) more accurately than if /// the operations were performed separately. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `log1pf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -831,6 +973,12 @@ impl f32 { /// Hyperbolic sine function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `sinhf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -854,6 +1002,12 @@ impl f32 { /// Hyperbolic cosine function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `coshf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -877,6 +1031,12 @@ impl f32 { /// Hyperbolic tangent function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `tanhf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -900,6 +1060,10 @@ impl f32 { /// Inverse hyperbolic sine function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -923,6 +1087,10 @@ impl f32 { /// Inverse hyperbolic cosine function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -948,6 +1116,10 @@ impl f32 { /// Inverse hyperbolic tangent function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// /// # Examples /// /// ``` @@ -969,6 +1141,12 @@ impl f32 { /// Gamma function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `tgammaf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` @@ -991,6 +1169,12 @@ impl f32 { /// /// The integer part of the tuple indicates the sign of the gamma function. /// + /// # Platform-specific precision + /// + /// The precision of this function varies by platform and Rust version. + /// This function currently corresponds to the `lgamma_r` from libc on Unix + /// and Windows. Note that this might change in the future. + /// /// # Examples /// /// ``` From 96edeb898982a24c8e3874bc37241fe26c2cebb6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 29 Feb 2024 19:40:27 +0100 Subject: [PATCH 61/62] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 2c5cd796af7..75124dc8ff5 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -d3d145ea1cae47ad392173f890577788117da3d9 +1a1876c9790f168fb51afa335a7ba3e6fc267d75 From 2a376ceb8dc0aaef39b43eada02b9c94e0d51656 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 29 Feb 2024 19:45:14 +0100 Subject: [PATCH 62/62] add regression test --- src/tools/miri/tests/fail/rustc-error2.rs | 16 ++++++++++++++++ src/tools/miri/tests/fail/rustc-error2.stderr | 9 +++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/tools/miri/tests/fail/rustc-error2.rs create mode 100644 src/tools/miri/tests/fail/rustc-error2.stderr diff --git a/src/tools/miri/tests/fail/rustc-error2.rs b/src/tools/miri/tests/fail/rustc-error2.rs new file mode 100644 index 00000000000..fd2c5393385 --- /dev/null +++ b/src/tools/miri/tests/fail/rustc-error2.rs @@ -0,0 +1,16 @@ +// Regression test for https://github.com/rust-lang/rust/issues/121508. +struct Struct(T); + +impl std::ops::Deref for Struct { + type Target = dyn Fn(T); + fn deref(&self) -> &assert_mem_uninitialized_valid::Target { + //~^ERROR: undeclared crate or module + unimplemented!() + } +} + +fn main() { + let f = Struct(Default::default()); + f(0); + f(0); +} diff --git a/src/tools/miri/tests/fail/rustc-error2.stderr b/src/tools/miri/tests/fail/rustc-error2.stderr new file mode 100644 index 00000000000..de2861a019c --- /dev/null +++ b/src/tools/miri/tests/fail/rustc-error2.stderr @@ -0,0 +1,9 @@ +error[E0433]: failed to resolve: use of undeclared crate or module `assert_mem_uninitialized_valid` + --> $DIR/rustc-error2.rs:LL:CC + | +LL | fn deref(&self) -> &assert_mem_uninitialized_valid::Target { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared crate or module `assert_mem_uninitialized_valid` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0433`.