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, RegionUtilitiesExt, 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_clause, clean_generic_param_def, clean_middle_ty,
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::GenericClauses::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 super::inline::record_extern_trait(cx, trait_def_id);
118
119 Some(clean::Item {
120 inner: Box::new(clean::ItemInner {
121 name: None,
122 attrs: Default::default(),
123 stability: None,
124 kind: clean::ImplItem(Box::new(clean::Impl {
125 safety: hir::Safety::Safe,
126 generics,
127 trait_: Some(clean_trait_ref_with_constraints(cx, trait_ref, ThinVec::new())),
128 for_: clean_middle_ty(ty::Binder::dummy(ty), cx, None, None),
129 items: Vec::new(),
130 polarity,
131 kind: clean::ImplKind::Auto,
132 is_deprecated: false,
133 })),
134 item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id },
135 cfg: None,
136 inline_stmt_id: None,
137 }),
138 })
139}
140
141#[derive(Debug)]
142enum DiscardPositiveImpls {
143 Yes,
144 No,
145}
146
147#[instrument(level = "debug", skip(cx, region_data, vid_to_region))]
148fn clean_param_env<'tcx>(
149 cx: &mut DocContext<'tcx>,
150 item_def_id: DefId,
151 param_env: ty::ParamEnv<'tcx>,
152 region_data: RegionConstraintData<'tcx>,
153 vid_to_region: FxIndexMap<ty::RegionVid, ty::Region<'tcx>>,
154) -> clean::Generics {
155 let tcx = cx.tcx;
156 let generics = tcx.generics_of(item_def_id);
157
158 let params: ThinVec<_> = generics
159 .own_params
160 .iter()
161 .inspect(|param| {
162 if cfg!(debug_assertions) {
163 debug_assert!(!param.is_anonymous_lifetime());
164 if let ty::GenericParamDefKind::Type { synthetic, .. } = param.kind {
165 debug_assert!(!synthetic && param.name != kw::SelfUpper);
166 }
167 }
168 })
169 .map(|param| clean_generic_param_def(param, clean::ParamDefaults::No, cx))
173 .collect();
174
175 let item_clauses: FxIndexSet<_> = tcx.param_env(item_def_id).caller_bounds().iter().collect();
177 let where_predicates = cx.with_exact_param_env(param_env, |cx| {
178 param_env
179 .caller_bounds()
180 .iter()
181 .filter(|clause| {
183 !item_clauses.contains(clause)
184 || clause.as_trait_clause().is_some_and(|clause| {
185 tcx.lang_items().sized_trait() == Some(clause.def_id())
186 })
187 })
188 .map(|clause| {
189 fold_regions(tcx, clause, |r, _| match r.kind() {
190 ty::ReVar(vid) => vid_to_region.get(&vid).copied().unwrap_or(r),
195 ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r,
196 ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReErased => {
199 bug!("unexpected region kind: {r:?}")
200 }
201 })
202 })
203 .flat_map(|clause| clean_clause(clause, cx))
204 .chain(clean_region_outlives_constraints(®ion_data, generics))
205 .collect()
206 });
207
208 let mut generics = clean::Generics { params, where_predicates };
209 simplify::sizedness_bounds(cx, &mut generics);
210 generics.where_predicates = simplify::where_clauses(cx.tcx, generics.where_predicates);
211 generics
212}
213
214fn clean_region_outlives_constraints<'tcx>(
226 regions: &RegionConstraintData<'tcx>,
227 generics: &'tcx ty::Generics,
228) -> ThinVec<clean::WherePredicate> {
229 let mut outlives_predicates = FxIndexMap::<_, Vec<_>>::default();
235 let mut map = FxIndexMap::<RegionTarget<'_>, auto_trait::RegionDeps<'_>>::default();
236
237 for c in regions.constraints.iter().flat_map(|(c, _)| c.iter_outlives()) {
242 match c.kind {
243 ConstraintKind::VarSubVar => {
244 let sub_vid = c.sub.as_var();
245 let sup_vid = c.sup.as_var();
246 let deps1 = map.entry(RegionTarget::RegionVid(sub_vid)).or_default();
247 deps1.larger.insert(RegionTarget::RegionVid(sup_vid));
248
249 let deps2 = map.entry(RegionTarget::RegionVid(sup_vid)).or_default();
250 deps2.smaller.insert(RegionTarget::RegionVid(sub_vid));
251 }
252 ConstraintKind::RegSubVar => {
253 let sup_vid = c.sup.as_var();
254 let deps = map.entry(RegionTarget::RegionVid(sup_vid)).or_default();
255 deps.smaller.insert(RegionTarget::Region(c.sub));
256 }
257 ConstraintKind::VarSubReg => {
258 let sub_vid = c.sub.as_var();
259 let deps = map.entry(RegionTarget::RegionVid(sub_vid)).or_default();
260 deps.larger.insert(RegionTarget::Region(c.sup));
261 }
262 ConstraintKind::RegSubReg => {
263 if early_bound_region_name(c.sub) != early_bound_region_name(c.sup) {
266 outlives_predicates
267 .entry(early_bound_region_name(c.sup).expect("no region_name found"))
268 .or_default()
269 .push(c.sub);
270 }
271 }
272 ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
273 unreachable!()
274 }
275 }
276 }
277
278 while !map.is_empty() {
295 let target = *map.keys().next().unwrap();
296 let deps = map.swap_remove(&target).unwrap();
297
298 for smaller in &deps.smaller {
299 for larger in &deps.larger {
300 match (smaller, larger) {
301 (&RegionTarget::Region(smaller), &RegionTarget::Region(larger)) => {
302 if early_bound_region_name(smaller) != early_bound_region_name(larger) {
303 outlives_predicates
304 .entry(
305 early_bound_region_name(larger).expect("no region name found"),
306 )
307 .or_default()
308 .push(smaller)
309 }
310 }
311 (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => {
312 if let IndexEntry::Occupied(v) = map.entry(*smaller) {
313 let smaller_deps = v.into_mut();
314 smaller_deps.larger.insert(*larger);
315 smaller_deps.larger.swap_remove(&target);
316 }
317 }
318 (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => {
319 if let IndexEntry::Occupied(v) = map.entry(*larger) {
320 let deps = v.into_mut();
321 deps.smaller.insert(*smaller);
322 deps.smaller.swap_remove(&target);
323 }
324 }
325 (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => {
326 if let IndexEntry::Occupied(v) = map.entry(*smaller) {
327 let smaller_deps = v.into_mut();
328 smaller_deps.larger.insert(*larger);
329 smaller_deps.larger.swap_remove(&target);
330 }
331 if let IndexEntry::Occupied(v) = map.entry(*larger) {
332 let larger_deps = v.into_mut();
333 larger_deps.smaller.insert(*smaller);
334 larger_deps.smaller.swap_remove(&target);
335 }
336 }
337 }
338 }
339 }
340 }
341
342 let region_params: FxIndexSet<_> = generics
343 .own_params
344 .iter()
345 .filter_map(|param| match param.kind {
346 ty::GenericParamDefKind::Lifetime => Some(param.name),
347 _ => None,
348 })
349 .collect();
350
351 region_params
352 .iter()
353 .filter_map(|&name| {
354 let bounds: FxIndexSet<_> = outlives_predicates
355 .get(&name)?
356 .iter()
357 .map(|®ion| {
358 let lifetime = early_bound_region_name(region)
359 .inspect(|name| assert!(region_params.contains(name)))
360 .map(Lifetime)
361 .unwrap_or(Lifetime::statik());
362 clean::GenericBound::Outlives(lifetime)
363 })
364 .collect();
365 if bounds.is_empty() {
366 return None;
367 }
368 Some(clean::WherePredicate::RegionPredicate {
369 lifetime: Lifetime(name),
370 bounds: bounds.into_iter().collect(),
371 })
372 })
373 .collect()
374}
375
376fn early_bound_region_name(region: Region<'_>) -> Option<Symbol> {
377 match region.kind() {
378 ty::ReEarlyParam(r) => Some(r.name),
379 _ => None,
380 }
381}