From 709ea7469f5093fb488607fcb966445c63d49a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?A=C3=AFssata?= Date: Mon, 11 Jul 2022 13:12:41 +0000 Subject: [PATCH 01/47] Add Read Impl for &Stdin --- library/std/src/io/stdio.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 261b570dee74..e929eaa63969 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -445,6 +445,29 @@ impl Read for Stdin { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for &Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.lock().read(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.lock().read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.lock().is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.lock().read_to_end(buf) + } + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + self.lock().read_to_string(buf) + } + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.lock().read_exact(buf) + } +} + // only used by platform-dependent io::copy specializations, i.e. unused on some platforms #[cfg(any(target_os = "linux", target_os = "android"))] impl StdinLock<'_> { From 8ea292278aadcbd923e317fc39447fd6ad2f0b6c Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 9 Aug 2023 14:36:54 +0100 Subject: [PATCH 02/47] Make `impl` impl take `?Sized` --- library/std/src/os/fd/owned.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 24f2bdcf4217..06b3c4583bf1 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -244,7 +244,7 @@ pub trait AsFd { } #[stable(feature = "io_safety", since = "1.63.0")] -impl AsFd for &T { +impl AsFd for &T { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { T::as_fd(self) @@ -252,7 +252,7 @@ impl AsFd for &T { } #[stable(feature = "io_safety", since = "1.63.0")] -impl AsFd for &mut T { +impl AsFd for &mut T { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { T::as_fd(self) @@ -396,7 +396,7 @@ impl From for crate::net::UdpSocket { /// impl MyTrait for Box {} /// # } /// ``` -impl AsFd for crate::sync::Arc { +impl AsFd for crate::sync::Arc { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { (**self).as_fd() @@ -404,7 +404,7 @@ impl AsFd for crate::sync::Arc { } #[stable(feature = "asfd_rc", since = "1.69.0")] -impl AsFd for crate::rc::Rc { +impl AsFd for crate::rc::Rc { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { (**self).as_fd() @@ -412,7 +412,7 @@ impl AsFd for crate::rc::Rc { } #[stable(feature = "asfd_ptrs", since = "1.64.0")] -impl AsFd for Box { +impl AsFd for Box { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { (**self).as_fd() From f4aeb7030993f5c143e9a84c50061892f6b7046f Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 28 Jan 2024 23:31:55 +0000 Subject: [PATCH 03/47] Make `impl` impl take `?Sized` --- library/std/src/os/windows/io/handle.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index b0540872c0b6..55c391390443 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -422,7 +422,7 @@ pub trait AsHandle { } #[stable(feature = "io_safety", since = "1.63.0")] -impl AsHandle for &T { +impl AsHandle for &T { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { T::as_handle(self) @@ -430,7 +430,7 @@ impl AsHandle for &T { } #[stable(feature = "io_safety", since = "1.63.0")] -impl AsHandle for &mut T { +impl AsHandle for &mut T { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { T::as_handle(self) @@ -450,7 +450,7 @@ impl AsHandle for &mut T { /// impl MyTrait for Box {} /// # } /// ``` -impl AsHandle for crate::sync::Arc { +impl AsHandle for crate::sync::Arc { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { (**self).as_handle() @@ -458,7 +458,7 @@ impl AsHandle for crate::sync::Arc { } #[stable(feature = "as_windows_ptrs", since = "1.71.0")] -impl AsHandle for crate::rc::Rc { +impl AsHandle for crate::rc::Rc { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { (**self).as_handle() @@ -466,7 +466,7 @@ impl AsHandle for crate::rc::Rc { } #[stable(feature = "as_windows_ptrs", since = "1.71.0")] -impl AsHandle for Box { +impl AsHandle for Box { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { (**self).as_handle() From c5aa659832334dca7cc9b27074bdc686b9650c12 Mon Sep 17 00:00:00 2001 From: GnomedDev Date: Tue, 20 Feb 2024 18:59:56 +0000 Subject: [PATCH 04/47] Reduce alignment of TypeId to u64 alignment --- library/core/src/any.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index e8f00e8760e1..7efbabb72574 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -605,7 +605,8 @@ impl dyn Any + Send + Sync { #[derive(Clone, Copy, Debug, Eq, PartialOrd, Ord)] #[stable(feature = "rust1", since = "1.0.0")] pub struct TypeId { - t: u128, + // See #115620 for this representation. + t: (u64, u64), } #[stable(feature = "rust1", since = "1.0.0")] @@ -637,7 +638,10 @@ impl TypeId { #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] pub const fn of() -> TypeId { let t: u128 = intrinsics::type_id::(); - TypeId { t } + + let t1 = (t >> 64) as u64; + let t2 = t as u64; + TypeId { t: (t1, t2) } } } @@ -657,7 +661,7 @@ impl hash::Hash for TypeId { // - It is correct to do so -- only hashing a subset of `self` is still // with an `Eq` implementation that considers the entire value, as // ours does. - (self.t as u64).hash(state); + self.t.0.hash(state); } } From aa25c481b77cb9e0ec25c6350d7ded110fae7f0e Mon Sep 17 00:00:00 2001 From: Gnome! Date: Tue, 20 Feb 2024 19:12:42 +0000 Subject: [PATCH 05/47] Add extra detail to field comment Co-authored-by: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> --- library/core/src/any.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 7efbabb72574..adc45038148e 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -605,7 +605,8 @@ impl dyn Any + Send + Sync { #[derive(Clone, Copy, Debug, Eq, PartialOrd, Ord)] #[stable(feature = "rust1", since = "1.0.0")] pub struct TypeId { - // See #115620 for this representation. + // We avoid using `u128` because that imposes higher alignment requirements on many platforms. + // See issue #115620 for more information. t: (u64, u64), } From e49cd1c578227e450f0a703b08fa429e3931c228 Mon Sep 17 00:00:00 2001 From: Kornel Date: Wed, 21 Feb 2024 15:56:50 +0000 Subject: [PATCH 06/47] TryReserveError to ErrorKind::OutOfMemory --- library/std/src/io/error.rs | 12 ++++++++++++ library/std/src/io/tests.rs | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 13cc0511e103..7ae15e0fd017 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -83,6 +83,18 @@ impl From for Error { } } +#[stable(feature = "io_error_from_try_reserve", since = "CURRENT_RUSTC_VERSION")] +impl From for Error { + /// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`]. + /// + /// `TryReserveError` won't be available as the error `source()`, + /// but this may change in the future. + fn from(_: alloc::collections::TryReserveError) -> Error { + // ErrorData::Custom allocates, which isn't great for handling OOM errors. + ErrorKind::OutOfMemory.into() + } +} + // Only derive debug in tests, to make sure it // doesn't accidentally get printed. #[cfg_attr(test, derive(Debug))] diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 5396f7f6e216..c306de3039fc 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -692,3 +692,13 @@ fn read_buf_full_read() { assert_eq!(BufReader::new(FullRead).fill_buf().unwrap().len(), DEFAULT_BUF_SIZE); } + +#[test] +// 64-bit only to be sure the allocator will fail fast on an impossible to satsify size +#[cfg(target_pointer_width = "64")] +fn try_oom_error() { + let mut v = Vec::::new(); + let reserve_err = v.try_reserve(isize::MAX as usize - 1).unwrap_err(); + let io_err = io::Error::from(reserve_err); + assert_eq!(io::ErrorKind::OutOfMemory, io_err.kind()); +} From aa581f0a0a3fbb61efe1f37786c237b2733dfbfa Mon Sep 17 00:00:00 2001 From: Kornel Date: Wed, 21 Feb 2024 15:59:41 +0000 Subject: [PATCH 07/47] Remove unnecessary map_err --- library/std/src/fs.rs | 8 ++++---- library/std/src/io/buffered/bufreader.rs | 2 +- library/std/src/io/impls.rs | 4 ++-- library/std/src/io/mod.rs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 6b1dd1b5af4c..fee529993715 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -261,7 +261,7 @@ pub fn read>(path: P) -> io::Result> { let mut file = File::open(path)?; let size = file.metadata().map(|m| m.len() as usize).ok(); let mut bytes = Vec::new(); - bytes.try_reserve_exact(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?; + bytes.try_reserve_exact(size.unwrap_or(0))?; io::default_read_to_end(&mut file, &mut bytes, size)?; Ok(bytes) } @@ -304,7 +304,7 @@ pub fn read_to_string>(path: P) -> io::Result { let mut file = File::open(path)?; let size = file.metadata().map(|m| m.len() as usize).ok(); let mut string = String::new(); - string.try_reserve_exact(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?; + string.try_reserve_exact(size.unwrap_or(0))?; io::default_read_to_string(&mut file, &mut string, size)?; Ok(string) } @@ -777,14 +777,14 @@ impl Read for &File { // Reserves space in the buffer based on the file size when available. fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { let size = buffer_capacity_required(self); - buf.try_reserve(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?; + buf.try_reserve(size.unwrap_or(0))?; io::default_read_to_end(self, buf, size) } // Reserves space in the buffer based on the file size when available. fn read_to_string(&mut self, buf: &mut String) -> io::Result { let size = buffer_capacity_required(self); - buf.try_reserve(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?; + buf.try_reserve(size.unwrap_or(0))?; io::default_read_to_string(self, buf, size) } } diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index e0dc9f96ae9b..83db332ee255 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -344,7 +344,7 @@ impl Read for BufReader { // delegate to the inner implementation. fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { let inner_buf = self.buffer(); - buf.try_reserve(inner_buf.len()).map_err(|_| io::ErrorKind::OutOfMemory)?; + buf.try_reserve(inner_buf.len())?; buf.extend_from_slice(inner_buf); let nread = inner_buf.len(); self.discard_buffer(); diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 557e64dc8674..cb972abd2b82 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -304,7 +304,7 @@ impl Read for &[u8] { #[inline] fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { let len = self.len(); - buf.try_reserve(len).map_err(|_| ErrorKind::OutOfMemory)?; + buf.try_reserve(len)?; buf.extend_from_slice(*self); *self = &self[len..]; Ok(len) @@ -452,7 +452,7 @@ impl Read for VecDeque { fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { // The total len is known upfront so we can reserve it in a single call. let len = self.len(); - buf.try_reserve(len).map_err(|_| ErrorKind::OutOfMemory)?; + buf.try_reserve(len)?; let (front, back) = self.as_slices(); buf.extend_from_slice(front); diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 102db62fced3..0d1c2702b782 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -465,7 +465,7 @@ pub(crate) fn default_read_to_end( if buf.len() == buf.capacity() { // buf is full, need more space - buf.try_reserve(PROBE_SIZE).map_err(|_| ErrorKind::OutOfMemory)?; + buf.try_reserve(PROBE_SIZE)?; } let mut spare = buf.spare_capacity_mut(); @@ -834,7 +834,7 @@ pub trait Read { /// if src_buf.is_empty() { /// break; /// } - /// dest_vec.try_reserve(src_buf.len()).map_err(|_| io::ErrorKind::OutOfMemory)?; + /// dest_vec.try_reserve(src_buf.len())?; /// dest_vec.extend_from_slice(src_buf); /// /// // Any irreversible side effects should happen after `try_reserve` succeeds, From ea476b1fa1cd42f5ee10a4d6cc95d6bbaf63ce86 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Fri, 23 Feb 2024 22:41:21 +0300 Subject: [PATCH 08/47] on the fly type casting for `build.rustc` and `build.cargo` Signed-off-by: onur-ozkan --- src/bootstrap/src/core/config/config.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 834025c81887..3461beba0bd0 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -811,8 +811,8 @@ define_config! { host: Option> = "host", target: Option> = "target", build_dir: Option = "build-dir", - cargo: Option = "cargo", - rustc: Option = "rustc", + cargo: Option = "cargo", + rustc: Option = "rustc", rustfmt: Option = "rustfmt", docs: Option = "docs", compiler_docs: Option = "compiler-docs", @@ -1421,7 +1421,7 @@ impl Config { if !flags.skip_stage0_validation { config.check_stage0_version(&rustc, "rustc"); } - PathBuf::from(rustc) + rustc } else { config.download_beta_toolchain(); config.out.join(config.build.triple).join("stage0/bin/rustc") @@ -1431,7 +1431,7 @@ impl Config { if !flags.skip_stage0_validation { config.check_stage0_version(&cargo, "cargo"); } - PathBuf::from(cargo) + cargo } else { config.download_beta_toolchain(); config.out.join(config.build.triple).join("stage0/bin/cargo") @@ -2292,7 +2292,7 @@ impl Config { } // check rustc/cargo version is same or lower with 1 apart from the building one - pub fn check_stage0_version(&self, program_path: &str, component_name: &'static str) { + pub fn check_stage0_version(&self, program_path: &Path, component_name: &'static str) { if self.dry_run() { return; } @@ -2303,7 +2303,8 @@ impl Config { let stage0_name = stage0_output.next().unwrap(); if stage0_name != component_name { fail(&format!( - "Expected to find {component_name} at {program_path} but it claims to be {stage0_name}" + "Expected to find {component_name} at {} but it claims to be {stage0_name}", + program_path.display() )); } From f142476cf3f0c0c2296c82836fd4c7c86e20d0db Mon Sep 17 00:00:00 2001 From: David Thomas Date: Sun, 25 Feb 2024 14:09:30 +0000 Subject: [PATCH 09/47] Fix Hash impl --- library/core/src/any.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index adc45038148e..a4252d0c9e03 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -662,7 +662,7 @@ impl hash::Hash for TypeId { // - It is correct to do so -- only hashing a subset of `self` is still // with an `Eq` implementation that considers the entire value, as // ours does. - self.t.0.hash(state); + self.t.1.hash(state); } } From b921a34f1738a615c17db513868b6c5219f32893 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 25 Feb 2024 23:47:10 -0800 Subject: [PATCH 10/47] Fix stable feature name and stabilization version of Read for &Stdin --- library/std/src/io/stdio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index e929eaa63969..6920281f24bf 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -445,7 +445,7 @@ impl Read for Stdin { } } -#[stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "read_shared_stdin", since = "CURRENT_RUSTC_VERSION")] impl Read for &Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.lock().read(buf) From b18280f9d5d07ff1a1b5352924c383c1ed3024e9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 25 Feb 2024 23:50:00 -0800 Subject: [PATCH 11/47] Fill in Read::read_buf for &Stdin --- library/std/src/io/stdio.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 6920281f24bf..19ed03056a2f 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -450,6 +450,9 @@ impl Read for &Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.lock().read(buf) } + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.lock().read_buf(buf) + } fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { self.lock().read_vectored(bufs) } From 228347878ebd45d5cb7e6f424bad18eb0b92543a Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Tue, 27 Feb 2024 18:54:18 -0300 Subject: [PATCH 12/47] Implement junction_point --- library/std/src/fs/tests.rs | 12 +-- library/std/src/os/windows/fs.rs | 12 +++ library/std/src/sys/pal/windows/c.rs | 11 --- library/std/src/sys/pal/windows/fs.rs | 130 ++++++++++++++------------ 4 files changed, 85 insertions(+), 80 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 5917bf8df029..a65e78542bf2 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -20,11 +20,9 @@ use crate::os::unix::fs::symlink as symlink_dir; #[cfg(unix)] use crate::os::unix::fs::symlink as symlink_file; #[cfg(unix)] -use crate::os::unix::fs::symlink as symlink_junction; +use crate::os::unix::fs::symlink as junction_point; #[cfg(windows)] -use crate::os::windows::fs::{symlink_dir, symlink_file, OpenOptionsExt}; -#[cfg(windows)] -use crate::sys::fs::symlink_junction; +use crate::os::windows::fs::{junction_point, symlink_dir, symlink_file, OpenOptionsExt}; #[cfg(target_os = "macos")] use crate::sys::weak::weak; @@ -598,7 +596,7 @@ fn recursive_rmdir() { check!(fs::create_dir_all(&dtt)); check!(fs::create_dir_all(&d2)); check!(check!(File::create(&canary)).write(b"foo")); - check!(symlink_junction(&d2, &dt.join("d2"))); + check!(junction_point(&d2, &dt.join("d2"))); let _ = symlink_file(&canary, &d1.join("canary")); check!(fs::remove_dir_all(&d1)); @@ -615,7 +613,7 @@ fn recursive_rmdir_of_symlink() { let canary = dir.join("do_not_delete"); check!(fs::create_dir_all(&dir)); check!(check!(File::create(&canary)).write(b"foo")); - check!(symlink_junction(&dir, &link)); + check!(junction_point(&dir, &link)); check!(fs::remove_dir_all(&link)); assert!(!link.is_dir()); @@ -1403,7 +1401,7 @@ fn create_dir_all_with_junctions() { fs::create_dir(&target).unwrap(); - check!(symlink_junction(&target, &junction)); + check!(junction_point(&target, &junction)); check!(fs::create_dir_all(&b)); // the junction itself is not a directory, but `is_dir()` on a Path // follows links diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index e9d7a13e9d5b..27947d91c99d 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -620,3 +620,15 @@ pub fn symlink_file, Q: AsRef>(original: P, link: Q) -> io: pub fn symlink_dir, Q: AsRef>(original: P, link: Q) -> io::Result<()> { sys::fs::symlink_inner(original.as_ref(), link.as_ref(), true) } + +/// Create a junction point. +/// +/// The `link` path will be a directory junction pointing to the original path. +/// If `link` is a relative path then it will be made absolute prior to creating the junction point. +/// The `original` path must be a directory or a link to a directory, otherwise the junction point will be broken. +/// +/// If either path is not a local file path then this will fail. +#[unstable(feature = "junction_point", issue = "121709")] +pub fn junction_point, Q: AsRef>(original: P, link: Q) -> io::Result<()> { + sys::fs::junction_point(original.as_ref(), link.as_ref()) +} diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index b007796722ba..abdbc0ad315d 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -25,7 +25,6 @@ pub type UINT = c_uint; pub type WCHAR = u16; pub type USHORT = c_ushort; pub type SIZE_T = usize; -pub type WORD = u16; pub type CHAR = c_char; pub type ULONG = c_ulong; @@ -142,16 +141,6 @@ pub struct MOUNT_POINT_REPARSE_BUFFER { pub PrintNameLength: c_ushort, pub PathBuffer: WCHAR, } -#[repr(C)] -pub struct REPARSE_MOUNTPOINT_DATA_BUFFER { - pub ReparseTag: DWORD, - pub ReparseDataLength: DWORD, - pub Reserved: WORD, - pub ReparseTargetLength: WORD, - pub ReparseTargetMaximumLength: WORD, - pub Reserved1: WORD, - pub ReparseTarget: WCHAR, -} #[repr(C)] pub struct SOCKADDR_STORAGE_LH { diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index 3a9e7b4660b3..e92c5e80eac9 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -1,7 +1,9 @@ +use core::ptr::addr_of; + use crate::os::windows::prelude::*; use crate::borrow::Cow; -use crate::ffi::{c_void, OsString}; +use crate::ffi::{c_void, OsStr, OsString}; use crate::fmt; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem::{self, MaybeUninit}; @@ -1446,75 +1448,79 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { Ok(size as u64) } -#[allow(dead_code)] -pub fn symlink_junction, Q: AsRef>( - original: P, - junction: Q, -) -> io::Result<()> { - symlink_junction_inner(original.as_ref(), junction.as_ref()) -} - -// Creating a directory junction on windows involves dealing with reparse -// points and the DeviceIoControl function, and this code is a skeleton of -// what can be found here: -// -// http://www.flexhex.com/docs/articles/hard-links.phtml -#[allow(dead_code)] -fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { - let d = DirBuilder::new(); - d.mkdir(junction)?; - +pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> { + // Create and open a new directory in one go. let mut opts = OpenOptions::new(); + opts.create_new(true); opts.write(true); - opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); - let f = File::open(junction, &opts)?; - let h = f.as_inner().as_raw_handle(); - unsafe { - let mut data = - Align8([MaybeUninit::::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]); - let data_ptr = data.0.as_mut_ptr(); - let data_end = data_ptr.add(c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize); - let db = data_ptr.cast::(); - // Zero the header to ensure it's fully initialized, including reserved parameters. - *db = mem::zeroed(); - let reparse_target_slice = { - let buf_start = ptr::addr_of_mut!((*db).ReparseTarget).cast::(); - // Compute offset in bytes and then divide so that we round down - // rather than hit any UB (admittedly this arithmetic should work - // out so that this isn't necessary) - let buf_len_bytes = usize::try_from(data_end.byte_offset_from(buf_start)).unwrap(); - let buf_len_wchars = buf_len_bytes / core::mem::size_of::(); - core::slice::from_raw_parts_mut(buf_start, buf_len_wchars) - }; + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_POSIX_SEMANTICS); + opts.attributes(c::FILE_ATTRIBUTE_DIRECTORY); - // FIXME: this conversion is very hacky - let iter = br"\??\" - .iter() - .map(|x| *x as u16) - .chain(original.as_os_str().encode_wide()) - .chain(core::iter::once(0)); - let mut i = 0; - for c in iter { - if i >= reparse_target_slice.len() { - return Err(crate::io::const_io_error!( - crate::io::ErrorKind::InvalidFilename, - "Input filename is too long" - )); - } - reparse_target_slice[i] = c; - i += 1; + let d = File::open(link, &opts)?; + + // We need to get an absolute, NT-style path. + let path_bytes = original.as_os_str().as_encoded_bytes(); + let abs_path: Vec = if path_bytes.starts_with(br"\\?\") || path_bytes.starts_with(br"\??\") + { + // It's already an absolute path, we just need to convert the prefix to `\??\` + let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&path_bytes[4..]) }; + r"\??\".encode_utf16().chain(bytes.encode_wide()).collect() + } else { + // Get an absolute path and then convert the prefix to `\??\` + let abs_path = crate::path::absolute(original)?.into_os_string().into_encoded_bytes(); + if abs_path.len() > 0 && abs_path[1..].starts_with(br":\") { + let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path) }; + r"\??\".encode_utf16().chain(bytes.encode_wide()).collect() + } else if abs_path.starts_with(br"\\.\") { + let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[4..]) }; + r"\??\".encode_utf16().chain(bytes.encode_wide()).collect() + } else if abs_path.starts_with(br"\\") { + let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[2..]) }; + r"\??\UNC\".encode_utf16().chain(bytes.encode_wide()).collect() + } else { + return Err(io::const_io_error!(io::ErrorKind::InvalidInput, "path is not valid")); } - (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT; - (*db).ReparseTargetMaximumLength = (i * 2) as c::WORD; - (*db).ReparseTargetLength = ((i - 1) * 2) as c::WORD; - (*db).ReparseDataLength = (*db).ReparseTargetLength as c::DWORD + 12; + }; + // Defined inline so we don't have to mess about with variable length buffer. + #[repr(C)] + pub struct MountPointBuffer { + ReparseTag: u32, + ReparseDataLength: u16, + Reserved: u16, + SubstituteNameOffset: u16, + SubstituteNameLength: u16, + PrintNameOffset: u16, + PrintNameLength: u16, + PathBuffer: [MaybeUninit; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize], + } + let data_len = 12 + (abs_path.len() * 2); + if data_len > u16::MAX as usize { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "`original` path is too long" + )); + } + let data_len = data_len as u16; + let mut header = MountPointBuffer { + ReparseTag: c::IO_REPARSE_TAG_MOUNT_POINT, + ReparseDataLength: data_len, + Reserved: 0, + SubstituteNameOffset: 0, + SubstituteNameLength: (abs_path.len() * 2) as u16, + PrintNameOffset: ((abs_path.len() + 1) * 2) as u16, + PrintNameLength: 0, + PathBuffer: [MaybeUninit::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize], + }; + unsafe { + let ptr = header.PathBuffer.as_mut_ptr(); + ptr.copy_from(abs_path.as_ptr().cast::>(), abs_path.len()); let mut ret = 0; cvt(c::DeviceIoControl( - h as *mut _, + d.as_raw_handle(), c::FSCTL_SET_REPARSE_POINT, - data_ptr.cast(), - (*db).ReparseDataLength + 8, + addr_of!(header).cast::(), + data_len as u32 + 8, ptr::null_mut(), 0, &mut ret, From f27a22c24a51ba734e4eb25c499320d819ebe236 Mon Sep 17 00:00:00 2001 From: Kornel Date: Tue, 30 Jan 2024 15:58:08 +0000 Subject: [PATCH 13/47] try_with_capacity for RawVec --- library/alloc/src/raw_vec.rs | 36 +++++++++++++++++------------- library/alloc/src/raw_vec/tests.rs | 7 +++--- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index dd8d6f6c7e63..2d86fa377f16 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -17,7 +17,6 @@ use crate::collections::TryReserveErrorKind::*; #[cfg(test)] mod tests; -#[cfg(not(no_global_oom_handling))] enum AllocInit { /// The contents of the new memory are uninitialized. Uninitialized, @@ -93,6 +92,8 @@ impl RawVec { /// zero-sized. Note that if `T` is zero-sized this means you will /// *not* get a `RawVec` with the requested capacity. /// + /// Non-fallible version of `try_with_capacity` + /// /// # Panics /// /// Panics if the requested capacity exceeds `isize::MAX` bytes. @@ -104,7 +105,7 @@ impl RawVec { #[must_use] #[inline] pub fn with_capacity(capacity: usize) -> Self { - Self::with_capacity_in(capacity, Global) + handle_reserve(Self::try_allocate_in(capacity, AllocInit::Uninitialized, Global)) } /// Like `with_capacity`, but guarantees the buffer is zeroed. @@ -142,7 +143,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { - Self::allocate_in(capacity, AllocInit::Uninitialized, alloc) + handle_reserve(Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc)) } /// Like `with_capacity_zeroed`, but parameterized over the choice @@ -150,7 +151,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { - Self::allocate_in(capacity, AllocInit::Zeroed, alloc) + handle_reserve(Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc)) } /// Converts the entire buffer into `Box<[MaybeUninit]>` with the specified `len`. @@ -179,35 +180,40 @@ impl RawVec { } } - #[cfg(not(no_global_oom_handling))] - fn allocate_in(capacity: usize, init: AllocInit, alloc: A) -> Self { + fn try_allocate_in( + capacity: usize, + init: AllocInit, + alloc: A, + ) -> Result { // Don't allocate here because `Drop` will not deallocate when `capacity` is 0. + if T::IS_ZST || capacity == 0 { - Self::new_in(alloc) + Ok(Self::new_in(alloc)) } else { // We avoid `unwrap_or_else` here because it bloats the amount of // LLVM IR generated. let layout = match Layout::array::(capacity) { Ok(layout) => layout, - Err(_) => capacity_overflow(), + Err(_) => return Err(CapacityOverflow.into()), }; - match alloc_guard(layout.size()) { - Ok(_) => {} - Err(_) => capacity_overflow(), + + if let Err(err) = alloc_guard(layout.size()) { + return Err(err); } + let result = match init { AllocInit::Uninitialized => alloc.allocate(layout), AllocInit::Zeroed => alloc.allocate_zeroed(layout), }; let ptr = match result { Ok(ptr) => ptr, - Err(_) => handle_alloc_error(layout), + Err(_) => return Err(AllocError { layout, non_exhaustive: () }.into()), }; // Allocators currently return a `NonNull<[u8]>` whose length // matches the size requested. If that ever changes, the capacity // here should change to `ptr.len() / mem::size_of::()`. - Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc } + Ok(Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc }) } } @@ -537,11 +543,11 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { // Central function for reserve error handling. #[cfg(not(no_global_oom_handling))] #[inline] -fn handle_reserve(result: Result<(), TryReserveError>) { +fn handle_reserve(result: Result) -> T { match result.map_err(|e| e.kind()) { + Ok(res) => res, Err(CapacityOverflow) => capacity_overflow(), Err(AllocError { layout, .. }) => handle_alloc_error(layout), - Ok(()) => { /* yay */ } } } diff --git a/library/alloc/src/raw_vec/tests.rs b/library/alloc/src/raw_vec/tests.rs index f8cada01c030..4194be530612 100644 --- a/library/alloc/src/raw_vec/tests.rs +++ b/library/alloc/src/raw_vec/tests.rs @@ -105,13 +105,14 @@ fn zst() { let v: RawVec = RawVec::with_capacity_in(100, Global); zst_sanity(&v); - let v: RawVec = RawVec::allocate_in(0, AllocInit::Uninitialized, Global); + let v: RawVec = RawVec::try_allocate_in(0, AllocInit::Uninitialized, Global).unwrap(); zst_sanity(&v); - let v: RawVec = RawVec::allocate_in(100, AllocInit::Uninitialized, Global); + let v: RawVec = RawVec::try_allocate_in(100, AllocInit::Uninitialized, Global).unwrap(); zst_sanity(&v); - let mut v: RawVec = RawVec::allocate_in(usize::MAX, AllocInit::Uninitialized, Global); + let mut v: RawVec = + RawVec::try_allocate_in(usize::MAX, AllocInit::Uninitialized, Global).unwrap(); zst_sanity(&v); // Check all these operations work as expected with zero-sized elements. From 78fb977d6b600865b7887245d24f6dca22a0099a Mon Sep 17 00:00:00 2001 From: Kornel Date: Tue, 30 Jan 2024 16:08:57 +0000 Subject: [PATCH 14/47] try_with_capacity for Vec, VecDeque, String #91913 --- .../alloc/src/collections/vec_deque/mod.rs | 24 +++++++++++++ library/alloc/src/lib.rs | 1 + library/alloc/src/raw_vec.rs | 9 +++++ library/alloc/src/string.rs | 13 +++++++ library/alloc/src/vec/mod.rs | 34 ++++++++++++++++++ library/alloc/tests/lib.rs | 1 + library/alloc/tests/string.rs | 11 ++++++ library/alloc/tests/vec.rs | 12 +++++++ library/alloc/tests/vec_deque.rs | 11 ++++++ tests/codegen/vec-with-capacity.rs | 35 +++++++++++++++++++ tests/ui/suggestions/deref-path-method.stderr | 4 +-- tests/ui/ufcs/bad-builder.stderr | 4 +-- 12 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 tests/codegen/vec-with-capacity.rs diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index c35bab5ef665..41adc2e79dc7 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -559,6 +559,30 @@ impl VecDeque { pub fn with_capacity(capacity: usize) -> VecDeque { Self::with_capacity_in(capacity, Global) } + + /// Creates an empty deque with space for at least `capacity` elements. + /// + /// # Errors + /// + /// Returns an error if the capacity exceeds `isize::MAX` _bytes_, + /// or if the allocator reports allocation failure. + /// + /// # Examples + /// + /// ``` + /// # #![feature(try_with_capacity)] + /// # #[allow(unused)] + /// # fn example() -> Result<(), std::collections::TryReserveError> { + /// use std::collections::VecDeque; + /// + /// let deque: VecDeque = VecDeque::try_with_capacity(10)?; + /// # Ok(()) } + /// ``` + #[inline] + #[unstable(feature = "try_with_capacity", issue = "91913")] + pub fn try_with_capacity(capacity: usize) -> Result, TryReserveError> { + Ok(VecDeque { head: 0, len: 0, buf: RawVec::try_with_capacity_in(capacity, Global)? }) + } } impl VecDeque { diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 28695ade5bf5..ca504b05a968 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -163,6 +163,7 @@ #![feature(trusted_len)] #![feature(trusted_random_access)] #![feature(try_trait_v2)] +#![feature(try_with_capacity)] #![feature(tuple_trait)] #![feature(unchecked_math)] #![feature(unicode_internals)] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 2d86fa377f16..c5cf12209d90 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -20,6 +20,7 @@ mod tests; enum AllocInit { /// The contents of the new memory are uninitialized. Uninitialized, + #[cfg(not(no_global_oom_handling))] /// The new memory is guaranteed to be zeroed. Zeroed, } @@ -146,6 +147,13 @@ impl RawVec { handle_reserve(Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc)) } + /// Like `try_with_capacity`, but parameterized over the choice of + /// allocator for the returned `RawVec`. + #[inline] + pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { + Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc) + } + /// Like `with_capacity_zeroed`, but parameterized over the choice /// of allocator for the returned `RawVec`. #[cfg(not(no_global_oom_handling))] @@ -203,6 +211,7 @@ impl RawVec { let result = match init { AllocInit::Uninitialized => alloc.allocate(layout), + #[cfg(not(no_global_oom_handling))] AllocInit::Zeroed => alloc.allocate_zeroed(layout), }; let ptr = match result { diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 98ded7f6cdf5..c4dcff1b1c49 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -492,6 +492,19 @@ impl String { String { vec: Vec::with_capacity(capacity) } } + /// Creates a new empty `String` with at least the specified capacity. + /// + /// # Errors + /// + /// Returns [`Err`] if the capacity exceeds `isize::MAX` bytes, + /// or if the memory allocator reports failure. + /// + #[inline] + #[unstable(feature = "try_with_capacity", issue = "91913")] + pub fn try_with_capacity(capacity: usize) -> Result { + Ok(String { vec: Vec::try_with_capacity(capacity)? }) + } + // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is // required for this method definition, is not available. Since we don't // require this method for testing purposes, I'll just stub it diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 7bd19875584a..4b8b095c752f 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -481,6 +481,22 @@ impl Vec { Self::with_capacity_in(capacity, Global) } + /// Constructs a new, empty `Vec` with at least the specified capacity. + /// + /// The vector will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is 0, the vector will not allocate. + /// + /// # Errors + /// + /// Returns an error if the capacity exceeds `isize::MAX` _bytes_, + /// or if the allocator reports allocation failure. + #[inline] + #[unstable(feature = "try_with_capacity", issue = "91913")] + pub fn try_with_capacity(capacity: usize) -> Result { + Self::try_with_capacity_in(capacity, Global) + } + /// Creates a `Vec` directly from a pointer, a length, and a capacity. /// /// # Safety @@ -672,6 +688,24 @@ impl Vec { Vec { buf: RawVec::with_capacity_in(capacity, alloc), len: 0 } } + /// Constructs a new, empty `Vec` with at least the specified capacity + /// with the provided allocator. + /// + /// The vector will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is 0, the vector will not allocate. + /// + /// # Errors + /// + /// Returns an error if the capacity exceeds `isize::MAX` _bytes_, + /// or if the allocator reports allocation failure. + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "try_with_capacity", issue = "91913")] + pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { + Ok(Vec { buf: RawVec::try_with_capacity_in(capacity, alloc)?, len: 0 }) + } + /// Creates a `Vec` directly from a pointer, a length, a capacity, /// and an allocator. /// diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index c4e89a58a05a..e4e1a02fd832 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -20,6 +20,7 @@ #![feature(pattern)] #![feature(trusted_len)] #![feature(try_reserve_kind)] +#![feature(try_with_capacity)] #![feature(unboxed_closures)] #![feature(associated_type_bounds)] #![feature(binary_heap_into_iter_sorted)] diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index 711e4eef2e72..e20ceae87b0d 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -723,6 +723,17 @@ fn test_reserve_exact() { assert!(s.capacity() >= 33) } +#[test] +#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc +fn test_try_with_capacity() { + let string = String::try_with_capacity(1000).unwrap(); + assert_eq!(0, string.len()); + assert!(string.capacity() >= 1000 && string.capacity() <= isize::MAX as usize); + + assert!(String::try_with_capacity(usize::MAX).is_err()); +} + #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM #[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 15ee4d652052..aa95b4e97708 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -1694,6 +1694,18 @@ fn test_reserve_exact() { assert!(v.capacity() >= 33) } +#[test] +#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc +fn test_try_with_capacity() { + let mut vec: Vec = Vec::try_with_capacity(5).unwrap(); + assert_eq!(0, vec.len()); + assert!(vec.capacity() >= 5 && vec.capacity() <= isize::MAX as usize / 4); + assert!(vec.spare_capacity_mut().len() >= 5); + + assert!(Vec::::try_with_capacity(isize::MAX as usize + 1).is_err()); +} + #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM #[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index eda2f8bb812b..cea5de4dd598 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -1182,6 +1182,17 @@ fn test_reserve_exact_2() { assert!(v.capacity() >= 33) } +#[test] +#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc +fn test_try_with_capacity() { + let vec: VecDeque = VecDeque::try_with_capacity(5).unwrap(); + assert_eq!(0, vec.len()); + assert!(vec.capacity() >= 5 && vec.capacity() <= isize::MAX as usize / 4); + + assert!(VecDeque::::try_with_capacity(isize::MAX as usize + 1).is_err()); +} + #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM #[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc diff --git a/tests/codegen/vec-with-capacity.rs b/tests/codegen/vec-with-capacity.rs new file mode 100644 index 000000000000..47051f2eef89 --- /dev/null +++ b/tests/codegen/vec-with-capacity.rs @@ -0,0 +1,35 @@ +//@ compile-flags: -O +//@ ignore-debug +// (with debug assertions turned on, `assert_unchecked` generates a real assertion) + +#![crate_type = "lib"] +#![feature(try_with_capacity)] + +// CHECK-LABEL: @with_capacity_does_not_grow1 +#[no_mangle] +pub fn with_capacity_does_not_grow1() -> Vec { + let v = Vec::with_capacity(1234); + // CHECK: call {{.*}}__rust_alloc( + // CHECK-NOT: call {{.*}}__rust_realloc + // CHECK-NOT: call {{.*}}capacity_overflow + // CHECK-NOT: call {{.*}}finish_grow + // CHECK-NOT: call {{.*}}reserve + // CHECK-NOT: memcpy + // CHECK-NOT: memset + v +} + +// CHECK-LABEL: @try_with_capacity_does_not_grow2 +#[no_mangle] +pub fn try_with_capacity_does_not_grow2() -> Option>> { + let v = Vec::try_with_capacity(1234).ok()?; + // CHECK: call {{.*}}__rust_alloc( + // CHECK-NOT: call {{.*}}__rust_realloc + // CHECK-NOT: call {{.*}}capacity_overflow + // CHECK-NOT: call {{.*}}finish_grow + // CHECK-NOT: call {{.*}}handle_alloc_error + // CHECK-NOT: call {{.*}}reserve + // CHECK-NOT: memcpy + // CHECK-NOT: memset + Some(v) +} diff --git a/tests/ui/suggestions/deref-path-method.stderr b/tests/ui/suggestions/deref-path-method.stderr index a2b68fa966fc..b27d9aef0661 100644 --- a/tests/ui/suggestions/deref-path-method.stderr +++ b/tests/ui/suggestions/deref-path-method.stderr @@ -7,9 +7,9 @@ LL | Vec::contains(&vec, &0); note: if you're trying to build a new `Vec<_, _>` consider using one of the following associated functions: Vec::::new Vec::::with_capacity + Vec::::try_with_capacity Vec::::from_raw_parts - Vec::::new_in - and 2 others + and 4 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: the function `contains` is implemented on `[_]` | diff --git a/tests/ui/ufcs/bad-builder.stderr b/tests/ui/ufcs/bad-builder.stderr index e1c5e45b3ebb..9cfeb7a5d09d 100644 --- a/tests/ui/ufcs/bad-builder.stderr +++ b/tests/ui/ufcs/bad-builder.stderr @@ -7,9 +7,9 @@ LL | Vec::::mew() note: if you're trying to build a new `Vec` consider using one of the following associated functions: Vec::::new Vec::::with_capacity + Vec::::try_with_capacity Vec::::from_raw_parts - Vec::::new_in - and 2 others + and 4 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: there is an associated function `new` with a similar name | From 784e6a1e080e5ba18e5c246e744e2d20525d1c3d Mon Sep 17 00:00:00 2001 From: Kornel Date: Wed, 31 Jan 2024 15:23:52 +0000 Subject: [PATCH 15/47] Move capacity_overflow function to make ui tests change less Code changes in raw_vec require blessing UI tests every time --- library/alloc/src/raw_vec.rs | 18 +++++++++--------- tests/ui/hygiene/panic-location.run.stderr | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index c5cf12209d90..5e37de18c954 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -17,6 +17,15 @@ use crate::collections::TryReserveErrorKind::*; #[cfg(test)] mod tests; +// One central function responsible for reporting capacity overflows. This'll +// ensure that the code generation related to these panics is minimal as there's +// only one location which panics rather than a bunch throughout the module. +#[cfg(not(no_global_oom_handling))] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +fn capacity_overflow() -> ! { + panic!("capacity overflow"); +} + enum AllocInit { /// The contents of the new memory are uninitialized. Uninitialized, @@ -576,12 +585,3 @@ fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> { Ok(()) } } - -// One central function responsible for reporting capacity overflows. This'll -// ensure that the code generation related to these panics is minimal as there's -// only one location which panics rather than a bunch throughout the module. -#[cfg(not(no_global_oom_handling))] -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] -fn capacity_overflow() -> ! { - panic!("capacity overflow"); -} diff --git a/tests/ui/hygiene/panic-location.run.stderr b/tests/ui/hygiene/panic-location.run.stderr index 5c552411da7f..ec0ce18c3dfa 100644 --- a/tests/ui/hygiene/panic-location.run.stderr +++ b/tests/ui/hygiene/panic-location.run.stderr @@ -1,3 +1,3 @@ -thread 'main' panicked at library/alloc/src/raw_vec.rs:571:5: +thread 'main' panicked at library/alloc/src/raw_vec.rs:26:5: capacity overflow note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace From 0a0074980fb7802ac621ceac92bf98bdc640bd78 Mon Sep 17 00:00:00 2001 From: Andrew Wock Date: Sun, 18 Feb 2024 18:06:16 -0500 Subject: [PATCH 16/47] Implement MaybeUninit::fill{,_with,_from} ACP: rust-lang/libs-team#156 Signed-off-by: Andrew Wock --- library/core/src/mem/maybe_uninit.rs | 202 ++++++++++++++++++++++-- library/core/tests/lib.rs | 1 + library/core/tests/mem.rs | 223 +++++++++++++++++++++++++-- 3 files changed, 401 insertions(+), 25 deletions(-) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index c19b5791562c..026e21586d40 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1125,22 +1125,6 @@ impl MaybeUninit { // unlike copy_from_slice this does not call clone_from_slice on the slice // this is because `MaybeUninit` does not implement Clone. - struct Guard<'a, T> { - slice: &'a mut [MaybeUninit], - initialized: usize, - } - - impl<'a, T> Drop for Guard<'a, T> { - fn drop(&mut self) { - let initialized_part = &mut self.slice[..self.initialized]; - // SAFETY: this raw slice will contain only initialized objects - // that's why, it is allowed to drop it. - unsafe { - crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part)); - } - } - } - assert_eq!(this.len(), src.len(), "destination and source slices have different lengths"); // NOTE: We need to explicitly slice them to the same length // for bounds checking to be elided, and the optimizer will @@ -1162,6 +1146,151 @@ impl MaybeUninit { unsafe { MaybeUninit::slice_assume_init_mut(this) } } + /// Fills `this` with elements by cloning `value`, returning a mutable reference to the now + /// initialized contents of `this`. + /// Any previously initialized elements will not be dropped. + /// + /// This is similar to [`slice::fill`]. + /// + /// # Panics + /// + /// This function will panic if any call to `Clone` panics. + /// + /// If such a panic occurs, any elements previously initialized during this operation will be + /// dropped. + /// + /// # Examples + /// + /// Fill an uninit vec with 1. + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = vec![MaybeUninit::uninit(); 10]; + /// let initialized = MaybeUninit::fill(buf.as_mut_slice(), 1); + /// assert_eq!(initialized, &mut [1; 10]); + /// ``` + #[doc(alias = "memset")] + #[unstable(feature = "maybe_uninit_fill", issue = "117428")] + pub fn fill<'a>(this: &'a mut [MaybeUninit], value: T) -> &'a mut [T] + where + T: Clone, + { + SpecFill::spec_fill(this, value); + // SAFETY: Valid elements have just been filled into `this` so it is initialized + unsafe { MaybeUninit::slice_assume_init_mut(this) } + } + + /// Fills `this` with elements returned by calling a closure repeatedly. + /// + /// This method uses a closure to create new values. If you'd rather `Clone` a given value, use + /// [`MaybeUninit::fill`]. If you want to use the `Default` trait to generate values, you can + /// pass [`Default::default`] as the argument. + /// + /// # Panics + /// + /// This function will panic if any call to the provided closure panics. + /// + /// If such a panic occurs, any elements previously initialized during this operation will be + /// dropped. + /// + /// # Examples + /// + /// Fill an uninit vec with the default value. + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = vec![MaybeUninit::::uninit(); 10]; + /// let initialized = MaybeUninit::fill_with(buf.as_mut_slice(), Default::default); + /// assert_eq!(initialized, &mut [0; 10]); + /// ``` + #[unstable(feature = "maybe_uninit_fill", issue = "117428")] + pub fn fill_with<'a, F>(this: &'a mut [MaybeUninit], mut f: F) -> &'a mut [T] + where + F: FnMut() -> T, + { + let mut guard = Guard { slice: this, initialized: 0 }; + + for element in guard.slice.iter_mut() { + element.write(f()); + guard.initialized += 1; + } + + super::forget(guard); + + // SAFETY: Valid elements have just been written into `this` so it is initialized + unsafe { MaybeUninit::slice_assume_init_mut(this) } + } + + /// Fills `this` with elements yielded by an iterator until either all elements have been + /// initialized or the iterator is empty. + /// + /// Returns two slices. The first slice contains the initialized portion of the original slice. + /// The second slice is the still-uninitialized remainder of the original slice. + /// + /// # Panics + /// + /// This function panics if the iterator's `next` function panics. + /// + /// If such a panic occurs, any elements previously initialized during this operation will be + /// dropped. + /// + /// # Examples + /// + /// Fill an uninit vec with a cycling iterator. + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = vec![MaybeUninit::uninit(); 5]; + /// + /// let iter = [1, 2, 3].into_iter().cycle(); + /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); + /// + /// assert_eq!(initialized, &mut [1, 2, 3, 1, 2]); + /// assert_eq!(0, remainder.len()); + /// ``` + /// + /// Fill an uninit vec, but not completely. + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = vec![MaybeUninit::uninit(); 5]; + /// let iter = [1, 2]; + /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); + /// + /// assert_eq!(initialized, &mut [1, 2]); + /// assert_eq!(remainder.len(), 3); + /// ``` + #[unstable(feature = "maybe_uninit_fill", issue = "117428")] + pub fn fill_from<'a, I>( + this: &'a mut [MaybeUninit], + it: I, + ) -> (&'a mut [T], &'a mut [MaybeUninit]) + where + I: IntoIterator, + { + let iter = it.into_iter(); + let mut guard = Guard { slice: this, initialized: 0 }; + + for (element, val) in guard.slice.iter_mut().zip(iter) { + element.write(val); + guard.initialized += 1; + } + + let initialized_len = guard.initialized; + super::forget(guard); + + // SAFETY: guard.initialized <= this.len() + let (initted, remainder) = unsafe { this.split_at_mut_unchecked(initialized_len) }; + + // SAFETY: Valid elements have just been written into `init`, so that portion + // of `this` is initialized. + (unsafe { MaybeUninit::slice_assume_init_mut(initted) }, remainder) + } + /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. /// /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still @@ -1315,3 +1444,44 @@ impl [MaybeUninit; N] { unsafe { intrinsics::transmute_unchecked(self) } } } + +struct Guard<'a, T> { + slice: &'a mut [MaybeUninit], + initialized: usize, +} + +impl<'a, T> Drop for Guard<'a, T> { + fn drop(&mut self) { + let initialized_part = &mut self.slice[..self.initialized]; + // SAFETY: this raw sub-slice will contain only initialized objects. + unsafe { + crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part)); + } + } +} + +trait SpecFill { + fn spec_fill(&mut self, value: T); +} + +impl SpecFill for [MaybeUninit] { + default fn spec_fill(&mut self, value: T) { + let mut guard = Guard { slice: self, initialized: 0 }; + + if let Some((last, elems)) = guard.slice.split_last_mut() { + for el in elems { + el.write(value.clone()); + guard.initialized += 1; + } + + last.write(value); + } + super::forget(guard); + } +} + +impl SpecFill for [MaybeUninit] { + fn spec_fill(&mut self, value: T) { + self.fill(MaybeUninit::new(value)); + } +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index fa0e9a979d06..c5a7e87c4aa4 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -54,6 +54,7 @@ #![feature(slice_from_ptr_range)] #![feature(slice_split_once)] #![feature(split_as_slice)] +#![feature(maybe_uninit_fill)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_write_slice)] #![feature(maybe_uninit_uninit_array_transpose)] diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index 0f7fde747690..e388800f400d 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -308,17 +308,17 @@ fn uninit_write_slice_cloned_mid_panic() { } } +#[derive(Clone)] +struct Bomb; + +impl Drop for Bomb { + fn drop(&mut self) { + panic!("dropped a bomb! kaboom!") + } +} + #[test] fn uninit_write_slice_cloned_no_drop() { - #[derive(Clone)] - struct Bomb; - - impl Drop for Bomb { - fn drop(&mut self) { - panic!("dropped a bomb! kaboom") - } - } - let mut dst = [MaybeUninit::uninit()]; let src = [Bomb]; @@ -327,6 +327,211 @@ fn uninit_write_slice_cloned_no_drop() { forget(src); } +#[test] +fn uninit_fill() { + let mut dst = [MaybeUninit::new(255); 64]; + let expect = [0; 64]; + + assert_eq!(MaybeUninit::fill(&mut dst, 0), &expect); +} + +#[cfg(panic = "unwind")] +struct CloneUntilPanic { + limit: usize, + rc: Rc<()>, +} + +#[cfg(panic = "unwind")] +impl Clone for CloneUntilPanic { + fn clone(&self) -> Self { + if Rc::strong_count(&self.rc) >= self.limit { + panic!("expected panic on clone"); + } + Self { limit: self.limit, rc: self.rc.clone() } + } +} + +#[test] +#[cfg(panic = "unwind")] +fn uninit_fill_clone_panic_drop() { + use std::panic; + + let rc = Rc::new(()); + + let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()]; + + let src = CloneUntilPanic { limit: 3, rc: rc.clone() }; + let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { + MaybeUninit::fill(&mut dst, src); + })); + + match err { + Ok(_) => unreachable!(), + Err(payload) => { + payload + .downcast::<&'static str>() + .and_then(|s| if *s == "expected panic on clone" { Ok(s) } else { Err(s) }) + .unwrap_or_else(|p| panic::resume_unwind(p)); + assert_eq!(Rc::strong_count(&rc), 1) + } + } +} + +#[test] +#[cfg(panic = "unwind")] +fn uninit_fill_clone_no_drop_clones() { + let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()]; + + MaybeUninit::fill(&mut dst, Bomb); +} + +#[test] +fn uninit_fill_with() { + let mut dst = [MaybeUninit::new(255); 64]; + let expect = [0; 64]; + + assert_eq!(MaybeUninit::fill_with(&mut dst, || 0), &expect); +} + +#[test] +#[cfg(panic = "unwind")] +fn uninit_fill_with_mid_panic() { + use std::panic; + + let rc = Rc::new(()); + + let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()]; + + let src = CloneUntilPanic { limit: 3, rc: rc.clone() }; + let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { + MaybeUninit::fill_with(&mut dst, || src.clone()); + })); + + drop(src); + + match err { + Ok(_) => unreachable!(), + Err(payload) => { + payload + .downcast::<&'static str>() + .and_then(|s| if *s == "expected panic on clone" { Ok(s) } else { Err(s) }) + .unwrap_or_else(|p| panic::resume_unwind(p)); + + assert_eq!(Rc::strong_count(&rc), 1) + } + } +} + +#[test] +#[cfg(panic = "unwind")] +fn uninit_fill_with_no_drop() { + let mut dst = [MaybeUninit::uninit()]; + let src = Bomb; + + MaybeUninit::fill_with(&mut dst, || src.clone()); + + forget(src); +} + +#[test] +fn uninit_fill_from() { + let mut dst = [MaybeUninit::new(255); 64]; + let src = [0; 64]; + + let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter()); + assert_eq!(initted, &src); + assert_eq!(remainder.len(), 0); +} + +#[test] +fn uninit_fill_from_partial() { + let mut dst = [MaybeUninit::new(255); 64]; + let src = [0; 48]; + + let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter()); + assert_eq!(initted, &src); + assert_eq!(remainder.len(), 16); +} + +#[test] +fn uninit_over_fill() { + let mut dst = [MaybeUninit::new(255); 64]; + let src = [0; 72]; + + let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter()); + assert_eq!(initted, &src[0..64]); + assert_eq!(remainder.len(), 0); +} + +#[test] +fn uninit_empty_fill() { + let mut dst = [MaybeUninit::new(255); 64]; + let src = [0; 0]; + + let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter()); + assert_eq!(initted, &src[0..0]); + assert_eq!(remainder.len(), 64); +} + +#[test] +#[cfg(panic = "unwind")] +fn uninit_fill_from_mid_panic() { + use std::panic; + + struct IterUntilPanic { + limit: usize, + rc: Rc<()>, + } + + impl Iterator for IterUntilPanic { + type Item = Rc<()>; + fn next(&mut self) -> Option { + if Rc::strong_count(&self.rc) >= self.limit { + panic!("expected panic on next"); + } + Some(self.rc.clone()) + } + } + + let rc = Rc::new(()); + + let mut dst = [ + MaybeUninit::uninit(), + MaybeUninit::uninit(), + MaybeUninit::uninit(), + MaybeUninit::uninit(), + ]; + + let src = IterUntilPanic { limit: 3, rc: rc.clone() }; + + let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { + MaybeUninit::fill_from(&mut dst, src); + })); + + match err { + Ok(_) => unreachable!(), + Err(payload) => { + payload + .downcast::<&'static str>() + .and_then(|s| if *s == "expected panic on next" { Ok(s) } else { Err(s) }) + .unwrap_or_else(|p| panic::resume_unwind(p)); + + assert_eq!(Rc::strong_count(&rc), 1) + } + } +} + +#[test] +#[cfg(panic = "unwind")] +fn uninit_fill_from_no_drop() { + let mut dst = [MaybeUninit::uninit()]; + let src = [Bomb]; + + MaybeUninit::fill_from(&mut dst, src.iter()); + + forget(src); +} + #[test] fn uninit_const_assume_init_read() { const FOO: u32 = unsafe { MaybeUninit::new(42).assume_init_read() }; From 9abe47e3720d8fc236464a9295389fc0b21f67e5 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Wed, 6 Mar 2024 18:01:09 +0100 Subject: [PATCH 17/47] fix `close_read_wakes_up` test --- library/std/src/net/tcp/tests.rs | 37 +++++++++++++++++--------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index b24b851a6454..ec8b62f96875 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -544,30 +544,33 @@ fn close_readwrite_smoke() { } #[test] +// FIXME: https://github.com/fortanix/rust-sgx/issues/110 #[cfg_attr(target_env = "sgx", ignore)] +// On windows, shutdown will not wake up blocking I/O operations. +#[cfg_attr(windows, ignore)] fn close_read_wakes_up() { each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); - let (tx1, rx) = channel::<()>(); + let listener = t!(TcpListener::bind(&addr)); let _t = thread::spawn(move || { - let _s = t!(a.accept()); - let _ = rx.recv(); + let (stream, _) = t!(listener.accept()); + stream }); - let s = t!(TcpStream::connect(&addr)); - let s2 = t!(s.try_clone()); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - let mut s2 = s2; - assert_eq!(t!(s2.read(&mut [0])), 0); - tx.send(()).unwrap(); - }); - // this should wake up the child thread - t!(s.shutdown(Shutdown::Read)); + let mut stream = t!(TcpStream::connect(&addr)); + let stream2 = t!(stream.try_clone()); - // this test will never finish if the child doesn't wake up - rx.recv().unwrap(); - drop(tx1); + let _t = thread::spawn(move || { + let stream2 = stream2; + + // to make it more likely that `read` happens before `shutdown` + thread::sleep(Duration::from_millis(1000)); + + // this should wake up the reader up + t!(stream2.shutdown(Shutdown::Read)); + }); + + // this `read` should get interrupted by `shutdown` + assert_eq!(t!(stream.read(&mut [0])), 0); }) } From 33ef4b963bb9f24119e638f91d08241ba71f0ca2 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 6 Mar 2024 19:39:36 +0100 Subject: [PATCH 18/47] Optimize Symbol::integer by utilizing itoa in-place formatting --- Cargo.lock | 1 + compiler/rustc_span/Cargo.toml | 1 + compiler/rustc_span/src/symbol.rs | 6 ++++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5feb21a65b77..c12b23e87fd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4570,6 +4570,7 @@ name = "rustc_span" version = "0.0.0" dependencies = [ "indexmap", + "itoa", "md-5", "rustc_arena", "rustc_data_structures", diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index 99de91a068ad..98ed985738ad 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start indexmap = { version = "2.0.0" } +itoa = "1.0" md5 = { package = "md-5", version = "0.10.0" } rustc_arena = { path = "../rustc_arena" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3784a08b1b72..96ad1e81e798 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2325,13 +2325,15 @@ pub mod sym { /// /// The first few non-negative integers each have a static symbol and therefore /// are fast. - pub fn integer + Copy + ToString>(n: N) -> Symbol { + pub fn integer + Copy + itoa::Integer>(n: N) -> Symbol { if let Result::Ok(idx) = n.try_into() { if idx < 10 { return Symbol::new(super::SYMBOL_DIGITS_BASE + idx as u32); } } - Symbol::intern(&n.to_string()) + let mut buffer = itoa::Buffer::new(); + let printed = buffer.format(n); + Symbol::intern(printed) } } From 5c87ca2d1f23214d4890c9db88bd39684d6c8852 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 29 Feb 2024 20:51:24 +0100 Subject: [PATCH 19/47] Add some weird test cases to the non_local_definitions lint tests --- tests/ui/lint/non_local_definitions.rs | 20 +++++++++ tests/ui/lint/non_local_definitions.stderr | 50 ++++++++++++++++------ 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/tests/ui/lint/non_local_definitions.rs b/tests/ui/lint/non_local_definitions.rs index bbcc81898713..d14312d237a2 100644 --- a/tests/ui/lint/non_local_definitions.rs +++ b/tests/ui/lint/non_local_definitions.rs @@ -245,6 +245,26 @@ fn bad() { //~^ WARN non-local `impl` definition } +trait Uto9 {} +trait Uto10 {} +const _: u32 = { + let _a = || { + impl Uto9 for Test {} + //~^ WARN non-local `impl` definition + + 1 + }; + + type A = [u32; { + impl Uto10 for Test {} + //~^ WARN non-local `impl` definition + + 1 + }]; + + 1 +}; + struct UwU(T); fn fun() { diff --git a/tests/ui/lint/non_local_definitions.stderr b/tests/ui/lint/non_local_definitions.stderr index b9583ae983f3..ef74e262f9db 100644 --- a/tests/ui/lint/non_local_definitions.stderr +++ b/tests/ui/lint/non_local_definitions.stderr @@ -442,7 +442,29 @@ LL | impl Uto8 for T {} = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:253:5 + --> $DIR/non_local_definitions.rs:252:9 + | +LL | impl Uto9 for Test {} + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: move this `impl` block outside the of the current closure `` and up 2 bodies + = note: an `impl` definition is non-local if it is nested inside an item and neither the type nor the trait are at the same nesting level as the `impl` block + = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue + +warning: non-local `impl` definition, they should be avoided as they go against expectation + --> $DIR/non_local_definitions.rs:259:9 + | +LL | impl Uto10 for Test {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: move this `impl` block outside the of the current constant expression `` and up 2 bodies + = note: an `impl` definition is non-local if it is nested inside an item and neither the type nor the trait are at the same nesting level as the `impl` block + = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue + +warning: non-local `impl` definition, they should be avoided as they go against expectation + --> $DIR/non_local_definitions.rs:273:5 | LL | / impl Default for UwU { LL | | @@ -458,7 +480,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:264:5 + --> $DIR/non_local_definitions.rs:284:5 | LL | / impl From for () { LL | | @@ -474,7 +496,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:273:5 + --> $DIR/non_local_definitions.rs:293:5 | LL | / impl AsRef for () { LL | | @@ -488,7 +510,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:284:5 + --> $DIR/non_local_definitions.rs:304:5 | LL | / impl PartialEq for G { LL | | @@ -504,7 +526,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:301:5 + --> $DIR/non_local_definitions.rs:321:5 | LL | / impl PartialEq for &Dog { LL | | @@ -520,7 +542,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:308:5 + --> $DIR/non_local_definitions.rs:328:5 | LL | / impl PartialEq<()> for Dog { LL | | @@ -536,7 +558,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:315:5 + --> $DIR/non_local_definitions.rs:335:5 | LL | / impl PartialEq<()> for &Dog { LL | | @@ -552,7 +574,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:322:5 + --> $DIR/non_local_definitions.rs:342:5 | LL | / impl PartialEq for () { LL | | @@ -568,7 +590,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:344:5 + --> $DIR/non_local_definitions.rs:364:5 | LL | / impl From>> for () { LL | | @@ -584,7 +606,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:351:5 + --> $DIR/non_local_definitions.rs:371:5 | LL | / impl From<()> for Wrap { LL | | @@ -600,7 +622,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:364:13 + --> $DIR/non_local_definitions.rs:384:13 | LL | impl MacroTrait for OutsideStruct {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -615,7 +637,7 @@ LL | m!(); = note: this warning originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:374:1 + --> $DIR/non_local_definitions.rs:394:1 | LL | non_local_macro::non_local_impl!(CargoUpdate); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -628,7 +650,7 @@ LL | non_local_macro::non_local_impl!(CargoUpdate); = note: this warning originates in the macro `non_local_macro::non_local_impl` (in Nightly builds, run with -Z macro-backtrace for more info) warning: non-local `macro_rules!` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:377:1 + --> $DIR/non_local_definitions.rs:397:1 | LL | non_local_macro::non_local_macro_rules!(my_macro); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -640,5 +662,5 @@ LL | non_local_macro::non_local_macro_rules!(my_macro); = note: the macro `non_local_macro::non_local_macro_rules` may come from an old version of the `non_local_macro` crate, try updating your dependency with `cargo update -p non_local_macro` = note: this warning originates in the macro `non_local_macro::non_local_macro_rules` (in Nightly builds, run with -Z macro-backtrace for more info) -warning: 50 warnings emitted +warning: 52 warnings emitted From 20200f65ca63c4b3cd75aa1ebb9829188c78b388 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 29 Feb 2024 20:59:09 +0100 Subject: [PATCH 20/47] Remove useless smallvec dependency in rustc_lint::non_local_def --- Cargo.lock | 1 - compiler/rustc_lint/Cargo.toml | 1 - compiler/rustc_lint/src/non_local_def.rs | 30 ++++++++++++++---------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 635146492b04..81c606849e7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4133,7 +4133,6 @@ dependencies = [ "rustc_target", "rustc_trait_selection", "rustc_type_ir", - "smallvec", "tracing", "unicode-security", ] diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 2271321b8bf2..fa1133e7780f 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -23,7 +23,6 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_type_ir = { path = "../rustc_type_ir" } -smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" unicode-security = "0.1.0" # tidy-alphabetical-end diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index a4fd5a7c45f9..597010b8925e 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -2,8 +2,6 @@ use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, Path, QPath, TyKind}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind}; -use smallvec::{smallvec, SmallVec}; - use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::{LateContext, LateLintPass, LintContext}; @@ -114,25 +112,25 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { // is using local items and so we don't lint on it. // We also ignore anon-const in item by including the anon-const - // parent as well; and since it's quite uncommon, we use smallvec - // to avoid unnecessary heap allocations. - let local_parents: SmallVec<[DefId; 1]> = if parent_def_kind == DefKind::Const + // parent as well. + let parent_parent = if parent_def_kind == DefKind::Const && parent_opt_item_name == Some(kw::Underscore) { - smallvec![parent, cx.tcx.parent(parent)] + Some(cx.tcx.parent(parent)) } else { - smallvec![parent] + None }; let self_ty_has_local_parent = match impl_.self_ty.kind { TyKind::Path(QPath::Resolved(_, ty_path)) => { - path_has_local_parent(ty_path, cx, &*local_parents) + path_has_local_parent(ty_path, cx, parent, parent_parent) } TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => { path_has_local_parent( principle_poly_trait_ref.trait_ref.path, cx, - &*local_parents, + parent, + parent_parent, ) } TyKind::TraitObject([], _, _) @@ -154,7 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { let of_trait_has_local_parent = impl_ .of_trait - .map(|of_trait| path_has_local_parent(of_trait.path, cx, &*local_parents)) + .map(|of_trait| path_has_local_parent(of_trait.path, cx, parent, parent_parent)) .unwrap_or(false); // If none of them have a local parent (LOGICAL NOR) this means that @@ -218,6 +216,14 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { /// std::convert::PartialEq> /// ^^^^^^^^^^^^^^^^^^^^^^^ /// ``` -fn path_has_local_parent(path: &Path<'_>, cx: &LateContext<'_>, local_parents: &[DefId]) -> bool { - path.res.opt_def_id().is_some_and(|did| local_parents.contains(&cx.tcx.parent(did))) +fn path_has_local_parent( + path: &Path<'_>, + cx: &LateContext<'_>, + impl_parent: DefId, + impl_parent_parent: Option, +) -> bool { + path.res.opt_def_id().is_some_and(|did| { + let res_parent = cx.tcx.parent(did); + res_parent == impl_parent || Some(res_parent) == impl_parent_parent + }) } From 6c4eadd74746eaa21c6da1756c440b1fdabf0729 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 29 Feb 2024 21:00:35 +0100 Subject: [PATCH 21/47] Add early-return when checking if a path is local --- compiler/rustc_lint/src/non_local_def.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 597010b8925e..1ae1c72b6e8f 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -223,7 +223,9 @@ fn path_has_local_parent( impl_parent_parent: Option, ) -> bool { path.res.opt_def_id().is_some_and(|did| { - let res_parent = cx.tcx.parent(did); - res_parent == impl_parent || Some(res_parent) == impl_parent_parent + did.is_local() && { + let res_parent = cx.tcx.parent(did); + res_parent == impl_parent || Some(res_parent) == impl_parent_parent + } }) } From 98dbe9abac8e02f9a60393ae2eb2ca448c69e7d4 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 29 Feb 2024 21:02:47 +0100 Subject: [PATCH 22/47] Use was_invoked_from_cargo method instead of hand-written one --- compiler/rustc_lint/src/non_local_def.rs | 2 +- tests/ui/lint/non_local_definitions.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 1ae1c72b6e8f..7c4d92d3ce03 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { if let Some(def_id) = oexpn.macro_def_id && let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind && def_id.krate != LOCAL_CRATE - && std::env::var_os("CARGO").is_some() + && rustc_session::utils::was_invoked_from_cargo() { Some(NonLocalDefinitionsCargoUpdateNote { macro_kind: macro_kind.descr(), diff --git a/tests/ui/lint/non_local_definitions.rs b/tests/ui/lint/non_local_definitions.rs index d14312d237a2..eee582a6f11b 100644 --- a/tests/ui/lint/non_local_definitions.rs +++ b/tests/ui/lint/non_local_definitions.rs @@ -1,7 +1,7 @@ //@ check-pass //@ edition:2021 //@ aux-build:non_local_macro.rs -//@ rustc-env:CARGO=/usr/bin/cargo +//@ rustc-env:CARGO_CRATE_NAME=non_local_def #![feature(inline_const)] #![warn(non_local_definitions)] From 4663fbb2cb6922c28c4bd4e4947d8213f00382b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Thu, 7 Mar 2024 23:03:42 +0000 Subject: [PATCH 23/47] Eagerly translate HelpUseLatestEdition in parser diagnostics --- .../rustc_parse/src/parser/diagnostics.rs | 2 +- .../ui/parser/help-set-edition-ice-122130.rs | 5 +++++ .../parser/help-set-edition-ice-122130.stderr | 21 +++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/ui/parser/help-set-edition-ice-122130.rs create mode 100644 tests/ui/parser/help-set-edition-ice-122130.stderr diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 2f7ac7d3a12e..de088b9364b2 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -667,7 +667,7 @@ impl<'a> Parser<'a> { { err.note("you may be trying to write a c-string literal"); err.note("c-string literals require Rust 2021 or later"); - HelpUseLatestEdition::new().add_to_diagnostic(&mut err); + err.subdiagnostic(self.dcx(), HelpUseLatestEdition::new()); } // `pub` may be used for an item or `pub(crate)` diff --git a/tests/ui/parser/help-set-edition-ice-122130.rs b/tests/ui/parser/help-set-edition-ice-122130.rs new file mode 100644 index 000000000000..bc5af04ecbc0 --- /dev/null +++ b/tests/ui/parser/help-set-edition-ice-122130.rs @@ -0,0 +1,5 @@ +enum will { + s#[c"owned_box"] + //~^ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `#` + //~|ERROR expected item, found `"owned_box"` +} diff --git a/tests/ui/parser/help-set-edition-ice-122130.stderr b/tests/ui/parser/help-set-edition-ice-122130.stderr new file mode 100644 index 000000000000..fe4d212f2db6 --- /dev/null +++ b/tests/ui/parser/help-set-edition-ice-122130.stderr @@ -0,0 +1,21 @@ +error: expected one of `(`, `,`, `=`, `{`, or `}`, found `#` + --> $DIR/help-set-edition-ice-122130.rs:2:6 + | +LL | s#[c"owned_box"] + | ^ expected one of `(`, `,`, `=`, `{`, or `}` + | + = note: you may be trying to write a c-string literal + = note: c-string literals require Rust 2021 or later + = help: pass `--edition 2021` to `rustc` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error: expected item, found `"owned_box"` + --> $DIR/help-set-edition-ice-122130.rs:2:9 + | +LL | s#[c"owned_box"] + | ^^^^^^^^^^^ expected item + | + = note: for a full list of items that can appear in modules, see + +error: aborting due to 2 previous errors + From bef1cd80d8f6264b5ed66b5696a758db5d47491b Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 7 Mar 2024 18:30:04 -0800 Subject: [PATCH 24/47] ci: add a runner for vanilla LLVM 18 --- .github/workflows/ci.yml | 4 ++ .../host-x86_64/x86_64-gnu-llvm-17/Dockerfile | 4 ++ .../host-x86_64/x86_64-gnu-llvm-18/Dockerfile | 55 +++++++++++++++++++ src/ci/github-actions/ci.yml | 5 ++ 4 files changed, 68 insertions(+) create mode 100644 src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d1056de25c1..cd13ce35007b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -315,6 +315,10 @@ jobs: - name: x86_64-gnu-distcheck os: ubuntu-20.04-8core-32gb env: {} + - name: x86_64-gnu-llvm-18 + env: + RUST_BACKTRACE: 1 + os: ubuntu-20.04-8core-32gb - name: x86_64-gnu-llvm-17 env: RUST_BACKTRACE: 1 diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile index fe30a9534410..1eedfe3f09b4 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile @@ -42,6 +42,10 @@ RUN sh /scripts/sccache.sh ENV NO_DOWNLOAD_CI_LLVM 1 ENV EXTERNAL_LLVM 1 +# This is not the latest LLVM version, so some components required by tests may +# be missing. +ENV IS_NOT_LATEST_LLVM 1 + # Using llvm-link-shared due to libffi issues -- see #34486 ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile new file mode 100644 index 000000000000..e8383500dfc9 --- /dev/null +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile @@ -0,0 +1,55 @@ +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + gcc-multilib \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + llvm-18-tools \ + llvm-18-dev \ + libedit-dev \ + libssl-dev \ + pkg-config \ + zlib1g-dev \ + xz-utils \ + nodejs \ + mingw-w64 \ + libgccjit-13-dev \ + && rm -rf /var/lib/apt/lists/* + +# Note: libgccjit needs to match the default gcc version for the linker to find it. + +# Install powershell (universal package) so we can test x.ps1 on Linux +# FIXME: need a "universal" version that supports libicu74, but for now it still works to ignore that dep. +RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ + dpkg --ignore-depends=libicu72 -i powershell.deb && \ + rm -f powershell.deb + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +# We are disabling CI LLVM since this builder is intentionally using a host +# LLVM, rather than the typical src/llvm-project LLVM. +ENV NO_DOWNLOAD_CI_LLVM 1 +ENV EXTERNAL_LLVM 1 + +# Using llvm-link-shared due to libffi issues -- see #34486 +ENV RUST_CONFIGURE_ARGS \ + --build=x86_64-unknown-linux-gnu \ + --llvm-root=/usr/lib/llvm-18 \ + --enable-llvm-link-shared \ + --set rust.thin-lto-import-instr-limit=10 + +COPY host-x86_64/x86_64-gnu-llvm-16/script.sh /tmp/ + +ENV SCRIPT /tmp/script.sh diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 2ba5d357a1d0..c392ccec3dc2 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -510,6 +510,11 @@ jobs: - name: x86_64-gnu-distcheck <<: *job-linux-8c + - name: x86_64-gnu-llvm-18 + env: + RUST_BACKTRACE: 1 + <<: *job-linux-8c + - name: x86_64-gnu-llvm-17 env: RUST_BACKTRACE: 1 From aba389017a6a8469f2d9c85c1fd5bbaeeb89f0b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 12 Feb 2024 20:55:08 +0100 Subject: [PATCH 25/47] Update host LLVM on x64 Linux to LLVM 18 --- src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh index 1d9568702cc3..005f4537c84a 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh @@ -4,7 +4,7 @@ set -ex source shared.sh -LLVM=llvmorg-17.0.4 +LLVM=llvmorg-18.1.0 mkdir llvm-project cd llvm-project From 52501c2a75143ca18eed4d4a72463c27dae450fd Mon Sep 17 00:00:00 2001 From: klensy Date: Fri, 23 Feb 2024 16:37:47 +0300 Subject: [PATCH 26/47] bump itertools to 0.12 still depend on 0.11: * clippy * rustfmt, sigh --- Cargo.lock | 27 +++++++++++------------ compiler/rustc_ast_passes/Cargo.toml | 2 +- compiler/rustc_ast_pretty/Cargo.toml | 2 +- compiler/rustc_borrowck/Cargo.toml | 2 +- compiler/rustc_codegen_llvm/Cargo.toml | 2 +- compiler/rustc_codegen_ssa/Cargo.toml | 2 +- compiler/rustc_hir_analysis/Cargo.toml | 2 +- compiler/rustc_hir_typeck/Cargo.toml | 2 +- compiler/rustc_mir_build/Cargo.toml | 2 +- compiler/rustc_mir_transform/Cargo.toml | 2 +- compiler/rustc_passes/Cargo.toml | 1 - compiler/rustc_trait_selection/Cargo.toml | 2 +- compiler/rustc_transmute/Cargo.toml | 2 +- compiler/rustc_ty_utils/Cargo.toml | 2 +- src/librustdoc/Cargo.toml | 2 +- 15 files changed, 26 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 853acd1abd6d..074c33cfb596 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3497,7 +3497,7 @@ dependencies = [ name = "rustc_ast_passes" version = "0.0.0" dependencies = [ - "itertools 0.11.0", + "itertools 0.12.1", "rustc_ast", "rustc_ast_pretty", "rustc_attr", @@ -3517,7 +3517,7 @@ dependencies = [ name = "rustc_ast_pretty" version = "0.0.0" dependencies = [ - "itertools 0.11.0", + "itertools 0.12.1", "rustc_ast", "rustc_lexer", "rustc_span", @@ -3558,7 +3558,7 @@ name = "rustc_borrowck" version = "0.0.0" dependencies = [ "either", - "itertools 0.11.0", + "itertools 0.12.1", "polonius-engine", "rustc_data_structures", "rustc_errors", @@ -3611,7 +3611,7 @@ name = "rustc_codegen_llvm" version = "0.0.0" dependencies = [ "bitflags 2.4.2", - "itertools 0.11.0", + "itertools 0.12.1", "libc", "measureme", "object", @@ -3647,7 +3647,7 @@ dependencies = [ "ar_archive_writer", "bitflags 2.4.2", "cc", - "itertools 0.11.0", + "itertools 0.12.1", "jobserver", "libc", "object", @@ -3929,7 +3929,7 @@ dependencies = [ name = "rustc_hir_analysis" version = "0.0.0" dependencies = [ - "itertools 0.11.0", + "itertools 0.12.1", "rustc_arena", "rustc_ast", "rustc_attr", @@ -3968,7 +3968,7 @@ dependencies = [ name = "rustc_hir_typeck" version = "0.0.0" dependencies = [ - "itertools 0.11.0", + "itertools 0.12.1", "rustc_ast", "rustc_ast_ir", "rustc_attr", @@ -4255,7 +4255,7 @@ name = "rustc_mir_build" version = "0.0.0" dependencies = [ "either", - "itertools 0.11.0", + "itertools 0.12.1", "rustc_apfloat", "rustc_arena", "rustc_ast", @@ -4302,7 +4302,7 @@ name = "rustc_mir_transform" version = "0.0.0" dependencies = [ "either", - "itertools 0.11.0", + "itertools 0.12.1", "rustc_arena", "rustc_ast", "rustc_attr", @@ -4382,7 +4382,6 @@ dependencies = [ name = "rustc_passes" version = "0.0.0" dependencies = [ - "itertools 0.11.0", "rustc_ast", "rustc_ast_pretty", "rustc_attr", @@ -4632,7 +4631,7 @@ name = "rustc_trait_selection" version = "0.0.0" dependencies = [ "bitflags 2.4.2", - "itertools 0.11.0", + "itertools 0.12.1", "rustc_ast", "rustc_ast_ir", "rustc_attr", @@ -4672,7 +4671,7 @@ dependencies = [ name = "rustc_transmute" version = "0.0.0" dependencies = [ - "itertools 0.11.0", + "itertools 0.12.1", "rustc_ast_ir", "rustc_data_structures", "rustc_hir", @@ -4688,7 +4687,7 @@ dependencies = [ name = "rustc_ty_utils" version = "0.0.0" dependencies = [ - "itertools 0.11.0", + "itertools 0.12.1", "rustc_ast_ir", "rustc_data_structures", "rustc_errors", @@ -4738,7 +4737,7 @@ dependencies = [ "askama", "expect-test", "indexmap", - "itertools 0.11.0", + "itertools 0.12.1", "minifier", "once_cell", "regex", diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index 99e79f65fb4a..eace5ce82089 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -itertools = "0.11" +itertools = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml index b38a2915a43d..9ae5c9b3cec6 100644 --- a/compiler/rustc_ast_pretty/Cargo.toml +++ b/compiler/rustc_ast_pretty/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -itertools = "0.11" +itertools = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_lexer = { path = "../rustc_lexer" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml index 714f46270f92..bafc62c7318b 100644 --- a/compiler/rustc_borrowck/Cargo.toml +++ b/compiler/rustc_borrowck/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start either = "1.5.0" -itertools = "0.11" +itertools = "0.12" polonius-engine = "0.13.0" rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 3948a49ee2aa..3fda59e8b523 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -9,7 +9,7 @@ test = false [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -itertools = "0.11" +itertools = "0.12" libc = "0.2" measureme = "11" object = { version = "0.32.0", default-features = false, features = ["std", "read"] } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 81ca42e1ad80..7851b9e8e038 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" ar_archive_writer = "0.1.5" bitflags = "2.4.1" cc = "1.0.90" -itertools = "0.11" +itertools = "0.12" jobserver = "0.1.28" pathdiff = "0.2.0" regex = "1.4" diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 648b569a217f..04ca7f123d3e 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -9,7 +9,7 @@ doctest = false [dependencies] # tidy-alphabetical-start -itertools = "0.11" +itertools = "0.12" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index 0a5fa37ed04f..9e7f0776b608 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -itertools = "0.11" +itertools = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index d71f71213220..6618e4f5a002 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start either = "1" -itertools = "0.11" +itertools = "0.12" rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 9cc083edb44e..bd0a54ef3638 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start either = "1" -itertools = "0.11" +itertools = "0.12" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index 80e6c104bd46..6abfa08c5308 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -itertools = "0.11" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 3a58d41fcd0a..811eb4c98104 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -itertools = "0.11.0" +itertools = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml index 3d0ad31747ff..79939d62a51a 100644 --- a/compiler/rustc_transmute/Cargo.toml +++ b/compiler/rustc_transmute/Cargo.toml @@ -29,5 +29,5 @@ rustc = [ [dev-dependencies] # tidy-alphabetical-start -itertools = "0.11" +itertools = "0.12" # tidy-alphabetical-end diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml index 2a30bd5d5390..01d5251bfa0c 100644 --- a/compiler/rustc_ty_utils/Cargo.toml +++ b/compiler/rustc_ty_utils/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -itertools = "0.11" +itertools = "0.12" rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index e13e95ef7088..bd0fbef998b2 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -9,7 +9,7 @@ path = "lib.rs" [dependencies] arrayvec = { version = "0.7", default-features = false } askama = { version = "0.12", default-features = false, features = ["config"] } -itertools = "0.11" +itertools = "0.12" indexmap = "2" minifier = "0.3.0" once_cell = "1.10.0" From 2de98c8b7ea10df2f827d94d7eab0b03df9df01c Mon Sep 17 00:00:00 2001 From: klensy Date: Wed, 28 Feb 2024 12:41:11 +0300 Subject: [PATCH 27/47] remove unused derive_more dep --- Cargo.lock | 9 --------- compiler/rustc_middle/Cargo.toml | 1 - src/tools/tidy/src/deps.rs | 1 - 3 files changed, 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 074c33cfb596..a2a4df274440 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -784,12 +784,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "core" version = "0.0.0" @@ -1035,10 +1029,8 @@ version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "convert_case", "proc-macro2", "quote", - "rustc_version", "syn 1.0.109", ] @@ -4217,7 +4209,6 @@ name = "rustc_middle" version = "0.0.0" dependencies = [ "bitflags 2.4.2", - "derive_more", "either", "field-offset", "gsgdt", diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 5a24a7ab0bd5..96c58ef411b7 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -derive_more = "0.99.17" either = "1.5.0" field-offset = "0.3.5" gsgdt = "0.1.2" diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 79bc380c1e9b..10fdfc0a65f7 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -209,7 +209,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "cc", "cfg-if", "compiler_builtins", - "convert_case", // dependency of derive_more "cpufeatures", "crc32fast", "crossbeam-channel", From 6721b392e9bdaf9bbbdfdda783f47d4ba5c0c8bd Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 8 Mar 2024 02:17:21 +0900 Subject: [PATCH 28/47] Replace `TypeWalker` usage with `TypeVisitor` --- .../rustc_trait_selection/src/traits/wf.rs | 601 +++++++++--------- .../const_kind_expr/issue_114151.rs | 1 + .../const_kind_expr/issue_114151.stderr | 10 +- 3 files changed, 311 insertions(+), 301 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index b09a803e856d..f89daf033f6c 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -2,7 +2,9 @@ use crate::infer::InferCtxt; use crate::traits; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, +}; use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_span::{Span, DUMMY_SP}; @@ -535,305 +537,8 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { /// Pushes all the predicates needed to validate that `ty` is WF into `out`. #[instrument(level = "debug", skip(self))] fn compute(&mut self, arg: GenericArg<'tcx>) { - let mut walker = arg.walk(); - let param_env = self.param_env; - let depth = self.recursion_depth; - while let Some(arg) = walker.next() { - debug!(?arg, ?self.out); - let ty = match arg.unpack() { - GenericArgKind::Type(ty) => ty, - - // No WF constraints for lifetimes being present, any outlives - // obligations are handled by the parent (e.g. `ty::Ref`). - GenericArgKind::Lifetime(_) => continue, - - GenericArgKind::Const(ct) => { - match ct.kind() { - ty::ConstKind::Unevaluated(uv) => { - if !ct.has_escaping_bound_vars() { - let obligations = self.nominal_obligations(uv.def, uv.args); - self.out.extend(obligations); - - let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::ConstEvaluatable(ct), - )); - let cause = self.cause(traits::WellFormed(None)); - self.out.push(traits::Obligation::with_depth( - self.tcx(), - cause, - self.recursion_depth, - self.param_env, - predicate, - )); - } - } - ty::ConstKind::Infer(_) => { - let cause = self.cause(traits::WellFormed(None)); - - self.out.push(traits::Obligation::with_depth( - self.tcx(), - cause, - self.recursion_depth, - self.param_env, - ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::WellFormed(ct.into()), - )), - )); - } - ty::ConstKind::Expr(_) => { - // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the - // trait bound `typeof(N): Add` holds. This is currently unnecessary - // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated` - // which means that the `DefId` would have been typeck'd elsewhere. However in - // the future we may allow directly lowering to `ConstKind::Expr` in which case - // we would not be proving bounds we should. - - let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::ConstEvaluatable(ct), - )); - let cause = self.cause(traits::WellFormed(None)); - self.out.push(traits::Obligation::with_depth( - self.tcx(), - cause, - self.recursion_depth, - self.param_env, - predicate, - )); - } - - ty::ConstKind::Error(_) - | ty::ConstKind::Param(_) - | ty::ConstKind::Bound(..) - | ty::ConstKind::Placeholder(..) => { - // These variants are trivially WF, so nothing to do here. - } - ty::ConstKind::Value(..) => { - // FIXME: Enforce that values are structurally-matchable. - } - } - continue; - } - }; - - debug!("wf bounds for ty={:?} ty.kind={:#?}", ty, ty.kind()); - - match *ty.kind() { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Error(_) - | ty::Str - | ty::CoroutineWitness(..) - | ty::Never - | ty::Param(_) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Foreign(..) => { - // WfScalar, WfParameter, etc - } - - // Can only infer to `ty::Int(_) | ty::Uint(_)`. - ty::Infer(ty::IntVar(_)) => {} - - // Can only infer to `ty::Float(_)`. - ty::Infer(ty::FloatVar(_)) => {} - - ty::Slice(subty) => { - self.require_sized(subty, traits::SliceOrArrayElem); - } - - ty::Array(subty, _) => { - self.require_sized(subty, traits::SliceOrArrayElem); - // Note that we handle the len is implicitly checked while walking `arg`. - } - - ty::Tuple(tys) => { - if let Some((_last, rest)) = tys.split_last() { - for &elem in rest { - self.require_sized(elem, traits::TupleElem); - } - } - } - - ty::RawPtr(_) => { - // Simple cases that are WF if their type args are WF. - } - - ty::Alias(ty::Projection, data) => { - walker.skip_current_subtree(); // Subtree handled by compute_projection. - self.compute_projection(data); - } - ty::Alias(ty::Inherent, data) => { - walker.skip_current_subtree(); // Subtree handled by compute_inherent_projection. - self.compute_inherent_projection(data); - } - - ty::Adt(def, args) => { - // WfNominalType - let obligations = self.nominal_obligations(def.did(), args); - self.out.extend(obligations); - } - - ty::FnDef(did, args) => { - let obligations = self.nominal_obligations(did, args); - self.out.extend(obligations); - } - - ty::Ref(r, rty, _) => { - // WfReference - if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { - let cause = self.cause(traits::ReferenceOutlivesReferent(ty)); - self.out.push(traits::Obligation::with_depth( - self.tcx(), - cause, - depth, - param_env, - ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(rty, r)), - )), - )); - } - } - - ty::Coroutine(did, args, ..) => { - // Walk ALL the types in the coroutine: this will - // include the upvar types as well as the yield - // type. Note that this is mildly distinct from - // the closure case, where we have to be careful - // about the signature of the closure. We don't - // have the problem of implied bounds here since - // coroutines don't take arguments. - let obligations = self.nominal_obligations(did, args); - self.out.extend(obligations); - } - - ty::Closure(did, args) => { - // Only check the upvar types for WF, not the rest - // of the types within. This is needed because we - // capture the signature and it may not be WF - // without the implied bounds. Consider a closure - // like `|x: &'a T|` -- it may be that `T: 'a` is - // not known to hold in the creator's context (and - // indeed the closure may not be invoked by its - // creator, but rather turned to someone who *can* - // verify that). - // - // The special treatment of closures here really - // ought not to be necessary either; the problem - // is related to #25860 -- there is no way for us - // to express a fn type complete with the implied - // bounds that it is assuming. I think in reality - // the WF rules around fn are a bit messed up, and - // that is the rot problem: `fn(&'a T)` should - // probably always be WF, because it should be - // shorthand for something like `where(T: 'a) { - // fn(&'a T) }`, as discussed in #25860. - walker.skip_current_subtree(); // subtree handled below - // FIXME(eddyb) add the type to `walker` instead of recursing. - self.compute(args.as_closure().tupled_upvars_ty().into()); - // Note that we cannot skip the generic types - // types. Normally, within the fn - // body where they are created, the generics will - // always be WF, and outside of that fn body we - // are not directly inspecting closure types - // anyway, except via auto trait matching (which - // only inspects the upvar types). - // But when a closure is part of a type-alias-impl-trait - // then the function that created the defining site may - // have had more bounds available than the type alias - // specifies. This may cause us to have a closure in the - // hidden type that is not actually well formed and - // can cause compiler crashes when the user abuses unsafe - // code to procure such a closure. - // See tests/ui/type-alias-impl-trait/wf_check_closures.rs - let obligations = self.nominal_obligations(did, args); - self.out.extend(obligations); - } - - ty::CoroutineClosure(did, args) => { - // See the above comments. The same apply to coroutine-closures. - walker.skip_current_subtree(); - self.compute(args.as_coroutine_closure().tupled_upvars_ty().into()); - let obligations = self.nominal_obligations(did, args); - self.out.extend(obligations); - } - - ty::FnPtr(_) => { - // let the loop iterate into the argument/return - // types appearing in the fn signature - } - - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { - // All of the requirements on type parameters - // have already been checked for `impl Trait` in - // return position. We do need to check type-alias-impl-trait though. - if self.tcx().is_type_alias_impl_trait(def_id) { - let obligations = self.nominal_obligations(def_id, args); - self.out.extend(obligations); - } - } - - ty::Alias(ty::Weak, ty::AliasTy { def_id, args, .. }) => { - let obligations = self.nominal_obligations(def_id, args); - self.out.extend(obligations); - } - - ty::Dynamic(data, r, _) => { - // WfObject - // - // Here, we defer WF checking due to higher-ranked - // regions. This is perhaps not ideal. - self.from_object_ty(ty, data, r); - - // FIXME(#27579) RFC also considers adding trait - // obligations that don't refer to Self and - // checking those - - let defer_to_coercion = self.tcx().features().object_safe_for_dispatch; - - if !defer_to_coercion { - if let Some(principal) = data.principal_def_id() { - self.out.push(traits::Obligation::with_depth( - self.tcx(), - self.cause(traits::WellFormed(None)), - depth, - param_env, - ty::Binder::dummy(ty::PredicateKind::ObjectSafe(principal)), - )); - } - } - } - - // Inference variables are the complicated case, since we don't - // know what type they are. We do two things: - // - // 1. Check if they have been resolved, and if so proceed with - // THAT type. - // 2. If not, we've at least simplified things (e.g., we went - // from `Vec<$0>: WF` to `$0: WF`), so we can - // register a pending obligation and keep - // moving. (Goal is that an "inductive hypothesis" - // is satisfied to ensure termination.) - // See also the comment on `fn obligations`, describing "livelock" - // prevention, which happens before this can be reached. - ty::Infer(_) => { - let cause = self.cause(traits::WellFormed(None)); - self.out.push(traits::Obligation::with_depth( - self.tcx(), - cause, - self.recursion_depth, - param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - ty.into(), - ))), - )); - } - } - - debug!(?self.out); - } + arg.visit_with(self); + debug!(?self.out); } #[instrument(level = "debug", skip(self))] @@ -933,6 +638,302 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } } +impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { + type Result = (); + + fn visit_ty(&mut self, t: as ty::Interner>::Ty) -> Self::Result { + debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind()); + + match *t.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Error(_) + | ty::Str + | ty::CoroutineWitness(..) + | ty::Never + | ty::Param(_) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Foreign(..) => { + // WfScalar, WfParameter, etc + } + + // Can only infer to `ty::Int(_) | ty::Uint(_)`. + ty::Infer(ty::IntVar(_)) => {} + + // Can only infer to `ty::Float(_)`. + ty::Infer(ty::FloatVar(_)) => {} + + ty::Slice(subty) => { + self.require_sized(subty, traits::SliceOrArrayElem); + } + + ty::Array(subty, _) => { + self.require_sized(subty, traits::SliceOrArrayElem); + // Note that we handle the len is implicitly checked while walking `arg`. + } + + ty::Tuple(tys) => { + if let Some((_last, rest)) = tys.split_last() { + for &elem in rest { + self.require_sized(elem, traits::TupleElem); + } + } + } + + ty::RawPtr(_) => { + // Simple cases that are WF if their type args are WF. + } + + ty::Alias(ty::Projection, data) => { + self.compute_projection(data); + return; // Subtree handled by compute_projection. + } + ty::Alias(ty::Inherent, data) => { + self.compute_inherent_projection(data); + return; // Subtree handled by compute_inherent_projection. + } + + ty::Adt(def, args) => { + // WfNominalType + let obligations = self.nominal_obligations(def.did(), args); + self.out.extend(obligations); + } + + ty::FnDef(did, args) => { + let obligations = self.nominal_obligations(did, args); + self.out.extend(obligations); + } + + ty::Ref(r, rty, _) => { + // WfReference + if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { + let cause = self.cause(traits::ReferenceOutlivesReferent(t)); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives( + ty::OutlivesPredicate(rty, r), + ))), + )); + } + } + + ty::Coroutine(did, args, ..) => { + // Walk ALL the types in the coroutine: this will + // include the upvar types as well as the yield + // type. Note that this is mildly distinct from + // the closure case, where we have to be careful + // about the signature of the closure. We don't + // have the problem of implied bounds here since + // coroutines don't take arguments. + let obligations = self.nominal_obligations(did, args); + self.out.extend(obligations); + } + + ty::Closure(did, args) => { + // Note that we cannot skip the generic types + // types. Normally, within the fn + // body where they are created, the generics will + // always be WF, and outside of that fn body we + // are not directly inspecting closure types + // anyway, except via auto trait matching (which + // only inspects the upvar types). + // But when a closure is part of a type-alias-impl-trait + // then the function that created the defining site may + // have had more bounds available than the type alias + // specifies. This may cause us to have a closure in the + // hidden type that is not actually well formed and + // can cause compiler crashes when the user abuses unsafe + // code to procure such a closure. + // See tests/ui/type-alias-impl-trait/wf_check_closures.rs + let obligations = self.nominal_obligations(did, args); + self.out.extend(obligations); + // Only check the upvar types for WF, not the rest + // of the types within. This is needed because we + // capture the signature and it may not be WF + // without the implied bounds. Consider a closure + // like `|x: &'a T|` -- it may be that `T: 'a` is + // not known to hold in the creator's context (and + // indeed the closure may not be invoked by its + // creator, but rather turned to someone who *can* + // verify that). + // + // The special treatment of closures here really + // ought not to be necessary either; the problem + // is related to #25860 -- there is no way for us + // to express a fn type complete with the implied + // bounds that it is assuming. I think in reality + // the WF rules around fn are a bit messed up, and + // that is the rot problem: `fn(&'a T)` should + // probably always be WF, because it should be + // shorthand for something like `where(T: 'a) { + // fn(&'a T) }`, as discussed in #25860. + let upvars = args.as_closure().tupled_upvars_ty(); + return upvars.visit_with(self); + } + + ty::CoroutineClosure(did, args) => { + // See the above comments. The same apply to coroutine-closures. + let obligations = self.nominal_obligations(did, args); + self.out.extend(obligations); + let upvars = args.as_coroutine_closure().tupled_upvars_ty(); + return upvars.visit_with(self); + } + + ty::FnPtr(_) => { + // Let the visitor iterate into the argument/return + // types appearing in the fn signature. + } + + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + // All of the requirements on type parameters + // have already been checked for `impl Trait` in + // return position. We do need to check type-alias-impl-trait though. + if self.tcx().is_type_alias_impl_trait(def_id) { + let obligations = self.nominal_obligations(def_id, args); + self.out.extend(obligations); + } + } + + ty::Alias(ty::Weak, ty::AliasTy { def_id, args, .. }) => { + let obligations = self.nominal_obligations(def_id, args); + self.out.extend(obligations); + } + + ty::Dynamic(data, r, _) => { + // WfObject + // + // Here, we defer WF checking due to higher-ranked + // regions. This is perhaps not ideal. + self.from_object_ty(t, data, r); + + // FIXME(#27579) RFC also considers adding trait + // obligations that don't refer to Self and + // checking those + + let defer_to_coercion = self.tcx().features().object_safe_for_dispatch; + + if !defer_to_coercion { + if let Some(principal) = data.principal_def_id() { + self.out.push(traits::Obligation::with_depth( + self.tcx(), + self.cause(traits::WellFormed(None)), + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::ObjectSafe(principal)), + )); + } + } + } + + // Inference variables are the complicated case, since we don't + // know what type they are. We do two things: + // + // 1. Check if they have been resolved, and if so proceed with + // THAT type. + // 2. If not, we've at least simplified things (e.g., we went + // from `Vec<$0>: WF` to `$0: WF`), so we can + // register a pending obligation and keep + // moving. (Goal is that an "inductive hypothesis" + // is satisfied to ensure termination.) + // See also the comment on `fn obligations`, describing "livelock" + // prevention, which happens before this can be reached. + ty::Infer(_) => { + let cause = self.cause(traits::WellFormed(None)); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( + t.into(), + ))), + )); + } + } + + t.super_visit_with(self) + } + + fn visit_const(&mut self, c: as ty::Interner>::Const) -> Self::Result { + match c.kind() { + ty::ConstKind::Unevaluated(uv) => { + if !c.has_escaping_bound_vars() { + let obligations = self.nominal_obligations(uv.def, uv.args); + self.out.extend(obligations); + + let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::ConstEvaluatable(c), + )); + let cause = self.cause(traits::WellFormed(None)); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + predicate, + )); + } + } + ty::ConstKind::Infer(_) => { + let cause = self.cause(traits::WellFormed(None)); + + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( + c.into(), + ))), + )); + } + ty::ConstKind::Expr(_) => { + // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the + // trait bound `typeof(N): Add` holds. This is currently unnecessary + // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated` + // which means that the `DefId` would have been typeck'd elsewhere. However in + // the future we may allow directly lowering to `ConstKind::Expr` in which case + // we would not be proving bounds we should. + + let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::ConstEvaluatable(c), + )); + let cause = self.cause(traits::WellFormed(None)); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + predicate, + )); + } + + ty::ConstKind::Error(_) + | ty::ConstKind::Param(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(..) => { + // These variants are trivially WF, so nothing to do here. + } + ty::ConstKind::Value(..) => { + // FIXME: Enforce that values are structurally-matchable. + } + } + + c.super_visit_with(self) + } + + fn visit_predicate(&mut self, _p: as ty::Interner>::Predicate) -> Self::Result { + bug!("predicate should not be checked for well-formedness"); + } +} + /// Given an object type like `SomeTrait + Send`, computes the lifetime /// bounds that must hold on the elided self type. These are derived /// from the declarations of `SomeTrait`, `Send`, and friends -- if diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.rs b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.rs index 6256000b4919..9cdb4158d2b3 100644 --- a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.rs +++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.rs @@ -20,6 +20,7 @@ where //~^^^ ERROR: function takes 1 generic argument but 2 generic arguments were supplied //~^^^^ ERROR: unconstrained generic constant //~^^^^^ ERROR: unconstrained generic constant `{const expr}` + //~^^^^^^ ERROR: unconstrained generic constant `{const expr}` } fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr index 6001d8247876..14c58f8d0da7 100644 --- a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr +++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr @@ -58,7 +58,15 @@ error: unconstrained generic constant `{const expr}` LL | foo::<_, L>([(); L + 1 + L]); | ^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: unconstrained generic constant `{const expr}` + --> $DIR/issue_114151.rs:17:5 + | +LL | foo::<_, L>([(); L + 1 + L]); + | ^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 6 previous errors Some errors have detailed explanations: E0107, E0308. For more information about an error, try `rustc --explain E0107`. From fdff4d7682071b981a40dcc3575300a661a1f382 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 8 Mar 2024 14:49:28 +0000 Subject: [PATCH 29/47] Move metadata header and version checks together This will make it easier to report rustc versions for older metadata formats. --- compiler/rustc_driver_impl/src/lib.rs | 1 + compiler/rustc_metadata/src/locator.rs | 99 ++++++++++++-------- compiler/rustc_metadata/src/rmeta/decoder.rs | 24 +++-- 3 files changed, 79 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 89ab5c6bd3b7..4edfd111e5cf 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -676,6 +676,7 @@ fn list_metadata(early_dcx: &EarlyDiagCtxt, sess: &Session, metadata_loader: &dy metadata_loader, &mut v, &sess.opts.unstable_opts.ls, + sess.cfg_version, ) .unwrap(); safe_println!("{}", String::from_utf8(v).unwrap()); diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index dcccace12b00..10bbebb3871e 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -569,31 +569,47 @@ impl<'a> CrateLocator<'a> { debug!("skipping empty file"); continue; } - let (hash, metadata) = - match get_metadata_section(self.target, flavor, &lib, self.metadata_loader) { - Ok(blob) => { - if let Some(h) = self.crate_matches(&blob, &lib) { - (h, blob) - } else { - info!("metadata mismatch"); - continue; - } - } - Err(MetadataError::LoadFailure(err)) => { - info!("no metadata found: {}", err); - // The file was present and created by the same compiler version, but we - // couldn't load it for some reason. Give a hard error instead of silently - // ignoring it, but only if we would have given an error anyway. - self.crate_rejections - .via_invalid - .push(CrateMismatch { path: lib, got: err }); + let (hash, metadata) = match get_metadata_section( + self.target, + flavor, + &lib, + self.metadata_loader, + self.cfg_version, + ) { + Ok(blob) => { + if let Some(h) = self.crate_matches(&blob, &lib) { + (h, blob) + } else { + info!("metadata mismatch"); continue; } - Err(err @ MetadataError::NotPresent(_)) => { - info!("no metadata found: {}", err); - continue; - } - }; + } + Err(MetadataError::VersionMismatch { expected_version, found_version }) => { + // The file was present and created by the same compiler version, but we + // couldn't load it for some reason. Give a hard error instead of silently + // ignoring it, but only if we would have given an error anyway. + info!( + "Rejecting via version: expected {} got {}", + expected_version, found_version + ); + self.crate_rejections + .via_version + .push(CrateMismatch { path: lib, got: found_version }); + continue; + } + Err(MetadataError::LoadFailure(err)) => { + info!("no metadata found: {}", err); + // The file was present and created by the same compiler version, but we + // couldn't load it for some reason. Give a hard error instead of silently + // ignoring it, but only if we would have given an error anyway. + self.crate_rejections.via_invalid.push(CrateMismatch { path: lib, got: err }); + continue; + } + Err(err @ MetadataError::NotPresent(_)) => { + info!("no metadata found: {}", err); + continue; + } + }; // If we see multiple hashes, emit an error about duplicate candidates. if slot.as_ref().is_some_and(|s| s.0 != hash) { if let Some(candidates) = err_data { @@ -648,16 +664,6 @@ impl<'a> CrateLocator<'a> { } fn crate_matches(&mut self, metadata: &MetadataBlob, libpath: &Path) -> Option { - let rustc_version = rustc_version(self.cfg_version); - let found_version = metadata.get_rustc_version(); - if found_version != rustc_version { - info!("Rejecting via version: expected {} got {}", rustc_version, found_version); - self.crate_rejections - .via_version - .push(CrateMismatch { path: libpath.to_path_buf(), got: found_version }); - return None; - } - let header = metadata.get_header(); if header.is_proc_macro_crate != self.is_proc_macro { info!( @@ -770,6 +776,7 @@ fn get_metadata_section<'p>( flavor: CrateFlavor, filename: &'p Path, loader: &dyn MetadataLoader, + cfg_version: &'static str, ) -> Result> { if !filename.exists() { return Err(MetadataError::NotPresent(filename)); @@ -847,13 +854,18 @@ fn get_metadata_section<'p>( } }; let blob = MetadataBlob(raw_bytes); - if blob.is_compatible() { - Ok(blob) - } else { - Err(MetadataError::LoadFailure(format!( + match blob.check_compatibility(cfg_version) { + Ok(()) => Ok(blob), + Err(None) => Err(MetadataError::LoadFailure(format!( "invalid metadata version found: {}", filename.display() - ))) + ))), + Err(Some(found_version)) => { + return Err(MetadataError::VersionMismatch { + expected_version: rustc_version(cfg_version), + found_version, + }); + } } } @@ -864,9 +876,10 @@ pub fn list_file_metadata( metadata_loader: &dyn MetadataLoader, out: &mut dyn Write, ls_kinds: &[String], + cfg_version: &'static str, ) -> IoResult<()> { let flavor = get_flavor_from_path(path); - match get_metadata_section(target, flavor, path, metadata_loader) { + match get_metadata_section(target, flavor, path, metadata_loader, cfg_version) { Ok(metadata) => metadata.list_crate_metadata(out, ls_kinds), Err(msg) => write!(out, "{msg}\n"), } @@ -932,6 +945,8 @@ enum MetadataError<'a> { NotPresent(&'a Path), /// The file was present and invalid. LoadFailure(String), + /// The file was present, but compiled with a different rustc version. + VersionMismatch { expected_version: String, found_version: String }, } impl fmt::Display for MetadataError<'_> { @@ -941,6 +956,12 @@ impl fmt::Display for MetadataError<'_> { f.write_str(&format!("no such file: '{}'", filename.display())) } MetadataError::LoadFailure(msg) => f.write_str(msg), + MetadataError::VersionMismatch { expected_version, found_version } => { + f.write_str(&format!( + "rustc version mismatch. expected {}, found {}", + expected_version, found_version, + )) + } } } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index da384007c22c..0467cf2969fc 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -684,13 +684,25 @@ impl<'a, 'tcx, I: Idx, T> Decodable> for LazyTable implement_ty_decoder!(DecodeContext<'a, 'tcx>); impl MetadataBlob { - pub(crate) fn is_compatible(&self) -> bool { - self.blob().starts_with(METADATA_HEADER) - } + pub(crate) fn check_compatibility( + &self, + cfg_version: &'static str, + ) -> Result<(), Option> { + if !self.blob().starts_with(METADATA_HEADER) { + if self.blob().starts_with(b"rust") { + return Err(Some("".to_owned())); + } + return Err(None); + } - pub(crate) fn get_rustc_version(&self) -> String { - LazyValue::::from_position(NonZero::new(METADATA_HEADER.len() + 8).unwrap()) - .decode(self) + let found_version = + LazyValue::::from_position(NonZero::new(METADATA_HEADER.len() + 8).unwrap()) + .decode(self); + if rustc_version(cfg_version) != found_version { + return Err(Some(found_version)); + } + + Ok(()) } fn root_pos(&self) -> NonZero { From 87ab9e8c6eac37374475fbc1409b4153dd52e1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 9 Mar 2024 00:24:14 +0100 Subject: [PATCH 30/47] Some tweaks to the parallel query cycle handler --- compiler/rustc_interface/src/util.rs | 19 ++++++++++++++--- compiler/rustc_query_system/src/query/job.rs | 22 +++++++------------- compiler/rustc_query_system/src/query/mod.rs | 2 +- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 0d50200133cb..23bd2dac57e3 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -101,10 +101,11 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( threads: usize, f: F, ) -> R { - use rustc_data_structures::{jobserver, sync::FromDyn}; + use rustc_data_structures::{defer, jobserver, sync::FromDyn}; use rustc_middle::ty::tls; use rustc_query_impl::QueryCtxt; - use rustc_query_system::query::{deadlock, QueryContext}; + use rustc_query_system::query::{break_query_cycles, QueryContext}; + use std::process; let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap()); @@ -128,7 +129,19 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( let query_map = FromDyn::from(tls::with(|tcx| QueryCtxt::new(tcx).collect_active_jobs())); let registry = rayon_core::Registry::current(); - thread::spawn(move || deadlock(query_map.into_inner(), ®istry)); + thread::Builder::new() + .name("rustc query cycle handler".to_string()) + .spawn(move || { + let on_panic = defer(|| { + eprintln!("query cycle handler thread panicked, aborting process"); + // We need to abort here as we failed to resolve the deadlock, + // otherwise the compiler could just hang, + process::abort(); + }); + break_query_cycles(query_map.into_inner(), ®istry); + on_panic.disable(); + }) + .unwrap(); }); if let Some(size) = get_stack_size() { builder = builder.stack_size(size); diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 1a54a2293573..248a741af907 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -17,10 +17,9 @@ use std::num::NonZero; use { parking_lot::{Condvar, Mutex}, rustc_data_structures::fx::FxHashSet, - rustc_data_structures::{defer, jobserver}, + rustc_data_structures::jobserver, rustc_span::DUMMY_SP, std::iter, - std::process, std::sync::Arc, }; @@ -514,12 +513,7 @@ fn remove_cycle( /// There may be multiple cycles involved in a deadlock, so this searches /// all active queries for cycles before finally resuming all the waiters at once. #[cfg(parallel_compiler)] -pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) { - let on_panic = defer(|| { - eprintln!("deadlock handler panicked, aborting process"); - process::abort(); - }); - +pub fn break_query_cycles(query_map: QueryMap, registry: &rayon_core::Registry) { let mut wakelist = Vec::new(); let mut jobs: Vec = query_map.keys().cloned().collect(); @@ -539,19 +533,17 @@ pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) { // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here // only considers the true dependency and won't detect a cycle. if !found_cycle { - if query_map.len() == 0 { - panic!("deadlock detected without any query!") - } else { - panic!("deadlock detected! current query map:\n{:#?}", query_map); - } + panic!( + "deadlock detected as we're unable to find a query cycle to break\n\ + current query map:\n{:#?}", + query_map + ); } // FIXME: Ensure this won't cause a deadlock before we return for waiter in wakelist.into_iter() { waiter.notify(registry); } - - on_panic.disable(); } #[inline(never)] diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 0aefe553a343..01b9d458f1e4 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -3,7 +3,7 @@ pub use self::plumbing::*; mod job; #[cfg(parallel_compiler)] -pub use self::job::deadlock; +pub use self::job::break_query_cycles; pub use self::job::{ print_query_stack, report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap, }; From 5a93a59fd557f7d93ab0b6cc192c47580951666a Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Mon, 26 Feb 2024 21:25:27 -0500 Subject: [PATCH 31/47] Distinguish between library and lang UB in assert_unsafe_precondition --- compiler/rustc_borrowck/src/type_check/mod.rs | 2 +- compiler/rustc_codegen_cranelift/src/base.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 3 +- .../rustc_const_eval/src/interpret/step.rs | 14 +- .../src/transform/check_consts/check.rs | 2 +- .../src/transform/validate.rs | 2 +- .../rustc_hir_analysis/src/check/intrinsic.rs | 5 +- compiler/rustc_middle/src/mir/pretty.rs | 2 +- compiler/rustc_middle/src/mir/syntax.rs | 12 +- compiler/rustc_middle/src/mir/tcx.rs | 2 +- .../src/move_paths/builder.rs | 2 +- compiler/rustc_mir_transform/src/gvn.rs | 2 +- .../src/known_panics_lint.rs | 2 +- .../src/lower_intrinsics.rs | 21 +- .../rustc_mir_transform/src/promote_consts.rs | 2 +- .../rustc_smir/src/rustc_smir/convert/mir.rs | 8 +- compiler/rustc_span/src/symbol.rs | 2 + compiler/stable_mir/src/mir/body.rs | 10 +- library/core/src/cell.rs | 6 +- library/core/src/char/convert.rs | 1 + library/core/src/hint.rs | 11 +- library/core/src/intrinsics.rs | 192 +++++++++++------- library/core/src/num/nonzero.rs | 10 +- library/core/src/ops/index_range.rs | 9 +- library/core/src/panic.rs | 37 ---- library/core/src/ptr/alignment.rs | 9 +- library/core/src/ptr/const_ptr.rs | 40 ++-- library/core/src/ptr/mod.rs | 40 ++-- library/core/src/ptr/non_null.rs | 1 + library/core/src/slice/index.rs | 52 +++-- library/core/src/slice/mod.rs | 41 ++-- library/core/src/slice/raw.rs | 2 + library/core/src/str/traits.rs | 24 ++- .../clippy_utils/src/qualify_min_const_fn.rs | 2 +- ...n.DataflowConstProp.32bit.panic-abort.diff | 12 +- ....DataflowConstProp.32bit.panic-unwind.diff | 12 +- ...n.DataflowConstProp.64bit.panic-abort.diff | 12 +- ....DataflowConstProp.64bit.panic-unwind.diff | 12 +- ...oxed_slice.main.GVN.32bit.panic-abort.diff | 12 +- ...xed_slice.main.GVN.32bit.panic-unwind.diff | 12 +- ...oxed_slice.main.GVN.64bit.panic-abort.diff | 12 +- ...xed_slice.main.GVN.64bit.panic-unwind.diff | 12 +- ...d.unwrap_unchecked.Inline.panic-abort.diff | 2 +- ....unwrap_unchecked.Inline.panic-unwind.diff | 2 +- ...unchecked.PreCodegen.after.panic-abort.mir | 2 +- ...nchecked.PreCodegen.after.panic-unwind.mir | 2 +- ...witch_targets.ub_if_b.PreCodegen.after.mir | 2 +- 47 files changed, 415 insertions(+), 263 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8d38b86fa343..412d50f493ee 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2000,7 +2000,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::SizedBound, ); } - &Rvalue::NullaryOp(NullOp::DebugAssertions, _) => {} + &Rvalue::NullaryOp(NullOp::UbCheck(_), _) => {} Rvalue::ShallowInitBox(operand, ty) => { self.check_operand(operand, location); diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 1ce920f3bdb7..2415c2c90b22 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -779,7 +779,7 @@ fn codegen_stmt<'tcx>( NullOp::OffsetOf(fields) => { layout.offset_of_subfield(fx, fields.iter()).bytes() } - NullOp::DebugAssertions => { + NullOp::UbCheck(_) => { let val = fx.tcx.sess.opts.debug_assertions; let val = CValue::by_val( fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()), diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 9ae82d4845e1..8c6b9faf39d3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -685,7 +685,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = layout.offset_of_subfield(bx.cx(), fields.iter()).bytes(); bx.cx().const_usize(val) } - mir::NullOp::DebugAssertions => { + mir::NullOp::UbCheck(_) => { + // In codegen, we want to check for language UB and library UB let val = bx.tcx().sess.opts.debug_assertions; bx.cx().const_bool(val) } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index d4c96f4573d9..752fdc2c6f84 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -258,10 +258,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val = layout.offset_of_subfield(self, fields.iter()).bytes(); Scalar::from_target_usize(val, self) } - mir::NullOp::DebugAssertions => { - // The checks hidden behind this are always better done by the interpreter - // itself, because it knows the runtime state better. - Scalar::from_bool(false) + mir::NullOp::UbCheck(kind) => { + // We want to enable checks for library UB, because the interpreter doesn't + // know about those on its own. + // But we want to disable checks for language UB, because the interpreter + // has its own better checks for that. + let should_check = match kind { + mir::UbKind::LibraryUb => true, + mir::UbKind::LanguageUb => false, + }; + Scalar::from_bool(should_check) } }; self.write_scalar(val, &dest)?; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 7ec78e7b9cfd..53308cd7f901 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -558,7 +558,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Cast(_, _, _) => {} Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions, + NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbCheck(_), _, ) => {} Rvalue::ShallowInitBox(_, _) => {} diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 74ba2f6039e8..f26c8f8592de 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -1157,7 +1157,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Rvalue::Repeat(_, _) | Rvalue::ThreadLocalRef(_) | Rvalue::AddressOf(_, _) - | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::DebugAssertions, _) + | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbCheck(_), _) | Rvalue::Discriminant(_) => {} } self.super_rvalue(rvalue, location); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 95c51cc0486f..35755a46df3a 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -127,7 +127,8 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::variant_count | sym::is_val_statically_known | sym::ptr_mask - | sym::debug_assertions + | sym::check_language_ub + | sym::check_library_ub | sym::fadd_algebraic | sym::fsub_algebraic | sym::fmul_algebraic @@ -584,7 +585,7 @@ pub fn check_intrinsic_type( (0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize) } - sym::debug_assertions => (0, 1, Vec::new(), tcx.types.bool), + sym::check_language_ub | sym::check_library_ub => (0, 1, Vec::new(), tcx.types.bool), sym::simd_eq | sym::simd_ne diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index e058302af312..18069547a7ed 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -915,7 +915,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { NullOp::SizeOf => write!(fmt, "SizeOf({t})"), NullOp::AlignOf => write!(fmt, "AlignOf({t})"), NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"), - NullOp::DebugAssertions => write!(fmt, "cfg!(debug_assertions)"), + NullOp::UbCheck(kind) => write!(fmt, "UbCheck({kind:?})"), } } ThreadLocalRef(did) => ty::tls::with(|tcx| { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index f188923f8761..0ab797134c0a 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1366,8 +1366,16 @@ pub enum NullOp<'tcx> { AlignOf, /// Returns the offset of a field OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>), - /// cfg!(debug_assertions), but expanded in codegen - DebugAssertions, + /// Returns whether we want to check for library UB or language UB at monomorphization time. + /// Both kinds of UB evaluate to `true` in codegen, and only library UB evalutes to `true` in + /// const-eval/Miri, because the interpreter has its own better checks for language UB. + UbCheck(UbKind), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] +pub enum UbKind { + LanguageUb, + LibraryUb, } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 5bc151de659c..0c29fe57d4fc 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -194,7 +194,7 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { tcx.types.usize } - Rvalue::NullaryOp(NullOp::DebugAssertions, _) => tcx.types.bool, + Rvalue::NullaryOp(NullOp::UbCheck(_), _) => tcx.types.bool, Rvalue::Aggregate(ref ak, ref ops) => match **ak { AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64), AggregateKind::Tuple => { diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index db48ecd702bd..0f900e6a5573 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -433,7 +433,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | Rvalue::Discriminant(..) | Rvalue::Len(..) | Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::DebugAssertions, + NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbCheck(_), _, ) => {} } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index a080e2423d47..a3a2108787a7 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -488,7 +488,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { NullOp::OffsetOf(fields) => { layout.offset_of_subfield(&self.ecx, fields.iter()).bytes() } - NullOp::DebugAssertions => return None, + NullOp::UbCheck(_) => return None, }; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); let imm = ImmTy::try_from_uint(val, usize_layout)?; diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 27477769cef8..4bca437ea6f2 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -639,7 +639,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { NullOp::OffsetOf(fields) => { op_layout.offset_of_subfield(self, fields.iter()).bytes() } - NullOp::DebugAssertions => return None, + NullOp::UbCheck(_) => return None, }; ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into() } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index f317c025e966..1bab240ef50f 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -20,13 +20,30 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { sym::unreachable => { terminator.kind = TerminatorKind::Unreachable; } - sym::debug_assertions => { + sym::check_language_ub => { let target = target.unwrap(); block.statements.push(Statement { source_info: terminator.source_info, kind: StatementKind::Assign(Box::new(( *destination, - Rvalue::NullaryOp(NullOp::DebugAssertions, tcx.types.bool), + Rvalue::NullaryOp( + NullOp::UbCheck(UbKind::LanguageUb), + tcx.types.bool, + ), + ))), + }); + terminator.kind = TerminatorKind::Goto { target }; + } + sym::check_library_ub => { + let target = target.unwrap(); + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::NullaryOp( + NullOp::UbCheck(UbKind::LibraryUb), + tcx.types.bool, + ), ))), }); terminator.kind = TerminatorKind::Goto { target }; diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 2e11da4d585e..9fe8c34a8bf5 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -446,7 +446,7 @@ impl<'tcx> Validator<'_, 'tcx> { NullOp::SizeOf => {} NullOp::AlignOf => {} NullOp::OffsetOf(_) => {} - NullOp::DebugAssertions => {} + NullOp::UbCheck(_) => {} }, Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable), diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 003a9a592001..c0876adf9050 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -251,13 +251,19 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> { type T = stable_mir::mir::NullOp; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { use rustc_middle::mir::NullOp::*; + use rustc_middle::mir::UbKind; match self { SizeOf => stable_mir::mir::NullOp::SizeOf, AlignOf => stable_mir::mir::NullOp::AlignOf, OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf( indices.iter().map(|idx| idx.stable(tables)).collect(), ), - DebugAssertions => stable_mir::mir::NullOp::DebugAssertions, + UbCheck(UbKind::LanguageUb) => { + stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LanguageUb) + } + UbCheck(UbKind::LibraryUb) => { + stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LibraryUb) + } } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9dadd4712477..31b2691d9fe8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -518,6 +518,8 @@ symbols! { cfi, cfi_encoding, char, + check_language_ub, + check_library_ub, client, clippy, clobber_abi, diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index be727f024c60..ae8e71bb950a 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -639,7 +639,7 @@ impl Rvalue { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { Ok(Ty::usize_ty()) } - Rvalue::NullaryOp(NullOp::DebugAssertions, _) => Ok(Ty::bool_ty()), + Rvalue::NullaryOp(NullOp::UbCheck(_), _) => Ok(Ty::bool_ty()), Rvalue::Aggregate(ak, ops) => match *ak { AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), AggregateKind::Tuple => Ok(Ty::new_tuple( @@ -1007,7 +1007,13 @@ pub enum NullOp { /// Returns the offset of a field. OffsetOf(Vec<(VariantIdx, FieldIdx)>), /// cfg!(debug_assertions), but at codegen time - DebugAssertions, + UbCheck(UbKind), +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum UbKind { + LanguageUb, + LibraryUb, } impl Operand { diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 19b05448c878..e8590ab11586 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -237,7 +237,6 @@ use crate::cmp::Ordering; use crate::fmt::{self, Debug, Display}; -use crate::intrinsics; use crate::marker::{PhantomData, Unsize}; use crate::mem::{self, size_of}; use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn}; @@ -436,7 +435,10 @@ impl Cell { #[stable(feature = "move_cell", since = "1.17.0")] pub fn swap(&self, other: &Self) { fn is_nonoverlapping(src: *const T, dst: *const T) -> bool { - intrinsics::is_nonoverlapping(src.cast(), dst.cast(), size_of::(), 1) + let src_usize = src.addr(); + let dst_usize = dst.addr(); + let diff = src_usize.abs_diff(dst_usize); + diff >= size_of::() } if ptr::eq(self, other) { diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 7bd592492a5c..70b9e89f9ea9 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -26,6 +26,7 @@ pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { // SAFETY: the caller must guarantee that `i` is a valid char value. unsafe { assert_unsafe_precondition!( + check_language_ub, "invalid value for `char`", (i: u32 = i) => char_try_from_u32(i).is_ok() ); diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 97c3c9e6fae9..ffe059bf65ca 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -98,12 +98,14 @@ use crate::intrinsics; #[rustc_const_stable(feature = "const_unreachable_unchecked", since = "1.57.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unreachable_unchecked() -> ! { + intrinsics::assert_unsafe_precondition!( + check_language_ub, + "hint::unreachable_unchecked must never be reached", + () => false + ); // SAFETY: the safety contract for `intrinsics::unreachable` must // be upheld by the caller. - unsafe { - intrinsics::assert_unsafe_precondition!("hint::unreachable_unchecked must never be reached", () => false); - intrinsics::unreachable() - } + unsafe { intrinsics::unreachable() } } /// Makes a *soundness* promise to the compiler that `cond` holds. @@ -147,6 +149,7 @@ pub const unsafe fn assert_unchecked(cond: bool) { // SAFETY: The caller promised `cond` is true. unsafe { intrinsics::assert_unsafe_precondition!( + check_language_ub, "hint::assert_unchecked must never be called when the condition is false", (cond: bool = cond) => cond, ); diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index aff1c589e628..eda8b7842ab4 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2628,24 +2628,38 @@ pub const fn is_val_statically_known(_arg: T) -> bool { false } -/// Returns the value of `cfg!(debug_assertions)`, but after monomorphization instead of in -/// macro expansion. +/// Returns whether we should check for library UB. This evaluate to the value of `cfg!(debug_assertions)` +/// during monomorphization. /// -/// This always returns `false` in const eval and Miri. The interpreter provides better -/// diagnostics than the checks that this is used to implement. However, this means -/// you should only be using this intrinsic to guard requirements that, if violated, -/// immediately lead to UB. Otherwise, const-eval and Miri will miss out on those -/// checks entirely. -/// -/// Since this is evaluated after monomorphization, branching on this value can be used to -/// implement debug assertions that are included in the precompiled standard library, but can -/// be optimized out by builds that monomorphize the standard library code with debug +/// This intrinsic is evaluated after monomorphization, and therefore branching on this value can +/// be used to implement debug assertions that are included in the precompiled standard library, +/// but can be optimized out by builds that monomorphize the standard library code with debug /// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`]. -#[rustc_const_unstable(feature = "delayed_debug_assertions", issue = "none")] +/// +/// We have separate intrinsics for library UB and language UB because checkers like the const-eval +/// interpreter and Miri already implement checks for language UB. Since such checkers do not know +/// about library preconditions, checks guarded by this intrinsic let them find more UB. +#[rustc_const_unstable(feature = "ub_checks", issue = "none")] #[unstable(feature = "core_intrinsics", issue = "none")] #[inline(always)] #[cfg_attr(not(bootstrap), rustc_intrinsic)] -pub(crate) const fn debug_assertions() -> bool { +pub(crate) const fn check_library_ub() -> bool { + cfg!(debug_assertions) +} + +/// Returns whether we should check for language UB. This evaluate to the value of `cfg!(debug_assertions)` +/// during monomorphization. +/// +/// Since checks implemented at the source level must come strictly before the operation that +/// executes UB, if we enabled language UB checks in const-eval/Miri we would miss out on the +/// interpreter's improved diagnostics for the cases that our source-level checks catch. +/// +/// See `check_library_ub` for more information. +#[rustc_const_unstable(feature = "ub_checks", issue = "none")] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[inline(always)] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +pub(crate) const fn check_language_ub() -> bool { cfg!(debug_assertions) } @@ -2700,13 +2714,24 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize { // (`transmute` also falls into this category, but it cannot be wrapped due to the // check that `T` and `U` have the same size.) -/// Check that the preconditions of an unsafe function are followed, if debug_assertions are on, -/// and only at runtime. +/// Check that the preconditions of an unsafe function are followed. The check is enabled at +/// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri +/// checks implemented with this macro for language UB are always ignored. /// /// This macro should be called as -/// `assert_unsafe_precondition!((expr => name: Type, expr => name: Type) => Expression)` -/// where each `expr` will be evaluated and passed in as function argument `name: Type`. Then all -/// those arguments are passed to a function via [`const_eval_select`]. +/// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)` +/// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all +/// those arguments are passed to a function with the body `check_expr`. +/// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB +/// according to the Rust Abstract Machine. Pick `check_library_ub` when this is guarding a violation +/// of a documented library precondition that does not *immediately* lead to language UB. +/// +/// If `check_library_ub` is used but the check is actually guarding language UB, the check will +/// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice +/// diagnostic, but our ability to detect UB is unchanged. +/// But if `check_language_ub` is used when the check is actually for library UB, the check is +/// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the +/// library UB, the backtrace Miri reports may be far removed from original cause. /// /// These checks are behind a condition which is evaluated at codegen time, not expansion time like /// [`debug_assert`]. This means that a standard library built with optimizations and debug @@ -2715,31 +2740,25 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize { /// this macro, that monomorphization will contain the check. /// /// Since these checks cannot be optimized out in MIR, some care must be taken in both call and -/// implementation to mitigate their compile-time overhead. The runtime function that we -/// [`const_eval_select`] to is monomorphic, `#[inline(never)]`, and `#[rustc_nounwind]`. That -/// combination of properties ensures that the code for the checks is only compiled once, and has a -/// minimal impact on the caller's code size. +/// implementation to mitigate their compile-time overhead. Calls to this macro always expand to +/// this structure: +/// ```ignore (pseudocode) +/// if ::core::intrinsics::check_language_ub() { +/// precondition_check(args) +/// } +/// ``` +/// where `precondition_check` is monomorphic with the attributes `#[rustc_nounwind]`, `#[inline]` and +/// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is +/// compiled only once and generates a minimal amount of IR because the check cannot be inlined in +/// MIR, but *can* be inlined and fully optimized by a codegen backend. /// -/// Callers should also avoid introducing any other `let` bindings or any code outside this macro in +/// Callers should avoid introducing any other `let` bindings or any code outside this macro in /// order to call it. Since the precompiled standard library is built with full debuginfo and these /// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough /// debuginfo to have a measurable compile-time impact on debug builds. -/// -/// # Safety -/// -/// Invoking this macro is only sound if the following code is already UB when the passed -/// expression evaluates to false. -/// -/// This macro expands to a check at runtime if debug_assertions is set. It has no effect at -/// compile time, but the semantics of the contained `const_eval_select` must be the same at -/// runtime and at compile time. Thus if the expression evaluates to false, this macro produces -/// different behavior at compile time and at runtime, and invoking it is incorrect. -/// -/// So in a sense it is UB if this macro is useful, but we expect callers of `unsafe fn` to make -/// the occasional mistake, and this check should help them figure things out. -#[allow_internal_unstable(const_eval_select, delayed_debug_assertions)] // permit this to be called in stably-const fn +#[allow_internal_unstable(ub_checks)] // permit this to be called in stably-const fn macro_rules! assert_unsafe_precondition { - ($message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => { + ($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => { { // #[cfg(bootstrap)] (this comment) // When the standard library is compiled with debug assertions, we want the check to inline for better performance. @@ -2761,17 +2780,17 @@ macro_rules! assert_unsafe_precondition { #[cfg_attr(not(bootstrap), rustc_no_mir_inline)] #[cfg_attr(not(bootstrap), inline)] #[rustc_nounwind] - fn precondition_check($($name:$ty),*) { + #[rustc_const_unstable(feature = "ub_checks", issue = "none")] + const fn precondition_check($($name:$ty),*) { if !$e { ::core::panicking::panic_nounwind( concat!("unsafe precondition(s) violated: ", $message) ); } } - const fn comptime($(_:$ty),*) {} - if ::core::intrinsics::debug_assertions() { - ::core::intrinsics::const_eval_select(($($arg,)*), comptime, precondition_check); + if ::core::intrinsics::$kind() { + precondition_check($($arg,)*); } } }; @@ -2781,31 +2800,55 @@ pub(crate) use assert_unsafe_precondition; /// Checks whether `ptr` is properly aligned with respect to /// `align_of::()`. #[inline] -pub(crate) fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool { +pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool { !ptr.is_null() && ptr.is_aligned_to(align) } #[inline] -pub(crate) fn is_valid_allocation_size(size: usize, len: usize) -> bool { +pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool { let max_len = if size == 0 { usize::MAX } else { isize::MAX as usize / size }; len <= max_len } /// Checks whether the regions of memory starting at `src` and `dst` of size /// `count * size` do *not* overlap. +/// +/// # Safety +/// This function must only be called such that if it returns false, we will execute UB. #[inline] -pub(crate) fn is_nonoverlapping(src: *const (), dst: *const (), size: usize, count: usize) -> bool { - let src_usize = src.addr(); - let dst_usize = dst.addr(); - let Some(size) = size.checked_mul(count) else { - crate::panicking::panic_nounwind( - "is_nonoverlapping: `size_of::() * count` overflows a usize", - ) - }; - let diff = src_usize.abs_diff(dst_usize); - // If the absolute distance between the ptrs is at least as big as the size of the buffer, - // they do not overlap. - diff >= size +pub(crate) const unsafe fn is_nonoverlapping( + src: *const (), + dst: *const (), + size: usize, + count: usize, +) -> bool { + #[inline] + fn runtime(src: *const (), dst: *const (), size: usize, count: usize) -> bool { + let src_usize = src.addr(); + let dst_usize = dst.addr(); + let Some(size) = size.checked_mul(count) else { + crate::panicking::panic_nounwind( + "is_nonoverlapping: `size_of::() * count` overflows a usize", + ) + }; + let diff = src_usize.abs_diff(dst_usize); + // If the absolute distance between the ptrs is at least as big as the size of the buffer, + // they do not overlap. + diff >= size + } + + #[inline] + const fn comptime(_: *const (), _: *const (), _: usize, _: usize) -> bool { + true + } + + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] + // SAFETY: This function's precondition is equivalent to that of `const_eval_select`. + // Programs which do not execute UB will only see this function return `true`, which makes the + // const and runtime implementation indistinguishable. + unsafe { + const_eval_select((src, dst, size, count), comptime, runtime) + } } /// Copies `count * size_of::()` bytes from `src` to `dst`. The source @@ -2906,25 +2949,26 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us pub fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); } + assert_unsafe_precondition!( + check_language_ub, + "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ + and the specified memory ranges do not overlap", + ( + src: *const () = src as *const (), + dst: *mut () = dst as *mut (), + size: usize = size_of::(), + align: usize = align_of::(), + count: usize = count, + ) => + is_aligned_and_not_null(src, align) + && is_aligned_and_not_null(dst, align) + // SAFETY: If this returns false, we're about to execute UB. + && unsafe { is_nonoverlapping(src, dst, size, count) } + ); + // SAFETY: the safety contract for `copy_nonoverlapping` must be // upheld by the caller. - unsafe { - assert_unsafe_precondition!( - "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ - and the specified memory ranges do not overlap", - ( - src: *const () = src as *const (), - dst: *mut () = dst as *mut (), - size: usize = size_of::(), - align: usize = align_of::(), - count: usize = count, - ) => - is_aligned_and_not_null(src, align) - && is_aligned_and_not_null(dst, align) - && is_nonoverlapping(src, dst, size, count) - ); - copy_nonoverlapping(src, dst, count) - } + unsafe { copy_nonoverlapping(src, dst, count) } } /// Copies `count * size_of::()` bytes from `src` to `dst`. The source @@ -3011,6 +3055,7 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { // SAFETY: the safety contract for `copy` must be upheld by the caller. unsafe { assert_unsafe_precondition!( + check_language_ub, "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ and the specified memory ranges do not overlap", ( @@ -3091,6 +3136,7 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. unsafe { assert_unsafe_precondition!( + check_language_ub, "ptr::write_bytes requires that the destination pointer is aligned and non-null", ( addr: *const () = dst as *const (), diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 9e34c0d240dc..012f224a4a26 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -328,8 +328,9 @@ where // SAFETY: The caller guarantees that `n` is non-zero, so this is unreachable. unsafe { intrinsics::assert_unsafe_precondition!( - "NonZero::new_unchecked requires the argument to be non-zero", - () => false, + check_language_ub, + "NonZero::new_unchecked requires the argument to be non-zero", + () => false, ); intrinsics::unreachable() } @@ -367,8 +368,9 @@ where // SAFETY: The caller guarantees that `n` references a value that is non-zero, so this is unreachable. unsafe { intrinsics::assert_unsafe_precondition!( - "NonZero::from_mut_unchecked requires the argument to dereference as non-zero", - () => false, + check_language_ub, + "NonZero::from_mut_unchecked requires the argument to dereference as non-zero", + () => false, ); intrinsics::unreachable() } diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index 07ea2e930d57..b28d88fa5bfc 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -1,4 +1,4 @@ -use crate::intrinsics::{unchecked_add, unchecked_sub}; +use crate::intrinsics::{assert_unsafe_precondition, unchecked_add, unchecked_sub}; use crate::iter::{FusedIterator, TrustedLen}; use crate::num::NonZero; @@ -19,9 +19,10 @@ impl IndexRange { /// - `start <= end` #[inline] pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self { - crate::panic::debug_assert_nounwind!( - start <= end, - "IndexRange::new_unchecked requires `start <= end`" + assert_unsafe_precondition!( + check_library_ub, + "IndexRange::new_unchecked requires `start <= end`", + (start: usize = start, end: usize = end) => start <= end, ); IndexRange { start, end } } diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 2bd42a4a8cad..b520efe93f90 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -139,43 +139,6 @@ pub macro unreachable_2021 { ), } -/// Like `assert_unsafe_precondition!` the defining features of this macro are that its -/// checks are enabled when they are monomorphized with debug assertions enabled, and upon failure -/// a non-unwinding panic is launched so that failures cannot compromise unwind safety. -/// -/// But there are many differences from `assert_unsafe_precondition!`. This macro does not use -/// `const_eval_select` internally, and therefore it is sound to call this with an expression -/// that evaluates to `false`. Also unlike `assert_unsafe_precondition!` the condition being -/// checked here is not put in an outlined function. If the check compiles to a lot of IR, this -/// can cause code bloat if the check is monomorphized many times. But it also means that the checks -/// from this macro can be deduplicated or otherwise optimized out. -/// -/// In general, this macro should be used to check all public-facing preconditions. But some -/// preconditions may be called too often or instantiated too often to make the overhead of the -/// checks tolerable. In such cases, place `#[cfg(debug_assertions)]` on the macro call. That will -/// disable the check in our precompiled standard library, but if a user wishes, they can still -/// enable the check by recompiling the standard library with debug assertions enabled. -#[doc(hidden)] -#[unstable(feature = "panic_internals", issue = "none")] -#[allow_internal_unstable(panic_internals, delayed_debug_assertions)] -#[rustc_macro_transparency = "semitransparent"] -pub macro debug_assert_nounwind { - ($cond:expr $(,)?) => { - if $crate::intrinsics::debug_assertions() { - if !$cond { - $crate::panicking::panic_nounwind($crate::concat!("assertion failed: ", $crate::stringify!($cond))); - } - } - }, - ($cond:expr, $message:expr) => { - if $crate::intrinsics::debug_assertions() { - if !$cond { - $crate::panicking::panic_nounwind($message); - } - } - }, -} - /// An internal trait used by std to pass data from std to `panic_unwind` and /// other panic runtimes. Not intended to be stabilized any time soon, do not /// use. diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 3508b0c7f238..8f44b7eb7c28 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -1,4 +1,6 @@ use crate::convert::{TryFrom, TryInto}; +#[cfg(debug_assertions)] +use crate::intrinsics::assert_unsafe_precondition; use crate::num::NonZero; use crate::{cmp, fmt, hash, mem, num}; @@ -77,9 +79,10 @@ impl Alignment { #[inline] pub const unsafe fn new_unchecked(align: usize) -> Self { #[cfg(debug_assertions)] - crate::panic::debug_assert_nounwind!( - align.is_power_of_two(), - "Alignment::new_unchecked requires a power of two" + assert_unsafe_precondition!( + check_language_ub, + "Alignment::new_unchecked requires a power of two", + (align: usize = align) => align.is_power_of_two() ); // SAFETY: By precondition, this must be a power of two, and diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 0c69bf2aef9c..764f5d1a76a1 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -48,7 +48,8 @@ impl *const T { } } - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block + // on bootstrap bump, remove unsafe block + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // SAFETY: The two versions are equivalent at runtime. unsafe { const_eval_select((self as *const u8,), const_impl, runtime_impl) @@ -809,18 +810,31 @@ impl *const T { where T: Sized, { - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: The comparison has no side-effects, and the intrinsic - // does this check internally in the CTFE implementation. - unsafe { - assert_unsafe_precondition!( - "ptr::sub_ptr requires `self >= origin`", - ( - this: *const () = self as *const (), - origin: *const () = origin as *const (), - ) => this >= origin - ) - }; + const fn runtime_ptr_ge(this: *const (), origin: *const ()) -> bool { + fn runtime(this: *const (), origin: *const ()) -> bool { + this >= origin + } + const fn comptime(_: *const (), _: *const ()) -> bool { + true + } + + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] + // on bootstrap bump, remove unsafe block + // SAFETY: This function is only used to provide the same check that the const eval + // interpreter does at runtime. + unsafe { + intrinsics::const_eval_select((this, origin), comptime, runtime) + } + } + + assert_unsafe_precondition!( + check_language_ub, + "ptr::sub_ptr requires `self >= origin`", + ( + this: *const () = self as *const (), + origin: *const () = origin as *const (), + ) => runtime_ptr_ge(this, origin) + ); let pointee_size = mem::size_of::(); assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index caa8ffb271dd..80a6d5779e14 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1019,25 +1019,22 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { }; } - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: the caller must guarantee that `x` and `y` are - // valid for writes and properly aligned. - unsafe { - assert_unsafe_precondition!( - "ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \ - and the specified memory ranges do not overlap", - ( - x: *mut () = x as *mut (), - y: *mut () = y as *mut (), - size: usize = size_of::(), - align: usize = align_of::(), - count: usize = count, - ) => - is_aligned_and_not_null(x, align) - && is_aligned_and_not_null(y, align) - && is_nonoverlapping(x, y, size, count) - ); - } + assert_unsafe_precondition!( + check_language_ub, + "ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \ + and the specified memory ranges do not overlap", + ( + x: *mut () = x as *mut (), + y: *mut () = y as *mut (), + size: usize = size_of::(), + align: usize = align_of::(), + count: usize = count, + ) => + is_aligned_and_not_null(x, align) + && is_aligned_and_not_null(y, align) + // SAFETY: If this returns false, we're about to execute UB. + && unsafe { is_nonoverlapping(x, y, size, count) } + ); // Split up the slice into small power-of-two-sized chunks that LLVM is able // to vectorize (unless it's a special type with more-than-pointer alignment, @@ -1125,6 +1122,7 @@ pub const unsafe fn replace(dst: *mut T, mut src: T) -> T { // allocated object. unsafe { assert_unsafe_precondition!( + check_language_ub, "ptr::replace requires that the pointer argument is aligned and non-null", ( addr: *const () = dst as *const (), @@ -1277,6 +1275,7 @@ pub const unsafe fn read(src: *const T) -> T { unsafe { #[cfg(debug_assertions)] // Too expensive to always enable (for now?) assert_unsafe_precondition!( + check_language_ub, "ptr::read requires that the pointer argument is aligned and non-null", ( addr: *const () = src as *const (), @@ -1485,6 +1484,7 @@ pub const unsafe fn write(dst: *mut T, src: T) { unsafe { #[cfg(debug_assertions)] // Too expensive to always enable (for now?) assert_unsafe_precondition!( + check_language_ub, "ptr::write requires that the pointer argument is aligned and non-null", ( addr: *mut () = dst as *mut (), @@ -1656,6 +1656,7 @@ pub unsafe fn read_volatile(src: *const T) -> T { // SAFETY: the caller must uphold the safety contract for `volatile_load`. unsafe { assert_unsafe_precondition!( + check_language_ub, "ptr::read_volatile requires that the pointer argument is aligned and non-null", ( addr: *const () = src as *const (), @@ -1734,6 +1735,7 @@ pub unsafe fn write_volatile(dst: *mut T, src: T) { // SAFETY: the caller must uphold the safety contract for `volatile_store`. unsafe { assert_unsafe_precondition!( + check_language_ub, "ptr::write_volatile requires that the pointer argument is aligned and non-null", ( addr: *mut () = dst as *mut (), diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index acb8c552a633..9c0236c172a9 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -218,6 +218,7 @@ impl NonNull { // SAFETY: the caller must guarantee that `ptr` is non-null. unsafe { assert_unsafe_precondition!( + check_language_ub, "NonNull::new_unchecked requires that the pointer is non-null", (ptr: *mut () = ptr as *mut ()) => !ptr.is_null() ); diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index c771ea704724..312dccbf1099 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -1,9 +1,9 @@ //! Indexing implementations for `[T]`. +use crate::intrinsics::assert_unsafe_precondition; use crate::intrinsics::const_eval_select; use crate::intrinsics::unchecked_sub; use crate::ops; -use crate::panic::debug_assert_nounwind; use crate::ptr; #[stable(feature = "rust1", since = "1.0.0")] @@ -230,9 +230,10 @@ unsafe impl SliceIndex<[T]> for usize { #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const T { - debug_assert_nounwind!( - self < slice.len(), - "slice::get_unchecked requires that the index is within the slice" + assert_unsafe_precondition!( + check_language_ub, + "slice::get_unchecked requires that the index is within the slice", + (this: usize = self, len: usize = slice.len()) => this < len ); // SAFETY: the caller guarantees that `slice` is not dangling, so it // cannot be longer than `isize::MAX`. They also guarantee that @@ -248,9 +249,10 @@ unsafe impl SliceIndex<[T]> for usize { #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T { - debug_assert_nounwind!( - self < slice.len(), - "slice::get_unchecked_mut requires that the index is within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::get_unchecked_mut requires that the index is within the slice", + (this: usize = self, len: usize = slice.len()) => this < len ); // SAFETY: see comments for `get_unchecked` above. unsafe { slice.as_mut_ptr().add(self) } @@ -297,9 +299,10 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { - debug_assert_nounwind!( - self.end() <= slice.len(), - "slice::get_unchecked requires that the index is within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::get_unchecked requires that the index is within the slice", + (end: usize = self.end(), len: usize = slice.len()) => end <= len ); // SAFETY: the caller guarantees that `slice` is not dangling, so it // cannot be longer than `isize::MAX`. They also guarantee that @@ -310,9 +313,10 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { - debug_assert_nounwind!( - self.end() <= slice.len(), - "slice::get_unchecked_mut requires that the index is within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::get_unchecked_mut requires that the index is within the slice", + (end: usize = self.end(), len: usize = slice.len()) => end <= len ); // SAFETY: see comments for `get_unchecked` above. @@ -367,9 +371,14 @@ unsafe impl SliceIndex<[T]> for ops::Range { #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { - debug_assert_nounwind!( - self.end >= self.start && self.end <= slice.len(), - "slice::get_unchecked requires that the range is within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::get_unchecked requires that the range is within the slice", + ( + start: usize = self.start, + end: usize = self.end, + len: usize = slice.len() + ) => end >= start && end <= len ); // SAFETY: the caller guarantees that `slice` is not dangling, so it @@ -384,9 +393,14 @@ unsafe impl SliceIndex<[T]> for ops::Range { #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { - debug_assert_nounwind!( - self.end >= self.start && self.end <= slice.len(), - "slice::get_unchecked_mut requires that the range is within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::get_unchecked_mut requires that the range is within the slice", + ( + start: usize = self.start, + end: usize = self.end, + len: usize = slice.len() + ) => end >= start && end <= len ); // SAFETY: see comments for `get_unchecked` above. unsafe { diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 2d5e091feaae..2dd01ba33afc 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -9,11 +9,11 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::fmt; use crate::hint; +use crate::intrinsics::assert_unsafe_precondition; use crate::intrinsics::exact_div; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::{Bound, OneSidedRange, Range, RangeBounds}; -use crate::panic::debug_assert_nounwind; use crate::ptr; use crate::simd::{self, Simd}; use crate::slice; @@ -945,9 +945,14 @@ impl [T] { #[unstable(feature = "slice_swap_unchecked", issue = "88539")] #[rustc_const_unstable(feature = "const_swap", issue = "83163")] pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) { - debug_assert_nounwind!( - a < self.len() && b < self.len(), - "slice::swap_unchecked requires that the indices are within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::swap_unchecked requires that the indices are within the slice", + ( + len: usize = self.len(), + a: usize = a, + b: usize = b, + ) => a < len && b < len, ); let ptr = self.as_mut_ptr(); @@ -1285,9 +1290,10 @@ impl [T] { #[inline] #[must_use] pub const unsafe fn as_chunks_unchecked(&self) -> &[[T; N]] { - debug_assert_nounwind!( - N != 0 && self.len() % N == 0, - "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks" + assert_unsafe_precondition!( + check_language_ub, + "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks", + (n: usize = N, len: usize = self.len()) => n != 0 && len % n == 0, ); // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length let new_len = unsafe { exact_div(self.len(), N) }; @@ -1439,9 +1445,10 @@ impl [T] { #[inline] #[must_use] pub const unsafe fn as_chunks_unchecked_mut(&mut self) -> &mut [[T; N]] { - debug_assert_nounwind!( - N != 0 && self.len() % N == 0, - "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks" + assert_unsafe_precondition!( + check_language_ub, + "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks", + (n: usize = N, len: usize = self.len()) => n != 0 && len % n == 0 ); // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length let new_len = unsafe { exact_div(self.len(), N) }; @@ -1971,9 +1978,10 @@ impl [T] { let len = self.len(); let ptr = self.as_ptr(); - debug_assert_nounwind!( - mid <= len, - "slice::split_at_unchecked requires the index to be within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::split_at_unchecked requires the index to be within the slice", + (mid: usize = mid, len: usize = len) => mid <= len, ); // SAFETY: Caller has to check that `0 <= mid <= self.len()` @@ -2021,9 +2029,10 @@ impl [T] { let len = self.len(); let ptr = self.as_mut_ptr(); - debug_assert_nounwind!( - mid <= len, - "slice::split_at_mut_unchecked requires the index to be within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::split_at_mut_unchecked requires the index to be within the slice", + (mid: usize = mid, len: usize = len) => mid <= len, ); // SAFETY: Caller has to check that `0 <= mid <= self.len()`. diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 571abc3e9990..2199614ce27e 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -96,6 +96,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] // SAFETY: the caller must uphold the safety contract for `from_raw_parts`. unsafe { assert_unsafe_precondition!( + check_language_ub, "slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`", ( data: *mut () = data as *mut (), @@ -149,6 +150,7 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. unsafe { assert_unsafe_precondition!( + check_language_ub, "slice::from_raw_parts_mut requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`", ( data: *mut () = data as *mut (), diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index 3b9e9c981271..ec81fd095d53 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -1,8 +1,8 @@ //! Trait implementations for `str`. use crate::cmp::Ordering; +use crate::intrinsics::assert_unsafe_precondition; use crate::ops; -use crate::panic::debug_assert_nounwind; use crate::ptr; use crate::slice::SliceIndex; @@ -192,15 +192,20 @@ unsafe impl SliceIndex for ops::Range { unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { let slice = slice as *const [u8]; - debug_assert_nounwind!( + assert_unsafe_precondition!( // We'd like to check that the bounds are on char boundaries, // but there's not really a way to do so without reading // behind the pointer, which has aliasing implications. // It's also not possible to move this check up to // `str::get_unchecked` without adding a special function // to `SliceIndex` just for this. - self.end >= self.start && self.end <= slice.len(), - "str::get_unchecked requires that the range is within the string slice" + check_library_ub, + "str::get_unchecked requires that the range is within the string slice", + ( + start: usize = self.start, + end: usize = self.end, + len: usize = slice.len() + ) => end >= start && end <= len, ); // SAFETY: the caller guarantees that `self` is in bounds of `slice` @@ -213,9 +218,14 @@ unsafe impl SliceIndex for ops::Range { unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { let slice = slice as *mut [u8]; - debug_assert_nounwind!( - self.end >= self.start && self.end <= slice.len(), - "str::get_unchecked_mut requires that the range is within the string slice" + assert_unsafe_precondition!( + check_library_ub, + "str::get_unchecked_mut requires that the range is within the string slice", + ( + start: usize = self.start, + end: usize = self.end, + len: usize = slice.len() + ) => end >= start && end <= len, ); // SAFETY: see comments for `get_unchecked`. diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index e369cb9d0a40..dadb0d662ce8 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -174,7 +174,7 @@ fn check_rvalue<'tcx>( )) } }, - Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions, _) + Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbCheck(_), _) | Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff index e6b8d5e6c21b..a958e5541fad 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff @@ -58,11 +58,10 @@ _7 = const 1_usize; _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); - StorageLive(_10); StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb3, otherwise: bb2]; + _8 = UbCheck(LanguageUb); + switchInt(move _8) -> [0: bb4, otherwise: bb2]; } bb1: { @@ -71,16 +70,21 @@ } bb2: { + StorageLive(_10); _10 = const {0x1 as *mut ()}; _9 = NonNull::::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb3, unwind unreachable]; } bb3: { + StorageDead(_10); + goto -> bb4; + } + + bb4: { StorageDead(_8); _11 = const {0x1 as *const [bool; 0]}; _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; StorageDead(_11); - StorageDead(_10); StorageDead(_6); _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; StorageDead(_5); diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff index bd74591018bc..b073e27729e2 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff @@ -58,11 +58,10 @@ _7 = const 1_usize; _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); - StorageLive(_10); StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb4, otherwise: bb3]; + _8 = UbCheck(LanguageUb); + switchInt(move _8) -> [0: bb5, otherwise: bb3]; } bb1: { @@ -75,16 +74,21 @@ } bb3: { + StorageLive(_10); _10 = const {0x1 as *mut ()}; _9 = NonNull::::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb4, unwind unreachable]; } bb4: { + StorageDead(_10); + goto -> bb5; + } + + bb5: { StorageDead(_8); _11 = const {0x1 as *const [bool; 0]}; _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; StorageDead(_11); - StorageDead(_10); StorageDead(_6); _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; StorageDead(_5); diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff index fdbb0b2df03a..0a9f339ddbac 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff @@ -58,11 +58,10 @@ _7 = const 1_usize; _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); - StorageLive(_10); StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb3, otherwise: bb2]; + _8 = UbCheck(LanguageUb); + switchInt(move _8) -> [0: bb4, otherwise: bb2]; } bb1: { @@ -71,16 +70,21 @@ } bb2: { + StorageLive(_10); _10 = const {0x1 as *mut ()}; _9 = NonNull::::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb3, unwind unreachable]; } bb3: { + StorageDead(_10); + goto -> bb4; + } + + bb4: { StorageDead(_8); _11 = const {0x1 as *const [bool; 0]}; _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; StorageDead(_11); - StorageDead(_10); StorageDead(_6); _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; StorageDead(_5); diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff index d6b5984b81dd..bbc791148af1 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff @@ -58,11 +58,10 @@ _7 = const 1_usize; _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); - StorageLive(_10); StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb4, otherwise: bb3]; + _8 = UbCheck(LanguageUb); + switchInt(move _8) -> [0: bb5, otherwise: bb3]; } bb1: { @@ -75,16 +74,21 @@ } bb3: { + StorageLive(_10); _10 = const {0x1 as *mut ()}; _9 = NonNull::::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb4, unwind unreachable]; } bb4: { + StorageDead(_10); + goto -> bb5; + } + + bb5: { StorageDead(_8); _11 = const {0x1 as *const [bool; 0]}; _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; StorageDead(_11); - StorageDead(_10); StorageDead(_6); _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; StorageDead(_5); diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff index c7445aaee6c5..3a11677f6f03 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff @@ -60,11 +60,10 @@ + _7 = const 1_usize; + _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); - StorageLive(_10); StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb3, otherwise: bb2]; + _8 = UbCheck(LanguageUb); + switchInt(move _8) -> [0: bb4, otherwise: bb2]; } bb1: { @@ -73,6 +72,7 @@ } bb2: { + StorageLive(_10); - _10 = _6 as *mut () (PtrToPtr); - _9 = NonNull::::new_unchecked::precondition_check(move _10) -> [return: bb3, unwind unreachable]; + _10 = const {0x1 as *mut ()}; @@ -80,13 +80,17 @@ } bb3: { + StorageDead(_10); + goto -> bb4; + } + + bb4: { StorageDead(_8); - _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); - _5 = NonNull::<[bool; 0]> { pointer: _11 }; + _11 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; StorageDead(_11); - StorageDead(_10); StorageDead(_6); - _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; + _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff index b8e961bc0875..9e7e08866b9d 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff @@ -60,11 +60,10 @@ + _7 = const 1_usize; + _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); - StorageLive(_10); StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb4, otherwise: bb3]; + _8 = UbCheck(LanguageUb); + switchInt(move _8) -> [0: bb5, otherwise: bb3]; } bb1: { @@ -77,6 +76,7 @@ } bb3: { + StorageLive(_10); - _10 = _6 as *mut () (PtrToPtr); - _9 = NonNull::::new_unchecked::precondition_check(move _10) -> [return: bb4, unwind unreachable]; + _10 = const {0x1 as *mut ()}; @@ -84,13 +84,17 @@ } bb4: { + StorageDead(_10); + goto -> bb5; + } + + bb5: { StorageDead(_8); - _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); - _5 = NonNull::<[bool; 0]> { pointer: _11 }; + _11 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; StorageDead(_11); - StorageDead(_10); StorageDead(_6); - _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; + _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff index 9678db90d059..beadfbc07b6e 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff @@ -60,11 +60,10 @@ + _7 = const 1_usize; + _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); - StorageLive(_10); StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb3, otherwise: bb2]; + _8 = UbCheck(LanguageUb); + switchInt(move _8) -> [0: bb4, otherwise: bb2]; } bb1: { @@ -73,6 +72,7 @@ } bb2: { + StorageLive(_10); - _10 = _6 as *mut () (PtrToPtr); - _9 = NonNull::::new_unchecked::precondition_check(move _10) -> [return: bb3, unwind unreachable]; + _10 = const {0x1 as *mut ()}; @@ -80,13 +80,17 @@ } bb3: { + StorageDead(_10); + goto -> bb4; + } + + bb4: { StorageDead(_8); - _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); - _5 = NonNull::<[bool; 0]> { pointer: _11 }; + _11 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; StorageDead(_11); - StorageDead(_10); StorageDead(_6); - _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; + _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff index 8aa6c9c23e9a..9ea86956b833 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff @@ -60,11 +60,10 @@ + _7 = const 1_usize; + _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); - StorageLive(_10); StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb4, otherwise: bb3]; + _8 = UbCheck(LanguageUb); + switchInt(move _8) -> [0: bb5, otherwise: bb3]; } bb1: { @@ -77,6 +76,7 @@ } bb3: { + StorageLive(_10); - _10 = _6 as *mut () (PtrToPtr); - _9 = NonNull::::new_unchecked::precondition_check(move _10) -> [return: bb4, unwind unreachable]; + _10 = const {0x1 as *mut ()}; @@ -84,13 +84,17 @@ } bb4: { + StorageDead(_10); + goto -> bb5; + } + + bb5: { StorageDead(_8); - _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); - _5 = NonNull::<[bool; 0]> { pointer: _11 }; + _11 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; StorageDead(_11); - StorageDead(_10); StorageDead(_6); - _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; + _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff index 52688c2e8672..6f7853a3e97f 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff @@ -37,7 +37,7 @@ + + bb2: { + StorageLive(_4); -+ _4 = cfg!(debug_assertions); ++ _4 = UbCheck(LanguageUb); + assume(_4); + _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; + } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff index fd83f1cb3315..cac06d4af088 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff @@ -41,7 +41,7 @@ - resume; + bb2: { + StorageLive(_4); -+ _4 = cfg!(debug_assertions); ++ _4 = UbCheck(LanguageUb); + assume(_4); + _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; + } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir index 8ec65935c661..5c6116501548 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir @@ -27,7 +27,7 @@ fn unwrap_unchecked(_1: Option) -> T { bb1: { StorageLive(_3); - _3 = cfg!(debug_assertions); + _3 = UbCheck(LanguageUb); assume(_3); _4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable]; } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir index 8ec65935c661..5c6116501548 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir @@ -27,7 +27,7 @@ fn unwrap_unchecked(_1: Option) -> T { bb1: { StorageLive(_3); - _3 = cfg!(debug_assertions); + _3 = UbCheck(LanguageUb); assume(_3); _4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable]; } diff --git a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir index 7c038b0ee88e..0597e453e221 100644 --- a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir @@ -23,7 +23,7 @@ fn ub_if_b(_1: Thing) -> Thing { bb2: { StorageLive(_3); - _3 = cfg!(debug_assertions); + _3 = UbCheck(LanguageUb); assume(_3); _4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable]; } From a047284b5ada26a1bcb8424dec5516695d7b7343 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 14 Jan 2024 22:26:47 +0100 Subject: [PATCH 32/47] Make `MaybeInfiniteInt::plus_one/minus_one` fallible --- .../rustc_pattern_analysis/src/constructor.rs | 35 ++++++++++--------- compiler/rustc_pattern_analysis/src/rustc.rs | 6 ++-- .../rustc_pattern_analysis/src/usefulness.rs | 2 +- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 767227619b04..69e294e47a56 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -192,7 +192,7 @@ impl fmt::Display for RangeEnd { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum MaybeInfiniteInt { NegInfinity, - /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite`. + /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite_{int,uint}`. #[non_exhaustive] Finite(u128), /// The integer after `u128::MAX`. We need it to represent `x..=u128::MAX` as an exclusive range. @@ -229,25 +229,22 @@ impl MaybeInfiniteInt { } /// Note: this will not turn a finite value into an infinite one or vice-versa. - pub fn minus_one(self) -> Self { + pub fn minus_one(self) -> Option { match self { - Finite(n) => match n.checked_sub(1) { - Some(m) => Finite(m), - None => panic!("Called `MaybeInfiniteInt::minus_one` on 0"), - }, - JustAfterMax => Finite(u128::MAX), - x => x, + Finite(n) => n.checked_sub(1).map(Finite), + JustAfterMax => Some(Finite(u128::MAX)), + x => Some(x), } } /// Note: this will not turn a finite value into an infinite one or vice-versa. - pub fn plus_one(self) -> Self { + pub fn plus_one(self) -> Option { match self { Finite(n) => match n.checked_add(1) { - Some(m) => Finite(m), - None => JustAfterMax, + Some(m) => Some(Finite(m)), + None => Some(JustAfterMax), }, - JustAfterMax => panic!("Called `MaybeInfiniteInt::plus_one` on u128::MAX+1"), - x => x, + JustAfterMax => None, + x => Some(x), } } } @@ -268,18 +265,24 @@ impl IntRange { pub fn is_singleton(&self) -> bool { // Since `lo` and `hi` can't be the same `Infinity` and `plus_one` never changes from finite // to infinite, this correctly only detects ranges that contain exacly one `Finite(x)`. - self.lo.plus_one() == self.hi + self.lo.plus_one() == Some(self.hi) } + /// Construct a singleton range. + /// `x` must be a `Finite(_)` value. #[inline] pub fn from_singleton(x: MaybeInfiniteInt) -> IntRange { - IntRange { lo: x, hi: x.plus_one() } + // `unwrap()` is ok on a finite value + IntRange { lo: x, hi: x.plus_one().unwrap() } } + /// Construct a range with these boundaries. + /// `lo` must not be `PosInfinity` or `JustAfterMax`. `hi` must not be `NegInfinity`. + /// If `end` is `Included`, `hi` must also not be `JustAfterMax`. #[inline] pub fn from_range(lo: MaybeInfiniteInt, mut hi: MaybeInfiniteInt, end: RangeEnd) -> IntRange { if end == RangeEnd::Included { - hi = hi.plus_one(); + hi = hi.plus_one().unwrap(); } if lo >= hi { // This should have been caught earlier by E0030. diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 5f5bfa7154a7..a36c6dda433d 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -718,12 +718,12 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { let value = mir::Const::from_ty_const(c, cx.tcx); lo = PatRangeBoundary::Finite(value); } - let hi = if matches!(range.hi, Finite(0)) { + let hi = if let Some(hi) = range.hi.minus_one() { + hi + } else { // The range encodes `..ty::MIN`, so we can't convert it to an inclusive range. end = rustc_hir::RangeEnd::Excluded; range.hi - } else { - range.hi.minus_one() }; let hi = cx.hoist_pat_range_bdy(hi, ty); PatKind::Range(Box::new(PatRange { lo, hi, end, ty: ty.inner() })) diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index c518844cc5e5..5c4b3752a510 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -1526,7 +1526,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( } } suffixes.push((child_row_id, pat)) - } else if this_range.hi == overlap.plus_one() { + } else if Some(this_range.hi) == overlap.plus_one() { // `this_range` looks like `this_range.lo..=overlap`; it overlaps with any // ranges that look like `overlap..=hi`. if !suffixes.is_empty() { From 77f679430c0453a97fb00b974128b701b853b5ae Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 14 Jan 2024 22:25:00 +0100 Subject: [PATCH 33/47] Declare new lint --- compiler/rustc_lint_defs/src/builtin.rs | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 94f8bbe2437f..f2560b35aa2e 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -67,6 +67,7 @@ declare_lint_pass! { MISSING_FRAGMENT_SPECIFIER, MUST_NOT_SUSPEND, NAMED_ARGUMENTS_USED_POSITIONALLY, + NON_CONTIGUOUS_RANGE_ENDPOINTS, NON_EXHAUSTIVE_OMITTED_PATTERNS, ORDER_DEPENDENT_TRAIT_OBJECTS, OVERLAPPING_RANGE_ENDPOINTS, @@ -812,6 +813,36 @@ declare_lint! { "detects range patterns with overlapping endpoints" } +declare_lint! { + /// The `non_contiguous_range_endpoints` lint detects likely off-by-one errors when using + /// exclusive [range patterns]. + /// + /// [range patterns]: https://doc.rust-lang.org/nightly/reference/patterns.html#range-patterns + /// + /// ### Example + /// + /// ```rust + /// # #![feature(exclusive_range_pattern)] + /// let x = 123u32; + /// match x { + /// 0..100 => { println!("small"); } + /// 101..1000 => { println!("large"); } + /// _ => { println!("larger"); } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is likely a mistake to have range patterns in a match expression that miss out a single + /// number. Check that the beginning and end values are what you expect, and keep in mind that + /// with `..=` the right bound is inclusive, and with `..` it is exclusive. + pub NON_CONTIGUOUS_RANGE_ENDPOINTS, + Warn, + "detects off-by-one errors with exclusive range patterns" +} + declare_lint! { /// The `bindings_with_variant_name` lint detects pattern bindings with /// the same name as one of the matched variants. From f783043ebf49e3a51daf271b335a74320155f965 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 17 Jan 2024 14:50:49 +0100 Subject: [PATCH 34/47] Allow lint where we don't care --- .../tests/ui/manual_range_patterns.fixed | 1 + .../clippy/tests/ui/manual_range_patterns.rs | 1 + .../tests/ui/manual_range_patterns.stderr | 38 ++--- .../half-open-range-pats-exhaustive-fail.rs | 1 + ...alf-open-range-pats-exhaustive-fail.stderr | 136 +++++++++--------- .../range_pat_interactions0.rs | 1 + tests/ui/match/issue-18060.rs | 1 + tests/ui/mir/mir_match_test.rs | 1 + .../integer-ranges/exhaustiveness.rs | 1 + .../integer-ranges/exhaustiveness.stderr | 24 ++-- .../usefulness/integer-ranges/reachability.rs | 1 + .../integer-ranges/reachability.stderr | 52 +++---- 12 files changed, 133 insertions(+), 125 deletions(-) diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.fixed b/src/tools/clippy/tests/ui/manual_range_patterns.fixed index b348d7071f6e..e9f6fbcc3fc8 100644 --- a/src/tools/clippy/tests/ui/manual_range_patterns.fixed +++ b/src/tools/clippy/tests/ui/manual_range_patterns.fixed @@ -1,4 +1,5 @@ #![allow(unused)] +#![allow(non_contiguous_range_endpoints)] #![warn(clippy::manual_range_patterns)] #![feature(exclusive_range_pattern)] diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.rs b/src/tools/clippy/tests/ui/manual_range_patterns.rs index a0750f54b73f..d525aaa24ad1 100644 --- a/src/tools/clippy/tests/ui/manual_range_patterns.rs +++ b/src/tools/clippy/tests/ui/manual_range_patterns.rs @@ -1,4 +1,5 @@ #![allow(unused)] +#![allow(non_contiguous_range_endpoints)] #![warn(clippy::manual_range_patterns)] #![feature(exclusive_range_pattern)] diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.stderr b/src/tools/clippy/tests/ui/manual_range_patterns.stderr index 7c19fdd475f1..af9256aeea39 100644 --- a/src/tools/clippy/tests/ui/manual_range_patterns.stderr +++ b/src/tools/clippy/tests/ui/manual_range_patterns.stderr @@ -1,5 +1,5 @@ error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:8:25 + --> tests/ui/manual_range_patterns.rs:9:25 | LL | let _ = matches!(f, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` @@ -8,109 +8,109 @@ LL | let _ = matches!(f, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10); = help: to override `-D warnings` add `#[allow(clippy::manual_range_patterns)]` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:9:25 + --> tests/ui/manual_range_patterns.rs:10:25 | LL | let _ = matches!(f, 4 | 2 | 3 | 1 | 5 | 6 | 9 | 7 | 8 | 10); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:16:25 + --> tests/ui/manual_range_patterns.rs:17:25 | LL | let _ = matches!(f, 1 | (2..=4)); | ^^^^^^^^^^^ help: try: `1..=4` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:17:25 + --> tests/ui/manual_range_patterns.rs:18:25 | LL | let _ = matches!(f, 1 | (2..4)); | ^^^^^^^^^^ help: try: `1..4` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:18:25 + --> tests/ui/manual_range_patterns.rs:19:25 | LL | let _ = matches!(f, (1..=10) | (2..=13) | (14..=48324728) | 48324729); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=48324729` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:19:25 + --> tests/ui/manual_range_patterns.rs:20:25 | LL | let _ = matches!(f, 0 | (1..=10) | 48324730 | (2..=13) | (14..=48324728) | 48324729); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0..=48324730` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:20:25 + --> tests/ui/manual_range_patterns.rs:21:25 | LL | let _ = matches!(f, 0..=1 | 0..=2 | 0..=3); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `0..=3` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:23:9 + --> tests/ui/manual_range_patterns.rs:24:9 | LL | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 => true, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:26:25 + --> tests/ui/manual_range_patterns.rs:27:25 | LL | let _ = matches!(f, -1 | -5 | 3 | -2 | -4 | -3 | 0 | 1 | 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-5..=3` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:28:25 + --> tests/ui/manual_range_patterns.rs:29:25 | LL | let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_001); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-1_000_001..=1_000_001` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:31:17 + --> tests/ui/manual_range_patterns.rs:32:17 | LL | matches!(f, 0x00 | 0x01 | 0x02 | 0x03); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x03` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:32:17 + --> tests/ui/manual_range_patterns.rs:33:17 | LL | matches!(f, 0x00..=0x05 | 0x06 | 0x07); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x07` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:33:17 + --> tests/ui/manual_range_patterns.rs:34:17 | LL | matches!(f, -0x09 | -0x08 | -0x07..=0x00); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-0x09..=0x00` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:35:17 + --> tests/ui/manual_range_patterns.rs:36:17 | LL | matches!(f, 0..5 | 5); | ^^^^^^^^ help: try: `0..=5` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:36:17 + --> tests/ui/manual_range_patterns.rs:37:17 | LL | matches!(f, 0 | 1..5); | ^^^^^^^^ help: try: `0..5` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:38:17 + --> tests/ui/manual_range_patterns.rs:39:17 | LL | matches!(f, 0..=5 | 6..10); | ^^^^^^^^^^^^^ help: try: `0..10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:39:17 + --> tests/ui/manual_range_patterns.rs:40:17 | LL | matches!(f, 0..5 | 5..=10); | ^^^^^^^^^^^^^ help: try: `0..=10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:40:17 + --> tests/ui/manual_range_patterns.rs:41:17 | LL | matches!(f, 5..=10 | 0..5); | ^^^^^^^^^^^^^ help: try: `0..=10` error: this OR pattern can be rewritten using a range - --> tests/ui/manual_range_patterns.rs:44:26 + --> tests/ui/manual_range_patterns.rs:45:26 | LL | matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.rs b/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.rs index 33b99259dfec..9d067229b6de 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.rs +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.rs @@ -1,6 +1,7 @@ // Test various non-exhaustive matches for `X..`, `..=X` and `..X` ranges. #![feature(exclusive_range_pattern)] +#![allow(non_contiguous_range_endpoints)] fn main() {} diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.stderr b/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.stderr index 1e68235303b2..6b20a820b730 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.stderr +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:14:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:15:8 | LL | m!(0f32, f32::NEG_INFINITY..); | ^^^^ pattern `_` not covered @@ -11,7 +11,7 @@ LL | match $s { $($t)+ => {}, _ => todo!() } | ++++++++++++++ error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:15:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:16:8 | LL | m!(0f32, ..f32::INFINITY); | ^^^^ pattern `_` not covered @@ -23,7 +23,7 @@ LL | match $s { $($t)+ => {}, _ => todo!() } | ++++++++++++++ error[E0004]: non-exhaustive patterns: `'\u{10ffff}'` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:24:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:25:8 | LL | m!('a', ..core::char::MAX); | ^^^ pattern `'\u{10ffff}'` not covered @@ -35,7 +35,7 @@ LL | match $s { $($t)+ => {}, '\u{10ffff}' => todo!() } | +++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `'\u{10fffe}'..='\u{10ffff}'` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:25:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:26:8 | LL | m!('a', ..ALMOST_MAX); | ^^^ pattern `'\u{10fffe}'..='\u{10ffff}'` not covered @@ -47,7 +47,7 @@ LL | match $s { $($t)+ => {}, '\u{10fffe}'..='\u{10ffff}' => todo!() } | ++++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `'\0'` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:26:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:27:8 | LL | m!('a', ALMOST_MIN..); | ^^^ pattern `'\0'` not covered @@ -59,7 +59,7 @@ LL | match $s { $($t)+ => {}, '\0' => todo!() } | +++++++++++++++++ error[E0004]: non-exhaustive patterns: `'\u{10ffff}'` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:27:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:28:8 | LL | m!('a', ..=ALMOST_MAX); | ^^^ pattern `'\u{10ffff}'` not covered @@ -71,7 +71,7 @@ LL | match $s { $($t)+ => {}, '\u{10ffff}' => todo!() } | +++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `'b'` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:28:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:29:8 | LL | m!('a', ..=VAL | VAL_2..); | ^^^ pattern `'b'` not covered @@ -83,7 +83,7 @@ LL | match $s { $($t)+ => {}, 'b' => todo!() } | ++++++++++++++++ error[E0004]: non-exhaustive patterns: `'b'` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:29:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:30:8 | LL | m!('a', ..VAL_1 | VAL_2..); | ^^^ pattern `'b'` not covered @@ -95,7 +95,7 @@ LL | match $s { $($t)+ => {}, 'b' => todo!() } | ++++++++++++++++ error[E0004]: non-exhaustive patterns: `u8::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:39:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:40:12 | LL | m!(0, ..u8::MAX); | ^ pattern `u8::MAX` not covered @@ -107,7 +107,7 @@ LL | match $s { $($t)+ => {}, u8::MAX => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `254_u8..=u8::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:40:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:41:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `254_u8..=u8::MAX` not covered @@ -119,7 +119,7 @@ LL | match $s { $($t)+ => {}, 254_u8..=u8::MAX => todo!() } | +++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `0_u8` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:41:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:42:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `0_u8` not covered @@ -131,7 +131,7 @@ LL | match $s { $($t)+ => {}, 0_u8 => todo!() } | +++++++++++++++++ error[E0004]: non-exhaustive patterns: `u8::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:42:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:43:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `u8::MAX` not covered @@ -143,7 +143,7 @@ LL | match $s { $($t)+ => {}, u8::MAX => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u8` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:43:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:44:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_u8` not covered @@ -155,7 +155,7 @@ LL | match $s { $($t)+ => {}, 43_u8 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u8` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:44:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:45:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_u8` not covered @@ -167,7 +167,7 @@ LL | match $s { $($t)+ => {}, 43_u8 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u16::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:52:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:53:12 | LL | m!(0, ..u16::MAX); | ^ pattern `u16::MAX` not covered @@ -179,7 +179,7 @@ LL | match $s { $($t)+ => {}, u16::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `65534_u16..=u16::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:53:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:54:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `65534_u16..=u16::MAX` not covered @@ -191,7 +191,7 @@ LL | match $s { $($t)+ => {}, 65534_u16..=u16::MAX => todo!() } | +++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `0_u16` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:54:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:55:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `0_u16` not covered @@ -203,7 +203,7 @@ LL | match $s { $($t)+ => {}, 0_u16 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u16::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:55:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:56:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `u16::MAX` not covered @@ -215,7 +215,7 @@ LL | match $s { $($t)+ => {}, u16::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u16` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:56:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:57:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_u16` not covered @@ -227,7 +227,7 @@ LL | match $s { $($t)+ => {}, 43_u16 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u16` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:57:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:58:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_u16` not covered @@ -239,7 +239,7 @@ LL | match $s { $($t)+ => {}, 43_u16 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u32::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:65:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:66:12 | LL | m!(0, ..u32::MAX); | ^ pattern `u32::MAX` not covered @@ -251,7 +251,7 @@ LL | match $s { $($t)+ => {}, u32::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `4294967294_u32..=u32::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:66:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:67:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `4294967294_u32..=u32::MAX` not covered @@ -263,7 +263,7 @@ LL | match $s { $($t)+ => {}, 4294967294_u32..=u32::MAX => todo!() } | ++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `0_u32` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:67:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:68:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `0_u32` not covered @@ -275,7 +275,7 @@ LL | match $s { $($t)+ => {}, 0_u32 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u32::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:68:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:69:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `u32::MAX` not covered @@ -287,7 +287,7 @@ LL | match $s { $($t)+ => {}, u32::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u32` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:69:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:70:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_u32` not covered @@ -299,7 +299,7 @@ LL | match $s { $($t)+ => {}, 43_u32 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u32` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:70:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:71:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_u32` not covered @@ -311,7 +311,7 @@ LL | match $s { $($t)+ => {}, 43_u32 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u64::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:78:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:79:12 | LL | m!(0, ..u64::MAX); | ^ pattern `u64::MAX` not covered @@ -323,7 +323,7 @@ LL | match $s { $($t)+ => {}, u64::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `18446744073709551614_u64..=u64::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:79:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:80:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `18446744073709551614_u64..=u64::MAX` not covered @@ -335,7 +335,7 @@ LL | match $s { $($t)+ => {}, 18446744073709551614_u64..=u64::MAX => tod | ++++++++++++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `0_u64` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:80:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:81:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `0_u64` not covered @@ -347,7 +347,7 @@ LL | match $s { $($t)+ => {}, 0_u64 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u64::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:81:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:82:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `u64::MAX` not covered @@ -359,7 +359,7 @@ LL | match $s { $($t)+ => {}, u64::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u64` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:82:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:83:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_u64` not covered @@ -371,7 +371,7 @@ LL | match $s { $($t)+ => {}, 43_u64 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u64` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:83:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:84:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_u64` not covered @@ -383,7 +383,7 @@ LL | match $s { $($t)+ => {}, 43_u64 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u128::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:91:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:92:12 | LL | m!(0, ..u128::MAX); | ^ pattern `u128::MAX` not covered @@ -395,7 +395,7 @@ LL | match $s { $($t)+ => {}, u128::MAX => todo!() } | ++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `340282366920938463463374607431768211454_u128..=u128::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:92:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:93:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `340282366920938463463374607431768211454_u128..=u128::MAX` not covered @@ -407,7 +407,7 @@ LL | match $s { $($t)+ => {}, 340282366920938463463374607431768211454_u1 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `0_u128` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:93:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:94:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `0_u128` not covered @@ -419,7 +419,7 @@ LL | match $s { $($t)+ => {}, 0_u128 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u128::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:94:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:95:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `u128::MAX` not covered @@ -431,7 +431,7 @@ LL | match $s { $($t)+ => {}, u128::MAX => todo!() } | ++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u128` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:95:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:96:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_u128` not covered @@ -443,7 +443,7 @@ LL | match $s { $($t)+ => {}, 43_u128 => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u128` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:96:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:97:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_u128` not covered @@ -455,7 +455,7 @@ LL | match $s { $($t)+ => {}, 43_u128 => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i8::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:107:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:108:12 | LL | m!(0, ..i8::MAX); | ^ pattern `i8::MAX` not covered @@ -467,7 +467,7 @@ LL | match $s { $($t)+ => {}, i8::MAX => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `126_i8..=i8::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:108:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:109:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `126_i8..=i8::MAX` not covered @@ -479,7 +479,7 @@ LL | match $s { $($t)+ => {}, 126_i8..=i8::MAX => todo!() } | +++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i8::MIN` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:109:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:110:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `i8::MIN` not covered @@ -491,7 +491,7 @@ LL | match $s { $($t)+ => {}, i8::MIN => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i8::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:110:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:111:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `i8::MAX` not covered @@ -503,7 +503,7 @@ LL | match $s { $($t)+ => {}, i8::MAX => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i8` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:111:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:112:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_i8` not covered @@ -515,7 +515,7 @@ LL | match $s { $($t)+ => {}, 43_i8 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i8` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:112:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:113:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_i8` not covered @@ -527,7 +527,7 @@ LL | match $s { $($t)+ => {}, 43_i8 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i16::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:120:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:121:12 | LL | m!(0, ..i16::MAX); | ^ pattern `i16::MAX` not covered @@ -539,7 +539,7 @@ LL | match $s { $($t)+ => {}, i16::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `32766_i16..=i16::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:121:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:122:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `32766_i16..=i16::MAX` not covered @@ -551,7 +551,7 @@ LL | match $s { $($t)+ => {}, 32766_i16..=i16::MAX => todo!() } | +++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i16::MIN` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:122:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:123:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `i16::MIN` not covered @@ -563,7 +563,7 @@ LL | match $s { $($t)+ => {}, i16::MIN => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i16::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:123:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:124:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `i16::MAX` not covered @@ -575,7 +575,7 @@ LL | match $s { $($t)+ => {}, i16::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i16` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:124:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:125:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_i16` not covered @@ -587,7 +587,7 @@ LL | match $s { $($t)+ => {}, 43_i16 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i16` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:125:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:126:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_i16` not covered @@ -599,7 +599,7 @@ LL | match $s { $($t)+ => {}, 43_i16 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i32::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:133:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:134:12 | LL | m!(0, ..i32::MAX); | ^ pattern `i32::MAX` not covered @@ -611,7 +611,7 @@ LL | match $s { $($t)+ => {}, i32::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `2147483646_i32..=i32::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:134:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:135:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `2147483646_i32..=i32::MAX` not covered @@ -623,7 +623,7 @@ LL | match $s { $($t)+ => {}, 2147483646_i32..=i32::MAX => todo!() } | ++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i32::MIN` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:135:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:136:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `i32::MIN` not covered @@ -635,7 +635,7 @@ LL | match $s { $($t)+ => {}, i32::MIN => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i32::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:136:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:137:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `i32::MAX` not covered @@ -647,7 +647,7 @@ LL | match $s { $($t)+ => {}, i32::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i32` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:137:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:138:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_i32` not covered @@ -659,7 +659,7 @@ LL | match $s { $($t)+ => {}, 43_i32 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i32` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:138:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:139:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_i32` not covered @@ -671,7 +671,7 @@ LL | match $s { $($t)+ => {}, 43_i32 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i64::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:146:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:147:12 | LL | m!(0, ..i64::MAX); | ^ pattern `i64::MAX` not covered @@ -683,7 +683,7 @@ LL | match $s { $($t)+ => {}, i64::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `9223372036854775806_i64..=i64::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:147:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:148:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `9223372036854775806_i64..=i64::MAX` not covered @@ -695,7 +695,7 @@ LL | match $s { $($t)+ => {}, 9223372036854775806_i64..=i64::MAX => todo | +++++++++++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i64::MIN` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:148:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:149:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `i64::MIN` not covered @@ -707,7 +707,7 @@ LL | match $s { $($t)+ => {}, i64::MIN => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i64::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:149:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:150:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `i64::MAX` not covered @@ -719,7 +719,7 @@ LL | match $s { $($t)+ => {}, i64::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i64` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:150:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:151:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_i64` not covered @@ -731,7 +731,7 @@ LL | match $s { $($t)+ => {}, 43_i64 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i64` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:151:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:152:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_i64` not covered @@ -743,7 +743,7 @@ LL | match $s { $($t)+ => {}, 43_i64 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i128::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:159:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:160:12 | LL | m!(0, ..i128::MAX); | ^ pattern `i128::MAX` not covered @@ -755,7 +755,7 @@ LL | match $s { $($t)+ => {}, i128::MAX => todo!() } | ++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `170141183460469231731687303715884105726_i128..=i128::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:160:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:161:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `170141183460469231731687303715884105726_i128..=i128::MAX` not covered @@ -767,7 +767,7 @@ LL | match $s { $($t)+ => {}, 170141183460469231731687303715884105726_i1 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i128::MIN` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:161:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:162:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `i128::MIN` not covered @@ -779,7 +779,7 @@ LL | match $s { $($t)+ => {}, i128::MIN => todo!() } | ++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i128::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:162:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:163:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `i128::MAX` not covered @@ -791,7 +791,7 @@ LL | match $s { $($t)+ => {}, i128::MAX => todo!() } | ++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i128` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:163:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:164:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_i128` not covered @@ -803,7 +803,7 @@ LL | match $s { $($t)+ => {}, 43_i128 => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i128` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:164:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:165:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_i128` not covered diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions0.rs b/tests/ui/half-open-range-patterns/range_pat_interactions0.rs index 7a82f9ce89a8..53b6b89ed166 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions0.rs +++ b/tests/ui/half-open-range-patterns/range_pat_interactions0.rs @@ -1,4 +1,5 @@ //@ run-pass +#![allow(non_contiguous_range_endpoints)] #![feature(exclusive_range_pattern)] #![feature(inline_const_pat)] diff --git a/tests/ui/match/issue-18060.rs b/tests/ui/match/issue-18060.rs index daba393a3fa3..9d480ba6b8bb 100644 --- a/tests/ui/match/issue-18060.rs +++ b/tests/ui/match/issue-18060.rs @@ -1,6 +1,7 @@ //@ run-pass // Regression test for #18060: match arms were matching in the wrong order. +#[allow(non_contiguous_range_endpoints)] fn main() { assert_eq!(2, match (1, 3) { (0, 2..=5) => 1, (1, 3) => 2, (_, 2..=5) => 3, (_, _) => 4 }); assert_eq!(2, match (1, 3) { (1, 3) => 2, (_, 2..=5) => 3, (_, _) => 4 }); diff --git a/tests/ui/mir/mir_match_test.rs b/tests/ui/mir/mir_match_test.rs index 925e1e6ab3d4..0da8522f2181 100644 --- a/tests/ui/mir/mir_match_test.rs +++ b/tests/ui/mir/mir_match_test.rs @@ -1,5 +1,6 @@ #![feature(exclusive_range_pattern)] #![allow(overlapping_range_endpoints)] +#![allow(non_contiguous_range_endpoints)] //@ run-pass diff --git a/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs b/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs index 0f5f49c4ca47..07156d9a08a3 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs +++ b/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs @@ -1,5 +1,6 @@ #![feature(exclusive_range_pattern)] #![allow(overlapping_range_endpoints)] +#![allow(non_contiguous_range_endpoints)] #![deny(unreachable_patterns)] macro_rules! m { diff --git a/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr b/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr index b585de206291..90d0fd7483a9 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr +++ b/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `u8::MAX` not covered - --> $DIR/exhaustiveness.rs:47:8 + --> $DIR/exhaustiveness.rs:48:8 | LL | m!(0u8, 0..255); | ^^^ pattern `u8::MAX` not covered @@ -11,7 +11,7 @@ LL | match $s { $($t)+ => {}, u8::MAX => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u8::MAX` not covered - --> $DIR/exhaustiveness.rs:48:8 + --> $DIR/exhaustiveness.rs:49:8 | LL | m!(0u8, 0..=254); | ^^^ pattern `u8::MAX` not covered @@ -23,7 +23,7 @@ LL | match $s { $($t)+ => {}, u8::MAX => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `0_u8` not covered - --> $DIR/exhaustiveness.rs:49:8 + --> $DIR/exhaustiveness.rs:50:8 | LL | m!(0u8, 1..=255); | ^^^ pattern `0_u8` not covered @@ -35,7 +35,7 @@ LL | match $s { $($t)+ => {}, 0_u8 => todo!() } | +++++++++++++++++ error[E0004]: non-exhaustive patterns: `42_u8` not covered - --> $DIR/exhaustiveness.rs:50:8 + --> $DIR/exhaustiveness.rs:51:8 | LL | m!(0u8, 0..42 | 43..=255); | ^^^ pattern `42_u8` not covered @@ -47,7 +47,7 @@ LL | match $s { $($t)+ => {}, 42_u8 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i8::MAX` not covered - --> $DIR/exhaustiveness.rs:51:8 + --> $DIR/exhaustiveness.rs:52:8 | LL | m!(0i8, -128..127); | ^^^ pattern `i8::MAX` not covered @@ -59,7 +59,7 @@ LL | match $s { $($t)+ => {}, i8::MAX => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i8::MAX` not covered - --> $DIR/exhaustiveness.rs:52:8 + --> $DIR/exhaustiveness.rs:53:8 | LL | m!(0i8, -128..=126); | ^^^ pattern `i8::MAX` not covered @@ -71,7 +71,7 @@ LL | match $s { $($t)+ => {}, i8::MAX => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i8::MIN` not covered - --> $DIR/exhaustiveness.rs:53:8 + --> $DIR/exhaustiveness.rs:54:8 | LL | m!(0i8, -127..=127); | ^^^ pattern `i8::MIN` not covered @@ -83,7 +83,7 @@ LL | match $s { $($t)+ => {}, i8::MIN => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `0_i8` not covered - --> $DIR/exhaustiveness.rs:54:11 + --> $DIR/exhaustiveness.rs:55:11 | LL | match 0i8 { | ^^^ pattern `0_i8` not covered @@ -96,7 +96,7 @@ LL + 0_i8 => todo!() | error[E0004]: non-exhaustive patterns: `u128::MAX` not covered - --> $DIR/exhaustiveness.rs:59:8 + --> $DIR/exhaustiveness.rs:60:8 | LL | m!(0u128, 0..=ALMOST_MAX); | ^^^^^ pattern `u128::MAX` not covered @@ -108,7 +108,7 @@ LL | match $s { $($t)+ => {}, u128::MAX => todo!() } | ++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `5_u128..=u128::MAX` not covered - --> $DIR/exhaustiveness.rs:60:8 + --> $DIR/exhaustiveness.rs:61:8 | LL | m!(0u128, 0..=4); | ^^^^^ pattern `5_u128..=u128::MAX` not covered @@ -120,7 +120,7 @@ LL | match $s { $($t)+ => {}, 5_u128..=u128::MAX => todo!() } | +++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `0_u128` not covered - --> $DIR/exhaustiveness.rs:61:8 + --> $DIR/exhaustiveness.rs:62:8 | LL | m!(0u128, 1..=u128::MAX); | ^^^^^ pattern `0_u128` not covered @@ -132,7 +132,7 @@ LL | match $s { $($t)+ => {}, 0_u128 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `(126_u8..=127_u8, false)` not covered - --> $DIR/exhaustiveness.rs:69:11 + --> $DIR/exhaustiveness.rs:70:11 | LL | match (0u8, true) { | ^^^^^^^^^^^ pattern `(126_u8..=127_u8, false)` not covered diff --git a/tests/ui/pattern/usefulness/integer-ranges/reachability.rs b/tests/ui/pattern/usefulness/integer-ranges/reachability.rs index 247fdd91572c..13b84e2c44bc 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/reachability.rs +++ b/tests/ui/pattern/usefulness/integer-ranges/reachability.rs @@ -1,5 +1,6 @@ #![feature(exclusive_range_pattern)] #![allow(overlapping_range_endpoints)] +#![allow(non_contiguous_range_endpoints)] #![deny(unreachable_patterns)] macro_rules! m { diff --git a/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr b/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr index c5b028d2038c..0f52dfd83b9c 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr +++ b/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr @@ -1,137 +1,137 @@ error: unreachable pattern - --> $DIR/reachability.rs:18:17 + --> $DIR/reachability.rs:19:17 | LL | m!(0u8, 42, 42); | ^^ | note: the lint level is defined here - --> $DIR/reachability.rs:3:9 + --> $DIR/reachability.rs:4:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:22:22 + --> $DIR/reachability.rs:23:22 | LL | m!(0u8, 20..=30, 20); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:23:22 + --> $DIR/reachability.rs:24:22 | LL | m!(0u8, 20..=30, 21); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:24:22 + --> $DIR/reachability.rs:25:22 | LL | m!(0u8, 20..=30, 25); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:25:22 + --> $DIR/reachability.rs:26:22 | LL | m!(0u8, 20..=30, 29); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:26:22 + --> $DIR/reachability.rs:27:22 | LL | m!(0u8, 20..=30, 30); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:29:21 + --> $DIR/reachability.rs:30:21 | LL | m!(0u8, 20..30, 20); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:30:21 + --> $DIR/reachability.rs:31:21 | LL | m!(0u8, 20..30, 21); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:31:21 + --> $DIR/reachability.rs:32:21 | LL | m!(0u8, 20..30, 25); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:32:21 + --> $DIR/reachability.rs:33:21 | LL | m!(0u8, 20..30, 29); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:36:22 + --> $DIR/reachability.rs:37:22 | LL | m!(0u8, 20..=30, 20..=30); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:37:22 + --> $DIR/reachability.rs:38:22 | LL | m!(0u8, 20.. 30, 20.. 30); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:38:22 + --> $DIR/reachability.rs:39:22 | LL | m!(0u8, 20..=30, 20.. 30); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:40:22 + --> $DIR/reachability.rs:41:22 | LL | m!(0u8, 20..=30, 21..=30); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:41:22 + --> $DIR/reachability.rs:42:22 | LL | m!(0u8, 20..=30, 20..=29); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:43:24 + --> $DIR/reachability.rs:44:24 | LL | m!('a', 'A'..='z', 'a'..='z'); | ^^^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:50:9 + --> $DIR/reachability.rs:51:9 | LL | 5..=8 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:56:9 + --> $DIR/reachability.rs:57:9 | LL | 5..15 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:63:9 + --> $DIR/reachability.rs:64:9 | LL | 5..25 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:71:9 + --> $DIR/reachability.rs:72:9 | LL | 5..25 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:77:9 + --> $DIR/reachability.rs:78:9 | LL | 5..15 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:84:9 + --> $DIR/reachability.rs:85:9 | LL | _ => {}, | - matches any value @@ -139,19 +139,19 @@ LL | '\u{D7FF}'..='\u{E000}' => {}, | ^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern error: unreachable pattern - --> $DIR/reachability.rs:89:9 + --> $DIR/reachability.rs:90:9 | LL | '\u{D7FF}'..='\u{E000}' => {}, | ^^^^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:105:9 + --> $DIR/reachability.rs:106:9 | LL | &FOO => {} | ^^^^ error: unreachable pattern - --> $DIR/reachability.rs:106:9 + --> $DIR/reachability.rs:107:9 | LL | BAR => {} | ^^^ From 8ac9a04257f73d9861625816d4c741096dd69c67 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 14 Jan 2024 22:24:10 +0100 Subject: [PATCH 35/47] Lint small gaps between ranges --- compiler/rustc_pattern_analysis/messages.ftl | 8 + compiler/rustc_pattern_analysis/src/errors.rs | 51 +++++ compiler/rustc_pattern_analysis/src/lib.rs | 23 ++- compiler/rustc_pattern_analysis/src/rustc.rs | 66 +++++- .../rustc_pattern_analysis/src/usefulness.rs | 46 ++++- .../integer-ranges/gap_between_ranges.rs | 117 +++++++++++ .../integer-ranges/gap_between_ranges.stderr | 193 ++++++++++++++++++ 7 files changed, 492 insertions(+), 12 deletions(-) create mode 100644 tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.rs create mode 100644 tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.stderr diff --git a/compiler/rustc_pattern_analysis/messages.ftl b/compiler/rustc_pattern_analysis/messages.ftl index 827928f97d7c..41a1d958f109 100644 --- a/compiler/rustc_pattern_analysis/messages.ftl +++ b/compiler/rustc_pattern_analysis/messages.ftl @@ -1,3 +1,11 @@ +pattern_analysis_excluside_range_missing_gap = multiple ranges are one apart + .label = this range doesn't match `{$gap}` because `..` is an exclusive range + .suggestion = use an inclusive range instead + +pattern_analysis_excluside_range_missing_max = exclusive range missing `{$max}` + .label = this range doesn't match `{$max}` because `..` is an exclusive range + .suggestion = use an inclusive range instead + pattern_analysis_non_exhaustive_omitted_pattern = some variants are not matched explicitly .help = ensure that all variants are matched explicitly by adding the suggested match arms .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index d0f56f0268df..e471b8abd735 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -76,6 +76,57 @@ impl<'tcx> AddToDiagnostic for Overlap<'tcx> { } } +#[derive(LintDiagnostic)] +#[diag(pattern_analysis_excluside_range_missing_max)] +pub struct ExclusiveRangeMissingMax<'tcx> { + #[label] + #[suggestion(code = "{suggestion}", applicability = "maybe-incorrect")] + /// This is an exclusive range that looks like `lo..max` (i.e. doesn't match `max`). + pub first_range: Span, + /// Suggest `lo..=max` instead. + pub suggestion: String, + pub max: Pat<'tcx>, +} + +#[derive(LintDiagnostic)] +#[diag(pattern_analysis_excluside_range_missing_gap)] +pub struct ExclusiveRangeMissingGap<'tcx> { + #[label] + #[suggestion(code = "{suggestion}", applicability = "maybe-incorrect")] + /// This is an exclusive range that looks like `lo..gap` (i.e. doesn't match `gap`). + pub first_range: Span, + pub gap: Pat<'tcx>, + /// Suggest `lo..=gap` instead. + pub suggestion: String, + #[subdiagnostic] + /// All these ranges skipped over `gap` which we think is probably a mistake. + pub gap_with: Vec>, +} + +pub struct GappedRange<'tcx> { + pub span: Span, + pub gap: Pat<'tcx>, + pub first_range: Pat<'tcx>, +} + +impl<'tcx> AddToDiagnostic for GappedRange<'tcx> { + fn add_to_diagnostic_with>( + self, + diag: &mut Diag<'_, G>, + _: F, + ) { + let GappedRange { span, gap, first_range } = self; + + // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]` + // does not support `#[subdiagnostic(eager)]`... + let message = format!( + "this could appear to continue range `{first_range}`, but `{gap}` isn't matched by \ + either of them" + ); + diag.span_label(span, message); + } +} + #[derive(LintDiagnostic)] #[diag(pattern_analysis_non_exhaustive_omitted_pattern)] #[help] diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 4b0955699fc3..f632eaf7ea4f 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -70,14 +70,8 @@ use rustc_middle::ty::Ty; use rustc_span::ErrorGuaranteed; use crate::constructor::{Constructor, ConstructorSet, IntRange}; -#[cfg(feature = "rustc")] -use crate::lints::lint_nonexhaustive_missing_variants; use crate::pat::DeconstructedPat; use crate::pat_column::PatternColumn; -#[cfg(feature = "rustc")] -use crate::rustc::RustcMatchCheckCtxt; -#[cfg(feature = "rustc")] -use crate::usefulness::{compute_match_usefulness, ValidityConstraint}; pub trait Captures<'a> {} impl<'a, T: ?Sized> Captures<'a> for T {} @@ -145,6 +139,18 @@ pub trait TypeCx: Sized + fmt::Debug { /// The maximum pattern complexity limit was reached. fn complexity_exceeded(&self) -> Result<(), Self::Error>; + + /// Lint that there is a gap `gap` between `pat` and all of `gapped_with` such that the gap is + /// not matched by another range. If `gapped_with` is empty, then `gap` is `T::MAX`. We only + /// detect singleton gaps. + /// The default implementation does nothing. + fn lint_non_contiguous_range_endpoints( + &self, + _pat: &DeconstructedPat, + _gap: IntRange, + _gapped_with: &[&DeconstructedPat], + ) { + } } /// The arm of a match expression. @@ -167,11 +173,14 @@ impl<'p, Cx: TypeCx> Copy for MatchArm<'p, Cx> {} /// useful, and runs some lints. #[cfg(feature = "rustc")] pub fn analyze_match<'p, 'tcx>( - tycx: &RustcMatchCheckCtxt<'p, 'tcx>, + tycx: &rustc::RustcMatchCheckCtxt<'p, 'tcx>, arms: &[rustc::MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, pattern_complexity_limit: Option, ) -> Result, ErrorGuaranteed> { + use lints::lint_nonexhaustive_missing_variants; + use usefulness::{compute_match_usefulness, ValidityConstraint}; + let scrut_ty = tycx.reveal_opaque_ty(scrut_ty); let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee); let report = diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index a36c6dda433d..0085f0ab6566 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -8,7 +8,7 @@ use rustc_index::{Idx, IndexVec}; use rustc_middle::middle::stability::EvalResult; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::{self, Const}; -use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; +use rustc_middle::thir::{self, FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, FieldDef, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef}; use rustc_session::lint; @@ -900,6 +900,70 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { let span = self.whole_match_span.unwrap_or(self.scrut_span); Err(self.tcx.dcx().span_err(span, "reached pattern complexity limit")) } + + fn lint_non_contiguous_range_endpoints( + &self, + pat: &crate::pat::DeconstructedPat, + gap: IntRange, + gapped_with: &[&crate::pat::DeconstructedPat], + ) { + let Some(&thir_pat) = pat.data() else { return }; + let thir::PatKind::Range(range) = &thir_pat.kind else { return }; + // Only lint when the left range is an exclusive range. + if range.end != rustc_hir::RangeEnd::Excluded { + return; + } + // `pat` is an exclusive range like `lo..gap`. `gapped_with` contains ranges that start with + // `gap+1`. + let suggested_range: thir::Pat<'_> = { + // Suggest `lo..=gap` instead. + let mut suggested_range = thir_pat.clone(); + let thir::PatKind::Range(range) = &mut suggested_range.kind else { unreachable!() }; + range.end = rustc_hir::RangeEnd::Included; + suggested_range + }; + let gap_as_pat = self.hoist_pat_range(&gap, *pat.ty()); + if gapped_with.is_empty() { + // If `gapped_with` is empty, `gap == T::MAX`. + self.tcx.emit_node_span_lint( + lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS, + self.match_lint_level, + thir_pat.span, + errors::ExclusiveRangeMissingMax { + // Point at this range. + first_range: thir_pat.span, + // That's the gap that isn't covered. + max: gap_as_pat.clone(), + // Suggest `lo..=max` instead. + suggestion: suggested_range.to_string(), + }, + ); + } else { + self.tcx.emit_node_span_lint( + lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS, + self.match_lint_level, + thir_pat.span, + errors::ExclusiveRangeMissingGap { + // Point at this range. + first_range: thir_pat.span, + // That's the gap that isn't covered. + gap: gap_as_pat.clone(), + // Suggest `lo..=gap` instead. + suggestion: suggested_range.to_string(), + // All these ranges skipped over `gap` which we think is probably a + // mistake. + gap_with: gapped_with + .iter() + .map(|pat| errors::GappedRange { + span: pat.data().unwrap().span, + gap: gap_as_pat.clone(), + first_range: thir_pat.clone(), + }) + .collect(), + }, + ); + } + } } /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 5c4b3752a510..a067bf1f0c23 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -1489,7 +1489,7 @@ impl WitnessMatrix { /// We can however get false negatives because exhaustiveness does not explore all cases. See the /// section on relevancy at the top of the file. fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( - mcx: &mut UsefulnessCtxt<'_, Cx>, + cx: &Cx, overlap_range: IntRange, matrix: &Matrix<'p, Cx>, specialized_matrix: &Matrix<'p, Cx>, @@ -1522,7 +1522,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( .map(|&(_, pat)| pat) .collect(); if !overlaps_with.is_empty() { - mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with); + cx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with); } } suffixes.push((child_row_id, pat)) @@ -1538,7 +1538,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( .map(|&(_, pat)| pat) .collect(); if !overlaps_with.is_empty() { - mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with); + cx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with); } } prefixes.push((child_row_id, pat)) @@ -1546,6 +1546,33 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( } } +/// Collect ranges that have a singleton gap between them. +fn collect_non_contiguous_range_endpoints<'p, Cx: TypeCx>( + cx: &Cx, + gap_range: &IntRange, + matrix: &Matrix<'p, Cx>, +) { + let gap = gap_range.lo; + // Ranges that look like `lo..gap`. + let mut onebefore: SmallVec<[_; 1]> = Default::default(); + // Ranges that start on `gap+1` or singletons `gap+1`. + let mut oneafter: SmallVec<[_; 1]> = Default::default(); + // Look through the column for ranges near the gap. + for pat in matrix.heads() { + let PatOrWild::Pat(pat) = pat else { continue }; + let Constructor::IntRange(this_range) = pat.ctor() else { continue }; + if gap == this_range.hi { + onebefore.push(pat) + } else if gap.plus_one() == Some(this_range.lo) { + oneafter.push(pat) + } + } + + for pat_before in onebefore { + cx.lint_non_contiguous_range_endpoints(pat_before, *gap_range, oneafter.as_slice()); + } +} + /// The core of the algorithm. /// /// This recursively computes witnesses of the non-exhaustiveness of `matrix` (if any). Also tracks @@ -1626,13 +1653,24 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( && spec_matrix.rows.len() >= 2 && spec_matrix.rows.iter().any(|row| !row.intersects.is_empty()) { - collect_overlapping_range_endpoints(mcx, overlap_range, matrix, &spec_matrix); + collect_overlapping_range_endpoints(mcx.tycx, overlap_range, matrix, &spec_matrix); } } matrix.unspecialize(spec_matrix); } + // Detect singleton gaps between ranges. + if missing_ctors.iter().any(|c| matches!(c, Constructor::IntRange(..))) { + for missing in &missing_ctors { + if let Constructor::IntRange(gap) = missing { + if gap.is_singleton() { + collect_non_contiguous_range_endpoints(mcx.tycx, gap, matrix); + } + } + } + } + // Record usefulness in the patterns. for row in matrix.rows() { if row.useful { diff --git a/tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.rs b/tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.rs new file mode 100644 index 000000000000..78849d7e1432 --- /dev/null +++ b/tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.rs @@ -0,0 +1,117 @@ +#![feature(exclusive_range_pattern)] +#![deny(non_contiguous_range_endpoints)] + +macro_rules! m { + ($s:expr, $t1:pat, $t2:pat) => { + match $s { + $t1 => {} + $t2 => {} + _ => {} + } + }; +} + +fn main() { + match 0u8 { + 20..30 => {} //~ ERROR multiple ranges are one apart + 31..=40 => {} + _ => {} + } + match 0u8 { + 20..30 => {} //~ ERROR multiple ranges are one apart + 31 => {} + _ => {} + } + + m!(0u8, 20..30, 31..=40); //~ ERROR multiple ranges are one apart + m!(0u8, 31..=40, 20..30); //~ ERROR multiple ranges are one apart + m!(0u8, 20..30, 29..=40); //~ WARN multiple patterns overlap on their endpoints + m!(0u8, 20..30, 30..=40); + m!(0u8, 20..30, 31..=40); //~ ERROR multiple ranges are one apart + m!(0u8, 20..30, 32..=40); + m!(0u8, 20..30, 31..=32); //~ ERROR multiple ranges are one apart + // Don't lint two singletons. + m!(0u8, 30, 32); + // Don't lint on the equivalent inclusive range + m!(0u8, 20..=29, 31..=40); + // Don't lint if the lower one is a singleton. + m!(0u8, 30, 32..=40); + + // Catch the `lo..MAX` case. + match 0u8 { + 0..255 => {} //~ ERROR exclusive range missing `u8::MAX` + _ => {} + } + // Except for `usize`, since `0..=usize::MAX` isn't exhaustive either. + match 0usize { + 0..usize::MAX => {} + _ => {} + } + + // Don't lint if the gap is caught by another range. + match 0u8 { + 0..10 => {} + 11..20 => {} + 10 => {} + _ => {} + } + match 0u8 { + 0..10 => {} + 11..20 => {} + 5..15 => {} + _ => {} + } + match 0u8 { + 0..255 => {} + 255 => {} + } + + // Test multiple at once + match 0u8 { + 0..10 => {} //~ ERROR multiple ranges are one apart + 11..20 => {} + 11..30 => {} + _ => {} + } + match 0u8 { + 0..10 => {} //~ ERROR multiple ranges are one apart + 11..20 => {} //~ ERROR multiple ranges are one apart + 21..30 => {} + _ => {} + } + match 0u8 { + 00..20 => {} //~ ERROR multiple ranges are one apart + 10..20 => {} //~ ERROR multiple ranges are one apart + 21..30 => {} + 21..40 => {} + _ => {} + } + + // Test nested + match (0u8, true) { + (0..10, true) => {} //~ ERROR multiple ranges are one apart + (11..20, true) => {} + _ => {} + } + match (true, 0u8) { + (true, 0..10) => {} //~ ERROR multiple ranges are one apart + (true, 11..20) => {} + _ => {} + } + // Asymmetry due to how exhaustiveness is computed. + match (0u8, true) { + (0..10, true) => {} //~ ERROR multiple ranges are one apart + (11..20, false) => {} + _ => {} + } + match (true, 0u8) { + (true, 0..10) => {} + (false, 11..20) => {} + _ => {} + } + match Some(0u8) { + Some(0..10) => {} //~ ERROR multiple ranges are one apart + Some(11..20) => {} + _ => {} + } +} diff --git a/tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.stderr b/tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.stderr new file mode 100644 index 000000000000..e5c2d788ba4e --- /dev/null +++ b/tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.stderr @@ -0,0 +1,193 @@ +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:16:9 + | +LL | 20..30 => {} + | ^^^^^^ + | | + | this range doesn't match `30_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `20_u8..=30_u8` +LL | 31..=40 => {} + | ------- this could appear to continue range `20_u8..30_u8`, but `30_u8` isn't matched by either of them + | +note: the lint level is defined here + --> $DIR/gap_between_ranges.rs:2:9 + | +LL | #![deny(non_contiguous_range_endpoints)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:21:9 + | +LL | 20..30 => {} + | ^^^^^^ + | | + | this range doesn't match `30_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `20_u8..=30_u8` +LL | 31 => {} + | -- this could appear to continue range `20_u8..30_u8`, but `30_u8` isn't matched by either of them + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:26:13 + | +LL | m!(0u8, 20..30, 31..=40); + | ^^^^^^ ------- this could appear to continue range `20_u8..30_u8`, but `30_u8` isn't matched by either of them + | | + | this range doesn't match `30_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `20_u8..=30_u8` + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:27:22 + | +LL | m!(0u8, 31..=40, 20..30); + | ------- ^^^^^^ + | | | + | | this range doesn't match `30_u8` because `..` is an exclusive range + | | help: use an inclusive range instead: `20_u8..=30_u8` + | this could appear to continue range `20_u8..30_u8`, but `30_u8` isn't matched by either of them + +warning: multiple patterns overlap on their endpoints + --> $DIR/gap_between_ranges.rs:28:21 + | +LL | m!(0u8, 20..30, 29..=40); + | ------ ^^^^^^^ ... with this range + | | + | this range overlaps on `29_u8`... + | + = note: you likely meant to write mutually exclusive ranges + = note: `#[warn(overlapping_range_endpoints)]` on by default + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:30:13 + | +LL | m!(0u8, 20..30, 31..=40); + | ^^^^^^ ------- this could appear to continue range `20_u8..30_u8`, but `30_u8` isn't matched by either of them + | | + | this range doesn't match `30_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `20_u8..=30_u8` + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:32:13 + | +LL | m!(0u8, 20..30, 31..=32); + | ^^^^^^ ------- this could appear to continue range `20_u8..30_u8`, but `30_u8` isn't matched by either of them + | | + | this range doesn't match `30_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `20_u8..=30_u8` + +error: exclusive range missing `u8::MAX` + --> $DIR/gap_between_ranges.rs:42:9 + | +LL | 0..255 => {} + | ^^^^^^ + | | + | this range doesn't match `u8::MAX` because `..` is an exclusive range + | help: use an inclusive range instead: `0_u8..=u8::MAX` + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:71:9 + | +LL | 0..10 => {} + | ^^^^^ + | | + | this range doesn't match `10_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `0_u8..=10_u8` +LL | 11..20 => {} + | ------ this could appear to continue range `0_u8..10_u8`, but `10_u8` isn't matched by either of them +LL | 11..30 => {} + | ------ this could appear to continue range `0_u8..10_u8`, but `10_u8` isn't matched by either of them + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:77:9 + | +LL | 0..10 => {} + | ^^^^^ + | | + | this range doesn't match `10_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `0_u8..=10_u8` +LL | 11..20 => {} + | ------ this could appear to continue range `0_u8..10_u8`, but `10_u8` isn't matched by either of them + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:78:9 + | +LL | 11..20 => {} + | ^^^^^^ + | | + | this range doesn't match `20_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `11_u8..=20_u8` +LL | 21..30 => {} + | ------ this could appear to continue range `11_u8..20_u8`, but `20_u8` isn't matched by either of them + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:83:9 + | +LL | 00..20 => {} + | ^^^^^^ + | | + | this range doesn't match `20_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `0_u8..=20_u8` +LL | 10..20 => {} +LL | 21..30 => {} + | ------ this could appear to continue range `0_u8..20_u8`, but `20_u8` isn't matched by either of them +LL | 21..40 => {} + | ------ this could appear to continue range `0_u8..20_u8`, but `20_u8` isn't matched by either of them + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:84:9 + | +LL | 10..20 => {} + | ^^^^^^ + | | + | this range doesn't match `20_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `10_u8..=20_u8` +LL | 21..30 => {} + | ------ this could appear to continue range `10_u8..20_u8`, but `20_u8` isn't matched by either of them +LL | 21..40 => {} + | ------ this could appear to continue range `10_u8..20_u8`, but `20_u8` isn't matched by either of them + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:92:10 + | +LL | (0..10, true) => {} + | ^^^^^ + | | + | this range doesn't match `10_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `0_u8..=10_u8` +LL | (11..20, true) => {} + | ------ this could appear to continue range `0_u8..10_u8`, but `10_u8` isn't matched by either of them + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:97:16 + | +LL | (true, 0..10) => {} + | ^^^^^ + | | + | this range doesn't match `10_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `0_u8..=10_u8` +LL | (true, 11..20) => {} + | ------ this could appear to continue range `0_u8..10_u8`, but `10_u8` isn't matched by either of them + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:103:10 + | +LL | (0..10, true) => {} + | ^^^^^ + | | + | this range doesn't match `10_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `0_u8..=10_u8` +LL | (11..20, false) => {} + | ------ this could appear to continue range `0_u8..10_u8`, but `10_u8` isn't matched by either of them + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:113:14 + | +LL | Some(0..10) => {} + | ^^^^^ + | | + | this range doesn't match `10_u8` because `..` is an exclusive range + | help: use an inclusive range instead: `0_u8..=10_u8` +LL | Some(11..20) => {} + | ------ this could appear to continue range `0_u8..10_u8`, but `10_u8` isn't matched by either of them + +error: aborting due to 16 previous errors; 1 warning emitted + From 564837e23d9293edddb3d7a997667a6c6796fb2b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 8 Mar 2024 23:20:29 -0500 Subject: [PATCH 36/47] Fix typo in `VisitorResult` --- compiler/rustc_ast_ir/src/visit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_ast_ir/src/visit.rs b/compiler/rustc_ast_ir/src/visit.rs index dec9f7a47d09..f6d6bf3a3e30 100644 --- a/compiler/rustc_ast_ir/src/visit.rs +++ b/compiler/rustc_ast_ir/src/visit.rs @@ -14,7 +14,7 @@ impl VisitorResult for () { type Residual = !; #[cfg(not(feature = "nightly"))] - type Residual = core::ops::Infallible; + type Residual = core::convert::Infallible; fn output() -> Self {} fn from_residual(_: Self::Residual) -> Self {} From 7c13421dc0498e05624946327b1a022f2fa7b294 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Sat, 9 Mar 2024 00:06:48 +0300 Subject: [PATCH 37/47] fix incorrect path resolution in tidy Previously, reading the current path from the environment led to failure when invoking x from outside the source root. This change fixes this issue by passing the already resolved root path into `ui_tests::check`. Signed-off-by: onur-ozkan --- src/tools/tidy/src/main.rs | 2 +- src/tools/tidy/src/ui_tests.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 4b98c91319da..2bce246c3082 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -102,7 +102,7 @@ fn main() { check!(tests_placement, &root_path); check!(tests_revision_unpaired_stdout_stderr, &tests_path); check!(debug_artifacts, &tests_path); - check!(ui_tests, &tests_path, bless); + check!(ui_tests, &root_path, bless); check!(mir_opt_tests, &tests_path, bless); check!(rustdoc_gui_tests, &tests_path); check!(rustdoc_css_themes, &librustdoc_path); diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 39b14adfea41..c946554b98ff 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -99,7 +99,8 @@ fn check_entries(tests_path: &Path, bad: &mut bool) { } } -pub fn check(path: &Path, bless: bool, bad: &mut bool) { +pub fn check(root_path: &Path, bless: bool, bad: &mut bool) { + let path = &root_path.join("tests"); check_entries(&path, bad); // the list of files in ui tests that are allowed to start with `issue-XXXX` @@ -193,7 +194,7 @@ pub fn check(path: &Path, bless: bool, bad: &mut bool) { */ [ "#; - let tidy_src = std::env::current_dir().unwrap().join("src/tools/tidy/src"); + let tidy_src = root_path.join("src/tools/tidy/src"); // instead of overwriting the file, recreate it and use an "atomic rename" // so we don't bork things on panic or a contributor using Ctrl+C let blessed_issues_path = tidy_src.join("issues_blessed.txt"); From b1f4657fe99b63d2ec9fa647701a7975339ac457 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Sat, 9 Mar 2024 12:01:19 +0530 Subject: [PATCH 38/47] Add missing regression tests for a couple of issues --- .../ice-unsized-struct-arg-issue-121612.rs | 13 +++++ ...ice-unsized-struct-arg-issue-121612.stderr | 57 +++++++++++++++++++ .../ice-unsized-struct-arg-issue2-121424.rs | 9 +++ ...ce-unsized-struct-arg-issue2-121424.stderr | 30 ++++++++++ 4 files changed, 109 insertions(+) create mode 100644 tests/ui/trait-bounds/ice-unsized-struct-arg-issue-121612.rs create mode 100644 tests/ui/trait-bounds/ice-unsized-struct-arg-issue-121612.stderr create mode 100644 tests/ui/trait-bounds/ice-unsized-struct-arg-issue2-121424.rs create mode 100644 tests/ui/trait-bounds/ice-unsized-struct-arg-issue2-121424.stderr diff --git a/tests/ui/trait-bounds/ice-unsized-struct-arg-issue-121612.rs b/tests/ui/trait-bounds/ice-unsized-struct-arg-issue-121612.rs new file mode 100644 index 000000000000..5ca4f49c3bac --- /dev/null +++ b/tests/ui/trait-bounds/ice-unsized-struct-arg-issue-121612.rs @@ -0,0 +1,13 @@ +// Regression test for issue #121612 + +trait Trait {} +impl Trait for bool {} +struct MySlice Idx>(bool, T); +//~^ ERROR cannot find type `Idx` in this scope +//~| ERROR cannot find type `Idx` in this scope +type MySliceBool = MySlice<[bool]>; +const MYSLICE_GOOD: &MySliceBool = &MySlice(true, [false]); +//~^ ERROR the size for values of type `[bool]` cannot be known at compilation time +//~| ERROR the size for values of type `[bool]` cannot be known at compilation time + +fn main() {} diff --git a/tests/ui/trait-bounds/ice-unsized-struct-arg-issue-121612.stderr b/tests/ui/trait-bounds/ice-unsized-struct-arg-issue-121612.stderr new file mode 100644 index 000000000000..0be80e9479f0 --- /dev/null +++ b/tests/ui/trait-bounds/ice-unsized-struct-arg-issue-121612.stderr @@ -0,0 +1,57 @@ +error[E0412]: cannot find type `Idx` in this scope + --> $DIR/ice-unsized-struct-arg-issue-121612.rs:5:30 + | +LL | struct MySlice Idx>(bool, T); + | ^^^ not found in this scope + +error[E0412]: cannot find type `Idx` in this scope + --> $DIR/ice-unsized-struct-arg-issue-121612.rs:5:38 + | +LL | struct MySlice Idx>(bool, T); + | ^^^ not found in this scope + +error[E0277]: the size for values of type `[bool]` cannot be known at compilation time + --> $DIR/ice-unsized-struct-arg-issue-121612.rs:9:22 + | +LL | const MYSLICE_GOOD: &MySliceBool = &MySlice(true, [false]); + | ^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[bool]` +note: required by an implicit `Sized` bound in `MySlice` + --> $DIR/ice-unsized-struct-arg-issue-121612.rs:5:16 + | +LL | struct MySlice Idx>(bool, T); + | ^ required by the implicit `Sized` requirement on this type parameter in `MySlice` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/ice-unsized-struct-arg-issue-121612.rs:5:16 + | +LL | struct MySlice Idx>(bool, T); + | ^ - ...if indirection were used here: `Box` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `[bool]` cannot be known at compilation time + --> $DIR/ice-unsized-struct-arg-issue-121612.rs:9:22 + | +LL | const MYSLICE_GOOD: &MySliceBool = &MySlice(true, [false]); + | ^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[bool]` +note: required by an implicit `Sized` bound in `MySlice` + --> $DIR/ice-unsized-struct-arg-issue-121612.rs:5:16 + | +LL | struct MySlice Idx>(bool, T); + | ^ required by the implicit `Sized` requirement on this type parameter in `MySlice` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/ice-unsized-struct-arg-issue-121612.rs:5:16 + | +LL | struct MySlice Idx>(bool, T); + | ^ - ...if indirection were used here: `Box` + | | + | this could be changed to `T: ?Sized`... + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0412. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/trait-bounds/ice-unsized-struct-arg-issue2-121424.rs b/tests/ui/trait-bounds/ice-unsized-struct-arg-issue2-121424.rs new file mode 100644 index 000000000000..1a9266e05def --- /dev/null +++ b/tests/ui/trait-bounds/ice-unsized-struct-arg-issue2-121424.rs @@ -0,0 +1,9 @@ +// Regression test for issue #121424 +#[repr(C)] +struct MySlice(bool, T); +type MySliceBool = MySlice<[bool]>; +const MYSLICE_GOOD: &MySliceBool = &MySlice(true, [false]); +//~^ ERROR the trait bound `[bool]: Copy` is not satisfied +//~| ERROR the trait bound `[bool]: Copy` is not satisfied + +fn main() {} diff --git a/tests/ui/trait-bounds/ice-unsized-struct-arg-issue2-121424.stderr b/tests/ui/trait-bounds/ice-unsized-struct-arg-issue2-121424.stderr new file mode 100644 index 000000000000..3738bbfb8de9 --- /dev/null +++ b/tests/ui/trait-bounds/ice-unsized-struct-arg-issue2-121424.stderr @@ -0,0 +1,30 @@ +error[E0277]: the trait bound `[bool]: Copy` is not satisfied + --> $DIR/ice-unsized-struct-arg-issue2-121424.rs:5:22 + | +LL | const MYSLICE_GOOD: &MySliceBool = &MySlice(true, [false]); + | ^^^^^^^^^^^ the trait `Copy` is not implemented for `[bool]` + | + = help: the trait `Copy` is implemented for `[T; N]` +note: required by a bound in `MySlice` + --> $DIR/ice-unsized-struct-arg-issue2-121424.rs:3:19 + | +LL | struct MySlice(bool, T); + | ^^^^ required by this bound in `MySlice` + +error[E0277]: the trait bound `[bool]: Copy` is not satisfied + --> $DIR/ice-unsized-struct-arg-issue2-121424.rs:5:22 + | +LL | const MYSLICE_GOOD: &MySliceBool = &MySlice(true, [false]); + | ^^^^^^^^^^^ the trait `Copy` is not implemented for `[bool]` + | + = help: the trait `Copy` is implemented for `[T; N]` +note: required by a bound in `MySlice` + --> $DIR/ice-unsized-struct-arg-issue2-121424.rs:3:19 + | +LL | struct MySlice(bool, T); + | ^^^^ required by this bound in `MySlice` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From 6aff1ca68ca056a483cbd935b6e202298bef0080 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 9 Mar 2024 11:11:10 +0100 Subject: [PATCH 39/47] fix warning when building libcore for Miri --- library/core/src/intrinsics.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index aff1c589e628..9f4a1ed6870b 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -3120,6 +3120,7 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize const fn compiletime(_ptr: *const (), _align: usize) {} + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block // SAFETY: the extra behavior at runtime is for UB checks only. unsafe { const_eval_select((ptr, align), compiletime, runtime); From 1082c36a4c734972a13734c7e9565a5f2b9fc79a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 9 Mar 2024 11:54:27 +0100 Subject: [PATCH 40/47] fn is_align_to: move some comments closer to the cast they refer to --- library/core/src/ptr/const_ptr.rs | 6 +++--- library/core/src/ptr/mut_ptr.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 0c69bf2aef9c..90dbc798fcf1 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1628,12 +1628,12 @@ impl *const T { #[inline] const fn const_impl(ptr: *const (), align: usize) -> bool { // We can't use the address of `self` in a `const fn`, so we use `align_offset` instead. - // The cast to `()` is used to - // 1. deal with fat pointers; and - // 2. ensure that `align_offset` doesn't actually try to compute an offset. ptr.align_offset(align) == 0 } + // The cast to `()` is used to + // 1. deal with fat pointers; and + // 2. ensure that `align_offset` (in `const_impl`) doesn't actually try to compute an offset. #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block // SAFETY: The two versions are equivalent at runtime. unsafe { diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 5ce3b1f298cb..6a9033a144de 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1900,12 +1900,12 @@ impl *mut T { #[inline] const fn const_impl(ptr: *mut (), align: usize) -> bool { // We can't use the address of `self` in a `const fn`, so we use `align_offset` instead. - // The cast to `()` is used to - // 1. deal with fat pointers; and - // 2. ensure that `align_offset` doesn't actually try to compute an offset. ptr.align_offset(align) == 0 } + // The cast to `()` is used to + // 1. deal with fat pointers; and + // 2. ensure that `align_offset` (in `const_impl`) doesn't actually try to compute an offset. #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block // SAFETY: The two versions are equivalent at runtime. unsafe { From e632e3f9a501daefde13a264e1d0aff54a417510 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 9 Mar 2024 13:05:13 +0100 Subject: [PATCH 41/47] miri: do not apply aliasing restrictions to Box with custom allocator --- .../src/interpret/validity.rs | 8 +- .../rustc_const_eval/src/interpret/visitor.rs | 10 +- .../rustc_hir_analysis/src/check/intrinsic.rs | 4 + compiler/rustc_middle/src/ty/sty.rs | 7 +- compiler/rustc_span/src/symbol.rs | 1 + library/alloc/src/boxed.rs | 24 ++-- library/core/src/intrinsics.rs | 13 ++ src/tools/miri/src/borrow_tracker/mod.rs | 15 ++- .../src/borrow_tracker/stacked_borrows/mod.rs | 34 ++++- .../src/borrow_tracker/tree_borrows/mod.rs | 33 +++-- src/tools/miri/src/shims/intrinsics/mod.rs | 13 ++ .../tests/pass/box-custom-alloc-aliasing.rs | 123 ++++++++++++++++++ 12 files changed, 250 insertions(+), 35 deletions(-) create mode 100644 src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index ff1cb43db864..c568f9acfd38 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -17,8 +17,8 @@ use rustc_middle::mir::interpret::{ ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance, ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*, }; -use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::{ Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange, @@ -783,7 +783,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } #[inline] - fn visit_box(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { + fn visit_box( + &mut self, + _box_ty: Ty<'tcx>, + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx> { self.check_safe_pointer(op, PointerKind::Box)?; Ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index b200ecbf73af..0e824f3f592d 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -3,7 +3,7 @@ use rustc_index::IndexVec; use rustc_middle::mir::interpret::InterpResult; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_target::abi::FieldIdx; use rustc_target::abi::{FieldsShape, VariantIdx, Variants}; @@ -47,10 +47,10 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { Ok(()) } /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into. - /// The type of `v` will be a raw pointer, but this is a field of `Box` and the - /// pointee type is the actual `T`. + /// The type of `v` will be a raw pointer to `T`, but this is a field of `Box` and the + /// pointee type is the actual `T`. `box_ty` provides the full type of the `Box` itself. #[inline(always)] - fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx> { + fn visit_box(&mut self, _box_ty: Ty<'tcx>, _v: &Self::V) -> InterpResult<'tcx> { Ok(()) } @@ -144,7 +144,7 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { assert_eq!(nonnull_ptr.layout().fields.count(), 1); let raw_ptr = self.ecx().project_field(&nonnull_ptr, 0)?; // the actual raw ptr // ... whose only field finally is a raw ptr we can dereference. - self.visit_box(&raw_ptr)?; + self.visit_box(ty, &raw_ptr)?; // The second `Box` field is the allocator, which we recursively check for validity // like in regular structs. diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 95c51cc0486f..5a74bdc8a84c 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -657,6 +657,10 @@ pub fn check_intrinsic_type( sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)), sym::simd_shuffle_generic => (2, 1, vec![param(0), param(0)], param(1)), + sym::retag_box_to_raw => { + (2, 0, vec![Ty::new_mut_ptr(tcx, param(0))], Ty::new_mut_ptr(tcx, param(0))) + } + other => { tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other }); return; diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 427c0f04bd11..39b5d3b6ea76 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2008,13 +2008,10 @@ impl<'tcx> Ty<'tcx> { // Single-argument Box is always global. (for "minicore" tests) return true; }; - if let Some(alloc_adt) = alloc.expect_ty().ty_adt_def() { + alloc.expect_ty().ty_adt_def().is_some_and(|alloc_adt| { let global_alloc = tcx.require_lang_item(LangItem::GlobalAlloc, None); alloc_adt.did() == global_alloc - } else { - // Allocator is not an ADT... - false - } + }) } _ => false, } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9dadd4712477..283c685eaa30 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1450,6 +1450,7 @@ symbols! { residual, result, resume, + retag_box_to_raw, return_position_impl_trait_in_trait, return_type_notation, rhs, diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 2736e5ee6c58..304f607000b0 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -155,6 +155,7 @@ use core::error::Error; use core::fmt; use core::future::Future; use core::hash::{Hash, Hasher}; +use core::intrinsics::retag_box_to_raw; use core::iter::FusedIterator; use core::marker::Tuple; use core::marker::Unsize; @@ -1110,8 +1111,16 @@ impl Box { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) { - let (leaked, alloc) = Box::into_unique(b); - (leaked.as_ptr(), alloc) + // This is the transition point from `Box` to raw pointers. For Stacked Borrows, these casts + // are relevant -- if this is a global allocator Box and we just get the pointer from `b.0`, + // it will have `Unique` permission, which is not what we want from a raw pointer. We could + // fix that by going through `&mut`, but then if this is *not* a global allocator Box, we'd + // be adding uniqueness assertions that we do not want. So for Miri's sake we pass this + // pointer through an intrinsic for box-to-raw casts, which can do the right thing wrt the + // aliasing model. + let b = mem::ManuallyDrop::new(b); + let alloc = unsafe { ptr::read(&b.1) }; + (unsafe { retag_box_to_raw::(b.0.as_ptr()) }, alloc) } #[unstable( @@ -1122,13 +1131,8 @@ impl Box { #[inline] #[doc(hidden)] pub fn into_unique(b: Self) -> (Unique, A) { - // Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a - // raw pointer for the type system. Turning it directly into a raw pointer would not be - // recognized as "releasing" the unique pointer to permit aliased raw accesses, - // so all raw pointer methods have to go through `Box::leak`. Turning *that* to a raw pointer - // behaves correctly. - let alloc = unsafe { ptr::read(&b.1) }; - (Unique::from(Box::leak(b)), alloc) + let (ptr, alloc) = Box::into_raw_with_allocator(b); + unsafe { (Unique::from(&mut *ptr), alloc) } } /// Returns a reference to the underlying allocator. @@ -1184,7 +1188,7 @@ impl Box { where A: 'a, { - unsafe { &mut *mem::ManuallyDrop::new(b).0.as_ptr() } + unsafe { &mut *Box::into_raw(b) } } /// Converts a `Box` into a `Pin>`. If `T` does not implement [`Unpin`], then diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index aff1c589e628..f19f5f322aa5 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2695,6 +2695,19 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize { unreachable!() } +/// Retag a box pointer as part of casting it to a raw pointer. This is the `Box` equivalent of +/// `(x: &mut T) as *mut T`. The input pointer must be the pointer of a `Box` (passed as raw pointer +/// to avoid all questions around move semantics and custom allocators), and `A` must be the `Box`'s +/// allocator. +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_nounwind] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +#[cfg_attr(bootstrap, inline)] +pub unsafe fn retag_box_to_raw(ptr: *mut T) -> *mut T { + // Miri needs to adjust the provenance, but for regular codegen this is not needed + ptr +} + // Some functions are defined here because they accidentally got made // available in this module on stable. See . // (`transmute` also falls into this category, but it cannot be wrapped due to the diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs index 262f7c449d27..884b8a3b9bc6 100644 --- a/src/tools/miri/src/borrow_tracker/mod.rs +++ b/src/tools/miri/src/borrow_tracker/mod.rs @@ -5,7 +5,7 @@ use std::num::NonZero; use smallvec::SmallVec; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_middle::mir::RetagKind; +use rustc_middle::{mir::RetagKind, ty::Ty}; use rustc_target::abi::Size; use crate::*; @@ -291,6 +291,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } + fn retag_box_to_raw( + &mut self, + val: &ImmTy<'tcx, Provenance>, + alloc_ty: Ty<'tcx>, + ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + let this = self.eval_context_mut(); + let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; + match method { + BorrowTrackerMethod::StackedBorrows => this.sb_retag_box_to_raw(val, alloc_ty), + BorrowTrackerMethod::TreeBorrows => this.tb_retag_box_to_raw(val, alloc_ty), + } + } + fn retag_place_contents( &mut self, kind: RetagKind, diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 86d22229714d..6eed62d7edc6 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -865,6 +865,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.sb_retag_reference(val, new_perm, RetagInfo { cause, in_field: false }) } + fn sb_retag_box_to_raw( + &mut self, + val: &ImmTy<'tcx, Provenance>, + alloc_ty: Ty<'tcx>, + ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + let this = self.eval_context_mut(); + let is_global_alloc = alloc_ty.ty_adt_def().is_some_and(|adt| { + let global_alloc = this.tcx.require_lang_item(rustc_hir::LangItem::GlobalAlloc, None); + adt.did() == global_alloc + }); + if is_global_alloc { + // Retag this as-if it was a mutable reference. + this.sb_retag_ptr_value(RetagKind::Raw, val) + } else { + Ok(val.clone()) + } + } + fn sb_retag_place_contents( &mut self, kind: RetagKind, @@ -916,10 +934,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { self.ecx } - fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { - // Boxes get a weak protectors, since they may be deallocated. - let new_perm = NewPermission::from_box_ty(place.layout.ty, self.kind, self.ecx); - self.retag_ptr_inplace(place, new_perm) + fn visit_box( + &mut self, + box_ty: Ty<'tcx>, + place: &PlaceTy<'tcx, Provenance>, + ) -> InterpResult<'tcx> { + // Only boxes for the global allocator get any special treatment. + if box_ty.is_box_global(*self.ecx.tcx) { + // Boxes get a weak protectors, since they may be deallocated. + let new_perm = NewPermission::from_box_ty(place.layout.ty, self.kind, self.ecx); + self.retag_ptr_inplace(place, new_perm)?; + } + Ok(()) } fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 4b944ea88f50..ae38ce6e7534 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -392,6 +392,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } + fn tb_retag_box_to_raw( + &mut self, + val: &ImmTy<'tcx, Provenance>, + _alloc_ty: Ty<'tcx>, + ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + // Casts to raw pointers are NOPs in Tree Borrows. + Ok(val.clone()) + } + /// Retag all pointers that are stored in this place. fn tb_retag_place_contents( &mut self, @@ -441,14 +450,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Regardless of how `Unique` is handled, Boxes are always reborrowed. /// When `Unique` is also reborrowed, then it behaves exactly like `Box` /// except for the fact that `Box` has a non-zero-sized reborrow. - fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { - let new_perm = NewPermission::from_unique_ty( - place.layout.ty, - self.kind, - self.ecx, - /* zero_size */ false, - ); - self.retag_ptr_inplace(place, new_perm) + fn visit_box( + &mut self, + box_ty: Ty<'tcx>, + place: &PlaceTy<'tcx, Provenance>, + ) -> InterpResult<'tcx> { + // Only boxes for the global allocator get any special treatment. + if box_ty.is_box_global(*self.ecx.tcx) { + let new_perm = NewPermission::from_unique_ty( + place.layout.ty, + self.kind, + self.ecx, + /* zero_size */ false, + ); + self.retag_ptr_inplace(place, new_perm)?; + } + Ok(()) } fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { diff --git a/src/tools/miri/src/shims/intrinsics/mod.rs b/src/tools/miri/src/shims/intrinsics/mod.rs index 46f0c771cb51..075ce44fbcee 100644 --- a/src/tools/miri/src/shims/intrinsics/mod.rs +++ b/src/tools/miri/src/shims/intrinsics/mod.rs @@ -129,6 +129,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_bytes_ptr(ptr, iter::repeat(val_byte).take(byte_count.bytes_usize()))?; } + // Memory model / provenance manipulation "ptr_mask" => { let [ptr, mask] = check_arg_count(args)?; @@ -139,6 +140,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_pointer(Pointer::new(ptr.provenance, masked_addr), dest)?; } + "retag_box_to_raw" => { + let [ptr] = check_arg_count(args)?; + let alloc_ty = generic_args[1].expect_ty(); + + let val = this.read_immediate(ptr)?; + let new_val = if this.machine.borrow_tracker.is_some() { + this.retag_box_to_raw(&val, alloc_ty)? + } else { + val + }; + this.write_immediate(*new_val, dest)?; + } // We want to return either `true` or `false` at random, or else something like // ``` diff --git a/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs b/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs new file mode 100644 index 000000000000..541e5f244db0 --- /dev/null +++ b/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs @@ -0,0 +1,123 @@ +//! Regression test for : +//! If `Box` has a local allocator, then it can't be `noalias` as the allocator +//! may want to access allocator state based on the data pointer. + +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows +#![feature(allocator_api)] +#![feature(strict_provenance)] + +use std::{ + alloc::{AllocError, Allocator, Layout}, + cell::{Cell, UnsafeCell}, + ptr::{self, addr_of, NonNull}, + thread::{self, ThreadId}, + mem, +}; + +const BIN_SIZE: usize = 8; + +// A bin represents a collection of blocks of a specific layout. +#[repr(align(128))] +struct MyBin { + top: Cell, + thread_id: ThreadId, + memory: UnsafeCell<[usize; BIN_SIZE]>, +} + +impl MyBin { + fn pop(&self) -> Option> { + let top = self.top.get(); + if top == BIN_SIZE { + return None; + } + // Cast the *entire* thing to a raw pointer to not restrict its provenance. + let bin = self as *const MyBin; + let base_ptr = UnsafeCell::raw_get(unsafe{ addr_of!((*bin).memory )}).cast::(); + let ptr = unsafe { NonNull::new_unchecked(base_ptr.add(top)) }; + self.top.set(top + 1); + Some(ptr.cast()) + } + + // Pretends to not be a throwaway allocation method like this. A more realistic + // substitute is using intrusive linked lists, which requires access to the + // metadata of this bin as well. + unsafe fn push(&self, ptr: NonNull) { + // For now just check that this really is in this bin. + let start = self.memory.get().addr(); + let end = start + BIN_SIZE * mem::size_of::(); + let addr = ptr.addr().get(); + assert!((start..end).contains(&addr)); + } +} + +// A collection of bins. +struct MyAllocator { + thread_id: ThreadId, + // Pretends to be some complex collection of bins, such as an array of linked lists. + bins: Box<[MyBin; 1]>, +} + +impl MyAllocator { + fn new() -> Self { + let thread_id = thread::current().id(); + MyAllocator { + thread_id, + bins: Box::new( + [MyBin { + top: Cell::new(0), + thread_id, + memory: UnsafeCell::default(), + }; 1], + ), + } + } + + // Pretends to be expensive finding a suitable bin for the layout. + fn find_bin(&self, layout: Layout) -> Option<&MyBin> { + if layout == Layout::new::() { + Some(&self.bins[0]) + } else { + None + } + } +} + +unsafe impl Allocator for MyAllocator { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + // Expensive bin search. + let bin = self.find_bin(layout).ok_or(AllocError)?; + let ptr = bin.pop().ok_or(AllocError)?; + Ok(NonNull::slice_from_raw_parts(ptr, layout.size())) + } + + unsafe fn deallocate(&self, ptr: NonNull, _layout: Layout) { + // Since manually finding the corresponding bin of `ptr` is very expensive, + // doing pointer arithmetics is preferred. + // But this means we access `top` via `ptr` rather than `self`! + // That is fundamentally the source of the aliasing trouble in this example. + let their_bin = ptr.as_ptr().map_addr(|addr| addr & !127).cast::(); + let thread_id = ptr::read(ptr::addr_of!((*their_bin).thread_id)); + if self.thread_id == thread_id { + unsafe { (*their_bin).push(ptr) }; + } else { + todo!("Deallocating from another thread") + } + } +} + +// Make sure to involve `Box` in allocating these, +// as that's where `noalias` may come from. +fn v(t: T, a: A) -> Vec { + (Box::new_in([t], a) as Box<[T], A>).into_vec() +} + +fn main() { + assert!(mem::size_of::() <= 128); // if it grows bigger, the trick to access the "header" no longer works + let my_alloc = MyAllocator::new(); + let a = v(1usize, &my_alloc); + let b = v(2usize, &my_alloc); + assert_eq!(a[0] + 1, b[0]); + assert_eq!(addr_of!(a[0]).wrapping_add(1), addr_of!(b[0])); + drop((a, b)); +} From 7e1969ac13b94694bb6c9315dcecdc6b0ee344ad Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 9 Mar 2024 21:16:18 +0800 Subject: [PATCH 42/47] Remove `Ord` from `ClosureKind` Using `Ord` to accomplish a meaning of subset relationship can be hard to read. The existing uses for that are easily replaced with a `match`, and in my opinion, more readable without needing to resorting to comments to explain the intention. --- compiler/rustc_hir_typeck/src/closure.rs | 17 +++++++---- compiler/rustc_middle/src/ty/instance.rs | 2 +- compiler/rustc_ty_utils/src/instance.rs | 37 +++++++++++++----------- compiler/rustc_type_ir/src/lib.rs | 14 +++++---- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 1ff961a90890..c17af666eb9f 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -19,7 +19,7 @@ use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::ArgKind; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; -use std::cmp; +use rustc_type_ir::ClosureKind; use std::iter; use std::ops::ControlFlow; @@ -437,10 +437,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Some(found_kind) = found_kind { - expected_kind = Some( - expected_kind - .map_or_else(|| found_kind, |current| cmp::min(current, found_kind)), - ); + // always use the closure kind that is more permissive. + match (expected_kind, found_kind) { + (None, _) => expected_kind = Some(found_kind), + (Some(ClosureKind::FnMut), ClosureKind::Fn) => { + expected_kind = Some(ClosureKind::Fn) + } + (Some(ClosureKind::FnOnce), ClosureKind::Fn | ClosureKind::FnMut) => { + expected_kind = Some(found_kind) + } + _ => {} + } } } } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 260d0885089e..e381bffa3954 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -30,7 +30,7 @@ pub struct Instance<'tcx> { pub args: GenericArgsRef<'tcx>, } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)] pub enum InstanceDef<'tcx> { /// A user-defined callable item. diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 381681fb1f41..2816bcc888b0 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -7,6 +7,7 @@ use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt}; use rustc_span::sym; use rustc_trait_selection::traits; +use rustc_type_ir::ClosureKind; use traits::{translate_args, Reveal}; use crate::errors::UnexpectedFnPtrAssociatedItem; @@ -296,23 +297,25 @@ fn resolve_associated_item<'tcx>( { match *rcvr_args.type_at(0).kind() { ty::CoroutineClosure(coroutine_closure_def_id, args) => { - // If we're computing `AsyncFnOnce`/`AsyncFnMut` for a by-ref closure, - // or `AsyncFnOnce` for a by-mut closure, then construct a new body that - // has the right return types. - // - // Specifically, `AsyncFnMut` for a by-ref coroutine-closure just needs - // to have its input and output types fixed (`&mut self` and returning - // `i16` coroutine kind). - if target_kind > args.as_coroutine_closure().kind() { - Some(Instance { - def: ty::InstanceDef::ConstructCoroutineInClosureShim { - coroutine_closure_def_id, - target_kind, - }, - args, - }) - } else { - Some(Instance::new(coroutine_closure_def_id, args)) + match (target_kind, args.as_coroutine_closure().kind()) { + (ClosureKind::FnOnce | ClosureKind::FnMut, ClosureKind::Fn) + | (ClosureKind::FnOnce, ClosureKind::FnMut) => { + // If we're computing `AsyncFnOnce`/`AsyncFnMut` for a by-ref closure, + // or `AsyncFnOnce` for a by-mut closure, then construct a new body that + // has the right return types. + // + // Specifically, `AsyncFnMut` for a by-ref coroutine-closure just needs + // to have its input and output types fixed (`&mut self` and returning + // `i16` coroutine kind). + Some(Instance { + def: ty::InstanceDef::ConstructCoroutineInClosureShim { + coroutine_closure_def_id, + target_kind, + }, + args, + }) + } + _ => Some(Instance::new(coroutine_closure_def_id, args)), } } ty::Closure(closure_def_id, args) => { diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 2ded1b956e51..c01baa58ae78 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -369,12 +369,9 @@ rustc_index::newtype_index! { /// /// You can get the environment type of a closure using /// `tcx.closure_env_ty()`. -#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum ClosureKind { - // Warning: Ordering is significant here! The ordering is chosen - // because the trait Fn is a subtrait of FnMut and so in turn, and - // hence we order it so that Fn < FnMut < FnOnce. Fn, FnMut, FnOnce, @@ -394,8 +391,15 @@ impl ClosureKind { /// Returns `true` if a type that impls this closure kind /// must also implement `other`. + #[rustfmt::skip] pub fn extends(self, other: ClosureKind) -> bool { - self <= other + use ClosureKind::*; + match (self, other) { + (Fn, Fn | FnMut | FnOnce) + | (FnMut, FnMut | FnOnce) + | (FnOnce, FnOnce) => true, + _ => false, + } } } From 50d0bea15384f1116afd6831de3cbdc8c154e677 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 9 Mar 2024 10:49:26 -0500 Subject: [PATCH 43/47] Improve docs --- library/core/src/intrinsics.rs | 15 +++++++++------ library/core/src/ptr/mod.rs | 3 +-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index eda8b7842ab4..7cda1225672b 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2799,6 +2799,10 @@ pub(crate) use assert_unsafe_precondition; /// Checks whether `ptr` is properly aligned with respect to /// `align_of::()`. +/// +/// In `const` this is approximate and can fail spuriously. It is primarily intended +/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the +/// check is anyway not executed in `const`. #[inline] pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool { !ptr.is_null() && ptr.is_aligned_to(align) @@ -2813,10 +2817,10 @@ pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool { /// Checks whether the regions of memory starting at `src` and `dst` of size /// `count * size` do *not* overlap. /// -/// # Safety -/// This function must only be called such that if it returns false, we will execute UB. +/// Note that in const-eval this function just returns `true` and therefore must +/// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`. #[inline] -pub(crate) const unsafe fn is_nonoverlapping( +pub(crate) const fn is_nonoverlapping( src: *const (), dst: *const (), size: usize, @@ -2842,7 +2846,7 @@ pub(crate) const unsafe fn is_nonoverlapping( true } - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block // SAFETY: This function's precondition is equivalent to that of `const_eval_select`. // Programs which do not execute UB will only see this function return `true`, which makes the // const and runtime implementation indistinguishable. @@ -2962,8 +2966,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us ) => is_aligned_and_not_null(src, align) && is_aligned_and_not_null(dst, align) - // SAFETY: If this returns false, we're about to execute UB. - && unsafe { is_nonoverlapping(src, dst, size, count) } + && is_nonoverlapping(src, dst, size, count) ); // SAFETY: the safety contract for `copy_nonoverlapping` must be diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 80a6d5779e14..30af75223684 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1032,8 +1032,7 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { ) => is_aligned_and_not_null(x, align) && is_aligned_and_not_null(y, align) - // SAFETY: If this returns false, we're about to execute UB. - && unsafe { is_nonoverlapping(x, y, size, count) } + && is_nonoverlapping(x, y, size, count) ); // Split up the slice into small power-of-two-sized chunks that LLVM is able From 27cf4bb98516c801c8b24d80cd78cd5eee8edaff Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 9 Mar 2024 11:34:15 -0500 Subject: [PATCH 44/47] Only enable library UB checks in const-eval/Miri when debug_assertions are enabled Co-authored-by: Ralf Jung --- compiler/rustc_const_eval/src/interpret/step.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 752fdc2c6f84..54bac70da388 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -264,7 +264,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // But we want to disable checks for language UB, because the interpreter // has its own better checks for that. let should_check = match kind { - mir::UbKind::LibraryUb => true, + mir::UbKind::LibraryUb => self.tcx.sess.opts.debug_assertions, mir::UbKind::LanguageUb => false, }; Scalar::from_bool(should_check) From af49c4df0a666f3ed0cefaae63851667dde9647d Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 9 Mar 2024 12:26:48 -0500 Subject: [PATCH 45/47] NonZero::from_mut_unchecked is library UB --- library/core/src/num/nonzero.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 012f224a4a26..6cbcac20ea1b 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -368,7 +368,7 @@ where // SAFETY: The caller guarantees that `n` references a value that is non-zero, so this is unreachable. unsafe { intrinsics::assert_unsafe_precondition!( - check_language_ub, + check_library_ub, "NonZero::from_mut_unchecked requires the argument to dereference as non-zero", () => false, ); From 2a1f97f77ff2a6f6e27aae983420f4ee57056527 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 9 Mar 2024 13:36:36 -0500 Subject: [PATCH 46/47] Explain why we don't use intrinsics::is_nonoverlapping --- library/core/src/cell.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index e8590ab11586..0e719e169deb 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -434,6 +434,8 @@ impl Cell { #[inline] #[stable(feature = "move_cell", since = "1.17.0")] pub fn swap(&self, other: &Self) { + // This function documents that it *will* panic, and intrinsics::is_nonoverlapping doesn't + // do the check in const, so trying to use it here would be inviting unnecessary fragility. fn is_nonoverlapping(src: *const T, dst: *const T) -> bool { let src_usize = src.addr(); let dst_usize = dst.addr(); From 83590ac06945f9c7c776d25699c76ffc8558e860 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Sat, 9 Mar 2024 23:33:27 -0700 Subject: [PATCH 47/47] fix legacy numeric constant diag items - missed syms for usize/isize - missed diag items on unsigned integers --- compiler/rustc_span/src/symbol.rs | 10 ++++++++++ library/core/src/num/uint_macros.rs | 2 ++ 2 files changed, 12 insertions(+) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 337910763757..708349e85aeb 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1000,6 +1000,11 @@ symbols! { is_val_statically_known, isa_attribute, isize, + isize_legacy_const_max, + isize_legacy_const_min, + isize_legacy_fn_max_value, + isize_legacy_fn_min_value, + isize_legacy_mod, issue, issue_5723_bootstrap, issue_tracker_base_url, @@ -1910,6 +1915,11 @@ symbols! { used_with_arg, using, usize, + usize_legacy_const_max, + usize_legacy_const_min, + usize_legacy_fn_max_value, + usize_legacy_fn_min_value, + usize_legacy_mod, va_arg, va_copy, va_end, diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index f2f29e4ad819..081a3c0b118e 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2972,6 +2972,7 @@ macro_rules! uint_impl { #[inline(always)] #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] #[deprecated(since = "TBD", note = "replaced by the `MIN` associated constant on this type")] + #[rustc_diagnostic_item = concat!(stringify!($SelfT), "_legacy_fn_min_value")] pub const fn min_value() -> Self { Self::MIN } /// New code should prefer to use @@ -2983,6 +2984,7 @@ macro_rules! uint_impl { #[inline(always)] #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] #[deprecated(since = "TBD", note = "replaced by the `MAX` associated constant on this type")] + #[rustc_diagnostic_item = concat!(stringify!($SelfT), "_legacy_fn_max_value")] pub const fn max_value() -> Self { Self::MAX } } }