about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthew Jasper <mjjasper1@gmail.com>2021-09-30 21:42:09 +0100
committerMatthew Jasper <mjjasper1@gmail.com>2021-09-30 21:42:09 +0100
commitc8f86cad2dda3181bfedc165d3dd4bf452770228 (patch)
tree012323d94c268be61063babbaf00b1dfc8290c02
parent6dc08b909b469d58dd8fa54c57ab193b8cf95257 (diff)
downloadrust-c8f86cad2dda3181bfedc165d3dd4bf452770228.tar.gz
rust-c8f86cad2dda3181bfedc165d3dd4bf452770228.zip
Elaborate predicates in min_specialization checks
Supertraits of specialization markers could circumvent checks for
min_specialization. Elaborating predicates prevents this.
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs4
-rw-r--r--compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs48
-rw-r--r--src/test/ui/specialization/min_specialization/spec-marker-supertraits.rs29
-rw-r--r--src/test/ui/specialization/min_specialization/spec-marker-supertraits.stderr13
4 files changed, 76 insertions, 18 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index b3c9cf4c173..d777f586e23 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -64,7 +64,9 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
 pub use self::specialize::{specialization_graph, translate_substs, OverlapError};
 pub use self::structural_match::search_for_structural_match_violation;
 pub use self::structural_match::NonStructuralMatchTy;
-pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs};
+pub use self::util::{
+    elaborate_obligations, elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs,
+};
 pub use self::util::{expand_trait_aliases, TraitAliasExpander};
 pub use self::util::{
     get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices,
diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
index 8ecd6034ad6..f4bb5761c19 100644
--- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
@@ -74,7 +74,7 @@ use rustc_infer::infer::{InferCtxt, RegionckMode, TyCtxtInferExt};
 use rustc_infer::traits::specialization_graph::Node;
 use rustc_middle::ty::subst::{GenericArg, InternalSubsts, SubstsRef};
 use rustc_middle::ty::trait_def::TraitSpecializationKind;
-use rustc_middle::ty::{self, InstantiatedPredicates, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
 use rustc_span::Span;
 use rustc_trait_selection::traits::{self, translate_substs, wf};
 
@@ -294,13 +294,27 @@ fn check_predicates<'tcx>(
     span: Span,
 ) {
     let tcx = infcx.tcx;
-    let impl1_predicates = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs);
+    let impl1_predicates: Vec<_> = traits::elaborate_predicates(
+        tcx,
+        tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs).predicates.into_iter(),
+    )
+    .map(|obligation| obligation.predicate)
+    .collect();
+
     let mut impl2_predicates = if impl2_node.is_from_trait() {
         // Always applicable traits have to be always applicable without any
         // assumptions.
-        InstantiatedPredicates::empty()
+        Vec::new()
     } else {
-        tcx.predicates_of(impl2_node.def_id()).instantiate(tcx, impl2_substs)
+        traits::elaborate_predicates(
+            tcx,
+            tcx.predicates_of(impl2_node.def_id())
+                .instantiate(tcx, impl2_substs)
+                .predicates
+                .into_iter(),
+        )
+        .map(|obligation| obligation.predicate)
+        .collect()
     };
     debug!(
         "check_always_applicable(\nimpl1_predicates={:?},\nimpl2_predicates={:?}\n)",
@@ -322,13 +336,12 @@ fn check_predicates<'tcx>(
     // which is sound because we forbid impls like the following
     //
     // impl<D: Debug> AlwaysApplicable for D { }
-    let always_applicable_traits =
-        impl1_predicates.predicates.iter().copied().filter(|&predicate| {
-            matches!(
-                trait_predicate_kind(tcx, predicate),
-                Some(TraitSpecializationKind::AlwaysApplicable)
-            )
-        });
+    let always_applicable_traits = impl1_predicates.iter().copied().filter(|&predicate| {
+        matches!(
+            trait_predicate_kind(tcx, predicate),
+            Some(TraitSpecializationKind::AlwaysApplicable)
+        )
+    });
 
     // Include the well-formed predicates of the type parameters of the impl.
     for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().substs {
@@ -340,18 +353,19 @@ fn check_predicates<'tcx>(
             arg,
             span,
         ) {
-            impl2_predicates
-                .predicates
-                .extend(obligations.into_iter().map(|obligation| obligation.predicate))
+            impl2_predicates.extend(
+                traits::elaborate_obligations(tcx, obligations)
+                    .map(|obligation| obligation.predicate),
+            )
         }
     }
-    impl2_predicates.predicates.extend(
+    impl2_predicates.extend(
         traits::elaborate_predicates(tcx, always_applicable_traits)
             .map(|obligation| obligation.predicate),
     );
 
-    for predicate in impl1_predicates.predicates {
-        if !impl2_predicates.predicates.contains(&predicate) {
+    for predicate in impl1_predicates {
+        if !impl2_predicates.contains(&predicate) {
             check_specialization_on(tcx, predicate, span)
         }
     }
diff --git a/src/test/ui/specialization/min_specialization/spec-marker-supertraits.rs b/src/test/ui/specialization/min_specialization/spec-marker-supertraits.rs
new file mode 100644
index 00000000000..3bb2480e9e2
--- /dev/null
+++ b/src/test/ui/specialization/min_specialization/spec-marker-supertraits.rs
@@ -0,0 +1,29 @@
+// Check that supertraits cannot be used to work around min_specialization
+// limitations.
+
+#![feature(min_specialization)]
+#![feature(rustc_attrs)]
+
+trait HasMethod {
+    fn method(&self);
+}
+
+#[rustc_unsafe_specialization_marker]
+trait Marker: HasMethod {}
+
+trait Spec {
+    fn spec_me(&self);
+}
+
+impl<T> Spec for T {
+    default fn spec_me(&self) {}
+}
+
+impl<T: Marker> Spec for T {
+    //~^ ERROR cannot specialize on trait `HasMethod`
+    fn spec_me(&self) {
+        self.method();
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/specialization/min_specialization/spec-marker-supertraits.stderr b/src/test/ui/specialization/min_specialization/spec-marker-supertraits.stderr
new file mode 100644
index 00000000000..964109dd10f
--- /dev/null
+++ b/src/test/ui/specialization/min_specialization/spec-marker-supertraits.stderr
@@ -0,0 +1,13 @@
+error: cannot specialize on trait `HasMethod`
+  --> $DIR/spec-marker-supertraits.rs:22:1
+   |
+LL | / impl<T: Marker> Spec for T {
+LL | |
+LL | |     fn spec_me(&self) {
+LL | |         self.method();
+LL | |     }
+LL | | }
+   | |_^
+
+error: aborting due to previous error
+