rustdoc/clean/
auto_trait.rs

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    // We are only interested in case the type *doesn't* implement the `Sized` trait.
46    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            // For negative impls, we use the generic params, but *not* the predicates,
94            // from the original type. Otherwise, the displayed impl appears to be a
95            // conditional negative impl, when it's really unconditional.
96            //
97            // For example, consider the struct Foo<T: Copy>(*mut T). Using
98            // the original predicates in our impl would cause us to generate
99            // `impl !Send for Foo<T: Copy>`, which makes it appear that Foo
100            // implements Send where T is not copy.
101            //
102            // Instead, we generate `impl !Send for Foo<T>`, which better
103            // expresses the fact that `Foo<T>` never implements `Send`,
104            // regardless of the choice of `T`.
105            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        // We're basing the generics of the synthetic auto trait impl off of the generics of the
167        // implementing type. Its generic parameters may have defaults, don't copy them over:
168        // Generic parameter defaults are meaningless in impls.
169        .map(|param| clean_generic_param_def(param, clean::ParamDefaults::No, cx))
170        .collect();
171
172    // FIXME(#111101): Incorporate the explicit predicates of the item here...
173    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        // FIXME: ...which hopefully allows us to simplify this:
179        .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                // FIXME: Don't `unwrap_or`, I think we should panic if we encounter an infer var that
188                // we can't map to a concrete region. However, `AutoTraitFinder` *does* leak those kinds
189                // of `ReVar`s for some reason at the time of writing. See `rustdoc-ui/` tests.
190                // This is in dire need of an investigation into `AutoTraitFinder`.
191                ty::ReVar(vid) => vid_to_region.get(&vid).copied().unwrap_or(r),
192                ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r,
193                // FIXME(#120606): `AutoTraitFinder` can actually leak placeholder regions which feels
194                // incorrect. Needs investigation.
195                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(&region_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
210/// Clean region outlives constraints to where-predicates.
211///
212/// This is essentially a simplified version of `lexical_region_resolve`.
213///
214/// However, here we determine what *needs to be* true in order for an impl to hold.
215/// `lexical_region_resolve`, along with much of the rest of the compiler, is concerned
216/// with determining if a given set up constraints / predicates *are* met, given some
217/// starting conditions like user-provided code.
218///
219/// For this reason, it's easier to perform the calculations we need on our own,
220/// rather than trying to make existing inference/solver code do what we want.
221fn clean_region_outlives_constraints<'tcx>(
222    regions: &RegionConstraintData<'tcx>,
223    generics: &'tcx ty::Generics,
224) -> ThinVec<clean::WherePredicate> {
225    // Our goal is to "flatten" the list of constraints by eliminating all intermediate
226    // `RegionVids` (region inference variables). At the end, all constraints should be
227    // between `Region`s. This gives us the information we need to create the where-predicates.
228    // This flattening is done in two parts.
229
230    let mut outlives_predicates = FxIndexMap::<_, Vec<_>>::default();
231    let mut map = FxIndexMap::<RegionTarget<'_>, auto_trait::RegionDeps<'_>>::default();
232
233    // (1)  We insert all of the constraints into a map.
234    // Each `RegionTarget` (a `RegionVid` or a `Region`) maps to its smaller and larger regions.
235    // Note that "larger" regions correspond to sub regions in the surface language.
236    // E.g., in `'a: 'b`, `'a` is the larger region.
237    for (constraint, _) in &regions.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                // The constraint is already in the form that we want, so we're done with it
256                // The desired order is [larger, smaller], so flip them.
257                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    // (2)  Here, we "flatten" the map one element at a time. All of the elements' sub and super
268    // regions are connected to each other. For example, if we have a graph that looks like this:
269    //
270    //     (A, B) - C - (D, E)
271    //
272    // where (A, B) are sub regions, and (D,E) are super regions.
273    // Then, after deleting 'C', the graph will look like this:
274    //
275    //             ... - A - (D, E, ...)
276    //             ... - B - (D, E, ...)
277    //     (A, B, ...) - D - ...
278    //     (A, B, ...) - E - ...
279    //
280    // where '...' signifies the existing sub and super regions of an entry. When two adjacent
281    // `Region`s are encountered, we've computed a final constraint, and add it to our list.
282    // Since we make sure to never re-add deleted items, this process will always finish.
283    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(|&region| {
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}