Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/traits.rs')
-rw-r--r--crates/hir-ty/src/traits.rs153
1 files changed, 151 insertions, 2 deletions
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index 00c8eb7745..2055c3151c 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/crates/hir-ty/src/traits.rs
@@ -4,13 +4,18 @@ use core::fmt;
use std::hash::Hash;
use base_db::Crate;
-use hir_def::{BlockId, TraitId, lang_item::LangItem};
+use hir_def::{
+ AdtId, AssocItemId, BlockId, HasModule, ImplId, Lookup, TraitId,
+ lang_item::LangItem,
+ nameres::DefMap,
+ signatures::{ConstFlags, EnumFlags, FnFlags, StructFlags, TraitFlags, TypeAliasFlags},
+};
use hir_expand::name::Name;
use intern::sym;
use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt};
use rustc_type_ir::{
TypingMode,
- inherent::{IntoKind, Span as _},
+ inherent::{AdtDef, BoundExistentialPredicates, IntoKind, Span as _},
solve::Certainty,
};
use triomphe::Arc;
@@ -263,3 +268,147 @@ fn implements_trait_unique_impl<'db>(
let result = crate::traits::next_trait_solve_in_ctxt(&infcx, goal);
matches!(result, Ok((_, Certainty::Yes)))
}
+
+pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id: ImplId) -> bool {
+ let self_ty = db.impl_self_ty(impl_id).instantiate_identity();
+ let self_ty = self_ty.kind();
+ let impl_allowed = match self_ty {
+ TyKind::Tuple(_)
+ | TyKind::FnDef(_, _)
+ | TyKind::Array(_, _)
+ | TyKind::Never
+ | TyKind::RawPtr(_, _)
+ | TyKind::Ref(_, _, _)
+ | TyKind::Slice(_)
+ | TyKind::Str
+ | TyKind::Bool
+ | TyKind::Char
+ | TyKind::Int(_)
+ | TyKind::Uint(_)
+ | TyKind::Float(_) => def_map.is_rustc_coherence_is_core(),
+
+ TyKind::Adt(adt_def, _) => adt_def.def_id().0.module(db).krate() == def_map.krate(),
+ TyKind::Dynamic(it, _) => it
+ .principal_def_id()
+ .is_some_and(|trait_id| trait_id.0.module(db).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::RawPtr(_, _)
+ | TyKind::Ref(_, _, _)
+ | TyKind::Slice(_)
+ | TyKind::Str
+ | TyKind::Bool
+ | TyKind::Char
+ | TyKind::Int(_)
+ | TyKind::Uint(_)
+ | TyKind::Float(_) => true,
+
+ TyKind::Adt(adt_def, _) => match adt_def.def_id().0 {
+ hir_def::AdtId::StructId(id) => db
+ .struct_signature(id)
+ .flags
+ .contains(StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
+ hir_def::AdtId::UnionId(id) => db
+ .union_signature(id)
+ .flags
+ .contains(StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
+ hir_def::AdtId::EnumId(it) => db
+ .enum_signature(it)
+ .flags
+ .contains(EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
+ },
+ TyKind::Dynamic(it, _) => it.principal_def_id().is_some_and(|trait_id| {
+ db.trait_signature(trait_id.0)
+ .flags
+ .contains(TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS)
+ }),
+
+ _ => false,
+ };
+ let items = impl_id.impl_items(db);
+ rustc_has_incoherent_inherent_impls
+ && !items.items.is_empty()
+ && items.items.iter().all(|&(_, assoc)| match assoc {
+ AssocItemId::FunctionId(it) => {
+ db.function_signature(it).flags.contains(FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL)
+ }
+ AssocItemId::ConstId(it) => {
+ db.const_signature(it).flags.contains(ConstFlags::RUSTC_ALLOW_INCOHERENT_IMPL)
+ }
+ AssocItemId::TypeAliasId(it) => db
+ .type_alias_signature(it)
+ .flags
+ .contains(TypeAliasFlags::RUSTC_ALLOW_INCOHERENT_IMPL),
+ })
+ }
+}
+
+/// Checks whether the impl satisfies the orphan rules.
+///
+/// Given `impl<P1..=Pn> Trait<T1..=Tn> for T0`, an `impl`` is valid only if at least one of the following is true:
+/// - Trait is a local trait
+/// - All of
+/// - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type.
+/// - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`)
+pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool {
+ let Some(impl_trait) = db.impl_trait(impl_) else {
+ // not a trait impl
+ return true;
+ };
+
+ let local_crate = impl_.lookup(db).container.krate();
+ let is_local = |tgt_crate| tgt_crate == local_crate;
+
+ let trait_ref = impl_trait.instantiate_identity();
+ let trait_id = trait_ref.def_id.0;
+ if is_local(trait_id.module(db).krate()) {
+ // trait to be implemented is local
+ return true;
+ }
+
+ let unwrap_fundamental = |mut ty: Ty<'db>| {
+ // Unwrap all layers of fundamental types with a loop.
+ loop {
+ match ty.kind() {
+ TyKind::Ref(_, referenced, _) => ty = referenced,
+ TyKind::Adt(adt_def, subs) => {
+ let AdtId::StructId(s) = adt_def.def_id().0 else {
+ break ty;
+ };
+ let struct_signature = db.struct_signature(s);
+ if struct_signature.flags.contains(StructFlags::FUNDAMENTAL) {
+ let next = subs.types().next();
+ match next {
+ Some(it) => ty = it,
+ None => break ty,
+ }
+ } else {
+ break ty;
+ }
+ }
+ _ => break ty,
+ }
+ }
+ };
+ // - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type.
+
+ // FIXME: param coverage
+ // - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`)
+ let is_not_orphan = trait_ref.args.types().any(|ty| match unwrap_fundamental(ty).kind() {
+ TyKind::Adt(adt_def, _) => is_local(adt_def.def_id().0.module(db).krate()),
+ TyKind::Error(_) => true,
+ TyKind::Dynamic(it, _) => {
+ it.principal_def_id().is_some_and(|trait_id| is_local(trait_id.0.module(db).krate()))
+ }
+ _ => false,
+ });
+ #[allow(clippy::let_and_return)]
+ is_not_orphan
+}