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();
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, 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 ®ions.constraints {
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 }
269 }
270
271 while !map.is_empty() {
288 let target = *map.keys().next().unwrap();
289 let deps = map.swap_remove(&target).unwrap();
290
291 for smaller in &deps.smaller {
292 for larger in &deps.larger {
293 match (smaller, larger) {
294 (&RegionTarget::Region(smaller), &RegionTarget::Region(larger)) => {
295 if early_bound_region_name(smaller) != early_bound_region_name(larger) {
296 outlives_predicates
297 .entry(
298 early_bound_region_name(larger).expect("no region name found"),
299 )
300 .or_default()
301 .push(smaller)
302 }
303 }
304 (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => {
305 if let IndexEntry::Occupied(v) = map.entry(*smaller) {
306 let smaller_deps = v.into_mut();
307 smaller_deps.larger.insert(*larger);
308 smaller_deps.larger.swap_remove(&target);
309 }
310 }
311 (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => {
312 if let IndexEntry::Occupied(v) = map.entry(*larger) {
313 let deps = v.into_mut();
314 deps.smaller.insert(*smaller);
315 deps.smaller.swap_remove(&target);
316 }
317 }
318 (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => {
319 if let IndexEntry::Occupied(v) = map.entry(*smaller) {
320 let smaller_deps = v.into_mut();
321 smaller_deps.larger.insert(*larger);
322 smaller_deps.larger.swap_remove(&target);
323 }
324 if let IndexEntry::Occupied(v) = map.entry(*larger) {
325 let larger_deps = v.into_mut();
326 larger_deps.smaller.insert(*smaller);
327 larger_deps.smaller.swap_remove(&target);
328 }
329 }
330 }
331 }
332 }
333 }
334
335 let region_params: FxIndexSet<_> = generics
336 .own_params
337 .iter()
338 .filter_map(|param| match param.kind {
339 ty::GenericParamDefKind::Lifetime => Some(param.name),
340 _ => None,
341 })
342 .collect();
343
344 region_params
345 .iter()
346 .filter_map(|&name| {
347 let bounds: FxIndexSet<_> = outlives_predicates
348 .get(&name)?
349 .iter()
350 .map(|®ion| {
351 let lifetime = early_bound_region_name(region)
352 .inspect(|name| assert!(region_params.contains(name)))
353 .map(Lifetime)
354 .unwrap_or(Lifetime::statik());
355 clean::GenericBound::Outlives(lifetime)
356 })
357 .collect();
358 if bounds.is_empty() {
359 return None;
360 }
361 Some(clean::WherePredicate::RegionPredicate {
362 lifetime: Lifetime(name),
363 bounds: bounds.into_iter().collect(),
364 })
365 })
366 .collect()
367}
368
369fn early_bound_region_name(region: Region<'_>) -> Option<Symbol> {
370 match region.kind() {
371 ty::ReEarlyParam(r) => Some(r.name),
372 _ => None,
373 }
374}