about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorian Diebold <flodiebold@gmail.com>2022-03-20 16:19:02 +0100
committerFlorian Diebold <flodiebold@gmail.com>2022-03-21 16:45:59 +0100
commit2d30dd67d34fcac0cd47bc64485239e0c2616e13 (patch)
tree8024cfb163844045066275fdf8feb35025ad942b
parent6133e6a002037bfae8e9ff3fbd0fa7c92c5ee68b (diff)
downloadrust-2d30dd67d34fcac0cd47bc64485239e0c2616e13.tar.gz
rust-2d30dd67d34fcac0cd47bc64485239e0c2616e13.zip
Expose coercion logic in hir API
-rw-r--r--crates/hir/src/lib.rs9
-rw-r--r--crates/hir_ty/src/infer.rs1
-rw-r--r--crates/hir_ty/src/infer/coerce.rs45
-rw-r--r--crates/hir_ty/src/infer/unify.rs18
-rw-r--r--crates/hir_ty/src/lib.rs2
5 files changed, 67 insertions, 8 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 3c12907b824..55d2d8b7e31 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2900,7 +2900,7 @@ impl Type {
         self.autoderef_(db).map(move |ty| self.derived(ty))
     }
 
-    pub fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
+    fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
         // There should be no inference vars in types passed here
         let canonical = hir_ty::replace_errors_with_variables(&self.ty);
         let environment = self.env.clone();
@@ -3238,7 +3238,12 @@ impl Type {
 
     pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool {
         let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), other.ty.clone()));
-        could_unify(db, self.env.clone(), &tys)
+        hir_ty::could_unify(db, self.env.clone(), &tys)
+    }
+
+    pub fn could_coerce_to(&self, db: &dyn HirDatabase, to: &Type) -> bool {
+        let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), to.ty.clone()));
+        hir_ty::could_coerce(db, self.env.clone(), &tys)
     }
 }
 
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 442774d0be0..d7913e1dcf3 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -45,6 +45,7 @@ use crate::{
 // https://github.com/rust-lang/rust/issues/57411
 #[allow(unreachable_pub)]
 pub use unify::could_unify;
+pub use coerce::could_coerce;
 
 pub(crate) mod unify;
 mod path;
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index 1275d596734..1570bf65693 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -5,9 +5,9 @@
 //! See <https://doc.rust-lang.org/nomicon/coercions.html> and
 //! `librustc_typeck/check/coercion.rs`.
 
-use std::iter;
+use std::{iter, sync::Arc};
 
-use chalk_ir::{cast::Cast, Goal, Mutability, TyVariableKind};
+use chalk_ir::{cast::Cast, Goal, Mutability, TyVariableKind, BoundVar};
 use hir_def::{expr::ExprId, lang_item::LangItemTarget};
 use stdx::always;
 use syntax::SmolStr;
@@ -19,7 +19,7 @@ use crate::{
         PointerCast, TypeError, TypeMismatch,
     },
     static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner,
-    Solution, Substitution, Ty, TyBuilder, TyExt, TyKind,
+    Solution, Substitution, Ty, TyBuilder, TyExt, TyKind, db::HirDatabase, TraitEnvironment, GenericArgData,
 };
 
 use super::unify::InferenceTable;
@@ -120,6 +120,45 @@ impl CoerceMany {
     }
 }
 
+pub fn could_coerce(
+    db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
+    tys: &Canonical<(Ty, Ty)>,
+) -> bool {
+    coerce(db, env, tys).is_ok()
+}
+
+pub(crate) fn coerce(
+    db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
+    tys: &Canonical<(Ty, Ty)>,
+) -> Result<(Vec<Adjustment>, Ty), TypeError> {
+    let mut table = InferenceTable::new(db, env);
+    let vars = table.fresh_subst(tys.binders.as_slice(Interner));
+    let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
+    let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner);
+    let (adjustments, ty) = table.coerce(&ty1_with_vars, &ty2_with_vars)?;
+    // default any type vars that weren't unified back to their original bound vars
+    // (kind of hacky)
+    let find_var = |iv| {
+        vars.iter(Interner).position(|v| match v.interned() {
+            chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner),
+            chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
+            chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner),
+        } == Some(iv))
+    };
+    let fallback = |iv, kind, default, binder| match kind {
+        chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv)
+            .map_or(default, |i| BoundVar::new(binder, i).to_ty(Interner).cast(Interner)),
+        chalk_ir::VariableKind::Lifetime => find_var(iv)
+            .map_or(default, |i| BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner)),
+        chalk_ir::VariableKind::Const(ty) => find_var(iv)
+            .map_or(default, |i| BoundVar::new(binder, i).to_const(Interner, ty).cast(Interner)),
+    };
+    // FIXME also map the types in the adjustments
+    Ok((adjustments, table.resolve_with_fallback(ty, &fallback)))
+}
+
 impl<'a> InferenceContext<'a> {
     /// Unify two types, but may coerce the first one to the second one
     /// using "implicit coercion rules" if needed.
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index ef0675d59f6..90bccb82f79 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -4,7 +4,7 @@ use std::{fmt, mem, sync::Arc};
 
 use chalk_ir::{
     cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, NoSolution,
-    TyVariableKind, UniverseIndex,
+    TyVariableKind, UniverseIndex, CanonicalVarKind,
 };
 use chalk_solve::infer::ParameterEnaVariableExt;
 use ena::unify::UnifyKey;
@@ -299,11 +299,25 @@ impl<'a> InferenceTable<'a> {
         self.resolve_with_fallback_inner(&mut Vec::new(), 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)
+            }),
+        )
+    }
+
     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)
+        let subst = self.fresh_subst(canonical.binders.as_slice(Interner));
+        subst.apply(canonical.value, Interner)
     }
 
     fn resolve_with_fallback_inner<T>(
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index 59e6fe2a040..945b4b0e4a6 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -51,7 +51,7 @@ pub use autoderef::autoderef;
 pub use builder::{ParamKind, TyBuilder};
 pub use chalk_ext::*;
 pub use infer::{
-    could_unify, Adjust, Adjustment, AutoBorrow, InferenceDiagnostic, InferenceResult,
+    could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, InferenceDiagnostic, InferenceResult,
 };
 pub use interner::Interner;
 pub use lower::{