Auto merge of #43185 - durka:thread-local-pub-restricted, r=alexcrichton
support pub(restricted) in thread_local! (round 2) Resurrected #40984 now that the issue blocking it was fixed. Original description: `pub(restricted)` was stabilized in #40556 so let's go! Here is a [playground](https://play.rust-lang.org/?gist=f55f32f164a6ed18c219fec8f8293b98&version=nightly&backtrace=1). I changed the interface of `__thread_local_inner!`, which is supposedly unstable but this is not checked for macros (#34097 cc @petrochenkov @jseyfried), so this may be an issue.
This commit is contained in:
commit
8658908cf8
4 changed files with 92 additions and 16 deletions
|
@ -277,6 +277,7 @@
|
||||||
#![feature(link_args)]
|
#![feature(link_args)]
|
||||||
#![feature(linkage)]
|
#![feature(linkage)]
|
||||||
#![feature(macro_reexport)]
|
#![feature(macro_reexport)]
|
||||||
|
#![feature(macro_vis_matcher)]
|
||||||
#![feature(needs_panic_runtime)]
|
#![feature(needs_panic_runtime)]
|
||||||
#![feature(needs_drop)]
|
#![feature(needs_drop)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
|
|
|
@ -110,12 +110,13 @@ impl<T: 'static> fmt::Debug for LocalKey<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(stage0))]
|
||||||
/// Declare a new thread local storage key of type [`std::thread::LocalKey`].
|
/// Declare a new thread local storage key of type [`std::thread::LocalKey`].
|
||||||
///
|
///
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
///
|
///
|
||||||
/// The macro wraps any number of static declarations and makes them thread local.
|
/// 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;
|
/// use std::cell::RefCell;
|
||||||
|
@ -135,6 +136,60 @@ impl<T: 'static> fmt::Debug for LocalKey<T> {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[allow_internal_unstable]
|
#[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 {
|
macro_rules! thread_local {
|
||||||
// rule 0: empty (base case for the recursion)
|
// rule 0: empty (base case for the recursion)
|
||||||
() => {};
|
() => {};
|
||||||
|
@ -164,6 +219,7 @@ macro_rules! thread_local {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage0)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[unstable(feature = "thread_local_internals",
|
#[unstable(feature = "thread_local_internals",
|
||||||
reason = "should not be necessary",
|
reason = "should not be necessary",
|
||||||
|
|
|
@ -222,7 +222,7 @@ pub fn compile(sess: &ParseSess, features: &RefCell<Features>, def: &ast::Item)
|
||||||
if let MatchedNonterminal(ref nt) = *m {
|
if let MatchedNonterminal(ref nt) = *m {
|
||||||
if let NtTT(ref tt) = **nt {
|
if let NtTT(ref tt) = **nt {
|
||||||
let tt = quoted::parse(tt.clone().into(), true, sess).pop().unwrap();
|
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;
|
return tt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,11 +272,12 @@ pub fn compile(sess: &ParseSess, features: &RefCell<Features>, def: &ast::Item)
|
||||||
|
|
||||||
fn check_lhs_nt_follows(sess: &ParseSess,
|
fn check_lhs_nt_follows(sess: &ParseSess,
|
||||||
features: &RefCell<Features>,
|
features: &RefCell<Features>,
|
||||||
|
attrs: &[ast::Attribute],
|
||||||
lhs: "ed::TokenTree) -> bool {
|
lhs: "ed::TokenTree) -> bool {
|
||||||
// lhs is going to be like TokenTree::Delimited(...), where the
|
// 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.
|
// entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
|
||||||
if let quoted::TokenTree::Delimited(_, ref tts) = *lhs {
|
if let quoted::TokenTree::Delimited(_, ref tts) = *lhs {
|
||||||
check_matcher(sess, features, &tts.tts)
|
check_matcher(sess, features, attrs, &tts.tts)
|
||||||
} else {
|
} else {
|
||||||
let msg = "invalid macro matcher; matchers must be contained in balanced delimiters";
|
let msg = "invalid macro matcher; matchers must be contained in balanced delimiters";
|
||||||
sess.span_diagnostic.span_err(lhs.span(), msg);
|
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,
|
fn check_matcher(sess: &ParseSess,
|
||||||
features: &RefCell<Features>,
|
features: &RefCell<Features>,
|
||||||
|
attrs: &[ast::Attribute],
|
||||||
matcher: &[quoted::TokenTree]) -> bool {
|
matcher: &[quoted::TokenTree]) -> bool {
|
||||||
let first_sets = FirstSets::new(matcher);
|
let first_sets = FirstSets::new(matcher);
|
||||||
let empty_suffix = TokenSet::empty();
|
let empty_suffix = TokenSet::empty();
|
||||||
let err = sess.span_diagnostic.err_count();
|
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()
|
err == sess.span_diagnostic.err_count()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,6 +577,7 @@ impl TokenSet {
|
||||||
// see `FirstSets::new`.
|
// see `FirstSets::new`.
|
||||||
fn check_matcher_core(sess: &ParseSess,
|
fn check_matcher_core(sess: &ParseSess,
|
||||||
features: &RefCell<Features>,
|
features: &RefCell<Features>,
|
||||||
|
attrs: &[ast::Attribute],
|
||||||
first_sets: &FirstSets,
|
first_sets: &FirstSets,
|
||||||
matcher: &[quoted::TokenTree],
|
matcher: &[quoted::TokenTree],
|
||||||
follow: &TokenSet) -> TokenSet {
|
follow: &TokenSet) -> TokenSet {
|
||||||
|
@ -605,7 +608,7 @@ fn check_matcher_core(sess: &ParseSess,
|
||||||
match *token {
|
match *token {
|
||||||
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => {
|
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => {
|
||||||
let can_be_followed_by_any;
|
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);
|
let msg = format!("invalid fragment specifier `{}`", bad_frag);
|
||||||
sess.span_diagnostic.struct_span_err(token.span(), &msg)
|
sess.span_diagnostic.struct_span_err(token.span(), &msg)
|
||||||
.help("valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, \
|
.help("valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, \
|
||||||
|
@ -631,7 +634,7 @@ fn check_matcher_core(sess: &ParseSess,
|
||||||
}
|
}
|
||||||
TokenTree::Delimited(span, ref d) => {
|
TokenTree::Delimited(span, ref d) => {
|
||||||
let my_suffix = TokenSet::singleton(d.close_tt(span));
|
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
|
// don't track non NT tokens
|
||||||
last.replace_with_irrelevant();
|
last.replace_with_irrelevant();
|
||||||
|
|
||||||
|
@ -663,7 +666,12 @@ fn check_matcher_core(sess: &ParseSess,
|
||||||
// At this point, `suffix_first` is built, and
|
// At this point, `suffix_first` is built, and
|
||||||
// `my_suffix` is some TokenSet that we can use
|
// `my_suffix` is some TokenSet that we can use
|
||||||
// for checking the interior of `seq_rep`.
|
// 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 {
|
if next.maybe_empty {
|
||||||
last.add_all(&next);
|
last.add_all(&next);
|
||||||
} else {
|
} else {
|
||||||
|
@ -836,12 +844,13 @@ fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> Result<bool, (String, &'
|
||||||
|
|
||||||
fn has_legal_fragment_specifier(sess: &ParseSess,
|
fn has_legal_fragment_specifier(sess: &ParseSess,
|
||||||
features: &RefCell<Features>,
|
features: &RefCell<Features>,
|
||||||
|
attrs: &[ast::Attribute],
|
||||||
tok: "ed::TokenTree) -> Result<(), String> {
|
tok: "ed::TokenTree) -> Result<(), String> {
|
||||||
debug!("has_legal_fragment_specifier({:?})", tok);
|
debug!("has_legal_fragment_specifier({:?})", tok);
|
||||||
if let quoted::TokenTree::MetaVarDecl(_, _, ref frag_spec) = *tok {
|
if let quoted::TokenTree::MetaVarDecl(_, _, ref frag_spec) = *tok {
|
||||||
let frag_name = frag_spec.name.as_str();
|
let frag_name = frag_spec.name.as_str();
|
||||||
let frag_span = tok.span();
|
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());
|
return Err(frag_name.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -850,13 +859,15 @@ fn has_legal_fragment_specifier(sess: &ParseSess,
|
||||||
|
|
||||||
fn is_legal_fragment_specifier(sess: &ParseSess,
|
fn is_legal_fragment_specifier(sess: &ParseSess,
|
||||||
features: &RefCell<Features>,
|
features: &RefCell<Features>,
|
||||||
|
attrs: &[ast::Attribute],
|
||||||
frag_name: &str,
|
frag_name: &str,
|
||||||
frag_span: Span) -> bool {
|
frag_span: Span) -> bool {
|
||||||
match frag_name {
|
match frag_name {
|
||||||
"item" | "block" | "stmt" | "expr" | "pat" |
|
"item" | "block" | "stmt" | "expr" | "pat" |
|
||||||
"path" | "ty" | "ident" | "meta" | "tt" | "" => true,
|
"path" | "ty" | "ident" | "meta" | "tt" | "" => true,
|
||||||
"vis" => {
|
"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;
|
let explain = feature_gate::EXPLAIN_VIS_MATCHER;
|
||||||
emit_feature_err(sess,
|
emit_feature_err(sess,
|
||||||
"macro_vis_matcher",
|
"macro_vis_matcher",
|
||||||
|
|
|
@ -11,13 +11,21 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
//! this tests the syntax of `thread_local!`
|
//! this tests the syntax of `thread_local!`
|
||||||
|
|
||||||
thread_local! {
|
mod foo {
|
||||||
// no docs
|
mod bar {
|
||||||
#[allow(unused)]
|
thread_local! {
|
||||||
static FOO: i32 = 42;
|
// no docs
|
||||||
/// docs
|
#[allow(unused)]
|
||||||
pub static BAR: String = String::from("bar");
|
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() {}
|
fn main() {}
|
||||||
|
|
Loading…
Add table
Reference in a new issue