diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index b4993aafc4c9e..5533ab5189542 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -643,7 +643,13 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { Ok(self.cat_rvalue_node(id, span, expr_ty)) } - Def::Static(_, mutbl) => { + Def::Static(def_id, mutbl) => { + // `#[thread_local]` statics may not outlive the current function. + for attr in &self.tcx.get_attrs(def_id)[..] { + if attr.check_name("thread_local") { + return Ok(self.cat_rvalue_node(id, span, expr_ty)); + } + } Ok(Rc::new(cmt_ { id:id, span:span, diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index 6530b356e33f4..34170a6609c46 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -442,4 +442,5 @@ static A : &'static u32 = &S.a; // ok! register_diagnostics! { E0526, // shuffle indices are not constant + E0625, // thread-local statics cannot be accessed at compile-time } diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 8321d9b99ae8d..2ecffdf9fdf7c 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -484,8 +484,20 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } }, - Lvalue::Static(_) => { + Lvalue::Static(ref global) => { self.add(Qualif::STATIC); + + if self.mode != Mode::Fn { + for attr in &self.tcx.get_attrs(global.def_id)[..] { + if attr.check_name("thread_local") { + span_err!(self.tcx.sess, self.span, E0625, + "thread-local statics cannot be \ + accessed at compile-time"); + return; + } + } + } + if self.mode == Mode::Const || self.mode == Mode::ConstFn { span_err!(self.tcx.sess, self.span, E0013, "{}s cannot refer to statics, use \ @@ -998,6 +1010,12 @@ impl MirPass for QualifyAndPromoteConstants { // Statics must be Sync. if mode == Mode::Static { + // `#[thread_local]` statics don't have to be `Sync`. + for attr in &tcx.get_attrs(def_id)[..] { + if attr.check_name("thread_local") { + return; + } + } let ty = mir.return_ty; tcx.infer_ctxt().enter(|infcx| { let param_env = ty::ParamEnv::empty(Reveal::UserFacing); diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index f7748aa3f041f..f95fc8a1b1a45 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -243,6 +243,7 @@ #![feature(allocator_api)] #![feature(alloc_system)] #![feature(allocator_internals)] +#![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(asm)] #![feature(box_syntax)] diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 0172f89e05b6c..48f611a343941 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -91,13 +91,13 @@ pub struct LocalKey { // // Note that the thunk is itself unsafe because the returned lifetime of the // slot where data lives, `'static`, is not actually valid. The lifetime - // here is actually `'thread`! + // here is actually slightly shorter than the currently running thread! // // Although this is an extra layer of indirection, it should in theory be // trivially devirtualizable by LLVM because the value of `inner` never // changes and the constant should be readonly within a crate. This mainly // only runs into problems when TLS statics are exported across crates. - inner: fn() -> Option<&'static UnsafeCell>>, + inner: unsafe fn() -> Option<&'static UnsafeCell>>, // initialization routine to invoke to create a value init: fn() -> T, @@ -157,12 +157,13 @@ macro_rules! thread_local { issue = "0")] #[macro_export] #[allow_internal_unstable] +#[cfg_attr(not(stage0), allow_internal_unsafe)] 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< + unsafe fn __getit() -> $crate::option::Option< &'static $crate::cell::UnsafeCell< $crate::option::Option<$t>>> { @@ -178,7 +179,9 @@ macro_rules! __thread_local_inner { __KEY.get() } - $crate::thread::LocalKey::new(__getit, __init) + unsafe { + $crate::thread::LocalKey::new(__getit, __init) + } }; } } @@ -252,8 +255,8 @@ impl LocalKey { #[unstable(feature = "thread_local_internals", reason = "recently added to create a key", issue = "0")] - pub const fn new(inner: fn() -> Option<&'static UnsafeCell>>, - init: fn() -> T) -> LocalKey { + pub const unsafe fn new(inner: unsafe fn() -> Option<&'static UnsafeCell>>, + init: fn() -> T) -> LocalKey { LocalKey { inner: inner, init: init, @@ -391,6 +394,7 @@ pub mod fast { } } + #[cfg(stage0)] unsafe impl ::marker::Sync for Key { } impl Key { @@ -402,14 +406,12 @@ pub mod fast { } } - pub fn get(&'static self) -> Option<&'static UnsafeCell>> { - unsafe { - if mem::needs_drop::() && self.dtor_running.get() { - return None - } - self.register_dtor(); + pub unsafe fn get(&self) -> Option<&'static UnsafeCell>> { + if mem::needs_drop::() && self.dtor_running.get() { + return None } - Some(&self.inner) + self.register_dtor(); + Some(&*(&self.inner as *const _)) } unsafe fn register_dtor(&self) { @@ -478,26 +480,24 @@ pub mod os { } } - pub fn get(&'static self) -> Option<&'static UnsafeCell>> { - unsafe { - let ptr = self.os.get() as *mut Value; - if !ptr.is_null() { - if ptr as usize == 1 { - return None - } - return Some(&(*ptr).value); + pub unsafe fn get(&'static self) -> Option<&'static UnsafeCell>> { + let ptr = self.os.get() as *mut Value; + if !ptr.is_null() { + if ptr as usize == 1 { + return None } - - // If the lookup returned null, we haven't initialized our own - // local copy, so do that now. - let ptr: Box> = box Value { - key: self, - value: UnsafeCell::new(None), - }; - let ptr = Box::into_raw(ptr); - self.os.set(ptr as *mut u8); - Some(&(*ptr).value) + return Some(&(*ptr).value); } + + // If the lookup returned null, we haven't initialized our own + // local copy, so do that now. + let ptr: Box> = box Value { + key: self, + value: UnsafeCell::new(None), + }; + let ptr = Box::into_raw(ptr); + self.os.set(ptr as *mut u8); + Some(&(*ptr).value) } } diff --git a/src/test/compile-fail/issue-17954.rs b/src/test/compile-fail/issue-17954.rs new file mode 100644 index 0000000000000..4befe3ebc865a --- /dev/null +++ b/src/test/compile-fail/issue-17954.rs @@ -0,0 +1,25 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(thread_local)] + +#[thread_local] +static FOO: u8 = 3; + +fn main() { + let a = &FOO; + //~^ ERROR borrowed value does not live long enough + //~| does not live long enough + //~| NOTE borrowed value must be valid for the static lifetime + + std::thread::spawn(move || { + println!("{}", a); + }); +} //~ temporary value only lives until here diff --git a/src/test/compile-fail/issue-43733-2.rs b/src/test/compile-fail/issue-43733-2.rs new file mode 100644 index 0000000000000..3dff34c2ebb12 --- /dev/null +++ b/src/test/compile-fail/issue-43733-2.rs @@ -0,0 +1,39 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(const_fn, drop_types_in_const)] +#![feature(cfg_target_thread_local, thread_local_internals)] + +// On platforms *without* `#[thread_local]`, use +// a custom non-`Sync` type to fake the same error. +#[cfg(not(target_thread_local))] +struct Key { + _data: std::cell::UnsafeCell>, + _flag: std::cell::Cell, +} + +#[cfg(not(target_thread_local))] +impl Key { + const fn new() -> Self { + Key { + _data: std::cell::UnsafeCell::new(None), + _flag: std::cell::Cell::new(false), + } + } +} + +#[cfg(target_thread_local)] +use std::thread::__FastLocalKeyInner as Key; + +static __KEY: Key<()> = Key::new(); +//~^ ERROR `std::cell::UnsafeCell>: std::marker::Sync` is not satisfied +//~| ERROR `std::cell::Cell: std::marker::Sync` is not satisfied + +fn main() {} diff --git a/src/test/compile-fail/issue-43733.rs b/src/test/compile-fail/issue-43733.rs new file mode 100644 index 0000000000000..a4aad21a9f832 --- /dev/null +++ b/src/test/compile-fail/issue-43733.rs @@ -0,0 +1,41 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(const_fn, drop_types_in_const)] +#![feature(cfg_target_thread_local, thread_local_internals)] + +type Foo = std::cell::RefCell; + +#[cfg(target_thread_local)] +static __KEY: std::thread::__FastLocalKeyInner = + std::thread::__FastLocalKeyInner::new(); + +#[cfg(not(target_thread_local))] +static __KEY: std::thread::__OsLocalKeyInner = + std::thread::__OsLocalKeyInner::new(); + +fn __getit() -> std::option::Option< + &'static std::cell::UnsafeCell< + std::option::Option>> +{ + __KEY.get() //~ ERROR invocation of unsafe method requires unsafe +} + +static FOO: std::thread::LocalKey = + std::thread::LocalKey::new(__getit, Default::default); +//~^ ERROR call to unsafe function requires unsafe + +fn main() { + FOO.with(|foo| println!("{}", foo.borrow())); + std::thread::spawn(|| { + FOO.with(|foo| *foo.borrow_mut() += "foo"); + }).join().unwrap(); + FOO.with(|foo| println!("{}", foo.borrow())); +} diff --git a/src/test/compile-fail/thread-local-in-ctfe.rs b/src/test/compile-fail/thread-local-in-ctfe.rs new file mode 100644 index 0000000000000..720e15991c059 --- /dev/null +++ b/src/test/compile-fail/thread-local-in-ctfe.rs @@ -0,0 +1,38 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(const_fn, thread_local)] + +#[thread_local] +static A: u32 = 1; + +static B: u32 = A; +//~^ ERROR thread-local statics cannot be accessed at compile-time +//~| ERROR cannot refer to other statics by value +//~| WARN non-constant path in constant expression + +static C: &u32 = &A; +//~^ ERROR thread-local statics cannot be accessed at compile-time + +const D: u32 = A; +//~^ ERROR thread-local statics cannot be accessed at compile-time +//~| ERROR cannot refer to statics by value +//~| WARN non-constant path in constant expression + +const E: &u32 = &A; +//~^ ERROR thread-local statics cannot be accessed at compile-time + +const fn f() -> u32 { + A + //~^ ERROR thread-local statics cannot be accessed at compile-time + //~| ERROR cannot refer to statics by value +} + +fn main() {} diff --git a/src/test/run-pass/auxiliary/thread-local-extern-static.rs b/src/test/run-pass/auxiliary/thread-local-extern-static.rs index d1971a5e1aea4..e9457886be80d 100644 --- a/src/test/run-pass/auxiliary/thread-local-extern-static.rs +++ b/src/test/run-pass/auxiliary/thread-local-extern-static.rs @@ -8,10 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(thread_local)] -#![feature(cfg_target_thread_local)] +#![feature(cfg_target_thread_local, const_fn, thread_local)] #![crate_type = "lib"] +#[cfg(target_thread_local)] +use std::cell::Cell; + #[no_mangle] -#[cfg_attr(target_thread_local, thread_local)] -pub static FOO: u32 = 3; +#[cfg(target_thread_local)] +#[thread_local] +pub static FOO: Cell = Cell::new(3); diff --git a/src/test/run-pass/issue-30756.rs b/src/test/run-pass/issue-30756.rs index d21b42f8c876e..621607e5f6fa8 100644 --- a/src/test/run-pass/issue-30756.rs +++ b/src/test/run-pass/issue-30756.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(unsafe_code)] +#![forbid(unsafe_code)] thread_local!(static FOO: u8 = 1); diff --git a/src/test/run-pass/thread-local-extern-static.rs b/src/test/run-pass/thread-local-extern-static.rs index 87188db9dc0ed..09c8b64776c7b 100644 --- a/src/test/run-pass/thread-local-extern-static.rs +++ b/src/test/run-pass/thread-local-extern-static.rs @@ -11,18 +11,26 @@ // ignore-windows // aux-build:thread-local-extern-static.rs -#![feature(thread_local)] -#![feature(cfg_target_thread_local)] +#![feature(cfg_target_thread_local, thread_local)] +#[cfg(target_thread_local)] extern crate thread_local_extern_static; +#[cfg(target_thread_local)] +use std::cell::Cell; + +#[cfg(target_thread_local)] extern { - #[cfg_attr(target_thread_local, thread_local)] - static FOO: u32; + #[thread_local] + static FOO: Cell; } +#[cfg(target_thread_local)] fn main() { unsafe { - assert_eq!(FOO, 3); + assert_eq!(FOO.get(), 3); } } + +#[cfg(not(target_thread_local))] +fn main() {}