Skip to content

Commit

Permalink
Auto merge of #59655 - Zoxc:symbols, r=petrochenkov
Browse files Browse the repository at this point in the history
Use a proc macro to declare preallocated symbols

r? @petrochenkov
  • Loading branch information
bors committed Apr 15, 2019
2 parents 1edb01b + f598091 commit fcf850f
Show file tree
Hide file tree
Showing 13 changed files with 327 additions and 141 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
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: 3 additions & 3 deletions src/librustc/middle/lib_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::ty::TyCtxt;
use crate::hir::intravisit::{self, NestedVisitorMap, Visitor};
use syntax::symbol::Symbol;
use syntax::ast::{Attribute, MetaItem, MetaItemKind};
use syntax_pos::Span;
use syntax_pos::{Span, symbols};
use rustc_data_structures::fx::{FxHashSet, FxHashMap};
use rustc_macros::HashStable;
use errors::DiagnosticId;
Expand Down Expand Up @@ -51,12 +51,12 @@ impl<'a, 'tcx> LibFeatureCollector<'a, 'tcx> {
}

fn extract(&self, attr: &Attribute) -> Option<(Symbol, Option<Symbol>, Span)> {
let stab_attrs = vec!["stable", "unstable", "rustc_const_unstable"];
let stab_attrs = [symbols::stable, symbols::unstable, symbols::rustc_const_unstable];

// Find a stability attribute (i.e., `#[stable (..)]`, `#[unstable (..)]`,
// `#[rustc_const_unstable (..)]`).
if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| {
attr.check_name(stab_attr)
attr.check_name(**stab_attr)
}) {
let meta_item = attr.meta();
if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta_item {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_incremental/persist/dirty_clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ impl<'a, 'tcx> FindAllAttrs<'a, 'tcx> {

fn is_active_attr(&mut self, attr: &Attribute) -> bool {
for attr_name in &self.attr_names {
if attr.check_name(attr_name) && check_config(self.tcx, attr) {
if attr.check_name(*attr_name) && check_config(self.tcx, attr) {
return true;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_lint/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {

let plugin_attributes = cx.sess().plugin_attributes.borrow_mut();
for &(ref name, ty) in plugin_attributes.iter() {
if ty == AttributeType::Whitelisted && attr.check_name(&name) {
if ty == AttributeType::Whitelisted && attr.check_name(&**name) {
debug!("{:?} (plugin attr) is whitelisted with ty {:?}", name, ty);
break;
}
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_macros/src/lib.rs
Original file line number Diff line number Diff line change
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);
163 changes: 163 additions & 0 deletions src/librustc_macros/src/symbols.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
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! {
macro_rules! keywords {
() => {
#keyword_stream

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

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

macro_rules! symbols {
() => {
#symbols_stream
}
}

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

impl PartialEq<Symbol> for Path {
fn eq(&self, symbol: &Symbol) -> bool {
self.segments.len() == 1 && {
let name = self.segments[0].ident.name;
// Make sure these symbols are pure strings
debug_assert!(!symbol.is_gensymed());
debug_assert!(!name.is_gensymed());
name == *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
15 changes: 12 additions & 3 deletions src/libsyntax/attr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ impl NestedMetaItem {
}

/// Returns `true` if this list item is a MetaItem with a name of `name`.
pub fn check_name(&self, name: &str) -> bool {
pub fn check_name<T>(&self, name: T) -> bool
where
Path: PartialEq<T>,
{
self.meta_item().map_or(false, |meta_item| meta_item.check_name(name))
}

Expand Down Expand Up @@ -151,7 +154,10 @@ impl Attribute {
/// attribute is marked as used.
///
/// To check the attribute name without marking it used, use the `path` field directly.
pub fn check_name(&self, name: &str) -> bool {
pub fn check_name<T>(&self, name: T) -> bool
where
Path: PartialEq<T>,
{
let matches = self.path == name;
if matches {
mark_used(self);
Expand Down Expand Up @@ -244,7 +250,10 @@ impl MetaItem {
}
}

pub fn check_name(&self, name: &str) -> bool {
pub fn check_name<T>(&self, name: T) -> bool
where
Path: PartialEq<T>,
{
self.path == name
}

Expand Down
28 changes: 14 additions & 14 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
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(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(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(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(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(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(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(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(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(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(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(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(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
2 changes: 1 addition & 1 deletion src/libsyntax_ext/proc_macro_decls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ pub fn modify(sess: &ParseSess,
}

pub fn is_proc_macro_attr(attr: &ast::Attribute) -> bool {
PROC_MACRO_KINDS.iter().any(|kind| attr.check_name(kind))
PROC_MACRO_KINDS.iter().any(|kind| attr.check_name(*kind))
}

impl<'a> CollectProcMacros<'a> {
Expand Down
Loading

0 comments on commit fcf850f

Please sign in to comment.