diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index f7efc1b665..1bb2f9f284 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -820,7 +820,52 @@ impl Trait { self.trait_data(db).items().to_vec() } - pub fn associated_type_by_name(self, db: &impl DefDatabase, name: Name) -> Option { + fn direct_super_traits(self, db: &impl HirDatabase) -> Vec { + let resolver = self.resolver(db); + // returning the iterator directly doesn't easily work because of + // lifetime problems, but since there usually shouldn't be more than a + // few direct traits this should be fine (we could even use some kind of + // SmallVec if performance is a concern) + self.generic_params(db) + .where_predicates + .iter() + .filter_map(|pred| match &pred.type_ref { + TypeRef::Path(p) if p.as_ident() == Some(&crate::name::SELF_TYPE) => { + pred.bound.as_path() + } + _ => None, + }) + .filter_map(|path| { + match resolver.resolve_path_without_assoc_items(db, path).take_types() { + Some(crate::Resolution::Def(ModuleDef::Trait(t))) => Some(t), + _ => None, + } + }) + .collect() + } + + /// Returns an iterator over the whole super trait hierarchy (including the + /// trait itself). + pub fn all_super_traits(self, db: &impl HirDatabase) -> Vec { + // we need to take care a bit here to avoid infinite loops in case of cycles + // (i.e. if we have `trait A: B; trait B: A;`) + let mut result = vec![self]; + let mut i = 0; + while i < result.len() { + let t = result[i]; + // yeah this is quadratic, but trait hierarchies should be flat + // enough that this doesn't matter + for tt in t.direct_super_traits(db) { + if !result.contains(&tt) { + result.push(tt); + } + } + i += 1; + } + result + } + + pub fn associated_type_by_name(self, db: &impl DefDatabase, name: &Name) -> Option { let trait_data = self.trait_data(db); trait_data .items() @@ -829,7 +874,15 @@ impl Trait { TraitItem::TypeAlias(t) => Some(*t), _ => None, }) - .find(|t| t.name(db) == name) + .find(|t| &t.name(db) == name) + } + + pub fn associated_type_by_name_including_super_traits( + self, + db: &impl HirDatabase, + name: &Name, + ) -> Option { + self.all_super_traits(db).into_iter().find_map(|t| t.associated_type_by_name(db, name)) } pub(crate) fn trait_data(self, db: &impl DefDatabase) -> Arc { diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index d6728cc9f4..c76df0698c 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -87,11 +87,15 @@ impl GenericParams { // traits get the Self type as an implicit first type parameter generics.params.push(GenericParam { idx: start, name: SELF_TYPE, default: None }); generics.fill(&it.source(db).ast, start + 1); + // add super traits as bounds on Self + // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar + let self_param = TypeRef::Path(SELF_TYPE.into()); + generics.fill_bounds(&it.source(db).ast, self_param); } GenericDef::TypeAlias(it) => generics.fill(&it.source(db).ast, start), // Note that we don't add `Self` here: in `impl`s, `Self` is not a // type-parameter, but rather is a type-alias for impl's target - // type, so this is handled by the resovler. + // type, so this is handled by the resolver. GenericDef::ImplBlock(it) => generics.fill(&it.source(db).ast, start), GenericDef::EnumVariant(_) => {} } @@ -108,6 +112,14 @@ impl GenericParams { } } + fn fill_bounds(&mut self, node: &impl ast::TypeBoundsOwner, type_ref: TypeRef) { + for bound in + node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds()) + { + self.add_where_predicate_from_bound(bound, type_ref.clone()); + } + } + fn fill_params(&mut self, params: ast::TypeParamList, start: u32) { for (idx, type_param) in params.type_params().enumerate() { let name = type_param.name().map_or_else(Name::missing, |it| it.as_name()); @@ -117,13 +129,7 @@ impl GenericParams { self.params.push(param); let type_ref = TypeRef::Path(name.into()); - for bound in type_param - .type_bound_list() - .iter() - .flat_map(|type_bound_list| type_bound_list.bounds()) - { - self.add_where_predicate_from_bound(bound, type_ref.clone()); - } + self.fill_bounds(&type_param, type_ref); } } diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index 9c4822d917..13bc901bc9 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs @@ -38,11 +38,6 @@ impl Name { Name::new(idx.to_string().into()) } - // Needed for Deref - pub(crate) fn target() -> Name { - Name::new("Target".into()) - } - // There's should be no way to extract a string out of `Name`: `Name` in the // future, `Name` will include hygiene information, and you can't encode // hygiene into a String. @@ -123,6 +118,7 @@ pub(crate) const FUTURE_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, pub(crate) const RESULT_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"result")); pub(crate) const RESULT_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Result")); pub(crate) const OUTPUT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output")); +pub(crate) const TARGET: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Target")); fn resolve_name(text: &SmolStr) -> SmolStr { let raw_start = "r#"; diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 24316fc911..d6c78593bc 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -1,11 +1,11 @@ use std::sync::Arc; use ra_syntax::{ - ast::{self, NameOwner}, + ast::{self, NameOwner, TypeAscriptionOwner}, AstNode, }; -use crate::{type_ref::TypeRef, AsName, Name}; +use crate::{name, type_ref::TypeRef, AsName, Name}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Path { @@ -76,8 +76,16 @@ impl Path { match segment.kind()? { ast::PathSegmentKind::Name(name) => { - let args = - segment.type_arg_list().and_then(GenericArgs::from_ast).map(Arc::new); + let args = segment + .type_arg_list() + .and_then(GenericArgs::from_ast) + .or_else(|| { + GenericArgs::from_fn_like_path_ast( + segment.param_list(), + segment.ret_type(), + ) + }) + .map(Arc::new); let segment = PathSegment { name: name.as_name(), args_and_bindings: args }; segments.push(segment); } @@ -187,6 +195,34 @@ impl GenericArgs { } } + /// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y) + /// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`). + pub(crate) fn from_fn_like_path_ast( + params: Option, + ret_type: Option, + ) -> Option { + let mut args = Vec::new(); + let mut bindings = Vec::new(); + if let Some(params) = params { + let mut param_types = Vec::new(); + for param in params.params() { + let type_ref = TypeRef::from_ast_opt(param.ascribed_type()); + param_types.push(type_ref); + } + let arg = GenericArg::Type(TypeRef::Tuple(param_types)); + args.push(arg); + } + if let Some(ret_type) = ret_type { + let type_ref = TypeRef::from_ast_opt(ret_type.type_ref()); + bindings.push((name::OUTPUT, type_ref)) + } + if args.is_empty() && bindings.is_empty() { + None + } else { + Some(GenericArgs { args, has_self_type: false, bindings }) + } + } + pub(crate) fn empty() -> GenericArgs { GenericArgs { args: Vec::new(), has_self_type: false, bindings: Vec::new() } } diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs index 08f52a53bc..caa17f64ea 100644 --- a/crates/ra_hir/src/ty/autoderef.rs +++ b/crates/ra_hir/src/ty/autoderef.rs @@ -8,7 +8,7 @@ use std::iter::successors; use log::{info, warn}; use super::{traits::Solution, Canonical, Ty, TypeWalk}; -use crate::{HasGenericParams, HirDatabase, Name, Resolver}; +use crate::{name, HasGenericParams, HirDatabase, Resolver}; const AUTODEREF_RECURSION_LIMIT: usize = 10; @@ -42,7 +42,7 @@ fn deref_by_trait( crate::lang_item::LangItemTarget::Trait(t) => t, _ => return None, }; - let target = deref_trait.associated_type_by_name(db, Name::target())?; + let target = deref_trait.associated_type_by_name(db, &name::TARGET)?; if target.generic_params(db).count_params_including_parent() != 1 { // the Target type + Deref trait should only have one generic parameter, diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index ec3b7ffeff..0e6ebd3652 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -1453,7 +1453,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { match self.resolver.resolve_path_segments(self.db, &into_iter_path).into_fully_resolved() { PerNs { types: Some(Def(Trait(trait_))), .. } => { - Some(trait_.associated_type_by_name(self.db, name::ITEM)?) + Some(trait_.associated_type_by_name(self.db, &name::ITEM)?) } _ => None, } @@ -1471,7 +1471,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { match self.resolver.resolve_path_segments(self.db, &ops_try_path).into_fully_resolved() { PerNs { types: Some(Def(Trait(trait_))), .. } => { - Some(trait_.associated_type_by_name(self.db, name::OK)?) + Some(trait_.associated_type_by_name(self.db, &name::OK)?) } _ => None, } @@ -1493,7 +1493,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { .into_fully_resolved() { PerNs { types: Some(Def(Trait(trait_))), .. } => { - Some(trait_.associated_type_by_name(self.db, name::OUTPUT)?) + Some(trait_.associated_type_by_name(self.db, &name::OUTPUT)?) } _ => None, } diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index f6f0137cff..480bae7401 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -132,14 +132,16 @@ impl Ty { if let Some(remaining_index) = remaining_index { if remaining_index == path.segments.len() - 1 { let segment = &path.segments[remaining_index]; - let associated_ty = - match trait_ref.trait_.associated_type_by_name(db, segment.name.clone()) { - Some(t) => t, - None => { - // associated type not found - return Ty::Unknown; - } - }; + let associated_ty = match trait_ref + .trait_ + .associated_type_by_name_including_super_traits(db, &segment.name) + { + Some(t) => t, + None => { + // associated type not found + return Ty::Unknown; + } + }; // FIXME handle type parameters on the segment Ty::Projection(ProjectionTy { associated_ty, parameters: trait_ref.substs }) } else { @@ -387,10 +389,11 @@ fn assoc_type_bindings_from_type_bound<'a>( .flat_map(|segment| segment.args_and_bindings.iter()) .flat_map(|args_and_bindings| args_and_bindings.bindings.iter()) .map(move |(name, type_ref)| { - let associated_ty = match trait_ref.trait_.associated_type_by_name(db, name.clone()) { - None => return GenericPredicate::Error, - Some(t) => t, - }; + let associated_ty = + match trait_ref.trait_.associated_type_by_name_including_super_traits(db, &name) { + None => return GenericPredicate::Error, + Some(t) => t, + }; let projection_ty = ProjectionTy { associated_ty, parameters: trait_ref.substs.clone() }; let ty = Ty::from_hir(db, resolver, type_ref); diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 9873a0440b..cf787bdaad 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -212,7 +212,13 @@ fn iterate_trait_method_candidates( // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) let env = lower::trait_env(db, resolver); // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope - let traits = ty.value.inherent_trait().into_iter().chain(resolver.traits_in_scope(db)); + let inherent_trait = ty.value.inherent_trait().into_iter(); + // if we have `T: Trait` in the param env, the trait doesn't need to be in scope + let traits_from_env = env + .trait_predicates_for_self_ty(&ty.value) + .map(|tr| tr.trait_) + .flat_map(|t| t.all_super_traits(db)); + let traits = inherent_trait.chain(traits_from_env).chain(resolver.traits_in_scope(db)); 'traits: for t in traits { let data = t.trait_data(db); diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index d92d4659b2..c4bddde85d 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3643,6 +3643,231 @@ fn test>(x: T) { "### ); } + +#[test] +fn where_clause_trait_in_scope_for_method_resolution() { + let t = type_at( + r#" +//- /main.rs +mod foo { + trait Trait { + fn foo(&self) -> u32 {} + } +} + +fn test(x: T) { + x.foo()<|>; +} +"#, + ); + assert_eq!(t, "u32"); +} + +#[test] +fn super_trait_method_resolution() { + assert_snapshot!( + infer(r#" +mod foo { + trait SuperTrait { + fn foo(&self) -> u32 {} + } +} +trait Trait1: foo::SuperTrait {} +trait Trait2 where Self: foo::SuperTrait {} + +fn test(x: T, y: U) { + x.foo(); + y.foo(); +} +"#), + @r###" + [50; 54) 'self': &Self + [63; 65) '{}': () + [182; 183) 'x': T + [188; 189) 'y': U + [194; 223) '{ ...o(); }': () + [200; 201) 'x': T + [200; 207) 'x.foo()': u32 + [213; 214) 'y': U + [213; 220) 'y.foo()': u32 + "### + ); +} + +#[test] +fn super_trait_cycle() { + // This just needs to not crash + assert_snapshot!( + infer(r#" +trait A: B {} +trait B: A {} + +fn test(x: T) { + x.foo(); +} +"#), + @r###" + [44; 45) 'x': T + [50; 66) '{ ...o(); }': () + [56; 57) 'x': T + [56; 63) 'x.foo()': {unknown} + "### + ); +} + +#[test] +fn super_trait_assoc_type_bounds() { + assert_snapshot!( + infer(r#" +trait SuperTrait { type Type; } +trait Trait where Self: SuperTrait {} + +fn get2>(t: T) -> U {} +fn set>(t: T) -> T {t} + +struct S; +impl SuperTrait for S { type Type = T; } +impl Trait for S {} + +fn test() { + get2(set(S)); +} +"#), + @r###" + [103; 104) 't': T + [114; 116) '{}': () + [146; 147) 't': T + [157; 160) '{t}': T + [158; 159) 't': T + [259; 280) '{ ...S)); }': () + [265; 269) 'get2': fn get2>(T) -> U + [265; 277) 'get2(set(S))': u64 + [270; 273) 'set': fn set>(T) -> T + [270; 276) 'set(S)': S + [274; 275) 'S': S + "### + ); +} + +#[test] +fn fn_trait() { + assert_snapshot!( + infer(r#" +trait FnOnce { + type Output; + + fn call_once(self, args: Args) -> >::Output; +} + +fn test u128>(f: F) { + f.call_once((1, 2)); +} +"#), + @r###" + [57; 61) 'self': Self + [63; 67) 'args': Args + [150; 151) 'f': F + [156; 184) '{ ...2)); }': () + [162; 163) 'f': F + [162; 181) 'f.call...1, 2))': {unknown} + [174; 180) '(1, 2)': (u32, u64) + [175; 176) '1': u32 + [178; 179) '2': u64 + "### + ); +} + +#[test] +fn closure_1() { + assert_snapshot!( + infer(r#" +trait FnOnce { + type Output; +} + +enum Option { Some(T), None } +impl Option { + fn map U>(self, f: F) -> U {} +} + +fn test() { + let x = Option::Some(1i32); + x.map(|v| v + 1); + x.map(|_v| 1u64); + let y: Option = x.map(|_v| 1); +} +"#), + @r###" + [128; 132) 'self': Option + [134; 135) 'f': F + [145; 147) '{}': () + [161; 280) '{ ... 1); }': () + [171; 172) 'x': Option + [175; 187) 'Option::Some': Some(T) -> Option + [175; 193) 'Option...(1i32)': Option + [188; 192) '1i32': i32 + [199; 200) 'x': Option + [199; 215) 'x.map(...v + 1)': {unknown} + [205; 214) '|v| v + 1': {unknown} + [206; 207) 'v': {unknown} + [209; 210) 'v': {unknown} + [209; 214) 'v + 1': i32 + [213; 214) '1': i32 + [221; 222) 'x': Option + [221; 237) 'x.map(... 1u64)': {unknown} + [227; 236) '|_v| 1u64': {unknown} + [228; 230) '_v': {unknown} + [232; 236) '1u64': u64 + [247; 248) 'y': Option + [264; 265) 'x': Option + [264; 277) 'x.map(|_v| 1)': Option + [270; 276) '|_v| 1': {unknown} + [271; 273) '_v': {unknown} + [275; 276) '1': i32 + "### + ); +} + +#[test] +fn closure_2() { + assert_snapshot!( + infer(r#" +trait FnOnce { + type Output; +} + +fn test u64>(f: F) { + f(1); + let g = |v| v + 1; + g(1u64); + let h = |v| 1u128 + v; +} +"#), + @r###" + [73; 74) 'f': F + [79; 155) '{ ...+ v; }': () + [85; 86) 'f': F + [85; 89) 'f(1)': {unknown} + [87; 88) '1': i32 + [99; 100) 'g': {unknown} + [103; 112) '|v| v + 1': {unknown} + [104; 105) 'v': {unknown} + [107; 108) 'v': {unknown} + [107; 112) 'v + 1': i32 + [111; 112) '1': i32 + [118; 119) 'g': {unknown} + [118; 125) 'g(1u64)': {unknown} + [120; 124) '1u64': u64 + [135; 136) 'h': {unknown} + [139; 152) '|v| 1u128 + v': {unknown} + [140; 141) 'v': u128 + [143; 148) '1u128': u128 + [143; 152) '1u128 + v': u128 + [151; 152) 'v': u128 + "### + ); +} + 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 6e0271a966..c0c132809b 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -96,6 +96,21 @@ pub struct TraitEnvironment { pub predicates: Vec, } +impl TraitEnvironment { + /// Returns trait refs with the given self type which are supposed to hold + /// in this trait env. E.g. if we are in `foo()`, this will + /// find that `T: SomeTrait` if we call it for `T`. + pub(crate) fn trait_predicates_for_self_ty<'a>( + &'a self, + ty: &'a Ty, + ) -> impl Iterator + 'a { + self.predicates.iter().filter_map(move |pred| match pred { + GenericPredicate::Implemented(tr) if tr.self_ty() == ty => Some(tr), + _ => None, + }) + } +} + /// Something (usually a goal), along with an environment. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct InEnvironment { diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index c201c5e50e..8a127efa19 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -314,7 +314,8 @@ impl ToChalk for Arc { // for env, we just ignore errors continue; } - clauses.push(pred.clone().to_chalk(db).cast()); + let program_clause: chalk_ir::ProgramClause = pred.clone().to_chalk(db).cast(); + clauses.push(program_clause.into_from_env_clause()); } chalk_ir::Environment::new().add_clauses(clauses) } @@ -636,7 +637,7 @@ pub(crate) fn impl_datum_query( _ => None, }) .filter_map(|t| { - let assoc_ty = trait_.associated_type_by_name(db, t.name(db))?; + let assoc_ty = trait_.associated_type_by_name(db, &t.name(db))?; let ty = db.type_for_def(t.into(), crate::Namespace::Types).subst(&bound_vars); Some(chalk_rust_ir::AssociatedTyValue { impl_id, diff --git a/crates/ra_hir/src/type_ref.rs b/crates/ra_hir/src/type_ref.rs index fa91bfb22f..bc8acc7eed 100644 --- a/crates/ra_hir/src/type_ref.rs +++ b/crates/ra_hir/src/type_ref.rs @@ -150,4 +150,11 @@ impl TypeBound { ast::TypeBoundKind::ForType(_) | ast::TypeBoundKind::Lifetime(_) => TypeBound::Error, } } + + pub fn as_path(&self) -> Option<&Path> { + match self { + TypeBound::Path(p) => Some(p), + _ => None, + } + } } diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index bcf753f784..d274b6fbc0 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -2312,6 +2312,12 @@ impl PathSegment { pub fn type_arg_list(&self) -> Option { AstChildren::new(&self.syntax).next() } + pub fn param_list(&self) -> Option { + AstChildren::new(&self.syntax).next() + } + pub fn ret_type(&self) -> Option { + AstChildren::new(&self.syntax).next() + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PathType { diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 3e6c2d3f3c..993e58e645 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -684,7 +684,7 @@ Grammar( ] ), "PathSegment": ( - options: [ "NameRef", "TypeArgList" ] + options: [ "NameRef", "TypeArgList", "ParamList", "RetType" ] ), "TypeArgList": (collections: [ ("type_args", "TypeArg"),