Auto merge of #12793 - lowr:fix/12739, r=Veykril

fix: sort and deduplicate auto traits in trait object types

Fixes #12739

Chalk solver doesn't sort and deduplicate auto traits in trait object types, so we need to handle them ourselves in the lowering phase, just like [`rustc`](880416180b/compiler/rustc_typeck/src/astconv/mod.rs (L1487-L1488)) and [`chalk-integration`](https://github.com/rust-lang/chalk/blob/master/chalk-integration/src/lowering.rs#L575) do.

Quoting from [the Chalk book](https://rust-lang.github.io/chalk/book/types/rust_types.html#dyn-types):

> Note that -- for this purpose -- ordering of bounds is significant. That means that if you create a `dyn Foo + Send` and a `dyn Send + Foo`, chalk would consider them distinct types. The assumption is that bounds are ordered in some canonical fashion somewhere else.

Also, trait object types with more than one non-auto traits were previously allowed, but are now disallowed with this patch.
This commit is contained in:
bors 2022-08-31 08:28:12 +00:00
commit 56d888689b
3 changed files with 132 additions and 9 deletions

View file

@ -1,8 +1,8 @@
//! Methods for lowering the HIR to types. There are two main cases here:
//!
//! - Lowering a type reference like `&usize` or `Option<foo::bar::Baz>` to a
//! type: The entry point for this is `Ty::from_hir`.
//! - Building the type for an item: This happens through the `type_for_def` query.
//! type: The entry point for this is `TyLoweringContext::lower_ty`.
//! - Building the type for an item: This happens through the `ty` query.
//!
//! This usually involves resolving names, collecting generic arguments etc.
use std::{
@ -47,7 +47,7 @@ use crate::{
consteval::{intern_const_scalar, path_to_const, unknown_const, unknown_const_as_generic},
db::HirDatabase,
make_binders,
mapping::ToChalk,
mapping::{from_chalk_trait_id, ToChalk},
static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
utils::Generics,
utils::{all_super_trait_refs, associated_type_by_name_including_super_traits, generics},
@ -982,13 +982,44 @@ impl<'a> TyLoweringContext<'a> {
fn lower_dyn_trait(&self, bounds: &[Interned<TypeBound>]) -> Ty {
let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
QuantifiedWhereClauses::from_iter(
let bounds =
bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false));
let mut auto_traits = SmallVec::<[_; 8]>::new();
let mut regular_traits = SmallVec::<[_; 2]>::new();
let mut other_bounds = SmallVec::<[_; 8]>::new();
for bound in bounds {
if let Some(id) = bound.trait_id() {
if ctx.db.trait_data(from_chalk_trait_id(id)).is_auto {
auto_traits.push(bound);
} else {
regular_traits.push(bound);
}
} else {
other_bounds.push(bound);
}
}
if regular_traits.len() > 1 {
return None;
}
auto_traits.sort_unstable_by_key(|b| b.trait_id().unwrap());
auto_traits.dedup();
Some(QuantifiedWhereClauses::from_iter(
Interner,
bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)),
)
regular_traits.into_iter().chain(other_bounds).chain(auto_traits),
))
});
let bounds = crate::make_single_type_binders(bounds);
TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
if let Some(bounds) = bounds {
let bounds = crate::make_single_type_binders(bounds);
TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
} else {
// FIXME: report error (additional non-auto traits)
TyKind::Error.intern(Interner)
}
}
fn lower_impl_trait(

View file

@ -3833,3 +3833,95 @@ fn test() {
"#,
)
}
#[test]
fn dyn_multiple_auto_traits_in_different_order() {
check_no_mismatches(
r#"
auto trait Send {}
auto trait Sync {}
fn f(t: &(dyn Sync + Send)) {}
fn g(t: &(dyn Send + Sync)) {
f(t);
}
"#,
);
check_no_mismatches(
r#"
auto trait Send {}
auto trait Sync {}
trait T {}
fn f(t: &(dyn T + Send + Sync)) {}
fn g(t: &(dyn Sync + T + Send)) {
f(t);
}
"#,
);
check_infer_with_mismatches(
r#"
auto trait Send {}
auto trait Sync {}
trait T1 {}
trait T2 {}
fn f(t: &(dyn T1 + T2 + Send + Sync)) {}
fn g(t: &(dyn Sync + T2 + T1 + Send)) {
f(t);
}
"#,
expect![[r#"
68..69 't': &{unknown}
101..103 '{}': ()
109..110 't': &{unknown}
142..155 '{ f(t); }': ()
148..149 'f': fn f(&{unknown})
148..152 'f(t)': ()
150..151 't': &{unknown}
"#]],
);
check_no_mismatches(
r#"
auto trait Send {}
auto trait Sync {}
trait T {
type Proj: Send + Sync;
}
fn f(t: &(dyn T<Proj = ()> + Send + Sync)) {}
fn g(t: &(dyn Sync + T<Proj = ()> + Send)) {
f(t);
}
"#,
);
}
#[test]
fn dyn_duplicate_auto_trait() {
check_no_mismatches(
r#"
auto trait Send {}
fn f(t: &(dyn Send + Send)) {}
fn g(t: &(dyn Send)) {
f(t);
}
"#,
);
check_no_mismatches(
r#"
auto trait Send {}
trait T {}
fn f(t: &(dyn T + Send + Send)) {}
fn g(t: &(dyn T + Send)) {
f(t);
}
"#,
);
}

View file

@ -1910,7 +1910,7 @@ impl<T> Vec<T> {
pub struct Box<T> {}
trait Display {}
trait Sync {}
auto trait Sync {}
fn main() {
// The block expression wrapping disables the constructor hint hiding logic