Skip to content

Implement RFC 873: Type Macros #27296

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

Merged
merged 11 commits into from
Aug 6, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1662,6 +1662,9 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
// handled specially and will not descend into this routine.
this.ty_infer(None, None, None, ast_ty.span)
}
ast::TyMac(_) => {
tcx.sess.span_bug(ast_ty.span, "unexpanded type macro found conversion")
}
};

tcx.ast_ty_to_ty_cache.borrow_mut().insert(ast_ty.id, typ);
Expand Down
3 changes: 3 additions & 0 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1611,6 +1611,9 @@ impl Clean<Type> for ast::Ty {
TyTypeof(..) => {
panic!("Unimplemented type {:?}", self.node)
},
TyMac(ref m) => {
cx.tcx().sess.span_bug(m.span, "unexpanded type macro found during cleaning")
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1471,6 +1471,8 @@ pub enum Ty_ {
/// TyInfer means the type should be inferred instead of it having been
/// specified. This can appear anywhere in a type.
TyInfer,
// A macro in the type position.
TyMac(Mac)
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
Expand Down
20 changes: 20 additions & 0 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,10 @@ pub trait MacResult {
fn make_stmts(self: Box<Self>) -> Option<SmallVector<P<ast::Stmt>>> {
make_stmts_default!(self)
}

fn make_ty(self: Box<Self>) -> Option<P<ast::Ty>> {
None
}
}

macro_rules! make_MacEager {
Expand Down Expand Up @@ -322,6 +326,7 @@ make_MacEager! {
items: SmallVector<P<ast::Item>>,
impl_items: SmallVector<P<ast::ImplItem>>,
stmts: SmallVector<P<ast::Stmt>>,
ty: P<ast::Ty>,
}

impl MacResult for MacEager {
Expand Down Expand Up @@ -359,6 +364,10 @@ impl MacResult for MacEager {
}
None
}

fn make_ty(self: Box<Self>) -> Option<P<ast::Ty>> {
self.ty
}
}

/// Fill-in macro expansion result, to allow compilation to continue
Expand Down Expand Up @@ -405,15 +414,24 @@ impl DummyResult {
}
}

pub fn raw_ty(sp: Span) -> P<ast::Ty> {
P(ast::Ty {
id: ast::DUMMY_NODE_ID,
node: ast::TyInfer,
span: sp
})
}
}

impl MacResult for DummyResult {
fn make_expr(self: Box<DummyResult>) -> Option<P<ast::Expr>> {
Some(DummyResult::raw_expr(self.span))
}

fn make_pat(self: Box<DummyResult>) -> Option<P<ast::Pat>> {
Some(P(DummyResult::raw_pat(self.span)))
}

fn make_items(self: Box<DummyResult>) -> Option<SmallVector<P<ast::Item>>> {
// this code needs a comment... why not always just return the Some() ?
if self.expr_only {
Expand All @@ -422,13 +440,15 @@ impl MacResult for DummyResult {
Some(SmallVector::zero())
}
}

fn make_impl_items(self: Box<DummyResult>) -> Option<SmallVector<P<ast::ImplItem>>> {
if self.expr_only {
None
} else {
Some(SmallVector::zero())
}
}

fn make_stmts(self: Box<DummyResult>) -> Option<SmallVector<P<ast::Stmt>>> {
Some(SmallVector::one(P(
codemap::respan(self.span,
Expand Down
47 changes: 47 additions & 0 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1552,6 +1552,45 @@ fn expand_and_rename_method(sig: ast::MethodSig, body: P<ast::Block>,
}, rewritten_body)
}

pub fn expand_type(t: P<ast::Ty>, fld: &mut MacroExpander) -> P<ast::Ty> {
let t = match t.node.clone() {
ast::Ty_::TyMac(mac) => {
if fld.cx.ecfg.features.unwrap().type_macros {
let expanded_ty = match expand_mac_invoc(mac, t.span,
|r| r.make_ty(),
mark_ty,
fld) {
Some(ty) => ty,
None => {
return DummyResult::raw_ty(t.span);
}
};

// Keep going, outside-in.
let fully_expanded = fld.fold_ty(expanded_ty);
fld.cx.bt_pop();

fully_expanded.map(|t| ast::Ty {
id: ast::DUMMY_NODE_ID,
node: t.node,
span: t.span,
})
} else {
feature_gate::emit_feature_err(
&fld.cx.parse_sess.span_diagnostic,
"type_macros",
t.span,
"type macros are experimental (see issue: #27336)");

DummyResult::raw_ty(t.span)
}
}
_ => t
};

fold::noop_fold_ty(t, fld)
}

/// A tree-folder that performs macro expansion
pub struct MacroExpander<'a, 'b:'a> {
pub cx: &'a mut ExtCtxt<'b>,
Expand Down Expand Up @@ -1602,6 +1641,10 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
.into_iter().map(|i| i.expect_impl_item()).collect()
}

fn fold_ty(&mut self, ty: P<ast::Ty>) -> P<ast::Ty> {
expand_type(ty, self)
}

fn new_span(&mut self, span: Span) -> Span {
new_span(self.cx, span)
}
Expand Down Expand Up @@ -1748,6 +1791,10 @@ fn mark_impl_item(ii: P<ast::ImplItem>, m: Mrk) -> P<ast::ImplItem> {
.expect_one("marking an impl item didn't return exactly one impl item")
}

fn mark_ty(ty: P<ast::Ty>, m: Mrk) -> P<ast::Ty> {
Marker { mark: m }.fold_ty(ty)
}

/// Check that there are no macro invocations left in the AST:
pub fn check_for_macros(sess: &parse::ParseSess, krate: &ast::Crate) {
visit::walk_crate(&mut MacroExterminator{sess:sess}, krate);
Expand Down
6 changes: 6 additions & 0 deletions src/libsyntax/ext/tt/macro_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
self.ensure_complete_parse(false);
Some(ret)
}

fn make_ty(self: Box<ParserAnyMacro<'a>>) -> Option<P<ast::Ty>> {
let ret = self.parser.borrow_mut().parse_ty();
self.ensure_complete_parse(true);
Some(ret)
}
}

struct MacroRulesMacroExpander {
Expand Down
6 changes: 6 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[

// Allows associated type defaults
("associated_type_defaults", "1.2.0", Active),
// Allows macros to appear in the type position.

("type_macros", "1.3.0", Active),
];
// (changing above list without updating src/doc/reference.md makes @cmr sad)

Expand Down Expand Up @@ -349,6 +352,7 @@ pub struct Features {
pub const_fn: bool,
pub static_recursion: bool,
pub default_type_parameter_fallback: bool,
pub type_macros: bool,
}

impl Features {
Expand All @@ -375,6 +379,7 @@ impl Features {
const_fn: false,
static_recursion: false,
default_type_parameter_fallback: false,
type_macros: false,
}
}
}
Expand Down Expand Up @@ -878,6 +883,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
const_fn: cx.has_feature("const_fn"),
static_recursion: cx.has_feature("static_recursion"),
default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"),
type_macros: cx.has_feature("type_macros"),
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,9 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
TyPolyTraitRef(bounds) => {
TyPolyTraitRef(bounds.move_map(|b| fld.fold_ty_param_bound(b)))
}
TyMac(mac) => {
TyMac(fld.fold_mac(mac))
}
},
span: fld.new_span(span)
})
Expand Down
17 changes: 15 additions & 2 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ use ast::{SelfExplicit, SelfRegion, SelfStatic, SelfValue};
use ast::{Delimited, SequenceRepetition, TokenTree, TraitItem, TraitRef};
use ast::{TtDelimited, TtSequence, TtToken};
use ast::{TupleVariantKind, Ty, Ty_, TypeBinding};
use ast::{TyMac};
use ast::{TyFixedLengthVec, TyBareFn, TyTypeof, TyInfer};
use ast::{TyParam, TyParamBound, TyParen, TyPath, TyPolyTraitRef, TyPtr};
use ast::{TyRptr, TyTup, TyU32, TyVec, UnUniq};
Expand Down Expand Up @@ -1369,8 +1370,20 @@ impl<'a> Parser<'a> {
} else if self.check(&token::ModSep) ||
self.token.is_ident() ||
self.token.is_path() {
// NAMED TYPE
try!(self.parse_ty_path())
let path = try!(self.parse_path(LifetimeAndTypesWithoutColons));
if self.check(&token::Not) {
// MACRO INVOCATION
try!(self.bump());
let delim = try!(self.expect_open_delim());
let tts = try!(self.parse_seq_to_end(&token::CloseDelim(delim),
seq_sep_none(),
|p| p.parse_token_tree()));
let hi = self.span.hi;
TyMac(spanned(lo, hi, MacInvocTT(path, tts, EMPTY_CTXT)))
} else {
// NAMED TYPE
TyPath(None, path)
}
} else if try!(self.eat(&token::Underscore) ){
// TYPE TO BE INFERRED
TyInfer
Expand Down
3 changes: 3 additions & 0 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,9 @@ impl<'a> State<'a> {
ast::TyInfer => {
try!(word(&mut self.s, "_"));
}
ast::TyMac(ref m) => {
try!(self.print_mac(m, token::Paren));
}
}
self.end()
}
Expand Down
3 changes: 3 additions & 0 deletions src/libsyntax/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
visitor.visit_expr(&**expression)
}
TyInfer => {}
TyMac(ref mac) => {
visitor.visit_mac(mac)
}
}
}

Expand Down
22 changes: 22 additions & 0 deletions src/test/compile-fail/type-macros-fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

macro_rules! Id {
{ $T:tt } => $T
}

struct Foo<T> {
x: Id!(T)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this have an //~ ERROR ...?

//~^ ERROR: type macros are experimental (see issue: #27336)
}

fn main() {
let foo = Foo { x: i32 };
}
2 changes: 1 addition & 1 deletion src/test/parse-fail/better-expected.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
// compile-flags: -Z parse-only

fn main() {
let x: [isize 3]; //~ ERROR expected one of `(`, `+`, `::`, `;`, `<`, or `]`, found `3`
let x: [isize 3]; //~ ERROR expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `]`, found `3`
}
2 changes: 1 addition & 1 deletion src/test/parse-fail/empty-impl-semicolon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@

// compile-flags: -Z parse-only

impl Foo; //~ ERROR expected one of `(`, `+`, `::`, `<`, `for`, `where`, or `{`, found `;`
impl Foo; //~ ERROR expected one of `!`, `(`, `+`, `::`, `<`, `for`, `where`, or `{`, found `;`
2 changes: 1 addition & 1 deletion src/test/parse-fail/multitrait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct S {
}

impl Cmp, ToString for S {
//~^ ERROR: expected one of `(`, `+`, `::`, `<`, `for`, `where`, or `{`, found `,`
//~^ ERROR: expected one of `!`, `(`, `+`, `::`, `<`, `for`, `where`, or `{`, found `,`
fn eq(&&other: S) { false }
fn to_string(&self) -> String { "hi".to_string() }
}
3 changes: 2 additions & 1 deletion src/test/parse-fail/removed-syntax-closure-lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@

// compile-flags: -Z parse-only

type closure = Box<lt/fn()>; //~ ERROR expected one of `(`, `+`, `,`, `::`, `<`, or `>`, found `/`
type closure = Box<lt/fn()>;
//~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `/`
2 changes: 1 addition & 1 deletion src/test/parse-fail/removed-syntax-fixed-vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@

// compile-flags: -Z parse-only

type v = [isize * 3]; //~ ERROR expected one of `(`, `+`, `::`, `;`, `<`, or `]`, found `*`
type v = [isize * 3]; //~ ERROR expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `]`, found `*`
2 changes: 1 addition & 1 deletion src/test/parse-fail/removed-syntax-mut-vec-ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@

type v = [mut isize];
//~^ ERROR expected identifier, found keyword `mut`
//~^^ ERROR expected one of `(`, `+`, `::`, `;`, `<`, or `]`, found `isize`
//~^^ ERROR expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `]`, found `isize`
2 changes: 1 addition & 1 deletion src/test/parse-fail/removed-syntax-ptr-lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@

// compile-flags: -Z parse-only

type bptr = &lifetime/isize; //~ ERROR expected one of `(`, `+`, `::`, `;`, or `<`, found `/`
type bptr = &lifetime/isize; //~ ERROR expected one of `!`, `(`, `+`, `::`, `;`, or `<`, found `/`
2 changes: 1 addition & 1 deletion src/test/parse-fail/removed-syntax-uniq-mut-ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@

type mut_box = Box<mut isize>;
//~^ ERROR expected identifier, found keyword `mut`
//~^^ ERROR expected one of `(`, `+`, `,`, `::`, `<`, or `>`, found `isize`
//~^^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `isize`
Loading