77use crate :: infer:: { CombinedSnapshot , InferOk , TyCtxtInferExt } ;
88use crate :: traits:: query:: evaluate_obligation:: InferCtxtExt ;
99use crate :: traits:: select:: IntercrateAmbiguityCause ;
10+ use crate :: traits:: util:: impl_trait_ref_and_oblig;
1011use crate :: traits:: SkipLeakCheck ;
1112use crate :: traits:: {
12- self , Normalized , Obligation , ObligationCause , PredicateObligation , SelectionContext ,
13+ self , FulfillmentContext , Normalized , Obligation , ObligationCause , PredicateObligation ,
14+ SelectionContext ,
1315} ;
1416use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
1517use rustc_middle:: ty:: fast_reject:: { self , SimplifyParams , StripReferences } ;
@@ -137,6 +139,7 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
137139
138140enum OverlapMode {
139141 Stable ,
142+ WithNegative ,
140143 Strict ,
141144}
142145
@@ -147,8 +150,16 @@ fn overlap_mode<'tcx>(tcx: TyCtxt<'tcx>, impl1_def_id: DefId, impl2_def_id: DefI
147150 bug ! ( "Use strict coherence on both impls" , ) ;
148151 }
149152
153+ if tcx. has_attr ( impl1_def_id, sym:: rustc_with_negative_coherence)
154+ != tcx. has_attr ( impl2_def_id, sym:: rustc_with_negative_coherence)
155+ {
156+ bug ! ( "Use with negative coherence on both impls" , ) ;
157+ }
158+
150159 if tcx. has_attr ( impl1_def_id, sym:: rustc_strict_coherence) {
151160 OverlapMode :: Strict
161+ } else if tcx. has_attr ( impl1_def_id, sym:: rustc_with_negative_coherence) {
162+ OverlapMode :: WithNegative
152163 } else {
153164 OverlapMode :: Stable
154165 }
@@ -188,9 +199,25 @@ fn overlap_within_probe<'cx, 'tcx>(
188199 let impl1_header = with_fresh_ty_vars ( selcx, param_env, impl1_def_id) ;
189200 let impl2_header = with_fresh_ty_vars ( selcx, param_env, impl2_def_id) ;
190201
191- let overlap_mode = overlap_mode ( tcx, impl1_def_id, impl2_def_id) ;
192- if stable_disjoint ( selcx, param_env, & impl1_header, impl2_header, overlap_mode) {
193- return None ;
202+ match overlap_mode ( tcx, impl1_def_id, impl2_def_id) {
203+ OverlapMode :: Stable => {
204+ if stable_disjoint ( selcx, param_env, & impl1_header, impl2_header, OverlapMode :: Stable ) {
205+ return None ;
206+ }
207+ }
208+ OverlapMode :: Strict => {
209+ if stable_disjoint ( selcx, param_env, & impl1_header, impl2_header, OverlapMode :: Strict ) {
210+ return None ;
211+ }
212+ }
213+ OverlapMode :: WithNegative => {
214+ if stable_disjoint ( selcx, param_env, & impl1_header, impl2_header, OverlapMode :: Stable )
215+ || explicit_disjoint ( selcx, impl1_def_id, impl2_def_id)
216+ || explicit_disjoint ( selcx, impl2_def_id, impl1_def_id)
217+ {
218+ return None ;
219+ }
220+ }
194221 }
195222
196223 if !skip_leak_check. is_yes ( ) {
@@ -280,6 +307,7 @@ fn stable_disjoint<'cx, 'tcx>(
280307 loose_check ( selcx, o) || tcx. features ( ) . negative_impls && strict_check ( selcx, o)
281308 }
282309 OverlapMode :: Strict => strict_check ( selcx, o) ,
310+ OverlapMode :: WithNegative => loose_check ( selcx, o) ,
283311 }
284312 } ) ;
285313 // FIXME: the call to `selcx.predicate_may_hold_fatal` above should be ported
@@ -294,6 +322,69 @@ fn stable_disjoint<'cx, 'tcx>(
294322 }
295323}
296324
325+ /// Given impl1 and impl2 check if both impls are never satisfied by a common type (including
326+ /// where-clauses) If so, return true, they are disjoint and false otherwise.
327+ fn explicit_disjoint < ' cx , ' tcx > (
328+ selcx : & mut SelectionContext < ' cx , ' tcx > ,
329+ impl1_def_id : DefId ,
330+ impl2_def_id : DefId ,
331+ ) -> bool {
332+ let tcx = selcx. infcx ( ) . tcx ;
333+
334+ // create a parameter environment corresponding to a (placeholder) instantiation of impl1
335+ let impl1_env = tcx. param_env ( impl1_def_id) ;
336+ let impl1_trait_ref = tcx. impl_trait_ref ( impl1_def_id) . unwrap ( ) ;
337+
338+ // Create an infcx, taking the predicates of impl1 as assumptions:
339+ tcx. infer_ctxt ( ) . enter ( |infcx| {
340+ // Normalize the trait reference. The WF rules ought to ensure
341+ // that this always succeeds.
342+ let impl1_trait_ref = match traits:: fully_normalize (
343+ & infcx,
344+ FulfillmentContext :: new ( ) ,
345+ ObligationCause :: dummy ( ) ,
346+ impl1_env,
347+ impl1_trait_ref,
348+ ) {
349+ Ok ( impl1_trait_ref) => impl1_trait_ref,
350+ Err ( err) => {
351+ bug ! ( "failed to fully normalize {:?}: {:?}" , impl1_trait_ref, err) ;
352+ }
353+ } ;
354+
355+ // Attempt to prove that impl2 applies, given all of the above.
356+ let selcx = & mut SelectionContext :: new ( & infcx) ;
357+ let impl2_substs = infcx. fresh_substs_for_item ( DUMMY_SP , impl2_def_id) ;
358+ let ( impl2_trait_ref, obligations) =
359+ impl_trait_ref_and_oblig ( selcx, impl1_env, impl2_def_id, impl2_substs) ;
360+
361+ // do the impls unify? If not, not disjoint.
362+ let more_obligations = match infcx
363+ . at ( & ObligationCause :: dummy ( ) , impl1_env)
364+ . eq ( impl1_trait_ref, impl2_trait_ref)
365+ {
366+ Ok ( InferOk { obligations, .. } ) => obligations,
367+ Err ( _) => {
368+ debug ! (
369+ "explicit_disjoint: {:?} does not unify with {:?}" ,
370+ impl1_trait_ref, impl2_trait_ref
371+ ) ;
372+ return false ;
373+ }
374+ } ;
375+
376+ let opt_failing_obligation =
377+ obligations. into_iter ( ) . chain ( more_obligations) . find ( |o| strict_check ( selcx, o) ) ;
378+
379+ if let Some ( failing_obligation) = opt_failing_obligation {
380+ debug ! ( "overlap: obligation unsatisfiable {:?}" , failing_obligation) ;
381+ true
382+ } else {
383+ false
384+ }
385+ } )
386+ }
387+
297388fn loose_check < ' cx , ' tcx > (
298389 selcx : & mut SelectionContext < ' cx , ' tcx > ,
299390 o : & PredicateObligation < ' tcx > ,
0 commit comments