@@ -14,10 +14,16 @@ 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 ;
17
19
use rustc_metadata:: rendered_const;
18
20
use rustc_middle:: mir;
21
+ use rustc_middle:: traits:: ObligationCause ;
19
22
use rustc_middle:: ty:: { self , GenericArgKind , GenericArgsRef , TyCtxt } ;
23
+ use rustc_middle:: ty:: { TypeVisitable , TypeVisitableExt } ;
20
24
use rustc_span:: symbol:: { kw, sym, Symbol } ;
25
+ use rustc_trait_selection:: infer:: TyCtxtInferExt ;
26
+ use rustc_trait_selection:: traits:: ObligationCtxt ;
21
27
use std:: fmt:: Write as _;
22
28
use std:: mem;
23
29
use std:: sync:: LazyLock as Lazy ;
@@ -76,44 +82,142 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
76
82
77
83
pub ( crate ) fn ty_args_to_args < ' tcx > (
78
84
cx : & mut DocContext < ' tcx > ,
79
- args : ty:: Binder < ' tcx , & ' tcx [ ty:: GenericArg < ' tcx > ] > ,
85
+ ty_args : ty:: Binder < ' tcx , & ' tcx [ ty:: GenericArg < ' tcx > ] > ,
80
86
has_self : bool ,
81
87
container : Option < DefId > ,
82
88
) -> 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
89
+ let param_env = ty:: ParamEnv :: empty ( ) ;
90
+ let cause = ObligationCause :: dummy ( ) ;
91
+ let params = container. map ( |container| & cx. tcx . generics_of ( container) . params ) ;
92
+ let mut elision_has_failed_once_before = false ;
93
+
94
+ let offset = if has_self { 1 } else { 0 } ;
95
+ let mut args = Vec :: with_capacity ( ty_args. skip_binder ( ) . len ( ) . saturating_sub ( offset) ) ;
96
+
97
+ let ty_arg_to_arg = |( index, arg) : ( usize , & ty:: GenericArg < ' tcx > ) | match arg. unpack ( ) {
98
+ GenericArgKind :: Lifetime ( lt) => {
99
+ Some ( GenericArg :: Lifetime ( clean_middle_region ( lt) . unwrap_or ( Lifetime :: elided ( ) ) ) )
100
+ }
101
+ GenericArgKind :: Type ( _) if has_self && index == 0 => None ,
102
+ GenericArgKind :: Type ( ty) => {
103
+ if !elision_has_failed_once_before
104
+ && let Some ( params) = params
105
+ && let Some ( default) = params[ index] . default_value ( cx. tcx )
106
+ {
107
+ let default =
108
+ ty_args. map_bound ( |args| default. instantiate ( cx. tcx , args) . expect_ty ( ) ) ;
109
+
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
+ ) {
118
+ return None ;
119
+ }
120
+
121
+ elision_has_failed_once_before = true ;
95
122
}
96
- GenericArgKind :: Type ( ty) => Some ( GenericArg :: Type ( clean_middle_ty (
97
- kind. rebind ( ty) ,
123
+
124
+ Some ( GenericArg :: Type ( clean_middle_ty (
125
+ ty_args. rebind ( ty) ,
98
126
cx,
99
127
None ,
100
128
container. map ( |container| crate :: clean:: ContainerTy :: Regular {
101
129
ty : container,
102
- args,
130
+ args : ty_args ,
103
131
has_self,
104
132
arg : index,
105
133
} ) ,
106
- ) ) ) ,
134
+ ) ) )
135
+ }
136
+ GenericArgKind :: Const ( ct) => {
107
137
// FIXME(effects): this relies on the host effect being called `host`, which users could also name
108
138
// their const generics.
109
139
// 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) ) ) )
140
+ if let ty:: ConstKind :: Param ( p) = ct. kind ( )
141
+ && p. name == sym:: host
142
+ {
143
+ return None ;
113
144
}
145
+
146
+ if !elision_has_failed_once_before
147
+ && let Some ( params) = params
148
+ && let Some ( default) = params[ index] . default_value ( cx. tcx )
149
+ {
150
+ let default =
151
+ ty_args. map_bound ( |args| default. instantiate ( cx. tcx , args) . expect_const ( ) ) ;
152
+
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
+ ) {
161
+ return None ;
162
+ }
163
+
164
+ elision_has_failed_once_before = true ;
165
+ }
166
+
167
+ Some ( GenericArg :: Const ( Box :: new ( clean_middle_const ( ty_args. rebind ( ct) , cx) ) ) )
114
168
}
115
- } ) ) ;
116
- ret_val
169
+ } ;
170
+
171
+ args. extend ( ty_args. skip_binder ( ) . iter ( ) . enumerate ( ) . rev ( ) . filter_map ( ty_arg_to_arg) ) ;
172
+ args. reverse ( ) ;
173
+ args
174
+ }
175
+
176
+ /// 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 ( ) {
192
+ return false ;
193
+ }
194
+
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 ( ) ;
207
+ }
208
+
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 ( )
117
221
}
118
222
119
223
fn external_generic_args < ' tcx > (
0 commit comments