Skip to content

Commit 92892d3

Browse files
committed
Check #[thread_local] statics correctly in the compiler.
1 parent 8a4facc commit 92892d3

File tree

12 files changed

+223
-43
lines changed

12 files changed

+223
-43
lines changed

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_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/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/test/compile-fail/issue-17954.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(thread_local)]
12+
13+
#[thread_local]
14+
static FOO: u8 = 3;
15+
16+
fn main() {
17+
let a = &FOO;
18+
//~^ ERROR borrowed value does not live long enough
19+
//~| does not live long enough
20+
//~| NOTE borrowed value must be valid for the static lifetime
21+
22+
std::thread::spawn(move || {
23+
println!("{}", a);
24+
});
25+
} //~ temporary value only lives until here

Diff for: src/test/compile-fail/issue-43733-2.rs

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(const_fn, drop_types_in_const)]
12+
#![feature(cfg_target_thread_local, thread_local_internals)]
13+
14+
// On platforms *without* `#[thread_local]`, use
15+
// a custom non-`Sync` type to fake the same error.
16+
#[cfg(not(target_thread_local))]
17+
struct Key<T> {
18+
_data: std::cell::UnsafeCell<Option<T>>,
19+
_flag: std::cell::Cell<bool>,
20+
}
21+
22+
#[cfg(not(target_thread_local))]
23+
impl<T> Key<T> {
24+
const fn new() -> Self {
25+
Key {
26+
_data: std::cell::UnsafeCell::new(None),
27+
_flag: std::cell::Cell::new(false),
28+
}
29+
}
30+
}
31+
32+
#[cfg(target_thread_local)]
33+
use std::thread::__FastLocalKeyInner as Key;
34+
35+
static __KEY: Key<()> = Key::new();
36+
//~^ ERROR `std::cell::UnsafeCell<std::option::Option<()>>: std::marker::Sync` is not satisfied
37+
//~| ERROR `std::cell::Cell<bool>: std::marker::Sync` is not satisfied
38+
39+
fn main() {}

Diff for: src/test/compile-fail/issue-43733.rs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(const_fn, drop_types_in_const)]
12+
#![feature(cfg_target_thread_local, thread_local_internals)]
13+
14+
type Foo = std::cell::RefCell<String>;
15+
16+
#[cfg(target_thread_local)]
17+
static __KEY: std::thread::__FastLocalKeyInner<Foo> =
18+
std::thread::__FastLocalKeyInner::new();
19+
20+
#[cfg(not(target_thread_local))]
21+
static __KEY: std::thread::__OsLocalKeyInner<Foo> =
22+
std::thread::__OsLocalKeyInner::new();
23+
24+
fn __getit() -> std::option::Option<
25+
&'static std::cell::UnsafeCell<
26+
std::option::Option<Foo>>>
27+
{
28+
__KEY.get() //~ ERROR invocation of unsafe method requires unsafe
29+
}
30+
31+
static FOO: std::thread::LocalKey<Foo> =
32+
std::thread::LocalKey::new(__getit, Default::default);
33+
//~^ ERROR call to unsafe function requires unsafe
34+
35+
fn main() {
36+
FOO.with(|foo| println!("{}", foo.borrow()));
37+
std::thread::spawn(|| {
38+
FOO.with(|foo| *foo.borrow_mut() += "foo");
39+
}).join().unwrap();
40+
FOO.with(|foo| println!("{}", foo.borrow()));
41+
}

Diff for: src/test/compile-fail/thread-local-in-ctfe.rs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(const_fn, thread_local)]
12+
13+
#[thread_local]
14+
static A: u32 = 1;
15+
16+
static B: u32 = A;
17+
//~^ ERROR thread-local statics cannot be accessed at compile-time
18+
//~| ERROR cannot refer to other statics by value
19+
//~| WARN non-constant path in constant expression
20+
21+
static C: &u32 = &A;
22+
//~^ ERROR thread-local statics cannot be accessed at compile-time
23+
24+
const D: u32 = A;
25+
//~^ ERROR thread-local statics cannot be accessed at compile-time
26+
//~| ERROR cannot refer to statics by value
27+
//~| WARN non-constant path in constant expression
28+
29+
const E: &u32 = &A;
30+
//~^ ERROR thread-local statics cannot be accessed at compile-time
31+
32+
const fn f() -> u32 {
33+
A
34+
//~^ ERROR thread-local statics cannot be accessed at compile-time
35+
//~| ERROR cannot refer to statics by value
36+
}
37+
38+
fn main() {}

Diff for: src/test/run-pass/auxiliary/thread-local-extern-static.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![feature(thread_local)]
12-
#![feature(cfg_target_thread_local)]
11+
#![feature(cfg_target_thread_local, const_fn, thread_local)]
1312
#![crate_type = "lib"]
1413

14+
#[cfg(target_thread_local)]
15+
use std::cell::Cell;
16+
1517
#[no_mangle]
16-
#[cfg_attr(target_thread_local, thread_local)]
17-
pub static FOO: u32 = 3;
18+
#[cfg(target_thread_local)]
19+
#[thread_local]
20+
pub static FOO: Cell<u32> = Cell::new(3);

Diff for: src/test/run-pass/issue-30756.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![deny(unsafe_code)]
11+
#![forbid(unsafe_code)]
1212

1313
thread_local!(static FOO: u8 = 1);
1414

Diff for: src/test/run-pass/thread-local-extern-static.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,26 @@
1111
// ignore-windows
1212
// aux-build:thread-local-extern-static.rs
1313

14-
#![feature(thread_local)]
15-
#![feature(cfg_target_thread_local)]
14+
#![feature(cfg_target_thread_local, thread_local)]
1615

16+
#[cfg(target_thread_local)]
1717
extern crate thread_local_extern_static;
1818

19+
#[cfg(target_thread_local)]
20+
use std::cell::Cell;
21+
22+
#[cfg(target_thread_local)]
1923
extern {
20-
#[cfg_attr(target_thread_local, thread_local)]
21-
static FOO: u32;
24+
#[thread_local]
25+
static FOO: Cell<u32>;
2226
}
2327

28+
#[cfg(target_thread_local)]
2429
fn main() {
2530
unsafe {
26-
assert_eq!(FOO, 3);
31+
assert_eq!(FOO.get(), 3);
2732
}
2833
}
34+
35+
#[cfg(not(target_thread_local))]
36+
fn main() {}

0 commit comments

Comments
 (0)