From 2d30dd67d34fcac0cd47bc64485239e0c2616e13 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 20 Mar 2022 16:19:02 +0100 Subject: [PATCH] Expose coercion logic in hir API --- crates/hir/src/lib.rs | 9 +++++-- crates/hir_ty/src/infer.rs | 1 + crates/hir_ty/src/infer/coerce.rs | 45 ++++++++++++++++++++++++++++--- crates/hir_ty/src/infer/unify.rs | 18 +++++++++++-- crates/hir_ty/src/lib.rs | 2 +- 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 3c12907b82..55d2d8b7e3 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 + 'a { + fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator + '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 442774d0be..d7913e1dcf 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 1275d59673..1570bf6569 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -5,9 +5,9 @@ //! See 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, + tys: &Canonical<(Ty, Ty)>, +) -> bool { + coerce(db, env, tys).is_ok() +} + +pub(crate) fn coerce( + db: &dyn HirDatabase, + env: Arc, + tys: &Canonical<(Ty, Ty)>, +) -> Result<(Vec, 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 ef0675d59f..90bccb82f7 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], + ) -> 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(&mut self, canonical: Canonical) -> T::Result where T: HasInterner + Fold + 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( diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 59e6fe2a04..945b4b0e4a 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::{