blob: 385149d78432ed59ef91ad763000254cacf5472e [file] [log] [blame]
//! Defining `SolverContext` for next-trait-solver.
use hir_def::{AssocItemId, GeneralConstId, TypeAliasId};
use rustc_next_trait_solver::delegate::SolverDelegate;
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::{
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: <Self::Interner as rustc_type_ir::Interner>::GenericArg,
span: <Self::Interner as rustc_type_ir::Interner>::Span,
) -> <Self::Interner as rustc_type_ir::Interner>::GenericArg {
unimplemented!()
}
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,
>,
>,
> {
unimplemented!()
}
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_with_infer(
&self,
cv_info: rustc_type_ir::CanonicalVarKind<Self::Interner>,
_span: <Self::Interner as rustc_type_ir::Interner>::Span,
universe_map: impl Fn(rustc_type_ir::UniverseIndex) -> rustc_type_ir::UniverseIndex,
) -> <Self::Interner as rustc_type_ir::Interner>::GenericArg {
self.0.instantiate_canonical_var(cv_info, 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_def_id: <Self::Interner as rustc_type_ir::Interner>::DefId,
) -> Result<Option<<Self::Interner as rustc_type_ir::Interner>::DefId>, ErrorGuaranteed> {
let impl_id = match impl_def_id {
SolverDefId::ImplId(id) => id,
_ => panic!("Unexpected SolverDefId"),
};
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)
// 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.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,
}
}
}