Skip to content

Commit 1fe8f22

Browse files
committed
Auto merge of #22899 - huonw:macro-stability, r=alexcrichton
Unstable items used in a macro expansion will now always trigger stability warnings, *unless* the unstable items are directly inside a macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns unless the span of the unstable item is a subspan of the definition of a macro marked with that attribute. E.g. #[allow_internal_unstable] macro_rules! foo { ($e: expr) => {{ $e; unstable(); // no warning only_called_by_foo!(); }} } macro_rules! only_called_by_foo { () => { unstable() } // warning } foo!(unstable()) // warning The unstable inside `foo` is fine, due to the attribute. But the `unstable` inside `only_called_by_foo` is not, since that macro doesn't have the attribute, and the `unstable` passed into `foo` is also not fine since it isn't contained in the macro itself (that is, even though it is only used directly in the macro). In the process this makes the stability tracking much more precise, e.g. previously `println!("{}", unstable())` got no warning, but now it does. As such, this is a bug fix that may cause [breaking-change]s. The attribute is definitely feature gated, since it explicitly allows side-stepping the feature gating system. --- This updates `thread_local!` macro to use the attribute, since it uses unstable features internally (initialising a struct with unstable fields).
2 parents b0746ff + b5c6ab2 commit 1fe8f22

28 files changed

+423
-84
lines changed

src/doc/reference.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2555,6 +2555,14 @@ The currently implemented features of the reference compiler are:
25552555
types, e.g. as the return type of a public function.
25562556
This capability may be removed in the future.
25572557

2558+
* `allow_internal_unstable` - Allows `macro_rules!` macros to be tagged with the
2559+
`#[allow_internal_unstable]` attribute, designed
2560+
to allow `std` macros to call
2561+
`#[unstable]`/feature-gated functionality
2562+
internally without imposing on callers
2563+
(i.e. making them behave like function calls in
2564+
terms of encapsulation).
2565+
25582566
If a feature is promoted to a language feature, then all existing programs will
25592567
start to receive compilation warnings about #[feature] directives which enabled
25602568
the new feature (because the directive is no longer necessary). However, if a

src/libcore/num/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,6 +1238,7 @@ pub fn from_f64<A: FromPrimitive>(n: f64) -> Option<A> {
12381238

12391239
macro_rules! impl_from_primitive {
12401240
($T:ty, $to_ty:ident) => (
1241+
#[allow(deprecated)]
12411242
impl FromPrimitive for $T {
12421243
#[inline] fn from_int(n: int) -> Option<$T> { n.$to_ty() }
12431244
#[inline] fn from_i8(n: i8) -> Option<$T> { n.$to_ty() }
@@ -1299,6 +1300,7 @@ macro_rules! impl_num_cast {
12991300
($T:ty, $conv:ident) => (
13001301
impl NumCast for $T {
13011302
#[inline]
1303+
#[allow(deprecated)]
13021304
fn from<N: ToPrimitive>(n: N) -> Option<$T> {
13031305
// `$conv` could be generated using `concat_idents!`, but that
13041306
// macro seems to be broken at the moment

src/librustc/metadata/creader.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ impl<'a> CrateReader<'a> {
542542
// overridden in plugin/load.rs
543543
export: false,
544544
use_locally: false,
545+
allow_internal_unstable: false,
545546

546547
body: body,
547548
});

src/librustc/metadata/macro_import.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ impl<'a> MacroLoader<'a> {
166166
Some(sel) => sel.contains_key(&name),
167167
};
168168
def.export = reexport.contains_key(&name);
169+
def.allow_internal_unstable = attr::contains_name(&def.attrs,
170+
"allow_internal_unstable");
171+
debug!("load_macros: loaded: {:?}", def);
169172
self.macros.push(def);
170173
}
171174

src/librustc/middle/stability.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,6 @@ pub fn check_item(tcx: &ty::ctxt, item: &ast::Item, warn_about_defns: bool,
362362
/// Helper for discovering nodes to check for stability
363363
pub fn check_expr(tcx: &ty::ctxt, e: &ast::Expr,
364364
cb: &mut FnMut(ast::DefId, Span, &Option<Stability>)) {
365-
if is_internal(tcx, e.span) { return; }
366-
367365
let span;
368366
let id = match e.node {
369367
ast::ExprMethodCall(i, _, _) => {
@@ -527,12 +525,13 @@ pub fn check_pat(tcx: &ty::ctxt, pat: &ast::Pat,
527525
fn maybe_do_stability_check(tcx: &ty::ctxt, id: ast::DefId, span: Span,
528526
cb: &mut FnMut(ast::DefId, Span, &Option<Stability>)) {
529527
if !is_staged_api(tcx, id) { return }
528+
if is_internal(tcx, span) { return }
530529
let ref stability = lookup(tcx, id);
531530
cb(id, span, stability);
532531
}
533532

534533
fn is_internal(tcx: &ty::ctxt, span: Span) -> bool {
535-
tcx.sess.codemap().span_is_internal(span)
534+
tcx.sess.codemap().span_allows_unstable(span)
536535
}
537536

538537
fn is_staged_api(tcx: &ty::ctxt, id: DefId) -> bool {

src/librustc/plugin/registry.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,12 @@ impl<'a> Registry<'a> {
8181
/// This is the most general hook into `libsyntax`'s expansion behavior.
8282
pub fn register_syntax_extension(&mut self, name: ast::Name, extension: SyntaxExtension) {
8383
self.syntax_exts.push((name, match extension {
84-
NormalTT(ext, _) => NormalTT(ext, Some(self.krate_span)),
85-
IdentTT(ext, _) => IdentTT(ext, Some(self.krate_span)),
84+
NormalTT(ext, _, allow_internal_unstable) => {
85+
NormalTT(ext, Some(self.krate_span), allow_internal_unstable)
86+
}
87+
IdentTT(ext, _, allow_internal_unstable) => {
88+
IdentTT(ext, Some(self.krate_span), allow_internal_unstable)
89+
}
8690
Decorator(ext) => Decorator(ext),
8791
Modifier(ext) => Modifier(ext),
8892
MultiModifier(ext) => MultiModifier(ext),
@@ -99,7 +103,8 @@ impl<'a> Registry<'a> {
99103
/// It builds for you a `NormalTT` that calls `expander`,
100104
/// and also takes care of interning the macro's name.
101105
pub fn register_macro(&mut self, name: &str, expander: MacroExpanderFn) {
102-
self.register_syntax_extension(token::intern(name), NormalTT(Box::new(expander), None));
106+
self.register_syntax_extension(token::intern(name),
107+
NormalTT(Box::new(expander), None, false));
103108
}
104109

105110
/// Register a compiler lint pass.

src/librustc_driver/driver.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -493,12 +493,16 @@ pub fn phase_2_configure_and_expand(sess: &Session,
493493
}
494494
);
495495

496-
// Needs to go *after* expansion to be able to check the results of macro expansion.
497-
time(time_passes, "complete gated feature checking", (), |_| {
496+
// Needs to go *after* expansion to be able to check the results
497+
// of macro expansion. This runs before #[cfg] to try to catch as
498+
// much as possible (e.g. help the programmer avoid platform
499+
// specific differences)
500+
time(time_passes, "complete gated feature checking 1", (), |_| {
498501
let features =
499502
syntax::feature_gate::check_crate(sess.codemap(),
500-
&sess.parse_sess.span_diagnostic,
501-
&krate);
503+
&sess.parse_sess.span_diagnostic,
504+
&krate,
505+
true);
502506
*sess.features.borrow_mut() = features;
503507
sess.abort_if_errors();
504508
});
@@ -521,6 +525,19 @@ pub fn phase_2_configure_and_expand(sess: &Session,
521525
time(time_passes, "checking that all macro invocations are gone", &krate, |krate|
522526
syntax::ext::expand::check_for_macros(&sess.parse_sess, krate));
523527

528+
// One final feature gating of the true AST that gets compiled
529+
// later, to make sure we've got everything (e.g. configuration
530+
// can insert new attributes via `cfg_attr`)
531+
time(time_passes, "complete gated feature checking 2", (), |_| {
532+
let features =
533+
syntax::feature_gate::check_crate(sess.codemap(),
534+
&sess.parse_sess.span_diagnostic,
535+
&krate,
536+
false);
537+
*sess.features.borrow_mut() = features;
538+
sess.abort_if_errors();
539+
});
540+
524541
Some(krate)
525542
}
526543

src/libstd/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@
125125
#![feature(hash)]
126126
#![feature(int_uint)]
127127
#![feature(unique)]
128+
#![feature(allow_internal_unstable)]
128129
#![cfg_attr(test, feature(test, rustc_private))]
129130

130131
// Don't link to std. We are std.

src/libstd/sys/common/thread_local.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
//! ```
5656
5757
#![allow(non_camel_case_types)]
58+
#![unstable(feature = "thread_local_internals")]
5859

5960
use prelude::v1::*;
6061

@@ -84,17 +85,14 @@ use sys::thread_local as imp;
8485
/// KEY.set(1 as *mut u8);
8586
/// }
8687
/// ```
87-
#[stable(feature = "rust1", since = "1.0.0")]
8888
pub struct StaticKey {
8989
/// Inner static TLS key (internals), created with by `INIT_INNER` in this
9090
/// module.
91-
#[stable(feature = "rust1", since = "1.0.0")]
9291
pub inner: StaticKeyInner,
9392
/// Destructor for the TLS value.
9493
///
9594
/// See `Key::new` for information about when the destructor runs and how
9695
/// it runs.
97-
#[stable(feature = "rust1", since = "1.0.0")]
9896
pub dtor: Option<unsafe extern fn(*mut u8)>,
9997
}
10098

@@ -131,7 +129,6 @@ pub struct Key {
131129
/// Constant initialization value for static TLS keys.
132130
///
133131
/// This value specifies no destructor by default.
134-
#[stable(feature = "rust1", since = "1.0.0")]
135132
pub const INIT: StaticKey = StaticKey {
136133
inner: INIT_INNER,
137134
dtor: None,
@@ -140,7 +137,6 @@ pub const INIT: StaticKey = StaticKey {
140137
/// Constant initialization value for the inner part of static TLS keys.
141138
///
142139
/// This value allows specific configuration of the destructor for a TLS key.
143-
#[stable(feature = "rust1", since = "1.0.0")]
144140
pub const INIT_INNER: StaticKeyInner = StaticKeyInner {
145141
key: atomic::ATOMIC_USIZE_INIT,
146142
};

src/libstd/thread_local/mod.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub mod scoped;
4545

4646
// Sure wish we had macro hygiene, no?
4747
#[doc(hidden)]
48-
#[stable(feature = "rust1", since = "1.0.0")]
48+
#[unstable(feature = "thread_local_internals")]
4949
pub mod __impl {
5050
pub use super::imp::Key as KeyInner;
5151
pub use super::imp::destroy_value;
@@ -117,6 +117,7 @@ pub struct Key<T> {
117117
/// Declare a new thread local storage key of type `std::thread_local::Key`.
118118
#[macro_export]
119119
#[stable(feature = "rust1", since = "1.0.0")]
120+
#[allow_internal_unstable]
120121
macro_rules! thread_local {
121122
(static $name:ident: $t:ty = $init:expr) => (
122123
static $name: ::std::thread_local::Key<$t> = {
@@ -176,6 +177,7 @@ macro_rules! thread_local {
176177

177178
#[macro_export]
178179
#[doc(hidden)]
180+
#[allow_internal_unstable]
179181
macro_rules! __thread_local_inner {
180182
(static $name:ident: $t:ty = $init:expr) => (
181183
#[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
@@ -337,22 +339,22 @@ mod imp {
337339
use ptr;
338340

339341
#[doc(hidden)]
340-
#[stable(since = "1.0.0", feature = "rust1")]
342+
#[unstable(feature = "thread_local_internals")]
341343
pub struct Key<T> {
342344
// Place the inner bits in an `UnsafeCell` to currently get around the
343345
// "only Sync statics" restriction. This allows any type to be placed in
344346
// the cell.
345347
//
346348
// Note that all access requires `T: 'static` so it can't be a type with
347349
// any borrowed pointers still.
348-
#[stable(since = "1.0.0", feature = "rust1")]
350+
#[unstable(feature = "thread_local_internals")]
349351
pub inner: UnsafeCell<T>,
350352

351353
// Metadata to keep track of the state of the destructor. Remember that
352354
// these variables are thread-local, not global.
353-
#[stable(since = "1.0.0", feature = "rust1")]
355+
#[unstable(feature = "thread_local_internals")]
354356
pub dtor_registered: UnsafeCell<bool>, // should be Cell
355-
#[stable(since = "1.0.0", feature = "rust1")]
357+
#[unstable(feature = "thread_local_internals")]
356358
pub dtor_running: UnsafeCell<bool>, // should be Cell
357359
}
358360

@@ -455,7 +457,7 @@ mod imp {
455457
}
456458

457459
#[doc(hidden)]
458-
#[stable(feature = "rust1", since = "1.0.0")]
460+
#[unstable(feature = "thread_local_internals")]
459461
pub unsafe extern fn destroy_value<T>(ptr: *mut u8) {
460462
let ptr = ptr as *mut Key<T>;
461463
// Right before we run the user destructor be sure to flag the
@@ -477,15 +479,15 @@ mod imp {
477479
use sys_common::thread_local::StaticKey as OsStaticKey;
478480

479481
#[doc(hidden)]
480-
#[stable(since = "1.0.0", feature = "rust1")]
482+
#[unstable(feature = "thread_local_internals")]
481483
pub struct Key<T> {
482484
// Statically allocated initialization expression, using an `UnsafeCell`
483485
// for the same reasons as above.
484-
#[stable(since = "1.0.0", feature = "rust1")]
486+
#[unstable(feature = "thread_local_internals")]
485487
pub inner: UnsafeCell<T>,
486488

487489
// OS-TLS key that we'll use to key off.
488-
#[stable(since = "1.0.0", feature = "rust1")]
490+
#[unstable(feature = "thread_local_internals")]
489491
pub os: OsStaticKey,
490492
}
491493

@@ -528,7 +530,7 @@ mod imp {
528530
}
529531

530532
#[doc(hidden)]
531-
#[stable(feature = "rust1", since = "1.0.0")]
533+
#[unstable(feature = "thread_local_internals")]
532534
pub unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) {
533535
// The OS TLS ensures that this key contains a NULL value when this
534536
// destructor starts to run. We set it back to a sentinel value of 1 to

0 commit comments

Comments
 (0)