@@ -57,22 +57,24 @@ pub(crate) fn check_impl_wf(
57
57
tcx : TyCtxt < ' _ > ,
58
58
impl_def_id : LocalDefId ,
59
59
) -> Result < ( ) , ErrorGuaranteed > {
60
- let min_specialization = tcx. features ( ) . min_specialization ( ) ;
61
- let mut res = Ok ( ( ) ) ;
62
60
debug_assert_matches ! ( tcx. def_kind( impl_def_id) , DefKind :: Impl { .. } ) ;
63
- res = res. and ( enforce_impl_params_are_constrained ( tcx, impl_def_id) ) ;
64
- if min_specialization {
61
+
62
+ // Check that the args are constrained. We queryfied the check for ty/const params
63
+ // since unconstrained type/const params cause ICEs in projection, so we want to
64
+ // detect those specifically and project those to `TyKind::Error`.
65
+ let mut res = tcx. ensure ( ) . enforce_impl_non_lifetime_params_are_constrained ( impl_def_id) ;
66
+ res = res. and ( enforce_impl_lifetime_params_are_constrained ( tcx, impl_def_id) ) ;
67
+
68
+ if tcx. features ( ) . min_specialization ( ) {
65
69
res = res. and ( check_min_specialization ( tcx, impl_def_id) ) ;
66
70
}
67
-
68
71
res
69
72
}
70
73
71
- fn enforce_impl_params_are_constrained (
74
+ pub ( crate ) fn enforce_impl_lifetime_params_are_constrained (
72
75
tcx : TyCtxt < ' _ > ,
73
76
impl_def_id : LocalDefId ,
74
77
) -> Result < ( ) , ErrorGuaranteed > {
75
- // Every lifetime used in an associated type must be constrained.
76
78
let impl_self_ty = tcx. type_of ( impl_def_id) . instantiate_identity ( ) ;
77
79
if impl_self_ty. references_error ( ) {
78
80
// Don't complain about unconstrained type params when self ty isn't known due to errors.
@@ -88,6 +90,7 @@ fn enforce_impl_params_are_constrained(
88
90
// Compilation must continue in order for other important diagnostics to keep showing up.
89
91
return Ok ( ( ) ) ;
90
92
}
93
+
91
94
let impl_generics = tcx. generics_of ( impl_def_id) ;
92
95
let impl_predicates = tcx. predicates_of ( impl_def_id) ;
93
96
let impl_trait_ref = tcx. impl_trait_ref ( impl_def_id) . map ( ty:: EarlyBinder :: instantiate_identity) ;
@@ -121,6 +124,84 @@ fn enforce_impl_params_are_constrained(
121
124
} )
122
125
. collect ( ) ;
123
126
127
+ let mut res = Ok ( ( ) ) ;
128
+ for param in & impl_generics. own_params {
129
+ match param. kind {
130
+ ty:: GenericParamDefKind :: Lifetime => {
131
+ let param_lt = cgp:: Parameter :: from ( param. to_early_bound_region_data ( ) ) ;
132
+ if lifetimes_in_associated_types. contains ( & param_lt) // (*)
133
+ && !input_parameters. contains ( & param_lt)
134
+ {
135
+ let mut diag = tcx. dcx ( ) . create_err ( UnconstrainedGenericParameter {
136
+ span : tcx. def_span ( param. def_id ) ,
137
+ param_name : param. name ,
138
+ param_def_kind : tcx. def_descr ( param. def_id ) ,
139
+ const_param_note : false ,
140
+ const_param_note2 : false ,
141
+ } ) ;
142
+ diag. code ( E0207 ) ;
143
+ res = Err ( diag. emit ( ) ) ;
144
+ }
145
+ // (*) This is a horrible concession to reality. I think it'd be
146
+ // better to just ban unconstrained lifetimes outright, but in
147
+ // practice people do non-hygienic macros like:
148
+ //
149
+ // ```
150
+ // macro_rules! __impl_slice_eq1 {
151
+ // ($Lhs: ty, $Rhs: ty, $Bound: ident) => {
152
+ // impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> {
153
+ // ....
154
+ // }
155
+ // }
156
+ // }
157
+ // ```
158
+ //
159
+ // In a concession to backwards compatibility, we continue to
160
+ // permit those, so long as the lifetimes aren't used in
161
+ // associated types. I believe this is sound, because lifetimes
162
+ // used elsewhere are not projected back out.
163
+ }
164
+ ty:: GenericParamDefKind :: Type { .. } | ty:: GenericParamDefKind :: Const { .. } => {
165
+ // Enforced in `enforce_impl_non_lifetime_params_are_constrained`.
166
+ }
167
+ }
168
+ }
169
+ res
170
+ }
171
+
172
+ pub ( crate ) fn enforce_impl_non_lifetime_params_are_constrained (
173
+ tcx : TyCtxt < ' _ > ,
174
+ impl_def_id : LocalDefId ,
175
+ ) -> Result < ( ) , ErrorGuaranteed > {
176
+ let impl_self_ty = tcx. type_of ( impl_def_id) . instantiate_identity ( ) ;
177
+ if impl_self_ty. references_error ( ) {
178
+ // Don't complain about unconstrained type params when self ty isn't known due to errors.
179
+ // (#36836)
180
+ tcx. dcx ( ) . span_delayed_bug (
181
+ tcx. def_span ( impl_def_id) ,
182
+ format ! (
183
+ "potentially unconstrained type parameters weren't evaluated: {impl_self_ty:?}" ,
184
+ ) ,
185
+ ) ;
186
+ // This is super fishy, but our current `rustc_hir_analysis::check_crate` pipeline depends on
187
+ // `type_of` having been called much earlier, and thus this value being read from cache.
188
+ // Compilation must continue in order for other important diagnostics to keep showing up.
189
+ return Ok ( ( ) ) ;
190
+ }
191
+ let impl_generics = tcx. generics_of ( impl_def_id) ;
192
+ let impl_predicates = tcx. predicates_of ( impl_def_id) ;
193
+ let impl_trait_ref = tcx. impl_trait_ref ( impl_def_id) . map ( ty:: EarlyBinder :: instantiate_identity) ;
194
+
195
+ impl_trait_ref. error_reported ( ) ?;
196
+
197
+ let mut input_parameters = cgp:: parameters_for_impl ( tcx, impl_self_ty, impl_trait_ref) ;
198
+ cgp:: identify_constrained_generic_params (
199
+ tcx,
200
+ impl_predicates,
201
+ impl_trait_ref,
202
+ & mut input_parameters,
203
+ ) ;
204
+
124
205
let mut res = Ok ( ( ) ) ;
125
206
for param in & impl_generics. own_params {
126
207
let err = match param. kind {
@@ -129,15 +210,14 @@ fn enforce_impl_params_are_constrained(
129
210
let param_ty = ty:: ParamTy :: for_def ( param) ;
130
211
!input_parameters. contains ( & cgp:: Parameter :: from ( param_ty) )
131
212
}
132
- ty:: GenericParamDefKind :: Lifetime => {
133
- let param_lt = cgp:: Parameter :: from ( param. to_early_bound_region_data ( ) ) ;
134
- lifetimes_in_associated_types. contains ( & param_lt) && // (*)
135
- !input_parameters. contains ( & param_lt)
136
- }
137
213
ty:: GenericParamDefKind :: Const { .. } => {
138
214
let param_ct = ty:: ParamConst :: for_def ( param) ;
139
215
!input_parameters. contains ( & cgp:: Parameter :: from ( param_ct) )
140
216
}
217
+ ty:: GenericParamDefKind :: Lifetime => {
218
+ // Enforced in `enforce_impl_type_params_are_constrained`.
219
+ false
220
+ }
141
221
} ;
142
222
if err {
143
223
let const_param_note = matches ! ( param. kind, ty:: GenericParamDefKind :: Const { .. } ) ;
@@ -153,23 +233,4 @@ fn enforce_impl_params_are_constrained(
153
233
}
154
234
}
155
235
res
156
-
157
- // (*) This is a horrible concession to reality. I think it'd be
158
- // better to just ban unconstrained lifetimes outright, but in
159
- // practice people do non-hygienic macros like:
160
- //
161
- // ```
162
- // macro_rules! __impl_slice_eq1 {
163
- // ($Lhs: ty, $Rhs: ty, $Bound: ident) => {
164
- // impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> {
165
- // ....
166
- // }
167
- // }
168
- // }
169
- // ```
170
- //
171
- // In a concession to backwards compatibility, we continue to
172
- // permit those, so long as the lifetimes aren't used in
173
- // associated types. I believe this is sound, because lifetimes
174
- // used elsewhere are not projected back out.
175
236
}
0 commit comments