Skip to content

Commit 6dfd8ae

Browse files
committed
Auto merge of #13192 - lowr:fix/dyn-sort-all-bounds, r=Veykril
fix: sort all bounds on trait object types Fixes #13181 #12793 allowed different ordering of trait bounds in trait object types but failed to account for the ordering of projection bounds. I opted for sorting all the bounds at once rather than splitting them into `SmallVec`s so it's easier to do the same thing for other bounds when we have them.
2 parents 5be2e65 + 265c75c commit 6dfd8ae

File tree

3 files changed

+82
-23
lines changed

3 files changed

+82
-23
lines changed

crates/hir-ty/src/chalk_ext.rs

+2
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ impl TyExt for Ty {
164164

165165
fn dyn_trait(&self) -> Option<TraitId> {
166166
let trait_ref = match self.kind(Interner) {
167+
// The principal trait bound should be the first element of the bounds. This is an
168+
// invariant ensured by `TyLoweringContext::lower_dyn_trait()`.
167169
TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| {
168170
match b.skip_binders() {
169171
WhereClause::Implemented(trait_ref) => Some(trait_ref),

crates/hir-ty/src/lower.rs

+52-23
Original file line numberDiff line numberDiff line change
@@ -981,43 +981,72 @@ impl<'a> TyLoweringContext<'a> {
981981

982982
fn lower_dyn_trait(&self, bounds: &[Interned<TypeBound>]) -> Ty {
983983
let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
984+
// INVARIANT: The principal trait bound must come first. Others may be in any order but
985+
// should be in the same order for the same set but possibly different order of bounds in
986+
// the input.
987+
// This invariant is used by `TyExt::dyn_trait()` and chalk.
984988
let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
985-
let bounds =
986-
bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false));
987-
988-
let mut auto_traits = SmallVec::<[_; 8]>::new();
989-
let mut regular_traits = SmallVec::<[_; 2]>::new();
990-
let mut other_bounds = SmallVec::<[_; 8]>::new();
991-
for bound in bounds {
992-
if let Some(id) = bound.trait_id() {
993-
if ctx.db.trait_data(from_chalk_trait_id(id)).is_auto {
994-
auto_traits.push(bound);
995-
} else {
996-
regular_traits.push(bound);
989+
let mut bounds: Vec<_> = bounds
990+
.iter()
991+
.flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false))
992+
.collect();
993+
994+
let mut multiple_regular_traits = false;
995+
let mut multiple_same_projection = false;
996+
bounds.sort_unstable_by(|lhs, rhs| {
997+
use std::cmp::Ordering;
998+
match (lhs.skip_binders(), rhs.skip_binders()) {
999+
(WhereClause::Implemented(lhs), WhereClause::Implemented(rhs)) => {
1000+
let lhs_id = lhs.trait_id;
1001+
let lhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(lhs_id)).is_auto;
1002+
let rhs_id = rhs.trait_id;
1003+
let rhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(rhs_id)).is_auto;
1004+
1005+
if !lhs_is_auto && !rhs_is_auto {
1006+
multiple_regular_traits = true;
1007+
}
1008+
// Note that the ordering here is important; this ensures the invariant
1009+
// mentioned above.
1010+
(lhs_is_auto, lhs_id).cmp(&(rhs_is_auto, rhs_id))
9971011
}
998-
} else {
999-
other_bounds.push(bound);
1012+
(WhereClause::Implemented(_), _) => Ordering::Less,
1013+
(_, WhereClause::Implemented(_)) => Ordering::Greater,
1014+
(WhereClause::AliasEq(lhs), WhereClause::AliasEq(rhs)) => {
1015+
match (&lhs.alias, &rhs.alias) {
1016+
(AliasTy::Projection(lhs_proj), AliasTy::Projection(rhs_proj)) => {
1017+
// We only compare the `associated_ty_id`s. We shouldn't have
1018+
// multiple bounds for an associated type in the correct Rust code,
1019+
// and if we do, we error out.
1020+
if lhs_proj.associated_ty_id == rhs_proj.associated_ty_id {
1021+
multiple_same_projection = true;
1022+
}
1023+
lhs_proj.associated_ty_id.cmp(&rhs_proj.associated_ty_id)
1024+
}
1025+
// We don't produce `AliasTy::Opaque`s yet.
1026+
_ => unreachable!(),
1027+
}
1028+
}
1029+
// We don't produce `WhereClause::{TypeOutlives, LifetimeOutlives}` yet.
1030+
_ => unreachable!(),
10001031
}
1001-
}
1032+
});
10021033

1003-
if regular_traits.len() > 1 {
1034+
if multiple_regular_traits || multiple_same_projection {
10041035
return None;
10051036
}
10061037

1007-
auto_traits.sort_unstable_by_key(|b| b.trait_id().unwrap());
1008-
auto_traits.dedup();
1038+
// As multiple occurrences of the same auto traits *are* permitted, we dedulicate the
1039+
// bounds. We shouldn't have repeated elements besides auto traits at this point.
1040+
bounds.dedup();
10091041

1010-
Some(QuantifiedWhereClauses::from_iter(
1011-
Interner,
1012-
regular_traits.into_iter().chain(other_bounds).chain(auto_traits),
1013-
))
1042+
Some(QuantifiedWhereClauses::from_iter(Interner, bounds))
10141043
});
10151044

10161045
if let Some(bounds) = bounds {
10171046
let bounds = crate::make_single_type_binders(bounds);
10181047
TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
10191048
} else {
1020-
// FIXME: report error (additional non-auto traits)
1049+
// FIXME: report error (additional non-auto traits or associated type rebound)
10211050
TyKind::Error.intern(Interner)
10221051
}
10231052
}

crates/hir-ty/src/tests/traits.rs

+28
Original file line numberDiff line numberDiff line change
@@ -3900,6 +3900,34 @@ fn g(t: &(dyn Sync + T<Proj = ()> + Send)) {
39003900
);
39013901
}
39023902

3903+
#[test]
3904+
fn dyn_multiple_projection_bounds() {
3905+
check_no_mismatches(
3906+
r#"
3907+
trait Trait {
3908+
type T;
3909+
type U;
3910+
}
3911+
3912+
fn f(t: &dyn Trait<T = (), U = ()>) {}
3913+
fn g(t: &dyn Trait<U = (), T = ()>) {
3914+
f(t);
3915+
}
3916+
"#,
3917+
);
3918+
3919+
check_types(
3920+
r#"
3921+
trait Trait {
3922+
type T;
3923+
}
3924+
3925+
fn f(t: &dyn Trait<T = (), T = ()>) {}
3926+
//^&{unknown}
3927+
"#,
3928+
);
3929+
}
3930+
39033931
#[test]
39043932
fn dyn_duplicate_auto_trait() {
39053933
check_no_mismatches(

0 commit comments

Comments
 (0)