8856: Use Chalk for unification r=flodiebold a=flodiebold

 - use Chalk's unification, get rid of our own `unify`
 - rewrite coercion to not use unification internals and to be more analogous to rustc
 - fix various coercion bugs
 - rewrite handling of obligations, since the old hacky optimization where we noted when an inference variable changes wasn't possible anymore
 - stop trying to deeply resolve types all the time during inference, instead only do it shallowly where necessary

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
bors[bot] 2021-05-21 17:51:53 +00:00 committed by GitHub
commit edbde25ca2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1240 additions and 1111 deletions

View File

@ -1712,15 +1712,17 @@ impl Type {
resolver: &Resolver,
ty: Ty,
) -> Type {
let environment =
resolver.generic_def().map_or_else(Default::default, |d| db.trait_environment(d));
let environment = resolver
.generic_def()
.map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
Type { krate, env: environment, ty }
}
fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type {
let resolver = lexical_env.resolver(db.upcast());
let environment =
resolver.generic_def().map_or_else(Default::default, |d| db.trait_environment(d));
let environment = resolver
.generic_def()
.map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
Type { krate, env: environment, ty }
}
@ -2051,11 +2053,7 @@ impl Type {
name: Option<&Name>,
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
) -> Option<T> {
// There should be no inference vars in types passed here
// FIXME check that?
// FIXME replace Unknown by bound vars here
let canonical =
Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(&Interner) };
let canonical = hir_ty::replace_errors_with_variables(self.ty.clone());
let env = self.env.clone();
let krate = krate.id;
@ -2223,8 +2221,9 @@ impl Type {
walk_type(db, self, &mut cb);
}
pub fn could_unify_with(&self, other: &Type) -> bool {
could_unify(&self.ty, &other.ty)
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)
}
}

View File

@ -6,15 +6,15 @@ use chalk_ir::{
cast::{Cast, CastTo, Caster},
fold::Fold,
interner::HasInterner,
AdtId, BoundVar, DebruijnIndex, Safety, Scalar,
AdtId, BoundVar, DebruijnIndex, Scalar,
};
use hir_def::{builtin_type::BuiltinType, GenericDefId, TraitId, TypeAliasId};
use smallvec::SmallVec;
use crate::{
db::HirDatabase, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders,
CallableSig, FnPointer, FnSig, FnSubst, GenericArg, Interner, ProjectionTy, Substitution,
TraitRef, Ty, TyDefId, TyExt, TyKind, ValueTyDefId,
CallableSig, GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt,
TyKind, ValueTyDefId,
};
/// This is a builder for `Ty` or anything that needs a `Substitution`.
@ -77,15 +77,7 @@ impl TyBuilder<()> {
}
pub fn fn_ptr(sig: CallableSig) -> Ty {
TyKind::Function(FnPointer {
num_binders: 0,
sig: FnSig { abi: (), safety: Safety::Safe, variadic: sig.is_varargs },
substitution: FnSubst(Substitution::from_iter(
&Interner,
sig.params_and_return.iter().cloned(),
)),
})
.intern(&Interner)
TyKind::Function(sig.to_fn_ptr()).intern(&Interner)
}
pub fn builtin(builtin: BuiltinType) -> Ty {

View File

@ -344,20 +344,20 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
}
fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> {
self
&self.db
}
}
impl<'a> chalk_ir::UnificationDatabase<Interner> for ChalkContext<'a> {
impl<'a> chalk_ir::UnificationDatabase<Interner> for &'a dyn HirDatabase {
fn fn_def_variance(
&self,
fn_def_id: chalk_ir::FnDefId<Interner>,
) -> chalk_ir::Variances<Interner> {
self.db.fn_def_variance(self.krate, fn_def_id)
HirDatabase::fn_def_variance(*self, fn_def_id)
}
fn adt_variance(&self, adt_id: chalk_ir::AdtId<Interner>) -> chalk_ir::Variances<Interner> {
self.db.adt_variance(self.krate, adt_id)
HirDatabase::adt_variance(*self, adt_id)
}
}
@ -651,11 +651,7 @@ pub(crate) fn fn_def_datum_query(
Arc::new(datum)
}
pub(crate) fn fn_def_variance_query(
db: &dyn HirDatabase,
_krate: CrateId,
fn_def_id: FnDefId,
) -> Variances {
pub(crate) fn fn_def_variance_query(db: &dyn HirDatabase, fn_def_id: FnDefId) -> Variances {
let callable_def: CallableDefId = from_chalk(db, fn_def_id);
let generic_params = generics(db.upcast(), callable_def.into());
Variances::from_iter(
@ -666,7 +662,6 @@ pub(crate) fn fn_def_variance_query(
pub(crate) fn adt_variance_query(
db: &dyn HirDatabase,
_krate: CrateId,
chalk_ir::AdtId(adt_id): AdtId,
) -> Variances {
let generic_params = generics(db.upcast(), adt_id.into());

View File

@ -18,6 +18,7 @@ pub trait TyExt {
fn is_unit(&self) -> bool;
fn is_never(&self) -> bool;
fn is_unknown(&self) -> bool;
fn is_ty_var(&self) -> bool;
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
fn as_builtin(&self) -> Option<BuiltinType>;
@ -55,6 +56,10 @@ impl TyExt for Ty {
matches!(self.kind(&Interner), TyKind::Error)
}
fn is_ty_var(&self) -> bool {
matches!(self.kind(&Interner), TyKind::InferenceVar(_, _))
}
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> {
match self.kind(&Interner) {
TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),

View File

@ -117,10 +117,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> Arc<chalk_db::FnDefDatum>;
#[salsa::invoke(chalk_db::fn_def_variance_query)]
fn fn_def_variance(&self, krate: CrateId, fn_def_id: FnDefId) -> chalk_db::Variances;
fn fn_def_variance(&self, fn_def_id: FnDefId) -> chalk_db::Variances;
#[salsa::invoke(chalk_db::adt_variance_query)]
fn adt_variance(&self, krate: CrateId, adt_id: chalk_db::AdtId) -> chalk_db::Variances;
fn adt_variance(&self, adt_id: chalk_db::AdtId) -> chalk_db::Variances;
#[salsa::invoke(chalk_db::associated_ty_value_query)]
fn associated_ty_value(
@ -134,14 +134,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn trait_solve(
&self,
krate: CrateId,
goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>,
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution>;
#[salsa::invoke(crate::traits::trait_solve_query)]
fn trait_solve_query(
&self,
krate: CrateId,
goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>,
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution>;
#[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)]
@ -168,7 +168,7 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
fn trait_solve_wait(
db: &dyn HirDatabase,
krate: CrateId,
goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>,
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution> {
let _p = profile::span("trait_solve::wait");
db.trait_solve_query(krate, goal)

View File

@ -13,8 +13,6 @@
//! to certain types. To record this, we use the union-find implementation from
//! the `ena` crate, which is extracted from rustc.
use std::borrow::Cow;
use std::mem;
use std::ops::Index;
use std::sync::Arc;
@ -27,8 +25,8 @@ use hir_def::{
path::{path, Path},
resolver::{HasResolver, Resolver, TypeNs},
type_ref::TypeRef,
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, Lookup, TraitId,
TypeAliasId, VariantId,
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup,
TraitId, TypeAliasId, VariantId,
};
use hir_expand::{diagnostics::DiagnosticSink, name::name};
use la_arena::ArenaMap;
@ -36,13 +34,11 @@ use rustc_hash::FxHashMap;
use stdx::impl_from;
use syntax::SmolStr;
use super::{
DomainGoal, Guidance, InEnvironment, ProjectionTy, Solution, TraitEnvironment, TraitRef, Ty,
};
use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
use crate::{
db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic,
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Canonical, Interner,
TyBuilder, TyExt, TyKind,
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, TyBuilder,
TyExt, TyKind,
};
// This lint has a false positive here. See the link below for details.
@ -106,6 +102,14 @@ impl Default for BindingMode {
}
}
#[derive(Debug)]
pub(crate) struct InferOk {
goals: Vec<InEnvironment<Goal>>,
}
#[derive(Debug)]
pub(crate) struct TypeError;
pub(crate) type InferResult = Result<InferOk, TypeError>;
/// A mismatch between an expected and an inferred type.
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct TypeMismatch {
@ -217,10 +221,8 @@ struct InferenceContext<'a> {
owner: DefWithBodyId,
body: Arc<Body>,
resolver: Resolver,
table: unify::InferenceTable,
table: unify::InferenceTable<'a>,
trait_env: Arc<TraitEnvironment>,
obligations: Vec<DomainGoal>,
last_obligations_check: Option<u32>,
result: InferenceResult,
/// The return type of the function being inferred, or the closure if we're
/// currently within one.
@ -252,15 +254,15 @@ fn find_breakable<'c>(
impl<'a> InferenceContext<'a> {
fn new(db: &'a dyn HirDatabase, owner: DefWithBodyId, resolver: Resolver) -> Self {
let krate = owner.module(db.upcast()).krate();
let trait_env = owner
.as_generic_def_id()
.map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
InferenceContext {
result: InferenceResult::default(),
table: unify::InferenceTable::new(),
obligations: Vec::default(),
last_obligations_check: None,
table: unify::InferenceTable::new(db, trait_env.clone()),
trait_env,
return_ty: TyKind::Error.intern(&Interner), // set in collect_fn_signature
trait_env: owner
.as_generic_def_id()
.map_or_else(Default::default, |d| db.trait_environment(d)),
db,
owner,
body: db.body(owner),
@ -271,19 +273,25 @@ impl<'a> InferenceContext<'a> {
}
fn err_ty(&self) -> Ty {
TyKind::Error.intern(&Interner)
self.result.standard_types.unknown.clone()
}
fn resolve_all(mut self) -> InferenceResult {
// FIXME resolve obligations as well (use Guidance if necessary)
self.table.resolve_obligations_as_possible();
// make sure diverging type variables are marked as such
self.table.propagate_diverging_flag();
let mut result = std::mem::take(&mut self.result);
for ty in result.type_of_expr.values_mut() {
let resolved = self.table.resolve_ty_completely(ty.clone());
*ty = resolved;
*ty = self.table.resolve_ty_completely(ty.clone());
}
for ty in result.type_of_pat.values_mut() {
let resolved = self.table.resolve_ty_completely(ty.clone());
*ty = resolved;
*ty = self.table.resolve_ty_completely(ty.clone());
}
for mismatch in result.type_mismatches.values_mut() {
mismatch.expected = self.table.resolve_ty_completely(mismatch.expected.clone());
mismatch.actual = self.table.resolve_ty_completely(mismatch.actual.clone());
}
result
}
@ -337,6 +345,14 @@ impl<'a> InferenceContext<'a> {
fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
match ty.kind(&Interner) {
TyKind::Error => self.table.new_type_var(),
TyKind::InferenceVar(..) => {
let ty_resolved = self.resolve_ty_shallow(&ty);
if ty_resolved.is_unknown() {
self.table.new_type_var()
} else {
ty
}
}
_ => ty,
}
}
@ -346,66 +362,19 @@ impl<'a> InferenceContext<'a> {
}
fn resolve_obligations_as_possible(&mut self) {
if self.last_obligations_check == Some(self.table.revision) {
// no change
return;
}
let _span = profile::span("resolve_obligations_as_possible");
self.last_obligations_check = Some(self.table.revision);
let obligations = mem::replace(&mut self.obligations, Vec::new());
for obligation in obligations {
let in_env = InEnvironment::new(&self.trait_env.env, obligation.clone());
let canonicalized = self.canonicalizer().canonicalize_obligation(in_env);
let solution =
self.db.trait_solve(self.resolver.krate().unwrap(), canonicalized.value.clone());
match solution {
Some(Solution::Unique(canonical_subst)) => {
canonicalized.apply_solution(
self,
Canonical {
binders: canonical_subst.binders,
// FIXME: handle constraints
value: canonical_subst.value.subst,
},
);
}
Some(Solution::Ambig(Guidance::Definite(substs))) => {
canonicalized.apply_solution(self, substs);
self.obligations.push(obligation);
}
Some(_) => {
// FIXME use this when trying to resolve everything at the end
self.obligations.push(obligation);
}
None => {
// FIXME obligation cannot be fulfilled => diagnostic
}
};
}
self.table.resolve_obligations_as_possible();
}
fn push_obligation(&mut self, o: DomainGoal) {
self.obligations.push(o);
self.last_obligations_check = None;
self.table.register_obligation(o.cast(&Interner));
}
fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
self.table.unify(ty1, ty2)
}
/// Resolves the type as far as currently possible, replacing type variables
/// by their known types. All types returned by the infer_* functions should
/// be resolved as far as possible, i.e. contain no type variables with
/// known type.
fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty {
fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty {
self.resolve_obligations_as_possible();
self.table.resolve_ty_as_possible(ty)
}
fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
self.table.resolve_ty_shallow(ty)
}
@ -439,7 +408,7 @@ impl<'a> InferenceContext<'a> {
};
self.push_obligation(trait_ref.cast(&Interner));
self.push_obligation(alias_eq.cast(&Interner));
self.resolve_ty_as_possible(ty)
ty
}
None => self.err_ty(),
}
@ -452,25 +421,7 @@ impl<'a> InferenceContext<'a> {
/// call). `make_ty` handles this already, but e.g. for field types we need
/// to do it as well.
fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
let ty = self.resolve_ty_as_possible(ty);
fold_tys(
ty,
|ty, _| match ty.kind(&Interner) {
TyKind::Alias(AliasTy::Projection(proj_ty)) => {
self.normalize_projection_ty(proj_ty.clone())
}
_ => ty,
},
DebruijnIndex::INNERMOST,
)
}
fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
let var = self.table.new_type_var();
let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() };
let obligation = alias_eq.cast(&Interner);
self.push_obligation(obligation);
var
self.table.normalize_associated_types_in(ty)
}
fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option<VariantId>) {
@ -720,17 +671,23 @@ impl<'a> InferenceContext<'a> {
/// When inferring an expression, we propagate downward whatever type hint we
/// are able in the form of an `Expectation`.
#[derive(Clone, PartialEq, Eq, Debug)]
struct Expectation {
ty: Ty,
/// See the `rvalue_hint` method.
rvalue_hint: bool,
enum Expectation {
None,
HasType(Ty),
// Castable(Ty), // rustc has this, we currently just don't propagate an expectation for casts
RValueLikeUnsized(Ty),
}
impl Expectation {
/// The expectation that the type of the expression needs to equal the given
/// type.
fn has_type(ty: Ty) -> Self {
Expectation { ty, rvalue_hint: false }
if ty.is_unknown() {
// FIXME: get rid of this?
Expectation::None
} else {
Expectation::HasType(ty)
}
}
/// The following explanation is copied straight from rustc:
@ -754,24 +711,41 @@ impl Expectation {
/// See the test case `test/ui/coerce-expect-unsized.rs` and #20169
/// for examples of where this comes up,.
fn rvalue_hint(ty: Ty) -> Self {
Expectation { ty, rvalue_hint: true }
match ty.strip_references().kind(&Interner) {
TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => Expectation::RValueLikeUnsized(ty),
_ => Expectation::has_type(ty),
}
}
/// This expresses no expectation on the type.
fn none() -> Self {
Expectation {
// FIXME
ty: TyKind::Error.intern(&Interner),
rvalue_hint: false,
Expectation::None
}
fn resolve(&self, table: &mut unify::InferenceTable) -> Expectation {
match self {
Expectation::None => Expectation::None,
Expectation::HasType(t) => Expectation::HasType(table.resolve_ty_shallow(t)),
Expectation::RValueLikeUnsized(t) => {
Expectation::RValueLikeUnsized(table.resolve_ty_shallow(t))
}
}
}
fn coercion_target(&self) -> Ty {
if self.rvalue_hint {
// FIXME
TyKind::Error.intern(&Interner)
} else {
self.ty.clone()
fn to_option(&self, table: &mut unify::InferenceTable) -> Option<Ty> {
match self.resolve(table) {
Expectation::None => None,
Expectation::HasType(t) |
// Expectation::Castable(t) |
Expectation::RValueLikeUnsized(t) => Some(t),
}
}
fn only_has_type(&self, table: &mut unify::InferenceTable) -> Option<Ty> {
match self {
Expectation::HasType(t) => Some(table.resolve_ty_shallow(t)),
// Expectation::Castable(_) |
Expectation::RValueLikeUnsized(_) | Expectation::None => None,
}
}
}

View File

@ -2,156 +2,414 @@
//! 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
//! See https://doc.rust-lang.org/nomicon/coercions.html and
//! librustc_typeck/check/coercion.rs.
use chalk_ir::{cast::Cast, Mutability, TyVariableKind};
use hir_def::lang_item::LangItemTarget;
use hir_def::{expr::ExprId, lang_item::LangItemTarget};
use crate::{autoderef, Canonical, Interner, Solution, Ty, TyBuilder, TyExt, TyKind};
use crate::{
autoderef, infer::TypeMismatch, static_lifetime, Canonical, DomainGoal, FnPointer, FnSig,
Interner, Solution, Substitution, Ty, TyBuilder, TyExt, TyKind,
};
use super::{InEnvironment, InferenceContext};
use super::{InEnvironment, InferOk, InferResult, InferenceContext, TypeError};
impl<'a> InferenceContext<'a> {
/// 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 from_ty = self.resolve_ty_shallow(from_ty);
let to_ty = self.resolve_ty_shallow(to_ty);
self.coerce_inner(from_ty, &to_ty)
match self.coerce_inner(from_ty, &to_ty) {
Ok(result) => {
self.table.register_infer_ok(result);
true
}
Err(_) => {
// FIXME deal with error
false
}
}
}
/// Merge two types from different branches, with possible coercion.
///
/// Mostly this means trying to coerce one to the other, but
/// - if we have two function types for different functions, we need to
/// - if we have two function types for different functions or closures, 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.
pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty {
if self.coerce(ty1, ty2) {
ty2.clone()
} else if self.coerce(ty2, ty1) {
ty1.clone()
} else {
if let (TyKind::FnDef(..), TyKind::FnDef(..)) =
(ty1.kind(&Interner), ty2.kind(&Interner))
{
pub(super) fn coerce_merge_branch(&mut self, id: Option<ExprId>, ty1: &Ty, ty2: &Ty) -> Ty {
let ty1 = self.resolve_ty_shallow(ty1);
let ty2 = self.resolve_ty_shallow(ty2);
// 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 sig = match (ty1.kind(&Interner), ty2.kind(&Interner)) {
(TyKind::FnDef(..), TyKind::FnDef(..))
| (TyKind::Closure(..), TyKind::FnDef(..))
| (TyKind::FnDef(..), TyKind::Closure(..))
| (TyKind::Closure(..), TyKind::Closure(..)) => {
// FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
// we should be coercing the closure to a fn pointer of the safety of the FnDef
cov_mark::hit!(coerce_fn_reification);
// 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");
let ptr_ty1 = TyBuilder::fn_ptr(sig1);
let ptr_ty2 = TyBuilder::fn_ptr(sig2);
self.coerce_merge_branch(&ptr_ty1, &ptr_ty2)
} else {
cov_mark::hit!(coerce_merge_fail_fallback);
ty1.clone()
let sig = ty1.callable_sig(self.db).expect("FnDef without callable sig");
Some(sig)
}
_ => None,
};
if let Some(sig) = sig {
let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(&Interner);
let result1 = self.coerce_inner(ty1.clone(), &target_ty);
let result2 = self.coerce_inner(ty2.clone(), &target_ty);
if let (Ok(result1), Ok(result2)) = (result1, result2) {
self.table.register_infer_ok(result1);
self.table.register_infer_ok(result2);
return target_ty;
}
}
// It might not seem like it, but order is important here: ty1 is our
// "previous" type, ty2 is the "new" one being added. If the previous
// type is a type variable and the new one is `!`, trying it the other
// way around first would mean we make the type variable `!`, instead of
// just marking it as possibly diverging.
if self.coerce(&ty2, &ty1) {
ty1.clone()
} else if self.coerce(&ty1, &ty2) {
ty2.clone()
} else {
if let Some(id) = id {
self.result
.type_mismatches
.insert(id.into(), TypeMismatch { expected: ty1.clone(), actual: ty2.clone() });
}
cov_mark::hit!(coerce_merge_fail_fallback);
ty1.clone()
}
}
fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult {
if from_ty.is_never() {
// Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
// type variable, we want `?T` to fallback to `!` if not
// otherwise constrained. An example where this arises:
//
// let _: Option<?T> = Some({ return; });
//
// here, we would coerce from `!` to `?T`.
match to_ty.kind(&Interner) {
TyKind::InferenceVar(tv, TyVariableKind::General) => {
self.table.set_diverging(*tv, true);
}
_ => {}
}
return Ok(InferOk { goals: Vec::new() });
}
// Consider coercing the subtype to a DST
if let Ok(ret) = self.try_coerce_unsized(&from_ty, &to_ty) {
return Ok(ret);
}
// Examine the supertype and consider auto-borrowing.
match to_ty.kind(&Interner) {
TyKind::Raw(mt, _) => {
return self.coerce_ptr(from_ty, to_ty, *mt);
}
TyKind::Ref(mt, _, _) => {
return self.coerce_ref(from_ty, to_ty, *mt);
}
_ => {}
}
match from_ty.kind(&Interner) {
TyKind::FnDef(..) => {
// Function items are coercible to any closure
// type; function pointers are not (that would
// require double indirection).
// Additionally, we permit coercion of function
// items to drop the unsafe qualifier.
self.coerce_from_fn_item(from_ty, to_ty)
}
TyKind::Function(from_fn_ptr) => {
// We permit coercion of fn pointers to drop the
// unsafe qualifier.
self.coerce_from_fn_pointer(from_ty.clone(), from_fn_ptr, to_ty)
}
TyKind::Closure(_, from_substs) => {
// Non-capturing closures are coercible to
// function pointers or unsafe function pointers.
// It cannot convert closures that require unsafe.
self.coerce_closure_to_fn(from_ty.clone(), from_substs, to_ty)
}
_ => {
// Otherwise, just use unification rules.
self.table.try_unify(&from_ty, to_ty)
}
}
}
fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool {
match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
// Never type will make type variable to fallback to Never Type instead of Unknown.
(TyKind::Never, TyKind::InferenceVar(tv, TyVariableKind::General)) => {
self.table.type_variable_table.set_diverging(*tv, true);
return true;
}
(TyKind::Never, _) => return true,
fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> InferResult {
let (_is_ref, from_mt, from_inner) = match from_ty.kind(&Interner) {
TyKind::Ref(mt, _, ty) => (true, mt, ty),
TyKind::Raw(mt, ty) => (false, mt, ty),
_ => return self.table.try_unify(&from_ty, to_ty),
};
// Trivial cases, this should go after `never` check to
// avoid infer result type to be never
_ => {
if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) {
return true;
}
}
}
coerce_mutabilities(*from_mt, to_mt)?;
// Pointer weakening and function to pointer
match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
// `*mut T` -> `*const T`
(TyKind::Raw(_, inner), TyKind::Raw(m2 @ Mutability::Not, ..)) => {
from_ty = TyKind::Raw(*m2, inner.clone()).intern(&Interner);
}
// `&mut T` -> `&T`
(TyKind::Ref(_, lt, inner), TyKind::Ref(m2 @ Mutability::Not, ..)) => {
from_ty = TyKind::Ref(*m2, lt.clone(), inner.clone()).intern(&Interner);
}
// `&T` -> `*const T`
// `&mut T` -> `*mut T`/`*const T`
(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);
}
// Check that the types which they point at are compatible.
let from_raw = TyKind::Raw(to_mt, from_inner.clone()).intern(&Interner);
// FIXME: behavior differs based on is_ref once we're computing adjustments
self.table.try_unify(&from_raw, to_ty)
}
// Illegal mutability conversion
(TyKind::Raw(Mutability::Not, ..), TyKind::Raw(Mutability::Mut, ..))
| (TyKind::Ref(Mutability::Not, ..), TyKind::Ref(Mutability::Mut, ..)) => return false,
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
/// To match `A` with `B`, autoderef will be performed,
/// calling `deref`/`deref_mut` where necessary.
fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> InferResult {
match from_ty.kind(&Interner) {
TyKind::Ref(mt, _, _) => {
coerce_mutabilities(*mt, to_mt)?;
}
_ => return self.table.try_unify(&from_ty, to_ty),
};
// `{function_type}` -> `fn()`
(TyKind::FnDef(..), TyKind::Function { .. }) => match from_ty.callable_sig(self.db) {
None => return false,
Some(sig) => {
from_ty = TyBuilder::fn_ptr(sig);
}
// NOTE: this code is mostly copied and adapted from rustc, and
// currently more complicated than necessary, carrying errors around
// etc.. This complication will become necessary when we actually track
// details of coercion errors though, so I think it's useful to leave
// the structure like it is.
let canonicalized = self.canonicalize(from_ty.clone());
let autoderef = autoderef::autoderef(
self.db,
self.resolver.krate(),
InEnvironment {
goal: canonicalized.value.clone(),
environment: self.trait_env.env.clone(),
},
);
let mut first_error = None;
let mut found = None;
(TyKind::Closure(.., substs), TyKind::Function { .. }) => {
from_ty = substs.at(&Interner, 0).assert_ty_ref(&Interner).clone();
for (autoderefs, referent_ty) in autoderef.enumerate() {
if autoderefs == 0 {
// Don't let this pass, otherwise it would cause
// &T to autoref to &&T.
continue;
}
_ => {}
}
let referent_ty = canonicalized.decanonicalize_ty(referent_ty.value);
if let Some(ret) = self.try_coerce_unsized(&from_ty, &to_ty) {
return ret;
}
// Auto Deref if cannot coerce
match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
// FIXME: DerefMut
(TyKind::Ref(.., st1), TyKind::Ref(.., st2)) => {
self.unify_autoderef_behind_ref(st1, st2)
// At this point, we have deref'd `a` to `referent_ty`. So
// imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`.
// In the autoderef loop for `&'a mut Vec<T>`, we would get
// three callbacks:
//
// - `&'a mut Vec<T>` -- 0 derefs, just ignore it
// - `Vec<T>` -- 1 deref
// - `[T]` -- 2 deref
//
// At each point after the first callback, we want to
// check to see whether this would match out target type
// (`&'b mut [T]`) if we autoref'd it. We can't just
// compare the referent types, though, because we still
// have to consider the mutability. E.g., in the case
// we've been considering, we have an `&mut` reference, so
// the `T` in `[T]` needs to be unified with equality.
//
// Therefore, we construct reference types reflecting what
// the types will be after we do the final auto-ref and
// compare those. Note that this means we use the target
// mutability [1], since it may be that we are coercing
// from `&mut T` to `&U`.
let lt = static_lifetime(); // FIXME: handle lifetimes correctly, see rustc
let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(&Interner);
match self.table.try_unify(&derefd_from_ty, to_ty) {
Ok(result) => {
found = Some(result);
break;
}
Err(err) => {
if first_error.is_none() {
first_error = Some(err);
}
}
}
}
// Otherwise, normal unify
_ => self.unify(&from_ty, to_ty),
// Extract type or return an error. We return the first error
// we got, which should be from relating the "base" type
// (e.g., in example above, the failure from relating `Vec<T>`
// to the target type), since that should be the least
// confusing.
let result = match found {
Some(d) => d,
None => {
let err = first_error.expect("coerce_borrowed_pointer had no error");
return Err(err);
}
};
Ok(result)
}
/// Attempts to coerce from the type of a Rust function item into a function pointer.
fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult {
match to_ty.kind(&Interner) {
TyKind::Function(_) => {
let from_sig = from_ty.callable_sig(self.db).expect("FnDef had no sig");
// FIXME check ABI: Intrinsics are not coercible to function pointers
// FIXME Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396)
// FIXME rustc normalizes assoc types in the sig here, not sure if necessary
let from_sig = from_sig.to_fn_ptr();
let from_fn_pointer = TyKind::Function(from_sig.clone()).intern(&Interner);
let ok = self.coerce_from_safe_fn(from_fn_pointer, &from_sig, to_ty)?;
Ok(ok)
}
_ => self.table.try_unify(&from_ty, to_ty),
}
}
fn coerce_from_fn_pointer(
&mut self,
from_ty: Ty,
from_f: &FnPointer,
to_ty: &Ty,
) -> InferResult {
self.coerce_from_safe_fn(from_ty, from_f, to_ty)
}
fn coerce_from_safe_fn(
&mut self,
from_ty: Ty,
from_fn_ptr: &FnPointer,
to_ty: &Ty,
) -> InferResult {
if let TyKind::Function(to_fn_ptr) = to_ty.kind(&Interner) {
if let (chalk_ir::Safety::Safe, chalk_ir::Safety::Unsafe) =
(from_fn_ptr.sig.safety, to_fn_ptr.sig.safety)
{
let from_unsafe =
TyKind::Function(safe_to_unsafe_fn_ty(from_fn_ptr.clone())).intern(&Interner);
return self.table.try_unify(&from_unsafe, to_ty);
}
}
self.table.try_unify(&from_ty, to_ty)
}
/// Attempts to coerce from the type of a non-capturing closure into a
/// function pointer.
fn coerce_closure_to_fn(
&mut self,
from_ty: Ty,
from_substs: &Substitution,
to_ty: &Ty,
) -> InferResult {
match to_ty.kind(&Interner) {
TyKind::Function(fn_ty) /* if from_substs is non-capturing (FIXME) */ => {
// We coerce the closure, which has fn type
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
// to
// `fn(arg0,arg1,...) -> _`
// or
// `unsafe fn(arg0,arg1,...) -> _`
let safety = fn_ty.sig.safety;
let pointer_ty = coerce_closure_fn_ty(from_substs, safety);
self.table.try_unify(&pointer_ty, to_ty)
}
_ => self.table.try_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> {
fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult {
// These 'if' statements require some explanation.
// The `CoerceUnsized` trait is special - it is only
// possible to write `impl CoerceUnsized<B> for A` where
// A and B have 'matching' fields. This rules out the following
// two types of blanket impls:
//
// `impl<T> CoerceUnsized<T> for SomeType`
// `impl<T> CoerceUnsized<SomeType> for T`
//
// Both of these trigger a special `CoerceUnsized`-related error (E0376)
//
// We can take advantage of this fact to avoid performing unecessary work.
// If either `source` or `target` is a type variable, then any applicable impl
// would need to be generic over the self-type (`impl<T> CoerceUnsized<SomeType> for T`)
// or generic over the `CoerceUnsized` type parameter (`impl<T> CoerceUnsized<T> for
// SomeType`).
//
// However, these are exactly the kinds of impls which are forbidden by
// the compiler! Therefore, we can be sure that coercion will always fail
// when either the source or target type is a type variable. This allows us
// to skip performing any trait selection, and immediately bail out.
if from_ty.is_ty_var() {
return Err(TypeError);
}
if to_ty.is_ty_var() {
return Err(TypeError);
}
// Handle reborrows before trying to solve `Source: CoerceUnsized<Target>`.
let coerce_from = match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
(TyKind::Ref(from_mt, _, from_inner), TyKind::Ref(to_mt, _, _)) => {
coerce_mutabilities(*from_mt, *to_mt)?;
let lt = static_lifetime();
TyKind::Ref(*to_mt, lt, from_inner.clone()).intern(&Interner)
}
(TyKind::Ref(from_mt, _, from_inner), TyKind::Raw(to_mt, _)) => {
coerce_mutabilities(*from_mt, *to_mt)?;
TyKind::Raw(*to_mt, from_inner.clone()).intern(&Interner)
}
_ => from_ty.clone(),
};
let krate = self.resolver.krate().unwrap();
let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) {
Some(LangItemTarget::TraitId(trait_)) => trait_,
_ => return None,
_ => return Err(TypeError),
};
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;
return Err(TypeError);
}
b.push(from_ty.clone()).push(to_ty.clone()).build()
b.push(coerce_from.clone()).push(to_ty.clone()).build()
};
let goal = InEnvironment::new(&self.trait_env.env, trait_ref.cast(&Interner));
let goal: InEnvironment<DomainGoal> =
InEnvironment::new(&self.trait_env.env, trait_ref.cast(&Interner));
let canonicalizer = self.canonicalizer();
let canonicalized = canonicalizer.canonicalize_obligation(goal);
let canonicalized = self.canonicalize(goal);
let solution = self.db.trait_solve(krate, canonicalized.value.clone())?;
// FIXME: rustc's coerce_unsized is more specialized -- it only tries to
// solve `CoerceUnsized` and `Unsize` goals at this point and leaves the
// rest for later. Also, there's some logic about sized type variables.
// Need to find out in what cases this is necessary
let solution = self
.db
.trait_solve(krate, canonicalized.value.clone().cast(&Interner))
.ok_or(TypeError)?;
match solution {
Solution::Unique(v) => {
canonicalized.apply_solution(
self,
&mut self.table,
Canonical {
binders: v.binders,
// FIXME handle constraints
@ -159,38 +417,40 @@ impl<'a> InferenceContext<'a> {
},
);
}
_ => return None,
// FIXME: should we accept ambiguous results here?
_ => return Err(TypeError),
};
Some(true)
}
/// 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
for derefed_ty in autoderef::autoderef(
self.db,
self.resolver.krate(),
InEnvironment {
goal: canonicalized.value.clone(),
environment: self.trait_env.env.clone(),
},
) {
let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value);
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`.
return self.table.unify(&from_ty, &to_ty);
} else if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) {
return true;
}
}
false
Ok(InferOk { goals: Vec::new() })
}
}
fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty {
let closure_sig = closure_substs.at(&Interner, 0).assert_ty_ref(&Interner).clone();
match closure_sig.kind(&Interner) {
TyKind::Function(fn_ty) => TyKind::Function(FnPointer {
num_binders: fn_ty.num_binders,
sig: FnSig { safety, ..fn_ty.sig },
substitution: fn_ty.substitution.clone(),
})
.intern(&Interner),
_ => TyKind::Error.intern(&Interner),
}
}
fn safe_to_unsafe_fn_ty(fn_ty: FnPointer) -> FnPointer {
FnPointer {
num_binders: fn_ty.num_binders,
sig: FnSig { safety: chalk_ir::Safety::Unsafe, ..fn_ty.sig },
substitution: fn_ty.substitution,
}
}
fn coerce_mutabilities(from: Mutability, to: Mutability) -> Result<(), TypeError> {
match (from, to) {
(Mutability::Mut, Mutability::Mut)
| (Mutability::Mut, Mutability::Not)
| (Mutability::Not, Mutability::Not) => Ok(()),
(Mutability::Not, Mutability::Mut) => Err(TypeError),
}
}

View File

@ -35,39 +35,43 @@ use super::{
impl<'a> InferenceContext<'a> {
pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
let ty = self.infer_expr_inner(tgt_expr, expected);
if ty.is_never() {
if self.resolve_ty_shallow(&ty).is_never() {
// Any expression that produces a value of type `!` must have diverged
self.diverges = Diverges::Always;
}
let could_unify = self.unify(&ty, &expected.ty);
if !could_unify {
self.result.type_mismatches.insert(
tgt_expr.into(),
TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() },
);
if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
let could_unify = self.unify(&ty, &expected_ty);
if !could_unify {
self.result.type_mismatches.insert(
tgt_expr.into(),
TypeMismatch { expected: expected_ty.clone(), actual: ty.clone() },
);
}
}
self.resolve_ty_as_possible(ty)
ty
}
/// Infer type of expression with possibly implicit coerce to the expected type.
/// Return the type after possible coercion.
pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
let ty = self.infer_expr_inner(expr, &expected);
let ty = if !self.coerce(&ty, &expected.coercion_target()) {
self.result.type_mismatches.insert(
expr.into(),
TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() },
);
// Return actual type when type mismatch.
// This is needed for diagnostic when return type mismatch.
ty
} else if expected.coercion_target().is_unknown() {
ty
let ty = if let Some(target) = expected.only_has_type(&mut self.table) {
if !self.coerce(&ty, &target) {
self.result.type_mismatches.insert(
expr.into(),
TypeMismatch { expected: target.clone(), actual: ty.clone() },
);
// Return actual type when type mismatch.
// This is needed for diagnostic when return type mismatch.
ty
} else {
target.clone()
}
} else {
expected.ty.clone()
ty
};
self.resolve_ty_as_possible(ty)
ty
}
fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
@ -98,10 +102,10 @@ impl<'a> InferenceContext<'a> {
goal: projection.trait_ref(self.db).cast(&Interner),
environment: trait_env,
};
let canonical = self.canonicalizer().canonicalize_obligation(obligation.clone());
if self.db.trait_solve(krate, canonical.value).is_some() {
let canonical = self.canonicalize(obligation.clone());
if self.db.trait_solve(krate, canonical.value.cast(&Interner)).is_some() {
self.push_obligation(obligation.goal);
let return_ty = self.normalize_projection_ty(projection);
let return_ty = self.table.normalize_projection_ty(projection);
Some((arg_tys, return_ty))
} else {
None
@ -131,17 +135,21 @@ impl<'a> InferenceContext<'a> {
let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let mut both_arms_diverge = Diverges::Always;
let mut result_ty = self.table.new_type_var();
let then_ty = self.infer_expr_inner(*then_branch, &expected);
both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty);
let else_ty = match else_branch {
Some(else_branch) => self.infer_expr_inner(*else_branch, &expected),
None => TyBuilder::unit(),
};
both_arms_diverge &= self.diverges;
// FIXME: create a synthetic `else {}` so we have something to refer to here instead of None?
result_ty = self.coerce_merge_branch(*else_branch, &result_ty, &else_ty);
self.diverges = condition_diverges | both_arms_diverge;
self.coerce_merge_branch(&then_ty, &else_ty)
result_ty
}
Expr::Block { statements, tail, label, id: _ } => {
let old_resolver = mem::replace(
@ -277,12 +285,13 @@ impl<'a> InferenceContext<'a> {
// Eagerly try to relate the closure type with the expected
// type, otherwise we often won't have enough information to
// infer the body.
self.coerce(&closure_ty, &expected.ty);
if let Some(t) = expected.only_has_type(&mut self.table) {
self.coerce(&closure_ty, &t);
}
// Now go through the argument patterns
for (arg_pat, arg_ty) in args.iter().zip(sig_tys) {
let resolved = self.resolve_ty_as_possible(arg_ty);
self.infer_pat(*arg_pat, &resolved, BindingMode::default());
self.infer_pat(*arg_pat, &arg_ty, BindingMode::default());
}
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
@ -297,13 +306,13 @@ impl<'a> InferenceContext<'a> {
}
Expr::Call { callee, args } => {
let callee_ty = self.infer_expr(*callee, &Expectation::none());
let canonicalized = self.canonicalizer().canonicalize_ty(callee_ty.clone());
let canonicalized = self.canonicalize(callee_ty.clone());
let mut derefs = autoderef(
self.db,
self.resolver.krate(),
InEnvironment {
goal: canonicalized.value.clone(),
environment: self.trait_env.env.clone(),
environment: self.table.trait_env.env.clone(),
},
);
let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs
@ -350,7 +359,7 @@ impl<'a> InferenceContext<'a> {
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
all_arms_diverge &= self.diverges;
result_ty = self.coerce_merge_branch(&result_ty, &arm_ty);
result_ty = self.coerce_merge_branch(Some(arm.expr), &result_ty, &arm_ty);
}
self.diverges = matchee_diverges | all_arms_diverge;
@ -364,12 +373,6 @@ impl<'a> InferenceContext<'a> {
}
Expr::Continue { .. } => TyKind::Never.intern(&Interner),
Expr::Break { expr, label } => {
let val_ty = if let Some(expr) = expr {
self.infer_expr(*expr, &Expectation::none())
} else {
TyBuilder::unit()
};
let last_ty =
if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
ctxt.break_ty.clone()
@ -377,7 +380,14 @@ impl<'a> InferenceContext<'a> {
self.err_ty()
};
let merged_type = self.coerce_merge_branch(&last_ty, &val_ty);
let val_ty = if let Some(expr) = expr {
self.infer_expr(*expr, &Expectation::none())
} else {
TyBuilder::unit()
};
// FIXME: create a synthetic `()` during lowering so we have something to refer to here?
let merged_type = self.coerce_merge_branch(*expr, &last_ty, &val_ty);
if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
ctxt.break_ty = merged_type;
@ -411,7 +421,9 @@ impl<'a> InferenceContext<'a> {
self.write_variant_resolution(tgt_expr.into(), variant);
}
self.unify(&ty, &expected.ty);
if let Some(t) = expected.only_has_type(&mut self.table) {
self.unify(&ty, &t);
}
let substs = ty
.as_adt()
@ -442,7 +454,7 @@ impl<'a> InferenceContext<'a> {
}
Expr::Field { expr, name } => {
let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty);
let canonicalized = self.canonicalize(receiver_ty);
let ty = autoderef::autoderef(
self.db,
self.resolver.krate(),
@ -514,6 +526,7 @@ impl<'a> InferenceContext<'a> {
self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok())
}
Expr::Cast { expr, type_ref } => {
// FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary)
let _inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
let cast_ty = self.make_ty(type_ref);
// FIXME check the cast...
@ -521,15 +534,17 @@ impl<'a> InferenceContext<'a> {
}
Expr::Ref { expr, rawness, mutability } => {
let mutability = lower_to_chalk_mutability(*mutability);
let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) =
&expected.ty.as_reference_or_ptr()
let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = expected
.only_has_type(&mut self.table)
.as_ref()
.and_then(|t| t.as_reference_or_ptr())
{
if *exp_mutability == Mutability::Mut && mutability == Mutability::Not {
// FIXME: throw type error - expected mut reference but found shared ref,
if exp_mutability == Mutability::Mut && mutability == Mutability::Not {
// FIXME: record type error - expected mut reference but found shared ref,
// which cannot be coerced
}
if *exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr {
// FIXME: throw type error - expected reference but found ptr,
if exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr {
// FIXME: record type error - expected reference but found ptr,
// which cannot be coerced
}
Expectation::rvalue_hint(Ty::clone(exp_inner))
@ -556,10 +571,11 @@ impl<'a> InferenceContext<'a> {
}
Expr::UnaryOp { expr, op } => {
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
let inner_ty = self.resolve_ty_shallow(&inner_ty);
match op {
UnaryOp::Deref => match self.resolver.krate() {
Some(krate) => {
let canonicalized = self.canonicalizer().canonicalize_ty(inner_ty);
let canonicalized = self.canonicalize(inner_ty);
match autoderef::deref(
self.db,
krate,
@ -612,8 +628,10 @@ impl<'a> InferenceContext<'a> {
_ => Expectation::none(),
};
let lhs_ty = self.infer_expr(*lhs, &lhs_expectation);
let lhs_ty = self.resolve_ty_shallow(&lhs_ty);
let rhs_expectation = op::binary_op_rhs_expectation(*op, lhs_ty.clone());
let rhs_ty = self.infer_expr(*rhs, &Expectation::has_type(rhs_expectation));
let rhs_ty = self.resolve_ty_shallow(&rhs_ty);
let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone());
@ -676,7 +694,7 @@ impl<'a> InferenceContext<'a> {
if let (Some(index_trait), Some(krate)) =
(self.resolve_ops_index(), self.resolver.krate())
{
let canonicalized = self.canonicalizer().canonicalize_ty(base_ty);
let canonicalized = self.canonicalize(base_ty);
let self_ty = method_resolution::resolve_indexing_op(
self.db,
&canonicalized.value,
@ -696,8 +714,12 @@ impl<'a> InferenceContext<'a> {
}
}
Expr::Tuple { exprs } => {
let mut tys = match expected.ty.kind(&Interner) {
TyKind::Tuple(_, substs) => substs
let mut tys = match expected
.only_has_type(&mut self.table)
.as_ref()
.map(|t| t.kind(&Interner))
{
Some(TyKind::Tuple(_, substs)) => substs
.iter(&Interner)
.map(|a| a.assert_ty_ref(&Interner).clone())
.chain(repeat_with(|| self.table.new_type_var()))
@ -713,14 +735,16 @@ impl<'a> InferenceContext<'a> {
TyKind::Tuple(tys.len(), Substitution::from_iter(&Interner, tys)).intern(&Interner)
}
Expr::Array(array) => {
let elem_ty = match expected.ty.kind(&Interner) {
TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
_ => self.table.new_type_var(),
};
let elem_ty =
match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(&Interner)) {
Some(TyKind::Array(st, _)) | Some(TyKind::Slice(st)) => st.clone(),
_ => self.table.new_type_var(),
};
let len = match array {
Array::ElementList(items) => {
for expr in items.iter() {
// FIXME: use CoerceMany (coerce_merge_branch)
self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone()));
}
Some(items.len() as u64)
@ -785,7 +809,6 @@ impl<'a> InferenceContext<'a> {
};
// use a new type variable if we got unknown here
let ty = self.insert_type_vars_shallow(ty);
let ty = self.resolve_ty_as_possible(ty);
self.write_expr_ty(tgt_expr, ty.clone());
ty
}
@ -813,7 +836,6 @@ impl<'a> InferenceContext<'a> {
}
}
let ty = self.resolve_ty_as_possible(ty);
self.infer_pat(*pat, &ty, BindingMode::default());
}
Statement::Expr { expr, .. } => {
@ -836,7 +858,9 @@ impl<'a> InferenceContext<'a> {
// we don't even make an attempt at coercion
self.table.new_maybe_never_var()
} else {
self.coerce(&TyBuilder::unit(), &expected.coercion_target());
if let Some(t) = expected.only_has_type(&mut self.table) {
self.coerce(&TyBuilder::unit(), &t);
}
TyBuilder::unit()
}
};
@ -852,7 +876,7 @@ impl<'a> InferenceContext<'a> {
generic_args: Option<&GenericArgs>,
) -> Ty {
let receiver_ty = self.infer_expr(receiver, &Expectation::none());
let canonicalized_receiver = self.canonicalizer().canonicalize_ty(receiver_ty.clone());
let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
@ -891,7 +915,8 @@ impl<'a> InferenceContext<'a> {
};
// Apply autoref so the below unification works correctly
// FIXME: return correct autorefs from lookup_method
let actual_receiver_ty = match expected_receiver_ty.as_reference() {
let actual_receiver_ty = match self.resolve_ty_shallow(&expected_receiver_ty).as_reference()
{
Some((_, lifetime, mutability)) => {
TyKind::Ref(mutability, lifetime, derefed_receiver_ty).intern(&Interner)
}
@ -971,6 +996,7 @@ impl<'a> InferenceContext<'a> {
}
fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
let callable_ty = self.resolve_ty_shallow(&callable_ty);
if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(&Interner) {
let def: CallableDefId = from_chalk(self.db, *fn_def);
let generic_predicates = self.db.generic_predicates(def.into());

View File

@ -94,14 +94,15 @@ impl<'a> InferenceContext<'a> {
pub(super) fn infer_pat(
&mut self,
pat: PatId,
mut expected: &Ty,
expected: &Ty,
mut default_bm: BindingMode,
) -> Ty {
let body = Arc::clone(&self.body); // avoid borrow checker problem
let mut expected = self.resolve_ty_shallow(expected);
if is_non_ref_pat(&body, pat) {
while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
expected = inner;
expected = self.resolve_ty_shallow(inner);
default_bm = match default_bm {
BindingMode::Move => BindingMode::Ref(mutability),
BindingMode::Ref(Mutability::Not) => BindingMode::Ref(Mutability::Not),
@ -147,9 +148,9 @@ impl<'a> InferenceContext<'a> {
}
Pat::Or(ref pats) => {
if let Some((first_pat, rest)) = pats.split_first() {
let ty = self.infer_pat(*first_pat, expected, default_bm);
let ty = self.infer_pat(*first_pat, &expected, default_bm);
for pat in rest {
self.infer_pat(*pat, expected, default_bm);
self.infer_pat(*pat, &expected, default_bm);
}
ty
} else {
@ -173,13 +174,13 @@ impl<'a> InferenceContext<'a> {
Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
p.as_deref(),
subpats,
expected,
&expected,
default_bm,
pat,
*ellipsis,
),
Pat::Record { path: p, args: fields, ellipsis: _ } => {
self.infer_record_pat(p.as_deref(), fields, expected, default_bm, pat)
self.infer_record_pat(p.as_deref(), fields, &expected, default_bm, pat)
}
Pat::Path(path) => {
// FIXME use correct resolver for the surrounding expression
@ -193,7 +194,7 @@ impl<'a> InferenceContext<'a> {
BindingMode::convert(*mode)
};
let inner_ty = if let Some(subpat) = subpat {
self.infer_pat(*subpat, expected, default_bm)
self.infer_pat(*subpat, &expected, default_bm)
} else {
expected.clone()
};
@ -206,7 +207,6 @@ impl<'a> InferenceContext<'a> {
}
BindingMode::Move => inner_ty.clone(),
};
let bound_ty = self.resolve_ty_as_possible(bound_ty);
self.write_pat_ty(pat, bound_ty);
return inner_ty;
}
@ -265,13 +265,12 @@ impl<'a> InferenceContext<'a> {
};
// use a new type variable if we got error type here
let ty = self.insert_type_vars_shallow(ty);
if !self.unify(&ty, expected) {
if !self.unify(&ty, &expected) {
self.result.type_mismatches.insert(
pat.into(),
TypeMismatch { expected: expected.clone(), actual: ty.clone() },
);
}
let ty = self.resolve_ty_as_possible(ty);
self.write_pat_ty(pat, ty.clone());
ty
}

View File

@ -65,7 +65,6 @@ impl<'a> InferenceContext<'a> {
let typable: ValueTyDefId = match value {
ValueNs::LocalBinding(pat) => {
let ty = self.result.type_of_pat.get(pat)?.clone();
let ty = self.resolve_ty_as_possible(ty);
return Some(ty);
}
ValueNs::FunctionId(it) => it.into(),
@ -218,14 +217,14 @@ impl<'a> InferenceContext<'a> {
return Some(result);
}
let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone());
let canonical_ty = self.canonicalize(ty.clone());
let krate = self.resolver.krate()?;
let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
method_resolution::iterate_method_candidates(
&canonical_ty.value,
self.db,
self.trait_env.clone(),
self.table.trait_env.clone(),
krate,
&traits_in_scope,
None,
@ -275,6 +274,7 @@ impl<'a> InferenceContext<'a> {
name: &Name,
id: ExprOrPatId,
) -> Option<(ValueNs, Option<Substitution>)> {
let ty = self.resolve_ty_shallow(ty);
let (enum_id, subst) = match ty.as_adt() {
Some((AdtId::EnumId(e), subst)) => (e, subst),
_ => return None,

File diff suppressed because it is too large Load Diff

View File

@ -15,9 +15,15 @@ use std::{fmt, sync::Arc};
#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub struct Interner;
#[derive(PartialEq, Eq, Hash, Debug)]
#[derive(PartialEq, Eq, Hash)]
pub struct InternedWrapper<T>(T);
impl<T: fmt::Debug> fmt::Debug for InternedWrapper<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl<T> std::ops::Deref for InternedWrapper<T> {
type Target = T;
@ -101,66 +107,65 @@ impl chalk_ir::interner::Interner for Interner {
opaque_ty: &chalk_ir::OpaqueTy<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_opaque_ty(opaque_ty, fmt)))
Some(write!(fmt, "{:?}", opaque_ty.opaque_ty_id))
}
fn debug_opaque_ty_id(
opaque_ty_id: chalk_ir::OpaqueTyId<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_opaque_ty_id(opaque_ty_id, fmt)))
Some(fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish())
}
fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt)))
Some(write!(fmt, "{:?}", ty.data(&Interner)))
}
fn debug_lifetime(
lifetime: &chalk_ir::Lifetime<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_lifetime(lifetime, fmt)))
Some(write!(fmt, "{:?}", lifetime.data(&Interner)))
}
fn debug_generic_arg(
parameter: &GenericArg,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_generic_arg(parameter, fmt)))
Some(write!(fmt, "{:?}", parameter.data(&Interner).inner_debug()))
}
fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_goal(goal, fmt)))
let goal_data = goal.data(&Interner);
Some(write!(fmt, "{:?}", goal_data))
}
fn debug_goals(
goals: &chalk_ir::Goals<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_goals(goals, fmt)))
Some(write!(fmt, "{:?}", goals.debug(&Interner)))
}
fn debug_program_clause_implication(
pci: &chalk_ir::ProgramClauseImplication<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_program_clause_implication(pci, fmt)))
Some(write!(fmt, "{:?}", pci.debug(&Interner)))
}
fn debug_substitution(
substitution: &chalk_ir::Substitution<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_substitution(substitution, fmt)))
Some(write!(fmt, "{:?}", substitution.debug(&Interner)))
}
fn debug_separator_trait_ref(
separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| {
Some(prog?.debug_separator_trait_ref(separator_trait_ref, fmt))
})
Some(write!(fmt, "{:?}", separator_trait_ref.debug(&Interner)))
}
fn debug_fn_def_id(
@ -173,47 +178,43 @@ impl chalk_ir::interner::Interner for Interner {
constant: &chalk_ir::Const<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_const(constant, fmt)))
Some(write!(fmt, "{:?}", constant.data(&Interner)))
}
fn debug_variable_kinds(
variable_kinds: &chalk_ir::VariableKinds<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_variable_kinds(variable_kinds, fmt)))
Some(write!(fmt, "{:?}", variable_kinds.as_slice(&Interner)))
}
fn debug_variable_kinds_with_angles(
variable_kinds: &chalk_ir::VariableKinds<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| {
Some(prog?.debug_variable_kinds_with_angles(variable_kinds, fmt))
})
Some(write!(fmt, "{:?}", variable_kinds.inner_debug(&Interner)))
}
fn debug_canonical_var_kinds(
canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| {
Some(prog?.debug_canonical_var_kinds(canonical_var_kinds, fmt))
})
Some(write!(fmt, "{:?}", canonical_var_kinds.as_slice(&Interner)))
}
fn debug_program_clause(
clause: &chalk_ir::ProgramClause<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_program_clause(clause, fmt)))
Some(write!(fmt, "{:?}", clause.data(&Interner)))
}
fn debug_program_clauses(
clauses: &chalk_ir::ProgramClauses<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_program_clauses(clauses, fmt)))
Some(write!(fmt, "{:?}", clauses.as_slice(&Interner)))
}
fn debug_quantified_where_clauses(
clauses: &chalk_ir::QuantifiedWhereClauses<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_quantified_where_clauses(clauses, fmt)))
Some(write!(fmt, "{:?}", clauses.as_slice(&Interner)))
}
fn intern_ty(&self, kind: chalk_ir::TyKind<Self>) -> Self::InternedType {

View File

@ -43,8 +43,9 @@ use hir_def::{
type_ref::{ConstScalar, Rawness},
TypeParamId,
};
use stdx::always;
use crate::{db::HirDatabase, display::HirDisplay, utils::generics};
use crate::{db::HirDatabase, utils::generics};
pub use autoderef::autoderef;
pub use builder::TyBuilder;
@ -113,6 +114,7 @@ pub type FnSig = chalk_ir::FnSig<Interner>;
pub type InEnvironment<T> = chalk_ir::InEnvironment<T>;
pub type DomainGoal = chalk_ir::DomainGoal<Interner>;
pub type Goal = chalk_ir::Goal<Interner>;
pub type AliasEq = chalk_ir::AliasEq<Interner>;
pub type Solution = chalk_solve::Solution<Interner>;
pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>;
@ -167,6 +169,7 @@ pub fn make_canonical<T: HasInterner<Interner = Interner>>(
Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) }
}
// FIXME: get rid of this, just replace it by FnPointer
/// A function signature as seen by type inference: Several parameter types and
/// one return type.
#[derive(Clone, PartialEq, Eq, Debug)]
@ -203,6 +206,17 @@ impl CallableSig {
}
}
pub fn to_fn_ptr(&self) -> FnPointer {
FnPointer {
num_binders: 0,
sig: FnSig { abi: (), safety: Safety::Safe, variadic: self.is_varargs },
substitution: FnSubst(Substitution::from_iter(
&Interner,
self.params_and_return.iter().cloned(),
)),
}
}
pub fn params(&self) -> &[Ty] {
&self.params_and_return[0..self.params_and_return.len() - 1]
}
@ -314,3 +328,58 @@ pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + Fold<Interner>>(
}
t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly")
}
pub fn replace_errors_with_variables<T>(t: T) -> Canonical<T::Result>
where
T: HasInterner<Interner = Interner> + Fold<Interner>,
T::Result: HasInterner<Interner = Interner>,
{
use chalk_ir::{
fold::{Folder, SuperFold},
Fallible,
};
struct ErrorReplacer {
vars: usize,
}
impl<'i> Folder<'i, Interner> for ErrorReplacer {
fn as_dyn(&mut self) -> &mut dyn Folder<'i, Interner> {
self
}
fn interner(&self) -> &'i Interner {
&Interner
}
fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
if let TyKind::Error = ty.kind(&Interner) {
let index = self.vars;
self.vars += 1;
Ok(TyKind::BoundVar(BoundVar::new(outer_binder, index)).intern(&Interner))
} else {
let ty = ty.super_fold_with(self.as_dyn(), outer_binder)?;
Ok(ty)
}
}
fn fold_inference_ty(
&mut self,
var: InferenceVar,
kind: TyVariableKind,
_outer_binder: DebruijnIndex,
) -> Fallible<Ty> {
always!(false);
Ok(TyKind::InferenceVar(var, kind).intern(&Interner))
}
}
let mut error_replacer = ErrorReplacer { vars: 0 };
let value = t
.fold_with(&mut error_replacer, DebruijnIndex::INNERMOST)
.expect("fold failed unexpectedly");
let kinds = (0..error_replacer.vars).map(|_| {
chalk_ir::CanonicalVarKind::new(
chalk_ir::VariableKind::Ty(TyVariableKind::General),
chalk_ir::UniverseIndex::ROOT,
)
});
Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) }
}

View File

@ -1035,9 +1035,11 @@ pub(crate) fn trait_environment_query(
clauses.push(program_clause.into_from_env_clause(&Interner));
}
let krate = def.module(db.upcast()).krate();
let env = chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses);
Arc::new(TraitEnvironment { traits_from_clauses: traits_in_scope, env })
Arc::new(TraitEnvironment { krate, traits_from_clauses: traits_in_scope, env })
}
/// Resolve the where clause(s) of an item with generics.

View File

@ -577,6 +577,7 @@ fn iterate_method_candidates_by_receiver(
if iterate_inherent_methods(
self_ty,
db,
env.clone(),
name,
Some(receiver_ty),
krate,
@ -613,8 +614,16 @@ fn iterate_method_candidates_for_self_ty(
name: Option<&Name>,
mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
) -> bool {
if iterate_inherent_methods(self_ty, db, name, None, krate, visible_from_module, &mut callback)
{
if iterate_inherent_methods(
self_ty,
db,
env.clone(),
name,
None,
krate,
visible_from_module,
&mut callback,
) {
return true;
}
iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback)
@ -653,12 +662,12 @@ fn iterate_trait_method_candidates(
for (_name, item) in data.items.iter() {
// Don't pass a `visible_from_module` down to `is_valid_candidate`,
// since only inherent methods should be included into visibility checking.
if !is_valid_candidate(db, name, receiver_ty, *item, self_ty, None) {
if !is_valid_candidate(db, env.clone(), name, receiver_ty, *item, self_ty, None) {
continue;
}
if !known_implemented {
let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone());
if db.trait_solve(krate, goal).is_none() {
if db.trait_solve(krate, goal.cast(&Interner)).is_none() {
continue 'traits;
}
}
@ -675,6 +684,7 @@ fn iterate_trait_method_candidates(
fn iterate_inherent_methods(
self_ty: &Canonical<Ty>,
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
name: Option<&Name>,
receiver_ty: Option<&Canonical<Ty>>,
krate: CrateId,
@ -690,14 +700,24 @@ fn iterate_inherent_methods(
for &impl_def in impls.for_self_ty(&self_ty.value) {
for &item in db.impl_data(impl_def).items.iter() {
if !is_valid_candidate(db, name, receiver_ty, item, self_ty, visible_from_module) {
if !is_valid_candidate(
db,
env.clone(),
name,
receiver_ty,
item,
self_ty,
visible_from_module,
) {
continue;
}
// we have to check whether the self type unifies with the type
// that the impl is for. If we have a receiver type, this
// already happens in `is_valid_candidate` above; if not, we
// check it here
if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() {
if receiver_ty.is_none()
&& inherent_impl_substs(db, env.clone(), impl_def, self_ty).is_none()
{
cov_mark::hit!(impl_self_type_match_without_receiver);
continue;
}
@ -722,7 +742,7 @@ pub fn resolve_indexing_op(
let deref_chain = autoderef_method_receiver(db, krate, ty);
for ty in deref_chain {
let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone());
if db.trait_solve(krate, goal).is_some() {
if db.trait_solve(krate, goal.cast(&Interner)).is_some() {
return Some(ty);
}
}
@ -731,6 +751,7 @@ pub fn resolve_indexing_op(
fn is_valid_candidate(
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
name: Option<&Name>,
receiver_ty: Option<&Canonical<Ty>>,
item: AssocItemId,
@ -749,7 +770,7 @@ fn is_valid_candidate(
if !data.has_self_param() {
return false;
}
let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) {
let transformed_receiver_ty = match transform_receiver_ty(db, env, m, self_ty) {
Some(ty) => ty,
None => return false,
};
@ -776,6 +797,7 @@ fn is_valid_candidate(
pub(crate) fn inherent_impl_substs(
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
impl_id: ImplId,
self_ty: &Canonical<Ty>,
) -> Option<Substitution> {
@ -798,7 +820,7 @@ pub(crate) fn inherent_impl_substs(
binders: CanonicalVarKinds::from_iter(&Interner, kinds),
value: (self_ty_with_vars, self_ty.value.clone()),
};
let substs = super::infer::unify(&tys)?;
let substs = super::infer::unify(db, env, &tys)?;
// We only want the substs for the vars we added, not the ones from self_ty.
// Also, if any of the vars we added are still in there, we replace them by
// Unknown. I think this can only really happen if self_ty contained
@ -823,6 +845,7 @@ fn fallback_bound_vars(s: Substitution, num_vars_to_keep: usize) -> Substitution
fn transform_receiver_ty(
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
function_id: FunctionId,
self_ty: &Canonical<Ty>,
) -> Option<Ty> {
@ -832,7 +855,7 @@ fn transform_receiver_ty(
.fill_with_unknown()
.build(),
AssocContainerId::ImplId(impl_id) => {
let impl_substs = inherent_impl_substs(db, impl_id, &self_ty)?;
let impl_substs = inherent_impl_substs(db, env, impl_id, &self_ty)?;
TyBuilder::subst_for_def(db, function_id)
.use_parent_substs(&impl_substs)
.fill_with_unknown()
@ -852,7 +875,7 @@ pub fn implements_trait(
trait_: TraitId,
) -> bool {
let goal = generic_implements_goal(db, env, trait_, ty.clone());
let solution = db.trait_solve(krate, goal);
let solution = db.trait_solve(krate, goal.cast(&Interner));
solution.is_some()
}
@ -865,7 +888,7 @@ pub fn implements_trait_unique(
trait_: TraitId,
) -> bool {
let goal = generic_implements_goal(db, env, trait_, ty.clone());
let solution = db.trait_solve(krate, goal);
let solution = db.trait_solve(krate, goal.cast(&Interner));
matches!(solution, Some(crate::Solution::Unique(_)))
}

View File

@ -1,6 +1,6 @@
use expect_test::expect;
use super::{check_infer, check_infer_with_mismatches};
use super::{check_infer, check_infer_with_mismatches, check_types};
#[test]
fn infer_block_expr_type_mismatch() {
@ -858,3 +858,57 @@ fn coerce_unsize_generic() {
"]],
);
}
#[test]
fn infer_two_closures_lub() {
check_types(
r#"
fn foo(c: i32) {
let add = |a: i32, b: i32| a + b;
let sub = |a, b| a - b;
//^ |i32, i32| -> i32
if c > 42 { add } else { sub };
//^ fn(i32, i32) -> i32
}
"#,
)
}
#[test]
fn infer_match_diverging_branch_1() {
check_types(
r#"
enum Result<T> { Ok(T), Err }
fn parse<T>() -> T { loop {} }
fn test() -> i32 {
let a = match parse() {
Ok(val) => val,
Err => return 0,
};
a
//^ i32
}
"#,
)
}
#[test]
fn infer_match_diverging_branch_2() {
// same as 1 except for order of branches
check_types(
r#"
enum Result<T> { Ok(T), Err }
fn parse<T>() -> T { loop {} }
fn test() -> i32 {
let a = match parse() {
Err => return 0,
Ok(val) => val,
};
a
//^ i32
}
"#,
)
}

View File

@ -747,7 +747,7 @@ fn foo(tuple: (u8, i16, f32)) {
209..210 '_': (u8, i16, f32)
214..216 '{}': ()
136..142: expected (u8, i16, f32), got (u8, i16)
170..182: expected (u8, i16, f32), got (u8, i16, f32, _)
170..182: expected (u8, i16, f32), got (u8, i16, f32, {unknown})
"#]],
);
}

View File

@ -86,8 +86,6 @@ fn bug_651() {
#[test]
fn recursive_vars() {
cov_mark::check!(type_var_cycles_resolve_completely);
cov_mark::check!(type_var_cycles_resolve_as_possible);
check_infer(
r#"
fn test() {
@ -97,12 +95,12 @@ fn recursive_vars() {
"#,
expect![[r#"
10..47 '{ ...&y]; }': ()
20..21 'y': &{unknown}
24..31 'unknown': &{unknown}
37..44 '[y, &y]': [&&{unknown}; 2]
38..39 'y': &{unknown}
41..43 '&y': &&{unknown}
42..43 'y': &{unknown}
20..21 'y': {unknown}
24..31 'unknown': {unknown}
37..44 '[y, &y]': [{unknown}; 2]
38..39 'y': {unknown}
41..43 '&y': &{unknown}
42..43 'y': {unknown}
"#]],
);
}
@ -119,19 +117,19 @@ fn recursive_vars_2() {
"#,
expect![[r#"
10..79 '{ ...x)]; }': ()
20..21 'x': &&{unknown}
24..31 'unknown': &&{unknown}
41..42 'y': &&{unknown}
45..52 'unknown': &&{unknown}
58..76 '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown}); 2]
59..65 '(x, y)': (&&&{unknown}, &&&{unknown})
60..61 'x': &&{unknown}
63..64 'y': &&{unknown}
67..75 '(&y, &x)': (&&&{unknown}, &&&{unknown})
68..70 '&y': &&&{unknown}
69..70 'y': &&{unknown}
72..74 '&x': &&&{unknown}
73..74 'x': &&{unknown}
20..21 'x': &{unknown}
24..31 'unknown': &{unknown}
41..42 'y': {unknown}
45..52 'unknown': {unknown}
58..76 '[(x, y..., &x)]': [(&{unknown}, {unknown}); 2]
59..65 '(x, y)': (&{unknown}, {unknown})
60..61 'x': &{unknown}
63..64 'y': {unknown}
67..75 '(&y, &x)': (&{unknown}, {unknown})
68..70 '&y': &{unknown}
69..70 'y': {unknown}
72..74 '&x': &&{unknown}
73..74 'x': &{unknown}
"#]],
);
}
@ -165,7 +163,6 @@ fn infer_std_crash_1() {
#[test]
fn infer_std_crash_2() {
cov_mark::check!(type_var_resolves_to_int_var);
// caused "equating two type variables, ...", taken from std
check_infer(
r#"
@ -257,27 +254,27 @@ fn infer_std_crash_5() {
expect![[r#"
26..322 '{ ... } }': ()
32..320 'for co... }': ()
36..43 'content': &{unknown}
36..43 'content': {unknown}
47..60 'doesnt_matter': {unknown}
61..320 '{ ... }': ()
75..79 'name': &&{unknown}
82..166 'if doe... }': &&{unknown}
75..79 'name': &{unknown}
82..166 'if doe... }': &{unknown}
85..98 'doesnt_matter': bool
99..128 '{ ... }': &&{unknown}
113..118 'first': &&{unknown}
134..166 '{ ... }': &&{unknown}
148..156 '&content': &&{unknown}
149..156 'content': &{unknown}
99..128 '{ ... }': &{unknown}
113..118 'first': &{unknown}
134..166 '{ ... }': &{unknown}
148..156 '&content': &{unknown}
149..156 'content': {unknown}
181..188 'content': &{unknown}
191..313 'if ICE... }': &{unknown}
194..231 'ICE_RE..._VALUE': {unknown}
194..247 'ICE_RE...&name)': bool
241..246 '&name': &&&{unknown}
242..246 'name': &&{unknown}
248..276 '{ ... }': &&{unknown}
262..266 'name': &&{unknown}
282..313 '{ ... }': &{unknown}
296..303 'content': &{unknown}
241..246 '&name': &&{unknown}
242..246 'name': &{unknown}
248..276 '{ ... }': &{unknown}
262..266 'name': &{unknown}
282..313 '{ ... }': {unknown}
296..303 'content': {unknown}
"#]],
);
}

View File

@ -1039,42 +1039,6 @@ fn infer_in_elseif() {
)
}
#[test]
fn infer_closure_unify() {
check_infer(
r#"
fn foo(f: bool) {
let a = |x| x;
let b = |x| x;
let id = if f { a } else { b };
id(123);
}
"#,
expect![[r#"
7..8 'f': bool
16..106 '{ ...23); }': ()
26..27 'a': |i32| -> i32
30..35 '|x| x': |i32| -> i32
31..32 'x': i32
34..35 'x': i32
45..46 'b': |i32| -> i32
49..54 '|x| x': |i32| -> i32
50..51 'x': i32
53..54 'x': i32
64..66 'id': |i32| -> i32
69..90 'if f {... { b }': |i32| -> i32
72..73 'f': bool
74..79 '{ a }': |i32| -> i32
76..77 'a': |i32| -> i32
85..90 '{ b }': |i32| -> i32
87..88 'b': |i32| -> i32
96..98 'id': |i32| -> i32
96..103 'id(123)': i32
99..102 '123': i32
"#]],
)
}
#[test]
fn infer_if_match_with_return() {
check_infer(

View File

@ -3104,7 +3104,7 @@ fn foo() {
568..573 'f(&s)': FnOnce::Output<dyn FnOnce(&Option<i32>), (&Option<i32>,)>
570..572 '&s': &Option<i32>
571..572 's': Option<i32>
549..562: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|_| -> ()>
549..562: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|{unknown}| -> ()>
"#]],
);
}

View File

@ -1,7 +1,7 @@
//! Implementation of Chalk debug helper functions using TLS.
use std::fmt;
use std::fmt::{self, Debug};
use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication};
use chalk_ir::AliasTy;
use itertools::Itertools;
use crate::{
@ -53,14 +53,6 @@ impl DebugContext<'_> {
write!(fmt, "{}::{}", trait_data.name, type_alias_data.name)
}
pub(crate) fn debug_opaque_ty_id(
&self,
opaque_ty_id: chalk_ir::OpaqueTyId<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Result<(), fmt::Error> {
fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish()
}
pub(crate) fn debug_alias(
&self,
alias_ty: &AliasTy<Interner>,
@ -68,7 +60,7 @@ impl DebugContext<'_> {
) -> Result<(), fmt::Error> {
match alias_ty {
AliasTy::Projection(projection_ty) => self.debug_projection_ty(projection_ty, fmt),
AliasTy::Opaque(opaque_ty) => self.debug_opaque_ty(opaque_ty, fmt),
AliasTy::Opaque(opaque_ty) => opaque_ty.fmt(fmt),
}
}
@ -96,79 +88,6 @@ impl DebugContext<'_> {
write!(fmt, ">::{}", type_alias_data.name)
}
pub(crate) fn debug_opaque_ty(
&self,
opaque_ty: &chalk_ir::OpaqueTy<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Result<(), fmt::Error> {
write!(fmt, "{:?}", opaque_ty.opaque_ty_id)
}
pub(crate) fn debug_ty(
&self,
ty: &chalk_ir::Ty<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Result<(), fmt::Error> {
write!(fmt, "{:?}", ty.data(&Interner))
}
pub(crate) fn debug_lifetime(
&self,
lifetime: &Lifetime<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Result<(), fmt::Error> {
write!(fmt, "{:?}", lifetime.data(&Interner))
}
pub(crate) fn debug_generic_arg(
&self,
parameter: &GenericArg<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Result<(), fmt::Error> {
write!(fmt, "{:?}", parameter.data(&Interner).inner_debug())
}
pub(crate) fn debug_goal(
&self,
goal: &Goal<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Result<(), fmt::Error> {
let goal_data = goal.data(&Interner);
write!(fmt, "{:?}", goal_data)
}
pub(crate) fn debug_goals(
&self,
goals: &Goals<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Result<(), fmt::Error> {
write!(fmt, "{:?}", goals.debug(&Interner))
}
pub(crate) fn debug_program_clause_implication(
&self,
pci: &ProgramClauseImplication<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Result<(), fmt::Error> {
write!(fmt, "{:?}", pci.debug(&Interner))
}
pub(crate) fn debug_substitution(
&self,
substitution: &chalk_ir::Substitution<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Result<(), fmt::Error> {
write!(fmt, "{:?}", substitution.debug(&Interner))
}
pub(crate) fn debug_separator_trait_ref(
&self,
separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> Result<(), fmt::Error> {
write!(fmt, "{:?}", separator_trait_ref.debug(&Interner))
}
pub(crate) fn debug_fn_def_id(
&self,
fn_def_id: chalk_ir::FnDefId<Interner>,
@ -190,57 +109,6 @@ impl DebugContext<'_> {
}
}
}
pub(crate) fn debug_const(
&self,
_constant: &chalk_ir::Const<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> fmt::Result {
write!(fmt, "const")
}
pub(crate) fn debug_variable_kinds(
&self,
variable_kinds: &chalk_ir::VariableKinds<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> fmt::Result {
write!(fmt, "{:?}", variable_kinds.as_slice(&Interner))
}
pub(crate) fn debug_variable_kinds_with_angles(
&self,
variable_kinds: &chalk_ir::VariableKinds<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> fmt::Result {
write!(fmt, "{:?}", variable_kinds.inner_debug(&Interner))
}
pub(crate) fn debug_canonical_var_kinds(
&self,
canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> fmt::Result {
write!(fmt, "{:?}", canonical_var_kinds.as_slice(&Interner))
}
pub(crate) fn debug_program_clause(
&self,
clause: &chalk_ir::ProgramClause<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> fmt::Result {
write!(fmt, "{:?}", clause.data(&Interner))
}
pub(crate) fn debug_program_clauses(
&self,
clauses: &chalk_ir::ProgramClauses<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> fmt::Result {
write!(fmt, "{:?}", clauses.as_slice(&Interner))
}
pub(crate) fn debug_quantified_where_clauses(
&self,
clauses: &chalk_ir::QuantifiedWhereClauses<Interner>,
fmt: &mut fmt::Formatter<'_>,
) -> fmt::Result {
write!(fmt, "{:?}", clauses.as_slice(&Interner))
}
}
mod unsafe_tls {

View File

@ -2,7 +2,7 @@
use std::env::var;
use chalk_ir::cast::Cast;
use chalk_ir::GoalData;
use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver};
use base_db::CrateId;
@ -10,7 +10,7 @@ use hir_def::{lang_item::LangItemTarget, TraitId};
use stdx::panic_context;
use crate::{
db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Guidance, HirDisplay, InEnvironment,
db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment,
Interner, Solution, TraitRefExt, Ty, TyKind, WhereClause,
};
@ -38,6 +38,7 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
/// we assume that `T: Default`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TraitEnvironment {
pub krate: CrateId,
// When we're using Chalk's Ty we can make this a BTreeMap since it's Ord,
// but for now it's too annoying...
pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>,
@ -45,6 +46,14 @@ pub struct TraitEnvironment {
}
impl TraitEnvironment {
pub fn empty(krate: CrateId) -> Self {
TraitEnvironment {
krate,
traits_from_clauses: Vec::new(),
env: chalk_ir::Environment::new(&Interner),
}
}
pub(crate) fn traits_in_scope_from_clauses<'a>(
&'a self,
ty: &'a Ty,
@ -59,34 +68,25 @@ impl TraitEnvironment {
}
}
impl Default for TraitEnvironment {
fn default() -> Self {
TraitEnvironment {
traits_from_clauses: Vec::new(),
env: chalk_ir::Environment::new(&Interner),
}
}
}
/// Solve a trait goal using Chalk.
pub(crate) fn trait_solve_query(
db: &dyn HirDatabase,
krate: CrateId,
goal: Canonical<InEnvironment<DomainGoal>>,
goal: Canonical<InEnvironment<Goal>>,
) -> Option<Solution> {
let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal {
DomainGoal::Holds(WhereClause::Implemented(it)) => {
let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal.data(&Interner) {
GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => {
db.trait_data(it.hir_trait_id()).name.to_string()
}
DomainGoal::Holds(WhereClause::AliasEq(_)) => "alias_eq".to_string(),
GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_string(),
_ => "??".to_string(),
});
log::info!("trait_solve_query({})", goal.value.goal.display(db));
log::info!("trait_solve_query({:?})", goal.value.goal);
if let DomainGoal::Holds(WhereClause::AliasEq(AliasEq {
if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(projection_ty),
..
})) = &goal.value.goal
}))) = &goal.value.goal.data(&Interner)
{
if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(&Interner).kind(&Interner) {
// Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
@ -94,11 +94,9 @@ pub(crate) fn trait_solve_query(
}
}
let canonical = goal.cast(&Interner);
// We currently don't deal with universes (I think / hope they're not yet
// relevant for our use cases?)
let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 };
let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 };
solve(db, krate, &u_canonical)
}

View File

@ -323,7 +323,7 @@ fn compute_type_match(
if completion_ty == expected_type {
Some(CompletionRelevanceTypeMatch::Exact)
} else if expected_type.could_unify_with(completion_ty) {
} else if expected_type.could_unify_with(ctx.db, completion_ty) {
Some(CompletionRelevanceTypeMatch::CouldUnify)
} else {
None