From c1ffe04aebbccadf6eef063ec2809442ce96cb3b Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Thu, 21 Apr 2016 09:23:10 -0600 Subject: [PATCH 1/2] Allow lifetimes to be passed to macros Partially fixes #10413. This only handles lifetimes, as they're much more "obvious" than the type parameter list would be, as that implies that we probably want to mirror `ast::Generics` as well as all of the individual pieces. That should probably have an RFC. These are essentially just treated the same as idents. They're a single token, and are quite pervasive, so we interpolate them back into a real token rather than handling `NtLifetime` all over the parser. This change is specifically needed in Diesel in order for us to provide a "normal macro" alternative to some of our syntax extensions. With this change, we'd be able to have a decent stable story without syntex. --- src/libsyntax/ext/tt/macro_parser.rs | 5 ++-- src/libsyntax/ext/tt/macro_rules.rs | 8 ++++--- src/libsyntax/ext/tt/transcribe.rs | 9 +++++++- src/libsyntax/fold.rs | 1 + src/libsyntax/parse/parser.rs | 3 +++ src/libsyntax/parse/token.rs | 2 ++ src/libsyntax/print/pprust.rs | 1 + .../macro-lifetime-used-with-bound.rs | 23 +++++++++++++++++++ .../macro-lifetime-used-with-static.rs | 23 +++++++++++++++++++ src/test/run-pass/macro-lifetime.rs | 23 +++++++++++++++++++ 10 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 src/test/run-pass/macro-lifetime-used-with-bound.rs create mode 100644 src/test/run-pass/macro-lifetime-used-with-static.rs create mode 100644 src/test/run-pass/macro-lifetime.rs diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index 4e4c644776a51..e59a90a2ef544 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -549,12 +549,13 @@ pub fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal { token::NtPath(Box::new(panictry!(p.parse_path(LifetimeAndTypesWithoutColons)))) }, "meta" => token::NtMeta(panictry!(p.parse_meta_item())), + "lifetime" => token::NtLifetime(panictry!(p.parse_lifetime())), _ => { p.span_fatal_help(sp, &format!("invalid fragment specifier `{}`", name), "valid fragment specifiers are `ident`, `block`, \ - `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \ - and `item`").emit(); + `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt`, \ + `lifetime`, and `item`").emit(); panic!(FatalError); } } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 87ab3dad50c70..ef3f54a3d0b9b 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -941,6 +941,7 @@ fn frag_can_be_followed_by_any(frag: &str) -> bool { "block" | // exactly one token tree "ident" | // exactly one token tree "meta" | // exactly one token tree + "lifetime" | // exactly one token tree "tt" => // exactly one token tree true, @@ -963,6 +964,7 @@ fn can_be_followed_by_any(frag: &str) -> bool { "block" | // exactly one token tree "ident" | // exactly one token tree "meta" | // exactly one token tree + "lifetime" | // exactly one token tree "tt" => // exactly one token tree true, @@ -1020,8 +1022,8 @@ fn is_in_follow(_: &ExtCtxt, tok: &Token, frag: &str) -> Result { _ => Ok(false) } }, - "ident" => { - // being a single token, idents are harmless + "ident" | "lifetime" => { + // being a single token, idents and lifetimes are harmless Ok(true) }, "meta" | "tt" => { @@ -1048,7 +1050,7 @@ fn has_legal_fragment_specifier(tok: &Token) -> Result<(), String> { fn is_legal_fragment_specifier(frag: &str) -> bool { match frag { "item" | "block" | "stmt" | "expr" | "pat" | - "path" | "ty" | "ident" | "meta" | "tt" => true, + "path" | "ty" | "ident" | "meta" | "tt" | "lifetime" => true, _ => false, } } diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs index ae99fe817395f..eaa7afdd6e09a 100644 --- a/src/libsyntax/ext/tt/transcribe.rs +++ b/src/libsyntax/ext/tt/transcribe.rs @@ -15,7 +15,7 @@ use codemap::{Span, DUMMY_SP}; use errors::Handler; use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal}; use parse::token::{DocComment, MatchNt, SubstNt}; -use parse::token::{Token, NtIdent, SpecialMacroVar}; +use parse::token::{Token, NtIdent, NtLifetime, SpecialMacroVar}; use parse::token; use parse::lexer::TokenAndSpan; @@ -297,6 +297,13 @@ pub fn tt_next_token(r: &mut TtReader) -> TokenAndSpan { r.cur_tok = token::Ident(sn.node, b); return ret_val; } + // interpolate lifetimes as well for the same reasons as + // idents + MatchedNonterminal(NtLifetime(ref lt)) => { + r.cur_span = lt.span; + r.cur_tok = token::Lifetime(ast::Ident::with_empty_ctxt(lt.name)); + return ret_val; + } MatchedNonterminal(ref other_whole_nt) => { // FIXME(pcwalton): Bad copy. r.cur_span = sp; diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 89451e795503f..7fbbcf2ff0254 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -681,6 +681,7 @@ pub fn noop_fold_interpolated(nt: token::Nonterminal, fld: &mut T) token::NtWhereClause(where_clause) => token::NtWhereClause(fld.fold_where_clause(where_clause)), token::NtArg(arg) => token::NtArg(fld.fold_arg(arg)), + token::NtLifetime(lifetime) => token::NtLifetime(fld.fold_lifetime(lifetime)), } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f3d3bbd9f9905..b28adcb02b007 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1955,6 +1955,9 @@ impl<'a> Parser<'a> { name: i.name }); } + token::Interpolated(token::NtLifetime(..)) => { + self.bug("lifetime interpolation not converted to real token"); + } _ => { return Err(self.fatal("expected a lifetime name")); } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 16417ac004461..3ebfcc7bb2a1c 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -397,6 +397,7 @@ pub enum Nonterminal { NtGenerics(ast::Generics), NtWhereClause(ast::WhereClause), NtArg(ast::Arg), + NtLifetime(ast::Lifetime), } impl fmt::Debug for Nonterminal { @@ -418,6 +419,7 @@ impl fmt::Debug for Nonterminal { NtGenerics(..) => f.pad("NtGenerics(..)"), NtWhereClause(..) => f.pad("NtWhereClause(..)"), NtArg(..) => f.pad("NtArg(..)"), + NtLifetime(..) => f.pad("NtLifetime(..)"), } } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 95f1b63168b47..e087cb4204a1e 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -302,6 +302,7 @@ pub fn token_to_string(tok: &Token) -> String { token::NtGenerics(ref e) => generics_to_string(&e), token::NtWhereClause(ref e) => where_clause_to_string(&e), token::NtArg(ref e) => arg_to_string(&e), + token::NtLifetime(ref e) => lifetime_to_string(&e), } } } diff --git a/src/test/run-pass/macro-lifetime-used-with-bound.rs b/src/test/run-pass/macro-lifetime-used-with-bound.rs new file mode 100644 index 0000000000000..b9e1fde6b1f3e --- /dev/null +++ b/src/test/run-pass/macro-lifetime-used-with-bound.rs @@ -0,0 +1,23 @@ +// Copyright 2012 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! foo { + ($l:lifetime, $l2:lifetime) => { + fn f<$l: $l2, $l2>(arg: &$l str, arg2: &$l2 str) -> &$l str { + arg + } + } +} + +pub fn main() { + foo!('a, 'b); + let x: &'static str = f("hi", "there"); + assert_eq!("hi", x); +} diff --git a/src/test/run-pass/macro-lifetime-used-with-static.rs b/src/test/run-pass/macro-lifetime-used-with-static.rs new file mode 100644 index 0000000000000..5c1f8683e00f6 --- /dev/null +++ b/src/test/run-pass/macro-lifetime-used-with-static.rs @@ -0,0 +1,23 @@ +// Copyright 2012 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! foo { + ($l:lifetime) => { + fn f(arg: &$l str) -> &$l str { + arg + } + } +} + +pub fn main() { + foo!('static); + let x: &'static str = f("hi"); + assert_eq!("hi", x); +} diff --git a/src/test/run-pass/macro-lifetime.rs b/src/test/run-pass/macro-lifetime.rs new file mode 100644 index 0000000000000..ff5798ff78d62 --- /dev/null +++ b/src/test/run-pass/macro-lifetime.rs @@ -0,0 +1,23 @@ +// Copyright 2012 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! foo { + ($l:lifetime) => { + fn f<$l>(arg: &$l str) -> &$l str { + arg + } + } +} + +pub fn main() { + foo!('a); + let x: &'static str = f("hi"); + assert_eq!("hi", x); +} From 711c3b5109b5b54ebf2e41b0c9acf7f325f6c7c8 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Sat, 23 Apr 2016 17:12:42 -0600 Subject: [PATCH 2/2] Implement `ToTokens` for `ast::Lifetime` All of the other non-terminals have this implementation. If we allow them in macros, we should allow them in quoting --- src/libsyntax/ext/quote.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index 77aeaf8459aec..350dffd024785 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -179,6 +179,13 @@ pub mod rt { } } + impl ToTokens for ast::Lifetime { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec { + let lifetime_ident = ast::Ident::with_empty_ctxt(self.name); + vec![TokenTree::Token(DUMMY_SP, token::Lifetime(lifetime_ident))] + } + } + macro_rules! impl_to_tokens_slice { ($t: ty, $sep: expr) => { impl ToTokens for [$t] {