hir_def/expr_store/lower/
generics.rs

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
6use std::sync::LazyLock;
7
8use either::Either;
9use hir_expand::name::{AsName, Name};
10use intern::sym;
11use la_arena::Arena;
12use syntax::ast::{self, HasName, HasTypeBounds};
13use thin_vec::ThinVec;
14use triomphe::Arc;
15
16use crate::{
17    GenericDefId, TypeOrConstParamId, TypeParamId,
18    expr_store::{TypePtr, lower::ExprCollector},
19    hir::generics::{
20        ConstParamData, GenericParams, LifetimeParamData, TypeOrConstParamData, TypeParamData,
21        TypeParamProvenance, WherePredicate,
22    },
23    type_ref::{LifetimeRef, LifetimeRefId, TypeBound, TypeRef, TypeRefId},
24};
25
26pub(crate) type ImplTraitLowerFn<'l> = &'l mut dyn for<'ec, 'db> FnMut(
27    &'ec mut ExprCollector<'db>,
28    TypePtr,
29    ThinVec<TypeBound>,
30) -> TypeRefId;
31
32pub(crate) struct GenericParamsCollector {
33    type_or_consts: Arena<TypeOrConstParamData>,
34    lifetimes: Arena<LifetimeParamData>,
35    where_predicates: Vec<WherePredicate>,
36    parent: GenericDefId,
37}
38
39impl GenericParamsCollector {
40    pub(crate) fn new(parent: GenericDefId) -> Self {
41        Self {
42            type_or_consts: Default::default(),
43            lifetimes: Default::default(),
44            where_predicates: Default::default(),
45            parent,
46        }
47    }
48    pub(crate) fn with_self_param(
49        ec: &mut ExprCollector<'_>,
50        parent: GenericDefId,
51        bounds: Option<ast::TypeBoundList>,
52    ) -> Self {
53        let mut this = Self::new(parent);
54        this.fill_self_param(ec, bounds);
55        this
56    }
57
58    pub(crate) fn lower(
59        &mut self,
60        ec: &mut ExprCollector<'_>,
61        generic_param_list: Option<ast::GenericParamList>,
62        where_clause: Option<ast::WhereClause>,
63    ) {
64        if let Some(params) = generic_param_list {
65            self.lower_param_list(ec, params)
66        }
67        if let Some(where_clause) = where_clause {
68            self.lower_where_predicates(ec, where_clause);
69        }
70    }
71
72    pub(crate) fn collect_impl_trait<R>(
73        &mut self,
74        ec: &mut ExprCollector<'_>,
75        cb: impl FnOnce(&mut ExprCollector<'_>, ImplTraitLowerFn<'_>) -> R,
76    ) -> R {
77        cb(
78            ec,
79            &mut Self::lower_argument_impl_trait(
80                &mut self.type_or_consts,
81                &mut self.where_predicates,
82                self.parent,
83            ),
84        )
85    }
86
87    pub(crate) fn finish(self) -> Arc<GenericParams> {
88        let Self { mut lifetimes, mut type_or_consts, mut where_predicates, parent: _ } = self;
89
90        if lifetimes.is_empty() && type_or_consts.is_empty() && where_predicates.is_empty() {
91            static EMPTY: LazyLock<Arc<GenericParams>> = LazyLock::new(|| {
92                Arc::new(GenericParams {
93                    lifetimes: Arena::new(),
94                    type_or_consts: Arena::new(),
95                    where_predicates: Box::default(),
96                })
97            });
98            return Arc::clone(&EMPTY);
99        }
100
101        lifetimes.shrink_to_fit();
102        type_or_consts.shrink_to_fit();
103        where_predicates.shrink_to_fit();
104        Arc::new(GenericParams {
105            type_or_consts,
106            lifetimes,
107            where_predicates: where_predicates.into_boxed_slice(),
108        })
109    }
110
111    fn lower_param_list(&mut self, ec: &mut ExprCollector<'_>, params: ast::GenericParamList) {
112        for generic_param in params.generic_params() {
113            let enabled = ec.expander.is_cfg_enabled(ec.db, ec.module.krate(), &generic_param);
114            if !enabled {
115                continue;
116            }
117
118            match generic_param {
119                ast::GenericParam::TypeParam(type_param) => {
120                    let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
121                    let default = type_param.default_type().map(|it| {
122                        ec.lower_type_ref(it, &mut ExprCollector::impl_trait_error_allocator)
123                    });
124                    let param = TypeParamData {
125                        name: Some(name.clone()),
126                        default,
127                        provenance: TypeParamProvenance::TypeParamList,
128                    };
129                    let idx = self.type_or_consts.alloc(param.into());
130                    let type_ref =
131                        TypeRef::TypeParam(TypeParamId::from_unchecked(TypeOrConstParamId {
132                            parent: self.parent,
133                            local_id: idx,
134                        }));
135                    let type_ref = ec.alloc_type_ref_desugared(type_ref);
136                    self.lower_bounds(ec, type_param.type_bound_list(), Either::Left(type_ref));
137                }
138                ast::GenericParam::ConstParam(const_param) => {
139                    let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
140                    let ty = ec.lower_type_ref_opt(
141                        const_param.ty(),
142                        &mut ExprCollector::impl_trait_error_allocator,
143                    );
144                    let param = ConstParamData {
145                        name,
146                        ty,
147                        default: const_param.default_val().map(|it| ec.lower_const_arg(it)),
148                    };
149                    let _idx = self.type_or_consts.alloc(param.into());
150                }
151                ast::GenericParam::LifetimeParam(lifetime_param) => {
152                    let lifetime = ec.lower_lifetime_ref_opt(lifetime_param.lifetime());
153                    if let LifetimeRef::Named(name) = &ec.store.lifetimes[lifetime] {
154                        let param = LifetimeParamData { name: name.clone() };
155                        let _idx = self.lifetimes.alloc(param);
156                        self.lower_bounds(
157                            ec,
158                            lifetime_param.type_bound_list(),
159                            Either::Right(lifetime),
160                        );
161                    }
162                }
163            }
164        }
165    }
166
167    fn lower_where_predicates(
168        &mut self,
169        ec: &mut ExprCollector<'_>,
170        where_clause: ast::WhereClause,
171    ) {
172        for pred in where_clause.predicates() {
173            let target = if let Some(type_ref) = pred.ty() {
174                Either::Left(
175                    ec.lower_type_ref(type_ref, &mut ExprCollector::impl_trait_error_allocator),
176                )
177            } else if let Some(lifetime) = pred.lifetime() {
178                Either::Right(ec.lower_lifetime_ref(lifetime))
179            } else {
180                continue;
181            };
182
183            let lifetimes: Option<Box<_>> = pred.generic_param_list().map(|param_list| {
184                // Higher-Ranked Trait Bounds
185                param_list
186                    .lifetime_params()
187                    .map(|lifetime_param| {
188                        lifetime_param
189                            .lifetime()
190                            .map_or_else(Name::missing, |lt| Name::new_lifetime(&lt.text()))
191                    })
192                    .collect()
193            });
194            for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) {
195                self.lower_type_bound_as_predicate(ec, bound, lifetimes.as_deref(), target);
196            }
197        }
198    }
199
200    fn lower_bounds(
201        &mut self,
202        ec: &mut ExprCollector<'_>,
203        type_bounds: Option<ast::TypeBoundList>,
204        target: Either<TypeRefId, LifetimeRefId>,
205    ) {
206        for bound in type_bounds.iter().flat_map(|type_bound_list| type_bound_list.bounds()) {
207            self.lower_type_bound_as_predicate(ec, bound, None, target);
208        }
209    }
210
211    fn lower_type_bound_as_predicate(
212        &mut self,
213        ec: &mut ExprCollector<'_>,
214        bound: ast::TypeBound,
215        hrtb_lifetimes: Option<&[Name]>,
216        target: Either<TypeRefId, LifetimeRefId>,
217    ) {
218        let bound = ec.lower_type_bound(
219            bound,
220            &mut Self::lower_argument_impl_trait(
221                &mut self.type_or_consts,
222                &mut self.where_predicates,
223                self.parent,
224            ),
225        );
226        let predicate = match (target, bound) {
227            (_, TypeBound::Error | TypeBound::Use(_)) => return,
228            (Either::Left(type_ref), bound) => match hrtb_lifetimes {
229                Some(hrtb_lifetimes) => WherePredicate::ForLifetime {
230                    lifetimes: ThinVec::from_iter(hrtb_lifetimes.iter().cloned()),
231                    target: type_ref,
232                    bound,
233                },
234                None => WherePredicate::TypeBound { target: type_ref, bound },
235            },
236            (Either::Right(lifetime), TypeBound::Lifetime(bound)) => {
237                WherePredicate::Lifetime { target: lifetime, bound }
238            }
239            (Either::Right(_), TypeBound::ForLifetime(..) | TypeBound::Path(..)) => return,
240        };
241        self.where_predicates.push(predicate);
242    }
243
244    fn lower_argument_impl_trait(
245        type_or_consts: &mut Arena<TypeOrConstParamData>,
246        where_predicates: &mut Vec<WherePredicate>,
247        parent: GenericDefId,
248    ) -> impl for<'ec, 'db> FnMut(&'ec mut ExprCollector<'db>, TypePtr, ThinVec<TypeBound>) -> TypeRefId
249    {
250        move |ec, ptr, impl_trait_bounds| {
251            let param = TypeParamData {
252                name: None,
253                default: None,
254                provenance: TypeParamProvenance::ArgumentImplTrait,
255            };
256            let param_id = TypeRef::TypeParam(TypeParamId::from_unchecked(TypeOrConstParamId {
257                parent,
258                local_id: type_or_consts.alloc(param.into()),
259            }));
260            let type_ref = ec.alloc_type_ref(param_id, ptr);
261            for bound in impl_trait_bounds {
262                where_predicates
263                    .push(WherePredicate::TypeBound { target: type_ref, bound: bound.clone() });
264            }
265            type_ref
266        }
267    }
268
269    fn fill_self_param(&mut self, ec: &mut ExprCollector<'_>, bounds: Option<ast::TypeBoundList>) {
270        let self_ = Name::new_symbol_root(sym::Self_);
271        let idx = self.type_or_consts.alloc(
272            TypeParamData {
273                name: Some(self_.clone()),
274                default: None,
275                provenance: TypeParamProvenance::TraitSelf,
276            }
277            .into(),
278        );
279        debug_assert_eq!(idx, GenericParams::SELF_PARAM_ID_IN_SELF);
280        let type_ref = TypeRef::TypeParam(TypeParamId::from_unchecked(TypeOrConstParamId {
281            parent: self.parent,
282            local_id: idx,
283        }));
284        let self_ = ec.alloc_type_ref_desugared(type_ref);
285        if let Some(bounds) = bounds {
286            self.lower_bounds(ec, Some(bounds), Either::Left(self_));
287        }
288    }
289}