From a9f6fd16c07d78386e726946bfe73c1c2a18102e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 18 Oct 2024 07:53:56 +0200 Subject: [PATCH 01/10] x86-32 float return for 'Rust' ABI: treat all float types consistently --- compiler/rustc_ty_utils/src/abi.rs | 37 ++++++++++++------------------ 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 7354ea5fb6a22..d069ea3dd2f26 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -1,6 +1,5 @@ use std::iter; -use rustc_abi::Float::*; use rustc_abi::Primitive::{Float, Pointer}; use rustc_abi::{Abi, AddressSpace, PointerKind, Scalar, Size}; use rustc_hir as hir; @@ -695,37 +694,31 @@ fn fn_abi_adjust_for_abi<'tcx>( } // Avoid returning floats in x87 registers on x86 as loading and storing from x87 - // registers will quiet signalling NaNs. + // registers will quiet signalling NaNs. Also avoid using SSE registers since they + // are not always available (depending on target features). if tcx.sess.target.arch == "x86" && arg_idx.is_none() // Intrinsics themselves are not actual "real" functions, so theres no need to // change their ABIs. && abi != SpecAbi::RustIntrinsic { - match arg.layout.abi { - // Handle similar to the way arguments with an `Abi::Aggregate` abi are handled - // below, by returning arguments up to the size of a pointer (32 bits on x86) - // cast to an appropriately sized integer. - Abi::Scalar(s) if s.primitive() == Float(F32) => { - // Same size as a pointer, return in a register. - arg.cast_to(Reg::i32()); - return; + let has_float = match arg.layout.abi { + Abi::Scalar(s) => matches!(s.primitive(), Float(_)), + Abi::ScalarPair(s1, s2) => { + matches!(s1.primitive(), Float(_)) || matches!(s2.primitive(), Float(_)) } - Abi::Scalar(s) if s.primitive() == Float(F64) => { - // Larger than a pointer, return indirectly. - arg.make_indirect(); - return; - } - Abi::ScalarPair(s1, s2) - if matches!(s1.primitive(), Float(F32 | F64)) - || matches!(s2.primitive(), Float(F32 | F64)) => - { + _ => false, // anyway not passed via registers on x86 + }; + if has_float { + if arg.layout.size <= Pointer(AddressSpace::DATA).size(cx) { + // Same size or smaller than pointer, return in a register. + arg.cast_to(Reg { kind: RegKind::Integer, size: arg.layout.size }); + } else { // Larger than a pointer, return indirectly. arg.make_indirect(); - return; } - _ => {} - }; + return; + } } match arg.layout.abi { From 8f2273e518c939f10c3ba891058cd7a43aedce23 Mon Sep 17 00:00:00 2001 From: Falco Hirschenberger Date: Fri, 11 Oct 2024 10:46:38 +0200 Subject: [PATCH 02/10] Fix #131471, range misleading field access Fixes #131471 by checking if the range-start is a literal. --- compiler/rustc_resolve/src/late.rs | 1 + tests/ui/range/misleading-field-access-hint.rs | 8 ++++++++ tests/ui/range/misleading-field-access-hint.stderr | 9 +++++++++ 3 files changed, 18 insertions(+) create mode 100644 tests/ui/range/misleading-field-access-hint.rs create mode 100644 tests/ui/range/misleading-field-access-hint.stderr diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b84cbf9c62941..98db36b12be67 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -4011,6 +4011,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let instead = res.is_some(); let suggestion = if let Some((start, end)) = this.diag_metadata.in_range && path[0].ident.span.lo() == end.span.lo() + && !matches!(start.kind, ExprKind::Lit(_)) { let mut sugg = "."; let mut span = start.span.between(end.span); diff --git a/tests/ui/range/misleading-field-access-hint.rs b/tests/ui/range/misleading-field-access-hint.rs new file mode 100644 index 0000000000000..252f1a4833c77 --- /dev/null +++ b/tests/ui/range/misleading-field-access-hint.rs @@ -0,0 +1,8 @@ +// Check if rustc still displays the misleading hint to write `.` instead of `..` +fn main() { + let width = 10; + // ... + for _ in 0..w { + //~^ ERROR cannot find value `w` + } +} diff --git a/tests/ui/range/misleading-field-access-hint.stderr b/tests/ui/range/misleading-field-access-hint.stderr new file mode 100644 index 0000000000000..9b112a5fdd834 --- /dev/null +++ b/tests/ui/range/misleading-field-access-hint.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find value `w` in this scope + --> $DIR/misleading-field-access-hint.rs:5:17 + | +LL | for _ in 0..w { + | ^ not found in this scope + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. From 1617501eeaef60bf80378f6736f210113bb68132 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 18 Oct 2024 12:30:18 -0400 Subject: [PATCH 03/10] Mark unexpected variant res suggestion as having placeholders --- compiler/rustc_hir_typeck/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 6b0a897faba94..9e36f7a9aea8c 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -419,7 +419,7 @@ fn report_unexpected_variant_res( } } - err.multipart_suggestion_verbose(descr, suggestion, Applicability::MaybeIncorrect); + err.multipart_suggestion_verbose(descr, suggestion, Applicability::HasPlaceholders); err } Res::Def(DefKind::Variant, _) if expr.is_none() => { From 588bfb4d50980f71434dc34b524d44545dd2d721 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Tue, 15 Oct 2024 15:18:57 +0530 Subject: [PATCH 04/10] std: uefi: Add basic Env variables - Implement environment variable functions - Using EFI Shell protocol. Signed-off-by: Ayush Singh --- library/std/src/sys/pal/uefi/helpers.rs | 23 ++++- library/std/src/sys/pal/uefi/os.rs | 124 +++++++++++++++++++++--- 2 files changed, 132 insertions(+), 15 deletions(-) diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 4ced7065c826d..abc8e69a285f3 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -10,7 +10,7 @@ //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) use r_efi::efi::{self, Guid}; -use r_efi::protocols::{device_path, device_path_to_text}; +use r_efi::protocols::{device_path, device_path_to_text, shell}; use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_io_error}; @@ -424,3 +424,24 @@ pub(crate) fn os_string_to_raw(s: &OsStr) -> Option> { let temp = s.encode_wide().chain(Some(0)).collect::>(); if temp[..temp.len() - 1].contains(&0) { None } else { Some(temp) } } + +pub(crate) fn open_shell() -> Option> { + static LAST_VALID_HANDLE: AtomicPtr = + AtomicPtr::new(crate::ptr::null_mut()); + + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { + if let Ok(protocol) = open_protocol::(handle, shell::PROTOCOL_GUID) { + return Some(protocol); + } + } + + let handles = locate_handles(shell::PROTOCOL_GUID).ok()?; + for handle in handles { + if let Ok(protocol) = open_protocol::(handle, shell::PROTOCOL_GUID) { + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); + return Some(protocol); + } + } + + None +} diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 4eb7698b43aa9..8160dcf35a1cc 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -192,44 +192,58 @@ pub fn current_exe() -> io::Result { helpers::device_path_to_text(protocol).map(PathBuf::from) } -pub struct Env(!); +pub struct EnvStrDebug<'a> { + iter: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut list = f.debug_list(); + for (a, b) in self.iter { + list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); + } + list.finish() + } +} + +pub struct Env(crate::vec::IntoIter<(OsString, OsString)>); impl Env { // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} + EnvStrDebug { iter: self.0.as_slice() } } } impl Iterator for Env { type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { - self.0 + self.0.next() } } impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) } } pub fn env() -> Env { - panic!("not supported on this platform") + let env = uefi_env::get_all().expect("not supported on this platform"); + Env(env.into_iter()) } -pub fn getenv(_: &OsStr) -> Option { - None +pub fn getenv(key: &OsStr) -> Option { + uefi_env::get(key) } -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> { + uefi_env::set(key, val) } -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> { + uefi_env::unset(key) } pub fn temp_dir() -> PathBuf { @@ -294,3 +308,85 @@ mod uefi_shell { None } } + +mod uefi_env { + use crate::ffi::{OsStr, OsString}; + use crate::io; + use crate::os::uefi::ffi::OsStringExt; + use crate::ptr::NonNull; + use crate::sys::{helpers, unsupported_err}; + + pub(crate) fn get(key: &OsStr) -> Option { + let shell = helpers::open_shell()?; + let mut key_ptr = helpers::os_string_to_raw(key)?; + unsafe { get_raw(shell, key_ptr.as_mut_ptr()) } + } + + pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> { + let mut key_ptr = helpers::os_string_to_raw(key) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; + let mut val_ptr = helpers::os_string_to_raw(val) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; + unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) } + } + + pub(crate) fn unset(key: &OsStr) -> io::Result<()> { + let mut key_ptr = helpers::os_string_to_raw(key) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; + unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) } + } + + pub(crate) fn get_all() -> io::Result> { + let shell = helpers::open_shell().ok_or(unsupported_err())?; + + let mut vars = Vec::new(); + let val = unsafe { ((*shell.as_ptr()).get_env)(crate::ptr::null_mut()) }; + + if val.is_null() { + return Ok(vars); + } + + let mut start = 0; + + // UEFI Shell returns all keys seperated by NULL. + // End of string is denoted by two NULLs + for i in 0.. { + if unsafe { *val.add(i) } == 0 { + // Two NULL signal end of string + if i == start { + break; + } + + let key = OsString::from_wide(unsafe { + crate::slice::from_raw_parts(val.add(start), i - start) + }); + // SAFETY: val.add(start) is always NULL terminated + let val = unsafe { get_raw(shell, val.add(start)) } + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; + + vars.push((key, val)); + start = i + 1; + } + } + + Ok(vars) + } + + unsafe fn get_raw( + shell: NonNull, + key_ptr: *mut r_efi::efi::Char16, + ) -> Option { + let val = unsafe { ((*shell.as_ptr()).get_env)(key_ptr) }; + helpers::os_string_from_raw(val) + } + + unsafe fn set_raw( + key_ptr: *mut r_efi::efi::Char16, + val_ptr: *mut r_efi::efi::Char16, + ) -> io::Result<()> { + let shell = helpers::open_shell().ok_or(unsupported_err())?; + let r = + unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } +} From 753536aba86310d85e71bb49062d883938602417 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Wed, 16 Oct 2024 09:10:54 +0530 Subject: [PATCH 05/10] std: uefi: Use common function for UEFI shell - Since in almost all cases, there will only be 1 UEFI shell, share the shell handle between all functions that require it. Signed-off-by: Ayush Singh --- library/std/src/sys/pal/uefi/os.rs | 38 ++---------------------------- 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 8160dcf35a1cc..27395f7c3c0b3 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -125,7 +125,7 @@ pub fn error_string(errno: RawOsError) -> String { } pub fn getcwd() -> io::Result { - match uefi_shell::open_shell() { + match helpers::open_shell() { Some(shell) => { // SAFETY: path_ptr is managed by UEFI shell and should not be deallocated let path_ptr = unsafe { ((*shell.as_ptr()).get_cur_dir)(crate::ptr::null_mut()) }; @@ -144,7 +144,7 @@ pub fn getcwd() -> io::Result { } pub fn chdir(p: &path::Path) -> io::Result<()> { - let shell = uefi_shell::open_shell().ok_or(unsupported_err())?; + let shell = helpers::open_shell().ok_or(unsupported_err())?; let mut p = helpers::os_string_to_raw(p.as_os_str()) .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))?; @@ -275,40 +275,6 @@ pub fn getpid() -> u32 { panic!("no pids on this platform") } -mod uefi_shell { - use r_efi::protocols::shell; - - use super::super::helpers; - use crate::ptr::NonNull; - use crate::sync::atomic::{AtomicPtr, Ordering}; - - pub fn open_shell() -> Option> { - static LAST_VALID_HANDLE: AtomicPtr = - AtomicPtr::new(crate::ptr::null_mut()); - - if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { - if let Ok(protocol) = helpers::open_protocol::( - handle, - r_efi::protocols::shell::PROTOCOL_GUID, - ) { - return Some(protocol); - } - } - - let handles = helpers::locate_handles(shell::PROTOCOL_GUID).ok()?; - for handle in handles { - if let Ok(protocol) = - helpers::open_protocol::(handle, shell::PROTOCOL_GUID) - { - LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); - return Some(protocol); - } - } - - None - } -} - mod uefi_env { use crate::ffi::{OsStr, OsString}; use crate::io; From be984c1889c66e4a3d1e117d577256e80ece97b1 Mon Sep 17 00:00:00 2001 From: printfn Date: Fri, 18 Oct 2024 21:17:08 +0000 Subject: [PATCH 06/10] Update `use` keyword docs to describe precise capturing --- library/std/src/keyword_docs.rs | 46 ++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 453b2708daab9..30d43c8bbfd8c 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -2146,10 +2146,13 @@ mod unsafe_keyword {} #[doc(keyword = "use")] // -/// Import or rename items from other crates or modules. +/// Import or rename items from other crates or modules, or specify precise capturing +/// with `use<..>`. /// -/// Usually a `use` keyword is used to shorten the path required to refer to a module item. -/// The keyword may appear in modules, blocks and even functions, usually at the top. +/// ## Importing items +/// +/// The `use` keyword is employed to shorten the path required to refer to a module item. +/// The keyword may appear in modules, blocks, and even functions, typically at the top. /// /// The most basic usage of the keyword is `use path::to::item;`, /// though a number of convenient shortcuts are supported: @@ -2190,19 +2193,48 @@ mod unsafe_keyword {} /// // Compiles. /// let _ = VariantA; /// -/// // Does not compile ! +/// // Does not compile! /// let n = new(); /// ``` /// -/// For more information on `use` and paths in general, see the [Reference]. +/// For more information on `use` and paths in general, see the [Reference][ref-use-decls]. /// /// The differences about paths and the `use` keyword between the 2015 and 2018 editions -/// can also be found in the [Reference]. +/// can also be found in the [Reference][ref-use-decls]. +/// +/// ## Precise capturing +/// +/// The `use<..>` syntax is used within certain `impl Trait` bounds to control which generic +/// parameters are captured. This is important for return-position `impl Trait` (RPIT) types, +/// as it affects borrow checking by controlling which generic parameters can be used in the +/// hidden type. +/// +/// For example, the following function demonstrates an error without precise capturing in +/// Rust 2021 and earlier editions: +/// +/// ```rust,compile_fail,edition2021 +/// fn f(x: &()) -> impl Sized { x } +/// ``` +/// +/// By using `use<'_>` for precise capturing, it can be resolved: +/// +/// ```rust +/// fn f(x: &()) -> impl Sized + use<'_> { x } +/// ``` +/// +/// This syntax specifies that the elided lifetime be captured and therefore available for +/// use in the hidden type. +/// +/// In Rust 2024, opaque types automatically capture all lifetime parameters in scope. +/// `use<..>` syntax serves as an important way of opting-out of that default. +/// +/// For more details about precise capturing, see the [Reference][ref-impl-trait]. /// /// [`crate`]: keyword.crate.html /// [`self`]: keyword.self.html /// [`super`]: keyword.super.html -/// [Reference]: ../reference/items/use-declarations.html +/// [ref-use-decls]: ../reference/items/use-declarations.html +/// [ref-impl-trait]: ../reference/types/impl-trait.html mod use_keyword {} #[doc(keyword = "where")] From 3cf8a61a7ad803b3c785d1052dc4dc9fecb7a92f Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Fri, 18 Oct 2024 20:28:32 +0000 Subject: [PATCH 07/10] rustdoc: Switch from FxHash to sha256 for static file hashing. --- Cargo.lock | 1 + src/librustdoc/Cargo.toml | 1 + src/librustdoc/html/static/css/rustdoc.css | 18 +++++++++--------- src/librustdoc/html/static_files.rs | 11 +++++------ 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5f81a5a84966a..4aa079612c834 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4608,6 +4608,7 @@ dependencies = [ "rustdoc-json-types", "serde", "serde_json", + "sha2", "smallvec", "tempfile", "threadpool", diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 34332de80b365..42df0b283817b 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -24,6 +24,7 @@ tracing = "0.1" tracing-tree = "0.3.0" threadpool = "1.8.1" unicode-segmentation = "1.9" +sha2 = "0.10.8" [dependencies.tracing-subscriber] version = "0.3.3" diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index df9776ff5f88c..2c17fd54006f5 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -44,7 +44,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: normal; font-weight: 400; src: local('Fira Sans'), - url("FiraSans-Regular-018c141bf0843ffd.woff2") format("woff2"); + url("FiraSans-Regular-0fe48ade.woff2") format("woff2"); font-display: swap; } @font-face { @@ -52,7 +52,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: normal; font-weight: 500; src: local('Fira Sans Medium'), - url("FiraSans-Medium-8f9a781e4970d388.woff2") format("woff2"); + url("FiraSans-Medium-e1aa3f0a.woff2") format("woff2"); font-display: swap; } @@ -62,7 +62,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: normal; font-weight: 400; src: local('Source Serif 4'), - url("SourceSerif4-Regular-46f98efaafac5295.ttf.woff2") format("woff2"); + url("SourceSerif4-Regular-6b053e98.ttf.woff2") format("woff2"); font-display: swap; } @font-face { @@ -70,7 +70,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: italic; font-weight: 400; src: local('Source Serif 4 Italic'), - url("SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2") format("woff2"); + url("SourceSerif4-It-ca3b17ed.ttf.woff2") format("woff2"); font-display: swap; } @font-face { @@ -78,7 +78,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: normal; font-weight: 700; src: local('Source Serif 4 Bold'), - url("SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2") format("woff2"); + url("SourceSerif4-Bold-6d4fd4c0.ttf.woff2") format("woff2"); font-display: swap; } @@ -89,28 +89,28 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-weight: 400; /* Avoid using locally installed font because bad versions are in circulation: * see https://github.com/rust-lang/rust/issues/24355 */ - src: url("SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2") format("woff2"); + src: url("SourceCodePro-Regular-8badfe75.ttf.woff2") format("woff2"); font-display: swap; } @font-face { font-family: 'Source Code Pro'; font-style: italic; font-weight: 400; - src: url("SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2") format("woff2"); + src: url("SourceCodePro-It-fc8b9304.ttf.woff2") format("woff2"); font-display: swap; } @font-face { font-family: 'Source Code Pro'; font-style: normal; font-weight: 600; - src: url("SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2") format("woff2"); + src: url("SourceCodePro-Semibold-aa29a496.ttf.woff2") format("woff2"); font-display: swap; } /* Avoid using legacy CJK serif fonts in Windows like Batang. */ @font-face { font-family: 'NanumBarunGothic'; - src: url("NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2") format("woff2"); + src: url("NanumBarunGothic-13b3dcba.ttf.woff2") format("woff2"); font-display: swap; unicode-range: U+AC00-D7AF, U+1100-11FF, U+3130-318F, U+A960-A97F, U+D7B0-D7FF; } diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index 6157598ba3888..9e0803f5d3fd1 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -3,12 +3,9 @@ //! All the static files are included here for centralized access in case anything other than the //! HTML rendering code (say, the theme checker) needs to access one of these files. -use std::hash::Hasher; use std::path::{Path, PathBuf}; use std::{fmt, str}; -use rustc_data_structures::fx::FxHasher; - pub(crate) struct StaticFile { pub(crate) filename: PathBuf, pub(crate) bytes: &'static [u8], @@ -64,9 +61,11 @@ pub(crate) fn static_filename(filename: &str, contents: &[u8]) -> PathBuf { } fn static_suffix(bytes: &[u8]) -> String { - let mut hasher = FxHasher::default(); - hasher.write(bytes); - format!("-{:016x}", hasher.finish()) + use sha2::Digest; + let bytes = sha2::Sha256::digest(bytes); + let mut digest = format!("-{bytes:x}"); + digest.truncate(9); + digest } macro_rules! static_files { From 65458aed68fe6786068bab00e5a46d7ecdd2a072 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?= <39484203+jieyouxu@users.noreply.github.com> Date: Thu, 17 Oct 2024 22:58:45 +0800 Subject: [PATCH 08/10] bootstrap: allow setting `--jobs` in config.toml --- config.example.toml | 5 ++ src/bootstrap/src/core/config/config.rs | 5 +- src/bootstrap/src/core/config/flags.rs | 3 +- src/bootstrap/src/core/config/tests.rs | 58 +++++++++++++++++++++++ src/bootstrap/src/utils/change_tracker.rs | 5 ++ 5 files changed, 73 insertions(+), 3 deletions(-) diff --git a/config.example.toml b/config.example.toml index 4b591b949b36d..168ac353cff75 100644 --- a/config.example.toml +++ b/config.example.toml @@ -414,6 +414,11 @@ # Specify the location of the Android NDK. Used when targeting Android. #android-ndk = "/path/to/android-ndk-r26d" +# Number of parallel jobs to be used for building and testing. If set to `0` or +# omitted, it will be automatically determined. This is the `-j`/`--jobs` flag +# passed to cargo invocations. +#jobs = 0 + # ============================================================================= # General install configuration options # ============================================================================= diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index c2ab439891eaa..aeb81b146382e 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -891,6 +891,7 @@ define_config! { metrics: Option = "metrics", android_ndk: Option = "android-ndk", optimized_compiler_builtins: Option = "optimized-compiler-builtins", + jobs: Option = "jobs", } } @@ -1289,7 +1290,6 @@ impl Config { config.rustc_error_format = flags.rustc_error_format; config.json_output = flags.json_output; config.on_fail = flags.on_fail; - config.jobs = Some(threads_from_config(flags.jobs as u32)); config.cmd = flags.cmd; config.incremental = flags.incremental; config.dry_run = if flags.dry_run { DryRun::UserSelected } else { DryRun::Disabled }; @@ -1511,8 +1511,11 @@ impl Config { metrics: _, android_ndk, optimized_compiler_builtins, + jobs, } = toml.build.unwrap_or_default(); + config.jobs = Some(threads_from_config(flags.jobs.unwrap_or(jobs.unwrap_or(0)))); + if let Some(file_build) = build { config.build = TargetSelection::from_user(&file_build); }; diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 3aefe517a5be6..bfeb811508c04 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -110,11 +110,10 @@ pub struct Flags { short, long, value_hint = clap::ValueHint::Other, - default_value_t = std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get), value_name = "JOBS" )] /// number of jobs to run in parallel - pub jobs: usize, + pub jobs: Option, // This overrides the deny-warnings configuration option, // which passes -Dwarnings to the compiler invocations. #[arg(global = true, long)] diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 2611b6cf51bbe..1f02757682c22 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -352,3 +352,61 @@ fn parse_rust_std_features_empty() { fn parse_rust_std_features_invalid() { parse("rust.std-features = \"backtrace\""); } + +#[test] +fn parse_jobs() { + assert_eq!(parse("build.jobs = 1").jobs, Some(1)); +} + +#[test] +fn jobs_precedence() { + // `--jobs` should take precedence over using `--set build.jobs`. + + let config = Config::parse_inner( + Flags::parse(&[ + "check".to_owned(), + "--config=/does/not/exist".to_owned(), + "--jobs=67890".to_owned(), + "--set=build.jobs=12345".to_owned(), + ]), + |&_| toml::from_str(""), + ); + assert_eq!(config.jobs, Some(67890)); + + // `--set build.jobs` should take precedence over `config.toml`. + let config = Config::parse_inner( + Flags::parse(&[ + "check".to_owned(), + "--config=/does/not/exist".to_owned(), + "--set=build.jobs=12345".to_owned(), + ]), + |&_| { + toml::from_str( + r#" + [build] + jobs = 67890 + "#, + ) + }, + ); + assert_eq!(config.jobs, Some(12345)); + + // `--jobs` > `--set build.jobs` > `config.toml` + let config = Config::parse_inner( + Flags::parse(&[ + "check".to_owned(), + "--jobs=123".to_owned(), + "--config=/does/not/exist".to_owned(), + "--set=build.jobs=456".to_owned(), + ]), + |&_| { + toml::from_str( + r#" + [build] + jobs = 789 + "#, + ) + }, + ); + assert_eq!(config.jobs, Some(123)); +} diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index b37786496cb56..9169bc90a45dc 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -275,4 +275,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "New option `./x setup editor` added, replacing `./x setup vscode` and adding support for vim, emacs and helix.", }, + ChangeInfo { + change_id: 131838, + severity: ChangeSeverity::Info, + summary: "Allow setting `--jobs` in config.toml with `build.jobs`.", + }, ]; From eea74be5c1e8ea8c0ae0c4959b85104de75acef7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Oct 2024 08:49:13 +0200 Subject: [PATCH 09/10] interpret errors: add map_err_kind, rename InterpError -> InterpErrorKind --- .../rustc_const_eval/src/const_eval/error.rs | 7 ++- .../src/const_eval/eval_queries.rs | 4 +- compiler/rustc_const_eval/src/errors.rs | 24 ++++---- .../rustc_const_eval/src/interpret/call.rs | 2 +- .../src/interpret/eval_context.rs | 15 +++-- .../src/interpret/intrinsics.rs | 3 +- .../src/interpret/validity.rs | 32 +++++----- .../src/mir/interpret/allocation.rs | 20 +++--- .../rustc_middle/src/mir/interpret/error.rs | 61 +++++++++++-------- .../rustc_middle/src/mir/interpret/mod.rs | 2 +- .../src/known_panics_lint.rs | 2 +- .../stacked_borrows/diagnostics.rs | 10 +-- .../tree_borrows/diagnostics.rs | 2 +- .../src/borrow_tracker/tree_borrows/tree.rs | 4 +- src/tools/miri/src/diagnostics.rs | 2 +- src/tools/miri/src/intrinsics/simd.rs | 10 +-- src/tools/miri/src/shims/foreign_items.rs | 3 +- 17 files changed, 108 insertions(+), 95 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index fd05664e2f227..6686413bf0254 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -11,7 +11,8 @@ use rustc_span::{Span, Symbol}; use super::CompileTimeMachine; use crate::errors::{self, FrameNote, ReportErrorExt}; use crate::interpret::{ - ErrorHandled, Frame, InterpError, InterpErrorInfo, MachineStopType, err_inval, err_machine_stop, + ErrorHandled, Frame, InterpErrorInfo, InterpErrorKind, MachineStopType, err_inval, + err_machine_stop, }; /// The CTFE machine has some custom error kinds. @@ -57,7 +58,7 @@ impl MachineStopType for ConstEvalErrKind { } } -/// The errors become [`InterpError::MachineStop`] when being raised. +/// The errors become [`InterpErrorKind::MachineStop`] when being raised. impl<'tcx> Into> for ConstEvalErrKind { fn into(self) -> InterpErrorInfo<'tcx> { err_machine_stop!(self).into() @@ -124,7 +125,7 @@ pub fn get_span_and_frames<'tcx>( /// `get_span_and_frames`. pub(super) fn report<'tcx, C, F, E>( tcx: TyCtxt<'tcx>, - error: InterpError<'tcx>, + error: InterpErrorKind<'tcx>, span: Span, get_span_and_frames: C, mk: F, diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 672353e629d44..7319c251bbd99 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -18,7 +18,7 @@ use tracing::{debug, instrument, trace}; use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine}; use crate::const_eval::CheckAlignment; use crate::interpret::{ - CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpError, + CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpErrorKind, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup, create_static_alloc, eval_nullary_intrinsic, intern_const_alloc_recursive, interp_ok, throw_exhaust, }; @@ -463,7 +463,7 @@ fn report_validation_error<'tcx>( error: InterpErrorInfo<'tcx>, alloc_id: AllocId, ) -> ErrorHandled { - if !matches!(error.kind(), InterpError::UndefinedBehavior(_)) { + if !matches!(error.kind(), InterpErrorKind::UndefinedBehavior(_)) { // Some other error happened during validation, e.g. an unsupported operation. return report_eval_error(ecx, cid, error); } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index c943236affc56..211668cf055c6 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -9,7 +9,7 @@ use rustc_errors::{ use rustc_hir::ConstContext; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::mir::interpret::{ - CheckInAllocMsg, CtfeProvenance, ExpectedKind, InterpError, InvalidMetaKind, + CheckInAllocMsg, CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind, InvalidProgramInfo, Misalignment, Pointer, PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, }; @@ -835,23 +835,23 @@ impl ReportErrorExt for UnsupportedOpInfo { } } -impl<'tcx> ReportErrorExt for InterpError<'tcx> { +impl<'tcx> ReportErrorExt for InterpErrorKind<'tcx> { fn diagnostic_message(&self) -> DiagMessage { match self { - InterpError::UndefinedBehavior(ub) => ub.diagnostic_message(), - InterpError::Unsupported(e) => e.diagnostic_message(), - InterpError::InvalidProgram(e) => e.diagnostic_message(), - InterpError::ResourceExhaustion(e) => e.diagnostic_message(), - InterpError::MachineStop(e) => e.diagnostic_message(), + InterpErrorKind::UndefinedBehavior(ub) => ub.diagnostic_message(), + InterpErrorKind::Unsupported(e) => e.diagnostic_message(), + InterpErrorKind::InvalidProgram(e) => e.diagnostic_message(), + InterpErrorKind::ResourceExhaustion(e) => e.diagnostic_message(), + InterpErrorKind::MachineStop(e) => e.diagnostic_message(), } } fn add_args(self, diag: &mut Diag<'_, G>) { match self { - InterpError::UndefinedBehavior(ub) => ub.add_args(diag), - InterpError::Unsupported(e) => e.add_args(diag), - InterpError::InvalidProgram(e) => e.add_args(diag), - InterpError::ResourceExhaustion(e) => e.add_args(diag), - InterpError::MachineStop(e) => e.add_args(&mut |name, value| { + InterpErrorKind::UndefinedBehavior(ub) => ub.add_args(diag), + InterpErrorKind::Unsupported(e) => e.add_args(diag), + InterpErrorKind::InvalidProgram(e) => e.add_args(diag), + InterpErrorKind::ResourceExhaustion(e) => e.add_args(diag), + InterpErrorKind::MachineStop(e) => e.add_args(&mut |name, value| { diag.arg(name, value); }), } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 4945563f4a4ee..85d99900c6ccd 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -471,7 +471,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Don't forget to mark "initially live" locals as live. self.storage_live_for_always_live_locals()?; }; - res.inspect_err(|_| { + res.inspect_err_kind(|_| { // Don't show the incomplete stack frame in the error stacktrace. self.stack_mut().pop(); }) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 02dd7821ef667..a1c773a4b8005 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -19,7 +19,7 @@ use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, instrument, trace}; use super::{ - Frame, FrameInfo, GlobalId, InterpError, InterpErrorInfo, InterpResult, MPlaceTy, Machine, + Frame, FrameInfo, GlobalId, InterpErrorInfo, InterpErrorKind, InterpResult, MPlaceTy, Machine, MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, err_inval, interp_ok, throw_inval, throw_ub, throw_ub_custom, }; @@ -73,7 +73,7 @@ where } impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { - type LayoutOfResult = Result, InterpError<'tcx>>; + type LayoutOfResult = Result, InterpErrorKind<'tcx>>; #[inline] fn layout_tcx_at_span(&self) -> Span { @@ -82,20 +82,25 @@ impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { } #[inline] - fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> InterpError<'tcx> { + fn handle_layout_err( + &self, + err: LayoutError<'tcx>, + _: Span, + _: Ty<'tcx>, + ) -> InterpErrorKind<'tcx> { err_inval!(Layout(err)) } } impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> { - type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpError<'tcx>>; + type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpErrorKind<'tcx>>; fn handle_fn_abi_err( &self, err: FnAbiError<'tcx>, _span: Span, _fn_abi_request: FnAbiRequest<'tcx>, - ) -> InterpError<'tcx> { + ) -> InterpErrorKind<'tcx> { match err { FnAbiError::Layout(err) => err_inval!(Layout(err)), FnAbiError::AdjustForForeignAbi(err) => { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 540898ec6456c..4e603f57c56fb 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -324,13 +324,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { dist.checked_neg().unwrap(), // i64::MIN is impossible as no allocation can be that large CheckInAllocMsg::OffsetFromTest, ) - .map_err(|_| { + .map_err_kind(|_| { // Make the error more specific. err_ub_custom!( fluent::const_eval_offset_from_different_allocations, name = intrinsic_name, ) - .into() })?; // Perform division by size to compute return value. diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 13641ef2bd3f6..b6120ce82fe5f 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_hir as hir; use rustc_middle::bug; use rustc_middle::mir::interpret::ValidationErrorKind::{self, *}; use rustc_middle::mir::interpret::{ - ExpectedKind, InterpError, InterpErrorInfo, InvalidMetaKind, Misalignment, PointerKind, - Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range, interp_ok, + ExpectedKind, InterpErrorKind, InvalidMetaKind, Misalignment, PointerKind, Provenance, + UnsupportedOpInfo, ValidationErrorInfo, alloc_range, interp_ok, }; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; @@ -37,8 +37,8 @@ use super::{ // for the validation errors #[rustfmt::skip] -use super::InterpError::UndefinedBehavior as Ub; -use super::InterpError::Unsupported as Unsup; +use super::InterpErrorKind::UndefinedBehavior as Ub; +use super::InterpErrorKind::Unsupported as Unsup; use super::UndefinedBehaviorInfo::*; use super::UnsupportedOpInfo::*; @@ -97,20 +97,19 @@ macro_rules! try_validation { ($e:expr, $where:expr, $( $( $p:pat_param )|+ => $kind: expr ),+ $(,)? ) => {{ - $e.map_err(|e| { + $e.map_err_kind(|e| { // We catch the error and turn it into a validation failure. We are okay with // allocation here as this can only slow down builds that fail anyway. - let (kind, backtrace) = e.into_parts(); - match kind { + match e { $( $($p)|+ => { err_validation_failure!( $where, $kind - ).into() + ) } ),+, - _ => InterpErrorInfo::from_parts(kind, backtrace), + e => e, } })? }}; @@ -1230,11 +1229,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // No need for an alignment check here, this is not an actual memory access. let alloc = self.ecx.get_ptr_alloc(mplace.ptr(), size)?.expect("we already excluded size 0"); - alloc.get_bytes_strip_provenance().map_err(|err| { + alloc.get_bytes_strip_provenance().map_err_kind(|kind| { // Some error happened, try to provide a more detailed description. // For some errors we might be able to provide extra information. // (This custom logic does not fit the `try_validation!` macro.) - let (kind, backtrace) = err.into_parts(); match kind { Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => { // Some byte was uninitialized, determine which @@ -1247,14 +1245,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.path.push(PathElem::ArrayElem(i)); if matches!(kind, Ub(InvalidUninitBytes(_))) { - err_validation_failure!(self.path, Uninit { expected }).into() + err_validation_failure!(self.path, Uninit { expected }) } else { - err_validation_failure!(self.path, PointerAsInt { expected }).into() + err_validation_failure!(self.path, PointerAsInt { expected }) } } // Propagate upwards (that will also check for unexpected errors). - _ => return InterpErrorInfo::from_parts(kind, backtrace), + err => err, } })?; @@ -1368,12 +1366,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { v.reset_padding(val)?; interp_ok(()) }) - .map_err(|err| { + .map_err_info(|err| { if !matches!( err.kind(), err_ub!(ValidationError { .. }) - | InterpError::InvalidProgram(_) - | InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField) + | InterpErrorKind::InvalidProgram(_) + | InterpErrorKind::Unsupported(UnsupportedOpInfo::ExternTypeField) ) { bug!( "Unexpected error during validation: {}", diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 04d035e27ba3e..ac3baf74ca7c4 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -18,9 +18,9 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_target::abi::{Align, HasDataLayout, Size}; use super::{ - AllocId, BadBytesAccess, CtfeProvenance, InterpError, InterpResult, Pointer, PointerArithmetic, - Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, UndefinedBehaviorInfo, - UnsupportedOpInfo, interp_ok, read_target_uint, write_target_uint, + AllocId, BadBytesAccess, CtfeProvenance, InterpErrorKind, InterpResult, Pointer, + PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, + UndefinedBehaviorInfo, UnsupportedOpInfo, interp_ok, read_target_uint, write_target_uint, }; use crate::ty; @@ -199,22 +199,22 @@ impl From for AllocError { } impl AllocError { - pub fn to_interp_error<'tcx>(self, alloc_id: AllocId) -> InterpError<'tcx> { + pub fn to_interp_error<'tcx>(self, alloc_id: AllocId) -> InterpErrorKind<'tcx> { use AllocError::*; match self { ScalarSizeMismatch(s) => { - InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ScalarSizeMismatch(s)) + InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ScalarSizeMismatch(s)) } - ReadPointerAsInt(info) => InterpError::Unsupported( + ReadPointerAsInt(info) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPointerAsInt(info.map(|b| (alloc_id, b))), ), - OverwritePartialPointer(offset) => InterpError::Unsupported( + OverwritePartialPointer(offset) => InterpErrorKind::Unsupported( UnsupportedOpInfo::OverwritePartialPointer(Pointer::new(alloc_id, offset)), ), - ReadPartialPointer(offset) => InterpError::Unsupported( + ReadPartialPointer(offset) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPartialPointer(Pointer::new(alloc_id, offset)), ), - InvalidUninitBytes(info) => InterpError::UndefinedBehavior( + InvalidUninitBytes(info) => InterpErrorKind::UndefinedBehavior( UndefinedBehaviorInfo::InvalidUninitBytes(info.map(|b| (alloc_id, b))), ), } @@ -318,7 +318,7 @@ impl Allocation { pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> { Self::uninit_inner(size, align, || { ty::tls::with(|tcx| tcx.dcx().delayed_bug("exhausted memory during interpretation")); - InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) + InterpErrorKind::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) }) .into() } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index fcb87e1943505..b520f21ce20c9 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -113,7 +113,7 @@ pub struct InterpErrorInfo<'tcx>(Box>); #[derive(Debug)] struct InterpErrorInfoInner<'tcx> { - kind: InterpError<'tcx>, + kind: InterpErrorKind<'tcx>, backtrace: InterpErrorBacktrace, } @@ -154,21 +154,21 @@ impl InterpErrorBacktrace { } impl<'tcx> InterpErrorInfo<'tcx> { - pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) { + pub fn into_parts(self) -> (InterpErrorKind<'tcx>, InterpErrorBacktrace) { let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self; (kind, backtrace) } - pub fn into_kind(self) -> InterpError<'tcx> { + pub fn into_kind(self) -> InterpErrorKind<'tcx> { self.0.kind } - pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self { + pub fn from_parts(kind: InterpErrorKind<'tcx>, backtrace: InterpErrorBacktrace) -> Self { Self(Box::new(InterpErrorInfoInner { kind, backtrace })) } #[inline] - pub fn kind(&self) -> &InterpError<'tcx> { + pub fn kind(&self) -> &InterpErrorKind<'tcx> { &self.0.kind } } @@ -179,13 +179,13 @@ fn print_backtrace(backtrace: &Backtrace) { impl From for InterpErrorInfo<'_> { fn from(err: ErrorGuaranteed) -> Self { - InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err.into())).into() + InterpErrorKind::InvalidProgram(InvalidProgramInfo::AlreadyReported(err.into())).into() } } impl From for InterpErrorInfo<'_> { fn from(err: ErrorHandled) -> Self { - InterpError::InvalidProgram(match err { + InterpErrorKind::InvalidProgram(match err { ErrorHandled::Reported(r, _span) => InvalidProgramInfo::AlreadyReported(r), ErrorHandled::TooGeneric(_span) => InvalidProgramInfo::TooGeneric, }) @@ -193,8 +193,8 @@ impl From for InterpErrorInfo<'_> { } } -impl<'tcx> From> for InterpErrorInfo<'tcx> { - fn from(kind: InterpError<'tcx>) -> Self { +impl<'tcx> From> for InterpErrorInfo<'tcx> { + fn from(kind: InterpErrorKind<'tcx>) -> Self { InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace: InterpErrorBacktrace::new(), @@ -590,7 +590,7 @@ impl dyn MachineStopType { } #[derive(Debug)] -pub enum InterpError<'tcx> { +pub enum InterpErrorKind<'tcx> { /// The program caused undefined behavior. UndefinedBehavior(UndefinedBehaviorInfo<'tcx>), /// The program did something the interpreter does not support (some of these *might* be UB @@ -606,25 +606,25 @@ pub enum InterpError<'tcx> { MachineStop(Box), } -impl InterpError<'_> { +impl InterpErrorKind<'_> { /// Some errors do string formatting even if the error is never printed. /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors, /// so this method lets us detect them and `bug!` on unexpected errors. pub fn formatted_string(&self) -> bool { matches!( self, - InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. }) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) + InterpErrorKind::Unsupported(UnsupportedOpInfo::Unsupported(_)) + | InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. }) + | InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) ) } } -// Macros for constructing / throwing `InterpError` +// Macros for constructing / throwing `InterpErrorKind` #[macro_export] macro_rules! err_unsup { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::Unsupported( + $crate::mir::interpret::InterpErrorKind::Unsupported( $crate::mir::interpret::UnsupportedOpInfo::$($tt)* ) }; @@ -638,7 +638,7 @@ macro_rules! err_unsup_format { #[macro_export] macro_rules! err_inval { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::InvalidProgram( + $crate::mir::interpret::InterpErrorKind::InvalidProgram( $crate::mir::interpret::InvalidProgramInfo::$($tt)* ) }; @@ -647,7 +647,7 @@ macro_rules! err_inval { #[macro_export] macro_rules! err_ub { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::UndefinedBehavior( + $crate::mir::interpret::InterpErrorKind::UndefinedBehavior( $crate::mir::interpret::UndefinedBehaviorInfo::$($tt)* ) }; @@ -680,7 +680,7 @@ macro_rules! err_ub_custom { #[macro_export] macro_rules! err_exhaust { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::ResourceExhaustion( + $crate::mir::interpret::InterpErrorKind::ResourceExhaustion( $crate::mir::interpret::ResourceExhaustionInfo::$($tt)* ) }; @@ -689,7 +689,7 @@ macro_rules! err_exhaust { #[macro_export] macro_rules! err_machine_stop { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::MachineStop(Box::new($($tt)*)) + $crate::mir::interpret::InterpErrorKind::MachineStop(Box::new($($tt)*)) }; } @@ -792,9 +792,9 @@ impl<'tcx, T> ops::FromResidual for InterpResult_<'tcx, T> { } // Allow `yeet`ing `InterpError` in functions returning `InterpResult_`. -impl<'tcx, T> ops::FromResidual>> for InterpResult_<'tcx, T> { +impl<'tcx, T> ops::FromResidual>> for InterpResult_<'tcx, T> { #[inline] - fn from_residual(ops::Yeet(e): ops::Yeet>) -> Self { + fn from_residual(ops::Yeet(e): ops::Yeet>) -> Self { Self::new(Err(e.into())) } } @@ -856,7 +856,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> { } #[inline] - pub fn map_err( + pub fn map_err_info( self, f: impl FnOnce(InterpErrorInfo<'tcx>) -> InterpErrorInfo<'tcx>, ) -> InterpResult<'tcx, T> { @@ -864,8 +864,19 @@ impl<'tcx, T> InterpResult_<'tcx, T> { } #[inline] - pub fn inspect_err(self, f: impl FnOnce(&InterpErrorInfo<'tcx>)) -> InterpResult<'tcx, T> { - InterpResult_::new(self.disarm().inspect_err(f)) + pub fn map_err_kind( + self, + f: impl FnOnce(InterpErrorKind<'tcx>) -> InterpErrorKind<'tcx>, + ) -> InterpResult<'tcx, T> { + InterpResult_::new(self.disarm().map_err(|mut e| { + e.0.kind = f(e.0.kind); + e + })) + } + + #[inline] + pub fn inspect_err_kind(self, f: impl FnOnce(&InterpErrorKind<'tcx>)) -> InterpResult<'tcx, T> { + InterpResult_::new(self.disarm().inspect_err(|e| f(&e.0.kind))) } #[inline] diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 115bcdbc58987..790ff3e2fe030 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -36,7 +36,7 @@ pub use self::allocation::{ pub use self::error::{ BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, - InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, InvalidProgramInfo, + InterpErrorInfo, InterpErrorKind, InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind, interp_ok, diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 8f490094d6030..2f97c408f2ae6 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -232,7 +232,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { F: FnOnce(&mut Self) -> InterpResult<'tcx, T>, { f(self) - .map_err(|err| { + .map_err_info(|err| { trace!("InterpCx operation failed: {:?}", err); // Some errors shouldn't come up because creating them causes // an allocation, which we should avoid. When that happens, diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs index 146f9902f6fe0..1684abeec6bfb 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs @@ -13,7 +13,7 @@ fn err_sb_ub<'tcx>( msg: String, help: Vec, history: Option, -) -> InterpError<'tcx> { +) -> InterpErrorKind<'tcx> { err_machine_stop!(TerminationInfo::StackedBorrowsUb { msg, help, history }) } @@ -376,7 +376,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { /// Report a descriptive error when `new` could not be granted from `derived_from`. #[inline(never)] // This is only called on fatal code paths - pub(super) fn grant_error(&self, stack: &Stack) -> InterpError<'tcx> { + pub(super) fn grant_error(&self, stack: &Stack) -> InterpErrorKind<'tcx> { let Operation::Retag(op) = &self.operation else { unreachable!("grant_error should only be called during a retag") }; @@ -402,7 +402,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { /// Report a descriptive error when `access` is not permitted based on `tag`. #[inline(never)] // This is only called on fatal code paths - pub(super) fn access_error(&self, stack: &Stack) -> InterpError<'tcx> { + pub(super) fn access_error(&self, stack: &Stack) -> InterpErrorKind<'tcx> { // Deallocation and retagging also do an access as part of their thing, so handle that here, too. let op = match &self.operation { Operation::Access(op) => op, @@ -424,7 +424,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { } #[inline(never)] // This is only called on fatal code paths - pub(super) fn protector_error(&self, item: &Item, kind: ProtectorKind) -> InterpError<'tcx> { + pub(super) fn protector_error(&self, item: &Item, kind: ProtectorKind) -> InterpErrorKind<'tcx> { let protected = match kind { ProtectorKind::WeakProtector => "weakly protected", ProtectorKind::StrongProtector => "strongly protected", @@ -445,7 +445,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { } #[inline(never)] // This is only called on fatal code paths - pub fn dealloc_error(&self, stack: &Stack) -> InterpError<'tcx> { + pub fn dealloc_error(&self, stack: &Stack) -> InterpErrorKind<'tcx> { let Operation::Dealloc(op) = &self.operation else { unreachable!("dealloc_error should only be called during a deallocation") }; diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index cb840f19e3b61..b2fd9b2bf054a 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -298,7 +298,7 @@ pub(super) struct TbError<'node> { impl TbError<'_> { /// Produce a UB error. - pub fn build<'tcx>(self) -> InterpError<'tcx> { + pub fn build<'tcx>(self) -> InterpErrorKind<'tcx> { use TransitionError::*; let cause = self.access_cause; let accessed = self.accessed_info; diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 15cefab1a68e7..a551b017dfc76 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -674,7 +674,7 @@ impl<'tcx> Tree { Ok(()) } }, - |args: ErrHandlerArgs<'_, TransitionError>| -> InterpError<'tcx> { + |args: ErrHandlerArgs<'_, TransitionError>| -> InterpErrorKind<'tcx> { let ErrHandlerArgs { error_kind, conflicting_info, accessed_info } = args; TbError { conflicting_info, @@ -772,7 +772,7 @@ impl<'tcx> Tree { let err_handler = |perms_range: Range, access_cause: diagnostics::AccessCause, args: ErrHandlerArgs<'_, TransitionError>| - -> InterpError<'tcx> { + -> InterpErrorKind<'tcx> { let ErrHandlerArgs { error_kind, conflicting_info, accessed_info } = args; TbError { conflicting_info, diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 475139a3b5192..f055662891ed6 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -214,7 +214,7 @@ pub fn report_error<'tcx>( ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, e: InterpErrorInfo<'tcx>, ) -> Option<(i64, bool)> { - use InterpError::*; + use InterpErrorKind::*; use UndefinedBehaviorInfo::*; let mut msg = vec![]; diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index de293495e8618..59776a484b579 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -245,17 +245,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let val = match which { Op::MirOp(mir_op) => { // This does NaN adjustments. - let val = this.binary_op(mir_op, &left, &right).map_err(|err| { - match err.kind() { - &InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => { + let val = this.binary_op(mir_op, &left, &right).map_err_kind(|kind| { + match kind { + InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => { // This resets the interpreter backtrace, but it's not worth avoiding that. let shift_amount = match shift_amount { Either::Left(v) => v.to_string(), Either::Right(v) => v.to_string(), }; - err_ub_format!("overflowing shift by {shift_amount} in `simd_{intrinsic_name}` in lane {i}").into() + err_ub_format!("overflowing shift by {shift_amount} in `simd_{intrinsic_name}` in lane {i}") } - _ => err + kind => kind } })?; if matches!(mir_op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) { diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 7fce5b6330696..f6f91e589697a 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -289,11 +289,10 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "miri_get_alloc_id" => { let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; - let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err(|_e| { + let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| { err_machine_stop!(TerminationInfo::Abort(format!( "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}" ))) - .into() })?; this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?; } From 1b11ba87ae96deea5d71729ad389f46e101a1991 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Oct 2024 11:29:30 +0200 Subject: [PATCH 10/10] zero-sized accesses are fine on null pointers --- library/core/src/intrinsics.rs | 6 +++--- library/core/src/primitive_docs.rs | 9 ++++++--- library/core/src/ptr/mod.rs | 14 +++++++------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 69ad4f415196c..38e858626b94b 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -3138,7 +3138,7 @@ pub const fn ptr_metadata + ?Sized, M>(_ptr: *cons /// [violate memory safety][read-ownership]. /// /// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointers must be non-null and properly aligned. +/// `0`, the pointers must be properly aligned. /// /// [`read`]: crate::ptr::read /// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value @@ -3261,7 +3261,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us /// [violate memory safety][read-ownership]. /// /// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointers must be non-null and properly aligned. +/// `0`, the pointers must be properly aligned. /// /// [`read`]: crate::ptr::read /// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value @@ -3342,7 +3342,7 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { /// * `dst` must be properly aligned. /// /// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointer must be non-null and properly aligned. +/// `0`, the pointer must be properly aligned. /// /// Additionally, note that changing `*dst` in this way can easily lead to undefined behavior (UB) /// later if the written bytes are not a valid representation of some `T`. For instance, the diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 89936dc12ac36..22fd47b05962b 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -505,9 +505,11 @@ impl () {} /// /// *[See also the `std::ptr` module](ptr).* /// -/// Working with raw pointers in Rust is uncommon, typically limited to a few patterns. -/// Raw pointers can be unaligned or [`null`]. However, when a raw pointer is -/// dereferenced (using the `*` operator), it must be non-null and aligned. +/// Working with raw pointers in Rust is uncommon, typically limited to a few patterns. Raw pointers +/// can be out-of-bounds, unaligned, or [`null`]. However, when loading from or storing to a raw +/// pointer, it must be [valid] for the given access and aligned. When using a field expression, +/// tuple index expression, or array/slice index expression on a raw pointer, it follows the rules +/// of [in-bounds pointer arithmetic][`offset`]. /// /// Storing through a raw pointer using `*ptr = data` calls `drop` on the old value, so /// [`write`] must be used if the type has drop glue and memory is not already @@ -613,6 +615,7 @@ impl () {} /// [`offset`]: pointer::offset /// [`into_raw`]: ../std/boxed/struct.Box.html#method.into_raw /// [`write`]: ptr::write +/// [valid]: ptr#safety #[stable(feature = "rust1", since = "1.0.0")] mod prim_pointer {} diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 09ff7f8cab173..f7036f30a99ec 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1024,7 +1024,7 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// /// * Both `x` and `y` must be properly aligned. /// -/// Note that even if `T` has size `0`, the pointers must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointers must be properly aligned. /// /// [valid]: self#safety /// @@ -1110,7 +1110,7 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { /// beginning at `y` with the same size. /// /// Note that even if the effectively copied size (`count * size_of::()`) is `0`, -/// the pointers must be non-null and properly aligned. +/// the pointers must be properly aligned. /// /// [valid]: self#safety /// @@ -1243,7 +1243,7 @@ const unsafe fn swap_nonoverlapping_simple_untyped(x: *mut T, y: *mut T, coun /// /// * `dst` must point to a properly initialized value of type `T`. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// [valid]: self#safety /// @@ -1300,7 +1300,7 @@ pub const unsafe fn replace(dst: *mut T, src: T) -> T { /// /// * `src` must point to a properly initialized value of type `T`. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// # Examples /// @@ -1555,7 +1555,7 @@ pub const unsafe fn read_unaligned(src: *const T) -> T { /// * `dst` must be properly aligned. Use [`write_unaligned`] if this is not the /// case. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// [valid]: self#safety /// @@ -1774,7 +1774,7 @@ pub const unsafe fn write_unaligned(dst: *mut T, src: T) { /// However, storing non-[`Copy`] types in volatile memory is almost certainly /// incorrect. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// [valid]: self#safety /// [read-ownership]: read#ownership-of-the-returned-value @@ -1853,7 +1853,7 @@ pub unsafe fn read_volatile(src: *const T) -> T { /// /// * `dst` must be properly aligned. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// [valid]: self#safety ///