diff --git a/src/librustc_ast_passes/feature_gate.rs b/src/librustc_ast_passes/feature_gate.rs
index 5f36149451d..f9f4c3efeb6 100644
--- a/src/librustc_ast_passes/feature_gate.rs
+++ b/src/librustc_ast_passes/feature_gate.rs
@@ -542,15 +542,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
     }
 
     fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) {
-        if let ast::Defaultness::Default(_) = i.kind.defaultness() {
-            gate_feature_post!(&self, specialization, i.span, "specialization is unstable");
-        }
-
-        match i.kind {
+        let is_fn = match i.kind {
             ast::AssocItemKind::Fn(_, ref sig, _, _) => {
                 if let (ast::Const::Yes(_), AssocCtxt::Trait) = (sig.header.constness, ctxt) {
                     gate_feature_post!(&self, const_fn, i.span, "const fn is unstable");
                 }
+                true
             }
             ast::AssocItemKind::TyAlias(_, ref generics, _, ref ty) => {
                 if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) {
@@ -565,8 +562,19 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                     self.check_impl_trait(ty);
                 }
                 self.check_gat(generics, i.span);
+                false
             }
-            _ => {}
+            _ => false,
+        };
+        if let ast::Defaultness::Default(_) = i.kind.defaultness() {
+            // Limit `min_specialization` to only specializing functions.
+            gate_feature_fn!(
+                &self,
+                |x: &Features| x.specialization || (is_fn && x.min_specialization),
+                i.span,
+                sym::specialization,
+                "specialization is unstable"
+            );
         }
         visit::walk_assoc_item(self, i, ctxt)
     }
diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs
index 3a0fc6f8da1..9f1fee8fc09 100644
--- a/src/librustc_feature/active.rs
+++ b/src/librustc_feature/active.rs
@@ -301,6 +301,11 @@ declare_features! (
     /// Allows specialization of implementations (RFC 1210).
     (active, specialization, "1.7.0", Some(31844), None),
 
+    /// A minimal, sound subset of specialization intended to be used by the
+    /// standard library until the soundness issues with specialization
+    /// are fixed.
+    (active, min_specialization, "1.7.0", Some(31844), None),
+
     /// Allows using `#[naked]` on functions.
     (active, naked_functions, "1.9.0", Some(32408), None),
 
diff --git a/src/librustc_trait_selection/traits/specialize/mod.rs b/src/librustc_trait_selection/traits/specialize/mod.rs
index d1d4a58fdf2..770253e635e 100644
--- a/src/librustc_trait_selection/traits/specialize/mod.rs
+++ b/src/librustc_trait_selection/traits/specialize/mod.rs
@@ -161,7 +161,9 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
 
     // The feature gate should prevent introducing new specializations, but not
     // taking advantage of upstream ones.
-    if !tcx.features().specialization && (impl1_def_id.is_local() || impl2_def_id.is_local()) {
+    let features = tcx.features();
+    let specialization_enabled = features.specialization || features.min_specialization;
+    if !specialization_enabled && (impl1_def_id.is_local() || impl2_def_id.is_local()) {
         return false;
     }