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 | 313 |
1 files changed, 215 insertions, 98 deletions
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index 00c8eb7745..fb598fe5ac 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -1,72 +1,65 @@ //! Trait solving using next trait solver. -use core::fmt; use std::hash::Hash; use base_db::Crate; -use hir_def::{BlockId, TraitId, lang_item::LangItem}; +use hir_def::{ + AdtId, AssocItemId, HasModule, ImplId, Lookup, TraitId, + lang_item::LangItems, + 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; use crate::{ db::HirDatabase, next_solver::{ - Canonical, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, SolverContext, Span, Ty, - TyKind, - infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause}, + Canonical, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, SolverContext, Span, + StoredClauses, Ty, TyKind, + infer::{ + DbInternerInferExt, InferCtxt, + traits::{Obligation, ObligationCause}, + }, obligation_ctxt::ObligationCtxt, }, }; -/// A set of clauses that we assume to be true. E.g. if we are inside this function: -/// ```rust -/// fn foo<T: Default>(t: T) {} -/// ``` -/// we assume that `T: Default`. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct TraitEnvironment<'db> { +/// Type for `hir`, because commonly we want both param env and a crate in an exported API. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ParamEnvAndCrate<'db> { + pub param_env: ParamEnv<'db>, pub krate: Crate, - pub block: Option<BlockId>, - // FIXME make this a BTreeMap - traits_from_clauses: Box<[(Ty<'db>, TraitId)]>, - pub env: ParamEnv<'db>, } -impl<'db> TraitEnvironment<'db> { - pub fn empty(krate: Crate) -> Arc<Self> { - Arc::new(TraitEnvironment { - krate, - block: None, - traits_from_clauses: Box::default(), - env: ParamEnv::empty(), - }) +impl<'db> ParamEnvAndCrate<'db> { + #[inline] + pub fn store(self) -> StoredParamEnvAndCrate { + StoredParamEnvAndCrate { param_env: self.param_env.clauses.store(), krate: self.krate } } +} - pub fn new( - krate: Crate, - block: Option<BlockId>, - traits_from_clauses: Box<[(Ty<'db>, TraitId)]>, - env: ParamEnv<'db>, - ) -> Arc<Self> { - Arc::new(TraitEnvironment { krate, block, traits_from_clauses, env }) - } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StoredParamEnvAndCrate { + param_env: StoredClauses, + pub krate: Crate, +} - // pub fn with_block(self: &mut Arc<Self>, block: BlockId) { - pub fn with_block(this: &mut Arc<Self>, block: BlockId) { - Arc::make_mut(this).block = Some(block); +impl StoredParamEnvAndCrate { + #[inline] + pub fn param_env(&self) -> ParamEnv<'_> { + ParamEnv { clauses: self.param_env.as_ref() } } - pub fn traits_in_scope_from_clauses(&self, ty: Ty<'db>) -> impl Iterator<Item = TraitId> + '_ { - self.traits_from_clauses - .iter() - .filter_map(move |(self_ty, trait_id)| (*self_ty == ty).then_some(*trait_id)) + #[inline] + pub fn as_ref(&self) -> ParamEnvAndCrate<'_> { + ParamEnvAndCrate { param_env: self.param_env(), krate: self.krate } } } @@ -74,11 +67,11 @@ impl<'db> TraitEnvironment<'db> { pub fn structurally_normalize_ty<'db>( infcx: &InferCtxt<'db>, ty: Ty<'db>, - env: Arc<TraitEnvironment<'db>>, + env: ParamEnv<'db>, ) -> Ty<'db> { let TyKind::Alias(..) = ty.kind() else { return ty }; let mut ocx = ObligationCtxt::new(infcx); - let ty = ocx.structurally_normalize_ty(&ObligationCause::dummy(), env.env, ty).unwrap_or(ty); + let ty = ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, ty).unwrap_or(ty); ty.replace_infer_with_error(infcx.interner) } @@ -117,6 +110,16 @@ pub fn next_trait_solve_canonical_in_ctxt<'db>( let res = context.evaluate_root_goal(goal, Span::dummy(), None); + let obligation = Obligation { + cause: ObligationCause::dummy(), + param_env: goal.param_env, + recursion_depth: 0, + predicate: goal.predicate, + }; + infer_ctxt.inspect_evaluated_obligation(&obligation, &res, || { + Some(context.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1) + }); + let res = res.map(|r| (r.has_changed, r.certainty)); tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); @@ -140,6 +143,16 @@ pub fn next_trait_solve_in_ctxt<'db, 'a>( let res = context.evaluate_root_goal(goal, Span::dummy(), None); + let obligation = Obligation { + cause: ObligationCause::dummy(), + param_env: goal.param_env, + recursion_depth: 0, + predicate: goal.predicate, + }; + infer_ctxt.inspect_evaluated_obligation(&obligation, &res, || { + Some(context.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1) + }); + let res = res.map(|r| (r.has_changed, r.certainty)); tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); @@ -147,7 +160,7 @@ pub fn next_trait_solve_in_ctxt<'db, 'a>( res } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Update)] pub enum FnTrait { // Warning: Order is important. If something implements `x` it should also implement // `y` if `y <= x`. @@ -160,54 +173,7 @@ pub enum FnTrait { AsyncFn, } -impl fmt::Display for FnTrait { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - FnTrait::FnOnce => write!(f, "FnOnce"), - FnTrait::FnMut => write!(f, "FnMut"), - FnTrait::Fn => write!(f, "Fn"), - FnTrait::AsyncFnOnce => write!(f, "AsyncFnOnce"), - FnTrait::AsyncFnMut => write!(f, "AsyncFnMut"), - FnTrait::AsyncFn => write!(f, "AsyncFn"), - } - } -} - impl FnTrait { - pub const fn function_name(&self) -> &'static str { - match self { - FnTrait::FnOnce => "call_once", - FnTrait::FnMut => "call_mut", - FnTrait::Fn => "call", - FnTrait::AsyncFnOnce => "async_call_once", - FnTrait::AsyncFnMut => "async_call_mut", - FnTrait::AsyncFn => "async_call", - } - } - - const fn lang_item(self) -> LangItem { - match self { - FnTrait::FnOnce => LangItem::FnOnce, - FnTrait::FnMut => LangItem::FnMut, - FnTrait::Fn => LangItem::Fn, - FnTrait::AsyncFnOnce => LangItem::AsyncFnOnce, - FnTrait::AsyncFnMut => LangItem::AsyncFnMut, - FnTrait::AsyncFn => LangItem::AsyncFn, - } - } - - pub const fn from_lang_item(lang_item: LangItem) -> Option<Self> { - match lang_item { - LangItem::FnOnce => Some(FnTrait::FnOnce), - LangItem::FnMut => Some(FnTrait::FnMut), - LangItem::Fn => Some(FnTrait::Fn), - LangItem::AsyncFnOnce => Some(FnTrait::AsyncFnOnce), - LangItem::AsyncFnMut => Some(FnTrait::AsyncFnMut), - LangItem::AsyncFn => Some(FnTrait::AsyncFn), - _ => None, - } - } - pub fn method_name(self) -> Name { match self { FnTrait::FnOnce => Name::new_symbol_root(sym::call_once), @@ -219,8 +185,15 @@ impl FnTrait { } } - pub fn get_id(self, db: &dyn HirDatabase, krate: Crate) -> Option<TraitId> { - self.lang_item().resolve_trait(db, krate) + pub fn get_id(self, lang_items: &LangItems) -> Option<TraitId> { + match self { + FnTrait::FnOnce => lang_items.FnOnce, + FnTrait::FnMut => lang_items.FnMut, + FnTrait::Fn => lang_items.Fn, + FnTrait::AsyncFnOnce => lang_items.AsyncFnOnce, + FnTrait::AsyncFnMut => lang_items.AsyncFnMut, + FnTrait::AsyncFn => lang_items.AsyncFn, + } } } @@ -228,7 +201,7 @@ impl FnTrait { pub fn implements_trait_unique<'db>( ty: Ty<'db>, db: &'db dyn HirDatabase, - env: Arc<TraitEnvironment<'db>>, + env: ParamEnvAndCrate<'db>, trait_: TraitId, ) -> bool { implements_trait_unique_impl(db, env, trait_, &mut |infcx| { @@ -239,7 +212,7 @@ pub fn implements_trait_unique<'db>( /// This should not be used in `hir-ty`, only in `hir`. pub fn implements_trait_unique_with_args<'db>( db: &'db dyn HirDatabase, - env: Arc<TraitEnvironment<'db>>, + env: ParamEnvAndCrate<'db>, trait_: TraitId, args: GenericArgs<'db>, ) -> bool { @@ -248,18 +221,162 @@ pub fn implements_trait_unique_with_args<'db>( fn implements_trait_unique_impl<'db>( db: &'db dyn HirDatabase, - env: Arc<TraitEnvironment<'db>>, + env: ParamEnvAndCrate<'db>, trait_: TraitId, create_args: &mut dyn FnMut(&InferCtxt<'db>) -> GenericArgs<'db>, ) -> bool { - let interner = DbInterner::new_with(db, Some(env.krate), env.block); + let interner = DbInterner::new_with(db, env.krate); // FIXME(next-solver): I believe this should be `PostAnalysis`. let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); let args = create_args(&infcx); let trait_ref = rustc_type_ir::TraitRef::new_from_args(interner, trait_.into(), args); - let goal = Goal::new(interner, env.env, trait_ref); + let goal = Goal::new(interner, env.param_env, trait_ref); 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(db) == def_map.krate(), + TyKind::Dynamic(it, _) => it + .principal_def_id() + .is_some_and(|trait_id| trait_id.0.module(db).krate(db) == 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(db); + 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(db)) { + // 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(db)), + TyKind::Error(_) => true, + TyKind::Dynamic(it, _) => { + it.principal_def_id().is_some_and(|trait_id| is_local(trait_id.0.module(db).krate(db))) + } + _ => false, + }); + #[allow(clippy::let_and_return)] + is_not_orphan +} |