Skip to content

Commit e957a91

Browse files
committed
Auto merge of #43746 - eddyb:sound-thread-local, r=alexcrichton
Check #[thread_local] statics correctly in the compiler. Fixes #43733 by introducing `#[allow_internal_unsafe]` analogous to `#[allow_internal_unstable]`, for letting a macro expand to `unsafe` blocks and functions even in `#![forbid(unsafe_code)]` crates. Fixes #17954 by not letting references to `#[thread_local]` statics escape the function they're taken in - we can't just use a magical lifetime because Rust has *lifetime parametrism*, so if we added the often-proposed `'thread` lifetime, we'd have no way to check it in generic code. To avoid potential edge cases in the compiler, the lifetime is actually that of a temporary at the same position, i.e. `&TLS_STATIC` has the same lifetime `&non_const_fn()` would. Referring to `#[thread_local]` `static`s at compile-time is banned now (as per PR discussion). Additionally, to remove `unsafe impl Sync` from `std::thread::local::fast::Key`, `#[thread_local]` statics are now not required to implement `Sync`, as they are not shared between threads.
2 parents edd82ee + eba7592 commit e957a91

30 files changed

+384
-72
lines changed

Diff for: src/librustc/hir/lowering.rs

+1
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ impl<'a> LoweringContext<'a> {
404404
format: codemap::CompilerDesugaring(Symbol::intern(reason)),
405405
span: Some(span),
406406
allow_internal_unstable: true,
407+
allow_internal_unsafe: false,
407408
},
408409
});
409410
span.ctxt = SyntaxContext::empty().apply_mark(mark);

Diff for: src/librustc/middle/mem_categorization.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,13 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
643643
Ok(self.cat_rvalue_node(id, span, expr_ty))
644644
}
645645

646-
Def::Static(_, mutbl) => {
646+
Def::Static(def_id, mutbl) => {
647+
// `#[thread_local]` statics may not outlive the current function.
648+
for attr in &self.tcx.get_attrs(def_id)[..] {
649+
if attr.check_name("thread_local") {
650+
return Ok(self.cat_rvalue_node(id, span, expr_ty));
651+
}
652+
}
647653
Ok(Rc::new(cmt_ {
648654
id:id,
649655
span:span,

Diff for: src/librustc_allocator/expand.rs

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ impl<'a> Folder for ExpandAllocatorDirectives<'a> {
7979
format: MacroAttribute(Symbol::intern(name)),
8080
span: None,
8181
allow_internal_unstable: true,
82+
allow_internal_unsafe: false,
8283
}
8384
});
8485
let span = Span {

Diff for: src/librustc_lint/builtin.rs

+17-8
Original file line numberDiff line numberDiff line change
@@ -195,24 +195,35 @@ impl LintPass for UnsafeCode {
195195
}
196196
}
197197

198+
impl UnsafeCode {
199+
fn report_unsafe(&self, cx: &LateContext, span: Span, desc: &'static str) {
200+
// This comes from a macro that has #[allow_internal_unsafe].
201+
if span.allows_unsafe() {
202+
return;
203+
}
204+
205+
cx.span_lint(UNSAFE_CODE, span, desc);
206+
}
207+
}
208+
198209
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode {
199210
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
200211
if let hir::ExprBlock(ref blk) = e.node {
201212
// Don't warn about generated blocks, that'll just pollute the output.
202213
if blk.rules == hir::UnsafeBlock(hir::UserProvided) {
203-
cx.span_lint(UNSAFE_CODE, blk.span, "usage of an `unsafe` block");
214+
self.report_unsafe(cx, blk.span, "usage of an `unsafe` block");
204215
}
205216
}
206217
}
207218

208219
fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
209220
match it.node {
210221
hir::ItemTrait(hir::Unsafety::Unsafe, ..) => {
211-
cx.span_lint(UNSAFE_CODE, it.span, "declaration of an `unsafe` trait")
222+
self.report_unsafe(cx, it.span, "declaration of an `unsafe` trait")
212223
}
213224

214225
hir::ItemImpl(hir::Unsafety::Unsafe, ..) => {
215-
cx.span_lint(UNSAFE_CODE, it.span, "implementation of an `unsafe` trait")
226+
self.report_unsafe(cx, it.span, "implementation of an `unsafe` trait")
216227
}
217228

218229
_ => return,
@@ -228,12 +239,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode {
228239
_: ast::NodeId) {
229240
match fk {
230241
FnKind::ItemFn(_, _, hir::Unsafety::Unsafe, ..) => {
231-
cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function")
242+
self.report_unsafe(cx, span, "declaration of an `unsafe` function")
232243
}
233244

234245
FnKind::Method(_, sig, ..) => {
235246
if sig.unsafety == hir::Unsafety::Unsafe {
236-
cx.span_lint(UNSAFE_CODE, span, "implementation of an `unsafe` method")
247+
self.report_unsafe(cx, span, "implementation of an `unsafe` method")
237248
}
238249
}
239250

@@ -244,9 +255,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode {
244255
fn check_trait_item(&mut self, cx: &LateContext, item: &hir::TraitItem) {
245256
if let hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Required(_)) = item.node {
246257
if sig.unsafety == hir::Unsafety::Unsafe {
247-
cx.span_lint(UNSAFE_CODE,
248-
item.span,
249-
"declaration of an `unsafe` method")
258+
self.report_unsafe(cx, item.span, "declaration of an `unsafe` method")
250259
}
251260
}
252261
}

Diff for: src/librustc_mir/diagnostics.rs

+1
Original file line numberDiff line numberDiff line change
@@ -442,4 +442,5 @@ static A : &'static u32 = &S.a; // ok!
442442

443443
register_diagnostics! {
444444
E0526, // shuffle indices are not constant
445+
E0625, // thread-local statics cannot be accessed at compile-time
445446
}

Diff for: src/librustc_mir/transform/qualify_consts.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -484,8 +484,20 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
484484
}
485485
}
486486
},
487-
Lvalue::Static(_) => {
487+
Lvalue::Static(ref global) => {
488488
self.add(Qualif::STATIC);
489+
490+
if self.mode != Mode::Fn {
491+
for attr in &self.tcx.get_attrs(global.def_id)[..] {
492+
if attr.check_name("thread_local") {
493+
span_err!(self.tcx.sess, self.span, E0625,
494+
"thread-local statics cannot be \
495+
accessed at compile-time");
496+
return;
497+
}
498+
}
499+
}
500+
489501
if self.mode == Mode::Const || self.mode == Mode::ConstFn {
490502
span_err!(self.tcx.sess, self.span, E0013,
491503
"{}s cannot refer to statics, use \
@@ -998,6 +1010,12 @@ impl MirPass for QualifyAndPromoteConstants {
9981010

9991011
// Statics must be Sync.
10001012
if mode == Mode::Static {
1013+
// `#[thread_local]` statics don't have to be `Sync`.
1014+
for attr in &tcx.get_attrs(def_id)[..] {
1015+
if attr.check_name("thread_local") {
1016+
return;
1017+
}
1018+
}
10011019
let ty = mir.return_ty;
10021020
tcx.infer_ctxt().enter(|infcx| {
10031021
let param_env = ty::ParamEnv::empty(Reveal::UserFacing);

Diff for: src/librustc_plugin/registry.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,19 @@ impl<'a> Registry<'a> {
102102
panic!("user-defined macros may not be named `macro_rules`");
103103
}
104104
self.syntax_exts.push((name, match extension {
105-
NormalTT(ext, _, allow_internal_unstable) => {
105+
NormalTT {
106+
expander,
107+
def_info: _,
108+
allow_internal_unstable,
109+
allow_internal_unsafe
110+
} => {
106111
let nid = ast::CRATE_NODE_ID;
107-
NormalTT(ext, Some((nid, self.krate_span)), allow_internal_unstable)
112+
NormalTT {
113+
expander,
114+
def_info: Some((nid, self.krate_span)),
115+
allow_internal_unstable,
116+
allow_internal_unsafe
117+
}
108118
}
109119
IdentTT(ext, _, allow_internal_unstable) => {
110120
IdentTT(ext, Some(self.krate_span), allow_internal_unstable)
@@ -134,8 +144,12 @@ impl<'a> Registry<'a> {
134144
/// It builds for you a `NormalTT` that calls `expander`,
135145
/// and also takes care of interning the macro's name.
136146
pub fn register_macro(&mut self, name: &str, expander: MacroExpanderFn) {
137-
self.register_syntax_extension(Symbol::intern(name),
138-
NormalTT(Box::new(expander), None, false));
147+
self.register_syntax_extension(Symbol::intern(name), NormalTT {
148+
expander: Box::new(expander),
149+
def_info: None,
150+
allow_internal_unstable: false,
151+
allow_internal_unsafe: false,
152+
});
139153
}
140154

141155
/// Register a compiler lint pass.

Diff for: src/librustc_resolve/macros.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ impl<'a> base::Resolver for Resolver<'a> {
313313
fn check_unused_macros(&self) {
314314
for did in self.unused_macros.iter() {
315315
let id_span = match *self.macro_map[did] {
316-
SyntaxExtension::NormalTT(_, isp, _) => isp,
316+
SyntaxExtension::NormalTT { def_info, .. } => def_info,
317317
SyntaxExtension::DeclMacro(.., osp) => osp,
318318
_ => None,
319319
};

Diff for: src/libstd/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@
243243
#![feature(allocator_api)]
244244
#![feature(alloc_system)]
245245
#![feature(allocator_internals)]
246+
#![feature(allow_internal_unsafe)]
246247
#![feature(allow_internal_unstable)]
247248
#![feature(asm)]
248249
#![feature(box_syntax)]

Diff for: src/libstd/thread/local.rs

+31-31
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,13 @@ pub struct LocalKey<T: 'static> {
9191
//
9292
// Note that the thunk is itself unsafe because the returned lifetime of the
9393
// slot where data lives, `'static`, is not actually valid. The lifetime
94-
// here is actually `'thread`!
94+
// here is actually slightly shorter than the currently running thread!
9595
//
9696
// Although this is an extra layer of indirection, it should in theory be
9797
// trivially devirtualizable by LLVM because the value of `inner` never
9898
// changes and the constant should be readonly within a crate. This mainly
9999
// only runs into problems when TLS statics are exported across crates.
100-
inner: fn() -> Option<&'static UnsafeCell<Option<T>>>,
100+
inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>,
101101

102102
// initialization routine to invoke to create a value
103103
init: fn() -> T,
@@ -157,12 +157,13 @@ macro_rules! thread_local {
157157
issue = "0")]
158158
#[macro_export]
159159
#[allow_internal_unstable]
160+
#[cfg_attr(not(stage0), allow_internal_unsafe)]
160161
macro_rules! __thread_local_inner {
161162
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => {
162163
$(#[$attr])* $vis static $name: $crate::thread::LocalKey<$t> = {
163164
fn __init() -> $t { $init }
164165

165-
fn __getit() -> $crate::option::Option<
166+
unsafe fn __getit() -> $crate::option::Option<
166167
&'static $crate::cell::UnsafeCell<
167168
$crate::option::Option<$t>>>
168169
{
@@ -178,7 +179,9 @@ macro_rules! __thread_local_inner {
178179
__KEY.get()
179180
}
180181

181-
$crate::thread::LocalKey::new(__getit, __init)
182+
unsafe {
183+
$crate::thread::LocalKey::new(__getit, __init)
184+
}
182185
};
183186
}
184187
}
@@ -252,8 +255,8 @@ impl<T: 'static> LocalKey<T> {
252255
#[unstable(feature = "thread_local_internals",
253256
reason = "recently added to create a key",
254257
issue = "0")]
255-
pub const fn new(inner: fn() -> Option<&'static UnsafeCell<Option<T>>>,
256-
init: fn() -> T) -> LocalKey<T> {
258+
pub const unsafe fn new(inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>,
259+
init: fn() -> T) -> LocalKey<T> {
257260
LocalKey {
258261
inner: inner,
259262
init: init,
@@ -391,6 +394,7 @@ pub mod fast {
391394
}
392395
}
393396

397+
#[cfg(stage0)]
394398
unsafe impl<T> ::marker::Sync for Key<T> { }
395399

396400
impl<T> Key<T> {
@@ -402,14 +406,12 @@ pub mod fast {
402406
}
403407
}
404408

405-
pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
406-
unsafe {
407-
if mem::needs_drop::<T>() && self.dtor_running.get() {
408-
return None
409-
}
410-
self.register_dtor();
409+
pub unsafe fn get(&self) -> Option<&'static UnsafeCell<Option<T>>> {
410+
if mem::needs_drop::<T>() && self.dtor_running.get() {
411+
return None
411412
}
412-
Some(&self.inner)
413+
self.register_dtor();
414+
Some(&*(&self.inner as *const _))
413415
}
414416

415417
unsafe fn register_dtor(&self) {
@@ -478,26 +480,24 @@ pub mod os {
478480
}
479481
}
480482

481-
pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
482-
unsafe {
483-
let ptr = self.os.get() as *mut Value<T>;
484-
if !ptr.is_null() {
485-
if ptr as usize == 1 {
486-
return None
487-
}
488-
return Some(&(*ptr).value);
483+
pub unsafe fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
484+
let ptr = self.os.get() as *mut Value<T>;
485+
if !ptr.is_null() {
486+
if ptr as usize == 1 {
487+
return None
489488
}
490-
491-
// If the lookup returned null, we haven't initialized our own
492-
// local copy, so do that now.
493-
let ptr: Box<Value<T>> = box Value {
494-
key: self,
495-
value: UnsafeCell::new(None),
496-
};
497-
let ptr = Box::into_raw(ptr);
498-
self.os.set(ptr as *mut u8);
499-
Some(&(*ptr).value)
489+
return Some(&(*ptr).value);
500490
}
491+
492+
// If the lookup returned null, we haven't initialized our own
493+
// local copy, so do that now.
494+
let ptr: Box<Value<T>> = box Value {
495+
key: self,
496+
value: UnsafeCell::new(None),
497+
};
498+
let ptr = Box::into_raw(ptr);
499+
self.os.set(ptr as *mut u8);
500+
Some(&(*ptr).value)
501501
}
502502
}
503503

Diff for: src/libsyntax/ext/base.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -532,10 +532,16 @@ pub enum SyntaxExtension {
532532
/// A normal, function-like syntax extension.
533533
///
534534
/// `bytes!` is a `NormalTT`.
535-
///
536-
/// The `bool` dictates whether the contents of the macro can
537-
/// directly use `#[unstable]` things (true == yes).
538-
NormalTT(Box<TTMacroExpander>, Option<(ast::NodeId, Span)>, bool),
535+
NormalTT {
536+
expander: Box<TTMacroExpander>,
537+
def_info: Option<(ast::NodeId, Span)>,
538+
/// Whether the contents of the macro can
539+
/// directly use `#[unstable]` things (true == yes).
540+
allow_internal_unstable: bool,
541+
/// Whether the contents of the macro can use `unsafe`
542+
/// without triggering the `unsafe_code` lint.
543+
allow_internal_unsafe: bool,
544+
},
539545

540546
/// A function-like syntax extension that has an extra ident before
541547
/// the block.
@@ -562,7 +568,7 @@ impl SyntaxExtension {
562568
pub fn kind(&self) -> MacroKind {
563569
match *self {
564570
SyntaxExtension::DeclMacro(..) |
565-
SyntaxExtension::NormalTT(..) |
571+
SyntaxExtension::NormalTT { .. } |
566572
SyntaxExtension::IdentTT(..) |
567573
SyntaxExtension::ProcMacro(..) =>
568574
MacroKind::Bang,

Diff for: src/libsyntax/ext/derive.rs

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ pub fn add_derived_markers<T>(cx: &mut ExtCtxt, span: Span, traits: &[ast::Path]
6464
format: ExpnFormat::MacroAttribute(Symbol::intern(&pretty_name)),
6565
span: None,
6666
allow_internal_unstable: true,
67+
allow_internal_unsafe: false,
6768
},
6869
});
6970

0 commit comments

Comments
 (0)