@@ -14,16 +14,11 @@ use rustc_ast::tokenstream::TokenTree;
1414use rustc_hir as hir;
1515use rustc_hir:: def:: { DefKind , Res } ;
1616use rustc_hir:: def_id:: { DefId , LocalDefId , LOCAL_CRATE } ;
17- use rustc_infer:: infer:: at:: ToTrace ;
18- use rustc_infer:: infer:: outlives:: env:: OutlivesEnvironment ;
1917use rustc_metadata:: rendered_const;
2018use rustc_middle:: mir;
21- use rustc_middle:: traits:: ObligationCause ;
2219use rustc_middle:: ty:: { self , GenericArgKind , GenericArgsRef , TyCtxt } ;
2320use rustc_middle:: ty:: { TypeVisitable , TypeVisitableExt } ;
2421use rustc_span:: symbol:: { kw, sym, Symbol } ;
25- use rustc_trait_selection:: infer:: TyCtxtInferExt ;
26- use rustc_trait_selection:: traits:: ObligationCtxt ;
2722use std:: fmt:: Write as _;
2823use std:: mem;
2924use std:: sync:: LazyLock as Lazy ;
@@ -86,8 +81,6 @@ pub(crate) fn ty_args_to_args<'tcx>(
8681 has_self : bool ,
8782 container : Option < DefId > ,
8883) -> Vec < GenericArg > {
89- let param_env = ty:: ParamEnv :: empty ( ) ;
90- let cause = ObligationCause :: dummy ( ) ;
9184 let params = container. map ( |container| & cx. tcx . generics_of ( container) . params ) ;
9285 let mut elision_has_failed_once_before = false ;
9386
@@ -107,14 +100,7 @@ pub(crate) fn ty_args_to_args<'tcx>(
107100 let default =
108101 ty_args. map_bound ( |args| default. instantiate ( cx. tcx , args) . expect_ty ( ) ) ;
109102
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) {
118104 return None ;
119105 }
120106
@@ -141,14 +127,7 @@ pub(crate) fn ty_args_to_args<'tcx>(
141127 let default =
142128 ty_args. map_bound ( |args| default. instantiate ( cx. tcx , args) . expect_const ( ) ) ;
143129
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) {
152131 return None ;
153132 }
154133
@@ -165,50 +144,53 @@ pub(crate) fn ty_args_to_args<'tcx>(
165144}
166145
167146/// 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+ }
179164
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.
182168 if actual. has_escaping_bound_vars ( ) || default. has_escaping_bound_vars ( ) {
183169 return false ;
184170 }
185171
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 ( )
195188 {
196- // Check the memory addresses of the interned arguments for equality.
197- return actual. skip_binder ( ) == default. skip_binder ( ) ;
189+ return false ;
198190 }
199191
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 ( )
212194}
213195
214196fn external_generic_args < ' tcx > (
0 commit comments