1
Fork 0

ensure that all publicly reachable const fn have const stability info

This commit is contained in:
Ralf Jung 2024-11-02 21:19:21 +01:00
parent 686eeb83e9
commit e96808162a
12 changed files with 177 additions and 204 deletions

View file

@ -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;
@ -166,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()
@ -294,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
});
@ -316,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,
@ -570,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
@ -587,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 });
}
@ -620,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)
}
@ -634,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);
}
@ -765,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