mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-25 11:17:13 +00:00
WIP switch inference table to next-solver
This commit is contained in:
parent
6da1ce7869
commit
d24e8c1d38
@ -27,7 +27,7 @@ use crate::{
|
||||
next_solver::{
|
||||
Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
|
||||
ParamConst, SolverDefId, Ty, ValueConst,
|
||||
mapping::{ChalkToNextSolver, convert_args_for_result, convert_binder_to_early_binder},
|
||||
mapping::{ChalkToNextSolver, convert_binder_to_early_binder},
|
||||
},
|
||||
};
|
||||
|
||||
@ -145,7 +145,7 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u
|
||||
SolverDefId::StaticId(id) => GeneralConstId::StaticId(id),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice());
|
||||
let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner);
|
||||
let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner);
|
||||
try_const_usize(db, ec)
|
||||
}
|
||||
@ -168,7 +168,7 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<
|
||||
SolverDefId::StaticId(id) => GeneralConstId::StaticId(id),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice());
|
||||
let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner);
|
||||
let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner);
|
||||
try_const_isize(db, &ec)
|
||||
}
|
||||
|
@ -568,7 +568,7 @@ impl InferenceContext<'_> {
|
||||
let supplied_sig = self.supplied_sig_of_closure(body, ret_type, arg_types, closure_kind);
|
||||
|
||||
let snapshot = self.table.snapshot();
|
||||
if !self.table.unify(&expected_sig.substitution, &supplied_sig.expected_sig.substitution) {
|
||||
if !self.table.unify::<_, crate::next_solver::GenericArgs<'_>>(&expected_sig.substitution.0, &supplied_sig.expected_sig.substitution.0) {
|
||||
self.table.rollback_to(snapshot);
|
||||
}
|
||||
|
||||
|
@ -7,22 +7,20 @@
|
||||
|
||||
use std::iter;
|
||||
|
||||
use chalk_ir::{BoundVar, Goal, Mutability, TyKind, TyVariableKind, cast::Cast};
|
||||
use hir_def::{hir::ExprId, lang_item::LangItem};
|
||||
use chalk_ir::{BoundVar, Mutability, TyKind, TyVariableKind, cast::Cast};
|
||||
use hir_def::{
|
||||
hir::ExprId,
|
||||
lang_item::LangItem,
|
||||
};
|
||||
use rustc_type_ir::solve::Certainty;
|
||||
use stdx::always;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
Canonical, DomainGoal, FnAbi, FnPointer, FnSig, InEnvironment, Interner, Lifetime,
|
||||
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
autoderef::{Autoderef, AutoderefKind},
|
||||
db::HirDatabase,
|
||||
infer::{
|
||||
autoderef::{Autoderef, AutoderefKind}, db::HirDatabase, infer::{
|
||||
Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast,
|
||||
TypeError, TypeMismatch,
|
||||
},
|
||||
traits::NextTraitSolveResult,
|
||||
utils::ClosureSubst,
|
||||
}, utils::ClosureSubst, Canonical, FnAbi, FnPointer, FnSig, Goal, InEnvironment, Interner, Lifetime, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt
|
||||
};
|
||||
|
||||
use super::unify::InferenceTable;
|
||||
@ -42,7 +40,7 @@ fn simple(kind: Adjust) -> impl FnOnce(Ty) -> Vec<Adjustment> {
|
||||
fn success(
|
||||
adj: Vec<Adjustment>,
|
||||
target: Ty,
|
||||
goals: Vec<InEnvironment<Goal<Interner>>>,
|
||||
goals: Vec<InEnvironment<Goal>>,
|
||||
) -> CoerceResult {
|
||||
Ok(InferOk { goals, value: (adj, target) })
|
||||
}
|
||||
@ -304,7 +302,7 @@ impl InferenceTable<'_> {
|
||||
fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty, coerce_never: CoerceNever) -> CoerceResult {
|
||||
if from_ty.is_never() {
|
||||
if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) {
|
||||
self.set_diverging(*tv, true);
|
||||
self.set_diverging(*tv, TyVariableKind::General, true);
|
||||
}
|
||||
if coerce_never == CoerceNever::Yes {
|
||||
// Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
|
||||
@ -707,41 +705,15 @@ impl InferenceTable<'_> {
|
||||
b.push(coerce_from).push(to_ty.clone()).build()
|
||||
};
|
||||
|
||||
let goal: InEnvironment<DomainGoal> =
|
||||
InEnvironment::new(&self.trait_env.env, coerce_unsized_tref.cast(Interner));
|
||||
let goal: Goal = coerce_unsized_tref.cast(Interner);
|
||||
|
||||
let canonicalized = self.canonicalize_with_free_vars(goal);
|
||||
|
||||
// FIXME: rustc's coerce_unsized is more specialized -- it only tries to
|
||||
// solve `CoerceUnsized` and `Unsize` goals at this point and leaves the
|
||||
// rest for later. Also, there's some logic about sized type variables.
|
||||
// Need to find out in what cases this is necessary
|
||||
let solution = self.db.trait_solve(
|
||||
krate,
|
||||
self.trait_env.block,
|
||||
canonicalized.value.clone().cast(Interner),
|
||||
);
|
||||
|
||||
match solution {
|
||||
// FIXME: this is a weaker guarantee than Chalk's `Guidance::Unique`
|
||||
// was. Chalk's unique guidance at least guarantees that the real solution
|
||||
// is some "subset" of the solutions matching the guidance, but the
|
||||
// substs for `Certainty::No` don't have that same guarantee (I think).
|
||||
NextTraitSolveResult::Certain(v) => {
|
||||
canonicalized.apply_solution(
|
||||
self,
|
||||
Canonical {
|
||||
binders: v.binders,
|
||||
// FIXME handle constraints
|
||||
value: v.value.subst,
|
||||
},
|
||||
);
|
||||
self.commit_if_ok(|table| {
|
||||
match table.solve_obligation(goal) {
|
||||
Ok(Certainty::Yes) => Ok(()),
|
||||
Ok(Certainty::Maybe(_)) => Ok(()),
|
||||
Err(_) => Err(TypeError),
|
||||
}
|
||||
// ...so, should think about how to get some actually get some guidance here
|
||||
NextTraitSolveResult::Uncertain(..) | NextTraitSolveResult::NoSolution => {
|
||||
return Err(TypeError);
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
||||
let unsize =
|
||||
Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: to_ty.clone() };
|
||||
|
@ -23,28 +23,9 @@ use stdx::always;
|
||||
use syntax::ast::RangeOp;
|
||||
|
||||
use crate::{
|
||||
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext,
|
||||
DeclOrigin, IncorrectGenericsLenKind, Interner, Rawness, Scalar, Substitution,
|
||||
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||
autoderef::{Autoderef, builtin_deref, deref_by_trait},
|
||||
consteval,
|
||||
generics::generics,
|
||||
infer::{
|
||||
BreakableKind,
|
||||
coerce::{CoerceMany, CoerceNever, CoercionCause},
|
||||
find_continuable,
|
||||
pat::contains_explicit_ref_binding,
|
||||
},
|
||||
lang_items::lang_items_for_bin_op,
|
||||
lower::{
|
||||
LifetimeElisionKind, ParamLoweringMode, lower_to_chalk_mutability,
|
||||
path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings},
|
||||
},
|
||||
mapping::{ToChalk, from_chalk},
|
||||
method_resolution::{self, VisibleFromModule},
|
||||
primitive::{self, UintTy},
|
||||
static_lifetime, to_chalk_trait_id,
|
||||
traits::FnTrait,
|
||||
autoderef::{builtin_deref, deref_by_trait, Autoderef}, consteval, generics::generics, infer::{
|
||||
coerce::{CoerceMany, CoerceNever, CoercionCause}, find_continuable, pat::contains_explicit_ref_binding, BreakableKind
|
||||
}, lang_items::lang_items_for_bin_op, lower::{lower_to_chalk_mutability, path::{substs_from_args_and_bindings, GenericArgsLowerer, TypeLikeConst}, ParamLoweringMode}, mapping::{from_chalk, ToChalk}, method_resolution::{self, VisibleFromModule}, next_solver::mapping::ChalkToNextSolver, primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, traits::FnTrait, Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, DeclOrigin, IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -826,7 +807,7 @@ impl InferenceContext<'_> {
|
||||
let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes);
|
||||
|
||||
if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) {
|
||||
let canonicalized = self.canonicalize(base_ty.clone());
|
||||
let canonicalized = ChalkToNextSolver::from_nextsolver(self.canonicalize(base_ty.clone().to_nextsolver(self.table.interner)), self.table.interner);
|
||||
let receiver_adjustments = method_resolution::resolve_indexing_op(
|
||||
self.db,
|
||||
self.table.trait_env.clone(),
|
||||
@ -932,6 +913,7 @@ impl InferenceContext<'_> {
|
||||
}
|
||||
None => {
|
||||
let expected_ty = expected.to_option(&mut self.table);
|
||||
tracing::debug!(?expected_ty);
|
||||
let opt_ty = match expected_ty.as_ref().map(|it| it.kind(Interner)) {
|
||||
Some(TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))) => expected_ty,
|
||||
Some(TyKind::Scalar(Scalar::Char)) => {
|
||||
@ -1678,7 +1660,7 @@ impl InferenceContext<'_> {
|
||||
None => {
|
||||
// no field found, lets attempt to resolve it like a function so that IDE things
|
||||
// work out while people are typing
|
||||
let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
|
||||
let canonicalized_receiver = self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner));
|
||||
let resolved = method_resolution::lookup_method(
|
||||
self.db,
|
||||
&canonicalized_receiver,
|
||||
@ -1824,7 +1806,7 @@ impl InferenceContext<'_> {
|
||||
expected: &Expectation,
|
||||
) -> Ty {
|
||||
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes);
|
||||
let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
|
||||
let canonicalized_receiver = self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner));
|
||||
|
||||
let resolved = method_resolution::lookup_method(
|
||||
self.db,
|
||||
|
@ -10,15 +10,7 @@ use hir_expand::name::Name;
|
||||
use stdx::never;
|
||||
|
||||
use crate::{
|
||||
InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt,
|
||||
TyKind, ValueTyDefId,
|
||||
builder::ParamKind,
|
||||
consteval, error_lifetime,
|
||||
generics::generics,
|
||||
infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext,
|
||||
lower::LifetimeElisionKind,
|
||||
method_resolution::{self, VisibleFromModule},
|
||||
to_chalk_trait_id,
|
||||
builder::ParamKind, consteval, error_lifetime, generics::generics, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, method_resolution::{self, VisibleFromModule}, next_solver::mapping::ChalkToNextSolver, to_chalk_trait_id, InferenceDiagnostic, Interner, LifetimeElisionKind, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId
|
||||
};
|
||||
|
||||
use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource};
|
||||
@ -322,7 +314,7 @@ impl InferenceContext<'_> {
|
||||
return Some(result);
|
||||
}
|
||||
|
||||
let canonical_ty = self.canonicalize(ty.clone());
|
||||
let canonical_ty = self.canonicalize(ty.clone().to_nextsolver(self.table.interner));
|
||||
|
||||
let mut not_visible = None;
|
||||
let res = method_resolution::iterate_method_candidates(
|
||||
|
@ -3,35 +3,27 @@
|
||||
use std::{fmt, mem};
|
||||
|
||||
use chalk_ir::{
|
||||
CanonicalVarKind, FloatTy, IntTy, TyVariableKind, UniverseIndex, cast::Cast,
|
||||
fold::TypeFoldable, interner::HasInterner, zip::Zip,
|
||||
cast::Cast, fold::TypeFoldable, interner::HasInterner, CanonicalVarKind, FloatTy, IntTy, TyVariableKind,
|
||||
};
|
||||
use chalk_solve::infer::ParameterEnaVariableExt;
|
||||
use either::Either;
|
||||
use ena::unify::UnifyKey;
|
||||
use hir_def::{AdtId, lang_item::LangItem};
|
||||
use hir_expand::name::Name;
|
||||
use intern::sym;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustc_next_trait_solver::solve::HasChanged;
|
||||
use rustc_type_ir::{inherent::Span, relate::{solver_relating::RelateExt, Relate}, solve::{Certainty, NoSolution}, FloatVid, IntVid, TyVid};
|
||||
use smallvec::SmallVec;
|
||||
use triomphe::Arc;
|
||||
|
||||
use super::{InferOk, InferResult, InferenceContext, TypeError};
|
||||
use crate::{
|
||||
AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal,
|
||||
GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime,
|
||||
OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment,
|
||||
TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause,
|
||||
consteval::unknown_const,
|
||||
db::HirDatabase,
|
||||
fold_generic_args, fold_tys_and_consts, to_chalk_trait_id,
|
||||
traits::{FnTrait, NextTraitSolveResult},
|
||||
consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, next_solver::{infer::{canonical::canonicalizer::OriginalQueryValues, snapshot::CombinedSnapshot, DbInternerInferExt, InferCtxt}, mapping::{ChalkToNextSolver, InferenceVarExt}, DbInterner, ParamEnvAnd, SolverDefIds}, to_chalk_trait_id, traits::{next_trait_solve, next_trait_solve_canonical, next_trait_solve_in_ctxt, FnTrait, NextTraitSolveResult}, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause
|
||||
};
|
||||
|
||||
impl InferenceContext<'_> {
|
||||
pub(super) fn canonicalize<T>(&mut self, t: T) -> Canonical<T>
|
||||
impl<'db> InferenceContext<'db> {
|
||||
pub(super) fn canonicalize<T>(&mut self, t: T) -> rustc_type_ir::Canonical<DbInterner<'db>, T>
|
||||
where
|
||||
T: TypeFoldable<Interner> + HasInterner<Interner = Interner>,
|
||||
T: rustc_type_ir::TypeFoldable<DbInterner<'db>>,
|
||||
{
|
||||
self.table.canonicalize(t)
|
||||
}
|
||||
@ -42,11 +34,11 @@ impl InferenceContext<'_> {
|
||||
) -> SmallVec<[WhereClause; 4]> {
|
||||
self.table.resolve_obligations_as_possible();
|
||||
|
||||
let root = self.table.var_unification_table.inference_var_root(self_ty);
|
||||
let root = InferenceVar::from_vid(self.table.infer_ctxt.root_var(self_ty.to_vid()));
|
||||
let pending_obligations = mem::take(&mut self.table.pending_obligations);
|
||||
let obligations = pending_obligations
|
||||
.iter()
|
||||
.filter_map(|obligation| match obligation.value.value.goal.data(Interner) {
|
||||
.filter_map(|obligation| match obligation.goal.data(Interner) {
|
||||
GoalData::DomainGoal(DomainGoal::Holds(clause)) => {
|
||||
let ty = match clause {
|
||||
WhereClause::AliasEq(AliasEq {
|
||||
@ -59,18 +51,9 @@ impl InferenceContext<'_> {
|
||||
WhereClause::TypeOutlives(to) => to.ty.clone(),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let uncanonical =
|
||||
chalk_ir::Substitute::apply(&obligation.free_vars, ty, Interner);
|
||||
if matches!(
|
||||
self.resolve_ty_shallow(&uncanonical).kind(Interner),
|
||||
TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root,
|
||||
) {
|
||||
Some(chalk_ir::Substitute::apply(
|
||||
&obligation.free_vars,
|
||||
clause.clone(),
|
||||
Interner,
|
||||
))
|
||||
let ty = self.resolve_ty_shallow(&ty);
|
||||
if matches!(ty.kind(Interner), TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root) {
|
||||
Some(clause.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -229,32 +212,31 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>;
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct InferenceTable<'a> {
|
||||
pub(crate) db: &'a dyn HirDatabase,
|
||||
pub(crate) interner: DbInterner<'a>,
|
||||
pub(crate) trait_env: Arc<TraitEnvironment>,
|
||||
pub(crate) tait_coercion_table: Option<FxHashMap<OpaqueTyId, Ty>>,
|
||||
var_unification_table: ChalkInferenceTable,
|
||||
type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
|
||||
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
|
||||
/// Double buffer used in [`Self::resolve_obligations_as_possible`] to cut down on
|
||||
/// temporary allocations.
|
||||
resolve_obligations_buffer: Vec<Canonicalized<InEnvironment<Goal>>>,
|
||||
infer_ctxt: InferCtxt<'a>,
|
||||
diverging_tys: FxHashSet<Ty>,
|
||||
pending_obligations: Vec<InEnvironment<Goal>>,
|
||||
}
|
||||
|
||||
pub(crate) struct InferenceTableSnapshot {
|
||||
var_table_snapshot: chalk_solve::infer::InferenceSnapshot<Interner>,
|
||||
type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
|
||||
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
|
||||
ctxt_snapshot: CombinedSnapshot,
|
||||
diverging_tys: FxHashSet<Ty>,
|
||||
pending_obligations: Vec<InEnvironment<Goal>>,
|
||||
}
|
||||
|
||||
impl<'a> InferenceTable<'a> {
|
||||
pub(crate) fn new(db: &'a dyn HirDatabase, trait_env: Arc<TraitEnvironment>) -> Self {
|
||||
let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block);
|
||||
InferenceTable {
|
||||
db,
|
||||
interner,
|
||||
trait_env,
|
||||
tait_coercion_table: None,
|
||||
var_unification_table: ChalkInferenceTable::new(),
|
||||
type_variable_table: SmallVec::new(),
|
||||
infer_ctxt: interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis { defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []) }),
|
||||
diverging_tys: FxHashSet::default(),
|
||||
pending_obligations: Vec::new(),
|
||||
resolve_obligations_buffer: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,29 +247,43 @@ impl<'a> InferenceTable<'a> {
|
||||
/// marked as diverging if necessary, so that resolving them gives the right
|
||||
/// result.
|
||||
pub(super) fn propagate_diverging_flag(&mut self) {
|
||||
for i in 0..self.type_variable_table.len() {
|
||||
if !self.type_variable_table[i].contains(TypeVariableFlags::DIVERGING) {
|
||||
continue;
|
||||
let mut new_tys = FxHashSet::default();
|
||||
for ty in self.diverging_tys.iter() {
|
||||
match ty.kind(Interner) {
|
||||
TyKind::InferenceVar(var, kind) => {
|
||||
match kind {
|
||||
TyVariableKind::General => {
|
||||
let root = InferenceVar::from(self.infer_ctxt.root_var(TyVid::from_u32(var.index())).as_u32());
|
||||
if root.index() != var.index() {
|
||||
new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner));
|
||||
}
|
||||
}
|
||||
TyVariableKind::Integer => {
|
||||
let root = InferenceVar::from(self.infer_ctxt.inner.borrow_mut().int_unification_table().find(IntVid::from_usize(var.index() as usize)).as_u32());
|
||||
if root.index() != var.index() {
|
||||
new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner));
|
||||
}
|
||||
}
|
||||
TyVariableKind::Float => {
|
||||
let root = InferenceVar::from(self.infer_ctxt.inner.borrow_mut().float_unification_table().find(FloatVid::from_usize(var.index() as usize)).as_u32());
|
||||
if root.index() != var.index() {
|
||||
new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let v = InferenceVar::from(i as u32);
|
||||
let root = self.var_unification_table.inference_var_root(v);
|
||||
self.modify_type_variable_flag(root, |f| {
|
||||
*f |= TypeVariableFlags::DIVERGING;
|
||||
});
|
||||
}
|
||||
self.diverging_tys.extend(new_tys.into_iter());
|
||||
}
|
||||
|
||||
pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) {
|
||||
self.modify_type_variable_flag(iv, |f| {
|
||||
f.set(TypeVariableFlags::DIVERGING, diverging);
|
||||
});
|
||||
pub(super) fn set_diverging(&mut self, iv: InferenceVar, kind: TyVariableKind, diverging: bool) {
|
||||
self.diverging_tys.insert(TyKind::InferenceVar(iv, kind).intern(Interner));
|
||||
}
|
||||
|
||||
fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty {
|
||||
let is_diverging = self
|
||||
.type_variable_table
|
||||
.get(iv.index() as usize)
|
||||
.is_some_and(|data| data.contains(TypeVariableFlags::DIVERGING));
|
||||
let is_diverging = self.diverging_tys.contains(&TyKind::InferenceVar(iv, kind).intern(Interner));
|
||||
if is_diverging {
|
||||
return TyKind::Never.intern(Interner);
|
||||
}
|
||||
@ -299,30 +295,27 @@ impl<'a> InferenceTable<'a> {
|
||||
.intern(Interner)
|
||||
}
|
||||
|
||||
pub(crate) fn canonicalize_with_free_vars<T>(&mut self, t: T) -> Canonicalized<T>
|
||||
pub(crate) fn canonicalize_with_free_vars<T>(&mut self, t: ParamEnvAnd<'a, T>) -> (rustc_type_ir::Canonical<DbInterner<'a>, ParamEnvAnd<'a, T>>, OriginalQueryValues<'a>)
|
||||
where
|
||||
T: TypeFoldable<Interner> + HasInterner<Interner = Interner>,
|
||||
T: rustc_type_ir::TypeFoldable<DbInterner<'a>>,
|
||||
{
|
||||
// try to resolve obligations before canonicalizing, since this might
|
||||
// result in new knowledge about variables
|
||||
self.resolve_obligations_as_possible();
|
||||
let result = self.var_unification_table.canonicalize(Interner, t);
|
||||
let free_vars = result
|
||||
.free_vars
|
||||
.into_iter()
|
||||
.map(|free_var| free_var.to_generic_arg(Interner))
|
||||
.collect();
|
||||
Canonicalized { value: result.quantified, free_vars }
|
||||
|
||||
let mut orig_values = OriginalQueryValues::default();
|
||||
let result = self.infer_ctxt.canonicalize_query(t, &mut orig_values);
|
||||
(result.canonical, orig_values)
|
||||
}
|
||||
|
||||
pub(crate) fn canonicalize<T>(&mut self, t: T) -> Canonical<T>
|
||||
pub(crate) fn canonicalize<T>(&mut self, t: T) -> rustc_type_ir::Canonical<DbInterner<'a>, T>
|
||||
where
|
||||
T: TypeFoldable<Interner> + HasInterner<Interner = Interner>,
|
||||
T: rustc_type_ir::TypeFoldable<DbInterner<'a>>,
|
||||
{
|
||||
// try to resolve obligations before canonicalizing, since this might
|
||||
// result in new knowledge about variables
|
||||
self.resolve_obligations_as_possible();
|
||||
self.var_unification_table.canonicalize(Interner, t).quantified
|
||||
self.infer_ctxt.canonicalize_response(t)
|
||||
}
|
||||
|
||||
/// Recurses through the given type, normalizing associated types mentioned
|
||||
@ -348,6 +341,7 @@ impl<'a> InferenceTable<'a> {
|
||||
self.resolve_ty_shallow(&ty)
|
||||
}
|
||||
TyKind::AssociatedType(id, subst) => {
|
||||
return Either::Left(self.resolve_ty_shallow(&ty));
|
||||
if ty.data(Interner).flags.intersects(
|
||||
chalk_ir::TypeFlags::HAS_TY_INFER
|
||||
| chalk_ir::TypeFlags::HAS_CT_INFER,
|
||||
@ -370,21 +364,24 @@ impl<'a> InferenceTable<'a> {
|
||||
)),
|
||||
);
|
||||
let in_env = InEnvironment::new(&self.trait_env.env, goal);
|
||||
let goal = in_env.to_nextsolver(self.interner);
|
||||
let goal = ParamEnvAnd { param_env: goal.param_env, value: goal.predicate };
|
||||
|
||||
let canonicalized = {
|
||||
let result =
|
||||
self.var_unification_table.canonicalize(Interner, in_env);
|
||||
let free_vars = result
|
||||
.free_vars
|
||||
.into_iter()
|
||||
.map(|free_var| free_var.to_generic_arg(Interner))
|
||||
.collect();
|
||||
Canonicalized { value: result.quantified, free_vars }
|
||||
let (canonical_goal, _orig_values) = {
|
||||
let mut orig_values = OriginalQueryValues::default();
|
||||
let result = self.infer_ctxt.canonicalize_query(goal, &mut orig_values);
|
||||
(result.canonical, orig_values)
|
||||
};
|
||||
let solution = self.db.trait_solve(
|
||||
let canonical_goal = rustc_type_ir::Canonical {
|
||||
max_universe: canonical_goal.max_universe,
|
||||
variables: canonical_goal.variables,
|
||||
value: crate::next_solver::Goal { param_env: canonical_goal.value.param_env, predicate: canonical_goal.value.value },
|
||||
};
|
||||
let solution = next_trait_solve_canonical(
|
||||
self.db,
|
||||
self.trait_env.krate,
|
||||
self.trait_env.block,
|
||||
canonicalized.value.clone(),
|
||||
canonical_goal.clone(),
|
||||
);
|
||||
if let NextTraitSolveResult::Certain(canonical_subst) = solution {
|
||||
// This is not great :) But let's just assert this for now and come back to it later.
|
||||
@ -512,38 +509,27 @@ impl<'a> InferenceTable<'a> {
|
||||
var
|
||||
}
|
||||
|
||||
fn modify_type_variable_flag<F>(&mut self, var: InferenceVar, cb: F)
|
||||
where
|
||||
F: FnOnce(&mut TypeVariableFlags),
|
||||
{
|
||||
let idx = var.index() as usize;
|
||||
if self.type_variable_table.len() <= idx {
|
||||
self.extend_type_variable_table(idx);
|
||||
}
|
||||
if let Some(f) = self.type_variable_table.get_mut(idx) {
|
||||
cb(f);
|
||||
}
|
||||
}
|
||||
fn extend_type_variable_table(&mut self, to_index: usize) {
|
||||
let count = to_index - self.type_variable_table.len() + 1;
|
||||
self.type_variable_table.extend(std::iter::repeat_n(TypeVariableFlags::default(), count));
|
||||
}
|
||||
|
||||
fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty {
|
||||
let var = self.var_unification_table.new_variable(UniverseIndex::ROOT);
|
||||
// Chalk might have created some type variables for its own purposes that we don't know about...
|
||||
self.extend_type_variable_table(var.index() as usize);
|
||||
assert_eq!(var.index() as usize, self.type_variable_table.len() - 1);
|
||||
let flags = self.type_variable_table.get_mut(var.index() as usize).unwrap();
|
||||
let var = match kind {
|
||||
TyVariableKind::General => {
|
||||
let var = self.infer_ctxt.next_ty_vid();
|
||||
InferenceVar::from(var.as_u32())
|
||||
}
|
||||
TyVariableKind::Integer => {
|
||||
let var = self.infer_ctxt.next_int_vid();
|
||||
InferenceVar::from(var.as_u32())
|
||||
}
|
||||
TyVariableKind::Float => {
|
||||
let var = self.infer_ctxt.next_float_vid();
|
||||
InferenceVar::from(var.as_u32())
|
||||
}
|
||||
};
|
||||
|
||||
let ty = var.to_ty(Interner, kind);
|
||||
if diverging {
|
||||
*flags |= TypeVariableFlags::DIVERGING;
|
||||
self.diverging_tys.insert(ty.clone());
|
||||
}
|
||||
if matches!(kind, TyVariableKind::Integer) {
|
||||
*flags |= TypeVariableFlags::INTEGER;
|
||||
} else if matches!(kind, TyVariableKind::Float) {
|
||||
*flags |= TypeVariableFlags::FLOAT;
|
||||
}
|
||||
var.to_ty_with_kind(Interner, kind)
|
||||
ty
|
||||
}
|
||||
|
||||
pub(crate) fn new_type_var(&mut self) -> Ty {
|
||||
@ -563,12 +549,14 @@ impl<'a> InferenceTable<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn new_const_var(&mut self, ty: Ty) -> Const {
|
||||
let var = self.var_unification_table.new_variable(UniverseIndex::ROOT);
|
||||
let var = self.infer_ctxt.next_const_vid();
|
||||
let var = InferenceVar::from(var.as_u32());
|
||||
var.to_const(Interner, ty)
|
||||
}
|
||||
|
||||
pub(crate) fn new_lifetime_var(&mut self) -> Lifetime {
|
||||
let var = self.var_unification_table.new_variable(UniverseIndex::ROOT);
|
||||
let var = self.infer_ctxt.next_region_vid();
|
||||
let var = InferenceVar::from(var.as_u32());
|
||||
var.to_lifetime(Interner)
|
||||
}
|
||||
|
||||
@ -580,16 +568,16 @@ impl<'a> InferenceTable<'a> {
|
||||
where
|
||||
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
|
||||
{
|
||||
self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback)
|
||||
self.resolve_with_fallback_inner(t, &fallback)
|
||||
}
|
||||
|
||||
pub(crate) fn fresh_subst(&mut self, binders: &[CanonicalVarKind<Interner>]) -> Substitution {
|
||||
Substitution::from_iter(
|
||||
Interner,
|
||||
binders.iter().map(|kind| {
|
||||
let param_infer_var =
|
||||
kind.map_ref(|&ui| self.var_unification_table.new_variable(ui));
|
||||
param_infer_var.to_generic_arg(Interner)
|
||||
binders.iter().map(|kind| match &kind.kind {
|
||||
chalk_ir::VariableKind::Ty(ty_variable_kind) => self.new_var(*ty_variable_kind, false).cast(Interner),
|
||||
chalk_ir::VariableKind::Lifetime => self.new_lifetime_var().cast(Interner),
|
||||
chalk_ir::VariableKind::Const(ty) => self.new_const_var(ty.clone()).cast(Interner),
|
||||
}),
|
||||
)
|
||||
}
|
||||
@ -601,16 +589,23 @@ impl<'a> InferenceTable<'a> {
|
||||
let subst = self.fresh_subst(canonical.binders.as_slice(Interner));
|
||||
subst.apply(canonical.value, Interner)
|
||||
}
|
||||
|
||||
pub(crate) fn instantiate_canonical_ns<T>(&mut self, canonical: rustc_type_ir::Canonical<DbInterner<'a>, T>) -> T
|
||||
where
|
||||
T: rustc_type_ir::TypeFoldable<DbInterner<'a>>,
|
||||
{
|
||||
self.infer_ctxt.instantiate_canonical(&canonical).0
|
||||
}
|
||||
|
||||
fn resolve_with_fallback_inner<T>(
|
||||
&mut self,
|
||||
var_stack: &mut Vec<InferenceVar>,
|
||||
t: T,
|
||||
fallback: &dyn Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
|
||||
) -> T
|
||||
where
|
||||
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
|
||||
{
|
||||
let mut var_stack = &mut vec![];
|
||||
t.fold_with(
|
||||
&mut resolve::Resolver { table: self, var_stack, fallback },
|
||||
DebruijnIndex::INNERMOST,
|
||||
@ -639,29 +634,26 @@ impl<'a> InferenceTable<'a> {
|
||||
let int_fallback = TyKind::Scalar(Scalar::Int(IntTy::I32)).intern(Interner);
|
||||
let float_fallback = TyKind::Scalar(Scalar::Float(FloatTy::F64)).intern(Interner);
|
||||
|
||||
let scalar_vars: Vec<_> = self
|
||||
.type_variable_table
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, flags)| {
|
||||
let kind = if flags.contains(TypeVariableFlags::INTEGER) {
|
||||
TyVariableKind::Integer
|
||||
} else if flags.contains(TypeVariableFlags::FLOAT) {
|
||||
TyVariableKind::Float
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// FIXME: This is not really the nicest way to get `InferenceVar`s. Can we get them
|
||||
// without directly constructing them from `index`?
|
||||
let var = InferenceVar::from(index as u32).to_ty(Interner, kind);
|
||||
Some(var)
|
||||
})
|
||||
.collect();
|
||||
|
||||
for var in scalar_vars {
|
||||
let int_vars = self.infer_ctxt.inner.borrow_mut().int_unification_table().len();
|
||||
for v in 0..int_vars {
|
||||
let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Integer);
|
||||
let maybe_resolved = self.resolve_ty_shallow(&var);
|
||||
if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) {
|
||||
// I don't think we can ever unify these vars with float vars, but keep this here for now
|
||||
let fallback = match kind {
|
||||
TyVariableKind::Integer => &int_fallback,
|
||||
TyVariableKind::Float => &float_fallback,
|
||||
TyVariableKind::General => unreachable!(),
|
||||
};
|
||||
self.unify(&var, fallback);
|
||||
}
|
||||
}
|
||||
let float_vars = self.infer_ctxt.inner.borrow_mut().float_unification_table().len();
|
||||
for v in 0..float_vars {
|
||||
let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Integer);
|
||||
let maybe_resolved = self.resolve_ty_shallow(&var);
|
||||
if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) {
|
||||
// I don't think we can ever unify these vars with float vars, but keep this here for now
|
||||
let fallback = match kind {
|
||||
TyVariableKind::Integer => &int_fallback,
|
||||
TyVariableKind::Float => &float_fallback,
|
||||
@ -673,7 +665,7 @@ impl<'a> InferenceTable<'a> {
|
||||
}
|
||||
|
||||
/// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
|
||||
pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
|
||||
pub(crate) fn unify<T: ChalkToNextSolver<'a, U>, U: Relate<DbInterner<'a>>>(&mut self, ty1: &T, ty2: &T) -> bool {
|
||||
let result = match self.try_unify(ty1, ty2) {
|
||||
Ok(r) => r,
|
||||
Err(_) => return false,
|
||||
@ -683,58 +675,65 @@ impl<'a> InferenceTable<'a> {
|
||||
}
|
||||
|
||||
/// Unify two relatable values (e.g. `Ty`) and check whether trait goals which arise from that could be fulfilled
|
||||
pub(crate) fn unify_deeply<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
|
||||
pub(crate) fn unify_deeply<T: ChalkToNextSolver<'a, U>, U: Relate<DbInterner<'a>>>(&mut self, ty1: &T, ty2: &T) -> bool {
|
||||
let result = match self.try_unify(ty1, ty2) {
|
||||
Ok(r) => r,
|
||||
Err(_) => return false,
|
||||
};
|
||||
result.goals.iter().all(|goal| {
|
||||
let canonicalized = self.canonicalize_with_free_vars(goal.clone());
|
||||
self.try_resolve_obligation(&canonicalized).certain()
|
||||
let goal = goal.to_nextsolver(self.interner);
|
||||
match next_trait_solve_in_ctxt(&self.infer_ctxt, goal) {
|
||||
Ok((_, Certainty::Yes)) => true,
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the
|
||||
/// caller needs to deal with them.
|
||||
pub(crate) fn try_unify<T: ?Sized + Zip<Interner>>(
|
||||
pub(crate) fn try_unify<T: ChalkToNextSolver<'a, U>, U: Relate<DbInterner<'a>>>(
|
||||
&mut self,
|
||||
t1: &T,
|
||||
t2: &T,
|
||||
) -> InferResult<()> {
|
||||
match self.var_unification_table.relate(
|
||||
Interner,
|
||||
&self.db,
|
||||
&self.trait_env.env,
|
||||
chalk_ir::Variance::Invariant,
|
||||
t1,
|
||||
t2,
|
||||
) {
|
||||
Ok(result) => Ok(InferOk { goals: result.goals, value: () }),
|
||||
Err(chalk_ir::NoSolution) => Err(TypeError),
|
||||
let param_env = self.trait_env.env.to_nextsolver(self.interner);
|
||||
let lhs = t1.to_nextsolver(self.interner);
|
||||
let rhs = t2.to_nextsolver(self.interner);
|
||||
let variance = rustc_type_ir::Variance::Invariant;
|
||||
let span = crate::next_solver::Span::dummy();
|
||||
match self.infer_ctxt.relate(param_env, lhs, variance, rhs, span) {
|
||||
Ok(res) => {
|
||||
let goals = res.into_iter().map(|g| ChalkToNextSolver::from_nextsolver(g, self.interner)).collect();
|
||||
Ok(InferOk { goals, value: () })
|
||||
}
|
||||
Err(_) => {
|
||||
Err(TypeError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If `ty` is a type variable with known type, returns that type;
|
||||
/// otherwise, return ty.
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty {
|
||||
if !ty.data(Interner).flags.intersects(chalk_ir::TypeFlags::HAS_FREE_LOCAL_NAMES) {
|
||||
return ty.clone();
|
||||
}
|
||||
self.resolve_obligations_as_possible();
|
||||
self.var_unification_table.normalize_ty_shallow(Interner, ty).unwrap_or_else(|| ty.clone())
|
||||
ChalkToNextSolver::from_nextsolver(self.infer_ctxt.resolve_vars_if_possible(ty.to_nextsolver(self.interner)), self.interner)
|
||||
}
|
||||
|
||||
pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot {
|
||||
let var_table_snapshot = self.var_unification_table.snapshot();
|
||||
let type_variable_table = self.type_variable_table.clone();
|
||||
let ctxt_snapshot = self.infer_ctxt.start_snapshot();
|
||||
let diverging_tys = self.diverging_tys.clone();
|
||||
let pending_obligations = self.pending_obligations.clone();
|
||||
InferenceTableSnapshot { var_table_snapshot, pending_obligations, type_variable_table }
|
||||
InferenceTableSnapshot {ctxt_snapshot, pending_obligations, diverging_tys }
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot) {
|
||||
self.var_unification_table.rollback_to(snapshot.var_table_snapshot);
|
||||
self.type_variable_table = snapshot.type_variable_table;
|
||||
self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot);
|
||||
self.diverging_tys = snapshot.diverging_tys;
|
||||
self.pending_obligations = snapshot.pending_obligations;
|
||||
}
|
||||
|
||||
@ -746,15 +745,39 @@ impl<'a> InferenceTable<'a> {
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) fn commit_if_ok<T, E>(&mut self, f: impl FnOnce(&mut InferenceTable<'_>) -> Result<T, E>) -> Result<T, E> {
|
||||
let snapshot = self.snapshot();
|
||||
let result = f(self);
|
||||
match result {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
self.rollback_to(snapshot);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Checks an obligation without registering it. Useful mostly to check
|
||||
/// whether a trait *might* be implemented before deciding to 'lock in' the
|
||||
/// choice (during e.g. method resolution or deref).
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn try_obligation(&mut self, goal: Goal) -> NextTraitSolveResult {
|
||||
let in_env = InEnvironment::new(&self.trait_env.env, goal);
|
||||
let canonicalized = self.canonicalize(in_env);
|
||||
let canonicalized = self.canonicalize(in_env.to_nextsolver(self.interner));
|
||||
|
||||
self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized)
|
||||
next_trait_solve_canonical(self.db, self.trait_env.krate, self.trait_env.block, canonicalized)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn solve_obligation(&mut self, goal: Goal) -> Result<Certainty, NoSolution> {
|
||||
let goal = InEnvironment::new(&self.trait_env.env, goal);
|
||||
let Some(goal) = self.unify_opaque_instead_of_solve(goal) else {
|
||||
return Ok(Certainty::Yes);
|
||||
};
|
||||
|
||||
let goal = goal.to_nextsolver(self.interner);
|
||||
let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal);
|
||||
result.map(|m| m.1)
|
||||
}
|
||||
|
||||
pub(crate) fn register_obligation(&mut self, goal: Goal) {
|
||||
@ -762,8 +785,8 @@ impl<'a> InferenceTable<'a> {
|
||||
self.register_obligation_in_env(in_env)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
fn register_obligation_in_env(&mut self, goal: InEnvironment<Goal>) {
|
||||
// If this goal is an `AliasEq` for an opaque type, just unify instead of trying to solve (since the next-solver is lazy)
|
||||
fn unify_opaque_instead_of_solve(&mut self, goal: InEnvironment<Goal>) -> Option<InEnvironment<Goal>> {
|
||||
match goal.goal.data(Interner) {
|
||||
chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
|
||||
chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }),
|
||||
@ -779,7 +802,7 @@ impl<'a> InferenceTable<'a> {
|
||||
.intern(Interner),
|
||||
ty,
|
||||
) {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -788,20 +811,20 @@ impl<'a> InferenceTable<'a> {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let canonicalized = {
|
||||
let result = self.var_unification_table.canonicalize(Interner, goal);
|
||||
let free_vars = result
|
||||
.free_vars
|
||||
.into_iter()
|
||||
.map(|free_var| free_var.to_generic_arg(Interner))
|
||||
.collect();
|
||||
Canonicalized { value: result.quantified, free_vars }
|
||||
};
|
||||
tracing::debug!(?canonicalized);
|
||||
let solution = self.try_resolve_obligation(&canonicalized);
|
||||
tracing::debug!(?solution);
|
||||
if solution.uncertain() {
|
||||
self.pending_obligations.push(canonicalized);
|
||||
Some(goal)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
fn register_obligation_in_env(&mut self, goal: InEnvironment<Goal>) {
|
||||
let Some(goal) = self.unify_opaque_instead_of_solve(goal) else { return };
|
||||
let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal.to_nextsolver(self.interner));
|
||||
tracing::debug!(?result);
|
||||
match result {
|
||||
Ok((_, Certainty::Yes)) => {}
|
||||
Err(rustc_type_ir::solve::NoSolution) => {}
|
||||
Ok((_, Certainty::Maybe(_))) => {
|
||||
self.pending_obligations.push(goal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -812,28 +835,35 @@ impl<'a> InferenceTable<'a> {
|
||||
pub(crate) fn resolve_obligations_as_possible(&mut self) {
|
||||
let _span = tracing::info_span!("resolve_obligations_as_possible").entered();
|
||||
let mut changed = true;
|
||||
let mut obligations = mem::take(&mut self.resolve_obligations_buffer);
|
||||
while mem::take(&mut changed) {
|
||||
mem::swap(&mut self.pending_obligations, &mut obligations);
|
||||
let mut obligations = mem::take(&mut self.pending_obligations);
|
||||
|
||||
for canonicalized in obligations.drain(..) {
|
||||
tracing::debug!(obligation = ?canonicalized);
|
||||
if !self.check_changed(&canonicalized) {
|
||||
tracing::debug!("not changed");
|
||||
self.pending_obligations.push(canonicalized);
|
||||
for goal in obligations.drain(..) {
|
||||
tracing::debug!(obligation = ?goal);
|
||||
|
||||
let Some(goal) = self.unify_opaque_instead_of_solve(goal) else {
|
||||
changed = true;
|
||||
continue;
|
||||
};
|
||||
|
||||
let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal.to_nextsolver(self.interner));
|
||||
let (has_changed, certainty) = match result {
|
||||
Ok(result) => result,
|
||||
Err(_) => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if matches!(has_changed, HasChanged::Yes) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
match certainty {
|
||||
Certainty::Yes => {}
|
||||
Certainty::Maybe(_) => self.pending_obligations.push(goal),
|
||||
}
|
||||
changed = true;
|
||||
let uncanonical = chalk_ir::Substitute::apply(
|
||||
&canonicalized.free_vars,
|
||||
canonicalized.value.value,
|
||||
Interner,
|
||||
);
|
||||
self.register_obligation_in_env(uncanonical);
|
||||
}
|
||||
}
|
||||
self.resolve_obligations_buffer = obligations;
|
||||
self.resolve_obligations_buffer.clear();
|
||||
}
|
||||
|
||||
pub(crate) fn fudge_inference<T: TypeFoldable<Interner>>(
|
||||
@ -904,32 +934,13 @@ impl<'a> InferenceTable<'a> {
|
||||
.fold_with(&mut VarFudger { table: self, highest_known_var }, DebruijnIndex::INNERMOST)
|
||||
}
|
||||
|
||||
/// This checks whether any of the free variables in the `canonicalized`
|
||||
/// have changed (either been unified with another variable, or with a
|
||||
/// value). If this is not the case, we don't need to try to solve the goal
|
||||
/// again -- it'll give the same result as last time.
|
||||
fn check_changed(&mut self, canonicalized: &Canonicalized<InEnvironment<Goal>>) -> bool {
|
||||
canonicalized.free_vars.iter().any(|var| {
|
||||
let iv = match var.data(Interner) {
|
||||
GenericArgData::Ty(ty) => ty.inference_var(Interner),
|
||||
GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
|
||||
GenericArgData::Const(c) => c.inference_var(Interner),
|
||||
}
|
||||
.expect("free var is not inference var");
|
||||
if self.var_unification_table.probe_var(iv).is_some() {
|
||||
return true;
|
||||
}
|
||||
let root = self.var_unification_table.inference_var_root(iv);
|
||||
iv != root
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
fn try_resolve_obligation(
|
||||
&mut self,
|
||||
canonicalized: &Canonicalized<InEnvironment<Goal>>,
|
||||
) -> NextTraitSolveResult {
|
||||
let solution = self.db.trait_solve(
|
||||
let solution = next_trait_solve(
|
||||
self.db,
|
||||
self.trait_env.krate,
|
||||
self.trait_env.block,
|
||||
canonicalized.value.clone(),
|
||||
@ -1014,33 +1025,15 @@ impl<'a> InferenceTable<'a> {
|
||||
.fill_with_unknown()
|
||||
.build();
|
||||
|
||||
let trait_env = self.trait_env.env.clone();
|
||||
let obligation = InEnvironment {
|
||||
goal: trait_ref.clone().cast(Interner),
|
||||
environment: trait_env.clone(),
|
||||
};
|
||||
let canonical = self.canonicalize(obligation.clone());
|
||||
if !self
|
||||
.db
|
||||
.trait_solve(krate, self.trait_env.block, canonical.cast(Interner))
|
||||
.no_solution()
|
||||
{
|
||||
self.register_obligation(obligation.goal);
|
||||
let goal: Goal = trait_ref.clone().cast(Interner);
|
||||
if !self.try_obligation(goal.clone()).no_solution() {
|
||||
self.register_obligation(goal);
|
||||
let return_ty = self.normalize_projection_ty(projection);
|
||||
for &fn_x in subtraits {
|
||||
let fn_x_trait = fn_x.get_id(self.db, krate)?;
|
||||
trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
|
||||
let obligation: chalk_ir::InEnvironment<chalk_ir::Goal<Interner>> =
|
||||
InEnvironment {
|
||||
goal: trait_ref.clone().cast(Interner),
|
||||
environment: trait_env.clone(),
|
||||
};
|
||||
let canonical = self.canonicalize(obligation.clone());
|
||||
if !self
|
||||
.db
|
||||
.trait_solve(krate, self.trait_env.block, canonical.cast(Interner))
|
||||
.no_solution()
|
||||
{
|
||||
let goal = trait_ref.clone().cast(Interner);
|
||||
if !self.try_obligation(goal).no_solution() {
|
||||
return Some((fn_x, arg_tys, return_ty));
|
||||
}
|
||||
}
|
||||
@ -1165,20 +1158,26 @@ impl<'a> InferenceTable<'a> {
|
||||
|
||||
impl fmt::Debug for InferenceTable<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("InferenceTable").field("num_vars", &self.type_variable_table.len()).finish()
|
||||
f.debug_struct("InferenceTable").finish()
|
||||
}
|
||||
}
|
||||
|
||||
mod resolve {
|
||||
use super::InferenceTable;
|
||||
use crate::{
|
||||
ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg,
|
||||
InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind,
|
||||
next_solver::mapping::ChalkToNextSolver, ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg, InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind
|
||||
};
|
||||
use chalk_ir::{
|
||||
cast::Cast,
|
||||
fold::{TypeFoldable, TypeFolder},
|
||||
};
|
||||
use rustc_type_ir::{FloatVid, IntVid, TyVid};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub(super) enum VarKind {
|
||||
Ty(TyVariableKind),
|
||||
Const,
|
||||
}
|
||||
|
||||
#[derive(chalk_derive::FallibleTypeFolder)]
|
||||
#[has_interner(Interner)]
|
||||
@ -1188,7 +1187,7 @@ mod resolve {
|
||||
F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
|
||||
> {
|
||||
pub(super) table: &'a mut InferenceTable<'b>,
|
||||
pub(super) var_stack: &'a mut Vec<InferenceVar>,
|
||||
pub(super) var_stack: &'a mut Vec<(InferenceVar, VarKind)>,
|
||||
pub(super) fallback: F,
|
||||
}
|
||||
impl<F> TypeFolder<Interner> for Resolver<'_, '_, F>
|
||||
@ -1209,25 +1208,79 @@ mod resolve {
|
||||
kind: TyVariableKind,
|
||||
outer_binder: DebruijnIndex,
|
||||
) -> Ty {
|
||||
let var = self.table.var_unification_table.inference_var_root(var);
|
||||
if self.var_stack.contains(&var) {
|
||||
// recursive type
|
||||
let default = self.table.fallback_value(var, kind).cast(Interner);
|
||||
return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
|
||||
.assert_ty_ref(Interner)
|
||||
.clone();
|
||||
}
|
||||
if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
|
||||
// known_ty may contain other variables that are known by now
|
||||
self.var_stack.push(var);
|
||||
let result = known_ty.fold_with(self, outer_binder);
|
||||
self.var_stack.pop();
|
||||
result.assert_ty_ref(Interner).clone()
|
||||
} else {
|
||||
let default = self.table.fallback_value(var, kind).cast(Interner);
|
||||
(self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
|
||||
.assert_ty_ref(Interner)
|
||||
.clone()
|
||||
match kind {
|
||||
TyVariableKind::General => {
|
||||
let vid = self.table.infer_ctxt.root_var(TyVid::from(var.index()));
|
||||
let var = InferenceVar::from(vid.as_u32());
|
||||
if self.var_stack.contains(&(var, VarKind::Ty(kind))) {
|
||||
// recursive type
|
||||
let default = self.table.fallback_value(var, kind).cast(Interner);
|
||||
return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
|
||||
.assert_ty_ref(Interner)
|
||||
.clone();
|
||||
}
|
||||
if let Ok(known_ty) = self.table.infer_ctxt.probe_ty_var(vid) {
|
||||
let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner);
|
||||
// known_ty may contain other variables that are known by now
|
||||
self.var_stack.push((var, VarKind::Ty(kind)));
|
||||
let result = known_ty.fold_with(self, outer_binder);
|
||||
self.var_stack.pop();
|
||||
result
|
||||
} else {
|
||||
let default = self.table.fallback_value(var, kind).cast(Interner);
|
||||
(self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
|
||||
.assert_ty_ref(Interner)
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
TyVariableKind::Integer => {
|
||||
let vid = self.table.infer_ctxt.inner.borrow_mut().int_unification_table().find(IntVid::from(var.index()));
|
||||
let var = InferenceVar::from(vid.as_u32());
|
||||
if self.var_stack.contains(&(var, VarKind::Ty(kind))) {
|
||||
// recursive type
|
||||
let default = self.table.fallback_value(var, kind).cast(Interner);
|
||||
return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
|
||||
.assert_ty_ref(Interner)
|
||||
.clone();
|
||||
}
|
||||
if let Some(known_ty) = self.table.infer_ctxt.resolve_int_var(vid) {
|
||||
let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner);
|
||||
// known_ty may contain other variables that are known by now
|
||||
self.var_stack.push((var, VarKind::Ty(kind)));
|
||||
let result = known_ty.fold_with(self, outer_binder);
|
||||
self.var_stack.pop();
|
||||
result
|
||||
} else {
|
||||
let default = self.table.fallback_value(var, kind).cast(Interner);
|
||||
(self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
|
||||
.assert_ty_ref(Interner)
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
TyVariableKind::Float => {
|
||||
let vid = self.table.infer_ctxt.inner.borrow_mut().float_unification_table().find(FloatVid::from(var.index()));
|
||||
let var = InferenceVar::from(vid.as_u32());
|
||||
if self.var_stack.contains(&(var, VarKind::Ty(kind))) {
|
||||
// recursive type
|
||||
let default = self.table.fallback_value(var, kind).cast(Interner);
|
||||
return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
|
||||
.assert_ty_ref(Interner)
|
||||
.clone();
|
||||
}
|
||||
if let Some(known_ty) = self.table.infer_ctxt.resolve_float_var(vid) {
|
||||
let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner);
|
||||
// known_ty may contain other variables that are known by now
|
||||
self.var_stack.push((var, VarKind::Ty(kind)));
|
||||
let result = known_ty.fold_with(self, outer_binder);
|
||||
self.var_stack.pop();
|
||||
result
|
||||
} else {
|
||||
let default = self.table.fallback_value(var, kind).cast(Interner);
|
||||
(self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
|
||||
.assert_ty_ref(Interner)
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1237,25 +1290,27 @@ mod resolve {
|
||||
var: InferenceVar,
|
||||
outer_binder: DebruijnIndex,
|
||||
) -> Const {
|
||||
let var = self.table.var_unification_table.inference_var_root(var);
|
||||
let vid = self.table.infer_ctxt.root_const_var(rustc_type_ir::ConstVid::from_u32(var.index()));
|
||||
let var = InferenceVar::from(vid.as_u32());
|
||||
let default = ConstData {
|
||||
ty: ty.clone(),
|
||||
value: ConstValue::Concrete(ConcreteConst { interned: ConstScalar::Unknown }),
|
||||
}
|
||||
.intern(Interner)
|
||||
.cast(Interner);
|
||||
if self.var_stack.contains(&var) {
|
||||
if self.var_stack.contains(&(var, VarKind::Const)) {
|
||||
// recursive
|
||||
return (self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
|
||||
.assert_const_ref(Interner)
|
||||
.clone();
|
||||
}
|
||||
if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
|
||||
if let Ok(known_const) = self.table.infer_ctxt.probe_const_var(vid) {
|
||||
let known_const: Const = ChalkToNextSolver::from_nextsolver(known_const, self.table.interner);
|
||||
// known_ty may contain other variables that are known by now
|
||||
self.var_stack.push(var);
|
||||
let result = known_ty.fold_with(self, outer_binder);
|
||||
self.var_stack.push((var, VarKind::Const));
|
||||
let result = known_const.fold_with(self, outer_binder);
|
||||
self.var_stack.pop();
|
||||
result.assert_const_ref(Interner).clone()
|
||||
result
|
||||
} else {
|
||||
(self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
|
||||
.assert_const_ref(Interner)
|
||||
|
@ -957,23 +957,13 @@ pub fn callable_sig_from_fn_trait(
|
||||
)
|
||||
.build();
|
||||
|
||||
let block = trait_env.block;
|
||||
let trait_env = trait_env.env.clone();
|
||||
let obligation =
|
||||
InEnvironment { goal: trait_ref.clone().cast(Interner), environment: trait_env.clone() };
|
||||
let canonical = table.canonicalize(obligation.clone());
|
||||
if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() {
|
||||
table.register_obligation(obligation.goal);
|
||||
if !table.try_obligation(trait_ref.clone().cast(Interner)).no_solution() {
|
||||
table.register_obligation(trait_ref.clone().cast(Interner));
|
||||
let return_ty = table.normalize_projection_ty(projection);
|
||||
for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
|
||||
let fn_x_trait = fn_x.get_id(db, krate)?;
|
||||
trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
|
||||
let obligation: chalk_ir::InEnvironment<chalk_ir::Goal<Interner>> = InEnvironment {
|
||||
goal: trait_ref.clone().cast(Interner),
|
||||
environment: trait_env.clone(),
|
||||
};
|
||||
let canonical = table.canonicalize(obligation.clone());
|
||||
if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() {
|
||||
if !table.try_obligation(trait_ref.clone().cast(Interner)).no_solution() {
|
||||
let ret_ty = table.resolve_completely(return_ty);
|
||||
let args_ty = table.resolve_completely(args_ty);
|
||||
let params = args_ty
|
||||
|
@ -16,25 +16,13 @@ use hir_def::{
|
||||
use hir_expand::name::Name;
|
||||
use intern::sym;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustc_type_ir::inherent::{IntoKind, SliceLike};
|
||||
use rustc_type_ir::inherent::{IntoKind, SliceLike, Ty as _};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use stdx::never;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData,
|
||||
Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef,
|
||||
TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause,
|
||||
autoderef::{self, AutoderefKind},
|
||||
db::HirDatabase,
|
||||
error_lifetime, from_chalk_trait_id, from_foreign_def_id,
|
||||
infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable},
|
||||
lang_items::is_box,
|
||||
next_solver::SolverDefId,
|
||||
primitive::{FloatTy, IntTy, UintTy},
|
||||
to_chalk_trait_id,
|
||||
traits::NextTraitSolveResult,
|
||||
utils::all_super_traits,
|
||||
autoderef::{self, AutoderefKind}, db::HirDatabase, from_chalk_trait_id, from_foreign_def_id, infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast}, lang_items::is_box, next_solver::{mapping::ChalkToNextSolver, SolverDefId}, primitive::{FloatTy, IntTy, UintTy}, to_chalk_trait_id, traits::{next_trait_solve_canonical}, utils::all_super_traits, AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause
|
||||
};
|
||||
|
||||
/// This is used as a key for indexing impls.
|
||||
@ -533,9 +521,9 @@ pub fn def_crates(db: &dyn HirDatabase, ty: &Ty, cur_crate: Crate) -> Option<Sma
|
||||
}
|
||||
|
||||
/// Look up the method with the given name.
|
||||
pub(crate) fn lookup_method(
|
||||
db: &dyn HirDatabase,
|
||||
ty: &Canonical<Ty>,
|
||||
pub(crate) fn lookup_method<'db>(
|
||||
db: &'db dyn HirDatabase,
|
||||
ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>,
|
||||
env: Arc<TraitEnvironment>,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
@ -697,9 +685,9 @@ impl ReceiverAdjustments {
|
||||
// This would be nicer if it just returned an iterator, but that runs into
|
||||
// lifetime problems, because we need to borrow temp `CrateImplDefs`.
|
||||
// FIXME add a context type here?
|
||||
pub(crate) fn iterate_method_candidates<T>(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
pub(crate) fn iterate_method_candidates<'db, T>(
|
||||
ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>,
|
||||
db: &'db dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
@ -1046,9 +1034,9 @@ pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool {
|
||||
is_not_orphan
|
||||
}
|
||||
|
||||
pub fn iterate_path_candidates(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
pub fn iterate_path_candidates<'db>(
|
||||
ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>,
|
||||
db: &'db dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
@ -1068,9 +1056,9 @@ pub fn iterate_path_candidates(
|
||||
)
|
||||
}
|
||||
|
||||
pub fn iterate_method_candidates_dyn(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
pub fn iterate_method_candidates_dyn<'db>(
|
||||
ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>,
|
||||
db: &'db dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
@ -1108,7 +1096,7 @@ pub fn iterate_method_candidates_dyn(
|
||||
// types*.
|
||||
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let ty = table.instantiate_canonical(ty.clone());
|
||||
let ty = table.instantiate_canonical_ns(ty.clone());
|
||||
let deref_chain = autoderef_method_receiver(&mut table, ty);
|
||||
|
||||
deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| {
|
||||
@ -1139,20 +1127,22 @@ pub fn iterate_method_candidates_dyn(
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(name = ?name))]
|
||||
fn iterate_method_candidates_with_autoref(
|
||||
table: &mut InferenceTable<'_>,
|
||||
receiver_ty: Canonical<Ty>,
|
||||
fn iterate_method_candidates_with_autoref<'db>(
|
||||
table: &mut InferenceTable<'db>,
|
||||
receiver_ty: crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>,
|
||||
first_adjustment: ReceiverAdjustments,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: Option<&Name>,
|
||||
callback: &mut dyn MethodCandidateCallback,
|
||||
) -> ControlFlow<()> {
|
||||
if receiver_ty.value.is_general_var(Interner, &receiver_ty.binders) {
|
||||
if matches!(receiver_ty.value.kind(), rustc_type_ir::TyKind::Bound(..)) {
|
||||
// don't try to resolve methods on unknown types
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
let interner = table.interner;
|
||||
|
||||
let mut iterate_method_candidates_by_receiver = move |receiver_ty, first_adjustment| {
|
||||
iterate_method_candidates_by_receiver(
|
||||
table,
|
||||
@ -1166,7 +1156,11 @@ fn iterate_method_candidates_with_autoref(
|
||||
};
|
||||
|
||||
let mut maybe_reborrowed = first_adjustment.clone();
|
||||
if let Some((_, _, m)) = receiver_ty.value.as_reference() {
|
||||
if let rustc_type_ir::TyKind::Ref(_, _, m) = receiver_ty.value.kind() {
|
||||
let m = match m {
|
||||
rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut,
|
||||
rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not,
|
||||
};
|
||||
// Prefer reborrow of references to move
|
||||
maybe_reborrowed.autoref = Some(AutorefOrPtrAdjustment::Autoref(m));
|
||||
maybe_reborrowed.autoderefs += 1;
|
||||
@ -1174,10 +1168,10 @@ fn iterate_method_candidates_with_autoref(
|
||||
|
||||
iterate_method_candidates_by_receiver(receiver_ty.clone(), maybe_reborrowed)?;
|
||||
|
||||
let refed = Canonical {
|
||||
value: TyKind::Ref(Mutability::Not, error_lifetime(), receiver_ty.value.clone())
|
||||
.intern(Interner),
|
||||
binders: receiver_ty.binders.clone(),
|
||||
let refed = crate::next_solver::Canonical {
|
||||
max_universe: receiver_ty.max_universe,
|
||||
variables: receiver_ty.variables,
|
||||
value: crate::next_solver::Ty::new_ref(interner, crate::next_solver::Region::error(interner), receiver_ty.value, rustc_ast_ir::Mutability::Not),
|
||||
};
|
||||
|
||||
iterate_method_candidates_by_receiver(
|
||||
@ -1185,10 +1179,10 @@ fn iterate_method_candidates_with_autoref(
|
||||
first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Not)),
|
||||
)?;
|
||||
|
||||
let ref_muted = Canonical {
|
||||
value: TyKind::Ref(Mutability::Mut, error_lifetime(), receiver_ty.value.clone())
|
||||
.intern(Interner),
|
||||
binders: receiver_ty.binders.clone(),
|
||||
let ref_muted = crate::next_solver::Canonical {
|
||||
max_universe: receiver_ty.max_universe,
|
||||
variables: receiver_ty.variables,
|
||||
value: crate::next_solver::Ty::new_ref(interner, crate::next_solver::Region::error(interner), receiver_ty.value, rustc_ast_ir::Mutability::Mut),
|
||||
};
|
||||
|
||||
iterate_method_candidates_by_receiver(
|
||||
@ -1196,10 +1190,11 @@ fn iterate_method_candidates_with_autoref(
|
||||
first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Mut)),
|
||||
)?;
|
||||
|
||||
if let Some((ty, Mutability::Mut)) = receiver_ty.value.as_raw_ptr() {
|
||||
let const_ptr_ty = Canonical {
|
||||
value: TyKind::Raw(Mutability::Not, ty.clone()).intern(Interner),
|
||||
binders: receiver_ty.binders,
|
||||
if let rustc_type_ir::TyKind::RawPtr(ty, rustc_ast_ir::Mutability::Mut) = receiver_ty.value.kind() {
|
||||
let const_ptr_ty = rustc_type_ir::Canonical {
|
||||
max_universe: rustc_type_ir::UniverseIndex::ZERO,
|
||||
value: crate::next_solver::Ty::new_ptr(interner, ty, rustc_ast_ir::Mutability::Not),
|
||||
variables: receiver_ty.variables,
|
||||
};
|
||||
iterate_method_candidates_by_receiver(
|
||||
const_ptr_ty,
|
||||
@ -1250,16 +1245,17 @@ where
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(name = ?name))]
|
||||
fn iterate_method_candidates_by_receiver(
|
||||
table: &mut InferenceTable<'_>,
|
||||
receiver_ty: Canonical<Ty>,
|
||||
fn iterate_method_candidates_by_receiver<'db>(
|
||||
table: &mut InferenceTable<'db>,
|
||||
receiver_ty: crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>,
|
||||
receiver_adjustments: ReceiverAdjustments,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: Option<&Name>,
|
||||
callback: &mut dyn MethodCandidateCallback,
|
||||
) -> ControlFlow<()> {
|
||||
let receiver_ty = table.instantiate_canonical(receiver_ty);
|
||||
let receiver_ty = table.instantiate_canonical_ns(receiver_ty);
|
||||
let receiver_ty: crate::Ty = ChalkToNextSolver::from_nextsolver(receiver_ty, table.interner);
|
||||
// We're looking for methods with *receiver* type receiver_ty. These could
|
||||
// be found in any of the derefs of receiver_ty, so we have to go through
|
||||
// that, including raw derefs.
|
||||
@ -1307,9 +1303,9 @@ fn iterate_method_candidates_by_receiver(
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(name = ?name))]
|
||||
fn iterate_method_candidates_for_self_ty(
|
||||
self_ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
fn iterate_method_candidates_for_self_ty<'db>(
|
||||
self_ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>,
|
||||
db: &'db dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
@ -1317,7 +1313,7 @@ fn iterate_method_candidates_for_self_ty(
|
||||
callback: &mut dyn MethodCandidateCallback,
|
||||
) -> ControlFlow<()> {
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let self_ty = table.instantiate_canonical(self_ty.clone());
|
||||
let self_ty = ChalkToNextSolver::from_nextsolver(table.instantiate_canonical_ns(self_ty.clone()), table.interner);
|
||||
iterate_inherent_methods(
|
||||
&self_ty,
|
||||
&mut table,
|
||||
@ -1354,7 +1350,7 @@ fn iterate_trait_method_candidates(
|
||||
) -> ControlFlow<()> {
|
||||
let db = table.db;
|
||||
|
||||
let canonical_self_ty = table.canonicalize(self_ty.clone());
|
||||
let canonical_self_ty = ChalkToNextSolver::from_nextsolver(table.canonicalize(self_ty.clone().to_nextsolver(table.interner)), table.interner);
|
||||
let TraitEnvironment { krate, block, .. } = *table.trait_env;
|
||||
|
||||
'traits: for &t in traits_in_scope {
|
||||
@ -1583,13 +1579,14 @@ pub(crate) fn resolve_indexing_op(
|
||||
) -> Option<ReceiverAdjustments> {
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let ty = table.instantiate_canonical(ty);
|
||||
let deref_chain = autoderef_method_receiver(&mut table, ty);
|
||||
let interner = table.interner;
|
||||
let deref_chain = autoderef_method_receiver(&mut table, ty.to_nextsolver(interner));
|
||||
for (ty, adj) in deref_chain {
|
||||
let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ty);
|
||||
if !db
|
||||
.trait_solve(table.trait_env.krate, table.trait_env.block, goal.cast(Interner))
|
||||
.no_solution()
|
||||
{
|
||||
//let goal = generic_implements_goal_ns(db, &table.trait_env, index_trait, &ty);
|
||||
let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ChalkToNextSolver::from_nextsolver(ty, interner));
|
||||
let goal: chalk_ir::Canonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>> = goal.cast(Interner);
|
||||
let goal = goal.to_nextsolver(interner);
|
||||
if !next_trait_solve_canonical(db, table.trait_env.krate, table.trait_env.block, goal).no_solution() {
|
||||
return Some(adj);
|
||||
}
|
||||
}
|
||||
@ -1773,26 +1770,11 @@ fn is_valid_impl_fn_candidate(
|
||||
});
|
||||
|
||||
for goal in goals.clone() {
|
||||
let in_env = InEnvironment::new(&table.trait_env.env, goal);
|
||||
let canonicalized = table.canonicalize_with_free_vars(in_env);
|
||||
let solution = table.db.trait_solve(
|
||||
table.trait_env.krate,
|
||||
table.trait_env.block,
|
||||
canonicalized.value.clone(),
|
||||
);
|
||||
|
||||
match solution {
|
||||
NextTraitSolveResult::Certain(canonical_subst) => {
|
||||
canonicalized.apply_solution(
|
||||
table,
|
||||
Canonical {
|
||||
binders: canonical_subst.binders,
|
||||
value: canonical_subst.value.subst,
|
||||
},
|
||||
);
|
||||
match table.solve_obligation(goal) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
return IsValidCandidate::No;
|
||||
}
|
||||
NextTraitSolveResult::Uncertain(..) => {}
|
||||
NextTraitSolveResult::NoSolution => return IsValidCandidate::No,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1857,25 +1839,66 @@ fn generic_implements_goal(
|
||||
Canonical { binders, value }
|
||||
}
|
||||
|
||||
fn autoderef_method_receiver(
|
||||
table: &mut InferenceTable<'_>,
|
||||
ty: Ty,
|
||||
) -> Vec<(Canonical<Ty>, ReceiverAdjustments)> {
|
||||
let mut deref_chain: Vec<_> = Vec::new();
|
||||
let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false, true);
|
||||
/*
|
||||
/// This creates Substs for a trait with the given Self type and type variables
|
||||
/// for all other parameters, to query the trait solver with it.
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn generic_implements_goal_ns<'db>(
|
||||
db: &'db dyn HirDatabase,
|
||||
interner: DbInterner<'db>,
|
||||
env: &TraitEnvironment,
|
||||
trait_: TraitId,
|
||||
self_ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>,
|
||||
) -> crate::next_solver::Canonical<'db, crate::next_solver::Goal<'db, crate::next_solver::Predicate<'db>>> {
|
||||
let variables = self_ty.variables;
|
||||
let trait_ref = TyBuilder::trait_ref(db, trait_)
|
||||
.push(ChalkToNextSolver::from_nextsolver(self_ty.value, interner))
|
||||
.fill_with_bound_vars(DebruijnIndex::INNERMOST, variables.len())
|
||||
.build();
|
||||
|
||||
let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis());
|
||||
let args = infer_ctxt.fresh_args_for_item(SolverDefId::TraitId(trait_));
|
||||
|
||||
rustc_type_ir::TraitRef::new(interner, SolverDefId::TraitId(trait_)).with_self_ty(interner, self_ty.value);
|
||||
|
||||
|
||||
let kinds =
|
||||
binders.iter().cloned().chain(trait_ref.substitution.iter(Interner).skip(1).map(|it| {
|
||||
let vk = match it.data(Interner) {
|
||||
GenericArgData::Ty(_) => VariableKind::Ty(chalk_ir::TyVariableKind::General),
|
||||
GenericArgData::Lifetime(_) => VariableKind::Lifetime,
|
||||
GenericArgData::Const(c) => VariableKind::Const(c.data(Interner).ty.clone()),
|
||||
};
|
||||
WithKind::new(vk, UniverseIndex::ROOT)
|
||||
}));
|
||||
let binders = CanonicalVarKinds::from_iter(Interner, kinds);
|
||||
|
||||
let obligation = trait_ref.cast(Interner);
|
||||
let value = InEnvironment::new(&env.env, obligation);
|
||||
crate::next_solver::Canonical { max_universe, value, variables }
|
||||
}
|
||||
*/
|
||||
|
||||
fn autoderef_method_receiver<'db>(
|
||||
table: &mut InferenceTable<'db>,
|
||||
ty: crate::next_solver::Ty<'db>,
|
||||
) -> Vec<(crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, ReceiverAdjustments)> {
|
||||
let interner = table.interner;
|
||||
let mut deref_chain = Vec::new();
|
||||
let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ChalkToNextSolver::from_nextsolver(ty, interner), false, true);
|
||||
while let Some((ty, derefs)) = autoderef.next() {
|
||||
deref_chain.push((
|
||||
autoderef.table.canonicalize(ty),
|
||||
autoderef.table.canonicalize(ty.to_nextsolver(interner)),
|
||||
ReceiverAdjustments { autoref: None, autoderefs: derefs, unsize_array: false },
|
||||
));
|
||||
}
|
||||
// As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!)
|
||||
if let Some((TyKind::Array(parameters, _), binders, adj)) =
|
||||
deref_chain.last().map(|(ty, adj)| (ty.value.kind(Interner), ty.binders.clone(), adj))
|
||||
if let Some((rustc_type_ir::Array(parameters, _), variables, max_universe, adj)) =
|
||||
deref_chain.last().map(|d| (d.0.value.kind(), d.0.variables.clone(), d.0.max_universe, d.1.clone()))
|
||||
{
|
||||
let unsized_ty = TyKind::Slice(parameters.clone()).intern(Interner);
|
||||
let unsized_ty = crate::next_solver::Ty::new_slice(interner, parameters);
|
||||
deref_chain.push((
|
||||
Canonical { value: unsized_ty, binders },
|
||||
crate::next_solver::Canonical { max_universe, value: unsized_ty, variables, },
|
||||
ReceiverAdjustments { unsize_array: true, ..adj.clone() },
|
||||
));
|
||||
}
|
||||
|
785
crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs
Normal file
785
crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs
Normal file
@ -0,0 +1,785 @@
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustc_index::Idx;
|
||||
use rustc_type_ir::inherent::{Const as _, IntoKind as _, Region as _, SliceLike, Ty as _};
|
||||
use rustc_type_ir::InferTy::{self, FloatVar, IntVar, TyVar};
|
||||
use rustc_type_ir::{BoundVar, CanonicalQueryInput, CanonicalTyVarKind, DebruijnIndex, Flags, InferConst, RegionKind, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex};
|
||||
use smallvec::SmallVec;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::next_solver::infer::InferCtxt;
|
||||
use crate::next_solver::{Binder, BoundRegion, BoundRegionKind, BoundTy, Canonical, CanonicalVarKind, CanonicalVars, Const, ConstKind, DbInterner, GenericArg, ParamEnvAnd, Placeholder, Region, Ty, TyKind};
|
||||
|
||||
/// When we canonicalize a value to form a query, we wind up replacing
|
||||
/// various parts of it with canonical variables. This struct stores
|
||||
/// those replaced bits to remember for when we process the query
|
||||
/// result.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OriginalQueryValues<'db> {
|
||||
/// Map from the universes that appear in the query to the universes in the
|
||||
/// caller context. For all queries except `evaluate_goal` (used by Chalk),
|
||||
/// we only ever put ROOT values into the query, so this map is very
|
||||
/// simple.
|
||||
pub universe_map: SmallVec<[UniverseIndex; 4]>,
|
||||
|
||||
/// This is equivalent to `CanonicalVarValues`, but using a
|
||||
/// `SmallVec` yields a significant performance win.
|
||||
pub var_values: SmallVec<[GenericArg<'db>; 8]>,
|
||||
}
|
||||
|
||||
impl<'db> Default for OriginalQueryValues<'db> {
|
||||
fn default() -> Self {
|
||||
let mut universe_map = SmallVec::default();
|
||||
universe_map.push(UniverseIndex::ROOT);
|
||||
|
||||
Self { universe_map, var_values: SmallVec::default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> InferCtxt<'db> {
|
||||
/// Canonicalizes a query value `V`. When we canonicalize a query,
|
||||
/// we not only canonicalize unbound inference variables, but we
|
||||
/// *also* replace all free regions whatsoever. So for example a
|
||||
/// query like `T: Trait<'static>` would be canonicalized to
|
||||
///
|
||||
/// ```text
|
||||
/// T: Trait<'?0>
|
||||
/// ```
|
||||
///
|
||||
/// with a mapping M that maps `'?0` to `'static`.
|
||||
///
|
||||
/// To get a good understanding of what is happening here, check
|
||||
/// out the [chapter in the rustc dev guide][c].
|
||||
///
|
||||
/// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query
|
||||
pub fn canonicalize_query<V>(
|
||||
&self,
|
||||
value: ParamEnvAnd<'db, V>,
|
||||
query_state: &mut OriginalQueryValues<'db>,
|
||||
) -> CanonicalQueryInput<DbInterner<'db>, ParamEnvAnd<'db, V>>
|
||||
where
|
||||
V: TypeFoldable<DbInterner<'db>>,
|
||||
{
|
||||
let (param_env, value) = value.into_parts();
|
||||
// FIXME(#118965): We don't canonicalize the static lifetimes that appear in the
|
||||
// `param_env` because they are treated differently by trait selection.
|
||||
let canonical_param_env = Canonicalizer::canonicalize(
|
||||
param_env,
|
||||
None,
|
||||
self.interner,
|
||||
&CanonicalizeFreeRegionsOtherThanStatic,
|
||||
query_state,
|
||||
);
|
||||
|
||||
let canonical = Canonicalizer::canonicalize_with_base(
|
||||
canonical_param_env,
|
||||
value,
|
||||
Some(self),
|
||||
self.interner,
|
||||
&CanonicalizeAllFreeRegions,
|
||||
query_state,
|
||||
)
|
||||
.unchecked_map(|(param_env, value)| ParamEnvAnd { param_env, value });
|
||||
CanonicalQueryInput { canonical, typing_mode: self.typing_mode() }
|
||||
}
|
||||
|
||||
/// Canonicalizes a query *response* `V`. When we canonicalize a
|
||||
/// query response, we only canonicalize unbound inference
|
||||
/// variables, and we leave other free regions alone. So,
|
||||
/// continuing with the example from `canonicalize_query`, if
|
||||
/// there was an input query `T: Trait<'static>`, it would have
|
||||
/// been canonicalized to
|
||||
///
|
||||
/// ```text
|
||||
/// T: Trait<'?0>
|
||||
/// ```
|
||||
///
|
||||
/// with a mapping M that maps `'?0` to `'static`. But if we found that there
|
||||
/// exists only one possible impl of `Trait`, and it looks like
|
||||
/// ```ignore (illustrative)
|
||||
/// impl<T> Trait<'static> for T { .. }
|
||||
/// ```
|
||||
/// then we would prepare a query result R that (among other
|
||||
/// things) includes a mapping to `'?0 := 'static`. When
|
||||
/// canonicalizing this query result R, we would leave this
|
||||
/// reference to `'static` alone.
|
||||
///
|
||||
/// To get a good understanding of what is happening here, check
|
||||
/// out the [chapter in the rustc dev guide][c].
|
||||
///
|
||||
/// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query-result
|
||||
pub fn canonicalize_response<V>(&self, value: V) -> Canonical<'db, V>
|
||||
where
|
||||
V: TypeFoldable<DbInterner<'db>>,
|
||||
{
|
||||
let mut query_state = OriginalQueryValues::default();
|
||||
Canonicalizer::canonicalize(
|
||||
value,
|
||||
Some(self),
|
||||
self.interner,
|
||||
&CanonicalizeQueryResponse,
|
||||
&mut query_state,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn canonicalize_user_type_annotation<V>(&self, value: V) -> Canonical<'db, V>
|
||||
where
|
||||
V: TypeFoldable<DbInterner<'db>>,
|
||||
{
|
||||
let mut query_state = OriginalQueryValues::default();
|
||||
Canonicalizer::canonicalize(
|
||||
value,
|
||||
Some(self),
|
||||
self.interner,
|
||||
&CanonicalizeUserTypeAnnotation,
|
||||
&mut query_state,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Controls how we canonicalize "free regions" that are not inference
|
||||
/// variables. This depends on what we are canonicalizing *for* --
|
||||
/// e.g., if we are canonicalizing to create a query, we want to
|
||||
/// replace those with inference variables, since we want to make a
|
||||
/// maximally general query. But if we are canonicalizing a *query
|
||||
/// response*, then we don't typically replace free regions, as they
|
||||
/// must have been introduced from other parts of the system.
|
||||
trait CanonicalizeMode {
|
||||
fn canonicalize_free_region<'db>(
|
||||
&self,
|
||||
canonicalizer: &mut Canonicalizer<'_, 'db>,
|
||||
r: Region<'db>,
|
||||
) -> Region<'db>;
|
||||
|
||||
fn any(&self) -> bool;
|
||||
|
||||
// Do we preserve universe of variables.
|
||||
fn preserve_universes(&self) -> bool;
|
||||
}
|
||||
|
||||
struct CanonicalizeQueryResponse;
|
||||
|
||||
impl CanonicalizeMode for CanonicalizeQueryResponse {
|
||||
fn canonicalize_free_region<'db>(
|
||||
&self,
|
||||
canonicalizer: &mut Canonicalizer<'_, 'db>,
|
||||
mut r: Region<'db>,
|
||||
) -> Region<'db> {
|
||||
let infcx = canonicalizer.infcx.unwrap();
|
||||
|
||||
if let RegionKind::ReVar(vid) = r.kind() {
|
||||
r = infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.unwrap_region_constraints()
|
||||
.opportunistic_resolve_var(canonicalizer.tcx, vid);
|
||||
debug!(
|
||||
"canonical: region var found with vid {vid:?}, \
|
||||
opportunistically resolved to {r:?}",
|
||||
);
|
||||
};
|
||||
|
||||
match r.kind() {
|
||||
RegionKind::ReLateParam(_) | RegionKind::ReErased | RegionKind::ReStatic | RegionKind::ReEarlyParam(..) | RegionKind::ReError(..) => r,
|
||||
|
||||
RegionKind::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region(
|
||||
CanonicalVarKind::PlaceholderRegion(placeholder),
|
||||
r,
|
||||
),
|
||||
|
||||
RegionKind::ReVar(vid) => {
|
||||
let universe = infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.unwrap_region_constraints()
|
||||
.probe_value(vid)
|
||||
.unwrap_err();
|
||||
canonicalizer.canonical_var_for_region(
|
||||
CanonicalVarKind::Region(universe),
|
||||
r,
|
||||
)
|
||||
}
|
||||
|
||||
_ => {
|
||||
// Other than `'static` or `'empty`, the query
|
||||
// response should be executing in a fully
|
||||
// canonicalized environment, so there shouldn't be
|
||||
// any other region names it can come up.
|
||||
//
|
||||
// rust-lang/rust#57464: `impl Trait` can leak local
|
||||
// scopes (in manner violating typeck). Therefore, use
|
||||
// `delayed_bug` to allow type error over an ICE.
|
||||
panic!("unexpected region in query response: `{r:?}`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn any(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn preserve_universes(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
struct CanonicalizeUserTypeAnnotation;
|
||||
|
||||
impl CanonicalizeMode for CanonicalizeUserTypeAnnotation {
|
||||
fn canonicalize_free_region<'db>(
|
||||
&self,
|
||||
canonicalizer: &mut Canonicalizer<'_, 'db>,
|
||||
r: Region<'db>,
|
||||
) -> Region<'db> {
|
||||
match r.kind() {
|
||||
RegionKind::ReEarlyParam(_)
|
||||
| RegionKind::ReLateParam(_)
|
||||
| RegionKind::ReErased
|
||||
| RegionKind::ReStatic
|
||||
| RegionKind::ReError(_) => r,
|
||||
RegionKind::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r),
|
||||
RegionKind::RePlaceholder(..) | RegionKind::ReBound(..) => {
|
||||
// We only expect region names that the user can type.
|
||||
panic!("unexpected region in query response: `{:?}`", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn any(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn preserve_universes(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
struct CanonicalizeAllFreeRegions;
|
||||
|
||||
impl CanonicalizeMode for CanonicalizeAllFreeRegions {
|
||||
fn canonicalize_free_region<'db>(
|
||||
&self,
|
||||
canonicalizer: &mut Canonicalizer<'_, 'db>,
|
||||
r: Region<'db>,
|
||||
) -> Region<'db> {
|
||||
canonicalizer.canonical_var_for_region_in_root_universe(r)
|
||||
}
|
||||
|
||||
fn any(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn preserve_universes(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
struct CanonicalizeFreeRegionsOtherThanStatic;
|
||||
|
||||
impl CanonicalizeMode for CanonicalizeFreeRegionsOtherThanStatic {
|
||||
fn canonicalize_free_region<'db>(
|
||||
&self,
|
||||
canonicalizer: &mut Canonicalizer<'_, 'db>,
|
||||
r: Region<'db>,
|
||||
) -> Region<'db> {
|
||||
if r.is_static() { r } else { canonicalizer.canonical_var_for_region_in_root_universe(r) }
|
||||
}
|
||||
|
||||
fn any(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn preserve_universes(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
struct Canonicalizer<'cx, 'db> {
|
||||
/// Set to `None` to disable the resolution of inference variables.
|
||||
infcx: Option<&'cx InferCtxt<'db>>,
|
||||
tcx: DbInterner<'db>,
|
||||
variables: SmallVec<[CanonicalVarKind<'db>; 8]>,
|
||||
query_state: &'cx mut OriginalQueryValues<'db>,
|
||||
// Note that indices is only used once `var_values` is big enough to be
|
||||
// heap-allocated.
|
||||
indices: FxHashMap<GenericArg<'db>, BoundVar>,
|
||||
canonicalize_mode: &'cx dyn CanonicalizeMode,
|
||||
needs_canonical_flags: TypeFlags,
|
||||
|
||||
binder_index: DebruijnIndex,
|
||||
}
|
||||
|
||||
impl<'cx, 'db> TypeFolder<DbInterner<'db>> for Canonicalizer<'cx, 'db> {
|
||||
fn cx(&self) -> DbInterner<'db> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn fold_binder<T>(&mut self, t: Binder<'db, T>) -> Binder<'db, T>
|
||||
where
|
||||
T: TypeFoldable<DbInterner<'db>>,
|
||||
{
|
||||
self.binder_index.shift_in(1);
|
||||
let t = t.super_fold_with(self);
|
||||
self.binder_index.shift_out(1);
|
||||
t
|
||||
}
|
||||
|
||||
fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
|
||||
match r.kind() {
|
||||
RegionKind::ReBound(index, ..) => {
|
||||
if index >= self.binder_index {
|
||||
panic!("escaping late-bound region during canonicalization");
|
||||
} else {
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
RegionKind::ReStatic
|
||||
| RegionKind::ReEarlyParam(..)
|
||||
| RegionKind::ReError(_)
|
||||
| RegionKind::ReLateParam(_)
|
||||
| RegionKind::RePlaceholder(..)
|
||||
| RegionKind::ReVar(_)
|
||||
| RegionKind::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, mut t: Ty<'db>) -> Ty<'db> {
|
||||
match t.kind() {
|
||||
TyKind::Infer(TyVar(mut vid)) => {
|
||||
// We need to canonicalize the *root* of our ty var.
|
||||
// This is so that our canonical response correctly reflects
|
||||
// any equated inference vars correctly!
|
||||
let root_vid = self.infcx.unwrap().root_var(vid);
|
||||
if root_vid != vid {
|
||||
t = Ty::new_var(self.tcx, root_vid);
|
||||
vid = root_vid;
|
||||
}
|
||||
|
||||
debug!("canonical: type var found with vid {:?}", vid);
|
||||
match self.infcx.unwrap().probe_ty_var(vid) {
|
||||
// `t` could be a float / int variable; canonicalize that instead.
|
||||
Ok(t) => {
|
||||
debug!("(resolved to {:?})", t);
|
||||
self.fold_ty(t)
|
||||
}
|
||||
|
||||
// `TyVar(vid)` is unresolved, track its universe index in the canonicalized
|
||||
// result.
|
||||
Err(mut ui) => {
|
||||
if !self.canonicalize_mode.preserve_universes() {
|
||||
// FIXME: perf problem described in #55921.
|
||||
ui = UniverseIndex::ROOT;
|
||||
}
|
||||
self.canonicalize_ty_var(
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
|
||||
t,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TyKind::Infer(IntVar(vid)) => {
|
||||
let nt = self.infcx.unwrap().opportunistic_resolve_int_var(vid);
|
||||
if nt != t {
|
||||
return self.fold_ty(nt);
|
||||
} else {
|
||||
self.canonicalize_ty_var(
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::Int),
|
||||
t,
|
||||
)
|
||||
}
|
||||
}
|
||||
TyKind::Infer(FloatVar(vid)) => {
|
||||
let nt = self.infcx.unwrap().opportunistic_resolve_float_var(vid);
|
||||
if nt != t {
|
||||
return self.fold_ty(nt);
|
||||
} else {
|
||||
self.canonicalize_ty_var(
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::Float),
|
||||
t,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
TyKind::Infer(InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_)) => {
|
||||
panic!("encountered a fresh type during canonicalization")
|
||||
}
|
||||
|
||||
TyKind::Placeholder(mut placeholder) => {
|
||||
if !self.canonicalize_mode.preserve_universes() {
|
||||
placeholder.universe = UniverseIndex::ROOT;
|
||||
}
|
||||
self.canonicalize_ty_var(
|
||||
CanonicalVarKind::PlaceholderTy(placeholder),
|
||||
t,
|
||||
)
|
||||
}
|
||||
|
||||
TyKind::Bound(debruijn, _) => {
|
||||
if debruijn >= self.binder_index {
|
||||
panic!("escaping bound type during canonicalization")
|
||||
} else {
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
TyKind::Closure(..)
|
||||
| TyKind::CoroutineClosure(..)
|
||||
| TyKind::Coroutine(..)
|
||||
| TyKind::CoroutineWitness(..)
|
||||
| TyKind::Bool
|
||||
| TyKind::Char
|
||||
| TyKind::Int(..)
|
||||
| TyKind::Uint(..)
|
||||
| TyKind::Float(..)
|
||||
| TyKind::Adt(..)
|
||||
| TyKind::Str
|
||||
| TyKind::Error(_)
|
||||
| TyKind::Array(..)
|
||||
| TyKind::Slice(..)
|
||||
| TyKind::RawPtr(..)
|
||||
| TyKind::Ref(..)
|
||||
| TyKind::FnDef(..)
|
||||
| TyKind::FnPtr(..)
|
||||
| TyKind::Dynamic(..)
|
||||
| TyKind::UnsafeBinder(_)
|
||||
| TyKind::Never
|
||||
| TyKind::Tuple(..)
|
||||
| TyKind::Alias(..)
|
||||
| TyKind::Foreign(..)
|
||||
| TyKind::Pat(..)
|
||||
| TyKind::Param(..) => {
|
||||
if t.flags().intersects(self.needs_canonical_flags) {
|
||||
t.super_fold_with(self)
|
||||
} else {
|
||||
t
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, mut ct: Const<'db>) -> Const<'db> {
|
||||
match ct.kind() {
|
||||
ConstKind::Infer(InferConst::Var(mut vid)) => {
|
||||
// We need to canonicalize the *root* of our const var.
|
||||
// This is so that our canonical response correctly reflects
|
||||
// any equated inference vars correctly!
|
||||
let root_vid = self.infcx.unwrap().root_const_var(vid);
|
||||
if root_vid != vid {
|
||||
ct = Const::new_var(self.tcx, root_vid);
|
||||
vid = root_vid;
|
||||
}
|
||||
|
||||
debug!("canonical: const var found with vid {:?}", vid);
|
||||
match self.infcx.unwrap().probe_const_var(vid) {
|
||||
Ok(c) => {
|
||||
debug!("(resolved to {:?})", c);
|
||||
return self.fold_const(c);
|
||||
}
|
||||
|
||||
// `ConstVar(vid)` is unresolved, track its universe index in the
|
||||
// canonicalized result
|
||||
Err(mut ui) => {
|
||||
if !self.canonicalize_mode.preserve_universes() {
|
||||
// FIXME: perf problem described in #55921.
|
||||
ui = UniverseIndex::ROOT;
|
||||
}
|
||||
return self.canonicalize_const_var(
|
||||
CanonicalVarKind::Const(ui),
|
||||
ct,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
ConstKind::Infer(InferConst::Fresh(_)) => {
|
||||
panic!("encountered a fresh const during canonicalization")
|
||||
}
|
||||
ConstKind::Bound(debruijn, _) => {
|
||||
if debruijn >= self.binder_index {
|
||||
panic!("escaping bound const during canonicalization")
|
||||
} else {
|
||||
return ct;
|
||||
}
|
||||
}
|
||||
ConstKind::Placeholder(placeholder) => {
|
||||
return self.canonicalize_const_var(
|
||||
CanonicalVarKind::PlaceholderConst(placeholder),
|
||||
ct,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if ct.flags().intersects(self.needs_canonical_flags) {
|
||||
ct.super_fold_with(self)
|
||||
} else {
|
||||
ct
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'db> Canonicalizer<'cx, 'db> {
|
||||
/// The main `canonicalize` method, shared impl of
|
||||
/// `canonicalize_query` and `canonicalize_response`.
|
||||
fn canonicalize<V>(
|
||||
value: V,
|
||||
infcx: Option<&InferCtxt<'db>>,
|
||||
tcx: DbInterner<'db>,
|
||||
canonicalize_region_mode: &dyn CanonicalizeMode,
|
||||
query_state: &mut OriginalQueryValues<'db>,
|
||||
) -> Canonical<'db, V>
|
||||
where
|
||||
V: TypeFoldable<DbInterner<'db>>,
|
||||
{
|
||||
let base = Canonical {
|
||||
max_universe: UniverseIndex::ROOT,
|
||||
variables: CanonicalVars::new_from_iter(tcx, []),
|
||||
value: (),
|
||||
};
|
||||
Canonicalizer::canonicalize_with_base(
|
||||
base,
|
||||
value,
|
||||
infcx,
|
||||
tcx,
|
||||
canonicalize_region_mode,
|
||||
query_state,
|
||||
)
|
||||
.unchecked_map(|((), val)| val)
|
||||
}
|
||||
|
||||
fn canonicalize_with_base<U, V>(
|
||||
base: Canonical<'db, U>,
|
||||
value: V,
|
||||
infcx: Option<&InferCtxt<'db>>,
|
||||
tcx: DbInterner<'db>,
|
||||
canonicalize_region_mode: &dyn CanonicalizeMode,
|
||||
query_state: &mut OriginalQueryValues<'db>,
|
||||
) -> Canonical<'db, (U, V)>
|
||||
where
|
||||
V: TypeFoldable<DbInterner<'db>>,
|
||||
{
|
||||
let needs_canonical_flags = if canonicalize_region_mode.any() {
|
||||
TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_FREE_REGIONS
|
||||
} else {
|
||||
TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER
|
||||
};
|
||||
|
||||
// Fast path: nothing that needs to be canonicalized.
|
||||
if !value.has_type_flags(needs_canonical_flags) {
|
||||
return base.unchecked_map(|b| (b, value));
|
||||
}
|
||||
|
||||
let mut canonicalizer = Canonicalizer {
|
||||
infcx,
|
||||
tcx,
|
||||
canonicalize_mode: canonicalize_region_mode,
|
||||
needs_canonical_flags,
|
||||
variables: SmallVec::from_slice(base.variables.as_slice()),
|
||||
query_state,
|
||||
indices: FxHashMap::default(),
|
||||
binder_index: DebruijnIndex::ZERO,
|
||||
};
|
||||
if canonicalizer.query_state.var_values.spilled() {
|
||||
canonicalizer.indices = canonicalizer
|
||||
.query_state
|
||||
.var_values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, &kind)| (kind, BoundVar::from(i)))
|
||||
.collect();
|
||||
}
|
||||
let out_value = value.fold_with(&mut canonicalizer);
|
||||
|
||||
// Once we have canonicalized `out_value`, it should not
|
||||
// contain anything that ties it to this inference context
|
||||
// anymore.
|
||||
debug_assert!(!out_value.has_infer() && !out_value.has_placeholders());
|
||||
|
||||
let canonical_variables = CanonicalVars::new_from_iter(tcx, canonicalizer.universe_canonicalized_variables());
|
||||
|
||||
let max_universe = canonical_variables
|
||||
.iter()
|
||||
.map(|cvar| cvar.universe())
|
||||
.max()
|
||||
.unwrap_or(UniverseIndex::ROOT);
|
||||
|
||||
Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) }
|
||||
}
|
||||
|
||||
/// Creates a canonical variable replacing `kind` from the input,
|
||||
/// or returns an existing variable if `kind` has already been
|
||||
/// seen. `kind` is expected to be an unbound variable (or
|
||||
/// potentially a free region).
|
||||
fn canonical_var(&mut self, info: CanonicalVarKind<'db>, kind: GenericArg<'db>) -> BoundVar {
|
||||
let Canonicalizer { variables, query_state, indices, .. } = self;
|
||||
|
||||
let var_values = &mut query_state.var_values;
|
||||
|
||||
let universe = info.universe();
|
||||
if universe != UniverseIndex::ROOT {
|
||||
assert!(self.canonicalize_mode.preserve_universes());
|
||||
|
||||
// Insert universe into the universe map. To preserve the order of the
|
||||
// universes in the value being canonicalized, we don't update the
|
||||
// universe in `info` until we have finished canonicalizing.
|
||||
match query_state.universe_map.binary_search(&universe) {
|
||||
Err(idx) => query_state.universe_map.insert(idx, universe),
|
||||
Ok(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
// This code is hot. `variables` and `var_values` are usually small
|
||||
// (fewer than 8 elements ~95% of the time). They are SmallVec's to
|
||||
// avoid allocations in those cases. We also don't use `indices` to
|
||||
// determine if a kind has been seen before until the limit of 8 has
|
||||
// been exceeded, to also avoid allocations for `indices`.
|
||||
if !var_values.spilled() {
|
||||
// `var_values` is stack-allocated. `indices` isn't used yet. Do a
|
||||
// direct linear search of `var_values`.
|
||||
if let Some(idx) = var_values.iter().position(|&k| k == kind) {
|
||||
// `kind` is already present in `var_values`.
|
||||
BoundVar::new(idx)
|
||||
} else {
|
||||
// `kind` isn't present in `var_values`. Append it. Likewise
|
||||
// for `info` and `variables`.
|
||||
variables.push(info);
|
||||
var_values.push(kind);
|
||||
assert_eq!(variables.len(), var_values.len());
|
||||
|
||||
// If `var_values` has become big enough to be heap-allocated,
|
||||
// fill up `indices` to facilitate subsequent lookups.
|
||||
if var_values.spilled() {
|
||||
assert!(indices.is_empty());
|
||||
*indices = var_values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, &kind)| (kind, BoundVar::new(i)))
|
||||
.collect();
|
||||
}
|
||||
// The cv is the index of the appended element.
|
||||
BoundVar::new(var_values.len() - 1)
|
||||
}
|
||||
} else {
|
||||
// `var_values` is large. Do a hashmap search via `indices`.
|
||||
*indices.entry(kind).or_insert_with(|| {
|
||||
variables.push(info);
|
||||
var_values.push(kind);
|
||||
assert_eq!(variables.len(), var_values.len());
|
||||
BoundVar::new(variables.len() - 1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the universe indexes used in `var_values` with their index in
|
||||
/// `query_state.universe_map`. This minimizes the maximum universe used in
|
||||
/// the canonicalized value.
|
||||
fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarKind<'db>; 8]> {
|
||||
if self.query_state.universe_map.len() == 1 {
|
||||
return self.variables;
|
||||
}
|
||||
|
||||
let reverse_universe_map: FxHashMap<UniverseIndex, UniverseIndex> = self
|
||||
.query_state
|
||||
.universe_map
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, universe)| (*universe, UniverseIndex::from_usize(idx)))
|
||||
.collect();
|
||||
|
||||
self.variables
|
||||
.iter()
|
||||
.map(|v| match *v {
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
|
||||
return *v;
|
||||
}
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => {
|
||||
CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u]))
|
||||
}
|
||||
CanonicalVarKind::Region(u) => {
|
||||
CanonicalVarKind::Region(reverse_universe_map[&u])
|
||||
}
|
||||
CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]),
|
||||
CanonicalVarKind::PlaceholderTy(placeholder) => {
|
||||
CanonicalVarKind::PlaceholderTy(Placeholder {
|
||||
universe: reverse_universe_map[&placeholder.universe],
|
||||
..placeholder
|
||||
})
|
||||
}
|
||||
CanonicalVarKind::PlaceholderRegion(placeholder) => {
|
||||
CanonicalVarKind::PlaceholderRegion(Placeholder {
|
||||
universe: reverse_universe_map[&placeholder.universe],
|
||||
..placeholder
|
||||
})
|
||||
}
|
||||
CanonicalVarKind::PlaceholderConst(placeholder) => {
|
||||
CanonicalVarKind::PlaceholderConst(Placeholder {
|
||||
universe: reverse_universe_map[&placeholder.universe],
|
||||
..placeholder
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Shorthand helper that creates a canonical region variable for
|
||||
/// `r` (always in the root universe). The reason that we always
|
||||
/// put these variables into the root universe is because this
|
||||
/// method is used during **query construction:** in that case, we
|
||||
/// are taking all the regions and just putting them into the most
|
||||
/// generic context we can. This may generate solutions that don't
|
||||
/// fit (e.g., that equate some region variable with a placeholder
|
||||
/// it can't name) on the caller side, but that's ok, the caller
|
||||
/// can figure that out. In the meantime, it maximizes our
|
||||
/// caching.
|
||||
///
|
||||
/// (This works because unification never fails -- and hence trait
|
||||
/// selection is never affected -- due to a universe mismatch.)
|
||||
fn canonical_var_for_region_in_root_universe(
|
||||
&mut self,
|
||||
r: Region<'db>,
|
||||
) -> Region<'db> {
|
||||
self.canonical_var_for_region(
|
||||
CanonicalVarKind::Region(UniverseIndex::ROOT),
|
||||
r,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a canonical variable (with the given `info`)
|
||||
/// representing the region `r`; return a region referencing it.
|
||||
fn canonical_var_for_region(
|
||||
&mut self,
|
||||
info: CanonicalVarKind<'db>,
|
||||
r: Region<'db>,
|
||||
) -> Region<'db> {
|
||||
let var = self.canonical_var(info, r.into());
|
||||
let br = BoundRegion { var, kind: BoundRegionKind::Anon };
|
||||
Region::new_bound(self.cx(), self.binder_index, br)
|
||||
}
|
||||
|
||||
/// Given a type variable `ty_var` of the given kind, first check
|
||||
/// if `ty_var` is bound to anything; if so, canonicalize
|
||||
/// *that*. Otherwise, create a new canonical variable for
|
||||
/// `ty_var`.
|
||||
fn canonicalize_ty_var(&mut self, info: CanonicalVarKind<'db>, ty_var: Ty<'db>) -> Ty<'db> {
|
||||
debug_assert!(!self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var)));
|
||||
let var = self.canonical_var(info, ty_var.into());
|
||||
Ty::new_bound(self.tcx, self.binder_index, BoundTy { kind: crate::next_solver::BoundTyKind::Anon, var })
|
||||
}
|
||||
|
||||
/// Given a type variable `const_var` of the given kind, first check
|
||||
/// if `const_var` is bound to anything; if so, canonicalize
|
||||
/// *that*. Otherwise, create a new canonical variable for
|
||||
/// `const_var`.
|
||||
fn canonicalize_const_var(
|
||||
&mut self,
|
||||
info: CanonicalVarKind<'db>,
|
||||
const_var: Const<'db>,
|
||||
) -> Const<'db> {
|
||||
debug_assert!(
|
||||
!self.infcx.is_some_and(|infcx| const_var != infcx.shallow_resolve_const(const_var))
|
||||
);
|
||||
let var = self.canonical_var(info, const_var.into());
|
||||
Const::new_bound(self.tcx, self.binder_index, var)
|
||||
}
|
||||
}
|
@ -42,6 +42,7 @@ use rustc_type_ir::{
|
||||
},
|
||||
};
|
||||
|
||||
pub mod canonicalizer;
|
||||
pub mod instantiate;
|
||||
|
||||
impl<'db> InferCtxt<'db> {
|
||||
|
@ -190,12 +190,13 @@ impl<'db> InferCtxtInner<'db> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn int_unification_table(&mut self) -> UnificationTable<'_, 'db, IntVid> {
|
||||
pub(crate) fn int_unification_table(&mut self) -> UnificationTable<'_, 'db, IntVid> {
|
||||
tracing::debug!(?self.int_unification_storage);
|
||||
self.int_unification_storage.with_log(&mut self.undo_log)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn float_unification_table(&mut self) -> UnificationTable<'_, 'db, FloatVid> {
|
||||
pub(crate) fn float_unification_table(&mut self) -> UnificationTable<'_, 'db, FloatVid> {
|
||||
self.float_unification_storage.with_log(&mut self.undo_log)
|
||||
}
|
||||
|
||||
@ -213,6 +214,7 @@ impl<'db> InferCtxtInner<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InferCtxt<'db> {
|
||||
pub interner: DbInterner<'db>,
|
||||
|
||||
@ -500,6 +502,10 @@ impl<'db> InferCtxt<'db> {
|
||||
self.next_ty_var_with_origin(TypeVariableOrigin { param_def_id: None })
|
||||
}
|
||||
|
||||
pub fn next_ty_vid(&self) -> TyVid {
|
||||
self.inner.borrow_mut().type_variables().new_var(self.universe(), TypeVariableOrigin { param_def_id: None })
|
||||
}
|
||||
|
||||
pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'db> {
|
||||
let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin);
|
||||
Ty::new_var(self.interner, vid)
|
||||
@ -519,6 +525,15 @@ impl<'db> InferCtxt<'db> {
|
||||
self.next_const_var_with_origin(ConstVariableOrigin { param_def_id: None })
|
||||
}
|
||||
|
||||
pub fn next_const_vid(&self) -> ConstVid {
|
||||
self
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.const_unification_table()
|
||||
.new_key(ConstVariableValue::Unknown { origin: ConstVariableOrigin { param_def_id: None }, universe: self.universe() })
|
||||
.vid
|
||||
}
|
||||
|
||||
pub fn next_const_var_with_origin(&self, origin: ConstVariableOrigin) -> Const<'db> {
|
||||
let vid = self
|
||||
.inner
|
||||
@ -546,12 +561,20 @@ impl<'db> InferCtxt<'db> {
|
||||
Ty::new_int_var(self.interner, next_int_var_id)
|
||||
}
|
||||
|
||||
pub fn next_int_vid(&self) -> IntVid {
|
||||
self.inner.borrow_mut().int_unification_table().new_key(IntVarValue::Unknown)
|
||||
}
|
||||
|
||||
pub fn next_float_var(&self) -> Ty<'db> {
|
||||
let next_float_var_id =
|
||||
self.inner.borrow_mut().float_unification_table().new_key(FloatVarValue::Unknown);
|
||||
Ty::new_float_var(self.interner, next_float_var_id)
|
||||
}
|
||||
|
||||
pub fn next_float_vid(&self) -> FloatVid {
|
||||
self.inner.borrow_mut().float_unification_table().new_key(FloatVarValue::Unknown)
|
||||
}
|
||||
|
||||
/// Creates a fresh region variable with the next available index.
|
||||
/// The variable will be created in the maximum universe created
|
||||
/// thus far, allowing it to name any region created thus far.
|
||||
@ -559,6 +582,10 @@ impl<'db> InferCtxt<'db> {
|
||||
self.next_region_var_in_universe(self.universe())
|
||||
}
|
||||
|
||||
pub fn next_region_vid(&self) -> RegionVid {
|
||||
self.inner.borrow_mut().unwrap_region_constraints().new_region_var(self.universe())
|
||||
}
|
||||
|
||||
/// Creates a fresh region variable with the next available index
|
||||
/// in the given universe; typically, you can use
|
||||
/// `next_region_var` and just use the maximal universe.
|
||||
@ -782,6 +809,18 @@ impl<'db> InferCtxt<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_int_var(&self, vid: IntVid) -> Option<Ty<'db>> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let value = inner.int_unification_table().probe_value(vid);
|
||||
match value {
|
||||
IntVarValue::IntType(ty) => Some(Ty::new_int(self.interner, ty)),
|
||||
IntVarValue::UintType(ty) => Some(Ty::new_uint(self.interner, ty)),
|
||||
IntVarValue::Unknown => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves a float var to a rigid int type, if it was constrained to one,
|
||||
/// or else the root float var in the unification table.
|
||||
pub fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> Ty<'db> {
|
||||
@ -795,6 +834,17 @@ impl<'db> InferCtxt<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_float_var(&self, vid: FloatVid) -> Option<Ty<'db>> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let value = inner.float_unification_table().probe_value(vid);
|
||||
match value {
|
||||
FloatVarValue::Known(ty) => Some(Ty::new_float(self.interner, ty)),
|
||||
FloatVarValue::Unknown => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Where possible, replaces type/const variables in
|
||||
/// `value` with their final value. Note that region variables
|
||||
/// are unaffected. If a type/const variable has not been unified, it
|
||||
|
@ -46,7 +46,7 @@ impl<'db> InferCtxt<'db> {
|
||||
UndoLogs::<UndoLog<'db>>::num_open_snapshots(&self.inner.borrow_mut().undo_log)
|
||||
}
|
||||
|
||||
fn start_snapshot(&self) -> CombinedSnapshot {
|
||||
pub(crate) fn start_snapshot(&self) -> CombinedSnapshot {
|
||||
debug!("start_snapshot()");
|
||||
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
@ -59,7 +59,7 @@ impl<'db> InferCtxt<'db> {
|
||||
}
|
||||
|
||||
#[instrument(skip(self, snapshot), level = "debug")]
|
||||
fn rollback_to(&self, snapshot: CombinedSnapshot) {
|
||||
pub(crate) fn rollback_to(&self, snapshot: CombinedSnapshot) {
|
||||
let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot;
|
||||
|
||||
self.universe.set(universe);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ use crate::{
|
||||
db::HirDatabase,
|
||||
next_solver::{
|
||||
ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate,
|
||||
mapping::{ChalkToNextSolver, convert_args_for_result},
|
||||
mapping::ChalkToNextSolver,
|
||||
util::sizedness_fast_path,
|
||||
},
|
||||
};
|
||||
@ -200,7 +200,7 @@ impl<'db> SolverDelegate for SolverContext<'db> {
|
||||
SolverDefId::StaticId(c) => GeneralConstId::StaticId(c),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let subst = convert_args_for_result(self.interner, uv.args.as_slice());
|
||||
let subst = ChalkToNextSolver::from_nextsolver(uv.args, self.interner);
|
||||
let ec = self.cx().db.const_eval(c, subst, None).ok()?;
|
||||
Some(ec.to_nextsolver(self.interner))
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Trait solving using Chalk.
|
||||
|
||||
use core::fmt;
|
||||
use std::hash::Hash;
|
||||
|
||||
use chalk_ir::{DebruijnIndex, GoalData, fold::TypeFoldable};
|
||||
use chalk_solve::rust_ir;
|
||||
@ -20,17 +21,9 @@ use stdx::never;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy,
|
||||
ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause,
|
||||
db::HirDatabase,
|
||||
infer::unify::InferenceTable,
|
||||
next_solver::{
|
||||
DbInterner, GenericArg, SolverContext, Span,
|
||||
infer::{DbInternerInferExt, InferCtxt},
|
||||
mapping::{ChalkToNextSolver, convert_canonical_args_for_result},
|
||||
util::mini_canonicalize,
|
||||
},
|
||||
utils::UnevaluatedConstEvaluatorFolder,
|
||||
db::HirDatabase, infer::unify::InferenceTable, next_solver::{
|
||||
infer::{DbInternerInferExt, InferCtxt}, mapping::{convert_canonical_args_for_result, ChalkToNextSolver}, util::mini_canonicalize, DbInterner, GenericArg, Predicate, SolverContext, Span
|
||||
}, utils::UnevaluatedConstEvaluatorFolder, AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause
|
||||
};
|
||||
|
||||
/// A set of clauses that we assume to be true. E.g. if we are inside this function:
|
||||
@ -231,7 +224,6 @@ impl NextTraitSolveResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// Solve a trait goal using Chalk.
|
||||
pub fn next_trait_solve(
|
||||
db: &dyn HirDatabase,
|
||||
krate: Crate,
|
||||
@ -290,6 +282,57 @@ pub fn next_trait_solve(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_trait_solve_canonical<'db>(
|
||||
db: &'db dyn HirDatabase,
|
||||
krate: Crate,
|
||||
block: Option<BlockId>,
|
||||
goal: crate::next_solver::Canonical<'db, crate::next_solver::Goal<'db, Predicate<'db>>>,
|
||||
) -> NextTraitSolveResult {
|
||||
// FIXME: should use analysis_in_body, but that needs GenericDefId::Block
|
||||
let context = SolverContext(
|
||||
DbInterner::new_with(db, Some(krate), block)
|
||||
.infer_ctxt()
|
||||
.build(TypingMode::non_body_analysis()),
|
||||
);
|
||||
|
||||
tracing::info!(?goal);
|
||||
|
||||
let (goal, var_values) =
|
||||
context.instantiate_canonical(&goal);
|
||||
tracing::info!(?var_values);
|
||||
|
||||
let res = context.evaluate_root_goal(
|
||||
goal.clone(),
|
||||
Span::dummy(),
|
||||
None
|
||||
);
|
||||
|
||||
let vars =
|
||||
var_values.var_values.iter().map(|g| context.0.resolve_vars_if_possible(g)).collect();
|
||||
let canonical_var_values = mini_canonicalize(context, vars);
|
||||
|
||||
let res = res.map(|r| (r.has_changed, r.certainty, canonical_var_values));
|
||||
|
||||
tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res);
|
||||
|
||||
match res {
|
||||
Err(_) => NextTraitSolveResult::NoSolution,
|
||||
Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain(
|
||||
convert_canonical_args_for_result(DbInterner::new_with(db, Some(krate), block), args)
|
||||
),
|
||||
Ok((_, Certainty::Maybe(_), args)) => {
|
||||
let subst = convert_canonical_args_for_result(
|
||||
DbInterner::new_with(db, Some(krate), block),
|
||||
args,
|
||||
);
|
||||
NextTraitSolveResult::Uncertain(chalk_ir::Canonical {
|
||||
binders: subst.binders,
|
||||
value: subst.value.subst,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Solve a trait goal using Chalk.
|
||||
pub fn next_trait_solve_in_ctxt<'db, 'a>(
|
||||
infer_ctxt: &'a InferCtxt<'db>,
|
||||
|
@ -14,7 +14,7 @@ use hir_expand::{
|
||||
mod_path::{ModPath, PathKind},
|
||||
name::Name,
|
||||
};
|
||||
use hir_ty::{db::HirDatabase, method_resolution};
|
||||
use hir_ty::{db::HirDatabase, method_resolution, next_solver::{mapping::ChalkToNextSolver, DbInterner}};
|
||||
|
||||
use crate::{
|
||||
Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl,
|
||||
@ -271,7 +271,7 @@ fn resolve_impl_trait_item<'db>(
|
||||
//
|
||||
// FIXME: resolve type aliases (which are not yielded by iterate_path_candidates)
|
||||
_ = method_resolution::iterate_path_candidates(
|
||||
&canonical,
|
||||
&canonical.to_nextsolver(DbInterner::new_with(db, Some(environment.krate), environment.block)),
|
||||
db,
|
||||
environment,
|
||||
&traits_in_scope,
|
||||
|
@ -5611,7 +5611,11 @@ impl<'db> Type<'db> {
|
||||
.map_or_else(|| TraitEnvironment::empty(krate.id), |d| db.trait_environment(d));
|
||||
|
||||
_ = method_resolution::iterate_method_candidates_dyn(
|
||||
&canonical,
|
||||
&canonical.to_nextsolver(DbInterner::new_with(
|
||||
db,
|
||||
Some(environment.krate),
|
||||
environment.block,
|
||||
)),
|
||||
db,
|
||||
environment,
|
||||
traits_in_scope,
|
||||
@ -5698,7 +5702,11 @@ impl<'db> Type<'db> {
|
||||
.map_or_else(|| TraitEnvironment::empty(krate.id), |d| db.trait_environment(d));
|
||||
|
||||
_ = method_resolution::iterate_path_candidates(
|
||||
&canonical,
|
||||
&canonical.to_nextsolver(DbInterner::new_with(
|
||||
db,
|
||||
Some(environment.krate),
|
||||
environment.block,
|
||||
)),
|
||||
db,
|
||||
environment,
|
||||
traits_in_scope,
|
||||
|
Loading…
x
Reference in New Issue
Block a user