From 065d3987012b138b123f0544de193f8bb560b1b5 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 6 Jul 2019 16:41:04 +0200 Subject: Add trait obligations for where clauses when calling functions/methods E.g. if we call `foo>(x)`, that adds an obligation that `x: Into`, etc. --- crates/ra_hir/src/db.rs | 4 +- crates/ra_hir/src/resolve.rs | 12 ++++ crates/ra_hir/src/ty.rs | 4 +- crates/ra_hir/src/ty/infer.rs | 15 ++++- crates/ra_hir/src/ty/lower.rs | 10 ++- crates/ra_hir/src/ty/tests.rs | 134 ++++++++++++++++++++++++++++++++++------- crates/ra_hir/src/ty/traits.rs | 11 +++- 7 files changed, 156 insertions(+), 34 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index a9c6c52d9..0e6e3fdb7 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -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)] diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index c8be27e54..e2a7639b0 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -221,6 +221,18 @@ impl Resolver { pub(crate) fn krate(&self) -> Option { self.module().map(|t| t.0.krate()) } + + pub(crate) fn where_predicates_in_scope<'a>( + &'a self, + ) -> impl Iterator + 'a { + self.scopes + .iter() + .filter_map(|scope| match scope { + Scope::GenericParams(params) => Some(params), + _ => None, + }) + .flat_map(|params| params.where_predicates.iter()) + } } impl Resolver { diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 11afdc0f3..d8c7945e1 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -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; diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index a23daabc2..f5d8cd4b1 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -849,8 +849,19 @@ 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(match def { + // TODO add helper function + CallableDef::Function(f) => f.into(), + CallableDef::Struct(s) => s.into(), + CallableDef::EnumVariant(_e) => unimplemented!(), + }); + 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 +1003,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)); diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 8b1b2a7f9..24755c6aa 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -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); diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 20fa74fb4..aacd94a26 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -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 -[119; 129) 'S(unknown)': S<{unknown}> -[121; 128) 'unknown': {unknown} -[135; 138) 'foo': fn foo>(T) -> () -[135; 141) 'foo(s)': () -[139; 140) 's': S<{unknown}>"### + ⋮ + ⋮[86; 87) 't': T + ⋮[92; 94) '{}': () + ⋮[105; 144) '{ ...(s); }': () + ⋮[115; 116) 's': S + ⋮[119; 120) 'S': S(T) -> S + ⋮[119; 129) 'S(unknown)': S + ⋮[121; 128) 'unknown': u32 + ⋮[135; 138) 'foo': fn foo>(T) -> () + ⋮[135; 141) 'foo(s)': () + ⋮[139; 140) 's': S + "### ); } @@ -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 -[125; 135) 'S(unknown)': S<{unknown}> -[127; 134) 'unknown': {unknown} -[145; 146) 'x': u32 -[154; 157) 'foo': fn foo>(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 + ⋮[125; 126) 'S': S(T) -> S + ⋮[125; 135) 'S(unknown)': S + ⋮[127; 134) 'unknown': u32 + ⋮[145; 146) 'x': u32 + ⋮[154; 157) 'foo': fn foo>(T) -> U + ⋮[154; 160) 'foo(s)': u32 + ⋮[158; 159) 's': S + "### ); } @@ -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 {} +impl Trait for S {} + +fn foo, 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 {} +impl Trait for S {} + +struct O; +impl O { + fn foo, 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 {} +impl Trait for S {} + +impl S { + fn foo(&self) -> U where Self: Trait {} +} + +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 {} +impl Trait<&str> for S {} + +struct O; +impl> O { + fn foo(&self) -> U {} +} + +fn test(o: O) { + 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::(file.syntax(), pos.offset).unwrap(); diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index cff4de316..23a26a971 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -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 { + 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, -- cgit v1.2.3 From f854a29c9adcfeaa7164928ff91daab9ca9a063c Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 6 Jul 2019 17:43:13 +0200 Subject: Make EnumVariant a GenericDef and simplify some code --- crates/ra_hir/src/generics.rs | 12 +++++++++--- crates/ra_hir/src/ty/infer.rs | 7 +------ crates/ra_hir/src/ty/lower.rs | 10 ++++++++++ crates/ra_hir/src/ty/traits/chalk.rs | 6 +----- 4 files changed, 21 insertions(+), 14 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index 9929331d3..521e47090 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -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), } } } diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index f5d8cd4b1..e79e5e223 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -849,12 +849,7 @@ 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(match def { - // TODO add helper function - CallableDef::Function(f) => f.into(), - CallableDef::Struct(s) => s.into(), - CallableDef::EnumVariant(_e) => unimplemented!(), - }); + 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) { diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 24755c6aa..b48ada760 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -509,3 +509,13 @@ pub enum CallableDef { EnumVariant(EnumVariant), } impl_froms!(CallableDef: Function, Struct, EnumVariant); + +impl From for GenericDef { + fn from(def: CallableDef) -> GenericDef { + match def { + CallableDef::Function(f) => f.into(), + CallableDef::Struct(s) => s.into(), + CallableDef::EnumVariant(e) => e.into(), + } + } +} diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 2a0537bc3..4c3744b44 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -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); -- cgit v1.2.3