1 //! Simplification of where-clauses and parameter bounds into a prettier and
2 //! more canonical form.
4 //! Currently all cross-crate-inlined function use `rustc_middle::ty` to reconstruct
5 //! the AST (e.g., see all of `clean::inline`), but this is not always a
6 //! non-lossy transformation. The current format of storage for where-clauses
7 //! for functions and such is simply a list of predicates. One example of this
8 //! is that the AST predicate of: `where T: Trait<Foo = Bar>` is encoded as:
9 //! `where T: Trait, <T as Trait>::Foo = Bar`.
11 //! This module attempts to reconstruct the original where and/or parameter
12 //! bounds by special casing scenarios such as these. Fun!
14 use rustc_data_structures
::fx
::FxIndexMap
;
15 use rustc_hir
::def_id
::DefId
;
17 use thin_vec
::ThinVec
;
20 use crate::clean
::GenericArgs
as PP
;
21 use crate::clean
::WherePredicate
as WP
;
22 use crate::core
::DocContext
;
24 pub(crate) fn where_clauses(cx
: &DocContext
<'_
>, clauses
: Vec
<WP
>) -> ThinVec
<WP
> {
25 // First, partition the where clause into its separate components.
27 // We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to
28 // the order of the generated bounds.
29 let mut tybounds
= FxIndexMap
::default();
30 let mut lifetimes
= Vec
::new();
31 let mut equalities
= Vec
::new();
33 for clause
in clauses
{
35 WP
::BoundPredicate { ty, bounds, bound_params }
=> {
36 let (b
, p
): &mut (Vec
<_
>, Vec
<_
>) = tybounds
.entry(ty
).or_default();
38 p
.extend(bound_params
);
40 WP
::RegionPredicate { lifetime, bounds }
=> {
41 lifetimes
.push((lifetime
, bounds
));
43 WP
::EqPredicate { lhs, rhs, bound_params }
=> equalities
.push((lhs
, rhs
, bound_params
)),
47 // Look for equality predicates on associated types that can be merged into
48 // general bound predicates.
49 equalities
.retain(|(lhs
, rhs
, bound_params
)| {
50 let Some((ty
, trait_did
, name
)) = lhs
.projection() else { return true; }
;
51 let Some((bounds
, _
)) = tybounds
.get_mut(ty
) else { return true }
;
52 let bound_params
= bound_params
54 .map(|param
| clean
::GenericParamDef
::lifetime(param
.0))
56 merge_bounds(cx
, bounds
, bound_params
, trait_did
, name
, rhs
)
59 // And finally, let's reassemble everything
60 let mut clauses
= ThinVec
::with_capacity(lifetimes
.len() + tybounds
.len() + equalities
.len());
62 lifetimes
.into_iter().map(|(lt
, bounds
)| WP
::RegionPredicate { lifetime: lt, bounds }
),
64 clauses
.extend(tybounds
.into_iter().map(|(ty
, (bounds
, bound_params
))| WP
::BoundPredicate
{
69 clauses
.extend(equalities
.into_iter().map(|(lhs
, rhs
, bound_params
)| WP
::EqPredicate
{
77 pub(crate) fn merge_bounds(
78 cx
: &clean
::DocContext
<'_
>,
79 bounds
: &mut Vec
<clean
::GenericBound
>,
80 mut bound_params
: Vec
<clean
::GenericParamDef
>,
82 assoc
: clean
::PathSegment
,
85 !bounds
.iter_mut().any(|b
| {
86 let trait_ref
= match *b
{
87 clean
::GenericBound
::TraitBound(ref mut tr
, _
) => tr
,
88 clean
::GenericBound
::Outlives(..) => return false,
90 // If this QPath's trait `trait_did` is the same as, or a supertrait
91 // of, the bound's trait `did` then we can keep going, otherwise
92 // this is just a plain old equality bound.
93 if !trait_is_same_or_supertrait(cx
, trait_ref
.trait_
.def_id(), trait_did
) {
96 let last
= trait_ref
.trait_
.segments
.last_mut().expect("segments were empty");
98 trait_ref
.generic_params
.append(&mut bound_params
);
99 // Sort parameters (likely) originating from a hashset alphabetically to
100 // produce predictable output (and to allow for full deduplication).
101 trait_ref
.generic_params
.sort_unstable_by(|p
, q
| p
.name
.as_str().cmp(q
.name
.as_str()));
102 trait_ref
.generic_params
.dedup_by_key(|p
| p
.name
);
105 PP
::AngleBracketed { ref mut bindings, .. }
=> {
106 bindings
.push(clean
::TypeBinding
{
107 assoc
: assoc
.clone(),
108 kind
: clean
::TypeBindingKind
::Equality { term: rhs.clone() }
,
111 PP
::Parenthesized { ref mut output, .. }
=> match output
{
112 Some(o
) => assert_eq
!(&clean
::Term
::Type(o
.as_ref().clone()), rhs
),
114 if *rhs
!= clean
::Term
::Type(clean
::Type
::Tuple(Vec
::new())) {
115 *output
= Some(Box
::new(rhs
.ty().unwrap().clone()));
124 fn trait_is_same_or_supertrait(cx
: &DocContext
<'_
>, child
: DefId
, trait_
: DefId
) -> bool
{
128 let predicates
= cx
.tcx
.super_predicates_of(child
);
129 debug_assert
!(cx
.tcx
.generics_of(child
).has_self
);
130 let self_ty
= cx
.tcx
.types
.self_param
;
134 .filter_map(|(pred
, _
)| {
135 if let ty
::PredicateKind
::Clause(ty
::Clause
::Trait(pred
)) = pred
.kind().skip_binder() {
136 if pred
.trait_ref
.self_ty() == self_ty { Some(pred.def_id()) }
else { None }
141 .any(|did
| trait_is_same_or_supertrait(cx
, did
, trait_
))