honor rustc_const_stable_indirect in non-staged_api crate with -Zforce-unstable-if-unmarked

This commit is contained in:
Ralf Jung 2024-11-02 20:15:18 +01:00
parent 668959740f
commit 686eeb83e9
10 changed files with 155 additions and 3 deletions

View File

@ -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(

View File

@ -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())
}

View File

@ -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);
}
}
}

View File

@ -391,6 +391,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)]

View File

@ -0,0 +1 @@
pub const fn just_a_fn() {}

View File

@ -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() {}

View File

@ -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() {
}

View File

@ -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

View File

@ -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() };
}

View File

@ -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