about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/adt.rs18
-rw-r--r--crates/hir-def/src/data.rs19
-rw-r--r--crates/hir-ty/src/db.rs15
-rw-r--r--crates/hir-ty/src/method_resolution.rs72
-rw-r--r--crates/hir-ty/src/tests/method_resolution.rs29
-rw-r--r--crates/test-utils/src/minicore.rs37
6 files changed, 153 insertions, 37 deletions
diff --git a/crates/hir-def/src/adt.rs b/crates/hir-def/src/adt.rs
index 938db032fbc..e5ab7bf3f6d 100644
--- a/crates/hir-def/src/adt.rs
+++ b/crates/hir-def/src/adt.rs
@@ -36,6 +36,7 @@ pub struct StructData {
     pub variant_data: Arc<VariantData>,
     pub repr: Option<ReprData>,
     pub visibility: RawVisibility,
+    pub rustc_has_incoherent_inherent_impls: bool,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -44,6 +45,7 @@ pub struct EnumData {
     pub variants: Arena<EnumVariantData>,
     pub repr: Option<ReprData>,
     pub visibility: RawVisibility,
+    pub rustc_has_incoherent_inherent_impls: bool,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -157,6 +159,10 @@ impl StructData {
         let item_tree = loc.id.item_tree(db);
         let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
         let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
+        let rustc_has_incoherent_inherent_impls = item_tree
+            .attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
+            .by_key("rustc_has_incoherent_inherent_impls")
+            .exists();
 
         let strukt = &item_tree[loc.id.value];
         let (variant_data, diagnostics) = lower_fields(
@@ -175,6 +181,7 @@ impl StructData {
                 variant_data: Arc::new(variant_data),
                 repr,
                 visibility: item_tree[strukt.visibility].clone(),
+                rustc_has_incoherent_inherent_impls,
             }),
             diagnostics.into(),
         )
@@ -194,6 +201,11 @@ impl StructData {
         let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
         let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
 
+        let rustc_has_incoherent_inherent_impls = item_tree
+            .attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
+            .by_key("rustc_has_incoherent_inherent_impls")
+            .exists();
+
         let union = &item_tree[loc.id.value];
         let (variant_data, diagnostics) = lower_fields(
             db,
@@ -211,6 +223,7 @@ impl StructData {
                 variant_data: Arc::new(variant_data),
                 repr,
                 visibility: item_tree[union.visibility].clone(),
+                rustc_has_incoherent_inherent_impls,
             }),
             diagnostics.into(),
         )
@@ -231,6 +244,10 @@ impl EnumData {
         let item_tree = loc.id.item_tree(db);
         let cfg_options = db.crate_graph()[krate].cfg_options.clone();
         let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
+        let rustc_has_incoherent_inherent_impls = item_tree
+            .attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
+            .by_key("rustc_has_incoherent_inherent_impls")
+            .exists();
 
         let enum_ = &item_tree[loc.id.value];
         let mut variants = Arena::new();
@@ -271,6 +288,7 @@ impl EnumData {
                 variants,
                 repr,
                 visibility: item_tree[enum_.visibility].clone(),
+                rustc_has_incoherent_inherent_impls,
             }),
             diagnostics.into(),
         )
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index 9c769690864..0e7acda4a75 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -168,6 +168,7 @@ pub struct TypeAliasData {
     pub type_ref: Option<Interned<TypeRef>>,
     pub visibility: RawVisibility,
     pub is_extern: bool,
+    pub rustc_has_incoherent_inherent_impls: bool,
     /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
     pub bounds: Vec<Interned<TypeBound>>,
 }
@@ -186,11 +187,17 @@ impl TypeAliasData {
             item_tree[typ.visibility].clone()
         };
 
+        let rustc_has_incoherent_inherent_impls = item_tree
+            .attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into())
+            .by_key("rustc_has_incoherent_inherent_impls")
+            .exists();
+
         Arc::new(TypeAliasData {
             name: typ.name.clone(),
             type_ref: typ.type_ref.clone(),
             visibility,
             is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
+            rustc_has_incoherent_inherent_impls,
             bounds: typ.bounds.to_vec(),
         })
     }
@@ -202,6 +209,7 @@ pub struct TraitData {
     pub items: Vec<(Name, AssocItemId)>,
     pub is_auto: bool,
     pub is_unsafe: bool,
+    pub rustc_has_incoherent_inherent_impls: bool,
     pub visibility: RawVisibility,
     /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore
     /// method calls to this trait's methods when the receiver is an array and the crate edition is
@@ -231,11 +239,11 @@ impl TraitData {
         let is_auto = tr_def.is_auto;
         let is_unsafe = tr_def.is_unsafe;
         let visibility = item_tree[tr_def.visibility].clone();
-        let skip_array_during_method_dispatch = item_tree
-            .attrs(db, module_id.krate(), ModItem::from(tree_id.value).into())
-            .by_key("rustc_skip_array_during_method_dispatch")
-            .exists();
-
+        let attrs = item_tree.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into());
+        let skip_array_during_method_dispatch =
+            attrs.by_key("rustc_skip_array_during_method_dispatch").exists();
+        let rustc_has_incoherent_inherent_impls =
+            attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
         let (items, attribute_calls, diagnostics) = match &tr_def.items {
             Some(items) => {
                 let mut collector = AssocItemCollector::new(
@@ -258,6 +266,7 @@ impl TraitData {
                 is_unsafe,
                 visibility,
                 skip_array_during_method_dispatch,
+                rustc_has_incoherent_inherent_impls,
             }),
             diagnostics.into(),
         )
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 932fce83563..ae6bf786cf5 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -3,13 +3,13 @@
 
 use std::sync::Arc;
 
-use arrayvec::ArrayVec;
 use base_db::{impl_intern_key, salsa, CrateId, Upcast};
 use hir_def::{
     db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
     FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
 };
 use la_arena::ArenaMap;
+use smallvec::SmallVec;
 
 use crate::{
     chalk_db,
@@ -92,10 +92,15 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>;
 
     /// Collects all crates in the dependency graph that have impls for the
-    /// given fingerprint. This is only used for primitive types; for
-    /// user-defined types we just look at the crate where the type is defined.
-    #[salsa::invoke(crate::method_resolution::inherent_impl_crates_query)]
-    fn inherent_impl_crates(&self, krate: CrateId, fp: TyFingerprint) -> ArrayVec<CrateId, 2>;
+    /// given fingerprint. This is only used for primitive types and types
+    /// annotated with `rustc_has_incoherent_inherent_impls`; for other types
+    /// we just look at the crate where the type is defined.
+    #[salsa::invoke(crate::method_resolution::incoherent_inherent_impl_crates)]
+    fn incoherent_inherent_impl_crates(
+        &self,
+        krate: CrateId,
+        fp: TyFingerprint,
+    ) -> SmallVec<[CrateId; 2]>;
 
     #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
     fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 8bcfa2728f0..0d980699ff5 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -2,18 +2,17 @@
 //! For details about how this works in rustc, see the method lookup page in the
 //! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
 //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
-use std::{iter, ops::ControlFlow, sync::Arc};
+use std::{ops::ControlFlow, sync::Arc};
 
-use arrayvec::ArrayVec;
 use base_db::{CrateId, Edition};
 use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
 use hir_def::{
     data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId,
-    FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId,
-    TraitId,
+    FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId,
 };
 use hir_expand::name::Name;
 use rustc_hash::{FxHashMap, FxHashSet};
+use smallvec::{smallvec, SmallVec};
 use stdx::never;
 
 use crate::{
@@ -336,21 +335,18 @@ impl InherentImpls {
     }
 }
 
-pub(crate) fn inherent_impl_crates_query(
+pub(crate) fn incoherent_inherent_impl_crates(
     db: &dyn HirDatabase,
     krate: CrateId,
     fp: TyFingerprint,
-) -> ArrayVec<CrateId, 2> {
+) -> SmallVec<[CrateId; 2]> {
     let _p = profile::span("inherent_impl_crates_query");
-    let mut res = ArrayVec::new();
+    let mut res = SmallVec::new();
     let crate_graph = db.crate_graph();
 
+    // should pass crate for finger print and do reverse deps
+
     for krate in crate_graph.transitive_deps(krate) {
-        if res.is_full() {
-            // we don't currently look for or store more than two crates here,
-            // so don't needlessly look at more crates than necessary.
-            break;
-        }
         let impls = db.inherent_impls_in_crate(krate);
         if impls.map.get(&fp).map_or(false, |v| !v.is_empty()) {
             res.push(krate);
@@ -392,19 +388,40 @@ pub fn def_crates(
     db: &dyn HirDatabase,
     ty: &Ty,
     cur_crate: CrateId,
-) -> Option<ArrayVec<CrateId, 2>> {
-    let mod_to_crate_ids = |module: ModuleId| Some(iter::once(module.krate()).collect());
-
-    let fp = TyFingerprint::for_inherent_impl(ty);
-
+) -> Option<SmallVec<[CrateId; 2]>> {
     match ty.kind(Interner) {
-        TyKind::Adt(AdtId(def_id), _) => mod_to_crate_ids(def_id.module(db.upcast())),
-        TyKind::Foreign(id) => {
-            mod_to_crate_ids(from_foreign_def_id(*id).lookup(db.upcast()).module(db.upcast()))
+        &TyKind::Adt(AdtId(def_id), _) => {
+            let rustc_has_incoherent_inherent_impls = match def_id {
+                hir_def::AdtId::StructId(id) => {
+                    db.struct_data(id).rustc_has_incoherent_inherent_impls
+                }
+                hir_def::AdtId::UnionId(id) => {
+                    db.union_data(id).rustc_has_incoherent_inherent_impls
+                }
+                hir_def::AdtId::EnumId(id) => db.enum_data(id).rustc_has_incoherent_inherent_impls,
+            };
+            Some(if rustc_has_incoherent_inherent_impls {
+                db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Adt(def_id))
+            } else {
+                smallvec![def_id.module(db.upcast()).krate()]
+            })
+        }
+        &TyKind::Foreign(id) => {
+            let alias = from_foreign_def_id(id);
+            Some(if db.type_alias_data(alias).rustc_has_incoherent_inherent_impls {
+                db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::ForeignType(id))
+            } else {
+                smallvec![alias.module(db.upcast()).krate()]
+            })
+        }
+        TyKind::Dyn(_) => {
+            let trait_id = ty.dyn_trait()?;
+            Some(if db.trait_data(trait_id).rustc_has_incoherent_inherent_impls {
+                db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Dyn(trait_id))
+            } else {
+                smallvec![trait_id.module(db.upcast()).krate()]
+            })
         }
-        TyKind::Dyn(_) => ty
-            .dyn_trait()
-            .and_then(|trait_| mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast()))),
         // for primitives, there may be impls in various places (core and alloc
         // mostly). We just check the whole crate graph for crates with impls
         // (cached behind a query).
@@ -412,10 +429,11 @@ pub fn def_crates(
         | TyKind::Str
         | TyKind::Slice(_)
         | TyKind::Array(..)
-        | TyKind::Raw(..) => {
-            Some(db.inherent_impl_crates(cur_crate, fp.expect("fingerprint for primitive")))
-        }
-        _ => return None,
+        | TyKind::Raw(..) => Some(db.incoherent_inherent_impl_crates(
+            cur_crate,
+            TyFingerprint::for_inherent_impl(ty).expect("fingerprint for primitive"),
+        )),
+        _ => None,
     }
 }
 
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index 5d76d185ffc..b6958c362c7 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -1867,3 +1867,32 @@ fn g<T: Trait>(a: T) {
         "#,
     );
 }
+
+#[test]
+fn incoherent_impls() {
+    check(
+        r#"
+//- minicore: error, send
+pub struct Box<T>(T);
+use core::error::Error;
+
+#[rustc_allow_incoherent_impl]
+impl dyn Error {
+    pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error>> {
+        loop {}
+    }
+}
+#[rustc_allow_incoherent_impl]
+impl dyn Error + Send {
+    /// Attempts to downcast the box to a concrete type.
+    pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error + Send>> {
+        let err: Box<dyn Error> = self;
+                               // ^^^^ expected Box<dyn Error>, got Box<dyn Error + Send>
+                               // FIXME, type mismatch should not occur
+        <dyn Error>::downcast(err).map_err(|_| loop {})
+      //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box<dyn Error>) -> Result<Box<{unknown}>, Box<dyn Error>>
+    }
+}
+"#,
+    );
+}
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 69d2e62b256..40a330a6bd1 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -20,6 +20,7 @@
 //!     derive:
 //!     drop:
 //!     eq: sized
+//!     error: fmt
 //!     fmt: result
 //!     fn:
 //!     from: sized
@@ -34,8 +35,10 @@
 //!     pin:
 //!     range:
 //!     result:
+//!     send: sized
 //!     sized:
 //!     slice:
+//!     sync: sized
 //!     try:
 //!     unsize: sized
 
@@ -47,6 +50,24 @@ pub mod marker {
     pub trait Sized {}
     // endregion:sized
 
+    // region:send
+    pub unsafe auto trait Send {}
+
+    impl<T: ?Sized> !Send for *const T {}
+    impl<T: ?Sized> !Send for *mut T {}
+    // region:sync
+    unsafe impl<T: Sync + ?Sized> Send for &T {}
+    unsafe impl<T: Send + ?Sized> Send for &mut T {}
+    // endregion:sync
+    // endregion:send
+
+    // region:sync
+    pub unsafe auto trait Sync {}
+
+    impl<T: ?Sized> !Sync for *const T {}
+    impl<T: ?Sized> !Sync for *mut T {}
+    // endregion:sync
+
     // region:unsize
     #[lang = "unsize"]
     pub trait Unsize<T: ?Sized> {}
@@ -438,6 +459,9 @@ pub mod fmt {
     pub trait Debug {
         fn fmt(&self, f: &mut Formatter<'_>) -> Result;
     }
+    pub trait Display {
+        fn fmt(&self, f: &mut Formatter<'_>) -> Result;
+    }
 }
 // endregion:fmt
 
@@ -693,6 +717,17 @@ impl bool {
 }
 // endregion:bool_impl
 
+// region:error
+pub mod error {
+    #[rustc_has_incoherent_inherent_impls]
+    pub trait Error: crate::fmt::Debug + crate::fmt::Display {
+        fn source(&self) -> Option<&(dyn Error + 'static)> {
+            None
+        }
+    }
+}
+// endregion:error
+
 pub mod prelude {
     pub mod v1 {
         pub use crate::{
@@ -705,7 +740,9 @@ pub mod prelude {
             iter::{IntoIterator, Iterator},     // :iterator
             macros::builtin::derive,            // :derive
             marker::Copy,                       // :copy
+            marker::Send,                       // :send
             marker::Sized,                      // :sized
+            marker::Sync,                       // :sync
             mem::drop,                          // :drop
             ops::Drop,                          // :drop
             ops::{Fn, FnMut, FnOnce},           // :fn