Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir_ty/src/infer/unify.rs')
-rw-r--r--crates/hir_ty/src/infer/unify.rs98
1 files changed, 77 insertions, 21 deletions
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index bb7cdb677e..21b48b9d80 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -1,6 +1,6 @@
//! Unification and canonicalization logic.
-use std::{fmt, mem, sync::Arc};
+use std::{fmt, iter, mem, sync::Arc};
use chalk_ir::{
cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, NoSolution,
@@ -8,12 +8,14 @@ use chalk_ir::{
};
use chalk_solve::infer::ParameterEnaVariableExt;
use ena::unify::UnifyKey;
+use hir_expand::name;
use super::{InferOk, InferResult, InferenceContext, TypeError};
use crate::{
- db::HirDatabase, fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, Const,
- DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar, Interner, Lifetime,
- ProjectionTy, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyKind, VariableKind,
+ db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar,
+ Canonical, Const, DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar,
+ Interner, Lifetime, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution,
+ TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
};
impl<'a> InferenceContext<'a> {
@@ -24,32 +26,20 @@ impl<'a> InferenceContext<'a> {
where
T::Result: HasInterner<Interner = Interner>,
{
- // try to resolve obligations before canonicalizing, since this might
- // result in new knowledge about variables
- self.resolve_obligations_as_possible();
self.table.canonicalize(t)
}
}
#[derive(Debug, Clone)]
-pub(super) struct Canonicalized<T>
+pub(crate) struct Canonicalized<T>
where
T: HasInterner<Interner = Interner>,
{
- pub(super) value: Canonical<T>,
+ pub(crate) value: Canonical<T>,
free_vars: Vec<GenericArg>,
}
impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
- /// this method is wrong and shouldn't exist
- pub(super) fn decanonicalize_ty(&self, table: &mut InferenceTable, ty: Canonical<Ty>) -> Ty {
- let mut vars = self.free_vars.clone();
- while ty.binders.len(Interner) > vars.len() {
- vars.push(table.new_type_var().cast(Interner));
- }
- chalk_ir::Substitute::apply(&vars, ty.value, Interner)
- }
-
pub(super) fn apply_solution(
&self,
ctx: &mut InferenceTable,
@@ -203,13 +193,16 @@ impl<'a> InferenceTable<'a> {
.intern(Interner)
}
- pub(super) fn canonicalize<T: Fold<Interner> + HasInterner<Interner = Interner>>(
+ pub(crate) fn canonicalize<T: Fold<Interner> + HasInterner<Interner = Interner>>(
&mut self,
t: T,
) -> Canonicalized<T::Result>
where
T::Result: HasInterner<Interner = Interner>,
{
+ // 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
@@ -225,7 +218,7 @@ impl<'a> InferenceTable<'a> {
/// type annotation (e.g. from a let type annotation, field type or function
/// call). `make_ty` handles this already, but e.g. for field types we need
/// to do it as well.
- pub(super) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
+ pub(crate) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
fold_tys(
ty,
|ty, _| match ty.kind(Interner) {
@@ -238,7 +231,7 @@ impl<'a> InferenceTable<'a> {
)
}
- pub(super) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
+ pub(crate) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
let var = self.new_type_var();
let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() };
let obligation = alias_eq.cast(Interner);
@@ -299,6 +292,13 @@ impl<'a> InferenceTable<'a> {
self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback)
}
+ pub(crate) fn instantiate_canonical<T>(&mut self, canonical: Canonical<T>) -> T::Result
+ where
+ T: HasInterner<Interner = Interner> + Fold<Interner> + std::fmt::Debug,
+ {
+ self.var_unification_table.instantiate_canonical(Interner, canonical)
+ }
+
fn resolve_with_fallback_inner<T>(
&mut self,
var_stack: &mut Vec<InferenceVar>,
@@ -351,6 +351,7 @@ impl<'a> InferenceTable<'a> {
/// If `ty` is a type variable with known type, returns that type;
/// otherwise, return ty.
pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty {
+ self.resolve_obligations_as_possible();
self.var_unification_table.normalize_ty_shallow(Interner, ty).unwrap_or_else(|| ty.clone())
}
@@ -363,6 +364,16 @@ impl<'a> InferenceTable<'a> {
self.var_unification_table.rollback_to(snapshot.var_table_snapshot);
}
+ /// 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).
+ pub(crate) fn try_obligation(&mut self, goal: Goal) -> Option<Solution> {
+ let in_env = InEnvironment::new(&self.trait_env.env, goal);
+ let canonicalized = self.canonicalize(in_env);
+ let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value);
+ solution
+ }
+
pub(crate) fn register_obligation(&mut self, goal: Goal) {
let in_env = InEnvironment::new(&self.trait_env.env, goal);
self.register_obligation_in_env(in_env)
@@ -522,6 +533,51 @@ impl<'a> InferenceTable<'a> {
}
}
}
+
+ pub(crate) fn callable_sig(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
+ match ty.callable_sig(self.db) {
+ Some(sig) => Some((sig.params().to_vec(), sig.ret().clone())),
+ None => self.callable_sig_from_fn_trait(ty, num_args),
+ }
+ }
+
+ fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
+ let krate = self.trait_env.krate;
+ let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
+ let output_assoc_type =
+ self.db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
+
+ let mut arg_tys = vec![];
+ let arg_ty = TyBuilder::tuple(num_args)
+ .fill(iter::repeat_with(|| {
+ let arg = self.new_type_var();
+ arg_tys.push(arg.clone());
+ arg
+ }))
+ .build();
+
+ let projection = {
+ let b = TyBuilder::assoc_type_projection(self.db, output_assoc_type);
+ if b.remaining() != 2 {
+ return None;
+ }
+ b.push(ty.clone()).push(arg_ty).build()
+ };
+
+ let trait_env = self.trait_env.env.clone();
+ let obligation = InEnvironment {
+ goal: projection.trait_ref(self.db).cast(Interner),
+ environment: trait_env,
+ };
+ let canonical = self.canonicalize(obligation.clone());
+ if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
+ self.register_obligation(obligation.goal);
+ let return_ty = self.normalize_projection_ty(projection);
+ Some((arg_tys, return_ty))
+ } else {
+ None
+ }
+ }
}
impl<'a> fmt::Debug for InferenceTable<'a> {