From 981dc02eaf876a25b95581411e841ba664dc9e97 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 4 Nov 2024 15:41:32 +1100 Subject: [PATCH 01/15] Revert "Avoid nested replacement ranges" from #129346. It caused a test regression in the `cfg_eval.rs` crate. (The bugfix in #129346 was in a different commit; this commit was just a code simplification.) --- .../rustc_parse/src/parser/attr_wrapper.rs | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 8bd615e6d7913..c85d0bd05cbd6 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -136,8 +136,9 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { node_replacements.array_windows() { assert!( - node_range.0.end <= next_node_range.0.start, - "Node ranges should be disjoint: ({:?}, {:?}) ({:?}, {:?})", + node_range.0.end <= next_node_range.0.start + || node_range.0.end >= next_node_range.0.end, + "Node ranges should be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", node_range, tokens, next_node_range, @@ -145,8 +146,20 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { ); } - // Process the replace ranges. - for (node_range, target) in node_replacements.into_iter() { + // Process the replace ranges, starting from the highest start + // position and working our way back. If have tokens like: + // + // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` + // + // Then we will generate replace ranges for both + // the `#[cfg(FALSE)] field: bool` and the entire + // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` + // + // By starting processing from the replace range with the greatest + // start position, we ensure that any (outer) replace range which + // encloses another (inner) replace range will fully overwrite the + // inner range's replacement. + for (node_range, target) in node_replacements.into_iter().rev() { assert!( !node_range.0.is_empty(), "Cannot replace an empty node range: {:?}", @@ -383,9 +396,10 @@ impl<'a> Parser<'a> { // from `ParserRange` form to `NodeRange` form. We will perform the actual // replacement only when we convert the `LazyAttrTokenStream` to an // `AttrTokenStream`. - self.capture_state - .parser_replacements - .drain(parser_replacements_start..parser_replacements_end) + self.capture_state.parser_replacements + [parser_replacements_start..parser_replacements_end] + .iter() + .cloned() .chain(inner_attr_parser_replacements) .map(|(parser_range, data)| { (NodeRange::new(parser_range, collect_pos.start_pos), data) From 560556827cd0d64ea45209a76f0cd7f9bc463949 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 3 Nov 2024 19:58:57 +0100 Subject: [PATCH 02/15] add const_eval_select macro to reduce redundancy --- library/core/src/ffi/c_str.rs | 99 +++++++++++------------ library/core/src/intrinsics.rs | 80 ++++++++++++++++--- library/core/src/macros/mod.rs | 23 +++--- library/core/src/ptr/const_ptr.rs | 128 ++++++++++++++---------------- library/core/src/ub_checks.rs | 65 ++++++++------- 5 files changed, 219 insertions(+), 176 deletions(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 4ea5cbf862645..b57ead374720e 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -3,11 +3,12 @@ use crate::cmp::Ordering; use crate::error::Error; use crate::ffi::c_char; +use crate::intrinsics::const_eval_select; use crate::iter::FusedIterator; use crate::marker::PhantomData; use crate::ptr::NonNull; use crate::slice::memchr; -use crate::{fmt, intrinsics, ops, slice, str}; +use crate::{fmt, ops, slice, str}; // FIXME: because this is doc(inline)d, we *have* to use intra-doc links because the actual link // depends on where the item is being documented. however, since this is libcore, we can't @@ -411,37 +412,35 @@ impl CStr { #[rustc_const_stable(feature = "const_cstr_unchecked", since = "1.59.0")] #[rustc_allow_const_fn_unstable(const_eval_select)] pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { - #[inline] - fn rt_impl(bytes: &[u8]) -> &CStr { - // Chance at catching some UB at runtime with debug builds. - debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0); - - // SAFETY: Casting to CStr is safe because its internal representation - // is a [u8] too (safe only inside std). - // Dereferencing the obtained pointer is safe because it comes from a - // reference. Making a reference is then safe because its lifetime - // is bound by the lifetime of the given `bytes`. - unsafe { &*(bytes as *const [u8] as *const CStr) } - } - - const fn const_impl(bytes: &[u8]) -> &CStr { - // Saturating so that an empty slice panics in the assert with a good - // message, not here due to underflow. - let mut i = bytes.len().saturating_sub(1); - assert!(!bytes.is_empty() && bytes[i] == 0, "input was not nul-terminated"); - - // Ending nul byte exists, skip to the rest. - while i != 0 { - i -= 1; - let byte = bytes[i]; - assert!(byte != 0, "input contained interior nul"); + const_eval_select!( + (bytes: &[u8]) -> &CStr: + if const { + // Saturating so that an empty slice panics in the assert with a good + // message, not here due to underflow. + let mut i = bytes.len().saturating_sub(1); + assert!(!bytes.is_empty() && bytes[i] == 0, "input was not nul-terminated"); + + // Ending nul byte exists, skip to the rest. + while i != 0 { + i -= 1; + let byte = bytes[i]; + assert!(byte != 0, "input contained interior nul"); + } + + // SAFETY: See runtime cast comment below. + unsafe { &*(bytes as *const [u8] as *const CStr) } + } else #[inline] { + // Chance at catching some UB at runtime with debug builds. + debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0); + + // SAFETY: Casting to CStr is safe because its internal representation + // is a [u8] too (safe only inside std). + // Dereferencing the obtained pointer is safe because it comes from a + // reference. Making a reference is then safe because its lifetime + // is bound by the lifetime of the given `bytes`. + unsafe { &*(bytes as *const [u8] as *const CStr) } } - - // SAFETY: See `rt_impl` cast. - unsafe { &*(bytes as *const [u8] as *const CStr) } - } - - intrinsics::const_eval_select((bytes,), const_impl, rt_impl) + ) } /// Returns the inner pointer to this C string. @@ -735,29 +734,27 @@ impl AsRef for CStr { #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0"))] #[rustc_allow_const_fn_unstable(const_eval_select)] const unsafe fn strlen(ptr: *const c_char) -> usize { - const fn strlen_ct(s: *const c_char) -> usize { - let mut len = 0; - - // SAFETY: Outer caller has provided a pointer to a valid C string. - while unsafe { *s.add(len) } != 0 { - len += 1; - } + const_eval_select!( + (s: *const c_char = ptr) -> usize: + if const { + let mut len = 0; + + // SAFETY: Outer caller has provided a pointer to a valid C string. + while unsafe { *s.add(len) } != 0 { + len += 1; + } - len - } + len + } else #[inline] { + extern "C" { + /// Provided by libc or compiler_builtins. + fn strlen(s: *const c_char) -> usize; + } - #[inline] - fn strlen_rt(s: *const c_char) -> usize { - extern "C" { - /// Provided by libc or compiler_builtins. - fn strlen(s: *const c_char) -> usize; + // SAFETY: Outer caller has provided a pointer to a valid C string. + unsafe { strlen(s) } } - - // SAFETY: Outer caller has provided a pointer to a valid C string. - unsafe { strlen(s) } - } - - intrinsics::const_eval_select((ptr,), strlen_ct, strlen_rt) + ) } /// An iterator over the bytes of a [`CStr`], without the nul terminator. diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index fc09da7bcbc65..d82d911426427 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2788,6 +2788,65 @@ where unreachable!() } +/// A macro to make it easier to invoke const_eval_select. Use as follows: +/// ```rust,ignore (just a macro example) +/// const_eval_select!( +/// #[inline] +/// (arg1: i32 = some_expr, arg2: T = other_expr) -> U: +/// if const { +/// // Compile-time code goes here. +/// } else { +/// // Run-time code goes here. +/// } +/// ) +/// ``` +pub(crate) macro const_eval_select { + ( + $(#[$attr:meta])* + ($($arg:ident : $ty:ty = $val:expr),* $(,)?) $( -> $ret:ty )?: + if const + $(#[$compiletime_attr:meta])* $compiletime:block + else + $(#[$runtime_attr:meta])* $runtime:block + ) => {{ + $(#[$attr])* + $(#[$runtime_attr])* + fn runtime($($arg: $ty),*) $( -> $ret )? { + $runtime + } + + $(#[$attr])* + $(#[$compiletime_attr])* + const fn compiletime($($arg: $ty),*) $( -> $ret )? { + // Don't warn if one of the arguments is unused. + $(let _ = $arg;)* + + $compiletime + } + + const_eval_select(($($val,)*), compiletime, runtime) + }}, + // We support leaving away the `val` expressions for *all* arguments + // (but not for *some* arguments, that's too tricky). + ( + $(#[$attr:meta])* + ($($arg:ident : $ty:ty),* $(,)?) -> $ret:ty: + if const + $(#[$compiletime_attr:meta])* $compiletime:block + else + $(#[$runtime_attr:meta])* $runtime:block + ) => { + $crate::intrinsics::const_eval_select!( + $(#[$attr])* + ($($arg : $ty = $arg),*) -> $ret: + if const + $(#[$compiletime_attr])* $compiletime + else + $(#[$runtime_attr])* $runtime + ) + }, +} + /// Returns whether the argument's value is statically known at /// compile-time. /// @@ -2830,7 +2889,7 @@ where /// # Stability concerns /// /// While it is safe to call, this intrinsic may behave differently in -/// a `const` context than otherwise. See the [`const_eval_select`] +/// a `const` context than otherwise. See the [`const_eval_select()`] /// documentation for an explanation of the issues this can cause. Unlike /// `const_eval_select`, this intrinsic isn't guaranteed to behave /// deterministically even in a `const` context. @@ -3734,14 +3793,15 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize fn miri_promise_symbolic_alignment(ptr: *const (), align: usize); } - fn runtime(ptr: *const (), align: usize) { - // SAFETY: this call is always safe. - unsafe { - miri_promise_symbolic_alignment(ptr, align); + const_eval_select!( + (ptr: *const (), align: usize): + if const { + // Do nothing. + } else { + // SAFETY: this call is always safe. + unsafe { + miri_promise_symbolic_alignment(ptr, align); + } } - } - - const fn compiletime(_ptr: *const (), _align: usize) {} - - const_eval_select((ptr, align), compiletime, runtime); + ) } diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 9a91ff82acd7c..c0bf798fb7069 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -24,18 +24,6 @@ macro_rules! panic { #[doc(hidden)] pub macro const_panic { ($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty = $val:expr),* $(,)?) => {{ - #[inline] - #[track_caller] - fn runtime($($arg: $ty),*) -> ! { - $crate::panic!($runtime_msg); - } - - #[inline] - #[track_caller] - const fn compiletime($(_: $ty),*) -> ! { - $crate::panic!($const_msg); - } - // Wrap call to `const_eval_select` in a function so that we can // add the `rustc_allow_const_fn_unstable`. This is okay to do // because both variants will panic, just with different messages. @@ -44,7 +32,16 @@ pub macro const_panic { #[track_caller] #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_panic", since = "CURRENT_RUSTC_VERSION"))] const fn do_panic($($arg: $ty),*) -> ! { - $crate::intrinsics::const_eval_select(($($arg),* ,), compiletime, runtime) + $crate::intrinsics::const_eval_select!( + #[inline] + #[track_caller] + ($($arg: $ty),*) -> !: + if const { + $crate::panic!($const_msg) + } else { + $crate::panic!($runtime_msg) + } + ) } do_panic($($val),*) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index a4e8e373e041e..7c5fa6b4e9072 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -33,26 +33,24 @@ impl *const T { #[rustc_diagnostic_item = "ptr_const_is_null"] #[inline] pub const fn is_null(self) -> bool { - #[inline] - fn runtime_impl(ptr: *const u8) -> bool { - ptr.addr() == 0 - } - - #[inline] - #[rustc_const_unstable(feature = "const_ptr_is_null", issue = "74939")] - const fn const_impl(ptr: *const u8) -> bool { - match (ptr).guaranteed_eq(null_mut()) { - Some(res) => res, - // To remain maximally convervative, we stop execution when we don't - // know whether the pointer is null or not. - // We can *not* return `false` here, that would be unsound in `NonNull::new`! - None => panic!("null-ness of this pointer cannot be determined in const context"), - } - } - // Compare via a cast to a thin pointer, so fat pointers are only // considering their "data" part for null-ness. - const_eval_select((self as *const u8,), const_impl, runtime_impl) + let ptr = self as *const u8; + const_eval_select!( + #[inline] + (ptr: *const u8) -> bool: + if const #[rustc_const_unstable(feature = "const_ptr_is_null", issue = "74939")] { + match (ptr).guaranteed_eq(null_mut()) { + Some(res) => res, + // To remain maximally convervative, we stop execution when we don't + // know whether the pointer is null or not. + // We can *not* return `false` here, that would be unsound in `NonNull::new`! + None => panic!("null-ness of this pointer cannot be determined in const context"), + } + } else { + ptr.addr() == 0 + } + ) } /// Casts to a pointer of another type. @@ -410,22 +408,20 @@ impl *const T { #[inline] #[rustc_allow_const_fn_unstable(const_eval_select)] const fn runtime_offset_nowrap(this: *const (), count: isize, size: usize) -> bool { - #[inline] - fn runtime(this: *const (), count: isize, size: usize) -> bool { - // We know `size <= isize::MAX` so the `as` cast here is not lossy. - let Some(byte_offset) = count.checked_mul(size as isize) else { - return false; - }; - let (_, overflow) = this.addr().overflowing_add_signed(byte_offset); - !overflow - } - - const fn comptime(_: *const (), _: isize, _: usize) -> bool { - true - } - // We can use const_eval_select here because this is only for UB checks. - intrinsics::const_eval_select((this, count, size), comptime, runtime) + const_eval_select!( + (this: *const (), count: isize, size: usize) -> bool: + if const { + true + } else #[inline] { + // We know `size <= isize::MAX` so the `as` cast here is not lossy. + let Some(byte_offset) = count.checked_mul(size as isize) else { + return false; + }; + let (_, overflow) = this.addr().overflowing_add_signed(byte_offset); + !overflow + } + ) } ub_checks::assert_unsafe_precondition!( @@ -763,14 +759,14 @@ impl *const T { { #[rustc_allow_const_fn_unstable(const_eval_select)] 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 - } - - intrinsics::const_eval_select((this, origin), comptime, runtime) + const_eval_select!( + (this: *const (), origin: *const ()) -> bool: + if const { + true + } else { + this >= origin + } + ) } ub_checks::assert_unsafe_precondition!( @@ -924,20 +920,18 @@ impl *const T { #[inline] #[rustc_allow_const_fn_unstable(const_eval_select)] const fn runtime_add_nowrap(this: *const (), count: usize, size: usize) -> bool { - #[inline] - fn runtime(this: *const (), count: usize, size: usize) -> bool { - let Some(byte_offset) = count.checked_mul(size) else { - return false; - }; - let (_, overflow) = this.addr().overflowing_add(byte_offset); - byte_offset <= (isize::MAX as usize) && !overflow - } - - const fn comptime(_: *const (), _: usize, _: usize) -> bool { - true - } - - intrinsics::const_eval_select((this, count, size), comptime, runtime) + const_eval_select!( + (this: *const (), count: usize, size: usize) -> bool: + if const { + true + } else #[inline] { + let Some(byte_offset) = count.checked_mul(size) else { + return false; + }; + let (_, overflow) = this.addr().overflowing_add(byte_offset); + byte_offset <= (isize::MAX as usize) && !overflow + } + ) } #[cfg(debug_assertions)] // Expensive, and doesn't catch much in the wild. @@ -1033,19 +1027,17 @@ impl *const T { #[inline] #[rustc_allow_const_fn_unstable(const_eval_select)] const fn runtime_sub_nowrap(this: *const (), count: usize, size: usize) -> bool { - #[inline] - fn runtime(this: *const (), count: usize, size: usize) -> bool { - let Some(byte_offset) = count.checked_mul(size) else { - return false; - }; - byte_offset <= (isize::MAX as usize) && this.addr() >= byte_offset - } - - const fn comptime(_: *const (), _: usize, _: usize) -> bool { - true - } - - intrinsics::const_eval_select((this, count, size), comptime, runtime) + const_eval_select!( + (this: *const (), count: usize, size: usize) -> bool: + if const { + true + } else #[inline] { + let Some(byte_offset) = count.checked_mul(size) else { + return false; + }; + byte_offset <= (isize::MAX as usize) && this.addr() >= byte_offset + } + ) } #[cfg(debug_assertions)] // Expensive, and doesn't catch much in the wild. diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs index dd1454f401eaf..972d9ae26d2bf 100644 --- a/library/core/src/ub_checks.rs +++ b/library/core/src/ub_checks.rs @@ -95,20 +95,19 @@ pub use intrinsics::ub_checks as check_library_ub; #[inline] #[rustc_allow_const_fn_unstable(const_eval_select)] pub(crate) const fn check_language_ub() -> bool { - #[inline] - fn runtime() -> bool { - // Disable UB checks in Miri. - !cfg!(miri) - } - - #[inline] - const fn comptime() -> bool { - // Always disable UB checks. - false - } - // Only used for UB checks so we may const_eval_select. - intrinsics::ub_checks() && const_eval_select((), comptime, runtime) + intrinsics::ub_checks() + && const_eval_select!( + #[inline] + () -> bool: + if const { + // Always disable UB checks. + false + } else { + // Disable UB checks in Miri. + !cfg!(miri) + } + ) } /// Checks whether `ptr` is properly aligned with respect to the given alignment, and @@ -154,26 +153,24 @@ pub(crate) const fn is_nonoverlapping( 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 - } - // This is just for safety checks so we can const_eval_select. - const_eval_select((src, dst, size, count), comptime, runtime) + const_eval_select!( + #[inline] + (src: *const (), dst: *const (), size: usize, count: usize) -> bool: + if const { + true + } else { + 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 + } + ) } From 7faa84e20e02c8b7a4afb7c5741e8b59819d7b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Bj=C3=B8rnager=20Jensen?= Date: Mon, 4 Nov 2024 12:28:26 +0100 Subject: [PATCH 03/15] Stabilise 'const_char_encode_utf16'; --- library/core/src/char/methods.rs | 7 +++++-- library/core/src/lib.rs | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 701e34b135e23..39f421df22747 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -711,7 +711,7 @@ impl char { /// '𝕊'.encode_utf16(&mut b); /// ``` #[stable(feature = "unicode_encode_char", since = "1.15.0")] - #[rustc_const_unstable(feature = "const_char_encode_utf16", issue = "130660")] + #[rustc_const_stable(feature = "const_char_encode_utf16", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn encode_utf16(self, dst: &mut [u16]) -> &mut [u16] { encode_utf16_raw(self as u32, dst) @@ -1819,7 +1819,10 @@ pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { /// Panics if the buffer is not large enough. /// A buffer of length 2 is large enough to encode any `char`. #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] -#[rustc_const_unstable(feature = "const_char_encode_utf16", issue = "130660")] +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_char_encode_utf16", since = "CURRENT_RUSTC_VERSION") +)] #[doc(hidden)] #[inline] pub const fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] { diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 12f6997dbeae7..ecebddaa0aef2 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -114,7 +114,6 @@ #![feature(const_align_of_val_raw)] #![feature(const_alloc_layout)] #![feature(const_black_box)] -#![feature(const_char_encode_utf16)] #![feature(const_eval_select)] #![feature(const_exact_div)] #![feature(const_float_methods)] From e9161db5b55ee5398e10a6806cb62d67e725278c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 4 Nov 2024 14:28:35 +0100 Subject: [PATCH 04/15] Fix invalid coverage computation when `--output-format=json` is enabled --- src/librustdoc/clean/mod.rs | 4 ++-- src/librustdoc/core.rs | 7 +++++++ src/librustdoc/passes/calculate_doc_coverage.rs | 1 + src/librustdoc/passes/strip_hidden.rs | 2 +- src/librustdoc/passes/strip_priv_imports.rs | 2 +- src/librustdoc/passes/strip_private.rs | 2 +- src/librustdoc/visit_ast.rs | 2 +- 7 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c367eed53e07b..bd4704e8b28ed 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2907,7 +2907,7 @@ fn clean_extern_crate<'tcx>( None => false, } }) - && !cx.output_format.is_json(); + && !cx.is_json(); let krate_owner_def_id = krate.owner_id.def_id; if please_inline { @@ -3000,7 +3000,7 @@ fn clean_use_statement_inner<'tcx>( // forcefully don't inline if this is not public or if the // #[doc(no_inline)] attribute is present. // Don't inline doc(hidden) imports so they can be stripped at a later stage. - let mut denied = cx.output_format.is_json() + let mut denied = cx.is_json() || !(visibility.is_public() || (cx.render_options.document_private && is_visible_from_parent_mod)) || pub_underscore diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 0f7d4d3e8f318..274b61ae1d3c0 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -121,6 +121,13 @@ impl<'tcx> DocContext<'tcx> { _ => None, } } + + /// Returns `true` if the JSON output format is enabled for generating the crate content. + /// + /// If another option like `--show-coverage` is enabled, it will return false. + pub(crate) fn is_json(&self) -> bool { + self.output_format.is_json() && !self.show_coverage + } } /// Creates a new `DiagCtxt` that can be used to emit warnings and errors. diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index d27e737764dcf..9f9a093da8a38 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -132,6 +132,7 @@ impl<'a, 'b> CoverageCalculator<'a, 'b> { fn print_results(&self) { let output_format = self.ctx.output_format; + // In this case we want to ensure that the `OutputFormat` is JSON and NOT the `DocContext`. if output_format.is_json() { println!("{}", self.to_json()); return; diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index aba04283e59dc..2998289fd273a 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -23,7 +23,7 @@ pub(crate) const STRIP_HIDDEN: Pass = Pass { /// Strip items marked `#[doc(hidden)]` pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { let mut retained = ItemIdSet::default(); - let is_json_output = cx.output_format.is_json() && !cx.show_coverage; + let is_json_output = cx.is_json(); // strip all #[doc(hidden)] items let krate = { diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs index 2e9f06bd0a30c..20659d5e8c69c 100644 --- a/src/librustdoc/passes/strip_priv_imports.rs +++ b/src/librustdoc/passes/strip_priv_imports.rs @@ -13,7 +13,7 @@ pub(crate) const STRIP_PRIV_IMPORTS: Pass = Pass { }; pub(crate) fn strip_priv_imports(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { - let is_json_output = cx.output_format.is_json() && !cx.show_coverage; + let is_json_output = cx.is_json(); ImportStripper { tcx: cx.tcx, is_json_output, diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index 78f0ad277408b..916c2f0657e47 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -18,7 +18,7 @@ pub(crate) const STRIP_PRIVATE: Pass = Pass { pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { // This stripper collects all *retained* nodes. let mut retained = ItemIdSet::default(); - let is_json_output = cx.output_format.is_json() && !cx.show_coverage; + let is_json_output = cx.is_json(); // strip all private items { diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index f789aca73784d..00f5251596b60 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -235,7 +235,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { return false; } - if self.cx.output_format.is_json() { + if self.cx.is_json() { return false; } From 0eff07ee4ec22e7f83462b4b1a42dc04e9d5570b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 4 Nov 2024 14:46:04 +0100 Subject: [PATCH 05/15] Add UI regressions tests for rustdoc `--show-coverage` option --- tests/rustdoc-ui/show-coverage-json.rs | 13 +++++++++++++ tests/rustdoc-ui/show-coverage-json.stdout | 1 + tests/rustdoc-ui/show-coverage.rs | 13 +++++++++++++ tests/rustdoc-ui/show-coverage.stdout | 7 +++++++ 4 files changed, 34 insertions(+) create mode 100644 tests/rustdoc-ui/show-coverage-json.rs create mode 100644 tests/rustdoc-ui/show-coverage-json.stdout create mode 100644 tests/rustdoc-ui/show-coverage.rs create mode 100644 tests/rustdoc-ui/show-coverage.stdout diff --git a/tests/rustdoc-ui/show-coverage-json.rs b/tests/rustdoc-ui/show-coverage-json.rs new file mode 100644 index 0000000000000..3851e34fe3599 --- /dev/null +++ b/tests/rustdoc-ui/show-coverage-json.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -Z unstable-options --show-coverage --output-format=json +//@ check-pass + +mod bar { + /// a + /// + /// ``` + /// let x = 0; + /// ``` + pub struct Foo; +} + +pub use bar::Foo; diff --git a/tests/rustdoc-ui/show-coverage-json.stdout b/tests/rustdoc-ui/show-coverage-json.stdout new file mode 100644 index 0000000000000..ed5b5a60212eb --- /dev/null +++ b/tests/rustdoc-ui/show-coverage-json.stdout @@ -0,0 +1 @@ +{"$DIR/show-coverage-json.rs":{"total":2,"with_docs":1,"total_examples":2,"with_examples":1}} diff --git a/tests/rustdoc-ui/show-coverage.rs b/tests/rustdoc-ui/show-coverage.rs new file mode 100644 index 0000000000000..00bb1606a82cb --- /dev/null +++ b/tests/rustdoc-ui/show-coverage.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -Z unstable-options --show-coverage +//@ check-pass + +mod bar { + /// a + /// + /// ``` + /// let x = 0; + /// ``` + pub struct Foo; +} + +pub use bar::Foo; diff --git a/tests/rustdoc-ui/show-coverage.stdout b/tests/rustdoc-ui/show-coverage.stdout new file mode 100644 index 0000000000000..b3b7679771f22 --- /dev/null +++ b/tests/rustdoc-ui/show-coverage.stdout @@ -0,0 +1,7 @@ ++-------------------------------------+------------+------------+------------+------------+ +| File | Documented | Percentage | Examples | Percentage | ++-------------------------------------+------------+------------+------------+------------+ +| ...ests/rustdoc-ui/show-coverage.rs | 1 | 50.0% | 1 | 50.0% | ++-------------------------------------+------------+------------+------------+------------+ +| Total | 1 | 50.0% | 1 | 50.0% | ++-------------------------------------+------------+------------+------------+------------+ From 1916bbfc6ebbde010f759888e318f6606741e42a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 4 Nov 2024 10:49:05 +0100 Subject: [PATCH 06/15] move const_panic/assert macros into core::panic module (since they are just internal helpers) --- library/core/src/char/methods.rs | 2 +- library/core/src/ffi/c_str.rs | 8 +- library/core/src/intrinsics.rs | 28 +++-- library/core/src/macros/mod.rs | 58 --------- library/core/src/num/f128.rs | 2 +- library/core/src/num/f16.rs | 2 +- library/core/src/num/f32.rs | 2 +- library/core/src/num/f64.rs | 2 +- library/core/src/num/mod.rs | 2 +- library/core/src/panic.rs | 56 +++++++++ library/core/src/panicking.rs | 62 +++++---- library/core/src/ptr/const_ptr.rs | 20 +-- library/core/src/ptr/mut_ptr.rs | 81 ++++++------ library/core/src/slice/ascii.rs | 152 +++++++++++------------ library/core/src/slice/index.rs | 2 +- library/core/src/slice/memchr.rs | 98 +++++++-------- library/core/src/str/validations.rs | 17 ++- library/core/src/ub_checks.rs | 26 ++-- tests/ui/consts/const-ptr-is-null.stderr | 4 +- 19 files changed, 301 insertions(+), 323 deletions(-) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 701e34b135e23..3915afb49d641 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1,7 +1,7 @@ //! impl char {} use super::*; -use crate::macros::const_panic; +use crate::panic::const_panic; use crate::slice; use crate::str::from_utf8_unchecked_mut; use crate::unicode::printable::is_printable; diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index b57ead374720e..85571222b5cd2 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -413,7 +413,7 @@ impl CStr { #[rustc_allow_const_fn_unstable(const_eval_select)] pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { const_eval_select!( - (bytes: &[u8]) -> &CStr: + @capture { bytes: &[u8] } -> &CStr: if const { // Saturating so that an empty slice panics in the assert with a good // message, not here due to underflow. @@ -429,7 +429,7 @@ impl CStr { // SAFETY: See runtime cast comment below. unsafe { &*(bytes as *const [u8] as *const CStr) } - } else #[inline] { + } else { // Chance at catching some UB at runtime with debug builds. debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0); @@ -735,7 +735,7 @@ impl AsRef for CStr { #[rustc_allow_const_fn_unstable(const_eval_select)] const unsafe fn strlen(ptr: *const c_char) -> usize { const_eval_select!( - (s: *const c_char = ptr) -> usize: + @capture { s: *const c_char = ptr } -> usize: if const { let mut len = 0; @@ -745,7 +745,7 @@ const unsafe fn strlen(ptr: *const c_char) -> usize { } len - } else #[inline] { + } else { extern "C" { /// Provided by libc or compiler_builtins. fn strlen(s: *const c_char) -> usize; diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index d82d911426427..09c76fdad9368 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2791,31 +2791,35 @@ where /// A macro to make it easier to invoke const_eval_select. Use as follows: /// ```rust,ignore (just a macro example) /// const_eval_select!( -/// #[inline] -/// (arg1: i32 = some_expr, arg2: T = other_expr) -> U: -/// if const { +/// @capture { arg1: i32 = some_expr, arg2: T = other_expr } -> U: +/// if const #[attributes_for_const_arm] { /// // Compile-time code goes here. -/// } else { +/// } else #[attributes_for_runtime_arm] { /// // Run-time code goes here. /// } /// ) /// ``` +/// The `@capture` block declares which surrounding variables / expressions can be +/// used inside the `if const`. +/// Note that the two arms of this `if` really each become their own function, which is why the +/// macro supports setting attributes for those functions. The runtime function is always +/// markes as `#[inline]`. +/// +/// See [`const_eval_select()`] for the rules and requirements around that intrinsic. pub(crate) macro const_eval_select { ( - $(#[$attr:meta])* - ($($arg:ident : $ty:ty = $val:expr),* $(,)?) $( -> $ret:ty )?: + @capture { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? : if const $(#[$compiletime_attr:meta])* $compiletime:block else $(#[$runtime_attr:meta])* $runtime:block ) => {{ - $(#[$attr])* $(#[$runtime_attr])* + #[inline] fn runtime($($arg: $ty),*) $( -> $ret )? { $runtime } - $(#[$attr])* $(#[$compiletime_attr])* const fn compiletime($($arg: $ty),*) $( -> $ret )? { // Don't warn if one of the arguments is unused. @@ -2829,16 +2833,14 @@ pub(crate) macro const_eval_select { // We support leaving away the `val` expressions for *all* arguments // (but not for *some* arguments, that's too tricky). ( - $(#[$attr:meta])* - ($($arg:ident : $ty:ty),* $(,)?) -> $ret:ty: + @capture { $($arg:ident : $ty:ty),* $(,)? } $( -> $ret:ty )? : if const $(#[$compiletime_attr:meta])* $compiletime:block else $(#[$runtime_attr:meta])* $runtime:block ) => { $crate::intrinsics::const_eval_select!( - $(#[$attr])* - ($($arg : $ty = $arg),*) -> $ret: + @capture { $($arg : $ty = $arg),* } $(-> $ret)? : if const $(#[$compiletime_attr])* $compiletime else @@ -3794,7 +3796,7 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize } const_eval_select!( - (ptr: *const (), align: usize): + @capture { ptr: *const (), align: usize}: if const { // Do nothing. } else { diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index c0bf798fb7069..771c2d31b60e0 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -12,51 +12,6 @@ macro_rules! panic { }; } -/// Helper macro for panicking in a `const fn`. -/// Invoke as: -/// ```rust,ignore (just an example) -/// core::macros::const_panic!("boring message", "flavored message {a} {b:?}", a: u32 = foo.len(), b: Something = bar); -/// ``` -/// where the first message will be printed in const-eval, -/// and the second message will be printed at runtime. -// All uses of this macro are FIXME(const-hack). -#[unstable(feature = "panic_internals", issue = "none")] -#[doc(hidden)] -pub macro const_panic { - ($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty = $val:expr),* $(,)?) => {{ - // Wrap call to `const_eval_select` in a function so that we can - // add the `rustc_allow_const_fn_unstable`. This is okay to do - // because both variants will panic, just with different messages. - #[rustc_allow_const_fn_unstable(const_eval_select)] - #[inline(always)] - #[track_caller] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_panic", since = "CURRENT_RUSTC_VERSION"))] - const fn do_panic($($arg: $ty),*) -> ! { - $crate::intrinsics::const_eval_select!( - #[inline] - #[track_caller] - ($($arg: $ty),*) -> !: - if const { - $crate::panic!($const_msg) - } else { - $crate::panic!($runtime_msg) - } - ) - } - - do_panic($($val),*) - }}, - // We support leaving away the `val` expressions for *all* arguments - // (but not for *some* arguments, that's too tricky). - ($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty),* $(,)?) => { - $crate::macros::const_panic!( - $const_msg, - $runtime_msg, - $($arg: $ty = $arg),* - ) - }, -} - /// Asserts that two expressions are equal to each other (using [`PartialEq`]). /// /// Assertions are always checked in both debug and release builds, and cannot @@ -241,19 +196,6 @@ pub macro assert_matches { }, } -/// A version of `assert` that prints a non-formatting message in const contexts. -/// -/// See [`const_panic!`]. -#[unstable(feature = "panic_internals", issue = "none")] -#[doc(hidden)] -pub macro const_assert { - ($condition: expr, $const_msg:literal, $runtime_msg:literal, $($arg:tt)*) => {{ - if !$crate::intrinsics::likely($condition) { - $crate::macros::const_panic!($const_msg, $runtime_msg, $($arg)*) - } - }} -} - /// A macro for defining `#[cfg]` match-like statements. /// /// It is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade of diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 7709e7de01b0b..0484611958d87 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -14,9 +14,9 @@ use crate::convert::FloatToInt; #[cfg(not(test))] use crate::intrinsics; -use crate::macros::const_assert; use crate::mem; use crate::num::FpCategory; +use crate::panic::const_assert; /// Basic mathematical constants. #[unstable(feature = "f128", issue = "116909")] diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index eb0225c58b837..898caf835bfd3 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -14,9 +14,9 @@ use crate::convert::FloatToInt; #[cfg(not(test))] use crate::intrinsics; -use crate::macros::const_assert; use crate::mem; use crate::num::FpCategory; +use crate::panic::const_assert; /// Basic mathematical constants. #[unstable(feature = "f16", issue = "116909")] diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 686a6c5092792..20ece883da60b 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -14,9 +14,9 @@ use crate::convert::FloatToInt; #[cfg(not(test))] use crate::intrinsics; -use crate::macros::const_assert; use crate::mem; use crate::num::FpCategory; +use crate::panic::const_assert; /// The radix or base of the internal representation of `f32`. /// Use [`f32::RADIX`] instead. diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 798cb4b1b5ccd..5640e71788b85 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -14,9 +14,9 @@ use crate::convert::FloatToInt; #[cfg(not(test))] use crate::intrinsics; -use crate::macros::const_assert; use crate::mem; use crate::num::FpCategory; +use crate::panic::const_assert; /// The radix or base of the internal representation of `f64`. /// Use [`f64::RADIX`] instead. diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index f4930ca5c7dbd..5a69dc0c7242b 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -2,7 +2,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::macros::const_panic; +use crate::panic::const_panic; use crate::str::FromStr; use crate::ub_checks::assert_unsafe_precondition; use crate::{ascii, intrinsics, mem}; diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index c95a000561c35..f8f3962ce55ac 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -189,3 +189,59 @@ pub unsafe trait PanicPayload: crate::fmt::Display { None } } + +/// Helper macro for panicking in a `const fn`. +/// Invoke as: +/// ```rust,ignore (just an example) +/// core::macros::const_panic!("boring message", "flavored message {a} {b:?}", a: u32 = foo.len(), b: Something = bar); +/// ``` +/// where the first message will be printed in const-eval, +/// and the second message will be printed at runtime. +// All uses of this macro are FIXME(const-hack). +#[unstable(feature = "panic_internals", issue = "none")] +#[doc(hidden)] +pub macro const_panic { + ($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty = $val:expr),* $(,)?) => {{ + // Wrap call to `const_eval_select` in a function so that we can + // add the `rustc_allow_const_fn_unstable`. This is okay to do + // because both variants will panic, just with different messages. + #[rustc_allow_const_fn_unstable(const_eval_select)] + #[inline(always)] + #[track_caller] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_panic", since = "CURRENT_RUSTC_VERSION"))] + const fn do_panic($($arg: $ty),*) -> ! { + $crate::intrinsics::const_eval_select!( + @capture { $($arg: $ty),* } -> !: + if const #[track_caller] { + $crate::panic!($const_msg) + } else #[track_caller] { + $crate::panic!($runtime_msg) + } + ) + } + + do_panic($($val),*) + }}, + // We support leaving away the `val` expressions for *all* arguments + // (but not for *some* arguments, that's too tricky). + ($const_msg:literal, $runtime_msg:literal, $($arg:ident : $ty:ty),* $(,)?) => { + $crate::panic::const_panic!( + $const_msg, + $runtime_msg, + $($arg: $ty = $arg),* + ) + }, +} + +/// A version of `assert` that prints a non-formatting message in const contexts. +/// +/// See [`const_panic!`]. +#[unstable(feature = "panic_internals", issue = "none")] +#[doc(hidden)] +pub macro const_assert { + ($condition: expr, $const_msg:literal, $runtime_msg:literal, $($arg:tt)*) => {{ + if !$crate::intrinsics::likely($condition) { + $crate::panic::const_panic!($const_msg, $runtime_msg, $($arg)*) + } + }} +} diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 9071d6719a30e..f603eb2971f6d 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -29,6 +29,7 @@ )] use crate::fmt; +use crate::intrinsics::const_eval_select; use crate::panic::{Location, PanicInfo}; #[cfg(feature = "panic_immediate_abort")] @@ -89,40 +90,35 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { #[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable #[rustc_allow_const_fn_unstable(const_eval_select)] pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! { - #[inline] // this should always be inlined into `panic_nounwind_fmt` - #[track_caller] - fn runtime(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! { - if cfg!(feature = "panic_immediate_abort") { - super::intrinsics::abort() + const_eval_select!( + @capture { fmt: fmt::Arguments<'_>, force_no_backtrace: bool } -> !: + if const #[track_caller] { + // We don't unwind anyway at compile-time so we can call the regular `panic_fmt`. + panic_fmt(fmt) + } else #[track_caller] { + if cfg!(feature = "panic_immediate_abort") { + super::intrinsics::abort() + } + + // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call + // that gets resolved to the `#[panic_handler]` function. + extern "Rust" { + #[lang = "panic_impl"] + fn panic_impl(pi: &PanicInfo<'_>) -> !; + } + + // PanicInfo with the `can_unwind` flag set to false forces an abort. + let pi = PanicInfo::new( + &fmt, + Location::caller(), + /* can_unwind */ false, + force_no_backtrace, + ); + + // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call. + unsafe { panic_impl(&pi) } } - - // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call - // that gets resolved to the `#[panic_handler]` function. - extern "Rust" { - #[lang = "panic_impl"] - fn panic_impl(pi: &PanicInfo<'_>) -> !; - } - - // PanicInfo with the `can_unwind` flag set to false forces an abort. - let pi = PanicInfo::new( - &fmt, - Location::caller(), - /* can_unwind */ false, - force_no_backtrace, - ); - - // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call. - unsafe { panic_impl(&pi) } - } - - #[inline] - #[track_caller] - const fn comptime(fmt: fmt::Arguments<'_>, _force_no_backtrace: bool) -> ! { - // We don't unwind anyway at compile-time so we can call the regular `panic_fmt`. - panic_fmt(fmt); - } - - super::intrinsics::const_eval_select((fmt, force_no_backtrace), comptime, runtime); + ) } // Next we define a bunch of higher-level wrappers that all bottom out in the two core functions diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 7c5fa6b4e9072..2d7507e2d53ee 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -37,8 +37,7 @@ impl *const T { // considering their "data" part for null-ness. let ptr = self as *const u8; const_eval_select!( - #[inline] - (ptr: *const u8) -> bool: + @capture { ptr: *const u8 } -> bool: if const #[rustc_const_unstable(feature = "const_ptr_is_null", issue = "74939")] { match (ptr).guaranteed_eq(null_mut()) { Some(res) => res, @@ -410,11 +409,12 @@ impl *const T { const fn runtime_offset_nowrap(this: *const (), count: isize, size: usize) -> bool { // We can use const_eval_select here because this is only for UB checks. const_eval_select!( - (this: *const (), count: isize, size: usize) -> bool: + @capture { this: *const (), count: isize, size: usize } -> bool: if const { true - } else #[inline] { - // We know `size <= isize::MAX` so the `as` cast here is not lossy. + } else { + // `size` is the size of a Rust type, so we know that + // `size <= isize::MAX` and thus `as` cast here is not lossy. let Some(byte_offset) = count.checked_mul(size as isize) else { return false; }; @@ -760,7 +760,7 @@ impl *const T { #[rustc_allow_const_fn_unstable(const_eval_select)] const fn runtime_ptr_ge(this: *const (), origin: *const ()) -> bool { const_eval_select!( - (this: *const (), origin: *const ()) -> bool: + @capture { this: *const (), origin: *const () } -> bool: if const { true } else { @@ -921,10 +921,10 @@ impl *const T { #[rustc_allow_const_fn_unstable(const_eval_select)] const fn runtime_add_nowrap(this: *const (), count: usize, size: usize) -> bool { const_eval_select!( - (this: *const (), count: usize, size: usize) -> bool: + @capture { this: *const (), count: usize, size: usize } -> bool: if const { true - } else #[inline] { + } else { let Some(byte_offset) = count.checked_mul(size) else { return false; }; @@ -1028,10 +1028,10 @@ impl *const T { #[rustc_allow_const_fn_unstable(const_eval_select)] const fn runtime_sub_nowrap(this: *const (), count: usize, size: usize) -> bool { const_eval_select!( - (this: *const (), count: usize, size: usize) -> bool: + @capture { this: *const (), count: usize, size: usize } -> bool: if const { true - } else #[inline] { + } else { let Some(byte_offset) = count.checked_mul(size) else { return false; }; diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 0d94a7f491c21..344ba46a50e20 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1,5 +1,6 @@ use super::*; use crate::cmp::Ordering::{Equal, Greater, Less}; +use crate::intrinsics::const_eval_select; use crate::mem::SizedTypeProperties; use crate::slice::{self, SliceIndex}; @@ -404,23 +405,21 @@ impl *mut T { #[inline] #[rustc_allow_const_fn_unstable(const_eval_select)] const fn runtime_offset_nowrap(this: *const (), count: isize, size: usize) -> bool { - #[inline] - fn runtime(this: *const (), count: isize, size: usize) -> bool { - // `size` is the size of a Rust type, so we know that - // `size <= isize::MAX` and thus `as` cast here is not lossy. - let Some(byte_offset) = count.checked_mul(size as isize) else { - return false; - }; - let (_, overflow) = this.addr().overflowing_add_signed(byte_offset); - !overflow - } - - const fn comptime(_: *const (), _: isize, _: usize) -> bool { - true - } - // We can use const_eval_select here because this is only for UB checks. - intrinsics::const_eval_select((this, count, size), comptime, runtime) + const_eval_select!( + @capture { this: *const (), count: isize, size: usize } -> bool: + if const { + true + } else { + // `size` is the size of a Rust type, so we know that + // `size <= isize::MAX` and thus `as` cast here is not lossy. + let Some(byte_offset) = count.checked_mul(size as isize) else { + return false; + }; + let (_, overflow) = this.addr().overflowing_add_signed(byte_offset); + !overflow + } + ) } ub_checks::assert_unsafe_precondition!( @@ -1002,20 +1001,18 @@ impl *mut T { #[inline] #[rustc_allow_const_fn_unstable(const_eval_select)] const fn runtime_add_nowrap(this: *const (), count: usize, size: usize) -> bool { - #[inline] - fn runtime(this: *const (), count: usize, size: usize) -> bool { - let Some(byte_offset) = count.checked_mul(size) else { - return false; - }; - let (_, overflow) = this.addr().overflowing_add(byte_offset); - byte_offset <= (isize::MAX as usize) && !overflow - } - - const fn comptime(_: *const (), _: usize, _: usize) -> bool { - true - } - - intrinsics::const_eval_select((this, count, size), comptime, runtime) + const_eval_select!( + @capture { this: *const (), count: usize, size: usize } -> bool: + if const { + true + } else { + let Some(byte_offset) = count.checked_mul(size) else { + return false; + }; + let (_, overflow) = this.addr().overflowing_add(byte_offset); + byte_offset <= (isize::MAX as usize) && !overflow + } + ) } #[cfg(debug_assertions)] // Expensive, and doesn't catch much in the wild. @@ -1111,19 +1108,17 @@ impl *mut T { #[inline] #[rustc_allow_const_fn_unstable(const_eval_select)] const fn runtime_sub_nowrap(this: *const (), count: usize, size: usize) -> bool { - #[inline] - fn runtime(this: *const (), count: usize, size: usize) -> bool { - let Some(byte_offset) = count.checked_mul(size) else { - return false; - }; - byte_offset <= (isize::MAX as usize) && this.addr() >= byte_offset - } - - const fn comptime(_: *const (), _: usize, _: usize) -> bool { - true - } - - intrinsics::const_eval_select((this, count, size), comptime, runtime) + const_eval_select!( + @capture { this: *const (), count: usize, size: usize } -> bool: + if const { + true + } else { + let Some(byte_offset) = count.checked_mul(size) else { + return false; + }; + byte_offset <= (isize::MAX as usize) && this.addr() >= byte_offset + } + ) } #[cfg(debug_assertions)] // Expensive, and doesn't catch much in the wild. diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 8dcd34929e18d..58ba3a1573a81 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -351,89 +351,87 @@ pub const fn is_ascii_simple(mut bytes: &[u8]) -> bool { const fn is_ascii(s: &[u8]) -> bool { // The runtime version behaves the same as the compiletime version, it's // just more optimized. - return const_eval_select((s,), compiletime, runtime); - - const fn compiletime(s: &[u8]) -> bool { - is_ascii_simple(s) - } - - #[inline] - fn runtime(s: &[u8]) -> bool { - const USIZE_SIZE: usize = mem::size_of::(); - - let len = s.len(); - let align_offset = s.as_ptr().align_offset(USIZE_SIZE); - - // If we wouldn't gain anything from the word-at-a-time implementation, fall - // back to a scalar loop. - // - // We also do this for architectures where `size_of::()` isn't - // sufficient alignment for `usize`, because it's a weird edge case. - if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::() { - return is_ascii_simple(s); - } + const_eval_select!( + @capture { s: &[u8] } -> bool: + if const { + is_ascii_simple(s) + } else { + const USIZE_SIZE: usize = mem::size_of::(); + + let len = s.len(); + let align_offset = s.as_ptr().align_offset(USIZE_SIZE); + + // If we wouldn't gain anything from the word-at-a-time implementation, fall + // back to a scalar loop. + // + // We also do this for architectures where `size_of::()` isn't + // sufficient alignment for `usize`, because it's a weird edge case. + if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::() { + return is_ascii_simple(s); + } - // We always read the first word unaligned, which means `align_offset` is - // 0, we'd read the same value again for the aligned read. - let offset_to_aligned = if align_offset == 0 { USIZE_SIZE } else { align_offset }; + // We always read the first word unaligned, which means `align_offset` is + // 0, we'd read the same value again for the aligned read. + let offset_to_aligned = if align_offset == 0 { USIZE_SIZE } else { align_offset }; - let start = s.as_ptr(); - // SAFETY: We verify `len < USIZE_SIZE` above. - let first_word = unsafe { (start as *const usize).read_unaligned() }; + let start = s.as_ptr(); + // SAFETY: We verify `len < USIZE_SIZE` above. + let first_word = unsafe { (start as *const usize).read_unaligned() }; - if contains_nonascii(first_word) { - return false; - } - // We checked this above, somewhat implicitly. Note that `offset_to_aligned` - // is either `align_offset` or `USIZE_SIZE`, both of are explicitly checked - // above. - debug_assert!(offset_to_aligned <= len); - - // SAFETY: word_ptr is the (properly aligned) usize ptr we use to read the - // middle chunk of the slice. - let mut word_ptr = unsafe { start.add(offset_to_aligned) as *const usize }; - - // `byte_pos` is the byte index of `word_ptr`, used for loop end checks. - let mut byte_pos = offset_to_aligned; - - // Paranoia check about alignment, since we're about to do a bunch of - // unaligned loads. In practice this should be impossible barring a bug in - // `align_offset` though. - // While this method is allowed to spuriously fail in CTFE, if it doesn't - // have alignment information it should have given a `usize::MAX` for - // `align_offset` earlier, sending things through the scalar path instead of - // this one, so this check should pass if it's reachable. - debug_assert!(word_ptr.is_aligned_to(mem::align_of::())); - - // Read subsequent words until the last aligned word, excluding the last - // aligned word by itself to be done in tail check later, to ensure that - // tail is always one `usize` at most to extra branch `byte_pos == len`. - while byte_pos < len - USIZE_SIZE { - // Sanity check that the read is in bounds - debug_assert!(byte_pos + USIZE_SIZE <= len); - // And that our assumptions about `byte_pos` hold. - debug_assert!(word_ptr.cast::() == start.wrapping_add(byte_pos)); - - // SAFETY: We know `word_ptr` is properly aligned (because of - // `align_offset`), and we know that we have enough bytes between `word_ptr` and the end - let word = unsafe { word_ptr.read() }; - if contains_nonascii(word) { + if contains_nonascii(first_word) { return false; } + // We checked this above, somewhat implicitly. Note that `offset_to_aligned` + // is either `align_offset` or `USIZE_SIZE`, both of are explicitly checked + // above. + debug_assert!(offset_to_aligned <= len); + + // SAFETY: word_ptr is the (properly aligned) usize ptr we use to read the + // middle chunk of the slice. + let mut word_ptr = unsafe { start.add(offset_to_aligned) as *const usize }; + + // `byte_pos` is the byte index of `word_ptr`, used for loop end checks. + let mut byte_pos = offset_to_aligned; + + // Paranoia check about alignment, since we're about to do a bunch of + // unaligned loads. In practice this should be impossible barring a bug in + // `align_offset` though. + // While this method is allowed to spuriously fail in CTFE, if it doesn't + // have alignment information it should have given a `usize::MAX` for + // `align_offset` earlier, sending things through the scalar path instead of + // this one, so this check should pass if it's reachable. + debug_assert!(word_ptr.is_aligned_to(mem::align_of::())); + + // Read subsequent words until the last aligned word, excluding the last + // aligned word by itself to be done in tail check later, to ensure that + // tail is always one `usize` at most to extra branch `byte_pos == len`. + while byte_pos < len - USIZE_SIZE { + // Sanity check that the read is in bounds + debug_assert!(byte_pos + USIZE_SIZE <= len); + // And that our assumptions about `byte_pos` hold. + debug_assert!(word_ptr.cast::() == start.wrapping_add(byte_pos)); + + // SAFETY: We know `word_ptr` is properly aligned (because of + // `align_offset`), and we know that we have enough bytes between `word_ptr` and the end + let word = unsafe { word_ptr.read() }; + if contains_nonascii(word) { + return false; + } + + byte_pos += USIZE_SIZE; + // SAFETY: We know that `byte_pos <= len - USIZE_SIZE`, which means that + // after this `add`, `word_ptr` will be at most one-past-the-end. + word_ptr = unsafe { word_ptr.add(1) }; + } - byte_pos += USIZE_SIZE; - // SAFETY: We know that `byte_pos <= len - USIZE_SIZE`, which means that - // after this `add`, `word_ptr` will be at most one-past-the-end. - word_ptr = unsafe { word_ptr.add(1) }; - } - - // Sanity check to ensure there really is only one `usize` left. This should - // be guaranteed by our loop condition. - debug_assert!(byte_pos <= len && len - byte_pos <= USIZE_SIZE); + // Sanity check to ensure there really is only one `usize` left. This should + // be guaranteed by our loop condition. + debug_assert!(byte_pos <= len && len - byte_pos <= USIZE_SIZE); - // SAFETY: This relies on `len >= USIZE_SIZE`, which we check at the start. - let last_word = unsafe { (start.add(len - USIZE_SIZE) as *const usize).read_unaligned() }; + // SAFETY: This relies on `len >= USIZE_SIZE`, which we check at the start. + let last_word = unsafe { (start.add(len - USIZE_SIZE) as *const usize).read_unaligned() }; - !contains_nonascii(last_word) - } + !contains_nonascii(last_word) + } + ) } diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index ebb4bdb144909..aafa19c0dd3d3 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -1,6 +1,6 @@ //! Indexing implementations for `[T]`. -use crate::macros::const_panic; +use crate::panic::const_panic; use crate::ub_checks::assert_unsafe_precondition; use crate::{ops, range}; diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index b7c4a1f6f08b1..339adad1b17bf 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -56,61 +56,59 @@ const fn memchr_naive(x: u8, text: &[u8]) -> Option { const fn memchr_aligned(x: u8, text: &[u8]) -> Option { // The runtime version behaves the same as the compiletime version, it's // just more optimized. - return const_eval_select((x, text), compiletime, runtime); - - const fn compiletime(x: u8, text: &[u8]) -> Option { - memchr_naive(x, text) - } - - #[inline] - fn runtime(x: u8, text: &[u8]) -> Option { - // Scan for a single byte value by reading two `usize` words at a time. - // - // Split `text` in three parts - // - unaligned initial part, before the first word aligned address in text - // - body, scan by 2 words at a time - // - the last remaining part, < 2 word size - - // search up to an aligned boundary - let len = text.len(); - let ptr = text.as_ptr(); - let mut offset = ptr.align_offset(USIZE_BYTES); - - if offset > 0 { - offset = offset.min(len); - let slice = &text[..offset]; - if let Some(index) = memchr_naive(x, slice) { - return Some(index); + const_eval_select!( + @capture { x: u8, text: &[u8] } -> Option: + if const { + memchr_naive(x, text) + } else { + // Scan for a single byte value by reading two `usize` words at a time. + // + // Split `text` in three parts + // - unaligned initial part, before the first word aligned address in text + // - body, scan by 2 words at a time + // - the last remaining part, < 2 word size + + // search up to an aligned boundary + let len = text.len(); + let ptr = text.as_ptr(); + let mut offset = ptr.align_offset(USIZE_BYTES); + + if offset > 0 { + offset = offset.min(len); + let slice = &text[..offset]; + if let Some(index) = memchr_naive(x, slice) { + return Some(index); + } } - } - // search the body of the text - let repeated_x = usize::repeat_u8(x); - while offset <= len - 2 * USIZE_BYTES { - // SAFETY: the while's predicate guarantees a distance of at least 2 * usize_bytes - // between the offset and the end of the slice. - unsafe { - let u = *(ptr.add(offset) as *const usize); - let v = *(ptr.add(offset + USIZE_BYTES) as *const usize); - - // break if there is a matching byte - let zu = contains_zero_byte(u ^ repeated_x); - let zv = contains_zero_byte(v ^ repeated_x); - if zu || zv { - break; + // search the body of the text + let repeated_x = usize::repeat_u8(x); + while offset <= len - 2 * USIZE_BYTES { + // SAFETY: the while's predicate guarantees a distance of at least 2 * usize_bytes + // between the offset and the end of the slice. + unsafe { + let u = *(ptr.add(offset) as *const usize); + let v = *(ptr.add(offset + USIZE_BYTES) as *const usize); + + // break if there is a matching byte + let zu = contains_zero_byte(u ^ repeated_x); + let zv = contains_zero_byte(v ^ repeated_x); + if zu || zv { + break; + } } + offset += USIZE_BYTES * 2; } - offset += USIZE_BYTES * 2; - } - // Find the byte after the point the body loop stopped. - // FIXME(const-hack): Use `?` instead. - // FIXME(const-hack, fee1-dead): use range slicing - let slice = - // SAFETY: offset is within bounds - unsafe { super::from_raw_parts(text.as_ptr().add(offset), text.len() - offset) }; - if let Some(i) = memchr_naive(x, slice) { Some(offset + i) } else { None } - } + // Find the byte after the point the body loop stopped. + // FIXME(const-hack): Use `?` instead. + // FIXME(const-hack, fee1-dead): use range slicing + let slice = + // SAFETY: offset is within bounds + unsafe { super::from_raw_parts(text.as_ptr().add(offset), text.len() - offset) }; + if let Some(i) = memchr_naive(x, slice) { Some(offset + i) } else { None } + } + ) } /// Returns the last index matching the byte `x` in `text`. diff --git a/library/core/src/str/validations.rs b/library/core/src/str/validations.rs index 6095b589e18c3..0f724dd961329 100644 --- a/library/core/src/str/validations.rs +++ b/library/core/src/str/validations.rs @@ -132,19 +132,16 @@ pub(super) const fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { let ascii_block_size = 2 * USIZE_BYTES; let blocks_end = if len >= ascii_block_size { len - ascii_block_size + 1 } else { 0 }; - let align = { - const fn compiletime(_v: &[u8]) -> usize { + // Below, we safely fall back to a slower codepath if the offset is `usize::MAX`, + // so the end-to-end behavior is the same at compiletime and runtime. + let align = const_eval_select!( + @capture { v: &[u8] } -> usize: + if const { usize::MAX - } - - fn runtime(v: &[u8]) -> usize { + } else { v.as_ptr().align_offset(USIZE_BYTES) } - - // Below, we safely fall back to a slower codepath if the offset is `usize::MAX`, - // so the end-to-end behavior is the same at compiletime and runtime. - const_eval_select((v,), compiletime, runtime) - }; + ); while index < len { let old_offset = index; diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs index 972d9ae26d2bf..8fcbda141dab7 100644 --- a/library/core/src/ub_checks.rs +++ b/library/core/src/ub_checks.rs @@ -98,8 +98,7 @@ pub(crate) const fn check_language_ub() -> bool { // Only used for UB checks so we may const_eval_select. intrinsics::ub_checks() && const_eval_select!( - #[inline] - () -> bool: + @capture { } -> bool: if const { // Always disable UB checks. false @@ -119,19 +118,15 @@ pub(crate) const fn check_language_ub() -> bool { #[inline] #[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize, is_zst: bool) -> bool { - #[inline] - fn runtime(ptr: *const (), align: usize, is_zst: bool) -> bool { - ptr.is_aligned_to(align) && (is_zst || !ptr.is_null()) - } - - #[inline] - #[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] - const fn comptime(ptr: *const (), _align: usize, is_zst: bool) -> bool { - is_zst || !ptr.is_null() - } - // This is just for safety checks so we can const_eval_select. - const_eval_select((ptr, align, is_zst), comptime, runtime) + const_eval_select!( + @capture { ptr: *const (), align: usize, is_zst: bool } -> bool: + if const #[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] { + is_zst || !ptr.is_null() + } else { + ptr.is_aligned_to(align) && (is_zst || !ptr.is_null()) + } + ) } #[inline] @@ -155,8 +150,7 @@ pub(crate) const fn is_nonoverlapping( ) -> bool { // This is just for safety checks so we can const_eval_select. const_eval_select!( - #[inline] - (src: *const (), dst: *const (), size: usize, count: usize) -> bool: + @capture { src: *const (), dst: *const (), size: usize, count: usize } -> bool: if const { true } else { diff --git a/tests/ui/consts/const-ptr-is-null.stderr b/tests/ui/consts/const-ptr-is-null.stderr index 20e44a1401f9e..5fd3514281832 100644 --- a/tests/ui/consts/const-ptr-is-null.stderr +++ b/tests/ui/consts/const-ptr-is-null.stderr @@ -3,7 +3,7 @@ error[E0080]: evaluation of constant value failed | = note: the evaluated program panicked at 'null-ness of this pointer cannot be determined in const context', $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | -note: inside `std::ptr::const_ptr::::is_null::const_impl` +note: inside `std::ptr::const_ptr::::is_null::compiletime` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL note: inside `std::ptr::const_ptr::::is_null` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -12,7 +12,7 @@ note: inside `MAYBE_NULL` | LL | assert!(!ptr.wrapping_sub(512).is_null()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `const_eval_select` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error From 5dfbc0383d6e7ad27f0bdae5542109dafc9cd4f1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 4 Nov 2024 17:15:28 +0100 Subject: [PATCH 07/15] Rename `DocContext::is_json` into `DocContext::is_json_output` --- src/librustdoc/clean/mod.rs | 4 ++-- src/librustdoc/core.rs | 4 ++-- src/librustdoc/passes/strip_hidden.rs | 2 +- src/librustdoc/passes/strip_priv_imports.rs | 2 +- src/librustdoc/passes/strip_private.rs | 2 +- src/librustdoc/visit_ast.rs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index bd4704e8b28ed..992be651dbe08 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2907,7 +2907,7 @@ fn clean_extern_crate<'tcx>( None => false, } }) - && !cx.is_json(); + && !cx.is_json_output(); let krate_owner_def_id = krate.owner_id.def_id; if please_inline { @@ -3000,7 +3000,7 @@ fn clean_use_statement_inner<'tcx>( // forcefully don't inline if this is not public or if the // #[doc(no_inline)] attribute is present. // Don't inline doc(hidden) imports so they can be stripped at a later stage. - let mut denied = cx.is_json() + let mut denied = cx.is_json_output() || !(visibility.is_public() || (cx.render_options.document_private && is_visible_from_parent_mod)) || pub_underscore diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 274b61ae1d3c0..7ba3cfb66bd23 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -124,8 +124,8 @@ impl<'tcx> DocContext<'tcx> { /// Returns `true` if the JSON output format is enabled for generating the crate content. /// - /// If another option like `--show-coverage` is enabled, it will return false. - pub(crate) fn is_json(&self) -> bool { + /// If another option like `--show-coverage` is enabled, it will return `false`. + pub(crate) fn is_json_output(&self) -> bool { self.output_format.is_json() && !self.show_coverage } } diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index 2998289fd273a..4ef5f7f20a917 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -23,7 +23,7 @@ pub(crate) const STRIP_HIDDEN: Pass = Pass { /// Strip items marked `#[doc(hidden)]` pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { let mut retained = ItemIdSet::default(); - let is_json_output = cx.is_json(); + let is_json_output = cx.is_json_output(); // strip all #[doc(hidden)] items let krate = { diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs index 20659d5e8c69c..b9b2431f06f2b 100644 --- a/src/librustdoc/passes/strip_priv_imports.rs +++ b/src/librustdoc/passes/strip_priv_imports.rs @@ -13,7 +13,7 @@ pub(crate) const STRIP_PRIV_IMPORTS: Pass = Pass { }; pub(crate) fn strip_priv_imports(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { - let is_json_output = cx.is_json(); + let is_json_output = cx.is_json_output(); ImportStripper { tcx: cx.tcx, is_json_output, diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index 916c2f0657e47..1bd8a7838ec08 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -18,7 +18,7 @@ pub(crate) const STRIP_PRIVATE: Pass = Pass { pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { // This stripper collects all *retained* nodes. let mut retained = ItemIdSet::default(); - let is_json_output = cx.is_json(); + let is_json_output = cx.is_json_output(); // strip all private items { diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 00f5251596b60..31c33fbf49737 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -235,7 +235,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { return false; } - if self.cx.is_json() { + if self.cx.is_json_output() { return false; } From 75c943ed2d9bdddac892fd2a1825954d65d6b7ef Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:01:29 -0500 Subject: [PATCH 08/15] Update books --- src/doc/edition-guide | 2 +- src/doc/reference | 2 +- src/doc/rust-by-example | 2 +- src/doc/rustc-dev-guide | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/edition-guide b/src/doc/edition-guide index 1f07c242f8162..2d482e203eb6d 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit 1f07c242f8162a711a5ac5a4ea8fa7ec884ee7a9 +Subproject commit 2d482e203eb6d6e353814cf1415c5f94e590b9e0 diff --git a/src/doc/reference b/src/doc/reference index 23ce619966541..da0f6dad76767 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 23ce619966541bf2c80d45fdfeecf3393e360a13 +Subproject commit da0f6dad767670da0e8cd5af8a7090db3272f626 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 8bede1b919a81..9db78608b17d5 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 8bede1b919a81ab7d0c961f6bbf68d3efa297bd2 +Subproject commit 9db78608b17d5f4a6c033b8a3038466b87d63206 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index 59d94ea75a0b1..6a5accdaf1025 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit 59d94ea75a0b157e148af14c73c2dd60efb7b60a +Subproject commit 6a5accdaf10255882b1e6c59dfe5f1c79ac95484 From 02a1ab807191891e888c19b4b8b54de33dba081b Mon Sep 17 00:00:00 2001 From: Eugene Shamis Date: Fri, 1 Nov 2024 15:33:07 -0400 Subject: [PATCH 09/15] Replace checked slice indexing by unchecked to support panic-free code Fixes #126425 Replace the potentially panicking `[]` indexing with `get_unchecked()` to prevent linking with panic-related code. --- library/core/src/fmt/num.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index f1540803f978d..aaf429bac8e7f 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -88,7 +88,9 @@ unsafe trait GenericRadix: Sized { }; } } - let buf = &buf[curr..]; + // SAFETY: `curr` is initialized to `buf.len()` and is only decremented, + // so it is always in bounds. + let buf = unsafe { buf.get_unchecked(curr..) }; // SAFETY: The only chars in `buf` are created by `Self::digit` which are assumed to be // valid UTF-8 let buf = unsafe { From 37f48da802f2f76e210c82731fc7483d7ae96bfd Mon Sep 17 00:00:00 2001 From: Eugene Shamis Date: Mon, 4 Nov 2024 09:50:08 -0500 Subject: [PATCH 10/15] Updated SAFETY comment to address underflow --- library/core/src/fmt/num.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index aaf429bac8e7f..5a5c4d600745f 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -88,8 +88,9 @@ unsafe trait GenericRadix: Sized { }; } } - // SAFETY: `curr` is initialized to `buf.len()` and is only decremented, - // so it is always in bounds. + // SAFETY: `curr` is initialized to `buf.len()` and is only decremented, so it can't overflow. It is + // decremented exactly once for each digit. Since u128 is the widest fixed width integer format dupported, + // the maximum number of digits (bits) is 128 for base-2, so `curr` won't underflow as well. let buf = unsafe { buf.get_unchecked(curr..) }; // SAFETY: The only chars in `buf` are created by `Self::digit` which are assumed to be // valid UTF-8 From 65d8f1b8bfe79a3b88048ce24629456b007f8f4a Mon Sep 17 00:00:00 2001 From: Eugene Shamis Date: Mon, 4 Nov 2024 12:43:57 -0500 Subject: [PATCH 11/15] Fixed typo, rebased --- library/core/src/fmt/num.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 5a5c4d600745f..d43f25d9fd129 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -89,7 +89,7 @@ unsafe trait GenericRadix: Sized { } } // SAFETY: `curr` is initialized to `buf.len()` and is only decremented, so it can't overflow. It is - // decremented exactly once for each digit. Since u128 is the widest fixed width integer format dupported, + // decremented exactly once for each digit. Since u128 is the widest fixed width integer format supported, // the maximum number of digits (bits) is 128 for base-2, so `curr` won't underflow as well. let buf = unsafe { buf.get_unchecked(curr..) }; // SAFETY: The only chars in `buf` are created by `Self::digit` which are assumed to be From 4872b6bcbd8f2800b370b7e05b1ca1e18054a166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Mon, 4 Nov 2024 19:08:28 +0100 Subject: [PATCH 12/15] Improve example of `impl Pattern for &[char]` The previous version used `['l', 'l']` as pattern, which would suggest that it matches the `ll` of `Hello world` as a whole. --- library/core/src/str/pattern.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index 665c9fc67d01e..52e2364893eb1 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -886,8 +886,8 @@ impl<'a, 'b> DoubleEndedSearcher<'a> for CharSliceSearcher<'a, 'b> {} /// # Examples /// /// ``` -/// assert_eq!("Hello world".find(&['l', 'l'] as &[_]), Some(2)); -/// assert_eq!("Hello world".find(&['l', 'l'][..]), Some(2)); +/// assert_eq!("Hello world".find(&['o', 'l'][..]), Some(2)); +/// assert_eq!("Hello world".find(&['h', 'w'][..]), Some(6)); /// ``` impl<'b> Pattern for &'b [char] { pattern_methods!('a, CharSliceSearcher<'a, 'b>, MultiCharEqPattern, CharSliceSearcher); From 107b4fdba21d437e7e36b2a62bc803d6a8f31021 Mon Sep 17 00:00:00 2001 From: NotWearingPants <26556598+NotWearingPants@users.noreply.github.com> Date: Mon, 4 Nov 2024 20:42:21 +0200 Subject: [PATCH 13/15] docs: fix grammar in doc comment at unix/process.rs --- library/std/src/os/unix/process.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index ef5adaf229088..7c3fa7d6507e7 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -143,7 +143,7 @@ pub trait CommandExt: Sealed { /// /// This function, unlike `spawn`, will **not** `fork` the process to create /// a new child. Like spawn, however, the default behavior for the stdio - /// descriptors will be to inherited from the current process. + /// descriptors will be to inherit them from the current process. /// /// # Notes /// From f4b72dcff01f03b0479b737c007c49c97039d3a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20D=C3=B6nszelmann?= Date: Mon, 4 Nov 2024 14:55:07 +0100 Subject: [PATCH 14/15] Move two attribute lints to be early pass (post expansion) --- .../src/attrs/allow_attributes.rs | 4 +- .../attrs/allow_attributes_without_reason.rs | 4 +- .../attrs/blanket_clippy_restriction_lints.rs | 6 +- .../src/attrs/deprecated_semver.rs | 4 +- .../src/attrs/duplicated_attributes.rs | 8 +- .../src/attrs/mixed_attributes_style.rs | 6 +- .../clippy/clippy_lints/src/attrs/mod.rs | 128 +++++++++++------- .../src/attrs/should_panic_without_expect.rs | 6 +- .../src/attrs/useless_attribute.rs | 8 +- src/tools/clippy/clippy_lints/src/lib.rs | 2 + .../clippy_utils/src/check_proc_macro.rs | 5 +- .../clippy/tests/ui/allow_attributes.stderr | 10 +- .../ui/allow_attributes_without_reason.stderr | 11 +- src/tools/clippy/tests/ui/attrs.stderr | 18 +-- .../blanket_clippy_restriction_lints.stderr | 14 +- 15 files changed, 122 insertions(+), 112 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs index a5a7b9f74a693..1879391ec290b 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs @@ -3,11 +3,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_ast::{AttrStyle, Attribute}; use rustc_errors::Applicability; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::{EarlyContext, LintContext}; use rustc_middle::lint::in_external_macro; // Separate each crate's features. -pub fn check<'cx>(cx: &LateContext<'cx>, attr: &'cx Attribute) { +pub fn check<'cx>(cx: &EarlyContext<'cx>, attr: &'cx Attribute) { if !in_external_macro(cx.sess(), attr.span) && let AttrStyle::Outer = attr.style && let Some(ident) = attr.ident() diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs index 5d4e864b9b0b5..788377fe83ce4 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs @@ -2,12 +2,12 @@ use super::{ALLOW_ATTRIBUTES_WITHOUT_REASON, Attribute}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_ast::{MetaItemInner, MetaItemKind}; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::{EarlyContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_span::sym; use rustc_span::symbol::Symbol; -pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[MetaItemInner], attr: &'cx Attribute) { +pub(super) fn check<'cx>(cx: &EarlyContext<'cx>, name: Symbol, items: &[MetaItemInner], attr: &'cx Attribute) { // Check if the reason is present if let Some(item) = items.last().and_then(MetaItemInner::meta_item) && let MetaItemKind::NameValue(_) = &item.kind diff --git a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs index 0baf889faa076..fecf316640636 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs @@ -2,11 +2,11 @@ use super::BLANKET_CLIPPY_RESTRICTION_LINTS; use super::utils::extract_clippy_lint; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use rustc_ast::MetaItemInner; -use rustc_lint::{LateContext, Level, LintContext}; +use rustc_lint::{EarlyContext, Level, LintContext}; use rustc_span::symbol::Symbol; use rustc_span::{DUMMY_SP, sym}; -pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[MetaItemInner]) { +pub(super) fn check(cx: &EarlyContext<'_>, name: Symbol, items: &[MetaItemInner]) { for lint in items { if let Some(lint_name) = extract_clippy_lint(lint) { if lint_name.as_str() == "restriction" && name != sym::allow { @@ -23,7 +23,7 @@ pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[MetaItemInner]) } } -pub(super) fn check_command_line(cx: &LateContext<'_>) { +pub(super) fn check_command_line(cx: &EarlyContext<'_>) { for (name, level) in &cx.sess().opts.lint_opts { if name == "clippy::restriction" && *level > Level::Allow { span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs index 1898c145c76a9..d3153ec6613b5 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs @@ -1,11 +1,11 @@ use super::DEPRECATED_SEMVER; use clippy_utils::diagnostics::span_lint; use rustc_ast::{LitKind, MetaItemLit}; -use rustc_lint::LateContext; +use rustc_lint::EarlyContext; use rustc_span::Span; use semver::Version; -pub(super) fn check(cx: &LateContext<'_>, span: Span, lit: &MetaItemLit) { +pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) { if let LitKind::Str(is, _) = lit.kind { if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() { return; diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs index 55f8e1072db74..2ddbc7a6a76dc 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs @@ -2,12 +2,12 @@ use super::DUPLICATED_ATTRIBUTES; use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::{Attribute, MetaItem}; use rustc_data_structures::fx::FxHashMap; -use rustc_lint::LateContext; +use rustc_lint::EarlyContext; use rustc_span::{Span, sym}; use std::collections::hash_map::Entry; fn emit_if_duplicated( - cx: &LateContext<'_>, + cx: &EarlyContext<'_>, attr: &MetaItem, attr_paths: &mut FxHashMap, complete_path: String, @@ -26,7 +26,7 @@ fn emit_if_duplicated( } fn check_duplicated_attr( - cx: &LateContext<'_>, + cx: &EarlyContext<'_>, attr: &MetaItem, attr_paths: &mut FxHashMap, parent: &mut Vec, @@ -65,7 +65,7 @@ fn check_duplicated_attr( } } -pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) { +pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) { let mut attr_paths = FxHashMap::default(); for attr in attrs { diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index 5d2ea36b366c1..32c28c09c3602 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_ast::{AttrKind, AttrStyle, Attribute}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::{EarlyContext, LintContext}; use rustc_span::source_map::SourceMap; use rustc_span::{SourceFile, Span, Symbol}; @@ -32,7 +32,7 @@ impl From<&AttrKind> for SimpleAttrKind { } } -pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) { +pub(super) fn check(cx: &EarlyContext<'_>, item_span: Span, attrs: &[Attribute]) { let mut inner_attr_kind: FxHashSet = FxHashSet::default(); let mut outer_attr_kind: FxHashSet = FxHashSet::default(); @@ -64,7 +64,7 @@ pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) } } -fn lint_mixed_attrs(cx: &LateContext<'_>, attrs: &[Attribute]) { +fn lint_mixed_attrs(cx: &EarlyContext<'_>, attrs: &[Attribute]) { let mut attrs_iter = attrs.iter().filter(|attr| !attr.span.from_expansion()); let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.last()) { first.span.with_hi(last.span.hi()) diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 1a34ca99fc2b9..684756ce87f1d 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -14,8 +14,8 @@ mod utils; use clippy_config::Conf; use clippy_config::msrvs::{self, Msrv}; -use rustc_ast::{Attribute, MetaItemInner, MetaItemKind}; -use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; +use rustc_ast::{Attribute, MetaItemInner, MetaItemKind, self as ast}; +use rustc_hir::{ImplItem, Item, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::sym; @@ -414,15 +414,7 @@ pub struct Attributes { } impl_lint_pass!(Attributes => [ - ALLOW_ATTRIBUTES, - ALLOW_ATTRIBUTES_WITHOUT_REASON, INLINE_ALWAYS, - DEPRECATED_SEMVER, - USELESS_ATTRIBUTE, - BLANKET_CLIPPY_RESTRICTION_LINTS, - SHOULD_PANIC_WITHOUT_EXPECT, - MIXED_ATTRIBUTES_STYLE, - DUPLICATED_ATTRIBUTES, ]); impl Attributes { @@ -434,53 +426,11 @@ impl Attributes { } impl<'tcx> LateLintPass<'tcx> for Attributes { - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - blanket_clippy_restriction_lints::check_command_line(cx); - duplicated_attributes::check(cx, cx.tcx.hir().krate_attrs()); - } - - fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) { - if let Some(items) = &attr.meta_item_list() { - if let Some(ident) = attr.ident() { - if is_lint_level(ident.name, attr.id) { - blanket_clippy_restriction_lints::check(cx, ident.name, items); - } - if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { - allow_attributes::check(cx, attr); - } - if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) - { - allow_attributes_without_reason::check(cx, ident.name, items, attr); - } - if items.is_empty() || !attr.has_name(sym::deprecated) { - return; - } - for item in items { - if let MetaItemInner::MetaItem(mi) = &item - && let MetaItemKind::NameValue(lit) = &mi.kind - && mi.has_name(sym::since) - { - deprecated_semver::check(cx, item.span(), lit); - } - } - } - } - if attr.has_name(sym::should_panic) { - should_panic_without_expect::check(cx, attr); - } - } - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); if is_relevant_item(cx, item) { inline_always::check(cx, item.span, item.ident.name, attrs); } - match item.kind { - ItemKind::ExternCrate(..) | ItemKind::Use(..) => useless_attribute::check(cx, item, attrs), - _ => {}, - } - mixed_attributes_style::check(cx, item.span, attrs); - duplicated_attributes::check(cx, attrs); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { @@ -526,3 +476,77 @@ impl EarlyLintPass for EarlyAttributes { extract_msrv_attr!(EarlyContext); } + +pub struct PostExpansionEarlyAttributes { + msrv: Msrv, +} + +impl PostExpansionEarlyAttributes { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + +impl_lint_pass!(PostExpansionEarlyAttributes => [ + ALLOW_ATTRIBUTES, + ALLOW_ATTRIBUTES_WITHOUT_REASON, + DEPRECATED_SEMVER, + USELESS_ATTRIBUTE, + BLANKET_CLIPPY_RESTRICTION_LINTS, + SHOULD_PANIC_WITHOUT_EXPECT, + MIXED_ATTRIBUTES_STYLE, + DUPLICATED_ATTRIBUTES, +]); + +impl EarlyLintPass for PostExpansionEarlyAttributes { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) { + blanket_clippy_restriction_lints::check_command_line(cx); + duplicated_attributes::check(cx, &krate.attrs); + } + + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { + if let Some(items) = &attr.meta_item_list() { + if let Some(ident) = attr.ident() { + if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { + allow_attributes::check(cx, attr); + } + if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) + { + allow_attributes_without_reason::check(cx, ident.name, items, attr); + } + if is_lint_level(ident.name, attr.id) { + blanket_clippy_restriction_lints::check(cx, ident.name, items); + } + if items.is_empty() || !attr.has_name(sym::deprecated) { + return; + } + for item in items { + if let MetaItemInner::MetaItem(mi) = &item + && let MetaItemKind::NameValue(lit) = &mi.kind + && mi.has_name(sym::since) + { + deprecated_semver::check(cx, item.span(), lit); + } + } + } + } + + if attr.has_name(sym::should_panic) { + should_panic_without_expect::check(cx, attr); + } + } + + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &'_ ast::Item) { + match item.kind { + ast::ItemKind::ExternCrate(..) | ast::ItemKind::Use(..) => useless_attribute::check(cx, item, &item.attrs), + _ => {}, + } + + mixed_attributes_style::check(cx, item.span, &item.attrs); + duplicated_attributes::check(cx, &item.attrs); + } + + extract_msrv_attr!(EarlyContext); +} diff --git a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs index 2d45cbbf621f4..fadd527288028 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs @@ -4,12 +4,12 @@ use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::TokenTree; use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind}; use rustc_errors::Applicability; -use rustc_lint::LateContext; +use rustc_lint::EarlyContext; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, attr: &Attribute) { +pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) { if let AttrKind::Normal(normal_attr) = &attr.kind { - if let AttrArgs::Eq(_, AttrArgsEq::Hir(_)) = &normal_attr.item.args { + if let AttrArgs::Eq(_, AttrArgsEq::Ast(_)) = &normal_attr.item.args { // `#[should_panic = ".."]` found, good return; } diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index 72e6ce59d5980..92b9f9cba5251 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -1,15 +1,15 @@ use super::utils::{extract_clippy_lint, is_lint_level, is_word}; -use super::{Attribute, USELESS_ATTRIBUTE}; +use super::USELESS_ATTRIBUTE; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{SpanRangeExt, first_line_of_span}; use rustc_ast::MetaItemInner; use rustc_errors::Applicability; -use rustc_hir::{Item, ItemKind}; -use rustc_lint::{LateContext, LintContext}; +use rustc_ast::{Item, ItemKind, Attribute}; +use rustc_lint::{EarlyContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) { +pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use)); for attr in attrs { diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 14110539709d6..3fd07ced0e4ae 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -412,6 +412,8 @@ use rustc_lint::{Lint, LintId}; pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf))); + + store.register_early_pass(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf))); } #[derive(Default)] diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index bfb3a76ad251c..c5e2c8c09a277 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -21,7 +21,7 @@ use rustc_hir::{ ImplItem, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource, }; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::{LateContext, LintContext, EarlyContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::symbol::{Ident, kw}; @@ -429,11 +429,12 @@ impl_with_search_pat!((_cx: LateContext<'tcx>, self: ImplItem<'_>) => impl_item_ impl_with_search_pat!((_cx: LateContext<'tcx>, self: FieldDef<'_>) => field_def_search_pat(self)); impl_with_search_pat!((_cx: LateContext<'tcx>, self: Variant<'_>) => variant_search_pat(self)); impl_with_search_pat!((_cx: LateContext<'tcx>, self: Ty<'_>) => ty_search_pat(self)); -impl_with_search_pat!((_cx: LateContext<'tcx>, self: Attribute) => attr_search_pat(self)); impl_with_search_pat!((_cx: LateContext<'tcx>, self: Ident) => ident_search_pat(*self)); impl_with_search_pat!((_cx: LateContext<'tcx>, self: Lit) => lit_search_pat(&self.node)); impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pat(self)); +impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: Attribute) => attr_search_pat(self)); + impl<'cx> WithSearchPat<'cx> for (&FnKind<'cx>, &Body<'cx>, HirId, Span) { type Context = LateContext<'cx>; diff --git a/src/tools/clippy/tests/ui/allow_attributes.stderr b/src/tools/clippy/tests/ui/allow_attributes.stderr index 10dac0bc80808..023b4d7e40439 100644 --- a/src/tools/clippy/tests/ui/allow_attributes.stderr +++ b/src/tools/clippy/tests/ui/allow_attributes.stderr @@ -19,13 +19,5 @@ error: #[allow] attribute found LL | #[allow(unused)] | ^^^^^ help: replace it with: `expect` -error: #[allow] attribute found - --> tests/ui/allow_attributes.rs:52:7 - | -LL | #[allow(unused)] - | ^^^^^ help: replace it with: `expect` - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr index 86d7845df0416..9c1ac5af91b06 100644 --- a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr +++ b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr @@ -43,14 +43,5 @@ LL | #[allow(unused)] | = help: try adding a reason at the end with `, reason = ".."` -error: `allow` attribute without specifying a reason - --> tests/ui/allow_attributes_without_reason.rs:46:5 - | -LL | #[allow(unused)] - | ^^^^^^^^^^^^^^^^ - | - = help: try adding a reason at the end with `, reason = ".."` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/attrs.stderr b/src/tools/clippy/tests/ui/attrs.stderr index cd409fc8701b6..a7fdceaba6f39 100644 --- a/src/tools/clippy/tests/ui/attrs.stderr +++ b/src/tools/clippy/tests/ui/attrs.stderr @@ -1,12 +1,3 @@ -error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea - --> tests/ui/attrs.rs:5:1 - | -LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::inline-always` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::inline_always)]` - error: the since field must contain a semver-compliant version --> tests/ui/attrs.rs:27:14 | @@ -22,5 +13,14 @@ error: the since field must contain a semver-compliant version LL | #[deprecated(since = "1")] | ^^^^^^^^^^^ +error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea + --> tests/ui/attrs.rs:5:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::inline-always` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inline_always)]` + error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr index d410f25b2c274..1bad259b09a36 100644 --- a/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr +++ b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr @@ -1,3 +1,10 @@ +error: `clippy::restriction` is not meant to be enabled as a group + | + = note: because of the command line `--warn clippy::restriction` + = help: enable the restriction lints you need individually + = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]` + error: `clippy::restriction` is not meant to be enabled as a group --> tests/ui/blanket_clippy_restriction_lints.rs:6:9 | @@ -5,8 +12,6 @@ LL | #![warn(clippy::restriction)] | ^^^^^^^^^^^^^^^^^^^ | = help: enable the restriction lints you need individually - = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]` error: `clippy::restriction` is not meant to be enabled as a group --> tests/ui/blanket_clippy_restriction_lints.rs:8:9 @@ -24,10 +29,5 @@ LL | #![forbid(clippy::restriction)] | = help: enable the restriction lints you need individually -error: `clippy::restriction` is not meant to be enabled as a group - | - = note: because of the command line `--warn clippy::restriction` - = help: enable the restriction lints you need individually - error: aborting due to 4 previous errors From ffad9aac27ff8a78f5d751bf88250470e2e9d790 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 2 Sep 2024 11:45:59 +0200 Subject: [PATCH 15/15] mark some target features as 'forbidden' so they cannot be (un)set For now, this is just a warning, but should become a hard error in the future --- compiler/rustc_codegen_gcc/messages.ftl | 11 +- compiler/rustc_codegen_gcc/src/errors.rs | 13 ++ compiler/rustc_codegen_gcc/src/gcc_util.rs | 65 +++++--- compiler/rustc_codegen_gcc/src/lib.rs | 3 +- compiler/rustc_codegen_llvm/messages.ftl | 5 + compiler/rustc_codegen_llvm/src/errors.rs | 9 ++ compiler/rustc_codegen_llvm/src/llvm_util.rs | 119 +++++++++------ compiler/rustc_codegen_ssa/messages.ftl | 3 + .../rustc_codegen_ssa/src/codegen_attrs.rs | 14 +- compiler/rustc_codegen_ssa/src/errors.rs | 9 ++ .../rustc_codegen_ssa/src/target_features.rs | 54 ++++--- compiler/rustc_middle/src/query/mod.rs | 5 +- compiler/rustc_session/src/config/cfg.rs | 5 +- compiler/rustc_target/src/target_features.rs | 142 +++++++++++------- .../using-target-feature-unstable.rs | 0 .../forbidden-target-feature-attribute.rs | 12 ++ .../forbidden-target-feature-attribute.stderr | 8 + .../forbidden-target-feature-cfg.rs | 15 ++ .../forbidden-target-feature-flag-disable.rs | 11 ++ ...rbidden-target-feature-flag-disable.stderr | 7 + .../forbidden-target-feature-flag.rs | 11 ++ .../forbidden-target-feature-flag.stderr | 7 + .../using-target-feature-unstable.rs | 0 23 files changed, 371 insertions(+), 157 deletions(-) rename tests/ui/{ => target-feature}/auxiliary/using-target-feature-unstable.rs (100%) create mode 100644 tests/ui/target-feature/forbidden-target-feature-attribute.rs create mode 100644 tests/ui/target-feature/forbidden-target-feature-attribute.stderr create mode 100644 tests/ui/target-feature/forbidden-target-feature-cfg.rs create mode 100644 tests/ui/target-feature/forbidden-target-feature-flag-disable.rs create mode 100644 tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr create mode 100644 tests/ui/target-feature/forbidden-target-feature-flag.rs create mode 100644 tests/ui/target-feature/forbidden-target-feature-flag.stderr rename tests/ui/{ => target-feature}/using-target-feature-unstable.rs (100%) diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl index bbae59ea7a55a..26ddc5732dd0b 100644 --- a/compiler/rustc_codegen_gcc/messages.ftl +++ b/compiler/rustc_codegen_gcc/messages.ftl @@ -8,6 +8,9 @@ codegen_gcc_invalid_minimum_alignment = codegen_gcc_lto_not_supported = LTO is not supported. You may get a linker error. +codegen_gcc_forbidden_ctarget_feature = + target feature `{$feature}` cannot be toggled with `-Ctarget-feature` + codegen_gcc_unwinding_inline_asm = GCC backend does not support unwinding from inline asm @@ -24,11 +27,15 @@ codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdyl codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err}) codegen_gcc_unknown_ctarget_feature = - unknown feature specified for `-Ctarget-feature`: `{$feature}` - .note = it is still passed through to the codegen backend + unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}` + .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future .possible_feature = you might have meant: `{$rust_feature}` .consider_filing_feature_request = consider filing a feature request +codegen_gcc_unstable_ctarget_feature = + unstable feature specified for `-Ctarget-feature`: `{$feature}` + .note = this feature is not stably supported; its behavior can change in the future + codegen_gcc_missing_features = add the missing features in a `target_feature` attribute diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index dc1895f437b52..7a586b5b04c52 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -17,6 +17,19 @@ pub(crate) struct UnknownCTargetFeature<'a> { pub rust_feature: PossibleFeature<'a>, } +#[derive(Diagnostic)] +#[diag(codegen_gcc_unstable_ctarget_feature)] +#[note] +pub(crate) struct UnstableCTargetFeature<'a> { + pub feature: &'a str, +} + +#[derive(Diagnostic)] +#[diag(codegen_gcc_forbidden_ctarget_feature)] +pub(crate) struct ForbiddenCTargetFeature<'a> { + pub feature: &'a str, +} + #[derive(Subdiagnostic)] pub(crate) enum PossibleFeature<'a> { #[help(codegen_gcc_possible_feature)] diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 3104088e0d5e9..65279c9495a3d 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -5,10 +5,13 @@ use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable; use rustc_data_structures::fx::FxHashMap; use rustc_middle::bug; use rustc_session::Session; -use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; +use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability}; use smallvec::{SmallVec, smallvec}; -use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix}; +use crate::errors::{ + ForbiddenCTargetFeature, PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix, + UnstableCTargetFeature, +}; /// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, /// `--target` and similar). @@ -43,7 +46,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec Vec { + let rust_feature = + known_features.iter().find_map(|&(rust_feature, _, _)| { + let gcc_features = to_gcc_features(sess, rust_feature); + if gcc_features.contains(&feature) + && !gcc_features.contains(&rust_feature) + { + Some(rust_feature) + } else { + None + } + }); + let unknown_feature = if let Some(rust_feature) = rust_feature { + UnknownCTargetFeature { + feature, + rust_feature: PossibleFeature::Some { rust_feature }, + } + } else { + UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } + }; + sess.dcx().emit_warn(unknown_feature); } - }); - let unknown_feature = if let Some(rust_feature) = rust_feature { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::Some { rust_feature }, + Some((_, Stability::Stable, _)) => {} + Some((_, Stability::Unstable(_), _)) => { + // An unstable feature. Warn about using it. + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); } - } else { - UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } - }; - sess.dcx().emit_warn(unknown_feature); - } + Some((_, Stability::Forbidden { .. }, _)) => { + sess.dcx().emit_err(ForbiddenCTargetFeature { feature }); + } + } - if diagnostics { // FIXME(nagisa): figure out how to not allocate a full hashset here. featsmap.insert(feature, enable_disable == '+'); } - // rustc-specific features do not get passed down to GCC… - if RUSTC_SPECIFIC_FEATURES.contains(&feature) { - return None; - } // ... otherwise though we run through `to_gcc_features` when // passing requests down to GCC. This means that all in-language // features also work on the command line instead of having two diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 7486eefeb85a7..f70dc94b26741 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -491,8 +491,9 @@ pub fn target_features( ) -> Vec { // TODO(antoyo): use global_gcc_features. sess.target - .supported_target_features() + .rust_target_features() .iter() + .filter(|(_, gate, _)| gate.is_supported()) .filter_map(|&(feature, gate, _)| { if sess.is_nightly_build() || allow_unstable || gate.is_stable() { Some(feature) diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index 0950e4bb26bac..63c64269eb805 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -7,6 +7,11 @@ codegen_llvm_dynamic_linking_with_lto = codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture +codegen_llvm_forbidden_ctarget_feature = + target feature `{$feature}` cannot be toggled with `-Ctarget-feature`: {$reason} + .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +codegen_llvm_forbidden_ctarget_feature_issue = for more information, see issue #116344 + codegen_llvm_from_llvm_diag = {$message} codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message} diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 0d436e1891ece..3cdb5b971d908 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -31,6 +31,15 @@ pub(crate) struct UnstableCTargetFeature<'a> { pub feature: &'a str, } +#[derive(Diagnostic)] +#[diag(codegen_llvm_forbidden_ctarget_feature)] +#[note] +#[note(codegen_llvm_forbidden_ctarget_feature_issue)] +pub(crate) struct ForbiddenCTargetFeature<'a> { + pub feature: &'a str, + pub reason: &'a str, +} + #[derive(Subdiagnostic)] pub(crate) enum PossibleFeature<'a> { #[help(codegen_llvm_possible_feature)] diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 9adb1299b3d31..8b27a6a66777b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -16,12 +16,12 @@ use rustc_session::Session; use rustc_session::config::{PrintKind, PrintRequest}; use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport}; -use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES}; +use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES, Stability}; use crate::back::write::create_informational_target_machine; use crate::errors::{ - FixedX18InvalidArch, InvalidTargetFeaturePrefix, PossibleFeature, UnknownCTargetFeature, - UnknownCTargetFeaturePrefix, UnstableCTargetFeature, + FixedX18InvalidArch, ForbiddenCTargetFeature, InvalidTargetFeaturePrefix, PossibleFeature, + UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature, }; use crate::llvm; @@ -280,19 +280,29 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Vec { - let mut features = vec![]; - - // Add base features for the target + let mut features: FxHashSet = Default::default(); + + // Add base features for the target. + // We do *not* add the -Ctarget-features there, and instead duplicate the logic for that below. + // The reason is that if LLVM considers a feature implied but we do not, we don't want that to + // show up in `cfg`. That way, `cfg` is entirely under our control -- except for the handling of + // the target CPU, that is still expanded to target features (with all their implied features) by + // LLVM. let target_machine = create_informational_target_machine(sess, true); + // Compute which of the known target features are enabled in the 'base' target machine. + // We only consider "supported" features; "forbidden" features are not reflected in `cfg` as of now. features.extend( sess.target - .supported_target_features() + .rust_target_features() .iter() + .filter(|(_, gate, _)| gate.is_supported()) .filter(|(feature, _, _)| { - // skip checking special features, as LLVM may not understands them + // skip checking special features, as LLVM may not understand them if RUSTC_SPECIAL_FEATURES.contains(feature) { return true; } @@ -323,7 +333,12 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { if enabled { features.extend(sess.target.implied_target_features(std::iter::once(feature))); } else { + // We don't care about the order in `features` since the only thing we use it for is the + // `features.contains` below. + #[allow(rustc::potential_query_instability)] features.retain(|f| { + // Keep a feature if it does not imply `feature`. Or, equivalently, + // remove the reverse-dependencies of `feature`. !sess.target.implied_target_features(std::iter::once(*f)).contains(&feature) }); } @@ -331,8 +346,9 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { // Filter enabled features based on feature gates sess.target - .supported_target_features() + .rust_target_features() .iter() + .filter(|(_, gate, _)| gate.is_supported()) .filter_map(|&(feature, gate, _)| { if sess.is_nightly_build() || allow_unstable || gate.is_stable() { Some(feature) @@ -392,9 +408,13 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach let mut known_llvm_target_features = FxHashSet::<&'static str>::default(); let mut rustc_target_features = sess .target - .supported_target_features() + .rust_target_features() .iter() - .filter_map(|(feature, _gate, _implied)| { + .filter_map(|(feature, gate, _implied)| { + if !gate.is_supported() { + // Only list (experimentally) supported features. + return None; + } // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these // strings. let llvm_feature = to_llvm_features(sess, *feature)?.llvm_feature_name; @@ -567,7 +587,7 @@ pub(crate) fn global_llvm_features( // -Ctarget-features if !only_base_features { - let supported_features = sess.target.supported_target_features(); + let known_features = sess.target.rust_target_features(); let mut featsmap = FxHashMap::default(); // insert implied features @@ -601,50 +621,53 @@ pub(crate) fn global_llvm_features( } }; + // Get the backend feature name, if any. + // This excludes rustc-specific features, which do not get passed to LLVM. let feature = backend_feature_name(sess, s)?; // Warn against use of LLVM specific feature names and unstable features on the CLI. if diagnostics { - let feature_state = supported_features.iter().find(|&&(v, _, _)| v == feature); - if feature_state.is_none() { - let rust_feature = - supported_features.iter().find_map(|&(rust_feature, _, _)| { - let llvm_features = to_llvm_features(sess, rust_feature)?; - if llvm_features.contains(feature) - && !llvm_features.contains(rust_feature) - { - Some(rust_feature) - } else { - None + let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); + match feature_state { + None => { + let rust_feature = + known_features.iter().find_map(|&(rust_feature, _, _)| { + let llvm_features = to_llvm_features(sess, rust_feature)?; + if llvm_features.contains(feature) + && !llvm_features.contains(rust_feature) + { + Some(rust_feature) + } else { + None + } + }); + let unknown_feature = if let Some(rust_feature) = rust_feature { + UnknownCTargetFeature { + feature, + rust_feature: PossibleFeature::Some { rust_feature }, } - }); - let unknown_feature = if let Some(rust_feature) = rust_feature { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::Some { rust_feature }, - } - } else { - UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } - }; - sess.dcx().emit_warn(unknown_feature); - } else if feature_state - .is_some_and(|(_name, feature_gate, _implied)| !feature_gate.is_stable()) - { - // An unstable feature. Warn about using it. - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + } else { + UnknownCTargetFeature { + feature, + rust_feature: PossibleFeature::None, + } + }; + sess.dcx().emit_warn(unknown_feature); + } + Some((_, Stability::Stable, _)) => {} + Some((_, Stability::Unstable(_), _)) => { + // An unstable feature. Warn about using it. + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + } + Some((_, Stability::Forbidden { reason }, _)) => { + sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason }); + } } - } - if diagnostics { // FIXME(nagisa): figure out how to not allocate a full hashset here. featsmap.insert(feature, enable_disable == '+'); } - // rustc-specific features do not get passed down to LLVM… - if RUSTC_SPECIFIC_FEATURES.contains(&feature) { - return None; - } - - // ... otherwise though we run through `to_llvm_features` when + // We run through `to_llvm_features` when // passing requests down to LLVM. This means that all in-language // features also work on the command line instead of having two // different names when the LLVM name and the Rust name differ. diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index d07274920feaf..bb74698a060af 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -58,6 +58,9 @@ codegen_ssa_failed_to_write = failed to write {$path}: {$error} codegen_ssa_field_associated_value_expected = associated value expected for `{$name}` +codegen_ssa_forbidden_target_feature_attr = + target feature `{$feature}` cannot be toggled with `#[target_feature]`: {$reason} + codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files were produced diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index a5bd3adbcddc9..31f8d479f7eb8 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -20,8 +20,8 @@ use rustc_span::symbol::Ident; use rustc_span::{Span, sym}; use rustc_target::spec::{SanitizerSet, abi}; -use crate::errors::{self, MissingFeatures, TargetFeatureDisableOrEnable}; -use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature}; +use crate::errors; +use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr}; fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage { use rustc_middle::mir::mono::Linkage::*; @@ -73,7 +73,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS; } - let supported_target_features = tcx.supported_target_features(LOCAL_CRATE); + let rust_target_features = tcx.rust_target_features(LOCAL_CRATE); let mut inline_span = None; let mut link_ordinal_span = None; @@ -281,10 +281,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { check_target_feature_trait_unsafe(tcx, did, attr.span); } } - from_target_feature( + from_target_feature_attr( tcx, attr, - supported_target_features, + rust_target_features, &mut codegen_fn_attrs.target_features, ); } @@ -676,10 +676,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { .next() .map_or_else(|| tcx.def_span(did), |a| a.span); tcx.dcx() - .create_err(TargetFeatureDisableOrEnable { + .create_err(errors::TargetFeatureDisableOrEnable { features, span: Some(span), - missing_features: Some(MissingFeatures), + missing_features: Some(errors::MissingFeatures), }) .emit(); } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index d67cf0e3a6d5f..670ad39d811e6 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1018,6 +1018,15 @@ pub(crate) struct TargetFeatureSafeTrait { pub def: Span, } +#[derive(Diagnostic)] +#[diag(codegen_ssa_forbidden_target_feature_attr)] +pub struct ForbiddenTargetFeatureAttr<'a> { + #[primary_span] + pub span: Span, + pub feature: &'a str, + pub reason: &'a str, +} + #[derive(Diagnostic)] #[diag(codegen_ssa_failed_to_get_layout)] pub struct FailedToGetLayout<'tcx> { diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 0845bcc5749f4..eee7cc75400d9 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -11,13 +11,16 @@ use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; +use rustc_target::target_features::{self, Stability}; use crate::errors; -pub(crate) fn from_target_feature( +/// Compute the enabled target features from the `#[target_feature]` function attribute. +/// Enabled target features are added to `target_features`. +pub(crate) fn from_target_feature_attr( tcx: TyCtxt<'_>, attr: &ast::Attribute, - supported_target_features: &UnordMap>, + rust_target_features: &UnordMap, target_features: &mut Vec, ) { let Some(list) = attr.meta_item_list() else { return }; @@ -46,12 +49,12 @@ pub(crate) fn from_target_feature( // We allow comma separation to enable multiple features. added_target_features.extend(value.as_str().split(',').filter_map(|feature| { - let Some(feature_gate) = supported_target_features.get(feature) else { + let Some(stability) = rust_target_features.get(feature) else { let msg = format!("the feature named `{feature}` is not valid for this target"); let mut err = tcx.dcx().struct_span_err(item.span(), msg); err.span_label(item.span(), format!("`{feature}` is not valid for this target")); if let Some(stripped) = feature.strip_prefix('+') { - let valid = supported_target_features.contains_key(stripped); + let valid = rust_target_features.contains_key(stripped); if valid { err.help("consider removing the leading `+` in the feature name"); } @@ -61,18 +64,31 @@ pub(crate) fn from_target_feature( }; // Only allow target features whose feature gates have been enabled. - let allowed = match feature_gate.as_ref().copied() { - Some(name) => rust_features.enabled(name), - None => true, + let allowed = match stability { + Stability::Forbidden { .. } => false, + Stability::Stable => true, + Stability::Unstable(name) => rust_features.enabled(*name), }; if !allowed { - feature_err( - &tcx.sess, - feature_gate.unwrap(), - item.span(), - format!("the target feature `{feature}` is currently unstable"), - ) - .emit(); + match stability { + Stability::Stable => unreachable!(), + &Stability::Unstable(lang_feature_name) => { + feature_err( + &tcx.sess, + lang_feature_name, + item.span(), + format!("the target feature `{feature}` is currently unstable"), + ) + .emit(); + } + Stability::Forbidden { reason } => { + tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { + span: item.span(), + feature, + reason, + }); + } + } } Some(Symbol::intern(feature)) })); @@ -138,20 +154,20 @@ pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { - supported_target_features: |tcx, cnum| { + rust_target_features: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); if tcx.sess.opts.actually_rustdoc { // rustdoc needs to be able to document functions that use all the features, so // whitelist them all - rustc_target::target_features::all_known_features() - .map(|(a, b)| (a.to_string(), b.as_feature_name())) + rustc_target::target_features::all_rust_features() + .map(|(a, b)| (a.to_string(), b)) .collect() } else { tcx.sess .target - .supported_target_features() + .rust_target_features() .iter() - .map(|&(a, b, _)| (a.to_string(), b.as_feature_name())) + .map(|&(a, b, _)| (a.to_string(), b)) .collect() } }, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d7a60a843b717..0f7f727212cf0 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -2185,10 +2185,11 @@ rustc_queries! { desc { "computing autoderef types for `{}`", goal.canonical.value.value } } - query supported_target_features(_: CrateNum) -> &'tcx UnordMap> { + /// Returns the Rust target features for the current target. These are not always the same as LLVM target features! + query rust_target_features(_: CrateNum) -> &'tcx UnordMap { arena_cache eval_always - desc { "looking up supported target features" } + desc { "looking up Rust target features" } } query implied_target_features(feature: Symbol) -> &'tcx Vec { diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 31ef2bda4f1e5..f30da4fbfc64b 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -370,8 +370,9 @@ impl CheckCfg { ins!(sym::sanitizer_cfi_normalize_integers, no_values); ins!(sym::target_feature, empty_values).extend( - rustc_target::target_features::all_known_features() - .map(|(f, _sb)| f) + rustc_target::target_features::all_rust_features() + .filter(|(_, s)| s.is_supported()) + .map(|(f, _s)| f) .chain(rustc_target::target_features::RUSTC_SPECIFIC_FEATURES.iter().cloned()) .map(Symbol::intern), ); diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 3df8f0590a354..eec07a8c3517e 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -1,10 +1,17 @@ +//! Declares Rust's target feature names for each target. +//! Note that these are similar to but not always identical to LLVM's feature names, +//! and Rust adds some features that do not correspond to LLVM features at all. use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_span::symbol::{Symbol, sym}; /// Features that control behaviour of rustc, rather than the codegen. +/// These exist globally and are not in the target-specific lists below. pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; -/// Features that require special handling when passing to LLVM. +/// Features that require special handling when passing to LLVM: +/// these are target-specific (i.e., must also be listed in the target-specific list below) +/// but do not correspond to an LLVM target feature. pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"]; /// Stability information for target features. @@ -16,26 +23,47 @@ pub enum Stability { /// This target feature is unstable; using it in `#[target_feature]` or `#[cfg(target_feature)]` /// requires enabling the given nightly feature. Unstable(Symbol), + /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be set in the basic + /// target definition. Used in particular for features that change the floating-point ABI. + Forbidden { reason: &'static str }, } use Stability::*; -impl Stability { - pub fn as_feature_name(self) -> Option { +impl HashStable for Stability { + #[inline] + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + std::mem::discriminant(self).hash_stable(hcx, hasher); match self { - Stable => None, - Unstable(s) => Some(s), + Stable => {} + Unstable(sym) => { + sym.hash_stable(hcx, hasher); + } + Forbidden { .. } => {} } } +} +impl Stability { pub fn is_stable(self) -> bool { matches!(self, Stable) } + + /// Forbidden features are not supported. + pub fn is_supported(self) -> bool { + !matches!(self, Forbidden { .. }) + } } // Here we list target features that rustc "understands": they can be used in `#[target_feature]` // and `#[cfg(target_feature)]`. They also do not trigger any warnings when used with // `-Ctarget-feature`. // +// Note that even unstable (and even entirely unlisted) features can be used with `-Ctarget-feature` +// on stable. Using a feature not on the list of Rust target features only emits a warning. +// Only `cfg(target_feature)` and `#[target_feature]` actually do any stability gating. +// `cfg(target_feature)` for unstable features just works on nightly without any feature gate. +// `#[target_feature]` requires a feature gate. +// // When adding features to the below lists // check whether they're named already elsewhere in rust // e.g. in stdarch and whether the given name matches LLVM's @@ -46,17 +74,27 @@ impl Stability { // per-function level, since we would then allow safe calls from functions with `+soft-float` to // functions without that feature! // -// When adding a new feature, be particularly mindful of features that affect function ABIs. Those -// need to be treated very carefully to avoid introducing unsoundness! This often affects features -// that enable/disable hardfloat support (see https://github.com/rust-lang/rust/issues/116344 for an -// example of this going wrong), but features enabling new SIMD registers are also a concern (see -// https://github.com/rust-lang/rust/issues/116558 for an example of this going wrong). +// It is important for soundness that features allowed here do *not* change the function call ABI. +// For example, disabling the `x87` feature on x86 changes how scalar floats are passed as +// arguments, so enabling toggling that feature would be unsound. In fact, since `-Ctarget-feature` +// will just allow unknown features (with a warning), we have to explicitly list features that change +// the ABI as `Forbidden` to ensure using them causes an error. Note that this is only effective if +// such features can never be toggled via `-Ctarget-cpu`! If that is ever a possibility, we will need +// extra checks ensuring that the LLVM-computed target features for a CPU did not (un)set a +// `Forbidden` feature. See https://github.com/rust-lang/rust/issues/116344 for some more context. +// FIXME: add such "forbidden" features for non-x86 targets. +// +// The one exception to features that change the ABI is features that enable larger vector +// registers. Those are permitted to be listed here. This is currently unsound (see +// https://github.com/rust-lang/rust/issues/116558); in the future we will have to ensure that +// functions can only use such vectors as arguments/return types if the corresponding target feature +// is enabled. // // Stabilizing a target feature requires t-lang approval. type ImpliedFeatures = &'static [&'static str]; -const ARM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("aclass", Unstable(sym::arm_target_feature), &[]), ("aes", Unstable(sym::arm_target_feature), &["neon"]), @@ -70,6 +108,7 @@ const ARM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("neon", Unstable(sym::arm_target_feature), &["vfp3"]), ("rclass", Unstable(sym::arm_target_feature), &[]), ("sha2", Unstable(sym::arm_target_feature), &["neon"]), + ("soft-float", Forbidden { reason: "unsound because it changes float ABI" }, &[]), // This is needed for inline assembly, but shouldn't be stabilized as-is // since it should be enabled per-function using #[instruction_set], not // #[target_feature]. @@ -87,9 +126,10 @@ const ARM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), ("virtualization", Unstable(sym::arm_target_feature), &[]), // tidy-alphabetical-end + // FIXME: need to also forbid turning off `fpregs` on hardfloat targets ]; -const AARCH64_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start // FEAT_AES & FEAT_PMULL ("aes", Stable, &["neon"]), @@ -277,7 +317,7 @@ const AARCH64_TIED_FEATURES: &[&[&str]] = &[ &["paca", "pacg"], // Together these represent `pauth` in LLVM ]; -const X86_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("adx", Stable, &[]), ("aes", Stable, &["sse2"]), @@ -328,6 +368,7 @@ const X86_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]), ("sm3", Unstable(sym::sha512_sm_x86), &["avx"]), ("sm4", Unstable(sym::sha512_sm_x86), &["avx2"]), + ("soft-float", Forbidden { reason: "unsound because it changes float ABI" }, &[]), ("sse", Stable, &[]), ("sse2", Stable, &["sse"]), ("sse3", Stable, &["sse2"]), @@ -344,16 +385,17 @@ const X86_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("xsaveopt", Stable, &["xsave"]), ("xsaves", Stable, &["xsave"]), // tidy-alphabetical-end + // FIXME: need to also forbid turning off `x87` on hardfloat targets ]; -const HEXAGON_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const HEXAGON_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("hvx", Unstable(sym::hexagon_target_feature), &[]), ("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]), // tidy-alphabetical-end ]; -const POWERPC_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const POWERPC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("altivec", Unstable(sym::powerpc_target_feature), &[]), ("partword-atomics", Unstable(sym::powerpc_target_feature), &[]), @@ -367,7 +409,7 @@ const POWERPC_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-end ]; -const MIPS_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const MIPS_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("fp64", Unstable(sym::mips_target_feature), &[]), ("msa", Unstable(sym::mips_target_feature), &[]), @@ -375,7 +417,7 @@ const MIPS_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-end ]; -const RISCV_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("a", Stable, &["zaamo", "zalrsc"]), ("c", Stable, &[]), @@ -415,7 +457,7 @@ const RISCV_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-end ]; -const WASM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const WASM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("atomics", Unstable(sym::wasm_target_feature), &[]), ("bulk-memory", Stable, &[]), @@ -431,10 +473,10 @@ const WASM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-end ]; -const BPF_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = +const BPF_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[("alu32", Unstable(sym::bpf_target_feature), &[])]; -const CSKY_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const CSKY_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("10e60", Unstable(sym::csky_target_feature), &["7e10"]), ("2e3", Unstable(sym::csky_target_feature), &["e2"]), @@ -481,7 +523,7 @@ const CSKY_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-end ]; -const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const LOONGARCH_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("d", Unstable(sym::loongarch_target_feature), &["f"]), ("f", Unstable(sym::loongarch_target_feature), &[]), @@ -495,7 +537,7 @@ const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-end ]; -const IBMZ_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ +const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("backchain", Unstable(sym::s390x_target_feature), &[]), ("vector", Unstable(sym::s390x_target_feature), &[]), @@ -506,41 +548,39 @@ const IBMZ_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ /// primitives may be documented. /// /// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator! -pub fn all_known_features() -> impl Iterator { +pub fn all_rust_features() -> impl Iterator { std::iter::empty() - .chain(ARM_ALLOWED_FEATURES.iter()) - .chain(AARCH64_ALLOWED_FEATURES.iter()) - .chain(X86_ALLOWED_FEATURES.iter()) - .chain(HEXAGON_ALLOWED_FEATURES.iter()) - .chain(POWERPC_ALLOWED_FEATURES.iter()) - .chain(MIPS_ALLOWED_FEATURES.iter()) - .chain(RISCV_ALLOWED_FEATURES.iter()) - .chain(WASM_ALLOWED_FEATURES.iter()) - .chain(BPF_ALLOWED_FEATURES.iter()) - .chain(CSKY_ALLOWED_FEATURES) - .chain(LOONGARCH_ALLOWED_FEATURES) - .chain(IBMZ_ALLOWED_FEATURES) + .chain(ARM_FEATURES.iter()) + .chain(AARCH64_FEATURES.iter()) + .chain(X86_FEATURES.iter()) + .chain(HEXAGON_FEATURES.iter()) + .chain(POWERPC_FEATURES.iter()) + .chain(MIPS_FEATURES.iter()) + .chain(RISCV_FEATURES.iter()) + .chain(WASM_FEATURES.iter()) + .chain(BPF_FEATURES.iter()) + .chain(CSKY_FEATURES) + .chain(LOONGARCH_FEATURES) + .chain(IBMZ_FEATURES) .cloned() .map(|(f, s, _)| (f, s)) } impl super::spec::Target { - pub fn supported_target_features( - &self, - ) -> &'static [(&'static str, Stability, ImpliedFeatures)] { + pub fn rust_target_features(&self) -> &'static [(&'static str, Stability, ImpliedFeatures)] { match &*self.arch { - "arm" => ARM_ALLOWED_FEATURES, - "aarch64" | "arm64ec" => AARCH64_ALLOWED_FEATURES, - "x86" | "x86_64" => X86_ALLOWED_FEATURES, - "hexagon" => HEXAGON_ALLOWED_FEATURES, - "mips" | "mips32r6" | "mips64" | "mips64r6" => MIPS_ALLOWED_FEATURES, - "powerpc" | "powerpc64" => POWERPC_ALLOWED_FEATURES, - "riscv32" | "riscv64" => RISCV_ALLOWED_FEATURES, - "wasm32" | "wasm64" => WASM_ALLOWED_FEATURES, - "bpf" => BPF_ALLOWED_FEATURES, - "csky" => CSKY_ALLOWED_FEATURES, - "loongarch64" => LOONGARCH_ALLOWED_FEATURES, - "s390x" => IBMZ_ALLOWED_FEATURES, + "arm" => ARM_FEATURES, + "aarch64" | "arm64ec" => AARCH64_FEATURES, + "x86" | "x86_64" => X86_FEATURES, + "hexagon" => HEXAGON_FEATURES, + "mips" | "mips32r6" | "mips64" | "mips64r6" => MIPS_FEATURES, + "powerpc" | "powerpc64" => POWERPC_FEATURES, + "riscv32" | "riscv64" => RISCV_FEATURES, + "wasm32" | "wasm64" => WASM_FEATURES, + "bpf" => BPF_FEATURES, + "csky" => CSKY_FEATURES, + "loongarch64" => LOONGARCH_FEATURES, + "s390x" => IBMZ_FEATURES, _ => &[], } } @@ -557,7 +597,7 @@ impl super::spec::Target { base_features: impl Iterator, ) -> FxHashSet { let implied_features = self - .supported_target_features() + .rust_target_features() .iter() .map(|(f, _, i)| (Symbol::intern(f), i)) .collect::>(); diff --git a/tests/ui/auxiliary/using-target-feature-unstable.rs b/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs similarity index 100% rename from tests/ui/auxiliary/using-target-feature-unstable.rs rename to tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs diff --git a/tests/ui/target-feature/forbidden-target-feature-attribute.rs b/tests/ui/target-feature/forbidden-target-feature-attribute.rs new file mode 100644 index 0000000000000..91c56b43689e1 --- /dev/null +++ b/tests/ui/target-feature/forbidden-target-feature-attribute.rs @@ -0,0 +1,12 @@ +//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib +//@ needs-llvm-components: x86 +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +#[target_feature(enable = "soft-float")] +//~^ERROR: cannot be toggled with +pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/forbidden-target-feature-attribute.stderr b/tests/ui/target-feature/forbidden-target-feature-attribute.stderr new file mode 100644 index 0000000000000..fb318531f7edc --- /dev/null +++ b/tests/ui/target-feature/forbidden-target-feature-attribute.stderr @@ -0,0 +1,8 @@ +error: target feature `soft-float` cannot be toggled with `#[target_feature]`: unsound because it changes float ABI + --> $DIR/forbidden-target-feature-attribute.rs:10:18 + | +LL | #[target_feature(enable = "soft-float")] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-feature/forbidden-target-feature-cfg.rs b/tests/ui/target-feature/forbidden-target-feature-cfg.rs new file mode 100644 index 0000000000000..5df26e26793d9 --- /dev/null +++ b/tests/ui/target-feature/forbidden-target-feature-cfg.rs @@ -0,0 +1,15 @@ +//@ compile-flags: --target=x86_64-unknown-none --crate-type=lib +//@ needs-llvm-components: x86 +//@ check-pass +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![allow(unexpected_cfgs)] + +#[lang = "sized"] +pub trait Sized {} + +// The compile_error macro does not exist, so if the `cfg` evaluates to `true` this +// complains about the missing macro rather than showing the error... but that's good enough. +#[cfg(target_feature = "soft-float")] +compile_error!("the soft-float feature should not be exposed in `cfg`"); diff --git a/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs b/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs new file mode 100644 index 0000000000000..b27e8a10afee7 --- /dev/null +++ b/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs @@ -0,0 +1,11 @@ +//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib +//@ needs-llvm-components: x86 +//@ compile-flags: -Ctarget-feature=-soft-float +// For now this is just a warning. +//@ build-pass +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} diff --git a/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr b/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr new file mode 100644 index 0000000000000..508e1fe0cf476 --- /dev/null +++ b/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr @@ -0,0 +1,7 @@ +warning: target feature `soft-float` cannot be toggled with `-Ctarget-feature`: unsound because it changes float ABI + | + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116344 + +warning: 1 warning emitted + diff --git a/tests/ui/target-feature/forbidden-target-feature-flag.rs b/tests/ui/target-feature/forbidden-target-feature-flag.rs new file mode 100644 index 0000000000000..93cebc6b53690 --- /dev/null +++ b/tests/ui/target-feature/forbidden-target-feature-flag.rs @@ -0,0 +1,11 @@ +//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib +//@ needs-llvm-components: x86 +//@ compile-flags: -Ctarget-feature=+soft-float +// For now this is just a warning. +//@ build-pass +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} diff --git a/tests/ui/target-feature/forbidden-target-feature-flag.stderr b/tests/ui/target-feature/forbidden-target-feature-flag.stderr new file mode 100644 index 0000000000000..508e1fe0cf476 --- /dev/null +++ b/tests/ui/target-feature/forbidden-target-feature-flag.stderr @@ -0,0 +1,7 @@ +warning: target feature `soft-float` cannot be toggled with `-Ctarget-feature`: unsound because it changes float ABI + | + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116344 + +warning: 1 warning emitted + diff --git a/tests/ui/using-target-feature-unstable.rs b/tests/ui/target-feature/using-target-feature-unstable.rs similarity index 100% rename from tests/ui/using-target-feature-unstable.rs rename to tests/ui/target-feature/using-target-feature-unstable.rs