Skip to content

Commit 6d062a1

Browse files
committed
Use a proc macro to declare preallocated symbols
1 parent 53f2165 commit 6d062a1

File tree

9 files changed

+312
-139
lines changed

9 files changed

+312
-139
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3400,6 +3400,7 @@ dependencies = [
34003400
"arena 0.0.0",
34013401
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
34023402
"rustc_data_structures 0.0.0",
3403+
"rustc_macros 0.1.0",
34033404
"scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
34043405
"serialize 0.0.0",
34053406
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",

src/librustc_macros/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@ use proc_macro::TokenStream;
99

1010
mod hash_stable;
1111
mod query;
12+
mod symbols;
1213

1314
#[proc_macro]
1415
pub fn rustc_queries(input: TokenStream) -> TokenStream {
1516
query::rustc_queries(input)
1617
}
1718

19+
#[proc_macro]
20+
pub fn symbols(input: TokenStream) -> TokenStream {
21+
symbols::symbols(input)
22+
}
23+
1824
decl_derive!([HashStable, attributes(stable_hasher)] => hash_stable::hash_stable_derive);

src/librustc_macros/src/symbols.rs

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
use proc_macro::TokenStream;
2+
use syn::{
3+
Token, Ident, LitStr,
4+
braced, parse_macro_input,
5+
};
6+
use syn::parse::{Result, Parse, ParseStream};
7+
use syn;
8+
use std::collections::HashSet;
9+
use quote::quote;
10+
11+
#[allow(non_camel_case_types)]
12+
mod kw {
13+
syn::custom_keyword!(Keywords);
14+
syn::custom_keyword!(Other);
15+
}
16+
17+
struct Keyword {
18+
name: Ident,
19+
value: LitStr,
20+
}
21+
22+
impl Parse for Keyword {
23+
fn parse(input: ParseStream<'_>) -> Result<Self> {
24+
let name = input.parse()?;
25+
input.parse::<Token![,]>()?;
26+
let value = input.parse()?;
27+
input.parse::<Token![,]>()?;
28+
29+
Ok(Keyword {
30+
name,
31+
value,
32+
})
33+
}
34+
}
35+
36+
struct Symbol(Ident);
37+
38+
impl Parse for Symbol {
39+
fn parse(input: ParseStream<'_>) -> Result<Self> {
40+
let ident: Ident = input.parse()?;
41+
input.parse::<Token![,]>()?;
42+
43+
Ok(Symbol(ident))
44+
}
45+
}
46+
47+
/// A type used to greedily parse another type until the input is empty.
48+
struct List<T>(Vec<T>);
49+
50+
impl<T: Parse> Parse for List<T> {
51+
fn parse(input: ParseStream<'_>) -> Result<Self> {
52+
let mut list = Vec::new();
53+
while !input.is_empty() {
54+
list.push(input.parse()?);
55+
}
56+
Ok(List(list))
57+
}
58+
}
59+
60+
struct Input {
61+
keywords: List<Keyword>,
62+
symbols: List<Symbol>,
63+
}
64+
65+
impl Parse for Input {
66+
fn parse(input: ParseStream<'_>) -> Result<Self> {
67+
input.parse::<kw::Keywords>()?;
68+
let content;
69+
braced!(content in input);
70+
let keywords = content.parse()?;
71+
72+
input.parse::<kw::Other>()?;
73+
let content;
74+
braced!(content in input);
75+
let symbols = content.parse()?;
76+
77+
Ok(Input {
78+
keywords,
79+
symbols,
80+
})
81+
}
82+
}
83+
84+
pub fn symbols(input: TokenStream) -> TokenStream {
85+
let input = parse_macro_input!(input as Input);
86+
87+
let mut keyword_stream = quote! {};
88+
let mut symbols_stream = quote! {};
89+
let mut prefill_stream = quote! {};
90+
let mut from_str_stream = quote! {};
91+
let mut counter = 0u32;
92+
let mut keys = HashSet::<String>::new();
93+
94+
let mut check_dup = |str: &str| {
95+
if !keys.insert(str.to_string()) {
96+
panic!("Symbol `{}` is duplicated", str);
97+
}
98+
};
99+
100+
for keyword in &input.keywords.0 {
101+
let name = &keyword.name;
102+
let value = &keyword.value;
103+
check_dup(&value.value());
104+
prefill_stream.extend(quote! {
105+
#value,
106+
});
107+
keyword_stream.extend(quote! {
108+
pub const #name: Keyword = Keyword {
109+
ident: Ident::with_empty_ctxt(super::Symbol::new(#counter))
110+
};
111+
});
112+
from_str_stream.extend(quote! {
113+
#value => Ok(#name),
114+
});
115+
counter += 1;
116+
}
117+
118+
for symbol in &input.symbols.0 {
119+
let value = &symbol.0;
120+
let value_str = value.to_string();
121+
check_dup(&value_str);
122+
prefill_stream.extend(quote! {
123+
#value_str,
124+
});
125+
symbols_stream.extend(quote! {
126+
pub const #value: Symbol = Symbol::new(#counter);
127+
});
128+
counter += 1;
129+
}
130+
131+
TokenStream::from(quote! {
132+
#[allow(non_upper_case_globals)]
133+
pub mod keywords {
134+
use super::{Symbol, Ident};
135+
#[derive(Clone, Copy, PartialEq, Eq)]
136+
pub struct Keyword {
137+
ident: Ident,
138+
}
139+
impl Keyword {
140+
#[inline] pub fn ident(self) -> Ident { self.ident }
141+
#[inline] pub fn name(self) -> Symbol { self.ident.name }
142+
}
143+
144+
#keyword_stream
145+
146+
impl std::str::FromStr for Keyword {
147+
type Err = ();
148+
149+
fn from_str(s: &str) -> Result<Self, ()> {
150+
match s {
151+
#from_str_stream
152+
_ => Err(()),
153+
}
154+
}
155+
}
156+
}
157+
158+
#[allow(non_upper_case_globals)]
159+
pub mod symbols {
160+
use super::Symbol;
161+
162+
#symbols_stream
163+
}
164+
165+
impl Interner {
166+
pub fn fresh() -> Self {
167+
Interner::prefill(&[
168+
#prefill_stream
169+
])
170+
}
171+
}
172+
})
173+
}

src/libsyntax/ast.rs

+6
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ pub struct Path {
6868
pub segments: Vec<PathSegment>,
6969
}
7070

71+
impl PartialEq<Symbol> for Path {
72+
fn eq(&self, symbol: &Symbol) -> bool {
73+
self.segments.len() == 1 && self.segments[0].ident.name.interned() == *symbol
74+
}
75+
}
76+
7177
impl<'a> PartialEq<&'a str> for Path {
7278
fn eq(&self, string: &&'a str) -> bool {
7379
self.segments.len() == 1 && self.segments[0].ident.name == *string

src/libsyntax/attr/mod.rs

+21
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ impl NestedMetaItem {
8585
self.meta_item().map_or(false, |meta_item| meta_item.check_name(name))
8686
}
8787

88+
/// Returns `true` if this list item is a MetaItem with a name of `name`.
89+
pub fn check_name_symbol(&self, name: Symbol) -> bool {
90+
self.meta_item().map_or(false, |meta_item| meta_item.check_name_symbol(name))
91+
}
92+
8893
/// For a single-segment meta-item returns its name, otherwise returns `None`.
8994
pub fn ident(&self) -> Option<Ident> {
9095
self.meta_item().and_then(|meta_item| meta_item.ident())
@@ -159,6 +164,18 @@ impl Attribute {
159164
matches
160165
}
161166

167+
/// Returns `true` if the attribute's path matches the argument. If it matches, then the
168+
/// attribute is marked as used.
169+
///
170+
/// To check the attribute name without marking it used, use the `path` field directly.
171+
pub fn check_name_symbol(&self, name: Symbol) -> bool {
172+
let matches = self.path == name;
173+
if matches {
174+
mark_used(self);
175+
}
176+
matches
177+
}
178+
162179
/// For a single-segment attribute returns its name, otherwise returns `None`.
163180
pub fn ident(&self) -> Option<Ident> {
164181
if self.path.segments.len() == 1 {
@@ -248,6 +265,10 @@ impl MetaItem {
248265
self.path == name
249266
}
250267

268+
pub fn check_name_symbol(&self, name: Symbol) -> bool {
269+
self.path == name
270+
}
271+
251272
pub fn is_value_str(&self) -> bool {
252273
self.value_str().is_some()
253274
}

src/libsyntax/feature_gate.rs

+14-14
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use crate::tokenstream::TokenTree;
2828
use errors::{DiagnosticBuilder, Handler};
2929
use rustc_data_structures::fx::FxHashMap;
3030
use rustc_target::spec::abi::Abi;
31-
use syntax_pos::{Span, DUMMY_SP};
31+
use syntax_pos::{Span, DUMMY_SP, symbols};
3232
use log::debug;
3333

3434
use std::env;
@@ -1366,7 +1366,7 @@ impl<'a> Context<'a> {
13661366
}
13671367
} else if n == "doc" {
13681368
if let Some(content) = attr.meta_item_list() {
1369-
if content.iter().any(|c| c.check_name("include")) {
1369+
if content.iter().any(|c| c.check_name_symbol(symbols::include)) {
13701370
gate_feature!(self, external_doc, attr.span,
13711371
"#[doc(include = \"...\")] is experimental"
13721372
);
@@ -1648,33 +1648,33 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
16481648
// check for gated attributes
16491649
self.context.check_attribute(attr, false);
16501650

1651-
if attr.check_name("doc") {
1651+
if attr.check_name_symbol(symbols::doc) {
16521652
if let Some(content) = attr.meta_item_list() {
1653-
if content.len() == 1 && content[0].check_name("cfg") {
1653+
if content.len() == 1 && content[0].check_name_symbol(symbols::cfg) {
16541654
gate_feature_post!(&self, doc_cfg, attr.span,
16551655
"#[doc(cfg(...))] is experimental"
16561656
);
1657-
} else if content.iter().any(|c| c.check_name("masked")) {
1657+
} else if content.iter().any(|c| c.check_name_symbol(symbols::masked)) {
16581658
gate_feature_post!(&self, doc_masked, attr.span,
16591659
"#[doc(masked)] is experimental"
16601660
);
1661-
} else if content.iter().any(|c| c.check_name("spotlight")) {
1661+
} else if content.iter().any(|c| c.check_name_symbol(symbols::spotlight)) {
16621662
gate_feature_post!(&self, doc_spotlight, attr.span,
16631663
"#[doc(spotlight)] is experimental"
16641664
);
1665-
} else if content.iter().any(|c| c.check_name("alias")) {
1665+
} else if content.iter().any(|c| c.check_name_symbol(symbols::alias)) {
16661666
gate_feature_post!(&self, doc_alias, attr.span,
16671667
"#[doc(alias = \"...\")] is experimental"
16681668
);
1669-
} else if content.iter().any(|c| c.check_name("keyword")) {
1669+
} else if content.iter().any(|c| c.check_name_symbol(symbols::keyword)) {
16701670
gate_feature_post!(&self, doc_keyword, attr.span,
16711671
"#[doc(keyword = \"...\")] is experimental"
16721672
);
16731673
}
16741674
}
16751675
}
16761676

1677-
match BUILTIN_ATTRIBUTES.iter().find(|(name, ..)| attr.path == name) {
1677+
match BUILTIN_ATTRIBUTES.iter().find(|(name, ..)| attr.path == *name) {
16781678
Some(&(name, _, template, _)) => self.check_builtin_attribute(attr, name, template),
16791679
None => if let Some(TokenTree::Token(_, token::Eq)) = attr.tokens.trees().next() {
16801680
// All key-value attributes are restricted to meta-item syntax.
@@ -1727,7 +1727,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
17271727
ast::ItemKind::Struct(..) => {
17281728
for attr in attr::filter_by_name(&i.attrs[..], "repr") {
17291729
for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
1730-
if item.check_name("simd") {
1730+
if item.check_name_symbol(symbols::simd) {
17311731
gate_feature_post!(&self, repr_simd, attr.span,
17321732
"SIMD types are experimental and possibly buggy");
17331733
}
@@ -1738,7 +1738,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
17381738
ast::ItemKind::Enum(..) => {
17391739
for attr in attr::filter_by_name(&i.attrs[..], "repr") {
17401740
for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
1741-
if item.check_name("align") {
1741+
if item.check_name_symbol(symbols::align) {
17421742
gate_feature_post!(&self, repr_align_enum, attr.span,
17431743
"`#[repr(align(x))]` on enums is experimental");
17441744
}
@@ -2062,7 +2062,7 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
20622062
// Process the edition umbrella feature-gates first, to ensure
20632063
// `edition_enabled_features` is completed before it's queried.
20642064
for attr in krate_attrs {
2065-
if !attr.check_name("feature") {
2065+
if !attr.check_name_symbol(symbols::feature) {
20662066
continue
20672067
}
20682068

@@ -2107,7 +2107,7 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
21072107
}
21082108

21092109
for attr in krate_attrs {
2110-
if !attr.check_name("feature") {
2110+
if !attr.check_name_symbol(symbols::feature) {
21112111
continue
21122112
}
21132113

@@ -2237,7 +2237,7 @@ fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate,
22372237
};
22382238
if !allow_features {
22392239
for attr in &krate.attrs {
2240-
if attr.check_name("feature") {
2240+
if attr.check_name_symbol(symbols::feature) {
22412241
let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
22422242
span_err!(span_handler, attr.span, E0554,
22432243
"#![feature] may not be used on the {} release channel",

src/libsyntax_pos/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ crate-type = ["dylib"]
1111

1212
[dependencies]
1313
serialize = { path = "../libserialize" }
14+
rustc_macros = { path = "../librustc_macros" }
1415
rustc_data_structures = { path = "../librustc_data_structures" }
1516
arena = { path = "../libarena" }
1617
scoped-tls = "1.0"

src/libsyntax_pos/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#![feature(non_exhaustive)]
1717
#![feature(optin_builtin_traits)]
1818
#![feature(rustc_attrs)]
19+
#![feature(proc_macro_hygiene)]
1920
#![feature(specialization)]
2021
#![feature(step_trait)]
2122

@@ -32,6 +33,7 @@ mod span_encoding;
3233
pub use span_encoding::{Span, DUMMY_SP};
3334

3435
pub mod symbol;
36+
pub use symbol::symbols;
3537

3638
mod analyze_source_file;
3739

0 commit comments

Comments
 (0)