Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Allow macros in identifier position #14266

Closed
wants to merge 10 commits into from
1 change: 1 addition & 0 deletions src/librustc/driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ pub fn phase_3_run_analysis_passes(sess: Session,

// Discard MTWT tables that aren't required past resolution.
syntax::ext::mtwt::clear_tables();
syntax::ast::clear_macro_idents();

let named_region_map = time(time_passes, "lifetime resolution", (),
|_| middle::resolve_lifetime::krate(&sess, krate));
Expand Down
45 changes: 45 additions & 0 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ use parse::token;

use std::fmt;
use std::fmt::Show;
use std::cell::RefCell;
use std::local_data::Ref;
use std::option::Option;
use std::rc::Rc;
use serialize::{Encodable, Decodable, Encoder, Decoder};
Expand All @@ -44,9 +46,52 @@ pub struct Ident {
pub ctxt: SyntaxContext
}

pub static MACRO_CTXT: SyntaxContext = 0xffffffffu32;

local_data_key!(key_macro_idents: RefCell<Vec<Mac>>)

fn get_macro_idents() -> Ref<RefCell<Vec<Mac>>> {
match key_macro_idents.get() {
Some(x) => x,
None => {
let x = RefCell::new(Vec::new());
key_macro_idents.replace(Some(x));
get_macro_idents()
}
}
}

pub fn clear_macro_idents() {
key_macro_idents.replace(None);
}

impl Ident {
/// Construct an identifier with the given name and an empty context:
pub fn new(name: Name) -> Ident { Ident {name: name, ctxt: EMPTY_CTXT}}

pub fn new_macro_ident(a: Mac) -> Ident {
let r = get_macro_idents();
let mut macro_idents = r.borrow_mut();
macro_idents.push(a);
let idx: Name = macro_idents.len() as u32 - 1;
Ident {name: idx, ctxt: MACRO_CTXT}
}

pub fn get_macro_ident(&self) -> Option<Mac> {
match self.ctxt {
MACRO_CTXT => {
let r = get_macro_idents();
let mut macro_idents = r.borrow_mut();
let i = self.name as uint;
if i < macro_idents.len() {
Some(macro_idents.get(i).clone())
} else {
None
}
},
_ => None
}
}
}

impl Eq for Ident {
Expand Down
4 changes: 4 additions & 0 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ pub trait MacResult {
fn make_items(&self) -> Option<SmallVector<@ast::Item>> {
None
}
/// Create an identifier
fn make_ident(&self) -> Option<ast::Ident> {
None
}

/// Create a statement.
///
Expand Down
52 changes: 33 additions & 19 deletions src/libsyntax/ext/concat_idents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,38 @@ use parse::token::{str_to_ident};

use std::strbuf::StrBuf;

pub struct ConcatIdent {
span: Span,
ident: ast::Ident,
}

impl MacResult for ConcatIdent {
fn make_expr(&self) -> Option<@ast::Expr> {
let e = @ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprPath(
ast::Path {
span: self.span,
global: false,
segments: vec!(
ast::PathSegment {
identifier: self.ident,
lifetimes: Vec::new(),
types: OwnedSlice::empty(),
}
)
}
),
span: self.span,
};
MacExpr::new(e).make_expr()
}

fn make_ident(&self) -> Option<ast::Ident> {
Some(self.ident)
}
}

pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> Box<base::MacResult> {
let mut res_str = StrBuf::new();
Expand All @@ -43,23 +75,5 @@ pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
}
}
let res = str_to_ident(res_str.into_owned());

let e = @ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprPath(
ast::Path {
span: sp,
global: false,
segments: vec!(
ast::PathSegment {
identifier: res,
lifetimes: Vec::new(),
types: OwnedSlice::empty(),
}
)
}
),
span: sp,
};
MacExpr::new(e)
box ConcatIdent { span: sp, ident: res } as Box<base::MacResult>
}
84 changes: 74 additions & 10 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ fn expand_loop_block(loop_block: P<Block>,
// and be renamed incorrectly.
let mut rename_list = vec!(rename);
let mut rename_fld = renames_to_fold(&mut rename_list);
let renamed_ident = rename_fld.fold_ident(label);
let renamed_ident = fold_ident_or_macro(label, &mut rename_fld);

// The rename *must* be added to the enclosed syntax context for
// `break` or `continue` to pick up because by definition they are
Expand Down Expand Up @@ -363,6 +363,57 @@ pub fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
attr::contains_name(attrs, "macro_escape")
}

fn expand_ident(ident: Ident, fld: &mut MacroExpander) -> Ident {
match ident.get_macro_ident() {
None => ident,
Some(mac) => {
let MacInvocTT(ref pth, ref tts, _) = mac.node;
let extname = pth.segments.get(0).identifier;
let extnamestr = token::get_ident(extname);
match fld.extsbox.find(&extname.name) {
None => {
fld.cx.span_err(pth.span, format!("macro undefined: '{}'", extnamestr));
parse::token::special_idents::invalid
}

Some(&NormalTT(ref expandfun, exp_span)) => {
fld.cx.bt_push(ExpnInfo {
call_site: mac.span,
callee: NameAndSpan {
name: extnamestr.get().to_strbuf(),
format: MacroBang,
span: exp_span,
}
});
let fm = fresh_mark();
// mark before expansion:
let marked_tts = mark_tts(tts.as_slice(), fm);

// TODO: Read expand_expr to see whether we want
// original_span(fld.cx) or mac.span
let mac_span = original_span(fld.cx);

match expandfun.expand(fld.cx,
mac_span.call_site,
marked_tts.as_slice()).make_ident() {
Some(ident) => ident,
None => {
fld.cx.span_err(pth.span,
format!("non-ident macro in ident pos: {}",
extnamestr));
parse::token::special_idents::invalid
}
}
}
_ => {
debug!("FIXME");
parse::token::special_idents::invalid
}
}
}
}
}

// Support for item-position macro invocations, exactly the same
// logic as for expression-position macro invocations.
pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander)
Expand Down Expand Up @@ -812,12 +863,17 @@ pub struct IdentRenamer<'a> {

impl<'a> Folder for IdentRenamer<'a> {
fn fold_ident(&mut self, id: Ident) -> Ident {
let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
mtwt::new_rename(from, to, ctxt)
});
Ident {
name: id.name,
ctxt: new_ctxt,
match id.get_macro_ident() {
Some(_mac) => id,
None => {
let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
mtwt::new_rename(from, to, ctxt)
});
Ident {
name: id.name,
ctxt: new_ctxt,
}
}
}
}
}
Expand Down Expand Up @@ -849,6 +905,10 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
expand_expr(expr, self)
}

fn fold_ident(&mut self, ident: ast::Ident) -> ast::Ident {
expand_ident(ident, self)
}

fn fold_item(&mut self, item: @ast::Item) -> SmallVector<@ast::Item> {
expand_item(item, self)
}
Expand Down Expand Up @@ -903,9 +963,13 @@ struct Marker { mark: Mrk }

impl Folder for Marker {
fn fold_ident(&mut self, id: Ident) -> Ident {
ast::Ident {
name: id.name,
ctxt: mtwt::new_mark(self.mark, id.ctxt)
match id.get_macro_ident() {
Some(_mac) => id,
None =>
ast::Ident {
name: id.name,
ctxt: mtwt::new_mark(self.mark, id.ctxt)
}
}
}
fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
Expand Down
37 changes: 22 additions & 15 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ pub trait Folder {
span: self.new_span(p.span),
global: p.global,
segments: p.segments.iter().map(|segment| ast::PathSegment {
identifier: self.fold_ident(segment.identifier),
identifier: fold_ident_or_macro(segment.identifier, self),
lifetimes: segment.lifetimes.iter().map(|l| self.fold_lifetime(l)).collect(),
types: segment.types.iter().map(|&typ| self.fold_ty(typ)).collect(),
}).collect()
Expand Down Expand Up @@ -408,18 +408,25 @@ pub fn fold_tts<T: Folder>(tts: &[TokenTree], fld: &mut T) -> Vec<TokenTree> {
sep.as_ref().map(|tok|maybe_fold_ident(tok,fld)),
is_optional),
TTNonterminal(sp,ref ident) =>
TTNonterminal(sp,fld.fold_ident(*ident))
TTNonterminal(sp,fold_ident_or_macro(*ident, fld))
}
}).collect()
}

pub fn fold_ident_or_macro<T: Folder>(i: Ident, folder: &mut T) -> Ident {
match i.get_macro_ident() {
Some(mac) => ast::Ident::new_macro_ident(folder.fold_mac(&mac)),
None => folder.fold_ident(i)
}
}

// apply ident folder if it's an ident, otherwise leave it alone
fn maybe_fold_ident<T: Folder>(t: &token::Token, fld: &mut T) -> token::Token {
match *t {
token::IDENT(id, followed_by_colons) => {
token::IDENT(fld.fold_ident(id), followed_by_colons)
token::IDENT(fold_ident_or_macro(id, fld), followed_by_colons)
}
token::LIFETIME(id) => token::LIFETIME(fld.fold_ident(id)),
token::LIFETIME(id) => token::LIFETIME(fold_ident_or_macro(id, fld)),
_ => (*t).clone()
}
}
Expand Down Expand Up @@ -518,7 +525,7 @@ fn fold_struct_field<T: Folder>(f: &StructField, fld: &mut T) -> StructField {

fn fold_field_<T: Folder>(field: Field, folder: &mut T) -> Field {
ast::Field {
ident: respan(field.ident.span, folder.fold_ident(field.ident.node)),
ident: respan(field.ident.span, fold_ident_or_macro(field.ident.node, folder)),
expr: folder.fold_expr(field.expr),
span: folder.new_span(field.span),
}
Expand Down Expand Up @@ -641,7 +648,7 @@ pub fn noop_fold_type_method<T: Folder>(m: &TypeMethod, fld: &mut T) -> TypeMeth
let id = fld.new_id(m.id); // Needs to be first, for ast_map.
TypeMethod {
id: id,
ident: fld.fold_ident(m.ident),
ident: fold_ident_or_macro(m.ident, fld),
attrs: m.attrs.iter().map(|a| fold_attribute_(*a, fld)).collect(),
fn_style: m.fn_style,
decl: fld.fold_fn_decl(m.decl),
Expand Down Expand Up @@ -683,7 +690,7 @@ pub fn noop_fold_item<T: Folder>(i: &Item, folder: &mut T) -> SmallVector<@Item>

SmallVector::one(@Item {
id: id,
ident: folder.fold_ident(ident),
ident: fold_ident_or_macro(ident, folder),
attrs: i.attrs.iter().map(|e| fold_attribute_(*e, folder)).collect(),
node: node,
vis: i.vis,
Expand All @@ -695,7 +702,7 @@ pub fn noop_fold_foreign_item<T: Folder>(ni: &ForeignItem, folder: &mut T) -> @F
let id = folder.new_id(ni.id); // Needs to be first, for ast_map.
@ForeignItem {
id: id,
ident: folder.fold_ident(ni.ident),
ident: fold_ident_or_macro(ni.ident, folder),
attrs: ni.attrs.iter().map(|x| fold_attribute_(*x, folder)).collect(),
node: match ni.node {
ForeignItemFn(ref fdec, ref generics) => {
Expand All @@ -719,7 +726,7 @@ pub fn noop_fold_method<T: Folder>(m: &Method, folder: &mut T) -> @Method {
let id = folder.new_id(m.id); // Needs to be first, for ast_map.
@Method {
id: id,
ident: folder.fold_ident(m.ident),
ident: fold_ident_or_macro(m.ident, folder),
attrs: m.attrs.iter().map(|a| fold_attribute_(*a, folder)).collect(),
generics: fold_generics(&m.generics, folder),
explicit_self: folder.fold_explicit_self(&m.explicit_self),
Expand Down Expand Up @@ -798,7 +805,7 @@ pub fn noop_fold_expr<T: Folder>(e: @Expr, folder: &mut T) -> @Expr {
}
ExprMethodCall(i, ref tps, ref args) => {
ExprMethodCall(
respan(i.span, folder.fold_ident(i.node)),
respan(i.span, fold_ident_or_macro(i.node, folder)),
tps.iter().map(|&x| folder.fold_ty(x)).collect(),
args.iter().map(|&x| folder.fold_expr(x)).collect())
}
Expand Down Expand Up @@ -827,11 +834,11 @@ pub fn noop_fold_expr<T: Folder>(e: @Expr, folder: &mut T) -> @Expr {
ExprForLoop(folder.fold_pat(pat),
folder.fold_expr(iter),
folder.fold_block(body),
maybe_ident.map(|i| folder.fold_ident(i)))
maybe_ident.map(|i| fold_ident_or_macro(i, folder)))
}
ExprLoop(body, opt_ident) => {
ExprLoop(folder.fold_block(body),
opt_ident.map(|x| folder.fold_ident(x)))
opt_ident.map(|x| fold_ident_or_macro(x, folder)))
}
ExprMatch(expr, ref arms) => {
ExprMatch(folder.fold_expr(expr),
Expand All @@ -854,15 +861,15 @@ pub fn noop_fold_expr<T: Folder>(e: @Expr, folder: &mut T) -> @Expr {
}
ExprField(el, id, ref tys) => {
ExprField(folder.fold_expr(el),
folder.fold_ident(id),
fold_ident_or_macro(id, folder),
tys.iter().map(|&x| folder.fold_ty(x)).collect())
}
ExprIndex(el, er) => {
ExprIndex(folder.fold_expr(el), folder.fold_expr(er))
}
ExprPath(ref pth) => ExprPath(folder.fold_path(pth)),
ExprBreak(opt_ident) => ExprBreak(opt_ident.map(|x| folder.fold_ident(x))),
ExprAgain(opt_ident) => ExprAgain(opt_ident.map(|x| folder.fold_ident(x))),
ExprBreak(opt_ident) => ExprBreak(opt_ident.map(|x| fold_ident_or_macro(x, folder))),
ExprAgain(opt_ident) => ExprAgain(opt_ident.map(|x| fold_ident_or_macro(x, folder))),
ExprRet(ref e) => {
ExprRet(e.map(|x| folder.fold_expr(x)))
}
Expand Down
Loading