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::{self, Region, Ty, fold_regions};
6use rustc_span::def_id::DefId;
7use rustc_span::symbol::{Symbol, kw};
8use rustc_trait_selection::traits::auto_trait::{self, RegionTarget};
9use thin_vec::ThinVec;
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, 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(
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 })),
130 item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id },
131 cfg: None,
132 inline_stmt_id: None,
133 }),
134 })
135}
136
137#[derive(Debug)]
138enum DiscardPositiveImpls {
139 Yes,
140 No,
141}
142
143#[instrument(level = "debug", skip(cx, region_data, vid_to_region))]
144fn clean_param_env<'tcx>(
145 cx: &mut DocContext<'tcx>,
146 item_def_id: DefId,
147 param_env: ty::ParamEnv<'tcx>,
148 region_data: RegionConstraintData<'tcx>,
149 vid_to_region: FxIndexMap<ty::RegionVid, ty::Region<'tcx>>,
150) -> clean::Generics {
151 let tcx = cx.tcx;
152 let generics = tcx.generics_of(item_def_id);
153
154 let params: ThinVec<_> = generics
155 .own_params
156 .iter()
157 .inspect(|param| {
158 if cfg!(debug_assertions) {
159 debug_assert!(!param.is_anonymous_lifetime());
160 if let ty::GenericParamDefKind::Type { synthetic, .. } = param.kind {
161 debug_assert!(!synthetic && param.name != kw::SelfUpper);
162 }
163 }
164 })
165 .map(|param| clean_generic_param_def(param, clean::ParamDefaults::No, cx))
169 .collect();
170
171 let item_predicates: FxIndexSet<_> =
173 tcx.param_env(item_def_id).caller_bounds().iter().collect();
174 let where_predicates = param_env
175 .caller_bounds()
176 .iter()
177 .filter(|pred| {
179 !item_predicates.contains(pred)
180 || pred
181 .as_trait_clause()
182 .is_some_and(|pred| tcx.lang_items().sized_trait() == Some(pred.def_id()))
183 })
184 .map(|pred| {
185 fold_regions(tcx, pred, |r, _| match *r {
186 ty::ReVar(vid) => vid_to_region.get(&vid).copied().unwrap_or(r),
191 ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r,
192 ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReErased => {
195 bug!("unexpected region kind: {r:?}")
196 }
197 })
198 })
199 .flat_map(|pred| clean_predicate(pred, cx))
200 .chain(clean_region_outlives_constraints(®ion_data, generics))
201 .collect();
202
203 let mut generics = clean::Generics { params, where_predicates };
204 simplify::sized_bounds(cx, &mut generics);
205 generics.where_predicates = simplify::where_clauses(cx, generics.where_predicates);
206 generics
207}
208
209fn clean_region_outlives_constraints<'tcx>(
221 regions: &RegionConstraintData<'tcx>,
222 generics: &'tcx ty::Generics,
223) -> ThinVec<clean::WherePredicate> {
224 let mut outlives_predicates = FxIndexMap::<_, Vec<_>>::default();
230 let mut map = FxIndexMap::<RegionTarget<'_>, auto_trait::RegionDeps<'_>>::default();
231
232 for (constraint, _) in ®ions.constraints {
237 match *constraint {
238 Constraint::VarSubVar(vid1, vid2) => {
239 let deps1 = map.entry(RegionTarget::RegionVid(vid1)).or_default();
240 deps1.larger.insert(RegionTarget::RegionVid(vid2));
241
242 let deps2 = map.entry(RegionTarget::RegionVid(vid2)).or_default();
243 deps2.smaller.insert(RegionTarget::RegionVid(vid1));
244 }
245 Constraint::RegSubVar(region, vid) => {
246 let deps = map.entry(RegionTarget::RegionVid(vid)).or_default();
247 deps.smaller.insert(RegionTarget::Region(region));
248 }
249 Constraint::VarSubReg(vid, region) => {
250 let deps = map.entry(RegionTarget::RegionVid(vid)).or_default();
251 deps.larger.insert(RegionTarget::Region(region));
252 }
253 Constraint::RegSubReg(r1, r2) => {
254 if early_bound_region_name(r1) != early_bound_region_name(r2) {
257 outlives_predicates
258 .entry(early_bound_region_name(r2).expect("no region_name found"))
259 .or_default()
260 .push(r1);
261 }
262 }
263 }
264 }
265
266 while !map.is_empty() {
283 let target = *map.keys().next().unwrap();
284 let deps = map.swap_remove(&target).unwrap();
285
286 for smaller in &deps.smaller {
287 for larger in &deps.larger {
288 match (smaller, larger) {
289 (&RegionTarget::Region(smaller), &RegionTarget::Region(larger)) => {
290 if early_bound_region_name(smaller) != early_bound_region_name(larger) {
291 outlives_predicates
292 .entry(
293 early_bound_region_name(larger).expect("no region name found"),
294 )
295 .or_default()
296 .push(smaller)
297 }
298 }
299 (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => {
300 if let IndexEntry::Occupied(v) = map.entry(*smaller) {
301 let smaller_deps = v.into_mut();
302 smaller_deps.larger.insert(*larger);
303 smaller_deps.larger.swap_remove(&target);
304 }
305 }
306 (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => {
307 if let IndexEntry::Occupied(v) = map.entry(*larger) {
308 let deps = v.into_mut();
309 deps.smaller.insert(*smaller);
310 deps.smaller.swap_remove(&target);
311 }
312 }
313 (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => {
314 if let IndexEntry::Occupied(v) = map.entry(*smaller) {
315 let smaller_deps = v.into_mut();
316 smaller_deps.larger.insert(*larger);
317 smaller_deps.larger.swap_remove(&target);
318 }
319 if let IndexEntry::Occupied(v) = map.entry(*larger) {
320 let larger_deps = v.into_mut();
321 larger_deps.smaller.insert(*smaller);
322 larger_deps.smaller.swap_remove(&target);
323 }
324 }
325 }
326 }
327 }
328 }
329
330 let region_params: FxIndexSet<_> = generics
331 .own_params
332 .iter()
333 .filter_map(|param| match param.kind {
334 ty::GenericParamDefKind::Lifetime => Some(param.name),
335 _ => None,
336 })
337 .collect();
338
339 region_params
340 .iter()
341 .filter_map(|&name| {
342 let bounds: FxIndexSet<_> = outlives_predicates
343 .get(&name)?
344 .iter()
345 .map(|®ion| {
346 let lifetime = early_bound_region_name(region)
347 .inspect(|name| assert!(region_params.contains(name)))
348 .map(Lifetime)
349 .unwrap_or(Lifetime::statik());
350 clean::GenericBound::Outlives(lifetime)
351 })
352 .collect();
353 if bounds.is_empty() {
354 return None;
355 }
356 Some(clean::WherePredicate::RegionPredicate {
357 lifetime: Lifetime(name),
358 bounds: bounds.into_iter().collect(),
359 })
360 })
361 .collect()
362}
363
364fn early_bound_region_name(region: Region<'_>) -> Option<Symbol> {
365 match *region {
366 ty::ReEarlyParam(r) => Some(r.name),
367 _ => None,
368 }
369}