1use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry};
2use rustc_data_structures::thin_vec::ThinVec;
3use rustc_hir as hir;
4use rustc_infer::infer::region_constraints::{ConstraintKind, RegionConstraintData};
5use rustc_middle::bug;
6use rustc_middle::ty::{self, Region, Ty, fold_regions};
7use rustc_span::def_id::DefId;
8use rustc_span::symbol::{Symbol, kw};
9use rustc_trait_selection::traits::auto_trait::{self, RegionTarget};
10use tracing::{debug, instrument};
11
12use crate::clean::{
13 self, Lifetime, clean_generic_param_def, clean_middle_ty, clean_predicate,
14 clean_trait_ref_with_constraints, clean_ty_generics_inner, simplify,
15};
16use crate::core::DocContext;
17
18#[instrument(level = "debug", skip(cx))]
19pub(crate) fn synthesize_auto_trait_impls<'tcx>(
20 cx: &mut DocContext<'tcx>,
21 item_def_id: DefId,
22) -> Vec<clean::Item> {
23 let tcx = cx.tcx;
24 let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id);
25 let ty = tcx.type_of(item_def_id).instantiate_identity().skip_norm_wip();
26
27 let finder = auto_trait::AutoTraitFinder::new(tcx);
28 let mut auto_trait_impls: Vec<_> = cx
29 .auto_traits
30 .clone()
31 .into_iter()
32 .filter_map(|trait_def_id| {
33 synthesize_auto_trait_impl(
34 cx,
35 ty,
36 trait_def_id,
37 typing_env,
38 item_def_id,
39 &finder,
40 DiscardPositiveImpls::No,
41 )
42 })
43 .collect();
44 if !ty.is_sized(tcx, typing_env)
46 && let Some(sized_trait_def_id) = tcx.lang_items().sized_trait()
47 && let Some(impl_item) = synthesize_auto_trait_impl(
48 cx,
49 ty,
50 sized_trait_def_id,
51 typing_env,
52 item_def_id,
53 &finder,
54 DiscardPositiveImpls::Yes,
55 )
56 {
57 auto_trait_impls.push(impl_item);
58 }
59 auto_trait_impls
60}
61
62#[instrument(level = "debug", skip(cx, finder))]
63fn synthesize_auto_trait_impl<'tcx>(
64 cx: &mut DocContext<'tcx>,
65 ty: Ty<'tcx>,
66 trait_def_id: DefId,
67 typing_env: ty::TypingEnv<'tcx>,
68 item_def_id: DefId,
69 finder: &auto_trait::AutoTraitFinder<'tcx>,
70 discard_positive_impls: DiscardPositiveImpls,
71) -> Option<clean::Item> {
72 let tcx = cx.tcx;
73 let trait_ref = ty::Binder::dummy(ty::TraitRef::new(tcx, trait_def_id, [ty]));
74 if !cx.synthetic_auto_trait_impls.insert((ty, trait_def_id)) {
75 debug!("already generated, aborting");
76 return None;
77 }
78
79 let result = finder.find_auto_trait_generics(ty, typing_env, trait_def_id, |info| {
80 clean_param_env(cx, item_def_id, info.full_user_env, info.region_data, info.vid_to_region)
81 });
82
83 let (generics, polarity) = match result {
84 auto_trait::AutoTraitResult::PositiveImpl(generics) => {
85 if let DiscardPositiveImpls::Yes = discard_positive_impls {
86 return None;
87 }
88
89 (generics, ty::ImplPolarity::Positive)
90 }
91 auto_trait::AutoTraitResult::NegativeImpl => {
92 let mut generics = clean_ty_generics_inner(
105 cx,
106 tcx.generics_of(item_def_id),
107 ty::GenericPredicates::default(),
108 );
109 generics.where_predicates.clear();
110
111 (generics, ty::ImplPolarity::Negative)
112 }
113 auto_trait::AutoTraitResult::NoImpl => return None,
114 auto_trait::AutoTraitResult::ExplicitImpl => return None,
115 };
116
117 Some(clean::Item {
118 inner: Box::new(clean::ItemInner {
119 name: None,
120 attrs: Default::default(),
121 stability: None,
122 kind: clean::ImplItem(Box::new(clean::Impl {
123 safety: hir::Safety::Safe,
124 generics,
125 trait_: Some(clean_trait_ref_with_constraints(cx, trait_ref, ThinVec::new())),
126 for_: clean_middle_ty(ty::Binder::dummy(ty), cx, None, None),
127 items: Vec::new(),
128 polarity,
129 kind: clean::ImplKind::Auto,
130 is_deprecated: false,
131 })),
132 item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id },
133 cfg: None,
134 inline_stmt_id: None,
135 }),
136 })
137}
138
139#[derive(Debug)]
140enum DiscardPositiveImpls {
141 Yes,
142 No,
143}
144
145#[instrument(level = "debug", skip(cx, region_data, vid_to_region))]
146fn clean_param_env<'tcx>(
147 cx: &mut DocContext<'tcx>,
148 item_def_id: DefId,
149 param_env: ty::ParamEnv<'tcx>,
150 region_data: RegionConstraintData<'tcx>,
151 vid_to_region: FxIndexMap<ty::RegionVid, ty::Region<'tcx>>,
152) -> clean::Generics {
153 let tcx = cx.tcx;
154 let generics = tcx.generics_of(item_def_id);
155
156 let params: ThinVec<_> = generics
157 .own_params
158 .iter()
159 .inspect(|param| {
160 if cfg!(debug_assertions) {
161 debug_assert!(!param.is_anonymous_lifetime());
162 if let ty::GenericParamDefKind::Type { synthetic, .. } = param.kind {
163 debug_assert!(!synthetic && param.name != kw::SelfUpper);
164 }
165 }
166 })
167 .map(|param| clean_generic_param_def(param, clean::ParamDefaults::No, cx))
171 .collect();
172
173 let item_clauses: FxIndexSet<_> = tcx.param_env(item_def_id).caller_bounds().iter().collect();
175 let where_predicates = cx.with_exact_param_env(param_env, |cx| {
176 param_env
177 .caller_bounds()
178 .iter()
179 .filter(|clause| {
181 !item_clauses.contains(clause)
182 || clause.as_trait_clause().is_some_and(|clause| {
183 tcx.lang_items().sized_trait() == Some(clause.def_id())
184 })
185 })
186 .map(|clause| {
187 fold_regions(tcx, clause, |r, _| match r.kind() {
188 ty::ReVar(vid) => vid_to_region.get(&vid).copied().unwrap_or(r),
193 ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r,
194 ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReErased => {
197 bug!("unexpected region kind: {r:?}")
198 }
199 })
200 })
201 .flat_map(|clause| clean_predicate(clause, cx))
202 .chain(clean_region_outlives_constraints(®ion_data, generics))
203 .collect()
204 });
205
206 let mut generics = clean::Generics { params, where_predicates };
207 simplify::sizedness_bounds(cx, &mut generics);
208 generics.where_predicates = simplify::where_clauses(cx.tcx, generics.where_predicates);
209 generics
210}
211
212fn clean_region_outlives_constraints<'tcx>(
224 regions: &RegionConstraintData<'tcx>,
225 generics: &'tcx ty::Generics,
226) -> ThinVec<clean::WherePredicate> {
227 let mut outlives_predicates = FxIndexMap::<_, Vec<_>>::default();
233 let mut map = FxIndexMap::<RegionTarget<'_>, auto_trait::RegionDeps<'_>>::default();
234
235 for c in regions.constraints.iter().flat_map(|(c, _)| c.iter_outlives()) {
240 match c.kind {
241 ConstraintKind::VarSubVar => {
242 let sub_vid = c.sub.as_var();
243 let sup_vid = c.sup.as_var();
244 let deps1 = map.entry(RegionTarget::RegionVid(sub_vid)).or_default();
245 deps1.larger.insert(RegionTarget::RegionVid(sup_vid));
246
247 let deps2 = map.entry(RegionTarget::RegionVid(sup_vid)).or_default();
248 deps2.smaller.insert(RegionTarget::RegionVid(sub_vid));
249 }
250 ConstraintKind::RegSubVar => {
251 let sup_vid = c.sup.as_var();
252 let deps = map.entry(RegionTarget::RegionVid(sup_vid)).or_default();
253 deps.smaller.insert(RegionTarget::Region(c.sub));
254 }
255 ConstraintKind::VarSubReg => {
256 let sub_vid = c.sub.as_var();
257 let deps = map.entry(RegionTarget::RegionVid(sub_vid)).or_default();
258 deps.larger.insert(RegionTarget::Region(c.sup));
259 }
260 ConstraintKind::RegSubReg => {
261 if early_bound_region_name(c.sub) != early_bound_region_name(c.sup) {
264 outlives_predicates
265 .entry(early_bound_region_name(c.sup).expect("no region_name found"))
266 .or_default()
267 .push(c.sub);
268 }
269 }
270 ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
271 unreachable!()
272 }
273 }
274 }
275
276 while !map.is_empty() {
293 let target = *map.keys().next().unwrap();
294 let deps = map.swap_remove(&target).unwrap();
295
296 for smaller in &deps.smaller {
297 for larger in &deps.larger {
298 match (smaller, larger) {
299 (&RegionTarget::Region(smaller), &RegionTarget::Region(larger)) => {
300 if early_bound_region_name(smaller) != early_bound_region_name(larger) {
301 outlives_predicates
302 .entry(
303 early_bound_region_name(larger).expect("no region name found"),
304 )
305 .or_default()
306 .push(smaller)
307 }
308 }
309 (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => {
310 if let IndexEntry::Occupied(v) = map.entry(*smaller) {
311 let smaller_deps = v.into_mut();
312 smaller_deps.larger.insert(*larger);
313 smaller_deps.larger.swap_remove(&target);
314 }
315 }
316 (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => {
317 if let IndexEntry::Occupied(v) = map.entry(*larger) {
318 let deps = v.into_mut();
319 deps.smaller.insert(*smaller);
320 deps.smaller.swap_remove(&target);
321 }
322 }
323 (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => {
324 if let IndexEntry::Occupied(v) = map.entry(*smaller) {
325 let smaller_deps = v.into_mut();
326 smaller_deps.larger.insert(*larger);
327 smaller_deps.larger.swap_remove(&target);
328 }
329 if let IndexEntry::Occupied(v) = map.entry(*larger) {
330 let larger_deps = v.into_mut();
331 larger_deps.smaller.insert(*smaller);
332 larger_deps.smaller.swap_remove(&target);
333 }
334 }
335 }
336 }
337 }
338 }
339
340 let region_params: FxIndexSet<_> = generics
341 .own_params
342 .iter()
343 .filter_map(|param| match param.kind {
344 ty::GenericParamDefKind::Lifetime => Some(param.name),
345 _ => None,
346 })
347 .collect();
348
349 region_params
350 .iter()
351 .filter_map(|&name| {
352 let bounds: FxIndexSet<_> = outlives_predicates
353 .get(&name)?
354 .iter()
355 .map(|®ion| {
356 let lifetime = early_bound_region_name(region)
357 .inspect(|name| assert!(region_params.contains(name)))
358 .map(Lifetime)
359 .unwrap_or(Lifetime::statik());
360 clean::GenericBound::Outlives(lifetime)
361 })
362 .collect();
363 if bounds.is_empty() {
364 return None;
365 }
366 Some(clean::WherePredicate::RegionPredicate {
367 lifetime: Lifetime(name),
368 bounds: bounds.into_iter().collect(),
369 })
370 })
371 .collect()
372}
373
374fn early_bound_region_name(region: Region<'_>) -> Option<Symbol> {
375 match region.kind() {
376 ty::ReEarlyParam(r) => Some(r.name),
377 _ => None,
378 }
379}