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.
This commit is contained in:
commit
6dfd8aebdf
@ -164,6 +164,8 @@ impl TyExt for Ty {
|
|||||||
|
|
||||||
fn dyn_trait(&self) -> Option<TraitId> {
|
fn dyn_trait(&self) -> Option<TraitId> {
|
||||||
let trait_ref = match self.kind(Interner) {
|
let trait_ref = match self.kind(Interner) {
|
||||||
|
// The principal trait bound should be the first element of the bounds. This is an
|
||||||
|
// invariant ensured by `TyLoweringContext::lower_dyn_trait()`.
|
||||||
TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| {
|
TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| {
|
||||||
match b.skip_binders() {
|
match b.skip_binders() {
|
||||||
WhereClause::Implemented(trait_ref) => Some(trait_ref),
|
WhereClause::Implemented(trait_ref) => Some(trait_ref),
|
||||||
|
@ -981,43 +981,72 @@ impl<'a> TyLoweringContext<'a> {
|
|||||||
|
|
||||||
fn lower_dyn_trait(&self, bounds: &[Interned<TypeBound>]) -> Ty {
|
fn lower_dyn_trait(&self, bounds: &[Interned<TypeBound>]) -> Ty {
|
||||||
let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
|
let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
|
||||||
|
// INVARIANT: The principal trait bound must come first. Others may be in any order but
|
||||||
|
// should be in the same order for the same set but possibly different order of bounds in
|
||||||
|
// the input.
|
||||||
|
// This invariant is used by `TyExt::dyn_trait()` and chalk.
|
||||||
let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
|
let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
|
||||||
let bounds =
|
let mut bounds: Vec<_> = bounds
|
||||||
bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false));
|
.iter()
|
||||||
|
.flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut auto_traits = SmallVec::<[_; 8]>::new();
|
let mut multiple_regular_traits = false;
|
||||||
let mut regular_traits = SmallVec::<[_; 2]>::new();
|
let mut multiple_same_projection = false;
|
||||||
let mut other_bounds = SmallVec::<[_; 8]>::new();
|
bounds.sort_unstable_by(|lhs, rhs| {
|
||||||
for bound in bounds {
|
use std::cmp::Ordering;
|
||||||
if let Some(id) = bound.trait_id() {
|
match (lhs.skip_binders(), rhs.skip_binders()) {
|
||||||
if ctx.db.trait_data(from_chalk_trait_id(id)).is_auto {
|
(WhereClause::Implemented(lhs), WhereClause::Implemented(rhs)) => {
|
||||||
auto_traits.push(bound);
|
let lhs_id = lhs.trait_id;
|
||||||
} else {
|
let lhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(lhs_id)).is_auto;
|
||||||
regular_traits.push(bound);
|
let rhs_id = rhs.trait_id;
|
||||||
|
let rhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(rhs_id)).is_auto;
|
||||||
|
|
||||||
|
if !lhs_is_auto && !rhs_is_auto {
|
||||||
|
multiple_regular_traits = true;
|
||||||
|
}
|
||||||
|
// Note that the ordering here is important; this ensures the invariant
|
||||||
|
// mentioned above.
|
||||||
|
(lhs_is_auto, lhs_id).cmp(&(rhs_is_auto, rhs_id))
|
||||||
}
|
}
|
||||||
} else {
|
(WhereClause::Implemented(_), _) => Ordering::Less,
|
||||||
other_bounds.push(bound);
|
(_, WhereClause::Implemented(_)) => Ordering::Greater,
|
||||||
|
(WhereClause::AliasEq(lhs), WhereClause::AliasEq(rhs)) => {
|
||||||
|
match (&lhs.alias, &rhs.alias) {
|
||||||
|
(AliasTy::Projection(lhs_proj), AliasTy::Projection(rhs_proj)) => {
|
||||||
|
// We only compare the `associated_ty_id`s. We shouldn't have
|
||||||
|
// multiple bounds for an associated type in the correct Rust code,
|
||||||
|
// and if we do, we error out.
|
||||||
|
if lhs_proj.associated_ty_id == rhs_proj.associated_ty_id {
|
||||||
|
multiple_same_projection = true;
|
||||||
|
}
|
||||||
|
lhs_proj.associated_ty_id.cmp(&rhs_proj.associated_ty_id)
|
||||||
|
}
|
||||||
|
// We don't produce `AliasTy::Opaque`s yet.
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We don't produce `WhereClause::{TypeOutlives, LifetimeOutlives}` yet.
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if regular_traits.len() > 1 {
|
if multiple_regular_traits || multiple_same_projection {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto_traits.sort_unstable_by_key(|b| b.trait_id().unwrap());
|
// As multiple occurrences of the same auto traits *are* permitted, we dedulicate the
|
||||||
auto_traits.dedup();
|
// bounds. We shouldn't have repeated elements besides auto traits at this point.
|
||||||
|
bounds.dedup();
|
||||||
|
|
||||||
Some(QuantifiedWhereClauses::from_iter(
|
Some(QuantifiedWhereClauses::from_iter(Interner, bounds))
|
||||||
Interner,
|
|
||||||
regular_traits.into_iter().chain(other_bounds).chain(auto_traits),
|
|
||||||
))
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(bounds) = bounds {
|
if let Some(bounds) = bounds {
|
||||||
let bounds = crate::make_single_type_binders(bounds);
|
let bounds = crate::make_single_type_binders(bounds);
|
||||||
TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
|
TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
|
||||||
} else {
|
} else {
|
||||||
// FIXME: report error (additional non-auto traits)
|
// FIXME: report error (additional non-auto traits or associated type rebound)
|
||||||
TyKind::Error.intern(Interner)
|
TyKind::Error.intern(Interner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3900,6 +3900,34 @@ fn g(t: &(dyn Sync + T<Proj = ()> + Send)) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dyn_multiple_projection_bounds() {
|
||||||
|
check_no_mismatches(
|
||||||
|
r#"
|
||||||
|
trait Trait {
|
||||||
|
type T;
|
||||||
|
type U;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f(t: &dyn Trait<T = (), U = ()>) {}
|
||||||
|
fn g(t: &dyn Trait<U = (), T = ()>) {
|
||||||
|
f(t);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_types(
|
||||||
|
r#"
|
||||||
|
trait Trait {
|
||||||
|
type T;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f(t: &dyn Trait<T = (), T = ()>) {}
|
||||||
|
//^&{unknown}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dyn_duplicate_auto_trait() {
|
fn dyn_duplicate_auto_trait() {
|
||||||
check_no_mismatches(
|
check_no_mismatches(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user