Skip to content

Commit

Permalink
Rollup merge of rust-lang#69870 - petrochenkov:cfgacc, r=matthewjasper
Browse files Browse the repository at this point in the history
expand: Implement something similar to `#[cfg(accessible(path))]`

cc rust-lang#64797

The feature is implemented as a `#[cfg_accessible(path)]` attribute macro rather than as `#[cfg(accessible(path))]` because it needs to wait until `path` becomes resolvable, and `cfg` cannot wait, but macros can wait.

Later we can think about desugaring or not desugaring `#[cfg(accessible(path))]` into `#[cfg_accessible(path)]`.

This implementation is also incomplete in the sense that it never returns "false" from `cfg_accessible(path)`, it requires some tweaks to resolve, which is not quite ready to answer queries like this during early resolution.

However, the most important part of this PR is not `cfg_accessible` itself, but expansion infrastructure for retrying expansions.
Before this PR we could say "we cannot resolve this macro path, let's try it later", with this PR we can say "we cannot expand this macro, let's try it later" as well.

This is a pre-requisite for
- turning `#[derive(...)]` into a regular attribute macro,
- properly supporting eager expansion for macros that cannot yet be resolved like
    ```
    fn main() {
        println!(not_available_yet!());
    }

    macro_rules! make_available {
        () => { #[macro_export] macro_rules! not_available_yet { () => { "Hello world!" } }}
    }

    make_available!();
    ```
  • Loading branch information
Centril authored Mar 16, 2020
2 parents 59f4ba9 + 2e65289 commit 1350be7
Show file tree
Hide file tree
Showing 22 changed files with 439 additions and 71 deletions.
12 changes: 12 additions & 0 deletions src/libcore/macros/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1404,6 +1404,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")]
Expand Down
9 changes: 9 additions & 0 deletions src/libcore/prelude/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
54 changes: 54 additions & 0 deletions src/librustc_builtin_macros/cfg_accessible.rs
Original file line number Diff line number Diff line change
@@ -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()),
}
}
}
6 changes: 3 additions & 3 deletions src/librustc_builtin_macros/deriving/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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)
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/librustc_builtin_macros/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_builtin_macros/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
31 changes: 17 additions & 14 deletions src/librustc_expand/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,37 +258,39 @@ 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,
ecx: &mut ExtCtxt<'_>,
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,
ecx: &mut ExtCtxt<'_>,
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))
}
}

Expand Down Expand Up @@ -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)]
Expand Down
Loading

0 comments on commit 1350be7

Please sign in to comment.