}
if let ty::TraitContainer = assoc_item.container {
+ // FIXME(fmease): `tcx.explicit_item_bounds` does not contain the bounds of GATs,
+ // e.g. the bounds `Copy`, `Display` & (implicitly) `Sized` in
+ // `type Assoc<T: Copy> where T: Display`. This also means that we
+ // later incorrectly render `where T: ?Sized`.
+ //
+ // The result of `tcx.explicit_predicates_of` *does* contain them but
+ // it does not contain the other bounds / predicates we need.
+ // Either merge those two interned lists somehow or refactor
+ // `clean_ty_generics` to call `explicit_item_bounds` by itself.
let bounds = tcx.explicit_item_bounds(assoc_item.def_id);
let predicates = ty::GenericPredicates { parent: None, predicates: bounds };
let mut generics =
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def_id::DefId;
use rustc_middle::ty;
-use rustc_span::Symbol;
use crate::clean;
use crate::clean::GenericArgs as PP;
//
// We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to
// the order of the generated bounds.
- let mut params: FxIndexMap<Symbol, (Vec<_>, Vec<_>)> = FxIndexMap::default();
+ let mut tybounds = FxIndexMap::default();
let mut lifetimes = Vec::new();
let mut equalities = Vec::new();
- let mut tybounds = Vec::new();
for clause in clauses {
match clause {
- WP::BoundPredicate { ty, bounds, bound_params } => match ty {
- clean::Generic(s) => {
- let (b, p) = params.entry(s).or_default();
- b.extend(bounds);
- p.extend(bound_params);
- }
- t => tybounds.push((t, (bounds, bound_params))),
- },
+ WP::BoundPredicate { ty, bounds, bound_params } => {
+ let (b, p): &mut (Vec<_>, Vec<_>) = tybounds.entry(ty).or_default();
+ b.extend(bounds);
+ p.extend(bound_params);
+ }
WP::RegionPredicate { lifetime, bounds } => {
lifetimes.push((lifetime, bounds));
}
}
// Look for equality predicates on associated types that can be merged into
- // general bound predicates
+ // general bound predicates.
equalities.retain(|&(ref lhs, ref rhs)| {
- let Some((self_, trait_did, name)) = lhs.projection() else {
- return true;
- };
- let clean::Generic(generic) = self_ else { return true };
- let Some((bounds, _)) = params.get_mut(generic) else { return true };
-
+ let Some((ty, trait_did, name)) = lhs.projection() else { return true; };
+ // FIXME(fmease): We don't handle HRTBs correctly here.
+ // Pass `_bound_params` (higher-rank lifetimes) to a modified version of
+ // `merge_bounds`. That vector is currently always empty though since we
+ // don't keep track of late-bound lifetimes when cleaning projection
+ // predicates to cleaned equality predicates while we should first query
+ // them with `collect_referenced_late_bound_regions` and then store them
+ // (or something similar). For prior art, see `clean::auto_trait`.
+ let Some((bounds, _bound_params)) = tybounds.get_mut(ty) else { return true };
merge_bounds(cx, bounds, trait_did, name, rhs)
});
clauses.extend(
lifetimes.into_iter().map(|(lt, bounds)| WP::RegionPredicate { lifetime: lt, bounds }),
);
- clauses.extend(params.into_iter().map(|(k, (bounds, params))| WP::BoundPredicate {
- ty: clean::Generic(k),
- bounds,
- bound_params: params,
- }));
clauses.extend(tybounds.into_iter().map(|(ty, (bounds, bound_params))| WP::BoundPredicate {
ty,
bounds,
--- /dev/null
+<h4 class="code-header">type <a href="#associatedtype.Out0" class="associatedtype">Out0</a>: <a class="trait" href="../assoc_item_trait_bounds_with_bindings/trait.Support.html" title="trait assoc_item_trait_bounds_with_bindings::Support">Support</a><Item = <a class="primitive" href="{{channel}}/std/primitive.unit.html">()</a>></h4>
\ No newline at end of file
--- /dev/null
+<h4 class="code-header">type <a href="#associatedtype.Out9" class="associatedtype">Out9</a>: <a class="trait" href="{{channel}}/core/ops/function/trait.FnMut.html" title="trait core::ops::function::FnMut">FnMut</a>(<a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a>) -> <a class="primitive" href="{{channel}}/std/primitive.bool.html">bool</a> + <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a></h4>
\ No newline at end of file
--- /dev/null
+// Regression test for issues #77763, #84579 and #102142.
+#![crate_name = "main"]
+
+// aux-build:assoc_item_trait_bounds_with_bindings.rs
+// build-aux-docs
+// ignore-cross-compile
+extern crate assoc_item_trait_bounds_with_bindings as aux;
+
+// FIXME(fmease): Don't render an incorrect `T: ?Sized` where-clause for parameters
+// of GATs like `Main::Out{2,4}`. Add a snapshot test once it's fixed.
+// FIXME(fmease): Print the `for<>` parameter list in the bounds of
+// `Main::Out{6,11,12}`.
+
+// @has main/trait.Main.html
+// @has - '//*[@id="associatedtype.Out0"]' 'type Out0: Support<Item = ()>'
+// @has - '//*[@id="associatedtype.Out1"]' 'type Out1: Support<Item = Self::Item>'
+// @has - '//*[@id="associatedtype.Out2"]' 'type Out2<T>: Support<Item = T>'
+// @has - '//*[@id="associatedtype.Out3"]' 'type Out3: Support<Produce<()> = bool>'
+// @has - '//*[@id="associatedtype.Out4"]' 'type Out4<T>: Support<Produce<T> = T>'
+// @has - '//*[@id="associatedtype.Out5"]' "type Out5: Support<Output<'static> = &'static ()>"
+// @has - '//*[@id="associatedtype.Out6"]' "type Out6: Support<Output<'a> = &'a ()>"
+// @has - '//*[@id="associatedtype.Out7"]' "type Out7: Support<Item = String, Produce<i32> = u32> + Unrelated"
+// @has - '//*[@id="associatedtype.Out8"]' "type Out8: Unrelated + Protocol<i16, Q1 = u128, Q0 = ()>"
+// @has - '//*[@id="associatedtype.Out9"]' "type Out9: FnMut(i32) -> bool + Clone"
+// @has - '//*[@id="associatedtype.Out10"]' "type Out10<'q>: Support<Output<'q> = ()>"
+// @has - '//*[@id="associatedtype.Out11"]' "type Out11: Helper<A<'s> = &'s (), B<'r> = ()>"
+// @has - '//*[@id="associatedtype.Out12"]' "type Out12: Helper<B<'w> = Cow<'w, str>, A<'w> = bool>"
+//
+// Snapshots: Check that we do not render any where-clauses for those associated types since all of
+// the trait bounds contained within were moved to the bounds of the respective item.
+//
+// @snapshot out0 - '//*[@id="associatedtype.Out0"]/*[@class="code-header"]'
+// @snapshot out9 - '//*[@id="associatedtype.Out9"]/*[@class="code-header"]'
+//
+// @has - '//*[@id="tymethod.make"]' \
+// "fn make<F>(F, impl FnMut(&str) -> bool)\
+// where \
+// F: FnOnce(u32) -> String, \
+// Self::Out2<()>: Protocol<u8, Q0 = Self::Item, Q1 = ()>"
+pub use aux::Main;
--- /dev/null
+pub trait Main {
+ type Item;
+
+ type Out0: Support<Item = ()>;
+ type Out1: Support<Item = Self::Item>;
+ type Out2<T>: Support<Item = T>;
+ type Out3: Support<Produce<()> = bool>;
+ type Out4<T>: Support<Produce<T> = T>;
+ type Out5: Support<Output<'static> = &'static ()>;
+ type Out6: for<'a> Support<Output<'a> = &'a ()>;
+ type Out7: Support<Item = String, Produce<i32> = u32> + Unrelated;
+ type Out8: Unrelated + Protocol<i16, Q1 = u128, Q0 = ()>;
+ type Out9: FnMut(i32) -> bool + Clone;
+ type Out10<'q>: Support<Output<'q> = ()>;
+ type Out11: for<'r, 's> Helper<A<'s> = &'s (), B<'r> = ()>;
+ type Out12: for<'w> Helper<B<'w> = std::borrow::Cow<'w, str>, A<'w> = bool>;
+
+ fn make<F>(_: F, _: impl FnMut(&str) -> bool)
+ where
+ F: FnOnce(u32) -> String,
+ Self::Out2<()>: Protocol<u8, Q0 = Self::Item, Q1 = ()>;
+}
+
+pub trait Support {
+ type Item;
+ type Output<'a>;
+ type Produce<T>;
+}
+
+pub trait Protocol<K> {
+ type Q0;
+ type Q1;
+}
+
+pub trait Unrelated {}
+
+pub trait Helper {
+ type A<'q>;
+ type B<'q>;
+}