Skip to content

Commit 11deb08

Browse files
committed
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.
2 parents fb92de7 + 83e43bb commit 11deb08

22 files changed

+250
-10
lines changed

src/librustc_typeck/astconv.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1662,6 +1662,9 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
16621662
// handled specially and will not descend into this routine.
16631663
this.ty_infer(None, None, None, ast_ty.span)
16641664
}
1665+
ast::TyMac(_) => {
1666+
tcx.sess.span_bug(ast_ty.span, "unexpanded type macro found conversion")
1667+
}
16651668
};
16661669

16671670
tcx.ast_ty_to_ty_cache.borrow_mut().insert(ast_ty.id, typ);

src/librustdoc/clean/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1611,6 +1611,9 @@ impl Clean<Type> for ast::Ty {
16111611
TyTypeof(..) => {
16121612
panic!("Unimplemented type {:?}", self.node)
16131613
},
1614+
TyMac(ref m) => {
1615+
cx.tcx().sess.span_bug(m.span, "unexpanded type macro found during cleaning")
1616+
}
16141617
}
16151618
}
16161619
}

src/libsyntax/ast.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1471,6 +1471,8 @@ pub enum Ty_ {
14711471
/// TyInfer means the type should be inferred instead of it having been
14721472
/// specified. This can appear anywhere in a type.
14731473
TyInfer,
1474+
// A macro in the type position.
1475+
TyMac(Mac)
14741476
}
14751477

14761478
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]

src/libsyntax/ext/base.rs

+20
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,10 @@ pub trait MacResult {
290290
fn make_stmts(self: Box<Self>) -> Option<SmallVector<P<ast::Stmt>>> {
291291
make_stmts_default!(self)
292292
}
293+
294+
fn make_ty(self: Box<Self>) -> Option<P<ast::Ty>> {
295+
None
296+
}
293297
}
294298

295299
macro_rules! make_MacEager {
@@ -322,6 +326,7 @@ make_MacEager! {
322326
items: SmallVector<P<ast::Item>>,
323327
impl_items: SmallVector<P<ast::ImplItem>>,
324328
stmts: SmallVector<P<ast::Stmt>>,
329+
ty: P<ast::Ty>,
325330
}
326331

327332
impl MacResult for MacEager {
@@ -359,6 +364,10 @@ impl MacResult for MacEager {
359364
}
360365
None
361366
}
367+
368+
fn make_ty(self: Box<Self>) -> Option<P<ast::Ty>> {
369+
self.ty
370+
}
362371
}
363372

364373
/// Fill-in macro expansion result, to allow compilation to continue
@@ -405,15 +414,24 @@ impl DummyResult {
405414
}
406415
}
407416

417+
pub fn raw_ty(sp: Span) -> P<ast::Ty> {
418+
P(ast::Ty {
419+
id: ast::DUMMY_NODE_ID,
420+
node: ast::TyInfer,
421+
span: sp
422+
})
423+
}
408424
}
409425

410426
impl MacResult for DummyResult {
411427
fn make_expr(self: Box<DummyResult>) -> Option<P<ast::Expr>> {
412428
Some(DummyResult::raw_expr(self.span))
413429
}
430+
414431
fn make_pat(self: Box<DummyResult>) -> Option<P<ast::Pat>> {
415432
Some(P(DummyResult::raw_pat(self.span)))
416433
}
434+
417435
fn make_items(self: Box<DummyResult>) -> Option<SmallVector<P<ast::Item>>> {
418436
// this code needs a comment... why not always just return the Some() ?
419437
if self.expr_only {
@@ -422,13 +440,15 @@ impl MacResult for DummyResult {
422440
Some(SmallVector::zero())
423441
}
424442
}
443+
425444
fn make_impl_items(self: Box<DummyResult>) -> Option<SmallVector<P<ast::ImplItem>>> {
426445
if self.expr_only {
427446
None
428447
} else {
429448
Some(SmallVector::zero())
430449
}
431450
}
451+
432452
fn make_stmts(self: Box<DummyResult>) -> Option<SmallVector<P<ast::Stmt>>> {
433453
Some(SmallVector::one(P(
434454
codemap::respan(self.span,

src/libsyntax/ext/expand.rs

+47
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,45 @@ fn expand_and_rename_method(sig: ast::MethodSig, body: P<ast::Block>,
15421542
}, rewritten_body)
15431543
}
15441544

1545+
pub fn expand_type(t: P<ast::Ty>, fld: &mut MacroExpander) -> P<ast::Ty> {
1546+
let t = match t.node.clone() {
1547+
ast::Ty_::TyMac(mac) => {
1548+
if fld.cx.ecfg.features.unwrap().type_macros {
1549+
let expanded_ty = match expand_mac_invoc(mac, t.span,
1550+
|r| r.make_ty(),
1551+
mark_ty,
1552+
fld) {
1553+
Some(ty) => ty,
1554+
None => {
1555+
return DummyResult::raw_ty(t.span);
1556+
}
1557+
};
1558+
1559+
// Keep going, outside-in.
1560+
let fully_expanded = fld.fold_ty(expanded_ty);
1561+
fld.cx.bt_pop();
1562+
1563+
fully_expanded.map(|t| ast::Ty {
1564+
id: ast::DUMMY_NODE_ID,
1565+
node: t.node,
1566+
span: t.span,
1567+
})
1568+
} else {
1569+
feature_gate::emit_feature_err(
1570+
&fld.cx.parse_sess.span_diagnostic,
1571+
"type_macros",
1572+
t.span,
1573+
"type macros are experimental (see issue: #27336)");
1574+
1575+
DummyResult::raw_ty(t.span)
1576+
}
1577+
}
1578+
_ => t
1579+
};
1580+
1581+
fold::noop_fold_ty(t, fld)
1582+
}
1583+
15451584
/// A tree-folder that performs macro expansion
15461585
pub struct MacroExpander<'a, 'b:'a> {
15471586
pub cx: &'a mut ExtCtxt<'b>,
@@ -1592,6 +1631,10 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
15921631
.into_iter().map(|i| i.expect_impl_item()).collect()
15931632
}
15941633

1634+
fn fold_ty(&mut self, ty: P<ast::Ty>) -> P<ast::Ty> {
1635+
expand_type(ty, self)
1636+
}
1637+
15951638
fn new_span(&mut self, span: Span) -> Span {
15961639
new_span(self.cx, span)
15971640
}
@@ -1744,6 +1787,10 @@ fn mark_impl_item(ii: P<ast::ImplItem>, m: Mrk) -> P<ast::ImplItem> {
17441787
.expect_one("marking an impl item didn't return exactly one impl item")
17451788
}
17461789

1790+
fn mark_ty(ty: P<ast::Ty>, m: Mrk) -> P<ast::Ty> {
1791+
Marker { mark: m }.fold_ty(ty)
1792+
}
1793+
17471794
/// Check that there are no macro invocations left in the AST:
17481795
pub fn check_for_macros(sess: &parse::ParseSess, krate: &ast::Crate) {
17491796
visit::walk_crate(&mut MacroExterminator{sess:sess}, krate);

src/libsyntax/ext/tt/macro_rules.rs

+6
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
117117
self.ensure_complete_parse(false);
118118
Some(ret)
119119
}
120+
121+
fn make_ty(self: Box<ParserAnyMacro<'a>>) -> Option<P<ast::Ty>> {
122+
let ret = self.parser.borrow_mut().parse_ty();
123+
self.ensure_complete_parse(true);
124+
Some(ret)
125+
}
120126
}
121127

122128
struct MacroRulesMacroExpander {

src/libsyntax/feature_gate.rs

+6
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
172172

173173
// Allows associated type defaults
174174
("associated_type_defaults", "1.2.0", Active),
175+
// Allows macros to appear in the type position.
176+
177+
("type_macros", "1.3.0", Active),
175178
];
176179
// (changing above list without updating src/doc/reference.md makes @cmr sad)
177180

@@ -354,6 +357,7 @@ pub struct Features {
354357
pub const_fn: bool,
355358
pub static_recursion: bool,
356359
pub default_type_parameter_fallback: bool,
360+
pub type_macros: bool,
357361
}
358362

359363
impl Features {
@@ -380,6 +384,7 @@ impl Features {
380384
const_fn: false,
381385
static_recursion: false,
382386
default_type_parameter_fallback: false,
387+
type_macros: false,
383388
}
384389
}
385390
}
@@ -883,6 +888,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
883888
const_fn: cx.has_feature("const_fn"),
884889
static_recursion: cx.has_feature("static_recursion"),
885890
default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"),
891+
type_macros: cx.has_feature("type_macros"),
886892
}
887893
}
888894

src/libsyntax/fold.rs

+3
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,9 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
429429
TyPolyTraitRef(bounds) => {
430430
TyPolyTraitRef(bounds.move_map(|b| fld.fold_ty_param_bound(b)))
431431
}
432+
TyMac(mac) => {
433+
TyMac(fld.fold_mac(mac))
434+
}
432435
},
433436
span: fld.new_span(span)
434437
})

src/libsyntax/parse/parser.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ use ast::{SelfExplicit, SelfRegion, SelfStatic, SelfValue};
5151
use ast::{Delimited, SequenceRepetition, TokenTree, TraitItem, TraitRef};
5252
use ast::{TtDelimited, TtSequence, TtToken};
5353
use ast::{TupleVariantKind, Ty, Ty_, TypeBinding};
54+
use ast::{TyMac};
5455
use ast::{TyFixedLengthVec, TyBareFn, TyTypeof, TyInfer};
5556
use ast::{TyParam, TyParamBound, TyParen, TyPath, TyPolyTraitRef, TyPtr};
5657
use ast::{TyRptr, TyTup, TyU32, TyVec, UnUniq};
@@ -1369,8 +1370,20 @@ impl<'a> Parser<'a> {
13691370
} else if self.check(&token::ModSep) ||
13701371
self.token.is_ident() ||
13711372
self.token.is_path() {
1372-
// NAMED TYPE
1373-
try!(self.parse_ty_path())
1373+
let path = try!(self.parse_path(LifetimeAndTypesWithoutColons));
1374+
if self.check(&token::Not) {
1375+
// MACRO INVOCATION
1376+
try!(self.bump());
1377+
let delim = try!(self.expect_open_delim());
1378+
let tts = try!(self.parse_seq_to_end(&token::CloseDelim(delim),
1379+
seq_sep_none(),
1380+
|p| p.parse_token_tree()));
1381+
let hi = self.span.hi;
1382+
TyMac(spanned(lo, hi, MacInvocTT(path, tts, EMPTY_CTXT)))
1383+
} else {
1384+
// NAMED TYPE
1385+
TyPath(None, path)
1386+
}
13741387
} else if try!(self.eat(&token::Underscore) ){
13751388
// TYPE TO BE INFERRED
13761389
TyInfer

src/libsyntax/print/pprust.rs

+3
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,9 @@ impl<'a> State<'a> {
734734
ast::TyInfer => {
735735
try!(word(&mut self.s, "_"));
736736
}
737+
ast::TyMac(ref m) => {
738+
try!(self.print_mac(m, token::Paren));
739+
}
737740
}
738741
self.end()
739742
}

src/libsyntax/visit.rs

+3
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
405405
visitor.visit_expr(&**expression)
406406
}
407407
TyInfer => {}
408+
TyMac(ref mac) => {
409+
visitor.visit_mac(mac)
410+
}
408411
}
409412
}
410413

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
macro_rules! Id {
12+
{ $T:tt } => $T
13+
}
14+
15+
struct Foo<T> {
16+
x: Id!(T)
17+
//~^ ERROR: type macros are experimental (see issue: #27336)
18+
}
19+
20+
fn main() {
21+
let foo = Foo { x: i32 };
22+
}

src/test/parse-fail/better-expected.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@
1111
// compile-flags: -Z parse-only
1212

1313
fn main() {
14-
let x: [isize 3]; //~ ERROR expected one of `(`, `+`, `::`, `;`, `<`, or `]`, found `3`
14+
let x: [isize 3]; //~ ERROR expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `]`, found `3`
1515
}

src/test/parse-fail/empty-impl-semicolon.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010

1111
// compile-flags: -Z parse-only
1212

13-
impl Foo; //~ ERROR expected one of `(`, `+`, `::`, `<`, `for`, `where`, or `{`, found `;`
13+
impl Foo; //~ ERROR expected one of `!`, `(`, `+`, `::`, `<`, `for`, `where`, or `{`, found `;`

src/test/parse-fail/multitrait.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ struct S {
1515
}
1616

1717
impl Cmp, ToString for S {
18-
//~^ ERROR: expected one of `(`, `+`, `::`, `<`, `for`, `where`, or `{`, found `,`
18+
//~^ ERROR: expected one of `!`, `(`, `+`, `::`, `<`, `for`, `where`, or `{`, found `,`
1919
fn eq(&&other: S) { false }
2020
fn to_string(&self) -> String { "hi".to_string() }
2121
}

src/test/parse-fail/removed-syntax-closure-lifetime.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@
1010

1111
// compile-flags: -Z parse-only
1212

13-
type closure = Box<lt/fn()>; //~ ERROR expected one of `(`, `+`, `,`, `::`, `<`, or `>`, found `/`
13+
type closure = Box<lt/fn()>;
14+
//~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `/`

src/test/parse-fail/removed-syntax-fixed-vec.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010

1111
// compile-flags: -Z parse-only
1212

13-
type v = [isize * 3]; //~ ERROR expected one of `(`, `+`, `::`, `;`, `<`, or `]`, found `*`
13+
type v = [isize * 3]; //~ ERROR expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `]`, found `*`

src/test/parse-fail/removed-syntax-mut-vec-ty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212

1313
type v = [mut isize];
1414
//~^ ERROR expected identifier, found keyword `mut`
15-
//~^^ ERROR expected one of `(`, `+`, `::`, `;`, `<`, or `]`, found `isize`
15+
//~^^ ERROR expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `]`, found `isize`

src/test/parse-fail/removed-syntax-ptr-lifetime.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010

1111
// compile-flags: -Z parse-only
1212

13-
type bptr = &lifetime/isize; //~ ERROR expected one of `(`, `+`, `::`, `;`, or `<`, found `/`
13+
type bptr = &lifetime/isize; //~ ERROR expected one of `!`, `(`, `+`, `::`, `;`, or `<`, found `/`

src/test/parse-fail/removed-syntax-uniq-mut-ty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212

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

0 commit comments

Comments
 (0)