-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proc_macro: implement TokenTree
, TokenKind
, hygienic quote!
, and other API
#40939
Conversation
30f3956
to
40f81aa
Compare
cc @rust-lang/compiler @abonander @dtolnay @Ericson2314 @SergioBenitez @SimonSapin @sgrif @tikue @wycats |
Can't we call |
Nice! I like the overall approach, some comments on the details:
Would impls for
To me the word "symbol" evokes punctuation, excluding letters. Could this be called something else?
So the
I’d also add:
In #[unstable(feature = "proc_macro", issue = "38356")]
#[macro_export]
macro_rules! quote { () => {} } In macro_rules! quote {
() => { TokenStream::empty() };
($($t:tt)*) => { [ $( quote_tree!($t), )* ].iter().cloned().collect::<TokenStream>() };
} Why are there two |
I think this PR addresses all of my previous comments in #38356 (comment) 👍 |
☔ The latest upstream changes (presumably #40950) made this pull request unmergeable. Please resolve the merge conflicts. |
Do you mean w.r.t. the internals? I believe we can still use the internal flattened representation you proposed with this API (imo, would be nice but not high priority).
Yeah. Currently,
Yeah, it's
Yeah, I agree that we should have
The non-empty one is incomplete (by design) and only used to bootstrap the internal implementation of |
9999e58
to
6151672
Compare
☔ The latest upstream changes (presumably #40811) made this pull request unmergeable. Please resolve the merge conflicts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a bunch of changes requested, but most are just about docs, and I don't think there is anything major. Assuming all the fixes are straightforward, then r=me
src/libproc_macro/lib.rs
Outdated
pub struct TokenStream { | ||
inner: TokenStream_, | ||
} | ||
pub struct TokenStream(tokenstream::TokenStream); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not this PR (and pretty nitty), but just pointing out that it should either token_stream
or Tokenstream
, currently the naming is inconsistent.
src/libproc_macro/lib.rs
Outdated
|
||
/// An iterator over `TokenTree`s. | ||
#[unstable(feature = "proc_macro", issue = "38356")] | ||
pub struct TokenIter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we call this a TokenTreeIter
? That leaves room for a flattening TokenIter and means the name and target correspond
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/libproc_macro/lib.rs
Outdated
} | ||
|
||
impl TokenTree { | ||
fn from_raw(stream: tokenstream::TokenStream, next: &mut Option<tokenstream::TokenStream>) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like from_raw
as a name - 'raw' can mean so many different things, and perhaps the most likely here is as in raw string which is wrong. How about from_internal
or from_rustc_internal
or something like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change to from_internal
.
src/libproc_macro/lib.rs
Outdated
TokenTree { span: Span(span), kind: kind } | ||
} | ||
|
||
fn to_raw(self) -> tokenstream::TokenStream { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
likewise here.
And somewhat connected, is there a way to mark the API which we expect macro authors to use? Is it all the pub
API? Or is some of that transitional or only intended for the compiler to use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's all the pub
API not in __internal
(which is unusable without #![feature(proc_macro_internals)]
).
src/libproc_macro/lib.rs
Outdated
@@ -80,7 +524,11 @@ pub struct LexError { | |||
/// all of the contents. | |||
#[unstable(feature = "proc_macro_internals", issue = "27812")] | |||
#[doc(hidden)] | |||
#[path = ""] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this for? I'm not sure what the effect is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mod quote;
in __internal
; this allows the file to be libproc_macro/quote.rs
. Thinking about this some more, I'll move mod quote
into the crate root and remove the #[path = ""]
.
src/libproc_macro/quote.rs
Outdated
|
||
//! # Quasiquoter | ||
//! This file contains the implementation internals of the quasiquoter provided by `qquote!`. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add an overview of how the quasi-quoter works please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
src/libsyntax/tokenstream.rs
Outdated
@@ -196,6 +201,10 @@ impl TokenStream { | |||
} | |||
} | |||
|
|||
pub fn builder() -> TokenStreamBuilder { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would be better as a free function rather than a method on TokenStream
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or it could be a new
method on TokenStreamBuilder
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change to TokenStreamBuilder::new()
(motivation for TokenStream::builder()
was to avoid making people use
anything else into their scopes).
src/libsyntax/tokenstream.rs
Outdated
@@ -225,13 +234,107 @@ impl TokenStream { | |||
} | |||
true | |||
} | |||
|
|||
pub fn as_tree(self) -> (TokenTree, bool /* joint? */) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could these public methods get docs please? (In particular here, some more explanation of what joint means would be good).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, since this takes self
by value, should this be named into_tree
instead? Or are the conventions different for reference counted things
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -8,47 +8,37 @@ | |||
// option. This file may not be copied, modified, or distributed | |||
// except according to those terms. | |||
|
|||
#![feature(plugin, plugin_registrar, rustc_private)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like anyone with an existing procedural macro is going to have to make some significant changes. Could you write up either here or on internals or somewhere, a guide to the changes needed to make a proc macro work after this PR please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Existing procedural macros will lose the syntax::tokenstream::TokenStream
quoter from proc_macro_internals
, AFAIK other than that they won't have to make any changes. I could include a syntax::tokenstream::TokenStream
quoter for back-compat.
How many people are actually using "existing procedural macros" though? This PR doesn't effect the widely used legacy plugin system; I didn't think many people were using SyntaxExtension::ProcMacro
from #36154.
src/libsyntax/parse/token.rs
Outdated
@@ -455,3 +461,38 @@ pub fn is_op(tok: &Token) -> bool { | |||
_ => true, | |||
} | |||
} | |||
|
|||
#[derive(Clone, Eq, PartialEq, Debug)] | |||
pub struct LazyTokenStream(RefCell<Option<TokenStream>>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this meant to be used by macro authors? If so could you document with why and when.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, nothing in libsyntax
can be used by proc macro authors.
src/libsyntax/parse/token.rs
Outdated
} | ||
|
||
#[derive(Clone, Eq, PartialEq, Debug)] | ||
pub struct LazyTokenStream(RefCell<Option<TokenStream>>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an aside: if the dynamic borrow checks end up too inefficient, then this could be re-implemented on top of MoveCell
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::cell::Cell
is now like MoveCell
:) rust-lang/rfcs#1651
src/libsyntax/parse/token.rs
Outdated
let mut opt_stream = self.0.borrow_mut(); | ||
if opt_stream.is_none() { | ||
*opt_stream = Some(f()); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: extra semicolon
Friendly ping that there are merge conflicts and some comments for you @jseyfried! ❤️ |
Should we use a different feature gate for the unstable APIs? I'm under the impression that language feature gates and library feature gates are meant to be distinct from each other, and |
Hmm, is there a strict separation between language/lib feature-gates? I wouldn't think so. |
It's kind of implied by the structure of the Unstable Book but that might be more of an issue for #41476 where this came up originally. |
status: still waiting for #40847. |
status: still waiting for #40847. |
1 similar comment
status: still waiting for #40847. |
The lack of expansion info broke clippy together with proc macros, since code inside derives will now trigger lints. Is there any new way to detect expanded code? If not, what needs to be done in order to create expansion info that doesn't influence save analysis. See https://github.com/Manishearth/rust-clippy/issues/1877 |
Ah, I wondered why I was seeing so many errors in the RLS :-( I think we must fix this. This feels like another reason not to conflate macro hygiene with expansion history. That might be too hard a way to fix this though.
Not using spans for hygiene seems a much nicer fix here - the span should reflect the expansion history and the hygiene should be altered (to erase hygiene, essentially). I expect we want the correct expansion info for errors as well as save-analysis. |
I already have a fix in #43179 |
@nrc That is, I don't think think hygiene bending requires us to split up span/hygiene info or construct an "fake" expansion history just for hygiene. I think it'd be nicer to have a single source of truth for expansion info that is expressive enough for hygiene bending. |
Tidy: allow common lang+lib features This allows changes to the Rust language that have both library and language components share one feature gate. The feature gates need to be "about the same change", so that both library and language components must either be both unstable, or both stable, and share the tracking issue. Removes the ugly "proc_macro" exception added by #40939. Closes #43089
Three small fixes for save-analysis First commit does some naive deduplication of macro uses. We end up with lots of duplication here because of the weird way we get this data (we extract a use for every span generated by a macro use). Second commit is basically a typo fix. Third commit is a bit interesting, it partially reverts a change from #40939 where temporary variables in format! (and thus println!) got a span with the primary pointing at the value stored into the temporary (e.g., `x` in `println!("...", x)`). If `format!` had a definition it should point at the temporary in the macro def, but since it is built-in, that is not possible (for now), so `DUMMY_SP` is the best we can do (using the span in the callee really breaks save-analysis because it thinks `x` is a definition as well as a reference). There aren't a test for this stuff because: the deduplication is filtered by any of the users of save-analysis, so it is purely an efficiency change. I couldn't actually find an example for the second commit that we have any machinery to test, and the third commit is tested by the RLS, so there will be a test once I update the RLS version and and uncomment the previously failing tests). r? @jseyfried
Remove `LazyTokenStream`. `LazyTokenStream` was added in #40939. Perhaps it was an effective optimization then, but no longer. This PR removes it, making the code both simpler and faster. r? @alexcrichton
…etrochenkov Remove `LazyTokenStream`. `LazyTokenStream` was added in rust-lang#40939. Perhaps it was an effective optimization then, but no longer. This PR removes it, making the code both simpler and faster. r? @alexcrichton
…etrochenkov Remove `LazyTokenStream`. `LazyTokenStream` was added in rust-lang#40939. Perhaps it was an effective optimization then, but no longer. This PR removes it, making the code both simpler and faster. r? @alexcrichton
…etrochenkov Remove `LazyTokenStream`. `LazyTokenStream` was added in rust-lang#40939. Perhaps it was an effective optimization then, but no longer. This PR removes it, making the code both simpler and faster. r? @alexcrichton
…etrochenkov Remove `LazyTokenStream`. `LazyTokenStream` was added in rust-lang#40939. Perhaps it was an effective optimization then, but no longer. This PR removes it, making the code both simpler and faster. r? @alexcrichton
…etrochenkov Remove `LazyTokenStream`. `LazyTokenStream` was added in rust-lang#40939. Perhaps it was an effective optimization then, but no longer. This PR removes it, making the code both simpler and faster. r? @alexcrichton
…etrochenkov Remove `LazyTokenStream`. `LazyTokenStream` was added in rust-lang#40939. Perhaps it was an effective optimization then, but no longer. This PR removes it, making the code both simpler and faster. r? @alexcrichton
This is verified to compile and work against the current version of rust-lang/rust#40939.
All new API is gated behind
#![feature(proc_macro)]
and may be used with#[proc_macro]
,#[proc_macro_attribute]
, and#[proc_macro_derive]
procedural macros.More specifically, this PR adds the following in
proc_macro
:For details on
quote!
hygiene, see this example and declarative macros 2.0.r? @nrc