Skip to main content

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 either::Either;
7use hir_expand::name::{AsName, Name};
8use intern::sym;
9use la_arena::Arena;
10use syntax::ast::{self, HasName, HasTypeBounds};
11use thin_vec::ThinVec;
12
13use crate::{
14    GenericDefId, TypeOrConstParamId, TypeParamId,
15    expr_store::{
16        TypePtr,
17        lower::{ExprCollector, LifetimeBoundScope, NamedLifetimeStore},
18    },
19    hir::generics::{
20        ConstParamData, GenericParams, LifetimeBoundType, LifetimeParamData, TypeOrConstParamData,
21        TypeParamData, 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) -> GenericParams {
88        let Self { mut lifetimes, mut type_or_consts, where_predicates, parent: _ } = self;
89        let early_bound_lifetimes_len =
90            lifetimes.iter().filter(|(_, lt)| lt.is_early_bound()).count();
91
92        lifetimes.shrink_to_fit();
93        type_or_consts.shrink_to_fit();
94        GenericParams {
95            early_bound_lifetimes_len,
96            type_or_consts,
97            lifetimes,
98            where_predicates: where_predicates.into_boxed_slice(),
99        }
100    }
101
102    fn lower_param_list(&mut self, ec: &mut ExprCollector<'_>, params: ast::GenericParamList) {
103        for generic_param in params.generic_params() {
104            let enabled = ec.check_cfg(&generic_param);
105            if !enabled {
106                continue;
107            }
108
109            match generic_param {
110                ast::GenericParam::TypeParam(type_param) => {
111                    let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
112                    let default = type_param.default_type().map(|it| {
113                        ec.lower_type_ref(it, &mut ExprCollector::impl_trait_error_allocator)
114                    });
115                    let param = TypeParamData {
116                        name: Some(name.clone()),
117                        default,
118                        provenance: TypeParamProvenance::TypeParamList,
119                    };
120                    let idx = self.type_or_consts.alloc(param.into());
121                    let type_ref =
122                        TypeRef::TypeParam(TypeParamId::from_unchecked(TypeOrConstParamId {
123                            parent: self.parent,
124                            local_id: idx,
125                        }));
126                    let type_ref = ec.alloc_type_ref_desugared(type_ref);
127                    ec.with_lifetime_bound_scope(LifetimeBoundScope::WhereClause, |ec| {
128                        self.lower_bounds(ec, type_param.type_bound_list(), Either::Left(type_ref))
129                    });
130                }
131                ast::GenericParam::ConstParam(const_param) => {
132                    let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
133                    let ty = ec.lower_type_ref_opt(
134                        const_param.ty(),
135                        &mut ExprCollector::impl_trait_error_allocator,
136                    );
137                    let default = const_param.default_val().map(|it| ec.lower_const_arg(it));
138                    let param = ConstParamData { name, ty, default };
139                    self.type_or_consts.alloc(param.into());
140                }
141                ast::GenericParam::LifetimeParam(lifetime_param) => {
142                    let lifetime = ec.lower_lifetime_ref_opt(lifetime_param.lifetime());
143                    if let LifetimeRef::Named(name) = &ec.store.lifetimes[lifetime] {
144                        let param = LifetimeParamData {
145                            name: name.clone(),
146                            bound_type: LifetimeBoundType::EarlyBound,
147                        };
148                        let _idx = self.lifetimes.alloc(param);
149                        ec.with_lifetime_bound_scope(LifetimeBoundScope::WhereClause, |ec| {
150                            self.lower_bounds(
151                                ec,
152                                lifetime_param.type_bound_list(),
153                                Either::Right(lifetime),
154                            )
155                        });
156                    }
157                }
158            }
159        }
160    }
161
162    fn lower_where_predicates(
163        &mut self,
164        ec: &mut ExprCollector<'_>,
165        where_clause: ast::WhereClause,
166    ) {
167        ec.with_lifetime_bound_scope(LifetimeBoundScope::WhereClause, |ec| {
168            for pred in where_clause.predicates() {
169                let target = if let Some(type_ref) = pred.ty() {
170                    Either::Left(
171                        ec.lower_type_ref(type_ref, &mut ExprCollector::impl_trait_error_allocator),
172                    )
173                } else if let Some(lifetime) = pred.lifetime() {
174                    Either::Right(ec.lower_lifetime_ref(lifetime))
175                } else {
176                    continue;
177                };
178
179                let lifetimes: Option<Box<_>> =
180                    pred.for_binder().and_then(|it| it.generic_param_list()).map(|param_list| {
181                        // Higher-Ranked Trait Bounds
182                        param_list
183                            .lifetime_params()
184                            .map(|lifetime_param| {
185                                lifetime_param
186                                    .lifetime()
187                                    .map_or_else(Name::missing, |lt| Name::new_lifetime(lt.text()))
188                            })
189                            .collect()
190                    });
191                for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) {
192                    self.lower_type_bound_as_predicate(ec, bound, lifetimes.as_deref(), target);
193                }
194            }
195        });
196    }
197
198    fn lower_bounds(
199        &mut self,
200        ec: &mut ExprCollector<'_>,
201        type_bounds: Option<ast::TypeBoundList>,
202        target: Either<TypeRefId, LifetimeRefId>,
203    ) {
204        for bound in type_bounds.iter().flat_map(|type_bound_list| type_bound_list.bounds()) {
205            self.lower_type_bound_as_predicate(ec, bound, None, target);
206        }
207    }
208
209    fn lower_type_bound_as_predicate(
210        &mut self,
211        ec: &mut ExprCollector<'_>,
212        bound: ast::TypeBound,
213        hrtb_lifetimes: Option<&[Name]>,
214        target: Either<TypeRefId, LifetimeRefId>,
215    ) {
216        let bound = ec.lower_type_bound(
217            bound,
218            &mut Self::lower_argument_impl_trait(
219                &mut self.type_or_consts,
220                &mut self.where_predicates,
221                self.parent,
222            ),
223        );
224        let predicate = match (target, bound) {
225            (_, TypeBound::Error | TypeBound::Use(_)) => return,
226            (Either::Left(type_ref), bound) => WherePredicate::TypeBound {
227                lifetimes: hrtb_lifetimes.map(|h| ThinVec::from_iter(h.iter().cloned())),
228                target: type_ref,
229                bound,
230            },
231            (Either::Right(lifetime), TypeBound::Lifetime(bound)) => {
232                WherePredicate::Lifetime { target: lifetime, bound }
233            }
234            (Either::Right(_), TypeBound::ForLifetime(..) | TypeBound::Path(..)) => return,
235        };
236        if let WherePredicate::Lifetime { target, .. } = predicate {
237            ec.push_named_target_lifetime(target);
238        }
239        self.where_predicates.push(predicate);
240    }
241
242    fn lower_argument_impl_trait(
243        type_or_consts: &mut Arena<TypeOrConstParamData>,
244        where_predicates: &mut Vec<WherePredicate>,
245        parent: GenericDefId,
246    ) -> impl for<'ec, 'db> FnMut(&'ec mut ExprCollector<'db>, TypePtr, ThinVec<TypeBound>) -> TypeRefId
247    {
248        move |ec, ptr, impl_trait_bounds| {
249            let param = TypeParamData {
250                name: None,
251                default: None,
252                provenance: TypeParamProvenance::ArgumentImplTrait,
253            };
254            let param_id = TypeRef::TypeParam(TypeParamId::from_unchecked(TypeOrConstParamId {
255                parent,
256                local_id: type_or_consts.alloc(param.into()),
257            }));
258            let type_ref = ec.alloc_type_ref(param_id, ptr);
259            for bound in impl_trait_bounds {
260                where_predicates.push(WherePredicate::TypeBound {
261                    lifetimes: None,
262                    target: type_ref,
263                    bound: bound.clone(),
264                });
265            }
266            type_ref
267        }
268    }
269
270    fn fill_self_param(&mut self, ec: &mut ExprCollector<'_>, bounds: Option<ast::TypeBoundList>) {
271        let self_ = Name::new_symbol_root(sym::Self_);
272        let idx = self.type_or_consts.alloc(
273            TypeParamData {
274                name: Some(self_),
275                default: None,
276                provenance: TypeParamProvenance::TraitSelf,
277            }
278            .into(),
279        );
280        debug_assert_eq!(idx, GenericParams::SELF_PARAM_ID_IN_SELF);
281        let type_ref = TypeRef::TypeParam(TypeParamId::from_unchecked(TypeOrConstParamId {
282            parent: self.parent,
283            local_id: idx,
284        }));
285        let self_ = ec.alloc_type_ref_desugared(type_ref);
286        if let Some(bounds) = bounds {
287            self.lower_bounds(ec, Some(bounds), Either::Left(self_));
288        }
289    }
290
291    pub(crate) fn update_to_late_bound_lifetimes(
292        &mut self,
293        named_lifetime_store: &NamedLifetimeStore,
294    ) {
295        for (_param_id, lifetime) in self.lifetimes.iter_mut() {
296            let lifetime_name = &lifetime.name;
297            if named_lifetime_store.lifetimes_in_where_clause.contains(lifetime_name) {
298                continue;
299            }
300
301            if !named_lifetime_store.lifetimes_constrained_by_input.contains(lifetime_name)
302                && named_lifetime_store.lifetimes_in_output.contains(lifetime_name)
303            {
304                continue;
305            }
306
307            lifetime.bound_type = LifetimeBoundType::LateBound
308        }
309    }
310}