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.rs | 153 |
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 +} |