@@ -15,6 +15,7 @@ use rustc_data_structures::fx::FxIndexSet;
15
15
use rustc_errors:: codes:: * ;
16
16
use rustc_errors:: { Diag , EmissionGuarantee } ;
17
17
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
18
+ use rustc_infer:: traits:: Obligation ;
18
19
use rustc_middle:: bug;
19
20
use rustc_middle:: query:: LocalCrate ;
20
21
use rustc_middle:: ty:: print:: PrintTraitRefExt as _;
@@ -230,23 +231,26 @@ pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool
230
231
/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies
231
232
/// to.
232
233
#[ instrument( skip( tcx) , level = "debug" ) ]
233
- pub ( super ) fn specializes ( tcx : TyCtxt < ' _ > , ( impl1_def_id, impl2_def_id) : ( DefId , DefId ) ) -> bool {
234
+ pub ( super ) fn specializes (
235
+ tcx : TyCtxt < ' _ > ,
236
+ ( specializing_impl_def_id, parent_impl_def_id) : ( DefId , DefId ) ,
237
+ ) -> bool {
234
238
// We check that the specializing impl comes from a crate that has specialization enabled,
235
239
// or if the specializing impl is marked with `allow_internal_unstable`.
236
240
//
237
241
// We don't really care if the specialized impl (the parent) is in a crate that has
238
242
// specialization enabled, since it's not being specialized, and it's already been checked
239
243
// for coherence.
240
- if !tcx. specialization_enabled_in ( impl1_def_id . krate ) {
241
- let span = tcx. def_span ( impl1_def_id ) ;
244
+ if !tcx. specialization_enabled_in ( specializing_impl_def_id . krate ) {
245
+ let span = tcx. def_span ( specializing_impl_def_id ) ;
242
246
if !span. allows_unstable ( sym:: specialization)
243
247
&& !span. allows_unstable ( sym:: min_specialization)
244
248
{
245
249
return false ;
246
250
}
247
251
}
248
252
249
- let impl1_trait_header = tcx. impl_trait_header ( impl1_def_id ) . unwrap ( ) ;
253
+ let specializing_impl_trait_header = tcx. impl_trait_header ( specializing_impl_def_id ) . unwrap ( ) ;
250
254
251
255
// We determine whether there's a subset relationship by:
252
256
//
@@ -261,27 +265,117 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
261
265
// See RFC 1210 for more details and justification.
262
266
263
267
// Currently we do not allow e.g., a negative impl to specialize a positive one
264
- if impl1_trait_header . polarity != tcx. impl_polarity ( impl2_def_id ) {
268
+ if specializing_impl_trait_header . polarity != tcx. impl_polarity ( parent_impl_def_id ) {
265
269
return false ;
266
270
}
267
271
268
- // create a parameter environment corresponding to an identity instantiation of impl1 ,
269
- // i.e. the most generic instantiation of impl1 .
270
- let param_env = tcx. param_env ( impl1_def_id ) ;
272
+ // create a parameter environment corresponding to an identity instantiation of the specializing impl ,
273
+ // i.e. the most generic instantiation of the specializing impl .
274
+ let param_env = tcx. param_env ( specializing_impl_def_id ) ;
271
275
272
- // Create an infcx, taking the predicates of impl1 as assumptions:
276
+ // Create an infcx, taking the predicates of the specializing impl as assumptions:
273
277
let infcx = tcx. infer_ctxt ( ) . build ( TypingMode :: non_body_analysis ( ) ) ;
274
278
275
- // Attempt to prove that impl2 applies, given all of the above.
276
- fulfill_implication (
277
- & infcx,
279
+ let specializing_impl_trait_ref =
280
+ specializing_impl_trait_header. trait_ref . instantiate_identity ( ) ;
281
+ let cause = & ObligationCause :: dummy ( ) ;
282
+ debug ! (
283
+ "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)" ,
284
+ param_env, specializing_impl_trait_ref, parent_impl_def_id
285
+ ) ;
286
+
287
+ // Attempt to prove that the parent impl applies, given all of the above.
288
+
289
+ let ocx = ObligationCtxt :: new ( & infcx) ;
290
+ let specializing_impl_trait_ref = ocx. normalize ( cause, param_env, specializing_impl_trait_ref) ;
291
+
292
+ if !ocx. select_all_or_error ( ) . is_empty ( ) {
293
+ infcx. dcx ( ) . span_delayed_bug (
294
+ infcx. tcx . def_span ( specializing_impl_def_id) ,
295
+ format ! ( "failed to fully normalize {specializing_impl_trait_ref}" ) ,
296
+ ) ;
297
+ return false ;
298
+ }
299
+
300
+ let parent_args = infcx. fresh_args_for_item ( DUMMY_SP , parent_impl_def_id) ;
301
+ let parent_impl_trait_ref = ocx. normalize (
302
+ cause,
278
303
param_env,
279
- impl1_trait_header. trait_ref . instantiate_identity ( ) ,
280
- impl1_def_id,
281
- impl2_def_id,
282
- & ObligationCause :: dummy ( ) ,
283
- )
284
- . is_ok ( )
304
+ infcx
305
+ . tcx
306
+ . impl_trait_ref ( parent_impl_def_id)
307
+ . expect ( "expected source impl to be a trait impl" )
308
+ . instantiate ( infcx. tcx , parent_args) ,
309
+ ) ;
310
+
311
+ // do the impls unify? If not, no specialization.
312
+ let Ok ( ( ) ) = ocx. eq ( cause, param_env, specializing_impl_trait_ref, parent_impl_trait_ref)
313
+ else {
314
+ return false ;
315
+ } ;
316
+
317
+ // Now check that the source trait ref satisfies all the where clauses of the target impl.
318
+ // This is not just for correctness; we also need this to constrain any params that may
319
+ // only be referenced via projection predicates.
320
+ let predicates = ocx. normalize (
321
+ cause,
322
+ param_env,
323
+ infcx. tcx . predicates_of ( parent_impl_def_id) . instantiate ( infcx. tcx , parent_args) ,
324
+ ) ;
325
+ let obligations = predicates_for_generics ( |_, _| cause. clone ( ) , param_env, predicates) ;
326
+ ocx. register_obligations ( obligations) ;
327
+
328
+ let errors = ocx. select_all_or_error ( ) ;
329
+ if !errors. is_empty ( ) {
330
+ // no dice!
331
+ debug ! (
332
+ "fulfill_implication: for impls on {:?} and {:?}, \
333
+ could not fulfill: {:?} given {:?}",
334
+ specializing_impl_trait_ref,
335
+ parent_impl_trait_ref,
336
+ errors,
337
+ param_env. caller_bounds( )
338
+ ) ;
339
+ return false ;
340
+ }
341
+
342
+ // If the parent impl is const, then the specializing impl must be const.
343
+ if tcx. is_conditionally_const ( parent_impl_def_id) {
344
+ let const_conditions = ocx. normalize (
345
+ cause,
346
+ param_env,
347
+ infcx. tcx . const_conditions ( parent_impl_def_id) . instantiate ( infcx. tcx , parent_args) ,
348
+ ) ;
349
+ ocx. register_obligations ( const_conditions. into_iter ( ) . map ( |( trait_ref, _) | {
350
+ Obligation :: new (
351
+ infcx. tcx ,
352
+ cause. clone ( ) ,
353
+ param_env,
354
+ trait_ref. to_host_effect_clause ( infcx. tcx , ty:: BoundConstness :: Maybe ) ,
355
+ )
356
+ } ) ) ;
357
+
358
+ let errors = ocx. select_all_or_error ( ) ;
359
+ if !errors. is_empty ( ) {
360
+ // no dice!
361
+ debug ! (
362
+ "fulfill_implication: for impls on {:?} and {:?}, \
363
+ could not fulfill: {:?} given {:?}",
364
+ specializing_impl_trait_ref,
365
+ parent_impl_trait_ref,
366
+ errors,
367
+ param_env. caller_bounds( )
368
+ ) ;
369
+ return false ;
370
+ }
371
+ }
372
+
373
+ debug ! (
374
+ "fulfill_implication: an impl for {:?} specializes {:?}" ,
375
+ specializing_impl_trait_ref, parent_impl_trait_ref
376
+ ) ;
377
+
378
+ true
285
379
}
286
380
287
381
/// Query provider for `specialization_graph_of`.
0 commit comments