@@ -17,6 +17,7 @@ use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
1717use rustc_metadata:: rendered_const;
1818use rustc_middle:: mir;
1919use rustc_middle:: ty:: { self , GenericArgKind , GenericArgsRef , TyCtxt } ;
20+ use rustc_middle:: ty:: { TypeVisitable , TypeVisitableExt } ;
2021use rustc_span:: symbol:: { kw, sym, Symbol } ;
2122use std:: fmt:: Write as _;
2223use std:: mem;
@@ -76,44 +77,123 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
7677
7778pub ( crate ) fn ty_args_to_args < ' tcx > (
7879 cx : & mut DocContext < ' tcx > ,
79- args : ty:: Binder < ' tcx , & ' tcx [ ty:: GenericArg < ' tcx > ] > ,
80+ ty_args : ty:: Binder < ' tcx , & ' tcx [ ty:: GenericArg < ' tcx > ] > ,
8081 has_self : bool ,
81- container : Option < DefId > ,
82+ owner : DefId ,
8283) -> 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+ // Fast path which avoids executing the query `generics_of`.
85+ if ty_args. skip_binder ( ) . is_empty ( ) {
86+ return Vec :: new ( ) ;
87+ }
88+
89+ let params = & cx. tcx . generics_of ( owner) . params ;
90+ let mut elision_has_failed_once_before = false ;
91+
92+ let offset = if has_self { 1 } else { 0 } ;
93+ let mut args = Vec :: with_capacity ( ty_args. skip_binder ( ) . len ( ) . saturating_sub ( offset) ) ;
94+
95+ let ty_arg_to_arg = |( index, arg) : ( usize , & ty:: GenericArg < ' tcx > ) | match arg. unpack ( ) {
96+ GenericArgKind :: Lifetime ( lt) => {
97+ Some ( GenericArg :: Lifetime ( clean_middle_region ( lt) . unwrap_or ( Lifetime :: elided ( ) ) ) )
98+ }
99+ GenericArgKind :: Type ( _) if has_self && index == 0 => None ,
100+ GenericArgKind :: Type ( ty) => {
101+ if !elision_has_failed_once_before
102+ && let Some ( default) = params[ index] . default_value ( cx. tcx )
103+ {
104+ let default =
105+ ty_args. map_bound ( |args| default. instantiate ( cx. tcx , args) . expect_ty ( ) ) ;
106+
107+ if can_elide_generic_arg ( ty_args. rebind ( ty) , default) {
108+ return None ;
109+ }
110+
111+ elision_has_failed_once_before = true ;
95112 }
96- GenericArgKind :: Type ( ty) => Some ( GenericArg :: Type ( clean_middle_ty (
97- kind. rebind ( ty) ,
113+
114+ Some ( GenericArg :: Type ( clean_middle_ty (
115+ ty_args. rebind ( ty) ,
98116 cx,
99117 None ,
100- container . map ( |container| crate :: clean:: ContainerTy :: Regular {
101- ty : container ,
102- args,
118+ Some ( crate :: clean:: ContainerTy :: Regular {
119+ ty : owner ,
120+ args : ty_args ,
103121 has_self,
104122 arg : index,
105123 } ) ,
106- ) ) ) ,
124+ ) ) )
125+ }
126+ GenericArgKind :: Const ( ct) => {
107127 // FIXME(effects): this relies on the host effect being called `host`, which users could also name
108128 // their const generics.
109129 // FIXME(effects): this causes `host = true` and `host = false` generics to also be emitted.
110- GenericArgKind :: Const ( ct) if let ty:: ConstKind :: Param ( p) = ct. kind ( ) && p. name == sym:: host => None ,
111- GenericArgKind :: Const ( ct) => {
112- Some ( GenericArg :: Const ( Box :: new ( clean_middle_const ( kind. rebind ( ct) , cx) ) ) )
130+ if let ty:: ConstKind :: Param ( p) = ct. kind ( )
131+ && p. name == sym:: host
132+ {
133+ return None ;
113134 }
135+
136+ if !elision_has_failed_once_before
137+ && let Some ( default) = params[ index] . default_value ( cx. tcx )
138+ {
139+ let default =
140+ ty_args. map_bound ( |args| default. instantiate ( cx. tcx , args) . expect_const ( ) ) ;
141+
142+ if can_elide_generic_arg ( ty_args. rebind ( ct) , default) {
143+ return None ;
144+ }
145+
146+ elision_has_failed_once_before = true ;
147+ }
148+
149+ Some ( GenericArg :: Const ( Box :: new ( clean_middle_const ( ty_args. rebind ( ct) , cx) ) ) )
114150 }
115- } ) ) ;
116- ret_val
151+ } ;
152+
153+ args. extend ( ty_args. skip_binder ( ) . iter ( ) . enumerate ( ) . rev ( ) . filter_map ( ty_arg_to_arg) ) ;
154+ args. reverse ( ) ;
155+ args
156+ }
157+
158+ /// Check if the generic argument `actual` coincides with the `default` and can therefore be elided.
159+ ///
160+ /// This uses a very conservative approach for performance and correctness reasons, meaning for
161+ /// several classes of terms it claims that they cannot be elided even if they theoretically could.
162+ /// This is absolutely fine since this concerns mostly edge cases.
163+ fn can_elide_generic_arg < ' tcx , Term > (
164+ actual : ty:: Binder < ' tcx , Term > ,
165+ default : ty:: Binder < ' tcx , Term > ,
166+ ) -> bool
167+ where
168+ Term : Eq + TypeVisitable < TyCtxt < ' tcx > > ,
169+ {
170+ // In practice, we shouldn't have any inference variables at this point. However to be safe, we
171+ // bail out if we do happen to stumble upon them. For performance reasons, we don't want to
172+ // construct an `InferCtxt` here to properly handle them.
173+ if actual. has_infer ( ) || default. has_infer ( ) {
174+ return false ;
175+ }
176+
177+ // Since we don't properly keep track of bound variables in rustdoc (yet), we don't attempt to
178+ // make any sense out of escaping bound variables. We simply don't have enough context and it
179+ // would be incorrect to try to do so anyway.
180+ if actual. has_escaping_bound_vars ( ) || default. has_escaping_bound_vars ( ) {
181+ return false ;
182+ }
183+
184+ // Theoretically we could now check if either term contains (non-escaping) late-bound regions or
185+ // projections, relate the two using an `InferCtxt` and check if the resulting obligations hold
186+ // since having projections means that the terms can potentially be further normalized thereby
187+ // revealing if they are equal after all. Regarding late-bound regions, they would need to be
188+ // liberated allowing us to consider more types to be equal by ignoring the names of binders
189+ // (e.g., `for<'a> ...` and `for<'b> ...`).
190+ //
191+ // However, we are mostly interested in eliding generic args that were originally elided by the
192+ // user and later filled in by the compiler (i.e., re-eliding) compared to eliding arbitrary
193+ // generic arguments if they happen to coincide with the default ignoring the fact we can't
194+ // possibly distinguish these two cases. Therefore and for performance reasons, we just xheck
195+ // the memory addresses of the interned arguments for equality.
196+ actual. skip_binder ( ) == default. skip_binder ( )
117197}
118198
119199fn external_generic_args < ' tcx > (
@@ -123,7 +203,7 @@ fn external_generic_args<'tcx>(
123203 bindings : ThinVec < TypeBinding > ,
124204 ty_args : ty:: Binder < ' tcx , GenericArgsRef < ' tcx > > ,
125205) -> GenericArgs {
126- let args = ty_args_to_args ( cx, ty_args. map_bound ( |args| & args[ ..] ) , has_self, Some ( did) ) ;
206+ let args = ty_args_to_args ( cx, ty_args. map_bound ( |args| & args[ ..] ) , has_self, did) ;
127207
128208 if cx. tcx . fn_trait_kind_from_def_id ( did) . is_some ( ) {
129209 let ty = ty_args
0 commit comments