diff options
Diffstat (limited to 'crates/ra_hir/src/ty')
-rw-r--r-- | crates/ra_hir/src/ty/lower.rs | 97 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 64 |
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<()>`. | ||
558 | pub(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 | |||
494 | pub(crate) fn trait_env( | 573 | pub(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] |
4083 | fn unselected_projection_in_trait_env() { | 4083 | fn 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] |
4105 | fn unselected_projection_in_trait_env_cycle() { | 4105 | fn unselected_projection_in_trait_env_2() { |
4106 | let t = type_at( | ||
4107 | r#" | ||
4108 | //- /main.rs | ||
4109 | trait Trait<T> { | ||
4110 | type Item; | ||
4111 | } | ||
4112 | |||
4113 | trait Trait2 { | ||
4114 | fn foo(&self) -> u32; | ||
4115 | } | ||
4116 | |||
4117 | fn 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] | ||
4131 | fn 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] | ||
4155 | fn unselected_projection_in_trait_env_cycle_2() { | ||
4156 | let t = type_at( | ||
4157 | r#" | ||
4158 | //- /main.rs | ||
4159 | trait Trait<T> { | ||
4160 | type Item; | ||
4161 | } | ||
4162 | |||
4163 | fn 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 | |||
4124 | fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { | 4172 | fn 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(); |