hir_def/expr_store/lower/
generics.rs1use 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 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}