diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 2fc107c663bb4..dda069324b08d 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -277,6 +277,7 @@ #![feature(link_args)] #![feature(linkage)] #![feature(macro_reexport)] +#![feature(macro_vis_matcher)] #![feature(needs_panic_runtime)] #![feature(needs_drop)] #![feature(never_type)] diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 28e8f72ac6416..be433e2b81e6b 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -110,12 +110,13 @@ impl fmt::Debug for LocalKey { } } +#[cfg(not(stage0))] /// Declare a new thread local storage key of type [`std::thread::LocalKey`]. /// /// # Syntax /// /// The macro wraps any number of static declarations and makes them thread local. -/// Each static may be public or private, and attributes are allowed. Example: +/// Publicity and attributes for each static are allowed. Example: /// /// ``` /// use std::cell::RefCell; @@ -135,6 +136,60 @@ impl fmt::Debug for LocalKey { #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable] +macro_rules! thread_local { + // empty (base case for the recursion) + () => {}; + + // process multiple declarations + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( + __thread_local_inner!($(#[$attr])* $vis $name, $t, $init); + thread_local!($($rest)*); + ); + + // handle a single declaration + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => ( + __thread_local_inner!($(#[$attr])* $vis $name, $t, $init); + ); +} + +#[cfg(not(stage0))] +#[doc(hidden)] +#[unstable(feature = "thread_local_internals", + reason = "should not be necessary", + issue = "0")] +#[macro_export] +#[allow_internal_unstable] +macro_rules! __thread_local_inner { + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => { + $(#[$attr])* $vis static $name: $crate::thread::LocalKey<$t> = { + fn __init() -> $t { $init } + + fn __getit() -> $crate::option::Option< + &'static $crate::cell::UnsafeCell< + $crate::option::Option<$t>>> + { + #[thread_local] + #[cfg(target_thread_local)] + static __KEY: $crate::thread::__FastLocalKeyInner<$t> = + $crate::thread::__FastLocalKeyInner::new(); + + #[cfg(not(target_thread_local))] + static __KEY: $crate::thread::__OsLocalKeyInner<$t> = + $crate::thread::__OsLocalKeyInner::new(); + + __KEY.get() + } + + $crate::thread::LocalKey::new(__getit, __init) + }; + } +} + +#[cfg(stage0)] +/// Declare a new thread local storage key of type `std::thread::LocalKey`. +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +#[allow_internal_unstable] macro_rules! thread_local { // rule 0: empty (base case for the recursion) () => {}; @@ -164,6 +219,7 @@ macro_rules! thread_local { ); } +#[cfg(stage0)] #[doc(hidden)] #[unstable(feature = "thread_local_internals", reason = "should not be necessary", diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index a98a9dd204046..f786b1abb8a1b 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -222,7 +222,7 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { let tt = quoted::parse(tt.clone().into(), true, sess).pop().unwrap(); - valid &= check_lhs_nt_follows(sess, features, &tt); + valid &= check_lhs_nt_follows(sess, features, &def.attrs, &tt); return tt; } } @@ -272,11 +272,12 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) fn check_lhs_nt_follows(sess: &ParseSess, features: &RefCell, + attrs: &[ast::Attribute], lhs: "ed::TokenTree) -> bool { // lhs is going to be like TokenTree::Delimited(...), where the // entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens. if let quoted::TokenTree::Delimited(_, ref tts) = *lhs { - check_matcher(sess, features, &tts.tts) + check_matcher(sess, features, attrs, &tts.tts) } else { let msg = "invalid macro matcher; matchers must be contained in balanced delimiters"; sess.span_diagnostic.span_err(lhs.span(), msg); @@ -328,11 +329,12 @@ fn check_rhs(sess: &ParseSess, rhs: "ed::TokenTree) -> bool { fn check_matcher(sess: &ParseSess, features: &RefCell, + attrs: &[ast::Attribute], matcher: &[quoted::TokenTree]) -> bool { let first_sets = FirstSets::new(matcher); let empty_suffix = TokenSet::empty(); let err = sess.span_diagnostic.err_count(); - check_matcher_core(sess, features, &first_sets, matcher, &empty_suffix); + check_matcher_core(sess, features, attrs, &first_sets, matcher, &empty_suffix); err == sess.span_diagnostic.err_count() } @@ -575,6 +577,7 @@ impl TokenSet { // see `FirstSets::new`. fn check_matcher_core(sess: &ParseSess, features: &RefCell, + attrs: &[ast::Attribute], first_sets: &FirstSets, matcher: &[quoted::TokenTree], follow: &TokenSet) -> TokenSet { @@ -605,7 +608,7 @@ fn check_matcher_core(sess: &ParseSess, match *token { TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { let can_be_followed_by_any; - if let Err(bad_frag) = has_legal_fragment_specifier(sess, features, token) { + if let Err(bad_frag) = has_legal_fragment_specifier(sess, features, attrs, token) { let msg = format!("invalid fragment specifier `{}`", bad_frag); sess.span_diagnostic.struct_span_err(token.span(), &msg) .help("valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, \ @@ -631,7 +634,7 @@ fn check_matcher_core(sess: &ParseSess, } TokenTree::Delimited(span, ref d) => { let my_suffix = TokenSet::singleton(d.close_tt(span)); - check_matcher_core(sess, features, first_sets, &d.tts, &my_suffix); + check_matcher_core(sess, features, attrs, first_sets, &d.tts, &my_suffix); // don't track non NT tokens last.replace_with_irrelevant(); @@ -663,7 +666,12 @@ fn check_matcher_core(sess: &ParseSess, // At this point, `suffix_first` is built, and // `my_suffix` is some TokenSet that we can use // for checking the interior of `seq_rep`. - let next = check_matcher_core(sess, features, first_sets, &seq_rep.tts, my_suffix); + let next = check_matcher_core(sess, + features, + attrs, + first_sets, + &seq_rep.tts, + my_suffix); if next.maybe_empty { last.add_all(&next); } else { @@ -836,12 +844,13 @@ fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> Result, + attrs: &[ast::Attribute], tok: "ed::TokenTree) -> Result<(), String> { debug!("has_legal_fragment_specifier({:?})", tok); if let quoted::TokenTree::MetaVarDecl(_, _, ref frag_spec) = *tok { let frag_name = frag_spec.name.as_str(); let frag_span = tok.span(); - if !is_legal_fragment_specifier(sess, features, &frag_name, frag_span) { + if !is_legal_fragment_specifier(sess, features, attrs, &frag_name, frag_span) { return Err(frag_name.to_string()); } } @@ -850,13 +859,15 @@ fn has_legal_fragment_specifier(sess: &ParseSess, fn is_legal_fragment_specifier(sess: &ParseSess, features: &RefCell, + attrs: &[ast::Attribute], frag_name: &str, frag_span: Span) -> bool { match frag_name { "item" | "block" | "stmt" | "expr" | "pat" | "path" | "ty" | "ident" | "meta" | "tt" | "" => true, "vis" => { - if !features.borrow().macro_vis_matcher { + if !features.borrow().macro_vis_matcher + && !attr::contains_name(attrs, "allow_internal_unstable") { let explain = feature_gate::EXPLAIN_VIS_MATCHER; emit_feature_err(sess, "macro_vis_matcher", diff --git a/src/test/run-pass/thread-local-syntax.rs b/src/test/run-pass/thread-local-syntax.rs index a5967249b5454..373824122fd51 100644 --- a/src/test/run-pass/thread-local-syntax.rs +++ b/src/test/run-pass/thread-local-syntax.rs @@ -11,13 +11,21 @@ #![deny(missing_docs)] //! this tests the syntax of `thread_local!` -thread_local! { - // no docs - #[allow(unused)] - static FOO: i32 = 42; - /// docs - pub static BAR: String = String::from("bar"); +mod foo { + mod bar { + thread_local! { + // no docs + #[allow(unused)] + static FOO: i32 = 42; + /// docs + pub static BAR: String = String::from("bar"); + + // look at these restrictions!! + pub(crate) static BAZ: usize = 0; + pub(in foo) static QUUX: usize = 0; + } + thread_local!(static SPLOK: u32 = 0); + } } -thread_local!(static BAZ: u32 = 0); fn main() {}