Auto merge of #30417 - alexcrichton:better-detect-elf-tls, r=alexcrichton
Currently a compiler can be built with the `--disable-elf-tls` option for compatibility with OSX 10.6 which doesn't have ELF TLS. This is unfortunate, however, as a whole new compiler must be generated which can take some time. These commits add a new (feature gated) `cfg(target_thread_local)` annotation set by the compiler which indicates whether `#[thread_local]` is available for use. The compiler now interprets `MACOSX_DEPLOYMENT_TARGET` (a standard environment variable) to set this flag on OSX. With this we may want to start compiling our OSX nightlies with `MACOSX_DEPLOYMENT_TARGET` set to 10.6 which would allow the compiler out-of-the-box to generate 10.6-compatible binaries. For now the compiler still by default targets OSX 10.7 by allowing ELF TLS by default (e.g. if `MACOSX_DEPLOYMENT_TARGET` isn't set).
This commit is contained in:
commit
42c3ef8f9f
12 changed files with 103 additions and 84 deletions
|
@ -660,6 +660,9 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig {
|
||||||
"windows" | "unix" => ret.push(attr::mk_word_item(fam)),
|
"windows" | "unix" => ret.push(attr::mk_word_item(fam)),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
if sess.target.target.options.has_elf_tls {
|
||||||
|
ret.push(attr::mk_word_item(InternedString::new("target_thread_local")));
|
||||||
|
}
|
||||||
if sess.opts.debug_assertions {
|
if sess.opts.debug_assertions {
|
||||||
ret.push(attr::mk_word_item(InternedString::new("debug_assertions")));
|
ret.push(attr::mk_word_item(InternedString::new("debug_assertions")));
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
use target::Target;
|
use target::Target;
|
||||||
|
|
||||||
pub fn target() -> Target {
|
pub fn target() -> Target {
|
||||||
let base = super::linux_base::opts();
|
let mut base = super::linux_base::opts();
|
||||||
|
base.has_elf_tls = false;
|
||||||
Target {
|
Target {
|
||||||
llvm_target: "aarch64-unknown-linux-gnu".to_string(),
|
llvm_target: "aarch64-unknown-linux-gnu".to_string(),
|
||||||
target_endian: "little".to_string(),
|
target_endian: "little".to_string(),
|
||||||
|
|
|
@ -8,10 +8,30 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
use target::TargetOptions;
|
use target::TargetOptions;
|
||||||
use std::default::Default;
|
|
||||||
|
|
||||||
pub fn opts() -> TargetOptions {
|
pub fn opts() -> TargetOptions {
|
||||||
|
// ELF TLS is only available in OSX 10.7+. If you try to compile for 10.6
|
||||||
|
// either the linker will complain if it is used or the binary will end up
|
||||||
|
// segfaulting at runtime when run on 10.6. Rust by default supports OSX
|
||||||
|
// 10.7+, but there is a standard environment variable,
|
||||||
|
// MACOSX_DEPLOYMENT_TARGET, which is used to signal targeting older
|
||||||
|
// versions of OSX. For example compiling on 10.10 with
|
||||||
|
// MACOSX_DEPLOYMENT_TARGET set to 10.6 will cause the linker to generate
|
||||||
|
// warnings about the usage of ELF TLS.
|
||||||
|
//
|
||||||
|
// Here we detect what version is being requested, defaulting to 10.7. ELF
|
||||||
|
// TLS is flagged as enabled if it looks to be supported.
|
||||||
|
let deployment_target = env::var("MACOSX_DEPLOYMENT_TARGET").ok();
|
||||||
|
let version = deployment_target.as_ref().and_then(|s| {
|
||||||
|
let mut i = s.splitn(2, ".");
|
||||||
|
i.next().and_then(|a| i.next().map(|b| (a, b)))
|
||||||
|
}).and_then(|(a, b)| {
|
||||||
|
a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok()
|
||||||
|
}).unwrap_or((10, 7));
|
||||||
|
|
||||||
TargetOptions {
|
TargetOptions {
|
||||||
// OSX has -dead_strip, which doesn't rely on ffunction_sections
|
// OSX has -dead_strip, which doesn't rely on ffunction_sections
|
||||||
function_sections: false,
|
function_sections: false,
|
||||||
|
@ -25,6 +45,7 @@ pub fn opts() -> TargetOptions {
|
||||||
archive_format: "bsd".to_string(),
|
archive_format: "bsd".to_string(),
|
||||||
pre_link_args: Vec::new(),
|
pre_link_args: Vec::new(),
|
||||||
exe_allocation_crate: super::maybe_jemalloc(),
|
exe_allocation_crate: super::maybe_jemalloc(),
|
||||||
|
has_elf_tls: version >= (10, 7),
|
||||||
.. Default::default()
|
.. Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,7 @@ pub fn opts(arch: Arch) -> TargetOptions {
|
||||||
dynamic_linking: false,
|
dynamic_linking: false,
|
||||||
executables: true,
|
executables: true,
|
||||||
pre_link_args: pre_link_args(arch),
|
pre_link_args: pre_link_args(arch),
|
||||||
|
has_elf_tls: false,
|
||||||
.. super::apple_base::opts()
|
.. super::apple_base::opts()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ use target::Target;
|
||||||
pub fn target() -> Target {
|
pub fn target() -> Target {
|
||||||
let mut base = super::android_base::opts();
|
let mut base = super::android_base::opts();
|
||||||
base.features = "+v7".to_string();
|
base.features = "+v7".to_string();
|
||||||
|
base.has_elf_tls = false;
|
||||||
|
|
||||||
Target {
|
Target {
|
||||||
llvm_target: "arm-linux-androideabi".to_string(),
|
llvm_target: "arm-linux-androideabi".to_string(),
|
||||||
|
|
|
@ -30,6 +30,7 @@ pub fn opts() -> TargetOptions {
|
||||||
position_independent_executables: true,
|
position_independent_executables: true,
|
||||||
archive_format: "gnu".to_string(),
|
archive_format: "gnu".to_string(),
|
||||||
exe_allocation_crate: super::maybe_jemalloc(),
|
exe_allocation_crate: super::maybe_jemalloc(),
|
||||||
|
has_elf_tls: true,
|
||||||
.. Default::default()
|
.. Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,6 +195,10 @@ pub struct TargetOptions {
|
||||||
/// Default crate for allocation symbols to link against
|
/// Default crate for allocation symbols to link against
|
||||||
pub lib_allocation_crate: String,
|
pub lib_allocation_crate: String,
|
||||||
pub exe_allocation_crate: String,
|
pub exe_allocation_crate: String,
|
||||||
|
|
||||||
|
/// Flag indicating whether ELF TLS (e.g. #[thread_local]) is available for
|
||||||
|
/// this target.
|
||||||
|
pub has_elf_tls: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TargetOptions {
|
impl Default for TargetOptions {
|
||||||
|
@ -240,6 +244,7 @@ impl Default for TargetOptions {
|
||||||
lib_allocation_crate: "alloc_system".to_string(),
|
lib_allocation_crate: "alloc_system".to_string(),
|
||||||
exe_allocation_crate: "alloc_system".to_string(),
|
exe_allocation_crate: "alloc_system".to_string(),
|
||||||
allow_asm: true,
|
allow_asm: true,
|
||||||
|
has_elf_tls: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -606,7 +606,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
|
||||||
feature_gated_cfgs.sort();
|
feature_gated_cfgs.sort();
|
||||||
feature_gated_cfgs.dedup();
|
feature_gated_cfgs.dedup();
|
||||||
for cfg in &feature_gated_cfgs {
|
for cfg in &feature_gated_cfgs {
|
||||||
cfg.check_and_emit(sess.diagnostic(), &features);
|
cfg.check_and_emit(sess.diagnostic(), &features, sess.codemap());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -218,6 +218,7 @@
|
||||||
#![feature(borrow_state)]
|
#![feature(borrow_state)]
|
||||||
#![feature(box_syntax)]
|
#![feature(box_syntax)]
|
||||||
#![feature(cfg_target_vendor)]
|
#![feature(cfg_target_vendor)]
|
||||||
|
#![feature(cfg_target_thread_local)]
|
||||||
#![feature(char_internals)]
|
#![feature(char_internals)]
|
||||||
#![feature(clone_from_slice)]
|
#![feature(clone_from_slice)]
|
||||||
#![feature(collections)]
|
#![feature(collections)]
|
||||||
|
|
|
@ -15,10 +15,6 @@
|
||||||
use cell::UnsafeCell;
|
use cell::UnsafeCell;
|
||||||
use mem;
|
use mem;
|
||||||
|
|
||||||
// Sure wish we had macro hygiene, no?
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use self::imp::Key as __KeyInner;
|
|
||||||
|
|
||||||
/// A thread local storage key which owns its contents.
|
/// A thread local storage key which owns its contents.
|
||||||
///
|
///
|
||||||
/// This key uses the fastest possible implementation available to it for the
|
/// This key uses the fastest possible implementation available to it for the
|
||||||
|
@ -61,41 +57,27 @@ pub use self::imp::Key as __KeyInner;
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub struct LocalKey<T:'static> {
|
pub struct LocalKey<T: 'static> {
|
||||||
// The key itself may be tagged with #[thread_local], and this `Key` is
|
// This outer `LocalKey<T>` type is what's going to be stored in statics,
|
||||||
// stored as a `static`, and it's not valid for a static to reference the
|
// but actual data inside will sometimes be tagged with #[thread_local].
|
||||||
// address of another thread_local static. For this reason we kinda wonkily
|
// It's not valid for a true static to reference a #[thread_local] static,
|
||||||
// work around this by generating a shim function which will give us the
|
// so we get around that by exposing an accessor through a layer of function
|
||||||
// address of the inner TLS key at runtime.
|
// indirection (this thunk).
|
||||||
//
|
//
|
||||||
// This is trivially devirtualizable by LLVM because we never store anything
|
// Note that the thunk is itself unsafe because the returned lifetime of the
|
||||||
// to this field and rustc can declare the `static` as constant as well.
|
// slot where data lives, `'static`, is not actually valid. The lifetime
|
||||||
inner: fn() -> &'static __KeyInner<T>,
|
// here is actually `'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: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>,
|
||||||
|
|
||||||
// initialization routine to invoke to create a value
|
// initialization routine to invoke to create a value
|
||||||
init: fn() -> T,
|
init: fn() -> T,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Macro pain #4586:
|
|
||||||
//
|
|
||||||
// When cross compiling, rustc will load plugins and macros from the *host*
|
|
||||||
// platform before search for macros from the target platform. This is primarily
|
|
||||||
// done to detect, for example, plugins. Ideally the macro below would be
|
|
||||||
// defined once per module below, but unfortunately this means we have the
|
|
||||||
// following situation:
|
|
||||||
//
|
|
||||||
// 1. We compile libstd for x86_64-unknown-linux-gnu, this thread_local!() macro
|
|
||||||
// will inject #[thread_local] statics.
|
|
||||||
// 2. We then try to compile a program for arm-linux-androideabi
|
|
||||||
// 3. The compiler has a host of linux and a target of android, so it loads
|
|
||||||
// macros from the *linux* libstd.
|
|
||||||
// 4. The macro generates a #[thread_local] field, but the android libstd does
|
|
||||||
// not use #[thread_local]
|
|
||||||
// 5. Compile error about structs with wrong fields.
|
|
||||||
//
|
|
||||||
// To get around this, we're forced to inject the #[cfg] logic into the macro
|
|
||||||
// itself. Woohoo.
|
|
||||||
|
|
||||||
/// Declare a new thread local storage key of type `std::thread::LocalKey`.
|
/// Declare a new thread local storage key of type `std::thread::LocalKey`.
|
||||||
///
|
///
|
||||||
/// See [LocalKey documentation](thread/struct.LocalKey.html) for more
|
/// See [LocalKey documentation](thread/struct.LocalKey.html) for more
|
||||||
|
@ -103,36 +85,14 @@ pub struct LocalKey<T:'static> {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[allow_internal_unstable]
|
#[allow_internal_unstable]
|
||||||
#[cfg(not(no_elf_tls))]
|
|
||||||
macro_rules! thread_local {
|
macro_rules! thread_local {
|
||||||
(static $name:ident: $t:ty = $init:expr) => (
|
(static $name:ident: $t:ty = $init:expr) => (
|
||||||
static $name: $crate::thread::LocalKey<$t> =
|
static $name: $crate::thread::LocalKey<$t> =
|
||||||
__thread_local_inner!($t, $init,
|
__thread_local_inner!($t, $init);
|
||||||
#[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
|
|
||||||
not(target_arch = "aarch64")),
|
|
||||||
thread_local)]);
|
|
||||||
);
|
);
|
||||||
(pub static $name:ident: $t:ty = $init:expr) => (
|
(pub static $name:ident: $t:ty = $init:expr) => (
|
||||||
pub static $name: $crate::thread::LocalKey<$t> =
|
pub static $name: $crate::thread::LocalKey<$t> =
|
||||||
__thread_local_inner!($t, $init,
|
__thread_local_inner!($t, $init);
|
||||||
#[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
|
|
||||||
not(target_arch = "aarch64")),
|
|
||||||
thread_local)]);
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[allow_internal_unstable]
|
|
||||||
#[cfg(no_elf_tls)]
|
|
||||||
macro_rules! thread_local {
|
|
||||||
(static $name:ident: $t:ty = $init:expr) => (
|
|
||||||
static $name: $crate::thread::LocalKey<$t> =
|
|
||||||
__thread_local_inner!($t, $init, #[]);
|
|
||||||
);
|
|
||||||
(pub static $name:ident: $t:ty = $init:expr) => (
|
|
||||||
pub static $name: $crate::thread::LocalKey<$t> =
|
|
||||||
__thread_local_inner!($t, $init, #[]);
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,12 +103,25 @@ macro_rules! thread_local {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[allow_internal_unstable]
|
#[allow_internal_unstable]
|
||||||
macro_rules! __thread_local_inner {
|
macro_rules! __thread_local_inner {
|
||||||
($t:ty, $init:expr, #[$($attr:meta),*]) => {{
|
($t:ty, $init:expr) => {{
|
||||||
$(#[$attr])*
|
|
||||||
static __KEY: $crate::thread::__LocalKeyInner<$t> =
|
|
||||||
$crate::thread::__LocalKeyInner::new();
|
|
||||||
fn __init() -> $t { $init }
|
fn __init() -> $t { $init }
|
||||||
fn __getit() -> &'static $crate::thread::__LocalKeyInner<$t> { &__KEY }
|
|
||||||
|
unsafe fn __getit() -> $crate::option::Option<
|
||||||
|
&'static $crate::cell::UnsafeCell<
|
||||||
|
$crate::option::Option<$t>>>
|
||||||
|
{
|
||||||
|
#[thread_local]
|
||||||
|
#[cfg(target_thread_local)]
|
||||||
|
static __KEY: $crate::thread::__ElfLocalKeyInner<$t> =
|
||||||
|
$crate::thread::__ElfLocalKeyInner::new();
|
||||||
|
|
||||||
|
#[cfg(not(target_thread_local))]
|
||||||
|
static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
|
||||||
|
$crate::thread::__OsLocalKeyInner::new();
|
||||||
|
|
||||||
|
__KEY.get()
|
||||||
|
}
|
||||||
|
|
||||||
$crate::thread::LocalKey::new(__getit, __init)
|
$crate::thread::LocalKey::new(__getit, __init)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -190,11 +163,11 @@ impl<T: 'static> LocalKey<T> {
|
||||||
#[unstable(feature = "thread_local_internals",
|
#[unstable(feature = "thread_local_internals",
|
||||||
reason = "recently added to create a key",
|
reason = "recently added to create a key",
|
||||||
issue = "0")]
|
issue = "0")]
|
||||||
pub const fn new(inner: fn() -> &'static __KeyInner<T>,
|
pub const fn new(inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>,
|
||||||
init: fn() -> T) -> LocalKey<T> {
|
init: fn() -> T) -> LocalKey<T> {
|
||||||
LocalKey {
|
LocalKey {
|
||||||
inner: inner,
|
inner: inner,
|
||||||
init: init
|
init: init,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,10 +184,10 @@ impl<T: 'static> LocalKey<T> {
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub fn with<F, R>(&'static self, f: F) -> R
|
pub fn with<F, R>(&'static self, f: F) -> R
|
||||||
where F: FnOnce(&T) -> R {
|
where F: FnOnce(&T) -> R {
|
||||||
let slot = (self.inner)();
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let slot = slot.get().expect("cannot access a TLS value during or \
|
let slot = (self.inner)();
|
||||||
after it is destroyed");
|
let slot = slot.expect("cannot access a TLS value during or \
|
||||||
|
after it is destroyed");
|
||||||
f(match *slot.get() {
|
f(match *slot.get() {
|
||||||
Some(ref inner) => inner,
|
Some(ref inner) => inner,
|
||||||
None => self.init(slot),
|
None => self.init(slot),
|
||||||
|
@ -270,7 +243,7 @@ impl<T: 'static> LocalKey<T> {
|
||||||
issue = "27716")]
|
issue = "27716")]
|
||||||
pub fn state(&'static self) -> LocalKeyState {
|
pub fn state(&'static self) -> LocalKeyState {
|
||||||
unsafe {
|
unsafe {
|
||||||
match (self.inner)().get() {
|
match (self.inner)() {
|
||||||
Some(cell) => {
|
Some(cell) => {
|
||||||
match *cell.get() {
|
match *cell.get() {
|
||||||
Some(..) => LocalKeyState::Valid,
|
Some(..) => LocalKeyState::Valid,
|
||||||
|
@ -283,11 +256,9 @@ impl<T: 'static> LocalKey<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(any(target_os = "macos", target_os = "linux"),
|
#[cfg(target_thread_local)]
|
||||||
not(target_arch = "aarch64"),
|
|
||||||
not(no_elf_tls)))]
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
mod imp {
|
pub mod elf {
|
||||||
use cell::{Cell, UnsafeCell};
|
use cell::{Cell, UnsafeCell};
|
||||||
use intrinsics;
|
use intrinsics;
|
||||||
use ptr;
|
use ptr;
|
||||||
|
@ -431,11 +402,8 @@ mod imp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(not(any(target_os = "macos", target_os = "linux")),
|
|
||||||
target_arch = "aarch64",
|
|
||||||
no_elf_tls))]
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
mod imp {
|
pub mod os {
|
||||||
use prelude::v1::*;
|
use prelude::v1::*;
|
||||||
|
|
||||||
use cell::{Cell, UnsafeCell};
|
use cell::{Cell, UnsafeCell};
|
||||||
|
|
|
@ -191,7 +191,10 @@ pub use self::local::{LocalKey, LocalKeyState};
|
||||||
pub use self::scoped_tls::ScopedKey;
|
pub use self::scoped_tls::ScopedKey;
|
||||||
|
|
||||||
#[unstable(feature = "libstd_thread_internals", issue = "0")]
|
#[unstable(feature = "libstd_thread_internals", issue = "0")]
|
||||||
#[doc(hidden)] pub use self::local::__KeyInner as __LocalKeyInner;
|
#[cfg(target_thread_local)]
|
||||||
|
#[doc(hidden)] pub use self::local::elf::Key as __ElfLocalKeyInner;
|
||||||
|
#[unstable(feature = "libstd_thread_internals", issue = "0")]
|
||||||
|
#[doc(hidden)] pub use self::local::os::Key as __OsLocalKeyInner;
|
||||||
#[unstable(feature = "libstd_thread_internals", issue = "0")]
|
#[unstable(feature = "libstd_thread_internals", issue = "0")]
|
||||||
#[doc(hidden)] pub use self::scoped_tls::__KeyInner as __ScopedKeyInner;
|
#[doc(hidden)] pub use self::scoped_tls::__KeyInner as __ScopedKeyInner;
|
||||||
|
|
||||||
|
|
|
@ -236,6 +236,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
|
||||||
|
|
||||||
// allow using type ascription in expressions
|
// allow using type ascription in expressions
|
||||||
("type_ascription", "1.6.0", Some(23416), Active),
|
("type_ascription", "1.6.0", Some(23416), Active),
|
||||||
|
|
||||||
|
// Allows cfg(target_thread_local)
|
||||||
|
("cfg_target_thread_local", "1.7.0", Some(29594), Active),
|
||||||
];
|
];
|
||||||
// (changing above list without updating src/doc/reference.md makes @cmr sad)
|
// (changing above list without updating src/doc/reference.md makes @cmr sad)
|
||||||
|
|
||||||
|
@ -414,6 +417,8 @@ const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)]
|
||||||
// (name in cfg, feature, function to check if the feature is enabled)
|
// (name in cfg, feature, function to check if the feature is enabled)
|
||||||
("target_feature", "cfg_target_feature", cfg_fn!(|x| x.cfg_target_feature)),
|
("target_feature", "cfg_target_feature", cfg_fn!(|x| x.cfg_target_feature)),
|
||||||
("target_vendor", "cfg_target_vendor", cfg_fn!(|x| x.cfg_target_vendor)),
|
("target_vendor", "cfg_target_vendor", cfg_fn!(|x| x.cfg_target_vendor)),
|
||||||
|
("target_thread_local", "cfg_target_thread_local",
|
||||||
|
cfg_fn!(|x| x.cfg_target_thread_local)),
|
||||||
];
|
];
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
@ -449,10 +454,13 @@ impl PartialOrd for GatedCfgAttr {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GatedCfgAttr {
|
impl GatedCfgAttr {
|
||||||
pub fn check_and_emit(&self, diagnostic: &Handler, features: &Features) {
|
pub fn check_and_emit(&self,
|
||||||
|
diagnostic: &Handler,
|
||||||
|
features: &Features,
|
||||||
|
codemap: &CodeMap) {
|
||||||
match *self {
|
match *self {
|
||||||
GatedCfgAttr::GatedCfg(ref cfg) => {
|
GatedCfgAttr::GatedCfg(ref cfg) => {
|
||||||
cfg.check_and_emit(diagnostic, features);
|
cfg.check_and_emit(diagnostic, features, codemap);
|
||||||
}
|
}
|
||||||
GatedCfgAttr::GatedAttr(span) => {
|
GatedCfgAttr::GatedAttr(span) => {
|
||||||
if !features.stmt_expr_attributes {
|
if !features.stmt_expr_attributes {
|
||||||
|
@ -479,9 +487,12 @@ impl GatedCfg {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn check_and_emit(&self, diagnostic: &Handler, features: &Features) {
|
fn check_and_emit(&self,
|
||||||
|
diagnostic: &Handler,
|
||||||
|
features: &Features,
|
||||||
|
codemap: &CodeMap) {
|
||||||
let (cfg, feature, has_feature) = GATED_CFGS[self.index];
|
let (cfg, feature, has_feature) = GATED_CFGS[self.index];
|
||||||
if !has_feature(features) {
|
if !has_feature(features) && !codemap.span_allows_unstable(self.span) {
|
||||||
let explain = format!("`cfg({})` is experimental and subject to change", cfg);
|
let explain = format!("`cfg({})` is experimental and subject to change", cfg);
|
||||||
emit_feature_err(diagnostic, feature, self.span, GateIssue::Language, &explain);
|
emit_feature_err(diagnostic, feature, self.span, GateIssue::Language, &explain);
|
||||||
}
|
}
|
||||||
|
@ -541,6 +552,7 @@ pub struct Features {
|
||||||
pub type_macros: bool,
|
pub type_macros: bool,
|
||||||
pub cfg_target_feature: bool,
|
pub cfg_target_feature: bool,
|
||||||
pub cfg_target_vendor: bool,
|
pub cfg_target_vendor: bool,
|
||||||
|
pub cfg_target_thread_local: bool,
|
||||||
pub augmented_assignments: bool,
|
pub augmented_assignments: bool,
|
||||||
pub braced_empty_structs: bool,
|
pub braced_empty_structs: bool,
|
||||||
pub staged_api: bool,
|
pub staged_api: bool,
|
||||||
|
@ -575,6 +587,7 @@ impl Features {
|
||||||
type_macros: false,
|
type_macros: false,
|
||||||
cfg_target_feature: false,
|
cfg_target_feature: false,
|
||||||
cfg_target_vendor: false,
|
cfg_target_vendor: false,
|
||||||
|
cfg_target_thread_local: false,
|
||||||
augmented_assignments: false,
|
augmented_assignments: false,
|
||||||
braced_empty_structs: false,
|
braced_empty_structs: false,
|
||||||
staged_api: false,
|
staged_api: false,
|
||||||
|
@ -1157,6 +1170,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &Handler,
|
||||||
type_macros: cx.has_feature("type_macros"),
|
type_macros: cx.has_feature("type_macros"),
|
||||||
cfg_target_feature: cx.has_feature("cfg_target_feature"),
|
cfg_target_feature: cx.has_feature("cfg_target_feature"),
|
||||||
cfg_target_vendor: cx.has_feature("cfg_target_vendor"),
|
cfg_target_vendor: cx.has_feature("cfg_target_vendor"),
|
||||||
|
cfg_target_thread_local: cx.has_feature("cfg_target_thread_local"),
|
||||||
augmented_assignments: cx.has_feature("augmented_assignments"),
|
augmented_assignments: cx.has_feature("augmented_assignments"),
|
||||||
braced_empty_structs: cx.has_feature("braced_empty_structs"),
|
braced_empty_structs: cx.has_feature("braced_empty_structs"),
|
||||||
staged_api: cx.has_feature("staged_api"),
|
staged_api: cx.has_feature("staged_api"),
|
||||||
|
|
Loading…
Reference in a new issue