@@ -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
@@ -150,14 +136,7 @@ pub(crate) fn ty_args_to_args<'tcx>(
150136 let default =
151137 ty_args. map_bound ( |args| default. instantiate ( cx. tcx , args) . expect_const ( ) ) ;
152138
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) {
161140 return None ;
162141 }
163142
@@ -174,50 +153,44 @@ pub(crate) fn ty_args_to_args<'tcx>(
174153}
175154
176155/// 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 ( ) {
192171 return false ;
193172 }
194173
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 ;
207179 }
208180
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 ( )
221194}
222195
223196fn external_generic_args < ' tcx > (
0 commit comments