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