Expose coercion logic in hir API

This commit is contained in:
Florian Diebold 2022-03-20 16:19:02 +01:00
parent 6133e6a002
commit 2d30dd67d3
5 changed files with 67 additions and 8 deletions

View file

@ -2900,7 +2900,7 @@ impl Type {
self.autoderef_(db).map(move |ty| self.derived(ty)) 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 // There should be no inference vars in types passed here
let canonical = hir_ty::replace_errors_with_variables(&self.ty); let canonical = hir_ty::replace_errors_with_variables(&self.ty);
let environment = self.env.clone(); let environment = self.env.clone();
@ -3238,7 +3238,12 @@ impl Type {
pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool { 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())); 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)
} }
} }

View file

@ -45,6 +45,7 @@ use crate::{
// https://github.com/rust-lang/rust/issues/57411 // https://github.com/rust-lang/rust/issues/57411
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
pub use unify::could_unify; pub use unify::could_unify;
pub use coerce::could_coerce;
pub(crate) mod unify; pub(crate) mod unify;
mod path; mod path;

View file

@ -5,9 +5,9 @@
//! See <https://doc.rust-lang.org/nomicon/coercions.html> and //! See <https://doc.rust-lang.org/nomicon/coercions.html> and
//! `librustc_typeck/check/coercion.rs`. //! `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 hir_def::{expr::ExprId, lang_item::LangItemTarget};
use stdx::always; use stdx::always;
use syntax::SmolStr; use syntax::SmolStr;
@ -19,7 +19,7 @@ use crate::{
PointerCast, TypeError, TypeMismatch, PointerCast, TypeError, TypeMismatch,
}, },
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner, 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; 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> { impl<'a> InferenceContext<'a> {
/// Unify two types, but may coerce the first one to the second one /// Unify two types, but may coerce the first one to the second one
/// using "implicit coercion rules" if needed. /// using "implicit coercion rules" if needed.

View file

@ -4,7 +4,7 @@ use std::{fmt, mem, sync::Arc};
use chalk_ir::{ use chalk_ir::{
cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, NoSolution, cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, NoSolution,
TyVariableKind, UniverseIndex, TyVariableKind, UniverseIndex, CanonicalVarKind,
}; };
use chalk_solve::infer::ParameterEnaVariableExt; use chalk_solve::infer::ParameterEnaVariableExt;
use ena::unify::UnifyKey; use ena::unify::UnifyKey;
@ -299,11 +299,25 @@ impl<'a> InferenceTable<'a> {
self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback) 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 pub(crate) fn instantiate_canonical<T>(&mut self, canonical: Canonical<T>) -> T::Result
where where
T: HasInterner<Interner = Interner> + Fold<Interner> + std::fmt::Debug, 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>( fn resolve_with_fallback_inner<T>(

View file

@ -51,7 +51,7 @@ pub use autoderef::autoderef;
pub use builder::{ParamKind, TyBuilder}; pub use builder::{ParamKind, TyBuilder};
pub use chalk_ext::*; pub use chalk_ext::*;
pub use infer::{ 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 interner::Interner;
pub use lower::{ pub use lower::{