about summary refs log tree commit diff
path: root/compiler/rustc_passes/src/stability.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_passes/src/stability.rs')
-rw-r--r--compiler/rustc_passes/src/stability.rs194
1 files changed, 98 insertions, 96 deletions
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 264dd364b5b..4a793f1875e 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -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