1496: Add trait obligations for where clauses when calling functions/methods r=matklad a=flodiebold

E.g. if we call `foo<T: Into<u32>>(x)`, that adds an obligation that `x: Into<u32>`, etc., which sometimes allows type inference to make further progress.

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
bors[bot] 2019-07-06 16:28:42 +00:00
commit 9dc9a7a3e2
9 changed files with 171 additions and 42 deletions

View File

@ -163,10 +163,10 @@ pub trait HirDatabase: DefDatabase + AstDatabase {
#[salsa::invoke(crate::ty::callable_item_sig)]
fn callable_item_signature(&self, def: CallableDef) -> FnSig;
#[salsa::invoke(crate::ty::generic_predicates)]
#[salsa::invoke(crate::ty::generic_predicates_query)]
fn generic_predicates(&self, def: GenericDef) -> Arc<[GenericPredicate]>;
#[salsa::invoke(crate::ty::generic_defaults)]
#[salsa::invoke(crate::ty::generic_defaults_query)]
fn generic_defaults(&self, def: GenericDef) -> Substs;
#[salsa::invoke(crate::expr::body_with_source_map_query)]

View File

@ -11,8 +11,8 @@ use crate::{
db::{AstDatabase, DefDatabase, HirDatabase},
path::Path,
type_ref::TypeRef,
AdtDef, AsName, Container, Enum, Function, HasSource, ImplBlock, Name, Struct, Trait,
TypeAlias, Union,
AdtDef, AsName, Container, Enum, EnumVariant, Function, HasSource, ImplBlock, Name, Struct,
Trait, TypeAlias, Union,
};
/// Data about a generic parameter (to a function, struct, impl, ...).
@ -50,8 +50,11 @@ pub enum GenericDef {
Trait(Trait),
TypeAlias(TypeAlias),
ImplBlock(ImplBlock),
// enum variants cannot have generics themselves, but their parent enums
// can, and this makes some code easier to write
EnumVariant(EnumVariant),
}
impl_froms!(GenericDef: Function, Struct, Union, Enum, Trait, TypeAlias, ImplBlock);
impl_froms!(GenericDef: Function, Struct, Union, Enum, Trait, TypeAlias, ImplBlock, EnumVariant);
impl GenericParams {
pub(crate) fn generic_params_query(
@ -62,6 +65,7 @@ impl GenericParams {
let parent = match def {
GenericDef::Function(it) => it.container(db).map(GenericDef::from),
GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from),
GenericDef::EnumVariant(it) => Some(it.parent_enum(db).into()),
GenericDef::Struct(_)
| GenericDef::Union(_)
| GenericDef::Enum(_)
@ -86,6 +90,7 @@ impl GenericParams {
}
GenericDef::TypeAlias(it) => generics.fill(&*it.source(db).ast, start),
GenericDef::ImplBlock(it) => generics.fill(&*it.source(db).ast, start),
GenericDef::EnumVariant(_) => {}
}
Arc::new(generics)
@ -184,6 +189,7 @@ impl GenericDef {
GenericDef::Trait(inner) => inner.resolver(db),
GenericDef::TypeAlias(inner) => inner.resolver(db),
GenericDef::ImplBlock(inner) => inner.resolver(db),
GenericDef::EnumVariant(inner) => inner.parent_enum(db).resolver(db),
}
}
}

View File

@ -221,6 +221,18 @@ impl Resolver {
pub(crate) fn krate(&self) -> Option<Crate> {
self.module().map(|t| t.0.krate())
}
pub(crate) fn where_predicates_in_scope<'a>(
&'a self,
) -> impl Iterator<Item = &'a crate::generics::WherePredicate> + 'a {
self.scopes
.iter()
.filter_map(|scope| match scope {
Scope::GenericParams(params) => Some(params),
_ => None,
})
.flat_map(|params| params.where_predicates.iter())
}
}
impl Resolver {

View File

@ -23,8 +23,8 @@ pub(crate) use autoderef::autoderef;
pub(crate) use infer::{infer_query, InferTy, InferenceResult};
pub use lower::CallableDef;
pub(crate) use lower::{
callable_item_sig, generic_defaults, generic_predicates, type_for_def, type_for_field,
TypableDef,
callable_item_sig, generic_defaults_query, generic_predicates_query, type_for_def,
type_for_field, TypableDef,
};
pub(crate) use traits::ProjectionPredicate;

View File

@ -849,8 +849,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
if let Ty::Apply(a_ty) = callable_ty {
if let TypeCtor::FnDef(def) = a_ty.ctor {
let generic_predicates = self.db.generic_predicates(def.into());
for predicate in generic_predicates.iter() {
let predicate = predicate.clone().subst(&a_ty.parameters);
if let Some(obligation) = Obligation::from_predicate(predicate) {
self.obligations.push(obligation);
}
}
// add obligation for trait implementation, if this is a trait method
// FIXME also register obligations from where clauses from the trait or impl and method
match def {
CallableDef::Function(f) => {
if let Some(trait_) = f.parent_trait(self.db) {
@ -992,7 +998,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
(Vec::new(), Ty::Unknown)
}
};
// FIXME register obligations from where clauses from the function
self.register_obligations_for_call(&callee_ty);
let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
for (arg, param) in args.iter().zip(param_iter) {
self.infer_expr(*arg, &Expectation::has_type(param));

View File

@ -318,15 +318,13 @@ pub(crate) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty {
}
/// Resolve the where clause(s) of an item with generics.
pub(crate) fn generic_predicates(
pub(crate) fn generic_predicates_query(
db: &impl HirDatabase,
def: GenericDef,
) -> Arc<[GenericPredicate]> {
let resolver = def.resolver(db);
let generic_params = def.generic_params(db);
let predicates = generic_params
.where_predicates
.iter()
let predicates = resolver
.where_predicates_in_scope()
.map(|pred| {
TraitRef::for_where_predicate(db, &resolver, pred)
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
@ -336,7 +334,7 @@ pub(crate) fn generic_predicates(
}
/// Resolve the default type params from generics
pub(crate) fn generic_defaults(db: &impl HirDatabase, def: GenericDef) -> Substs {
pub(crate) fn generic_defaults_query(db: &impl HirDatabase, def: GenericDef) -> Substs {
let resolver = def.resolver(db);
let generic_params = def.generic_params(db);
@ -511,3 +509,13 @@ pub enum CallableDef {
EnumVariant(EnumVariant),
}
impl_froms!(CallableDef: Function, Struct, EnumVariant);
impl From<CallableDef> for GenericDef {
fn from(def: CallableDef) -> GenericDef {
match def {
CallableDef::Function(f) => f.into(),
CallableDef::Struct(s) => s.into(),
CallableDef::EnumVariant(e) => e.into(),
}
}
}

View File

@ -2232,16 +2232,18 @@ fn test() {
}
"#),
@r###"
[86; 87) 't': T
[92; 94) '{}': ()
[105; 144) '{ ...(s); }': ()
[115; 116) 's': S<{unknown}>
[119; 120) 'S': S<{unknown}>(T) -> S<T>
[119; 129) 'S(unknown)': S<{unknown}>
[121; 128) 'unknown': {unknown}
[135; 138) 'foo': fn foo<S<{unknown}>>(T) -> ()
[135; 141) 'foo(s)': ()
[139; 140) 's': S<{unknown}>"###
[86; 87) 't': T
[92; 94) '{}': ()
[105; 144) '{ ...(s); }': ()
[115; 116) 's': S<u32>
[119; 120) 'S': S<u32>(T) -> S<T>
[119; 129) 'S(unknown)': S<u32>
[121; 128) 'unknown': u32
[135; 138) 'foo': fn foo<S<u32>>(T) -> ()
[135; 141) 'foo(s)': ()
[139; 140) 's': S<u32>
"###
);
}
@ -2259,17 +2261,19 @@ fn test() {
}
"#),
@r###"
[87; 88) 't': T
[98; 100) '{}': ()
[111; 163) '{ ...(s); }': ()
[121; 122) 's': S<{unknown}>
[125; 126) 'S': S<{unknown}>(T) -> S<T>
[125; 135) 'S(unknown)': S<{unknown}>
[127; 134) 'unknown': {unknown}
[145; 146) 'x': u32
[154; 157) 'foo': fn foo<u32, S<{unknown}>>(T) -> U
[154; 160) 'foo(s)': u32
[158; 159) 's': S<{unknown}>"###
[87; 88) 't': T
[98; 100) '{}': ()
[111; 163) '{ ...(s); }': ()
[121; 122) 's': S<u32>
[125; 126) 'S': S<u32>(T) -> S<T>
[125; 135) 'S(unknown)': S<u32>
[127; 134) 'unknown': u32
[145; 146) 'x': u32
[154; 157) 'foo': fn foo<u32, S<u32>>(T) -> U
[154; 160) 'foo(s)': u32
[158; 159) 's': S<u32>
"###
);
}
@ -2822,6 +2826,94 @@ fn test(s: S) {
assert_eq!(t, "{unknown}");
}
#[test]
fn obligation_from_function_clause() {
let t = type_at(
r#"
//- /main.rs
struct S;
trait Trait<T> {}
impl Trait<u32> for S {}
fn foo<T: Trait<U>, U>(t: T) -> U {}
fn test(s: S) {
foo(s)<|>;
}
"#,
);
assert_eq!(t, "u32");
}
#[test]
fn obligation_from_method_clause() {
let t = type_at(
r#"
//- /main.rs
struct S;
trait Trait<T> {}
impl Trait<isize> for S {}
struct O;
impl O {
fn foo<T: Trait<U>, U>(&self, t: T) -> U {}
}
fn test() {
O.foo(S)<|>;
}
"#,
);
assert_eq!(t, "isize");
}
#[test]
fn obligation_from_self_method_clause() {
let t = type_at(
r#"
//- /main.rs
struct S;
trait Trait<T> {}
impl Trait<i64> for S {}
impl S {
fn foo<U>(&self) -> U where Self: Trait<U> {}
}
fn test() {
S.foo()<|>;
}
"#,
);
assert_eq!(t, "i64");
}
#[test]
fn obligation_from_impl_clause() {
let t = type_at(
r#"
//- /main.rs
struct S;
trait Trait<T> {}
impl Trait<&str> for S {}
struct O<T>;
impl<U, T: Trait<U>> O<T> {
fn foo(&self) -> U {}
}
fn test(o: O<S>) {
o.foo()<|>;
}
"#,
);
assert_eq!(t, "&str");
}
fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
let file = db.parse(pos.file_id).ok().unwrap();
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();

View File

@ -7,7 +7,7 @@ use parking_lot::Mutex;
use ra_prof::profile;
use rustc_hash::FxHashSet;
use super::{Canonical, ProjectionTy, TraitRef, Ty};
use super::{Canonical, GenericPredicate, ProjectionTy, TraitRef, Ty};
use crate::{db::HirDatabase, Crate, ImplBlock, Trait};
use self::chalk::{from_chalk, ToChalk};
@ -78,6 +78,15 @@ pub enum Obligation {
// Projection(ProjectionPredicate),
}
impl Obligation {
pub fn from_predicate(predicate: GenericPredicate) -> Option<Obligation> {
match predicate {
GenericPredicate::Implemented(trait_ref) => Some(Obligation::Trait(trait_ref)),
GenericPredicate::Error => None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ProjectionPredicate {
pub projection_ty: ProjectionTy,

View File

@ -428,11 +428,7 @@ pub(crate) fn struct_datum_query(
CallableDef::Struct(s) => s.module(db).krate(db),
CallableDef::EnumVariant(v) => v.parent_enum(db).module(db).krate(db),
} != Some(krate);
let generic_def: GenericDef = match callable {
CallableDef::Function(f) => f.into(),
CallableDef::Struct(s) => s.into(),
CallableDef::EnumVariant(v) => v.parent_enum(db).into(),
};
let generic_def: GenericDef = callable.into();
let generic_params = generic_def.generic_params(db);
let bound_vars = Substs::bound_vars(&generic_params);
let where_clauses = convert_where_clauses(db, generic_def, &bound_vars);