From 6680c9c5c797101fc5e0608cb2c3657517333148 Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Fri, 2 Jan 2015 16:41:24 -0500 Subject: [PATCH 1/5] syntax: implement 'macro input future proofing' See RFC 550 (https://github.com/rust-lang/rfcs/pull/550) for the motivation and details. If this breaks your code, add one of the listed tokens after the relevant non-terminal in your matcher. [breaking-change] --- src/libsyntax/ext/tt/macro_rules.rs | 148 +++++++++++++++++- src/libsyntax/parse/token.rs | 1 + .../macro-input-future-proofing.rs | 26 +++ 3 files changed, 167 insertions(+), 8 deletions(-) create mode 100644 src/test/compile-fail/macro-input-future-proofing.rs diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 9837c8088fa..96a0f7de0fd 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ast::{Ident, TtDelimited, TtSequence, TtToken}; +use ast::{TokenTree, TtDelimited, TtSequence, TtToken}; use ast; use codemap::{Span, DUMMY_SP}; use ext::base::{ExtCtxt, MacResult, SyntaxExtension}; @@ -19,8 +19,8 @@ use ext::tt::macro_parser::{parse, parse_or_else}; use parse::lexer::new_tt_reader; use parse::parser::Parser; use parse::attr::ParserAttr; -use parse::token::{special_idents, gensym_ident}; -use parse::token::{MatchNt, NtTT}; +use parse::token::{special_idents, gensym_ident, NtTT, Token}; +use parse::token::Token::*; use parse::token; use print; use ptr::P; @@ -109,8 +109,8 @@ impl<'a> MacResult for ParserAnyMacro<'a> { } struct MacroRulesMacroExpander { - name: Ident, - imported_from: Option, + name: ast::Ident, + imported_from: Option, lhses: Vec>, rhses: Vec>, } @@ -134,8 +134,8 @@ impl TTMacroExpander for MacroRulesMacroExpander { /// Given `lhses` and `rhses`, this is the new macro we create fn generic_extension<'cx>(cx: &'cx ExtCtxt, sp: Span, - name: Ident, - imported_from: Option, + name: ast::Ident, + imported_from: Option, arg: &[ast::TokenTree], lhses: &[Rc], rhses: &[Rc]) @@ -260,6 +260,10 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt, _ => cx.span_bug(def.span, "wrong-structured lhs") }; + for lhs in lhses.iter() { + check_lhs_nt_follows(cx, &**lhs, def.span); + } + let rhses = match *argument_map[rhs_nm] { MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(), _ => cx.span_bug(def.span, "wrong-structured rhs") @@ -274,3 +278,131 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt, NormalTT(exp, Some(def.span)) } + +fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &NamedMatch, sp: Span) { + // lhs is going to be like MatchedNonterminal(NtTT(TtDelimited(...))), where + // the entire lhs is those tts. + // if ever we get box/deref patterns, this could turn into an `if let + // &MatchedNonterminal(NtTT(box TtDelimited(...))) = lhs` + let matcher = match lhs { + &MatchedNonterminal(NtTT(ref inner)) => match &**inner { + &TtDelimited(_, ref tts) => tts.tts[], + _ => cx.span_bug(sp, "wrong-structured lhs for follow check") + }, + _ => cx.span_bug(sp, "wrong-structured lhs for follow check") + }; + + check_matcher(cx, matcher, &Eof); + // we don't abort on errors on rejection, the driver will do that for us + // after parsing/expansion. we can report every error in every macro this way. +} + +fn check_matcher(cx: &mut ExtCtxt, matcher: &[TokenTree], follow: &Token) { + use print::pprust::token_to_string; + + // 1. If there are no tokens in M, accept + if matcher.is_empty() { + return; + } + + // 2. For each token T in M: + let mut tokens = matcher.iter().peekable(); + while let Some(token) = tokens.next() { + match *token { + TtToken(sp, MatchNt(ref name, ref frag_spec, _, _)) => { + // ii. If T is a simple NT, look ahead to the next token T' in + // M. + let next_token = match tokens.peek() { + // If T' closes a complex NT, replace T' with F + Some(&&TtToken(_, CloseDelim(_))) => follow, + Some(&&TtToken(_, ref tok)) => tok, + // T' is any NT (this catches complex NTs, the next + // iteration will die if it's a TtDelimited). + Some(_) => continue, + // else, we're at the end of the macro or sequence + None => follow + }; + + // If T' is in the set FOLLOW(NT), continue. Else, reject. + match *next_token { + Eof | MatchNt(..) => continue, + _ if is_in_follow(cx, next_token, frag_spec.as_str()) => continue, + ref tok => cx.span_err(sp, format!("`${0}:{1}` is followed by `{2}`, which \ + is not allowed for `{1}` fragments", + name.as_str(), frag_spec.as_str(), + token_to_string(tok))[]) + } + }, + TtSequence(_, ref seq) => { + // iii. Else, T is a complex NT. + match seq.separator { + // If T has the form $(...)U+ or $(...)U* for some token U, + // run the algorithm on the contents with F set to U. If it + // accepts, continue, else, reject. + Some(ref u) => check_matcher(cx, seq.tts[], u), + // If T has the form $(...)+ or $(...)*, run the algorithm + // on the contents with F set to EOF. If it accepts, + // continue, else, reject. + None => check_matcher(cx, seq.tts[], &Eof) + } + }, + TtToken(..) => { + // i. If T is not an NT, continue. + continue + }, + TtDelimited(_, ref tts) => { + // if we don't pass in that close delimiter, we'll incorrectly consider the matcher + // `{ $foo:ty }` as having a follow that isn't `}` + check_matcher(cx, tts.tts[], &tts.close_token()) + } + } + } +} + +fn is_in_follow(cx: &ExtCtxt, tok: &Token, frag: &str) -> bool { + if let &CloseDelim(_) = tok { + return true; + } + + match frag { + "item" => { + // since items *must* be followed by either a `;` or a `}`, we can + // accept anything after them + true + }, + "block" => { + // anything can follow block, the braces provide a easy boundary to + // maintain + true + }, + "stmt" | "expr" => { + match *tok { + Comma | Semi => true, + _ => false + } + }, + "pat" => { + match *tok { + FatArrow | Comma | Eq => true, + _ => false + } + }, + "path" | "ty" => { + match *tok { + Comma | RArrow | Colon | Eq | Gt => true, + Ident(i, _) if i.as_str() == "as" => true, + _ => false + } + }, + "ident" => { + // being a single token, idents are harmless + true + }, + "meta" | "tt" => { + // being either a single token or a delimited sequence, tt is + // harmless + true + }, + _ => cx.bug(format!("unrecognized builtin nonterminal {}", frag)[]), + } +} diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 094aacf3207..205589ba78e 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -392,6 +392,7 @@ impl fmt::Show for Nonterminal { } } + // Get the first "argument" macro_rules! first { ( $first:expr, $( $remainder:expr, )* ) => ( $first ) diff --git a/src/test/compile-fail/macro-input-future-proofing.rs b/src/test/compile-fail/macro-input-future-proofing.rs new file mode 100644 index 00000000000..2804fc14796 --- /dev/null +++ b/src/test/compile-fail/macro-input-future-proofing.rs @@ -0,0 +1,26 @@ +// 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! errors_everywhere { + ($ty:ty <) => () //~ ERROR `$ty:ty` is followed by `<`, which is not allowed for `ty` fragments + ($ty:ty < foo ,) => () //~ ERROR `$ty:ty` is followed by `<`, which is not allowed for `ty` + ($ty:ty , ) => () + ( ( $ty:ty ) ) => () + ( { $ty:ty } ) => () + ( [ $ty:ty ] ) => () + ($bl:block < ) => () + ($pa:pat >) => () //~ ERROR `$pa:pat` is followed by `>` which is not allowed for `pat` + ($pa:pat , ) => () + ($pa:pat | ) => () + ($pa:pat $pb:pat $ty:ty ,) => () + ($($ty:ty)-+) => () //~ ERROR `$ty:ty` is followed by `-` which is not allowed for `ty` +} + +fn main() { } From e0b4287df6635158043e7015d89b619af7f7748d Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Mon, 5 Jan 2015 01:51:03 -0500 Subject: [PATCH 2/5] Fix fallout --- src/doc/guide-macros.md | 36 ++-- src/libcore/str/mod.rs | 21 +- src/librustc_trans/trans/context.rs | 182 +++++++++--------- src/libstd/sys/common/backtrace.rs | 28 +-- .../macro-input-future-proofing.rs | 24 +-- src/test/run-pass/const-polymorphic-paths.rs | 51 +++-- .../nullable-pointer-iotareduction.rs | 22 +-- 7 files changed, 181 insertions(+), 183 deletions(-) diff --git a/src/doc/guide-macros.md b/src/doc/guide-macros.md index dc6d281307a..9cb4d154de7 100644 --- a/src/doc/guide-macros.md +++ b/src/doc/guide-macros.md @@ -161,7 +161,7 @@ instead of `*` to mean "at least one". # let input_1 = T::SpecialA(0); # let input_2 = T::SpecialA(0); macro_rules! early_return { - ($inp:expr, [ $($sp:path)|+ ]) => ( + ($inp:expr, [ $($sp:path),+ ]) => ( match $inp { $( $sp(x) => { return x; } @@ -171,7 +171,7 @@ macro_rules! early_return { ) } // ... -early_return!(input_1, [T::SpecialA|T::SpecialC|T::SpecialD]); +early_return!(input_1, [T::SpecialA,T::SpecialC,T::SpecialD]); // ... early_return!(input_2, [T::SpecialB]); # return 0; @@ -245,7 +245,7 @@ can solve the problem: ~~~~ macro_rules! biased_match { // special case: `let (x) = ...` is illegal, so use `let x = ...` instead - ( ($e:expr) ~ ($p:pat) else $err:stmt ; + ( ($e:expr) -> ($p:pat) else $err:stmt ; binds $bind_res:ident ) => ( let $bind_res = match $e { @@ -254,7 +254,7 @@ macro_rules! biased_match { }; ); // more than one name; use a tuple - ( ($e:expr) ~ ($p:pat) else $err:stmt ; + ( ($e:expr) -> ($p:pat) else $err:stmt ; binds $( $bind_res:ident ),* ) => ( let ( $( $bind_res ),* ) = match $e { @@ -268,9 +268,9 @@ macro_rules! biased_match { # struct T2 { body: T3 } # enum T3 { Good2(uint), Bad2} # fn f(x: T1) -> uint { -biased_match!((x) ~ (T1::Good1(g1, val)) else { return 0 }; +biased_match!((x) -> (T1::Good1(g1, val)) else { return 0 }; binds g1, val ); -biased_match!((g1.body) ~ (T3::Good2(result) ) +biased_match!((g1.body) -> (T3::Good2(result) ) else { panic!("Didn't get good_2") }; binds result ); // complicated stuff goes here @@ -286,7 +286,7 @@ pattern we want is clear: ~~~~ # fn main() {} # macro_rules! b { - ( $( ($e:expr) ~ ($p:pat) else $err:stmt ; )* + ( $( ($e:expr) -> ($p:pat) else $err:stmt ; )* binds $( $bind_res:ident ),* ) # => (0) } @@ -317,8 +317,8 @@ input patterns: ~~~~ # fn main() {} # macro_rules! b { - ( ($e :expr) ~ ($p :pat) else $err :stmt ; - $( ($e_rest:expr) ~ ($p_rest:pat) else $err_rest:stmt ; )* + ( ($e :expr) -> ($p :pat) else $err :stmt ; + $( ($e_rest:expr) -> ($p_rest:pat) else $err_rest:stmt ; )* binds $( $bind_res:ident ),* ) # => (0) } @@ -333,14 +333,14 @@ piece of syntax (the `let`) which we only want to transcribe once. macro_rules! biased_match_rec { // Handle the first layer - ( ($e :expr) ~ ($p :pat) else $err :stmt ; - $( ($e_rest:expr) ~ ($p_rest:pat) else $err_rest:stmt ; )* + ( ($e :expr) -> ($p :pat) else $err :stmt ; + $( ($e_rest:expr) -> ($p_rest:pat) else $err_rest:stmt ; )* binds $( $bind_res:ident ),* ) => ( match $e { $p => { // Recursively handle the next layer - biased_match_rec!($( ($e_rest) ~ ($p_rest) else $err_rest ; )* + biased_match_rec!($( ($e_rest) -> ($p_rest) else $err_rest ; )* binds $( $bind_res ),* ) } @@ -354,20 +354,20 @@ macro_rules! biased_match_rec { // Wrap the whole thing in a `let`. macro_rules! biased_match { // special case: `let (x) = ...` is illegal, so use `let x = ...` instead - ( $( ($e:expr) ~ ($p:pat) else $err:stmt ; )* + ( $( ($e:expr) -> ($p:pat) else $err:stmt ; )* binds $bind_res:ident ) => ( let $bind_res = biased_match_rec!( - $( ($e) ~ ($p) else $err ; )* + $( ($e) -> ($p) else $err ; )* binds $bind_res ); ); // more than one name: use a tuple - ( $( ($e:expr) ~ ($p:pat) else $err:stmt ; )* + ( $( ($e:expr) -> ($p:pat) else $err:stmt ; )* binds $( $bind_res:ident ),* ) => ( let ( $( $bind_res ),* ) = biased_match_rec!( - $( ($e) ~ ($p) else $err ; )* + $( ($e) -> ($p) else $err ; )* binds $( $bind_res ),* ); ) @@ -379,8 +379,8 @@ macro_rules! biased_match { # enum T3 { Good2(uint), Bad2} # fn f(x: T1) -> uint { biased_match!( - (x) ~ (T1::Good1(g1, val)) else { return 0 }; - (g1.body) ~ (T3::Good2(result) ) else { panic!("Didn't get Good2") }; + (x) -> (T1::Good1(g1, val)) else { return 0 }; + (g1.body) -> (T3::Good2(result) ) else { panic!("Didn't get Good2") }; binds val, result ); // complicated stuff goes here return result + val; diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index a39787b8207..b0a1fa0b1b4 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -35,9 +35,8 @@ use slice::{self, SliceExt}; use uint; macro_rules! delegate_iter { - (exact $te:ty in $ti:ty) => { - delegate_iter!{$te in $ti} - #[stable] + (exact $te:ty : $ti:ty) => { + delegate_iter!{$te : $ti} impl<'a> ExactSizeIterator for $ti { #[inline] fn len(&self) -> uint { @@ -45,7 +44,7 @@ macro_rules! delegate_iter { } } }; - ($te:ty in $ti:ty) => { + ($te:ty : $ti:ty) => { #[stable] impl<'a> Iterator for $ti { type Item = $te; @@ -67,7 +66,7 @@ macro_rules! delegate_iter { } } }; - (pattern $te:ty in $ti:ty) => { + (pattern $te:ty : $ti:ty) => { #[stable] impl<'a, P: CharEq> Iterator for $ti { type Item = $te; @@ -89,7 +88,7 @@ macro_rules! delegate_iter { } } }; - (pattern forward $te:ty in $ti:ty) => { + (pattern forward $te:ty : $ti:ty) => { #[stable] impl<'a, P: CharEq> Iterator for $ti { type Item = $te; @@ -415,7 +414,7 @@ impl<'a> DoubleEndedIterator for CharIndices<'a> { #[stable] #[derive(Clone)] pub struct Bytes<'a>(Map<&'a u8, u8, slice::Iter<'a, u8>, BytesDeref>); -delegate_iter!{exact u8 in Bytes<'a>} +delegate_iter!{exact u8 : Bytes<'a>} /// A temporary fn new type that ensures that the `Bytes` iterator /// is cloneable. @@ -1165,25 +1164,25 @@ impl<'a, S: ?Sized> Str for &'a S where S: Str { #[derive(Clone)] #[stable] pub struct Split<'a, P>(CharSplits<'a, P>); -delegate_iter!{pattern &'a str in Split<'a, P>} +delegate_iter!{pattern &'a str : Split<'a, P>} /// Return type of `StrExt::split_terminator` #[derive(Clone)] #[unstable = "might get removed in favour of a constructor method on Split"] pub struct SplitTerminator<'a, P>(CharSplits<'a, P>); -delegate_iter!{pattern &'a str in SplitTerminator<'a, P>} +delegate_iter!{pattern &'a str : SplitTerminator<'a, P>} /// Return type of `StrExt::splitn` #[derive(Clone)] #[stable] pub struct SplitN<'a, P>(CharSplitsN<'a, P>); -delegate_iter!{pattern forward &'a str in SplitN<'a, P>} +delegate_iter!{pattern forward &'a str : SplitN<'a, P>} /// Return type of `StrExt::rsplitn` #[derive(Clone)] #[stable] pub struct RSplitN<'a, P>(CharSplitsN<'a, P>); -delegate_iter!{pattern forward &'a str in RSplitN<'a, P>} +delegate_iter!{pattern forward &'a str : RSplitN<'a, P>} /// Methods for string slices #[allow(missing_docs)] diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 3726cf14023..bced3be01f0 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -737,7 +737,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option { macro_rules! ifn { - ($name:expr fn() -> $ret:expr) => ( + ($name:expr, fn() -> $ret:expr) => ( if *key == $name { let f = base::decl_cdecl_fn( ccx, $name, Type::func(&[], &$ret), @@ -746,7 +746,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option $ret:expr) => ( + ($name:expr, fn($($arg:expr),*) -> $ret:expr) => ( if *key == $name { let f = base::decl_cdecl_fn(ccx, $name, Type::func(&[$($arg),*], &$ret), ty::mk_nil(ccx.tcx())); @@ -769,111 +769,111 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option void); - ifn!("llvm.memcpy.p0i8.p0i8.i64" fn(i8p, i8p, t_i64, t_i32, i1) -> void); - ifn!("llvm.memmove.p0i8.p0i8.i32" fn(i8p, i8p, t_i32, t_i32, i1) -> void); - ifn!("llvm.memmove.p0i8.p0i8.i64" fn(i8p, i8p, t_i64, t_i32, i1) -> void); - ifn!("llvm.memset.p0i8.i32" fn(i8p, t_i8, t_i32, t_i32, i1) -> void); - ifn!("llvm.memset.p0i8.i64" fn(i8p, t_i8, t_i64, t_i32, i1) -> void); + ifn!("llvm.memcpy.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void); + ifn!("llvm.memcpy.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void); + ifn!("llvm.memmove.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void); + ifn!("llvm.memmove.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void); + ifn!("llvm.memset.p0i8.i32", fn(i8p, t_i8, t_i32, t_i32, i1) -> void); + ifn!("llvm.memset.p0i8.i64", fn(i8p, t_i8, t_i64, t_i32, i1) -> void); - ifn!("llvm.trap" fn() -> void); - ifn!("llvm.debugtrap" fn() -> void); - ifn!("llvm.frameaddress" fn(t_i32) -> i8p); + ifn!("llvm.trap", fn() -> void); + ifn!("llvm.debugtrap", fn() -> void); + ifn!("llvm.frameaddress", fn(t_i32) -> i8p); - ifn!("llvm.powi.f32" fn(t_f32, t_i32) -> t_f32); - ifn!("llvm.powi.f64" fn(t_f64, t_i32) -> t_f64); - ifn!("llvm.pow.f32" fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.pow.f64" fn(t_f64, t_f64) -> t_f64); + ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); + ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64); + ifn!("llvm.pow.f32", fn(t_f32, t_f32) -> t_f32); + ifn!("llvm.pow.f64", fn(t_f64, t_f64) -> t_f64); - ifn!("llvm.sqrt.f32" fn(t_f32) -> t_f32); - ifn!("llvm.sqrt.f64" fn(t_f64) -> t_f64); - ifn!("llvm.sin.f32" fn(t_f32) -> t_f32); - ifn!("llvm.sin.f64" fn(t_f64) -> t_f64); - ifn!("llvm.cos.f32" fn(t_f32) -> t_f32); - ifn!("llvm.cos.f64" fn(t_f64) -> t_f64); - ifn!("llvm.exp.f32" fn(t_f32) -> t_f32); - ifn!("llvm.exp.f64" fn(t_f64) -> t_f64); - ifn!("llvm.exp2.f32" fn(t_f32) -> t_f32); - ifn!("llvm.exp2.f64" fn(t_f64) -> t_f64); - ifn!("llvm.log.f32" fn(t_f32) -> t_f32); - ifn!("llvm.log.f64" fn(t_f64) -> t_f64); - ifn!("llvm.log10.f32" fn(t_f32) -> t_f32); - ifn!("llvm.log10.f64" fn(t_f64) -> t_f64); - ifn!("llvm.log2.f32" fn(t_f32) -> t_f32); - ifn!("llvm.log2.f64" fn(t_f64) -> t_f64); + ifn!("llvm.sqrt.f32", fn(t_f32) -> t_f32); + ifn!("llvm.sqrt.f64", fn(t_f64) -> t_f64); + ifn!("llvm.sin.f32", fn(t_f32) -> t_f32); + ifn!("llvm.sin.f64", fn(t_f64) -> t_f64); + ifn!("llvm.cos.f32", fn(t_f32) -> t_f32); + ifn!("llvm.cos.f64", fn(t_f64) -> t_f64); + ifn!("llvm.exp.f32", fn(t_f32) -> t_f32); + ifn!("llvm.exp.f64", fn(t_f64) -> t_f64); + ifn!("llvm.exp2.f32", fn(t_f32) -> t_f32); + ifn!("llvm.exp2.f64", fn(t_f64) -> t_f64); + ifn!("llvm.log.f32", fn(t_f32) -> t_f32); + ifn!("llvm.log.f64", fn(t_f64) -> t_f64); + ifn!("llvm.log10.f32", fn(t_f32) -> t_f32); + ifn!("llvm.log10.f64", fn(t_f64) -> t_f64); + ifn!("llvm.log2.f32", fn(t_f32) -> t_f32); + ifn!("llvm.log2.f64", fn(t_f64) -> t_f64); - ifn!("llvm.fma.f32" fn(t_f32, t_f32, t_f32) -> t_f32); - ifn!("llvm.fma.f64" fn(t_f64, t_f64, t_f64) -> t_f64); + ifn!("llvm.fma.f32", fn(t_f32, t_f32, t_f32) -> t_f32); + ifn!("llvm.fma.f64", fn(t_f64, t_f64, t_f64) -> t_f64); - ifn!("llvm.fabs.f32" fn(t_f32) -> t_f32); - ifn!("llvm.fabs.f64" fn(t_f64) -> t_f64); + ifn!("llvm.fabs.f32", fn(t_f32) -> t_f32); + ifn!("llvm.fabs.f64", fn(t_f64) -> t_f64); - ifn!("llvm.floor.f32" fn(t_f32) -> t_f32); - ifn!("llvm.floor.f64" fn(t_f64) -> t_f64); - ifn!("llvm.ceil.f32" fn(t_f32) -> t_f32); - ifn!("llvm.ceil.f64" fn(t_f64) -> t_f64); - ifn!("llvm.trunc.f32" fn(t_f32) -> t_f32); - ifn!("llvm.trunc.f64" fn(t_f64) -> t_f64); + ifn!("llvm.floor.f32", fn(t_f32) -> t_f32); + ifn!("llvm.floor.f64", fn(t_f64) -> t_f64); + ifn!("llvm.ceil.f32", fn(t_f32) -> t_f32); + ifn!("llvm.ceil.f64", fn(t_f64) -> t_f64); + ifn!("llvm.trunc.f32", fn(t_f32) -> t_f32); + ifn!("llvm.trunc.f64", fn(t_f64) -> t_f64); - ifn!("llvm.rint.f32" fn(t_f32) -> t_f32); - ifn!("llvm.rint.f64" fn(t_f64) -> t_f64); - ifn!("llvm.nearbyint.f32" fn(t_f32) -> t_f32); - ifn!("llvm.nearbyint.f64" fn(t_f64) -> t_f64); + ifn!("llvm.rint.f32", fn(t_f32) -> t_f32); + ifn!("llvm.rint.f64", fn(t_f64) -> t_f64); + ifn!("llvm.nearbyint.f32", fn(t_f32) -> t_f32); + ifn!("llvm.nearbyint.f64", fn(t_f64) -> t_f64); - ifn!("llvm.ctpop.i8" fn(t_i8) -> t_i8); - ifn!("llvm.ctpop.i16" fn(t_i16) -> t_i16); - ifn!("llvm.ctpop.i32" fn(t_i32) -> t_i32); - ifn!("llvm.ctpop.i64" fn(t_i64) -> t_i64); + ifn!("llvm.ctpop.i8", fn(t_i8) -> t_i8); + ifn!("llvm.ctpop.i16", fn(t_i16) -> t_i16); + ifn!("llvm.ctpop.i32", fn(t_i32) -> t_i32); + ifn!("llvm.ctpop.i64", fn(t_i64) -> t_i64); - ifn!("llvm.ctlz.i8" fn(t_i8 , i1) -> t_i8); - ifn!("llvm.ctlz.i16" fn(t_i16, i1) -> t_i16); - ifn!("llvm.ctlz.i32" fn(t_i32, i1) -> t_i32); - ifn!("llvm.ctlz.i64" fn(t_i64, i1) -> t_i64); + ifn!("llvm.ctlz.i8", fn(t_i8 , i1) -> t_i8); + ifn!("llvm.ctlz.i16", fn(t_i16, i1) -> t_i16); + ifn!("llvm.ctlz.i32", fn(t_i32, i1) -> t_i32); + ifn!("llvm.ctlz.i64", fn(t_i64, i1) -> t_i64); - ifn!("llvm.cttz.i8" fn(t_i8 , i1) -> t_i8); - ifn!("llvm.cttz.i16" fn(t_i16, i1) -> t_i16); - ifn!("llvm.cttz.i32" fn(t_i32, i1) -> t_i32); - ifn!("llvm.cttz.i64" fn(t_i64, i1) -> t_i64); + ifn!("llvm.cttz.i8", fn(t_i8 , i1) -> t_i8); + ifn!("llvm.cttz.i16", fn(t_i16, i1) -> t_i16); + ifn!("llvm.cttz.i32", fn(t_i32, i1) -> t_i32); + ifn!("llvm.cttz.i64", fn(t_i64, i1) -> t_i64); - ifn!("llvm.bswap.i16" fn(t_i16) -> t_i16); - ifn!("llvm.bswap.i32" fn(t_i32) -> t_i32); - ifn!("llvm.bswap.i64" fn(t_i64) -> t_i64); + ifn!("llvm.bswap.i16", fn(t_i16) -> t_i16); + ifn!("llvm.bswap.i32", fn(t_i32) -> t_i32); + ifn!("llvm.bswap.i64", fn(t_i64) -> t_i64); - ifn!("llvm.sadd.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.sadd.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.sadd.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.sadd.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.sadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.sadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.sadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.sadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.uadd.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.uadd.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.uadd.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.uadd.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.uadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.uadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.uadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.uadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.ssub.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.ssub.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.ssub.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.ssub.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.ssub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.ssub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.ssub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.ssub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.usub.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.usub.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.usub.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.usub.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.usub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.usub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.usub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.usub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.smul.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.smul.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.smul.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.smul.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.smul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.smul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.smul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.smul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.umul.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.umul.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.umul.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.umul.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.umul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.umul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.umul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.umul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.lifetime.start" fn(t_i64,i8p) -> void); - ifn!("llvm.lifetime.end" fn(t_i64, i8p) -> void); + ifn!("llvm.lifetime.start", fn(t_i64,i8p) -> void); + ifn!("llvm.lifetime.end", fn(t_i64, i8p) -> void); - ifn!("llvm.expect.i1" fn(i1, i1) -> i1); - ifn!("llvm.assume" fn(i1) -> void); + ifn!("llvm.expect.i1", fn(i1, i1) -> i1); + ifn!("llvm.assume", fn(i1) -> void); // Some intrinsics were introduced in later versions of LLVM, but they have // fallbacks in libc or libm and such. Currently, all of these intrinsics @@ -882,7 +882,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option $ret:expr) => ( if unsafe { llvm::LLVMVersionMinor() >= 4 } { // The `if key == $name` is already in ifn! - ifn!($name fn($($arg),*) -> $ret); + ifn!($name, fn($($arg),*) -> $ret); } else if *key == $name { let f = base::decl_cdecl_fn(ccx, stringify!($cname), Type::func(&[$($arg),*], &$ret), @@ -900,8 +900,8 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option void); - ifn!("llvm.dbg.value" fn(Type::metadata(ccx), t_i64, Type::metadata(ccx)) -> void); + ifn!("llvm.dbg.declare", fn(Type::metadata(ccx), Type::metadata(ccx)) -> void); + ifn!("llvm.dbg.value", fn(Type::metadata(ccx), t_i64, Type::metadata(ccx)) -> void); } return None; } diff --git a/src/libstd/sys/common/backtrace.rs b/src/libstd/sys/common/backtrace.rs index d4039fd96ff..be44aa99f49 100644 --- a/src/libstd/sys/common/backtrace.rs +++ b/src/libstd/sys/common/backtrace.rs @@ -88,7 +88,7 @@ pub fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> { while rest.len() > 0 { if rest.starts_with("$") { macro_rules! demangle { - ($($pat:expr => $demangled:expr),*) => ({ + ($($pat:expr, => $demangled:expr),*) => ({ $(if rest.starts_with($pat) { try!(writer.write_str($demangled)); rest = rest.slice_from($pat.len()); @@ -103,22 +103,22 @@ pub fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> { // see src/librustc/back/link.rs for these mappings demangle! ( - "$SP$" => "@", - "$UP$" => "Box", - "$RP$" => "*", - "$BP$" => "&", - "$LT$" => "<", - "$GT$" => ">", - "$LP$" => "(", - "$RP$" => ")", - "$C$" => ",", + "$SP$", => "@", + "$UP$", => "Box", + "$RP$", => "*", + "$BP$", => "&", + "$LT$", => "<", + "$GT$", => ">", + "$LP$", => "(", + "$RP$", => ")", + "$C$", => ",", // in theory we can demangle any Unicode code point, but // for simplicity we just catch the common ones. - "$u{20}" => " ", - "$u{27}" => "'", - "$u{5b}" => "[", - "$u{5d}" => "]" + "$u{20}", => " ", + "$u{27}", => "'", + "$u{5b}", => "[", + "$u{5d}", => "]" ) } else { let idx = match rest.find('$') { diff --git a/src/test/compile-fail/macro-input-future-proofing.rs b/src/test/compile-fail/macro-input-future-proofing.rs index 2804fc14796..1f2db624065 100644 --- a/src/test/compile-fail/macro-input-future-proofing.rs +++ b/src/test/compile-fail/macro-input-future-proofing.rs @@ -9,18 +9,18 @@ // except according to those terms. macro_rules! errors_everywhere { - ($ty:ty <) => () //~ ERROR `$ty:ty` is followed by `<`, which is not allowed for `ty` fragments - ($ty:ty < foo ,) => () //~ ERROR `$ty:ty` is followed by `<`, which is not allowed for `ty` - ($ty:ty , ) => () - ( ( $ty:ty ) ) => () - ( { $ty:ty } ) => () - ( [ $ty:ty ] ) => () - ($bl:block < ) => () - ($pa:pat >) => () //~ ERROR `$pa:pat` is followed by `>` which is not allowed for `pat` - ($pa:pat , ) => () - ($pa:pat | ) => () - ($pa:pat $pb:pat $ty:ty ,) => () - ($($ty:ty)-+) => () //~ ERROR `$ty:ty` is followed by `-` which is not allowed for `ty` + ($ty:ty <) => (); //~ ERROR `$ty:ty` is followed by `<`, which is not allowed for `ty` + ($ty:ty < foo ,) => (); //~ ERROR `$ty:ty` is followed by `<`, which is not allowed for `ty` + ($ty:ty , ) => (); + ( ( $ty:ty ) ) => (); + ( { $ty:ty } ) => (); + ( [ $ty:ty ] ) => (); + ($bl:block < ) => (); + ($pa:pat >) => (); //~ ERROR `$pa:pat` is followed by `>`, which is not allowed for `pat` + ($pa:pat , ) => (); + ($pa:pat | ) => (); //~ ERROR `$pa:pat` is followed by `|` + ($pa:pat $pb:pat $ty:ty ,) => (); + ($($ty:ty)-+) => (); //~ ERROR `$ty:ty` is followed by `-`, which is not allowed for `ty` } fn main() { } diff --git a/src/test/run-pass/const-polymorphic-paths.rs b/src/test/run-pass/const-polymorphic-paths.rs index 25c1464adfa..28b346c9ed4 100644 --- a/src/test/run-pass/const-polymorphic-paths.rs +++ b/src/test/run-pass/const-polymorphic-paths.rs @@ -29,7 +29,7 @@ fn odd(x: uint) -> bool { x % 2 == 1 } fn dummy_rng() -> DummyRng { DummyRng::new_unseeded() } macro_rules! tests { - ($($expr:expr: $ty:ty /($($test:expr),*);)+) => (pub fn main() {$({ + ($($expr:expr, $ty:ty, ($($test:expr),*);)+) => (pub fn main() {$({ const C: $ty = $expr; static S: $ty = $expr; assert!(eq(C($($test),*), $expr($($test),*))); @@ -40,45 +40,44 @@ macro_rules! tests { tests! { // Free function. - id: fn(int) -> int /(5); - id::: fn(int) -> int /(5); + id, fn(int) -> int, (5); + id::, fn(int) -> int, (5); // Enum variant constructor. - Some: fn(int) -> Option /(5); - Some::: fn(int) -> Option /(5); + Some, fn(int) -> Option, (5); + Some::, fn(int) -> Option, (5); // Tuple struct constructor. - Newt: fn(int) -> Newt /(5); - Newt::: fn(int) -> Newt /(5); + Newt, fn(int) -> Newt, (5); + Newt::, fn(int) -> Newt, (5); // Inherent static methods. - Vec::new: fn() -> Vec<()> /(); - Vec::<()>::new: fn() -> Vec<()> /(); - Vec::with_capacity: fn(uint) -> Vec<()> /(5); - Vec::<()>::with_capacity: fn(uint) -> Vec<()> /(5); - Bitv::from_fn: fn(uint, fn(uint) -> bool) -> Bitv /(5, odd); - Bitv::from_fn:: bool>: fn(uint, fn(uint) -> bool) -> Bitv /(5, odd); + Vec::new, fn() -> Vec<()>, (); + Vec::<()>::new, fn() -> Vec<()>, (); + Vec::with_capacity, fn(uint) -> Vec<()>, (5); + Vec::<()>::with_capacity, fn(uint) -> Vec<()>, (5); + Bitv::from_fn, fn(uint, fn(uint) -> bool) -> Bitv, (5, odd); + Bitv::from_fn:: bool>, fn(uint, fn(uint) -> bool) -> Bitv, (5, odd); // Inherent non-static method. - Vec::map_in_place: fn(Vec, fn(u8) -> i8) -> Vec - /(vec![b'f', b'o', b'o'], u8_as_i8); - Vec::map_in_place:: i8>: fn(Vec, fn(u8) -> i8) -> Vec - /(vec![b'f', b'o', b'o'], u8_as_i8); + Vec::map_in_place, fn(Vec, fn(u8) -> i8) -> Vec, (vec![b'f', b'o', b'o'], u8_as_i8); + Vec::map_in_place:: i8>, fn(Vec, fn(u8) -> i8) -> Vec, + (vec![b'f', b'o', b'o'], u8_as_i8); // FIXME these break with "type parameter might not appear here pointing at ``. // Vec::::map_in_place: fn(Vec, fn(u8) -> i8) -> Vec - // /(vec![b'f', b'o', b'o'], u8_as_i8); + // , (vec![b'f', b'o', b'o'], u8_as_i8); // Vec::::map_in_place:: i8>: fn(Vec, fn(u8) -> i8) -> Vec - // /(vec![b'f', b'o', b'o'], u8_as_i8); + // , (vec![b'f', b'o', b'o'], u8_as_i8); // Trait static methods. // FIXME qualified path expressions aka UFCS i.e. ::method. - Default::default: fn() -> int /(); - Rand::rand: fn(&mut DummyRng) -> int /(&mut dummy_rng()); - Rand::rand::: fn(&mut DummyRng) -> int /(&mut dummy_rng()); + Default::default, fn() -> int, (); + Rand::rand, fn(&mut DummyRng) -> int, (&mut dummy_rng()); + Rand::rand::, fn(&mut DummyRng) -> int, (&mut dummy_rng()); // Trait non-static methods. - Clone::clone: fn(&int) -> int /(&5); - FromIterator::from_iter: fn(OptionIter) -> Vec /(Some(5).into_iter()); - FromIterator::from_iter::>: fn(OptionIter) -> Vec - /(Some(5).into_iter()); + Clone::clone, fn(&int) -> int, (&5); + FromIterator::from_iter, fn(OptionIter) -> Vec, (Some(5).into_iter()); + FromIterator::from_iter::>, fn(OptionIter) -> Vec + , (Some(5).into_iter()); } diff --git a/src/test/run-pass/nullable-pointer-iotareduction.rs b/src/test/run-pass/nullable-pointer-iotareduction.rs index 9b9a7f68995..0293c4e36ac 100644 --- a/src/test/run-pass/nullable-pointer-iotareduction.rs +++ b/src/test/run-pass/nullable-pointer-iotareduction.rs @@ -35,10 +35,10 @@ impl E { } macro_rules! check_option { - ($e:expr: $T:ty) => {{ - check_option!($e: $T, |ptr| assert!(*ptr == $e)); + ($e:expr, $T:ty) => {{ + check_option!($e, $T, |ptr| assert!(*ptr == $e)); }}; - ($e:expr: $T:ty, |$v:ident| $chk:expr) => {{ + ($e:expr, $T:ty, |$v:ident| $chk:expr) => {{ assert!(option::Option::None::<$T>.is_none()); let e = $e; let s_ = option::Option::Some::<$T>(e); @@ -48,10 +48,10 @@ macro_rules! check_option { } macro_rules! check_fancy { - ($e:expr: $T:ty) => {{ - check_fancy!($e: $T, |ptr| assert!(*ptr == $e)); + ($e:expr, $T:ty) => {{ + check_fancy!($e, $T, |ptr| assert!(*ptr == $e)); }}; - ($e:expr: $T:ty, |$v:ident| $chk:expr) => {{ + ($e:expr, $T:ty, |$v:ident| $chk:expr) => {{ assert!(E::Nothing::<$T>((), ((), ()), [23i8; 0]).is_none()); let e = $e; let t_ = E::Thing::<$T>(23, e); @@ -71,12 +71,12 @@ macro_rules! check_type { } pub fn main() { - check_type!(&17: &int); - check_type!(box 18: Box); - check_type!("foo".to_string(): String); - check_type!(vec!(20, 22): Vec ); + check_type!(&17, &int); + check_type!(box 18, Box); + check_type!("foo".to_string(), String); + check_type!(vec!(20, 22), Vec ); let mint: uint = unsafe { mem::transmute(main) }; - check_type!(main: fn(), |pthing| { + check_type!(main, fn(), |pthing| { assert!(mint == unsafe { mem::transmute(*pthing) }) }); } From ac8e10519a298cdad3acb50506af3eec79995729 Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Tue, 6 Jan 2015 18:02:00 -0500 Subject: [PATCH 3/5] Stricter rules surrounding adjacent nonterminals and sequences --- src/libcore/macros.rs | 5 +- src/libsyntax/ext/tt/macro_rules.rs | 103 +++++++++++++----- .../macro-input-future-proofing.rs | 4 + 3 files changed, 82 insertions(+), 30 deletions(-) diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index a579f9db416..14e0be2cf16 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -186,8 +186,11 @@ macro_rules! write { #[macro_export] #[stable] macro_rules! writeln { - ($dst:expr, $fmt:expr $($arg:tt)*) => ( + ($dst:expr, $fmt:expr, $($arg:tt)*) => ( write!($dst, concat!($fmt, "\n") $($arg)*) + ); + ($dst:expr, $fmt:expr) => ( + write!($dst, concat!($fmt, "\n")) ) } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 96a0f7de0fd..9e1b18ad18a 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -292,58 +292,102 @@ fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &NamedMatch, sp: Span) { _ => cx.span_bug(sp, "wrong-structured lhs for follow check") }; - check_matcher(cx, matcher, &Eof); + check_matcher(cx, matcher.iter(), &Eof); // we don't abort on errors on rejection, the driver will do that for us // after parsing/expansion. we can report every error in every macro this way. } -fn check_matcher(cx: &mut ExtCtxt, matcher: &[TokenTree], follow: &Token) { +// returns the last token that was checked, for TtSequence. this gets used later on. +fn check_matcher<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token) +-> Option<(Span, Token)> where I: Iterator { use print::pprust::token_to_string; - // 1. If there are no tokens in M, accept - if matcher.is_empty() { - return; - } + let mut last = None; // 2. For each token T in M: - let mut tokens = matcher.iter().peekable(); + let mut tokens = matcher.peekable(); while let Some(token) = tokens.next() { - match *token { + last = match *token { TtToken(sp, MatchNt(ref name, ref frag_spec, _, _)) => { // ii. If T is a simple NT, look ahead to the next token T' in // M. let next_token = match tokens.peek() { // If T' closes a complex NT, replace T' with F - Some(&&TtToken(_, CloseDelim(_))) => follow, - Some(&&TtToken(_, ref tok)) => tok, - // T' is any NT (this catches complex NTs, the next - // iteration will die if it's a TtDelimited). - Some(_) => continue, + Some(&&TtToken(_, CloseDelim(_))) => follow.clone(), + Some(&&TtToken(_, ref tok)) => tok.clone(), + Some(&&TtSequence(sp, _)) => { + cx.span_err(sp, format!("`${0}:{1}` is followed by a sequence \ + repetition, which is not allowed for `{1}` \ + fragments", name.as_str(), frag_spec.as_str())[]); + Eof + }, + // die next iteration + Some(&&TtDelimited(_, ref delim)) => delim.close_token(), // else, we're at the end of the macro or sequence - None => follow + None => follow.clone() }; + let tok = if let TtToken(_, ref tok) = *token { tok } else { unreachable!() }; // If T' is in the set FOLLOW(NT), continue. Else, reject. - match *next_token { - Eof | MatchNt(..) => continue, - _ if is_in_follow(cx, next_token, frag_spec.as_str()) => continue, - ref tok => cx.span_err(sp, format!("`${0}:{1}` is followed by `{2}`, which \ - is not allowed for `{1}` fragments", - name.as_str(), frag_spec.as_str(), - token_to_string(tok))[]) + match &next_token { + &Eof => return Some((sp, tok.clone())), + _ if is_in_follow(cx, &next_token, frag_spec.as_str()) => continue, + next => { + cx.span_err(sp, format!("`${0}:{1}` is followed by `{2}`, which \ + is not allowed for `{1}` fragments", + name.as_str(), frag_spec.as_str(), + token_to_string(next))[]); + continue + }, } }, - TtSequence(_, ref seq) => { + TtSequence(sp, ref seq) => { // iii. Else, T is a complex NT. match seq.separator { // If T has the form $(...)U+ or $(...)U* for some token U, // run the algorithm on the contents with F set to U. If it // accepts, continue, else, reject. - Some(ref u) => check_matcher(cx, seq.tts[], u), - // If T has the form $(...)+ or $(...)*, run the algorithm - // on the contents with F set to EOF. If it accepts, - // continue, else, reject. - None => check_matcher(cx, seq.tts[], &Eof) + Some(ref u) => { + let last = check_matcher(cx, seq.tts.iter(), u); + match last { + // Since the delimiter isn't required after the last repetition, make + // sure that the *next* token is sane. This doesn't actually compute + // the FIRST of the rest of the matcher yet, it only considers single + // tokens and simple NTs. This is imprecise, but conservatively + // correct. + Some((span, tok)) => { + let fol = match tokens.peek() { + Some(&&TtToken(_, ref tok)) => tok.clone(), + Some(&&TtDelimited(_, ref delim)) => delim.close_token(), + Some(_) => { + cx.span_err(sp, "sequence repetition followed by \ + another sequence repetition, which is not allowed"); + Eof + }, + None => Eof + }; + check_matcher(cx, Some(&TtToken(span, tok.clone())).into_iter(), + &fol) + }, + None => last, + } + }, + // If T has the form $(...)+ or $(...)*, run the algorithm on the contents with + // F set to the token following the sequence. If it accepts, continue, else, + // reject. + None => { + let fol = match tokens.peek() { + Some(&&TtToken(_, ref tok)) => tok.clone(), + Some(&&TtDelimited(_, ref delim)) => delim.close_token(), + Some(_) => { + cx.span_err(sp, "sequence repetition followed by another \ + sequence repetition, which is not allowed"); + Eof + }, + None => Eof + }; + check_matcher(cx, seq.tts.iter(), &fol) + } } }, TtToken(..) => { @@ -352,11 +396,12 @@ fn check_matcher(cx: &mut ExtCtxt, matcher: &[TokenTree], follow: &Token) { }, TtDelimited(_, ref tts) => { // if we don't pass in that close delimiter, we'll incorrectly consider the matcher - // `{ $foo:ty }` as having a follow that isn't `}` - check_matcher(cx, tts.tts[], &tts.close_token()) + // `{ $foo:ty }` as having a follow that isn't `RBrace` + check_matcher(cx, tts.tts.iter(), &tts.close_token()) } } } + last } fn is_in_follow(cx: &ExtCtxt, tok: &Token, frag: &str) -> bool { diff --git a/src/test/compile-fail/macro-input-future-proofing.rs b/src/test/compile-fail/macro-input-future-proofing.rs index 1f2db624065..15f6d88fd89 100644 --- a/src/test/compile-fail/macro-input-future-proofing.rs +++ b/src/test/compile-fail/macro-input-future-proofing.rs @@ -20,6 +20,10 @@ macro_rules! errors_everywhere { ($pa:pat , ) => (); ($pa:pat | ) => (); //~ ERROR `$pa:pat` is followed by `|` ($pa:pat $pb:pat $ty:ty ,) => (); + //~^ ERROR `$pa:pat` is followed by `$pb:pat`, which is not allowed + //~^^ ERROR `$pb:pat` is followed by `$ty:ty`, which is not allowed + ($($ty:ty)* -) => (); //~ ERROR `$ty:ty` is followed by `-` + ($($a:ty, $b:ty)* -) => (); //~ ERROR `$b:ty` is followed by `-` ($($ty:ty)-+) => (); //~ ERROR `$ty:ty` is followed by `-`, which is not allowed for `ty` } From bd4119f9654eda89e359234a08b1ac4fae53287c Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Tue, 6 Jan 2015 18:46:37 -0500 Subject: [PATCH 4/5] Minor fallout/update FOLLOW sets --- src/libcore/macros.rs | 8 ++++---- src/libstd/rt/macros.rs | 16 ++++++++++++---- src/libsyntax/ext/tt/macro_rules.rs | 4 ++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index 14e0be2cf16..fcd93ad4a02 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -186,12 +186,12 @@ macro_rules! write { #[macro_export] #[stable] macro_rules! writeln { - ($dst:expr, $fmt:expr, $($arg:tt)*) => ( - write!($dst, concat!($fmt, "\n") $($arg)*) - ); ($dst:expr, $fmt:expr) => ( write!($dst, concat!($fmt, "\n")) - ) + ); + ($dst:expr, $fmt:expr, $($arg:expr),*) => ( + write!($dst, concat!($fmt, "\n"), $($arg,)*) + ); } /// A utility macro for indicating unreachable code. diff --git a/src/libstd/rt/macros.rs b/src/libstd/rt/macros.rs index bbc96d0b19f..1e3ab6d34da 100644 --- a/src/libstd/rt/macros.rs +++ b/src/libstd/rt/macros.rs @@ -14,16 +14,24 @@ //! they aren't defined anywhere outside of the `rt` module. macro_rules! rterrln { - ($fmt:expr $($arg:tt)*) => ( { - ::rt::util::dumb_print(format_args!(concat!($fmt, "\n") $($arg)*)) + ($fmt:expr) => ( { + ::rt::util::dumb_print(format_args!(concat!($fmt, "\n"))) + } ); + ($fmt:expr, $($arg:expr),*) => ( { + ::rt::util::dumb_print(format_args!(concat!($fmt, "\n"), $($arg)*)) } ) } // Some basic logging. Enabled by passing `--cfg rtdebug` to the libstd build. macro_rules! rtdebug { - ($($arg:tt)*) => ( { + ($arg:expr) => ( { if cfg!(rtdebug) { - rterrln!($($arg)*) + rterrln!($arg) + } + } ); + ($str:expr, $($arg:expr),*) => ( { + if cfg!(rtdebug) { + rterrln!($str, $($arg)*) } }) } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 9e1b18ad18a..6a064ec1313 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -422,7 +422,7 @@ fn is_in_follow(cx: &ExtCtxt, tok: &Token, frag: &str) -> bool { }, "stmt" | "expr" => { match *tok { - Comma | Semi => true, + FatArrow | Comma | Semi => true, _ => false } }, @@ -434,7 +434,7 @@ fn is_in_follow(cx: &ExtCtxt, tok: &Token, frag: &str) -> bool { }, "path" | "ty" => { match *tok { - Comma | RArrow | Colon | Eq | Gt => true, + Comma | FatArrow | Colon | Eq | Gt => true, Ident(i, _) if i.as_str() == "as" => true, _ => false } From e9cbdd866d1c9c01e9affaf6875e37e58a073758 Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Tue, 6 Jan 2015 18:47:49 -0500 Subject: [PATCH 5/5] serialize macro fix --- src/libserialize/serialize.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs index a04f67f7651..41e449ba571 100644 --- a/src/libserialize/serialize.rs +++ b/src/libserialize/serialize.rs @@ -499,7 +499,7 @@ macro_rules! peel { /// Evaluates to the number of identifiers passed to it, for example: `count_idents!(a, b, c) == 3 macro_rules! count_idents { () => { 0u }; - ($_i:ident $(, $rest:ident)*) => { 1 + count_idents!($($rest),*) } + ($_i:ident, $($rest:ident),*) => { 1 + count_idents!($($rest),*) } } macro_rules! tuple {