about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-08-06 19:11:17 +0000
committerbors <bors@rust-lang.org>2015-08-06 19:11:17 +0000
commit11deb083f5bc3e57e73fc82de4bef7b1d4dad7b1 (patch)
tree0f575e98b114fe6b854a1fd53ff1f7749ba3621c
parentfb92de75c1c4b7eaaf5d425fb2587407c00701fc (diff)
parent83e43bb728b95d52039824d63b1ba5bbde5c5d7b (diff)
downloadrust-11deb083f5bc3e57e73fc82de4bef7b1d4dad7b1.tar.gz
rust-11deb083f5bc3e57e73fc82de4bef7b1d4dad7b1.zip
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.
-rw-r--r--src/librustc_typeck/astconv.rs3
-rw-r--r--src/librustdoc/clean/mod.rs3
-rw-r--r--src/libsyntax/ast.rs2
-rw-r--r--src/libsyntax/ext/base.rs20
-rw-r--r--src/libsyntax/ext/expand.rs47
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs6
-rw-r--r--src/libsyntax/feature_gate.rs6
-rw-r--r--src/libsyntax/fold.rs3
-rw-r--r--src/libsyntax/parse/parser.rs17
-rw-r--r--src/libsyntax/print/pprust.rs3
-rw-r--r--src/libsyntax/visit.rs3
-rw-r--r--src/test/compile-fail/type-macros-fail.rs22
-rw-r--r--src/test/parse-fail/better-expected.rs2
-rw-r--r--src/test/parse-fail/empty-impl-semicolon.rs2
-rw-r--r--src/test/parse-fail/multitrait.rs2
-rw-r--r--src/test/parse-fail/removed-syntax-closure-lifetime.rs3
-rw-r--r--src/test/parse-fail/removed-syntax-fixed-vec.rs2
-rw-r--r--src/test/parse-fail/removed-syntax-mut-vec-ty.rs2
-rw-r--r--src/test/parse-fail/removed-syntax-ptr-lifetime.rs2
-rw-r--r--src/test/parse-fail/removed-syntax-uniq-mut-ty.rs2
-rw-r--r--src/test/run-pass/type-macros-hlist.rs89
-rw-r--r--src/test/run-pass/type-macros-simple.rs19
22 files changed, 250 insertions, 10 deletions
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index a2c968a290b..99f375c3286 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -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);
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 11a0e0eaa49..64575e68569 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -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")
+            }
         }
     }
 }
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index db173d08308..eefd3da9f4a 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -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)]
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index b2cff3ed53c..d4b5e67eeb4 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -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,
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 66b3768a476..cd60ee0691c 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -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);
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index adc88c329a3..d16fde7bc39 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -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 {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 4a1b74d89d0..dd6fc7ebd0c 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -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"),
     }
 }
 
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index dab6d41df30..72fe9a7711d 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -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)
     })
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index e7ab9a73c0f..7b8ad7d7af6 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -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
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index f0973e0ba6e..3b8ee91374c 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -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()
     }
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 649052d123c..b32ed15b50f 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -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)
+        }
     }
 }
 
diff --git a/src/test/compile-fail/type-macros-fail.rs b/src/test/compile-fail/type-macros-fail.rs
new file mode 100644
index 00000000000..f854e540ee8
--- /dev/null
+++ b/src/test/compile-fail/type-macros-fail.rs
@@ -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 };
+}
diff --git a/src/test/parse-fail/better-expected.rs b/src/test/parse-fail/better-expected.rs
index 3f0e0d36d8f..b60201c251d 100644
--- a/src/test/parse-fail/better-expected.rs
+++ b/src/test/parse-fail/better-expected.rs
@@ -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`
 }
diff --git a/src/test/parse-fail/empty-impl-semicolon.rs b/src/test/parse-fail/empty-impl-semicolon.rs
index d9f8add8cfb..9939f1e36ea 100644
--- a/src/test/parse-fail/empty-impl-semicolon.rs
+++ b/src/test/parse-fail/empty-impl-semicolon.rs
@@ -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 `;`
diff --git a/src/test/parse-fail/multitrait.rs b/src/test/parse-fail/multitrait.rs
index 2a8d6d99957..b7c9b165884 100644
--- a/src/test/parse-fail/multitrait.rs
+++ b/src/test/parse-fail/multitrait.rs
@@ -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() }
 }
diff --git a/src/test/parse-fail/removed-syntax-closure-lifetime.rs b/src/test/parse-fail/removed-syntax-closure-lifetime.rs
index 04d3757dcda..b305b1894a8 100644
--- a/src/test/parse-fail/removed-syntax-closure-lifetime.rs
+++ b/src/test/parse-fail/removed-syntax-closure-lifetime.rs
@@ -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 `/`
diff --git a/src/test/parse-fail/removed-syntax-fixed-vec.rs b/src/test/parse-fail/removed-syntax-fixed-vec.rs
index ae61d1ea858..0f34db08852 100644
--- a/src/test/parse-fail/removed-syntax-fixed-vec.rs
+++ b/src/test/parse-fail/removed-syntax-fixed-vec.rs
@@ -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 `*`
diff --git a/src/test/parse-fail/removed-syntax-mut-vec-ty.rs b/src/test/parse-fail/removed-syntax-mut-vec-ty.rs
index 00ea593656a..91918f01bb0 100644
--- a/src/test/parse-fail/removed-syntax-mut-vec-ty.rs
+++ b/src/test/parse-fail/removed-syntax-mut-vec-ty.rs
@@ -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`
diff --git a/src/test/parse-fail/removed-syntax-ptr-lifetime.rs b/src/test/parse-fail/removed-syntax-ptr-lifetime.rs
index e586753087a..ebef0e56e3e 100644
--- a/src/test/parse-fail/removed-syntax-ptr-lifetime.rs
+++ b/src/test/parse-fail/removed-syntax-ptr-lifetime.rs
@@ -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 `/`
diff --git a/src/test/parse-fail/removed-syntax-uniq-mut-ty.rs b/src/test/parse-fail/removed-syntax-uniq-mut-ty.rs
index 77e64e7c997..e1637901266 100644
--- a/src/test/parse-fail/removed-syntax-uniq-mut-ty.rs
+++ b/src/test/parse-fail/removed-syntax-uniq-mut-ty.rs
@@ -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`
diff --git a/src/test/run-pass/type-macros-hlist.rs b/src/test/run-pass/type-macros-hlist.rs
new file mode 100644
index 00000000000..803b0eae99e
--- /dev/null
+++ b/src/test/run-pass/type-macros-hlist.rs
@@ -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 ] , (  ) ])
+}
diff --git a/src/test/run-pass/type-macros-simple.rs b/src/test/run-pass/type-macros-simple.rs
new file mode 100644
index 00000000000..22dfd507f7e
--- /dev/null
+++ b/src/test/run-pass/type-macros-simple.rs
@@ -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);
+}