about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs1
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs99
-rw-r--r--src/test/ui/coherence/auxiliary/option_future.rs8
-rw-r--r--src/test/ui/coherence/coherence-overlap-negative-trait2.rs18
5 files changed, 123 insertions, 4 deletions
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 723cc06864a..0e643ff5998 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -697,6 +697,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_strict_coherence, Normal, template!(Word), WarnFollowing),
+    rustc_attr!(TEST, rustc_with_negative_coherence, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_variance, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ..."), WarnFollowing),
     rustc_attr!(TEST, rustc_regions, Normal, template!(Word), WarnFollowing),
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 702e3594660..7e0ce89be67 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1203,6 +1203,7 @@ symbols! {
         rustc_trivial_field_reads,
         rustc_unsafe_specialization_marker,
         rustc_variance,
+        rustc_with_negative_coherence,
         rustdoc,
         rustdoc_internals,
         rustfmt,
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 170740d2cbd..42b7139c006 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -7,9 +7,11 @@
 use crate::infer::{CombinedSnapshot, InferOk, TyCtxtInferExt};
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
 use crate::traits::select::IntercrateAmbiguityCause;
+use crate::traits::util::impl_trait_ref_and_oblig;
 use crate::traits::SkipLeakCheck;
 use crate::traits::{
-    self, Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext,
+    self, FulfillmentContext, Normalized, Obligation, ObligationCause, PredicateObligation,
+    SelectionContext,
 };
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_middle::ty::fast_reject::{self, SimplifyParams, StripReferences};
@@ -137,6 +139,7 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
 
 enum OverlapMode {
     Stable,
+    WithNegative,
     Strict,
 }
 
@@ -147,8 +150,16 @@ fn overlap_mode<'tcx>(tcx: TyCtxt<'tcx>, impl1_def_id: DefId, impl2_def_id: DefI
         bug!("Use strict coherence on both impls",);
     }
 
+    if tcx.has_attr(impl1_def_id, sym::rustc_with_negative_coherence)
+        != tcx.has_attr(impl2_def_id, sym::rustc_with_negative_coherence)
+    {
+        bug!("Use with negative coherence on both impls",);
+    }
+
     if tcx.has_attr(impl1_def_id, sym::rustc_strict_coherence) {
         OverlapMode::Strict
+    } else if tcx.has_attr(impl1_def_id, sym::rustc_with_negative_coherence) {
+        OverlapMode::WithNegative
     } else {
         OverlapMode::Stable
     }
@@ -188,9 +199,25 @@ fn overlap_within_probe<'cx, 'tcx>(
     let impl1_header = with_fresh_ty_vars(selcx, param_env, impl1_def_id);
     let impl2_header = with_fresh_ty_vars(selcx, param_env, impl2_def_id);
 
-    let overlap_mode = overlap_mode(tcx, impl1_def_id, impl2_def_id);
-    if stable_disjoint(selcx, param_env, &impl1_header, impl2_header, overlap_mode) {
-        return None;
+    match overlap_mode(tcx, impl1_def_id, impl2_def_id) {
+        OverlapMode::Stable => {
+            if stable_disjoint(selcx, param_env, &impl1_header, impl2_header, OverlapMode::Stable) {
+                return None;
+            }
+        }
+        OverlapMode::Strict => {
+            if stable_disjoint(selcx, param_env, &impl1_header, impl2_header, OverlapMode::Strict) {
+                return None;
+            }
+        }
+        OverlapMode::WithNegative => {
+            if stable_disjoint(selcx, param_env, &impl1_header, impl2_header, OverlapMode::Stable)
+                || explicit_disjoint(selcx, impl1_def_id, impl2_def_id)
+                || explicit_disjoint(selcx, impl2_def_id, impl1_def_id)
+            {
+                return None;
+            }
+        }
     }
 
     if !skip_leak_check.is_yes() {
@@ -280,6 +307,7 @@ fn stable_disjoint<'cx, 'tcx>(
                     loose_check(selcx, o) || tcx.features().negative_impls && strict_check(selcx, o)
                 }
                 OverlapMode::Strict => strict_check(selcx, o),
+                OverlapMode::WithNegative => loose_check(selcx, o),
             }
         });
     // FIXME: the call to `selcx.predicate_may_hold_fatal` above should be ported
@@ -294,6 +322,69 @@ fn stable_disjoint<'cx, 'tcx>(
     }
 }
 
+/// Given impl1 and impl2 check if both impls are never satisfied by a common type (including
+/// where-clauses) If so, return true, they are disjoint and false otherwise.
+fn explicit_disjoint<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    impl1_def_id: DefId,
+    impl2_def_id: DefId,
+) -> bool {
+    let tcx = selcx.infcx().tcx;
+
+    // create a parameter environment corresponding to a (placeholder) instantiation of impl1
+    let impl1_env = tcx.param_env(impl1_def_id);
+    let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap();
+
+    // Create an infcx, taking the predicates of impl1 as assumptions:
+    tcx.infer_ctxt().enter(|infcx| {
+        // Normalize the trait reference. The WF rules ought to ensure
+        // that this always succeeds.
+        let impl1_trait_ref = match traits::fully_normalize(
+            &infcx,
+            FulfillmentContext::new(),
+            ObligationCause::dummy(),
+            impl1_env,
+            impl1_trait_ref,
+        ) {
+            Ok(impl1_trait_ref) => impl1_trait_ref,
+            Err(err) => {
+                bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err);
+            }
+        };
+
+        // Attempt to prove that impl2 applies, given all of the above.
+        let selcx = &mut SelectionContext::new(&infcx);
+        let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id);
+        let (impl2_trait_ref, obligations) =
+            impl_trait_ref_and_oblig(selcx, impl1_env, impl2_def_id, impl2_substs);
+
+        // do the impls unify? If not, not disjoint.
+        let more_obligations = match infcx
+            .at(&ObligationCause::dummy(), impl1_env)
+            .eq(impl1_trait_ref, impl2_trait_ref)
+        {
+            Ok(InferOk { obligations, .. }) => obligations,
+            Err(_) => {
+                debug!(
+                    "explicit_disjoint: {:?} does not unify with {:?}",
+                    impl1_trait_ref, impl2_trait_ref
+                );
+                return false;
+            }
+        };
+
+        let opt_failing_obligation =
+            obligations.into_iter().chain(more_obligations).find(|o| strict_check(selcx, o));
+
+        if let Some(failing_obligation) = opt_failing_obligation {
+            debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
+            true
+        } else {
+            false
+        }
+    })
+}
+
 fn loose_check<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     o: &PredicateObligation<'tcx>,
diff --git a/src/test/ui/coherence/auxiliary/option_future.rs b/src/test/ui/coherence/auxiliary/option_future.rs
new file mode 100644
index 00000000000..f71df1b87fc
--- /dev/null
+++ b/src/test/ui/coherence/auxiliary/option_future.rs
@@ -0,0 +1,8 @@
+#![crate_type = "lib"]
+#![feature(negative_impls)]
+#![feature(rustc_attrs)]
+
+pub trait Future {}
+
+#[rustc_with_negative_coherence]
+impl<E> !Future for Option<E> where E: Sized {}
diff --git a/src/test/ui/coherence/coherence-overlap-negative-trait2.rs b/src/test/ui/coherence/coherence-overlap-negative-trait2.rs
new file mode 100644
index 00000000000..1f47b5ba46e
--- /dev/null
+++ b/src/test/ui/coherence/coherence-overlap-negative-trait2.rs
@@ -0,0 +1,18 @@
+// check-pass
+// aux-build:option_future.rs
+//
+// Check that if we promise to not impl what would overlap it doesn't actually overlap
+
+#![feature(rustc_attrs)]
+
+extern crate option_future as lib;
+use lib::Future;
+
+trait Termination {}
+
+#[rustc_with_negative_coherence]
+impl<E> Termination for Option<E> where E: Sized {}
+#[rustc_with_negative_coherence]
+impl<F> Termination for F where F: Future + Sized {}
+
+fn main() {}