Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/next_solver/solver.rs')
-rw-r--r--crates/hir-ty/src/next_solver/solver.rs289
1 files changed, 289 insertions, 0 deletions
diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs
new file mode 100644
index 0000000000..946e57e6cb
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/solver.rs
@@ -0,0 +1,289 @@
+//! Defining `SolverContext` for next-trait-solver.
+
+use hir_def::{AssocItemId, GeneralConstId, TypeAliasId};
+use rustc_next_trait_solver::delegate::SolverDelegate;
+use rustc_type_ir::GenericArgKind;
+use rustc_type_ir::lang_items::SolverTraitLangItem;
+use rustc_type_ir::{
+ InferCtxtLike, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt, UniverseIndex,
+ inherent::{IntoKind, SliceLike, Span as _, Term as _, Ty as _},
+ solve::{Certainty, NoSolution},
+};
+
+use crate::next_solver::mapping::NextSolverToChalk;
+use crate::next_solver::{CanonicalVarKind, ImplIdWrapper};
+use crate::{
+ TraitRefExt,
+ db::HirDatabase,
+ next_solver::{
+ ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, mapping::ChalkToNextSolver,
+ util::sizedness_fast_path,
+ },
+};
+
+use super::{
+ Canonical, CanonicalVarValues, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
+ ParamEnv, Predicate, SolverDefId, Span, Ty, UnevaluatedConst,
+ 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(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().into(),
+ GenericArgKind::Type(_) => self.next_ty_var().into(),
+ GenericArgKind::Const(_) => self.next_const_var().into(),
+ }
+ }
+
+ fn leak_check(
+ &self,
+ max_input_universe: rustc_type_ir::UniverseIndex,
+ ) -> Result<(), NoSolution> {
+ Ok(())
+ }
+
+ fn well_formed_goals(
+ &self,
+ param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv,
+ 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_outlives_constraints(
+ &self,
+ ) -> Vec<
+ rustc_type_ir::OutlivesPredicate<
+ Self::Interner,
+ <Self::Interner as rustc_type_ir::Interner>::GenericArg,
+ >,
+ > {
+ // 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: <Self::Interner as Interner>::Span,
+ var_values: &[GenericArg<'db>],
+ universe_map: impl Fn(rustc_type_ir::UniverseIndex) -> rustc_type_ir::UniverseIndex,
+ ) -> GenericArg<'db> {
+ self.0.instantiate_canonical_var(kind, var_values, universe_map)
+ }
+
+ fn add_item_bounds_for_hidden_type(
+ &self,
+ def_id: <Self::Interner as rustc_type_ir::Interner>::DefId,
+ args: <Self::Interner as rustc_type_ir::Interner>::GenericArgs,
+ param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv,
+ hidden_ty: <Self::Interner as rustc_type_ir::Interner>::Ty,
+ goals: &mut Vec<
+ rustc_type_ir::solve::Goal<
+ Self::Interner,
+ <Self::Interner as rustc_type_ir::Interner>::Predicate,
+ >,
+ >,
+ ) {
+ unimplemented!()
+ }
+
+ fn fetch_eligible_assoc_item(
+ &self,
+ goal_trait_ref: rustc_type_ir::TraitRef<Self::Interner>,
+ trait_assoc_def_id: <Self::Interner as rustc_type_ir::Interner>::DefId,
+ impl_id: ImplIdWrapper,
+ ) -> Result<Option<<Self::Interner as rustc_type_ir::Interner>::DefId>, ErrorGuaranteed> {
+ let trait_assoc_id = match trait_assoc_def_id {
+ SolverDefId::TypeAliasId(id) => id,
+ _ => panic!("Unexpected SolverDefId"),
+ };
+ let trait_ref = self
+ .0
+ .interner
+ .db()
+ .impl_trait(impl_id.0)
+ // ImplIds for impls where the trait ref can't be resolved should never reach solver
+ .expect("invalid impl passed to next-solver")
+ .into_value_and_skipped_binders()
+ .0;
+ let trait_ = trait_ref.hir_trait_id();
+ let trait_data = trait_.trait_items(self.0.interner.db());
+ let id =
+ impl_id.0.impl_items(self.0.interner.db()).items.iter().find_map(|item| -> Option<_> {
+ match item {
+ (_, AssocItemId::TypeAliasId(type_alias)) => {
+ let name = &self.0.interner.db().type_alias_signature(*type_alias).name;
+ let found_trait_assoc_id = trait_data.associated_type_by_name(name)?;
+ (found_trait_assoc_id == trait_assoc_id).then_some(*type_alias)
+ }
+ _ => None,
+ }
+ });
+ Ok(id.map(SolverDefId::TypeAliasId))
+ }
+
+ fn is_transmutable(
+ &self,
+ dst: <Self::Interner as rustc_type_ir::Interner>::Ty,
+ src: <Self::Interner as rustc_type_ir::Interner>::Ty,
+ assume: <Self::Interner as rustc_type_ir::Interner>::Const,
+ ) -> Result<Certainty, NoSolution> {
+ unimplemented!()
+ }
+
+ fn evaluate_const(
+ &self,
+ param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv,
+ uv: rustc_type_ir::UnevaluatedConst<Self::Interner>,
+ ) -> Option<<Self::Interner as rustc_type_ir::Interner>::Const> {
+ let c = match uv.def {
+ SolverDefId::ConstId(c) => GeneralConstId::ConstId(c),
+ SolverDefId::StaticId(c) => GeneralConstId::StaticId(c),
+ _ => unreachable!(),
+ };
+ let subst = uv.args.to_chalk(self.interner);
+ let ec = self.cx().db.const_eval(c, subst, None).ok()?;
+ Some(ec.to_nextsolver(self.interner))
+ }
+
+ 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.cx().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.cx(), 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::Clause(ClauseKind::RegionOutlives(outlives)) => Some(Certainty::Yes),
+ PredicateKind::Clause(ClauseKind::TypeOutlives(outlives)) => Some(Certainty::Yes),
+ PredicateKind::Subtype(SubtypePredicate { a, b, .. })
+ | PredicateKind::Coerce(CoercePredicate { a, b }) => {
+ if self.shallow_resolve(a).is_ty_var() && self.shallow_resolve(b).is_ty_var() {
+ // FIXME: We also need to register a subtype relation between these vars
+ // when those are added, and if they aren't in the same sub root then
+ // we should mark this goal as `has_changed`.
+ Some(Certainty::AMBIGUOUS)
+ } else {
+ 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)) => {
+ if arg.is_trivially_wf(self.interner) {
+ Some(Certainty::Yes)
+ } else if arg.is_infer() {
+ Some(Certainty::AMBIGUOUS)
+ } else {
+ None
+ }
+ }
+ _ => None,
+ }
+ }
+}