Declarative macros 2.0 without hygiene.

This commit is contained in:
Jeffrey Seyfried 2017-03-18 01:55:51 +00:00
parent 9c6430b325
commit 2a1d2edb82
14 changed files with 116 additions and 46 deletions

View file

@ -0,0 +1,10 @@
# `decl_macro`
The tracking issue for this feature is: [#39412]
[#39412]: https://github.com/rust-lang/rust/issues/39412
------------------------

View file

@ -1502,10 +1502,11 @@ impl<'a> LoweringContext<'a> {
pub fn lower_item(&mut self, i: &Item) -> Option<hir::Item> {
let mut name = i.ident.name;
let attrs = self.lower_attrs(&i.attrs);
if let ItemKind::MacroDef(ref tts) = i.node {
if i.attrs.iter().any(|attr| attr.path == "macro_export") {
if let ItemKind::MacroDef(ref def) = i.node {
if !def.legacy || i.attrs.iter().any(|attr| attr.path == "macro_export") {
let (body, legacy) = (def.stream(), def.legacy);
self.exported_macros.push(hir::MacroDef {
name: name, attrs: attrs, id: i.id, span: i.span, body: tts.stream(),
name: name, attrs: attrs, id: i.id, span: i.span, body: body, legacy: legacy,
});
}
return None;

View file

@ -536,6 +536,7 @@ pub struct MacroDef {
pub id: NodeId,
pub span: Span,
pub body: TokenStream,
pub legacy: bool,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]

View file

@ -332,6 +332,7 @@ impl_stable_hash_for!(struct hir::MacroDef {
attrs,
id,
span,
legacy,
body
});

View file

@ -388,6 +388,7 @@ impl CrateStore for cstore::CStore {
attrs: attrs.iter().cloned().collect(),
node: ast::ItemKind::MacroDef(ast::MacroDef {
tokens: body.into(),
legacy: true,
}),
vis: ast::Visibility::Inherited,
})

View file

@ -77,7 +77,7 @@ struct LegacyMacroImports {
impl<'a> Resolver<'a> {
/// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined;
/// otherwise, reports an error.
fn define<T>(&mut self, parent: Module<'a>, ident: Ident, ns: Namespace, def: T)
pub fn define<T>(&mut self, parent: Module<'a>, ident: Ident, ns: Namespace, def: T)
where T: ToNameBinding<'a>,
{
let binding = def.to_name_binding(self.arenas);
@ -730,7 +730,7 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> {
fn visit_item(&mut self, item: &'a Item) {
let macro_use = match item.node {
ItemKind::MacroDef(..) => {
self.resolver.define_macro(item, &mut self.legacy_scope);
self.resolver.define_macro(item, self.expansion, &mut self.legacy_scope);
return
}
ItemKind::Mac(..) => {

View file

@ -1058,6 +1058,13 @@ impl<'a> NameBinding<'a> {
_ => true,
}
}
fn is_macro_def(&self) -> bool {
match self.kind {
NameBindingKind::Def(Def::Macro(..)) => true,
_ => false,
}
}
}
/// Interns the names of the primitive types.
@ -1377,8 +1384,9 @@ impl<'a> Resolver<'a> {
vis: ty::Visibility::Public,
}),
// `#![feature(proc_macro)]` implies `#[feature(extern_macros)]`
use_extern_macros: features.use_extern_macros || features.proc_macro,
// The `proc_macro` and `decl_macro` features imply `use_extern_macros`
use_extern_macros:
features.use_extern_macros || features.proc_macro || features.decl_macro,
crate_loader: crate_loader,
macro_names: FxHashSet(),

View file

@ -687,7 +687,10 @@ impl<'a> Resolver<'a> {
});
}
pub fn define_macro(&mut self, item: &ast::Item, legacy_scope: &mut LegacyScope<'a>) {
pub fn define_macro(&mut self,
item: &ast::Item,
expansion: Mark,
legacy_scope: &mut LegacyScope<'a>) {
self.local_macro_def_scopes.insert(item.id, self.current_module);
let ident = item.ident;
if ident.name == "macro_rules" {
@ -699,16 +702,24 @@ impl<'a> Resolver<'a> {
&self.session.features,
item));
self.macro_map.insert(def_id, ext);
*legacy_scope = LegacyScope::Binding(self.arenas.alloc_legacy_binding(LegacyBinding {
parent: Cell::new(*legacy_scope), name: ident.name, def_id: def_id, span: item.span,
}));
self.macro_names.insert(ident.name);
if attr::contains_name(&item.attrs, "macro_export") {
let def = Def::Macro(def_id, MacroKind::Bang);
self.macro_exports.push(Export { name: ident.name, def: def, span: item.span });
let def = match item.node { ast::ItemKind::MacroDef(ref def) => def, _ => unreachable!() };
if def.legacy {
self.macro_names.insert(ident.name);
*legacy_scope = LegacyScope::Binding(self.arenas.alloc_legacy_binding(LegacyBinding {
parent: Cell::new(*legacy_scope), name: ident.name, def_id: def_id, span: item.span,
}));
if attr::contains_name(&item.attrs, "macro_export") {
let def = Def::Macro(def_id, MacroKind::Bang);
self.macro_exports.push(Export { name: ident.name, def: def, span: item.span });
} else {
self.unused_macros.insert(def_id);
}
} else {
self.unused_macros.insert(def_id);
let module = self.current_module;
let def = Def::Macro(def_id, MacroKind::Bang);
let vis = self.resolve_visibility(&item.vis);
self.define(module, ident, MacroNS, (def, vis, item.span, expansion));
}
}

View file

@ -803,7 +803,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
};
if binding.vis == ty::Visibility::Public &&
(binding.is_import() || binding.is_extern_crate()) {
(binding.is_import() || binding.is_macro_def()) {
let def = binding.def();
if def != Def::Err {
if !def.def_id().is_local() {

View file

@ -1022,6 +1022,7 @@ impl Mac_ {
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct MacroDef {
pub tokens: ThinTokenStream,
pub legacy: bool,
}
impl MacroDef {

View file

@ -162,6 +162,12 @@ pub fn compile(sess: &ParseSess, features: &RefCell<Features>, def: &ast::Item)
let lhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("lhs"));
let rhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("rhs"));
// Parse the macro_rules! invocation
let body = match def.node {
ast::ItemKind::MacroDef(ref body) => body,
_ => unreachable!(),
};
// The pattern that macro_rules matches.
// The grammar for macro_rules! is:
// $( $lhs:tt => $rhs:tt );+
@ -174,7 +180,7 @@ pub fn compile(sess: &ParseSess, features: &RefCell<Features>, def: &ast::Item)
quoted::TokenTree::Token(DUMMY_SP, token::FatArrow),
quoted::TokenTree::MetaVarDecl(DUMMY_SP, rhs_nm, ast::Ident::from_str("tt")),
],
separator: Some(token::Semi),
separator: Some(if body.legacy { token::Semi } else { token::Comma }),
op: quoted::KleeneOp::OneOrMore,
num_captures: 2,
})),
@ -187,12 +193,7 @@ pub fn compile(sess: &ParseSess, features: &RefCell<Features>, def: &ast::Item)
})),
];
// Parse the macro_rules! invocation
let body = match def.node {
ast::ItemKind::MacroDef(ref body) => body.stream(),
_ => unreachable!(),
};
let argument_map = match parse(sess, body, &argument_gram, None, true) {
let argument_map = match parse(sess, body.stream(), &argument_gram, None, true) {
Success(m) => m,
Failure(sp, tok) => {
let s = parse_failure_msg(tok);

View file

@ -309,9 +309,12 @@ declare_features! (
// The `unadjusted` ABI. Perma unstable.
(active, abi_unadjusted, "1.16.0", None),
// Macros 1.1
// Procedural macros 2.0.
(active, proc_macro, "1.16.0", Some(38356)),
// Declarative macros 2.0 (`macro`).
(active, decl_macro, "1.17.0", Some(39412)),
// Allows attributes on struct literal fields.
(active, struct_field_attributes, "1.16.0", Some(38814)),
@ -1229,6 +1232,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
}
}
ast::ItemKind::MacroDef(ast::MacroDef { legacy: false, .. }) => {
let msg = "`macro` is experimental";
gate_feature_post!(&self, decl_macro, i.span, msg);
}
_ => {}
}

View file

@ -522,6 +522,7 @@ pub fn noop_fold_mac<T: Folder>(Spanned {node, span}: Mac, fld: &mut T) -> Mac {
pub fn noop_fold_macro_def<T: Folder>(def: MacroDef, fld: &mut T) -> MacroDef {
MacroDef {
tokens: fld.fold_tts(def.tokens.into()).into(),
legacy: def.legacy,
}
}

View file

@ -3758,33 +3758,59 @@ impl<'a> Parser<'a> {
fn eat_macro_def(&mut self, attrs: &[Attribute], vis: &Visibility)
-> PResult<'a, Option<P<Item>>> {
let lo = self.span;
match self.token {
token::Ident(ident) if ident.name == "macro_rules" => {
if self.look_ahead(1, |t| *t == token::Not) {
let prev_span = self.prev_span;
self.complain_if_pub_macro(vis, prev_span);
self.bump();
self.bump();
let (ident, def) = match self.token {
token::Ident(ident) if ident.name == keywords::Macro.name() => {
self.bump();
let ident = self.parse_ident()?;
let tokens = if self.check(&token::OpenDelim(token::Brace)) {
match self.parse_token_tree() {
TokenTree::Delimited(_, ref delimited) => delimited.stream(),
_ => unreachable!(),
}
} else if self.check(&token::OpenDelim(token::Paren)) {
let args = self.parse_token_tree();
let body = if self.check(&token::OpenDelim(token::Brace)) {
self.parse_token_tree()
} else {
self.unexpected()?;
unreachable!()
};
TokenStream::concat(vec![
args.into(),
TokenTree::Token(lo.to(self.prev_span), token::FatArrow).into(),
body.into(),
])
} else {
self.unexpected()?;
unreachable!()
};
(ident, ast::MacroDef { tokens: tokens.into(), legacy: false })
}
token::Ident(ident) if ident.name == "macro_rules" &&
self.look_ahead(1, |t| *t == token::Not) => {
let prev_span = self.prev_span;
self.complain_if_pub_macro(vis, prev_span);
self.bump();
self.bump();
let ident = self.parse_ident()?;
let (delim, tokens) = self.expect_delimited_token_tree()?;
if delim != token::Brace {
if !self.eat(&token::Semi) {
let msg = "macros that expand to items must either \
be surrounded with braces or followed by a semicolon";
self.span_err(self.prev_span, msg);
}
}
(ident, ast::MacroDef { tokens: tokens, legacy: true })
}
_ => return Ok(None),
};
let id = self.parse_ident()?;
let (delim, tts) = self.expect_delimited_token_tree()?;
if delim != token::Brace {
if !self.eat(&token::Semi) {
let msg = "macros that expand to items must either be surrounded with braces \
or followed by a semicolon";
self.span_err(self.prev_span, msg);
}
}
let span = lo.to(self.prev_span);
let kind = ItemKind::MacroDef(ast::MacroDef {
tokens: tts,
});
Ok(Some(self.mk_item(span, id, kind, Visibility::Inherited, attrs.to_owned())))
Ok(Some(self.mk_item(span, ident, ItemKind::MacroDef(def), vis.clone(), attrs.to_vec())))
}
fn parse_stmt_without_recovery(&mut self,