about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_infer/src/infer/at.rs25
-rw-r--r--compiler/rustc_middle/src/hir/mod.rs10
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs7
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs26
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs87
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs20
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs20
-rw-r--r--src/test/ui/coherence/coherence-negative-inherent-where-bounds.rs25
-rw-r--r--src/test/ui/coherence/coherence-negative-inherent.rs22
9 files changed, 177 insertions, 65 deletions
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
index 94991fdb201..ff8082840e1 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -28,7 +28,7 @@
 use super::*;
 
 use rustc_middle::ty::relate::{Relate, TypeRelation};
-use rustc_middle::ty::Const;
+use rustc_middle::ty::{Const, ImplSubject};
 
 pub struct At<'a, 'tcx> {
     pub infcx: &'a InferCtxt<'a, 'tcx>,
@@ -272,6 +272,29 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
     }
 }
 
+impl<'tcx> ToTrace<'tcx> for ImplSubject<'tcx> {
+    fn to_trace(
+        tcx: TyCtxt<'tcx>,
+        cause: &ObligationCause<'tcx>,
+        a_is_expected: bool,
+        a: Self,
+        b: Self,
+    ) -> TypeTrace<'tcx> {
+        match (a, b) {
+            (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => {
+                ToTrace::to_trace(tcx, cause, a_is_expected, trait_ref_a, trait_ref_b)
+            }
+            (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => {
+                ToTrace::to_trace(tcx, cause, a_is_expected, ty_a, ty_b)
+            }
+            (ImplSubject::Trait(_), ImplSubject::Inherent(_))
+            | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => {
+                bug!("can not trace TraitRef and Ty");
+            }
+        }
+    }
+}
+
 impl<'tcx> ToTrace<'tcx> for Ty<'tcx> {
     fn to_trace(
         _: TyCtxt<'tcx>,
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index 1053f0cefbe..a9e22d16ee0 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -7,10 +7,10 @@ pub mod nested_filter;
 pub mod place;
 
 use crate::ty::query::Providers;
-use crate::ty::TyCtxt;
+use crate::ty::{ImplSubject, TyCtxt};
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_hir::def_id::LocalDefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::*;
 use rustc_query_system::ich::StableHashingContext;
 use rustc_span::DUMMY_SP;
@@ -54,6 +54,12 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn parent_module(self, id: HirId) -> LocalDefId {
         self.parent_module_from_def_id(id.owner)
     }
+
+    pub fn impl_subject(self, def_id: DefId) -> ImplSubject<'tcx> {
+        self.impl_trait_ref(def_id)
+            .map(ImplSubject::Trait)
+            .unwrap_or_else(|| ImplSubject::Inherent(self.type_of(def_id)))
+    }
 }
 
 pub fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 3124cc5dba8..31db66dc242 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -44,6 +44,7 @@ use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_span::Span;
 use rustc_target::abi::Align;
 
+use std::fmt::Debug;
 use std::hash::Hash;
 use std::ops::ControlFlow;
 use std::{fmt, str};
@@ -172,6 +173,12 @@ pub struct ImplHeader<'tcx> {
     pub predicates: Vec<Predicate<'tcx>>,
 }
 
+#[derive(Copy, Clone, Debug, TypeFoldable)]
+pub enum ImplSubject<'tcx> {
+    Trait(TraitRef<'tcx>),
+    Inherent(Ty<'tcx>),
+}
+
 #[derive(
     Copy,
     Clone,
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index 5d6cbcf6907..d9b55563996 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -7,7 +7,7 @@
 use crate::mir::interpret::{get_slice_bytes, ConstValue, GlobalAlloc, Scalar};
 use crate::ty::error::{ExpectedFound, TypeError};
 use crate::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
-use crate::ty::{self, Term, Ty, TyCtxt, TypeFoldable};
+use crate::ty::{self, ImplSubject, Term, Ty, TyCtxt, TypeFoldable};
 use rustc_hir as ast;
 use rustc_hir::def_id::DefId;
 use rustc_span::DUMMY_SP;
@@ -356,6 +356,30 @@ impl<'tcx> Relate<'tcx> for GeneratorWitness<'tcx> {
     }
 }
 
+impl<'tcx> Relate<'tcx> for ImplSubject<'tcx> {
+    #[inline]
+    fn relate<R: TypeRelation<'tcx>>(
+        relation: &mut R,
+        a: ImplSubject<'tcx>,
+        b: ImplSubject<'tcx>,
+    ) -> RelateResult<'tcx, ImplSubject<'tcx>> {
+        match (a, b) {
+            (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => {
+                let trait_ref = ty::TraitRef::relate(relation, trait_ref_a, trait_ref_b)?;
+                Ok(ImplSubject::Trait(trait_ref))
+            }
+            (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => {
+                let ty = Ty::relate(relation, ty_a, ty_b)?;
+                Ok(ImplSubject::Inherent(ty))
+            }
+            (ImplSubject::Trait(_), ImplSubject::Inherent(_))
+            | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => {
+                bug!("can not relate TraitRef and Ty");
+            }
+        }
+    }
+}
+
 impl<'tcx> Relate<'tcx> for Ty<'tcx> {
     #[inline]
     fn relate<R: TypeRelation<'tcx>>(
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 94a4001bbb9..ec4f8084e21 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -7,7 +7,7 @@
 use crate::infer::outlives::env::OutlivesEnvironment;
 use crate::infer::{CombinedSnapshot, InferOk, RegionckMode};
 use crate::traits::select::IntercrateAmbiguityCause;
-use crate::traits::util::impl_trait_ref_and_oblig;
+use crate::traits::util::impl_subject_and_oblig;
 use crate::traits::SkipLeakCheck;
 use crate::traits::{
     self, FulfillmentContext, Normalized, Obligation, ObligationCause, PredicateObligation,
@@ -23,9 +23,10 @@ use rustc_middle::traits::specialization_graph::OverlapMode;
 use rustc_middle::ty::fast_reject::{self, TreatParams};
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::subst::Subst;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt};
 use rustc_span::symbol::sym;
 use rustc_span::DUMMY_SP;
+use std::fmt::Debug;
 use std::iter;
 
 /// Whether we do the orphan check relative to this crate or
@@ -300,60 +301,62 @@ fn negative_impl<'cx, 'tcx>(
     debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
     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(
+        // create a parameter environment corresponding to a (placeholder) instantiation of impl1
+        let impl_env = tcx.param_env(impl1_def_id);
+        let subject1 = match traits::fully_normalize(
             &infcx,
             FulfillmentContext::new(),
             ObligationCause::dummy(),
-            impl1_env,
-            impl1_trait_ref,
+            impl_env,
+            tcx.impl_subject(impl1_def_id),
         ) {
-            Ok(impl1_trait_ref) => impl1_trait_ref,
-            Err(err) => {
-                bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err);
-            }
+            Ok(s) => s,
+            Err(err) => bug!("failed to fully normalize {:?}: {:?}", impl1_def_id, 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 Ok(InferOk { obligations: more_obligations, .. }) = infcx
-            .at(&ObligationCause::dummy(), impl1_env)
-            .eq(impl1_trait_ref, impl2_trait_ref)
-        else {
-            debug!(
-                "explicit_disjoint: {:?} does not unify with {:?}",
-                impl1_trait_ref, impl2_trait_ref
-            );
-            return false;
-        };
+        let (subject2, obligations) =
+            impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs);
 
-        let opt_failing_obligation = obligations
-            .into_iter()
-            .chain(more_obligations)
-            .find(|o| negative_impl_exists(selcx, impl1_env, impl1_def_id, o));
-
-        if let Some(failing_obligation) = opt_failing_obligation {
-            debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
-            true
-        } else {
-            false
-        }
+        !equate(&infcx, impl_env, impl1_def_id, subject1, subject2, obligations)
     })
 }
 
-/// Try to prove that a negative impl exist for the given obligation and their super predicates.
+fn equate<'cx, 'tcx>(
+    infcx: &InferCtxt<'cx, 'tcx>,
+    impl_env: ty::ParamEnv<'tcx>,
+    impl1_def_id: DefId,
+    subject1: ImplSubject<'tcx>,
+    subject2: ImplSubject<'tcx>,
+    obligations: impl Iterator<Item = PredicateObligation<'tcx>>,
+) -> bool {
+    // do the impls unify? If not, not disjoint.
+    let Ok(InferOk { obligations: more_obligations, .. }) =
+        infcx.at(&ObligationCause::dummy(), impl_env).eq(subject1, subject2)
+    else {
+        debug!("explicit_disjoint: {:?} does not unify with {:?}", subject1, subject2);
+        return true;
+    };
+
+    let selcx = &mut SelectionContext::new(&infcx);
+    let opt_failing_obligation = obligations
+        .into_iter()
+        .chain(more_obligations)
+        .find(|o| negative_impl_exists(selcx, impl_env, impl1_def_id, o));
+
+    if let Some(failing_obligation) = opt_failing_obligation {
+        debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
+        false
+    } else {
+        true
+    }
+}
+
+/// Try to prove that a negative impl exist for the given obligation and its super predicates.
 #[instrument(level = "debug", skip(selcx))]
 fn negative_impl_exists<'cx, 'tcx>(
     selcx: &SelectionContext<'cx, 'tcx>,
@@ -367,7 +370,7 @@ fn negative_impl_exists<'cx, 'tcx>(
         return true;
     }
 
-    // Try to prove a negative obligation exist for super predicates
+    // Try to prove a negative obligation exists for super predicates
     for o in util::elaborate_predicates(infcx.tcx, iter::once(o.predicate)) {
         if resolve_negative_obligation(infcx, param_env, region_context, &o) {
             return true;
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 79471065ccc..328e0d2e0e9 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -20,12 +20,12 @@ use rustc_errors::{struct_span_err, EmissionGuarantee};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::lint::LintDiagnosticBuilder;
 use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, ImplSubject, TyCtxt};
 use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
 use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
 use rustc_span::{Span, DUMMY_SP};
 
-use super::util::impl_trait_ref_and_oblig;
+use super::util;
 use super::{FulfillmentContext, SelectionContext};
 
 /// Information pertinent to an overlapping impl error.
@@ -186,18 +186,20 @@ fn fulfill_implication<'a, 'tcx>(
         param_env, source_trait_ref, target_impl
     );
 
+    let source_trait = ImplSubject::Trait(source_trait_ref);
+
     let selcx = &mut SelectionContext::new(&infcx);
     let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl);
-    let (target_trait_ref, obligations) =
-        impl_trait_ref_and_oblig(selcx, param_env, target_impl, target_substs);
+    let (target_trait, obligations) =
+        util::impl_subject_and_oblig(selcx, param_env, target_impl, target_substs);
 
     // do the impls unify? If not, no specialization.
     let Ok(InferOk { obligations: more_obligations, .. }) =
-        infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait_ref, target_trait_ref)
+        infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait, target_trait)
     else {
         debug!(
             "fulfill_implication: {:?} does not unify with {:?}",
-            source_trait_ref, target_trait_ref
+            source_trait, target_trait
         );
         return Err(());
     };
@@ -225,7 +227,7 @@ fn fulfill_implication<'a, 'tcx>(
             [] => {
                 debug!(
                     "fulfill_implication: an impl for {:?} specializes {:?}",
-                    source_trait_ref, target_trait_ref
+                    source_trait, target_trait
                 );
 
                 // Now resolve the *substitution* we built for the target earlier, replacing
@@ -237,8 +239,8 @@ fn fulfill_implication<'a, 'tcx>(
                 debug!(
                     "fulfill_implication: for impls on {:?} and {:?}, \
                      could not fulfill: {:?} given {:?}",
-                    source_trait_ref,
-                    target_trait_ref,
+                    source_trait,
+                    target_trait,
                     errors,
                     param_env.caller_bounds()
                 );
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index f800e7a1402..7543d1f9a7b 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -6,7 +6,7 @@ use smallvec::SmallVec;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef};
-use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeFoldable};
 
 use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext};
 pub use rustc_infer::traits::{self, util::*};
@@ -190,19 +190,19 @@ impl Iterator for SupertraitDefIds<'_> {
 // Other
 ///////////////////////////////////////////////////////////////////////////
 
-/// Instantiate all bound parameters of the impl with the given substs,
-/// returning the resulting trait ref and all obligations that arise.
+/// Instantiate all bound parameters of the impl subject with the given substs,
+/// returning the resulting subject and all obligations that arise.
 /// The obligations are closed under normalization.
-pub fn impl_trait_ref_and_oblig<'a, 'tcx>(
+pub fn impl_subject_and_oblig<'a, 'tcx>(
     selcx: &mut SelectionContext<'a, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     impl_def_id: DefId,
     impl_substs: SubstsRef<'tcx>,
-) -> (ty::TraitRef<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
-    let impl_trait_ref = selcx.tcx().impl_trait_ref(impl_def_id).unwrap();
-    let impl_trait_ref = impl_trait_ref.subst(selcx.tcx(), impl_substs);
-    let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } =
-        super::normalize(selcx, param_env, ObligationCause::dummy(), impl_trait_ref);
+) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
+    let subject = selcx.tcx().impl_subject(impl_def_id);
+    let subject = subject.subst(selcx.tcx(), impl_substs);
+    let Normalized { value: subject, obligations: normalization_obligations1 } =
+        super::normalize(selcx, param_env, ObligationCause::dummy(), subject);
 
     let predicates = selcx.tcx().predicates_of(impl_def_id);
     let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
@@ -215,7 +215,7 @@ pub fn impl_trait_ref_and_oblig<'a, 'tcx>(
         .chain(normalization_obligations1.into_iter())
         .chain(normalization_obligations2.into_iter());
 
-    (impl_trait_ref, impl_obligations)
+    (subject, impl_obligations)
 }
 
 pub fn predicates_for_generics<'tcx>(
diff --git a/src/test/ui/coherence/coherence-negative-inherent-where-bounds.rs b/src/test/ui/coherence/coherence-negative-inherent-where-bounds.rs
new file mode 100644
index 00000000000..39ccaa6ac35
--- /dev/null
+++ b/src/test/ui/coherence/coherence-negative-inherent-where-bounds.rs
@@ -0,0 +1,25 @@
+// check-pass
+
+#![feature(negative_impls)]
+#![feature(rustc_attrs)]
+#![feature(with_negative_coherence)]
+
+trait Foo {}
+
+impl !Foo for u32 {}
+
+#[rustc_strict_coherence]
+struct MyStruct<T>(T);
+
+impl MyStruct<u32> {
+    fn method(&self) {}
+}
+
+impl<T> MyStruct<T>
+where
+    T: Foo,
+{
+    fn method(&self) {}
+}
+
+fn main() {}
diff --git a/src/test/ui/coherence/coherence-negative-inherent.rs b/src/test/ui/coherence/coherence-negative-inherent.rs
new file mode 100644
index 00000000000..a9e1acc8044
--- /dev/null
+++ b/src/test/ui/coherence/coherence-negative-inherent.rs
@@ -0,0 +1,22 @@
+// check-pass
+
+#![feature(negative_impls)]
+#![feature(rustc_attrs)]
+#![feature(with_negative_coherence)]
+
+#[rustc_strict_coherence]
+trait Foo {}
+
+impl !Foo for u32 {}
+
+struct MyStruct<T>(T);
+
+impl<T: Foo> MyStruct<T> {
+    fn method(&self) {}
+}
+
+impl MyStruct<u32> {
+    fn method(&self) {}
+}
+
+fn main() {}