1use 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 param_list
186 .lifetime_params()
187 .map(|lifetime_param| {
188 lifetime_param
189 .lifetime()
190 .map_or_else(Name::missing, |lt| Name::new_lifetime(<.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}