@@ -15,6 +15,7 @@ use rustc_data_structures::fx::FxIndexSet;
1515use rustc_errors:: codes:: * ;
1616use rustc_errors:: { Diag , EmissionGuarantee } ;
1717use rustc_hir:: def_id:: { DefId , LocalDefId } ;
18+ use rustc_infer:: traits:: Obligation ;
1819use rustc_middle:: bug;
1920use rustc_middle:: query:: LocalCrate ;
2021use rustc_middle:: ty:: print:: PrintTraitRefExt as _;
@@ -224,29 +225,38 @@ pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool
224225 tcx. features ( ) . specialization ( ) || tcx. features ( ) . min_specialization ( )
225226}
226227
227- /// Is `impl1 ` a specialization of `impl2 `?
228+ /// Is `specializing_impl_def_id ` a specialization of `parent_impl_def_id `?
228229///
229- /// Specialization is determined by the sets of types to which the impls apply;
230- /// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies
231- /// to.
230+ /// For every type that could apply to `specializing_impl_def_id`, we prove that
231+ /// the `parent_impl_def_id` also applies (i.e. it has a valid impl header and
232+ /// its where-clauses hold).
233+ ///
234+ /// For the purposes of const traits, we also check that the specializing
235+ /// impl is not more restrictive than the parent impl. That is, if the
236+ /// `parent_impl_def_id` is a const impl (conditionally based off of some `~const`
237+ /// bounds), then `specializing_impl_def_id` must also be const for the same
238+ /// set of types.
232239#[ instrument( skip( tcx) , level = "debug" ) ]
233- pub ( super ) fn specializes ( tcx : TyCtxt < ' _ > , ( impl1_def_id, impl2_def_id) : ( DefId , DefId ) ) -> bool {
240+ pub ( super ) fn specializes (
241+ tcx : TyCtxt < ' _ > ,
242+ ( specializing_impl_def_id, parent_impl_def_id) : ( DefId , DefId ) ,
243+ ) -> bool {
234244 // We check that the specializing impl comes from a crate that has specialization enabled,
235245 // or if the specializing impl is marked with `allow_internal_unstable`.
236246 //
237247 // We don't really care if the specialized impl (the parent) is in a crate that has
238248 // specialization enabled, since it's not being specialized, and it's already been checked
239249 // for coherence.
240- if !tcx. specialization_enabled_in ( impl1_def_id . krate ) {
241- let span = tcx. def_span ( impl1_def_id ) ;
250+ if !tcx. specialization_enabled_in ( specializing_impl_def_id . krate ) {
251+ let span = tcx. def_span ( specializing_impl_def_id ) ;
242252 if !span. allows_unstable ( sym:: specialization)
243253 && !span. allows_unstable ( sym:: min_specialization)
244254 {
245255 return false ;
246256 }
247257 }
248258
249- let impl1_trait_header = tcx. impl_trait_header ( impl1_def_id ) . unwrap ( ) ;
259+ let specializing_impl_trait_header = tcx. impl_trait_header ( specializing_impl_def_id ) . unwrap ( ) ;
250260
251261 // We determine whether there's a subset relationship by:
252262 //
@@ -261,27 +271,123 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
261271 // See RFC 1210 for more details and justification.
262272
263273 // 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 ) {
274+ if specializing_impl_trait_header . polarity != tcx. impl_polarity ( parent_impl_def_id ) {
265275 return false ;
266276 }
267277
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 ) ;
278+ // create a parameter environment corresponding to an identity instantiation of the specializing impl ,
279+ // i.e. the most generic instantiation of the specializing impl .
280+ let param_env = tcx. param_env ( specializing_impl_def_id ) ;
271281
272- // Create an infcx, taking the predicates of impl1 as assumptions:
282+ // Create an infcx, taking the predicates of the specializing impl as assumptions:
273283 let infcx = tcx. infer_ctxt ( ) . build ( TypingMode :: non_body_analysis ( ) ) ;
274284
275- // Attempt to prove that impl2 applies, given all of the above.
276- fulfill_implication (
277- & infcx,
285+ let specializing_impl_trait_ref =
286+ specializing_impl_trait_header. trait_ref . instantiate_identity ( ) ;
287+ let cause = & ObligationCause :: dummy ( ) ;
288+ debug ! (
289+ "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)" ,
290+ param_env, specializing_impl_trait_ref, parent_impl_def_id
291+ ) ;
292+
293+ // Attempt to prove that the parent impl applies, given all of the above.
294+
295+ let ocx = ObligationCtxt :: new ( & infcx) ;
296+ let specializing_impl_trait_ref = ocx. normalize ( cause, param_env, specializing_impl_trait_ref) ;
297+
298+ if !ocx. select_all_or_error ( ) . is_empty ( ) {
299+ infcx. dcx ( ) . span_delayed_bug (
300+ infcx. tcx . def_span ( specializing_impl_def_id) ,
301+ format ! ( "failed to fully normalize {specializing_impl_trait_ref}" ) ,
302+ ) ;
303+ return false ;
304+ }
305+
306+ let parent_args = infcx. fresh_args_for_item ( DUMMY_SP , parent_impl_def_id) ;
307+ let parent_impl_trait_ref = ocx. normalize (
308+ cause,
278309 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 ( )
310+ infcx
311+ . tcx
312+ . impl_trait_ref ( parent_impl_def_id)
313+ . expect ( "expected source impl to be a trait impl" )
314+ . instantiate ( infcx. tcx , parent_args) ,
315+ ) ;
316+
317+ // do the impls unify? If not, no specialization.
318+ let Ok ( ( ) ) = ocx. eq ( cause, param_env, specializing_impl_trait_ref, parent_impl_trait_ref)
319+ else {
320+ return false ;
321+ } ;
322+
323+ // Now check that the source trait ref satisfies all the where clauses of the target impl.
324+ // This is not just for correctness; we also need this to constrain any params that may
325+ // only be referenced via projection predicates.
326+ let predicates = ocx. normalize (
327+ cause,
328+ param_env,
329+ infcx. tcx . predicates_of ( parent_impl_def_id) . instantiate ( infcx. tcx , parent_args) ,
330+ ) ;
331+ let obligations = predicates_for_generics ( |_, _| cause. clone ( ) , param_env, predicates) ;
332+ ocx. register_obligations ( obligations) ;
333+
334+ let errors = ocx. select_all_or_error ( ) ;
335+ if !errors. is_empty ( ) {
336+ // no dice!
337+ debug ! (
338+ "fulfill_implication: for impls on {:?} and {:?}, \
339+ could not fulfill: {:?} given {:?}",
340+ specializing_impl_trait_ref,
341+ parent_impl_trait_ref,
342+ errors,
343+ param_env. caller_bounds( )
344+ ) ;
345+ return false ;
346+ }
347+
348+ // If the parent impl is const, then the specializing impl must be const,
349+ // and it must not be *more restrictive* than the parent impl (that is,
350+ // it cannot be const in fewer cases than the parent impl).
351+ if tcx. is_conditionally_const ( parent_impl_def_id) {
352+ if !tcx. is_conditionally_const ( specializing_impl_def_id) {
353+ return false ;
354+ }
355+
356+ let const_conditions = ocx. normalize (
357+ cause,
358+ param_env,
359+ infcx. tcx . const_conditions ( parent_impl_def_id) . instantiate ( infcx. tcx , parent_args) ,
360+ ) ;
361+ ocx. register_obligations ( const_conditions. into_iter ( ) . map ( |( trait_ref, _) | {
362+ Obligation :: new (
363+ infcx. tcx ,
364+ cause. clone ( ) ,
365+ param_env,
366+ trait_ref. to_host_effect_clause ( infcx. tcx , ty:: BoundConstness :: Maybe ) ,
367+ )
368+ } ) ) ;
369+
370+ let errors = ocx. select_all_or_error ( ) ;
371+ if !errors. is_empty ( ) {
372+ // no dice!
373+ debug ! (
374+ "fulfill_implication: for impls on {:?} and {:?}, \
375+ could not fulfill: {:?} given {:?}",
376+ specializing_impl_trait_ref,
377+ parent_impl_trait_ref,
378+ errors,
379+ param_env. caller_bounds( )
380+ ) ;
381+ return false ;
382+ }
383+ }
384+
385+ debug ! (
386+ "fulfill_implication: an impl for {:?} specializes {:?}" ,
387+ specializing_impl_trait_ref, parent_impl_trait_ref
388+ ) ;
389+
390+ true
285391}
286392
287393/// Query provider for `specialization_graph_of`.
0 commit comments