summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc/query/mod.rs8
-rw-r--r--src/librustc_ast_lowering/item.rs10
-rw-r--r--src/librustc_mir/const_eval/fn_queries.rs130
-rw-r--r--src/librustc_mir/transform/check_consts/validation.rs20
-rw-r--r--src/librustc_mir/transform/qualify_min_const_fn.rs8
-rw-r--r--src/librustc_passes/stability.rs291
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/assoc-type.rs28
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs30
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr9
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs41
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs33
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr34
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs16
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr12
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr10
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs5
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr12
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/generic-bound.rs32
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs2
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr20
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/stability.rs43
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/stability.stderr24
22 files changed, 597 insertions, 221 deletions
diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs
index 45ab3fc0b85..3cee10f1356 100644
--- a/src/librustc/query/mod.rs
+++ b/src/librustc/query/mod.rs
@@ -279,6 +279,14 @@ rustc_queries! {
             desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) }
         }
 
+        /// Returns `true` if this is a const `impl`. **Do not call this function manually.**
+        ///
+        /// This query caches the base data for the `is_const_impl` helper function, which also
+        /// takes into account stability attributes (e.g., `#[rustc_const_unstable]`).
+        query is_const_impl_raw(key: DefId) -> bool {
+            desc { |tcx| "checking if item is const impl: `{}`", tcx.def_path_str(key) }
+        }
+
         query asyncness(key: DefId) -> hir::IsAsync {
             desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) }
         }
diff --git a/src/librustc_ast_lowering/item.rs b/src/librustc_ast_lowering/item.rs
index e0db8606bc2..ad7221b16b2 100644
--- a/src/librustc_ast_lowering/item.rs
+++ b/src/librustc_ast_lowering/item.rs
@@ -66,15 +66,7 @@ impl<'a> Visitor<'a> for ItemLowerer<'a, '_, '_> {
         if let Some(hir_id) = item_hir_id {
             self.lctx.with_parent_item_lifetime_defs(hir_id, |this| {
                 let this = &mut ItemLowerer { lctx: this };
-                if let ItemKind::Impl { constness, ref of_trait, .. } = item.kind {
-                    if let Const::Yes(span) = constness {
-                        this.lctx
-                            .diagnostic()
-                            .struct_span_err(item.span, "const trait impls are not yet implemented")
-                            .span_label(span, "const because of this")
-                            .emit();
-                    }
-
+                if let ItemKind::Impl { ref of_trait, .. } = item.kind {
                     this.with_trait_impl_ref(of_trait, |this| visit::walk_item(this, item));
                 } else {
                     visit::walk_item(this, item);
diff --git a/src/librustc_mir/const_eval/fn_queries.rs b/src/librustc_mir/const_eval/fn_queries.rs
index 4144bbc41d2..27efcd50841 100644
--- a/src/librustc_mir/const_eval/fn_queries.rs
+++ b/src/librustc_mir/const_eval/fn_queries.rs
@@ -3,7 +3,7 @@ use rustc::ty::query::Providers;
 use rustc::ty::TyCtxt;
 use rustc_attr as attr;
 use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_span::symbol::Symbol;
 use rustc_target::spec::abi::Abi;
 
@@ -82,72 +82,96 @@ pub fn is_min_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
     }
 }
 
-pub fn provide(providers: &mut Providers<'_>) {
-    /// Const evaluability whitelist is here to check evaluability at the
-    /// top level beforehand.
-    fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
-        if tcx.is_closure(def_id) {
-            return None;
-        }
+pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
+    let parent_id = tcx.hir().get_parent_did(hir_id);
+    if !parent_id.is_top_level_module() {
+        is_const_impl_raw(tcx, LocalDefId::from_def_id(parent_id))
+    } else {
+        false
+    }
+}
 
-        match tcx.fn_sig(def_id).abi() {
-            Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
-                Some(tcx.lookup_const_stability(def_id).is_some())
-            }
-            _ => None,
+/// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
+/// said intrinsic is on the whitelist for being const callable.
+fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+    let hir_id =
+        tcx.hir().as_local_hir_id(def_id).expect("Non-local call to local provider is_const_fn");
+
+    let node = tcx.hir().get(hir_id);
+
+    if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
+        whitelisted
+    } else if let Some(fn_like) = FnLikeNode::from_node(node) {
+        if fn_like.constness() == hir::Constness::Const {
+            return true;
         }
-    }
 
-    /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
-    /// said intrinsic is on the whitelist for being const callable.
-    fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
-        let hir_id = tcx
-            .hir()
-            .as_local_hir_id(def_id)
-            .expect("Non-local call to local provider is_const_fn");
+        // If the function itself is not annotated with `const`, it may still be a `const fn`
+        // if it resides in a const trait impl.
+        is_parent_const_impl_raw(tcx, hir_id)
+    } else if let hir::Node::Ctor(_) = node {
+        true
+    } else {
+        false
+    }
+}
 
-        let node = tcx.hir().get(hir_id);
+/// Const evaluability whitelist is here to check evaluability at the
+/// top level beforehand.
+fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
+    if tcx.is_closure(def_id) {
+        return None;
+    }
 
-        if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
-            whitelisted
-        } else if let Some(fn_like) = FnLikeNode::from_node(node) {
-            fn_like.constness() == hir::Constness::Const
-        } else if let hir::Node::Ctor(_) = node {
-            true
-        } else {
-            false
+    match tcx.fn_sig(def_id).abi() {
+        Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
+            Some(tcx.lookup_const_stability(def_id).is_some())
         }
+        _ => None,
     }
+}
 
-    fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
-        is_const_fn(tcx, def_id)
-            && match tcx.lookup_const_stability(def_id) {
-                Some(stab) => {
-                    if cfg!(debug_assertions) && stab.promotable {
-                        let sig = tcx.fn_sig(def_id);
-                        assert_eq!(
-                            sig.unsafety(),
-                            hir::Unsafety::Normal,
-                            "don't mark const unsafe fns as promotable",
-                            // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
-                        );
-                    }
-                    stab.promotable
+/// Checks whether the given item is an `impl` that has a `const` modifier.
+fn is_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
+    let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+    let node = tcx.hir().get(hir_id);
+    matches!(
+        node,
+        hir::Node::Item(hir::Item {
+            kind: hir::ItemKind::Impl { constness: hir::Constness::Const, .. },
+            ..
+        })
+    )
+}
+
+fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+    is_const_fn(tcx, def_id)
+        && match tcx.lookup_const_stability(def_id) {
+            Some(stab) => {
+                if cfg!(debug_assertions) && stab.promotable {
+                    let sig = tcx.fn_sig(def_id);
+                    assert_eq!(
+                        sig.unsafety(),
+                        hir::Unsafety::Normal,
+                        "don't mark const unsafe fns as promotable",
+                        // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
+                    );
                 }
-                None => false,
+                stab.promotable
             }
-    }
+            None => false,
+        }
+}
 
-    fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
-        is_const_fn(tcx, def_id)
-            && tcx
-                .lookup_const_stability(def_id)
-                .map(|stab| stab.allow_const_fn_ptr)
-                .unwrap_or(false)
-    }
+fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+    is_const_fn(tcx, def_id)
+        && tcx.lookup_const_stability(def_id).map(|stab| stab.allow_const_fn_ptr).unwrap_or(false)
+}
 
+pub fn provide(providers: &mut Providers<'_>) {
     *providers = Providers {
         is_const_fn_raw,
+        is_const_impl_raw: |tcx, def_id| is_const_impl_raw(tcx, LocalDefId::from_def_id(def_id)),
         is_promotable_const_fn,
         const_fn_is_allowed_fn_ptr,
         ..*providers
diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs
index c5a51bfb282..1553f826c7e 100644
--- a/src/librustc_mir/transform/check_consts/validation.rs
+++ b/src/librustc_mir/transform/check_consts/validation.rs
@@ -4,7 +4,7 @@ use rustc::middle::lang_items;
 use rustc::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
 use rustc::mir::*;
 use rustc::ty::cast::CastTy;
-use rustc::ty::{self, TyCtxt};
+use rustc::ty::{self, Instance, InstanceDef, TyCtxt};
 use rustc_errors::struct_span_err;
 use rustc_hir::{def_id::DefId, HirId};
 use rustc_index::bit_set::BitSet;
@@ -502,8 +502,8 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
             TerminatorKind::Call { func, .. } => {
                 let fn_ty = func.ty(*self.body, self.tcx);
 
-                let def_id = match fn_ty.kind {
-                    ty::FnDef(def_id, _) => def_id,
+                let (def_id, substs) = match fn_ty.kind {
+                    ty::FnDef(def_id, substs) => (def_id, substs),
 
                     ty::FnPtr(_) => {
                         self.check_op(ops::FnCallIndirect);
@@ -520,6 +520,20 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
                     return;
                 }
 
+                // See if this is a trait method for a concrete type whose impl of that trait is
+                // `const`.
+                if self.tcx.features().const_trait_impl {
+                    let instance = Instance::resolve(self.tcx, self.param_env, def_id, substs);
+                    debug!("Resolving ({:?}) -> {:?}", def_id, instance);
+                    if let Some(func) = instance {
+                        if let InstanceDef::Item(def_id) = func.def {
+                            if is_const_fn(self.tcx, def_id) {
+                                return;
+                            }
+                        }
+                    }
+                }
+
                 if is_lang_panic_fn(self.tcx, def_id) {
                     self.check_op(ops::Panic);
                 } else if let Some(feature) = is_unstable_const_fn(self.tcx, def_id) {
diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs
index b12f4ce3269..5a99ee27301 100644
--- a/src/librustc_mir/transform/qualify_min_const_fn.rs
+++ b/src/librustc_mir/transform/qualify_min_const_fn.rs
@@ -10,6 +10,14 @@ use std::borrow::Cow;
 type McfResult = Result<(), (Span, Cow<'static, str>)>;
 
 pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -> McfResult {
+    // Prevent const trait methods from being annotated as `stable`.
+    if tcx.features().staged_api {
+        let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
+        if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) {
+            return Err((body.span, "trait methods cannot be stable const fn".into()));
+        }
+    }
+
     let mut current = def_id;
     loop {
         let predicates = tcx.predicates_of(current);
diff --git a/src/librustc_passes/stability.rs b/src/librustc_passes/stability.rs
index 99f005c29e8..d9296deb1d9 100644
--- a/src/librustc_passes/stability.rs
+++ b/src/librustc_passes/stability.rs
@@ -9,7 +9,7 @@ use rustc::session::parse::feature_err;
 use rustc::session::Session;
 use rustc::ty::query::Providers;
 use rustc::ty::TyCtxt;
-use rustc_attr::{self as attr, Stability};
+use rustc_attr::{self as attr, ConstStability, Stability};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
@@ -41,6 +41,7 @@ struct Annotator<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     index: &'a mut Index<'tcx>,
     parent_stab: Option<&'tcx Stability>,
+    parent_const_stab: Option<&'tcx ConstStability>,
     parent_depr: Option<DeprecationEntry>,
     in_trait_impl: bool,
 }
@@ -58,144 +59,197 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
     ) where
         F: FnOnce(&mut Self),
     {
-        if self.tcx.features().staged_api {
-            // This crate explicitly wants staged API.
-            debug!("annotate(id = {:?}, attrs = {:?})", hir_id, attrs);
-            if let Some(..) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) {
-                self.tcx.sess.span_err(
-                    item_sp,
-                    "`#[deprecated]` cannot be used in staged API; \
-                                                 use `#[rustc_deprecated]` instead",
-                );
+        if !self.tcx.features().staged_api {
+            self.forbid_staged_api_attrs(hir_id, attrs, item_sp, kind, visit_children);
+            return;
+        }
+
+        // This crate explicitly wants staged API.
+
+        debug!("annotate(id = {:?}, attrs = {:?})", hir_id, attrs);
+        if let Some(..) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) {
+            self.tcx.sess.span_err(
+                item_sp,
+                "`#[deprecated]` cannot be used in staged API; \
+                                             use `#[rustc_deprecated]` instead",
+            );
+        }
+
+        let (stab, const_stab) = attr::find_stability(&self.tcx.sess.parse_sess, attrs, item_sp);
+
+        let const_stab = const_stab.map(|const_stab| {
+            let const_stab = self.tcx.intern_const_stability(const_stab);
+            self.index.const_stab_map.insert(hir_id, const_stab);
+            const_stab
+        });
+
+        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.level.is_unstable() {
+                    self.index.const_stab_map.insert(hir_id, parent);
+                }
             }
-            let (stab, const_stab) =
-                attr::find_stability(&self.tcx.sess.parse_sess, attrs, item_sp);
-            if let Some(const_stab) = const_stab {
-                let const_stab = self.tcx.intern_const_stability(const_stab);
-                self.index.const_stab_map.insert(hir_id, const_stab);
+        }
+
+        let stab = stab.map(|mut stab| {
+            // Error if prohibited, or can't inherit anything from a container.
+            if kind == AnnotationKind::Prohibited
+                || (kind == AnnotationKind::Container
+                    && stab.level.is_stable()
+                    && stab.rustc_depr.is_none())
+            {
+                self.tcx.sess.span_err(item_sp, "This stability annotation is useless");
             }
-            if let Some(mut stab) = stab {
-                // Error if prohibited, or can't inherit anything from a container.
-                if kind == AnnotationKind::Prohibited
-                    || (kind == AnnotationKind::Container
-                        && stab.level.is_stable()
-                        && stab.rustc_depr.is_none())
-                {
-                    self.tcx.sess.span_err(item_sp, "This stability annotation is useless");
-                }
 
-                debug!("annotate: found {:?}", stab);
-                // If parent is deprecated and we're not, inherit this by merging
-                // deprecated_since and its reason.
-                if let Some(parent_stab) = self.parent_stab {
-                    if parent_stab.rustc_depr.is_some() && stab.rustc_depr.is_none() {
-                        stab.rustc_depr = parent_stab.rustc_depr
-                    }
+            debug!("annotate: found {:?}", stab);
+            // If parent is deprecated and we're not, inherit this by merging
+            // deprecated_since and its reason.
+            if let Some(parent_stab) = self.parent_stab {
+                if parent_stab.rustc_depr.is_some() && stab.rustc_depr.is_none() {
+                    stab.rustc_depr = parent_stab.rustc_depr
                 }
+            }
 
-                let stab = self.tcx.intern_stability(stab);
-
-                // Check if deprecated_since < stable_since. If it is,
-                // this is *almost surely* an accident.
-                if let (
-                    &Some(attr::RustcDeprecation { since: dep_since, .. }),
-                    &attr::Stable { since: stab_since },
-                ) = (&stab.rustc_depr, &stab.level)
+            let stab = self.tcx.intern_stability(stab);
+
+            // Check if deprecated_since < stable_since. If it is,
+            // this is *almost surely* an accident.
+            if let (
+                &Some(attr::RustcDeprecation { since: dep_since, .. }),
+                &attr::Stable { since: stab_since },
+            ) = (&stab.rustc_depr, &stab.level)
+            {
+                // Explicit version of iter::order::lt to handle parse errors properly
+                for (dep_v, stab_v) in
+                    dep_since.as_str().split('.').zip(stab_since.as_str().split('.'))
                 {
-                    // Explicit version of iter::order::lt to handle parse errors properly
-                    for (dep_v, stab_v) in
-                        dep_since.as_str().split('.').zip(stab_since.as_str().split('.'))
-                    {
-                        if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) {
-                            match dep_v.cmp(&stab_v) {
-                                Ordering::Less => {
-                                    self.tcx.sess.span_err(
-                                        item_sp,
-                                        "An API can't be stabilized \
-                                                                     after it is deprecated",
-                                    );
-                                    break;
-                                }
-                                Ordering::Equal => continue,
-                                Ordering::Greater => break,
+                    if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) {
+                        match dep_v.cmp(&stab_v) {
+                            Ordering::Less => {
+                                self.tcx.sess.span_err(
+                                    item_sp,
+                                    "An API can't be stabilized \
+                                                                 after it is deprecated",
+                                );
+                                break;
                             }
-                        } else {
-                            // Act like it isn't less because the question is now nonsensical,
-                            // and this makes us not do anything else interesting.
-                            self.tcx.sess.span_err(
-                                item_sp,
-                                "Invalid stability or deprecation \
-                                                             version found",
-                            );
-                            break;
+                            Ordering::Equal => continue,
+                            Ordering::Greater => break,
                         }
+                    } else {
+                        // Act like it isn't less because the question is now nonsensical,
+                        // and this makes us not do anything else interesting.
+                        self.tcx.sess.span_err(
+                            item_sp,
+                            "Invalid stability or deprecation \
+                                                         version found",
+                        );
+                        break;
                     }
                 }
-
-                self.index.stab_map.insert(hir_id, stab);
-
-                let orig_parent_stab = replace(&mut self.parent_stab, Some(stab));
-                visit_children(self);
-                self.parent_stab = orig_parent_stab;
-            } else {
-                debug!("annotate: not found, parent = {:?}", self.parent_stab);
-                if let Some(stab) = self.parent_stab {
-                    if stab.level.is_unstable() {
-                        self.index.stab_map.insert(hir_id, stab);
-                    }
-                }
-                visit_children(self);
-            }
-        } else {
-            // Emit errors for non-staged-api crates.
-            let unstable_attrs = [
-                sym::unstable,
-                sym::stable,
-                sym::rustc_deprecated,
-                sym::rustc_const_unstable,
-                sym::rustc_const_stable,
-            ];
-            for attr in attrs {
-                let name = attr.name_or_empty();
-                if unstable_attrs.contains(&name) {
-                    attr::mark_used(attr);
-                    struct_span_err!(
-                        self.tcx.sess,
-                        attr.span,
-                        E0734,
-                        "stability attributes may not be used outside of the standard library",
-                    )
-                    .emit();
-                }
             }
 
-            // Propagate unstability.  This can happen even for non-staged-api crates in case
-            // -Zforce-unstable-if-unmarked is set.
+            self.index.stab_map.insert(hir_id, stab);
+            stab
+        });
+
+        if stab.is_none() {
+            debug!("annotate: stab not found, parent = {:?}", self.parent_stab);
             if let Some(stab) = self.parent_stab {
                 if stab.level.is_unstable() {
                     self.index.stab_map.insert(hir_id, stab);
                 }
             }
+        }
 
-            if let Some(depr) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) {
-                if kind == AnnotationKind::Prohibited {
-                    self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless");
-                }
+        self.recurse_with_stability_attrs(stab, const_stab, visit_children);
+    }
+
+    fn recurse_with_stability_attrs(
+        &mut self,
+        stab: Option<&'tcx Stability>,
+        const_stab: Option<&'tcx ConstStability>,
+        f: impl FnOnce(&mut Self),
+    ) {
+        // These will be `Some` if this item changes the corresponding stability attribute.
+        let mut replaced_parent_stab = None;
+        let mut replaced_parent_const_stab = None;
+
+        if let Some(stab) = stab {
+            replaced_parent_stab = Some(replace(&mut self.parent_stab, Some(stab)));
+        }
+        if let Some(const_stab) = const_stab {
+            replaced_parent_const_stab =
+                Some(replace(&mut self.parent_const_stab, Some(const_stab)));
+        }
 
-                // `Deprecation` is just two pointers, no need to intern it
-                let depr_entry = DeprecationEntry::local(depr, hir_id);
-                self.index.depr_map.insert(hir_id, depr_entry.clone());
-
-                let orig_parent_depr = replace(&mut self.parent_depr, Some(depr_entry));
-                visit_children(self);
-                self.parent_depr = orig_parent_depr;
-            } else if let Some(parent_depr) = self.parent_depr.clone() {
-                self.index.depr_map.insert(hir_id, parent_depr);
-                visit_children(self);
-            } else {
-                visit_children(self);
+        f(self);
+
+        if let Some(orig_parent_stab) = replaced_parent_stab {
+            self.parent_stab = orig_parent_stab;
+        }
+        if let Some(orig_parent_const_stab) = replaced_parent_const_stab {
+            self.parent_const_stab = orig_parent_const_stab;
+        }
+    }
+
+    fn forbid_staged_api_attrs(
+        &mut self,
+        hir_id: HirId,
+        attrs: &[Attribute],
+        item_sp: Span,
+        kind: AnnotationKind,
+        visit_children: impl FnOnce(&mut Self),
+    ) {
+        // Emit errors for non-staged-api crates.
+        let unstable_attrs = [
+            sym::unstable,
+            sym::stable,
+            sym::rustc_deprecated,
+            sym::rustc_const_unstable,
+            sym::rustc_const_stable,
+        ];
+        for attr in attrs {
+            let name = attr.name_or_empty();
+            if unstable_attrs.contains(&name) {
+                attr::mark_used(attr);
+                struct_span_err!(
+                    self.tcx.sess,
+                    attr.span,
+                    E0734,
+                    "stability attributes may not be used outside of the standard library",
+                )
+                .emit();
+            }
+        }
+
+        // Propagate unstability.  This can happen even for non-staged-api crates in case
+        // -Zforce-unstable-if-unmarked is set.
+        if let Some(stab) = self.parent_stab {
+            if stab.level.is_unstable() {
+                self.index.stab_map.insert(hir_id, stab);
             }
         }
+
+        if let Some(depr) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) {
+            if kind == AnnotationKind::Prohibited {
+                self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless");
+            }
+
+            // `Deprecation` is just two pointers, no need to intern it
+            let depr_entry = DeprecationEntry::local(depr, hir_id);
+            self.index.depr_map.insert(hir_id, depr_entry.clone());
+
+            let orig_parent_depr = replace(&mut self.parent_depr, Some(depr_entry));
+            visit_children(self);
+            self.parent_depr = orig_parent_depr;
+        } else if let Some(parent_depr) = self.parent_depr.clone() {
+            self.index.depr_map.insert(hir_id, parent_depr);
+            visit_children(self);
+        } else {
+            visit_children(self);
+        }
     }
 }
 
@@ -376,6 +430,7 @@ fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> {
             tcx,
             index: &mut index,
             parent_stab: None,
+            parent_const_stab: None,
             parent_depr: None,
             in_trait_impl: false,
         };
diff --git a/src/test/ui/rfc-2632-const-trait-impl/assoc-type.rs b/src/test/ui/rfc-2632-const-trait-impl/assoc-type.rs
new file mode 100644
index 00000000000..194929fa287
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/assoc-type.rs
@@ -0,0 +1,28 @@
+// ignore-test
+
+// FIXME: This test should fail since, within a const impl of `Foo`, the bound on `Foo::Bar` should
+// require a const impl of `Add` for the associated type.
+
+#![allow(incomplete_features)]
+#![feature(const_trait_impl)]
+#![feature(const_fn)]
+
+struct NonConstAdd(i32);
+
+impl std::ops::Add for NonConstAdd {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        NonConstAdd(self.0 + rhs.0)
+    }
+}
+
+trait Foo {
+    type Bar: std::ops::Add;
+}
+
+impl const Foo for NonConstAdd {
+    type Bar = NonConstAdd;
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs
new file mode 100644
index 00000000000..8e6ef12810c
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs
@@ -0,0 +1,30 @@
+#![allow(incomplete_features)]
+#![feature(const_trait_impl)]
+#![feature(const_fn)]
+
+pub trait Plus {
+    fn plus(self, rhs: Self) -> Self;
+}
+
+impl const Plus for i32 {
+    fn plus(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Plus for u32 {
+    fn plus(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+pub const fn add_i32(a: i32, b: i32) -> i32 {
+    a.plus(b) // ok
+}
+
+pub const fn add_u32(a: u32, b: u32) -> u32 {
+    a.plus(b)
+    //~^ ERROR calls in constant functions are limited to constant functions
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr
new file mode 100644
index 00000000000..0c320d54c76
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr
@@ -0,0 +1,9 @@
+error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+  --> $DIR/call-const-trait-method-fail.rs:26:5
+   |
+LL |     a.plus(b)
+   |     ^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0015`.
diff --git a/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs
new file mode 100644
index 00000000000..6a2112ea554
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs
@@ -0,0 +1,41 @@
+// run-pass
+
+#![allow(incomplete_features)]
+#![feature(const_trait_impl)]
+#![feature(const_fn)]
+
+struct Int(i32);
+
+impl const std::ops::Add for Int {
+    type Output = Int;
+
+    fn add(self, rhs: Self) -> Self {
+        Int(self.0.plus(rhs.0))
+    }
+}
+
+impl const PartialEq for Int {
+    fn eq(&self, rhs: &Self) -> bool {
+        self.0 == rhs.0
+    }
+}
+
+pub trait Plus {
+    fn plus(self, rhs: Self) -> Self;
+}
+
+impl const Plus for i32 {
+    fn plus(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+pub const fn add_i32(a: i32, b: i32) -> i32 {
+    a.plus(b)
+}
+
+const ADD_INT: Int = Int(1i32) + Int(2i32);
+
+fn main() {
+    assert!(ADD_INT == Int(3i32));
+}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs
new file mode 100644
index 00000000000..e148ad9a0ee
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs
@@ -0,0 +1,33 @@
+#![allow(incomplete_features)]
+#![feature(const_trait_impl)]
+
+pub struct Int(i32);
+
+impl const std::ops::Add for i32 {
+    //~^ ERROR conflicting implementations of trait
+    //~| ERROR only traits defined in the current crate can be implemented for arbitrary types
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl std::ops::Add for Int {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        Int(self.0 + rhs.0)
+    }
+}
+
+impl const std::ops::Add for Int {
+    //~^ ERROR conflicting implementations of trait
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        Int(self.0 + rhs.0)
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr
new file mode 100644
index 00000000000..b57472d9595
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr
@@ -0,0 +1,34 @@
+error[E0119]: conflicting implementations of trait `std::ops::Add` for type `i32`:
+  --> $DIR/const-and-non-const-impl.rs:6:1
+   |
+LL | impl const std::ops::Add for i32 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: conflicting implementation in crate `core`:
+           - impl std::ops::Add for i32;
+
+error[E0119]: conflicting implementations of trait `std::ops::Add` for type `Int`:
+  --> $DIR/const-and-non-const-impl.rs:24:1
+   |
+LL | impl std::ops::Add for Int {
+   | -------------------------- first implementation here
+...
+LL | impl const std::ops::Add for Int {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Int`
+
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+  --> $DIR/const-and-non-const-impl.rs:6:1
+   |
+LL | impl const std::ops::Add for i32 {
+   | ^^^^^^^^^^^-------------^^^^^---
+   | |          |                 |
+   | |          |                 `i32` is not defined in the current crate
+   | |          `i32` is not defined in the current crate
+   | impl doesn't use only types from inside the current crate
+   |
+   = note: define and implement a trait or new type instead
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0117, E0119.
+For more information about an error, try `rustc --explain E0117`.
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs b/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs
new file mode 100644
index 00000000000..3278f35bad2
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs
@@ -0,0 +1,16 @@
+#![allow(incomplete_features)]
+#![feature(const_trait_impl)]
+
+struct S;
+trait T {
+    fn foo();
+}
+
+fn non_const() {}
+
+impl const T for S {
+    fn foo() { non_const() }
+    //~^ ERROR can only call other `const fn`
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr
new file mode 100644
index 00000000000..7fe3a9fd852
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr
@@ -0,0 +1,12 @@
+error[E0723]: can only call other `const fn` within a `const fn`, but `const non_const` is not stable as `const fn`
+  --> $DIR/const-check-fns-in-const-impl.rs:12:16
+   |
+LL |     fn foo() { non_const() }
+   |                ^^^^^^^^^^^
+   |
+   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0723`.
diff --git a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr
index 061af3c94b4..d1ab99e33e9 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr
@@ -1,10 +1,8 @@
-error: const trait impls are not yet implemented
-  --> $DIR/feature-gate.rs:9:1
+error: fatal error triggered by #[rustc_error]
+  --> $DIR/feature-gate.rs:14:1
    |
-LL | impl const T for S {}
-   | ^^^^^-----^^^^^^^^^^^
-   |      |
-   |      const because of this
+LL | fn main() {}
+   | ^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs
index 49b6c0926c5..d9772431941 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs
@@ -3,11 +3,12 @@
 
 #![cfg_attr(gated, feature(const_trait_impl))]
 #![allow(incomplete_features)]
+#![feature(rustc_attrs)]
 
 struct S;
 trait T {}
 impl const T for S {}
 //[stock]~^ ERROR const trait impls are experimental
-//[stock,gated]~^^ ERROR const trait impls are not yet implemented
 
-fn main() {}
+#[rustc_error]
+fn main() {} //[gated]~ ERROR fatal error triggered by #[rustc_error]
diff --git a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr
index cfe226ea7a7..724090e49cd 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr
@@ -1,5 +1,5 @@
 error[E0658]: const trait impls are experimental
-  --> $DIR/feature-gate.rs:9:6
+  --> $DIR/feature-gate.rs:10:6
    |
 LL | impl const T for S {}
    |      ^^^^^
@@ -7,14 +7,6 @@ LL | impl const T for S {}
    = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
    = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
 
-error: const trait impls are not yet implemented
-  --> $DIR/feature-gate.rs:9:1
-   |
-LL | impl const T for S {}
-   | ^^^^^-----^^^^^^^^^^^
-   |      |
-   |      const because of this
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/rfc-2632-const-trait-impl/generic-bound.rs b/src/test/ui/rfc-2632-const-trait-impl/generic-bound.rs
new file mode 100644
index 00000000000..7829ffe2a38
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/generic-bound.rs
@@ -0,0 +1,32 @@
+// run-pass
+
+#![allow(incomplete_features)]
+#![feature(const_trait_impl)]
+#![feature(const_fn)]
+
+use std::marker::PhantomData;
+
+struct S<T>(PhantomData<T>);
+
+impl<T> Copy for S<T> {}
+impl<T> Clone for S<T> {
+    fn clone(&self) -> Self {
+        S(PhantomData)
+    }
+}
+
+impl<T> const std::ops::Add for S<T> {
+    type Output = Self;
+
+    fn add(self, _: Self) -> Self {
+        S(std::marker::PhantomData)
+    }
+}
+
+const fn twice<T: std::ops::Add>(arg: S<T>) -> S<T> {
+    arg + arg
+}
+
+fn main() {
+    let _ = twice(S(PhantomData::<i32>));
+}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs
index 7f064c0c53a..04123a532bd 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs
@@ -8,10 +8,8 @@ trait T {}
 
 impl const S {}
 //~^ ERROR inherent impls cannot be `const`
-//~| ERROR const trait impls are not yet implemented
 
 impl const T {}
 //~^ ERROR inherent impls cannot be `const`
-//~| ERROR const trait impls are not yet implemented
 
 fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr
index bdc95ff2a57..3ea58a3728a 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr
@@ -9,7 +9,7 @@ LL | impl const S {}
    = note: only trait implementations may be annotated with `const`
 
 error: inherent impls cannot be `const`
-  --> $DIR/inherent-impl.rs:13:1
+  --> $DIR/inherent-impl.rs:12:1
    |
 LL | impl const T {}
    | ^^^^^-----^^^^^
@@ -18,21 +18,5 @@ LL | impl const T {}
    |
    = note: only trait implementations may be annotated with `const`
 
-error: const trait impls are not yet implemented
-  --> $DIR/inherent-impl.rs:9:1
-   |
-LL | impl const S {}
-   | ^^^^^-----^^^^^
-   |      |
-   |      const because of this
-
-error: const trait impls are not yet implemented
-  --> $DIR/inherent-impl.rs:13:1
-   |
-LL | impl const T {}
-   | ^^^^^-----^^^^^
-   |      |
-   |      const because of this
-
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/rfc-2632-const-trait-impl/stability.rs b/src/test/ui/rfc-2632-const-trait-impl/stability.rs
new file mode 100644
index 00000000000..03a6fb51503
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/stability.rs
@@ -0,0 +1,43 @@
+#![allow(incomplete_features)]
+#![feature(allow_internal_unstable)]
+#![feature(const_add)]
+#![feature(const_trait_impl)]
+#![feature(staged_api)]
+
+pub struct Int(i32);
+
+#[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_stable(feature = "rust1", since = "1.0.0")]
+impl const std::ops::Sub for Int {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        //~^ ERROR trait methods cannot be stable const fn
+        Int(self.0 - rhs.0)
+    }
+}
+
+#[rustc_const_unstable(feature = "const_add", issue = "none")]
+impl const std::ops::Add for Int {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        Int(self.0 + rhs.0)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_stable(feature = "rust1", since = "1.0.0")]
+pub const fn foo() -> Int {
+    Int(1i32) + Int(2i32)
+    //~^ ERROR can only call other `const fn` within a `const fn`
+}
+
+// ok
+#[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "bar", issue = "none")]
+pub const fn bar() -> Int {
+    Int(1i32) + Int(2i32)
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/stability.stderr b/src/test/ui/rfc-2632-const-trait-impl/stability.stderr
new file mode 100644
index 00000000000..1ecff62955b
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/stability.stderr
@@ -0,0 +1,24 @@
+error[E0723]: trait methods cannot be stable const fn
+  --> $DIR/stability.rs:14:5
+   |
+LL | /     fn sub(self, rhs: Self) -> Self {
+LL | |
+LL | |         Int(self.0 - rhs.0)
+LL | |     }
+   | |_____^
+   |
+   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+
+error[E0723]: can only call other `const fn` within a `const fn`, but `const <Int as std::ops::Add>::add` is not stable as `const fn`
+  --> $DIR/stability.rs:32:5
+   |
+LL |     Int(1i32) + Int(2i32)
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0723`.