Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add trait obligations for where clauses when calling functions/methods #1496

Merged
merged 2 commits into from Jul 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/ra_hir/src/db.rs
Expand Up @@ -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)]
Expand Down
12 changes: 9 additions & 3 deletions crates/ra_hir/src/generics.rs
Expand Up @@ -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, ...).
Expand Down Expand Up @@ -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(
Expand All @@ -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(_)
Expand All @@ -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)
Expand Down Expand Up @@ -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),
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions crates/ra_hir/src/resolve.rs
Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions crates/ra_hir/src/ty.rs
Expand Up @@ -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;

Expand Down
10 changes: 8 additions & 2 deletions crates/ra_hir/src/ty/infer.rs
Expand Up @@ -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) {
Expand Down Expand Up @@ -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));
Expand Down
20 changes: 14 additions & 6 deletions crates/ra_hir/src/ty/lower.rs
Expand Up @@ -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)
Expand All @@ -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);

Expand Down Expand Up @@ -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(),
}
}
}
134 changes: 113 additions & 21 deletions crates/ra_hir/src/ty/tests.rs
Expand Up @@ -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>
"###
);
}

Expand All @@ -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>
"###
);
}

Expand Down Expand Up @@ -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();
Expand Down
11 changes: 10 additions & 1 deletion crates/ra_hir/src/ty/traits.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 1 addition & 5 deletions crates/ra_hir/src/ty/traits/chalk.rs
Expand Up @@ -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);
Expand Down