diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 7112c26757717..4ef43735a62c8 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -1112,24 +1112,6 @@ pub struct Expr {
 }
 
 impl Expr {
-    /// Returns `true` if this expression would be valid somewhere that expects a value;
-    /// for example, an `if` condition.
-    pub fn returns(&self) -> bool {
-        if let ExprKind::Block(ref block, _) = self.kind {
-            match block.stmts.last().map(|last_stmt| &last_stmt.kind) {
-                // Implicit return
-                Some(StmtKind::Expr(_)) => true,
-                // Last statement is an explicit return?
-                Some(StmtKind::Semi(expr)) => matches!(expr.kind, ExprKind::Ret(_)),
-                // This is a block that doesn't end in either an implicit or explicit return.
-                _ => false,
-            }
-        } else {
-            // This is not a block, it is a value.
-            true
-        }
-    }
-
     /// Is this expr either `N`, or `{ N }`.
     ///
     /// If this is not the case, name resolution does not resolve `N` when using
diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
index 357c6900a70e5..7ac44312695d9 100644
--- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
@@ -137,3 +137,12 @@ hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(l
 hir_analysis_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}`
 
 hir_analysis_add_missing_parentheses_in_range = you must surround the range in parentheses to call its `{$func_name}` function
+
+hir_analysis_const_impl_for_non_const_trait =
+    const `impl` for trait `{$trait_name}` which is not marked with `#[const_trait]`
+    .suggestion = mark `{$trait_name}` as const
+    .note = marking a trait with `#[const_trait]` ensures all default method bodies are `const`
+    .adding = adding a non-const method body in the future would be a breaking change
+
+hir_analysis_const_bound_for_non_const_trait =
+    ~const can only be applied to `#[const_trait]` traits
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index a0350c26d827c..6baf98449775d 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -36,7 +36,7 @@ use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECT
 use rustc_span::edition::Edition;
 use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, Ident, Symbol};
-use rustc_span::Span;
+use rustc_span::{sym, Span};
 use rustc_target::spec::abi;
 use rustc_trait_selection::traits;
 use rustc_trait_selection::traits::astconv_object_safety_violations;
@@ -275,6 +275,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             item_segment.args(),
             item_segment.infer_args,
             None,
+            None,
         );
         if let Some(b) = item_segment.args().bindings.first() {
             Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
@@ -324,6 +325,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         generic_args: &'a hir::GenericArgs<'_>,
         infer_args: bool,
         self_ty: Option<Ty<'tcx>>,
+        constness: Option<ty::BoundConstness>,
     ) -> (SubstsRef<'tcx>, GenericArgCountResult) {
         // If the type is parameterized by this region, then replace this
         // region with the current anon region binding (in other words,
@@ -534,6 +536,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             &mut substs_ctx,
         );
 
+        if let Some(ty::BoundConstness::ConstIfConst) = constness
+            && generics.has_self && !tcx.has_attr(def_id, sym::const_trait)
+        {
+            tcx.sess.emit_err(crate::errors::ConstBoundForNonConstTrait { span } );
+        }
+
         (substs, arg_count)
     }
 
@@ -601,6 +609,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             item_segment.args(),
             item_segment.infer_args,
             None,
+            None,
         );
 
         if let Some(b) = item_segment.args().bindings.first() {
@@ -620,6 +629,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         &self,
         trait_ref: &hir::TraitRef<'_>,
         self_ty: Ty<'tcx>,
+        constness: ty::BoundConstness,
     ) -> ty::TraitRef<'tcx> {
         self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
 
@@ -629,6 +639,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             self_ty,
             trait_ref.path.segments.last().unwrap(),
             true,
+            Some(constness),
         )
     }
 
@@ -655,6 +666,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             args,
             infer_args,
             Some(self_ty),
+            Some(constness),
         );
 
         let tcx = self.tcx();
@@ -680,6 +692,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 speculative,
                 &mut dup_bindings,
                 binding_span.unwrap_or(binding.span),
+                constness,
             );
             // Okay to ignore `Err` because of `ErrorGuaranteed` (see above).
         }
@@ -783,6 +796,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         self_ty: Ty<'tcx>,
         trait_segment: &hir::PathSegment<'_>,
         is_impl: bool,
+        constness: Option<ty::BoundConstness>,
     ) -> ty::TraitRef<'tcx> {
         let (substs, _) = self.create_substs_for_ast_trait_ref(
             span,
@@ -790,6 +804,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             self_ty,
             trait_segment,
             is_impl,
+            constness,
         );
         if let Some(b) = trait_segment.args().bindings.first() {
             Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
@@ -805,6 +820,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         self_ty: Ty<'tcx>,
         trait_segment: &'a hir::PathSegment<'a>,
         is_impl: bool,
+        constness: Option<ty::BoundConstness>,
     ) -> (SubstsRef<'tcx>, GenericArgCountResult) {
         self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);
 
@@ -816,6 +832,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             trait_segment.args(),
             trait_segment.infer_args,
             Some(self_ty),
+            constness,
         )
     }
 
@@ -1027,6 +1044,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         speculative: bool,
         dup_bindings: &mut FxHashMap<DefId, Span>,
         path_span: Span,
+        constness: ty::BoundConstness,
     ) -> Result<(), ErrorGuaranteed> {
         // Given something like `U: SomeTrait<T = X>`, we want to produce a
         // predicate like `<U as SomeTrait>::T = X`. This is somewhat
@@ -1122,10 +1140,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 trait_ref.substs,
             );
 
-            debug!(
-                "add_predicates_for_ast_type_binding: substs for trait-ref and assoc_item: {:?}",
-                substs_trait_ref_and_assoc_item
-            );
+            debug!(?substs_trait_ref_and_assoc_item);
 
             ty::ProjectionTy {
                 item_def_id: assoc_item.def_id,
@@ -1146,8 +1161,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     tcx.collect_constrained_late_bound_regions(&projection_ty);
                 let late_bound_in_ty =
                     tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty));
-                debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref);
-                debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
+                debug!(?late_bound_in_trait_ref);
+                debug!(?late_bound_in_ty);
 
                 // FIXME: point at the type params that don't have appropriate lifetimes:
                 // struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
@@ -1648,6 +1663,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
     // Checks that `bounds` contains exactly one element and reports appropriate
     // errors otherwise.
+    #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, is_equality), ret)]
     fn one_bound_for_assoc_type<I>(
         &self,
         all_candidates: impl Fn() -> I,
@@ -1677,10 +1693,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 return Err(reported);
             }
         };
-        debug!("one_bound_for_assoc_type: bound = {:?}", bound);
+        debug!(?bound);
 
         if let Some(bound2) = next_cand {
-            debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2);
+            debug!(?bound2);
 
             let is_equality = is_equality();
             let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates);
@@ -1776,6 +1792,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
     // parameter or `Self`.
     // NOTE: When this function starts resolving `Trait::AssocTy` successfully
     // it should also start reporting the `BARE_TRAIT_OBJECTS` lint.
+    #[instrument(level = "debug", skip(self, hir_ref_id, span, qself, assoc_segment), fields(assoc_ident=?assoc_segment.ident), ret)]
     pub fn associated_path_to_ty(
         &self,
         hir_ref_id: hir::HirId,
@@ -1793,8 +1810,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             Res::Err
         };
 
-        debug!("associated_path_to_ty: {:?}::{}", qself_ty, assoc_ident);
-
         // Check if we have an enum variant.
         let mut variant_resolution = None;
         if let ty::Adt(adt_def, _) = qself_ty.kind() {
@@ -2050,6 +2065,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         item_def_id: DefId,
         trait_segment: &hir::PathSegment<'_>,
         item_segment: &hir::PathSegment<'_>,
+        constness: ty::BoundConstness,
     ) -> Ty<'tcx> {
         let tcx = self.tcx();
 
@@ -2094,8 +2110,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
         debug!("qpath_to_ty: self_type={:?}", self_ty);
 
-        let trait_ref =
-            self.ast_path_to_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false);
+        let trait_ref = self.ast_path_to_mono_trait_ref(
+            span,
+            trait_def_id,
+            self_ty,
+            trait_segment,
+            false,
+            Some(constness),
+        );
 
         let item_substs = self.create_substs_for_associated_item(
             span,
@@ -2534,12 +2556,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             Res::Def(DefKind::AssocTy, def_id) => {
                 debug_assert!(path.segments.len() >= 2);
                 self.prohibit_generics(path.segments[..path.segments.len() - 2].iter(), |_| {});
+                // HACK: until we support `<Type as ~const Trait>`, assume all of them are.
+                let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) {
+                    ty::BoundConstness::ConstIfConst
+                } else {
+                    ty::BoundConstness::NotConst
+                };
                 self.qpath_to_ty(
                     span,
                     opt_self_ty,
                     def_id,
                     &path.segments[path.segments.len() - 2],
                     path.segments.last().unwrap(),
+                    constness,
                 )
             }
             Res::PrimTy(prim_ty) => {
@@ -2658,6 +2687,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     &GenericArgs::none(),
                     true,
                     None,
+                    None,
                 );
                 EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id)))
                     .subst(tcx, substs)
@@ -2766,6 +2796,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         }
     }
 
+    #[instrument(level = "debug", skip(self, hir_id, unsafety, abi, decl, generics, hir_ty), ret)]
     pub fn ty_of_fn(
         &self,
         hir_id: hir::HirId,
@@ -2775,8 +2806,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         generics: Option<&hir::Generics<'_>>,
         hir_ty: Option<&hir::Ty<'_>>,
     ) -> ty::PolyFnSig<'tcx> {
-        debug!("ty_of_fn");
-
         let tcx = self.tcx();
         let bound_vars = tcx.late_bound_vars(hir_id);
         debug!(?bound_vars);
@@ -2826,7 +2855,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(),
         };
 
-        debug!("ty_of_fn: output_ty={:?}", output_ty);
+        debug!(?output_ty);
 
         let fn_ty = tcx.mk_fn_sig(input_tys.into_iter(), output_ty, decl.c_variadic, unsafety, abi);
         let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
@@ -2903,8 +2932,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) =
                 hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") };
 
-        let trait_ref =
-            self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty));
+        let trait_ref = self.instantiate_mono_trait_ref(
+            i.of_trait.as_ref()?,
+            self.ast_ty_to_ty(i.self_ty),
+            ty::BoundConstness::NotConst,
+        );
 
         let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind(
             tcx,
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 33ed3b96aa81f..70a171c02b26b 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -713,6 +713,10 @@ fn resolve_regions_with_wf_tys<'tcx>(
 
     add_constraints(&infcx, region_bound_pairs);
 
+    infcx.process_registered_region_obligations(
+        outlives_environment.region_bound_pairs(),
+        param_env,
+    );
     let errors = infcx.resolve_regions(&outlives_environment);
 
     debug!(?errors, "errors");
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 66ca7d7aa08f9..e261bb07f9545 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -1143,7 +1143,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
         }
 
         ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
-            // Do not try to inference the return type for a impl method coming from a trait
+            // Do not try to infer the return type for a impl method coming from a trait
             if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) =
                 tcx.hir().get(tcx.hir().get_parent_node(hir_id))
                 && i.of_trait.is_some()
@@ -1286,15 +1286,46 @@ fn infer_return_ty_for_fn_sig<'tcx>(
 
 fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
     let icx = ItemCtxt::new(tcx, def_id);
-    match tcx.hir().expect_item(def_id.expect_local()).kind {
+    let item = tcx.hir().expect_item(def_id.expect_local());
+    match item.kind {
         hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| {
             let selfty = tcx.type_of(def_id);
-            <dyn AstConv<'_>>::instantiate_mono_trait_ref(&icx, ast_trait_ref, selfty)
+            <dyn AstConv<'_>>::instantiate_mono_trait_ref(
+                &icx,
+                ast_trait_ref,
+                selfty,
+                check_impl_constness(tcx, impl_.constness, ast_trait_ref),
+            )
         }),
         _ => bug!(),
     }
 }
 
+fn check_impl_constness(
+    tcx: TyCtxt<'_>,
+    constness: hir::Constness,
+    ast_trait_ref: &hir::TraitRef<'_>,
+) -> ty::BoundConstness {
+    match constness {
+        hir::Constness::Const => {
+            if let Some(trait_def_id) = ast_trait_ref.trait_def_id() && !tcx.has_attr(trait_def_id, sym::const_trait) {
+                let trait_name = tcx.item_name(trait_def_id).to_string();
+                tcx.sess.emit_err(errors::ConstImplForNonConstTrait {
+                    trait_ref_span: ast_trait_ref.path.span,
+                    trait_name,
+                    local_trait_span: trait_def_id.as_local().map(|_| tcx.def_span(trait_def_id).shrink_to_lo()),
+                    marking: (),
+                    adding: (),
+                });
+                ty::BoundConstness::NotConst
+            } else {
+                ty::BoundConstness::ConstIfConst
+            }
+        },
+        hir::Constness::NotConst => ty::BoundConstness::NotConst,
+    }
+}
+
 fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ImplPolarity {
     let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl);
     let item = tcx.hir().expect_item(def_id.expect_local());
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 707fd6c75278d..c7777a946893a 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -249,6 +249,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
     // Now create the real type and const parameters.
     let type_start = own_start - has_self as u32 + params.len() as u32;
     let mut i = 0;
+    let mut next_index = || {
+        let prev = i;
+        i += 1;
+        prev as u32 + type_start
+    };
 
     const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \
     `struct`, `enum`, `type`, or `trait` definitions";
@@ -278,15 +283,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
 
             let kind = ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic };
 
-            let param_def = ty::GenericParamDef {
-                index: type_start + i as u32,
+            Some(ty::GenericParamDef {
+                index: next_index(),
                 name: param.name.ident().name,
                 def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
                 pure_wrt_drop: param.pure_wrt_drop,
                 kind,
-            };
-            i += 1;
-            Some(param_def)
+            })
         }
         GenericParamKind::Const { default, .. } => {
             if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() {
@@ -297,15 +300,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
                 );
             }
 
-            let param_def = ty::GenericParamDef {
-                index: type_start + i as u32,
+            Some(ty::GenericParamDef {
+                index: next_index(),
                 name: param.name.ident().name,
                 def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
                 pure_wrt_drop: param.pure_wrt_drop,
                 kind: ty::GenericParamDefKind::Const { has_default: default.is_some() },
-            };
-            i += 1;
-            Some(param_def)
+            })
         }
     }));
 
@@ -323,8 +324,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
             &["<closure_kind>", "<closure_signature>", "<upvars>"][..]
         };
 
-        params.extend(dummy_args.iter().enumerate().map(|(i, &arg)| ty::GenericParamDef {
-            index: type_start + i as u32,
+        params.extend(dummy_args.iter().map(|&arg| ty::GenericParamDef {
+            index: next_index(),
             name: Symbol::intern(arg),
             def_id,
             pure_wrt_drop: false,
@@ -337,7 +338,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
         let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
         if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node {
             params.push(ty::GenericParamDef {
-                index: type_start,
+                index: next_index(),
                 name: Symbol::intern("<const_ty>"),
                 def_id,
                 pure_wrt_drop: false,
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 9457da32ce65a..bd0c1f5dd1098 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -249,3 +249,24 @@ pub struct ExpectedUsedSymbol {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_const_impl_for_non_const_trait)]
+pub struct ConstImplForNonConstTrait {
+    #[primary_span]
+    pub trait_ref_span: Span,
+    pub trait_name: String,
+    #[suggestion(applicability = "machine-applicable", code = "#[const_trait]")]
+    pub local_trait_span: Option<Span>,
+    #[note]
+    pub marking: (),
+    #[note(adding)]
+    pub adding: (),
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_const_bound_for_non_const_trait)]
+pub struct ConstBoundForNonConstTrait {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 0870833cc35ae..30feabe1a0959 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -303,32 +303,6 @@ impl<'tcx> WfPredicates<'tcx> {
         let obligations = if trait_pred.constness == ty::BoundConstness::NotConst {
             self.nominal_obligations_without_const(trait_ref.def_id, trait_ref.substs)
         } else {
-            if !tcx.has_attr(trait_ref.def_id, rustc_span::sym::const_trait) {
-                if let Some(item) = self.item &&
-                   let hir::ItemKind::Impl(impl_) = item.kind &&
-                   let Some(trait_) = &impl_.of_trait &&
-                   let Some(def_id) = trait_.trait_def_id() &&
-                   def_id == trait_ref.def_id
-                {
-                    let trait_name = tcx.item_name(def_id);
-                    let mut err = tcx.sess.struct_span_err(
-                        self.span,
-                        &format!("const `impl` for trait `{trait_name}` which is not marked with `#[const_trait]`"),
-                    );
-                    if def_id.is_local() {
-                        let sp = tcx.def_span(def_id).shrink_to_lo();
-                        err.span_suggestion(sp, &format!("mark `{trait_name}` as const"), "#[const_trait]", rustc_errors::Applicability::MachineApplicable);
-                    }
-                    err.note("marking a trait with `#[const_trait]` ensures all default method bodies are `const`");
-                    err.note("adding a non-const method body in the future would be a breaking change");
-                    err.emit();
-                } else {
-                    tcx.sess.span_err(
-                        self.span,
-                        "~const can only be applied to `#[const_trait]` traits",
-                    );
-                }
-            }
             self.nominal_obligations(trait_ref.def_id, trait_ref.substs)
         };
 
diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs
index 80036bcc4def7..3412d3730d011 100644
--- a/library/core/src/hint.rs
+++ b/library/core/src/hint.rs
@@ -101,7 +101,7 @@ pub const unsafe fn unreachable_unchecked() -> ! {
     // SAFETY: the safety contract for `intrinsics::unreachable` must
     // be upheld by the caller.
     unsafe {
-        intrinsics::assert_unsafe_precondition!(() => false);
+        intrinsics::assert_unsafe_precondition!("hint::unreachable_unchecked must never be reached", () => false);
         intrinsics::unreachable()
     }
 }
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 29f796fad6d50..1dc79afe83fdb 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -2203,7 +2203,7 @@ extern "rust-intrinsic" {
 /// the occasional mistake, and this check should help them figure things out.
 #[allow_internal_unstable(const_eval_select)] // permit this to be called in stably-const fn
 macro_rules! assert_unsafe_precondition {
-    ($([$($tt:tt)*])?($($i:ident:$ty:ty),*$(,)?) => $e:expr) => {
+    ($name:expr, $([$($tt:tt)*])?($($i:ident:$ty:ty),*$(,)?) => $e:expr) => {
         if cfg!(debug_assertions) {
             // allow non_snake_case to allow capturing const generics
             #[allow(non_snake_case)]
@@ -2211,7 +2211,9 @@ macro_rules! assert_unsafe_precondition {
             fn runtime$(<$($tt)*>)?($($i:$ty),*) {
                 if !$e {
                     // don't unwind to reduce impact on code size
-                    ::core::panicking::panic_str_nounwind("unsafe precondition violated");
+                    ::core::panicking::panic_str_nounwind(
+                        concat!("unsafe precondition(s) violated: ", $name)
+                    );
                 }
             }
             #[allow(non_snake_case)]
@@ -2350,7 +2352,10 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
     // SAFETY: the safety contract for `copy_nonoverlapping` must be
     // upheld by the caller.
     unsafe {
-        assert_unsafe_precondition!([T](src: *const T, dst: *mut T, count: usize) =>
+        assert_unsafe_precondition!(
+            "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
+            and the specified memory ranges do not overlap",
+            [T](src: *const T, dst: *mut T, count: usize) =>
             is_aligned_and_not_null(src)
                 && is_aligned_and_not_null(dst)
                 && is_nonoverlapping(src, dst, count)
@@ -2436,8 +2441,11 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
 
     // SAFETY: the safety contract for `copy` must be upheld by the caller.
     unsafe {
-        assert_unsafe_precondition!([T](src: *const T, dst: *mut T) =>
-            is_aligned_and_not_null(src) && is_aligned_and_not_null(dst));
+        assert_unsafe_precondition!(
+            "ptr::copy requires that both pointer arguments are aligned aligned and non-null",
+            [T](src: *const T, dst: *mut T) =>
+            is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)
+        );
         copy(src, dst, count)
     }
 }
@@ -2505,7 +2513,10 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
 
     // SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
     unsafe {
-        assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
+        assert_unsafe_precondition!(
+            "ptr::write_bytes requires that the destination pointer is aligned and non-null",
+            [T](dst: *mut T) => is_aligned_and_not_null(dst)
+        );
         write_bytes(dst, val, count)
     }
 }
diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs
index da402d66502a6..6b6f3417f8ad5 100644
--- a/library/core/src/num/nonzero.rs
+++ b/library/core/src/num/nonzero.rs
@@ -56,7 +56,10 @@ macro_rules! nonzero_integers {
                 pub const unsafe fn new_unchecked(n: $Int) -> Self {
                     // SAFETY: this is guaranteed to be safe by the caller.
                     unsafe {
-                        core::intrinsics::assert_unsafe_precondition!((n: $Int) => n != 0);
+                        core::intrinsics::assert_unsafe_precondition!(
+                            concat!(stringify!($Ty), "::new_unchecked requires a non-zero argument"),
+                            (n: $Int) => n != 0
+                        );
                         Self(n)
                     }
                 }
diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs
index 41ffe11f610dd..3e06776d2c6fa 100644
--- a/library/core/src/ops/index_range.rs
+++ b/library/core/src/ops/index_range.rs
@@ -19,7 +19,12 @@ impl IndexRange {
     #[inline]
     pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self {
         // SAFETY: comparisons on usize are pure
-        unsafe { assert_unsafe_precondition!((start: usize, end: usize) => start <= end) };
+        unsafe {
+            assert_unsafe_precondition!(
+               "IndexRange::new_unchecked requires `start <= end`",
+                (start: usize, end: usize) => start <= end
+            )
+        };
         IndexRange { start, end }
     }
 
diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs
index 846efbc4ebf6c..1390e09dd96ae 100644
--- a/library/core/src/ptr/alignment.rs
+++ b/library/core/src/ptr/alignment.rs
@@ -76,7 +76,12 @@ impl Alignment {
     #[inline]
     pub const unsafe fn new_unchecked(align: usize) -> Self {
         // SAFETY: Precondition passed to the caller.
-        unsafe { assert_unsafe_precondition!((align: usize) => align.is_power_of_two()) };
+        unsafe {
+            assert_unsafe_precondition!(
+               "Alignment::new_unchecked requires a power of two",
+                (align: usize) => align.is_power_of_two()
+            )
+        };
 
         // SAFETY: By precondition, this must be a power of two, and
         // our variants encompass all possible powers of two.
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index ed16c5f051f7b..5a083227bb0ef 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -761,7 +761,10 @@ impl<T: ?Sized> *const T {
         // SAFETY: The comparison has no side-effects, and the intrinsic
         // does this check internally in the CTFE implementation.
         unsafe {
-            assert_unsafe_precondition!([T](this: *const T, origin: *const T) => this >= origin)
+            assert_unsafe_precondition!(
+                "ptr::sub_ptr requires `this >= origin`",
+                [T](this: *const T, origin: *const T) => this >= origin
+            )
         };
 
         let pointee_size = mem::size_of::<T>();
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index 3a70981d2e138..565c38d222a2c 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -581,12 +581,21 @@ pub const fn invalid_mut<T>(addr: usize) -> *mut T {
 /// Convert an address back to a pointer, picking up a previously 'exposed' provenance.
 ///
 /// This is equivalent to `addr as *const T`. The provenance of the returned pointer is that of *any*
-/// pointer that was previously passed to [`expose_addr`][pointer::expose_addr] or a `ptr as usize`
-/// cast. If there is no previously 'exposed' provenance that justifies the way this pointer will be
-/// used, the program has undefined behavior. Note that there is no algorithm that decides which
-/// provenance will be used. You can think of this as "guessing" the right provenance, and the guess
-/// will be "maximally in your favor", in the sense that if there is any way to avoid undefined
-/// behavior, then that is the guess that will be taken.
+/// pointer that was previously exposed by passing it to [`expose_addr`][pointer::expose_addr],
+/// or a `ptr as usize` cast. In addition, memory which is outside the control of the Rust abstract
+/// machine (MMIO registers, for example) is always considered to be exposed, so long as this memory
+/// is disjoint from memory that will be used by the abstract machine such as the stack, heap,
+/// and statics.
+///
+/// If there is no 'exposed' provenance that justifies the way this pointer will be used,
+/// the program has undefined behavior. In particular, the aliasing rules still apply: pointers
+/// and references that have been invalidated due to aliasing accesses cannot be used any more,
+/// even if they have been exposed!
+///
+/// Note that there is no algorithm that decides which provenance will be used. You can think of this
+/// as "guessing" the right provenance, and the guess will be "maximally in your favor", in the sense
+/// that if there is any way to avoid undefined behavior (while upholding all aliasing requirements),
+/// then that is the guess that will be taken.
 ///
 /// On platforms with multiple address spaces, it is your responsibility to ensure that the
 /// address makes sense in the address space that this pointer will be used with.
@@ -889,7 +898,10 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
     // SAFETY: the caller must guarantee that `x` and `y` are
     // valid for writes and properly aligned.
     unsafe {
-        assert_unsafe_precondition!([T](x: *mut T, y: *mut T, count: usize) =>
+        assert_unsafe_precondition!(
+            "ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \
+            and the specified memory ranges do not overlap",
+            [T](x: *mut T, y: *mut T, count: usize) =>
             is_aligned_and_not_null(x)
                 && is_aligned_and_not_null(y)
                 && is_nonoverlapping(x, y, count)
@@ -986,7 +998,10 @@ pub const unsafe fn replace<T>(dst: *mut T, mut src: T) -> T {
     // and cannot overlap `src` since `dst` must point to a distinct
     // allocated object.
     unsafe {
-        assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
+        assert_unsafe_precondition!(
+            "ptr::replace requires that the pointer argument is aligned and non-null",
+            [T](dst: *mut T) => is_aligned_and_not_null(dst)
+        );
         mem::swap(&mut *dst, &mut src); // cannot overlap
     }
     src
@@ -1117,7 +1132,10 @@ pub const unsafe fn read<T>(src: *const T) -> T {
     // Also, since we just wrote a valid value into `tmp`, it is guaranteed
     // to be properly initialized.
     unsafe {
-        assert_unsafe_precondition!([T](src: *const T) => is_aligned_and_not_null(src));
+        assert_unsafe_precondition!(
+            "ptr::read requires that the pointer argument is aligned and non-null",
+            [T](src: *const T) => is_aligned_and_not_null(src)
+        );
         copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
         tmp.assume_init()
     }
@@ -1311,7 +1329,10 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) {
     // `dst` cannot overlap `src` because the caller has mutable access
     // to `dst` while `src` is owned by this function.
     unsafe {
-        assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
+        assert_unsafe_precondition!(
+            "ptr::write requires that the pointer argument is aligned and non-null",
+            [T](dst: *mut T) => is_aligned_and_not_null(dst)
+        );
         copy_nonoverlapping(&src as *const T, dst, 1);
         intrinsics::forget(src);
     }
@@ -1475,7 +1496,10 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
 pub unsafe fn read_volatile<T>(src: *const T) -> T {
     // SAFETY: the caller must uphold the safety contract for `volatile_load`.
     unsafe {
-        assert_unsafe_precondition!([T](src: *const T) => is_aligned_and_not_null(src));
+        assert_unsafe_precondition!(
+            "ptr::read_volatile requires that the pointer argument is aligned and non-null",
+            [T](src: *const T) => is_aligned_and_not_null(src)
+        );
         intrinsics::volatile_load(src)
     }
 }
@@ -1546,7 +1570,10 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
 pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
     // SAFETY: the caller must uphold the safety contract for `volatile_store`.
     unsafe {
-        assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
+        assert_unsafe_precondition!(
+            "ptr::write_volatile requires that the pointer argument is aligned and non-null",
+            [T](dst: *mut T) => is_aligned_and_not_null(dst)
+        );
         intrinsics::volatile_store(dst, src);
     }
 }
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index 7264d57ba6aed..c18264d13ebac 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -197,7 +197,7 @@ impl<T: ?Sized> NonNull<T> {
     pub const unsafe fn new_unchecked(ptr: *mut T) -> Self {
         // SAFETY: the caller must guarantee that `ptr` is non-null.
         unsafe {
-            assert_unsafe_precondition!([T: ?Sized](ptr: *mut T) => !ptr.is_null());
+            assert_unsafe_precondition!("NonNull::new_unchecked requires that the pointer is non-null", [T: ?Sized](ptr: *mut T) => !ptr.is_null());
             NonNull { pointer: ptr as _ }
         }
     }
diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs
index 916d0262dedbc..6d2f7330d5db5 100644
--- a/library/core/src/slice/index.rs
+++ b/library/core/src/slice/index.rs
@@ -232,7 +232,10 @@ unsafe impl<T> const SliceIndex<[T]> for usize {
         // `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
         // so the call to `add` is safe.
         unsafe {
-            assert_unsafe_precondition!([T](this: usize, slice: *const [T]) => this < slice.len());
+            assert_unsafe_precondition!(
+                "slice::get_unchecked requires that the index is within the slice",
+                [T](this: usize, slice: *const [T]) => this < slice.len()
+            );
             slice.as_ptr().add(self)
         }
     }
@@ -242,7 +245,10 @@ unsafe impl<T> const SliceIndex<[T]> for usize {
         let this = self;
         // SAFETY: see comments for `get_unchecked` above.
         unsafe {
-            assert_unsafe_precondition!([T](this: usize, slice: *mut [T]) => this < slice.len());
+            assert_unsafe_precondition!(
+                "slice::get_unchecked_mut requires that the index is within the slice",
+                [T](this: usize, slice: *mut [T]) => this < slice.len()
+            );
             slice.as_mut_ptr().add(self)
         }
     }
@@ -295,8 +301,10 @@ unsafe impl<T> const SliceIndex<[T]> for ops::IndexRange {
         // so the call to `add` is safe.
 
         unsafe {
-            assert_unsafe_precondition!([T](end: usize, slice: *const [T]) =>
-                end <= slice.len());
+            assert_unsafe_precondition!(
+                "slice::get_unchecked requires that the index is within the slice",
+                [T](end: usize, slice: *const [T]) => end <= slice.len()
+            );
             ptr::slice_from_raw_parts(slice.as_ptr().add(self.start()), self.len())
         }
     }
@@ -306,8 +314,10 @@ unsafe impl<T> const SliceIndex<[T]> for ops::IndexRange {
         let end = self.end();
         // SAFETY: see comments for `get_unchecked` above.
         unsafe {
-            assert_unsafe_precondition!([T](end: usize, slice: *mut [T]) =>
-                end <= slice.len());
+            assert_unsafe_precondition!(
+                "slice::get_unchecked_mut requires that the index is within the slice",
+                [T](end: usize, slice: *mut [T]) => end <= slice.len()
+            );
             ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start()), self.len())
         }
     }
@@ -367,8 +377,11 @@ unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> {
         // so the call to `add` is safe.
 
         unsafe {
-            assert_unsafe_precondition!([T](this: ops::Range<usize>, slice: *const [T]) =>
-            this.end >= this.start && this.end <= slice.len());
+            assert_unsafe_precondition!(
+                "slice::get_unchecked requires that the range is within the slice",
+                [T](this: ops::Range<usize>, slice: *const [T]) =>
+                this.end >= this.start && this.end <= slice.len()
+            );
             ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start)
         }
     }
@@ -378,8 +391,11 @@ unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> {
         let this = ops::Range { start: self.start, end: self.end };
         // SAFETY: see comments for `get_unchecked` above.
         unsafe {
-            assert_unsafe_precondition!([T](this: ops::Range<usize>, slice: *mut [T]) =>
-                this.end >= this.start && this.end <= slice.len());
+            assert_unsafe_precondition!(
+                "slice::get_unchecked_mut requires that the range is within the slice",
+                [T](this: ops::Range<usize>, slice: *mut [T]) =>
+                this.end >= this.start && this.end <= slice.len()
+            );
             ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start)
         }
     }
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 94ab13ed2e04f..4f1bb17344b29 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -653,7 +653,10 @@ impl<T> [T] {
         let ptr = this.as_mut_ptr();
         // SAFETY: caller has to guarantee that `a < self.len()` and `b < self.len()`
         unsafe {
-            assert_unsafe_precondition!([T](a: usize, b: usize, this: &mut [T]) => a < this.len() && b < this.len());
+            assert_unsafe_precondition!(
+                "slice::swap_unchecked requires that the indices are within the slice",
+                [T](a: usize, b: usize, this: &mut [T]) => a < this.len() && b < this.len()
+            );
             ptr::swap(ptr.add(a), ptr.add(b));
         }
     }
@@ -969,7 +972,10 @@ impl<T> [T] {
         let this = self;
         // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
         let new_len = unsafe {
-            assert_unsafe_precondition!([T](this: &[T], N: usize) => N != 0 && this.len() % N == 0);
+            assert_unsafe_precondition!(
+                "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks",
+                [T](this: &[T], N: usize) => N != 0 && this.len() % N == 0
+            );
             exact_div(self.len(), N)
         };
         // SAFETY: We cast a slice of `new_len * N` elements into
@@ -1109,7 +1115,10 @@ impl<T> [T] {
         let this = &*self;
         // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
         let new_len = unsafe {
-            assert_unsafe_precondition!([T](this: &[T], N: usize) => N != 0 && this.len() % N == 0);
+            assert_unsafe_precondition!(
+                "slice::as_chunks_unchecked_mut requires `N != 0` and the slice to split exactly into `N`-element chunks",
+                [T](this: &[T], N: usize) => N != 0 && this.len() % N == 0
+            );
             exact_div(this.len(), N)
         };
         // SAFETY: We cast a slice of `new_len * N` elements into
@@ -1685,7 +1694,10 @@ impl<T> [T] {
         // `[ptr; mid]` and `[mid; len]` are not overlapping, so returning a mutable reference
         // is fine.
         unsafe {
-            assert_unsafe_precondition!((mid: usize, len: usize) => mid <= len);
+            assert_unsafe_precondition!(
+                "slice::split_at_mut_unchecked requires the index to be within the slice",
+                (mid: usize, len: usize) => mid <= len
+            );
             (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid))
         }
     }
diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs
index dace748fed455..052fd34d0b6b7 100644
--- a/library/core/src/slice/raw.rs
+++ b/library/core/src/slice/raw.rs
@@ -92,8 +92,10 @@ use crate::ptr;
 pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] {
     // SAFETY: the caller must uphold the safety contract for `from_raw_parts`.
     unsafe {
-        assert_unsafe_precondition!([T](data: *const T, len: usize) =>
-            is_aligned_and_not_null(data) && is_valid_allocation_size::<T>(len)
+        assert_unsafe_precondition!(
+            "slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`",
+            [T](data: *const T, len: usize) => is_aligned_and_not_null(data)
+                && is_valid_allocation_size::<T>(len)
         );
         &*ptr::slice_from_raw_parts(data, len)
     }
@@ -135,8 +137,10 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
 pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] {
     // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`.
     unsafe {
-        assert_unsafe_precondition!([T](data: *mut T, len: usize) =>
-            is_aligned_and_not_null(data) && is_valid_allocation_size::<T>(len)
+        assert_unsafe_precondition!(
+            "slice::from_raw_parts_mut requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`",
+            [T](data: *mut T, len: usize) => is_aligned_and_not_null(data)
+                && is_valid_allocation_size::<T>(len)
         );
         &mut *ptr::slice_from_raw_parts_mut(data, len)
     }
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index e22b2f3340af0..37a49f2d78acd 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -1,3 +1,6 @@
+// miri has some special hacks here that make things unused.
+#![cfg_attr(miri, allow(unused))]
+
 use crate::os::unix::prelude::*;
 
 use crate::ffi::{CStr, OsStr, OsString};
@@ -850,7 +853,6 @@ impl DirEntry {
         target_os = "fuchsia",
         target_os = "redox"
     )))]
-    #[cfg_attr(miri, allow(unused))]
     fn name_cstr(&self) -> &CStr {
         unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
     }
@@ -862,7 +864,6 @@ impl DirEntry {
         target_os = "fuchsia",
         target_os = "redox"
     ))]
-    #[cfg_attr(miri, allow(unused))]
     fn name_cstr(&self) -> &CStr {
         &self.name
     }
diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs
index 4de694557982d..141f16d17f022 100644
--- a/library/test/src/lib.rs
+++ b/library/test/src/lib.rs
@@ -20,6 +20,7 @@
 #![feature(is_terminal)]
 #![feature(staged_api)]
 #![feature(process_exitcode_internals)]
+#![feature(panic_can_unwind)]
 #![feature(test)]
 
 // Public reexports
@@ -54,6 +55,7 @@ use std::{
     collections::VecDeque,
     env, io,
     io::prelude::Write,
+    mem::ManuallyDrop,
     panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo},
     process::{self, Command, Termination},
     sync::mpsc::{channel, Sender},
@@ -112,6 +114,29 @@ pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>, options: Option<Opt
             process::exit(ERROR_EXIT_CODE);
         }
     } else {
+        if !opts.nocapture {
+            // If we encounter a non-unwinding panic, flush any captured output from the current test,
+            // and stop  capturing output to ensure that the non-unwinding panic message is visible.
+            // We also acquire the locks for both output streams to prevent output from other threads
+            // from interleaving with the panic message or appearing after it.
+            let builtin_panic_hook = panic::take_hook();
+            let hook = Box::new({
+                move |info: &'_ PanicInfo<'_>| {
+                    if !info.can_unwind() {
+                        std::mem::forget(std::io::stderr().lock());
+                        let mut stdout = ManuallyDrop::new(std::io::stdout().lock());
+                        if let Some(captured) = io::set_output_capture(None) {
+                            if let Ok(data) = captured.lock() {
+                                let _ = stdout.write_all(&data);
+                                let _ = stdout.flush();
+                            }
+                        }
+                    }
+                    builtin_panic_hook(info);
+                }
+            });
+            panic::set_hook(hook);
+        }
         match console::run_tests_console(&opts, tests) {
             Ok(true) => {}
             Ok(false) => process::exit(ERROR_EXIT_CODE),
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index b60c77195639f..5a1a6c0f0f9b1 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -1293,7 +1293,7 @@ h3.variant {
 	content: "\00a0\00a0\00a0";
 }
 
-.notable-traits .notable, .notable-traits .docblock {
+.notable-traits .docblock {
 	margin: 0;
 }
 
diff --git a/src/test/rustdoc-gui/notable-trait.goml b/src/test/rustdoc-gui/notable-trait.goml
index 997fb5cf0adec..efe0cb15f08a0 100644
--- a/src/test/rustdoc-gui/notable-trait.goml
+++ b/src/test/rustdoc-gui/notable-trait.goml
@@ -24,7 +24,23 @@ assert-position: (
     "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
     {"x": 951},
 )
-
+// The tooltip should be beside the `i`
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+compare-elements-position-near: (
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
+    {"y": 2}
+)
+compare-elements-position-false: (
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
+    ("x")
+)
+// The docblock should be flush with the border.
+assert-css: (
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']/*[@class='docblock']",
+    {"margin-left": "0px"}
+)
 
 // Now only the `i` should be on the next line.
 size: (1055, 600)
@@ -81,6 +97,27 @@ assert-position: (
     "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
     {"x": 289},
 )
+// The tooltip should be below `i`
+compare-elements-position-near-false: (
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
+    {"y": 2}
+)
+compare-elements-position-false: (
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
+    ("x")
+)
+compare-elements-position-near: (
+    "//*[@id='method.create_an_iterator_from_read']/parent::*",
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
+    {"x": 5}
+)
+// The docblock should be flush with the border.
+assert-css: (
+    "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']/*[@class='docblock']",
+    {"margin-left": "0px"}
+)
 
 // Checking on very small mobile. The `i` should be on its own line.
 size: (365, 600)
diff --git a/src/test/ui/marker_trait_attr/overlap-marker-trait-with-static-lifetime.rs b/src/test/ui/marker_trait_attr/overlap-marker-trait-with-static-lifetime.rs
new file mode 100644
index 0000000000000..62aa22d41ed8e
--- /dev/null
+++ b/src/test/ui/marker_trait_attr/overlap-marker-trait-with-static-lifetime.rs
@@ -0,0 +1,10 @@
+// check-pass
+#![feature(marker_trait_attr)]
+
+#[marker]
+trait Marker {}
+
+impl Marker for &'static () {}
+impl Marker for &'static () {}
+
+fn main() {}
diff --git a/src/test/ui/marker_trait_attr/overlap-marker-trait-with-underscore-lifetime.rs b/src/test/ui/marker_trait_attr/overlap-marker-trait-with-underscore-lifetime.rs
new file mode 100644
index 0000000000000..eabce1aeff140
--- /dev/null
+++ b/src/test/ui/marker_trait_attr/overlap-marker-trait-with-underscore-lifetime.rs
@@ -0,0 +1,9 @@
+#![feature(marker_trait_attr)]
+
+#[marker]
+trait Marker {}
+
+impl Marker for &'_ () {} //~ ERROR type annotations needed
+impl Marker for &'_ () {} //~ ERROR type annotations needed
+
+fn main() {}
diff --git a/src/test/ui/marker_trait_attr/overlap-marker-trait-with-underscore-lifetime.stderr b/src/test/ui/marker_trait_attr/overlap-marker-trait-with-underscore-lifetime.stderr
new file mode 100644
index 0000000000000..235c89e200aee
--- /dev/null
+++ b/src/test/ui/marker_trait_attr/overlap-marker-trait-with-underscore-lifetime.stderr
@@ -0,0 +1,31 @@
+error[E0283]: type annotations needed: cannot satisfy `&(): Marker`
+  --> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:6:6
+   |
+LL | impl Marker for &'_ () {}
+   |      ^^^^^^
+   |
+note: multiple `impl`s satisfying `&(): Marker` found
+  --> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:6:1
+   |
+LL | impl Marker for &'_ () {}
+   | ^^^^^^^^^^^^^^^^^^^^^^
+LL | impl Marker for &'_ () {}
+   | ^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0283]: type annotations needed: cannot satisfy `&(): Marker`
+  --> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:7:6
+   |
+LL | impl Marker for &'_ () {}
+   |      ^^^^^^
+   |
+note: multiple `impl`s satisfying `&(): Marker` found
+  --> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:6:1
+   |
+LL | impl Marker for &'_ () {}
+   | ^^^^^^^^^^^^^^^^^^^^^^
+LL | impl Marker for &'_ () {}
+   | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0283`.
diff --git a/src/test/ui/marker_trait_attr/overlap-marker-trait.rs b/src/test/ui/marker_trait_attr/overlap-marker-trait.rs
index 8794d42f4113f..67e551797755b 100644
--- a/src/test/ui/marker_trait_attr/overlap-marker-trait.rs
+++ b/src/test/ui/marker_trait_attr/overlap-marker-trait.rs
@@ -7,7 +7,8 @@
 
 use std::fmt::{Debug, Display};
 
-#[marker] trait Marker {}
+#[marker]
+trait Marker {}
 
 impl<T: Debug> Marker for T {}
 impl<T: Display> Marker for T {}
diff --git a/src/test/ui/marker_trait_attr/overlap-marker-trait.stderr b/src/test/ui/marker_trait_attr/overlap-marker-trait.stderr
index 1f34105979441..133bc0484ee00 100644
--- a/src/test/ui/marker_trait_attr/overlap-marker-trait.stderr
+++ b/src/test/ui/marker_trait_attr/overlap-marker-trait.stderr
@@ -1,11 +1,11 @@
 error[E0277]: the trait bound `NotDebugOrDisplay: Marker` is not satisfied
-  --> $DIR/overlap-marker-trait.rs:27:17
+  --> $DIR/overlap-marker-trait.rs:28:17
    |
 LL |     is_marker::<NotDebugOrDisplay>();
    |                 ^^^^^^^^^^^^^^^^^ the trait `Marker` is not implemented for `NotDebugOrDisplay`
    |
 note: required by a bound in `is_marker`
-  --> $DIR/overlap-marker-trait.rs:15:17
+  --> $DIR/overlap-marker-trait.rs:16:17
    |
 LL | fn is_marker<T: Marker>() { }
    |                 ^^^^^^ required by this bound in `is_marker`
diff --git a/src/test/ui/marker_trait_attr/overlap-permitted-for-annotated-marker-traits.rs b/src/test/ui/marker_trait_attr/overlap-permitted-for-annotated-marker-traits.rs
index 383313902379e..f7654458feb01 100644
--- a/src/test/ui/marker_trait_attr/overlap-permitted-for-annotated-marker-traits.rs
+++ b/src/test/ui/marker_trait_attr/overlap-permitted-for-annotated-marker-traits.rs
@@ -7,7 +7,8 @@
 
 use std::fmt::{Debug, Display};
 
-#[marker] trait MyMarker {}
+#[marker]
+trait MyMarker {}
 
 impl<T: Debug> MyMarker for T {}
 impl<T: Display> MyMarker for T {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr b/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr
index b86acb2cc9ab2..d4f42b787e4da 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr
@@ -1,8 +1,8 @@
 error: ~const can only be applied to `#[const_trait]` traits
-  --> $DIR/super-traits-fail-2.rs:11:12
+  --> $DIR/super-traits-fail-2.rs:11:19
    |
 LL | trait Bar: ~const Foo {}
-   |            ^^^^^^^^^^
+   |                   ^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr b/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr
index b86acb2cc9ab2..d4f42b787e4da 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr
@@ -1,8 +1,8 @@
 error: ~const can only be applied to `#[const_trait]` traits
-  --> $DIR/super-traits-fail-2.rs:11:12
+  --> $DIR/super-traits-fail-2.rs:11:19
    |
 LL | trait Bar: ~const Foo {}
-   |            ^^^^^^^^^^
+   |                   ^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr b/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr
index 191edca1761c8..d433e1cfa698a 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr
@@ -1,14 +1,14 @@
 error: ~const can only be applied to `#[const_trait]` traits
-  --> $DIR/super-traits-fail-3.rs:12:12
+  --> $DIR/super-traits-fail-3.rs:12:19
    |
 LL | trait Bar: ~const Foo {}
-   |            ^^^^^^^^^^
+   |                   ^^^
 
 error: ~const can only be applied to `#[const_trait]` traits
-  --> $DIR/super-traits-fail-3.rs:15:17
+  --> $DIR/super-traits-fail-3.rs:15:24
    |
 LL | const fn foo<T: ~const Bar>(x: &T) {
-   |                 ^^^^^^^^^^
+   |                        ^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr b/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr
index a3b4c302a57f8..2a7e8e00bc78c 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr
@@ -1,8 +1,8 @@
 error: ~const can only be applied to `#[const_trait]` traits
-  --> $DIR/super-traits-fail-3.rs:12:12
+  --> $DIR/super-traits-fail-3.rs:12:19
    |
 LL | trait Bar: ~const Foo {}
-   |            ^^^^^^^^^^
+   |                   ^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr b/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr
index 9d61166546593..e5978c12a0981 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr
@@ -1,8 +1,8 @@
 error: ~const can only be applied to `#[const_trait]` traits
-  --> $DIR/super-traits-fail-3.rs:15:17
+  --> $DIR/super-traits-fail-3.rs:15:24
    |
 LL | const fn foo<T: ~const Bar>(x: &T) {
-   |                 ^^^^^^^^^^
+   |                        ^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/wf/issue-103573.rs b/src/test/ui/wf/issue-103573.rs
new file mode 100644
index 0000000000000..bcbf4f941ecd9
--- /dev/null
+++ b/src/test/ui/wf/issue-103573.rs
@@ -0,0 +1,22 @@
+trait TraitA {
+    type TypeA;
+}
+
+trait TraitD {
+    type TypeD;
+}
+
+pub trait TraitB {
+    type TypeB: TraitD;
+
+    fn f(_: &<Self::TypeB as TraitD>::TypeD);
+}
+
+pub trait TraitC<E> {
+    type TypeC<'a>: TraitB;
+
+    fn g<'a>(_: &<<Self::TypeC<'a> as TraitB>::TypeB as TraitA>::TypeA);
+    //~^ ERROR the trait bound `<<Self as TraitC<E>>::TypeC<'a> as TraitB>::TypeB: TraitA` is not satisfied
+}
+
+fn main() {}
diff --git a/src/test/ui/wf/issue-103573.stderr b/src/test/ui/wf/issue-103573.stderr
new file mode 100644
index 0000000000000..fcf3f15e4d3f6
--- /dev/null
+++ b/src/test/ui/wf/issue-103573.stderr
@@ -0,0 +1,14 @@
+error[E0277]: the trait bound `<<Self as TraitC<E>>::TypeC<'a> as TraitB>::TypeB: TraitA` is not satisfied
+  --> $DIR/issue-103573.rs:18:5
+   |
+LL |     fn g<'a>(_: &<<Self::TypeC<'a> as TraitB>::TypeB as TraitA>::TypeA);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TraitA` is not implemented for `<<Self as TraitC<E>>::TypeC<'a> as TraitB>::TypeB`
+   |
+help: consider further restricting the associated type
+   |
+LL |     fn g<'a>(_: &<<Self::TypeC<'a> as TraitB>::TypeB as TraitA>::TypeA) where <<Self as TraitC<E>>::TypeC<'a> as TraitB>::TypeB: TraitA;
+   |                                                                         +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.