@@ -17,6 +17,7 @@ use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
1717use rustc_metadata:: rendered_const;
1818use rustc_middle:: mir;
1919use rustc_middle:: ty:: { self , GenericArgKind , GenericArgsRef , TyCtxt } ;
20+ use rustc_middle:: ty:: { TypeVisitable , TypeVisitableExt } ;
2021use rustc_span:: symbol:: { kw, sym, Symbol } ;
2122use std:: fmt:: Write as _;
2223use std:: mem;
@@ -76,40 +77,120 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
7677
7778pub ( crate ) fn ty_args_to_args < ' tcx > (
7879 cx : & mut DocContext < ' tcx > ,
79- args : ty:: Binder < ' tcx , & ' tcx [ ty:: GenericArg < ' tcx > ] > ,
80+ ty_args : ty:: Binder < ' tcx , & ' tcx [ ty:: GenericArg < ' tcx > ] > ,
8081 has_self : bool ,
8182 container : Option < DefId > ,
8283) -> Vec < GenericArg > {
83- let mut skip_first = has_self;
84- let mut ret_val =
85- Vec :: with_capacity ( args. skip_binder ( ) . len ( ) . saturating_sub ( if skip_first { 1 } else { 0 } ) ) ;
86-
87- ret_val. extend ( args. iter ( ) . enumerate ( ) . filter_map ( |( index, kind) | {
88- match kind. skip_binder ( ) . unpack ( ) {
89- GenericArgKind :: Lifetime ( lt) => {
90- Some ( GenericArg :: Lifetime ( clean_middle_region ( lt) . unwrap_or ( Lifetime :: elided ( ) ) ) )
91- }
92- GenericArgKind :: Type ( _) if skip_first => {
93- skip_first = false ;
94- None
84+ let params = container. map ( |container| & cx. tcx . generics_of ( container) . params ) ;
85+ let mut elision_has_failed_once_before = false ;
86+
87+ let offset = if has_self { 1 } else { 0 } ;
88+ let mut args = Vec :: with_capacity ( ty_args. skip_binder ( ) . len ( ) . saturating_sub ( offset) ) ;
89+
90+ let ty_arg_to_arg = |( index, arg) : ( usize , & ty:: GenericArg < ' tcx > ) | match arg. unpack ( ) {
91+ GenericArgKind :: Lifetime ( lt) => {
92+ Some ( GenericArg :: Lifetime ( clean_middle_region ( lt) . unwrap_or ( Lifetime :: elided ( ) ) ) )
93+ }
94+ GenericArgKind :: Type ( _) if has_self && index == 0 => None ,
95+ GenericArgKind :: Type ( ty) => {
96+ if !elision_has_failed_once_before
97+ && let Some ( params) = params
98+ && let Some ( default) = params[ index] . default_value ( cx. tcx )
99+ {
100+ let default =
101+ ty_args. map_bound ( |args| default. instantiate ( cx. tcx , args) . expect_ty ( ) ) ;
102+
103+ if can_elide_generic_arg ( ty_args. rebind ( ty) , default) {
104+ return None ;
105+ }
106+
107+ elision_has_failed_once_before = true ;
95108 }
96- GenericArgKind :: Type ( ty) => Some ( GenericArg :: Type ( clean_middle_ty (
97- kind. rebind ( ty) ,
109+
110+ Some ( GenericArg :: Type ( clean_middle_ty (
111+ ty_args. rebind ( ty) ,
98112 cx,
99113 None ,
100114 container. map ( |container| crate :: clean:: ContainerTy :: Regular {
101115 ty : container,
102- args,
116+ args : ty_args ,
103117 has_self,
104118 arg : index,
105119 } ) ,
106- ) ) ) ,
107- GenericArgKind :: Const ( ct) => {
108- Some ( GenericArg :: Const ( Box :: new ( clean_middle_const ( kind. rebind ( ct) , cx) ) ) )
120+ ) ) )
121+ }
122+ GenericArgKind :: Const ( ct) => {
123+ if !elision_has_failed_once_before
124+ && let Some ( params) = params
125+ && let Some ( default) = params[ index] . default_value ( cx. tcx )
126+ {
127+ let default =
128+ ty_args. map_bound ( |args| default. instantiate ( cx. tcx , args) . expect_const ( ) ) ;
129+
130+ if can_elide_generic_arg ( ty_args. rebind ( ct) , default) {
131+ return None ;
132+ }
133+
134+ elision_has_failed_once_before = true ;
109135 }
136+
137+ Some ( GenericArg :: Const ( Box :: new ( clean_middle_const ( ty_args. rebind ( ct) , cx) ) ) )
110138 }
111- } ) ) ;
112- ret_val
139+ } ;
140+
141+ args. extend ( ty_args. skip_binder ( ) . iter ( ) . enumerate ( ) . rev ( ) . filter_map ( ty_arg_to_arg) ) ;
142+ args. reverse ( ) ;
143+ args
144+ }
145+
146+ /// Check if the generic argument `actual` coincides with the `default` and can therefore be elided.
147+ ///
148+ /// This uses a very conservative approach for performance and correctness reasons, meaning for
149+ /// several classes of terms it claims that they cannot be elided even if they theoretically could.
150+ /// This is absolutely fine since this concerns mostly edge cases.
151+ fn can_elide_generic_arg < ' tcx , Term > (
152+ actual : ty:: Binder < ' tcx , Term > ,
153+ default : ty:: Binder < ' tcx , Term > ,
154+ ) -> bool
155+ where
156+ Term : Eq + TypeVisitable < TyCtxt < ' tcx > > ,
157+ {
158+ // In practice, we shouldn't have any inference variables at this point. However to be safe, we
159+ // bail out if we do happen to stumble upon them. For performance reasons, we don't want to
160+ // construct an `InferCtxt` here to properly handle them.
161+ if actual. has_infer ( ) || default. has_infer ( ) {
162+ return false ;
163+ }
164+
165+ // Since we don't properly keep track of bound variables in rustdoc (yet), we don't attempt to
166+ // make any sense out of escaping bound variables. We simply don't have enough context and it
167+ // would be incorrect to try to do so anyway.
168+ if actual. has_escaping_bound_vars ( ) || default. has_escaping_bound_vars ( ) {
169+ return false ;
170+ }
171+
172+ // Theoretically we could now check if either term contains (non-escaping) late-bound regions or
173+ // projections, relate the two using an `InferCtxt` and check if the resulting obligations hold
174+ // since having projections means that the terms can potentially be further normalized thereby
175+ // revealing if they are equal after all. Regarding late-bound regions, they would need to be
176+ // liberated allowing us to consider more types to be equal by ignoring the names of binders
177+ // (e.g., `for<'a> ...` and `for<'b> ...`).
178+ //
179+ // However, we are mostly interested in eliding generic args that were originally elided by the
180+ // user and later filled in by the compiler (i.e., re-eliding) compared to eliding arbitrary
181+ // generic arguments if they happen to coincide with the default ignoring the fact we can't
182+ // possibly distinguish these two cases. Therefore and for performance reasons, we just bail out
183+ // instead.
184+ if actual. has_late_bound_regions ( )
185+ || actual. has_projections ( )
186+ || default. has_late_bound_regions ( )
187+ || default. has_projections ( )
188+ {
189+ return false ;
190+ }
191+
192+ // Check the memory addresses of the interned arguments for equality.
193+ actual. skip_binder ( ) == default. skip_binder ( )
113194}
114195
115196fn external_generic_args < ' tcx > (
0 commit comments