diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 421a81c0d2342..adf7d901be9c0 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -404,6 +404,7 @@ impl<'a> LoweringContext<'a> { format: codemap::CompilerDesugaring(Symbol::intern(reason)), span: Some(span), allow_internal_unstable: true, + allow_internal_unsafe: false, }, }); span.ctxt = SyntaxContext::empty().apply_mark(mark); 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_allocator/expand.rs b/src/librustc_allocator/expand.rs index 676c3c51ea2a3..78b07a33389e2 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -79,6 +79,7 @@ impl<'a> Folder for ExpandAllocatorDirectives<'a> { format: MacroAttribute(Symbol::intern(name)), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, } }); let span = Span { diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 88432e6429031..7767cf434032a 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -195,12 +195,23 @@ impl LintPass for UnsafeCode { } } +impl UnsafeCode { + fn report_unsafe(&self, cx: &LateContext, span: Span, desc: &'static str) { + // This comes from a macro that has #[allow_internal_unsafe]. + if span.allows_unsafe() { + return; + } + + cx.span_lint(UNSAFE_CODE, span, desc); + } +} + impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { if let hir::ExprBlock(ref blk) = e.node { // Don't warn about generated blocks, that'll just pollute the output. if blk.rules == hir::UnsafeBlock(hir::UserProvided) { - cx.span_lint(UNSAFE_CODE, blk.span, "usage of an `unsafe` block"); + self.report_unsafe(cx, blk.span, "usage of an `unsafe` block"); } } } @@ -208,11 +219,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { match it.node { hir::ItemTrait(hir::Unsafety::Unsafe, ..) => { - cx.span_lint(UNSAFE_CODE, it.span, "declaration of an `unsafe` trait") + self.report_unsafe(cx, it.span, "declaration of an `unsafe` trait") } hir::ItemImpl(hir::Unsafety::Unsafe, ..) => { - cx.span_lint(UNSAFE_CODE, it.span, "implementation of an `unsafe` trait") + self.report_unsafe(cx, it.span, "implementation of an `unsafe` trait") } _ => return, @@ -228,12 +239,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { _: ast::NodeId) { match fk { FnKind::ItemFn(_, _, hir::Unsafety::Unsafe, ..) => { - cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function") + self.report_unsafe(cx, span, "declaration of an `unsafe` function") } FnKind::Method(_, sig, ..) => { if sig.unsafety == hir::Unsafety::Unsafe { - cx.span_lint(UNSAFE_CODE, span, "implementation of an `unsafe` method") + self.report_unsafe(cx, span, "implementation of an `unsafe` method") } } @@ -244,9 +255,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { fn check_trait_item(&mut self, cx: &LateContext, item: &hir::TraitItem) { if let hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Required(_)) = item.node { if sig.unsafety == hir::Unsafety::Unsafe { - cx.span_lint(UNSAFE_CODE, - item.span, - "declaration of an `unsafe` method") + self.report_unsafe(cx, item.span, "declaration of an `unsafe` method") } } } 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/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs index 3027489d65be2..aac21f2af0d96 100644 --- a/src/librustc_plugin/registry.rs +++ b/src/librustc_plugin/registry.rs @@ -102,9 +102,19 @@ impl<'a> Registry<'a> { panic!("user-defined macros may not be named `macro_rules`"); } self.syntax_exts.push((name, match extension { - NormalTT(ext, _, allow_internal_unstable) => { + NormalTT { + expander, + def_info: _, + allow_internal_unstable, + allow_internal_unsafe + } => { let nid = ast::CRATE_NODE_ID; - NormalTT(ext, Some((nid, self.krate_span)), allow_internal_unstable) + NormalTT { + expander, + def_info: Some((nid, self.krate_span)), + allow_internal_unstable, + allow_internal_unsafe + } } IdentTT(ext, _, allow_internal_unstable) => { IdentTT(ext, Some(self.krate_span), allow_internal_unstable) @@ -134,8 +144,12 @@ impl<'a> Registry<'a> { /// It builds for you a `NormalTT` that calls `expander`, /// and also takes care of interning the macro's name. pub fn register_macro(&mut self, name: &str, expander: MacroExpanderFn) { - self.register_syntax_extension(Symbol::intern(name), - NormalTT(Box::new(expander), None, false)); + self.register_syntax_extension(Symbol::intern(name), NormalTT { + expander: Box::new(expander), + def_info: None, + allow_internal_unstable: false, + allow_internal_unsafe: false, + }); } /// Register a compiler lint pass. diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 98eaa056177ef..7572c4aa6802a 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -313,7 +313,7 @@ impl<'a> base::Resolver for Resolver<'a> { fn check_unused_macros(&self) { for did in self.unused_macros.iter() { let id_span = match *self.macro_map[did] { - SyntaxExtension::NormalTT(_, isp, _) => isp, + SyntaxExtension::NormalTT { def_info, .. } => def_info, SyntaxExtension::DeclMacro(.., osp) => osp, _ => None, }; 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/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 194d30e25d410..72b2552f64fc6 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -532,10 +532,16 @@ pub enum SyntaxExtension { /// A normal, function-like syntax extension. /// /// `bytes!` is a `NormalTT`. - /// - /// The `bool` dictates whether the contents of the macro can - /// directly use `#[unstable]` things (true == yes). - NormalTT(Box, Option<(ast::NodeId, Span)>, bool), + NormalTT { + expander: Box, + def_info: Option<(ast::NodeId, Span)>, + /// Whether the contents of the macro can + /// directly use `#[unstable]` things (true == yes). + allow_internal_unstable: bool, + /// Whether the contents of the macro can use `unsafe` + /// without triggering the `unsafe_code` lint. + allow_internal_unsafe: bool, + }, /// A function-like syntax extension that has an extra ident before /// the block. @@ -562,7 +568,7 @@ impl SyntaxExtension { pub fn kind(&self) -> MacroKind { match *self { SyntaxExtension::DeclMacro(..) | - SyntaxExtension::NormalTT(..) | + SyntaxExtension::NormalTT { .. } | SyntaxExtension::IdentTT(..) | SyntaxExtension::ProcMacro(..) => MacroKind::Bang, diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index e7c5d8278d977..38715f7275de5 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -64,6 +64,7 @@ pub fn add_derived_markers(cx: &mut ExtCtxt, span: Span, traits: &[ast::Path] format: ExpnFormat::MacroAttribute(Symbol::intern(&pretty_name)), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, }, }); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 4843a66a750fa..9625602fa4a5a 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -411,6 +411,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { format: MacroAttribute(Symbol::intern(&format!("{}", attr.path))), span: None, allow_internal_unstable: false, + allow_internal_unsafe: false, } }); @@ -458,7 +459,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let path = &mac.node.path; let ident = ident.unwrap_or_else(|| keywords::Invalid.ident()); - let validate_and_set_expn_info = |def_site_span, allow_internal_unstable| { + let validate_and_set_expn_info = |def_site_span, + allow_internal_unstable, + allow_internal_unsafe| { if ident.name != keywords::Invalid.name() { return Err(format!("macro {}! expects no ident argument, given '{}'", path, ident)); } @@ -467,7 +470,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { callee: NameAndSpan { format: MacroBang(Symbol::intern(&format!("{}", path))), span: def_site_span, - allow_internal_unstable: allow_internal_unstable, + allow_internal_unstable, + allow_internal_unsafe, }, }); Ok(()) @@ -476,20 +480,26 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let opt_expanded = match *ext { DeclMacro(ref expand, def_span) => { if let Err(msg) = validate_and_set_expn_info(def_span.map(|(_, s)| s), - false) { + false, false) { self.cx.span_err(path.span, &msg); return kind.dummy(span); } kind.make_from(expand.expand(self.cx, span, mac.node.stream())) } - NormalTT(ref expandfun, def_info, allow_internal_unstable) => { + NormalTT { + ref expander, + def_info, + allow_internal_unstable, + allow_internal_unsafe + } => { if let Err(msg) = validate_and_set_expn_info(def_info.map(|(_, s)| s), - allow_internal_unstable) { + allow_internal_unstable, + allow_internal_unsafe) { self.cx.span_err(path.span, &msg); return kind.dummy(span); } - kind.make_from(expandfun.expand(self.cx, span, mac.node.stream())) + kind.make_from(expander.expand(self.cx, span, mac.node.stream())) } IdentTT(ref expander, tt_span, allow_internal_unstable) => { @@ -504,7 +514,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { callee: NameAndSpan { format: MacroBang(Symbol::intern(&format!("{}", path))), span: tt_span, - allow_internal_unstable: allow_internal_unstable, + allow_internal_unstable, + allow_internal_unsafe: false, } }); @@ -540,6 +551,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { span: None, // FIXME probably want to follow macro_rules macros here. allow_internal_unstable: false, + allow_internal_unsafe: false, }, }); @@ -578,6 +590,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { format: MacroAttribute(pretty_name), span: None, allow_internal_unstable: false, + allow_internal_unsafe: false, } }; diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 80b6794d1e3cc..7b3fe2bd993a9 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -269,7 +269,7 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) valid &= check_lhs_no_empty_seq(sess, &[lhs.clone()]) } - let exp: Box<_> = Box::new(MacroRulesMacroExpander { + let expander: Box<_> = Box::new(MacroRulesMacroExpander { name: def.ident, lhses: lhses, rhses: rhses, @@ -278,9 +278,15 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) if body.legacy { let allow_internal_unstable = attr::contains_name(&def.attrs, "allow_internal_unstable"); - NormalTT(exp, Some((def.id, def.span)), allow_internal_unstable) + let allow_internal_unsafe = attr::contains_name(&def.attrs, "allow_internal_unsafe"); + NormalTT { + expander, + def_info: Some((def.id, def.span)), + allow_internal_unstable, + allow_internal_unsafe + } } else { - SyntaxExtension::DeclMacro(exp, Some((def.id, def.span))) + SyntaxExtension::DeclMacro(expander, Some((def.id, def.span))) } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index aeb574bc3af3c..46ee126b9d901 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -194,6 +194,14 @@ declare_features! ( // rustc internal (active, allow_internal_unstable, "1.0.0", None), + // Allows the use of #[allow_internal_unsafe]. This is an + // attribute on macro_rules! and can't use the attribute handling + // below (it has to be checked before expansion possibly makes + // macros disappear). + // + // rustc internal + (active, allow_internal_unsafe, "1.0.0", None), + // #23121. Array patterns have some hazards yet. (active, slice_patterns, "1.0.0", Some(23121)), @@ -735,6 +743,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG EXPLAIN_ALLOW_INTERNAL_UNSTABLE, cfg_fn!(allow_internal_unstable))), + ("allow_internal_unsafe", Normal, Gated(Stability::Unstable, + "allow_internal_unsafe", + EXPLAIN_ALLOW_INTERNAL_UNSAFE, + cfg_fn!(allow_internal_unsafe))), + ("fundamental", Whitelisted, Gated(Stability::Unstable, "fundamental", "the `#[fundamental]` attribute \ @@ -1045,6 +1058,8 @@ pub const EXPLAIN_TRACE_MACROS: &'static str = "`trace_macros` is not stable enough for use and is subject to change"; pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str = "allow_internal_unstable side-steps feature gating and stability checks"; +pub const EXPLAIN_ALLOW_INTERNAL_UNSAFE: &'static str = + "allow_internal_unsafe side-steps the unsafe_code lint"; pub const EXPLAIN_CUSTOM_DERIVE: &'static str = "`#[derive]` for custom traits is deprecated and will be removed in the future."; diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index d9ed96f293a80..430976e7d3ce4 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -28,6 +28,7 @@ fn ignored_span(sp: Span) -> Span { format: MacroAttribute(Symbol::intern("std_inject")), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, } }); Span { ctxt: SyntaxContext::empty().apply_mark(mark), ..sp } diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 887479a247249..c05e166e01331 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -291,6 +291,7 @@ fn generate_test_harness(sess: &ParseSess, format: MacroAttribute(Symbol::intern("test")), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, } }); diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 5eab81dd28fc4..439538a8b5ee3 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -64,7 +64,12 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, macro_rules! register { ($( $name:ident: $f:expr, )*) => { $( register(Symbol::intern(stringify!($name)), - NormalTT(Box::new($f as MacroExpanderFn), None, false)); + NormalTT { + expander: Box::new($f as MacroExpanderFn), + def_info: None, + allow_internal_unstable: false, + allow_internal_unsafe: false, + }); )* } } @@ -112,7 +117,12 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, // format_args uses `unstable` things internally. register(Symbol::intern("format_args"), - NormalTT(Box::new(format::expand_format_args), None, true)); + NormalTT { + expander: Box::new(format::expand_format_args), + def_info: None, + allow_internal_unstable: true, + allow_internal_unsafe: false, + }); for (name, ext) in user_exts { register(name, ext); diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs index ab6d73e5061a0..700386f68fee0 100644 --- a/src/libsyntax_ext/proc_macro_registrar.rs +++ b/src/libsyntax_ext/proc_macro_registrar.rs @@ -368,6 +368,7 @@ fn mk_registrar(cx: &mut ExtCtxt, format: MacroAttribute(Symbol::intern("proc_macro")), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, } }); let span = Span { ctxt: SyntaxContext::empty().apply_mark(mark), ..DUMMY_SP }; diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 804b91ab09e3c..514cc26666e39 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -310,6 +310,9 @@ pub struct NameAndSpan { /// features internally without forcing the whole crate to opt-in /// to them. pub allow_internal_unstable: bool, + /// Whether the macro is allowed to use `unsafe` internally + /// even if the user crate has `#![forbid(unsafe_code)]`. + pub allow_internal_unsafe: bool, /// The span of the macro definition itself. The macro may not /// have a sensible definition span (e.g. something defined /// completely inside libsyntax) in which case this is None. diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 7006f45455e38..e162bc26412f2 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -153,6 +153,16 @@ impl Span { } } + /// Check if a span is "internal" to a macro in which `unsafe` + /// can be used without triggering the `unsafe_code` lint + // (that is, a macro marked with `#[allow_internal_unsafe]`). + pub fn allows_unsafe(&self) -> bool { + match self.ctxt.outer().expn_info() { + Some(info) => info.callee.allow_internal_unsafe, + None => false, + } + } + pub fn macro_backtrace(mut self) -> Vec { let mut prev_span = DUMMY_SP; let mut result = vec![]; diff --git a/src/test/compile-fail/feature-gate-allow-internal-unsafe-nested-macro.rs b/src/test/compile-fail/feature-gate-allow-internal-unsafe-nested-macro.rs new file mode 100644 index 0000000000000..590dc619f2f1a --- /dev/null +++ b/src/test/compile-fail/feature-gate-allow-internal-unsafe-nested-macro.rs @@ -0,0 +1,27 @@ +// 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. + +// gate-test-allow_internal_unsafe + +#![allow(unused_macros)] + +macro_rules! bar { + () => { + // more layers don't help: + #[allow_internal_unsafe] //~ ERROR allow_internal_unsafe side-steps + macro_rules! baz { + () => {} + } + } +} + +bar!(); + +fn main() {} 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-fulldeps/auxiliary/plugin_args.rs b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs index 134e36c587bed..8da2ae8b29abe 100644 --- a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs +++ b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs @@ -48,5 +48,10 @@ impl TTMacroExpander for Expander { pub fn plugin_registrar(reg: &mut Registry) { let args = reg.args().to_owned(); reg.register_syntax_extension(Symbol::intern("plugin_args"), - NormalTT(Box::new(Expander { args: args, }), None, false)); + NormalTT { + expander: Box::new(Expander { args: args, }), + def_info: None, + allow_internal_unstable: false, + allow_internal_unsafe: false, + }); } 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() {}