2019-10-12 15:39:20 +00:00
|
|
|
//! Coercion logic. Coercions are certain type conversions that can implicitly
|
|
|
|
//! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions
|
|
|
|
//! like going from `&Vec<T>` to `&[T]`.
|
|
|
|
//!
|
|
|
|
//! See: https://doc.rust-lang.org/nomicon/coercions.html
|
|
|
|
|
2021-03-20 10:23:59 +00:00
|
|
|
use chalk_ir::{cast::Cast, Mutability, TyVariableKind};
|
2021-03-01 18:30:34 +00:00
|
|
|
use hir_def::lang_item::LangItemTarget;
|
2019-10-12 15:39:20 +00:00
|
|
|
|
2021-04-04 18:27:40 +00:00
|
|
|
use crate::{autoderef, Interner, Solution, Ty, TyBuilder, TyKind};
|
2019-10-12 15:39:20 +00:00
|
|
|
|
2021-03-01 11:35:11 +00:00
|
|
|
use super::{InEnvironment, InferenceContext};
|
2019-11-21 12:39:09 +00:00
|
|
|
|
2020-03-13 15:05:46 +00:00
|
|
|
impl<'a> InferenceContext<'a> {
|
2019-10-12 15:39:20 +00:00
|
|
|
/// Unify two types, but may coerce the first one to the second one
|
|
|
|
/// using "implicit coercion rules" if needed.
|
|
|
|
pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool {
|
|
|
|
let from_ty = self.resolve_ty_shallow(from_ty).into_owned();
|
|
|
|
let to_ty = self.resolve_ty_shallow(to_ty);
|
|
|
|
self.coerce_inner(from_ty, &to_ty)
|
|
|
|
}
|
|
|
|
|
2020-05-08 20:12:16 +00:00
|
|
|
/// Merge two types from different branches, with possible coercion.
|
2019-10-12 15:39:20 +00:00
|
|
|
///
|
2020-05-08 20:12:16 +00:00
|
|
|
/// Mostly this means trying to coerce one to the other, but
|
|
|
|
/// - if we have two function types for different functions, we need to
|
|
|
|
/// coerce both to function pointers;
|
|
|
|
/// - if we were concerned with lifetime subtyping, we'd need to look for a
|
|
|
|
/// least upper bound.
|
2020-02-18 13:32:19 +00:00
|
|
|
pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty {
|
2019-10-12 15:39:20 +00:00
|
|
|
if self.coerce(ty1, ty2) {
|
|
|
|
ty2.clone()
|
|
|
|
} else if self.coerce(ty2, ty1) {
|
|
|
|
ty1.clone()
|
|
|
|
} else {
|
2021-03-13 13:44:51 +00:00
|
|
|
if let (TyKind::FnDef(..), TyKind::FnDef(..)) =
|
2021-04-03 11:08:29 +00:00
|
|
|
(ty1.kind(&Interner), ty2.kind(&Interner))
|
2021-03-13 13:44:51 +00:00
|
|
|
{
|
2021-03-08 20:19:44 +00:00
|
|
|
cov_mark::hit!(coerce_fn_reification);
|
2020-05-08 20:12:16 +00:00
|
|
|
// Special case: two function types. Try to coerce both to
|
|
|
|
// pointers to have a chance at getting a match. See
|
|
|
|
// https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
|
|
|
|
let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig");
|
|
|
|
let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig");
|
2021-04-03 18:27:57 +00:00
|
|
|
let ptr_ty1 = TyBuilder::fn_ptr(sig1);
|
|
|
|
let ptr_ty2 = TyBuilder::fn_ptr(sig2);
|
2020-05-08 20:12:16 +00:00
|
|
|
self.coerce_merge_branch(&ptr_ty1, &ptr_ty2)
|
|
|
|
} else {
|
2021-03-08 20:19:44 +00:00
|
|
|
cov_mark::hit!(coerce_merge_fail_fallback);
|
2020-05-29 14:03:06 +00:00
|
|
|
ty1.clone()
|
2020-05-08 20:12:16 +00:00
|
|
|
}
|
2019-10-12 15:39:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool {
|
2021-04-03 11:08:29 +00:00
|
|
|
match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
|
2019-10-12 15:39:20 +00:00
|
|
|
// Never type will make type variable to fallback to Never Type instead of Unknown.
|
2021-03-13 13:44:51 +00:00
|
|
|
(TyKind::Never, TyKind::InferenceVar(tv, TyVariableKind::General)) => {
|
2021-03-01 11:35:11 +00:00
|
|
|
self.table.type_variable_table.set_diverging(*tv, true);
|
2019-10-12 15:39:20 +00:00
|
|
|
return true;
|
|
|
|
}
|
2021-03-13 13:44:51 +00:00
|
|
|
(TyKind::Never, _) => return true,
|
2019-10-12 15:39:20 +00:00
|
|
|
|
|
|
|
// Trivial cases, this should go after `never` check to
|
|
|
|
// avoid infer result type to be never
|
|
|
|
_ => {
|
2020-04-17 17:41:37 +00:00
|
|
|
if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) {
|
2019-10-12 15:39:20 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pointer weakening and function to pointer
|
2021-04-03 11:08:29 +00:00
|
|
|
match (from_ty.interned_mut(), to_ty.kind(&Interner)) {
|
2021-02-28 18:13:37 +00:00
|
|
|
// `*mut T` -> `*const T`
|
2019-10-12 15:39:20 +00:00
|
|
|
// `&mut T` -> `&T`
|
2021-03-13 13:44:51 +00:00
|
|
|
(TyKind::Raw(m1, ..), TyKind::Raw(m2 @ Mutability::Not, ..))
|
|
|
|
| (TyKind::Ref(m1, ..), TyKind::Ref(m2 @ Mutability::Not, ..)) => {
|
2021-02-28 18:13:37 +00:00
|
|
|
*m1 = *m2;
|
|
|
|
}
|
|
|
|
// `&T` -> `*const T`
|
|
|
|
// `&mut T` -> `*mut T`/`*const T`
|
2021-03-13 13:44:51 +00:00
|
|
|
(TyKind::Ref(.., substs), &TyKind::Raw(m2 @ Mutability::Not, ..))
|
|
|
|
| (TyKind::Ref(Mutability::Mut, substs), &TyKind::Raw(m2, ..)) => {
|
|
|
|
from_ty = TyKind::Raw(m2, substs.clone()).intern(&Interner);
|
2019-10-12 15:39:20 +00:00
|
|
|
}
|
|
|
|
|
2021-02-28 18:13:37 +00:00
|
|
|
// Illegal mutability conversion
|
2021-03-13 13:44:51 +00:00
|
|
|
(TyKind::Raw(Mutability::Not, ..), TyKind::Raw(Mutability::Mut, ..))
|
|
|
|
| (TyKind::Ref(Mutability::Not, ..), TyKind::Ref(Mutability::Mut, ..)) => return false,
|
2019-10-12 15:39:20 +00:00
|
|
|
|
|
|
|
// `{function_type}` -> `fn()`
|
2021-03-13 13:44:51 +00:00
|
|
|
(TyKind::FnDef(..), TyKind::Function { .. }) => match from_ty.callable_sig(self.db) {
|
2021-02-28 18:13:37 +00:00
|
|
|
None => return false,
|
|
|
|
Some(sig) => {
|
2021-04-03 18:27:57 +00:00
|
|
|
from_ty = TyBuilder::fn_ptr(sig);
|
2019-10-12 15:39:20 +00:00
|
|
|
}
|
2021-02-28 18:13:37 +00:00
|
|
|
},
|
2019-10-12 15:39:20 +00:00
|
|
|
|
2021-03-13 13:44:51 +00:00
|
|
|
(TyKind::Closure(.., substs), TyKind::Function { .. }) => {
|
2021-04-01 19:04:02 +00:00
|
|
|
from_ty = substs.at(&Interner, 0).assert_ty_ref(&Interner).clone();
|
2019-12-20 17:53:40 +00:00
|
|
|
}
|
|
|
|
|
2019-10-12 15:39:20 +00:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(ret) = self.try_coerce_unsized(&from_ty, &to_ty) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Auto Deref if cannot coerce
|
2021-04-03 11:08:29 +00:00
|
|
|
match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
|
2019-10-12 15:39:20 +00:00
|
|
|
// FIXME: DerefMut
|
2021-03-14 16:40:55 +00:00
|
|
|
(TyKind::Ref(_, st1), TyKind::Ref(_, st2)) => self.unify_autoderef_behind_ref(st1, st2),
|
2019-10-12 15:39:20 +00:00
|
|
|
|
|
|
|
// Otherwise, normal unify
|
|
|
|
_ => self.unify(&from_ty, to_ty),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Coerce a type using `from_ty: CoerceUnsized<ty_ty>`
|
|
|
|
///
|
|
|
|
/// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html
|
|
|
|
fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> Option<bool> {
|
2020-02-21 17:24:18 +00:00
|
|
|
let krate = self.resolver.krate().unwrap();
|
|
|
|
let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) {
|
|
|
|
Some(LangItemTarget::TraitId(trait_)) => trait_,
|
2019-10-12 15:39:20 +00:00
|
|
|
_ => return None,
|
|
|
|
};
|
|
|
|
|
2021-04-03 19:59:13 +00:00
|
|
|
let trait_ref = {
|
|
|
|
let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait);
|
|
|
|
if b.remaining() != 2 {
|
|
|
|
// The CoerceUnsized trait should have two generic params: Self and T.
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
b.push(from_ty.clone()).push(to_ty.clone()).build()
|
|
|
|
};
|
2019-10-12 15:39:20 +00:00
|
|
|
|
2021-03-21 19:19:07 +00:00
|
|
|
let goal = InEnvironment::new(self.trait_env.env.clone(), trait_ref.cast(&Interner));
|
2019-10-12 15:39:20 +00:00
|
|
|
|
2020-02-21 17:24:18 +00:00
|
|
|
let canonicalizer = self.canonicalizer();
|
|
|
|
let canonicalized = canonicalizer.canonicalize_obligation(goal);
|
2019-10-12 15:39:20 +00:00
|
|
|
|
2020-02-21 17:24:18 +00:00
|
|
|
let solution = self.db.trait_solve(krate, canonicalized.value.clone())?;
|
2019-10-12 15:39:20 +00:00
|
|
|
|
2020-02-21 17:24:18 +00:00
|
|
|
match solution {
|
|
|
|
Solution::Unique(v) => {
|
|
|
|
canonicalized.apply_solution(self, v.0);
|
2019-10-12 15:39:20 +00:00
|
|
|
}
|
2020-02-21 17:24:18 +00:00
|
|
|
_ => return None,
|
|
|
|
};
|
2019-10-12 15:39:20 +00:00
|
|
|
|
2020-02-21 17:24:18 +00:00
|
|
|
Some(true)
|
2019-10-12 15:39:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Unify `from_ty` to `to_ty` with optional auto Deref
|
|
|
|
///
|
|
|
|
/// Note that the parameters are already stripped the outer reference.
|
|
|
|
fn unify_autoderef_behind_ref(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool {
|
|
|
|
let canonicalized = self.canonicalizer().canonicalize_ty(from_ty.clone());
|
|
|
|
let to_ty = self.resolve_ty_shallow(&to_ty);
|
|
|
|
// FIXME: Auto DerefMut
|
2019-11-25 10:10:26 +00:00
|
|
|
for derefed_ty in autoderef::autoderef(
|
|
|
|
self.db,
|
|
|
|
self.resolver.krate(),
|
|
|
|
InEnvironment {
|
2021-03-21 19:19:07 +00:00
|
|
|
goal: canonicalized.value.clone(),
|
|
|
|
environment: self.trait_env.env.clone(),
|
2019-11-25 10:10:26 +00:00
|
|
|
},
|
|
|
|
) {
|
2019-10-12 15:39:20 +00:00
|
|
|
let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value);
|
2021-02-28 18:13:37 +00:00
|
|
|
let from_ty = self.resolve_ty_shallow(&derefed_ty);
|
|
|
|
// Stop when constructor matches.
|
|
|
|
if from_ty.equals_ctor(&to_ty) {
|
|
|
|
// It will not recurse to `coerce`.
|
2021-03-14 16:40:55 +00:00
|
|
|
return self.table.unify(&from_ty, &to_ty);
|
2021-02-28 18:13:37 +00:00
|
|
|
} else if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) {
|
|
|
|
return true;
|
2019-10-12 15:39:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|