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+ PredicateObligations , SelectionContext ,
1315} ;
1416use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
1517use rustc_middle:: ty:: fast_reject:: { self , SimplifyParams , StripReferences } ;
@@ -135,45 +137,83 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
135137 header
136138}
137139
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+
138184/// Can both impl `a` and impl `b` be satisfied by a common type (including
139185/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls.
140186fn overlap < ' cx , ' tcx > (
141187 selcx : & mut SelectionContext < ' cx , ' tcx > ,
142188 skip_leak_check : SkipLeakCheck ,
143- a_def_id : DefId ,
144- b_def_id : DefId ,
189+ impl1_def_id : DefId ,
190+ impl2_def_id : DefId ,
145191) -> 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 ) ;
147193
148194 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)
150196 } )
151197}
152198
153199fn overlap_within_probe < ' cx , ' tcx > (
154200 selcx : & mut SelectionContext < ' cx , ' tcx > ,
155201 skip_leak_check : SkipLeakCheck ,
156- a_def_id : DefId ,
157- b_def_id : DefId ,
202+ impl1_def_id : DefId ,
203+ impl2_def_id : DefId ,
158204 snapshot : & CombinedSnapshot < ' _ , ' tcx > ,
159205) -> 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 ;
166208
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+ }
177217 }
178218
179219 // For the purposes of this check, we don't bring any placeholder
@@ -182,26 +222,61 @@ fn overlap_within_probe<'cx, 'tcx>(
182222 // empty environment.
183223 let param_env = ty:: ParamEnv :: empty ( ) ;
184224
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 ) ;
187227
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 ) ;
190230
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) {
199236 return None ;
200237 }
201- } ;
238+ }
202239
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+ }
204270
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 {
205280 // There's no overlap if obligations are unsatisfiable or if the obligation negated is
206281 // satisfied.
207282 //
@@ -225,11 +300,11 @@ fn overlap_within_probe<'cx, 'tcx>(
225300 // at some point an impl for `&'?a str: Error` could be added.
226301 let infcx = selcx. infcx ( ) ;
227302 let tcx = infcx. tcx ;
228- let opt_failing_obligation = a_impl_header
303+ let opt_failing_obligation = impl1_header
229304 . predicates
230305 . iter ( )
231306 . copied ( )
232- . chain ( b_impl_header . predicates )
307+ . chain ( impl2_header . predicates )
233308 . map ( |p| infcx. resolve_vars_if_possible ( p) )
234309 . map ( |p| Obligation {
235310 cause : ObligationCause :: dummy ( ) ,
@@ -239,40 +314,105 @@ fn overlap_within_probe<'cx, 'tcx>(
239314 } )
240315 . chain ( obligations)
241316 . 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)
251318 } ) ;
252319 // FIXME: the call to `selcx.predicate_may_hold_fatal` above should be ported
253320 // to the canonical trait query form, `infcx.predicate_may_hold`, once
254321 // the new system supports intercrate mode (which coherence needs).
255322
256323 if let Some ( failing_obligation) = opt_failing_obligation {
257324 debug ! ( "overlap: obligation unsatisfiable {:?}" , failing_obligation) ;
258- return None ;
325+ true
326+ } else {
327+ false
259328 }
329+ }
260330
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 ;
267339
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 ( ) ;
271343
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+ } ;
274381
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 )
276416}
277417
278418pub fn trait_ref_is_knowable < ' tcx > (
0 commit comments