about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-03-14 19:57:01 +0000
committerbors <bors@rust-lang.org>2023-03-14 19:57:01 +0000
commitb4d7ea0692f2a72276704109ee13ffb3284f4863 (patch)
tree586473b9ce4a940ffb50e76f0c67c2547f0b9e4f
parentc15335c8b02dbc0ccc04f04c56d6471120db1bd2 (diff)
parent510e4b402994124cff40893ad240646e7e0bfa54 (diff)
downloadrust-b4d7ea0692f2a72276704109ee13ffb3284f4863.tar.gz
rust-b4d7ea0692f2a72276704109ee13ffb3284f4863.zip
Auto merge of #13994 - Veykril:incoherent-impl, r=Veykril
lint incoherent inherent impls
-rw-r--r--crates/hir-def/src/adt.rs19
-rw-r--r--crates/hir-def/src/data.rs33
-rw-r--r--crates/hir-def/src/nameres.rs10
-rw-r--r--crates/hir-def/src/nameres/collector.rs5
-rw-r--r--crates/hir-ty/src/chalk_ext.rs15
-rw-r--r--crates/hir-ty/src/diagnostics.rs6
-rw-r--r--crates/hir-ty/src/method_resolution.rs92
-rw-r--r--crates/hir-ty/src/tests/method_resolution.rs11
-rw-r--r--crates/hir-ty/src/tests/simple.rs60
-rw-r--r--crates/hir/src/diagnostics.rs5
-rw-r--r--crates/hir/src/lib.rs20
-rw-r--r--crates/ide-completion/src/completions/dot.rs6
-rw-r--r--crates/ide-completion/src/tests/pattern.rs1
-rw-r--r--crates/ide-completion/src/tests/special.rs1
-rw-r--r--crates/ide-diagnostics/src/handlers/incoherent_impl.rs77
-rw-r--r--crates/ide-diagnostics/src/lib.rs2
-rw-r--r--crates/ide/src/goto_implementation.rs1
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs12
-rw-r--r--crates/test-utils/src/minicore.rs2
19 files changed, 305 insertions, 73 deletions
diff --git a/crates/hir-def/src/adt.rs b/crates/hir-def/src/adt.rs
index 9bc1c54a3c6..b336f59ffee 100644
--- a/crates/hir-def/src/adt.rs
+++ b/crates/hir-def/src/adt.rs
@@ -40,6 +40,7 @@ pub struct StructData {
     pub repr: Option<ReprOptions>,
     pub visibility: RawVisibility,
     pub rustc_has_incoherent_inherent_impls: bool,
+    pub fundamental: bool,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -173,10 +174,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 attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
+        let rustc_has_incoherent_inherent_impls =
+            attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
+        let fundamental = attrs.by_key("fundamental").exists();
 
         let strukt = &item_tree[loc.id.value];
         let (variant_data, diagnostics) = lower_fields(
@@ -196,6 +197,7 @@ impl StructData {
                 repr,
                 visibility: item_tree[strukt.visibility].clone(),
                 rustc_has_incoherent_inherent_impls,
+                fundamental,
             }),
             diagnostics.into(),
         )
@@ -215,10 +217,10 @@ 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 attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
+        let rustc_has_incoherent_inherent_impls =
+            attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
+        let fundamental = attrs.by_key("fundamental").exists();
 
         let union = &item_tree[loc.id.value];
         let (variant_data, diagnostics) = lower_fields(
@@ -238,6 +240,7 @@ impl StructData {
                 repr,
                 visibility: item_tree[union.visibility].clone(),
                 rustc_has_incoherent_inherent_impls,
+                fundamental,
             }),
             diagnostics.into(),
         )
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index ee6e269fe55..1633a33bedd 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -35,6 +35,7 @@ pub struct FunctionData {
     pub visibility: RawVisibility,
     pub abi: Option<Interned<str>>,
     pub legacy_const_generics_indices: Box<[u32]>,
+    pub rustc_allow_incoherent_impl: bool,
     flags: FnFlags,
 }
 
@@ -84,13 +85,14 @@ impl FunctionData {
             }
         }
 
-        let legacy_const_generics_indices = item_tree
-            .attrs(db, krate, ModItem::from(loc.id.value).into())
+        let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
+        let legacy_const_generics_indices = attrs
             .by_key("rustc_legacy_const_generics")
             .tt_values()
             .next()
             .map(parse_rustc_legacy_const_generics)
             .unwrap_or_default();
+        let rustc_allow_incoherent_impl = attrs.by_key("rustc_allow_incoherent_impl").exists();
 
         Arc::new(FunctionData {
             name: func.name.clone(),
@@ -108,6 +110,7 @@ impl FunctionData {
             abi: func.abi.clone(),
             legacy_const_generics_indices,
             flags,
+            rustc_allow_incoherent_impl,
         })
     }
 
@@ -171,6 +174,7 @@ pub struct TypeAliasData {
     pub visibility: RawVisibility,
     pub is_extern: bool,
     pub rustc_has_incoherent_inherent_impls: bool,
+    pub rustc_allow_incoherent_impl: bool,
     /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
     pub bounds: Vec<Interned<TypeBound>>,
 }
@@ -189,10 +193,14 @@ 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();
+        let attrs = item_tree.attrs(
+            db,
+            loc.container.module(db).krate(),
+            ModItem::from(loc.id.value).into(),
+        );
+        let rustc_has_incoherent_inherent_impls =
+            attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
+        let rustc_allow_incoherent_impl = attrs.by_key("rustc_allow_incoherent_impl").exists();
 
         Arc::new(TypeAliasData {
             name: typ.name.clone(),
@@ -200,6 +208,7 @@ impl TypeAliasData {
             visibility,
             is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
             rustc_has_incoherent_inherent_impls,
+            rustc_allow_incoherent_impl,
             bounds: typ.bounds.to_vec(),
         })
     }
@@ -212,11 +221,12 @@ pub struct TraitData {
     pub is_auto: bool,
     pub is_unsafe: bool,
     pub rustc_has_incoherent_inherent_impls: bool,
+    pub skip_array_during_method_dispatch: bool,
+    pub fundamental: 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
     /// 2015 or 2018.
-    pub skip_array_during_method_dispatch: bool,
     // box it as the vec is usually empty anyways
     pub attribute_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
 }
@@ -245,6 +255,7 @@ impl TraitData {
             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 fundamental = attrs.by_key("fundamental").exists();
         let mut collector =
             AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr));
         collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items);
@@ -260,6 +271,7 @@ impl TraitData {
                 visibility,
                 skip_array_during_method_dispatch,
                 rustc_has_incoherent_inherent_impls,
+                fundamental,
             }),
             diagnostics.into(),
         )
@@ -450,6 +462,7 @@ pub struct ConstData {
     pub name: Option<Name>,
     pub type_ref: Interned<TypeRef>,
     pub visibility: RawVisibility,
+    pub rustc_allow_incoherent_impl: bool,
 }
 
 impl ConstData {
@@ -463,10 +476,16 @@ impl ConstData {
             item_tree[konst.visibility].clone()
         };
 
+        let rustc_allow_incoherent_impl = item_tree
+            .attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into())
+            .by_key("rustc_allow_incoherent_impl")
+            .exists();
+
         Arc::new(ConstData {
             name: konst.name.clone(),
             type_ref: konst.type_ref.clone(),
             visibility,
+            rustc_allow_incoherent_impl,
         })
     }
 }
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index 59b52c3caa3..121b25c6c97 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -120,6 +120,8 @@ pub struct DefMap {
     registered_tools: Vec<SmolStr>,
     /// Unstable features of Rust enabled with `#![feature(A, B)]`.
     unstable_features: FxHashSet<SmolStr>,
+    /// #[rustc_coherence_is_core]
+    rustc_coherence_is_core: bool,
 
     edition: Edition,
     recursion_limit: Option<u32>,
@@ -292,6 +294,7 @@ impl DefMap {
             registered_tools: Vec::new(),
             unstable_features: FxHashSet::default(),
             diagnostics: Vec::new(),
+            rustc_coherence_is_core: false,
         }
     }
 
@@ -325,6 +328,10 @@ impl DefMap {
         self.unstable_features.contains(feature)
     }
 
+    pub fn is_rustc_coherence_is_core(&self) -> bool {
+        self.rustc_coherence_is_core
+    }
+
     pub fn root(&self) -> LocalModuleId {
         self.root
     }
@@ -337,7 +344,7 @@ impl DefMap {
         self.proc_macro_loading_error.as_deref()
     }
 
-    pub(crate) fn krate(&self) -> CrateId {
+    pub fn krate(&self) -> CrateId {
         self.krate
     }
 
@@ -502,6 +509,7 @@ impl DefMap {
             krate: _,
             prelude: _,
             root: _,
+            rustc_coherence_is_core: _,
         } = self;
 
         extern_prelude.shrink_to_fit();
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 6901930133f..ddcee77ec4c 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -296,6 +296,11 @@ impl DefCollector<'_> {
                     continue;
                 }
 
+                if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") {
+                    self.def_map.rustc_coherence_is_core = true;
+                    continue;
+                }
+
                 if *attr_name == hir_expand::name![feature] {
                     let features =
                         attr.parse_path_comma_token_tree().into_iter().flatten().filter_map(
diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs
index e6aefbf2716..2141894922f 100644
--- a/crates/hir-ty/src/chalk_ext.rs
+++ b/crates/hir-ty/src/chalk_ext.rs
@@ -12,7 +12,7 @@ use hir_def::{
 use crate::{
     db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
     from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
-    CallableDefId, CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
+    CallableDefId, CallableSig, DynTy, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
     QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
 };
 
@@ -378,6 +378,19 @@ impl ProjectionTyExt for ProjectionTy {
     }
 }
 
+pub trait DynTyExt {
+    fn principal(&self) -> Option<&TraitRef>;
+}
+
+impl DynTyExt for DynTy {
+    fn principal(&self) -> Option<&TraitRef> {
+        self.bounds.skip_binders().interned().get(0).and_then(|b| match b.skip_binders() {
+            crate::WhereClause::Implemented(trait_ref) => Some(trait_ref),
+            _ => None,
+        })
+    }
+}
+
 pub trait TraitRefExt {
     fn hir_trait_id(&self) -> TraitId;
 }
diff --git a/crates/hir-ty/src/diagnostics.rs b/crates/hir-ty/src/diagnostics.rs
index 37eb06be1d3..4b147b99707 100644
--- a/crates/hir-ty/src/diagnostics.rs
+++ b/crates/hir-ty/src/diagnostics.rs
@@ -11,3 +11,9 @@ pub use crate::diagnostics::{
     },
     unsafe_check::{missing_unsafe, unsafe_expressions, UnsafeExpr},
 };
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct IncoherentImpl {
+    pub file_id: hir_expand::HirFileId,
+    pub impl_: syntax::AstPtr<syntax::ast::Impl>,
+}
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 92a17fc3a99..f3a27632bf5 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -19,13 +19,13 @@ use stdx::never;
 use crate::{
     autoderef::{self, AutoderefKind},
     db::HirDatabase,
-    from_foreign_def_id,
+    from_chalk_trait_id, from_foreign_def_id,
     infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
     primitive::{FloatTy, IntTy, UintTy},
     static_lifetime, to_chalk_trait_id,
     utils::all_super_traits,
-    AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner,
-    Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt,
+    AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, InEnvironment,
+    Interner, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt,
 };
 
 /// This is used as a key for indexing impls.
@@ -266,11 +266,12 @@ impl TraitImpls {
 #[derive(Debug, Eq, PartialEq)]
 pub struct InherentImpls {
     map: FxHashMap<TyFingerprint, Vec<ImplId>>,
+    invalid_impls: Vec<ImplId>,
 }
 
 impl InherentImpls {
     pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
-        let mut impls = Self { map: FxHashMap::default() };
+        let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
 
         let crate_def_map = db.crate_def_map(krate);
         impls.collect_def_map(db, &crate_def_map);
@@ -283,7 +284,7 @@ impl InherentImpls {
         db: &dyn HirDatabase,
         block: BlockId,
     ) -> Option<Arc<Self>> {
-        let mut impls = Self { map: FxHashMap::default() };
+        let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
         if let Some(block_def_map) = db.block_def_map(block) {
             impls.collect_def_map(db, &block_def_map);
             impls.shrink_to_fit();
@@ -306,11 +307,17 @@ impl InherentImpls {
                 }
 
                 let self_ty = db.impl_self_ty(impl_id);
-                let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders());
-                if let Some(fp) = fp {
-                    self.map.entry(fp).or_default().push(impl_id);
+                let self_ty = self_ty.skip_binders();
+
+                match is_inherent_impl_coherent(db, def_map, &data, self_ty) {
+                    true => {
+                        // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
+                        if let Some(fp) = TyFingerprint::for_inherent_impl(self_ty) {
+                            self.map.entry(fp).or_default().push(impl_id);
+                        }
+                    }
+                    false => self.invalid_impls.push(impl_id),
                 }
-                // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
             }
 
             // To better support custom derives, collect impls in all unnamed const items.
@@ -334,6 +341,10 @@ impl InherentImpls {
     pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
         self.map.values().flat_map(|v| v.iter().copied())
     }
+
+    pub fn invalid_impls(&self) -> &[ImplId] {
+        &self.invalid_impls
+    }
 }
 
 pub(crate) fn incoherent_inherent_impl_crates(
@@ -775,6 +786,69 @@ fn find_matching_impl(
     }
 }
 
+fn is_inherent_impl_coherent(
+    db: &dyn HirDatabase,
+    def_map: &DefMap,
+    impl_data: &ImplData,
+    self_ty: &Ty,
+) -> bool {
+    let self_ty = self_ty.kind(Interner);
+    let impl_allowed = match self_ty {
+        TyKind::Tuple(_, _)
+        | TyKind::FnDef(_, _)
+        | TyKind::Array(_, _)
+        | TyKind::Never
+        | TyKind::Raw(_, _)
+        | TyKind::Ref(_, _, _)
+        | TyKind::Slice(_)
+        | TyKind::Str
+        | TyKind::Scalar(_) => def_map.is_rustc_coherence_is_core(),
+
+        &TyKind::Adt(AdtId(adt), _) => adt.module(db.upcast()).krate() == def_map.krate(),
+        TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| {
+            from_chalk_trait_id(trait_ref.trait_id).module(db.upcast()).krate() == def_map.krate()
+        }),
+
+        _ => true,
+    };
+    impl_allowed || {
+        let rustc_has_incoherent_inherent_impls = match self_ty {
+            TyKind::Tuple(_, _)
+            | TyKind::FnDef(_, _)
+            | TyKind::Array(_, _)
+            | TyKind::Never
+            | TyKind::Raw(_, _)
+            | TyKind::Ref(_, _, _)
+            | TyKind::Slice(_)
+            | TyKind::Str
+            | TyKind::Scalar(_) => true,
+
+            &TyKind::Adt(AdtId(adt), _) => match adt {
+                hir_def::AdtId::StructId(it) => {
+                    db.struct_data(it).rustc_has_incoherent_inherent_impls
+                }
+                hir_def::AdtId::UnionId(it) => {
+                    db.union_data(it).rustc_has_incoherent_inherent_impls
+                }
+                hir_def::AdtId::EnumId(it) => db.enum_data(it).rustc_has_incoherent_inherent_impls,
+            },
+            TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| {
+                db.trait_data(from_chalk_trait_id(trait_ref.trait_id))
+                    .rustc_has_incoherent_inherent_impls
+            }),
+
+            _ => false,
+        };
+        rustc_has_incoherent_inherent_impls
+            && !impl_data.items.is_empty()
+            && impl_data.items.iter().copied().all(|assoc| match assoc {
+                AssocItemId::FunctionId(it) => db.function_data(it).rustc_allow_incoherent_impl,
+                AssocItemId::ConstId(it) => db.const_data(it).rustc_allow_incoherent_impl,
+                AssocItemId::TypeAliasId(it) => db.type_alias_data(it).rustc_allow_incoherent_impl,
+            })
+    }
+}
+
 pub fn iterate_path_candidates(
     ty: &Canonical<Ty>,
     db: &dyn HirDatabase,
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index e568e7013fa..378d4783361 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -9,6 +9,7 @@ fn infer_slice_method() {
     check_types(
         r#"
 impl<T> [T] {
+    #[rustc_allow_incoherent_impl]
     fn foo(&self) -> T {
         loop {}
     }
@@ -35,6 +36,7 @@ fn test() {
 //- /lib.rs crate:other_crate
 mod foo {
     impl f32 {
+        #[rustc_allow_incoherent_impl]
         pub fn foo(self) -> f32 { 0. }
     }
 }
@@ -47,6 +49,7 @@ fn infer_array_inherent_impl() {
     check_types(
         r#"
 impl<T, const N: usize> [T; N] {
+    #[rustc_allow_incoherent_impl]
     fn foo(&self) -> T {
         loop {}
     }
@@ -1437,6 +1440,7 @@ fn resolve_const_generic_array_methods() {
         r#"
 #[lang = "array"]
 impl<T, const N: usize> [T; N] {
+    #[rustc_allow_incoherent_impl]
     pub fn map<F, U>(self, f: F) -> [U; N]
     where
         F: FnMut(T) -> U,
@@ -1445,6 +1449,7 @@ impl<T, const N: usize> [T; N] {
 
 #[lang = "slice"]
 impl<T> [T] {
+    #[rustc_allow_incoherent_impl]
     pub fn map<F, U>(self, f: F) -> &[U]
     where
         F: FnMut(T) -> U,
@@ -1468,6 +1473,7 @@ struct Const<const N: usize>;
 
 #[lang = "array"]
 impl<T, const N: usize> [T; N] {
+    #[rustc_allow_incoherent_impl]
     pub fn my_map<F, U, const X: usize>(self, f: F, c: Const<X>) -> [U; X]
     where
         F: FnMut(T) -> U,
@@ -1476,6 +1482,7 @@ impl<T, const N: usize> [T; N] {
 
 #[lang = "slice"]
 impl<T> [T] {
+    #[rustc_allow_incoherent_impl]
     pub fn my_map<F, const X: usize, U>(self, f: F, c: Const<X>) -> &[U]
     where
         F: FnMut(T) -> U,
@@ -1874,14 +1881,14 @@ fn incoherent_impls() {
 pub struct Box<T>(T);
 use core::error::Error;
 
-#[rustc_allow_incoherent_impl]
 impl dyn Error {
+    #[rustc_allow_incoherent_impl]
     pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error>> {
         loop {}
     }
 }
-#[rustc_allow_incoherent_impl]
 impl dyn Error + Send {
+    #[rustc_allow_incoherent_impl]
     /// 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;
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 0e9c349afef..13cc3fea52d 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -1116,21 +1116,22 @@ fn infer_inherent_method() {
 fn infer_inherent_method_str() {
     check_infer(
         r#"
-        #[lang = "str"]
-        impl str {
-            fn foo(&self) -> i32 {}
-        }
+#![rustc_coherence_is_core]
+#[lang = "str"]
+impl str {
+    fn foo(&self) -> i32 {}
+}
 
-        fn test() {
-            "foo".foo();
-        }
-        "#,
+fn test() {
+    "foo".foo();
+}
+"#,
         expect![[r#"
-            39..43 'self': &str
-            52..54 '{}': i32
-            68..88 '{     ...o(); }': ()
-            74..79 '"foo"': &str
-            74..85 '"foo".foo()': i32
+            67..71 'self': &str
+            80..82 '{}': i32
+            96..116 '{     ...o(); }': ()
+            102..107 '"foo"': &str
+            102..113 '"foo".foo()': i32
         "#]],
     );
 }
@@ -2640,6 +2641,7 @@ impl<T> [T] {}
 
 #[lang = "slice_alloc"]
 impl<T> [T] {
+    #[rustc_allow_incoherent_impl]
     pub fn into_vec<A: Allocator>(self: Box<Self, A>) -> Vec<T, A> {
         unimplemented!()
     }
@@ -2655,22 +2657,22 @@ struct Astruct;
 impl B for Astruct {}
 "#,
         expect![[r#"
-            569..573 'self': Box<[T], A>
-            602..634 '{     ...     }': Vec<T, A>
-            648..761 '{     ...t]); }': ()
-            658..661 'vec': Vec<i32, Global>
-            664..679 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
-            664..691 '<[_]>:...1i32])': Vec<i32, Global>
-            680..690 'box [1i32]': Box<[i32; 1], Global>
-            684..690 '[1i32]': [i32; 1]
-            685..689 '1i32': i32
-            701..702 'v': Vec<Box<dyn B, Global>, Global>
-            722..739 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global>
-            722..758 '<[_]> ...ruct])': Vec<Box<dyn B, Global>, Global>
-            740..757 'box [b...truct]': Box<[Box<dyn B, Global>; 1], Global>
-            744..757 '[box Astruct]': [Box<dyn B, Global>; 1]
-            745..756 'box Astruct': Box<Astruct, Global>
-            749..756 'Astruct': Astruct
+            604..608 'self': Box<[T], A>
+            637..669 '{     ...     }': Vec<T, A>
+            683..796 '{     ...t]); }': ()
+            693..696 'vec': Vec<i32, Global>
+            699..714 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
+            699..726 '<[_]>:...1i32])': Vec<i32, Global>
+            715..725 'box [1i32]': Box<[i32; 1], Global>
+            719..725 '[1i32]': [i32; 1]
+            720..724 '1i32': i32
+            736..737 'v': Vec<Box<dyn B, Global>, Global>
+            757..774 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global>
+            757..793 '<[_]> ...ruct])': Vec<Box<dyn B, Global>, Global>
+            775..792 'box [b...truct]': Box<[Box<dyn B, Global>; 1], Global>
+            779..792 '[box Astruct]': [Box<dyn B, Global>; 1]
+            780..791 'box Astruct': Box<Astruct, Global>
+            784..791 'Astruct': Astruct
         "#]],
     )
 }
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 8f019a81b2d..253d62dafc6 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -3,6 +3,8 @@
 //!
 //! This probably isn't the best way to do this -- ideally, diagnostics should
 //! be expressed in terms of hir types themselves.
+pub use hir_ty::diagnostics::{IncoherentImpl, IncorrectCase};
+
 use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
@@ -35,6 +37,7 @@ diagnostics![
     InactiveCode,
     IncorrectCase,
     InvalidDeriveTarget,
+    IncoherentImpl,
     MacroError,
     MalformedDerive,
     MismatchedArgCount,
@@ -220,5 +223,3 @@ pub struct NeedMut {
 pub struct UnusedMut {
     pub local: Local,
 }
-
-pub use hir_ty::diagnostics::IncorrectCase;
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 27c4577d60d..35424feec8b 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -85,10 +85,10 @@ use crate::db::{DefDatabase, HirDatabase};
 pub use crate::{
     attrs::{HasAttrs, Namespace},
     diagnostics::{
-        AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncorrectCase,
-        InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
-        MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
-        ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
+        AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl,
+        IncorrectCase, InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount,
+        MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem,
+        PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
         UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall,
         UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
     },
@@ -604,11 +604,23 @@ impl Module {
             }
         }
 
+        let inherent_impls = db.inherent_impls_in_crate(self.id.krate());
+
         for impl_def in self.impl_defs(db) {
             for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() {
                 emit_def_diagnostic(db, acc, diag);
             }
 
+            if inherent_impls.invalid_impls().contains(&impl_def.id) {
+                let loc = impl_def.id.lookup(db.upcast());
+                let tree = loc.id.item_tree(db.upcast());
+                let node = &tree[loc.id.value];
+                let file_id = loc.id.file_id();
+                let ast_id_map = db.ast_id_map(file_id);
+
+                acc.push(IncoherentImpl { impl_: ast_id_map.get(node.ast_id()), file_id }.into())
+            }
+
             for item in impl_def.items(db) {
                 let def: DefWithBody = match item {
                     AssocItem::Function(it) => it.into(),
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 09ac57153ae..77246379e7b 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -415,7 +415,6 @@ fn foo(a: lib::A) { a.$0 }
     fn test_local_impls() {
         check(
             r#"
-//- /lib.rs crate:lib
 pub struct A {}
 mod m {
     impl super::A {
@@ -427,9 +426,8 @@ mod m {
         }
     }
 }
-//- /main.rs crate:main deps:lib
-fn foo(a: lib::A) {
-    impl lib::A {
+fn foo(a: A) {
+    impl A {
         fn local_method(&self) {}
     }
     a.$0
diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs
index ad9254e7f2e..c0e485c36fd 100644
--- a/crates/ide-completion/src/tests/pattern.rs
+++ b/crates/ide-completion/src/tests/pattern.rs
@@ -614,6 +614,7 @@ fn f(u: U) {
 
     check_empty(
         r#"
+#![rustc_coherence_is_core]
 #[lang = "u32"]
 impl u32 {
     pub const MIN: Self = 0;
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
index cb71c7b2bde..f8a6f6cd3ed 100644
--- a/crates/ide-completion/src/tests/special.rs
+++ b/crates/ide-completion/src/tests/special.rs
@@ -608,6 +608,7 @@ fn f() {
 }
 
 //- /core.rs crate:core
+#![rustc_coherence_is_core]
 #[lang = "u8"]
 impl u8 {
     pub const MAX: Self = 255;
diff --git a/crates/ide-diagnostics/src/handlers/incoherent_impl.rs b/crates/ide-diagnostics/src/handlers/incoherent_impl.rs
new file mode 100644
index 00000000000..72af9ebfcbb
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/incoherent_impl.rs
@@ -0,0 +1,77 @@
+use hir::InFile;
+
+use crate::{Diagnostic, DiagnosticsContext, Severity};
+
+// Diagnostic: incoherent-impl
+//
+// This diagnostic is triggered if the targe type of an impl is from a foreign crate.
+pub(crate) fn incoherent_impl(ctx: &DiagnosticsContext<'_>, d: &hir::IncoherentImpl) -> Diagnostic {
+    Diagnostic::new(
+        "incoherent-impl",
+        format!("cannot define inherent `impl` for foreign type"),
+        ctx.sema.diagnostics_display_range(InFile::new(d.file_id, d.impl_.clone().into())).range,
+    )
+    .severity(Severity::Error)
+}
+
+#[cfg(test)]
+mod change_case {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn primitive() {
+        check_diagnostics(
+            r#"
+  impl bool {}
+//^^^^^^^^^^^^ error: cannot define inherent `impl` for foreign type
+"#,
+        );
+    }
+
+    #[test]
+    fn primitive_rustc_allow_incoherent_impl() {
+        check_diagnostics(
+            r#"
+impl bool {
+    #[rustc_allow_incoherent_impl]
+    fn falsch(self) -> Self { false }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn rustc_allow_incoherent_impl() {
+        check_diagnostics(
+            r#"
+//- /lib.rs crate:foo
+#[rustc_has_incoherent_inherent_impls]
+pub struct S;
+//- /main.rs crate:main deps:foo
+impl foo::S {
+    #[rustc_allow_incoherent_impl]
+    fn func(self) {}
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+//- /lib.rs crate:foo
+pub struct S;
+//- /main.rs crate:main deps:foo
+  impl foo::S { #[rustc_allow_incoherent_impl] fn func(self) {} }
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cannot define inherent `impl` for foreign type
+"#,
+        );
+        check_diagnostics(
+            r#"
+//- /lib.rs crate:foo
+#[rustc_has_incoherent_inherent_impls]
+pub struct S;
+//- /main.rs crate:main deps:foo
+  impl foo::S { fn func(self) {} }
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cannot define inherent `impl` for foreign type
+"#,
+        );
+    }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index f6c9b79c30c..71f136b8c90 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -29,6 +29,7 @@ mod handlers {
     pub(crate) mod break_outside_of_loop;
     pub(crate) mod expected_function;
     pub(crate) mod inactive_code;
+    pub(crate) mod incoherent_impl;
     pub(crate) mod incorrect_case;
     pub(crate) mod invalid_derive_target;
     pub(crate) mod macro_error;
@@ -254,6 +255,7 @@ pub fn diagnostics(
             AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
             AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d),
             AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
+            AnyDiagnostic::IncoherentImpl(d) => handlers::incoherent_impl::incoherent_impl(&ctx, &d),
             AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
             AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
             AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index 190ab80ba0f..a1a119629a9 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -297,6 +297,7 @@ impl Foo<str> {}
 //- /lib.rs crate:main deps:core
 fn foo(_: bool$0) {{}}
 //- /libcore.rs crate:core
+#![rustc_coherence_is_core]
 #[lang = "bool"]
 impl bool {}
    //^^^^
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index 0a7513e465a..1e1771259b1 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -435,7 +435,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 3386..3394,
+                                        range: 3415..3423,
                                     },
                                 ),
                                 tooltip: "",
@@ -448,7 +448,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 3418..3422,
+                                        range: 3447..3451,
                                     },
                                 ),
                                 tooltip: "",
@@ -468,7 +468,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 3386..3394,
+                                        range: 3415..3423,
                                     },
                                 ),
                                 tooltip: "",
@@ -481,7 +481,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 3418..3422,
+                                        range: 3447..3451,
                                     },
                                 ),
                                 tooltip: "",
@@ -501,7 +501,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 3386..3394,
+                                        range: 3415..3423,
                                     },
                                 ),
                                 tooltip: "",
@@ -514,7 +514,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 3418..3422,
+                                        range: 3447..3451,
                                     },
                                 ),
                                 tooltip: "",
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 93ff76a040c..ca6de4061a4 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -44,6 +44,8 @@
 //!     try: infallible
 //!     unsize: sized
 
+#![rustc_coherence_is_core]
+
 pub mod marker {
     // region:sized
     #[lang = "sized"]