mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-21 22:34:05 +00:00
Rollup merge of #132541 - RalfJung:const-stable-extern-crate, r=compiler-errors
Proper support for cross-crate recursive const stability checks ~~Stacked on top of https://github.com/rust-lang/rust/pull/132492; only the last three commits are new.~~ In a crate without `staged_api` but with `-Zforce-unstable-if-unmarked`, we now subject all functions marked with `#[rustc_const_stable_indirect]` to recursive const stability checks. We require an opt-in so that by default, a crate can be built with `-Zforce-unstable-if-unmarked` and use nightly features as usual. This property is recorded in the crate metadata so when a `staged_api` crate calls such a function, it sees the `#[rustc_const_stable_indirect]` and allows it to be exposed on stable. This, finally, will let us expose `const fn` from hashbrown on stable. The second commit makes const stability more like regular stability: via `check_missing_const_stability`, we ensure that all publicly reachable functions have a const stability attribute -- both in `staged_api` crates and `-Zforce-unstable-if-unmarked` crates. To achieve this, we move around the stability computation so that const stability is computed after regular stability is done. This lets us access the final result of the regular stability computation, which we use so that `const fn` can inherit the regular stability (but only if that is "unstable"). Fortunately, this lets us get rid of an `Option` in `ConstStability`. This is the last PR that I have planned in this series. r? `@compiler-errors`
This commit is contained in:
commit
4a699fc475
@ -16,9 +16,9 @@ use rustc_session::lint::BuiltinLintDiag;
|
||||
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_session::{RustcVersion, Session};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::symbol::{Symbol, kw, sym};
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
|
||||
use crate::fluent_generated;
|
||||
use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
|
||||
@ -92,9 +92,7 @@ impl Stability {
|
||||
#[derive(HashStable_Generic)]
|
||||
pub struct ConstStability {
|
||||
pub level: StabilityLevel,
|
||||
/// This can be `None` for functions that do not have an explicit const feature.
|
||||
/// We still track them for recursive const stability checks.
|
||||
pub feature: Option<Symbol>,
|
||||
pub feature: Symbol,
|
||||
/// This is true iff the `const_stable_indirect` attribute is present.
|
||||
pub const_stable_indirect: bool,
|
||||
/// whether the function has a `#[rustc_promotable]` attribute
|
||||
@ -272,22 +270,19 @@ pub fn find_stability(
|
||||
|
||||
/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable`
|
||||
/// attributes in `attrs`. Returns `None` if no stability attributes are found.
|
||||
///
|
||||
/// `is_const_fn` indicates whether this is a function marked as `const`.
|
||||
pub fn find_const_stability(
|
||||
sess: &Session,
|
||||
attrs: &[Attribute],
|
||||
item_sp: Span,
|
||||
is_const_fn: bool,
|
||||
) -> Option<(ConstStability, Span)> {
|
||||
let mut const_stab: Option<(ConstStability, Span)> = None;
|
||||
let mut promotable = false;
|
||||
let mut const_stable_indirect = None;
|
||||
let mut const_stable_indirect = false;
|
||||
|
||||
for attr in attrs {
|
||||
match attr.name_or_empty() {
|
||||
sym::rustc_promotable => promotable = true,
|
||||
sym::rustc_const_stable_indirect => const_stable_indirect = Some(attr.span),
|
||||
sym::rustc_const_stable_indirect => const_stable_indirect = true,
|
||||
sym::rustc_const_unstable => {
|
||||
if const_stab.is_some() {
|
||||
sess.dcx()
|
||||
@ -299,7 +294,7 @@ pub fn find_const_stability(
|
||||
const_stab = Some((
|
||||
ConstStability {
|
||||
level,
|
||||
feature: Some(feature),
|
||||
feature,
|
||||
const_stable_indirect: false,
|
||||
promotable: false,
|
||||
},
|
||||
@ -317,7 +312,7 @@ pub fn find_const_stability(
|
||||
const_stab = Some((
|
||||
ConstStability {
|
||||
level,
|
||||
feature: Some(feature),
|
||||
feature,
|
||||
const_stable_indirect: false,
|
||||
promotable: false,
|
||||
},
|
||||
@ -340,7 +335,7 @@ pub fn find_const_stability(
|
||||
}
|
||||
}
|
||||
}
|
||||
if const_stable_indirect.is_some() {
|
||||
if const_stable_indirect {
|
||||
match &mut const_stab {
|
||||
Some((stab, _)) => {
|
||||
if stab.is_const_unstable() {
|
||||
@ -351,36 +346,37 @@ pub fn find_const_stability(
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
_ => {
|
||||
// This function has no const stability attribute, but has `const_stable_indirect`.
|
||||
// We ignore that; unmarked functions are subject to recursive const stability
|
||||
// checks by default so we do carry out the user's intent.
|
||||
}
|
||||
}
|
||||
}
|
||||
// Make sure if `const_stable_indirect` is present, that is recorded. Also make sure all `const
|
||||
// fn` get *some* marker, since we are a staged_api crate and therefore will do recursive const
|
||||
// stability checks for them. We need to do this because the default for whether an unmarked
|
||||
// function enforces recursive stability differs between staged-api crates and force-unmarked
|
||||
// crates: in force-unmarked crates, only functions *explicitly* marked `const_stable_indirect`
|
||||
// enforce recursive stability. Therefore when `lookup_const_stability` is `None`, we have to
|
||||
// assume the function does not have recursive stability. All functions that *do* have recursive
|
||||
// stability must explicitly record this, and so that's what we do for all `const fn` in a
|
||||
// staged_api crate.
|
||||
if (is_const_fn || const_stable_indirect.is_some()) && const_stab.is_none() {
|
||||
let c = ConstStability {
|
||||
feature: None,
|
||||
const_stable_indirect: const_stable_indirect.is_some(),
|
||||
promotable: false,
|
||||
level: StabilityLevel::Unstable {
|
||||
reason: UnstableReason::Default,
|
||||
issue: None,
|
||||
is_soft: false,
|
||||
implied_by: None,
|
||||
},
|
||||
};
|
||||
const_stab = Some((c, const_stable_indirect.unwrap_or(DUMMY_SP)));
|
||||
}
|
||||
|
||||
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: 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(
|
||||
|
@ -3,6 +3,7 @@
|
||||
use std::assert_matches::assert_matches;
|
||||
use std::borrow::Cow;
|
||||
use std::mem;
|
||||
use std::num::NonZero;
|
||||
use std::ops::Deref;
|
||||
|
||||
use rustc_attr::{ConstStability, StabilityLevel};
|
||||
@ -709,6 +710,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
|
||||
// Intrinsics are language primitives, not regular calls, so treat them separately.
|
||||
if let Some(intrinsic) = tcx.intrinsic(callee) {
|
||||
if !tcx.is_const_fn(callee) {
|
||||
// Non-const intrinsic.
|
||||
self.check_op(ops::IntrinsicNonConst { name: intrinsic.name });
|
||||
// If we allowed this, we're in miri-unleashed mode, so we might
|
||||
// as well skip the remaining checks.
|
||||
return;
|
||||
}
|
||||
// We use `intrinsic.const_stable` to determine if this can be safely exposed to
|
||||
// stable code, rather than `const_stable_indirect`. This is to make
|
||||
// `#[rustc_const_stable_indirect]` an attribute that is always safe to add.
|
||||
@ -716,17 +724,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
// fallback body is safe to expose on stable.
|
||||
let is_const_stable = intrinsic.const_stable
|
||||
|| (!intrinsic.must_be_overridden
|
||||
&& tcx.is_const_fn(callee)
|
||||
&& is_safe_to_expose_on_stable_const_fn(tcx, callee));
|
||||
match tcx.lookup_const_stability(callee) {
|
||||
None => {
|
||||
// Non-const intrinsic.
|
||||
self.check_op(ops::IntrinsicNonConst { name: intrinsic.name });
|
||||
}
|
||||
Some(ConstStability { feature: None, .. }) => {
|
||||
// Intrinsic does not need a separate feature gate (we rely on the
|
||||
// regular stability checker). However, we have to worry about recursive
|
||||
// const stability.
|
||||
// This doesn't need a separate const-stability check -- const-stability equals
|
||||
// regular stability, and regular stability is checked separately.
|
||||
// However, we *do* have to worry about *recursive* const stability.
|
||||
if !is_const_stable && self.enforce_recursive_const_stability() {
|
||||
self.dcx().emit_err(errors::UnmarkedIntrinsicExposed {
|
||||
span: self.span,
|
||||
@ -735,8 +738,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
Some(ConstStability {
|
||||
feature: Some(feature),
|
||||
level: StabilityLevel::Unstable { .. },
|
||||
feature,
|
||||
..
|
||||
}) => {
|
||||
self.check_op(ops::IntrinsicUnstable {
|
||||
@ -773,7 +776,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => {
|
||||
// All good.
|
||||
}
|
||||
None | Some(ConstStability { feature: None, .. }) => {
|
||||
None => {
|
||||
// This doesn't need a separate const-stability check -- const-stability equals
|
||||
// regular stability, and regular stability is checked separately.
|
||||
// However, we *do* have to worry about *recursive* const stability.
|
||||
@ -787,8 +790,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
Some(ConstStability {
|
||||
feature: Some(feature),
|
||||
level: StabilityLevel::Unstable { implied_by: implied_feature, .. },
|
||||
level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. },
|
||||
feature,
|
||||
..
|
||||
}) => {
|
||||
// An unstable const fn with a feature gate.
|
||||
@ -810,7 +813,23 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
// to allow this.
|
||||
let feature_enabled = callee.is_local()
|
||||
|| tcx.features().enabled(feature)
|
||||
|| implied_feature.is_some_and(|f| tcx.features().enabled(f));
|
||||
|| implied_feature.is_some_and(|f| tcx.features().enabled(f))
|
||||
|| {
|
||||
// When we're compiling the compiler itself we may pull in
|
||||
// crates from crates.io, but those crates may depend on other
|
||||
// crates also pulled in from crates.io. We want to ideally be
|
||||
// able to compile everything without requiring upstream
|
||||
// modifications, so in the case that this looks like a
|
||||
// `rustc_private` crate (e.g., a compiler crate) and we also have
|
||||
// the `-Z force-unstable-if-unmarked` flag present (we're
|
||||
// compiling a compiler crate), then let this missing feature
|
||||
// annotation slide.
|
||||
// This matches what we do in `eval_stability_allow_unstable` for
|
||||
// regular stability.
|
||||
feature == sym::rustc_private
|
||||
&& issue == NonZero::new(27812)
|
||||
&& self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked
|
||||
};
|
||||
// We do *not* honor this if we are in the "danger zone": we have to enforce
|
||||
// recursive const-stability and the callee is not safe-to-expose. In that
|
||||
// case we need `check_op` to do the check.
|
||||
|
@ -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 `-Zforce-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())
|
||||
}
|
||||
|
||||
@ -109,14 +110,15 @@ pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> b
|
||||
|
||||
match tcx.lookup_const_stability(def_id) {
|
||||
None => {
|
||||
// Only marked functions can be trusted. Note that this may be a function in a
|
||||
// non-staged-API crate where no recursive checks were done!
|
||||
false
|
||||
// In a `staged_api` crate, we do enforce recursive const stability for all unmarked
|
||||
// functions, so we can trust local functions. But in another crate we don't know which
|
||||
// rules were applied, so we can't trust that.
|
||||
def_id.is_local() && tcx.features().staged_api()
|
||||
}
|
||||
Some(stab) => {
|
||||
// We consider things safe-to-expose if they are stable, if they don't have any explicit
|
||||
// const stability attribute, or if they are marked as `const_stable_indirect`.
|
||||
stab.is_const_stable() || stab.feature.is_none() || stab.const_stable_indirect
|
||||
// We consider things safe-to-expose if they are stable or if they are marked as
|
||||
// `const_stable_indirect`.
|
||||
stab.is_const_stable() || stab.const_stable_indirect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -866,9 +866,7 @@ impl SyntaxExtension {
|
||||
})
|
||||
.unwrap_or_else(|| (None, helper_attrs));
|
||||
let stability = attr::find_stability(sess, attrs, span);
|
||||
// We set `is_const_fn` false to avoid getting any implicit const stability.
|
||||
let const_stability =
|
||||
attr::find_const_stability(sess, attrs, span, /* is_const_fn */ false);
|
||||
let const_stability = attr::find_const_stability(sess, attrs, span);
|
||||
let body_stability = attr::find_body_stability(sess, attrs);
|
||||
if let Some((_, sp)) = const_stability {
|
||||
sess.dcx().emit_err(errors::MacroConstStability {
|
||||
|
@ -16,7 +16,7 @@ use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId};
|
||||
use rustc_hir::hir_id::CRATE_HIR_ID;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{Constness, FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
|
||||
use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};
|
||||
use rustc_middle::middle::privacy::EffectiveVisibilities;
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,68 +166,11 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
|
||||
return;
|
||||
}
|
||||
|
||||
// # Regular and body stability
|
||||
|
||||
let stab = attr::find_stability(self.tcx.sess, attrs, item_sp);
|
||||
let const_stab = attr::find_const_stability(
|
||||
self.tcx.sess,
|
||||
attrs,
|
||||
item_sp,
|
||||
fn_sig.is_some_and(|s| s.header.is_const()),
|
||||
);
|
||||
let body_stab = attr::find_body_stability(self.tcx.sess, attrs);
|
||||
|
||||
// If the current node is a function with const stability attributes (directly given or
|
||||
// implied), check if the function/method is const or the parent impl block is const.
|
||||
if let Some(fn_sig) = fn_sig
|
||||
&& !fn_sig.header.is_const()
|
||||
&& const_stab.is_some()
|
||||
{
|
||||
self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span });
|
||||
}
|
||||
|
||||
// If this is marked const *stable*, it must also be regular-stable.
|
||||
if let Some((const_stab, const_span)) = const_stab
|
||||
&& let Some(fn_sig) = fn_sig
|
||||
&& const_stab.is_const_stable()
|
||||
&& !stab.is_some_and(|(s, _)| s.is_stable())
|
||||
{
|
||||
self.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::ConstStableNotStable { fn_sig_span: fn_sig.span, const_span });
|
||||
}
|
||||
|
||||
// Stable *language* features shouldn't be used as unstable library features.
|
||||
// (Not doing this for stable library features is checked by tidy.)
|
||||
if let Some((
|
||||
ConstStability { level: Unstable { .. }, feature: Some(feature), .. },
|
||||
const_span,
|
||||
)) = const_stab
|
||||
{
|
||||
if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() {
|
||||
self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature {
|
||||
span: const_span,
|
||||
item_sp,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let const_stab = const_stab.map(|(const_stab, _span)| {
|
||||
self.index.const_stab_map.insert(def_id, const_stab);
|
||||
const_stab
|
||||
});
|
||||
|
||||
// `impl const Trait for Type` items forward their const stability to their
|
||||
// immediate children.
|
||||
// FIXME(const_trait_impl): how is this supposed to interact with `#[rustc_const_stable_indirect]`?
|
||||
// Currently, once that is set, we do not inherit anything from the parent any more.
|
||||
if const_stab.is_none() {
|
||||
debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab);
|
||||
if let Some(parent) = self.parent_const_stab {
|
||||
if parent.is_const_unstable() {
|
||||
self.index.const_stab_map.insert(def_id, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((depr, span)) = &depr
|
||||
&& depr.is_since_rustc_version()
|
||||
&& stab.is_none()
|
||||
@ -289,15 +237,6 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
|
||||
self.index.implications.insert(implied_by, feature);
|
||||
}
|
||||
|
||||
if let Some(ConstStability {
|
||||
level: Unstable { implied_by: Some(implied_by), .. },
|
||||
feature,
|
||||
..
|
||||
}) = const_stab
|
||||
{
|
||||
self.index.implications.insert(implied_by, feature.unwrap());
|
||||
}
|
||||
|
||||
self.index.stab_map.insert(def_id, stab);
|
||||
stab
|
||||
});
|
||||
@ -311,6 +250,91 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
let final_stab = self.index.stab_map.get(&def_id);
|
||||
|
||||
// # Const stability
|
||||
|
||||
let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item_sp);
|
||||
|
||||
// If the current node is a function with const stability attributes (directly given or
|
||||
// implied), check if the function/method is const.
|
||||
if let Some(fn_sig) = fn_sig
|
||||
&& !fn_sig.header.is_const()
|
||||
&& const_stab.is_some()
|
||||
{
|
||||
self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span });
|
||||
}
|
||||
|
||||
// If this is marked const *stable*, it must also be regular-stable.
|
||||
if let Some((const_stab, const_span)) = const_stab
|
||||
&& let Some(fn_sig) = fn_sig
|
||||
&& const_stab.is_const_stable()
|
||||
&& !stab.is_some_and(|s| s.is_stable())
|
||||
{
|
||||
self.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::ConstStableNotStable { fn_sig_span: fn_sig.span, const_span });
|
||||
}
|
||||
|
||||
// Stable *language* features shouldn't be used as unstable library features.
|
||||
// (Not doing this for stable library features is checked by tidy.)
|
||||
if let Some((ConstStability { level: Unstable { .. }, feature, .. }, const_span)) =
|
||||
const_stab
|
||||
{
|
||||
if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() {
|
||||
self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature {
|
||||
span: const_span,
|
||||
item_sp,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// After checking the immediate attributes, get rid of the span and compute implied
|
||||
// const stability: inherit feature gate from regular stability.
|
||||
let mut const_stab = const_stab.map(|(stab, _span)| stab);
|
||||
|
||||
// If this is a const fn but not annotated with stability markers, see if we can inherit regular stability.
|
||||
if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() &&
|
||||
// We only ever inherit unstable features.
|
||||
let Some(inherit_regular_stab) =
|
||||
final_stab.filter(|s| s.is_unstable())
|
||||
{
|
||||
const_stab = Some(ConstStability {
|
||||
// We subject these implicitly-const functions to recursive const stability.
|
||||
const_stable_indirect: true,
|
||||
promotable: false,
|
||||
level: inherit_regular_stab.level,
|
||||
feature: inherit_regular_stab.feature,
|
||||
});
|
||||
}
|
||||
|
||||
// Now that everything is computed, insert it into the table.
|
||||
const_stab.inspect(|const_stab| {
|
||||
self.index.const_stab_map.insert(def_id, *const_stab);
|
||||
});
|
||||
|
||||
if let Some(ConstStability {
|
||||
level: Unstable { implied_by: Some(implied_by), .. },
|
||||
feature,
|
||||
..
|
||||
}) = const_stab
|
||||
{
|
||||
self.index.implications.insert(implied_by, feature);
|
||||
}
|
||||
|
||||
// `impl const Trait for Type` items forward their const stability to their
|
||||
// immediate children.
|
||||
// FIXME(const_trait_impl): how is this supposed to interact with `#[rustc_const_stable_indirect]`?
|
||||
// Currently, once that is set, we do not inherit anything from the parent any more.
|
||||
if const_stab.is_none() {
|
||||
debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab);
|
||||
if let Some(parent) = self.parent_const_stab {
|
||||
if parent.is_const_unstable() {
|
||||
self.index.const_stab_map.insert(def_id, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.recurse_with_stability_attrs(
|
||||
depr.map(|(d, _)| DeprecationEntry::local(d, def_id)),
|
||||
stab,
|
||||
@ -565,13 +589,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_missing_or_wrong_const_stability(&self, def_id: LocalDefId, span: Span) {
|
||||
// The visitor runs for "unstable-if-unmarked" crates, but we don't yet support
|
||||
// that on the const side.
|
||||
if !self.tcx.features().staged_api() {
|
||||
return;
|
||||
}
|
||||
|
||||
fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) {
|
||||
// if the const impl is derived using the `derive_const` attribute,
|
||||
// then it would be "stable" at least for the impl.
|
||||
// We gate usages of it using `feature(const_trait_impl)` anyways
|
||||
@ -582,12 +600,12 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
|
||||
|
||||
let is_const = self.tcx.is_const_fn(def_id.to_def_id())
|
||||
|| self.tcx.is_const_trait_impl(def_id.to_def_id());
|
||||
let is_stable =
|
||||
self.tcx.lookup_stability(def_id).is_some_and(|stability| stability.level.is_stable());
|
||||
let missing_const_stability_attribute =
|
||||
self.tcx.lookup_const_stability(def_id).is_none_or(|s| s.feature.is_none());
|
||||
|
||||
if is_const && is_stable && missing_const_stability_attribute {
|
||||
// Reachable const fn must have a stability attribute.
|
||||
if is_const
|
||||
&& self.effective_visibilities.is_reachable(def_id)
|
||||
&& self.tcx.lookup_const_stability(def_id).is_none()
|
||||
{
|
||||
let descr = self.tcx.def_descr(def_id.to_def_id());
|
||||
self.tcx.dcx().emit_err(errors::MissingConstStabAttr { span, descr });
|
||||
}
|
||||
@ -615,7 +633,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
|
||||
}
|
||||
|
||||
// Ensure stable `const fn` have a const stability attribute.
|
||||
self.check_missing_or_wrong_const_stability(i.owner_id.def_id, i.span);
|
||||
self.check_missing_const_stability(i.owner_id.def_id, i.span);
|
||||
|
||||
intravisit::walk_item(self, i)
|
||||
}
|
||||
@ -629,7 +647,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
|
||||
let impl_def_id = self.tcx.hir().get_parent_item(ii.hir_id());
|
||||
if self.tcx.impl_trait_ref(impl_def_id).is_none() {
|
||||
self.check_missing_stability(ii.owner_id.def_id, ii.span);
|
||||
self.check_missing_or_wrong_const_stability(ii.owner_id.def_id, ii.span);
|
||||
self.check_missing_const_stability(ii.owner_id.def_id, ii.span);
|
||||
}
|
||||
intravisit::walk_impl_item(self, ii);
|
||||
}
|
||||
@ -760,23 +778,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
|
||||
// For implementations of traits, check the stability of each item
|
||||
// individually as it's possible to have a stable trait with unstable
|
||||
// items.
|
||||
hir::ItemKind::Impl(hir::Impl {
|
||||
constness,
|
||||
of_trait: Some(ref t),
|
||||
self_ty,
|
||||
items,
|
||||
..
|
||||
}) => {
|
||||
hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref t), self_ty, items, .. }) => {
|
||||
let features = self.tcx.features();
|
||||
if features.staged_api() {
|
||||
let attrs = self.tcx.hir().attrs(item.hir_id());
|
||||
let stab = attr::find_stability(self.tcx.sess, attrs, item.span);
|
||||
let const_stab = attr::find_const_stability(
|
||||
self.tcx.sess,
|
||||
attrs,
|
||||
item.span,
|
||||
matches!(constness, Constness::Const),
|
||||
);
|
||||
let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item.span);
|
||||
|
||||
// If this impl block has an #[unstable] attribute, give an
|
||||
// error if all involved types and traits are stable, because
|
||||
|
@ -1011,9 +1011,7 @@ fn render_stability_since_raw_with_extra(
|
||||
// don't display const unstable if entirely unstable
|
||||
None
|
||||
} else {
|
||||
let unstable = if let Some(n) = issue
|
||||
&& let Some(feature) = feature
|
||||
{
|
||||
let unstable = if let Some(n) = issue {
|
||||
format!(
|
||||
"<a \
|
||||
href=\"https://github.com/rust-lang/rust/issues/{n}\" \
|
||||
|
@ -393,12 +393,8 @@ fn is_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
|
||||
|
||||
msrv.meets(const_stab_rust_version)
|
||||
} else {
|
||||
// Unstable const fn, check if the feature is enabled. We need both the regular stability
|
||||
// feature and (if set) the const stability feature to const-call this function.
|
||||
let stab = tcx.lookup_stability(def_id);
|
||||
let is_enabled = stab.is_some_and(|s| s.is_stable() || tcx.features().enabled(s.feature))
|
||||
&& const_stab.feature.is_none_or(|f| tcx.features().enabled(f));
|
||||
is_enabled && msrv.current().is_none()
|
||||
// Unstable const fn, check if the feature is enabled.
|
||||
tcx.features().enabled(const_stab.feature) && msrv.current().is_none()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -16,13 +16,13 @@ const fn const_main() {
|
||||
unsafe {
|
||||
unstable_intrinsic::size_of_val(&x);
|
||||
//~^ERROR: unstable library feature `unstable`
|
||||
//~|ERROR: cannot be (indirectly) exposed to stable
|
||||
//~|ERROR: not yet stable as a const intrinsic
|
||||
unstable_intrinsic::min_align_of_val(&x);
|
||||
//~^ERROR: unstable library feature `unstable`
|
||||
//~|ERROR: not yet stable as a const intrinsic
|
||||
|
||||
size_of_val(&x);
|
||||
//~^ERROR: cannot be (indirectly) exposed to stable
|
||||
//~^ERROR: cannot use `#[feature(local)]`
|
||||
min_align_of_val(&x);
|
||||
//~^ERROR: cannot use `#[feature(local)]`
|
||||
}
|
||||
@ -59,6 +59,6 @@ mod fallback {
|
||||
#[rustc_intrinsic]
|
||||
const unsafe fn copy<T>(src: *const T, _dst: *mut T, _count: usize) {
|
||||
super::size_of_val(src);
|
||||
//~^ ERROR cannot be (indirectly) exposed to stable
|
||||
//~^ ERROR cannot use `#[feature(local)]`
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,13 @@ LL | unstable_intrinsic::min_align_of_val(&x);
|
||||
= help: add `#![feature(unstable)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error: intrinsic `unstable_intrinsic::size_of_val` cannot be (indirectly) exposed to stable
|
||||
error: `size_of_val` is not yet stable as a const intrinsic
|
||||
--> $DIR/const-unstable-intrinsic.rs:17:9
|
||||
|
|
||||
LL | unstable_intrinsic::size_of_val(&x);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_const_stable_intrinsic]` (but this requires team approval)
|
||||
= help: add `#![feature(unstable)]` to the crate attributes to enable
|
||||
|
||||
error: `min_align_of_val` is not yet stable as a const intrinsic
|
||||
--> $DIR/const-unstable-intrinsic.rs:20:9
|
||||
@ -34,13 +34,22 @@ LL | unstable_intrinsic::min_align_of_val(&x);
|
||||
|
|
||||
= help: add `#![feature(unstable)]` to the crate attributes to enable
|
||||
|
||||
error: intrinsic `size_of_val` cannot be (indirectly) exposed to stable
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local)]`
|
||||
--> $DIR/const-unstable-intrinsic.rs:24:9
|
||||
|
|
||||
LL | size_of_val(&x);
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_const_stable_intrinsic]` (but this requires team approval)
|
||||
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 | const fn const_main() {
|
||||
|
|
||||
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(local)]
|
||||
LL | const fn const_main() {
|
||||
|
|
||||
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local)]`
|
||||
--> $DIR/const-unstable-intrinsic.rs:26:9
|
||||
@ -67,13 +76,22 @@ LL | unsafe { copy(src, dst, count) }
|
||||
|
|
||||
= help: mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_const_stable_intrinsic]` (but this requires team approval)
|
||||
|
||||
error: intrinsic `size_of_val` cannot be (indirectly) exposed to stable
|
||||
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local)]`
|
||||
--> $DIR/const-unstable-intrinsic.rs:61:9
|
||||
|
|
||||
LL | super::size_of_val(src);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_const_stable_intrinsic]` (but this requires team approval)
|
||||
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 | const unsafe fn copy<T>(src: *const T, _dst: *mut T, _count: usize) {
|
||||
|
|
||||
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(local)]
|
||||
LL | const unsafe fn copy<T>(src: *const T, _dst: *mut T, _count: usize) {
|
||||
|
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
pub const fn just_a_fn() {}
|
@ -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() {}
|
@ -0,0 +1,21 @@
|
||||
//@ 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 unmarked_const_fn_crate;
|
||||
extern crate unstable_if_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() {}
|
@ -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
|
||||
|
@ -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() };
|
||||
}
|
@ -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
|
||||
|
@ -56,9 +56,3 @@ const fn barfoo_unmarked() {}
|
||||
#[rustc_const_stable(feature = "barfoo_const", since = "1.0.0")]
|
||||
pub const fn barfoo_unstable() {}
|
||||
//~^ ERROR can only be applied to functions that are declared `#[stable]`
|
||||
|
||||
// `#[rustc_const_stable_indirect]` also requires a const fn
|
||||
#[rustc_const_stable_indirect]
|
||||
#[unstable(feature = "unstable", issue = "none")]
|
||||
pub fn not_a_const_fn() {}
|
||||
//~^ ERROR require the function or method to be `const`
|
||||
|
@ -86,17 +86,5 @@ LL | #[rustc_const_stable(feature = "barfoo_const", since = "1.0.0")]
|
||||
LL | pub const fn barfoo_unstable() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: attributes `#[rustc_const_unstable]`, `#[rustc_const_stable]` and `#[rustc_const_stable_indirect]` require the function or method to be `const`
|
||||
--> $DIR/rustc-const-stability-require-const.rs:63:1
|
||||
|
|
||||
LL | pub fn not_a_const_fn() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: make the function or method const
|
||||
--> $DIR/rustc-const-stability-require-const.rs:63:1
|
||||
|
|
||||
LL | pub fn not_a_const_fn() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
|
@ -1,17 +1,3 @@
|
||||
error: can't mark as unstable using an already stable feature
|
||||
--> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1
|
||||
|
|
||||
LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this feature is already stable
|
||||
LL | const fn my_fun() {}
|
||||
| -------------------- the stability attribute annotates this item
|
||||
|
|
||||
help: consider removing the attribute
|
||||
--> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1
|
||||
|
|
||||
LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: can't mark as unstable using an already stable feature
|
||||
--> $DIR/unstable-attribute-rejects-already-stable-features.rs:6:1
|
||||
|
|
||||
@ -27,5 +13,19 @@ help: consider removing the attribute
|
||||
LL | #[unstable(feature = "arbitrary_enum_discriminant", issue = "42")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: can't mark as unstable using an already stable feature
|
||||
--> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1
|
||||
|
|
||||
LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this feature is already stable
|
||||
LL | const fn my_fun() {}
|
||||
| -------------------- the stability attribute annotates this item
|
||||
|
|
||||
help: consider removing the attribute
|
||||
--> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1
|
||||
|
|
||||
LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user