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
+ PredicateObligations , SelectionContext ,
13
15
} ;
14
16
use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
15
17
use rustc_middle:: ty:: fast_reject:: { self , SimplifyParams , StripReferences } ;
@@ -135,45 +137,83 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
135
137
header
136
138
}
137
139
140
+ /// What kind of overlap check are we doing -- this exists just for testing and feature-gating
141
+ /// purposes.
142
+ #[ derive( Copy , Clone , PartialEq , Eq , Hash , Debug ) ]
143
+ enum OverlapMode {
144
+ /// The 1.0 rules (either types fail to unify, or where clauses are not implemented for crate-local types)
145
+ Stable ,
146
+ /// Feature-gated test: Stable, *or* there is an explicit negative impl that rules out one of the where-clauses.
147
+ WithNegative ,
148
+ /// Just check for negative impls, not for "where clause not implemented": used for testing.
149
+ Strict ,
150
+ }
151
+
152
+ impl OverlapMode {
153
+ fn use_negative_impl ( & self ) -> bool {
154
+ * self == OverlapMode :: Strict || * self == OverlapMode :: WithNegative
155
+ }
156
+
157
+ fn use_implicit_negative ( & self ) -> bool {
158
+ * self == OverlapMode :: Stable || * self == OverlapMode :: WithNegative
159
+ }
160
+ }
161
+
162
+ fn overlap_mode < ' tcx > ( tcx : TyCtxt < ' tcx > , impl1_def_id : DefId , impl2_def_id : DefId ) -> OverlapMode {
163
+ if tcx. has_attr ( impl1_def_id, sym:: rustc_strict_coherence)
164
+ != tcx. has_attr ( impl2_def_id, sym:: rustc_strict_coherence)
165
+ {
166
+ bug ! ( "Use strict coherence on both impls" , ) ;
167
+ }
168
+
169
+ if tcx. has_attr ( impl1_def_id, sym:: rustc_with_negative_coherence)
170
+ != tcx. has_attr ( impl2_def_id, sym:: rustc_with_negative_coherence)
171
+ {
172
+ bug ! ( "Use with negative coherence on both impls" , ) ;
173
+ }
174
+
175
+ if tcx. has_attr ( impl1_def_id, sym:: rustc_strict_coherence) {
176
+ OverlapMode :: Strict
177
+ } else if tcx. has_attr ( impl1_def_id, sym:: rustc_with_negative_coherence) {
178
+ OverlapMode :: WithNegative
179
+ } else {
180
+ OverlapMode :: Stable
181
+ }
182
+ }
183
+
138
184
/// Can both impl `a` and impl `b` be satisfied by a common type (including
139
185
/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls.
140
186
fn overlap < ' cx , ' tcx > (
141
187
selcx : & mut SelectionContext < ' cx , ' tcx > ,
142
188
skip_leak_check : SkipLeakCheck ,
143
- a_def_id : DefId ,
144
- b_def_id : DefId ,
189
+ impl1_def_id : DefId ,
190
+ impl2_def_id : DefId ,
145
191
) -> Option < OverlapResult < ' tcx > > {
146
- debug ! ( "overlap(a_def_id ={:?}, b_def_id ={:?})" , a_def_id , b_def_id ) ;
192
+ debug ! ( "overlap(impl1_def_id ={:?}, impl2_def_id ={:?})" , impl1_def_id , impl2_def_id ) ;
147
193
148
194
selcx. infcx ( ) . probe_maybe_skip_leak_check ( skip_leak_check. is_yes ( ) , |snapshot| {
149
- overlap_within_probe ( selcx, skip_leak_check, a_def_id , b_def_id , snapshot)
195
+ overlap_within_probe ( selcx, skip_leak_check, impl1_def_id , impl2_def_id , snapshot)
150
196
} )
151
197
}
152
198
153
199
fn overlap_within_probe < ' cx , ' tcx > (
154
200
selcx : & mut SelectionContext < ' cx , ' tcx > ,
155
201
skip_leak_check : SkipLeakCheck ,
156
- a_def_id : DefId ,
157
- b_def_id : DefId ,
202
+ impl1_def_id : DefId ,
203
+ impl2_def_id : DefId ,
158
204
snapshot : & CombinedSnapshot < ' _ , ' tcx > ,
159
205
) -> Option < OverlapResult < ' tcx > > {
160
- fn loose_check < ' cx , ' tcx > (
161
- selcx : & mut SelectionContext < ' cx , ' tcx > ,
162
- o : & PredicateObligation < ' tcx > ,
163
- ) -> bool {
164
- !selcx. predicate_may_hold_fatal ( o)
165
- }
206
+ let infcx = selcx. infcx ( ) ;
207
+ let tcx = infcx. tcx ;
166
208
167
- fn strict_check < ' cx , ' tcx > (
168
- selcx : & SelectionContext < ' cx , ' tcx > ,
169
- o : & PredicateObligation < ' tcx > ,
170
- ) -> bool {
171
- let infcx = selcx. infcx ( ) ;
172
- let tcx = infcx. tcx ;
173
- o. flip_polarity ( tcx)
174
- . as_ref ( )
175
- . map ( |o| selcx. infcx ( ) . predicate_must_hold_modulo_regions ( o) )
176
- . unwrap_or ( false )
209
+ let overlap_mode = overlap_mode ( tcx, impl1_def_id, impl2_def_id) ;
210
+
211
+ if overlap_mode. use_negative_impl ( ) {
212
+ if negative_impl ( selcx, impl1_def_id, impl2_def_id)
213
+ || negative_impl ( selcx, impl2_def_id, impl1_def_id)
214
+ {
215
+ return None ;
216
+ }
177
217
}
178
218
179
219
// For the purposes of this check, we don't bring any placeholder
@@ -182,26 +222,61 @@ fn overlap_within_probe<'cx, 'tcx>(
182
222
// empty environment.
183
223
let param_env = ty:: ParamEnv :: empty ( ) ;
184
224
185
- let a_impl_header = with_fresh_ty_vars ( selcx, param_env, a_def_id ) ;
186
- let b_impl_header = with_fresh_ty_vars ( selcx, param_env, b_def_id ) ;
225
+ let impl1_header = with_fresh_ty_vars ( selcx, param_env, impl1_def_id ) ;
226
+ let impl2_header = with_fresh_ty_vars ( selcx, param_env, impl2_def_id ) ;
187
227
188
- debug ! ( "overlap: a_impl_header ={:?}" , a_impl_header ) ;
189
- debug ! ( "overlap: b_impl_header ={:?}" , b_impl_header ) ;
228
+ debug ! ( "overlap: impl1_header ={:?}" , impl1_header ) ;
229
+ debug ! ( "overlap: impl2_header ={:?}" , impl2_header ) ;
190
230
191
- // Do `a` and `b` unify? If not, no overlap.
192
- let obligations = match selcx
193
- . infcx ( )
194
- . at ( & ObligationCause :: dummy ( ) , param_env)
195
- . eq_impl_headers ( & a_impl_header, & b_impl_header)
196
- {
197
- Ok ( InferOk { obligations, value : ( ) } ) => obligations,
198
- Err ( _) => {
231
+ let obligations = equate_impl_headers ( selcx, & impl1_header, & impl2_header) ?;
232
+ debug ! ( "overlap: unification check succeeded" ) ;
233
+
234
+ if overlap_mode. use_implicit_negative ( ) {
235
+ if implicit_negative ( selcx, param_env, & impl1_header, impl2_header, obligations) {
199
236
return None ;
200
237
}
201
- } ;
238
+ }
202
239
203
- debug ! ( "overlap: unification check succeeded" ) ;
240
+ if !skip_leak_check. is_yes ( ) {
241
+ if infcx. leak_check ( true , snapshot) . is_err ( ) {
242
+ debug ! ( "overlap: leak check failed" ) ;
243
+ return None ;
244
+ }
245
+ }
246
+
247
+ let intercrate_ambiguity_causes = selcx. take_intercrate_ambiguity_causes ( ) ;
248
+ debug ! ( "overlap: intercrate_ambiguity_causes={:#?}" , intercrate_ambiguity_causes) ;
249
+
250
+ let involves_placeholder =
251
+ matches ! ( selcx. infcx( ) . region_constraints_added_in_snapshot( snapshot) , Some ( true ) ) ;
252
+
253
+ let impl_header = selcx. infcx ( ) . resolve_vars_if_possible ( impl1_header) ;
254
+ Some ( OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder } )
255
+ }
256
+
257
+ fn equate_impl_headers < ' cx , ' tcx > (
258
+ selcx : & mut SelectionContext < ' cx , ' tcx > ,
259
+ impl1_header : & ty:: ImplHeader < ' tcx > ,
260
+ impl2_header : & ty:: ImplHeader < ' tcx > ,
261
+ ) -> Option < PredicateObligations < ' tcx > > {
262
+ // Do `a` and `b` unify? If not, no overlap.
263
+ selcx
264
+ . infcx ( )
265
+ . at ( & ObligationCause :: dummy ( ) , ty:: ParamEnv :: empty ( ) )
266
+ . eq_impl_headers ( impl1_header, impl2_header)
267
+ . map ( |infer_ok| infer_ok. obligations )
268
+ . ok ( )
269
+ }
204
270
271
+ /// Given impl1 and impl2 check if both impls can be satisfied by a common type (including
272
+ /// where-clauses) If so, return false, otherwise return true, they are disjoint.
273
+ fn implicit_negative < ' cx , ' tcx > (
274
+ selcx : & mut SelectionContext < ' cx , ' tcx > ,
275
+ param_env : ty:: ParamEnv < ' tcx > ,
276
+ impl1_header : & ty:: ImplHeader < ' tcx > ,
277
+ impl2_header : ty:: ImplHeader < ' tcx > ,
278
+ obligations : PredicateObligations < ' tcx > ,
279
+ ) -> bool {
205
280
// There's no overlap if obligations are unsatisfiable or if the obligation negated is
206
281
// satisfied.
207
282
//
@@ -225,11 +300,11 @@ fn overlap_within_probe<'cx, 'tcx>(
225
300
// at some point an impl for `&'?a str: Error` could be added.
226
301
let infcx = selcx. infcx ( ) ;
227
302
let tcx = infcx. tcx ;
228
- let opt_failing_obligation = a_impl_header
303
+ let opt_failing_obligation = impl1_header
229
304
. predicates
230
305
. iter ( )
231
306
. copied ( )
232
- . chain ( b_impl_header . predicates )
307
+ . chain ( impl2_header . predicates )
233
308
. map ( |p| infcx. resolve_vars_if_possible ( p) )
234
309
. map ( |p| Obligation {
235
310
cause : ObligationCause :: dummy ( ) ,
@@ -239,40 +314,105 @@ fn overlap_within_probe<'cx, 'tcx>(
239
314
} )
240
315
. chain ( obligations)
241
316
. find ( |o| {
242
- // if both impl headers are set to strict coherence it means that this will be accepted
243
- // only if it's stated that T: !Trait. So only prove that the negated obligation holds.
244
- if tcx. has_attr ( a_def_id, sym:: rustc_strict_coherence)
245
- && tcx. has_attr ( b_def_id, sym:: rustc_strict_coherence)
246
- {
247
- strict_check ( selcx, o)
248
- } else {
249
- loose_check ( selcx, o) || tcx. features ( ) . negative_impls && strict_check ( selcx, o)
250
- }
317
+ loose_check ( selcx, o) || tcx. features ( ) . negative_impls && negative_impl_exists ( selcx, o)
251
318
} ) ;
252
319
// FIXME: the call to `selcx.predicate_may_hold_fatal` above should be ported
253
320
// to the canonical trait query form, `infcx.predicate_may_hold`, once
254
321
// the new system supports intercrate mode (which coherence needs).
255
322
256
323
if let Some ( failing_obligation) = opt_failing_obligation {
257
324
debug ! ( "overlap: obligation unsatisfiable {:?}" , failing_obligation) ;
258
- return None ;
325
+ true
326
+ } else {
327
+ false
259
328
}
329
+ }
260
330
261
- if !skip_leak_check. is_yes ( ) {
262
- if infcx. leak_check ( true , snapshot) . is_err ( ) {
263
- debug ! ( "overlap: leak check failed" ) ;
264
- return None ;
265
- }
266
- }
331
+ /// Given impl1 and impl2 check if both impls are never satisfied by a common type (including
332
+ /// where-clauses) If so, return true, they are disjoint and false otherwise.
333
+ fn negative_impl < ' cx , ' tcx > (
334
+ selcx : & mut SelectionContext < ' cx , ' tcx > ,
335
+ impl1_def_id : DefId ,
336
+ impl2_def_id : DefId ,
337
+ ) -> bool {
338
+ let tcx = selcx. infcx ( ) . tcx ;
267
339
268
- let impl_header = selcx . infcx ( ) . resolve_vars_if_possible ( a_impl_header ) ;
269
- let intercrate_ambiguity_causes = selcx . take_intercrate_ambiguity_causes ( ) ;
270
- debug ! ( "overlap: intercrate_ambiguity_causes={:#?}" , intercrate_ambiguity_causes ) ;
340
+ // create a parameter environment corresponding to a (placeholder) instantiation of impl1
341
+ let impl1_env = tcx . param_env ( impl1_def_id ) ;
342
+ let impl1_trait_ref = tcx . impl_trait_ref ( impl1_def_id ) . unwrap ( ) ;
271
343
272
- let involves_placeholder =
273
- matches ! ( selcx. infcx( ) . region_constraints_added_in_snapshot( snapshot) , Some ( true ) ) ;
344
+ // Create an infcx, taking the predicates of impl1 as assumptions:
345
+ tcx. infer_ctxt ( ) . enter ( |infcx| {
346
+ // Normalize the trait reference. The WF rules ought to ensure
347
+ // that this always succeeds.
348
+ let impl1_trait_ref = match traits:: fully_normalize (
349
+ & infcx,
350
+ FulfillmentContext :: new ( ) ,
351
+ ObligationCause :: dummy ( ) ,
352
+ impl1_env,
353
+ impl1_trait_ref,
354
+ ) {
355
+ Ok ( impl1_trait_ref) => impl1_trait_ref,
356
+ Err ( err) => {
357
+ bug ! ( "failed to fully normalize {:?}: {:?}" , impl1_trait_ref, err) ;
358
+ }
359
+ } ;
360
+
361
+ // Attempt to prove that impl2 applies, given all of the above.
362
+ let selcx = & mut SelectionContext :: new ( & infcx) ;
363
+ let impl2_substs = infcx. fresh_substs_for_item ( DUMMY_SP , impl2_def_id) ;
364
+ let ( impl2_trait_ref, obligations) =
365
+ impl_trait_ref_and_oblig ( selcx, impl1_env, impl2_def_id, impl2_substs) ;
366
+
367
+ // do the impls unify? If not, not disjoint.
368
+ let more_obligations = match infcx
369
+ . at ( & ObligationCause :: dummy ( ) , impl1_env)
370
+ . eq ( impl1_trait_ref, impl2_trait_ref)
371
+ {
372
+ Ok ( InferOk { obligations, .. } ) => obligations,
373
+ Err ( _) => {
374
+ debug ! (
375
+ "explicit_disjoint: {:?} does not unify with {:?}" ,
376
+ impl1_trait_ref, impl2_trait_ref
377
+ ) ;
378
+ return false ;
379
+ }
380
+ } ;
274
381
275
- Some ( OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder } )
382
+ let opt_failing_obligation = obligations
383
+ . into_iter ( )
384
+ . chain ( more_obligations)
385
+ . find ( |o| negative_impl_exists ( selcx, o) ) ;
386
+
387
+ if let Some ( failing_obligation) = opt_failing_obligation {
388
+ debug ! ( "overlap: obligation unsatisfiable {:?}" , failing_obligation) ;
389
+ true
390
+ } else {
391
+ false
392
+ }
393
+ } )
394
+ }
395
+
396
+ fn loose_check < ' cx , ' tcx > (
397
+ selcx : & mut SelectionContext < ' cx , ' tcx > ,
398
+ o : & PredicateObligation < ' tcx > ,
399
+ ) -> bool {
400
+ !selcx. predicate_may_hold_fatal ( o)
401
+ }
402
+
403
+ fn negative_impl_exists < ' cx , ' tcx > (
404
+ selcx : & SelectionContext < ' cx , ' tcx > ,
405
+ o : & PredicateObligation < ' tcx > ,
406
+ ) -> bool {
407
+ let infcx = selcx. infcx ( ) ;
408
+ let tcx = infcx. tcx ;
409
+ o. flip_polarity ( tcx)
410
+ . as_ref ( )
411
+ . map ( |o| {
412
+ // FIXME This isn't quite correct, regions should be included
413
+ selcx. infcx ( ) . predicate_must_hold_modulo_regions ( o)
414
+ } )
415
+ . unwrap_or ( false )
276
416
}
277
417
278
418
pub fn trait_ref_is_knowable < ' tcx > (
0 commit comments