]> git.lizzy.rs Git - rust.git/commitdiff
Add attributes to allow specializing on traits
authorMatthew Jasper <mjjasper1@gmail.com>
Sat, 8 Feb 2020 17:56:25 +0000 (17:56 +0000)
committerMatthew Jasper <mjjasper1@gmail.com>
Sun, 15 Mar 2020 12:44:25 +0000 (12:44 +0000)
13 files changed:
src/libcore/marker.rs
src/librustc/ty/trait_def.rs
src/librustc_feature/builtin_attrs.rs
src/librustc_metadata/rmeta/decoder.rs
src/librustc_metadata/rmeta/encoder.rs
src/librustc_metadata/rmeta/mod.rs
src/librustc_span/symbol.rs
src/librustc_typeck/check/wfcheck.rs
src/librustc_typeck/coherence/mod.rs
src/librustc_typeck/collect.rs
src/test/ui/specialization/min_specialization/auxiliary/specialization-trait.rs [new file with mode: 0644]
src/test/ui/specialization/min_specialization/impl_specialization_trait.rs [new file with mode: 0644]
src/test/ui/specialization/min_specialization/impl_specialization_trait.stderr [new file with mode: 0644]

index 2800f11cc01b1ae43600b4e82df2ceac154050e8..2b908f07af8da585f6cdbe7f9ff2590709662d05 100644 (file)
@@ -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.
 }
index 0cf1c397648edcfe29787d6ac570f1b53cbceffc..2b5d72de3586f0e2adea7be4a240bf0ff9d10ffd 100644 (file)
@@ -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 @@ pub fn new(
         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(
index e9a5364c65838c2b502020315a1601125f1d202b..83830b9d78ad4d9f66460c7959dbd74995a72b7f 100644 (file)
@@ -530,6 +530,14 @@ macro_rules! experimental {
         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:
index 1d8eb0cde468af20fb6008e4688ca89a354a1ab8..0fab64978ee00272f520fa6ccc87b31ca11fb03a 100644 (file)
@@ -651,6 +651,7 @@ fn get_trait_def(&self, item_id: DefIndex, sess: &Session) -> ty::TraitDef {
                     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 @@ fn get_trait_def(&self, item_id: DefIndex, sess: &Session) -> ty::TraitDef {
                 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"),
index ce62f15f85d93683ebfffd6962352d682f52ea81..1d8b2c0f6f553708762b27d3e02573f0e0548382 100644 (file)
@@ -1114,6 +1114,7 @@ fn encode_info_for_item(&mut self, def_id: DefId, item: &'tcx hir::Item<'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))
index 89e26b15d502bb2bf73267a0b62ef75aec4ae44a..2672d772b1cf1af304ac173a7ced21f6ce73aa4e 100644 (file)
@@ -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)]
index bca4bfee85ad80ed69cbe44522da45281fc48a6f..389a8bb99208228e2330692f4d4b9ec2cc33f52a 100644 (file)
         min_align_of,
         min_const_fn,
         min_const_unsafe_fn,
+        min_specialization,
         mips_target_feature,
         mmx_target_feature,
         module,
         rustc_proc_macro_decls,
         rustc_promotable,
         rustc_regions,
+        rustc_unsafe_specialization_marker,
+        rustc_specialization_trait,
         rustc_stable,
         rustc_std_internal_symbol,
         rustc_symbol_name,
index 026e68e10e04d2fc743c76c59be4544e60ee007a..2f1654c18d91c5a021a5dc38b3907088fd42560e 100644 (file)
@@ -4,6 +4,7 @@
 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,
index 0d0149f9673586aa90e029cca6e160318debcc83..27b2c19499ccd61f89ba71aca9627dccafcea8cc 100644 (file)
@@ -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() {
index cd63dacdcda0e0a3f07fa4cdd57b05c8fcdaffa0..6597cc476892cf69b23b708536224c152b0b3b62 100644 (file)
@@ -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 (file)
index 0000000..6ec0d26
--- /dev/null
@@ -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 (file)
index 0000000..723ed71
--- /dev/null
@@ -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 (file)
index 0000000..934103d
--- /dev/null
@@ -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
+