Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2101,24 +2101,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
{
return;
}
if self.tcx.features().more_maybe_bounds() {
return;
}
}
RelaxedBoundPolicy::Forbidden(reason) => {
if self.tcx.features().more_maybe_bounds() {
return;
}

match reason {
RelaxedBoundForbiddenReason::TraitObjectTy => {
if self.tcx.features().more_maybe_bounds() {
return;
}

self.dcx().span_err(
span,
"relaxed bounds are not permitted in trait object types",
);
return;
}
RelaxedBoundForbiddenReason::SuperTrait => {
if self.tcx.features().more_maybe_bounds() {
return;
}

let mut diag = self.dcx().struct_span_err(
span,
"relaxed bounds are not permitted in supertrait bounds",
Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_hir_analysis/src/collect/predicates_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
}
};

if let Node::TraitItem(item) = node {
let mut bounds = Vec::new();
icx.lowerer().add_default_trait_item_bounds(item, &mut bounds);
predicates.extend(bounds);
}

let generics = tcx.generics_of(def_id);

// Below we'll consider the bounds on the type parameters (including `Self`)
Expand Down
142 changes: 13 additions & 129 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use std::assert_matches::assert_matches;
use std::ops::ControlFlow;

use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
use rustc_errors::struct_span_code_err;
use rustc_hir as hir;
use rustc_hir::PolyTraitRef;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_hir::{AmbigArg, PolyTraitRef};
use rustc_middle::bug;
use rustc_middle::ty::{
self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
Expand Down Expand Up @@ -230,122 +231,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}

/// Checks whether `Self: DefaultAutoTrait` bounds should be added on trait super bounds
/// or associated items.
///
/// To keep backward compatibility with existing code, `experimental_default_bounds` bounds
/// should be added everywhere, including super bounds. However this causes a huge performance
/// costs. For optimization purposes instead of adding default supertraits, bounds
/// are added to the associated items:
///
/// ```ignore(illustrative)
/// // Default bounds are generated in the following way:
/// trait Trait {
/// fn foo(&self) where Self: Leak {}
/// }
///
/// // instead of this:
/// trait Trait: Leak {
/// fn foo(&self) {}
/// }
/// ```
/// It is not always possible to do this because of backward compatibility:
///
/// ```ignore(illustrative)
/// pub trait Trait<Rhs = Self> {}
/// pub trait Trait1 : Trait {}
/// //~^ ERROR: `Rhs` requires `DefaultAutoTrait`, but `Self` is not `DefaultAutoTrait`
/// ```
///
/// or:
///
/// ```ignore(illustrative)
/// trait Trait {
/// type Type where Self: Sized;
/// }
/// trait Trait2<T> : Trait<Type = T> {}
/// //~^ ERROR: `DefaultAutoTrait` required for `Trait2`, by implicit `Self: DefaultAutoTrait` in `Trait::Type`
/// ```
///
/// Therefore, `experimental_default_bounds` are still being added to supertraits if
/// the `SelfTyParam` or `AssocItemConstraint` were found in a trait header.
fn requires_default_supertraits(
&self,
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
hir_generics: &'tcx hir::Generics<'tcx>,
) -> bool {
struct TraitInfoCollector;

impl<'tcx> hir::intravisit::Visitor<'tcx> for TraitInfoCollector {
type Result = ControlFlow<()>;

fn visit_assoc_item_constraint(
&mut self,
_constraint: &'tcx hir::AssocItemConstraint<'tcx>,
) -> Self::Result {
ControlFlow::Break(())
}

fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
if matches!(
&t.kind,
hir::TyKind::Path(hir::QPath::Resolved(
_,
hir::Path { res: hir::def::Res::SelfTyParam { .. }, .. },
))
) {
return ControlFlow::Break(());
}
hir::intravisit::walk_ty(self, t)
}
}

let mut found = false;
for bound in hir_bounds {
found |= hir::intravisit::walk_param_bound(&mut TraitInfoCollector, bound).is_break();
}
found |= hir::intravisit::walk_generics(&mut TraitInfoCollector, hir_generics).is_break();
found
}

/// Implicitly add `Self: DefaultAutoTrait` clauses on trait associated items if
/// they are not added as super trait bounds to the trait itself. See
/// `requires_default_supertraits` for more information.
pub(crate) fn add_default_trait_item_bounds(
&self,
trait_item: &hir::TraitItem<'tcx>,
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
) {
let tcx = self.tcx();
if !tcx.sess.opts.unstable_opts.experimental_default_bounds {
return;
}

let parent = tcx.local_parent(trait_item.hir_id().owner.def_id);
let hir::Node::Item(parent_trait) = tcx.hir_node_by_def_id(parent) else {
unreachable!();
};

let (trait_generics, trait_bounds) = match parent_trait.kind {
hir::ItemKind::Trait(_, _, _, _, generics, supertraits, _) => (generics, supertraits),
hir::ItemKind::TraitAlias(_, generics, supertraits) => (generics, supertraits),
_ => unreachable!(),
};

if !self.requires_default_supertraits(trait_bounds, trait_generics) {
let self_ty_where_predicates = (parent, trait_item.generics.predicates);
self.add_default_traits(
bounds,
tcx.types.self_param,
&[],
Some(self_ty_where_predicates),
trait_item.span,
);
}
}

/// Lazily sets `experimental_default_bounds` to true on trait super bounds.
/// See `requires_default_supertraits` for more information.
/// Sets `experimental_default_bounds` to true on trait super bounds.
pub(crate) fn add_default_super_traits(
&self,
trait_def_id: LocalDefId,
Expand All @@ -354,21 +240,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
hir_generics: &'tcx hir::Generics<'tcx>,
span: Span,
) {
if !self.tcx().sess.opts.unstable_opts.experimental_default_bounds {
assert_matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias);

if self.tcx().trait_is_auto(trait_def_id.to_def_id()) {
return;
}

assert!(matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias));
if self.requires_default_supertraits(hir_bounds, hir_generics) {
let self_ty_where_predicates = (trait_def_id, hir_generics.predicates);
self.add_default_traits(
bounds,
self.tcx().types.self_param,
hir_bounds,
Some(self_ty_where_predicates),
span,
);
}
self.add_default_traits(
bounds,
self.tcx().types.self_param,
hir_bounds,
Some((trait_def_id, hir_generics.predicates)),
span,
);
}

pub(crate) fn add_default_traits(
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable {
.explicit_super_predicates_of(def_id)
.iter_identity_copied()
.filter_map(|(pred, _)| pred.as_trait_clause())
.filter(|pred| !cx.tcx.is_lang_item(pred.def_id(), hir::LangItem::MetaSized));
.filter(|pred| !cx.tcx.is_lang_item(pred.def_id(), hir::LangItem::MetaSized))
.filter(|pred| !cx.tcx.is_default_trait(pred.def_id()));
if direct_super_traits_iter.count() > 1 {
cx.emit_span_lint(
MULTIPLE_SUPERTRAIT_UPCASTABLE,
Expand Down
14 changes: 14 additions & 0 deletions tests/ui/trait-bounds/more_maybe_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}

// FIXME: `?Trait1` should be rejected, `Trait1` isn't marked `#[lang = "default_traitN"]`.
fn baz<T>() where T: Iterator<Item: ?Trait1> {}
//~^ ERROR this relaxed bound is not permitted here
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the above FIXME

Copy link
Member

@fmease fmease Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moreover this test is quite a mixed bag, it's unclear what the test intentions are. In #142693 I almost deleted it for that reason but I kept it to track bugs pertaining to more_maybe_bounds instead (as can be seen by the top-level FIXME).

I don't think we should test "this relaxed bound is not permitted here" in this file, I'd advise you to create a new one whose name is based on tests/ui/unsized/relaxed-bounds-invalid-places.rs (obv not inside unsized/; maybe have both test files in trait-bounds/ actually).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my understanding there are 2 checks for relaxed bounds consistency:

  • Syntactic check during AST->HIR lowering (validate_relaxed_bound) to ensure that ?Trait bounds are in a right place.
  • Semantic check during HIR->ty lowering (check_and_report_invalid_relaxed_bounds) to ensure that ? modifier is applied to "right" traits. And this check has to be done regardless of the place and results of the first check.

This PR fixes the first check but not the second and therefore FIXME comments in this file are still relevant.

So, your suggestion is to keep these checks in separate files for readability reasons, right? In my opinion it isn't very useful because the invocation of second check implies that the first check has already been invoked. That is, the test that covers the second check also covers the first one.


struct S1<T>(T);

impl<T> S1<T> {
fn f() where T: ?Trait1 {}
//~^ ERROR this relaxed bound is not permitted here
}

trait Trait5<'a> {}

struct S2<T>(T) where for<'a> T: ?Trait5<'a>;
//~^ ERROR this relaxed bound is not permitted here
//~| ERROR bound modifier `?` can only be applied to default traits like `Sized`

struct S;
impl !Trait2 for S {}
Expand Down
32 changes: 31 additions & 1 deletion tests/ui/trait-bounds/more_maybe_bounds.stderr
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
error: this relaxed bound is not permitted here
--> $DIR/more_maybe_bounds.rs:23:37
|
LL | fn baz<T>() where T: Iterator<Item: ?Trait1> {}
| ^^^^^^^
|
= note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item

error: this relaxed bound is not permitted here
--> $DIR/more_maybe_bounds.rs:29:21
|
LL | fn f() where T: ?Trait1 {}
| ^^^^^^^
|
= note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item

error: this relaxed bound is not permitted here
--> $DIR/more_maybe_bounds.rs:35:34
|
LL | struct S2<T>(T) where for<'a> T: ?Trait5<'a>;
| ^^^^^^^^^^^
|
= note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item

error: bound modifier `?` can only be applied to default traits like `Sized`
--> $DIR/more_maybe_bounds.rs:17:20
|
Expand All @@ -16,5 +40,11 @@ error: bound modifier `?` can only be applied to default traits like `Sized`
LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}
| ^^^^^^^

error: aborting due to 3 previous errors
error: bound modifier `?` can only be applied to default traits like `Sized`
--> $DIR/more_maybe_bounds.rs:35:34
|
LL | struct S2<T>(T) where for<'a> T: ?Trait5<'a>;
| ^^^^^^^^^^^

error: aborting due to 7 previous errors

This file was deleted.

Loading
Loading