1use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry};
2use rustc_hir as hir;
3use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
4use rustc_middle::bug;
5use rustc_middle::ty::fold::fold_regions;
6use rustc_middle::ty::{self, Region, Ty};
7use rustc_span::def_id::DefId;
8use rustc_span::symbol::{Symbol, kw};
9use rustc_trait_selection::traits::auto_trait::{self, RegionTarget};
10use thin_vec::ThinVec;
11use tracing::{debug, instrument};
12
13use crate::clean::{
14 self, Lifetime, clean_generic_param_def, clean_middle_ty, clean_predicate,
15 clean_trait_ref_with_constraints, clean_ty_generics, simplify,
16};
17use crate::core::DocContext;
18
19#[instrument(level = "debug", skip(cx))]
20pub(crate) fn synthesize_auto_trait_impls<'tcx>(
21 cx: &mut DocContext<'tcx>,
22 item_def_id: DefId,
23) -> Vec<clean::Item> {
24 let tcx = cx.tcx;
25 let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id);
26 let ty = tcx.type_of(item_def_id).instantiate_identity();
27
28 let finder = auto_trait::AutoTraitFinder::new(tcx);
29 let mut auto_trait_impls: Vec<_> = cx
30 .auto_traits
31 .clone()
32 .into_iter()
33 .filter_map(|trait_def_id| {
34 synthesize_auto_trait_impl(
35 cx,
36 ty,
37 trait_def_id,
38 typing_env,
39 item_def_id,
40 &finder,
41 DiscardPositiveImpls::No,
42 )
43 })
44 .collect();
45 if !ty.is_sized(tcx, typing_env)
47 && let Some(sized_trait_def_id) = tcx.lang_items().sized_trait()
48 && let Some(impl_item) = synthesize_auto_trait_impl(
49 cx,
50 ty,
51 sized_trait_def_id,
52 typing_env,
53 item_def_id,
54 &finder,
55 DiscardPositiveImpls::Yes,
56 )
57 {
58 auto_trait_impls.push(impl_item);
59 }
60 auto_trait_impls
61}
62
63#[instrument(level = "debug", skip(cx, finder))]
64fn synthesize_auto_trait_impl<'tcx>(
65 cx: &mut DocContext<'tcx>,
66 ty: Ty<'tcx>,
67 trait_def_id: DefId,
68 typing_env: ty::TypingEnv<'tcx>,
69 item_def_id: DefId,
70 finder: &auto_trait::AutoTraitFinder<'tcx>,
71 discard_positive_impls: DiscardPositiveImpls,
72) -> Option<clean::Item> {
73 let tcx = cx.tcx;
74 let trait_ref = ty::Binder::dummy(ty::TraitRef::new(tcx, trait_def_id, [ty]));
75 if !cx.generated_synthetics.insert((ty, trait_def_id)) {
76 debug!("already generated, aborting");
77 return None;
78 }
79
80 let result = finder.find_auto_trait_generics(ty, typing_env, trait_def_id, |info| {
81 clean_param_env(cx, item_def_id, info.full_user_env, info.region_data, info.vid_to_region)
82 });
83
84 let (generics, polarity) = match result {
85 auto_trait::AutoTraitResult::PositiveImpl(generics) => {
86 if let DiscardPositiveImpls::Yes = discard_positive_impls {
87 return None;
88 }
89
90 (generics, ty::ImplPolarity::Positive)
91 }
92 auto_trait::AutoTraitResult::NegativeImpl => {
93 let mut generics = clean_ty_generics(
106 cx,
107 tcx.generics_of(item_def_id),
108 ty::GenericPredicates::default(),
109 );
110 generics.where_predicates.clear();
111
112 (generics, ty::ImplPolarity::Negative)
113 }
114 auto_trait::AutoTraitResult::ExplicitImpl => return None,
115 };
116
117 Some(clean::Item {
118 name: None,
119 inner: Box::new(clean::ItemInner {
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 })),
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#[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 {
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 (constraint, _) in ®ions.constraints {
238 match *constraint {
239 Constraint::VarSubVar(vid1, vid2) => {
240 let deps1 = map.entry(RegionTarget::RegionVid(vid1)).or_default();
241 deps1.larger.insert(RegionTarget::RegionVid(vid2));
242
243 let deps2 = map.entry(RegionTarget::RegionVid(vid2)).or_default();
244 deps2.smaller.insert(RegionTarget::RegionVid(vid1));
245 }
246 Constraint::RegSubVar(region, vid) => {
247 let deps = map.entry(RegionTarget::RegionVid(vid)).or_default();
248 deps.smaller.insert(RegionTarget::Region(region));
249 }
250 Constraint::VarSubReg(vid, region) => {
251 let deps = map.entry(RegionTarget::RegionVid(vid)).or_default();
252 deps.larger.insert(RegionTarget::Region(region));
253 }
254 Constraint::RegSubReg(r1, r2) => {
255 if early_bound_region_name(r1) != early_bound_region_name(r2) {
258 outlives_predicates
259 .entry(early_bound_region_name(r2).expect("no region_name found"))
260 .or_default()
261 .push(r1);
262 }
263 }
264 }
265 }
266
267 while !map.is_empty() {
284 let target = *map.keys().next().unwrap();
285 let deps = map.swap_remove(&target).unwrap();
286
287 for smaller in &deps.smaller {
288 for larger in &deps.larger {
289 match (smaller, larger) {
290 (&RegionTarget::Region(smaller), &RegionTarget::Region(larger)) => {
291 if early_bound_region_name(smaller) != early_bound_region_name(larger) {
292 outlives_predicates
293 .entry(
294 early_bound_region_name(larger).expect("no region name found"),
295 )
296 .or_default()
297 .push(smaller)
298 }
299 }
300 (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => {
301 if let IndexEntry::Occupied(v) = map.entry(*smaller) {
302 let smaller_deps = v.into_mut();
303 smaller_deps.larger.insert(*larger);
304 smaller_deps.larger.swap_remove(&target);
305 }
306 }
307 (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => {
308 if let IndexEntry::Occupied(v) = map.entry(*larger) {
309 let deps = v.into_mut();
310 deps.smaller.insert(*smaller);
311 deps.smaller.swap_remove(&target);
312 }
313 }
314 (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => {
315 if let IndexEntry::Occupied(v) = map.entry(*smaller) {
316 let smaller_deps = v.into_mut();
317 smaller_deps.larger.insert(*larger);
318 smaller_deps.larger.swap_remove(&target);
319 }
320 if let IndexEntry::Occupied(v) = map.entry(*larger) {
321 let larger_deps = v.into_mut();
322 larger_deps.smaller.insert(*smaller);
323 larger_deps.smaller.swap_remove(&target);
324 }
325 }
326 }
327 }
328 }
329 }
330
331 let region_params: FxIndexSet<_> = generics
332 .own_params
333 .iter()
334 .filter_map(|param| match param.kind {
335 ty::GenericParamDefKind::Lifetime => Some(param.name),
336 _ => None,
337 })
338 .collect();
339
340 region_params
341 .iter()
342 .filter_map(|&name| {
343 let bounds: FxIndexSet<_> = outlives_predicates
344 .get(&name)?
345 .iter()
346 .map(|®ion| {
347 let lifetime = early_bound_region_name(region)
348 .inspect(|name| assert!(region_params.contains(name)))
349 .map(Lifetime)
350 .unwrap_or(Lifetime::statik());
351 clean::GenericBound::Outlives(lifetime)
352 })
353 .collect();
354 if bounds.is_empty() {
355 return None;
356 }
357 Some(clean::WherePredicate::RegionPredicate {
358 lifetime: Lifetime(name),
359 bounds: bounds.into_iter().collect(),
360 })
361 })
362 .collect()
363}
364
365fn early_bound_region_name(region: Region<'_>) -> Option<Symbol> {
366 match *region {
367 ty::ReEarlyParam(r) => Some(r.name),
368 _ => None,
369 }
370}