diff --git a/src/libcore/macros/mod.rs b/src/libcore/macros/mod.rs
index 76e58f0cc62bc..3b615a5246c29 100644
--- a/src/libcore/macros/mod.rs
+++ b/src/libcore/macros/mod.rs
@@ -1383,6 +1383,18 @@ pub(crate) mod builtin {
         /* compiler built-in */
     }
 
+    /// Keeps the item it's applied to if the passed path is accessible, and removes it otherwise.
+    #[cfg(not(bootstrap))]
+    #[unstable(
+        feature = "cfg_accessible",
+        issue = "64797",
+        reason = "`cfg_accessible` is not fully implemented"
+    )]
+    #[rustc_builtin_macro]
+    pub macro cfg_accessible($item:item) {
+        /* compiler built-in */
+    }
+
     /// Unstable implementation detail of the `rustc` compiler, do not use.
     #[rustc_builtin_macro]
     #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/src/libcore/prelude/v1.rs b/src/libcore/prelude/v1.rs
index 66b5a90b77b91..c91370b271992 100644
--- a/src/libcore/prelude/v1.rs
+++ b/src/libcore/prelude/v1.rs
@@ -67,3 +67,12 @@ pub use crate::{
 pub use crate::macros::builtin::{
     bench, global_allocator, test, test_case, RustcDecodable, RustcEncodable,
 };
+
+#[cfg(not(bootstrap))]
+#[unstable(
+    feature = "cfg_accessible",
+    issue = "64797",
+    reason = "`cfg_accessible` is not fully implemented"
+)]
+#[doc(no_inline)]
+pub use crate::macros::builtin::cfg_accessible;
diff --git a/src/librustc_builtin_macros/cfg_accessible.rs b/src/librustc_builtin_macros/cfg_accessible.rs
new file mode 100644
index 0000000000000..3607a4d0d15b6
--- /dev/null
+++ b/src/librustc_builtin_macros/cfg_accessible.rs
@@ -0,0 +1,54 @@
+//! Implementation of the `#[cfg_accessible(path)]` attribute macro.
+
+use rustc_ast::ast;
+use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier};
+use rustc_feature::AttributeTemplate;
+use rustc_parse::validate_attr;
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+
+crate struct Expander;
+
+fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> {
+    match mi.meta_item_list() {
+        None => {}
+        Some([]) => ecx.span_err(mi.span, "`cfg_accessible` path is not specified"),
+        Some([_, .., l]) => ecx.span_err(l.span(), "multiple `cfg_accessible` paths are specified"),
+        Some([nmi]) => match nmi.meta_item() {
+            None => ecx.span_err(nmi.span(), "`cfg_accessible` path cannot be a literal"),
+            Some(mi) => {
+                if !mi.is_word() {
+                    ecx.span_err(mi.span, "`cfg_accessible` path cannot accept arguments");
+                }
+                return Some(&mi.path);
+            }
+        },
+    }
+    None
+}
+
+impl MultiItemModifier for Expander {
+    fn expand(
+        &self,
+        ecx: &mut ExtCtxt<'_>,
+        _span: Span,
+        meta_item: &ast::MetaItem,
+        item: Annotatable,
+    ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
+        let template = AttributeTemplate { list: Some("path"), ..Default::default() };
+        let attr = &ecx.attribute(meta_item.clone());
+        validate_attr::check_builtin_attribute(ecx.parse_sess, attr, sym::cfg_accessible, template);
+
+        let path = match validate_input(ecx, meta_item) {
+            Some(path) => path,
+            None => return ExpandResult::Ready(Vec::new()),
+        };
+
+        let failure_msg = "cannot determine whether the path is accessible or not";
+        match ecx.resolver.cfg_accessible(ecx.current_expansion.id, path) {
+            Ok(true) => ExpandResult::Ready(vec![item]),
+            Ok(false) => ExpandResult::Ready(Vec::new()),
+            Err(_) => ExpandResult::Retry(item, failure_msg.into()),
+        }
+    }
+}
diff --git a/src/librustc_builtin_macros/deriving/mod.rs b/src/librustc_builtin_macros/deriving/mod.rs
index 5ba9d3800e118..b5ad67abf6201 100644
--- a/src/librustc_builtin_macros/deriving/mod.rs
+++ b/src/librustc_builtin_macros/deriving/mod.rs
@@ -2,7 +2,7 @@
 
 use rustc_ast::ast::{self, ItemKind, MetaItem};
 use rustc_ast::ptr::P;
-use rustc_expand::base::{Annotatable, ExtCtxt, MultiItemModifier};
+use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier};
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::Span;
 
@@ -48,13 +48,13 @@ impl MultiItemModifier for BuiltinDerive {
         span: Span,
         meta_item: &MetaItem,
         item: Annotatable,
-    ) -> Vec<Annotatable> {
+    ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
         // FIXME: Built-in derives often forget to give spans contexts,
         // so we are doing it here in a centralized way.
         let span = ecx.with_def_site_ctxt(span);
         let mut items = Vec::new();
         (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a));
-        items
+        ExpandResult::Ready(items)
     }
 }
 
diff --git a/src/librustc_builtin_macros/lib.rs b/src/librustc_builtin_macros/lib.rs
index 9a8b0a87cb793..26a59c6b1bedb 100644
--- a/src/librustc_builtin_macros/lib.rs
+++ b/src/librustc_builtin_macros/lib.rs
@@ -22,6 +22,7 @@ use rustc_span::symbol::sym;
 mod asm;
 mod assert;
 mod cfg;
+mod cfg_accessible;
 mod compile_error;
 mod concat;
 mod concat_idents;
@@ -85,6 +86,7 @@ pub fn register_builtin_macros(resolver: &mut dyn Resolver, edition: Edition) {
 
     register_attr! {
         bench: test::expand_bench,
+        cfg_accessible: cfg_accessible::Expander,
         global_allocator: global_allocator::expand,
         test: test::expand_test,
         test_case: test::expand_test_case,
diff --git a/src/librustc_builtin_macros/util.rs b/src/librustc_builtin_macros/util.rs
index 8ef76a8657e1e..b486eadd1a8be 100644
--- a/src/librustc_builtin_macros/util.rs
+++ b/src/librustc_builtin_macros/util.rs
@@ -6,7 +6,7 @@ use rustc_span::Symbol;
 
 pub fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, name: Symbol) {
     // All the built-in macro attributes are "words" at the moment.
-    let template = AttributeTemplate::only_word();
+    let template = AttributeTemplate { word: true, ..Default::default() };
     let attr = ecx.attribute(meta_item.clone());
     validate_attr::check_builtin_attribute(ecx.parse_sess, &attr, name, template);
 }
diff --git a/src/librustc_expand/base.rs b/src/librustc_expand/base.rs
index f15e626c2783b..4839651ac844b 100644
--- a/src/librustc_expand/base.rs
+++ b/src/librustc_expand/base.rs
@@ -258,8 +258,17 @@ impl Annotatable {
     }
 }
 
-// `meta_item` is the annotation, and `item` is the item being modified.
-// FIXME Decorators should follow the same pattern too.
+/// Result of an expansion that may need to be retried.
+/// Consider using this for non-`MultiItemModifier` expanders as well.
+pub enum ExpandResult<T, U> {
+    /// Expansion produced a result (possibly dummy).
+    Ready(T),
+    /// Expansion could not produce a result and needs to be retried.
+    /// The string is an explanation that will be printed if we are stuck in an infinite retry loop.
+    Retry(U, String),
+}
+
+// `meta_item` is the attribute, and `item` is the item being modified.
 pub trait MultiItemModifier {
     fn expand(
         &self,
@@ -267,13 +276,12 @@ pub trait MultiItemModifier {
         span: Span,
         meta_item: &ast::MetaItem,
         item: Annotatable,
-    ) -> Vec<Annotatable>;
+    ) -> ExpandResult<Vec<Annotatable>, Annotatable>;
 }
 
-impl<F, T> MultiItemModifier for F
+impl<F> MultiItemModifier for F
 where
-    F: Fn(&mut ExtCtxt<'_>, Span, &ast::MetaItem, Annotatable) -> T,
-    T: Into<Vec<Annotatable>>,
+    F: Fn(&mut ExtCtxt<'_>, Span, &ast::MetaItem, Annotatable) -> Vec<Annotatable>,
 {
     fn expand(
         &self,
@@ -281,14 +289,8 @@ where
         span: Span,
         meta_item: &ast::MetaItem,
         item: Annotatable,
-    ) -> Vec<Annotatable> {
-        (*self)(ecx, span, meta_item, item).into()
-    }
-}
-
-impl Into<Vec<Annotatable>> for Annotatable {
-    fn into(self) -> Vec<Annotatable> {
-        vec![self]
+    ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
+        ExpandResult::Ready(self(ecx, span, meta_item, item))
     }
 }
 
@@ -895,6 +897,7 @@ pub trait Resolver {
 
     fn has_derive_copy(&self, expn_id: ExpnId) -> bool;
     fn add_derive_copy(&mut self, expn_id: ExpnId);
+    fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result<bool, Indeterminate>;
 }
 
 #[derive(Clone)]
diff --git a/src/librustc_expand/expand.rs b/src/librustc_expand/expand.rs
index effa89e8bfb21..f2af6755517f1 100644
--- a/src/librustc_expand/expand.rs
+++ b/src/librustc_expand/expand.rs
@@ -408,7 +408,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         let mut undetermined_invocations = Vec::new();
         let (mut progress, mut force) = (false, !self.monotonic);
         loop {
-            let invoc = if let Some(invoc) = invocations.pop() {
+            let (invoc, res) = if let Some(invoc) = invocations.pop() {
                 invoc
             } else {
                 self.resolve_imports();
@@ -420,30 +420,51 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 continue;
             };
 
-            let eager_expansion_root =
-                if self.monotonic { invoc.expansion_data.id } else { orig_expansion_data.id };
-            let res = match self.cx.resolver.resolve_macro_invocation(
-                &invoc,
-                eager_expansion_root,
-                force,
-            ) {
-                Ok(res) => res,
-                Err(Indeterminate) => {
-                    undetermined_invocations.push(invoc);
-                    continue;
+            let res = match res {
+                Some(res) => res,
+                None => {
+                    let eager_expansion_root = if self.monotonic {
+                        invoc.expansion_data.id
+                    } else {
+                        orig_expansion_data.id
+                    };
+                    match self.cx.resolver.resolve_macro_invocation(
+                        &invoc,
+                        eager_expansion_root,
+                        force,
+                    ) {
+                        Ok(res) => res,
+                        Err(Indeterminate) => {
+                            // Cannot resolve, will retry this invocation later.
+                            undetermined_invocations.push((invoc, None));
+                            continue;
+                        }
+                    }
                 }
             };
 
-            progress = true;
             let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data;
             self.cx.current_expansion = invoc.expansion_data.clone();
 
             // FIXME(jseyfried): Refactor out the following logic
             let (expanded_fragment, new_invocations) = match res {
-                InvocationRes::Single(ext) => {
-                    let fragment = self.expand_invoc(invoc, &ext.kind);
-                    self.collect_invocations(fragment, &[])
-                }
+                InvocationRes::Single(ext) => match self.expand_invoc(invoc, &ext.kind) {
+                    ExpandResult::Ready(fragment) => self.collect_invocations(fragment, &[]),
+                    ExpandResult::Retry(invoc, explanation) => {
+                        if force {
+                            // We are stuck, stop retrying and produce a dummy fragment.
+                            let span = invoc.span();
+                            self.cx.span_err(span, &explanation);
+                            let fragment = invoc.fragment_kind.dummy(span);
+                            self.collect_invocations(fragment, &[])
+                        } else {
+                            // Cannot expand, will retry this invocation later.
+                            undetermined_invocations
+                                .push((invoc, Some(InvocationRes::Single(ext))));
+                            continue;
+                        }
+                    }
+                },
                 InvocationRes::DeriveContainer(_exts) => {
                     // FIXME: Consider using the derive resolutions (`_exts`) immediately,
                     // instead of enqueuing the derives to be resolved again later.
@@ -463,14 +484,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     for path in derives {
                         let expn_id = ExpnId::fresh(None);
                         derive_placeholders.push(NodeId::placeholder_from_expn_id(expn_id));
-                        invocations.push(Invocation {
-                            kind: InvocationKind::Derive { path, item: item.clone() },
-                            fragment_kind: invoc.fragment_kind,
-                            expansion_data: ExpansionData {
-                                id: expn_id,
-                                ..invoc.expansion_data.clone()
+                        invocations.push((
+                            Invocation {
+                                kind: InvocationKind::Derive { path, item: item.clone() },
+                                fragment_kind: invoc.fragment_kind,
+                                expansion_data: ExpansionData {
+                                    id: expn_id,
+                                    ..invoc.expansion_data.clone()
+                                },
                             },
-                        });
+                            None,
+                        ));
                     }
                     let fragment =
                         invoc.fragment_kind.expect_from_annotatables(::std::iter::once(item));
@@ -478,6 +502,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 }
             };
 
+            progress = true;
             if expanded_fragments.len() < depth {
                 expanded_fragments.push(Vec::new());
             }
@@ -535,7 +560,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         &mut self,
         mut fragment: AstFragment,
         extra_placeholders: &[NodeId],
-    ) -> (AstFragment, Vec<Invocation>) {
+    ) -> (AstFragment, Vec<(Invocation, Option<InvocationRes>)>) {
         // Resolve `$crate`s in the fragment for pretty-printing.
         self.cx.resolver.resolve_dollar_crates();
 
@@ -635,13 +660,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         self.cx.trace_macros_diag();
     }
 
-    fn expand_invoc(&mut self, invoc: Invocation, ext: &SyntaxExtensionKind) -> AstFragment {
+    fn expand_invoc(
+        &mut self,
+        invoc: Invocation,
+        ext: &SyntaxExtensionKind,
+    ) -> ExpandResult<AstFragment, Invocation> {
         if self.cx.current_expansion.depth > self.cx.ecfg.recursion_limit {
             self.error_recursion_limit_reached();
         }
 
         let (fragment_kind, span) = (invoc.fragment_kind, invoc.span());
-        match invoc.kind {
+        ExpandResult::Ready(match invoc.kind {
             InvocationKind::Bang { mac, .. } => match ext {
                 SyntaxExtensionKind::Bang(expander) => {
                     self.gate_proc_macro_expansion_kind(span, fragment_kind);
@@ -663,7 +692,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 }
                 _ => unreachable!(),
             },
-            InvocationKind::Attr { attr, mut item, .. } => match ext {
+            InvocationKind::Attr { attr, mut item, derives, after_derive } => match ext {
                 SyntaxExtensionKind::Attr(expander) => {
                     self.gate_proc_macro_input(&item);
                     self.gate_proc_macro_attr_item(span, &item);
@@ -679,8 +708,25 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 SyntaxExtensionKind::LegacyAttr(expander) => {
                     match validate_attr::parse_meta(self.cx.parse_sess, &attr) {
                         Ok(meta) => {
-                            let item = expander.expand(self.cx, span, &meta, item);
-                            fragment_kind.expect_from_annotatables(item)
+                            let items = match expander.expand(self.cx, span, &meta, item) {
+                                ExpandResult::Ready(items) => items,
+                                ExpandResult::Retry(item, explanation) => {
+                                    // Reassemble the original invocation for retrying.
+                                    return ExpandResult::Retry(
+                                        Invocation {
+                                            kind: InvocationKind::Attr {
+                                                attr,
+                                                item,
+                                                derives,
+                                                after_derive,
+                                            },
+                                            ..invoc
+                                        },
+                                        explanation,
+                                    );
+                                }
+                            };
+                            fragment_kind.expect_from_annotatables(items)
                         }
                         Err(mut err) => {
                             err.emit();
@@ -702,19 +748,31 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 SyntaxExtensionKind::Derive(expander)
                 | SyntaxExtensionKind::LegacyDerive(expander) => {
                     if !item.derive_allowed() {
-                        return fragment_kind.dummy(span);
+                        return ExpandResult::Ready(fragment_kind.dummy(span));
                     }
                     if let SyntaxExtensionKind::Derive(..) = ext {
                         self.gate_proc_macro_input(&item);
                     }
                     let meta = ast::MetaItem { kind: ast::MetaItemKind::Word, span, path };
-                    let items = expander.expand(self.cx, span, &meta, item);
+                    let items = match expander.expand(self.cx, span, &meta, item) {
+                        ExpandResult::Ready(items) => items,
+                        ExpandResult::Retry(item, explanation) => {
+                            // Reassemble the original invocation for retrying.
+                            return ExpandResult::Retry(
+                                Invocation {
+                                    kind: InvocationKind::Derive { path: meta.path, item },
+                                    ..invoc
+                                },
+                                explanation,
+                            );
+                        }
+                    };
                     fragment_kind.expect_from_annotatables(items)
                 }
                 _ => unreachable!(),
             },
             InvocationKind::DeriveContainer { .. } => unreachable!(),
-        }
+        })
     }
 
     fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) {
@@ -933,7 +991,7 @@ pub fn ensure_complete_parse<'a>(
 struct InvocationCollector<'a, 'b> {
     cx: &'a mut ExtCtxt<'b>,
     cfg: StripUnconfigured<'a>,
-    invocations: Vec<Invocation>,
+    invocations: Vec<(Invocation, Option<InvocationRes>)>,
     monotonic: bool,
 }
 
@@ -955,15 +1013,18 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
         };
         let expn_id = ExpnId::fresh(expn_data);
         let vis = kind.placeholder_visibility();
-        self.invocations.push(Invocation {
-            kind,
-            fragment_kind,
-            expansion_data: ExpansionData {
-                id: expn_id,
-                depth: self.cx.current_expansion.depth + 1,
-                ..self.cx.current_expansion.clone()
+        self.invocations.push((
+            Invocation {
+                kind,
+                fragment_kind,
+                expansion_data: ExpansionData {
+                    id: expn_id,
+                    depth: self.cx.current_expansion.depth + 1,
+                    ..self.cx.current_expansion.clone()
+                },
             },
-        });
+            None,
+        ));
         placeholder(fragment_kind, NodeId::placeholder_from_expn_id(expn_id), vis)
     }
 
diff --git a/src/librustc_expand/proc_macro.rs b/src/librustc_expand/proc_macro.rs
index 84a546345bb28..cb9afa4cd4f02 100644
--- a/src/librustc_expand/proc_macro.rs
+++ b/src/librustc_expand/proc_macro.rs
@@ -79,7 +79,7 @@ impl MultiItemModifier for ProcMacroDerive {
         span: Span,
         _meta_item: &ast::MetaItem,
         item: Annotatable,
-    ) -> Vec<Annotatable> {
+    ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
         let item = match item {
             Annotatable::Arm(..)
             | Annotatable::Field(..)
@@ -99,7 +99,7 @@ impl MultiItemModifier for ProcMacroDerive {
                     "proc-macro derives may only be \
                                     applied to a struct, enum, or union",
                 );
-                return Vec::new();
+                return ExpandResult::Ready(Vec::new());
             }
         };
         match item.kind {
@@ -110,7 +110,7 @@ impl MultiItemModifier for ProcMacroDerive {
                     "proc-macro derives may only be \
                                     applied to a struct, enum, or union",
                 );
-                return Vec::new();
+                return ExpandResult::Ready(Vec::new());
             }
         }
 
@@ -158,7 +158,7 @@ impl MultiItemModifier for ProcMacroDerive {
             FatalError.raise();
         }
 
-        items
+        ExpandResult::Ready(items)
     }
 }
 
diff --git a/src/librustc_feature/builtin_attrs.rs b/src/librustc_feature/builtin_attrs.rs
index e9a5364c65838..e0e38c2dba941 100644
--- a/src/librustc_feature/builtin_attrs.rs
+++ b/src/librustc_feature/builtin_attrs.rs
@@ -85,19 +85,13 @@ impl AttributeGate {
 
 /// A template that the attribute input must match.
 /// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Default)]
 pub struct AttributeTemplate {
     pub word: bool,
     pub list: Option<&'static str>,
     pub name_value_str: Option<&'static str>,
 }
 
-impl AttributeTemplate {
-    pub fn only_word() -> Self {
-        Self { word: true, list: None, name_value_str: None }
-    }
-}
-
 /// A convenience macro for constructing attribute templates.
 /// E.g., `template!(Word, List: "description")` means that the attribute
 /// supports forms `#[attr]` and `#[attr(description)]`.
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index e11aec906693e..4e41ea82c47c9 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -339,6 +339,42 @@ impl<'a> base::Resolver for Resolver<'a> {
     fn add_derive_copy(&mut self, expn_id: ExpnId) {
         self.containers_deriving_copy.insert(expn_id);
     }
+
+    // The function that implements the resolution logic of `#[cfg_accessible(path)]`.
+    // Returns true if the path can certainly be resolved in one of three namespaces,
+    // returns false if the path certainly cannot be resolved in any of the three namespaces.
+    // Returns `Indeterminate` if we cannot give a certain answer yet.
+    fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result<bool, Indeterminate> {
+        let span = path.span;
+        let path = &Segment::from_path(path);
+        let parent_scope = self.invocation_parent_scopes[&expn_id];
+
+        let mut indeterminate = false;
+        for ns in [TypeNS, ValueNS, MacroNS].iter().copied() {
+            match self.resolve_path(path, Some(ns), &parent_scope, false, span, CrateLint::No) {
+                PathResult::Module(ModuleOrUniformRoot::Module(_)) => return Ok(true),
+                PathResult::NonModule(partial_res) if partial_res.unresolved_segments() == 0 => {
+                    return Ok(true);
+                }
+                PathResult::Indeterminate => indeterminate = true,
+                // FIXME: `resolve_path` is not ready to report partially resolved paths
+                // correctly, so we just report an error if the path was reported as unresolved.
+                // This needs to be fixed for `cfg_accessible` to be useful.
+                PathResult::NonModule(..) | PathResult::Failed { .. } => {}
+                PathResult::Module(_) => panic!("unexpected path resolution"),
+            }
+        }
+
+        if indeterminate {
+            return Err(Indeterminate);
+        }
+
+        self.session
+            .struct_span_err(span, "not sure whether the path is accessible or not")
+            .span_note(span, "`cfg_accessible` is not fully implemented")
+            .emit();
+        Ok(false)
+    }
 }
 
 impl<'a> Resolver<'a> {
diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs
index c39f9f360c027..55284e73efcfa 100644
--- a/src/librustc_span/symbol.rs
+++ b/src/librustc_span/symbol.rs
@@ -181,6 +181,7 @@ symbols! {
         caller_location,
         cdylib,
         cfg,
+        cfg_accessible,
         cfg_attr,
         cfg_attr_multi,
         cfg_doctest,
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 784868b52e517..9950a8132555f 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -240,6 +240,7 @@
 #![feature(atomic_mut_ptr)]
 #![feature(box_syntax)]
 #![feature(c_variadic)]
+#![cfg_attr(not(bootstrap), feature(cfg_accessible))]
 #![feature(cfg_target_has_atomic)]
 #![feature(cfg_target_thread_local)]
 #![feature(char_error_internals)]
diff --git a/src/libstd/prelude/v1.rs b/src/libstd/prelude/v1.rs
index 7c0efe828c27a..6712f5ba5808c 100644
--- a/src/libstd/prelude/v1.rs
+++ b/src/libstd/prelude/v1.rs
@@ -53,6 +53,15 @@ pub use core::prelude::v1::{
     PartialEq, PartialOrd, RustcDecodable, RustcEncodable,
 };
 
+#[cfg(not(bootstrap))]
+#[unstable(
+    feature = "cfg_accessible",
+    issue = "64797",
+    reason = "`cfg_accessible` is not fully implemented"
+)]
+#[doc(hidden)]
+pub use core::prelude::v1::cfg_accessible;
+
 // The file so far is equivalent to src/libcore/prelude/v1.rs,
 // and below to src/liballoc/prelude.rs.
 // Those files are duplicated rather than using glob imports
diff --git a/src/test/ui/conditional-compilation/cfg_accessible-input-validation.rs b/src/test/ui/conditional-compilation/cfg_accessible-input-validation.rs
new file mode 100644
index 0000000000000..c51c908a4262e
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg_accessible-input-validation.rs
@@ -0,0 +1,24 @@
+#![feature(cfg_accessible)]
+
+#[cfg_accessible] //~ ERROR malformed `cfg_accessible` attribute input
+struct S1;
+
+#[cfg_accessible = "value"] //~ ERROR malformed `cfg_accessible` attribute input
+struct S2;
+
+#[cfg_accessible()] //~ ERROR `cfg_accessible` path is not specified
+struct S3;
+
+#[cfg_accessible(std, core)] //~ ERROR multiple `cfg_accessible` paths are specified
+struct S4;
+
+#[cfg_accessible("std")] //~ ERROR `cfg_accessible` path cannot be a literal
+struct S5;
+
+#[cfg_accessible(std = "value")] //~ ERROR `cfg_accessible` path cannot accept arguments
+struct S6;
+
+#[cfg_accessible(std(value))] //~ ERROR `cfg_accessible` path cannot accept arguments
+struct S7;
+
+fn main() {}
diff --git a/src/test/ui/conditional-compilation/cfg_accessible-input-validation.stderr b/src/test/ui/conditional-compilation/cfg_accessible-input-validation.stderr
new file mode 100644
index 0000000000000..86706c766356e
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg_accessible-input-validation.stderr
@@ -0,0 +1,44 @@
+error: malformed `cfg_accessible` attribute input
+  --> $DIR/cfg_accessible-input-validation.rs:3:1
+   |
+LL | #[cfg_accessible]
+   | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[cfg_accessible(path)]`
+
+error: malformed `cfg_accessible` attribute input
+  --> $DIR/cfg_accessible-input-validation.rs:6:1
+   |
+LL | #[cfg_accessible = "value"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[cfg_accessible(path)]`
+
+error: `cfg_accessible` path is not specified
+  --> $DIR/cfg_accessible-input-validation.rs:9:1
+   |
+LL | #[cfg_accessible()]
+   | ^^^^^^^^^^^^^^^^^^^
+
+error: multiple `cfg_accessible` paths are specified
+  --> $DIR/cfg_accessible-input-validation.rs:12:23
+   |
+LL | #[cfg_accessible(std, core)]
+   |                       ^^^^
+
+error: `cfg_accessible` path cannot be a literal
+  --> $DIR/cfg_accessible-input-validation.rs:15:18
+   |
+LL | #[cfg_accessible("std")]
+   |                  ^^^^^
+
+error: `cfg_accessible` path cannot accept arguments
+  --> $DIR/cfg_accessible-input-validation.rs:18:18
+   |
+LL | #[cfg_accessible(std = "value")]
+   |                  ^^^^^^^^^^^^^
+
+error: `cfg_accessible` path cannot accept arguments
+  --> $DIR/cfg_accessible-input-validation.rs:21:18
+   |
+LL | #[cfg_accessible(std(value))]
+   |                  ^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/test/ui/conditional-compilation/cfg_accessible-stuck.rs b/src/test/ui/conditional-compilation/cfg_accessible-stuck.rs
new file mode 100644
index 0000000000000..8bc93fa324378
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg_accessible-stuck.rs
@@ -0,0 +1,9 @@
+#![feature(cfg_accessible)]
+
+#[cfg_accessible(Z)] //~ ERROR cannot determine whether the path is accessible or not
+struct S;
+
+#[cfg_accessible(S)] //~ ERROR cannot determine whether the path is accessible or not
+struct Z;
+
+fn main() {}
diff --git a/src/test/ui/conditional-compilation/cfg_accessible-stuck.stderr b/src/test/ui/conditional-compilation/cfg_accessible-stuck.stderr
new file mode 100644
index 0000000000000..9641441a819b0
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg_accessible-stuck.stderr
@@ -0,0 +1,14 @@
+error: cannot determine whether the path is accessible or not
+  --> $DIR/cfg_accessible-stuck.rs:6:1
+   |
+LL | #[cfg_accessible(S)]
+   | ^^^^^^^^^^^^^^^^^^^^
+
+error: cannot determine whether the path is accessible or not
+  --> $DIR/cfg_accessible-stuck.rs:3:1
+   |
+LL | #[cfg_accessible(Z)]
+   | ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/conditional-compilation/cfg_accessible-unstable.rs b/src/test/ui/conditional-compilation/cfg_accessible-unstable.rs
new file mode 100644
index 0000000000000..e9247e67a2a26
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg_accessible-unstable.rs
@@ -0,0 +1,2 @@
+#[cfg_accessible(std)] //~ ERROR use of unstable library feature 'cfg_accessible'
+fn main() {}
diff --git a/src/test/ui/conditional-compilation/cfg_accessible-unstable.stderr b/src/test/ui/conditional-compilation/cfg_accessible-unstable.stderr
new file mode 100644
index 0000000000000..2f55b9559c78f
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg_accessible-unstable.stderr
@@ -0,0 +1,12 @@
+error[E0658]: use of unstable library feature 'cfg_accessible': `cfg_accessible` is not fully implemented
+  --> $DIR/cfg_accessible-unstable.rs:1:3
+   |
+LL | #[cfg_accessible(std)]
+   |   ^^^^^^^^^^^^^^
+   |
+   = note: see issue #64797 <https://github.com/rust-lang/rust/issues/64797> for more information
+   = help: add `#![feature(cfg_accessible)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/conditional-compilation/cfg_accessible.rs b/src/test/ui/conditional-compilation/cfg_accessible.rs
new file mode 100644
index 0000000000000..07b0be5b1ae26
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg_accessible.rs
@@ -0,0 +1,43 @@
+#![feature(cfg_accessible)]
+
+mod m {
+    pub struct ExistingPublic;
+    struct ExistingPrivate;
+}
+
+#[cfg_accessible(m::ExistingPublic)]
+struct ExistingPublic;
+
+// FIXME: Not implemented yet.
+#[cfg_accessible(m::ExistingPrivate)] //~ ERROR not sure whether the path is accessible or not
+struct ExistingPrivate;
+
+// FIXME: Not implemented yet.
+#[cfg_accessible(m::NonExistent)] //~ ERROR not sure whether the path is accessible or not
+struct ExistingPrivate;
+
+#[cfg_accessible(n::AccessibleExpanded)] // OK, `cfg_accessible` can wait and retry.
+struct AccessibleExpanded;
+
+macro_rules! generate_accessible_expanded {
+    () => {
+        mod n {
+            pub struct AccessibleExpanded;
+        }
+    };
+}
+
+generate_accessible_expanded!();
+
+struct S {
+    field: u8,
+}
+
+// FIXME: Not implemented yet.
+#[cfg_accessible(S::field)] //~ ERROR not sure whether the path is accessible or not
+struct Field;
+
+fn main() {
+    ExistingPublic;
+    AccessibleExpanded;
+}
diff --git a/src/test/ui/conditional-compilation/cfg_accessible.stderr b/src/test/ui/conditional-compilation/cfg_accessible.stderr
new file mode 100644
index 0000000000000..167765cd66ee6
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg_accessible.stderr
@@ -0,0 +1,38 @@
+error: not sure whether the path is accessible or not
+  --> $DIR/cfg_accessible.rs:12:18
+   |
+LL | #[cfg_accessible(m::ExistingPrivate)]
+   |                  ^^^^^^^^^^^^^^^^^^
+   |
+note: `cfg_accessible` is not fully implemented
+  --> $DIR/cfg_accessible.rs:12:18
+   |
+LL | #[cfg_accessible(m::ExistingPrivate)]
+   |                  ^^^^^^^^^^^^^^^^^^
+
+error: not sure whether the path is accessible or not
+  --> $DIR/cfg_accessible.rs:16:18
+   |
+LL | #[cfg_accessible(m::NonExistent)]
+   |                  ^^^^^^^^^^^^^^
+   |
+note: `cfg_accessible` is not fully implemented
+  --> $DIR/cfg_accessible.rs:16:18
+   |
+LL | #[cfg_accessible(m::NonExistent)]
+   |                  ^^^^^^^^^^^^^^
+
+error: not sure whether the path is accessible or not
+  --> $DIR/cfg_accessible.rs:37:18
+   |
+LL | #[cfg_accessible(S::field)]
+   |                  ^^^^^^^^
+   |
+note: `cfg_accessible` is not fully implemented
+  --> $DIR/cfg_accessible.rs:37:18
+   |
+LL | #[cfg_accessible(S::field)]
+   |                  ^^^^^^^^
+
+error: aborting due to 3 previous errors
+