about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-07-26 20:14:16 +0000
committerbors <bors@rust-lang.org>2024-07-26 20:14:16 +0000
commit7c2012d0ec3aae89fefc40e5d6b317a0949cda36 (patch)
tree50993e684ae086ee44409abd47a29e0ff5dcca36
parent47243b335e02c341528dd3beb77fa0e030d7ccda (diff)
parentfd9d0bfbacc2d5af183ba394ca16cb2581dab02d (diff)
downloadrust-7c2012d0ec3aae89fefc40e5d6b317a0949cda36.tar.gz
rust-7c2012d0ec3aae89fefc40e5d6b317a0949cda36.zip
Auto merge of #121676 - Bryanskiy:polarity, r=petrochenkov
Support ?Trait bounds in supertraits and dyn Trait under a feature gate

This patch allows `maybe` polarity bounds under a feature gate. The only language change here is that corresponding hard errors are replaced by feature gates. Example:
```rust
#![feature(allow_maybe_polarity)]
...
trait Trait1 : ?Trait { ... } // ok
fn foo(_: Box<(dyn Trait2 + ?Trait)>) {} // ok
fn bar<T: ?Sized + ?Trait>(_: &T) {} // ok
```
Maybe bounds still don't do anything (except for `Sized` trait), however this patch will allow us to [experiment with default auto traits](https://github.com/rust-lang/rust/pull/120706#issuecomment-1934006762).

This is a part of the [MCP: Low level components for async drop](https://github.com/rust-lang/compiler-team/issues/727)
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs10
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs28
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs28
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs6
-rw-r--r--compiler/rustc_hir/src/intravisit.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs18
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs9
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs5
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs4
-rw-r--r--compiler/rustc_lint/src/traits.rs6
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs4
-rw-r--r--src/librustdoc/clean/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/types/borrowed_box.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/types/type_complexity.rs1
-rw-r--r--tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs24
-rw-r--r--tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr71
-rw-r--r--tests/ui/maybe-bounds.stderr15
-rw-r--r--tests/ui/parser/trait-object-trait-parens.stderr18
-rw-r--r--tests/ui/traits/maybe-polarity-pass.rs27
-rw-r--r--tests/ui/traits/maybe-polarity-pass.stderr20
-rw-r--r--tests/ui/traits/maybe-polarity-repeated.rs9
-rw-r--r--tests/ui/traits/maybe-polarity-repeated.stderr21
-rw-r--r--tests/ui/traits/wf-object/maybe-bound.stderr26
-rw-r--r--tests/ui/traits/wf-object/only-maybe-bound.stderr8
-rw-r--r--tests/ui/unsized/maybe-bounds-where.stderr31
36 files changed, 364 insertions, 83 deletions
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index f990b4ba69b..68745056438 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1525,8 +1525,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     continue;
                 }
                 let is_param = *is_param.get_or_insert_with(compute_is_param);
-                if !is_param {
-                    self.dcx().emit_err(MisplacedRelaxTraitBound { span: bound.span() });
+                if !is_param && !self.tcx.features().more_maybe_bounds {
+                    self.tcx
+                        .sess
+                        .create_feature_err(
+                            MisplacedRelaxTraitBound { span: bound.span() },
+                            sym::more_maybe_bounds,
+                        )
+                        .emit();
                 }
             }
         }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 0f5f4d8023b..218493cb7ea 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1216,6 +1216,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     itctx,
                     TraitBoundModifiers::NONE,
                 );
+                let bound = (bound, hir::TraitBoundModifier::None);
                 let bounds = this.arena.alloc_from_iter([bound]);
                 let lifetime_bound = this.elided_dyn_bound(t.span);
                 (bounds, lifetime_bound)
@@ -1348,21 +1349,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             // We can safely ignore constness here since AST validation
                             // takes care of rejecting invalid modifier combinations and
                             // const trait bounds in trait object types.
-                            GenericBound::Trait(ty, modifiers) => match modifiers.polarity {
-                                BoundPolarity::Positive | BoundPolarity::Negative(_) => {
-                                    Some(this.lower_poly_trait_ref(
-                                        ty,
-                                        itctx,
-                                        // Still, don't pass along the constness here; we don't want to
-                                        // synthesize any host effect args, it'd only cause problems.
-                                        TraitBoundModifiers {
-                                            constness: BoundConstness::Never,
-                                            ..*modifiers
-                                        },
-                                    ))
-                                }
-                                BoundPolarity::Maybe(_) => None,
-                            },
+                            GenericBound::Trait(ty, modifiers) => {
+                                // Still, don't pass along the constness here; we don't want to
+                                // synthesize any host effect args, it'd only cause problems.
+                                let modifiers = TraitBoundModifiers {
+                                    constness: BoundConstness::Never,
+                                    ..*modifiers
+                                };
+                                let trait_ref = this.lower_poly_trait_ref(ty, itctx, modifiers);
+                                let polarity = this.lower_trait_bound_modifiers(modifiers);
+                                Some((trait_ref, polarity))
+                            }
                             GenericBound::Outlives(lifetime) => {
                                 if lifetime_bound.is_none() {
                                     lifetime_bound = Some(this.lower_lifetime(lifetime));
@@ -2688,6 +2685,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             trait_ref: hir::TraitRef { path, hir_ref_id: hir_id },
                             span: self.lower_span(span),
                         };
+                        let principal = (principal, hir::TraitBoundModifier::None);
 
                         // The original ID is taken by the `PolyTraitRef`,
                         // so the `Ty` itself needs a different one.
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 1088db74cc9..9b063a330b7 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -1345,14 +1345,28 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         match bound {
             GenericBound::Trait(trait_ref, modifiers) => {
                 match (ctxt, modifiers.constness, modifiers.polarity) {
-                    (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
-                        self.dcx().emit_err(errors::OptionalTraitSupertrait {
-                            span: trait_ref.span,
-                            path_str: pprust::path_to_string(&trait_ref.trait_ref.path),
-                        });
+                    (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_))
+                        if !self.features.more_maybe_bounds =>
+                    {
+                        self.session
+                            .create_feature_err(
+                                errors::OptionalTraitSupertrait {
+                                    span: trait_ref.span,
+                                    path_str: pprust::path_to_string(&trait_ref.trait_ref.path),
+                                },
+                                sym::more_maybe_bounds,
+                            )
+                            .emit();
                     }
-                    (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
-                        self.dcx().emit_err(errors::OptionalTraitObject { span: trait_ref.span });
+                    (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_))
+                        if !self.features.more_maybe_bounds =>
+                    {
+                        self.session
+                            .create_feature_err(
+                                errors::OptionalTraitObject { span: trait_ref.span },
+                                sym::more_maybe_bounds,
+                            )
+                            .emit();
                     }
                     (
                         BoundKind::TraitObject,
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 9b5ed3b0876..d3d07181096 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -205,6 +205,8 @@ declare_features! (
     (unstable, lifetime_capture_rules_2024, "1.76.0", None),
     /// Allows `#[link(..., cfg(..))]`; perma-unstable per #37406
     (unstable, link_cfg, "1.14.0", None),
+    /// Allows using `?Trait` trait bounds in more contexts.
+    (internal, more_maybe_bounds, "CURRENT_RUSTC_VERSION", None),
     /// Allows the `multiple_supertrait_upcastable` lint.
     (unstable, multiple_supertrait_upcastable, "1.69.0", None),
     /// Allow negative trait bounds. This is an internal-only feature for testing the trait solver!
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 20b15499234..8c8f760bc41 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2832,7 +2832,11 @@ pub enum TyKind<'hir> {
     OpaqueDef(ItemId, &'hir [GenericArg<'hir>], bool),
     /// A trait object type `Bound1 + Bound2 + Bound3`
     /// where `Bound` is a trait or a lifetime.
-    TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax),
+    TraitObject(
+        &'hir [(PolyTraitRef<'hir>, TraitBoundModifier)],
+        &'hir Lifetime,
+        TraitObjectSyntax,
+    ),
     /// Unused for now.
     Typeof(&'hir AnonConst),
     /// `TyKind::Infer` means the type should be inferred instead of it having been
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index c202ee41e31..696f548f1ba 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -902,7 +902,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
             try_visit!(visitor.visit_array_length(length));
         }
         TyKind::TraitObject(bounds, ref lifetime, _syntax) => {
-            walk_list!(visitor, visit_poly_trait_ref, bounds);
+            for (bound, _modifier) in bounds {
+                try_visit!(visitor.visit_poly_trait_ref(bound));
+            }
             try_visit!(visitor.visit_lifetime(lifetime));
         }
         TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)),
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 0316ef69bf8..456dc4e4e07 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -831,7 +831,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GATArgsCollector<'tcx> {
 
 fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
     match ty.kind {
-        hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments {
+        hir::TyKind::TraitObject([(trait_ref, _)], ..) => match trait_ref.trait_ref.path.segments {
             [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()),
             _ => false,
         },
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 349dc9ad00e..02ea95852f0 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -652,7 +652,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                 debug!(?bounds, ?lifetime, "TraitObject");
                 let scope = Scope::TraitRefBoundary { s: self.scope };
                 self.with(scope, |this| {
-                    for bound in bounds {
+                    for (bound, _) in bounds {
                         this.visit_poly_trait_ref_inner(
                             bound,
                             NonLifetimeBinderAllowed::Deny("trait object types"),
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index 214eb6b2f06..30c04aa47a3 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -8,7 +8,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::bug;
 use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
 use rustc_span::symbol::Ident;
-use rustc_span::{ErrorGuaranteed, Span, Symbol};
+use rustc_span::{sym, ErrorGuaranteed, Span, Symbol};
 use rustc_trait_selection::traits;
 use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
 use smallvec::SmallVec;
@@ -73,10 +73,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
         }
 
+        let mut unique_bounds = FxIndexSet::default();
+        let mut seen_repeat = false;
+        for unbound in &unbounds {
+            if let Res::Def(DefKind::Trait, unbound_def_id) = unbound.trait_ref.path.res {
+                seen_repeat |= !unique_bounds.insert(unbound_def_id);
+            }
+        }
         if unbounds.len() > 1 {
-            self.dcx().emit_err(errors::MultipleRelaxedDefaultBounds {
+            let err = errors::MultipleRelaxedDefaultBounds {
                 spans: unbounds.iter().map(|ptr| ptr.span).collect(),
-            });
+            };
+            if seen_repeat {
+                self.dcx().emit_err(err);
+            } else if !tcx.features().more_maybe_bounds {
+                self.tcx().sess.create_feature_err(err, sym::more_maybe_bounds).emit();
+            };
         }
 
         let mut seen_sized_unbound = false;
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 56f508a2d43..20f06d77489 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -719,7 +719,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         &self,
         associated_types: FxIndexMap<Span, FxIndexSet<DefId>>,
         potential_assoc_types: Vec<usize>,
-        trait_bounds: &[hir::PolyTraitRef<'_>],
+        trait_bounds: &[(hir::PolyTraitRef<'_>, hir::TraitBoundModifier)],
     ) {
         if associated_types.values().all(|v| v.is_empty()) {
             return;
@@ -765,12 +765,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         // related to issue #91997, turbofishes added only when in an expr or pat
         let mut in_expr_or_pat = false;
         if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
-            let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id));
+            let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.0.trait_ref.hir_ref_id));
             in_expr_or_pat = match grandparent {
                 hir::Node::Expr(_) | hir::Node::Pat(_) => true,
                 _ => false,
             };
-            match bound.trait_ref.path.segments {
+            match bound.0.trait_ref.path.segments {
                 // FIXME: `trait_ref.path.span` can point to a full path with multiple
                 // segments, even though `trait_ref.path.segments` is of length `1`. Work
                 // around that bug here, even though it should be fixed elsewhere.
@@ -811,7 +811,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         // and we can then use their span to indicate this to the user.
         let bound_names = trait_bounds
             .iter()
-            .filter_map(|poly_trait_ref| {
+            .filter_map(|(poly_trait_ref, _)| {
                 let path = poly_trait_ref.trait_ref.path.segments.last()?;
                 let args = path.args?;
 
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
index 29c71c3fa50..e7aad0a29c5 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
@@ -34,7 +34,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 .ok()
                 .is_some_and(|s| s.trim_end().ends_with('<'));
 
-        let is_global = poly_trait_ref.trait_ref.path.is_global();
+        let is_global = poly_trait_ref.0.trait_ref.path.is_global();
 
         let mut sugg = vec![(
             self_ty.span.shrink_to_lo(),
@@ -176,7 +176,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let mut is_downgradable = true;
         let is_object_safe = match self_ty.kind {
             hir::TyKind::TraitObject(objects, ..) => {
-                objects.iter().all(|o| match o.trait_ref.path.res {
+                objects.iter().all(|(o, _)| match o.trait_ref.path.res {
                     Res::Def(DefKind::Trait, id) => {
                         if Some(id) == owner {
                             // For recursive traits, don't downgrade the error. (#119652)
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
index aafadc7f9cb..b3c7a1ff8a8 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
@@ -27,7 +27,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         &self,
         span: Span,
         hir_id: hir::HirId,
-        hir_trait_bounds: &[hir::PolyTraitRef<'tcx>],
+        hir_trait_bounds: &[(hir::PolyTraitRef<'tcx>, hir::TraitBoundModifier)],
         lifetime: &hir::Lifetime,
         borrowed: bool,
         representation: DynKind,
@@ -37,7 +37,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let mut bounds = Bounds::default();
         let mut potential_assoc_types = Vec::new();
         let dummy_self = self.tcx().types.trait_object_dummy_self;
-        for trait_bound in hir_trait_bounds.iter().rev() {
+        for (trait_bound, modifier) in hir_trait_bounds.iter().rev() {
+            if *modifier == hir::TraitBoundModifier::Maybe {
+                continue;
+            }
             if let GenericArgCountResult {
                 correct:
                     Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
@@ -249,7 +252,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 let args = tcx.mk_args(&args);
 
                 let span = i.bottom().1;
-                let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
+                let empty_generic_args = hir_trait_bounds.iter().any(|(hir_bound, _)| {
                     hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
                         && hir_bound.span.contains(span)
                 });
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index a982b84e755..e25f43e169d 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -300,13 +300,16 @@ impl<'a> State<'a> {
                     self.word_space("dyn");
                 }
                 let mut first = true;
-                for bound in bounds {
+                for (bound, modifier) in bounds {
                     if first {
                         first = false;
                     } else {
                         self.nbsp();
                         self.word_space("+");
                     }
+                    if *modifier == TraitBoundModifier::Maybe {
+                        self.word("?");
+                    }
                     self.print_poly_trait_ref(bound);
                 }
                 if !lifetime.is_elided() {
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index 2f8eea6cd18..93c606a954e 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -429,7 +429,7 @@ fn ty_has_local_parent(
             path_has_local_parent(ty_path, cx, impl_parent, impl_parent_parent)
         }
         TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => path_has_local_parent(
-            principle_poly_trait_ref.trait_ref.path,
+            principle_poly_trait_ref.0.trait_ref.path,
             cx,
             impl_parent,
             impl_parent_parent,
@@ -527,7 +527,7 @@ fn self_ty_kind_for_diagnostic(ty: &rustc_hir::Ty<'_>, tcx: TyCtxt<'_>) -> (Span
                 .to_string(),
         ),
         TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => {
-            let path = &principle_poly_trait_ref.trait_ref.path;
+            let path = &principle_poly_trait_ref.0.trait_ref.path;
             (
                 path_span_without_args(path),
                 path.res
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index 6983e7abbd6..552245f0cdd 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -113,9 +113,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
 
     fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
         let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return };
-        for bound in &bounds[..] {
+        for (bound, modifier) in &bounds[..] {
             let def_id = bound.trait_ref.trait_def_id();
-            if cx.tcx.lang_items().drop_trait() == def_id {
+            if cx.tcx.lang_items().drop_trait() == def_id
+                && *modifier != hir::TraitBoundModifier::Maybe
+            {
                 let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return };
                 cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id });
             }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 6d58c4877cb..cfd263d5951 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1230,6 +1230,7 @@ symbols! {
         modifiers,
         module,
         module_path,
+        more_maybe_bounds,
         more_qualified_paths,
         more_struct_aliases,
         movbe_target_feature,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
index b91b755d683..a892ce58861 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
@@ -84,7 +84,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
             }
 
             hir::TyKind::TraitObject(bounds, ..) => {
-                for bound in bounds {
+                for (bound, _) in bounds {
                     self.current_index.shift_in(1);
                     self.visit_poly_trait_ref(bound);
                     self.current_index.shift_out(1);
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
index ce157ff3dc8..e5d97976534 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
@@ -607,7 +607,7 @@ impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
             _,
         ) = t.kind
         {
-            for ptr in poly_trait_refs {
+            for (ptr, _) in poly_trait_refs {
                 if Some(self.1) == ptr.trait_ref.trait_def_id() {
                     self.0.push(ptr.span);
                 }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
index 87fdc5ddff8..89ae6f4ccab 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
@@ -437,7 +437,7 @@ pub fn report_object_safety_error<'tcx>(
         if tcx.parent_hir_node(hir_id).fn_sig().is_some() {
             // Do not suggest `impl Trait` when dealing with things like super-traits.
             err.span_suggestion_verbose(
-                ty.span.until(trait_ref.span),
+                ty.span.until(trait_ref.0.span),
                 "consider using an opaque type instead",
                 "impl ",
                 Applicability::MaybeIncorrect,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 885216e6216..d1381d24764 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -3040,11 +3040,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     match ty.kind {
                         hir::TyKind::TraitObject(traits, _, _) => {
                             let (span, kw) = match traits {
-                                [first, ..] if first.span.lo() == ty.span.lo() => {
+                                [(first, _), ..] if first.span.lo() == ty.span.lo() => {
                                     // Missing `dyn` in front of trait object.
                                     (ty.span.shrink_to_lo(), "dyn ")
                                 }
-                                [first, ..] => (ty.span.until(first.span), ""),
+                                [(first, _), ..] => (ty.span.until(first.span), ""),
                                 [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"),
                             };
                             let needs_parens = traits.len() != 1;
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 2cd9b6fcd38..26011926cdd 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1858,7 +1858,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
         }
         TyKind::Path(_) => clean_qpath(ty, cx),
         TyKind::TraitObject(bounds, ref lifetime, _) => {
-            let bounds = bounds.iter().map(|bound| clean_poly_trait_ref(bound, cx)).collect();
+            let bounds = bounds.iter().map(|(bound, _)| clean_poly_trait_ref(bound, cx)).collect();
             let lifetime =
                 if !lifetime.is_elided() { Some(clean_lifetime(*lifetime, cx)) } else { None };
             DynTrait(bounds, lifetime)
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index 9f063ced313..ba34af9c100 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -543,7 +543,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
                 if !lt.is_elided() {
                     self.unelided_trait_object_lifetime = true;
                 }
-                for bound in bounds {
+                for (bound, _) in bounds {
                     self.visit_poly_trait_ref(bound);
                 }
             },
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 922a0006ee5..59b74122f30 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -183,7 +183,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
 
             // Iterate the bounds and add them to our seen hash
             // If we haven't yet seen it, add it to the fixed traits
-            for bound in bounds {
+            for (bound, _) in bounds {
                 let Some(def_id) = bound.trait_ref.trait_def_id() else {
                     continue;
                 };
@@ -198,9 +198,9 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
             // If the number of unique traits isn't the same as the number of traits in the bounds,
             // there must be 1 or more duplicates
             if bounds.len() != unique_traits.len() {
-                let mut bounds_span = bounds[0].span;
+                let mut bounds_span = bounds[0].0.span;
 
-                for bound in bounds.iter().skip(1) {
+                for (bound, _) in bounds.iter().skip(1) {
                     bounds_span = bounds_span.to(bound.span);
                 }
 
diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
index 801e8862619..83cc9f2d8df 100644
--- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
@@ -81,14 +81,17 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
 
 // Returns true if given type is `Any` trait.
 fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool {
-    if let TyKind::TraitObject(traits, ..) = t.kind
-        && !traits.is_empty()
-        && let Some(trait_did) = traits[0].trait_ref.trait_def_id()
-        // Only Send/Sync can be used as additional traits, so it is enough to
-        // check only the first trait.
-        && cx.tcx.is_diagnostic_item(sym::Any, trait_did)
-    {
-        return true;
+    if let TyKind::TraitObject(traits, ..) = t.kind {
+        return traits
+            .iter()
+            .any(|(bound, _)| {
+                if let Some(trait_did) = bound.trait_ref.trait_def_id()
+                    && cx.tcx.is_diagnostic_item(sym::Any, trait_did)
+                {
+                    return true;
+                }
+                false
+            });
     }
 
     false
diff --git a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
index b6e765d7c39..7fcfd5c8f35 100644
--- a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
@@ -55,6 +55,7 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
             TyKind::TraitObject(param_bounds, _, _) => {
                 let has_lifetime_parameters = param_bounds.iter().any(|bound| {
                     bound
+                        .0
                         .bound_generic_params
                         .iter()
                         .any(|param| matches!(param.kind, GenericParamKind::Lifetime { .. }))
diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs
new file mode 100644
index 00000000000..debe143b091
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs
@@ -0,0 +1,24 @@
+#![feature(auto_traits)]
+
+trait Trait1 {}
+auto trait Trait2 {}
+trait Trait3: ?Trait1 {}
+//~^  ERROR `?Trait` is not permitted in supertraits
+trait Trait4 where Self: ?Trait1 {}
+//~^ ERROR ?Trait` bounds are only permitted at the point where a type parameter is declared
+
+fn foo(_: Box<dyn Trait1 + ?Trait2>) {}
+//~^  ERROR `?Trait` is not permitted in trait object types
+fn bar<T: ?Trait1 + ?Trait2>(_: T) {}
+//~^ ERROR type parameter has more than one relaxed default bound, only one is supported
+//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+
+trait Trait {}
+// Do not suggest `#![feature(more_maybe_bounds)]` for repetitions
+fn baz<T: ?Trait + ?Trait>(_ : T) {}
+//~^ ERROR type parameter has more than one relaxed default bound, only one is supported
+//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr
new file mode 100644
index 00000000000..e6d65e05997
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr
@@ -0,0 +1,71 @@
+error[E0658]: `?Trait` is not permitted in supertraits
+  --> $DIR/feature-gate-more-maybe-bounds.rs:5:15
+   |
+LL | trait Trait3: ?Trait1 {}
+   |               ^^^^^^^
+   |
+   = note: traits are `?Trait1` by default
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: `?Trait` is not permitted in trait object types
+  --> $DIR/feature-gate-more-maybe-bounds.rs:10:28
+   |
+LL | fn foo(_: Box<dyn Trait1 + ?Trait2>) {}
+   |                            ^^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared
+  --> $DIR/feature-gate-more-maybe-bounds.rs:7:26
+   |
+LL | trait Trait4 where Self: ?Trait1 {}
+   |                          ^^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0203]: type parameter has more than one relaxed default bound, only one is supported
+  --> $DIR/feature-gate-more-maybe-bounds.rs:12:11
+   |
+LL | fn bar<T: ?Trait1 + ?Trait2>(_: T) {}
+   |           ^^^^^^^   ^^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+  --> $DIR/feature-gate-more-maybe-bounds.rs:12:11
+   |
+LL | fn bar<T: ?Trait1 + ?Trait2>(_: T) {}
+   |           ^^^^^^^
+
+warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+  --> $DIR/feature-gate-more-maybe-bounds.rs:12:21
+   |
+LL | fn bar<T: ?Trait1 + ?Trait2>(_: T) {}
+   |                     ^^^^^^^
+
+error[E0203]: type parameter has more than one relaxed default bound, only one is supported
+  --> $DIR/feature-gate-more-maybe-bounds.rs:19:11
+   |
+LL | fn baz<T: ?Trait + ?Trait>(_ : T) {}
+   |           ^^^^^^   ^^^^^^
+
+warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+  --> $DIR/feature-gate-more-maybe-bounds.rs:19:11
+   |
+LL | fn baz<T: ?Trait + ?Trait>(_ : T) {}
+   |           ^^^^^^
+
+warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+  --> $DIR/feature-gate-more-maybe-bounds.rs:19:20
+   |
+LL | fn baz<T: ?Trait + ?Trait>(_ : T) {}
+   |                    ^^^^^^
+
+error: aborting due to 5 previous errors; 4 warnings emitted
+
+Some errors have detailed explanations: E0203, E0658.
+For more information about an error, try `rustc --explain E0203`.
diff --git a/tests/ui/maybe-bounds.stderr b/tests/ui/maybe-bounds.stderr
index 1d823b6acb2..230d11fd0ae 100644
--- a/tests/ui/maybe-bounds.stderr
+++ b/tests/ui/maybe-bounds.stderr
@@ -1,22 +1,31 @@
-error: `?Trait` is not permitted in supertraits
+error[E0658]: `?Trait` is not permitted in supertraits
   --> $DIR/maybe-bounds.rs:1:11
    |
 LL | trait Tr: ?Sized {}
    |           ^^^^^^
    |
    = note: traits are `?Sized` by default
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: `?Trait` is not permitted in trait object types
+error[E0658]: `?Trait` is not permitted in trait object types
   --> $DIR/maybe-bounds.rs:4:20
    |
 LL | type A1 = dyn Tr + (?Sized);
    |                    ^^^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: `?Trait` is not permitted in trait object types
+error[E0658]: `?Trait` is not permitted in trait object types
   --> $DIR/maybe-bounds.rs:6:28
    |
 LL | type A2 = dyn for<'a> Tr + (?Sized);
    |                            ^^^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error: aborting due to 3 previous errors
 
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/parser/trait-object-trait-parens.stderr b/tests/ui/parser/trait-object-trait-parens.stderr
index 3134746b930..ff32b173d49 100644
--- a/tests/ui/parser/trait-object-trait-parens.stderr
+++ b/tests/ui/parser/trait-object-trait-parens.stderr
@@ -1,20 +1,29 @@
-error: `?Trait` is not permitted in trait object types
+error[E0658]: `?Trait` is not permitted in trait object types
   --> $DIR/trait-object-trait-parens.rs:8:24
    |
 LL |     let _: Box<(Obj) + (?Sized) + (for<'a> Trait<'a>)>;
    |                        ^^^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: `?Trait` is not permitted in trait object types
+error[E0658]: `?Trait` is not permitted in trait object types
   --> $DIR/trait-object-trait-parens.rs:13:16
    |
 LL |     let _: Box<?Sized + (for<'a> Trait<'a>) + (Obj)>;
    |                ^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: `?Trait` is not permitted in trait object types
+error[E0658]: `?Trait` is not permitted in trait object types
   --> $DIR/trait-object-trait-parens.rs:18:44
    |
 LL |     let _: Box<for<'a> Trait<'a> + (Obj) + (?Sized)>;
    |                                            ^^^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 warning: trait objects without an explicit `dyn` are deprecated
   --> $DIR/trait-object-trait-parens.rs:8:16
@@ -91,4 +100,5 @@ LL |     let _: Box<for<'a> Trait<'a> + (Obj) + (?Sized)>;
 
 error: aborting due to 6 previous errors; 3 warnings emitted
 
-For more information about this error, try `rustc --explain E0225`.
+Some errors have detailed explanations: E0225, E0658.
+For more information about an error, try `rustc --explain E0225`.
diff --git a/tests/ui/traits/maybe-polarity-pass.rs b/tests/ui/traits/maybe-polarity-pass.rs
new file mode 100644
index 00000000000..075a0d8dcac
--- /dev/null
+++ b/tests/ui/traits/maybe-polarity-pass.rs
@@ -0,0 +1,27 @@
+//@ check-pass
+
+#![feature(auto_traits)]
+#![feature(more_maybe_bounds)]
+#![feature(negative_impls)]
+
+trait Trait1 {}
+auto trait Trait2 {}
+
+trait Trait3 : ?Trait1 {}
+trait Trait4 where Self: Trait1 {}
+
+fn foo(_: Box<(dyn Trait3 + ?Trait2)>) {}
+fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}
+//~^ WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+
+struct S;
+impl !Trait2 for S {}
+impl Trait1 for S {}
+impl Trait3 for S {}
+
+fn main() {
+    foo(Box::new(S));
+    bar(&S);
+}
diff --git a/tests/ui/traits/maybe-polarity-pass.stderr b/tests/ui/traits/maybe-polarity-pass.stderr
new file mode 100644
index 00000000000..09ed52f5b0d
--- /dev/null
+++ b/tests/ui/traits/maybe-polarity-pass.stderr
@@ -0,0 +1,20 @@
+warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+  --> $DIR/maybe-polarity-pass.rs:14:20
+   |
+LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}
+   |                    ^^^^^^^
+
+warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+  --> $DIR/maybe-polarity-pass.rs:14:30
+   |
+LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}
+   |                              ^^^^^^^
+
+warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+  --> $DIR/maybe-polarity-pass.rs:14:40
+   |
+LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}
+   |                                        ^^^^^^^
+
+warning: 3 warnings emitted
+
diff --git a/tests/ui/traits/maybe-polarity-repeated.rs b/tests/ui/traits/maybe-polarity-repeated.rs
new file mode 100644
index 00000000000..4b5ec83fffa
--- /dev/null
+++ b/tests/ui/traits/maybe-polarity-repeated.rs
@@ -0,0 +1,9 @@
+#![feature(more_maybe_bounds)]
+
+trait Trait {}
+fn foo<T: ?Trait + ?Trait>(_: T) {}
+//~^ ERROR type parameter has more than one relaxed default bound, only one is supported
+//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+
+fn main() {}
diff --git a/tests/ui/traits/maybe-polarity-repeated.stderr b/tests/ui/traits/maybe-polarity-repeated.stderr
new file mode 100644
index 00000000000..610c484fbec
--- /dev/null
+++ b/tests/ui/traits/maybe-polarity-repeated.stderr
@@ -0,0 +1,21 @@
+error[E0203]: type parameter has more than one relaxed default bound, only one is supported
+  --> $DIR/maybe-polarity-repeated.rs:4:11
+   |
+LL | fn foo<T: ?Trait + ?Trait>(_: T) {}
+   |           ^^^^^^   ^^^^^^
+
+warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+  --> $DIR/maybe-polarity-repeated.rs:4:11
+   |
+LL | fn foo<T: ?Trait + ?Trait>(_: T) {}
+   |           ^^^^^^
+
+warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+  --> $DIR/maybe-polarity-repeated.rs:4:20
+   |
+LL | fn foo<T: ?Trait + ?Trait>(_: T) {}
+   |                    ^^^^^^
+
+error: aborting due to 1 previous error; 2 warnings emitted
+
+For more information about this error, try `rustc --explain E0203`.
diff --git a/tests/ui/traits/wf-object/maybe-bound.stderr b/tests/ui/traits/wf-object/maybe-bound.stderr
index 2fe3f0fc39f..be7afabd0d0 100644
--- a/tests/ui/traits/wf-object/maybe-bound.stderr
+++ b/tests/ui/traits/wf-object/maybe-bound.stderr
@@ -1,32 +1,48 @@
-error: `?Trait` is not permitted in trait object types
+error[E0658]: `?Trait` is not permitted in trait object types
   --> $DIR/maybe-bound.rs:5:15
    |
 LL | type _0 = dyn ?Sized + Foo;
    |               ^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: `?Trait` is not permitted in trait object types
+error[E0658]: `?Trait` is not permitted in trait object types
   --> $DIR/maybe-bound.rs:8:21
    |
 LL | type _1 = dyn Foo + ?Sized;
    |                     ^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: `?Trait` is not permitted in trait object types
+error[E0658]: `?Trait` is not permitted in trait object types
   --> $DIR/maybe-bound.rs:11:21
    |
 LL | type _2 = dyn Foo + ?Sized + ?Sized;
    |                     ^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: `?Trait` is not permitted in trait object types
+error[E0658]: `?Trait` is not permitted in trait object types
   --> $DIR/maybe-bound.rs:11:30
    |
 LL | type _2 = dyn Foo + ?Sized + ?Sized;
    |                              ^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: `?Trait` is not permitted in trait object types
+error[E0658]: `?Trait` is not permitted in trait object types
   --> $DIR/maybe-bound.rs:15:15
    |
 LL | type _3 = dyn ?Sized + Foo;
    |               ^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error: aborting due to 5 previous errors
 
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/traits/wf-object/only-maybe-bound.stderr b/tests/ui/traits/wf-object/only-maybe-bound.stderr
index cbc41feec1e..26269476eaa 100644
--- a/tests/ui/traits/wf-object/only-maybe-bound.stderr
+++ b/tests/ui/traits/wf-object/only-maybe-bound.stderr
@@ -1,8 +1,11 @@
-error: `?Trait` is not permitted in trait object types
+error[E0658]: `?Trait` is not permitted in trait object types
   --> $DIR/only-maybe-bound.rs:3:15
    |
 LL | type _0 = dyn ?Sized;
    |               ^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0224]: at least one trait is required for an object type
   --> $DIR/only-maybe-bound.rs:3:11
@@ -12,4 +15,5 @@ LL | type _0 = dyn ?Sized;
 
 error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0224`.
+Some errors have detailed explanations: E0224, E0658.
+For more information about an error, try `rustc --explain E0224`.
diff --git a/tests/ui/unsized/maybe-bounds-where.stderr b/tests/ui/unsized/maybe-bounds-where.stderr
index 683bd387bb2..f7851261667 100644
--- a/tests/ui/unsized/maybe-bounds-where.stderr
+++ b/tests/ui/unsized/maybe-bounds-where.stderr
@@ -1,32 +1,47 @@
-error: `?Trait` bounds are only permitted at the point where a type parameter is declared
+error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared
   --> $DIR/maybe-bounds-where.rs:1:28
    |
 LL | struct S1<T>(T) where (T): ?Sized;
    |                            ^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: `?Trait` bounds are only permitted at the point where a type parameter is declared
+error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared
   --> $DIR/maybe-bounds-where.rs:4:27
    |
 LL | struct S2<T>(T) where u8: ?Sized;
    |                           ^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: `?Trait` bounds are only permitted at the point where a type parameter is declared
+error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared
   --> $DIR/maybe-bounds-where.rs:7:35
    |
 LL | struct S3<T>(T) where &'static T: ?Sized;
    |                                   ^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: `?Trait` bounds are only permitted at the point where a type parameter is declared
+error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared
   --> $DIR/maybe-bounds-where.rs:12:34
    |
 LL | struct S4<T>(T) where for<'a> T: ?Trait<'a>;
    |                                  ^^^^^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: `?Trait` bounds are only permitted at the point where a type parameter is declared
+error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared
   --> $DIR/maybe-bounds-where.rs:21:21
    |
 LL |     fn f() where T: ?Sized {}
    |                     ^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
   --> $DIR/maybe-bounds-where.rs:12:34
@@ -39,6 +54,9 @@ error[E0203]: type parameter has more than one relaxed default bound, only one i
    |
 LL | struct S5<T>(*const T) where T: ?Trait<'static> + ?Sized;
    |                                 ^^^^^^^^^^^^^^^   ^^^^^^
+   |
+   = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
   --> $DIR/maybe-bounds-where.rs:16:33
@@ -48,4 +66,5 @@ LL | struct S5<T>(*const T) where T: ?Trait<'static> + ?Sized;
 
 error: aborting due to 6 previous errors; 2 warnings emitted
 
-For more information about this error, try `rustc --explain E0203`.
+Some errors have detailed explanations: E0203, E0658.
+For more information about an error, try `rustc --explain E0203`.