Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir_ty/src/infer/coerce.rs')
| -rw-r--r-- | crates/hir_ty/src/infer/coerce.rs | 100 |
1 files changed, 78 insertions, 22 deletions
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 528e3ba882..f54440bf5b 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -5,23 +5,26 @@ //! 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, BoundVar, Goal, Mutability, TyVariableKind}; use hir_def::{expr::ExprId, lang_item::LangItemTarget}; use stdx::always; use syntax::SmolStr; use crate::{ autoderef::{Autoderef, AutoderefKind}, + db::HirDatabase, infer::{ - Adjust, Adjustment, AutoBorrow, InferOk, InferResult, InferenceContext, OverloadedDeref, - PointerCast, TypeError, TypeMismatch, + Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, + TypeError, TypeMismatch, }, static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner, - Solution, Substitution, Ty, TyBuilder, TyExt, TyKind, + Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, }; +use super::unify::InferenceTable; + pub(crate) type CoerceResult = Result<InferOk<(Vec<Adjustment>, Ty)>, TypeError>; /// Do not require any adjustments, i.e. coerce `x -> x`. @@ -84,8 +87,8 @@ impl CoerceMany { }; if let Some(sig) = sig { let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(Interner); - let result1 = ctx.coerce_inner(self.expected_ty.clone(), &target_ty); - let result2 = ctx.coerce_inner(expr_ty.clone(), &target_ty); + let result1 = ctx.table.coerce_inner(self.expected_ty.clone(), &target_ty); + let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty); if let (Ok(result1), Ok(result2)) = (result1, result2) { ctx.table.register_infer_ok(result1); ctx.table.register_infer_ok(result2); @@ -118,6 +121,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. @@ -126,16 +168,31 @@ impl<'a> InferenceContext<'a> { expr: Option<ExprId>, from_ty: &Ty, to_ty: &Ty, - ) -> InferResult<Ty> { + ) -> Result<Ty, TypeError> { + let from_ty = self.resolve_ty_shallow(from_ty); + let to_ty = self.resolve_ty_shallow(to_ty); + let (adjustments, ty) = self.table.coerce(&from_ty, &to_ty)?; + if let Some(expr) = expr { + self.write_expr_adj(expr, adjustments); + } + Ok(ty) + } +} + +impl<'a> InferenceTable<'a> { + /// Unify two types, but may coerce the first one to the second one + /// using "implicit coercion rules" if needed. + pub(crate) fn coerce( + &mut self, + from_ty: &Ty, + to_ty: &Ty, + ) -> Result<(Vec<Adjustment>, Ty), TypeError> { let from_ty = self.resolve_ty_shallow(from_ty); let to_ty = self.resolve_ty_shallow(to_ty); match self.coerce_inner(from_ty, &to_ty) { Ok(InferOk { value: (adjustments, ty), goals }) => { - if let Some(expr) = expr { - self.write_expr_adj(expr, adjustments); - } - self.table.register_infer_ok(InferOk { value: (), goals }); - Ok(InferOk { value: ty, goals: Vec::new() }) + self.register_infer_ok(InferOk { value: (), goals }); + Ok((adjustments, ty)) } Err(e) => { // FIXME deal with error @@ -154,7 +211,7 @@ impl<'a> InferenceContext<'a> { // // here, we would coerce from `!` to `?T`. if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) { - self.table.set_diverging(*tv, true); + self.set_diverging(*tv, true); } return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]); } @@ -203,8 +260,7 @@ impl<'a> InferenceContext<'a> { where F: FnOnce(Ty) -> Vec<Adjustment>, { - self.table - .try_unify(t1, t2) + self.try_unify(t1, t2) .and_then(|InferOk { goals, .. }| success(f(t1.clone()), t1.clone(), goals)) } @@ -259,9 +315,9 @@ impl<'a> InferenceContext<'a> { // details of coercion errors though, so I think it's useful to leave // the structure like it is. - let snapshot = self.table.snapshot(); + let snapshot = self.snapshot(); - let mut autoderef = Autoderef::new(&mut self.table, from_ty.clone()); + let mut autoderef = Autoderef::new(self, from_ty.clone()); let mut first_error = None; let mut found = None; @@ -317,7 +373,7 @@ impl<'a> InferenceContext<'a> { let InferOk { value: ty, goals } = match found { Some(d) => d, None => { - self.table.rollback_to(snapshot); + self.rollback_to(snapshot); let err = first_error.expect("coerce_borrowed_pointer had no error"); return Err(err); } @@ -513,7 +569,7 @@ impl<'a> InferenceContext<'a> { let coerce_from = reborrow.as_ref().map_or_else(|| from_ty.clone(), |(_, adj)| adj.target.clone()); - let krate = self.resolver.krate().unwrap(); + let krate = self.trait_env.krate; let coerce_unsized_trait = match self.db.lang_item(krate, SmolStr::new_inline("coerce_unsized")) { Some(LangItemTarget::TraitId(trait_)) => trait_, @@ -546,7 +602,7 @@ impl<'a> InferenceContext<'a> { match solution { Solution::Unique(v) => { canonicalized.apply_solution( - &mut self.table, + self, Canonical { binders: v.binders, // FIXME handle constraints @@ -556,7 +612,7 @@ impl<'a> InferenceContext<'a> { } Solution::Ambig(Guidance::Definite(subst)) => { // FIXME need to record an obligation here - canonicalized.apply_solution(&mut self.table, subst) + canonicalized.apply_solution(self, subst) } // FIXME actually we maybe should also accept unknown guidance here _ => return Err(TypeError), |