diff options
author | Igor Aleksanov <[email protected]> | 2020-08-14 05:34:07 +0100 |
---|---|---|
committer | Igor Aleksanov <[email protected]> | 2020-08-14 05:34:07 +0100 |
commit | c26c911ec1e6c2ad1dcb7d155a6a1d528839ad1a (patch) | |
tree | 7cff36c38234be0afb65273146d8247083a5cfeb /crates/hir_def/src/generics.rs | |
parent | 3c018bf84de5c693b5ee1c6bec0fed3b201c2060 (diff) | |
parent | f1f73649a686dc6e6449afc35e0fa6fed00e225d (diff) |
Merge branch 'master' into add-disable-diagnostics
Diffstat (limited to 'crates/hir_def/src/generics.rs')
-rw-r--r-- | crates/hir_def/src/generics.rs | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs new file mode 100644 index 000000000..835fe3fbd --- /dev/null +++ b/crates/hir_def/src/generics.rs | |||
@@ -0,0 +1,339 @@ | |||
1 | //! Many kinds of items or constructs can have generic parameters: functions, | ||
2 | //! structs, impls, traits, etc. This module provides a common HIR for these | ||
3 | //! generic parameters. See also the `Generics` type and the `generics_of` query | ||
4 | //! in rustc. | ||
5 | use std::sync::Arc; | ||
6 | |||
7 | use arena::{map::ArenaMap, Arena}; | ||
8 | use base_db::FileId; | ||
9 | use either::Either; | ||
10 | use hir_expand::{ | ||
11 | name::{name, AsName, Name}, | ||
12 | InFile, | ||
13 | }; | ||
14 | use syntax::ast::{self, GenericParamsOwner, NameOwner, TypeBoundsOwner}; | ||
15 | |||
16 | use crate::{ | ||
17 | body::LowerCtx, | ||
18 | child_by_source::ChildBySource, | ||
19 | db::DefDatabase, | ||
20 | dyn_map::DynMap, | ||
21 | keys, | ||
22 | src::HasChildSource, | ||
23 | src::HasSource, | ||
24 | type_ref::{TypeBound, TypeRef}, | ||
25 | AdtId, GenericDefId, LocalTypeParamId, Lookup, TypeParamId, | ||
26 | }; | ||
27 | |||
28 | /// Data about a generic parameter (to a function, struct, impl, ...). | ||
29 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
30 | pub struct TypeParamData { | ||
31 | pub name: Option<Name>, | ||
32 | pub default: Option<TypeRef>, | ||
33 | pub provenance: TypeParamProvenance, | ||
34 | } | ||
35 | |||
36 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | ||
37 | pub enum TypeParamProvenance { | ||
38 | TypeParamList, | ||
39 | TraitSelf, | ||
40 | ArgumentImplTrait, | ||
41 | } | ||
42 | |||
43 | /// Data about the generic parameters of a function, struct, impl, etc. | ||
44 | #[derive(Clone, PartialEq, Eq, Debug, Default)] | ||
45 | pub struct GenericParams { | ||
46 | pub types: Arena<TypeParamData>, | ||
47 | // lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>, | ||
48 | pub where_predicates: Vec<WherePredicate>, | ||
49 | } | ||
50 | |||
51 | /// A single predicate from a where clause, i.e. `where Type: Trait`. Combined | ||
52 | /// where clauses like `where T: Foo + Bar` are turned into multiple of these. | ||
53 | /// It might still result in multiple actual predicates though, because of | ||
54 | /// associated type bindings like `Iterator<Item = u32>`. | ||
55 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
56 | pub struct WherePredicate { | ||
57 | pub target: WherePredicateTarget, | ||
58 | pub bound: TypeBound, | ||
59 | } | ||
60 | |||
61 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
62 | pub enum WherePredicateTarget { | ||
63 | TypeRef(TypeRef), | ||
64 | /// For desugared where predicates that can directly refer to a type param. | ||
65 | TypeParam(LocalTypeParamId), | ||
66 | } | ||
67 | |||
68 | type SourceMap = ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>; | ||
69 | |||
70 | impl GenericParams { | ||
71 | pub(crate) fn generic_params_query( | ||
72 | db: &dyn DefDatabase, | ||
73 | def: GenericDefId, | ||
74 | ) -> Arc<GenericParams> { | ||
75 | let _p = profile::span("generic_params_query"); | ||
76 | |||
77 | let generics = match def { | ||
78 | GenericDefId::FunctionId(id) => { | ||
79 | let id = id.lookup(db).id; | ||
80 | let tree = db.item_tree(id.file_id); | ||
81 | let item = &tree[id.value]; | ||
82 | tree[item.generic_params].clone() | ||
83 | } | ||
84 | GenericDefId::AdtId(AdtId::StructId(id)) => { | ||
85 | let id = id.lookup(db).id; | ||
86 | let tree = db.item_tree(id.file_id); | ||
87 | let item = &tree[id.value]; | ||
88 | tree[item.generic_params].clone() | ||
89 | } | ||
90 | GenericDefId::AdtId(AdtId::EnumId(id)) => { | ||
91 | let id = id.lookup(db).id; | ||
92 | let tree = db.item_tree(id.file_id); | ||
93 | let item = &tree[id.value]; | ||
94 | tree[item.generic_params].clone() | ||
95 | } | ||
96 | GenericDefId::AdtId(AdtId::UnionId(id)) => { | ||
97 | let id = id.lookup(db).id; | ||
98 | let tree = db.item_tree(id.file_id); | ||
99 | let item = &tree[id.value]; | ||
100 | tree[item.generic_params].clone() | ||
101 | } | ||
102 | GenericDefId::TraitId(id) => { | ||
103 | let id = id.lookup(db).id; | ||
104 | let tree = db.item_tree(id.file_id); | ||
105 | let item = &tree[id.value]; | ||
106 | tree[item.generic_params].clone() | ||
107 | } | ||
108 | GenericDefId::TypeAliasId(id) => { | ||
109 | let id = id.lookup(db).id; | ||
110 | let tree = db.item_tree(id.file_id); | ||
111 | let item = &tree[id.value]; | ||
112 | tree[item.generic_params].clone() | ||
113 | } | ||
114 | GenericDefId::ImplId(id) => { | ||
115 | let id = id.lookup(db).id; | ||
116 | let tree = db.item_tree(id.file_id); | ||
117 | let item = &tree[id.value]; | ||
118 | tree[item.generic_params].clone() | ||
119 | } | ||
120 | GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => GenericParams::default(), | ||
121 | }; | ||
122 | Arc::new(generics) | ||
123 | } | ||
124 | |||
125 | fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) { | ||
126 | let mut generics = GenericParams { types: Arena::default(), where_predicates: Vec::new() }; | ||
127 | let mut sm = ArenaMap::default(); | ||
128 | |||
129 | // FIXME: add `: Sized` bound for everything except for `Self` in traits | ||
130 | let file_id = match def { | ||
131 | GenericDefId::FunctionId(it) => { | ||
132 | let src = it.lookup(db).source(db); | ||
133 | let lower_ctx = LowerCtx::new(db, src.file_id); | ||
134 | generics.fill(&lower_ctx, &mut sm, &src.value); | ||
135 | // lower `impl Trait` in arguments | ||
136 | let data = db.function_data(it); | ||
137 | for param in &data.params { | ||
138 | generics.fill_implicit_impl_trait_args(param); | ||
139 | } | ||
140 | src.file_id | ||
141 | } | ||
142 | GenericDefId::AdtId(AdtId::StructId(it)) => { | ||
143 | let src = it.lookup(db).source(db); | ||
144 | let lower_ctx = LowerCtx::new(db, src.file_id); | ||
145 | generics.fill(&lower_ctx, &mut sm, &src.value); | ||
146 | src.file_id | ||
147 | } | ||
148 | GenericDefId::AdtId(AdtId::UnionId(it)) => { | ||
149 | let src = it.lookup(db).source(db); | ||
150 | let lower_ctx = LowerCtx::new(db, src.file_id); | ||
151 | generics.fill(&lower_ctx, &mut sm, &src.value); | ||
152 | src.file_id | ||
153 | } | ||
154 | GenericDefId::AdtId(AdtId::EnumId(it)) => { | ||
155 | let src = it.lookup(db).source(db); | ||
156 | let lower_ctx = LowerCtx::new(db, src.file_id); | ||
157 | generics.fill(&lower_ctx, &mut sm, &src.value); | ||
158 | src.file_id | ||
159 | } | ||
160 | GenericDefId::TraitId(it) => { | ||
161 | let src = it.lookup(db).source(db); | ||
162 | let lower_ctx = LowerCtx::new(db, src.file_id); | ||
163 | |||
164 | // traits get the Self type as an implicit first type parameter | ||
165 | let self_param_id = generics.types.alloc(TypeParamData { | ||
166 | name: Some(name![Self]), | ||
167 | default: None, | ||
168 | provenance: TypeParamProvenance::TraitSelf, | ||
169 | }); | ||
170 | sm.insert(self_param_id, Either::Left(src.value.clone())); | ||
171 | // add super traits as bounds on Self | ||
172 | // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar | ||
173 | let self_param = TypeRef::Path(name![Self].into()); | ||
174 | generics.fill_bounds(&lower_ctx, &src.value, self_param); | ||
175 | |||
176 | generics.fill(&lower_ctx, &mut sm, &src.value); | ||
177 | src.file_id | ||
178 | } | ||
179 | GenericDefId::TypeAliasId(it) => { | ||
180 | let src = it.lookup(db).source(db); | ||
181 | let lower_ctx = LowerCtx::new(db, src.file_id); | ||
182 | |||
183 | generics.fill(&lower_ctx, &mut sm, &src.value); | ||
184 | src.file_id | ||
185 | } | ||
186 | // Note that we don't add `Self` here: in `impl`s, `Self` is not a | ||
187 | // type-parameter, but rather is a type-alias for impl's target | ||
188 | // type, so this is handled by the resolver. | ||
189 | GenericDefId::ImplId(it) => { | ||
190 | let src = it.lookup(db).source(db); | ||
191 | let lower_ctx = LowerCtx::new(db, src.file_id); | ||
192 | |||
193 | generics.fill(&lower_ctx, &mut sm, &src.value); | ||
194 | src.file_id | ||
195 | } | ||
196 | // We won't be using this ID anyway | ||
197 | GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => FileId(!0).into(), | ||
198 | }; | ||
199 | |||
200 | (generics, InFile::new(file_id, sm)) | ||
201 | } | ||
202 | |||
203 | pub(crate) fn fill( | ||
204 | &mut self, | ||
205 | lower_ctx: &LowerCtx, | ||
206 | sm: &mut SourceMap, | ||
207 | node: &dyn GenericParamsOwner, | ||
208 | ) { | ||
209 | if let Some(params) = node.generic_param_list() { | ||
210 | self.fill_params(lower_ctx, sm, params) | ||
211 | } | ||
212 | if let Some(where_clause) = node.where_clause() { | ||
213 | self.fill_where_predicates(lower_ctx, where_clause); | ||
214 | } | ||
215 | } | ||
216 | |||
217 | pub(crate) fn fill_bounds( | ||
218 | &mut self, | ||
219 | lower_ctx: &LowerCtx, | ||
220 | node: &dyn ast::TypeBoundsOwner, | ||
221 | type_ref: TypeRef, | ||
222 | ) { | ||
223 | for bound in | ||
224 | node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds()) | ||
225 | { | ||
226 | self.add_where_predicate_from_bound(lower_ctx, bound, type_ref.clone()); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | fn fill_params( | ||
231 | &mut self, | ||
232 | lower_ctx: &LowerCtx, | ||
233 | sm: &mut SourceMap, | ||
234 | params: ast::GenericParamList, | ||
235 | ) { | ||
236 | for type_param in params.type_params() { | ||
237 | let name = type_param.name().map_or_else(Name::missing, |it| it.as_name()); | ||
238 | // FIXME: Use `Path::from_src` | ||
239 | let default = type_param.default_type().map(|it| TypeRef::from_ast(lower_ctx, it)); | ||
240 | let param = TypeParamData { | ||
241 | name: Some(name.clone()), | ||
242 | default, | ||
243 | provenance: TypeParamProvenance::TypeParamList, | ||
244 | }; | ||
245 | let param_id = self.types.alloc(param); | ||
246 | sm.insert(param_id, Either::Right(type_param.clone())); | ||
247 | |||
248 | let type_ref = TypeRef::Path(name.into()); | ||
249 | self.fill_bounds(&lower_ctx, &type_param, type_ref); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | fn fill_where_predicates(&mut self, lower_ctx: &LowerCtx, where_clause: ast::WhereClause) { | ||
254 | for pred in where_clause.predicates() { | ||
255 | let type_ref = match pred.ty() { | ||
256 | Some(type_ref) => type_ref, | ||
257 | None => continue, | ||
258 | }; | ||
259 | let type_ref = TypeRef::from_ast(lower_ctx, type_ref); | ||
260 | for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) { | ||
261 | self.add_where_predicate_from_bound(lower_ctx, bound, type_ref.clone()); | ||
262 | } | ||
263 | } | ||
264 | } | ||
265 | |||
266 | fn add_where_predicate_from_bound( | ||
267 | &mut self, | ||
268 | lower_ctx: &LowerCtx, | ||
269 | bound: ast::TypeBound, | ||
270 | type_ref: TypeRef, | ||
271 | ) { | ||
272 | if bound.question_mark_token().is_some() { | ||
273 | // FIXME: remove this bound | ||
274 | return; | ||
275 | } | ||
276 | let bound = TypeBound::from_ast(lower_ctx, bound); | ||
277 | self.where_predicates | ||
278 | .push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound }); | ||
279 | } | ||
280 | |||
281 | pub(crate) fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) { | ||
282 | type_ref.walk(&mut |type_ref| { | ||
283 | if let TypeRef::ImplTrait(bounds) = type_ref { | ||
284 | let param = TypeParamData { | ||
285 | name: None, | ||
286 | default: None, | ||
287 | provenance: TypeParamProvenance::ArgumentImplTrait, | ||
288 | }; | ||
289 | let param_id = self.types.alloc(param); | ||
290 | for bound in bounds { | ||
291 | self.where_predicates.push(WherePredicate { | ||
292 | target: WherePredicateTarget::TypeParam(param_id), | ||
293 | bound: bound.clone(), | ||
294 | }); | ||
295 | } | ||
296 | } | ||
297 | }); | ||
298 | } | ||
299 | |||
300 | pub fn find_by_name(&self, name: &Name) -> Option<LocalTypeParamId> { | ||
301 | self.types | ||
302 | .iter() | ||
303 | .find_map(|(id, p)| if p.name.as_ref() == Some(name) { Some(id) } else { None }) | ||
304 | } | ||
305 | |||
306 | pub fn find_trait_self_param(&self) -> Option<LocalTypeParamId> { | ||
307 | self.types.iter().find_map(|(id, p)| { | ||
308 | if p.provenance == TypeParamProvenance::TraitSelf { | ||
309 | Some(id) | ||
310 | } else { | ||
311 | None | ||
312 | } | ||
313 | }) | ||
314 | } | ||
315 | } | ||
316 | |||
317 | impl HasChildSource for GenericDefId { | ||
318 | type ChildId = LocalTypeParamId; | ||
319 | type Value = Either<ast::Trait, ast::TypeParam>; | ||
320 | fn child_source(&self, db: &dyn DefDatabase) -> InFile<SourceMap> { | ||
321 | let (_, sm) = GenericParams::new(db, *self); | ||
322 | sm | ||
323 | } | ||
324 | } | ||
325 | |||
326 | impl ChildBySource for GenericDefId { | ||
327 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | ||
328 | let mut res = DynMap::default(); | ||
329 | let arena_map = self.child_source(db); | ||
330 | let arena_map = arena_map.as_ref(); | ||
331 | for (local_id, src) in arena_map.value.iter() { | ||
332 | let id = TypeParamId { parent: *self, local_id }; | ||
333 | if let Either::Right(type_param) = src { | ||
334 | res[keys::TYPE_PARAM].insert(arena_map.with_value(type_param.clone()), id) | ||
335 | } | ||
336 | } | ||
337 | res | ||
338 | } | ||
339 | } | ||