From ad5927870cd32c5feb7f42dc4bca4a1b21e61a0e Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Sat, 25 Jul 2015 21:30:35 -0700 Subject: [PATCH 01/11] Add a macro invocation to the type AST Reapplied the changes from https://github.com/freebroccolo/rust/commit/dc64b731d7f66c2b43d5e5e8c721be7bd3b59540 to a clean branch of master --- src/librustc_typeck/astconv.rs | 3 +++ src/librustdoc/clean/mod.rs | 3 +++ src/libsyntax/ast.rs | 2 ++ src/libsyntax/fold.rs | 3 +++ src/libsyntax/print/pprust.rs | 3 +++ src/libsyntax/visit.rs | 3 +++ 6 files changed, 17 insertions(+) diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index a2c968a290b8a..cfd681e51f4e1 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(m.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 11a0e0eaa496e..a79a571f0c7a4 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1611,6 +1611,9 @@ impl Clean for ast::Ty { TyTypeof(..) => { panic!("Unimplemented type {:?}", self.node) }, + TyMac(..) => { + 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 db173d0830815..eefd3da9f4af3 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/fold.rs b/src/libsyntax/fold.rs index dab6d41df3003..72fe9a7711d99 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -429,6 +429,9 @@ pub fn noop_fold_ty(t: P, fld: &mut T) -> P { 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/print/pprust.rs b/src/libsyntax/print/pprust.rs index 6cfe85bc37e81..17fa0922da9ef 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 649052d123c88..b32ed15b50f9d 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) + } } } From edca562c87362c80e409f53d28e19617ca44646a Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Sat, 25 Jul 2015 21:40:57 -0700 Subject: [PATCH 02/11] Make parser recognize macro invocations in types Reapplied the changes from https://github.com/freebroccolo/rust/commit/8b07abaa6e8ab42d37656dfad89de0eb5810c3b3 to a clean branch of master --- src/libsyntax/parse/parser.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index e7ab9a73c0ffd..7b8ad7d7af683 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 From 9fb11fe9f21051f4f03da55f949de402e78a95d5 Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Sat, 25 Jul 2015 21:54:19 -0700 Subject: [PATCH 03/11] Extend macro machinery to expand macros in types Reapplied the changes from https://github.com/freebroccolo/rust/commit/7aafe24139abc2d1f302bbb166bcaa006f12cf4d to a clean branch of master --- src/libsyntax/ext/base.rs | 15 ++++++++++++ src/libsyntax/ext/expand.rs | 37 +++++++++++++++++++++++++++++ src/libsyntax/ext/tt/macro_rules.rs | 6 +++++ 3 files changed, 58 insertions(+) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 409ae86db35d4..28c7ead20bcae 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) -> Option>> { make_stmts_default!(self) } + + fn make_ty(self: Box) -> Option> { + None + } } macro_rules! make_MacEager { @@ -322,6 +326,7 @@ make_MacEager! { items: SmallVector>, impl_items: SmallVector>, stmts: SmallVector>, + ty: P, } impl MacResult for MacEager { @@ -359,6 +364,10 @@ impl MacResult for MacEager { } None } + + fn make_ty(self: Box) -> Option> { + self.ty + } } /// Fill-in macro expansion result, to allow compilation to continue @@ -405,6 +414,12 @@ impl DummyResult { } } + pub fn raw_ty(sp: Span) -> P { + P(ast::Ty { + id: ast:DUMMY_NODE_ID, + node: ast::TyInfer, + span: sp + }) } impl MacResult for DummyResult { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 6e49b190f7c21..cd340fc91891f 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1552,6 +1552,35 @@ fn expand_and_rename_method(sig: ast::MethodSig, body: P, }, rewritten_body) } +pub fn expand_type(t: P, fld: &mut MacroExpander) -> P { + let t = match t.node.clone() { + ast::Ty_::TyMac(mac) => { + 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, + }) + } + _ => 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>, @@ -1602,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) -> P { + expand_type(ty, self) + } + fn new_span(&mut self, span: Span) -> Span { new_span(self.cx, span) } @@ -1748,6 +1781,10 @@ fn mark_impl_item(ii: P, m: Mrk) -> P { .expect_one("marking an impl item didn't return exactly one impl item") } +fn mark_ty(ty: P, m: Mrk) -> P { + 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 adc88c329a317..d16fde7bc394e 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>) -> Option> { + let ret = self.parser.borrow_mut().parse_ty(); + self.ensure_complete_parse(true); + Some(ret) + } } struct MacroRulesMacroExpander { From 8602a7d89843dcd7d41d2bb569817ddb760dbf7e Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Sat, 25 Jul 2015 22:17:43 -0700 Subject: [PATCH 04/11] Update and add test case Test case from here: https://github.com/freebroccolo/rust/commit/9e93fef3c0e61836a8b56f727eb7a2e94bb4ca09 --- src/librustc_typeck/astconv.rs | 2 +- src/libsyntax/ext/base.rs | 7 ++- src/test/run-pass/type-macros.rs | 82 ++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 src/test/run-pass/type-macros.rs diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index cfd681e51f4e1..99f375c32868e 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -1663,7 +1663,7 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>, this.ty_infer(None, None, None, ast_ty.span) } ast::TyMac(_) => { - tcx.sess.span_bug(m.span, "unexpanded type macro found conversion") + tcx.sess.span_bug(ast_ty.span, "unexpanded type macro found conversion") } }; diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 28c7ead20bcae..1b5dcf6483346 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -416,19 +416,22 @@ impl DummyResult { pub fn raw_ty(sp: Span) -> P { P(ast::Ty { - id: ast:DUMMY_NODE_ID, + id: ast::DUMMY_NODE_ID, node: ast::TyInfer, span: sp }) + } } impl MacResult for DummyResult { fn make_expr(self: Box) -> Option> { Some(DummyResult::raw_expr(self.span)) } + fn make_pat(self: Box) -> Option> { Some(P(DummyResult::raw_pat(self.span))) } + fn make_items(self: Box) -> Option>> { // this code needs a comment... why not always just return the Some() ? if self.expr_only { @@ -437,6 +440,7 @@ impl MacResult for DummyResult { Some(SmallVector::zero()) } } + fn make_impl_items(self: Box) -> Option>> { if self.expr_only { None @@ -444,6 +448,7 @@ impl MacResult for DummyResult { Some(SmallVector::zero()) } } + fn make_stmts(self: Box) -> Option>> { Some(SmallVector::one(P( codemap::respan(self.span, diff --git a/src/test/run-pass/type-macros.rs b/src/test/run-pass/type-macros.rs new file mode 100644 index 0000000000000..3ca3be8efed4b --- /dev/null +++ b/src/test/run-pass/type-macros.rs @@ -0,0 +1,82 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +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); // cons cell of HList + +// trait to classify valid HLists +trait HList {} +impl HList for Nil {} +impl HList for Cons {} + +// 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 Add for Nil { + type Output = Ys; + + fn add(self, rhs: Ys) -> Ys { + rhs + } +} + +// cons case for HList append +impl Add for Cons where + Xs: Add, +{ + type Output = Cons; + + fn add(self, rhs: Ys) -> Cons { + 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)+ } => { >::Output }; + { $LHS:tt + $($RHS:tt)+ } => { >::Output }; + { $LHS:ty } => { $LHS }; +} + +// test demonstrating term level `xs + ys` and type level `Expr!(Xs + Ys)` +fn main() { + fn aux(xs: Xs, ys: Ys) -> Expr!(Xs + Ys) + where Xs: Add { + xs + ys + } + + let xs: HList![&str, bool, Vec] = 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]) + + (HList![u64] + HList![[u8; 3], ()]) + + HList![]) + = aux(xs, ys); + assert_eq!(zs, hlist!["foo", false, vec![], 0, [0, 1, 2], ()]) +} + From 0bb5a1a8249e3c5b97b4e60a2ab37cc5a2300306 Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Sat, 25 Jul 2015 23:53:23 -0700 Subject: [PATCH 05/11] Tidy test case --- src/test/run-pass/type-macros.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/run-pass/type-macros.rs b/src/test/run-pass/type-macros.rs index 3ca3be8efed4b..a5eb37b8cc3c6 100644 --- a/src/test/run-pass/type-macros.rs +++ b/src/test/run-pass/type-macros.rs @@ -57,9 +57,11 @@ impl Add for Cons where // type macro Expr allows us to expand the + operator appropriately macro_rules! Expr { { ( $($LHS:tt)+ ) } => { Expr!($($LHS)+) }; - { HList ! [ $($LHS:tt)* ] + $($RHS:tt)+ } => { >::Output }; - { $LHS:tt + $($RHS:tt)+ } => { >::Output }; - { $LHS:ty } => { $LHS }; + { HList ! [ $($LHS:tt)* ] + $($RHS:tt)+ } => { + >::Output + }; + { $LHS:tt + $($RHS:tt)+ } => { >::Output }; + { $LHS:ty } => { $LHS }; } // test demonstrating term level `xs + ys` and type level `Expr!(Xs + Ys)` From 15e7aa79f5280047a34e47628fb50c3eb3c898ad Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Tue, 28 Jul 2015 09:00:32 -0700 Subject: [PATCH 06/11] Add feature gate --- src/librustdoc/clean/mod.rs | 2 +- src/libsyntax/ext/expand.rs | 48 +++++++++++++++++++++-------------- src/libsyntax/feature_gate.rs | 8 ++++-- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index a79a571f0c7a4..64575e6856993 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1611,7 +1611,7 @@ impl Clean for ast::Ty { TyTypeof(..) => { panic!("Unimplemented type {:?}", self.node) }, - TyMac(..) => { + TyMac(ref m) => { cx.tcx().sess.span_bug(m.span, "unexpanded type macro found during cleaning") } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index cd340fc91891f..aadc3cfbafe00 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1555,29 +1555,39 @@ fn expand_and_rename_method(sig: ast::MethodSig, body: P, pub fn expand_type(t: P, fld: &mut MacroExpander) -> P { let t = match t.node.clone() { ast::Ty_::TyMac(mac) => { - 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(); + 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); + } + }; - fully_expanded.map(|t| ast::Ty { - id: ast::DUMMY_NODE_ID, - node: t.node, - span: 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 tracking issue: 27336)"); + t + } } _ => t }; + fold::noop_fold_ty(t, fld) } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 945e457a77b73..02764215612e6 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -169,6 +169,8 @@ 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) @@ -228,8 +230,7 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[ "no_std is experimental")), ("lang", Gated("lang_items", "language items are subject to change")), - ("linkage", Gated("linkage", - "the `linkage` attribute is experimental \ + ("linkage", Gated("linkage", "the `linkage` attribute is experimental \ and not portable across platforms")), ("thread_local", Gated("thread_local", "`#[thread_local]` is an experimental feature, and does not \ @@ -349,6 +350,7 @@ pub struct Features { pub const_fn: bool, pub static_recursion: bool, pub default_type_parameter_fallback: bool, + pub type_macros: bool, } impl Features { @@ -375,6 +377,7 @@ impl Features { const_fn: false, static_recursion: false, default_type_parameter_fallback: false, + type_macros: false, } } } @@ -878,6 +881,7 @@ fn check_crate_inner(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"), } } From 89d401f6aba9166246e329b1aa18bd5f32fbed7e Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Sat, 1 Aug 2015 14:35:46 -0700 Subject: [PATCH 07/11] Address nits --- src/libsyntax/ext/expand.rs | 1 - src/test/run-pass/type-macros.rs | 84 -------------------------------- 2 files changed, 85 deletions(-) delete mode 100644 src/test/run-pass/type-macros.rs diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index aadc3cfbafe00..b80551c997275 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1567,7 +1567,6 @@ pub fn expand_type(t: P, fld: &mut MacroExpander) -> P { }; // Keep going, outside-in. - // let fully_expanded = fld.fold_ty(expanded_ty); fld.cx.bt_pop(); diff --git a/src/test/run-pass/type-macros.rs b/src/test/run-pass/type-macros.rs deleted file mode 100644 index a5eb37b8cc3c6..0000000000000 --- a/src/test/run-pass/type-macros.rs +++ /dev/null @@ -1,84 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -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); // cons cell of HList - -// trait to classify valid HLists -trait HList {} -impl HList for Nil {} -impl HList for Cons {} - -// 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 Add for Nil { - type Output = Ys; - - fn add(self, rhs: Ys) -> Ys { - rhs - } -} - -// cons case for HList append -impl Add for Cons where - Xs: Add, -{ - type Output = Cons; - - fn add(self, rhs: Ys) -> Cons { - 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)+ } => { - >::Output - }; - { $LHS:tt + $($RHS:tt)+ } => { >::Output }; - { $LHS:ty } => { $LHS }; -} - -// test demonstrating term level `xs + ys` and type level `Expr!(Xs + Ys)` -fn main() { - fn aux(xs: Xs, ys: Ys) -> Expr!(Xs + Ys) - where Xs: Add { - xs + ys - } - - let xs: HList![&str, bool, Vec] = 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]) + - (HList![u64] + HList![[u8; 3], ()]) + - HList![]) - = aux(xs, ys); - assert_eq!(zs, hlist!["foo", false, vec![], 0, [0, 1, 2], ()]) -} - From f004079de22fa32cc3342bc770f415b9d395beb6 Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Mon, 3 Aug 2015 13:51:23 -0700 Subject: [PATCH 08/11] Actually commit tests --- src/libsyntax/ext/expand.rs | 2 +- src/test/compile-fail/type-macros-fail.rs | 21 ++++++ src/test/run-pass/type-macros-hlist.rs | 86 +++++++++++++++++++++++ src/test/run-pass/type-macros-simple.rs | 19 +++++ 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/test/compile-fail/type-macros-fail.rs create mode 100644 src/test/run-pass/type-macros-hlist.rs create mode 100644 src/test/run-pass/type-macros-simple.rs diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index b80551c997275..517fd9421ec69 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1580,7 +1580,7 @@ pub fn expand_type(t: P, fld: &mut MacroExpander) -> P { &fld.cx.parse_sess.span_diagnostic, "type_macros", t.span, - "type macros are experimental (see tracking issue: 27336)"); + "type macros are experimental (see issue: #27336)"); t } } 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 0000000000000..8e8f21519bc56 --- /dev/null +++ b/src/test/compile-fail/type-macros-fail.rs @@ -0,0 +1,21 @@ +// 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 or the MIT license +// , 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 { + x: Id!(T) +} + +fn main() { + let foo = Foo { x: i32 }; +} 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 0000000000000..7c7392894d974 --- /dev/null +++ b/src/test/run-pass/type-macros-hlist.rs @@ -0,0 +1,86 @@ +// 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 or the MIT license +// , 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); // cons cell of HList + +// trait to classify valid HLists +trait HList {} +impl HList for Nil {} +impl HList for Cons {} + +// 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 Add for Nil { + type Output = Ys; + + fn add(self, rhs: Ys) -> Ys { + rhs + } +} + +// cons case for HList append +impl Add for Cons where + Xs: Add, +{ + type Output = Cons; + + fn add(self, rhs: Ys) -> Cons { + 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)+ } => { + >::Output + }; + { $LHS:tt + $($RHS:tt)+ } => { >::Output }; + { $LHS:ty } => { $LHS }; +} + +// test demonstrating term level `xs + ys` and type level `Expr!(Xs + Ys)` +fn main() { + fn aux(xs: Xs, ys: Ys) -> Expr!(Xs + Ys) + where Xs: Add { + xs + ys + } + + let xs: HList![&str, bool, Vec] = 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]) + + (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 0000000000000..22dfd507f7e2e --- /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 or the MIT license +// , 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); +} From 2919e3268674f832883631a0476a2379768d55e8 Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Mon, 3 Aug 2015 20:32:02 -0700 Subject: [PATCH 09/11] Address final nits --- src/libsyntax/feature_gate.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 02764215612e6..5c6f8cee6d66c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -169,7 +169,8 @@ 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. + // 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) @@ -230,7 +231,8 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[ "no_std is experimental")), ("lang", Gated("lang_items", "language items are subject to change")), - ("linkage", Gated("linkage", "the `linkage` attribute is experimental \ + ("linkage", Gated("linkage", + "the `linkage` attribute is experimental \ and not portable across platforms")), ("thread_local", Gated("thread_local", "`#[thread_local]` is an experimental feature, and does not \ From 471370a16b05d15ae769e96b90d1a306a469baca Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Tue, 4 Aug 2015 16:04:26 -0700 Subject: [PATCH 10/11] Fix last nits --- src/libsyntax/ext/expand.rs | 3 +- src/test/compile-fail/type-macros-fail.rs | 1 + src/test/run-pass/type-macros-hlist.rs | 99 ++++++++++++----------- 3 files changed, 54 insertions(+), 49 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 517fd9421ec69..d23f96bd36f23 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1581,7 +1581,8 @@ pub fn expand_type(t: P, fld: &mut MacroExpander) -> P { "type_macros", t.span, "type macros are experimental (see issue: #27336)"); - t + + DummyResult::raw_ty(t.span) } } _ => t diff --git a/src/test/compile-fail/type-macros-fail.rs b/src/test/compile-fail/type-macros-fail.rs index 8e8f21519bc56..f854e540ee83d 100644 --- a/src/test/compile-fail/type-macros-fail.rs +++ b/src/test/compile-fail/type-macros-fail.rs @@ -14,6 +14,7 @@ macro_rules! Id { struct Foo { x: Id!(T) + //~^ ERROR: type macros are experimental (see issue: #27336) } fn main() { diff --git a/src/test/run-pass/type-macros-hlist.rs b/src/test/run-pass/type-macros-hlist.rs index 7c7392894d974..803b0eae99e88 100644 --- a/src/test/run-pass/type-macros-hlist.rs +++ b/src/test/run-pass/type-macros-hlist.rs @@ -13,74 +13,77 @@ use std::ops::*; #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -struct Nil; // empty HList +struct Nil; + // empty HList #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -struct Cons(H, T); // cons cell of HList +struct Cons(H, T); + // cons cell of HList -// trait to classify valid HLists -trait HList {} -impl HList for Nil {} -impl HList for Cons {} + // trait to classify valid HLists +trait HList { } +impl HList for Nil { } +impl HList for Cons { } // term-level macro for HLists -macro_rules! hlist { - {} => { Nil }; - { $head:expr } => { Cons($head, Nil) }; - { $head:expr, $($tail:expr),* } => { Cons($head, hlist!($($tail),*)) }; -} +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),*)> }; -} +macro_rules! HList({ } => { Nil } ; { $ head : ty } => { + Cons < $ head , Nil > } ; { + $ head : ty , $ ( $ tail : ty ) , * } => { + Cons < $ head , HList ! ( $ ( $ tail ) , * ) > } ;); // nil case for HList append -impl Add for Nil { - type Output = Ys; +impl Add for Nil { + type + Output + = + Ys; - fn add(self, rhs: Ys) -> Ys { - rhs - } + fn add(self, rhs: Ys) -> Ys { rhs } } // cons case for HList append -impl Add for Cons where - Xs: Add, -{ - type Output = Cons; +impl Add for Cons + where Xs: Add { + type + Output + = + Cons; - fn add(self, rhs: Ys) -> Cons { - Cons(self.0, self.1 + rhs) - } + fn add(self, rhs: Ys) -> Cons { 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)+ } => { - >::Output - }; - { $LHS:tt + $($RHS:tt)+ } => { >::Output }; - { $LHS:ty } => { $LHS }; -} +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: Xs, ys: Ys) -> Expr!(Xs + Ys) - where Xs: Add { - xs + ys - } + fn aux(xs: Xs, ys: Ys) -> Expr!(Xs + Ys) where + Xs: Add { + xs + ys + } - let xs: HList![&str, bool, Vec] = hlist!["foo", false, vec![]]; - let ys: HList![u64, [u8; 3], ()] = hlist![0, [0, 1, 2], ()]; + 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]) + - (HList![u64] + HList![[u8; 3], ()]) + - HList![]) - = aux(xs, ys); - assert_eq!(zs, hlist!["foo", false, vec![], 0, [0, 1, 2], ()]) + 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 ] , ( ) ]) } - From 83e43bb728b95d52039824d63b1ba5bbde5c5d7b Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Wed, 5 Aug 2015 14:18:29 -0700 Subject: [PATCH 11/11] Fix expected parse error --- src/test/parse-fail/better-expected.rs | 2 +- src/test/parse-fail/empty-impl-semicolon.rs | 2 +- src/test/parse-fail/multitrait.rs | 2 +- src/test/parse-fail/removed-syntax-closure-lifetime.rs | 3 ++- src/test/parse-fail/removed-syntax-fixed-vec.rs | 2 +- src/test/parse-fail/removed-syntax-mut-vec-ty.rs | 2 +- src/test/parse-fail/removed-syntax-ptr-lifetime.rs | 2 +- src/test/parse-fail/removed-syntax-uniq-mut-ty.rs | 2 +- 8 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/test/parse-fail/better-expected.rs b/src/test/parse-fail/better-expected.rs index 3f0e0d36d8f38..b60201c251dcc 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 d9f8add8cfbea..9939f1e36ea9d 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 2a8d6d9995730..b7c9b16588466 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 04d3757dcda70..b305b1894a810 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; //~ ERROR expected one of `(`, `+`, `,`, `::`, `<`, or `>`, found `/` +type closure = Box; +//~^ 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 ae61d1ea85861..0f34db0885202 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 00ea593656abb..91918f01bb03e 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 e586753087ada..ebef0e56e3e2b 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 77e64e7c997ea..e1637901266e0 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; //~^ ERROR expected identifier, found keyword `mut` - //~^^ ERROR expected one of `(`, `+`, `,`, `::`, `<`, or `>`, found `isize` + //~^^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `isize`