@@ -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
@@ -141,14 +127,7 @@ pub(crate) fn ty_args_to_args<'tcx>(
141
127
let default =
142
128
ty_args. map_bound ( |args| default. instantiate ( cx. tcx , args) . expect_const ( ) ) ;
143
129
144
- if can_elide_generic_arg (
145
- cx. tcx ,
146
- & cause,
147
- param_env,
148
- ty_args. rebind ( ct) ,
149
- default,
150
- params[ index] . def_id ,
151
- ) {
130
+ if can_elide_generic_arg ( ty_args. rebind ( ct) , default) {
152
131
return None ;
153
132
}
154
133
@@ -165,50 +144,53 @@ pub(crate) fn ty_args_to_args<'tcx>(
165
144
}
166
145
167
146
/// Check if the generic argument `actual` coincides with the `default` and can therefore be elided.
168
- fn can_elide_generic_arg < ' tcx , T : ToTrace < ' tcx > + TypeVisitable < TyCtxt < ' tcx > > > (
169
- tcx : TyCtxt < ' tcx > ,
170
- cause : & ObligationCause < ' tcx > ,
171
- param_env : ty:: ParamEnv < ' tcx > ,
172
- actual : ty:: Binder < ' tcx , T > ,
173
- default : ty:: Binder < ' tcx , T > ,
174
- did : DefId ,
175
- ) -> bool {
176
- // The operations below are only correct if we don't have any inference variables.
177
- debug_assert ! ( !actual. has_infer( ) ) ;
178
- debug_assert ! ( !default . has_infer( ) ) ;
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
+ }
179
164
180
- // Since we don't properly keep track of bound variables, don't attempt to make
181
- // any sense out of escaping bound variables (we just don't have enough context).
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.
182
168
if actual. has_escaping_bound_vars ( ) || default. has_escaping_bound_vars ( ) {
183
169
return false ;
184
170
}
185
171
186
- // If the arguments contain projections or (non-escaping) late-bound regions, we have to examine
187
- // them more closely and can't take the fast path.
188
- // Having projections means that there's potential to be further normalized thereby revealing if
189
- // they are equal after all. Regarding late-bound regions, they can be liberated allowing us to
190
- // consider more types to be equal by ignoring the names of binders.
191
- if !actual. has_late_bound_regions ( )
192
- && !actual. has_projections ( )
193
- && !default. has_late_bound_regions ( )
194
- && !default. has_projections ( )
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 ( )
195
188
{
196
- // Check the memory addresses of the interned arguments for equality.
197
- return actual. skip_binder ( ) == default. skip_binder ( ) ;
189
+ return false ;
198
190
}
199
191
200
- let actual = tcx. liberate_late_bound_regions ( did, actual) ;
201
- let default = tcx. liberate_late_bound_regions ( did, default) ;
202
-
203
- let infcx = tcx. infer_ctxt ( ) . build ( ) ;
204
- let ocx = ObligationCtxt :: new ( & infcx) ;
205
-
206
- let actual = ocx. normalize ( cause, param_env, actual) ;
207
- let default = ocx. normalize ( cause, param_env, default) ;
208
-
209
- ocx. eq ( cause, param_env, actual, default) . is_ok ( )
210
- && ocx. select_all_or_error ( ) . is_empty ( )
211
- && infcx. resolve_regions ( & OutlivesEnvironment :: new ( param_env) ) . is_empty ( )
192
+ // Check the memory addresses of the interned arguments for equality.
193
+ actual. skip_binder ( ) == default. skip_binder ( )
212
194
}
213
195
214
196
fn external_generic_args < ' tcx > (
0 commit comments