diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 2753ac529d12d..3a7ea36f60162 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -381,6 +381,26 @@ pub fn find_const_stability( const_stab } +/// Calculates the const stability for a const function in a `-Zforce-unstable-if-unmarked` crate +/// without the `staged_api` feature. +pub fn unmarked_crate_const_stab( + _sess: &Session, + attrs: &[Attribute], + regular_stab: Stability, +) -> ConstStability { + assert!(regular_stab.level.is_unstable()); + // The only attribute that matters here is `rustc_const_stable_indirect`. + // We enforce recursive const stability rules for those functions. + let const_stable_indirect = + attrs.iter().any(|a| a.name_or_empty() == sym::rustc_const_stable_indirect); + ConstStability { + feature: Some(regular_stab.feature), + const_stable_indirect, + promotable: false, + level: regular_stab.level, + } +} + /// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`. /// Returns `None` if no stability attributes are found. pub fn find_body_stability( diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index dcdaafaecc215..3b2a79793ae4b 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -53,10 +53,11 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { } pub fn enforce_recursive_const_stability(&self) -> bool { - // We can skip this if `staged_api` is not enabled, since in such crates - // `lookup_const_stability` will always be `None`. + // We can skip this if neither `staged_api` nor `-Zforrce-unstable-if-unmarked` are enabled, + // since in such crates `lookup_const_stability` will always be `None`. self.const_kind == Some(hir::ConstContext::ConstFn) - && self.tcx.features().staged_api() + && (self.tcx.features().staged_api() + || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked) && is_safe_to_expose_on_stable_const_fn(self.tcx, self.def_id().to_def_id()) } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index c12005683f12e..2759a7a828907 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -149,6 +149,11 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if let Some(stab) = self.parent_stab { if inherit_deprecation.yes() && stab.is_unstable() { self.index.stab_map.insert(def_id, stab); + if fn_sig.is_some_and(|s| s.header.is_const()) { + let const_stab = + attr::unmarked_crate_const_stab(self.tcx.sess, attrs, stab); + self.index.const_stab_map.insert(def_id, const_stab); + } } } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 1de52eb7b21b2..a8755df78c6b0 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -392,6 +392,11 @@ #![feature(stdarch_internal)] // tidy-alphabetical-end // +// Library features (crates without staged_api): +// tidy-alphabetical-start +#![feature(rustc_private)] +// tidy-alphabetical-end +// // Only for re-exporting: // tidy-alphabetical-start #![feature(assert_matches)] diff --git a/tests/ui/consts/min_const_fn/auxiliary/unmarked_const_fn_crate.rs b/tests/ui/consts/min_const_fn/auxiliary/unmarked_const_fn_crate.rs new file mode 100644 index 0000000000000..aec92c5ae1609 --- /dev/null +++ b/tests/ui/consts/min_const_fn/auxiliary/unmarked_const_fn_crate.rs @@ -0,0 +1 @@ +pub const fn just_a_fn() {} diff --git a/tests/ui/consts/min_const_fn/auxiliary/unstable_if_unmarked_const_fn_crate.rs b/tests/ui/consts/min_const_fn/auxiliary/unstable_if_unmarked_const_fn_crate.rs new file mode 100644 index 0000000000000..f102902fce3eb --- /dev/null +++ b/tests/ui/consts/min_const_fn/auxiliary/unstable_if_unmarked_const_fn_crate.rs @@ -0,0 +1,8 @@ +//@ compile-flags: -Zforce-unstable-if-unmarked + +#![feature(rustc_attrs)] + +pub const fn not_stably_const() {} + +#[rustc_const_stable_indirect] +pub const fn expose_on_stable() {} diff --git a/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.rs b/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.rs new file mode 100644 index 0000000000000..f03bfb81a1462 --- /dev/null +++ b/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.rs @@ -0,0 +1,23 @@ +//@ aux-build:unstable_if_unmarked_const_fn_crate.rs +//@ aux-build:unmarked_const_fn_crate.rs +#![feature(staged_api, rustc_private)] +#![stable(since="1.0.0", feature = "stable")] + +extern crate unstable_if_unmarked_const_fn_crate; +extern crate unmarked_const_fn_crate; + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_stable(feature = "rust1", since = "1.0.0")] +const fn stable_fn() { + // This one is fine. + unstable_if_unmarked_const_fn_crate::expose_on_stable(); + // This one is not. + unstable_if_unmarked_const_fn_crate::not_stably_const(); + //~^ERROR: cannot use `#[feature(rustc_private)]` + unmarked_const_fn_crate::just_a_fn(); + //~^ERROR: cannot be (indirectly) exposed to stable +} + +fn main() { + +} diff --git a/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr b/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr new file mode 100644 index 0000000000000..a655c0faab629 --- /dev/null +++ b/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr @@ -0,0 +1,28 @@ +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(rustc_private)]` + --> $DIR/recursive_const_stab_unmarked_crate_imports.rs:15:5 + | +LL | unstable_if_unmarked_const_fn_crate::not_stably_const(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features +help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn stable_fn() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(rustc_private)] +LL | const fn stable_fn() { + | + +error: `just_a_fn` cannot be (indirectly) exposed to stable + --> $DIR/recursive_const_stab_unmarked_crate_imports.rs:17:5 + | +LL | unmarked_const_fn_crate::just_a_fn(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.rs b/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.rs new file mode 100644 index 0000000000000..51811b14203ad --- /dev/null +++ b/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.rs @@ -0,0 +1,24 @@ +//@ compile-flags: -Zforce-unstable-if-unmarked +//@ edition: 2021 +#![feature(const_async_blocks, rustc_attrs, rustc_private)] + +pub const fn not_stably_const() { + // We need to do something const-unstable here. + // For now we use `async`, eventually we might have to add a auxiliary crate + // as a dependency just to be sure we have something const-unstable. + let _x = async { 15 }; +} + +#[rustc_const_stable_indirect] +pub const fn expose_on_stable() { + // Calling `not_stably_const` here is *not* okay. + not_stably_const(); + //~^ERROR: cannot use `#[feature(rustc_private)]` + // Also directly using const-unstable things is not okay. + let _x = async { 15 }; + //~^ERROR: cannot use `#[feature(const_async_blocks)]` +} + +fn main() { + const { expose_on_stable() }; +} diff --git a/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr b/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr new file mode 100644 index 0000000000000..d4ba0f9df2daf --- /dev/null +++ b/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr @@ -0,0 +1,37 @@ +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(rustc_private)]` + --> $DIR/recursive_const_stab_unstable_if_unmarked.rs:15:5 + | +LL | not_stably_const(); + | ^^^^^^^^^^^^^^^^^^ + | + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features +help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | pub const fn expose_on_stable() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(rustc_private)] +LL | pub const fn expose_on_stable() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_async_blocks)]` + --> $DIR/recursive_const_stab_unstable_if_unmarked.rs:18:14 + | +LL | let _x = async { 15 }; + | ^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | pub const fn expose_on_stable() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_async_blocks)] +LL | pub const fn expose_on_stable() { + | + +error: aborting due to 2 previous errors +