Skip to content

Commit

Permalink
Auto merge of rust-lang#27296 - jroesch:type-macros, r=huonw
Browse files Browse the repository at this point in the history
This pull request implements the functionality for [RFC 873](https://github.com/rust-lang/rfcs/blob/master/text/0873-type-macros.md). This is currently just an update of @freebroccolo's branch from January, the corresponding commits are linked in each commit message.

@nikomatsakis and I had talked about updating the macro language to support a lifetime fragment specifier, and it is possible to do that work on this branch as well. If so we can (collectively) talk about it next week during the pre-RustCamp work week.
  • Loading branch information
bors committed Aug 6, 2015
2 parents fb92de7 + 83e43bb commit 11deb08
Show file tree
Hide file tree
Showing 22 changed files with 250 additions and 10 deletions.
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 @@ -1542,6 +1542,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 @@ -1592,6 +1631,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 @@ -1744,6 +1787,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 @@ -172,6 +172,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 @@ -354,6 +357,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 @@ -380,6 +384,7 @@ impl Features {
const_fn: false,
static_recursion: false,
default_type_parameter_fallback: false,
type_macros: false,
}
}
}
Expand Down Expand Up @@ -883,6 +888,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:https://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http:https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http:https://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)
//~^ 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

0 comments on commit 11deb08

Please sign in to comment.