• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
6 use base_db::FileId;
7 use either::Either;
8 use hir_expand::{
9     name::{AsName, Name},
10     ExpandResult, HirFileId, InFile,
11 };
12 use intern::Interned;
13 use la_arena::{Arena, ArenaMap, Idx};
14 use once_cell::unsync::Lazy;
15 use stdx::impl_from;
16 use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
17 use triomphe::Arc;
18 
19 use crate::{
20     child_by_source::ChildBySource,
21     db::DefDatabase,
22     dyn_map::{keys, DynMap},
23     expander::Expander,
24     lower::LowerCtx,
25     nameres::{DefMap, MacroSubNs},
26     src::{HasChildSource, HasSource},
27     type_ref::{LifetimeRef, TypeBound, TypeRef},
28     AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
29     LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
30 };
31 
32 /// Data about a generic type parameter (to a function, struct, impl, ...).
33 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
34 pub struct TypeParamData {
35     pub name: Option<Name>,
36     pub default: Option<Interned<TypeRef>>,
37     pub provenance: TypeParamProvenance,
38 }
39 
40 /// Data about a generic lifetime parameter (to a function, struct, impl, ...).
41 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
42 pub struct LifetimeParamData {
43     pub name: Name,
44 }
45 
46 /// Data about a generic const parameter (to a function, struct, impl, ...).
47 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
48 pub struct ConstParamData {
49     pub name: Name,
50     pub ty: Interned<TypeRef>,
51     pub has_default: bool,
52 }
53 
54 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
55 pub enum TypeParamProvenance {
56     TypeParamList,
57     TraitSelf,
58     ArgumentImplTrait,
59 }
60 
61 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
62 pub enum TypeOrConstParamData {
63     TypeParamData(TypeParamData),
64     ConstParamData(ConstParamData),
65 }
66 
67 impl TypeOrConstParamData {
name(&self) -> Option<&Name>68     pub fn name(&self) -> Option<&Name> {
69         match self {
70             TypeOrConstParamData::TypeParamData(x) => x.name.as_ref(),
71             TypeOrConstParamData::ConstParamData(x) => Some(&x.name),
72         }
73     }
74 
has_default(&self) -> bool75     pub fn has_default(&self) -> bool {
76         match self {
77             TypeOrConstParamData::TypeParamData(x) => x.default.is_some(),
78             TypeOrConstParamData::ConstParamData(x) => x.has_default,
79         }
80     }
81 
type_param(&self) -> Option<&TypeParamData>82     pub fn type_param(&self) -> Option<&TypeParamData> {
83         match self {
84             TypeOrConstParamData::TypeParamData(x) => Some(x),
85             TypeOrConstParamData::ConstParamData(_) => None,
86         }
87     }
88 
const_param(&self) -> Option<&ConstParamData>89     pub fn const_param(&self) -> Option<&ConstParamData> {
90         match self {
91             TypeOrConstParamData::TypeParamData(_) => None,
92             TypeOrConstParamData::ConstParamData(x) => Some(x),
93         }
94     }
95 
is_trait_self(&self) -> bool96     pub fn is_trait_self(&self) -> bool {
97         match self {
98             TypeOrConstParamData::TypeParamData(x) => {
99                 x.provenance == TypeParamProvenance::TraitSelf
100             }
101             TypeOrConstParamData::ConstParamData(_) => false,
102         }
103     }
104 }
105 
106 impl_from!(TypeParamData, ConstParamData for TypeOrConstParamData);
107 
108 /// Data about the generic parameters of a function, struct, impl, etc.
109 #[derive(Clone, PartialEq, Eq, Debug, Default, Hash)]
110 pub struct GenericParams {
111     pub type_or_consts: Arena<TypeOrConstParamData>,
112     pub lifetimes: Arena<LifetimeParamData>,
113     pub where_predicates: Vec<WherePredicate>,
114 }
115 
116 /// A single predicate from a where clause, i.e. `where Type: Trait`. Combined
117 /// where clauses like `where T: Foo + Bar` are turned into multiple of these.
118 /// It might still result in multiple actual predicates though, because of
119 /// associated type bindings like `Iterator<Item = u32>`.
120 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
121 pub enum WherePredicate {
122     TypeBound {
123         target: WherePredicateTypeTarget,
124         bound: Interned<TypeBound>,
125     },
126     Lifetime {
127         target: LifetimeRef,
128         bound: LifetimeRef,
129     },
130     ForLifetime {
131         lifetimes: Box<[Name]>,
132         target: WherePredicateTypeTarget,
133         bound: Interned<TypeBound>,
134     },
135 }
136 
137 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
138 pub enum WherePredicateTypeTarget {
139     TypeRef(Interned<TypeRef>),
140     /// For desugared where predicates that can directly refer to a type param.
141     TypeOrConstParam(LocalTypeOrConstParamId),
142 }
143 
144 impl GenericParams {
145     /// Iterator of type_or_consts field
iter( &self, ) -> impl DoubleEndedIterator<Item = (Idx<TypeOrConstParamData>, &TypeOrConstParamData)>146     pub fn iter(
147         &self,
148     ) -> impl DoubleEndedIterator<Item = (Idx<TypeOrConstParamData>, &TypeOrConstParamData)> {
149         self.type_or_consts.iter()
150     }
151 
generic_params_query( db: &dyn DefDatabase, def: GenericDefId, ) -> Interned<GenericParams>152     pub(crate) fn generic_params_query(
153         db: &dyn DefDatabase,
154         def: GenericDefId,
155     ) -> Interned<GenericParams> {
156         let _p = profile::span("generic_params_query");
157         macro_rules! id_to_generics {
158             ($id:ident) => {{
159                 let id = $id.lookup(db).id;
160                 let tree = id.item_tree(db);
161                 let item = &tree[id.value];
162                 item.generic_params.clone()
163             }};
164         }
165 
166         match def {
167             GenericDefId::FunctionId(id) => {
168                 let loc = id.lookup(db);
169                 let tree = loc.id.item_tree(db);
170                 let item = &tree[loc.id.value];
171 
172                 let mut generic_params = GenericParams::clone(&item.explicit_generic_params);
173 
174                 let module = loc.container.module(db);
175                 let func_data = db.function_data(id);
176 
177                 // Don't create an `Expander` nor call `loc.source(db)` if not needed since this
178                 // causes a reparse after the `ItemTree` has been created.
179                 let mut expander = Lazy::new(|| {
180                     (module.def_map(db), Expander::new(db, loc.source(db).file_id, module))
181                 });
182                 for param in &func_data.params {
183                     generic_params.fill_implicit_impl_trait_args(db, &mut expander, param);
184                 }
185 
186                 Interned::new(generic_params)
187             }
188             GenericDefId::AdtId(AdtId::StructId(id)) => id_to_generics!(id),
189             GenericDefId::AdtId(AdtId::EnumId(id)) => id_to_generics!(id),
190             GenericDefId::AdtId(AdtId::UnionId(id)) => id_to_generics!(id),
191             GenericDefId::TraitId(id) => id_to_generics!(id),
192             GenericDefId::TraitAliasId(id) => id_to_generics!(id),
193             GenericDefId::TypeAliasId(id) => id_to_generics!(id),
194             GenericDefId::ImplId(id) => id_to_generics!(id),
195             GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => {
196                 Interned::new(GenericParams::default())
197             }
198         }
199     }
200 
fill(&mut self, lower_ctx: &LowerCtx<'_>, node: &dyn HasGenericParams)201     pub(crate) fn fill(&mut self, lower_ctx: &LowerCtx<'_>, node: &dyn HasGenericParams) {
202         if let Some(params) = node.generic_param_list() {
203             self.fill_params(lower_ctx, params)
204         }
205         if let Some(where_clause) = node.where_clause() {
206             self.fill_where_predicates(lower_ctx, where_clause);
207         }
208     }
209 
fill_bounds( &mut self, lower_ctx: &LowerCtx<'_>, type_bounds: Option<ast::TypeBoundList>, target: Either<TypeRef, LifetimeRef>, )210     pub(crate) fn fill_bounds(
211         &mut self,
212         lower_ctx: &LowerCtx<'_>,
213         type_bounds: Option<ast::TypeBoundList>,
214         target: Either<TypeRef, LifetimeRef>,
215     ) {
216         for bound in type_bounds.iter().flat_map(|type_bound_list| type_bound_list.bounds()) {
217             self.add_where_predicate_from_bound(lower_ctx, bound, None, target.clone());
218         }
219     }
220 
fill_params(&mut self, lower_ctx: &LowerCtx<'_>, params: ast::GenericParamList)221     fn fill_params(&mut self, lower_ctx: &LowerCtx<'_>, params: ast::GenericParamList) {
222         for type_or_const_param in params.type_or_const_params() {
223             match type_or_const_param {
224                 ast::TypeOrConstParam::Type(type_param) => {
225                     let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
226                     // FIXME: Use `Path::from_src`
227                     let default = type_param
228                         .default_type()
229                         .map(|it| Interned::new(TypeRef::from_ast(lower_ctx, it)));
230                     let param = TypeParamData {
231                         name: Some(name.clone()),
232                         default,
233                         provenance: TypeParamProvenance::TypeParamList,
234                     };
235                     self.type_or_consts.alloc(param.into());
236                     let type_ref = TypeRef::Path(name.into());
237                     self.fill_bounds(
238                         lower_ctx,
239                         type_param.type_bound_list(),
240                         Either::Left(type_ref),
241                     );
242                 }
243                 ast::TypeOrConstParam::Const(const_param) => {
244                     let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
245                     let ty = const_param
246                         .ty()
247                         .map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it));
248                     let param = ConstParamData {
249                         name,
250                         ty: Interned::new(ty),
251                         has_default: const_param.default_val().is_some(),
252                     };
253                     self.type_or_consts.alloc(param.into());
254                 }
255             }
256         }
257         for lifetime_param in params.lifetime_params() {
258             let name =
259                 lifetime_param.lifetime().map_or_else(Name::missing, |lt| Name::new_lifetime(&lt));
260             let param = LifetimeParamData { name: name.clone() };
261             self.lifetimes.alloc(param);
262             let lifetime_ref = LifetimeRef::new_name(name);
263             self.fill_bounds(
264                 lower_ctx,
265                 lifetime_param.type_bound_list(),
266                 Either::Right(lifetime_ref),
267             );
268         }
269     }
270 
fill_where_predicates(&mut self, lower_ctx: &LowerCtx<'_>, where_clause: ast::WhereClause)271     fn fill_where_predicates(&mut self, lower_ctx: &LowerCtx<'_>, where_clause: ast::WhereClause) {
272         for pred in where_clause.predicates() {
273             let target = if let Some(type_ref) = pred.ty() {
274                 Either::Left(TypeRef::from_ast(lower_ctx, type_ref))
275             } else if let Some(lifetime) = pred.lifetime() {
276                 Either::Right(LifetimeRef::new(&lifetime))
277             } else {
278                 continue;
279             };
280 
281             let lifetimes: Option<Box<_>> = pred.generic_param_list().map(|param_list| {
282                 // Higher-Ranked Trait Bounds
283                 param_list
284                     .lifetime_params()
285                     .map(|lifetime_param| {
286                         lifetime_param
287                             .lifetime()
288                             .map_or_else(Name::missing, |lt| Name::new_lifetime(&lt))
289                     })
290                     .collect()
291             });
292             for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) {
293                 self.add_where_predicate_from_bound(
294                     lower_ctx,
295                     bound,
296                     lifetimes.as_ref(),
297                     target.clone(),
298                 );
299             }
300         }
301     }
302 
add_where_predicate_from_bound( &mut self, lower_ctx: &LowerCtx<'_>, bound: ast::TypeBound, hrtb_lifetimes: Option<&Box<[Name]>>, target: Either<TypeRef, LifetimeRef>, )303     fn add_where_predicate_from_bound(
304         &mut self,
305         lower_ctx: &LowerCtx<'_>,
306         bound: ast::TypeBound,
307         hrtb_lifetimes: Option<&Box<[Name]>>,
308         target: Either<TypeRef, LifetimeRef>,
309     ) {
310         let bound = TypeBound::from_ast(lower_ctx, bound);
311         let predicate = match (target, bound) {
312             (Either::Left(type_ref), bound) => match hrtb_lifetimes {
313                 Some(hrtb_lifetimes) => WherePredicate::ForLifetime {
314                     lifetimes: hrtb_lifetimes.clone(),
315                     target: WherePredicateTypeTarget::TypeRef(Interned::new(type_ref)),
316                     bound: Interned::new(bound),
317                 },
318                 None => WherePredicate::TypeBound {
319                     target: WherePredicateTypeTarget::TypeRef(Interned::new(type_ref)),
320                     bound: Interned::new(bound),
321                 },
322             },
323             (Either::Right(lifetime), TypeBound::Lifetime(bound)) => {
324                 WherePredicate::Lifetime { target: lifetime, bound }
325             }
326             _ => return,
327         };
328         self.where_predicates.push(predicate);
329     }
330 
fill_implicit_impl_trait_args( &mut self, db: &dyn DefDatabase, exp: &mut Lazy<(Arc<DefMap>, Expander), impl FnOnce() -> (Arc<DefMap>, Expander)>, type_ref: &TypeRef, )331     pub(crate) fn fill_implicit_impl_trait_args(
332         &mut self,
333         db: &dyn DefDatabase,
334         exp: &mut Lazy<(Arc<DefMap>, Expander), impl FnOnce() -> (Arc<DefMap>, Expander)>,
335         type_ref: &TypeRef,
336     ) {
337         type_ref.walk(&mut |type_ref| {
338             if let TypeRef::ImplTrait(bounds) = type_ref {
339                 let param = TypeParamData {
340                     name: None,
341                     default: None,
342                     provenance: TypeParamProvenance::ArgumentImplTrait,
343                 };
344                 let param_id = self.type_or_consts.alloc(param.into());
345                 for bound in bounds {
346                     self.where_predicates.push(WherePredicate::TypeBound {
347                         target: WherePredicateTypeTarget::TypeOrConstParam(param_id),
348                         bound: bound.clone(),
349                     });
350                 }
351             }
352             if let TypeRef::Macro(mc) = type_ref {
353                 let macro_call = mc.to_node(db.upcast());
354                 let (def_map, expander) = &mut **exp;
355 
356                 let module = expander.module.local_id;
357                 let resolver = |path| {
358                     def_map
359                         .resolve_path(
360                             db,
361                             module,
362                             &path,
363                             crate::item_scope::BuiltinShadowMode::Other,
364                             Some(MacroSubNs::Bang),
365                         )
366                         .0
367                         .take_macros()
368                 };
369                 if let Ok(ExpandResult { value: Some((mark, expanded)), .. }) =
370                     expander.enter_expand(db, macro_call, resolver)
371                 {
372                     let ctx = expander.ctx(db);
373                     let type_ref = TypeRef::from_ast(&ctx, expanded.tree());
374                     self.fill_implicit_impl_trait_args(db, &mut *exp, &type_ref);
375                     exp.1.exit(db, mark);
376                 }
377             }
378         });
379     }
380 
shrink_to_fit(&mut self)381     pub(crate) fn shrink_to_fit(&mut self) {
382         let Self { lifetimes, type_or_consts: types, where_predicates } = self;
383         lifetimes.shrink_to_fit();
384         types.shrink_to_fit();
385         where_predicates.shrink_to_fit();
386     }
387 
find_type_by_name(&self, name: &Name, parent: GenericDefId) -> Option<TypeParamId>388     pub fn find_type_by_name(&self, name: &Name, parent: GenericDefId) -> Option<TypeParamId> {
389         self.type_or_consts.iter().find_map(|(id, p)| {
390             if p.name().as_ref() == Some(&name) && p.type_param().is_some() {
391                 Some(TypeParamId::from_unchecked(TypeOrConstParamId { local_id: id, parent }))
392             } else {
393                 None
394             }
395         })
396     }
397 
find_const_by_name(&self, name: &Name, parent: GenericDefId) -> Option<ConstParamId>398     pub fn find_const_by_name(&self, name: &Name, parent: GenericDefId) -> Option<ConstParamId> {
399         self.type_or_consts.iter().find_map(|(id, p)| {
400             if p.name().as_ref() == Some(&name) && p.const_param().is_some() {
401                 Some(ConstParamId::from_unchecked(TypeOrConstParamId { local_id: id, parent }))
402             } else {
403                 None
404             }
405         })
406     }
407 
find_trait_self_param(&self) -> Option<LocalTypeOrConstParamId>408     pub fn find_trait_self_param(&self) -> Option<LocalTypeOrConstParamId> {
409         self.type_or_consts.iter().find_map(|(id, p)| {
410             matches!(
411                 p,
412                 TypeOrConstParamData::TypeParamData(TypeParamData {
413                     provenance: TypeParamProvenance::TraitSelf,
414                     ..
415                 })
416             )
417             .then(|| id)
418         })
419     }
420 }
421 
file_id_and_params_of( def: GenericDefId, db: &dyn DefDatabase, ) -> (HirFileId, Option<ast::GenericParamList>)422 fn file_id_and_params_of(
423     def: GenericDefId,
424     db: &dyn DefDatabase,
425 ) -> (HirFileId, Option<ast::GenericParamList>) {
426     match def {
427         GenericDefId::FunctionId(it) => {
428             let src = it.lookup(db).source(db);
429             (src.file_id, src.value.generic_param_list())
430         }
431         GenericDefId::AdtId(AdtId::StructId(it)) => {
432             let src = it.lookup(db).source(db);
433             (src.file_id, src.value.generic_param_list())
434         }
435         GenericDefId::AdtId(AdtId::UnionId(it)) => {
436             let src = it.lookup(db).source(db);
437             (src.file_id, src.value.generic_param_list())
438         }
439         GenericDefId::AdtId(AdtId::EnumId(it)) => {
440             let src = it.lookup(db).source(db);
441             (src.file_id, src.value.generic_param_list())
442         }
443         GenericDefId::TraitId(it) => {
444             let src = it.lookup(db).source(db);
445             (src.file_id, src.value.generic_param_list())
446         }
447         GenericDefId::TraitAliasId(it) => {
448             let src = it.lookup(db).source(db);
449             (src.file_id, src.value.generic_param_list())
450         }
451         GenericDefId::TypeAliasId(it) => {
452             let src = it.lookup(db).source(db);
453             (src.file_id, src.value.generic_param_list())
454         }
455         GenericDefId::ImplId(it) => {
456             let src = it.lookup(db).source(db);
457             (src.file_id, src.value.generic_param_list())
458         }
459         // We won't be using this ID anyway
460         GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => (FileId(!0).into(), None),
461     }
462 }
463 
464 impl HasChildSource<LocalTypeOrConstParamId> for GenericDefId {
465     type Value = Either<ast::TypeOrConstParam, ast::TraitOrAlias>;
child_source( &self, db: &dyn DefDatabase, ) -> InFile<ArenaMap<LocalTypeOrConstParamId, Self::Value>>466     fn child_source(
467         &self,
468         db: &dyn DefDatabase,
469     ) -> InFile<ArenaMap<LocalTypeOrConstParamId, Self::Value>> {
470         let generic_params = db.generic_params(*self);
471         let mut idx_iter = generic_params.type_or_consts.iter().map(|(idx, _)| idx);
472 
473         let (file_id, generic_params_list) = file_id_and_params_of(*self, db);
474 
475         let mut params = ArenaMap::default();
476 
477         // For traits and trait aliases the first type index is `Self`, we need to add it before
478         // the other params.
479         match *self {
480             GenericDefId::TraitId(id) => {
481                 let trait_ref = id.lookup(db).source(db).value;
482                 let idx = idx_iter.next().unwrap();
483                 params.insert(idx, Either::Right(ast::TraitOrAlias::Trait(trait_ref)));
484             }
485             GenericDefId::TraitAliasId(id) => {
486                 let alias = id.lookup(db).source(db).value;
487                 let idx = idx_iter.next().unwrap();
488                 params.insert(idx, Either::Right(ast::TraitOrAlias::TraitAlias(alias)));
489             }
490             _ => {}
491         }
492 
493         if let Some(generic_params_list) = generic_params_list {
494             for (idx, ast_param) in idx_iter.zip(generic_params_list.type_or_const_params()) {
495                 params.insert(idx, Either::Left(ast_param));
496             }
497         }
498 
499         InFile::new(file_id, params)
500     }
501 }
502 
503 impl HasChildSource<LocalLifetimeParamId> for GenericDefId {
504     type Value = ast::LifetimeParam;
child_source( &self, db: &dyn DefDatabase, ) -> InFile<ArenaMap<LocalLifetimeParamId, Self::Value>>505     fn child_source(
506         &self,
507         db: &dyn DefDatabase,
508     ) -> InFile<ArenaMap<LocalLifetimeParamId, Self::Value>> {
509         let generic_params = db.generic_params(*self);
510         let idx_iter = generic_params.lifetimes.iter().map(|(idx, _)| idx);
511 
512         let (file_id, generic_params_list) = file_id_and_params_of(*self, db);
513 
514         let mut params = ArenaMap::default();
515 
516         if let Some(generic_params_list) = generic_params_list {
517             for (idx, ast_param) in idx_iter.zip(generic_params_list.lifetime_params()) {
518                 params.insert(idx, ast_param);
519             }
520         }
521 
522         InFile::new(file_id, params)
523     }
524 }
525 
526 impl ChildBySource for GenericDefId {
child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId)527     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
528         let (gfile_id, generic_params_list) = file_id_and_params_of(*self, db);
529         if gfile_id != file_id {
530             return;
531         }
532 
533         let generic_params = db.generic_params(*self);
534         let mut toc_idx_iter = generic_params.type_or_consts.iter().map(|(idx, _)| idx);
535         let lts_idx_iter = generic_params.lifetimes.iter().map(|(idx, _)| idx);
536 
537         // For traits the first type index is `Self`, skip it.
538         if let GenericDefId::TraitId(_) = *self {
539             toc_idx_iter.next().unwrap(); // advance_by(1);
540         }
541 
542         if let Some(generic_params_list) = generic_params_list {
543             for (local_id, ast_param) in
544                 toc_idx_iter.zip(generic_params_list.type_or_const_params())
545             {
546                 let id = TypeOrConstParamId { parent: *self, local_id };
547                 match ast_param {
548                     ast::TypeOrConstParam::Type(a) => res[keys::TYPE_PARAM].insert(a, id),
549                     ast::TypeOrConstParam::Const(a) => res[keys::CONST_PARAM].insert(a, id),
550                 }
551             }
552             for (local_id, ast_param) in lts_idx_iter.zip(generic_params_list.lifetime_params()) {
553                 let id = LifetimeParamId { parent: *self, local_id };
554                 res[keys::LIFETIME_PARAM].insert(ast_param, id);
555             }
556         }
557     }
558 }
559