7
7
use crate :: infer:: { CombinedSnapshot , InferOk , TyCtxtInferExt } ;
8
8
use crate :: traits:: query:: evaluate_obligation:: InferCtxtExt ;
9
9
use crate :: traits:: select:: IntercrateAmbiguityCause ;
10
+ use crate :: traits:: util:: impl_trait_ref_and_oblig;
10
11
use crate :: traits:: SkipLeakCheck ;
11
12
use crate :: traits:: {
12
- self , Normalized , Obligation , ObligationCause , PredicateObligation , SelectionContext ,
13
+ self , FulfillmentContext , Normalized , Obligation , ObligationCause , PredicateObligation ,
14
+ SelectionContext ,
13
15
} ;
14
16
use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
15
17
use rustc_middle:: ty:: fast_reject:: { self , SimplifyParams , StripReferences } ;
@@ -137,6 +139,7 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
137
139
138
140
enum OverlapMode {
139
141
Stable ,
142
+ WithNegative ,
140
143
Strict ,
141
144
}
142
145
@@ -147,8 +150,16 @@ fn overlap_mode<'tcx>(tcx: TyCtxt<'tcx>, impl1_def_id: DefId, impl2_def_id: DefI
147
150
bug ! ( "Use strict coherence on both impls" , ) ;
148
151
}
149
152
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
+
150
159
if tcx. has_attr ( impl1_def_id, sym:: rustc_strict_coherence) {
151
160
OverlapMode :: Strict
161
+ } else if tcx. has_attr ( impl1_def_id, sym:: rustc_with_negative_coherence) {
162
+ OverlapMode :: WithNegative
152
163
} else {
153
164
OverlapMode :: Stable
154
165
}
@@ -188,9 +199,25 @@ fn overlap_within_probe<'cx, 'tcx>(
188
199
let impl1_header = with_fresh_ty_vars ( selcx, param_env, impl1_def_id) ;
189
200
let impl2_header = with_fresh_ty_vars ( selcx, param_env, impl2_def_id) ;
190
201
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
+ }
194
221
}
195
222
196
223
if !skip_leak_check. is_yes ( ) {
@@ -280,6 +307,7 @@ fn stable_disjoint<'cx, 'tcx>(
280
307
loose_check ( selcx, o) || tcx. features ( ) . negative_impls && strict_check ( selcx, o)
281
308
}
282
309
OverlapMode :: Strict => strict_check ( selcx, o) ,
310
+ OverlapMode :: WithNegative => loose_check ( selcx, o) ,
283
311
}
284
312
} ) ;
285
313
// FIXME: the call to `selcx.predicate_may_hold_fatal` above should be ported
@@ -294,6 +322,69 @@ fn stable_disjoint<'cx, 'tcx>(
294
322
}
295
323
}
296
324
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
+
297
388
fn loose_check < ' cx , ' tcx > (
298
389
selcx : & mut SelectionContext < ' cx , ' tcx > ,
299
390
o : & PredicateObligation < ' tcx > ,
0 commit comments