about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRyo Yoshida <low.ryoshida@gmail.com>2022-07-18 19:49:14 +0900
committerRyo Yoshida <low.ryoshida@gmail.com>2022-08-30 20:52:42 +0900
commit7ecead23c82f783665aa0bdb2f9ff6d26de545e9 (patch)
treec2a375ab10a54ce54dcd0483520fbd2ac822e1f3
parent5543dd88c98eca686d63fc032ae927a0565565b8 (diff)
downloadrust-7ecead23c82f783665aa0bdb2f9ff6d26de545e9.tar.gz
rust-7ecead23c82f783665aa0bdb2f9ff6d26de545e9.zip
fix: sort and deduplicate auto traits in trait object types
-rw-r--r--crates/hir-ty/src/lower.rs47
-rw-r--r--crates/hir-ty/src/tests/traits.rs92
-rw-r--r--crates/ide/src/inlay_hints.rs2
3 files changed, 132 insertions, 9 deletions
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index ae115c8c0da..64a07070f02 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -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},
@@ -969,13 +969,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(
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 0f37970e2b3..e67c27aa2db 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -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);
+}
+        "#,
+    );
+}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index ed19784d1fa..e9034daefa8 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -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