From ad0bef1e6819da41867cf8ea17ddaef10da1e541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Mon, 21 Jul 2014 19:13:31 +0200 Subject: [PATCH] Allow macros in type expressions --- src/librustc/middle/typeck/astconv.rs | 3 + src/libsyntax/ast.rs | 1 + src/libsyntax/ext/base.rs | 30 +++++++ src/libsyntax/ext/expand.rs | 54 ++++++++++- src/libsyntax/ext/tt/macro_rules.rs | 5 ++ src/libsyntax/fold.rs | 123 ++++++++++++++------------ src/libsyntax/parse/parser.rs | 23 ++++- src/libsyntax/print/pprust.rs | 3 + src/libsyntax/visit.rs | 3 + src/test/run-pass/macro-type.rs | 53 +++++++++++ 10 files changed, 234 insertions(+), 64 deletions(-) create mode 100644 src/test/run-pass/macro-type.rs diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index e129492dbc2f8..da793752ffa2f 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -859,6 +859,9 @@ pub fn ast_ty_to_ty( } } } + ast::TyMac(ref _mac) => { + tcx.sess.span_bug(ast_ty.span, "type macro not expanded"); + } ast::TyTypeof(_e) => { tcx.sess.span_bug(ast_ty.span, "typeof is reserved but unimplemented"); } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 7ad9a18a15e1b..f9d728fa6ba13 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -859,6 +859,7 @@ pub enum Ty_ { TyPath(Path, Option>, NodeId), // for #7264; see above /// No-op; kept solely so that we can pretty-print faithfully TyParen(P), + TyMac(Mac), TyTypeof(Gc), /// TyInfer means the type should be inferred instead of it having been /// specified. This can appear anywhere in a type. diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 49bd3697884c7..44e07ff8a063f 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -114,6 +114,10 @@ pub trait MacResult { fn make_expr(&self) -> Option> { None } + /// Create an type. + fn make_ty(&self) -> Option> { + None + } /// Create zero or more items. fn make_items(&self) -> Option>> { None @@ -153,6 +157,20 @@ impl MacResult for MacExpr { Some(self.e) } } +/// A convenience type for macros that return a single type. +pub struct MacTy { + t: Gc, +} +impl MacTy { + pub fn new(t: Gc) -> Box { + box MacTy { t: t } as Box + } +} +impl MacResult for MacTy { + fn make_ty(&self) -> Option> { + Some(self.t) + } +} /// A convenience type for macros that return a single pattern. pub struct MacPat { p: Gc, @@ -223,6 +241,15 @@ impl DummyResult { } } + /// A plain dummy type. + pub fn raw_ty(sp: Span) -> Gc { + box(GC) ast::Ty { + id: ast::DUMMY_NODE_ID, + node: ast::TyNil, // FIXME: Is this suitable? + span: sp, + } + } + /// A plain dummy pattern. pub fn raw_pat(sp: Span) -> Gc { box(GC) ast::Pat { @@ -238,6 +265,9 @@ impl MacResult for DummyResult { fn make_expr(&self) -> Option> { Some(DummyResult::raw_expr(self.span)) } + fn make_ty(&self) -> Option> { + Some(DummyResult::raw_ty(self.span)) + } fn make_pat(&self) -> Option> { Some(DummyResult::raw_pat(self.span)) } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index fdb698441fc0c..e6bc39d01c809 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ast::{P, Block, Crate, DeclLocal, ExprMac, PatMac}; +use ast::{P, Block, Crate, DeclLocal, ExprMac, PatMac, TyMac}; use ast::{Local, Ident, MacInvocTT}; use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi}; use ast::TokenTree; @@ -31,6 +31,36 @@ use util::small_vector::SmallVector; use std::gc::{Gc, GC}; +pub fn expand_ty(ty: Gc, fld: &mut MacroExpander) -> Gc { + match ty.node { + TyMac(ref mac) => { + let expanded_ty = match expand_mac_invoc(mac,&ty.span, + |r|{r.make_ty()}, + |ty,fm|{mark_ty(ty,fm)}, + fld) { + Some(ty) => ty, + None => { + return DummyResult::raw_ty(ty.span); + } + }; + + // Keep going, outside-in. + // + // FIXME(pcwalton): Is it necessary to clone the + // node here? + let fully_expanded = + fld.fold_ty(expanded_ty).node.clone(); + fld.cx.bt_pop(); + + box(GC) ast::Ty { + id: ast::DUMMY_NODE_ID, + node: fully_expanded, + span: ty.span, + } + } + _ => noop_fold_ty(ty, fld) + } +} pub fn expand_expr(e: Gc, fld: &mut MacroExpander) -> Gc { match e.node { @@ -616,13 +646,15 @@ fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander) } => { // take it apart: let Local { - ty: _, + ty: ty, pat: pat, init: init, id: id, span: span, source: source, } = **local; + // expand the ty (it might contain macro uses): + let expanded_ty = fld.fold_ty(ty); // expand the pat (it might contain macro uses): let expanded_pat = fld.fold_pat(pat); // find the PatIdents in the pattern: @@ -646,7 +678,7 @@ fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander) let new_init_opt = init.map(|e| fld.fold_expr(e)); let rewritten_local = box(GC) Local { - ty: local.ty, + ty: expanded_ty, pat: rewritten_pat, init: new_init_opt, id: id, @@ -988,6 +1020,10 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { expand_pat(pat, self) } + fn fold_ty(&mut self, ty: Gc) -> Gc { + expand_ty(ty, self) + } + fn fold_item(&mut self, item: Gc) -> SmallVector> { expand_item(item, self) } @@ -1115,6 +1151,11 @@ fn mark_expr(expr: Gc, m: Mrk) -> Gc { Marker{mark:m}.fold_expr(expr) } +// apply a given mark to the given type. Used following the expansion of a macro. +fn mark_ty(ty: Gc, m: Mrk) -> Gc { + Marker{mark:m}.fold_ty(ty) +} + // apply a given mark to the given pattern. Used following the expansion of a macro. fn mark_pat(pat: Gc, m: Mrk) -> Gc { Marker{mark:m}.fold_pat(pat) @@ -1528,6 +1569,13 @@ mod test { 0) } + // macro_rules in type + #[test] fn macro_in_type_posn(){ + expand_crate_str( + "macro_rules! type_macro (()=>(int)) + fn f(x: type_macro!()){}".to_string()); + } + // macro_rules in method position. Sadly, unimplemented. #[test] fn macro_in_method_posn(){ expand_crate_str( diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 1eb37abb781a3..0cdbcb0ea1a05 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -59,6 +59,11 @@ impl<'a> ParserAnyMacro<'a> { } impl<'a> MacResult for ParserAnyMacro<'a> { + fn make_ty(&self) -> Option> { + let ret = self.parser.borrow_mut().parse_ty(true); // Allow plus in types? + self.ensure_complete_parse(true); + Some(ret) + } fn make_expr(&self) -> Option> { let ret = self.parser.borrow_mut().parse_expr(); self.ensure_complete_parse(true); diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index e31ec04865384..ecbc2c50969f4 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -160,65 +160,7 @@ pub trait Folder { } fn fold_ty(&mut self, t: P) -> P { - let id = self.new_id(t.id); - let node = match t.node { - TyNil | TyBot | TyInfer => t.node.clone(), - TyBox(ty) => TyBox(self.fold_ty(ty)), - TyUniq(ty) => TyUniq(self.fold_ty(ty)), - TyVec(ty) => TyVec(self.fold_ty(ty)), - TyPtr(ref mt) => TyPtr(fold_mt(mt, self)), - TyRptr(ref region, ref mt) => { - TyRptr(fold_opt_lifetime(region, self), fold_mt(mt, self)) - } - TyClosure(ref f, ref region) => { - TyClosure(box(GC) ClosureTy { - fn_style: f.fn_style, - onceness: f.onceness, - bounds: fold_opt_bounds(&f.bounds, self), - decl: self.fold_fn_decl(&*f.decl), - lifetimes: f.lifetimes.iter().map(|l| self.fold_lifetime(l)).collect(), - }, fold_opt_lifetime(region, self)) - } - TyProc(ref f) => { - TyProc(box(GC) ClosureTy { - fn_style: f.fn_style, - onceness: f.onceness, - bounds: fold_opt_bounds(&f.bounds, self), - decl: self.fold_fn_decl(&*f.decl), - lifetimes: f.lifetimes.iter().map(|l| self.fold_lifetime(l)).collect(), - }) - } - TyBareFn(ref f) => { - TyBareFn(box(GC) BareFnTy { - lifetimes: f.lifetimes.iter().map(|l| self.fold_lifetime(l)).collect(), - fn_style: f.fn_style, - abi: f.abi, - decl: self.fold_fn_decl(&*f.decl) - }) - } - TyUnboxedFn(ref f) => { - TyUnboxedFn(box(GC) UnboxedFnTy { - decl: self.fold_fn_decl(&*f.decl), - }) - } - TyTup(ref tys) => TyTup(tys.iter().map(|&ty| self.fold_ty(ty)).collect()), - TyParen(ref ty) => TyParen(self.fold_ty(*ty)), - TyPath(ref path, ref bounds, id) => { - let id = self.new_id(id); - TyPath(self.fold_path(path), - fold_opt_bounds(bounds, self), - id) - } - TyFixedLengthVec(ty, e) => { - TyFixedLengthVec(self.fold_ty(ty), self.fold_expr(e)) - } - TyTypeof(expr) => TyTypeof(self.fold_expr(expr)), - }; - P(Ty { - id: id, - span: self.new_span(t.span), - node: node, - }) + noop_fold_ty(t, self) } fn fold_mod(&mut self, m: &Mod) -> Mod { @@ -1011,6 +953,69 @@ pub fn noop_fold_expr(e: Gc, folder: &mut T) -> Gc { } } +pub fn noop_fold_ty(t: P, folder: &mut T) -> P { + let id = folder.new_id(t.id); + let node = match t.node { + TyNil | TyBot | TyInfer => t.node.clone(), + TyBox(ty) => TyBox(folder.fold_ty(ty)), + TyUniq(ty) => TyUniq(folder.fold_ty(ty)), + TyVec(ty) => TyVec(folder.fold_ty(ty)), + TyPtr(ref mt) => TyPtr(fold_mt(mt, folder)), + TyRptr(ref region, ref mt) => { + TyRptr(fold_opt_lifetime(region, folder), fold_mt(mt, folder)) + } + TyClosure(ref f, ref region) => { + TyClosure(box(GC) ClosureTy { + fn_style: f.fn_style, + onceness: f.onceness, + bounds: fold_opt_bounds(&f.bounds, folder), + decl: folder.fold_fn_decl(&*f.decl), + lifetimes: f.lifetimes.iter().map(|l| folder.fold_lifetime(l)).collect(), + }, fold_opt_lifetime(region, folder)) + } + TyProc(ref f) => { + TyProc(box(GC) ClosureTy { + fn_style: f.fn_style, + onceness: f.onceness, + bounds: fold_opt_bounds(&f.bounds, folder), + decl: folder.fold_fn_decl(&*f.decl), + lifetimes: f.lifetimes.iter().map(|l| folder.fold_lifetime(l)).collect(), + }) + } + TyBareFn(ref f) => { + TyBareFn(box(GC) BareFnTy { + lifetimes: f.lifetimes.iter().map(|l| folder.fold_lifetime(l)).collect(), + fn_style: f.fn_style, + abi: f.abi, + decl: folder.fold_fn_decl(&*f.decl) + }) + } + TyUnboxedFn(ref f) => { + TyUnboxedFn(box(GC) UnboxedFnTy { + decl: folder.fold_fn_decl(&*f.decl), + }) + } + TyTup(ref tys) => TyTup(tys.iter().map(|&ty| folder.fold_ty(ty)).collect()), + TyParen(ref ty) => TyParen(folder.fold_ty(*ty)), + TyMac(ref mac) => TyMac(folder.fold_mac(mac)), + TyPath(ref path, ref bounds, id) => { + let id = folder.new_id(id); + TyPath(folder.fold_path(path), + fold_opt_bounds(bounds, folder), + id) + } + TyFixedLengthVec(ty, e) => { + TyFixedLengthVec(folder.fold_ty(ty), folder.fold_expr(e)) + } + TyTypeof(expr) => TyTypeof(folder.fold_expr(expr)), + }; + P(Ty { + id: id, + span: folder.new_span(t.span), + node: node, + }) +} + pub fn noop_fold_stmt(s: &Stmt, folder: &mut T) -> SmallVector> { let nodes = match s.node { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 73de47e7b12e7..4d460f4aff9fb 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -50,7 +50,7 @@ use ast::{TokenTree, TraitMethod, TraitRef, TTDelim, TTSeq, TTTok}; use ast::{TTNonterminal, TupleVariantKind, Ty, Ty_, TyBot, TyBox}; use ast::{TypeField, TyFixedLengthVec, TyClosure, TyProc, TyBareFn}; use ast::{TyTypeof, TyInfer, TypeMethod}; -use ast::{TyNil, TyParam, TyParamBound, TyParen, TyPath, TyPtr, TyRptr}; +use ast::{TyNil, TyParam, TyParamBound, TyParen, TyPath, TyMac, TyPtr, TyRptr}; use ast::{TyTup, TyU32, TyUnboxedFn, TyUniq, TyVec, UnUniq}; use ast::{UnboxedFnTy, UnboxedFnTyParamBound, UnnamedField, UnsafeBlock}; use ast::{UnsafeFn, ViewItem, ViewItem_, ViewItemExternCrate, ViewItemUse}; @@ -1456,7 +1456,26 @@ impl<'a> Parser<'a> { path, bounds } = self.parse_path(mode); - TyPath(path, bounds, ast::DUMMY_NODE_ID) + + + if self.token == token::NOT { + // MACRO INVOCATION type + self.bump(); + + let ket = token::close_delimiter_for(&self.token) + .unwrap_or_else(|| self.fatal("expected open delimiter")); + self.bump(); + + let tts = self.parse_seq_to_end(&ket, + seq_sep_none(), + |p| p.parse_token_tree()); + let hi = self.span.hi; + + TyMac(codemap::Spanned {node: MacInvocTT(path, tts, EMPTY_CTXT), + span: mk_sp(lo, hi)}) + } else { + TyPath(path, bounds, ast::DUMMY_NODE_ID) + } } else if self.eat(&token::UNDERSCORE) { // TYPE TO BE INFERRED TyInfer diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 305e67a916eb9..bb4aa0737a916 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -580,6 +580,9 @@ impl<'a> State<'a> { ast::TyPath(ref path, ref bounds, _) => { try!(self.print_bounded_path(path, bounds)); } + ast::TyMac(ref mac) => { + try!(self.print_mac(mac)); + } ast::TyFixedLengthVec(ref ty, ref v) => { try!(word(&mut self.s, "[")); try!(self.print_type(&**ty)); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 3c6f06ddfc35d..9c919505e88a4 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -412,6 +412,9 @@ pub fn walk_ty>(visitor: &mut V, typ: &Ty, env: E) { walk_ty_param_bounds(visitor, bounds, env.clone()) } } + TyMac(ref macro) => { + visitor.visit_mac(macro, env.clone()); + } TyFixedLengthVec(ref ty, ref expression) => { visitor.visit_ty(&**ty, env.clone()); visitor.visit_expr(&**expression, env) diff --git a/src/test/run-pass/macro-type.rs b/src/test/run-pass/macro-type.rs new file mode 100644 index 0000000000000..4d0dd1214b7be --- /dev/null +++ b/src/test/run-pass/macro-type.rs @@ -0,0 +1,53 @@ +// Copyright 2012-2014 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(macro_rules)] + +struct Nested { + _f: T +} + +macro_rules! int_type( + () => ( + int + ) +) + +macro_rules! nested_type( + ($x:ty) => ( + Nested<$x> + ) +) + +macro_rules! indirect( + () => ( + nested_type!(int_type!()) + ) +) + +macro_rules! ident_type( + ($x:ident) => ( + $x + ) +) + +pub fn main() { + let a: int_type!(); + let _: int = a; + + let b: nested_type!(bool); + let _: Nested = b; + + let c: indirect!(); + let _: Nested = c; + + let d: ident_type!(u32); + let _: u32 = d; +}