Skip to content

Commit

Permalink
Use a proc macro to declare preallocated symbols
Browse files Browse the repository at this point in the history
  • Loading branch information
Zoxc committed Apr 15, 2019
1 parent a55f6be commit 10855a3
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 139 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Expand Up @@ -3387,6 +3387,7 @@ dependencies = [
"arena 0.0.0",
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_data_structures 0.0.0",
"rustc_macros 0.1.0",
"scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serialize 0.0.0",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_macros/src/lib.rs
Expand Up @@ -9,10 +9,16 @@ use proc_macro::TokenStream;

mod hash_stable;
mod query;
mod symbols;

#[proc_macro]
pub fn rustc_queries(input: TokenStream) -> TokenStream {
query::rustc_queries(input)
}

#[proc_macro]
pub fn symbols(input: TokenStream) -> TokenStream {
symbols::symbols(input)
}

decl_derive!([HashStable, attributes(stable_hasher)] => hash_stable::hash_stable_derive);
173 changes: 173 additions & 0 deletions src/librustc_macros/src/symbols.rs
@@ -0,0 +1,173 @@
use proc_macro::TokenStream;
use syn::{
Token, Ident, LitStr,
braced, parse_macro_input,
};
use syn::parse::{Result, Parse, ParseStream};
use syn;
use std::collections::HashSet;
use quote::quote;

#[allow(non_camel_case_types)]
mod kw {
syn::custom_keyword!(Keywords);
syn::custom_keyword!(Other);
}

struct Keyword {
name: Ident,
value: LitStr,
}

impl Parse for Keyword {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let name = input.parse()?;
input.parse::<Token![,]>()?;
let value = input.parse()?;
input.parse::<Token![,]>()?;

Ok(Keyword {
name,
value,
})
}
}

struct Symbol(Ident);

impl Parse for Symbol {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let ident: Ident = input.parse()?;
input.parse::<Token![,]>()?;

Ok(Symbol(ident))
}
}

/// A type used to greedily parse another type until the input is empty.
struct List<T>(Vec<T>);

impl<T: Parse> Parse for List<T> {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let mut list = Vec::new();
while !input.is_empty() {
list.push(input.parse()?);
}
Ok(List(list))
}
}

struct Input {
keywords: List<Keyword>,
symbols: List<Symbol>,
}

impl Parse for Input {
fn parse(input: ParseStream<'_>) -> Result<Self> {
input.parse::<kw::Keywords>()?;
let content;
braced!(content in input);
let keywords = content.parse()?;

input.parse::<kw::Other>()?;
let content;
braced!(content in input);
let symbols = content.parse()?;

Ok(Input {
keywords,
symbols,
})
}
}

pub fn symbols(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as Input);

let mut keyword_stream = quote! {};
let mut symbols_stream = quote! {};
let mut prefill_stream = quote! {};
let mut from_str_stream = quote! {};
let mut counter = 0u32;
let mut keys = HashSet::<String>::new();

let mut check_dup = |str: &str| {
if !keys.insert(str.to_string()) {
panic!("Symbol `{}` is duplicated", str);
}
};

for keyword in &input.keywords.0 {
let name = &keyword.name;
let value = &keyword.value;
check_dup(&value.value());
prefill_stream.extend(quote! {
#value,
});
keyword_stream.extend(quote! {
pub const #name: Keyword = Keyword {
ident: Ident::with_empty_ctxt(super::Symbol::new(#counter))
};
});
from_str_stream.extend(quote! {
#value => Ok(#name),
});
counter += 1;
}

for symbol in &input.symbols.0 {
let value = &symbol.0;
let value_str = value.to_string();
check_dup(&value_str);
prefill_stream.extend(quote! {
#value_str,
});
symbols_stream.extend(quote! {
pub const #value: Symbol = Symbol::new(#counter);
});
counter += 1;
}

TokenStream::from(quote! {
#[allow(non_upper_case_globals)]
pub mod keywords {
use super::{Symbol, Ident};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Keyword {
ident: Ident,
}
impl Keyword {
#[inline] pub fn ident(self) -> Ident { self.ident }
#[inline] pub fn name(self) -> Symbol { self.ident.name }
}

#keyword_stream

impl std::str::FromStr for Keyword {
type Err = ();

fn from_str(s: &str) -> Result<Self, ()> {
match s {
#from_str_stream
_ => Err(()),
}
}
}
}

#[allow(non_upper_case_globals)]
pub mod symbols {
use super::Symbol;

#symbols_stream
}

impl Interner {
pub fn fresh() -> Self {
Interner::prefill(&[
#prefill_stream
])
}
}
})
}
6 changes: 6 additions & 0 deletions src/libsyntax/ast.rs
Expand Up @@ -68,6 +68,12 @@ pub struct Path {
pub segments: Vec<PathSegment>,
}

impl PartialEq<Symbol> for Path {
fn eq(&self, symbol: &Symbol) -> bool {
self.segments.len() == 1 && self.segments[0].ident.name.interned() == *symbol
}
}

impl<'a> PartialEq<&'a str> for Path {
fn eq(&self, string: &&'a str) -> bool {
self.segments.len() == 1 && self.segments[0].ident.name == *string
Expand Down
21 changes: 21 additions & 0 deletions src/libsyntax/attr/mod.rs
Expand Up @@ -85,6 +85,11 @@ impl NestedMetaItem {
self.meta_item().map_or(false, |meta_item| meta_item.check_name(name))
}

/// Returns `true` if this list item is a MetaItem with a name of `name`.
pub fn check_name_symbol(&self, name: Symbol) -> bool {
self.meta_item().map_or(false, |meta_item| meta_item.check_name_symbol(name))
}

/// For a single-segment meta-item returns its name, otherwise returns `None`.
pub fn ident(&self) -> Option<Ident> {
self.meta_item().and_then(|meta_item| meta_item.ident())
Expand Down Expand Up @@ -159,6 +164,18 @@ impl Attribute {
matches
}

/// Returns `true` if the attribute's path matches the argument. If it matches, then the
/// attribute is marked as used.
///
/// To check the attribute name without marking it used, use the `path` field directly.
pub fn check_name_symbol(&self, name: Symbol) -> bool {
let matches = self.path == name;
if matches {
mark_used(self);
}
matches
}

/// For a single-segment attribute returns its name, otherwise returns `None`.
pub fn ident(&self) -> Option<Ident> {
if self.path.segments.len() == 1 {
Expand Down Expand Up @@ -248,6 +265,10 @@ impl MetaItem {
self.path == name
}

pub fn check_name_symbol(&self, name: Symbol) -> bool {
self.path == name
}

pub fn is_value_str(&self) -> bool {
self.value_str().is_some()
}
Expand Down
28 changes: 14 additions & 14 deletions src/libsyntax/feature_gate.rs
Expand Up @@ -28,7 +28,7 @@ use crate::tokenstream::TokenTree;
use errors::{DiagnosticBuilder, Handler};
use rustc_data_structures::fx::FxHashMap;
use rustc_target::spec::abi::Abi;
use syntax_pos::{Span, DUMMY_SP};
use syntax_pos::{Span, DUMMY_SP, symbols};
use log::debug;

use std::env;
Expand Down Expand Up @@ -1366,7 +1366,7 @@ impl<'a> Context<'a> {
}
} else if n == "doc" {
if let Some(content) = attr.meta_item_list() {
if content.iter().any(|c| c.check_name("include")) {
if content.iter().any(|c| c.check_name_symbol(symbols::include)) {
gate_feature!(self, external_doc, attr.span,
"#[doc(include = \"...\")] is experimental"
);
Expand Down Expand Up @@ -1667,33 +1667,33 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
// check for gated attributes
self.context.check_attribute(attr, false);

if attr.check_name("doc") {
if attr.check_name_symbol(symbols::doc) {
if let Some(content) = attr.meta_item_list() {
if content.len() == 1 && content[0].check_name("cfg") {
if content.len() == 1 && content[0].check_name_symbol(symbols::cfg) {
gate_feature_post!(&self, doc_cfg, attr.span,
"#[doc(cfg(...))] is experimental"
);
} else if content.iter().any(|c| c.check_name("masked")) {
} else if content.iter().any(|c| c.check_name_symbol(symbols::masked)) {
gate_feature_post!(&self, doc_masked, attr.span,
"#[doc(masked)] is experimental"
);
} else if content.iter().any(|c| c.check_name("spotlight")) {
} else if content.iter().any(|c| c.check_name_symbol(symbols::spotlight)) {
gate_feature_post!(&self, doc_spotlight, attr.span,
"#[doc(spotlight)] is experimental"
);
} else if content.iter().any(|c| c.check_name("alias")) {
} else if content.iter().any(|c| c.check_name_symbol(symbols::alias)) {
gate_feature_post!(&self, doc_alias, attr.span,
"#[doc(alias = \"...\")] is experimental"
);
} else if content.iter().any(|c| c.check_name("keyword")) {
} else if content.iter().any(|c| c.check_name_symbol(symbols::keyword)) {
gate_feature_post!(&self, doc_keyword, attr.span,
"#[doc(keyword = \"...\")] is experimental"
);
}
}
}

match BUILTIN_ATTRIBUTES.iter().find(|(name, ..)| attr.path == name) {
match BUILTIN_ATTRIBUTES.iter().find(|(name, ..)| attr.path == *name) {
Some(&(name, _, template, _)) => self.check_builtin_attribute(attr, name, template),
None => if let Some(TokenTree::Token(_, token::Eq)) = attr.tokens.trees().next() {
// All key-value attributes are restricted to meta-item syntax.
Expand Down Expand Up @@ -1748,7 +1748,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
ast::ItemKind::Struct(..) => {
for attr in attr::filter_by_name(&i.attrs[..], "repr") {
for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
if item.check_name("simd") {
if item.check_name_symbol(symbols::simd) {
gate_feature_post!(&self, repr_simd, attr.span,
"SIMD types are experimental and possibly buggy");
}
Expand All @@ -1759,7 +1759,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
ast::ItemKind::Enum(..) => {
for attr in attr::filter_by_name(&i.attrs[..], "repr") {
for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
if item.check_name("align") {
if item.check_name_symbol(symbols::align) {
gate_feature_post!(&self, repr_align_enum, attr.span,
"`#[repr(align(x))]` on enums is experimental");
}
Expand Down Expand Up @@ -2083,7 +2083,7 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
// Process the edition umbrella feature-gates first, to ensure
// `edition_enabled_features` is completed before it's queried.
for attr in krate_attrs {
if !attr.check_name("feature") {
if !attr.check_name_symbol(symbols::feature) {
continue
}

Expand Down Expand Up @@ -2128,7 +2128,7 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
}

for attr in krate_attrs {
if !attr.check_name("feature") {
if !attr.check_name_symbol(symbols::feature) {
continue
}

Expand Down Expand Up @@ -2258,7 +2258,7 @@ fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate,
};
if !allow_features {
for attr in &krate.attrs {
if attr.check_name("feature") {
if attr.check_name_symbol(symbols::feature) {
let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
span_err!(span_handler, attr.span, E0554,
"#![feature] may not be used on the {} release channel",
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax_pos/Cargo.toml
Expand Up @@ -11,6 +11,7 @@ crate-type = ["dylib"]

[dependencies]
serialize = { path = "../libserialize" }
rustc_macros = { path = "../librustc_macros" }
rustc_data_structures = { path = "../librustc_data_structures" }
arena = { path = "../libarena" }
scoped-tls = "1.0"
Expand Down
2 changes: 2 additions & 0 deletions src/libsyntax_pos/lib.rs
Expand Up @@ -16,6 +16,7 @@
#![feature(non_exhaustive)]
#![feature(optin_builtin_traits)]
#![feature(rustc_attrs)]
#![feature(proc_macro_hygiene)]
#![feature(specialization)]
#![feature(step_trait)]

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

pub mod symbol;
pub use symbol::symbols;

mod analyze_source_file;

Expand Down

1 comment on commit 10855a3

@QGB
Copy link

@QGB QGB commented on 10855a3 Nov 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

207 161 macro_rules! symbols {
208 163 #symbols_stream
https://github.com/rust-lang/rust/tree/master/src/librustc_macros/src/symbols.rs#L163

Please sign in to comment.