about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-08-29 13:24:54 +0000
committerbors <bors@rust-lang.org>2024-08-29 13:24:54 +0000
commit28142e4018279d75ff51d31e6b65eb2405f8f338 (patch)
tree07a2507568d52b35005f46e24a2dad34a8b5556a
parent23bea2b2736ac69091df5069282d12887a4d9e4f (diff)
parenta54a7a8a6afb6d67a5a66806068e60f55a0947e6 (diff)
downloadrust-28142e4018279d75ff51d31e6b65eb2405f8f338.tar.gz
rust-28142e4018279d75ff51d31e6b65eb2405f8f338.zip
Auto merge of #17814 - ShoyuVanilla:object-safety, r=Veykril
feat: Implement object safety and its hovering hint

Resolves #17779

- [x] Fill missing implementations
- [x] Hover rendering
- [x] Implement object safety's own test suite, like layout
- [x] Add test cases (from rustc maybe)
- [x] Clean up ugly codes
- [x] Add doc string
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/generics.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs631
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs363
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs62
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs74
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs4
-rw-r--r--src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs1
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs30
19 files changed, 1194 insertions, 66 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
index a3e4da5d1af..e74e3d78988 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
@@ -381,9 +381,9 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
         TyKind::Error.intern(Interner)
     }
 
-    fn is_object_safe(&self, _trait_id: chalk_ir::TraitId<Interner>) -> bool {
-        // FIXME: implement actual object safety
-        true
+    fn is_object_safe(&self, trait_id: chalk_ir::TraitId<Interner>) -> bool {
+        let trait_ = from_chalk_trait_id(trait_id);
+        crate::object_safety::object_safety(self.db, trait_).is_none()
     }
 
     fn closure_kind(
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
index 86228250c20..c4cbaaa3017 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
@@ -2007,7 +2007,7 @@ fn function_traits() {
     );
     check_number(
         r#"
-    //- minicore: coerce_unsized, fn
+    //- minicore: coerce_unsized, fn, dispatch_from_dyn
     fn add2(x: u8) -> u8 {
         x + 2
     }
@@ -2062,7 +2062,7 @@ fn function_traits() {
 fn dyn_trait() {
     check_number(
         r#"
-    //- minicore: coerce_unsized, index, slice
+    //- minicore: coerce_unsized, index, slice, dispatch_from_dyn
     trait Foo {
         fn foo(&self) -> u8 { 10 }
     }
@@ -2085,7 +2085,7 @@ fn dyn_trait() {
     );
     check_number(
         r#"
-    //- minicore: coerce_unsized, index, slice
+    //- minicore: coerce_unsized, index, slice, dispatch_from_dyn
     trait Foo {
         fn foo(&self) -> i32 { 10 }
     }
@@ -2109,7 +2109,7 @@ fn dyn_trait() {
     );
     check_number(
         r#"
-    //- minicore: coerce_unsized, index, slice
+    //- minicore: coerce_unsized, index, slice, dispatch_from_dyn
     trait A {
         fn x(&self) -> i32;
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
index 5972b80d169..6f294494c01 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -89,7 +89,7 @@ fn size_of_val() {
     );
     check_number(
         r#"
-        //- minicore: coerce_unsized, fmt, builtin_impls
+        //- minicore: coerce_unsized, fmt, builtin_impls, dispatch_from_dyn
         extern "rust-intrinsic" {
             pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
         }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
index 9a1f2158bf7..ebbe8a448cc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -11,7 +11,7 @@ use base_db::{
 use hir_def::{
     db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, CallableDefId,
     ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
-    LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId,
+    LifetimeParamId, LocalFieldId, StaticId, TraitId, TypeAliasId, TypeOrConstParamId, VariantId,
 };
 use la_arena::ArenaMap;
 use smallvec::SmallVec;
@@ -24,6 +24,7 @@ use crate::{
     lower::{GenericDefaults, GenericPredicates},
     method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
     mir::{BorrowckResult, MirBody, MirLowerError},
+    object_safety::ObjectSafetyViolation,
     Binders, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult, Interner,
     PolyFnSig, Substitution, TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId,
 };
@@ -107,6 +108,9 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     #[salsa::invoke(crate::layout::target_data_layout_query)]
     fn target_data_layout(&self, krate: CrateId) -> Result<Arc<TargetDataLayout>, Arc<str>>;
 
+    #[salsa::invoke(crate::object_safety::object_safety_of_trait_query)]
+    fn object_safety_of_trait(&self, trait_: TraitId) -> Option<ObjectSafetyViolation>;
+
     #[salsa::invoke(crate::lower::ty_query)]
     #[salsa::cycle(crate::lower::ty_recover)]
     fn ty(&self, def: TyDefId) -> Binders<Ty>;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs
index a96c101a388..89ca707c2e6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs
@@ -225,6 +225,23 @@ impl Generics {
     }
 }
 
+pub(crate) fn trait_self_param_idx(db: &dyn DefDatabase, def: GenericDefId) -> Option<usize> {
+    match def {
+        GenericDefId::TraitId(_) | GenericDefId::TraitAliasId(_) => {
+            let params = db.generic_params(def);
+            params.trait_self_param().map(|idx| idx.into_raw().into_u32() as usize)
+        }
+        GenericDefId::ImplId(_) => None,
+        _ => {
+            let parent_def = parent_generic_def(db, def)?;
+            let parent_params = db.generic_params(parent_def);
+            let parent_self_idx = parent_params.trait_self_param()?.into_raw().into_u32() as usize;
+            let self_params = db.generic_params(def);
+            Some(self_params.len() + parent_self_idx)
+        }
+    }
+}
+
 fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<GenericDefId> {
     let container = match def {
         GenericDefId::FunctionId(it) => it.lookup(db).container,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index 2f4e764f4ce..a316de2b48d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -42,6 +42,7 @@ pub mod lang_items;
 pub mod layout;
 pub mod method_resolution;
 pub mod mir;
+pub mod object_safety;
 pub mod primitive;
 pub mod traits;
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 370d9ba99cb..841e0216ae2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -58,7 +58,7 @@ use crate::{
     },
     db::HirDatabase,
     error_lifetime,
-    generics::{generics, Generics},
+    generics::{generics, trait_self_param_idx, Generics},
     make_binders,
     mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk},
     static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
@@ -1747,21 +1747,7 @@ fn implicitly_sized_clauses<'a, 'subst: 'a>(
         .lang_item(resolver.krate(), LangItem::Sized)
         .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id))?;
 
-    let get_trait_self_idx = |container: ItemContainerId| {
-        if matches!(container, ItemContainerId::TraitId(_)) {
-            let generics = generics(db.upcast(), def);
-            Some(generics.len_self())
-        } else {
-            None
-        }
-    };
-    let trait_self_idx = match def {
-        GenericDefId::TraitId(_) => Some(0),
-        GenericDefId::FunctionId(it) => get_trait_self_idx(it.lookup(db.upcast()).container),
-        GenericDefId::ConstId(it) => get_trait_self_idx(it.lookup(db.upcast()).container),
-        GenericDefId::TypeAliasId(it) => get_trait_self_idx(it.lookup(db.upcast()).container),
-        _ => None,
-    };
+    let trait_self_idx = trait_self_param_idx(db.upcast(), def);
 
     Some(
         substitution
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
index 371a5278dc9..6825bc4862a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
@@ -849,7 +849,7 @@ fn main() {
 fn regression_14966() {
     check_pass(
         r#"
-//- minicore: fn, copy, coerce_unsized
+//- minicore: fn, copy, coerce_unsized, dispatch_from_dyn
 trait A<T> {
     fn a(&self) {}
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs
new file mode 100644
index 00000000000..f3bbb6b04f9
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs
@@ -0,0 +1,631 @@
+//! Compute the object-safety of a trait
+
+use std::ops::ControlFlow;
+
+use chalk_ir::{
+    cast::Cast,
+    visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
+    DebruijnIndex,
+};
+use chalk_solve::rust_ir::InlineBound;
+use hir_def::{
+    lang_item::LangItem, AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId,
+    TypeAliasId,
+};
+use rustc_hash::{FxHashMap, FxHashSet};
+use smallvec::SmallVec;
+
+use crate::{
+    all_super_traits,
+    db::HirDatabase,
+    from_assoc_type_id, from_chalk_trait_id,
+    generics::{generics, trait_self_param_idx},
+    lower::callable_item_sig,
+    to_assoc_type_id, to_chalk_trait_id,
+    utils::elaborate_clause_supertraits,
+    AliasEq, AliasTy, Binders, BoundVar, CallableSig, GoalData, ImplTraitId, Interner, OpaqueTyId,
+    ProjectionTyExt, Solution, Substitution, TraitRef, Ty, TyKind, WhereClause,
+};
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum ObjectSafetyViolation {
+    SizedSelf,
+    SelfReferential,
+    Method(FunctionId, MethodViolationCode),
+    AssocConst(ConstId),
+    GAT(TypeAliasId),
+    // This doesn't exist in rustc, but added for better visualization
+    HasNonSafeSuperTrait(TraitId),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum MethodViolationCode {
+    StaticMethod,
+    ReferencesSelfInput,
+    ReferencesSelfOutput,
+    ReferencesImplTraitInTrait,
+    AsyncFn,
+    WhereClauseReferencesSelf,
+    Generic,
+    UndispatchableReceiver,
+}
+
+pub fn object_safety(db: &dyn HirDatabase, trait_: TraitId) -> Option<ObjectSafetyViolation> {
+    for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() {
+        if db.object_safety_of_trait(super_trait).is_some() {
+            return Some(ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait));
+        }
+    }
+
+    db.object_safety_of_trait(trait_)
+}
+
+pub fn object_safety_with_callback<F>(
+    db: &dyn HirDatabase,
+    trait_: TraitId,
+    cb: &mut F,
+) -> ControlFlow<()>
+where
+    F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>,
+{
+    for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() {
+        if db.object_safety_of_trait(super_trait).is_some() {
+            cb(ObjectSafetyViolation::HasNonSafeSuperTrait(trait_))?;
+        }
+    }
+
+    object_safety_of_trait_with_callback(db, trait_, cb)
+}
+
+pub fn object_safety_of_trait_with_callback<F>(
+    db: &dyn HirDatabase,
+    trait_: TraitId,
+    cb: &mut F,
+) -> ControlFlow<()>
+where
+    F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>,
+{
+    // Check whether this has a `Sized` bound
+    if generics_require_sized_self(db, trait_.into()) {
+        cb(ObjectSafetyViolation::SizedSelf)?;
+    }
+
+    // Check if there exist bounds that referencing self
+    if predicates_reference_self(db, trait_) {
+        cb(ObjectSafetyViolation::SelfReferential)?;
+    }
+    if bounds_reference_self(db, trait_) {
+        cb(ObjectSafetyViolation::SelfReferential)?;
+    }
+
+    // rustc checks for non-lifetime binders here, but we don't support HRTB yet
+
+    let trait_data = db.trait_data(trait_);
+    for (_, assoc_item) in &trait_data.items {
+        object_safety_violation_for_assoc_item(db, trait_, *assoc_item, cb)?;
+    }
+
+    ControlFlow::Continue(())
+}
+
+pub fn object_safety_of_trait_query(
+    db: &dyn HirDatabase,
+    trait_: TraitId,
+) -> Option<ObjectSafetyViolation> {
+    let mut res = None;
+    object_safety_of_trait_with_callback(db, trait_, &mut |osv| {
+        res = Some(osv);
+        ControlFlow::Break(())
+    });
+
+    res
+}
+
+fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> bool {
+    let krate = def.module(db.upcast()).krate();
+    let Some(sized) = db.lang_item(krate, LangItem::Sized).and_then(|l| l.as_trait()) else {
+        return false;
+    };
+
+    let Some(trait_self_param_idx) = trait_self_param_idx(db.upcast(), def) else {
+        return false;
+    };
+
+    let predicates = &*db.generic_predicates(def);
+    let predicates = predicates.iter().map(|p| p.skip_binders().skip_binders().clone());
+    elaborate_clause_supertraits(db, predicates).any(|pred| match pred {
+        WhereClause::Implemented(trait_ref) => {
+            if from_chalk_trait_id(trait_ref.trait_id) == sized {
+                if let TyKind::BoundVar(it) =
+                    *trait_ref.self_type_parameter(Interner).kind(Interner)
+                {
+                    // Since `generic_predicates` is `Binder<Binder<..>>`, the `DebrujinIndex` of
+                    // self-parameter is `1`
+                    return it
+                        .index_if_bound_at(DebruijnIndex::ONE)
+                        .is_some_and(|idx| idx == trait_self_param_idx);
+                }
+            }
+            false
+        }
+        _ => false,
+    })
+}
+
+// rustc gathers all the spans that references `Self` for error rendering,
+// but we don't have good way to render such locations.
+// So, just return single boolean value for existence of such `Self` reference
+fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool {
+    db.generic_predicates(trait_.into())
+        .iter()
+        .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No))
+}
+
+// Same as the above, `predicates_reference_self`
+fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool {
+    let trait_data = db.trait_data(trait_);
+    trait_data
+        .items
+        .iter()
+        .filter_map(|(_, it)| match *it {
+            AssocItemId::TypeAliasId(id) => {
+                let assoc_ty_id = to_assoc_type_id(id);
+                let assoc_ty_data = db.associated_ty_data(assoc_ty_id);
+                Some(assoc_ty_data)
+            }
+            _ => None,
+        })
+        .any(|assoc_ty_data| {
+            assoc_ty_data.binders.skip_binders().bounds.iter().any(|bound| {
+                let def = from_assoc_type_id(assoc_ty_data.id).into();
+                match bound.skip_binders() {
+                    InlineBound::TraitBound(it) => it.args_no_self.iter().any(|arg| {
+                        contains_illegal_self_type_reference(
+                            db,
+                            def,
+                            trait_,
+                            arg,
+                            DebruijnIndex::ONE,
+                            AllowSelfProjection::Yes,
+                        )
+                    }),
+                    InlineBound::AliasEqBound(it) => it.parameters.iter().any(|arg| {
+                        contains_illegal_self_type_reference(
+                            db,
+                            def,
+                            trait_,
+                            arg,
+                            DebruijnIndex::ONE,
+                            AllowSelfProjection::Yes,
+                        )
+                    }),
+                }
+            })
+        })
+}
+
+#[derive(Clone, Copy)]
+enum AllowSelfProjection {
+    Yes,
+    No,
+}
+
+fn predicate_references_self(
+    db: &dyn HirDatabase,
+    trait_: TraitId,
+    predicate: &Binders<Binders<WhereClause>>,
+    allow_self_projection: AllowSelfProjection,
+) -> bool {
+    match predicate.skip_binders().skip_binders() {
+        WhereClause::Implemented(trait_ref) => {
+            trait_ref.substitution.iter(Interner).skip(1).any(|arg| {
+                contains_illegal_self_type_reference(
+                    db,
+                    trait_.into(),
+                    trait_,
+                    arg,
+                    DebruijnIndex::ONE,
+                    allow_self_projection,
+                )
+            })
+        }
+        WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), .. }) => {
+            proj.substitution.iter(Interner).skip(1).any(|arg| {
+                contains_illegal_self_type_reference(
+                    db,
+                    trait_.into(),
+                    trait_,
+                    arg,
+                    DebruijnIndex::ONE,
+                    allow_self_projection,
+                )
+            })
+        }
+        _ => false,
+    }
+}
+
+fn contains_illegal_self_type_reference<T: TypeVisitable<Interner>>(
+    db: &dyn HirDatabase,
+    def: GenericDefId,
+    trait_: TraitId,
+    t: &T,
+    outer_binder: DebruijnIndex,
+    allow_self_projection: AllowSelfProjection,
+) -> bool {
+    let Some(trait_self_param_idx) = trait_self_param_idx(db.upcast(), def) else {
+        return false;
+    };
+    struct IllegalSelfTypeVisitor<'a> {
+        db: &'a dyn HirDatabase,
+        trait_: TraitId,
+        super_traits: Option<SmallVec<[TraitId; 4]>>,
+        trait_self_param_idx: usize,
+        allow_self_projection: AllowSelfProjection,
+    }
+    impl<'a> TypeVisitor<Interner> for IllegalSelfTypeVisitor<'a> {
+        type BreakTy = ();
+
+        fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
+            self
+        }
+
+        fn interner(&self) -> Interner {
+            Interner
+        }
+
+        fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow<Self::BreakTy> {
+            match ty.kind(Interner) {
+                TyKind::BoundVar(BoundVar { debruijn, index }) => {
+                    if *debruijn == outer_binder && *index == self.trait_self_param_idx {
+                        ControlFlow::Break(())
+                    } else {
+                        ty.super_visit_with(self.as_dyn(), outer_binder)
+                    }
+                }
+                TyKind::Alias(AliasTy::Projection(proj)) => match self.allow_self_projection {
+                    AllowSelfProjection::Yes => {
+                        let trait_ = proj.trait_(self.db);
+                        if self.super_traits.is_none() {
+                            self.super_traits =
+                                Some(all_super_traits(self.db.upcast(), self.trait_));
+                        }
+                        if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) {
+                            ControlFlow::Continue(())
+                        } else {
+                            ty.super_visit_with(self.as_dyn(), outer_binder)
+                        }
+                    }
+                    AllowSelfProjection::No => ty.super_visit_with(self.as_dyn(), outer_binder),
+                },
+                _ => ty.super_visit_with(self.as_dyn(), outer_binder),
+            }
+        }
+
+        fn visit_const(
+            &mut self,
+            constant: &chalk_ir::Const<Interner>,
+            outer_binder: DebruijnIndex,
+        ) -> std::ops::ControlFlow<Self::BreakTy> {
+            constant.data(Interner).ty.super_visit_with(self.as_dyn(), outer_binder)
+        }
+    }
+
+    let mut visitor = IllegalSelfTypeVisitor {
+        db,
+        trait_,
+        super_traits: None,
+        trait_self_param_idx,
+        allow_self_projection,
+    };
+    t.visit_with(visitor.as_dyn(), outer_binder).is_break()
+}
+
+fn object_safety_violation_for_assoc_item<F>(
+    db: &dyn HirDatabase,
+    trait_: TraitId,
+    item: AssocItemId,
+    cb: &mut F,
+) -> ControlFlow<()>
+where
+    F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>,
+{
+    // Any item that has a `Self : Sized` requisite is otherwise
+    // exempt from the regulations.
+    if generics_require_sized_self(db, item.into()) {
+        return ControlFlow::Continue(());
+    }
+
+    match item {
+        AssocItemId::ConstId(it) => cb(ObjectSafetyViolation::AssocConst(it)),
+        AssocItemId::FunctionId(it) => {
+            virtual_call_violations_for_method(db, trait_, it, &mut |mvc| {
+                cb(ObjectSafetyViolation::Method(it, mvc))
+            })
+        }
+        AssocItemId::TypeAliasId(it) => {
+            let def_map = db.crate_def_map(trait_.krate(db.upcast()));
+            if def_map.is_unstable_feature_enabled(&intern::sym::generic_associated_type_extended) {
+                ControlFlow::Continue(())
+            } else {
+                let generic_params = db.generic_params(item.into());
+                if generic_params.len_type_or_consts() > 0 {
+                    cb(ObjectSafetyViolation::GAT(it))
+                } else {
+                    ControlFlow::Continue(())
+                }
+            }
+        }
+    }
+}
+
+fn virtual_call_violations_for_method<F>(
+    db: &dyn HirDatabase,
+    trait_: TraitId,
+    func: FunctionId,
+    cb: &mut F,
+) -> ControlFlow<()>
+where
+    F: FnMut(MethodViolationCode) -> ControlFlow<()>,
+{
+    let func_data = db.function_data(func);
+    if !func_data.has_self_param() {
+        cb(MethodViolationCode::StaticMethod)?;
+    }
+
+    if func_data.is_async() {
+        cb(MethodViolationCode::AsyncFn)?;
+    }
+
+    let sig = callable_item_sig(db, func.into());
+    if sig.skip_binders().params().iter().skip(1).any(|ty| {
+        contains_illegal_self_type_reference(
+            db,
+            func.into(),
+            trait_,
+            ty,
+            DebruijnIndex::INNERMOST,
+            AllowSelfProjection::Yes,
+        )
+    }) {
+        cb(MethodViolationCode::ReferencesSelfInput)?;
+    }
+
+    if contains_illegal_self_type_reference(
+        db,
+        func.into(),
+        trait_,
+        sig.skip_binders().ret(),
+        DebruijnIndex::INNERMOST,
+        AllowSelfProjection::Yes,
+    ) {
+        cb(MethodViolationCode::ReferencesSelfOutput)?;
+    }
+
+    if !func_data.is_async() {
+        if let Some(mvc) = contains_illegal_impl_trait_in_trait(db, &sig) {
+            cb(mvc)?;
+        }
+    }
+
+    let generic_params = db.generic_params(func.into());
+    if generic_params.len_type_or_consts() > 0 {
+        cb(MethodViolationCode::Generic)?;
+    }
+
+    if func_data.has_self_param() && !receiver_is_dispatchable(db, trait_, func, &sig) {
+        cb(MethodViolationCode::UndispatchableReceiver)?;
+    }
+
+    let predicates = &*db.generic_predicates(func.into());
+    let mut parent_predicates = (*db.generic_predicates(trait_.into()))
+        .iter()
+        .map(|b| b.skip_binders().skip_binders().clone())
+        .fold(FxHashMap::default(), |mut acc, item| {
+            acc.entry(item)
+                .and_modify(|cnt| {
+                    *cnt += 1;
+                })
+                .or_insert(1);
+            acc
+        });
+    let trait_self_idx = trait_self_param_idx(db.upcast(), func.into());
+    for pred in predicates {
+        let pred = pred.skip_binders().skip_binders();
+
+        // Skip predicates from parent, i.e. the trait that contains this method
+        if let Some(cnt) = parent_predicates.get_mut(pred) {
+            if *cnt > 0 {
+                *cnt -= 1;
+                continue;
+            }
+        }
+
+        if matches!(pred, WhereClause::TypeOutlives(_)) {
+            continue;
+        }
+
+        // Allow `impl AutoTrait` predicates
+        if let WhereClause::Implemented(TraitRef { trait_id, substitution }) = pred {
+            let trait_data = db.trait_data(from_chalk_trait_id(*trait_id));
+            if trait_data.is_auto
+                && substitution
+                    .as_slice(Interner)
+                    .first()
+                    .and_then(|arg| arg.ty(Interner))
+                    .and_then(|ty| ty.bound_var(Interner))
+                    .is_some_and(|b| {
+                        b.debruijn == DebruijnIndex::ONE && Some(b.index) == trait_self_idx
+                    })
+            {
+                continue;
+            }
+        }
+
+        if contains_illegal_self_type_reference(
+            db,
+            func.into(),
+            trait_,
+            pred,
+            DebruijnIndex::ONE,
+            AllowSelfProjection::Yes,
+        ) {
+            cb(MethodViolationCode::WhereClauseReferencesSelf)?;
+            break;
+        }
+    }
+
+    ControlFlow::Continue(())
+}
+
+fn receiver_is_dispatchable(
+    db: &dyn HirDatabase,
+    trait_: TraitId,
+    func: FunctionId,
+    sig: &Binders<CallableSig>,
+) -> bool {
+    let Some(trait_self_idx) = trait_self_param_idx(db.upcast(), func.into()) else {
+        return false;
+    };
+
+    // `self: Self` can't be dispatched on, but this is already considered object safe.
+    // See rustc's comment on https://github.com/rust-lang/rust/blob/3f121b9461cce02a703a0e7e450568849dfaa074/compiler/rustc_trait_selection/src/traits/object_safety.rs#L433-L437
+    if sig
+        .skip_binders()
+        .params()
+        .first()
+        .and_then(|receiver| receiver.bound_var(Interner))
+        .is_some_and(|b| {
+            b == BoundVar { debruijn: DebruijnIndex::INNERMOST, index: trait_self_idx }
+        })
+    {
+        return true;
+    }
+
+    let placeholder_subst = generics(db.upcast(), func.into()).placeholder_subst(db);
+
+    let substituted_sig = sig.clone().substitute(Interner, &placeholder_subst);
+    let Some(receiver_ty) = substituted_sig.params().first() else {
+        return false;
+    };
+
+    let krate = func.module(db.upcast()).krate();
+    let traits = (
+        db.lang_item(krate, LangItem::Unsize).and_then(|it| it.as_trait()),
+        db.lang_item(krate, LangItem::DispatchFromDyn).and_then(|it| it.as_trait()),
+    );
+    let (Some(unsize_did), Some(dispatch_from_dyn_did)) = traits else {
+        return false;
+    };
+
+    // Type `U`
+    let unsized_self_ty =
+        TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32)).intern(Interner);
+    // `Receiver[Self => U]`
+    let Some(unsized_receiver_ty) = receiver_for_self_ty(db, func, unsized_self_ty.clone()) else {
+        return false;
+    };
+
+    let self_ty = placeholder_subst.as_slice(Interner)[trait_self_idx].assert_ty_ref(Interner);
+    let unsized_predicate = WhereClause::Implemented(TraitRef {
+        trait_id: to_chalk_trait_id(unsize_did),
+        substitution: Substitution::from_iter(Interner, [self_ty.clone(), unsized_self_ty.clone()]),
+    });
+    let trait_predicate = WhereClause::Implemented(TraitRef {
+        trait_id: to_chalk_trait_id(trait_),
+        substitution: Substitution::from_iter(
+            Interner,
+            std::iter::once(unsized_self_ty.clone().cast(Interner))
+                .chain(placeholder_subst.iter(Interner).skip(1).cloned()),
+        ),
+    });
+
+    let generic_predicates = &*db.generic_predicates(func.into());
+
+    let clauses = std::iter::once(unsized_predicate)
+        .chain(std::iter::once(trait_predicate))
+        .chain(generic_predicates.iter().map(|pred| {
+            pred.clone().substitute(Interner, &placeholder_subst).into_value_and_skipped_binders().0
+        }))
+        .map(|pred| {
+            pred.cast::<chalk_ir::ProgramClause<Interner>>(Interner).into_from_env_clause(Interner)
+        });
+    let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses);
+
+    let obligation = WhereClause::Implemented(TraitRef {
+        trait_id: to_chalk_trait_id(dispatch_from_dyn_did),
+        substitution: Substitution::from_iter(Interner, [receiver_ty.clone(), unsized_receiver_ty]),
+    });
+    let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(obligation)).intern(Interner);
+
+    let in_env = chalk_ir::InEnvironment::new(&env, goal);
+
+    let mut table = chalk_solve::infer::InferenceTable::<Interner>::new();
+    let canonicalized = table.canonicalize(Interner, in_env);
+    let solution = db.trait_solve(krate, None, canonicalized.quantified);
+
+    matches!(solution, Some(Solution::Unique(_)))
+}
+
+fn receiver_for_self_ty(db: &dyn HirDatabase, func: FunctionId, ty: Ty) -> Option<Ty> {
+    let generics = generics(db.upcast(), func.into());
+    let trait_self_idx = trait_self_param_idx(db.upcast(), func.into())?;
+    let subst = generics.placeholder_subst(db);
+    let subst = Substitution::from_iter(
+        Interner,
+        subst.iter(Interner).enumerate().map(|(idx, arg)| {
+            if idx == trait_self_idx {
+                ty.clone().cast(Interner)
+            } else {
+                arg.clone()
+            }
+        }),
+    );
+    let sig = callable_item_sig(db, func.into());
+    let sig = sig.substitute(Interner, &subst);
+    sig.params_and_return.first().cloned()
+}
+
+fn contains_illegal_impl_trait_in_trait(
+    db: &dyn HirDatabase,
+    sig: &Binders<CallableSig>,
+) -> Option<MethodViolationCode> {
+    struct OpaqueTypeCollector(FxHashSet<OpaqueTyId>);
+
+    impl TypeVisitor<Interner> for OpaqueTypeCollector {
+        type BreakTy = ();
+
+        fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
+            self
+        }
+
+        fn interner(&self) -> Interner {
+            Interner
+        }
+
+        fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow<Self::BreakTy> {
+            if let TyKind::OpaqueType(opaque_ty_id, _) = ty.kind(Interner) {
+                self.0.insert(*opaque_ty_id);
+            }
+            ty.super_visit_with(self.as_dyn(), outer_binder)
+        }
+    }
+
+    let ret = sig.skip_binders().ret();
+    let mut visitor = OpaqueTypeCollector(FxHashSet::default());
+    ret.visit_with(visitor.as_dyn(), DebruijnIndex::INNERMOST);
+
+    // Since we haven't implemented RPITIT in proper way like rustc yet,
+    // just check whether `ret` contains RPIT for now
+    for opaque_ty in visitor.0 {
+        let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.into());
+        if matches!(impl_trait_id, ImplTraitId::ReturnTypeImplTrait(..)) {
+            return Some(MethodViolationCode::ReferencesImplTraitInTrait);
+        }
+    }
+
+    None
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs
new file mode 100644
index 00000000000..585313e5b9b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs
@@ -0,0 +1,363 @@
+use std::ops::ControlFlow;
+
+use hir_def::db::DefDatabase;
+use rustc_hash::{FxHashMap, FxHashSet};
+use syntax::ToSmolStr;
+use test_fixture::WithFixture;
+
+use crate::{object_safety::object_safety_with_callback, test_db::TestDB};
+
+use super::{
+    MethodViolationCode::{self, *},
+    ObjectSafetyViolation,
+};
+
+use ObjectSafetyViolationKind::*;
+
+#[allow(clippy::upper_case_acronyms)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+enum ObjectSafetyViolationKind {
+    SizedSelf,
+    SelfReferential,
+    Method(MethodViolationCode),
+    AssocConst,
+    GAT,
+    HasNonSafeSuperTrait,
+}
+
+fn check_object_safety<'a>(
+    ra_fixture: &str,
+    expected: impl IntoIterator<Item = (&'a str, Vec<ObjectSafetyViolationKind>)>,
+) {
+    let mut expected: FxHashMap<_, _> =
+        expected.into_iter().map(|(id, osvs)| (id, FxHashSet::from_iter(osvs))).collect();
+    let (db, file_ids) = TestDB::with_many_files(ra_fixture);
+    for (trait_id, name) in file_ids.into_iter().flat_map(|file_id| {
+        let module_id = db.module_for_file(file_id);
+        let def_map = module_id.def_map(&db);
+        let scope = &def_map[module_id.local_id].scope;
+        scope
+            .declarations()
+            .filter_map(|def| {
+                if let hir_def::ModuleDefId::TraitId(trait_id) = def {
+                    let name =
+                        db.trait_data(trait_id).name.display_no_db(file_id.edition()).to_smolstr();
+                    Some((trait_id, name))
+                } else {
+                    None
+                }
+            })
+            .collect::<Vec<_>>()
+    }) {
+        let Some(expected) = expected.remove(name.as_str()) else {
+            continue;
+        };
+        let mut osvs = FxHashSet::default();
+        object_safety_with_callback(&db, trait_id, &mut |osv| {
+            osvs.insert(match osv {
+                ObjectSafetyViolation::SizedSelf => SizedSelf,
+                ObjectSafetyViolation::SelfReferential => SelfReferential,
+                ObjectSafetyViolation::Method(_, mvc) => Method(mvc),
+                ObjectSafetyViolation::AssocConst(_) => AssocConst,
+                ObjectSafetyViolation::GAT(_) => GAT,
+                ObjectSafetyViolation::HasNonSafeSuperTrait(_) => HasNonSafeSuperTrait,
+            });
+            ControlFlow::Continue(())
+        });
+        assert_eq!(osvs, expected, "Object safety violations for `{name}` do not match;");
+    }
+
+    let remains: Vec<_> = expected.keys().collect();
+    assert!(remains.is_empty(), "Following traits do not exist in the test fixture; {remains:?}");
+}
+
+#[test]
+fn item_bounds_can_reference_self() {
+    check_object_safety(
+        r#"
+//- minicore: eq
+pub trait Foo {
+    type X: PartialEq;
+    type Y: PartialEq<Self::Y>;
+    type Z: PartialEq<Self::Y>;
+}
+"#,
+        [("Foo", vec![])],
+    );
+}
+
+#[test]
+fn associated_consts() {
+    check_object_safety(
+        r#"
+trait Bar {
+    const X: usize;
+}
+"#,
+        [("Bar", vec![AssocConst])],
+    );
+}
+
+#[test]
+fn bounds_reference_self() {
+    check_object_safety(
+        r#"
+//- minicore: eq
+trait X {
+    type U: PartialEq<Self>;
+}
+"#,
+        [("X", vec![SelfReferential])],
+    );
+}
+
+#[test]
+fn by_value_self() {
+    check_object_safety(
+        r#"
+//- minicore: dispatch_from_dyn
+trait Bar {
+    fn bar(self);
+}
+
+trait Baz {
+    fn baz(self: Self);
+}
+
+trait Quux {
+    // Legal because of the where clause:
+    fn baz(self: Self) where Self : Sized;
+}
+"#,
+        [("Bar", vec![]), ("Baz", vec![]), ("Quux", vec![])],
+    );
+}
+
+#[test]
+fn generic_methods() {
+    check_object_safety(
+        r#"
+//- minicore: dispatch_from_dyn
+trait Bar {
+    fn bar<T>(&self, t: T);
+}
+
+trait Quux {
+    fn bar<T>(&self, t: T)
+        where Self : Sized;
+}
+
+trait Qax {
+    fn bar<'a>(&self, t: &'a ());
+}
+"#,
+        [("Bar", vec![Method(Generic)]), ("Quux", vec![]), ("Qax", vec![])],
+    );
+}
+
+#[test]
+fn mentions_self() {
+    check_object_safety(
+        r#"
+//- minicore: dispatch_from_dyn
+trait Bar {
+    fn bar(&self, x: &Self);
+}
+
+trait Baz {
+    fn baz(&self) -> Self;
+}
+
+trait Quux {
+    fn quux(&self, s: &Self) -> Self where Self : Sized;
+}
+"#,
+        [
+            ("Bar", vec![Method(ReferencesSelfInput)]),
+            ("Baz", vec![Method(ReferencesSelfOutput)]),
+            ("Quux", vec![]),
+        ],
+    );
+}
+
+#[test]
+fn no_static() {
+    check_object_safety(
+        r#"
+//- minicore: dispatch_from_dyn
+trait Foo {
+    fn foo() {}
+}
+"#,
+        [("Foo", vec![Method(StaticMethod)])],
+    );
+}
+
+#[test]
+fn sized_self() {
+    check_object_safety(
+        r#"
+//- minicore: dispatch_from_dyn
+trait Bar: Sized {
+    fn bar<T>(&self, t: T);
+}
+"#,
+        [("Bar", vec![SizedSelf])],
+    );
+
+    check_object_safety(
+        r#"
+//- minicore: dispatch_from_dyn
+trait Bar
+    where Self : Sized
+{
+    fn bar<T>(&self, t: T);
+}
+"#,
+        [("Bar", vec![SizedSelf])],
+    );
+}
+
+#[test]
+fn supertrait_gat() {
+    check_object_safety(
+        r#"
+//- minicore: dispatch_from_dyn
+trait GatTrait {
+    type Gat<T>;
+}
+
+trait SuperTrait<T>: GatTrait {}
+"#,
+        [("GatTrait", vec![GAT]), ("SuperTrait", vec![HasNonSafeSuperTrait])],
+    );
+}
+
+#[test]
+fn supertrait_mentions_self() {
+    check_object_safety(
+        r#"
+//- minicore: dispatch_from_dyn
+trait Bar<T> {
+    fn bar(&self, x: &T);
+}
+
+trait Baz : Bar<Self> {
+}
+"#,
+        [("Bar", vec![]), ("Baz", vec![SizedSelf, SelfReferential])],
+    );
+}
+
+#[test]
+fn rustc_issue_19538() {
+    check_object_safety(
+        r#"
+//- minicore: dispatch_from_dyn
+trait Foo {
+    fn foo<T>(&self, val: T);
+}
+
+trait Bar: Foo {}
+"#,
+        [("Foo", vec![Method(Generic)]), ("Bar", vec![HasNonSafeSuperTrait])],
+    );
+}
+
+#[test]
+fn rustc_issue_22040() {
+    check_object_safety(
+        r#"
+//- minicore: fmt, eq, dispatch_from_dyn
+use core::fmt::Debug;
+
+trait Expr: Debug + PartialEq {
+    fn print_element_count(&self);
+}
+"#,
+        [("Expr", vec![SelfReferential])],
+    );
+}
+
+#[test]
+fn rustc_issue_102762() {
+    check_object_safety(
+        r#"
+//- minicore: future, send, sync, dispatch_from_dyn, deref
+use core::pin::Pin;
+
+struct Box<T: ?Sized> {}
+impl<T: ?Sized> core::ops::Deref for Box<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        loop {}
+    }
+}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U>> for Box<T> {}
+
+struct Vec<T> {}
+
+pub trait Fetcher: Send + Sync {
+    fn get<'a>(self: &'a Box<Self>) -> Pin<Box<dyn Future<Output = Vec<u8>> + 'a>>
+    where
+        Self: Sync,
+    {
+        loop {}
+    }
+}
+"#,
+        [("Fetcher", vec![Method(UndispatchableReceiver)])],
+    );
+}
+
+#[test]
+fn rustc_issue_102933() {
+    check_object_safety(
+        r#"
+//- minicore: future, dispatch_from_dyn, deref
+use core::future::Future;
+
+struct Box<T: ?Sized> {}
+impl<T: ?Sized> core::ops::Deref for Box<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        loop {}
+    }
+}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U>> for Box<T> {}
+
+pub trait Service {
+    type Response;
+    type Future: Future<Output = Self::Response>;
+}
+
+pub trait A1: Service<Response = i32> {}
+
+pub trait A2: Service<Future = Box<dyn Future<Output = i32>>> + A1 {
+    fn foo(&self) {}
+}
+
+pub trait B1: Service<Future = Box<dyn Future<Output = i32>>> {}
+
+pub trait B2: Service<Response = i32> + B1 {
+    fn foo(&self) {}
+}
+        "#,
+        [("A2", vec![]), ("B2", vec![])],
+    );
+}
+
+#[test]
+fn rustc_issue_106247() {
+    check_object_safety(
+        r#"
+//- minicore: sync, dispatch_from_dyn
+pub trait Trait {
+    fn method(&self) where Self: Sync;
+}
+"#,
+        [("Trait", vec![])],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index 17fbe4db317..98f30f0f6f7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -1065,7 +1065,7 @@ fn test() {
 fn bare_dyn_trait_binders_9639() {
     check_no_mismatches(
         r#"
-//- minicore: fn, coerce_unsized
+//- minicore: fn, coerce_unsized, dispatch_from_dyn
 fn infix_parse<T, S>(_state: S, _level_code: &Fn(S)) -> T {
     loop {}
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index a98cff2a08b..0b2d6bdd259 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -1448,14 +1448,20 @@ fn foo<X>() -> Foo<impl Future<Output = ()>> {
 fn dyn_trait() {
     check_infer(
         r#"
-//- minicore: sized
+//- minicore: deref, dispatch_from_dyn
 trait Trait<T> {
     fn foo(&self) -> T;
     fn foo2(&self) -> i64;
 }
-fn bar() -> dyn Trait<u64> {}
 
-fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) {
+struct Box<T: ?Sized> {}
+impl<T: ?Sized> core::ops::Deref for Box<T> {
+    type Target = T;
+}
+
+fn bar() -> Box<dyn Trait<u64>> {}
+
+fn test(x: Box<dyn Trait<u64>>, y: &dyn Trait<u64>) {
     x;
     y;
     let z = bar();
@@ -1469,27 +1475,27 @@ fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) {
         expect![[r#"
             29..33 'self': &'? Self
             54..58 'self': &'? Self
-            97..99 '{}': dyn Trait<u64>
-            109..110 'x': dyn Trait<u64>
-            128..129 'y': &'? dyn Trait<u64>
-            148..265 '{     ...2(); }': ()
-            154..155 'x': dyn Trait<u64>
-            161..162 'y': &'? dyn Trait<u64>
-            172..173 'z': dyn Trait<u64>
-            176..179 'bar': fn bar() -> dyn Trait<u64>
-            176..181 'bar()': dyn Trait<u64>
-            187..188 'x': dyn Trait<u64>
-            187..194 'x.foo()': u64
-            200..201 'y': &'? dyn Trait<u64>
-            200..207 'y.foo()': u64
-            213..214 'z': dyn Trait<u64>
-            213..220 'z.foo()': u64
-            226..227 'x': dyn Trait<u64>
-            226..234 'x.foo2()': i64
-            240..241 'y': &'? dyn Trait<u64>
-            240..248 'y.foo2()': i64
-            254..255 'z': dyn Trait<u64>
-            254..262 'z.foo2()': i64
+            198..200 '{}': Box<dyn Trait<u64>>
+            210..211 'x': Box<dyn Trait<u64>>
+            234..235 'y': &'? dyn Trait<u64>
+            254..371 '{     ...2(); }': ()
+            260..261 'x': Box<dyn Trait<u64>>
+            267..268 'y': &'? dyn Trait<u64>
+            278..279 'z': Box<dyn Trait<u64>>
+            282..285 'bar': fn bar() -> Box<dyn Trait<u64>>
+            282..287 'bar()': Box<dyn Trait<u64>>
+            293..294 'x': Box<dyn Trait<u64>>
+            293..300 'x.foo()': u64
+            306..307 'y': &'? dyn Trait<u64>
+            306..313 'y.foo()': u64
+            319..320 'z': Box<dyn Trait<u64>>
+            319..326 'z.foo()': u64
+            332..333 'x': Box<dyn Trait<u64>>
+            332..340 'x.foo2()': i64
+            346..347 'y': &'? dyn Trait<u64>
+            346..354 'y.foo2()': i64
+            360..361 'z': Box<dyn Trait<u64>>
+            360..368 'z.foo2()': i64
         "#]],
     );
 }
@@ -1534,7 +1540,7 @@ fn test(s: S<u32, i32>) {
 fn dyn_trait_bare() {
     check_infer(
         r#"
-//- minicore: sized
+//- minicore: sized, dispatch_from_dyn
 trait Trait {
     fn foo(&self) -> u64;
 }
@@ -1570,7 +1576,7 @@ fn test(x: Trait, y: &Trait) -> u64 {
 
     check_infer_with_mismatches(
         r#"
-//- minicore: fn, coerce_unsized
+//- minicore: fn, coerce_unsized, dispatch_from_dyn
 struct S;
 impl S {
     fn foo(&self) {}
@@ -3106,7 +3112,7 @@ fn dyn_fn_param_informs_call_site_closure_signature() {
     cov_mark::check!(dyn_fn_param_informs_call_site_closure_signature);
     check_types(
         r#"
-//- minicore: fn, coerce_unsized
+//- minicore: fn, coerce_unsized, dispatch_from_dyn
 struct S;
 impl S {
     fn inherent(&self) -> u8 { 0 }
@@ -3151,7 +3157,7 @@ fn infer_box_fn_arg() {
     // The type mismatch is because we don't define Unsize and CoerceUnsized
     check_infer_with_mismatches(
         r#"
-//- minicore: fn, deref, option
+//- minicore: fn, deref, option, dispatch_from_dyn
 #[lang = "owned_box"]
 pub struct Box<T: ?Sized> {
     inner: *mut T,
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 58340c74fea..96a6e6f1f12 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -66,7 +66,7 @@ use hir_ty::{
     diagnostics::BodyValidationDiagnostic,
     error_lifetime, known_const_to_ast,
     layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
-    method_resolution::{self},
+    method_resolution,
     mir::{interpret_mir, MutBorrowKind},
     primitive::UintTy,
     traits::FnTrait,
@@ -145,6 +145,7 @@ pub use {
         display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
         layout::LayoutError,
         mir::{MirEvalError, MirLowerError},
+        object_safety::{MethodViolationCode, ObjectSafetyViolation},
         FnAbi, PointerCast, Safety,
     },
     // FIXME: Properly encapsulate mir
@@ -2641,6 +2642,10 @@ impl Trait {
             .count()
     }
 
+    pub fn object_safety(&self, db: &dyn HirDatabase) -> Option<ObjectSafetyViolation> {
+        hir_ty::object_safety::object_safety(db, self.id)
+    }
+
     fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> {
         db.trait_data(self.id)
             .macro_calls
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
index 87932bf989f..18647206236 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -84,7 +84,7 @@ fn foo() {
     fn replace_filter_map_next_dont_work_for_not_sized_issues_16596() {
         check_diagnostics(
             r#"
-//- minicore: iterators
+//- minicore: iterators, dispatch_from_dyn
 fn foo() {
     let mut j = [0].into_iter();
     let i: &mut dyn Iterator<Item = i32>  = &mut j;
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index 3e41b42be44..ead2d45847d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -4,7 +4,8 @@ use std::{mem, ops::Not};
 use either::Either;
 use hir::{
     Adt, AsAssocItem, AsExternAssocItem, CaptureKind, HasCrate, HasSource, HirDisplay, Layout,
-    LayoutError, Name, Semantics, Trait, Type, TypeInfo,
+    LayoutError, MethodViolationCode, Name, ObjectSafetyViolation, Semantics, Trait, Type,
+    TypeInfo,
 };
 use ide_db::{
     base_db::SourceDatabase,
@@ -526,6 +527,14 @@ pub(super) fn definition(
         _ => None,
     };
 
+    let object_safety_info = if let Definition::Trait(it) = def {
+        let mut object_safety_info = String::new();
+        render_object_safety(db, &mut object_safety_info, it.object_safety(db));
+        Some(object_safety_info)
+    } else {
+        None
+    };
+
     let mut desc = String::new();
     if let Some(notable_traits) = render_notable_trait_comment(db, notable_traits, edition) {
         desc.push_str(&notable_traits);
@@ -535,6 +544,10 @@ pub(super) fn definition(
         desc.push_str(&layout_info);
         desc.push('\n');
     }
+    if let Some(object_safety_info) = object_safety_info {
+        desc.push_str(&object_safety_info);
+        desc.push('\n');
+    }
     desc.push_str(&label);
     if let Some(value) = value {
         desc.push_str(" = ");
@@ -964,3 +977,62 @@ fn keyword_hints(
         _ => KeywordHint::new(token.text().to_owned(), format!("{}_keyword", token.text())),
     }
 }
+
+fn render_object_safety(
+    db: &RootDatabase,
+    buf: &mut String,
+    safety: Option<ObjectSafetyViolation>,
+) {
+    let Some(osv) = safety else {
+        buf.push_str("// Object Safety: Yes");
+        return;
+    };
+    buf.push_str("// Object Safety: No\n// - Reason: ");
+    match osv {
+        ObjectSafetyViolation::SizedSelf => {
+            buf.push_str("has a `Self: Sized` bound");
+        }
+        ObjectSafetyViolation::SelfReferential => {
+            buf.push_str("has a bound that references `Self`");
+        }
+        ObjectSafetyViolation::Method(func, mvc) => {
+            let name = hir::Function::from(func).name(db);
+            format_to!(
+                buf,
+                "has a method `{}` that is non dispatchable because of:\n//   - ",
+                name.as_str()
+            );
+            let desc = match mvc {
+                MethodViolationCode::StaticMethod => "missing a receiver",
+                MethodViolationCode::ReferencesSelfInput => "a parameter references `Self`",
+                MethodViolationCode::ReferencesSelfOutput => "the return type references `Self`",
+                MethodViolationCode::ReferencesImplTraitInTrait => {
+                    "the return type contains `impl Trait`"
+                }
+                MethodViolationCode::AsyncFn => "being async",
+                MethodViolationCode::WhereClauseReferencesSelf => {
+                    "a where clause references `Self`"
+                }
+                MethodViolationCode::Generic => "a non-lifetime generic parameter",
+                MethodViolationCode::UndispatchableReceiver => "a non-dispatchable receiver type",
+            };
+            buf.push_str(desc);
+        }
+        ObjectSafetyViolation::AssocConst(const_) => {
+            let name = hir::Const::from(const_).name(db);
+            if let Some(name) = name {
+                format_to!(buf, "has an associated constant `{}`", name.as_str());
+            } else {
+                buf.push_str("has an associated constant");
+            }
+        }
+        ObjectSafetyViolation::GAT(alias) => {
+            let name = hir::TypeAlias::from(alias).name(db);
+            format_to!(buf, "has a generic associated type `{}`", name.as_str());
+        }
+        ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait) => {
+            let name = hir::Trait::from(super_trait).name(db);
+            format_to!(buf, "has a object unsafe supertrait `{}`", name.as_str());
+        }
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 9585bdbe4c5..f2f83d538cc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -5442,7 +5442,7 @@ const FOO$0: Option<&i32> = Some(2).as_ref();
 fn hover_const_eval_dyn_trait() {
     check(
         r#"
-//- minicore: fmt, coerce_unsized, builtin_impls
+//- minicore: fmt, coerce_unsized, builtin_impls, dispatch_from_dyn
 use core::fmt::Debug;
 
 const FOO$0: &dyn Debug = &2i32;
@@ -7107,6 +7107,7 @@ impl T$0 for () {}
             ```
 
             ```rust
+            // Object Safety: Yes
             trait T {}
             ```
         "#]],
@@ -7126,6 +7127,7 @@ impl T$0 for () {}
             ```
 
             ```rust
+            // Object Safety: Yes
             trait T {}
             ```
         "#]],
@@ -7149,6 +7151,9 @@ impl T$0 for () {}
             ```
 
             ```rust
+            // Object Safety: No
+            // - Reason: has a method `func` that is non dispatchable because of:
+            //   - missing a receiver
             trait T { /* … */ }
             ```
         "#]],
@@ -7172,6 +7177,9 @@ impl T$0 for () {}
             ```
 
             ```rust
+            // Object Safety: No
+            // - Reason: has a method `func` that is non dispatchable because of:
+            //   - missing a receiver
             trait T {
                 fn func();
                 const FLAG: i32;
@@ -7199,6 +7207,9 @@ impl T$0 for () {}
             ```
 
             ```rust
+            // Object Safety: No
+            // - Reason: has a method `func` that is non dispatchable because of:
+            //   - missing a receiver
             trait T {
                 fn func();
                 const FLAG: i32;
@@ -7226,6 +7237,9 @@ impl T$0 for () {}
             ```
 
             ```rust
+            // Object Safety: No
+            // - Reason: has a method `func` that is non dispatchable because of:
+            //   - missing a receiver
             trait T {
                 fn func();
                 const FLAG: i32;
@@ -8465,8 +8479,8 @@ impl Iterator for S {
                                 file_id: FileId(
                                     1,
                                 ),
-                                full_range: 7800..8042,
-                                focus_range: 7865..7871,
+                                full_range: 7801..8043,
+                                focus_range: 7866..7872,
                                 name: "Future",
                                 kind: Trait,
                                 container_name: "future",
@@ -8479,8 +8493,8 @@ impl Iterator for S {
                                 file_id: FileId(
                                     1,
                                 ),
-                                full_range: 8672..9171,
-                                focus_range: 8749..8757,
+                                full_range: 8673..9172,
+                                focus_range: 8750..8758,
                                 name: "Iterator",
                                 kind: Trait,
                                 container_name: "iterator",
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
index 756198d0c01..31c1a991d53 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
@@ -288,7 +288,7 @@ mod tests {
         check_with_config(
             InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
             r#"
-//- minicore: coerce_unsized, fn, eq, index
+//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn
 fn main() {
     let _: u32         = loop {};
                        //^^^^^^^<never-to-any>
@@ -428,7 +428,7 @@ impl core::ops::IndexMut for Struct {}
                 ..DISABLED_CONFIG
             },
             r#"
-//- minicore: coerce_unsized, fn, eq, index
+//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn
 fn main() {
 
     Struct.consume();
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
index 7eb8e4a5e2e..f48daba54e7 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -242,6 +242,7 @@ define_symbols! {
     future_output,
     Future,
     ge,
+    generic_associated_type_extended,
     get_context,
     global_allocator,
     global_asm,
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index 3be4469beef..7e1518c25c7 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -33,6 +33,7 @@
 //!     from: sized
 //!     future: pin
 //!     coroutine: pin
+//!     dispatch_from_dyn: unsize, pin
 //!     hash:
 //!     include:
 //!     index: sized
@@ -822,6 +823,24 @@ pub mod ops {
     }
     pub use self::coroutine::{Coroutine, CoroutineState};
     // endregion:coroutine
+
+    // region:dispatch_from_dyn
+    mod dispatch_from_dyn {
+        use crate::marker::Unsize;
+
+        #[lang = "dispatch_from_dyn"]
+        pub trait DispatchFromDyn<T> {}
+
+        impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
+
+        impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {}
+
+        impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
+
+        impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
+    }
+    pub use self::dispatch_from_dyn::DispatchFromDyn;
+    // endregion:dispatch_from_dyn
 }
 
 // region:eq
@@ -1183,6 +1202,12 @@ pub mod pin {
         }
     }
     // endregion:deref
+    // region:dispatch_from_dyn
+    impl<Ptr, U> crate::ops::DispatchFromDyn<Pin<U>> for Pin<Ptr> where
+        Ptr: crate::ops::DispatchFromDyn<U>
+    {
+    }
+    // endregion:dispatch_from_dyn
 }
 // endregion:pin
 
@@ -1309,7 +1334,10 @@ pub mod iter {
                     self
                 }
                 // region:iterators
-                fn take(self, n: usize) -> crate::iter::Take<Self> {
+                fn take(self, n: usize) -> crate::iter::Take<Self>
+                where
+                    Self: Sized,
+                {
                     loop {}
                 }
                 fn filter_map<B, F>(self, _f: F) -> crate::iter::FilterMap<Self, F>