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).
This commit is contained in:
bors
2015-03-06 05:20:11 +00:00
28 changed files with 423 additions and 84 deletions

View File

@@ -2555,6 +2555,14 @@ The currently implemented features of the reference compiler are:
types, e.g. as the return type of a public function. types, e.g. as the return type of a public function.
This capability may be removed in the future. This capability may be removed in the future.
* `allow_internal_unstable` - Allows `macro_rules!` macros to be tagged with the
`#[allow_internal_unstable]` attribute, designed
to allow `std` macros to call
`#[unstable]`/feature-gated functionality
internally without imposing on callers
(i.e. making them behave like function calls in
terms of encapsulation).
If a feature is promoted to a language feature, then all existing programs will If a feature is promoted to a language feature, then all existing programs will
start to receive compilation warnings about #[feature] directives which enabled start to receive compilation warnings about #[feature] directives which enabled
the new feature (because the directive is no longer necessary). However, if a the new feature (because the directive is no longer necessary). However, if a

View File

@@ -1238,6 +1238,7 @@ pub fn from_f64<A: FromPrimitive>(n: f64) -> Option<A> {
macro_rules! impl_from_primitive { macro_rules! impl_from_primitive {
($T:ty, $to_ty:ident) => ( ($T:ty, $to_ty:ident) => (
#[allow(deprecated)]
impl FromPrimitive for $T { impl FromPrimitive for $T {
#[inline] fn from_int(n: int) -> Option<$T> { n.$to_ty() } #[inline] fn from_int(n: int) -> Option<$T> { n.$to_ty() }
#[inline] fn from_i8(n: i8) -> Option<$T> { n.$to_ty() } #[inline] fn from_i8(n: i8) -> Option<$T> { n.$to_ty() }
@@ -1299,6 +1300,7 @@ macro_rules! impl_num_cast {
($T:ty, $conv:ident) => ( ($T:ty, $conv:ident) => (
impl NumCast for $T { impl NumCast for $T {
#[inline] #[inline]
#[allow(deprecated)]
fn from<N: ToPrimitive>(n: N) -> Option<$T> { fn from<N: ToPrimitive>(n: N) -> Option<$T> {
// `$conv` could be generated using `concat_idents!`, but that // `$conv` could be generated using `concat_idents!`, but that
// macro seems to be broken at the moment // macro seems to be broken at the moment

View File

@@ -542,6 +542,7 @@ impl<'a> CrateReader<'a> {
// overridden in plugin/load.rs // overridden in plugin/load.rs
export: false, export: false,
use_locally: false, use_locally: false,
allow_internal_unstable: false,
body: body, body: body,
}); });

View File

@@ -166,6 +166,9 @@ impl<'a> MacroLoader<'a> {
Some(sel) => sel.contains_key(&name), Some(sel) => sel.contains_key(&name),
}; };
def.export = reexport.contains_key(&name); def.export = reexport.contains_key(&name);
def.allow_internal_unstable = attr::contains_name(&def.attrs,
"allow_internal_unstable");
debug!("load_macros: loaded: {:?}", def);
self.macros.push(def); self.macros.push(def);
} }

View File

@@ -362,8 +362,6 @@ pub fn check_item(tcx: &ty::ctxt, item: &ast::Item, warn_about_defns: bool,
/// Helper for discovering nodes to check for stability /// Helper for discovering nodes to check for stability
pub fn check_expr(tcx: &ty::ctxt, e: &ast::Expr, pub fn check_expr(tcx: &ty::ctxt, e: &ast::Expr,
cb: &mut FnMut(ast::DefId, Span, &Option<Stability>)) { cb: &mut FnMut(ast::DefId, Span, &Option<Stability>)) {
if is_internal(tcx, e.span) { return; }
let span; let span;
let id = match e.node { let id = match e.node {
ast::ExprMethodCall(i, _, _) => { ast::ExprMethodCall(i, _, _) => {
@@ -527,12 +525,13 @@ pub fn check_pat(tcx: &ty::ctxt, pat: &ast::Pat,
fn maybe_do_stability_check(tcx: &ty::ctxt, id: ast::DefId, span: Span, fn maybe_do_stability_check(tcx: &ty::ctxt, id: ast::DefId, span: Span,
cb: &mut FnMut(ast::DefId, Span, &Option<Stability>)) { cb: &mut FnMut(ast::DefId, Span, &Option<Stability>)) {
if !is_staged_api(tcx, id) { return } if !is_staged_api(tcx, id) { return }
if is_internal(tcx, span) { return }
let ref stability = lookup(tcx, id); let ref stability = lookup(tcx, id);
cb(id, span, stability); cb(id, span, stability);
} }
fn is_internal(tcx: &ty::ctxt, span: Span) -> bool { fn is_internal(tcx: &ty::ctxt, span: Span) -> bool {
tcx.sess.codemap().span_is_internal(span) tcx.sess.codemap().span_allows_unstable(span)
} }
fn is_staged_api(tcx: &ty::ctxt, id: DefId) -> bool { fn is_staged_api(tcx: &ty::ctxt, id: DefId) -> bool {

View File

@@ -81,8 +81,12 @@ impl<'a> Registry<'a> {
/// This is the most general hook into `libsyntax`'s expansion behavior. /// This is the most general hook into `libsyntax`'s expansion behavior.
pub fn register_syntax_extension(&mut self, name: ast::Name, extension: SyntaxExtension) { pub fn register_syntax_extension(&mut self, name: ast::Name, extension: SyntaxExtension) {
self.syntax_exts.push((name, match extension { self.syntax_exts.push((name, match extension {
NormalTT(ext, _) => NormalTT(ext, Some(self.krate_span)), NormalTT(ext, _, allow_internal_unstable) => {
IdentTT(ext, _) => IdentTT(ext, Some(self.krate_span)), NormalTT(ext, Some(self.krate_span), allow_internal_unstable)
}
IdentTT(ext, _, allow_internal_unstable) => {
IdentTT(ext, Some(self.krate_span), allow_internal_unstable)
}
Decorator(ext) => Decorator(ext), Decorator(ext) => Decorator(ext),
Modifier(ext) => Modifier(ext), Modifier(ext) => Modifier(ext),
MultiModifier(ext) => MultiModifier(ext), MultiModifier(ext) => MultiModifier(ext),
@@ -99,7 +103,8 @@ impl<'a> Registry<'a> {
/// It builds for you a `NormalTT` that calls `expander`, /// It builds for you a `NormalTT` that calls `expander`,
/// and also takes care of interning the macro's name. /// and also takes care of interning the macro's name.
pub fn register_macro(&mut self, name: &str, expander: MacroExpanderFn) { pub fn register_macro(&mut self, name: &str, expander: MacroExpanderFn) {
self.register_syntax_extension(token::intern(name), NormalTT(Box::new(expander), None)); self.register_syntax_extension(token::intern(name),
NormalTT(Box::new(expander), None, false));
} }
/// Register a compiler lint pass. /// Register a compiler lint pass.

View File

@@ -493,12 +493,16 @@ pub fn phase_2_configure_and_expand(sess: &Session,
} }
); );
// Needs to go *after* expansion to be able to check the results of macro expansion. // Needs to go *after* expansion to be able to check the results
time(time_passes, "complete gated feature checking", (), |_| { // of macro expansion. This runs before #[cfg] to try to catch as
// much as possible (e.g. help the programmer avoid platform
// specific differences)
time(time_passes, "complete gated feature checking 1", (), |_| {
let features = let features =
syntax::feature_gate::check_crate(sess.codemap(), syntax::feature_gate::check_crate(sess.codemap(),
&sess.parse_sess.span_diagnostic, &sess.parse_sess.span_diagnostic,
&krate); &krate,
true);
*sess.features.borrow_mut() = features; *sess.features.borrow_mut() = features;
sess.abort_if_errors(); sess.abort_if_errors();
}); });
@@ -521,6 +525,19 @@ pub fn phase_2_configure_and_expand(sess: &Session,
time(time_passes, "checking that all macro invocations are gone", &krate, |krate| time(time_passes, "checking that all macro invocations are gone", &krate, |krate|
syntax::ext::expand::check_for_macros(&sess.parse_sess, krate)); syntax::ext::expand::check_for_macros(&sess.parse_sess, krate));
// One final feature gating of the true AST that gets compiled
// later, to make sure we've got everything (e.g. configuration
// can insert new attributes via `cfg_attr`)
time(time_passes, "complete gated feature checking 2", (), |_| {
let features =
syntax::feature_gate::check_crate(sess.codemap(),
&sess.parse_sess.span_diagnostic,
&krate,
false);
*sess.features.borrow_mut() = features;
sess.abort_if_errors();
});
Some(krate) Some(krate)
} }

View File

@@ -125,6 +125,7 @@
#![feature(hash)] #![feature(hash)]
#![feature(int_uint)] #![feature(int_uint)]
#![feature(unique)] #![feature(unique)]
#![feature(allow_internal_unstable)]
#![cfg_attr(test, feature(test, rustc_private))] #![cfg_attr(test, feature(test, rustc_private))]
// Don't link to std. We are std. // Don't link to std. We are std.

View File

@@ -55,6 +55,7 @@
//! ``` //! ```
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![unstable(feature = "thread_local_internals")]
use prelude::v1::*; use prelude::v1::*;
@@ -84,17 +85,14 @@ use sys::thread_local as imp;
/// KEY.set(1 as *mut u8); /// KEY.set(1 as *mut u8);
/// } /// }
/// ``` /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct StaticKey { pub struct StaticKey {
/// Inner static TLS key (internals), created with by `INIT_INNER` in this /// Inner static TLS key (internals), created with by `INIT_INNER` in this
/// module. /// module.
#[stable(feature = "rust1", since = "1.0.0")]
pub inner: StaticKeyInner, pub inner: StaticKeyInner,
/// Destructor for the TLS value. /// Destructor for the TLS value.
/// ///
/// See `Key::new` for information about when the destructor runs and how /// See `Key::new` for information about when the destructor runs and how
/// it runs. /// it runs.
#[stable(feature = "rust1", since = "1.0.0")]
pub dtor: Option<unsafe extern fn(*mut u8)>, pub dtor: Option<unsafe extern fn(*mut u8)>,
} }
@@ -131,7 +129,6 @@ pub struct Key {
/// Constant initialization value for static TLS keys. /// Constant initialization value for static TLS keys.
/// ///
/// This value specifies no destructor by default. /// This value specifies no destructor by default.
#[stable(feature = "rust1", since = "1.0.0")]
pub const INIT: StaticKey = StaticKey { pub const INIT: StaticKey = StaticKey {
inner: INIT_INNER, inner: INIT_INNER,
dtor: None, dtor: None,
@@ -140,7 +137,6 @@ pub const INIT: StaticKey = StaticKey {
/// Constant initialization value for the inner part of static TLS keys. /// Constant initialization value for the inner part of static TLS keys.
/// ///
/// This value allows specific configuration of the destructor for a TLS key. /// This value allows specific configuration of the destructor for a TLS key.
#[stable(feature = "rust1", since = "1.0.0")]
pub const INIT_INNER: StaticKeyInner = StaticKeyInner { pub const INIT_INNER: StaticKeyInner = StaticKeyInner {
key: atomic::ATOMIC_USIZE_INIT, key: atomic::ATOMIC_USIZE_INIT,
}; };

View File

@@ -45,7 +45,7 @@ pub mod scoped;
// Sure wish we had macro hygiene, no? // Sure wish we had macro hygiene, no?
#[doc(hidden)] #[doc(hidden)]
#[stable(feature = "rust1", since = "1.0.0")] #[unstable(feature = "thread_local_internals")]
pub mod __impl { pub mod __impl {
pub use super::imp::Key as KeyInner; pub use super::imp::Key as KeyInner;
pub use super::imp::destroy_value; pub use super::imp::destroy_value;
@@ -117,6 +117,7 @@ pub struct Key<T> {
/// Declare a new thread local storage key of type `std::thread_local::Key`. /// Declare a new thread local storage key of type `std::thread_local::Key`.
#[macro_export] #[macro_export]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable]
macro_rules! thread_local { macro_rules! thread_local {
(static $name:ident: $t:ty = $init:expr) => ( (static $name:ident: $t:ty = $init:expr) => (
static $name: ::std::thread_local::Key<$t> = { static $name: ::std::thread_local::Key<$t> = {
@@ -176,6 +177,7 @@ macro_rules! thread_local {
#[macro_export] #[macro_export]
#[doc(hidden)] #[doc(hidden)]
#[allow_internal_unstable]
macro_rules! __thread_local_inner { macro_rules! __thread_local_inner {
(static $name:ident: $t:ty = $init:expr) => ( (static $name:ident: $t:ty = $init:expr) => (
#[cfg_attr(all(any(target_os = "macos", target_os = "linux"), #[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
@@ -337,7 +339,7 @@ mod imp {
use ptr; use ptr;
#[doc(hidden)] #[doc(hidden)]
#[stable(since = "1.0.0", feature = "rust1")] #[unstable(feature = "thread_local_internals")]
pub struct Key<T> { pub struct Key<T> {
// Place the inner bits in an `UnsafeCell` to currently get around the // Place the inner bits in an `UnsafeCell` to currently get around the
// "only Sync statics" restriction. This allows any type to be placed in // "only Sync statics" restriction. This allows any type to be placed in
@@ -345,14 +347,14 @@ mod imp {
// //
// Note that all access requires `T: 'static` so it can't be a type with // Note that all access requires `T: 'static` so it can't be a type with
// any borrowed pointers still. // any borrowed pointers still.
#[stable(since = "1.0.0", feature = "rust1")] #[unstable(feature = "thread_local_internals")]
pub inner: UnsafeCell<T>, pub inner: UnsafeCell<T>,
// Metadata to keep track of the state of the destructor. Remember that // Metadata to keep track of the state of the destructor. Remember that
// these variables are thread-local, not global. // these variables are thread-local, not global.
#[stable(since = "1.0.0", feature = "rust1")] #[unstable(feature = "thread_local_internals")]
pub dtor_registered: UnsafeCell<bool>, // should be Cell pub dtor_registered: UnsafeCell<bool>, // should be Cell
#[stable(since = "1.0.0", feature = "rust1")] #[unstable(feature = "thread_local_internals")]
pub dtor_running: UnsafeCell<bool>, // should be Cell pub dtor_running: UnsafeCell<bool>, // should be Cell
} }
@@ -455,7 +457,7 @@ mod imp {
} }
#[doc(hidden)] #[doc(hidden)]
#[stable(feature = "rust1", since = "1.0.0")] #[unstable(feature = "thread_local_internals")]
pub unsafe extern fn destroy_value<T>(ptr: *mut u8) { pub unsafe extern fn destroy_value<T>(ptr: *mut u8) {
let ptr = ptr as *mut Key<T>; let ptr = ptr as *mut Key<T>;
// Right before we run the user destructor be sure to flag the // Right before we run the user destructor be sure to flag the
@@ -477,15 +479,15 @@ mod imp {
use sys_common::thread_local::StaticKey as OsStaticKey; use sys_common::thread_local::StaticKey as OsStaticKey;
#[doc(hidden)] #[doc(hidden)]
#[stable(since = "1.0.0", feature = "rust1")] #[unstable(feature = "thread_local_internals")]
pub struct Key<T> { pub struct Key<T> {
// Statically allocated initialization expression, using an `UnsafeCell` // Statically allocated initialization expression, using an `UnsafeCell`
// for the same reasons as above. // for the same reasons as above.
#[stable(since = "1.0.0", feature = "rust1")] #[unstable(feature = "thread_local_internals")]
pub inner: UnsafeCell<T>, pub inner: UnsafeCell<T>,
// OS-TLS key that we'll use to key off. // OS-TLS key that we'll use to key off.
#[stable(since = "1.0.0", feature = "rust1")] #[unstable(feature = "thread_local_internals")]
pub os: OsStaticKey, pub os: OsStaticKey,
} }
@@ -528,7 +530,7 @@ mod imp {
} }
#[doc(hidden)] #[doc(hidden)]
#[stable(feature = "rust1", since = "1.0.0")] #[unstable(feature = "thread_local_internals")]
pub unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) { pub unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) {
// The OS TLS ensures that this key contains a NULL value when this // The OS TLS ensures that this key contains a NULL value when this
// destructor starts to run. We set it back to a sentinel value of 1 to // destructor starts to run. We set it back to a sentinel value of 1 to

View File

@@ -65,6 +65,7 @@ pub struct Key<T> { #[doc(hidden)] pub inner: __impl::KeyInner<T> }
/// This macro declares a `static` item on which methods are used to get and /// This macro declares a `static` item on which methods are used to get and
/// set the value stored within. /// set the value stored within.
#[macro_export] #[macro_export]
#[allow_internal_unstable]
macro_rules! scoped_thread_local { macro_rules! scoped_thread_local {
(static $name:ident: $t:ty) => ( (static $name:ident: $t:ty) => (
__scoped_thread_local_inner!(static $name: $t); __scoped_thread_local_inner!(static $name: $t);
@@ -76,6 +77,7 @@ macro_rules! scoped_thread_local {
#[macro_export] #[macro_export]
#[doc(hidden)] #[doc(hidden)]
#[allow_internal_unstable]
macro_rules! __scoped_thread_local_inner { macro_rules! __scoped_thread_local_inner {
(static $name:ident: $t:ty) => ( (static $name:ident: $t:ty) => (
#[cfg_attr(not(any(windows, #[cfg_attr(not(any(windows,

View File

@@ -1743,6 +1743,7 @@ pub struct MacroDef {
pub imported_from: Option<Ident>, pub imported_from: Option<Ident>,
pub export: bool, pub export: bool,
pub use_locally: bool, pub use_locally: bool,
pub allow_internal_unstable: bool,
pub body: Vec<TokenTree>, pub body: Vec<TokenTree>,
} }

View File

@@ -243,6 +243,10 @@ pub struct NameAndSpan {
pub name: String, pub name: String,
/// The format with which the macro was invoked. /// The format with which the macro was invoked.
pub format: MacroFormat, pub format: MacroFormat,
/// Whether the macro is allowed to use #[unstable]/feature-gated
/// features internally without forcing the whole crate to opt-in
/// to them.
pub allow_internal_unstable: bool,
/// The span of the macro definition itself. The macro may not /// The span of the macro definition itself. The macro may not
/// have a sensible definition span (e.g. something defined /// have a sensible definition span (e.g. something defined
/// completely inside libsyntax) in which case this is None. /// completely inside libsyntax) in which case this is None.
@@ -830,41 +834,43 @@ impl CodeMap {
} }
} }
/// Check if a span is "internal" to a macro. This means that it is entirely generated by a /// Check if a span is "internal" to a macro in which #[unstable]
/// macro expansion and contains no code that was passed in as an argument. /// items can be used (that is, a macro marked with
pub fn span_is_internal(&self, span: Span) -> bool { /// `#[allow_internal_unstable]`).
// first, check if the given expression was generated by a macro or not pub fn span_allows_unstable(&self, span: Span) -> bool {
// we need to go back the expn_info tree to check only the arguments debug!("span_allows_unstable(span = {:?})", span);
// of the initial macro call, not the nested ones. let mut allows_unstable = false;
let mut is_internal = false; let mut expn_id = span.expn_id;
let mut expnid = span.expn_id; loop {
while self.with_expn_info(expnid, |expninfo| { let quit = self.with_expn_info(expn_id, |expninfo| {
match expninfo { debug!("span_allows_unstable: expninfo = {:?}", expninfo);
Some(ref info) => { expninfo.map_or(/* hit the top level */ true, |info| {
// save the parent expn_id for next loop iteration
expnid = info.call_site.expn_id; let span_comes_from_this_expansion =
if info.callee.name == "format_args" { info.callee.span.map_or(span == info.call_site, |mac_span| {
// This is a hack because the format_args builtin calls unstable APIs. mac_span.lo <= span.lo && span.hi < mac_span.hi
// I spent like 6 hours trying to solve this more generally but am stupid. });
is_internal = true;
false debug!("span_allows_unstable: from this expansion? {}, allows unstable? {}",
} else if info.callee.span.is_none() { span_comes_from_this_expansion,
// it's a compiler built-in, we *really* don't want to mess with it info.callee.allow_internal_unstable);
// so we skip it, unless it was called by a regular macro, in which case if span_comes_from_this_expansion {
// we will handle the caller macro next turn allows_unstable = info.callee.allow_internal_unstable;
is_internal = true; // we've found the right place, stop looking
true // continue looping true
} else { } else {
// was this expression from the current macro arguments ? // not the right place, keep looking
is_internal = !( span.lo > info.call_site.lo && expn_id = info.call_site.expn_id;
span.hi < info.call_site.hi ); false
true // continue looping
} }
}, })
_ => false // stop looping });
if quit {
break
} }
}) { /* empty while loop body */ } }
return is_internal; debug!("span_allows_unstable? {}", allows_unstable);
allows_unstable
} }
} }

View File

@@ -214,6 +214,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
name: "asm".to_string(), name: "asm".to_string(),
format: codemap::MacroBang, format: codemap::MacroBang,
span: None, span: None,
allow_internal_unstable: false,
}, },
}); });

View File

@@ -430,12 +430,15 @@ pub enum SyntaxExtension {
/// A normal, function-like syntax extension. /// A normal, function-like syntax extension.
/// ///
/// `bytes!` is a `NormalTT`. /// `bytes!` is a `NormalTT`.
NormalTT(Box<TTMacroExpander + 'static>, Option<Span>), ///
/// The `bool` dictates whether the contents of the macro can
/// directly use `#[unstable]` things (true == yes).
NormalTT(Box<TTMacroExpander + 'static>, Option<Span>, bool),
/// A function-like syntax extension that has an extra ident before /// A function-like syntax extension that has an extra ident before
/// the block. /// the block.
/// ///
IdentTT(Box<IdentMacroExpander + 'static>, Option<Span>), IdentTT(Box<IdentMacroExpander + 'static>, Option<Span>, bool),
/// Represents `macro_rules!` itself. /// Represents `macro_rules!` itself.
MacroRulesTT, MacroRulesTT,
@@ -465,14 +468,14 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
-> SyntaxEnv { -> SyntaxEnv {
// utility function to simplify creating NormalTT syntax extensions // utility function to simplify creating NormalTT syntax extensions
fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension { fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension {
NormalTT(Box::new(f), None) NormalTT(Box::new(f), None, false)
} }
let mut syntax_expanders = SyntaxEnv::new(); let mut syntax_expanders = SyntaxEnv::new();
syntax_expanders.insert(intern("macro_rules"), MacroRulesTT); syntax_expanders.insert(intern("macro_rules"), MacroRulesTT);
syntax_expanders.insert(intern("format_args"), syntax_expanders.insert(intern("format_args"),
builtin_normal_expander( // format_args uses `unstable` things internally.
ext::format::expand_format_args)); NormalTT(Box::new(ext::format::expand_format_args), None, true));
syntax_expanders.insert(intern("env"), syntax_expanders.insert(intern("env"),
builtin_normal_expander( builtin_normal_expander(
ext::env::expand_env)); ext::env::expand_env));

View File

@@ -1216,7 +1216,8 @@ impl<'a> TraitDef<'a> {
callee: codemap::NameAndSpan { callee: codemap::NameAndSpan {
name: format!("derive({})", trait_name), name: format!("derive({})", trait_name),
format: codemap::MacroAttribute, format: codemap::MacroAttribute,
span: Some(self.span) span: Some(self.span),
allow_internal_unstable: false,
} }
}); });
to_set to_set

View File

@@ -22,7 +22,7 @@ use attr::AttrMetaMethods;
use codemap; use codemap;
use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
use ext::base::*; use ext::base::*;
use feature_gate::{Features}; use feature_gate::{self, Features};
use fold; use fold;
use fold::*; use fold::*;
use parse; use parse;
@@ -395,13 +395,14 @@ fn expand_mac_invoc<T, F, G>(mac: ast::Mac, span: codemap::Span,
None None
} }
Some(rc) => match *rc { Some(rc) => match *rc {
NormalTT(ref expandfun, exp_span) => { NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
fld.cx.bt_push(ExpnInfo { fld.cx.bt_push(ExpnInfo {
call_site: span, call_site: span,
callee: NameAndSpan { callee: NameAndSpan {
name: extnamestr.to_string(), name: extnamestr.to_string(),
format: MacroBang, format: MacroBang,
span: exp_span, span: exp_span,
allow_internal_unstable: allow_internal_unstable,
}, },
}); });
let fm = fresh_mark(); let fm = fresh_mark();
@@ -530,6 +531,9 @@ fn expand_item_modifiers(mut it: P<ast::Item>, fld: &mut MacroExpander)
name: mname.to_string(), name: mname.to_string(),
format: MacroAttribute, format: MacroAttribute,
span: None, span: None,
// attributes can do whatever they like,
// for now
allow_internal_unstable: true,
} }
}); });
it = mac.expand(fld.cx, attr.span, &*attr.node.value, it); it = mac.expand(fld.cx, attr.span, &*attr.node.value, it);
@@ -614,7 +618,7 @@ pub fn expand_item_mac(it: P<ast::Item>,
} }
Some(rc) => match *rc { Some(rc) => match *rc {
NormalTT(ref expander, span) => { NormalTT(ref expander, span, allow_internal_unstable) => {
if it.ident.name != parse::token::special_idents::invalid.name { if it.ident.name != parse::token::special_idents::invalid.name {
fld.cx fld.cx
.span_err(path_span, .span_err(path_span,
@@ -628,14 +632,15 @@ pub fn expand_item_mac(it: P<ast::Item>,
callee: NameAndSpan { callee: NameAndSpan {
name: extnamestr.to_string(), name: extnamestr.to_string(),
format: MacroBang, format: MacroBang,
span: span span: span,
allow_internal_unstable: allow_internal_unstable,
} }
}); });
// mark before expansion: // mark before expansion:
let marked_before = mark_tts(&tts[..], fm); let marked_before = mark_tts(&tts[..], fm);
expander.expand(fld.cx, it.span, &marked_before[..]) expander.expand(fld.cx, it.span, &marked_before[..])
} }
IdentTT(ref expander, span) => { IdentTT(ref expander, span, allow_internal_unstable) => {
if it.ident.name == parse::token::special_idents::invalid.name { if it.ident.name == parse::token::special_idents::invalid.name {
fld.cx.span_err(path_span, fld.cx.span_err(path_span,
&format!("macro {}! expects an ident argument", &format!("macro {}! expects an ident argument",
@@ -647,7 +652,8 @@ pub fn expand_item_mac(it: P<ast::Item>,
callee: NameAndSpan { callee: NameAndSpan {
name: extnamestr.to_string(), name: extnamestr.to_string(),
format: MacroBang, format: MacroBang,
span: span span: span,
allow_internal_unstable: allow_internal_unstable,
} }
}); });
// mark before expansion: // mark before expansion:
@@ -661,16 +667,35 @@ pub fn expand_item_mac(it: P<ast::Item>,
); );
return SmallVector::zero(); return SmallVector::zero();
} }
fld.cx.bt_push(ExpnInfo { fld.cx.bt_push(ExpnInfo {
call_site: it.span, call_site: it.span,
callee: NameAndSpan { callee: NameAndSpan {
name: extnamestr.to_string(), name: extnamestr.to_string(),
format: MacroBang, format: MacroBang,
span: None, span: None,
// `macro_rules!` doesn't directly allow
// unstable (this is orthogonal to whether
// the macro it creates allows it)
allow_internal_unstable: false,
} }
}); });
// DON'T mark before expansion. // DON'T mark before expansion.
let allow_internal_unstable = attr::contains_name(&it.attrs,
"allow_internal_unstable");
// ensure any #[allow_internal_unstable]s are
// detected (including nested macro definitions
// etc.)
if allow_internal_unstable && !fld.cx.ecfg.enable_allow_internal_unstable() {
feature_gate::emit_feature_err(
&fld.cx.parse_sess.span_diagnostic,
"allow_internal_unstable",
it.span,
feature_gate::EXPLAIN_ALLOW_INTERNAL_UNSTABLE)
}
let def = ast::MacroDef { let def = ast::MacroDef {
ident: it.ident, ident: it.ident,
attrs: it.attrs.clone(), attrs: it.attrs.clone(),
@@ -679,6 +704,7 @@ pub fn expand_item_mac(it: P<ast::Item>,
imported_from: None, imported_from: None,
export: attr::contains_name(&it.attrs, "macro_export"), export: attr::contains_name(&it.attrs, "macro_export"),
use_locally: true, use_locally: true,
allow_internal_unstable: allow_internal_unstable,
body: tts, body: tts,
}; };
fld.cx.insert_macro(def); fld.cx.insert_macro(def);
@@ -959,13 +985,14 @@ fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> {
} }
Some(rc) => match *rc { Some(rc) => match *rc {
NormalTT(ref expander, tt_span) => { NormalTT(ref expander, tt_span, allow_internal_unstable) => {
fld.cx.bt_push(ExpnInfo { fld.cx.bt_push(ExpnInfo {
call_site: span, call_site: span,
callee: NameAndSpan { callee: NameAndSpan {
name: extnamestr.to_string(), name: extnamestr.to_string(),
format: MacroBang, format: MacroBang,
span: tt_span span: tt_span,
allow_internal_unstable: allow_internal_unstable,
} }
}); });
@@ -1094,7 +1121,10 @@ fn expand_annotatable(a: Annotatable,
callee: NameAndSpan { callee: NameAndSpan {
name: mname.to_string(), name: mname.to_string(),
format: MacroAttribute, format: MacroAttribute,
span: None span: None,
// attributes can do whatever they like,
// for now.
allow_internal_unstable: true,
} }
}); });
@@ -1244,6 +1274,9 @@ fn expand_item_multi_modifier(mut it: Annotatable,
name: mname.to_string(), name: mname.to_string(),
format: MacroAttribute, format: MacroAttribute,
span: None, span: None,
// attributes can do whatever they like,
// for now
allow_internal_unstable: true,
} }
}); });
it = mac.expand(fld.cx, attr.span, &*attr.node.value, it); it = mac.expand(fld.cx, attr.span, &*attr.node.value, it);
@@ -1457,6 +1490,13 @@ impl<'feat> ExpansionConfig<'feat> {
_ => false, _ => false,
} }
} }
pub fn enable_allow_internal_unstable(&self) -> bool {
match self.features {
Some(&Features { allow_internal_unstable: true, .. }) => true,
_ => false
}
}
} }
pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess, pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,

View File

@@ -267,7 +267,7 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
rhses: rhses, rhses: rhses,
}; };
NormalTT(exp, Some(def.span)) NormalTT(exp, Some(def.span), def.allow_internal_unstable)
} }
fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &NamedMatch, sp: Span) { fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &NamedMatch, sp: Span) {

View File

@@ -142,6 +142,12 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
// Allows the use of `static_assert` // Allows the use of `static_assert`
("static_assert", "1.0.0", Active), ("static_assert", "1.0.0", Active),
// Allows the use of #[allow_internal_unstable]. 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).
("allow_internal_unstable", "1.0.0", 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)
@@ -279,7 +285,7 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
("recursion_limit", CrateLevel), ("recursion_limit", CrateLevel),
]; ];
#[derive(PartialEq, Copy)] #[derive(PartialEq, Copy, Debug)]
pub enum AttributeType { pub enum AttributeType {
/// Normal, builtin attribute that is consumed /// Normal, builtin attribute that is consumed
/// by the compiler before the unused_attribute check /// by the compiler before the unused_attribute check
@@ -308,6 +314,7 @@ pub struct Features {
pub allow_log_syntax: bool, pub allow_log_syntax: bool,
pub allow_concat_idents: bool, pub allow_concat_idents: bool,
pub allow_trace_macros: bool, pub allow_trace_macros: bool,
pub allow_internal_unstable: bool,
pub old_orphan_check: bool, pub old_orphan_check: bool,
pub simd_ffi: bool, pub simd_ffi: bool,
pub unmarked_api: bool, pub unmarked_api: bool,
@@ -328,6 +335,7 @@ impl Features {
allow_log_syntax: false, allow_log_syntax: false,
allow_concat_idents: false, allow_concat_idents: false,
allow_trace_macros: false, allow_trace_macros: false,
allow_internal_unstable: false,
old_orphan_check: false, old_orphan_check: false,
simd_ffi: false, simd_ffi: false,
unmarked_api: false, unmarked_api: false,
@@ -341,17 +349,20 @@ struct Context<'a> {
features: Vec<&'static str>, features: Vec<&'static str>,
span_handler: &'a SpanHandler, span_handler: &'a SpanHandler,
cm: &'a CodeMap, cm: &'a CodeMap,
do_warnings: bool,
} }
impl<'a> Context<'a> { impl<'a> Context<'a> {
fn gate_feature(&self, feature: &str, span: Span, explain: &str) { fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
if !self.has_feature(feature) { let has_feature = self.has_feature(feature);
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature);
if !has_feature {
emit_feature_err(self.span_handler, feature, span, explain); emit_feature_err(self.span_handler, feature, span, explain);
} }
} }
fn warn_feature(&self, feature: &str, span: Span, explain: &str) { fn warn_feature(&self, feature: &str, span: Span, explain: &str) {
if !self.has_feature(feature) { if !self.has_feature(feature) && self.do_warnings {
emit_feature_warn(self.span_handler, feature, span, explain); emit_feature_warn(self.span_handler, feature, span, explain);
} }
} }
@@ -387,6 +398,8 @@ pub const EXPLAIN_CONCAT_IDENTS: &'static str =
pub const EXPLAIN_TRACE_MACROS: &'static str = pub const EXPLAIN_TRACE_MACROS: &'static str =
"`trace_macros` is not stable enough for use and is subject to change"; "`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";
struct MacroVisitor<'a> { struct MacroVisitor<'a> {
context: &'a Context<'a> context: &'a Context<'a>
@@ -421,6 +434,13 @@ impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
self.context.gate_feature("concat_idents", path.span, EXPLAIN_CONCAT_IDENTS); self.context.gate_feature("concat_idents", path.span, EXPLAIN_CONCAT_IDENTS);
} }
} }
fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
if attr.name() == "allow_internal_unstable" {
self.context.gate_feature("allow_internal_unstable", attr.span,
EXPLAIN_ALLOW_INTERNAL_UNSTABLE)
}
}
} }
struct PostExpansionVisitor<'a> { struct PostExpansionVisitor<'a> {
@@ -429,7 +449,7 @@ struct PostExpansionVisitor<'a> {
impl<'a> PostExpansionVisitor<'a> { impl<'a> PostExpansionVisitor<'a> {
fn gate_feature(&self, feature: &str, span: Span, explain: &str) { fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
if !self.context.cm.span_is_internal(span) { if !self.context.cm.span_allows_unstable(span) {
self.context.gate_feature(feature, span, explain) self.context.gate_feature(feature, span, explain)
} }
} }
@@ -617,12 +637,14 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
} }
fn visit_attribute(&mut self, attr: &ast::Attribute) { fn visit_attribute(&mut self, attr: &ast::Attribute) {
debug!("visit_attribute(attr = {:?})", attr);
let name = &*attr.name(); let name = &*attr.name();
for &(n, ty) in KNOWN_ATTRIBUTES { for &(n, ty) in KNOWN_ATTRIBUTES {
if n == name { if n == name {
if let Gated(gate, desc) = ty { if let Gated(gate, desc) = ty {
self.gate_feature(gate, attr.span, desc); self.gate_feature(gate, attr.span, desc);
} }
debug!("visit_attribute: {:?} is known, {:?}", name, ty);
return; return;
} }
} }
@@ -679,6 +701,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
} }
fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate, fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
do_warnings: bool,
check: F) check: F)
-> Features -> Features
where F: FnOnce(&mut Context, &ast::Crate) where F: FnOnce(&mut Context, &ast::Crate)
@@ -686,6 +709,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
let mut cx = Context { let mut cx = Context {
features: Vec::new(), features: Vec::new(),
span_handler: span_handler, span_handler: span_handler,
do_warnings: do_warnings,
cm: cm, cm: cm,
}; };
@@ -754,6 +778,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
allow_log_syntax: cx.has_feature("log_syntax"), allow_log_syntax: cx.has_feature("log_syntax"),
allow_concat_idents: cx.has_feature("concat_idents"), allow_concat_idents: cx.has_feature("concat_idents"),
allow_trace_macros: cx.has_feature("trace_macros"), allow_trace_macros: cx.has_feature("trace_macros"),
allow_internal_unstable: cx.has_feature("allow_internal_unstable"),
old_orphan_check: cx.has_feature("old_orphan_check"), old_orphan_check: cx.has_feature("old_orphan_check"),
simd_ffi: cx.has_feature("simd_ffi"), simd_ffi: cx.has_feature("simd_ffi"),
unmarked_api: cx.has_feature("unmarked_api"), unmarked_api: cx.has_feature("unmarked_api"),
@@ -764,13 +789,14 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate) pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
-> Features { -> Features {
check_crate_inner(cm, span_handler, krate, check_crate_inner(cm, span_handler, krate, true,
|ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate)) |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
} }
pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate) pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
-> Features { do_warnings: bool) -> Features
check_crate_inner(cm, span_handler, krate, {
check_crate_inner(cm, span_handler, krate, do_warnings,
|ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx }, |ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
krate)) krate))
} }

View File

@@ -256,7 +256,8 @@ fn generate_test_harness(sess: &ParseSess,
callee: NameAndSpan { callee: NameAndSpan {
name: "test".to_string(), name: "test".to_string(),
format: MacroAttribute, format: MacroAttribute,
span: None span: None,
allow_internal_unstable: false,
} }
}); });
@@ -288,7 +289,8 @@ fn ignored_span(cx: &TestCtxt, sp: Span) -> Span {
callee: NameAndSpan { callee: NameAndSpan {
name: "test".to_string(), name: "test".to_string(),
format: MacroAttribute, format: MacroAttribute,
span: None span: None,
allow_internal_unstable: true,
} }
}; };
let expn_id = cx.sess.span_diagnostic.cm.record_expansion(info); let expn_id = cx.sess.span_diagnostic.cm.record_expansion(info);

View File

@@ -0,0 +1,60 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(staged_api, allow_internal_unstable)]
#![staged_api]
#![stable(feature = "stable", since = "1.0.0")]
#[unstable(feature = "function")]
pub fn unstable() {}
#[stable(feature = "stable", since = "1.0.0")]
pub struct Foo {
#[unstable(feature = "struct_field")]
pub x: u8
}
#[allow_internal_unstable]
#[macro_export]
macro_rules! call_unstable_allow {
() => { $crate::unstable() }
}
#[allow_internal_unstable]
#[macro_export]
macro_rules! construct_unstable_allow {
($e: expr) => {
$crate::Foo { x: $e }
}
}
#[allow_internal_unstable]
#[macro_export]
macro_rules! pass_through_allow {
($e: expr) => { $e }
}
#[macro_export]
macro_rules! call_unstable_noallow {
() => { $crate::unstable() }
}
#[macro_export]
macro_rules! construct_unstable_noallow {
($e: expr) => {
$crate::Foo { x: $e }
}
}
#[macro_export]
macro_rules! pass_through_noallow {
($e: expr) => { $e }
}

View File

@@ -47,5 +47,5 @@ pub fn plugin_registrar(reg: &mut Registry) {
let args = reg.args().clone(); let args = reg.args().clone();
reg.register_syntax_extension(token::intern("plugin_args"), reg.register_syntax_extension(token::intern("plugin_args"),
// FIXME (#22405): Replace `Box::new` with `box` here when/if possible. // FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
NormalTT(Box::new(Expander { args: args, }), None)); NormalTT(Box::new(Expander { args: args, }), None, false));
} }

View File

@@ -0,0 +1,20 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
macro_rules! foo {
() => {
#[cfg_attr(all(), unknown)] //~ ERROR `unknown` is currently unknown
fn foo() {}
}
}
foo!();
fn main() {}

View File

@@ -0,0 +1,23 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
macro_rules! bar {
() => {
// more layers don't help:
#[allow_internal_unstable]
macro_rules! baz { //~ ERROR allow_internal_unstable side-steps
() => {}
}
}
}
bar!();
fn main() {}

View File

@@ -0,0 +1,16 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[allow_internal_unstable] //~ ERROR allow_internal_unstable side-steps
macro_rules! foo {
() => {}
}
fn main() {}

View File

@@ -0,0 +1,30 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// this has to be separate to internal-unstable.rs because these tests
// have error messages pointing deep into the internals of the
// cross-crate macros, and hence need to use error-pattern instead of
// the // ~ form.
// aux-build:internal_unstable.rs
// error-pattern:use of unstable library feature 'function'
// error-pattern:use of unstable library feature 'struct_field'
// error-pattern:compilation successful
#![feature(rustc_attrs)]
#[macro_use]
extern crate internal_unstable;
#[rustc_error]
fn main() {
call_unstable_noallow!();
construct_unstable_noallow!(0);
}

View File

@@ -0,0 +1,23 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:internal_unstable.rs
#![feature(rustc_attrs)]
#![allow(dead_code)]
extern crate internal_unstable;
thread_local!(static FOO: () = ());
thread_local!(static BAR: () = internal_unstable::unstable()); //~ WARN use of unstable
#[rustc_error]
fn main() {} //~ ERROR

View File

@@ -0,0 +1,51 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:internal_unstable.rs
#![feature(rustc_attrs, allow_internal_unstable)]
#[macro_use]
extern crate internal_unstable;
macro_rules! foo {
($e: expr, $f: expr) => {{
$e;
$f;
internal_unstable::unstable(); //~ WARN use of unstable
}}
}
#[allow_internal_unstable]
macro_rules! bar {
($e: expr) => {{
foo!($e,
internal_unstable::unstable());
internal_unstable::unstable();
}}
}
#[rustc_error]
fn main() { //~ ERROR
// ok, the instability is contained.
call_unstable_allow!();
construct_unstable_allow!(0);
// bad.
pass_through_allow!(internal_unstable::unstable()); //~ WARN use of unstable
pass_through_noallow!(internal_unstable::unstable()); //~ WARN use of unstable
println!("{:?}", internal_unstable::unstable()); //~ WARN use of unstable
bar!(internal_unstable::unstable()); //~ WARN use of unstable
}