Declarative macros 2.0 without hygiene.
This commit is contained in:
parent
9c6430b325
commit
2a1d2edb82
14 changed files with 116 additions and 46 deletions
10
src/doc/unstable-book/src/language-features/decl-macro.md
Normal file
10
src/doc/unstable-book/src/language-features/decl-macro.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# `decl_macro`
|
||||||
|
|
||||||
|
The tracking issue for this feature is: [#39412]
|
||||||
|
|
||||||
|
[#39412]: https://github.com/rust-lang/rust/issues/39412
|
||||||
|
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1502,10 +1502,11 @@ impl<'a> LoweringContext<'a> {
|
||||||
pub fn lower_item(&mut self, i: &Item) -> Option<hir::Item> {
|
pub fn lower_item(&mut self, i: &Item) -> Option<hir::Item> {
|
||||||
let mut name = i.ident.name;
|
let mut name = i.ident.name;
|
||||||
let attrs = self.lower_attrs(&i.attrs);
|
let attrs = self.lower_attrs(&i.attrs);
|
||||||
if let ItemKind::MacroDef(ref tts) = i.node {
|
if let ItemKind::MacroDef(ref def) = i.node {
|
||||||
if i.attrs.iter().any(|attr| attr.path == "macro_export") {
|
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 {
|
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;
|
return None;
|
||||||
|
|
|
@ -536,6 +536,7 @@ pub struct MacroDef {
|
||||||
pub id: NodeId,
|
pub id: NodeId,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub body: TokenStream,
|
pub body: TokenStream,
|
||||||
|
pub legacy: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
||||||
|
|
|
@ -332,6 +332,7 @@ impl_stable_hash_for!(struct hir::MacroDef {
|
||||||
attrs,
|
attrs,
|
||||||
id,
|
id,
|
||||||
span,
|
span,
|
||||||
|
legacy,
|
||||||
body
|
body
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -388,6 +388,7 @@ impl CrateStore for cstore::CStore {
|
||||||
attrs: attrs.iter().cloned().collect(),
|
attrs: attrs.iter().cloned().collect(),
|
||||||
node: ast::ItemKind::MacroDef(ast::MacroDef {
|
node: ast::ItemKind::MacroDef(ast::MacroDef {
|
||||||
tokens: body.into(),
|
tokens: body.into(),
|
||||||
|
legacy: true,
|
||||||
}),
|
}),
|
||||||
vis: ast::Visibility::Inherited,
|
vis: ast::Visibility::Inherited,
|
||||||
})
|
})
|
||||||
|
|
|
@ -77,7 +77,7 @@ struct LegacyMacroImports {
|
||||||
impl<'a> Resolver<'a> {
|
impl<'a> Resolver<'a> {
|
||||||
/// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined;
|
/// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined;
|
||||||
/// otherwise, reports an error.
|
/// 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>,
|
where T: ToNameBinding<'a>,
|
||||||
{
|
{
|
||||||
let binding = def.to_name_binding(self.arenas);
|
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) {
|
fn visit_item(&mut self, item: &'a Item) {
|
||||||
let macro_use = match item.node {
|
let macro_use = match item.node {
|
||||||
ItemKind::MacroDef(..) => {
|
ItemKind::MacroDef(..) => {
|
||||||
self.resolver.define_macro(item, &mut self.legacy_scope);
|
self.resolver.define_macro(item, self.expansion, &mut self.legacy_scope);
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ItemKind::Mac(..) => {
|
ItemKind::Mac(..) => {
|
||||||
|
|
|
@ -1058,6 +1058,13 @@ impl<'a> NameBinding<'a> {
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_macro_def(&self) -> bool {
|
||||||
|
match self.kind {
|
||||||
|
NameBindingKind::Def(Def::Macro(..)) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interns the names of the primitive types.
|
/// Interns the names of the primitive types.
|
||||||
|
@ -1377,8 +1384,9 @@ impl<'a> Resolver<'a> {
|
||||||
vis: ty::Visibility::Public,
|
vis: ty::Visibility::Public,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// `#![feature(proc_macro)]` implies `#[feature(extern_macros)]`
|
// The `proc_macro` and `decl_macro` features imply `use_extern_macros`
|
||||||
use_extern_macros: features.use_extern_macros || features.proc_macro,
|
use_extern_macros:
|
||||||
|
features.use_extern_macros || features.proc_macro || features.decl_macro,
|
||||||
|
|
||||||
crate_loader: crate_loader,
|
crate_loader: crate_loader,
|
||||||
macro_names: FxHashSet(),
|
macro_names: FxHashSet(),
|
||||||
|
|
|
@ -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);
|
self.local_macro_def_scopes.insert(item.id, self.current_module);
|
||||||
let ident = item.ident;
|
let ident = item.ident;
|
||||||
if ident.name == "macro_rules" {
|
if ident.name == "macro_rules" {
|
||||||
|
@ -699,16 +702,24 @@ impl<'a> Resolver<'a> {
|
||||||
&self.session.features,
|
&self.session.features,
|
||||||
item));
|
item));
|
||||||
self.macro_map.insert(def_id, ext);
|
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 = match item.node { ast::ItemKind::MacroDef(ref def) => def, _ => unreachable!() };
|
||||||
let def = Def::Macro(def_id, MacroKind::Bang);
|
if def.legacy {
|
||||||
self.macro_exports.push(Export { name: ident.name, def: def, span: item.span });
|
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 {
|
} 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -803,7 +803,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if binding.vis == ty::Visibility::Public &&
|
if binding.vis == ty::Visibility::Public &&
|
||||||
(binding.is_import() || binding.is_extern_crate()) {
|
(binding.is_import() || binding.is_macro_def()) {
|
||||||
let def = binding.def();
|
let def = binding.def();
|
||||||
if def != Def::Err {
|
if def != Def::Err {
|
||||||
if !def.def_id().is_local() {
|
if !def.def_id().is_local() {
|
||||||
|
|
|
@ -1022,6 +1022,7 @@ impl Mac_ {
|
||||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
||||||
pub struct MacroDef {
|
pub struct MacroDef {
|
||||||
pub tokens: ThinTokenStream,
|
pub tokens: ThinTokenStream,
|
||||||
|
pub legacy: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MacroDef {
|
impl MacroDef {
|
||||||
|
|
|
@ -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 lhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("lhs"));
|
||||||
let rhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("rhs"));
|
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 pattern that macro_rules matches.
|
||||||
// The grammar for macro_rules! is:
|
// The grammar for macro_rules! is:
|
||||||
// $( $lhs:tt => $rhs:tt );+
|
// $( $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::Token(DUMMY_SP, token::FatArrow),
|
||||||
quoted::TokenTree::MetaVarDecl(DUMMY_SP, rhs_nm, ast::Ident::from_str("tt")),
|
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,
|
op: quoted::KleeneOp::OneOrMore,
|
||||||
num_captures: 2,
|
num_captures: 2,
|
||||||
})),
|
})),
|
||||||
|
@ -187,12 +193,7 @@ pub fn compile(sess: &ParseSess, features: &RefCell<Features>, def: &ast::Item)
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Parse the macro_rules! invocation
|
let argument_map = match parse(sess, body.stream(), &argument_gram, None, true) {
|
||||||
let body = match def.node {
|
|
||||||
ast::ItemKind::MacroDef(ref body) => body.stream(),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let argument_map = match parse(sess, body, &argument_gram, None, true) {
|
|
||||||
Success(m) => m,
|
Success(m) => m,
|
||||||
Failure(sp, tok) => {
|
Failure(sp, tok) => {
|
||||||
let s = parse_failure_msg(tok);
|
let s = parse_failure_msg(tok);
|
||||||
|
|
|
@ -309,9 +309,12 @@ declare_features! (
|
||||||
// The `unadjusted` ABI. Perma unstable.
|
// The `unadjusted` ABI. Perma unstable.
|
||||||
(active, abi_unadjusted, "1.16.0", None),
|
(active, abi_unadjusted, "1.16.0", None),
|
||||||
|
|
||||||
// Macros 1.1
|
// Procedural macros 2.0.
|
||||||
(active, proc_macro, "1.16.0", Some(38356)),
|
(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.
|
// Allows attributes on struct literal fields.
|
||||||
(active, struct_field_attributes, "1.16.0", Some(38814)),
|
(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);
|
||||||
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
pub fn noop_fold_macro_def<T: Folder>(def: MacroDef, fld: &mut T) -> MacroDef {
|
||||||
MacroDef {
|
MacroDef {
|
||||||
tokens: fld.fold_tts(def.tokens.into()).into(),
|
tokens: fld.fold_tts(def.tokens.into()).into(),
|
||||||
|
legacy: def.legacy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3758,33 +3758,59 @@ impl<'a> Parser<'a> {
|
||||||
fn eat_macro_def(&mut self, attrs: &[Attribute], vis: &Visibility)
|
fn eat_macro_def(&mut self, attrs: &[Attribute], vis: &Visibility)
|
||||||
-> PResult<'a, Option<P<Item>>> {
|
-> PResult<'a, Option<P<Item>>> {
|
||||||
let lo = self.span;
|
let lo = self.span;
|
||||||
match self.token {
|
let (ident, def) = match self.token {
|
||||||
token::Ident(ident) if ident.name == "macro_rules" => {
|
token::Ident(ident) if ident.name == keywords::Macro.name() => {
|
||||||
if self.look_ahead(1, |t| *t == token::Not) {
|
self.bump();
|
||||||
let prev_span = self.prev_span;
|
let ident = self.parse_ident()?;
|
||||||
self.complain_if_pub_macro(vis, prev_span);
|
let tokens = if self.check(&token::OpenDelim(token::Brace)) {
|
||||||
self.bump();
|
match self.parse_token_tree() {
|
||||||
self.bump();
|
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),
|
_ => 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 span = lo.to(self.prev_span);
|
||||||
let kind = ItemKind::MacroDef(ast::MacroDef {
|
Ok(Some(self.mk_item(span, ident, ItemKind::MacroDef(def), vis.clone(), attrs.to_vec())))
|
||||||
tokens: tts,
|
|
||||||
});
|
|
||||||
Ok(Some(self.mk_item(span, id, kind, Visibility::Inherited, attrs.to_owned())))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_stmt_without_recovery(&mut self,
|
fn parse_stmt_without_recovery(&mut self,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue