Repository navigation
rustdoc's resugaring of projection predicates to type bindings is flawed #113015
Description
First discovered thanks to bevyengine/bevy#8898: There, the Output type of the cross-crate opaque impl FnMut(…) -> bool gets dropped by rustdoc resulting in impl FnMut(…). This specific bug is caused by rustdoc's opaque bound cleaner not realizing that it can & should utilize the Output assoc ty defined on FnMut's supertrait FnOnce. During further investigations I've discovered more bugs in this area.
In rustdoc there are two three major and distinct places where we resugar bounds. Both All three are flawed but each one exhibits different bugs.
- (1)
clean_middle_opaque_boundsfor bounds of opaques (existential impl-Trait)- we only check for
TraitRefequality while we should also check the super predicates (incl. their substs tho!)- (once we do, ensure that we don't intro name clashes into the list of type bindings (this can be triggered by relatively simple user code involving supertraits))
- we only check for
- (2)
clean_ty_genericsfor bounds in where-clauses and APIT (universal impl-Trait)- we do check super predicates but we only ever check
DefIds (of traits) and never the relevant substs which is super incorrect leading rustdoc to move some projection predicates to unrelated trait bounds - the process mutates state by yanking those predicates rendering it order dependent (consider permutations of super and subtraits each one with own type bindings)
- we do check super predicates but we only ever check
- (3)
param_env_to_genericsinauto_trait.rsfor where-clauses of synthetic impls- update: replaced by
simplify::{where_clauses,sized_bounds}in #123340 - doesn't use
simplify::merge_boundsat all unlike the other two places - doesn't merge two partially resugared bounds like
Tr<A = …>andTr<B = …> - doesn't render bound vars / higher-ranked parameters at all
- (I haven't thoroughly checked yet how supertraits are handled but it seems like
Fn-family traits are special-cased)
- update: replaced by
Probably there are some more but less important bugs to be found (e.g. related to bound vars I can imagine).
I'm already deep in the weeds working on a correct & most general fix for this.
Opening this issue to keep the description of the future PR more slender.
@rustbot claim
@rustbot label C-bug T-rustdoc A-cross-crate-reexports
Activity
[-]rustdoc's resugaring of projections predicates to type bindings is flawed when supertraits are involved[/-][+]rustdoc's resugaring of projection predicates to type bindings is flawed when supertraits are involved[/+]Rollup merge of rust-lang#116388 - fmease:rustdoc-fix-n-clean-up-x-cr…
Unrolled build for rust-lang#116388
RalfJung commentedon Mar 23, 2024
The duplicate
Target = [u8]here is another instance of this bug.Auto merge of rust-lang#122974 - fmease:rustdoc-auto-trait-experiment…
Auto merge of rust-lang#123340 - fmease:rustdoc-simplify-auto-trait-i…
Auto merge of rust-lang#123340 - fmease:rustdoc-simplify-auto-trait-i…
fmease commentedon Apr 16, 2024
Major: OPAQ: We don't take supertrait bounds into account when reconstructing assoc item constraints (“type bindings”) from projections (#116735 would've fixed that but I've closed it in favor of a more general PR).
Source
Rendered inlined cross-crate re-export
Source
Rendered inlined cross-crate re-export
fmease commentedon Apr 16, 2024
Major: WHERE & APIT: We don't take generic arguments of super trait bounds into account when reconstructing assoc item constraints (“type bindings”) from projections, like at all!
Source
Rendered inlined cross-crate re-export
Source
Rendered inline cross-crate re-export
fmease commentedon Apr 16, 2024
Minor: WHERE: We don't try to reconstruct overarching / outer binders of clauses.
Source
Rendered inlined cross-crate re-export
Edit: Medium: WHERE: We drop the "outer" binder of type-outlives predicates:
Source
Rendered inlined cross-crate re-export
fmease commentedon Apr 16, 2024
Minor: WHERE: We don't necessarily preserve the order bounds that differ in LHS (bounded ty, lt). However, the HIR cleaner doesn't seem to do that either (requires a different reproducer though).
Source
Rendered inlined cross-crate re-export
Order is lifetime bounds, then type bounds(, then equality bounds). This happens in
simplify::where_clauses.fmease commentedon Apr 16, 2024
Minor: OPAQ: We don't necessarily preserve the order of bounds, we move traits in front of lifetime bounds.
fmease commentedon Apr 16, 2024
Medium: WHERE & APIT: The cleaning process is order-dependent.
[-]rustdoc's resugaring of projection predicates to type bindings is flawed when supertraits are involved[/-][+]rustdoc's resugaring of projection predicates to type bindings is flawed[/+]jethrogb commentedon Mar 12, 2026
Is this the same bug? https://docs.rs/kube/latest/kube/trait.Resource.html
This renders as:
instead of the (IMO, much more logical):
fmease commentedon Mar 18, 2026
Yes it is. We should add the binding to the most specific trait bound. At the moment, it's order dependent, see also #113015 (comment). It can be boiled down to: