Auto merge of #27296 - jroesch:type-macros, r=huonw
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.
This commit is contained in:
commit
11deb083f5
@ -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);
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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 {
|
||||
@ -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 {
|
||||
@ -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
|
||||
@ -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 {
|
||||
@ -422,6 +440,7 @@ impl MacResult for DummyResult {
|
||||
Some(SmallVector::zero())
|
||||
}
|
||||
}
|
||||
|
||||
fn make_impl_items(self: Box<DummyResult>) -> Option<SmallVector<P<ast::ImplItem>>> {
|
||||
if self.expr_only {
|
||||
None
|
||||
@ -429,6 +448,7 @@ impl MacResult for DummyResult {
|
||||
Some(SmallVector::zero())
|
||||
}
|
||||
}
|
||||
|
||||
fn make_stmts(self: Box<DummyResult>) -> Option<SmallVector<P<ast::Stmt>>> {
|
||||
Some(SmallVector::one(P(
|
||||
codemap::respan(self.span,
|
||||
|
@ -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>,
|
||||
@ -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)
|
||||
}
|
||||
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
||||
@ -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 {
|
||||
@ -380,6 +384,7 @@ impl Features {
|
||||
const_fn: false,
|
||||
static_recursion: false,
|
||||
default_type_parameter_fallback: false,
|
||||
type_macros: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
})
|
||||
|
@ -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};
|
||||
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
22
src/test/compile-fail/type-macros-fail.rs
Normal file
22
src/test/compile-fail/type-macros-fail.rs
Normal file
@ -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)
|
||||
//~^ ERROR: type macros are experimental (see issue: #27336)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let foo = Foo { x: i32 };
|
||||
}
|
@ -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`
|
||||
}
|
||||
|
@ -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 `;`
|
||||
|
@ -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() }
|
||||
}
|
||||
|
@ -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 `/`
|
||||
|
@ -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 `*`
|
||||
|
@ -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`
|
||||
|
@ -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 `/`
|
||||
|
@ -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`
|
||||
|
89
src/test/run-pass/type-macros-hlist.rs
Normal file
89
src/test/run-pass/type-macros-hlist.rs
Normal file
@ -0,0 +1,89 @@
|
||||
// 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.
|
||||
|
||||
#![feature(type_macros)]
|
||||
|
||||
use std::ops::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
struct Nil;
|
||||
// empty HList
|
||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
struct Cons<H, T: HList>(H, T);
|
||||
// cons cell of HList
|
||||
|
||||
// trait to classify valid HLists
|
||||
trait HList { }
|
||||
impl HList for Nil { }
|
||||
impl <H, T: HList> HList for Cons<H, T> { }
|
||||
|
||||
// term-level macro for HLists
|
||||
macro_rules! hlist({ } => { Nil } ; { $ head : expr } => {
|
||||
Cons ( $ head , Nil ) } ; {
|
||||
$ head : expr , $ ( $ tail : expr ) , * } => {
|
||||
Cons ( $ head , hlist ! ( $ ( $ tail ) , * ) ) } ;);
|
||||
|
||||
// type-level macro for HLists
|
||||
macro_rules! HList({ } => { Nil } ; { $ head : ty } => {
|
||||
Cons < $ head , Nil > } ; {
|
||||
$ head : ty , $ ( $ tail : ty ) , * } => {
|
||||
Cons < $ head , HList ! ( $ ( $ tail ) , * ) > } ;);
|
||||
|
||||
// nil case for HList append
|
||||
impl <Ys: HList> Add<Ys> for Nil {
|
||||
type
|
||||
Output
|
||||
=
|
||||
Ys;
|
||||
|
||||
fn add(self, rhs: Ys) -> Ys { rhs }
|
||||
}
|
||||
|
||||
// cons case for HList append
|
||||
impl <Rec: HList + Sized, X, Xs: HList, Ys: HList> Add<Ys> for Cons<X, Xs>
|
||||
where Xs: Add<Ys, Output = Rec> {
|
||||
type
|
||||
Output
|
||||
=
|
||||
Cons<X, Rec>;
|
||||
|
||||
fn add(self, rhs: Ys) -> Cons<X, Rec> { Cons(self.0, self.1 + rhs) }
|
||||
}
|
||||
|
||||
// type macro Expr allows us to expand the + operator appropriately
|
||||
macro_rules! Expr({ ( $ ( $ LHS : tt ) + ) } => { Expr ! ( $ ( $ LHS ) + ) } ;
|
||||
{ HList ! [ $ ( $ LHS : tt ) * ] + $ ( $ RHS : tt ) + } => {
|
||||
< Expr ! ( HList ! [ $ ( $ LHS ) * ] ) as Add < Expr ! (
|
||||
$ ( $ RHS ) + ) >> :: Output } ; {
|
||||
$ LHS : tt + $ ( $ RHS : tt ) + } => {
|
||||
< Expr ! ( $ LHS ) as Add < Expr ! ( $ ( $ RHS ) + ) >> ::
|
||||
Output } ; { $ LHS : ty } => { $ LHS } ;);
|
||||
|
||||
// test demonstrating term level `xs + ys` and type level `Expr!(Xs + Ys)`
|
||||
fn main() {
|
||||
fn aux<Xs: HList, Ys: HList>(xs: Xs, ys: Ys) -> Expr!(Xs + Ys) where
|
||||
Xs: Add<Ys> {
|
||||
xs + ys
|
||||
}
|
||||
|
||||
let xs: HList!(& str , bool , Vec < u64 >) =
|
||||
hlist!("foo" , false , vec ! [ ]);
|
||||
let ys: HList!(u64 , [ u8 ; 3 ] , ( )) =
|
||||
hlist!(0 , [ 0 , 1 , 2 ] , ( ));
|
||||
|
||||
// demonstrate recursive expansion of Expr!
|
||||
let zs:
|
||||
Expr!((
|
||||
HList ! [ & str ] + HList ! [ bool ] + HList ! [ Vec < u64 >
|
||||
] ) + ( HList ! [ u64 ] + HList ! [ [ u8 ; 3 ] , ( ) ] ) +
|
||||
HList ! [ ]) = aux(xs, ys);
|
||||
assert_eq!(zs , hlist ! [
|
||||
"foo" , false , vec ! [ ] , 0 , [ 0 , 1 , 2 ] , ( ) ])
|
||||
}
|
19
src/test/run-pass/type-macros-simple.rs
Normal file
19
src/test/run-pass/type-macros-simple.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// 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.
|
||||
|
||||
#![feature(type_macros)]
|
||||
|
||||
macro_rules! Tuple {
|
||||
{ $A:ty,$B:ty } => { ($A, $B) }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x: Tuple!(i32, i32) = (1, 2);
|
||||
}
|
Loading…
Reference in New Issue
Block a user