aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/ty
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/ty')
-rw-r--r--crates/ra_hir/src/ty/lower.rs97
-rw-r--r--crates/ra_hir/src/ty/tests.rs64
2 files changed, 144 insertions, 17 deletions
diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs
index a83842b0f..8d71abc95 100644
--- a/crates/ra_hir/src/ty/lower.rs
+++ b/crates/ra_hir/src/ty/lower.rs
@@ -86,6 +86,35 @@ impl Ty {
86 } 86 }
87 } 87 }
88 88
89 /// This is only for `generic_predicates_for_param`, where we can't just
90 /// lower the self types of the predicates since that could lead to cycles.
91 /// So we just check here if the `type_ref` resolves to a generic param, and which.
92 fn from_hir_only_param(
93 db: &impl HirDatabase,
94 resolver: &Resolver,
95 type_ref: &TypeRef,
96 ) -> Option<u32> {
97 let path = match type_ref {
98 TypeRef::Path(path) => path,
99 _ => return None,
100 };
101 if let crate::PathKind::Type(_) = &path.kind {
102 return None;
103 }
104 if path.segments.len() > 1 {
105 return None;
106 }
107 let resolution = match resolver.resolve_path_in_type_ns(db, path) {
108 Some((it, None)) => it,
109 _ => return None,
110 };
111 if let TypeNs::GenericParam(idx) = resolution {
112 Some(idx)
113 } else {
114 None
115 }
116 }
117
89 pub(crate) fn from_type_relative_path( 118 pub(crate) fn from_type_relative_path(
90 db: &impl HirDatabase, 119 db: &impl HirDatabase,
91 resolver: &Resolver, 120 resolver: &Resolver,
@@ -189,11 +218,37 @@ impl Ty {
189 } 218 }
190 219
191 fn select_associated_type( 220 fn select_associated_type(
192 _db: &impl HirDatabase, 221 db: &impl HirDatabase,
193 _resolver: &Resolver, 222 resolver: &Resolver,
194 _self_ty: Ty, 223 self_ty: Ty,
195 _segment: &PathSegment, 224 segment: &PathSegment,
196 ) -> Ty { 225 ) -> Ty {
226 let param_idx = match self_ty {
227 Ty::Param { idx, .. } => idx,
228 _ => return Ty::Unknown, // Error: Ambiguous associated type
229 };
230 let def = match resolver.generic_def() {
231 Some(def) => def,
232 None => return Ty::Unknown, // this can't actually happen
233 };
234 let predicates = db.generic_predicates_for_param(def, param_idx);
235 let traits_from_env = predicates.iter().filter_map(|pred| match pred {
236 GenericPredicate::Implemented(tr) if tr.self_ty() == &self_ty => Some(tr.trait_),
237 _ => None,
238 });
239 let traits = traits_from_env.flat_map(|t| t.all_super_traits(db));
240 for t in traits {
241 if let Some(associated_ty) = t.associated_type_by_name(db, &segment.name) {
242 let generics = t.generic_params(db);
243 let mut substs = Vec::new();
244 substs.push(self_ty.clone());
245 substs.extend(
246 iter::repeat(Ty::Unknown).take(generics.count_params_including_parent() - 1),
247 );
248 // FIXME handle type parameters on the segment
249 return Ty::Projection(ProjectionTy { associated_ty, parameters: substs.into() });
250 }
251 }
197 Ty::Unknown 252 Ty::Unknown
198 } 253 }
199 254
@@ -269,9 +324,10 @@ pub(super) fn substs_from_path_segment(
269 add_self_param: bool, 324 add_self_param: bool,
270) -> Substs { 325) -> Substs {
271 let mut substs = Vec::new(); 326 let mut substs = Vec::new();
272 let def_generics = def_generic.map(|def| def.generic_params(db)).unwrap_or_default(); 327 let def_generics = def_generic.map(|def| def.generic_params(db));
273 328
274 let parent_param_count = def_generics.count_parent_params(); 329 let (parent_param_count, param_count) =
330 def_generics.map_or((0, 0), |g| (g.count_parent_params(), g.params.len()));
275 substs.extend(iter::repeat(Ty::Unknown).take(parent_param_count)); 331 substs.extend(iter::repeat(Ty::Unknown).take(parent_param_count));
276 if add_self_param { 332 if add_self_param {
277 // FIXME this add_self_param argument is kind of a hack: Traits have the 333 // FIXME this add_self_param argument is kind of a hack: Traits have the
@@ -283,7 +339,7 @@ pub(super) fn substs_from_path_segment(
283 if let Some(generic_args) = &segment.args_and_bindings { 339 if let Some(generic_args) = &segment.args_and_bindings {
284 // if args are provided, it should be all of them, but we can't rely on that 340 // if args are provided, it should be all of them, but we can't rely on that
285 let self_param_correction = if add_self_param { 1 } else { 0 }; 341 let self_param_correction = if add_self_param { 1 } else { 0 };
286 let param_count = def_generics.params.len() - self_param_correction; 342 let param_count = param_count - self_param_correction;
287 for arg in generic_args.args.iter().take(param_count) { 343 for arg in generic_args.args.iter().take(param_count) {
288 match arg { 344 match arg {
289 GenericArg::Type(type_ref) => { 345 GenericArg::Type(type_ref) => {
@@ -295,10 +351,10 @@ pub(super) fn substs_from_path_segment(
295 } 351 }
296 // add placeholders for args that were not provided 352 // add placeholders for args that were not provided
297 let supplied_params = substs.len(); 353 let supplied_params = substs.len();
298 for _ in supplied_params..def_generics.count_params_including_parent() { 354 for _ in supplied_params..parent_param_count + param_count {
299 substs.push(Ty::Unknown); 355 substs.push(Ty::Unknown);
300 } 356 }
301 assert_eq!(substs.len(), def_generics.count_params_including_parent()); 357 assert_eq!(substs.len(), parent_param_count + param_count);
302 358
303 // handle defaults 359 // handle defaults
304 if let Some(def_generic) = def_generic { 360 if let Some(def_generic) = def_generic {
@@ -491,6 +547,29 @@ pub(crate) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty {
491 Ty::from_hir(db, &resolver, type_ref) 547 Ty::from_hir(db, &resolver, type_ref)
492} 548}
493 549
550/// This query exists only to be used when resolving short-hand associated types
551/// like `T::Item`.
552///
553/// See the analogous query in rustc and its comment:
554/// https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46
555/// This is a query mostly to handle cycles somewhat gracefully; e.g. the
556/// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but
557/// these are fine: `T: Foo<U::Item>, U: Foo<()>`.
558pub(crate) fn generic_predicates_for_param_query(
559 db: &impl HirDatabase,
560 def: GenericDef,
561 param_idx: u32,
562) -> Arc<[GenericPredicate]> {
563 let resolver = def.resolver(db);
564 let predicates = resolver
565 .where_predicates_in_scope()
566 // we have to filter out all other predicates *first*, before attempting to lower them
567 .filter(|pred| Ty::from_hir_only_param(db, &resolver, &pred.type_ref) == Some(param_idx))
568 .flat_map(|pred| GenericPredicate::from_where_predicate(db, &resolver, pred))
569 .collect::<Vec<_>>();
570 predicates.into()
571}
572
494pub(crate) fn trait_env( 573pub(crate) fn trait_env(
495 db: &impl HirDatabase, 574 db: &impl HirDatabase,
496 resolver: &Resolver, 575 resolver: &Resolver,
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 3b0a99460..3ac1fbdd5 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -2740,17 +2740,17 @@ fn test() {
2740 [202; 203) 't': T 2740 [202; 203) 't': T
2741 [221; 223) '{}': () 2741 [221; 223) '{}': ()
2742 [234; 300) '{ ...(S); }': () 2742 [234; 300) '{ ...(S); }': ()
2743 [244; 245) 'x': {unknown} 2743 [244; 245) 'x': u32
2744 [248; 252) 'foo1': fn foo1<S>(T) -> {unknown} 2744 [248; 252) 'foo1': fn foo1<S>(T) -> <T as Iterable>::Item
2745 [248; 255) 'foo1(S)': {unknown} 2745 [248; 255) 'foo1(S)': u32
2746 [253; 254) 'S': S 2746 [253; 254) 'S': S
2747 [265; 266) 'y': u32 2747 [265; 266) 'y': u32
2748 [269; 273) 'foo2': fn foo2<S>(T) -> <T as Iterable>::Item 2748 [269; 273) 'foo2': fn foo2<S>(T) -> <T as Iterable>::Item
2749 [269; 276) 'foo2(S)': u32 2749 [269; 276) 'foo2(S)': u32
2750 [274; 275) 'S': S 2750 [274; 275) 'S': S
2751 [286; 287) 'z': {unknown} 2751 [286; 287) 'z': u32
2752 [290; 294) 'foo3': fn foo3<S>(T) -> {unknown} 2752 [290; 294) 'foo3': fn foo3<S>(T) -> <T as Iterable>::Item
2753 [290; 297) 'foo3(S)': {unknown} 2753 [290; 297) 'foo3(S)': u32
2754 [295; 296) 'S': S 2754 [295; 296) 'S': S
2755 "### 2755 "###
2756 ); 2756 );
@@ -4080,7 +4080,7 @@ fn test<F: FnOnce(u32) -> u64>(f: F) {
4080} 4080}
4081 4081
4082#[test] 4082#[test]
4083fn unselected_projection_in_trait_env() { 4083fn unselected_projection_in_trait_env_1() {
4084 let t = type_at( 4084 let t = type_at(
4085 r#" 4085 r#"
4086//- /main.rs 4086//- /main.rs
@@ -4102,7 +4102,33 @@ fn test<T: Trait>() where T::Item: Trait2 {
4102} 4102}
4103 4103
4104#[test] 4104#[test]
4105fn unselected_projection_in_trait_env_cycle() { 4105fn unselected_projection_in_trait_env_2() {
4106 let t = type_at(
4107 r#"
4108//- /main.rs
4109trait Trait<T> {
4110 type Item;
4111}
4112
4113trait Trait2 {
4114 fn foo(&self) -> u32;
4115}
4116
4117fn test<T, U>() where T::Item: Trait2, T: Trait<U::Item>, U: Trait<()> {
4118 let x: T::Item = no_matter;
4119 x.foo()<|>;
4120}
4121"#,
4122 );
4123 assert_eq!(t, "u32");
4124}
4125
4126#[test]
4127// FIXME this is currently a Salsa panic; it would be nicer if it just returned
4128// in Unknown, and we should be able to do that once Salsa allows us to handle
4129// the cycle. But at least it doesn't overflow for now.
4130#[should_panic]
4131fn unselected_projection_in_trait_env_cycle_1() {
4106 let t = type_at( 4132 let t = type_at(
4107 r#" 4133 r#"
4108//- /main.rs 4134//- /main.rs
@@ -4121,6 +4147,28 @@ fn test<T: Trait>() where T: Trait2<T::Item> {
4121 assert_eq!(t, "{unknown}"); 4147 assert_eq!(t, "{unknown}");
4122} 4148}
4123 4149
4150#[test]
4151// FIXME this is currently a Salsa panic; it would be nicer if it just returned
4152// in Unknown, and we should be able to do that once Salsa allows us to handle
4153// the cycle. But at least it doesn't overflow for now.
4154#[should_panic]
4155fn unselected_projection_in_trait_env_cycle_2() {
4156 let t = type_at(
4157 r#"
4158//- /main.rs
4159trait Trait<T> {
4160 type Item;
4161}
4162
4163fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> {
4164 let x: T::Item = no_matter<|>;
4165}
4166"#,
4167 );
4168 // this is a legitimate cycle
4169 assert_eq!(t, "{unknown}");
4170}
4171
4124fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { 4172fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
4125 let file = db.parse(pos.file_id).ok().unwrap(); 4173 let file = db.parse(pos.file_id).ok().unwrap();
4126 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); 4174 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();