Skip to content

Commit a55ea23

Browse files
Do not deduplicate list of associated types provided by dyn principal
1 parent 6c0de7f commit a55ea23

16 files changed

+302
-62
lines changed

compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs

+96-28
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
1+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
22
use rustc_errors::codes::*;
33
use rustc_errors::struct_span_code_err;
44
use rustc_hir as hir;
@@ -59,9 +59,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
5959
}
6060
}
6161

62-
let (trait_bounds, mut projection_bounds) =
62+
let (elaborated_trait_bounds, elaborated_projection_bounds) =
6363
traits::expand_trait_aliases(tcx, user_written_bounds.clauses());
64-
let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = trait_bounds
64+
let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = elaborated_trait_bounds
6565
.into_iter()
6666
.partition(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
6767

@@ -102,38 +102,87 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
102102
}
103103
}
104104

105+
// Map the projection bounds onto a key that makes it easy to remove redundant
106+
// bounds that are constrained by supertraits of the principal def id.
107+
//
108+
// Also make sure we detect conflicting bounds from expanding a trait alias and
109+
// also specifying it manually, like:
110+
// ```
111+
// type Alias = Trait<Assoc = i32>;
112+
// let _: &dyn Alias<Assoc = u32> = /* ... */;
113+
// ```
114+
let mut projection_bounds = FxIndexMap::default();
115+
for (proj, proj_span) in elaborated_projection_bounds {
116+
let key = (
117+
proj.skip_binder().projection_term.def_id,
118+
tcx.anonymize_bound_vars(
119+
proj.map_bound(|proj| proj.projection_term.trait_ref(tcx)),
120+
),
121+
);
122+
if let Some((old_proj, old_proj_span)) =
123+
projection_bounds.insert(key, (proj, proj_span))
124+
&& tcx.anonymize_bound_vars(proj) != tcx.anonymize_bound_vars(old_proj)
125+
{
126+
let item = tcx.item_name(proj.item_def_id());
127+
self.dcx()
128+
.struct_span_err(
129+
span,
130+
format!(
131+
"conflicting associated type bounds for `{item}` when \
132+
expanding trait alias"
133+
),
134+
)
135+
.with_span_label(
136+
old_proj_span,
137+
format!("`{item}` is specified to be `{}` here", old_proj.term()),
138+
)
139+
.with_span_label(
140+
proj_span,
141+
format!("`{item}` is specified to be `{}` here", proj.term()),
142+
)
143+
.emit();
144+
}
145+
}
146+
105147
let principal_trait = regular_traits.into_iter().next();
106148

107-
let mut needed_associated_types = FxIndexSet::default();
108-
if let Some((principal_trait, spans)) = &principal_trait {
109-
let pred: ty::Predicate<'tcx> = (*principal_trait).upcast(tcx);
110-
for ClauseWithSupertraitSpan { pred, supertrait_span } in
149+
let mut needed_associated_types = vec![];
150+
if let Some((principal_trait, ref spans)) = principal_trait {
151+
let principal_trait = principal_trait.map_bound(|trait_pred| {
152+
assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive);
153+
trait_pred.trait_ref
154+
});
155+
156+
for ClauseWithSupertraitSpan { clause, supertrait_span } in
111157
traits::elaborate(tcx, [ClauseWithSupertraitSpan::new(
112-
pred,
158+
ty::TraitRef::identity(tcx, principal_trait.def_id()).upcast(tcx),
113159
*spans.last().unwrap(),
114160
)])
115161
.filter_only_self()
116162
{
117-
debug!("observing object predicate `{pred:?}`");
163+
let clause = clause.instantiate_supertrait(tcx, principal_trait);
164+
debug!("observing object predicate `{clause:?}`");
118165

119-
let bound_predicate = pred.kind();
166+
let bound_predicate = clause.kind();
120167
match bound_predicate.skip_binder() {
121-
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
168+
ty::ClauseKind::Trait(pred) => {
122169
// FIXME(negative_bounds): Handle this correctly...
123170
let trait_ref =
124171
tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref));
125172
needed_associated_types.extend(
126-
tcx.associated_items(trait_ref.def_id())
173+
tcx.associated_items(pred.trait_ref.def_id)
127174
.in_definition_order()
175+
// We only care about associated types.
128176
.filter(|item| item.kind == ty::AssocKind::Type)
177+
// No RPITITs -- even with `async_fn_in_dyn_trait`, they are implicit.
129178
.filter(|item| !item.is_impl_trait_in_trait())
130179
// If the associated type has a `where Self: Sized` bound,
131180
// we do not need to constrain the associated type.
132181
.filter(|item| !tcx.generics_require_sized_self(item.def_id))
133182
.map(|item| (item.def_id, trait_ref)),
134183
);
135184
}
136-
ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
185+
ty::ClauseKind::Projection(pred) => {
137186
let pred = bound_predicate.rebind(pred);
138187
// A `Self` within the original bound will be instantiated with a
139188
// `trait_object_dummy_self`, so check for that.
@@ -161,8 +210,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
161210
// `dyn MyTrait<MyOutput = X, Output = X>`, which is uglier but works. See
162211
// the discussion in #56288 for alternatives.
163212
if !references_self {
164-
// Include projections defined on supertraits.
165-
projection_bounds.push((pred, supertrait_span));
213+
let key = (
214+
pred.skip_binder().projection_term.def_id,
215+
tcx.anonymize_bound_vars(
216+
pred.map_bound(|proj| proj.projection_term.trait_ref(tcx)),
217+
),
218+
);
219+
if !projection_bounds.contains_key(&key) {
220+
projection_bounds.insert(key, (pred, supertrait_span));
221+
}
166222
}
167223

168224
self.check_elaborated_projection_mentions_input_lifetimes(
@@ -182,12 +238,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
182238
// types that we expect to be provided by the user, so the following loop
183239
// removes all the associated types that have a corresponding `Projection`
184240
// clause, either from expanding trait aliases or written by the user.
185-
for &(projection_bound, span) in &projection_bounds {
241+
for &(projection_bound, span) in projection_bounds.values() {
186242
let def_id = projection_bound.item_def_id();
187-
let trait_ref = tcx.anonymize_bound_vars(
188-
projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)),
189-
);
190-
needed_associated_types.swap_remove(&(def_id, trait_ref));
191243
if tcx.generics_require_sized_self(def_id) {
192244
tcx.emit_node_span_lint(
193245
UNUSED_ASSOCIATED_TYPE_BOUNDS,
@@ -198,9 +250,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
198250
}
199251
}
200252

253+
let mut missing_assoc_types = FxIndexSet::default();
254+
let projection_bounds: Vec<_> = needed_associated_types
255+
.into_iter()
256+
.filter_map(|key| {
257+
if let Some(assoc) = projection_bounds.get(&key) {
258+
Some(*assoc)
259+
} else {
260+
missing_assoc_types.insert(key);
261+
None
262+
}
263+
})
264+
.collect();
265+
201266
if let Err(guar) = self.check_for_required_assoc_tys(
202267
principal_trait.as_ref().map_or(smallvec![], |(_, spans)| spans.clone()),
203-
needed_associated_types,
268+
missing_assoc_types,
204269
potential_assoc_types,
205270
hir_bounds,
206271
) {
@@ -266,7 +331,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
266331
})
267332
});
268333

269-
let existential_projections = projection_bounds.iter().map(|(bound, _)| {
334+
let existential_projections = projection_bounds.into_iter().map(|(bound, _)| {
270335
bound.map_bound(|mut b| {
271336
assert_eq!(b.projection_term.self_ty(), dummy_self);
272337

@@ -291,12 +356,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
291356
})
292357
});
293358

294-
let auto_trait_predicates = auto_traits.into_iter().map(|(trait_pred, _)| {
295-
assert_eq!(trait_pred.polarity(), ty::PredicatePolarity::Positive);
296-
assert_eq!(trait_pred.self_ty().skip_binder(), dummy_self);
359+
let mut auto_trait_predicates: Vec<_> = auto_traits
360+
.into_iter()
361+
.map(|(trait_pred, _)| {
362+
assert_eq!(trait_pred.polarity(), ty::PredicatePolarity::Positive);
363+
assert_eq!(trait_pred.self_ty().skip_binder(), dummy_self);
297364

298-
ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_pred.def_id()))
299-
});
365+
ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_pred.def_id()))
366+
})
367+
.collect();
368+
auto_trait_predicates.dedup();
300369

301370
// N.b. principal, projections, auto traits
302371
// FIXME: This is actually wrong with multiple principals in regards to symbol mangling
@@ -306,7 +375,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
306375
.chain(auto_trait_predicates)
307376
.collect::<SmallVec<[_; 8]>>();
308377
v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
309-
v.dedup();
310378
let existential_predicates = tcx.mk_poly_existential_predicates(&v);
311379

312380
// Use explicitly-specified region bound, unless the bound is missing.

compiler/rustc_middle/src/ty/relate.rs

+4-14
Original file line numberDiff line numberDiff line change
@@ -79,20 +79,11 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate<
7979
b: Self,
8080
) -> RelateResult<'tcx, Self> {
8181
let tcx = relation.cx();
82-
83-
// FIXME: this is wasteful, but want to do a perf run to see how slow it is.
84-
// We need to perform this deduplication as we sometimes generate duplicate projections
85-
// in `a`.
86-
let mut a_v: Vec<_> = a.into_iter().collect();
87-
let mut b_v: Vec<_> = b.into_iter().collect();
88-
a_v.dedup();
89-
b_v.dedup();
90-
if a_v.len() != b_v.len() {
82+
if a.len() != b.len() {
9183
return Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b)));
9284
}
93-
94-
let v = iter::zip(a_v, b_v).map(|(ep_a, ep_b)| {
95-
match (ep_a.skip_binder(), ep_b.skip_binder()) {
85+
let v =
86+
iter::zip(a, b).map(|(ep_a, ep_b)| match (ep_a.skip_binder(), ep_b.skip_binder()) {
9687
(ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => {
9788
Ok(ep_a.rebind(ty::ExistentialPredicate::Trait(
9889
relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(),
@@ -109,8 +100,7 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate<
109100
ty::ExistentialPredicate::AutoTrait(b),
110101
) if a == b => Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))),
111102
_ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))),
112-
}
113-
});
103+
});
114104
tcx.mk_poly_existential_predicates_from_iter(v)
115105
}
116106
}

compiler/rustc_type_ir/src/elaborate.rs

+6-9
Original file line numberDiff line numberDiff line change
@@ -44,25 +44,22 @@ pub trait Elaboratable<I: Interner> {
4444
}
4545

4646
pub struct ClauseWithSupertraitSpan<I: Interner> {
47-
pub pred: I::Predicate,
47+
pub clause: I::Clause,
4848
// Span of the supertrait predicatae that lead to this clause.
4949
pub supertrait_span: I::Span,
5050
}
5151
impl<I: Interner> ClauseWithSupertraitSpan<I> {
52-
pub fn new(pred: I::Predicate, span: I::Span) -> Self {
53-
ClauseWithSupertraitSpan { pred, supertrait_span: span }
52+
pub fn new(clause: I::Clause, span: I::Span) -> Self {
53+
ClauseWithSupertraitSpan { clause, supertrait_span: span }
5454
}
5555
}
5656
impl<I: Interner> Elaboratable<I> for ClauseWithSupertraitSpan<I> {
5757
fn predicate(&self) -> <I as Interner>::Predicate {
58-
self.pred
58+
self.clause.as_predicate()
5959
}
6060

6161
fn child(&self, clause: <I as Interner>::Clause) -> Self {
62-
ClauseWithSupertraitSpan {
63-
pred: clause.as_predicate(),
64-
supertrait_span: self.supertrait_span,
65-
}
62+
ClauseWithSupertraitSpan { clause, supertrait_span: self.supertrait_span }
6663
}
6764

6865
fn child_with_derived_cause(
@@ -72,7 +69,7 @@ impl<I: Interner> Elaboratable<I> for ClauseWithSupertraitSpan<I> {
7269
_parent_trait_pred: crate::Binder<I, crate::TraitPredicate<I>>,
7370
_index: usize,
7471
) -> Self {
75-
ClauseWithSupertraitSpan { pred: clause.as_predicate(), supertrait_span }
72+
ClauseWithSupertraitSpan { clause, supertrait_span }
7673
}
7774
}
7875

tests/ui/associated-types/associated-types-overridden-binding-2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ trait I32Iterator = Iterator<Item = i32>;
44

55
fn main() {
66
let _: &dyn I32Iterator<Item = u32> = &vec![42].into_iter();
7-
//~^ ERROR expected `IntoIter<u32>` to be an iterator that yields `i32`, but it yields `u32`
7+
//~^ ERROR conflicting associated type bounds
88
}

tests/ui/associated-types/associated-types-overridden-binding-2.stderr

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
error[E0271]: expected `IntoIter<u32>` to be an iterator that yields `i32`, but it yields `u32`
2-
--> $DIR/associated-types-overridden-binding-2.rs:6:43
1+
error: conflicting associated type bounds for `Item` when expanding trait alias
2+
--> $DIR/associated-types-overridden-binding-2.rs:6:13
33
|
4+
LL | trait I32Iterator = Iterator<Item = i32>;
5+
| ---------- `Item` is specified to be `i32` here
6+
...
47
LL | let _: &dyn I32Iterator<Item = u32> = &vec![42].into_iter();
5-
| ^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32`
6-
|
7-
= note: required for the cast from `&std::vec::IntoIter<u32>` to `&dyn Iterator<Item = u32, Item = i32>`
8+
| ^^^^^^^^^^^^^^^^----------^
9+
| |
10+
| `Item` is specified to be `u32` here
811

912
error: aborting due to 1 previous error
1013

11-
For more information about this error, try `rustc --explain E0271`.

tests/ui/associated-types/associated-types-overridden-binding.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ trait U32Iterator = I32Iterator<Item = u32>; //~ ERROR type annotations needed
88

99
fn main() {
1010
let _: &dyn I32Iterator<Item = u32>;
11+
//~^ ERROR conflicting associated type bounds
1112
}

tests/ui/associated-types/associated-types-overridden-binding.stderr

+12-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ note: required by a bound in `I32Iterator`
2222
LL | trait I32Iterator = Iterator<Item = i32>;
2323
| ^^^^^^^^^^ required by this bound in `I32Iterator`
2424

25-
error: aborting due to 2 previous errors
25+
error: conflicting associated type bounds for `Item` when expanding trait alias
26+
--> $DIR/associated-types-overridden-binding.rs:10:13
27+
|
28+
LL | trait I32Iterator = Iterator<Item = i32>;
29+
| ---------- `Item` is specified to be `i32` here
30+
...
31+
LL | let _: &dyn I32Iterator<Item = u32>;
32+
| ^^^^^^^^^^^^^^^^----------^
33+
| |
34+
| `Item` is specified to be `u32` here
35+
36+
error: aborting due to 3 previous errors
2637

2738
For more information about this error, try `rustc --explain E0284`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ check-pass
2+
3+
trait Foo: Fn(Bar) {}
4+
impl<T> Foo for T where T: Fn(Bar) {}
5+
6+
struct Bar;
7+
impl Bar {
8+
fn bar(&self) {}
9+
}
10+
11+
fn main() {
12+
let x: &dyn Foo = &|x| {
13+
x.bar();
14+
};
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//@ check-pass
2+
3+
trait Sup<T> {
4+
type Assoc;
5+
}
6+
7+
impl<T> Sup<T> for () {
8+
type Assoc = T;
9+
}
10+
11+
trait Trait<A, B>: Sup<A, Assoc = A> + Sup<B, Assoc = B> {}
12+
13+
impl<T, U> Trait<T, U> for () {}
14+
15+
fn main() {
16+
let x: &dyn Trait<(), _> = &();
17+
let y: &dyn Trait<_, ()> = x;
18+
}

0 commit comments

Comments
 (0)