@@ -14,16 +14,11 @@ use rustc_ast::tokenstream::TokenTree;
14
14
use rustc_hir as hir;
15
15
use rustc_hir:: def:: { DefKind , Res } ;
16
16
use rustc_hir:: def_id:: { DefId , LocalDefId , LOCAL_CRATE } ;
17
- use rustc_infer:: infer:: at:: ToTrace ;
18
- use rustc_infer:: infer:: outlives:: env:: OutlivesEnvironment ;
19
17
use rustc_metadata:: rendered_const;
20
18
use rustc_middle:: mir;
21
- use rustc_middle:: traits:: ObligationCause ;
22
19
use rustc_middle:: ty:: { self , GenericArgKind , GenericArgsRef , TyCtxt } ;
23
20
use rustc_middle:: ty:: { TypeVisitable , TypeVisitableExt } ;
24
21
use rustc_span:: symbol:: { kw, sym, Symbol } ;
25
- use rustc_trait_selection:: infer:: TyCtxtInferExt ;
26
- use rustc_trait_selection:: traits:: ObligationCtxt ;
27
22
use std:: fmt:: Write as _;
28
23
use std:: mem;
29
24
use std:: sync:: LazyLock as Lazy ;
@@ -86,8 +81,6 @@ pub(crate) fn ty_args_to_args<'tcx>(
86
81
has_self : bool ,
87
82
container : Option < DefId > ,
88
83
) -> Vec < GenericArg > {
89
- let param_env = ty:: ParamEnv :: empty ( ) ;
90
- let cause = ObligationCause :: dummy ( ) ;
91
84
let params = container. map ( |container| & cx. tcx . generics_of ( container) . params ) ;
92
85
let mut elision_has_failed_once_before = false ;
93
86
@@ -107,14 +100,7 @@ pub(crate) fn ty_args_to_args<'tcx>(
107
100
let default =
108
101
ty_args. map_bound ( |args| default. instantiate ( cx. tcx , args) . expect_ty ( ) ) ;
109
102
110
- if can_elide_generic_arg (
111
- cx. tcx ,
112
- & cause,
113
- param_env,
114
- ty_args. rebind ( ty) ,
115
- default,
116
- params[ index] . def_id ,
117
- ) {
103
+ if can_elide_generic_arg ( ty_args. rebind ( ty) , default) {
118
104
return None ;
119
105
}
120
106
@@ -150,14 +136,7 @@ pub(crate) fn ty_args_to_args<'tcx>(
150
136
let default =
151
137
ty_args. map_bound ( |args| default. instantiate ( cx. tcx , args) . expect_const ( ) ) ;
152
138
153
- if can_elide_generic_arg (
154
- cx. tcx ,
155
- & cause,
156
- param_env,
157
- ty_args. rebind ( ct) ,
158
- default,
159
- params[ index] . def_id ,
160
- ) {
139
+ if can_elide_generic_arg ( ty_args. rebind ( ct) , default) {
161
140
return None ;
162
141
}
163
142
@@ -174,50 +153,44 @@ pub(crate) fn ty_args_to_args<'tcx>(
174
153
}
175
154
176
155
/// Check if the generic argument `actual` coincides with the `default` and can therefore be elided.
177
- fn can_elide_generic_arg < ' tcx , T : ToTrace < ' tcx > + TypeVisitable < TyCtxt < ' tcx > > > (
178
- tcx : TyCtxt < ' tcx > ,
179
- cause : & ObligationCause < ' tcx > ,
180
- param_env : ty :: ParamEnv < ' tcx > ,
181
- actual : ty :: Binder < ' tcx , T > ,
182
- default : ty:: Binder < ' tcx , T > ,
183
- did : DefId ,
184
- ) -> bool {
185
- // The operations below are only correct if we don't have any inference variables.
186
- debug_assert ! ( !actual . has_infer ( ) ) ;
187
- debug_assert ! ( ! default . has_infer ( ) ) ;
188
-
189
- // Since we don't properly keep track of bound variables, don't attempt to make
190
- // any sense out of escaping bound variables (we just don't have enough context) .
191
- if actual. has_escaping_bound_vars ( ) || default. has_escaping_bound_vars ( ) {
156
+ ///
157
+ /// This uses a very conservative approach for performance and correctness reasons, meaning for
158
+ /// several classes of terms it claims that they cannot be elided even if they theoretically could.
159
+ /// This is absolutely fine since this concerns mostly edge cases.
160
+ fn can_elide_generic_arg < ' tcx , Term > (
161
+ actual : ty:: Binder < ' tcx , Term > ,
162
+ default : ty :: Binder < ' tcx , Term > ,
163
+ ) -> bool
164
+ where
165
+ Term : Eq + TypeVisitable < TyCtxt < ' tcx > > ,
166
+ {
167
+ // In practice, we shouldn't have any inference variables at this point. However to be safe, we
168
+ // bail out if we do happen to stumble upon them. For performance reasons, we don't want to
169
+ // construct an `InferCtxt` here to properly handle them .
170
+ if actual. has_infer ( ) || default. has_infer ( ) {
192
171
return false ;
193
172
}
194
173
195
- // If the arguments contain projections or (non-escaping) late-bound regions, we have to examine
196
- // them more closely and can't take the fast path.
197
- // Having projections means that there's potential to be further normalized thereby revealing if
198
- // they are equal after all. Regarding late-bound regions, they can be liberated allowing us to
199
- // consider more types to be equal by ignoring the names of binders.
200
- if !actual. has_late_bound_regions ( )
201
- && !actual. has_projections ( )
202
- && !default. has_late_bound_regions ( )
203
- && !default. has_projections ( )
204
- {
205
- // Check the memory addresses of the interned arguments for equality.
206
- return actual. skip_binder ( ) == default. skip_binder ( ) ;
174
+ // Since we don't properly keep track of bound variables in rustdoc (yet), we don't attempt to
175
+ // make any sense out of escaping bound variables. We simply don't have enough context and it
176
+ // would be incorrect to try to do so anyway.
177
+ if actual. has_escaping_bound_vars ( ) || default. has_escaping_bound_vars ( ) {
178
+ return false ;
207
179
}
208
180
209
- let actual = tcx. liberate_late_bound_regions ( did, actual) ;
210
- let default = tcx. liberate_late_bound_regions ( did, default) ;
211
-
212
- let infcx = tcx. infer_ctxt ( ) . build ( ) ;
213
- let ocx = ObligationCtxt :: new ( & infcx) ;
214
-
215
- let actual = ocx. normalize ( cause, param_env, actual) ;
216
- let default = ocx. normalize ( cause, param_env, default) ;
217
-
218
- ocx. eq ( cause, param_env, actual, default) . is_ok ( )
219
- && ocx. select_all_or_error ( ) . is_empty ( )
220
- && infcx. resolve_regions ( & OutlivesEnvironment :: new ( param_env) ) . is_empty ( )
181
+ // Theoretically we could now check if either term contains (non-escaping) late-bound regions or
182
+ // projections, relate the two using an `InferCtxt` and check if the resulting obligations hold
183
+ // since having projections means that the terms can potentially be further normalized thereby
184
+ // revealing if they are equal after all. Regarding late-bound regions, they would need to be
185
+ // liberated allowing us to consider more types to be equal by ignoring the names of binders
186
+ // (e.g., `for<'a> ...` and `for<'b> ...`).
187
+ //
188
+ // However, we are mostly interested in eliding generic args that were originally elided by the
189
+ // user and later filled in by the compiler (i.e., re-eliding) compared to eliding arbitrary
190
+ // generic arguments if they happen to coincide with the default ignoring the fact we can't
191
+ // possibly distinguish these two cases. Therefore and for performance reasons, we just xheck
192
+ // the memory addresses of the interned arguments for equality.
193
+ actual. skip_binder ( ) == default. skip_binder ( )
221
194
}
222
195
223
196
fn external_generic_args < ' tcx > (
0 commit comments