@@ -17,6 +17,7 @@ use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
17
17
use rustc_metadata:: rendered_const;
18
18
use rustc_middle:: mir;
19
19
use rustc_middle:: ty:: { self , GenericArgKind , GenericArgsRef , TyCtxt } ;
20
+ use rustc_middle:: ty:: { TypeVisitable , TypeVisitableExt } ;
20
21
use rustc_span:: symbol:: { kw, sym, Symbol } ;
21
22
use std:: fmt:: Write as _;
22
23
use std:: mem;
@@ -76,40 +77,120 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
76
77
77
78
pub ( crate ) fn ty_args_to_args < ' tcx > (
78
79
cx : & mut DocContext < ' tcx > ,
79
- args : ty:: Binder < ' tcx , & ' tcx [ ty:: GenericArg < ' tcx > ] > ,
80
+ ty_args : ty:: Binder < ' tcx , & ' tcx [ ty:: GenericArg < ' tcx > ] > ,
80
81
has_self : bool ,
81
82
container : Option < DefId > ,
82
83
) -> 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 ;
95
108
}
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) ,
98
112
cx,
99
113
None ,
100
114
container. map ( |container| crate :: clean:: ContainerTy :: Regular {
101
115
ty : container,
102
- args,
116
+ args : ty_args ,
103
117
has_self,
104
118
arg : index,
105
119
} ) ,
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 ;
109
135
}
136
+
137
+ Some ( GenericArg :: Const ( Box :: new ( clean_middle_const ( ty_args. rebind ( ct) , cx) ) ) )
110
138
}
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 ( )
113
194
}
114
195
115
196
fn external_generic_args < ' tcx > (
0 commit comments