Skip to main content

rustdoc/clean/
auto_trait.rs

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::ty::{self, CallerBoundsIterator, Region, Ty, fold_regions};
6use rustc_span::bug;
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    // We are only interested in case the type *doesn't* implement the `Sized` trait.
45    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            // For negative impls, we use the generic params, but *not* the predicates,
93            // from the original type. Otherwise, the displayed impl appears to be a
94            // conditional negative impl, when it's really unconditional.
95            //
96            // For example, consider the struct Foo<T: Copy>(*mut T). Using
97            // the original predicates in our impl would cause us to generate
98            // `impl !Send for Foo<T: Copy>`, which makes it appear that Foo
99            // implements Send where T is not copy.
100            //
101            // Instead, we generate `impl !Send for Foo<T>`, which better
102            // expresses the fact that `Foo<T>` never implements `Send`,
103            // regardless of the choice of `T`.
104            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::ItemKind::Impl(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        // We're basing the generics of the synthetic auto trait impl off of the generics of the
170        // implementing type. Its generic parameters may have defaults, don't copy them over:
171        // Generic parameter defaults are meaningless in impls.
172        .map(|param| clean_generic_param_def(param, clean::ParamDefaults::No, cx))
173        .collect();
174
175    // FIXME(#111101): Incorporate the explicit predicates of the item here...
176    let item_clauses: FxIndexSet<_> =
177        tcx.param_env(item_def_id).caller_bounds().all_clauses(tcx).iter().collect();
178    let where_predicates = cx.with_exact_param_env(param_env, |cx| {
179        param_env
180            .caller_bounds()
181            .all_clauses(tcx)
182            .iter()
183            // FIXME: ...which hopefully allows us to simplify this:
184            .filter(|clause| {
185                !item_clauses.contains(clause)
186                    || clause.as_trait_clause().is_some_and(|clause| {
187                        tcx.lang_items().sized_trait() == Some(clause.def_id())
188                    })
189            })
190            .map(|clause| {
191                fold_regions(tcx, clause, |r, _| match r.kind() {
192                    // FIXME: Don't `unwrap_or`, I think we should panic if we encounter an infer var that
193                    // we can't map to a concrete region. However, `AutoTraitFinder` *does* leak those kinds
194                    // of `ReVar`s for some reason at the time of writing. See `rustdoc-ui/` tests.
195                    // This is in dire need of an investigation into `AutoTraitFinder`.
196                    ty::ReVar(vid) => vid_to_region.get(&vid).copied().unwrap_or(r),
197                    ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r,
198                    // FIXME(#120606): `AutoTraitFinder` can actually leak placeholder regions which feels
199                    // incorrect. Needs investigation.
200                    ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReErased => {
201                        bug!("unexpected region kind: {r:?}")
202                    }
203                })
204            })
205            .flat_map(|clause| clean_clause(clause, cx))
206            .chain(clean_region_outlives_constraints(&region_data, generics))
207            .collect()
208    });
209
210    let mut generics = clean::Generics { params, where_predicates };
211    simplify::sizedness_bounds(cx, &mut generics);
212    generics.where_predicates = simplify::where_clauses(cx.tcx, generics.where_predicates);
213    generics
214}
215
216/// Clean region outlives constraints to where-predicates.
217///
218/// This is essentially a simplified version of `lexical_region_resolve`.
219///
220/// However, here we determine what *needs to be* true in order for an impl to hold.
221/// `lexical_region_resolve`, along with much of the rest of the compiler, is concerned
222/// with determining if a given set up constraints / predicates *are* met, given some
223/// starting conditions like user-provided code.
224///
225/// For this reason, it's easier to perform the calculations we need on our own,
226/// rather than trying to make existing inference/solver code do what we want.
227fn clean_region_outlives_constraints<'tcx>(
228    regions: &RegionConstraintData<'tcx>,
229    generics: &'tcx ty::Generics,
230) -> ThinVec<clean::WherePredicate> {
231    // Our goal is to "flatten" the list of constraints by eliminating all intermediate
232    // `RegionVids` (region inference variables). At the end, all constraints should be
233    // between `Region`s. This gives us the information we need to create the where-predicates.
234    // This flattening is done in two parts.
235
236    let mut outlives_predicates = FxIndexMap::<_, Vec<_>>::default();
237    let mut map = FxIndexMap::<RegionTarget<'_>, auto_trait::RegionDeps<'_>>::default();
238
239    // (1)  We insert all of the constraints into a map.
240    // Each `RegionTarget` (a `RegionVid` or a `Region`) maps to its smaller and larger regions.
241    // Note that "larger" regions correspond to sub regions in the surface language.
242    // E.g., in `'a: 'b`, `'a` is the larger region.
243    for c in regions.constraints.iter().flat_map(|(c, _)| c.iter_outlives()) {
244        match c.kind {
245            ConstraintKind::VarSubVar => {
246                let sub_vid = c.sub.as_var();
247                let sup_vid = c.sup.as_var();
248                let deps1 = map.entry(RegionTarget::RegionVid(sub_vid)).or_default();
249                deps1.larger.insert(RegionTarget::RegionVid(sup_vid));
250
251                let deps2 = map.entry(RegionTarget::RegionVid(sup_vid)).or_default();
252                deps2.smaller.insert(RegionTarget::RegionVid(sub_vid));
253            }
254            ConstraintKind::RegSubVar => {
255                let sup_vid = c.sup.as_var();
256                let deps = map.entry(RegionTarget::RegionVid(sup_vid)).or_default();
257                deps.smaller.insert(RegionTarget::Region(c.sub));
258            }
259            ConstraintKind::VarSubReg => {
260                let sub_vid = c.sub.as_var();
261                let deps = map.entry(RegionTarget::RegionVid(sub_vid)).or_default();
262                deps.larger.insert(RegionTarget::Region(c.sup));
263            }
264            ConstraintKind::RegSubReg => {
265                // The constraint is already in the form that we want, so we're done with it
266                // The desired order is [larger, smaller], so flip them.
267                if early_bound_region_name(c.sub) != early_bound_region_name(c.sup) {
268                    outlives_predicates
269                        .entry(early_bound_region_name(c.sup).expect("no region_name found"))
270                        .or_default()
271                        .push(c.sub);
272                }
273            }
274            ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
275                unreachable!()
276            }
277        }
278    }
279
280    // (2)  Here, we "flatten" the map one element at a time. All of the elements' sub and super
281    // regions are connected to each other. For example, if we have a graph that looks like this:
282    //
283    //     (A, B) - C - (D, E)
284    //
285    // where (A, B) are sub regions, and (D,E) are super regions.
286    // Then, after deleting 'C', the graph will look like this:
287    //
288    //             ... - A - (D, E, ...)
289    //             ... - B - (D, E, ...)
290    //     (A, B, ...) - D - ...
291    //     (A, B, ...) - E - ...
292    //
293    // where '...' signifies the existing sub and super regions of an entry. When two adjacent
294    // `Region`s are encountered, we've computed a final constraint, and add it to our list.
295    // Since we make sure to never re-add deleted items, this process will always finish.
296    while !map.is_empty() {
297        let target = *map.keys().next().unwrap();
298        let deps = map.swap_remove(&target).unwrap();
299
300        for smaller in &deps.smaller {
301            for larger in &deps.larger {
302                match (smaller, larger) {
303                    (&RegionTarget::Region(smaller), &RegionTarget::Region(larger)) => {
304                        if early_bound_region_name(smaller) != early_bound_region_name(larger) {
305                            outlives_predicates
306                                .entry(
307                                    early_bound_region_name(larger).expect("no region name found"),
308                                )
309                                .or_default()
310                                .push(smaller)
311                        }
312                    }
313                    (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => {
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                    }
320                    (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => {
321                        if let IndexEntry::Occupied(v) = map.entry(*larger) {
322                            let deps = v.into_mut();
323                            deps.smaller.insert(*smaller);
324                            deps.smaller.swap_remove(&target);
325                        }
326                    }
327                    (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => {
328                        if let IndexEntry::Occupied(v) = map.entry(*smaller) {
329                            let smaller_deps = v.into_mut();
330                            smaller_deps.larger.insert(*larger);
331                            smaller_deps.larger.swap_remove(&target);
332                        }
333                        if let IndexEntry::Occupied(v) = map.entry(*larger) {
334                            let larger_deps = v.into_mut();
335                            larger_deps.smaller.insert(*smaller);
336                            larger_deps.smaller.swap_remove(&target);
337                        }
338                    }
339                }
340            }
341        }
342    }
343
344    let region_params: FxIndexSet<_> = generics
345        .own_params
346        .iter()
347        .filter_map(|param| match param.kind {
348            ty::GenericParamDefKind::Lifetime => Some(param.name),
349            _ => None,
350        })
351        .collect();
352
353    region_params
354        .iter()
355        .filter_map(|&name| {
356            let bounds: FxIndexSet<_> = outlives_predicates
357                .get(&name)?
358                .iter()
359                .map(|&region| {
360                    let lifetime = early_bound_region_name(region)
361                        .inspect(|name| assert!(region_params.contains(name)))
362                        .map(Lifetime)
363                        .unwrap_or(Lifetime::statik());
364                    clean::GenericBound::Outlives(lifetime)
365                })
366                .collect();
367            if bounds.is_empty() {
368                return None;
369            }
370            Some(clean::WherePredicate::RegionPredicate {
371                lifetime: Lifetime(name),
372                bounds: bounds.into_iter().collect(),
373            })
374        })
375        .collect()
376}
377
378fn early_bound_region_name(region: Region<'_>) -> Option<Symbol> {
379    match region.kind() {
380        ty::ReEarlyParam(r) => Some(r.name),
381        _ => None,
382    }
383}