| //! Defining `SolverContext` for next-trait-solver. |
| |
| use hir_def::{ |
| AssocItemId, |
| signatures::{ConstSignature, TypeAliasSignature}, |
| }; |
| use rustc_next_trait_solver::delegate::SolverDelegate; |
| use rustc_type_ir::{ |
| AliasTyKind, GenericArgKind, InferCtxtLike, InferTy, Interner, PredicatePolarity, TypeFlags, |
| TypeVisitableExt, |
| inherent::{IntoKind, Term as _, Ty as _}, |
| lang_items::SolverTraitLangItem, |
| solve::{Certainty, FetchEligibleAssocItemResponse, NoSolution, VisibleForLeakCheck}, |
| }; |
| use tracing::debug; |
| |
| use crate::{ |
| ParamEnvAndCrate, Span, |
| db::GeneralConstId, |
| next_solver::{ |
| AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, ErrorGuaranteed, |
| GenericArgs, ImplOrTraitAssocTermId, OpaqueTyIdWrapper, ParamEnv, Predicate, PredicateKind, |
| RegionConstraint, SubtypePredicate, TermId, TraitAssocTermId, Ty, TyKind, TypingMode, |
| UnevaluatedConst, fold::fold_tys, util::sizedness_fast_path, |
| }, |
| }; |
| |
| use super::{ |
| Const, DbInterner, GenericArg, |
| infer::{DbInternerInferExt, InferCtxt, canonical::instantiate::CanonicalExt}, |
| }; |
| |
| pub type Goal<'db, P> = rustc_type_ir::solve::Goal<DbInterner<'db>, P>; |
| |
| #[repr(transparent)] |
| pub(crate) struct SolverContext<'db>(pub(crate) InferCtxt<'db>); |
| |
| impl<'a, 'db> From<&'a InferCtxt<'db>> for &'a SolverContext<'db> { |
| fn from(infcx: &'a InferCtxt<'db>) -> Self { |
| // SAFETY: `repr(transparent)` |
| unsafe { std::mem::transmute(infcx) } |
| } |
| } |
| |
| impl<'db> std::ops::Deref for SolverContext<'db> { |
| type Target = InferCtxt<'db>; |
| |
| fn deref(&self) -> &Self::Target { |
| &self.0 |
| } |
| } |
| |
| impl<'db> SolverDelegate for SolverContext<'db> { |
| type Interner = DbInterner<'db>; |
| type Infcx = InferCtxt<'db>; |
| |
| fn cx(&self) -> Self::Interner { |
| self.0.interner |
| } |
| |
| fn build_with_canonical<V>( |
| cx: Self::Interner, |
| canonical: &rustc_type_ir::CanonicalQueryInput<Self::Interner, V>, |
| ) -> (Self, V, rustc_type_ir::CanonicalVarValues<Self::Interner>) |
| where |
| V: rustc_type_ir::TypeFoldable<Self::Interner>, |
| { |
| let (infcx, value, vars) = cx.infer_ctxt().build_with_canonical(Span::Dummy, canonical); |
| (SolverContext(infcx), value, vars) |
| } |
| |
| fn fresh_var_for_kind_with_span(&self, arg: GenericArg<'db>, span: Span) -> GenericArg<'db> { |
| match arg.kind() { |
| GenericArgKind::Lifetime(_) => self.next_region_var(span).into(), |
| GenericArgKind::Type(_) => self.next_ty_var(span).into(), |
| GenericArgKind::Const(_) => self.next_const_var(span).into(), |
| } |
| } |
| |
| fn leak_check( |
| &self, |
| _max_input_universe: rustc_type_ir::UniverseIndex, |
| ) -> Result<(), NoSolution> { |
| Ok(()) |
| } |
| |
| fn well_formed_goals( |
| &self, |
| _param_env: ParamEnv<'db>, |
| _arg: <Self::Interner as rustc_type_ir::Interner>::Term, |
| ) -> Option< |
| Vec< |
| rustc_type_ir::solve::Goal< |
| Self::Interner, |
| <Self::Interner as rustc_type_ir::Interner>::Predicate, |
| >, |
| >, |
| > { |
| // FIXME(next-solver): |
| None |
| } |
| |
| fn make_deduplicated_region_constraints( |
| &self, |
| ) -> Vec<(RegionConstraint<'db>, VisibleForLeakCheck)> { |
| // FIXME: add if we care about regions |
| vec![] |
| } |
| |
| fn instantiate_canonical<V>( |
| &self, |
| canonical: rustc_type_ir::Canonical<Self::Interner, V>, |
| values: rustc_type_ir::CanonicalVarValues<Self::Interner>, |
| ) -> V |
| where |
| V: rustc_type_ir::TypeFoldable<Self::Interner>, |
| { |
| canonical.instantiate(self.cx(), &values) |
| } |
| |
| fn instantiate_canonical_var( |
| &self, |
| kind: CanonicalVarKind<'db>, |
| span: Span, |
| var_values: &[GenericArg<'db>], |
| universe_map: impl Fn(rustc_type_ir::UniverseIndex) -> rustc_type_ir::UniverseIndex, |
| ) -> GenericArg<'db> { |
| self.0.instantiate_canonical_var(span, kind, var_values, universe_map) |
| } |
| |
| fn add_item_bounds_for_hidden_type( |
| &self, |
| opaque_id: OpaqueTyIdWrapper<'_>, |
| args: GenericArgs<'db>, |
| param_env: ParamEnv<'db>, |
| hidden_ty: Ty<'db>, |
| goals: &mut Vec<Goal<'db, Predicate<'db>>>, |
| ) { |
| let interner = self.interner; |
| // Require that the hidden type is well-formed. We have to |
| // make sure we wf-check the hidden type to fix #114728. |
| // |
| // However, we don't check that all types are well-formed. |
| // We only do so for types provided by the user or if they are |
| // "used", e.g. for method selection. |
| // |
| // This means we never check the wf requirements of the hidden |
| // type during MIR borrowck, causing us to infer the wrong |
| // lifetime for its member constraints which then results in |
| // unexpected region errors. |
| goals.push(Goal::new(interner, param_env, ClauseKind::WellFormed(hidden_ty.into()))); |
| |
| let replace_opaques_in = |clause: Clause<'db>| { |
| fold_tys(interner, clause, |ty| match ty.kind() { |
| // Replace all other mentions of the same opaque type with the hidden type, |
| // as the bounds must hold on the hidden type after all. |
| TyKind::Alias(AliasTy { |
| kind: AliasTyKind::Opaque { def_id: def_id2 }, |
| args: args2, |
| .. |
| }) if opaque_id == def_id2 && args == args2 => hidden_ty, |
| _ => ty, |
| }) |
| }; |
| |
| let item_bounds = opaque_id.0.predicates(interner.db); |
| for predicate in item_bounds.iter_instantiated_copied(interner, args.as_slice()) { |
| let predicate = replace_opaques_in(predicate.skip_norm_wip()); |
| |
| // Require that the predicate holds for the concrete type. |
| debug!(?predicate); |
| goals.push(Goal::new(interner, param_env, predicate)); |
| } |
| } |
| |
| fn fetch_eligible_assoc_item( |
| &self, |
| _goal_trait_ref: rustc_type_ir::TraitRef<Self::Interner>, |
| trait_assoc_def_id: TraitAssocTermId, |
| impl_id: AnyImplId, |
| ) -> FetchEligibleAssocItemResponse<Self::Interner> { |
| let AnyImplId::ImplId(impl_id) = impl_id else { |
| // Builtin derive traits don't have type/consts assoc items. |
| return FetchEligibleAssocItemResponse::Err(ErrorGuaranteed); |
| }; |
| let impl_items = impl_id.impl_items(self.0.interner.db()); |
| let id = match trait_assoc_def_id.0 { |
| TermId::TypeAliasId(trait_assoc_id) => { |
| let trait_assoc_data = TypeAliasSignature::of(self.0.interner.db, trait_assoc_id); |
| impl_items |
| .items |
| .iter() |
| .find_map(|(impl_assoc_name, impl_assoc_id)| { |
| if let AssocItemId::TypeAliasId(impl_assoc_id) = *impl_assoc_id |
| && *impl_assoc_name == trait_assoc_data.name |
| { |
| Some(impl_assoc_id) |
| } else { |
| None |
| } |
| }) |
| .or_else(|| { |
| if trait_assoc_data.ty.is_some() { Some(trait_assoc_id) } else { None } |
| }) |
| .map(|def| ImplOrTraitAssocTermId(TermId::TypeAliasId(def))) |
| } |
| TermId::ConstId(trait_assoc_id) => { |
| let trait_assoc_data = ConstSignature::of(self.0.interner.db, trait_assoc_id); |
| let trait_assoc_name = trait_assoc_data |
| .name |
| .as_ref() |
| .expect("unnamed consts should not get passed to the solver"); |
| impl_items |
| .items |
| .iter() |
| .find_map(|(impl_assoc_name, impl_assoc_id)| { |
| if let AssocItemId::ConstId(impl_assoc_id) = *impl_assoc_id |
| && impl_assoc_name == trait_assoc_name |
| { |
| Some(impl_assoc_id) |
| } else { |
| None |
| } |
| }) |
| .or_else( |
| || { |
| if trait_assoc_data.has_body() { Some(trait_assoc_id) } else { None } |
| }, |
| ) |
| .map(|def| ImplOrTraitAssocTermId(TermId::ConstId(def))) |
| } |
| }; |
| match id { |
| Some(id) => FetchEligibleAssocItemResponse::Found(id), |
| None => match self.typing_mode_raw() { |
| TypingMode::ErasedNotCoherence(_) => { |
| FetchEligibleAssocItemResponse::NotFoundBecauseErased |
| } |
| typing_mode => { |
| FetchEligibleAssocItemResponse::NotFound(typing_mode.assert_not_erased()) |
| } |
| }, |
| } |
| } |
| |
| fn is_transmutable( |
| &self, |
| _src: Ty<'db>, |
| _dst: Ty<'db>, |
| _assume: <Self::Interner as rustc_type_ir::Interner>::Const, |
| ) -> Result<Certainty, NoSolution> { |
| // It's better to return some value while not fully implement |
| // then panic in the mean time |
| Ok(Certainty::Yes) |
| } |
| |
| fn evaluate_const( |
| &self, |
| param_env: ParamEnv<'db>, |
| uv: UnevaluatedConst<'db>, |
| ) -> Option<Const<'db>> { |
| let ec = match uv.def.0 { |
| GeneralConstId::ConstId(c) => { |
| let subst = uv.args; |
| self.cx().db.const_eval(c, subst, None).ok()? |
| } |
| GeneralConstId::StaticId(c) => self.cx().db.const_eval_static(c).ok()?, |
| GeneralConstId::AnonConstId(c) => { |
| let subst = uv.args; |
| self.cx().db.anon_const_eval(c, subst, None).ok()? |
| } |
| }; |
| Some(Const::new_from_allocation( |
| self.interner, |
| &ec, |
| ParamEnvAndCrate { param_env, krate: self.interner.expect_crate() }, |
| )) |
| } |
| |
| fn compute_goal_fast_path( |
| &self, |
| goal: rustc_type_ir::solve::Goal< |
| Self::Interner, |
| <Self::Interner as rustc_type_ir::Interner>::Predicate, |
| >, |
| _span: <Self::Interner as rustc_type_ir::Interner>::Span, |
| ) -> Option<Certainty> { |
| if let Some(trait_pred) = goal.predicate.as_trait_clause() { |
| if self.shallow_resolve(trait_pred.self_ty().skip_binder()).is_ty_var() |
| // We don't do this fast path when opaques are defined since we may |
| // eventually use opaques to incompletely guide inference via ty var |
| // self types. |
| // FIXME: Properly consider opaques here. |
| && self.inner.borrow_mut().opaque_types().is_empty() |
| { |
| return Some(Certainty::AMBIGUOUS); |
| } |
| |
| if trait_pred.polarity() == PredicatePolarity::Positive { |
| match self.0.interner.as_trait_lang_item(trait_pred.def_id()) { |
| Some(SolverTraitLangItem::Sized) | Some(SolverTraitLangItem::MetaSized) => { |
| let predicate = self.resolve_vars_if_possible(goal.predicate); |
| if sizedness_fast_path(self.interner, predicate, goal.param_env) { |
| return Some(Certainty::Yes); |
| } |
| } |
| Some(SolverTraitLangItem::Copy | SolverTraitLangItem::Clone) => { |
| let self_ty = |
| self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder()); |
| // Unlike `Sized` traits, which always prefer the built-in impl, |
| // `Copy`/`Clone` may be shadowed by a param-env candidate which |
| // could force a lifetime error or guide inference. While that's |
| // not generally desirable, it is observable, so for now let's |
| // ignore this fast path for types that have regions or infer. |
| if !self_ty |
| .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER) |
| && self_ty.is_trivially_pure_clone_copy() |
| { |
| return Some(Certainty::Yes); |
| } |
| } |
| _ => {} |
| } |
| } |
| } |
| |
| let pred = goal.predicate.kind(); |
| match pred.no_bound_vars()? { |
| PredicateKind::DynCompatible(def_id) |
| if self.0.interner.trait_is_dyn_compatible(def_id) => |
| { |
| Some(Certainty::Yes) |
| } |
| PredicateKind::Clause(ClauseKind::RegionOutlives(outlives)) => { |
| self.0.sub_regions(outlives.1, outlives.0); |
| Some(Certainty::Yes) |
| } |
| PredicateKind::Clause(ClauseKind::TypeOutlives(outlives)) => { |
| self.0.register_type_outlives_constraint(outlives.0, outlives.1); |
| |
| Some(Certainty::Yes) |
| } |
| PredicateKind::Subtype(SubtypePredicate { a, b, .. }) |
| | PredicateKind::Coerce(CoercePredicate { a, b }) => { |
| match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) { |
| ( |
| TyKind::Infer(InferTy::TyVar(a_vid)), |
| TyKind::Infer(InferTy::TyVar(b_vid)), |
| ) => { |
| self.sub_unify_ty_vids_raw(a_vid, b_vid); |
| Some(Certainty::AMBIGUOUS) |
| } |
| _ => None, |
| } |
| } |
| PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, _)) => { |
| if self.shallow_resolve_const(ct).is_ct_infer() { |
| Some(Certainty::AMBIGUOUS) |
| } else { |
| None |
| } |
| } |
| PredicateKind::Clause(ClauseKind::WellFormed(arg)) => { |
| let arg = self.shallow_resolve_term(arg); |
| if arg.is_trivially_wf(self.interner) { |
| Some(Certainty::Yes) |
| } else if arg.is_infer() { |
| Some(Certainty::AMBIGUOUS) |
| } else { |
| None |
| } |
| } |
| _ => None, |
| } |
| } |
| } |