Skip to content

Commit d2ff6a2

Browse files
committed
Re-do recursive const stability checks
Fundamentally, we have *three* disjoint categories of functions: 1. const-stable functions 2. private/unstable functions that are meant to be callable from const-stable functions 3. functions that can make use of unstable const features This PR implements the following system: - `#[rustc_const_stable]` puts functions in the first category. It may only be applied to `#[stable]` functions. - `#[rustc_const_unstable]` by default puts functions in the third category. The new attribute `#[rustc_const_stable_indirect]` can be added to such a function to move it into the second category. - `const fn` without a const stability marker are in the second category if they are still unstable. They automatically inherit the feature gate for regular calls, it can now also be used for const-calls. Also, several holes in recursive const stability checking are being closed. There's still one potential hole that is hard to avoid, which is when MIR building automatically inserts calls to a particular function in stable functions -- which happens in the panic machinery. Those need to *not* be `rustc_const_unstable` (or manually get a `rustc_const_stable_indirect`) to be sure they follow recursive const stability. But that's a fairly rare and special case so IMO it's fine. The net effect of this is that a `#[unstable]` or unmarked function can be constified simply by marking it as `const fn`, and it will then be const-callable from stable `const fn` and subject to recursive const stability requirements. If it is publicly reachable (which implies it cannot be unmarked), it will be const-unstable under the same feature gate. Only if the function ever becomes `#[stable]` does it need a `#[rustc_const_unstable]` or `#[rustc_const_stable]` marker to decide if this should also imply const-stability. Adding `#[rustc_const_unstable]` is only needed for (a) functions that need to use unstable const lang features (including intrinsics), or (b) `#[stable]` functions that are not yet intended to be const-stable. Adding `#[rustc_const_stable]` is only needed for functions that are actually meant to be directly callable from stable const code. `#[rustc_const_stable_indirect]` is used to mark intrinsics as const-callable and for `#[rustc_const_unstable]` functions that are actually called from other, exposed-on-stable `const fn`. No other attributes are required.
1 parent 2348c06 commit d2ff6a2

38 files changed

+252
-146
lines changed

alloc/src/raw_vec.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ impl<T> RawVec<T, Global> {
103103
/// `RawVec` with capacity `usize::MAX`. Useful for implementing
104104
/// delayed allocation.
105105
#[must_use]
106-
#[rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81")]
106+
#[cfg_attr(bootstrap, rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81"))]
107107
pub const fn new() -> Self {
108108
Self::new_in(Global)
109109
}
@@ -179,7 +179,7 @@ impl<T, A: Allocator> RawVec<T, A> {
179179
/// Like `new`, but parameterized over the choice of allocator for
180180
/// the returned `RawVec`.
181181
#[inline]
182-
#[rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81")]
182+
#[cfg_attr(bootstrap, rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81"))]
183183
pub const fn new_in(alloc: A) -> Self {
184184
Self { inner: RawVecInner::new_in(alloc, align_of::<T>()), _marker: PhantomData }
185185
}
@@ -409,7 +409,7 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec<T, A> {
409409

410410
impl<A: Allocator> RawVecInner<A> {
411411
#[inline]
412-
#[rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81")]
412+
#[cfg_attr(bootstrap, rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81"))]
413413
const fn new_in(alloc: A, align: usize) -> Self {
414414
let ptr = unsafe { core::mem::transmute(align) };
415415
// `cap: 0` means "unallocated". zero-sized types are ignored.

core/src/alloc/layout.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ impl Layout {
6666
#[stable(feature = "alloc_layout", since = "1.28.0")]
6767
#[rustc_const_stable(feature = "const_alloc_layout_size_align", since = "1.50.0")]
6868
#[inline]
69-
#[rustc_allow_const_fn_unstable(ptr_alignment_type)]
7069
pub const fn from_size_align(size: usize, align: usize) -> Result<Self, LayoutError> {
7170
if Layout::is_size_align_valid(size, align) {
7271
// SAFETY: Layout::is_size_align_valid checks the preconditions for this call.
@@ -127,7 +126,6 @@ impl Layout {
127126
#[rustc_const_stable(feature = "const_alloc_layout_unchecked", since = "1.36.0")]
128127
#[must_use]
129128
#[inline]
130-
#[rustc_allow_const_fn_unstable(ptr_alignment_type)]
131129
pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self {
132130
assert_unsafe_precondition!(
133131
check_library_ub,
@@ -159,7 +157,7 @@ impl Layout {
159157
#[must_use = "this returns the minimum alignment, \
160158
without modifying the layout"]
161159
#[inline]
162-
#[rustc_allow_const_fn_unstable(ptr_alignment_type)]
160+
#[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(ptr_alignment_type))]
163161
pub const fn align(&self) -> usize {
164162
self.align.as_usize()
165163
}

core/src/cell.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2287,6 +2287,7 @@ impl<T> SyncUnsafeCell<T> {
22872287

22882288
/// Unwraps the value, consuming the cell.
22892289
#[inline]
2290+
#[rustc_const_unstable(feature = "sync_unsafe_cell", issue = "95439")]
22902291
pub const fn into_inner(self) -> T {
22912292
self.value.into_inner()
22922293
}

core/src/cell/lazy.rs

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
7979
/// assert_eq!(LazyCell::into_inner(lazy).ok(), Some("HELLO, WORLD!".to_string()));
8080
/// ```
8181
#[unstable(feature = "lazy_cell_into_inner", issue = "125623")]
82+
#[rustc_const_unstable(feature = "lazy_cell_into_inner", issue = "125623")]
8283
pub const fn into_inner(this: Self) -> Result<T, F> {
8384
match this.state.into_inner() {
8485
State::Init(data) => Ok(data),

core/src/char/methods.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1770,7 +1770,7 @@ const fn len_utf16(code: u32) -> usize {
17701770
/// Panics if the buffer is not large enough.
17711771
/// A buffer of length four is large enough to encode any `char`.
17721772
#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")]
1773-
#[rustc_const_stable(feature = "const_char_encode_utf8", since = "1.83.0")]
1773+
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_char_encode_utf8", since = "1.83.0"))]
17741774
#[doc(hidden)]
17751775
#[inline]
17761776
#[rustc_allow_const_fn_unstable(const_eval_select)]

core/src/ffi/c_str.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,11 @@ enum FromBytesWithNulErrorKind {
137137

138138
// FIXME: const stability attributes should not be required here, I think
139139
impl FromBytesWithNulError {
140-
#[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")]
140+
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0"))]
141141
const fn interior_nul(pos: usize) -> FromBytesWithNulError {
142142
FromBytesWithNulError { kind: FromBytesWithNulErrorKind::InteriorNul(pos) }
143143
}
144-
#[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")]
144+
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0"))]
145145
const fn not_nul_terminated() -> FromBytesWithNulError {
146146
FromBytesWithNulError { kind: FromBytesWithNulErrorKind::NotNulTerminated }
147147
}
@@ -730,7 +730,7 @@ impl AsRef<CStr> for CStr {
730730
/// located within `isize::MAX` from `ptr`.
731731
#[inline]
732732
#[unstable(feature = "cstr_internals", issue = "none")]
733-
#[rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0")]
733+
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0"))]
734734
#[rustc_allow_const_fn_unstable(const_eval_select)]
735735
const unsafe fn strlen(ptr: *const c_char) -> usize {
736736
const fn strlen_ct(s: *const c_char) -> usize {

core/src/fmt/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,10 @@ pub struct Arguments<'a> {
333333
#[unstable(feature = "fmt_internals", issue = "none")]
334334
impl<'a> Arguments<'a> {
335335
#[inline]
336-
#[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")]
336+
#[cfg_attr(
337+
bootstrap,
338+
rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")
339+
)]
337340
pub const fn new_const<const N: usize>(pieces: &'a [&'static str; N]) -> Self {
338341
const { assert!(N <= 1) };
339342
Arguments { pieces, fmt: None, args: &[] }
@@ -438,6 +441,7 @@ impl<'a> Arguments<'a> {
438441
#[rustc_const_unstable(feature = "const_arguments_as_str", issue = "103900")]
439442
#[must_use]
440443
#[inline]
444+
#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)]
441445
pub const fn as_str(&self) -> Option<&'static str> {
442446
match (self.pieces, self.args) {
443447
([], []) => Some(""),

core/src/hint.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ pub const fn black_box<T>(dummy: T) -> T {
506506
/// # }
507507
/// ```
508508
#[unstable(feature = "hint_must_use", issue = "94745")]
509-
#[rustc_const_unstable(feature = "hint_must_use", issue = "94745")]
509+
#[cfg_attr(bootstrap, rustc_const_unstable(feature = "hint_must_use", issue = "94745"))]
510510
#[must_use] // <-- :)
511511
#[inline(always)]
512512
pub const fn must_use<T>(value: T) -> T {

0 commit comments

Comments
 (0)