about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorMatthias Krüger <476013+matthiaskrgr@users.noreply.github.com>2025-10-01 18:42:34 +0200
committerGitHub <noreply@github.com>2025-10-01 18:42:34 +0200
commit15b7792a65a5f50d22f77c02cff75f8015c48bc8 (patch)
treefecf7a676803cc87c864fbc49faf120f7e9e195c /compiler
parent1e1a39441bd11aba541a48ba714d939490fc7b85 (diff)
parent9f667cdd243d905848ccfc031f4c1373828c9a7d (diff)
downloadrust-15b7792a65a5f50d22f77c02cff75f8015c48bc8.tar.gz
rust-15b7792a65a5f50d22f77c02cff75f8015c48bc8.zip
Rollup merge of #146593 - Jules-Bertholet:restrict-e0719, r=BoxyUwU
Allow specifying multiple bounds for same associated item, except in trait objects

Supersedes https://github.com/rust-lang/rust/pull/143146, fixes https://github.com/rust-lang/rust/issues/143143.

This PR proposes to stop enforcing E0719 in all contexts other than trait object types.

E0719 forbids constraining the same associated item twice within the same angle-bracket delimited associated item bound list (the `…` inside `T: Trait<…>`). For example, the following are forbidden:

| Forbidden                                  | Working alternative                                                |
|--------------------------------------------|--------------------------------------------------------------------|
| `T: Trait<Gat<u32> = u32, Gat<u64> = u64>` | `T: Trait<Gat<u32> = u32> + Trait<Gat<u64> = u64>`                 |
| `T: Iterator<Item = u32, Item = i32>`      | `T: Iterator<Item = u32> + Iterator<Item = i32>` (trivially false) |
| `T: Iterator<Item = u32, Item = u32>`      | `T: Iterator<Item = u32>`                                          |
| `T: Iterator<Item: Send, Item: Sync>`      | `T: Iterator<Item: Send + Sync>`                                   |
| `T: Trait<ASSOC = 3, ASSOC = 4>`           | `T: Trait<ASSOC = 3> + Trait<ASSOC = 4>` (trivially false)         |
| `T: Trait<ASSOC = 3, ASSOC = 3>`           | `T: Trait<ASSOC = 3>`                                              |

With this PR, all those previously forbidden examples would start working, as well as their APIT and RPIT equivalents.

Types like `dyn Iterator<Item = u32, Item = u32>` will continue to be rejected, however. See https://github.com/rust-lang/rust/pull/143146#issuecomment-3274421752 for the reason why.

```@rustbot``` label T-lang T-types needs-fcp
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0719.md14
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs20
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs18
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs32
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs18
6 files changed, 77 insertions, 35 deletions
diff --git a/compiler/rustc_error_codes/src/error_codes/E0719.md b/compiler/rustc_error_codes/src/error_codes/E0719.md
index cd981db1058..6aec38b42a3 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0719.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0719.md
@@ -1,4 +1,4 @@
-An associated type value was specified more than once.
+An associated item was specified more than once in a trait object.
 
 Erroneous code example:
 
@@ -7,21 +7,15 @@ trait FooTrait {}
 trait BarTrait {}
 
 // error: associated type `Item` in trait `Iterator` is specified twice
-struct Foo<T: Iterator<Item: FooTrait, Item: BarTrait>> { f: T }
+type Foo = dyn Iterator<Item = u32, Item = u32>;
 ```
 
-`Item` in trait `Iterator` cannot be specified multiple times for struct `Foo`.
-To fix this, create a new trait that is a combination of the desired traits and
-specify the associated type with the new trait.
+To fix this, remove the duplicate specifier:
 
 Corrected example:
 
 ```
-trait FooTrait {}
-trait BarTrait {}
-trait FooBarTrait: FooTrait + BarTrait {}
-
-struct Foo<T: Iterator<Item: FooBarTrait>> { f: T } // ok!
+type Foo = dyn Iterator<Item = u32>; // ok!
 ```
 
 For more information about associated types, see [the book][bk-at]. For more
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index ba54fa8cc0d..9841fafc82c 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -12,7 +12,7 @@ use tracing::{debug, instrument};
 
 use super::ItemCtxt;
 use super::predicates_of::assert_only_contains_predicates_from;
-use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter};
+use crate::hir_ty_lowering::{HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter};
 
 /// For associated types we include both bounds written on the type
 /// (`type X: Trait`) and predicates from the trait: `where Self::X: Trait`.
@@ -37,7 +37,14 @@ fn associated_type_bounds<'tcx>(
 
         let icx = ItemCtxt::new(tcx, assoc_item_def_id);
         let mut bounds = Vec::new();
-        icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
+        icx.lowerer().lower_bounds(
+            item_ty,
+            hir_bounds,
+            &mut bounds,
+            ty::List::empty(),
+            filter,
+            OverlappingAsssocItemConstraints::Allowed,
+        );
 
         match filter {
             PredicateFilter::All
@@ -347,7 +354,14 @@ fn opaque_type_bounds<'tcx>(
     ty::print::with_reduced_queries!({
         let icx = ItemCtxt::new(tcx, opaque_def_id);
         let mut bounds = Vec::new();
-        icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
+        icx.lowerer().lower_bounds(
+            item_ty,
+            hir_bounds,
+            &mut bounds,
+            ty::List::empty(),
+            filter,
+            OverlappingAsssocItemConstraints::Allowed,
+        );
         // Implicit bounds are added to opaque types unless a `?Trait` bound is found
         match filter {
             PredicateFilter::All
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index dd3590f9ac5..ffdf2a2f4c0 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -18,7 +18,9 @@ use super::item_bounds::explicit_item_bounds_with_filter;
 use crate::collect::ItemCtxt;
 use crate::constrained_generic_params as cgp;
 use crate::delegation::inherit_predicates_for_delegation_item;
-use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter, RegionInferReason};
+use crate::hir_ty_lowering::{
+    HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason,
+};
 
 /// Returns a list of all type predicates (explicit and implicit) for the definition with
 /// ID `def_id`. This includes all predicates returned by `explicit_predicates_of`, plus
@@ -187,6 +189,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
             &mut bounds,
             ty::List::empty(),
             PredicateFilter::All,
+            OverlappingAsssocItemConstraints::Allowed,
         );
         icx.lowerer().add_sizedness_bounds(
             &mut bounds,
@@ -289,6 +292,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                     &mut bounds,
                     bound_vars,
                     PredicateFilter::All,
+                    OverlappingAsssocItemConstraints::Allowed,
                 );
                 predicates.extend(bounds);
             }
@@ -659,7 +663,14 @@ pub(super) fn implied_predicates_with_filter<'tcx>(
 
     let self_param_ty = tcx.types.self_param;
     let mut bounds = Vec::new();
-    icx.lowerer().lower_bounds(self_param_ty, superbounds, &mut bounds, ty::List::empty(), filter);
+    icx.lowerer().lower_bounds(
+        self_param_ty,
+        superbounds,
+        &mut bounds,
+        ty::List::empty(),
+        filter,
+        OverlappingAsssocItemConstraints::Allowed,
+    );
     match filter {
         PredicateFilter::All
         | PredicateFilter::SelfOnly
@@ -984,6 +995,7 @@ impl<'tcx> ItemCtxt<'tcx> {
                 &mut bounds,
                 bound_vars,
                 filter,
+                OverlappingAsssocItemConstraints::Allowed,
             );
         }
 
@@ -1063,6 +1075,7 @@ pub(super) fn const_conditions<'tcx>(
                     &mut bounds,
                     bound_vars,
                     PredicateFilter::ConstIfConst,
+                    OverlappingAsssocItemConstraints::Allowed,
                 );
             }
             _ => {}
@@ -1083,6 +1096,7 @@ pub(super) fn const_conditions<'tcx>(
             &mut bounds,
             ty::List::empty(),
             PredicateFilter::ConstIfConst,
+            OverlappingAsssocItemConstraints::Allowed,
         );
     }
 
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 99dc8e6e522..8682fdc5494 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -21,7 +21,8 @@ use tracing::{debug, instrument};
 use super::errors::GenericsArgsErrExtend;
 use crate::errors;
 use crate::hir_ty_lowering::{
-    AssocItemQSelf, FeedConstTy, HirTyLowerer, PredicateFilter, RegionInferReason,
+    AssocItemQSelf, FeedConstTy, HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter,
+    RegionInferReason,
 };
 
 #[derive(Debug, Default)]
@@ -338,6 +339,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
         bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
         predicate_filter: PredicateFilter,
+        overlapping_assoc_constraints: OverlappingAsssocItemConstraints,
     ) where
         'tcx: 'hir,
     {
@@ -362,6 +364,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         param_ty,
                         bounds,
                         predicate_filter,
+                        overlapping_assoc_constraints,
                     );
                 }
                 hir::GenericBound::Outlives(lifetime) => {
@@ -402,7 +405,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         trait_ref: ty::PolyTraitRef<'tcx>,
         constraint: &hir::AssocItemConstraint<'tcx>,
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
-        duplicates: &mut FxIndexMap<DefId, Span>,
+        duplicates: Option<&mut FxIndexMap<DefId, Span>>,
         path_span: Span,
         predicate_filter: PredicateFilter,
     ) -> Result<(), ErrorGuaranteed> {
@@ -458,17 +461,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             )
             .expect("failed to find associated item");
 
-        duplicates
-            .entry(assoc_item.def_id)
-            .and_modify(|prev_span| {
-                self.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
-                    span: constraint.span,
-                    prev_span: *prev_span,
-                    item_name: constraint.ident,
-                    def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
-                });
-            })
-            .or_insert(constraint.span);
+        if let Some(duplicates) = duplicates {
+            duplicates
+                .entry(assoc_item.def_id)
+                .and_modify(|prev_span| {
+                    self.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
+                        span: constraint.span,
+                        prev_span: *prev_span,
+                        item_name: constraint.ident,
+                        def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
+                    });
+                })
+                .or_insert(constraint.span);
+        }
 
         let projection_term = if let ty::AssocTag::Fn = assoc_tag {
             let bound_vars = tcx.late_bound_vars(constraint.hir_id);
@@ -600,6 +605,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                             bounds,
                             projection_ty.bound_vars(),
                             predicate_filter,
+                            OverlappingAsssocItemConstraints::Allowed,
                         );
                     }
                     PredicateFilter::SelfOnly
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs
index c248cd7fec2..c0b13773089 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs
@@ -23,7 +23,9 @@ use tracing::{debug, instrument};
 
 use super::HirTyLowerer;
 use crate::errors::SelfInTypeAlias;
-use crate::hir_ty_lowering::{GenericArgCountMismatch, PredicateFilter, RegionInferReason};
+use crate::hir_ty_lowering::{
+    GenericArgCountMismatch, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason,
+};
 
 impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     /// Lower a trait object type from the HIR to our internal notion of a type.
@@ -60,6 +62,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 dummy_self,
                 &mut user_written_bounds,
                 PredicateFilter::SelfOnly,
+                OverlappingAsssocItemConstraints::Forbidden,
             );
             if let Err(GenericArgCountMismatch { invalid_args, .. }) = result.correct {
                 potential_assoc_types.extend(invalid_args);
@@ -157,10 +160,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 self.dcx()
                     .struct_span_err(
                         span,
-                        format!(
-                            "conflicting associated type bounds for `{item}` when \
-                            expanding trait alias"
-                        ),
+                        format!("conflicting associated type bounds for `{item}`"),
                     )
                     .with_span_label(
                         old_proj_span,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 9b198d04454..eb660804c2b 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -332,6 +332,15 @@ pub(crate) enum GenericArgPosition {
     MethodCall,
 }
 
+/// Whether to allow duplicate associated iten constraints in a trait ref, e.g.
+/// `Trait<Assoc = Ty, Assoc = Ty>`. This is forbidden in `dyn Trait<...>`
+/// but allowed everywhere else.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub(crate) enum OverlappingAsssocItemConstraints {
+    Allowed,
+    Forbidden,
+}
+
 /// A marker denoting that the generic arguments that were
 /// provided did not match the respective generic parameters.
 #[derive(Clone, Debug)]
@@ -752,6 +761,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         self_ty: Ty<'tcx>,
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
         predicate_filter: PredicateFilter,
+        overlapping_assoc_item_constraints: OverlappingAsssocItemConstraints,
     ) -> GenericArgCountResult {
         let tcx = self.tcx();
 
@@ -908,7 +918,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
         }
 
-        let mut dup_constraints = FxIndexMap::default();
+        let mut dup_constraints = (overlapping_assoc_item_constraints
+            == OverlappingAsssocItemConstraints::Forbidden)
+            .then_some(FxIndexMap::default());
+
         for constraint in trait_segment.args().constraints {
             // Don't register any associated item constraints for negative bounds,
             // since we should have emitted an error for them earlier, and they
@@ -927,7 +940,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 poly_trait_ref,
                 constraint,
                 bounds,
-                &mut dup_constraints,
+                dup_constraints.as_mut(),
                 constraint.span,
                 predicate_filter,
             );
@@ -2484,6 +2497,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     &mut bounds,
                     ty::List::empty(),
                     PredicateFilter::All,
+                    OverlappingAsssocItemConstraints::Allowed,
                 );
                 self.add_sizedness_bounds(
                     &mut bounds,