about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthew Jasper <mjjasper1@gmail.com>2020-02-08 17:56:25 +0000
committerMatthew Jasper <mjjasper1@gmail.com>2020-03-15 12:44:25 +0000
commitc24b4bf41098edd860d03463e00f2590293196dc (patch)
tree4b53bc73915e60e9daad70c02ed4dcbc0d66494e
parenta62dd0e3bab098a4dd389a7942c2f4861f83443f (diff)
downloadrust-c24b4bf41098edd860d03463e00f2590293196dc.tar.gz
rust-c24b4bf41098edd860d03463e00f2590293196dc.zip
Add attributes to allow specializing on traits
-rw-r--r--src/libcore/marker.rs1
-rw-r--r--src/librustc/ty/trait_def.rs33
-rw-r--r--src/librustc_feature/builtin_attrs.rs8
-rw-r--r--src/librustc_metadata/rmeta/decoder.rs2
-rw-r--r--src/librustc_metadata/rmeta/encoder.rs1
-rw-r--r--src/librustc_metadata/rmeta/mod.rs1
-rw-r--r--src/librustc_span/symbol.rs3
-rw-r--r--src/librustc_typeck/check/wfcheck.rs5
-rw-r--r--src/librustc_typeck/coherence/mod.rs16
-rw-r--r--src/librustc_typeck/collect.rs17
-rw-r--r--src/test/ui/specialization/min_specialization/auxiliary/specialization-trait.rs6
-rw-r--r--src/test/ui/specialization/min_specialization/impl_specialization_trait.rs16
-rw-r--r--src/test/ui/specialization/min_specialization/impl_specialization_trait.stderr10
13 files changed, 116 insertions, 3 deletions
diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs
index 2800f11cc01..2b908f07af8 100644
--- a/src/libcore/marker.rs
+++ b/src/libcore/marker.rs
@@ -90,6 +90,7 @@ impl<T: ?Sized> !Send for *mut T {}
           ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>"
 )]
 #[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable
+#[cfg_attr(not(bootstrap), rustc_specialization_trait)]
 pub trait Sized {
     // Empty.
 }
diff --git a/src/librustc/ty/trait_def.rs b/src/librustc/ty/trait_def.rs
index 0cf1c397648..2b5d72de358 100644
--- a/src/librustc/ty/trait_def.rs
+++ b/src/librustc/ty/trait_def.rs
@@ -33,11 +33,33 @@ pub struct TraitDef {
     /// and thus `impl`s of it are allowed to overlap.
     pub is_marker: bool,
 
+    /// Used to determine whether the standard library is allowed to specialize
+    /// on this trait.
+    pub specialization_kind: TraitSpecializationKind,
+
     /// The ICH of this trait's DefPath, cached here so it doesn't have to be
     /// recomputed all the time.
     pub def_path_hash: DefPathHash,
 }
 
+/// Whether this trait is treated specially by the standard library
+/// specialization lint.
+#[derive(HashStable, PartialEq, Clone, Copy, RustcEncodable, RustcDecodable)]
+pub enum TraitSpecializationKind {
+    /// The default. Specializing on this trait is not allowed.
+    None,
+    /// Specializing on this trait is allowed because it doesn't have any
+    /// methods. For example `Sized` or `FusedIterator`.
+    /// Applies to traits with the `rustc_unsafe_specialization_marker`
+    /// attribute.
+    Marker,
+    /// Specializing on this trait is allowed because all of the impls of this
+    /// trait are "always applicable". Always applicable means that if
+    /// `X<'x>: T<'y>` for any lifetimes, then `for<'a, 'b> X<'a>: T<'b>`.
+    /// Applies to traits with the `rustc_specialization_trait` attribute.
+    AlwaysApplicable,
+}
+
 #[derive(Default)]
 pub struct TraitImpls {
     blanket_impls: Vec<DefId>,
@@ -52,9 +74,18 @@ impl<'tcx> TraitDef {
         paren_sugar: bool,
         has_auto_impl: bool,
         is_marker: bool,
+        specialization_kind: TraitSpecializationKind,
         def_path_hash: DefPathHash,
     ) -> TraitDef {
-        TraitDef { def_id, unsafety, paren_sugar, has_auto_impl, is_marker, def_path_hash }
+        TraitDef {
+            def_id,
+            unsafety,
+            paren_sugar,
+            has_auto_impl,
+            is_marker,
+            specialization_kind,
+            def_path_hash,
+        }
     }
 
     pub fn ancestors(
diff --git a/src/librustc_feature/builtin_attrs.rs b/src/librustc_feature/builtin_attrs.rs
index e9a5364c658..83830b9d78a 100644
--- a/src/librustc_feature/builtin_attrs.rs
+++ b/src/librustc_feature/builtin_attrs.rs
@@ -530,6 +530,14 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         rustc_test_marker, Normal, template!(Word),
         "the `#[rustc_test_marker]` attribute is used internally to track tests",
     ),
+    rustc_attr!(
+        rustc_unsafe_specialization_marker, Normal, template!(Word),
+        "the `#[rustc_unsafe_specialization_marker]` attribute is used to check specializations"
+    ),
+    rustc_attr!(
+        rustc_specialization_trait, Normal, template!(Word),
+        "the `#[rustc_specialization_trait]` attribute is used to check specializations"
+    ),
 
     // ==========================================================================
     // Internal attributes, Testing:
diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs
index 1d8eb0cde46..0fab64978ee 100644
--- a/src/librustc_metadata/rmeta/decoder.rs
+++ b/src/librustc_metadata/rmeta/decoder.rs
@@ -651,6 +651,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                     data.paren_sugar,
                     data.has_auto_impl,
                     data.is_marker,
+                    data.specialization_kind,
                     self.def_path_table.def_path_hash(item_id),
                 )
             }
@@ -660,6 +661,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                 false,
                 false,
                 false,
+                ty::trait_def::TraitSpecializationKind::None,
                 self.def_path_table.def_path_hash(item_id),
             ),
             _ => bug!("def-index does not refer to trait or trait alias"),
diff --git a/src/librustc_metadata/rmeta/encoder.rs b/src/librustc_metadata/rmeta/encoder.rs
index ce62f15f85d..1d8b2c0f6f5 100644
--- a/src/librustc_metadata/rmeta/encoder.rs
+++ b/src/librustc_metadata/rmeta/encoder.rs
@@ -1114,6 +1114,7 @@ impl EncodeContext<'tcx> {
                     paren_sugar: trait_def.paren_sugar,
                     has_auto_impl: self.tcx.trait_is_auto(def_id),
                     is_marker: trait_def.is_marker,
+                    specialization_kind: trait_def.specialization_kind,
                 };
 
                 EntryKind::Trait(self.lazy(data))
diff --git a/src/librustc_metadata/rmeta/mod.rs b/src/librustc_metadata/rmeta/mod.rs
index 89e26b15d50..2672d772b1c 100644
--- a/src/librustc_metadata/rmeta/mod.rs
+++ b/src/librustc_metadata/rmeta/mod.rs
@@ -350,6 +350,7 @@ struct TraitData {
     paren_sugar: bool,
     has_auto_impl: bool,
     is_marker: bool,
+    specialization_kind: ty::trait_def::TraitSpecializationKind,
 }
 
 #[derive(RustcEncodable, RustcDecodable)]
diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs
index bca4bfee85a..389a8bb9920 100644
--- a/src/librustc_span/symbol.rs
+++ b/src/librustc_span/symbol.rs
@@ -453,6 +453,7 @@ symbols! {
         min_align_of,
         min_const_fn,
         min_const_unsafe_fn,
+        min_specialization,
         mips_target_feature,
         mmx_target_feature,
         module,
@@ -654,6 +655,8 @@ symbols! {
         rustc_proc_macro_decls,
         rustc_promotable,
         rustc_regions,
+        rustc_unsafe_specialization_marker,
+        rustc_specialization_trait,
         rustc_stable,
         rustc_std_internal_symbol,
         rustc_symbol_name,
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index 026e68e10e0..2f1654c18d9 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -4,6 +4,7 @@ use crate::constrained_generic_params::{identify_constrained_generic_params, Par
 use rustc::middle::lang_items;
 use rustc::session::parse::feature_err;
 use rustc::ty::subst::{InternalSubsts, Subst};
+use rustc::ty::trait_def::TraitSpecializationKind;
 use rustc::ty::{
     self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
 };
@@ -412,7 +413,9 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
     let trait_def_id = tcx.hir().local_def_id(item.hir_id);
 
     let trait_def = tcx.trait_def(trait_def_id);
-    if trait_def.is_marker {
+    if trait_def.is_marker
+        || matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker)
+    {
         for associated_def_id in &*tcx.associated_item_def_ids(trait_def_id) {
             struct_span_err!(
                 tcx.sess,
diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs
index 0d0149f9673..27b2c19499c 100644
--- a/src/librustc_typeck/coherence/mod.rs
+++ b/src/librustc_typeck/coherence/mod.rs
@@ -76,6 +76,22 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt<'_>, impl_def_id: DefId, tra
         return;
     }
 
+    if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable =
+        tcx.trait_def(trait_def_id).specialization_kind
+    {
+        if !tcx.features().specialization && !tcx.features().min_specialization {
+            let span = impl_header_span(tcx, impl_def_id);
+            tcx.sess
+                .struct_span_err(
+                    span,
+                    "implementing `rustc_specialization_trait` traits is unstable",
+                )
+                .help("add `#![feature(min_specialization)]` to the crate attributes to enable")
+                .emit();
+            return;
+        }
+    }
+
     let trait_name = if did == li.fn_trait() {
         "Fn"
     } else if did == li.fn_mut_trait() {
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index cd63dacdcda..6597cc47689 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1032,8 +1032,23 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::TraitDef {
     }
 
     let is_marker = tcx.has_attr(def_id, sym::marker);
+    let spec_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) {
+        ty::trait_def::TraitSpecializationKind::Marker
+    } else if tcx.has_attr(def_id, sym::rustc_specialization_trait) {
+        ty::trait_def::TraitSpecializationKind::AlwaysApplicable
+    } else {
+        ty::trait_def::TraitSpecializationKind::None
+    };
     let def_path_hash = tcx.def_path_hash(def_id);
-    let def = ty::TraitDef::new(def_id, unsafety, paren_sugar, is_auto, is_marker, def_path_hash);
+    let def = ty::TraitDef::new(
+        def_id,
+        unsafety,
+        paren_sugar,
+        is_auto,
+        is_marker,
+        spec_kind,
+        def_path_hash,
+    );
     tcx.arena.alloc(def)
 }
 
diff --git a/src/test/ui/specialization/min_specialization/auxiliary/specialization-trait.rs b/src/test/ui/specialization/min_specialization/auxiliary/specialization-trait.rs
new file mode 100644
index 00000000000..6ec0d261d51
--- /dev/null
+++ b/src/test/ui/specialization/min_specialization/auxiliary/specialization-trait.rs
@@ -0,0 +1,6 @@
+#![feature(rustc_attrs)]
+
+#[rustc_specialization_trait]
+pub trait SpecTrait {
+    fn method(&self);
+}
diff --git a/src/test/ui/specialization/min_specialization/impl_specialization_trait.rs b/src/test/ui/specialization/min_specialization/impl_specialization_trait.rs
new file mode 100644
index 00000000000..723ed71c3e9
--- /dev/null
+++ b/src/test/ui/specialization/min_specialization/impl_specialization_trait.rs
@@ -0,0 +1,16 @@
+// Check that specialization traits can't be implemented without a feature.
+
+// gate-test-min_specialization
+
+// aux-build:specialization-trait.rs
+
+extern crate specialization_trait;
+
+struct A {}
+
+impl specialization_trait::SpecTrait for A {
+    //~^ ERROR implementing `rustc_specialization_trait` traits is unstable
+    fn method(&self) {}
+}
+
+fn main() {}
diff --git a/src/test/ui/specialization/min_specialization/impl_specialization_trait.stderr b/src/test/ui/specialization/min_specialization/impl_specialization_trait.stderr
new file mode 100644
index 00000000000..934103d49dc
--- /dev/null
+++ b/src/test/ui/specialization/min_specialization/impl_specialization_trait.stderr
@@ -0,0 +1,10 @@
+error: implementing `rustc_specialization_trait` traits is unstable
+  --> $DIR/impl_specialization_trait.rs:11:1
+   |
+LL | impl specialization_trait::SpecTrait for A {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: add `#![feature(min_specialization)]` to the crate attributes to enable
+
+error: aborting due to previous error
+