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

+8
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

+2
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

+1
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

+3
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

+2-3
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

+8-3
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

+21-4
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

+1
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

+1-5
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

+12-10
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

src/libstd/thread_local/scoped.rs

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ pub struct Key<T> { #[doc(hidden)] pub inner: __impl::KeyInner<T> }
6565
/// This macro declares a `static` item on which methods are used to get and
6666
/// set the value stored within.
6767
#[macro_export]
68+
#[allow_internal_unstable]
6869
macro_rules! scoped_thread_local {
6970
(static $name:ident: $t:ty) => (
7071
__scoped_thread_local_inner!(static $name: $t);
@@ -76,6 +77,7 @@ macro_rules! scoped_thread_local {
7677

7778
#[macro_export]
7879
#[doc(hidden)]
80+
#[allow_internal_unstable]
7981
macro_rules! __scoped_thread_local_inner {
8082
(static $name:ident: $t:ty) => (
8183
#[cfg_attr(not(any(windows,

src/libsyntax/ast.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1743,6 +1743,7 @@ pub struct MacroDef {
17431743
pub imported_from: Option<Ident>,
17441744
pub export: bool,
17451745
pub use_locally: bool,
1746+
pub allow_internal_unstable: bool,
17461747
pub body: Vec<TokenTree>,
17471748
}
17481749

src/libsyntax/codemap.rs

+38-32
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,10 @@ pub struct NameAndSpan {
243243
pub name: String,
244244
/// The format with which the macro was invoked.
245245
pub format: MacroFormat,
246+
/// Whether the macro is allowed to use #[unstable]/feature-gated
247+
/// features internally without forcing the whole crate to opt-in
248+
/// to them.
249+
pub allow_internal_unstable: bool,
246250
/// The span of the macro definition itself. The macro may not
247251
/// have a sensible definition span (e.g. something defined
248252
/// completely inside libsyntax) in which case this is None.
@@ -830,41 +834,43 @@ impl CodeMap {
830834
}
831835
}
832836

833-
/// Check if a span is "internal" to a macro. This means that it is entirely generated by a
834-
/// macro expansion and contains no code that was passed in as an argument.
835-
pub fn span_is_internal(&self, span: Span) -> bool {
836-
// first, check if the given expression was generated by a macro or not
837-
// we need to go back the expn_info tree to check only the arguments
838-
// of the initial macro call, not the nested ones.
839-
let mut is_internal = false;
840-
let mut expnid = span.expn_id;
841-
while self.with_expn_info(expnid, |expninfo| {
842-
match expninfo {
843-
Some(ref info) => {
844-
// save the parent expn_id for next loop iteration
845-
expnid = info.call_site.expn_id;
846-
if info.callee.name == "format_args" {
847-
// This is a hack because the format_args builtin calls unstable APIs.
848-
// I spent like 6 hours trying to solve this more generally but am stupid.
849-
is_internal = true;
850-
false
851-
} else if info.callee.span.is_none() {
852-
// it's a compiler built-in, we *really* don't want to mess with it
853-
// so we skip it, unless it was called by a regular macro, in which case
854-
// we will handle the caller macro next turn
855-
is_internal = true;
856-
true // continue looping
837+
/// Check if a span is "internal" to a macro in which #[unstable]
838+
/// items can be used (that is, a macro marked with
839+
/// `#[allow_internal_unstable]`).
840+
pub fn span_allows_unstable(&self, span: Span) -> bool {
841+
debug!("span_allows_unstable(span = {:?})", span);
842+
let mut allows_unstable = false;
843+
let mut expn_id = span.expn_id;
844+
loop {
845+
let quit = self.with_expn_info(expn_id, |expninfo| {
846+
debug!("span_allows_unstable: expninfo = {:?}", expninfo);
847+
expninfo.map_or(/* hit the top level */ true, |info| {
848+
849+
let span_comes_from_this_expansion =
850+
info.callee.span.map_or(span == info.call_site, |mac_span| {
851+
mac_span.lo <= span.lo && span.hi < mac_span.hi
852+
});
853+
854+
debug!("span_allows_unstable: from this expansion? {}, allows unstable? {}",
855+
span_comes_from_this_expansion,
856+
info.callee.allow_internal_unstable);
857+
if span_comes_from_this_expansion {
858+
allows_unstable = info.callee.allow_internal_unstable;
859+
// we've found the right place, stop looking
860+
true
857861
} else {
858-
// was this expression from the current macro arguments ?
859-
is_internal = !( span.lo > info.call_site.lo &&
860-
span.hi < info.call_site.hi );
861-
true // continue looping
862+
// not the right place, keep looking
863+
expn_id = info.call_site.expn_id;
864+
false
862865
}
863-
},
864-
_ => false // stop looping
866+
})
867+
});
868+
if quit {
869+
break
865870
}
866-
}) { /* empty while loop body */ }
867-
return is_internal;
871+
}
872+
debug!("span_allows_unstable? {}", allows_unstable);
873+
allows_unstable
868874
}
869875
}
870876

src/libsyntax/ext/asm.rs

+1
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
214214
name: "asm".to_string(),
215215
format: codemap::MacroBang,
216216
span: None,
217+
allow_internal_unstable: false,
217218
},
218219
});
219220

0 commit comments

Comments
 (0)