Auto merge of #58098 - oli-obk:maybe_allow_internal_unstable, r=petrochenkov

Require a list of features in `#[allow_internal_unstable]`

The blanket-permission slip is not great and will likely give us trouble some point down the road.
This commit is contained in:
bors 2019-02-12 12:10:10 +00:00
commit c84e797642
41 changed files with 622 additions and 512 deletions

View file

@ -34,7 +34,8 @@
#[cfg(not(test))] #[cfg(not(test))]
#[macro_export] #[macro_export]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable] #[cfg_attr(not(stage0), allow_internal_unstable(box_syntax))]
#[cfg_attr(stage0, allow_internal_unstable)]
macro_rules! vec { macro_rules! vec {
($elem:expr; $n:expr) => ( ($elem:expr; $n:expr) => (
$crate::vec::from_elem($elem, $n) $crate::vec::from_elem($elem, $n)

View file

@ -1,6 +1,7 @@
/// Entry point of thread panic, for details, see std::macros /// Entry point of thread panic, for details, see std::macros
#[macro_export] #[macro_export]
#[allow_internal_unstable] #[cfg_attr(not(stage0), allow_internal_unstable(core_panic, __rust_unstable_column))]
#[cfg_attr(stage0, allow_internal_unstable)]
#[stable(feature = "core", since = "1.6.0")] #[stable(feature = "core", since = "1.6.0")]
macro_rules! panic { macro_rules! panic {
() => ( () => (
@ -409,7 +410,8 @@ macro_rules! write {
/// ``` /// ```
#[macro_export] #[macro_export]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable] #[cfg_attr(stage0, allow_internal_unstable)]
#[cfg_attr(not(stage0), allow_internal_unstable(format_args_nl))]
macro_rules! writeln { macro_rules! writeln {
($dst:expr) => ( ($dst:expr) => (
write!($dst, "\n") write!($dst, "\n")

View file

@ -44,6 +44,7 @@ use crate::middle::cstore::CrateStore;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::thin_vec::ThinVec;
use rustc_data_structures::sync::Lrc;
use crate::session::Session; use crate::session::Session;
use crate::session::config::nightly_options; use crate::session::config::nightly_options;
use crate::util::common::FN_OUTPUT_NAME; use crate::util::common::FN_OUTPUT_NAME;
@ -681,13 +682,20 @@ impl<'a> LoweringContext<'a> {
Ident::with_empty_ctxt(Symbol::gensym(s)) Ident::with_empty_ctxt(Symbol::gensym(s))
} }
fn allow_internal_unstable(&self, reason: CompilerDesugaringKind, span: Span) -> Span { /// Reuses the span but adds information like the kind of the desugaring and features that are
/// allowed inside this span.
fn mark_span_with_reason(
&self,
reason: CompilerDesugaringKind,
span: Span,
allow_internal_unstable: Option<Lrc<[Symbol]>>,
) -> Span {
let mark = Mark::fresh(Mark::root()); let mark = Mark::fresh(Mark::root());
mark.set_expn_info(source_map::ExpnInfo { mark.set_expn_info(source_map::ExpnInfo {
call_site: span, call_site: span,
def_site: Some(span), def_site: Some(span),
format: source_map::CompilerDesugaring(reason), format: source_map::CompilerDesugaring(reason),
allow_internal_unstable: true, allow_internal_unstable,
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
edition: source_map::hygiene::default_edition(), edition: source_map::hygiene::default_edition(),
@ -964,7 +972,13 @@ impl<'a> LoweringContext<'a> {
attrs: ThinVec::new(), attrs: ThinVec::new(),
}; };
let unstable_span = self.allow_internal_unstable(CompilerDesugaringKind::Async, span); let unstable_span = self.mark_span_with_reason(
CompilerDesugaringKind::Async,
span,
Some(vec![
Symbol::intern("gen_future"),
].into()),
);
let gen_future = self.expr_std_path( let gen_future = self.expr_std_path(
unstable_span, &["future", "from_generator"], None, ThinVec::new()); unstable_span, &["future", "from_generator"], None, ThinVec::new());
hir::ExprKind::Call(P(gen_future), hir_vec![generator]) hir::ExprKind::Call(P(gen_future), hir_vec![generator])
@ -1360,9 +1374,10 @@ impl<'a> LoweringContext<'a> {
// desugaring that explicitly states that we don't want to track that. // desugaring that explicitly states that we don't want to track that.
// Not tracking it makes lints in rustc and clippy very fragile as // Not tracking it makes lints in rustc and clippy very fragile as
// frequently opened issues show. // frequently opened issues show.
let exist_ty_span = self.allow_internal_unstable( let exist_ty_span = self.mark_span_with_reason(
CompilerDesugaringKind::ExistentialReturnType, CompilerDesugaringKind::ExistentialReturnType,
span, span,
None,
); );
let exist_ty_def_index = self let exist_ty_def_index = self
@ -3927,8 +3942,13 @@ impl<'a> LoweringContext<'a> {
}), }),
ExprKind::TryBlock(ref body) => { ExprKind::TryBlock(ref body) => {
self.with_catch_scope(body.id, |this| { self.with_catch_scope(body.id, |this| {
let unstable_span = let unstable_span = this.mark_span_with_reason(
this.allow_internal_unstable(CompilerDesugaringKind::TryBlock, body.span); CompilerDesugaringKind::TryBlock,
body.span,
Some(vec![
Symbol::intern("try_trait"),
].into()),
);
let mut block = this.lower_block(body, true).into_inner(); let mut block = this.lower_block(body, true).into_inner();
let tail = block.expr.take().map_or_else( let tail = block.expr.take().map_or_else(
|| { || {
@ -4360,9 +4380,10 @@ impl<'a> LoweringContext<'a> {
// expand <head> // expand <head>
let head = self.lower_expr(head); let head = self.lower_expr(head);
let head_sp = head.span; let head_sp = head.span;
let desugared_span = self.allow_internal_unstable( let desugared_span = self.mark_span_with_reason(
CompilerDesugaringKind::ForLoop, CompilerDesugaringKind::ForLoop,
head_sp, head_sp,
None,
); );
let iter = self.str_to_ident("iter"); let iter = self.str_to_ident("iter");
@ -4525,8 +4546,13 @@ impl<'a> LoweringContext<'a> {
// return Try::from_error(From::from(err)), // return Try::from_error(From::from(err)),
// } // }
let unstable_span = let unstable_span = self.mark_span_with_reason(
self.allow_internal_unstable(CompilerDesugaringKind::QuestionMark, e.span); CompilerDesugaringKind::QuestionMark,
e.span,
Some(vec![
Symbol::intern("try_trait")
].into()),
);
// `Try::into_result(<expr>)` // `Try::into_result(<expr>)`
let discr = { let discr = {

View file

@ -561,11 +561,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
/// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to
/// `id`. /// `id`.
pub fn eval_stability(self, def_id: DefId, id: Option<NodeId>, span: Span) -> EvalResult { pub fn eval_stability(self, def_id: DefId, id: Option<NodeId>, span: Span) -> EvalResult {
if span.allows_unstable() {
debug!("stability: skipping span={:?} since it is internal", span);
return EvalResult::Allow;
}
let lint_deprecated = |def_id: DefId, let lint_deprecated = |def_id: DefId,
id: NodeId, id: NodeId,
note: Option<Symbol>, note: Option<Symbol>,
@ -694,6 +689,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
match stability { match stability {
Some(&Stability { level: attr::Unstable { reason, issue }, feature, .. }) => { Some(&Stability { level: attr::Unstable { reason, issue }, feature, .. }) => {
if span.allows_unstable(&feature.as_str()) {
debug!("stability: skipping span={:?} since it is internal", span);
return EvalResult::Allow;
}
if self.stability().active_features.contains(&feature) { if self.stability().active_features.contains(&feature) {
return EvalResult::Allow; return EvalResult::Allow;
} }

View file

@ -91,7 +91,9 @@ impl MutVisitor for ExpandAllocatorDirectives<'_> {
call_site: item.span, // use the call site of the static call_site: item.span, // use the call site of the static
def_site: None, def_site: None,
format: MacroAttribute(Symbol::intern(name)), format: MacroAttribute(Symbol::intern(name)),
allow_internal_unstable: true, allow_internal_unstable: Some(vec![
Symbol::intern("rustc_attrs"),
].into()),
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
edition: hygiene::default_edition(), edition: hygiene::default_edition(),

View file

@ -1,7 +1,8 @@
/// A simple static assertion macro. The first argument should be a unique /// A simple static assertion macro. The first argument should be a unique
/// ALL_CAPS identifier that describes the condition. /// ALL_CAPS identifier that describes the condition.
#[macro_export] #[macro_export]
#[allow_internal_unstable] #[cfg_attr(stage0, allow_internal_unstable)]
#[cfg_attr(not(stage0), allow_internal_unstable(type_ascription))]
macro_rules! static_assert { macro_rules! static_assert {
($name:ident: $test:expr) => { ($name:ident: $test:expr) => {
// Use the bool to access an array such that if the bool is false, the access // Use the bool to access an array such that if the bool is false, the access

View file

@ -570,7 +570,7 @@ impl<'a> CrateLoader<'a> {
ProcMacro::Bang { name, client } => { ProcMacro::Bang { name, client } => {
(name, SyntaxExtension::ProcMacro { (name, SyntaxExtension::ProcMacro {
expander: Box::new(BangProcMacro { client }), expander: Box::new(BangProcMacro { client }),
allow_internal_unstable: false, allow_internal_unstable: None,
edition: root.edition, edition: root.edition,
}) })
} }

View file

@ -425,7 +425,9 @@ impl cstore::CStore {
let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote); let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote);
let ext = SyntaxExtension::ProcMacro { let ext = SyntaxExtension::ProcMacro {
expander: Box::new(BangProcMacro { client }), expander: Box::new(BangProcMacro { client }),
allow_internal_unstable: true, allow_internal_unstable: Some(vec![
Symbol::intern("proc_macro_def_site"),
].into()),
edition: data.root.edition, edition: data.root.edition,
}; };
return LoadedMacro::ProcMacro(Lrc::new(ext)); return LoadedMacro::ProcMacro(Lrc::new(ext));

View file

@ -909,7 +909,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
// Check `#[unstable]` const fns or `#[rustc_const_unstable]` // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
// functions without the feature gate active in this crate in // functions without the feature gate active in this crate in
// order to report a better error message than the one below. // order to report a better error message than the one below.
if self.span.allows_unstable() { if self.span.allows_unstable(&feature.as_str()) {
// `allow_internal_unstable` can make such calls stable. // `allow_internal_unstable` can make such calls stable.
is_const_fn = true; is_const_fn = true;
} else { } else {

View file

@ -110,8 +110,8 @@ impl<'a> Registry<'a> {
edition, edition,
} }
} }
IdentTT(ext, _, allow_internal_unstable) => { IdentTT { expander, span: _, allow_internal_unstable } => {
IdentTT(ext, Some(self.krate_span), allow_internal_unstable) IdentTT { expander, span: Some(self.krate_span), allow_internal_unstable }
} }
_ => extension, _ => extension,
})); }));
@ -126,7 +126,7 @@ impl<'a> Registry<'a> {
self.register_syntax_extension(Symbol::intern(name), NormalTT { self.register_syntax_extension(Symbol::intern(name), NormalTT {
expander: Box::new(expander), expander: Box::new(expander),
def_info: None, def_info: None,
allow_internal_unstable: false, allow_internal_unstable: None,
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
unstable_feature: None, unstable_feature: None,

View file

@ -53,7 +53,8 @@
/// ``` /// ```
#[macro_export] #[macro_export]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable] #[cfg_attr(stage0, allow_internal_unstable)]
#[cfg_attr(not(stage0), allow_internal_unstable(__rust_unstable_column, libstd_sys_internals))]
macro_rules! panic { macro_rules! panic {
() => ({ () => ({
panic!("explicit panic") panic!("explicit panic")
@ -111,7 +112,8 @@ macro_rules! panic {
/// ``` /// ```
#[macro_export] #[macro_export]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable] #[cfg_attr(stage0, allow_internal_unstable)]
#[cfg_attr(not(stage0), allow_internal_unstable(print_internals))]
macro_rules! print { macro_rules! print {
($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))); ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)));
} }
@ -143,7 +145,8 @@ macro_rules! print {
/// ``` /// ```
#[macro_export] #[macro_export]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable] #[cfg_attr(stage0, allow_internal_unstable)]
#[cfg_attr(not(stage0), allow_internal_unstable(print_internals, format_args_nl))]
macro_rules! println { macro_rules! println {
() => (print!("\n")); () => (print!("\n"));
($($arg:tt)*) => ({ ($($arg:tt)*) => ({
@ -174,7 +177,8 @@ macro_rules! println {
/// ``` /// ```
#[macro_export] #[macro_export]
#[stable(feature = "eprint", since = "1.19.0")] #[stable(feature = "eprint", since = "1.19.0")]
#[allow_internal_unstable] #[cfg_attr(stage0, allow_internal_unstable)]
#[cfg_attr(not(stage0), allow_internal_unstable(print_internals))]
macro_rules! eprint { macro_rules! eprint {
($($arg:tt)*) => ($crate::io::_eprint(format_args!($($arg)*))); ($($arg:tt)*) => ($crate::io::_eprint(format_args!($($arg)*)));
} }
@ -202,7 +206,8 @@ macro_rules! eprint {
/// ``` /// ```
#[macro_export] #[macro_export]
#[stable(feature = "eprint", since = "1.19.0")] #[stable(feature = "eprint", since = "1.19.0")]
#[allow_internal_unstable] #[cfg_attr(stage0, allow_internal_unstable)]
#[cfg_attr(not(stage0), allow_internal_unstable(print_internals, format_args_nl))]
macro_rules! eprintln { macro_rules! eprintln {
() => (eprint!("\n")); () => (eprint!("\n"));
($($arg:tt)*) => ({ ($($arg:tt)*) => ({
@ -325,7 +330,8 @@ macro_rules! dbg {
/// A macro to await on an async call. /// A macro to await on an async call.
#[macro_export] #[macro_export]
#[unstable(feature = "await_macro", issue = "50547")] #[unstable(feature = "await_macro", issue = "50547")]
#[allow_internal_unstable] #[cfg_attr(stage0, allow_internal_unstable)]
#[cfg_attr(not(stage0), allow_internal_unstable(gen_future, generators))]
#[allow_internal_unsafe] #[allow_internal_unsafe]
macro_rules! await { macro_rules! await {
($e:expr) => { { ($e:expr) => { {

View file

@ -279,6 +279,9 @@ use self::select::StartResult;
use self::select::StartResult::*; use self::select::StartResult::*;
use self::blocking::SignalToken; use self::blocking::SignalToken;
#[cfg(all(test, not(target_os = "emscripten")))]
mod select_tests;
mod blocking; mod blocking;
mod oneshot; mod oneshot;
mod select; mod select;

View file

@ -158,7 +158,7 @@ impl Select {
} }
/// Helper method for skipping the preflight checks during testing /// Helper method for skipping the preflight checks during testing
fn wait2(&self, do_preflight_checks: bool) -> usize { pub(super) fn wait2(&self, do_preflight_checks: bool) -> usize {
// Note that this is currently an inefficient implementation. We in // Note that this is currently an inefficient implementation. We in
// theory have knowledge about all receivers in the set ahead of time, // theory have knowledge about all receivers in the set ahead of time,
// so this method shouldn't really have to iterate over all of them yet // so this method shouldn't really have to iterate over all of them yet
@ -352,417 +352,3 @@ impl<'rx, T:Send+'rx> fmt::Debug for Handle<'rx, T> {
f.debug_struct("Handle").finish() f.debug_struct("Handle").finish()
} }
} }
#[allow(unused_imports)]
#[cfg(all(test, not(target_os = "emscripten")))]
mod tests {
use thread;
use sync::mpsc::*;
// Don't use the libstd version so we can pull in the right Select structure
// (std::comm points at the wrong one)
macro_rules! select {
(
$($name:pat = $rx:ident.$meth:ident() => $code:expr),+
) => ({
let sel = Select::new();
$( let mut $rx = sel.handle(&$rx); )+
unsafe {
$( $rx.add(); )+
}
let ret = sel.wait();
$( if ret == $rx.id() { let $name = $rx.$meth(); $code } else )+
{ unreachable!() }
})
}
#[test]
fn smoke() {
let (tx1, rx1) = channel::<i32>();
let (tx2, rx2) = channel::<i32>();
tx1.send(1).unwrap();
select! {
foo = rx1.recv() => { assert_eq!(foo.unwrap(), 1); },
_bar = rx2.recv() => { panic!() }
}
tx2.send(2).unwrap();
select! {
_foo = rx1.recv() => { panic!() },
bar = rx2.recv() => { assert_eq!(bar.unwrap(), 2) }
}
drop(tx1);
select! {
foo = rx1.recv() => { assert!(foo.is_err()); },
_bar = rx2.recv() => { panic!() }
}
drop(tx2);
select! {
bar = rx2.recv() => { assert!(bar.is_err()); }
}
}
#[test]
fn smoke2() {
let (_tx1, rx1) = channel::<i32>();
let (_tx2, rx2) = channel::<i32>();
let (_tx3, rx3) = channel::<i32>();
let (_tx4, rx4) = channel::<i32>();
let (tx5, rx5) = channel::<i32>();
tx5.send(4).unwrap();
select! {
_foo = rx1.recv() => { panic!("1") },
_foo = rx2.recv() => { panic!("2") },
_foo = rx3.recv() => { panic!("3") },
_foo = rx4.recv() => { panic!("4") },
foo = rx5.recv() => { assert_eq!(foo.unwrap(), 4); }
}
}
#[test]
fn closed() {
let (_tx1, rx1) = channel::<i32>();
let (tx2, rx2) = channel::<i32>();
drop(tx2);
select! {
_a1 = rx1.recv() => { panic!() },
a2 = rx2.recv() => { assert!(a2.is_err()); }
}
}
#[test]
fn unblocks() {
let (tx1, rx1) = channel::<i32>();
let (_tx2, rx2) = channel::<i32>();
let (tx3, rx3) = channel::<i32>();
let _t = thread::spawn(move|| {
for _ in 0..20 { thread::yield_now(); }
tx1.send(1).unwrap();
rx3.recv().unwrap();
for _ in 0..20 { thread::yield_now(); }
});
select! {
a = rx1.recv() => { assert_eq!(a.unwrap(), 1); },
_b = rx2.recv() => { panic!() }
}
tx3.send(1).unwrap();
select! {
a = rx1.recv() => { assert!(a.is_err()) },
_b = rx2.recv() => { panic!() }
}
}
#[test]
fn both_ready() {
let (tx1, rx1) = channel::<i32>();
let (tx2, rx2) = channel::<i32>();
let (tx3, rx3) = channel::<()>();
let _t = thread::spawn(move|| {
for _ in 0..20 { thread::yield_now(); }
tx1.send(1).unwrap();
tx2.send(2).unwrap();
rx3.recv().unwrap();
});
select! {
a = rx1.recv() => { assert_eq!(a.unwrap(), 1); },
a = rx2.recv() => { assert_eq!(a.unwrap(), 2); }
}
select! {
a = rx1.recv() => { assert_eq!(a.unwrap(), 1); },
a = rx2.recv() => { assert_eq!(a.unwrap(), 2); }
}
assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty));
assert_eq!(rx2.try_recv(), Err(TryRecvError::Empty));
tx3.send(()).unwrap();
}
#[test]
fn stress() {
const AMT: i32 = 10000;
let (tx1, rx1) = channel::<i32>();
let (tx2, rx2) = channel::<i32>();
let (tx3, rx3) = channel::<()>();
let _t = thread::spawn(move|| {
for i in 0..AMT {
if i % 2 == 0 {
tx1.send(i).unwrap();
} else {
tx2.send(i).unwrap();
}
rx3.recv().unwrap();
}
});
for i in 0..AMT {
select! {
i1 = rx1.recv() => { assert!(i % 2 == 0 && i == i1.unwrap()); },
i2 = rx2.recv() => { assert!(i % 2 == 1 && i == i2.unwrap()); }
}
tx3.send(()).unwrap();
}
}
#[allow(unused_must_use)]
#[test]
fn cloning() {
let (tx1, rx1) = channel::<i32>();
let (_tx2, rx2) = channel::<i32>();
let (tx3, rx3) = channel::<()>();
let _t = thread::spawn(move|| {
rx3.recv().unwrap();
tx1.clone();
assert_eq!(rx3.try_recv(), Err(TryRecvError::Empty));
tx1.send(2).unwrap();
rx3.recv().unwrap();
});
tx3.send(()).unwrap();
select! {
_i1 = rx1.recv() => {},
_i2 = rx2.recv() => panic!()
}
tx3.send(()).unwrap();
}
#[allow(unused_must_use)]
#[test]
fn cloning2() {
let (tx1, rx1) = channel::<i32>();
let (_tx2, rx2) = channel::<i32>();
let (tx3, rx3) = channel::<()>();
let _t = thread::spawn(move|| {
rx3.recv().unwrap();
tx1.clone();
assert_eq!(rx3.try_recv(), Err(TryRecvError::Empty));
tx1.send(2).unwrap();
rx3.recv().unwrap();
});
tx3.send(()).unwrap();
select! {
_i1 = rx1.recv() => {},
_i2 = rx2.recv() => panic!()
}
tx3.send(()).unwrap();
}
#[test]
fn cloning3() {
let (tx1, rx1) = channel::<()>();
let (tx2, rx2) = channel::<()>();
let (tx3, rx3) = channel::<()>();
let _t = thread::spawn(move|| {
let s = Select::new();
let mut h1 = s.handle(&rx1);
let mut h2 = s.handle(&rx2);
unsafe { h2.add(); }
unsafe { h1.add(); }
assert_eq!(s.wait(), h2.id);
tx3.send(()).unwrap();
});
for _ in 0..1000 { thread::yield_now(); }
drop(tx1.clone());
tx2.send(()).unwrap();
rx3.recv().unwrap();
}
#[test]
fn preflight1() {
let (tx, rx) = channel();
tx.send(()).unwrap();
select! {
_n = rx.recv() => {}
}
}
#[test]
fn preflight2() {
let (tx, rx) = channel();
tx.send(()).unwrap();
tx.send(()).unwrap();
select! {
_n = rx.recv() => {}
}
}
#[test]
fn preflight3() {
let (tx, rx) = channel();
drop(tx.clone());
tx.send(()).unwrap();
select! {
_n = rx.recv() => {}
}
}
#[test]
fn preflight4() {
let (tx, rx) = channel();
tx.send(()).unwrap();
let s = Select::new();
let mut h = s.handle(&rx);
unsafe { h.add(); }
assert_eq!(s.wait2(false), h.id);
}
#[test]
fn preflight5() {
let (tx, rx) = channel();
tx.send(()).unwrap();
tx.send(()).unwrap();
let s = Select::new();
let mut h = s.handle(&rx);
unsafe { h.add(); }
assert_eq!(s.wait2(false), h.id);
}
#[test]
fn preflight6() {
let (tx, rx) = channel();
drop(tx.clone());
tx.send(()).unwrap();
let s = Select::new();
let mut h = s.handle(&rx);
unsafe { h.add(); }
assert_eq!(s.wait2(false), h.id);
}
#[test]
fn preflight7() {
let (tx, rx) = channel::<()>();
drop(tx);
let s = Select::new();
let mut h = s.handle(&rx);
unsafe { h.add(); }
assert_eq!(s.wait2(false), h.id);
}
#[test]
fn preflight8() {
let (tx, rx) = channel();
tx.send(()).unwrap();
drop(tx);
rx.recv().unwrap();
let s = Select::new();
let mut h = s.handle(&rx);
unsafe { h.add(); }
assert_eq!(s.wait2(false), h.id);
}
#[test]
fn preflight9() {
let (tx, rx) = channel();
drop(tx.clone());
tx.send(()).unwrap();
drop(tx);
rx.recv().unwrap();
let s = Select::new();
let mut h = s.handle(&rx);
unsafe { h.add(); }
assert_eq!(s.wait2(false), h.id);
}
#[test]
fn oneshot_data_waiting() {
let (tx1, rx1) = channel();
let (tx2, rx2) = channel();
let _t = thread::spawn(move|| {
select! {
_n = rx1.recv() => {}
}
tx2.send(()).unwrap();
});
for _ in 0..100 { thread::yield_now() }
tx1.send(()).unwrap();
rx2.recv().unwrap();
}
#[test]
fn stream_data_waiting() {
let (tx1, rx1) = channel();
let (tx2, rx2) = channel();
tx1.send(()).unwrap();
tx1.send(()).unwrap();
rx1.recv().unwrap();
rx1.recv().unwrap();
let _t = thread::spawn(move|| {
select! {
_n = rx1.recv() => {}
}
tx2.send(()).unwrap();
});
for _ in 0..100 { thread::yield_now() }
tx1.send(()).unwrap();
rx2.recv().unwrap();
}
#[test]
fn shared_data_waiting() {
let (tx1, rx1) = channel();
let (tx2, rx2) = channel();
drop(tx1.clone());
tx1.send(()).unwrap();
rx1.recv().unwrap();
let _t = thread::spawn(move|| {
select! {
_n = rx1.recv() => {}
}
tx2.send(()).unwrap();
});
for _ in 0..100 { thread::yield_now() }
tx1.send(()).unwrap();
rx2.recv().unwrap();
}
#[test]
fn sync1() {
let (tx, rx) = sync_channel::<i32>(1);
tx.send(1).unwrap();
select! {
n = rx.recv() => { assert_eq!(n.unwrap(), 1); }
}
}
#[test]
fn sync2() {
let (tx, rx) = sync_channel::<i32>(0);
let _t = thread::spawn(move|| {
for _ in 0..100 { thread::yield_now() }
tx.send(1).unwrap();
});
select! {
n = rx.recv() => { assert_eq!(n.unwrap(), 1); }
}
}
#[test]
fn sync3() {
let (tx1, rx1) = sync_channel::<i32>(0);
let (tx2, rx2): (Sender<i32>, Receiver<i32>) = channel();
let _t = thread::spawn(move|| { tx1.send(1).unwrap(); });
let _t = thread::spawn(move|| { tx2.send(2).unwrap(); });
select! {
n = rx1.recv() => {
let n = n.unwrap();
assert_eq!(n, 1);
assert_eq!(rx2.recv().unwrap(), 2);
},
n = rx2.recv() => {
let n = n.unwrap();
assert_eq!(n, 2);
assert_eq!(rx1.recv().unwrap(), 1);
}
}
}
}

View file

@ -0,0 +1,413 @@
#![allow(unused_imports)]
/// This file exists to hack around https://github.com/rust-lang/rust/issues/47238
use thread;
use sync::mpsc::*;
// Don't use the libstd version so we can pull in the right Select structure
// (std::comm points at the wrong one)
macro_rules! select {
(
$($name:pat = $rx:ident.$meth:ident() => $code:expr),+
) => ({
let sel = Select::new();
$( let mut $rx = sel.handle(&$rx); )+
unsafe {
$( $rx.add(); )+
}
let ret = sel.wait();
$( if ret == $rx.id() { let $name = $rx.$meth(); $code } else )+
{ unreachable!() }
})
}
#[test]
fn smoke() {
let (tx1, rx1) = channel::<i32>();
let (tx2, rx2) = channel::<i32>();
tx1.send(1).unwrap();
select! {
foo = rx1.recv() => { assert_eq!(foo.unwrap(), 1); },
_bar = rx2.recv() => { panic!() }
}
tx2.send(2).unwrap();
select! {
_foo = rx1.recv() => { panic!() },
bar = rx2.recv() => { assert_eq!(bar.unwrap(), 2) }
}
drop(tx1);
select! {
foo = rx1.recv() => { assert!(foo.is_err()); },
_bar = rx2.recv() => { panic!() }
}
drop(tx2);
select! {
bar = rx2.recv() => { assert!(bar.is_err()); }
}
}
#[test]
fn smoke2() {
let (_tx1, rx1) = channel::<i32>();
let (_tx2, rx2) = channel::<i32>();
let (_tx3, rx3) = channel::<i32>();
let (_tx4, rx4) = channel::<i32>();
let (tx5, rx5) = channel::<i32>();
tx5.send(4).unwrap();
select! {
_foo = rx1.recv() => { panic!("1") },
_foo = rx2.recv() => { panic!("2") },
_foo = rx3.recv() => { panic!("3") },
_foo = rx4.recv() => { panic!("4") },
foo = rx5.recv() => { assert_eq!(foo.unwrap(), 4); }
}
}
#[test]
fn closed() {
let (_tx1, rx1) = channel::<i32>();
let (tx2, rx2) = channel::<i32>();
drop(tx2);
select! {
_a1 = rx1.recv() => { panic!() },
a2 = rx2.recv() => { assert!(a2.is_err()); }
}
}
#[test]
fn unblocks() {
let (tx1, rx1) = channel::<i32>();
let (_tx2, rx2) = channel::<i32>();
let (tx3, rx3) = channel::<i32>();
let _t = thread::spawn(move|| {
for _ in 0..20 { thread::yield_now(); }
tx1.send(1).unwrap();
rx3.recv().unwrap();
for _ in 0..20 { thread::yield_now(); }
});
select! {
a = rx1.recv() => { assert_eq!(a.unwrap(), 1); },
_b = rx2.recv() => { panic!() }
}
tx3.send(1).unwrap();
select! {
a = rx1.recv() => { assert!(a.is_err()) },
_b = rx2.recv() => { panic!() }
}
}
#[test]
fn both_ready() {
let (tx1, rx1) = channel::<i32>();
let (tx2, rx2) = channel::<i32>();
let (tx3, rx3) = channel::<()>();
let _t = thread::spawn(move|| {
for _ in 0..20 { thread::yield_now(); }
tx1.send(1).unwrap();
tx2.send(2).unwrap();
rx3.recv().unwrap();
});
select! {
a = rx1.recv() => { assert_eq!(a.unwrap(), 1); },
a = rx2.recv() => { assert_eq!(a.unwrap(), 2); }
}
select! {
a = rx1.recv() => { assert_eq!(a.unwrap(), 1); },
a = rx2.recv() => { assert_eq!(a.unwrap(), 2); }
}
assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty));
assert_eq!(rx2.try_recv(), Err(TryRecvError::Empty));
tx3.send(()).unwrap();
}
#[test]
fn stress() {
const AMT: i32 = 10000;
let (tx1, rx1) = channel::<i32>();
let (tx2, rx2) = channel::<i32>();
let (tx3, rx3) = channel::<()>();
let _t = thread::spawn(move|| {
for i in 0..AMT {
if i % 2 == 0 {
tx1.send(i).unwrap();
} else {
tx2.send(i).unwrap();
}
rx3.recv().unwrap();
}
});
for i in 0..AMT {
select! {
i1 = rx1.recv() => { assert!(i % 2 == 0 && i == i1.unwrap()); },
i2 = rx2.recv() => { assert!(i % 2 == 1 && i == i2.unwrap()); }
}
tx3.send(()).unwrap();
}
}
#[allow(unused_must_use)]
#[test]
fn cloning() {
let (tx1, rx1) = channel::<i32>();
let (_tx2, rx2) = channel::<i32>();
let (tx3, rx3) = channel::<()>();
let _t = thread::spawn(move|| {
rx3.recv().unwrap();
tx1.clone();
assert_eq!(rx3.try_recv(), Err(TryRecvError::Empty));
tx1.send(2).unwrap();
rx3.recv().unwrap();
});
tx3.send(()).unwrap();
select! {
_i1 = rx1.recv() => {},
_i2 = rx2.recv() => panic!()
}
tx3.send(()).unwrap();
}
#[allow(unused_must_use)]
#[test]
fn cloning2() {
let (tx1, rx1) = channel::<i32>();
let (_tx2, rx2) = channel::<i32>();
let (tx3, rx3) = channel::<()>();
let _t = thread::spawn(move|| {
rx3.recv().unwrap();
tx1.clone();
assert_eq!(rx3.try_recv(), Err(TryRecvError::Empty));
tx1.send(2).unwrap();
rx3.recv().unwrap();
});
tx3.send(()).unwrap();
select! {
_i1 = rx1.recv() => {},
_i2 = rx2.recv() => panic!()
}
tx3.send(()).unwrap();
}
#[test]
fn cloning3() {
let (tx1, rx1) = channel::<()>();
let (tx2, rx2) = channel::<()>();
let (tx3, rx3) = channel::<()>();
let _t = thread::spawn(move|| {
let s = Select::new();
let mut h1 = s.handle(&rx1);
let mut h2 = s.handle(&rx2);
unsafe { h2.add(); }
unsafe { h1.add(); }
assert_eq!(s.wait(), h2.id());
tx3.send(()).unwrap();
});
for _ in 0..1000 { thread::yield_now(); }
drop(tx1.clone());
tx2.send(()).unwrap();
rx3.recv().unwrap();
}
#[test]
fn preflight1() {
let (tx, rx) = channel();
tx.send(()).unwrap();
select! {
_n = rx.recv() => {}
}
}
#[test]
fn preflight2() {
let (tx, rx) = channel();
tx.send(()).unwrap();
tx.send(()).unwrap();
select! {
_n = rx.recv() => {}
}
}
#[test]
fn preflight3() {
let (tx, rx) = channel();
drop(tx.clone());
tx.send(()).unwrap();
select! {
_n = rx.recv() => {}
}
}
#[test]
fn preflight4() {
let (tx, rx) = channel();
tx.send(()).unwrap();
let s = Select::new();
let mut h = s.handle(&rx);
unsafe { h.add(); }
assert_eq!(s.wait2(false), h.id());
}
#[test]
fn preflight5() {
let (tx, rx) = channel();
tx.send(()).unwrap();
tx.send(()).unwrap();
let s = Select::new();
let mut h = s.handle(&rx);
unsafe { h.add(); }
assert_eq!(s.wait2(false), h.id());
}
#[test]
fn preflight6() {
let (tx, rx) = channel();
drop(tx.clone());
tx.send(()).unwrap();
let s = Select::new();
let mut h = s.handle(&rx);
unsafe { h.add(); }
assert_eq!(s.wait2(false), h.id());
}
#[test]
fn preflight7() {
let (tx, rx) = channel::<()>();
drop(tx);
let s = Select::new();
let mut h = s.handle(&rx);
unsafe { h.add(); }
assert_eq!(s.wait2(false), h.id());
}
#[test]
fn preflight8() {
let (tx, rx) = channel();
tx.send(()).unwrap();
drop(tx);
rx.recv().unwrap();
let s = Select::new();
let mut h = s.handle(&rx);
unsafe { h.add(); }
assert_eq!(s.wait2(false), h.id());
}
#[test]
fn preflight9() {
let (tx, rx) = channel();
drop(tx.clone());
tx.send(()).unwrap();
drop(tx);
rx.recv().unwrap();
let s = Select::new();
let mut h = s.handle(&rx);
unsafe { h.add(); }
assert_eq!(s.wait2(false), h.id());
}
#[test]
fn oneshot_data_waiting() {
let (tx1, rx1) = channel();
let (tx2, rx2) = channel();
let _t = thread::spawn(move|| {
select! {
_n = rx1.recv() => {}
}
tx2.send(()).unwrap();
});
for _ in 0..100 { thread::yield_now() }
tx1.send(()).unwrap();
rx2.recv().unwrap();
}
#[test]
fn stream_data_waiting() {
let (tx1, rx1) = channel();
let (tx2, rx2) = channel();
tx1.send(()).unwrap();
tx1.send(()).unwrap();
rx1.recv().unwrap();
rx1.recv().unwrap();
let _t = thread::spawn(move|| {
select! {
_n = rx1.recv() => {}
}
tx2.send(()).unwrap();
});
for _ in 0..100 { thread::yield_now() }
tx1.send(()).unwrap();
rx2.recv().unwrap();
}
#[test]
fn shared_data_waiting() {
let (tx1, rx1) = channel();
let (tx2, rx2) = channel();
drop(tx1.clone());
tx1.send(()).unwrap();
rx1.recv().unwrap();
let _t = thread::spawn(move|| {
select! {
_n = rx1.recv() => {}
}
tx2.send(()).unwrap();
});
for _ in 0..100 { thread::yield_now() }
tx1.send(()).unwrap();
rx2.recv().unwrap();
}
#[test]
fn sync1() {
let (tx, rx) = sync_channel::<i32>(1);
tx.send(1).unwrap();
select! {
n = rx.recv() => { assert_eq!(n.unwrap(), 1); }
}
}
#[test]
fn sync2() {
let (tx, rx) = sync_channel::<i32>(0);
let _t = thread::spawn(move|| {
for _ in 0..100 { thread::yield_now() }
tx.send(1).unwrap();
});
select! {
n = rx.recv() => { assert_eq!(n.unwrap(), 1); }
}
}
#[test]
fn sync3() {
let (tx1, rx1) = sync_channel::<i32>(0);
let (tx2, rx2): (Sender<i32>, Receiver<i32>) = channel();
let _t = thread::spawn(move|| { tx1.send(1).unwrap(); });
let _t = thread::spawn(move|| { tx2.send(2).unwrap(); });
select! {
n = rx1.recv() => {
let n = n.unwrap();
assert_eq!(n, 1);
assert_eq!(rx2.recv().unwrap(), 2);
},
n = rx2.recv() => {
let n = n.unwrap();
assert_eq!(n, 2);
assert_eq!(rx1.recv().unwrap(), 1);
}
}
}

View file

@ -126,7 +126,8 @@ impl<T: 'static> fmt::Debug for LocalKey<T> {
/// [`std::thread::LocalKey`]: ../std/thread/struct.LocalKey.html /// [`std::thread::LocalKey`]: ../std/thread/struct.LocalKey.html
#[macro_export] #[macro_export]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable] #[cfg_attr(stage0, allow_internal_unstable)]
#[cfg_attr(not(stage0), allow_internal_unstable(thread_local_internals))]
macro_rules! thread_local { macro_rules! thread_local {
// empty (base case for the recursion) // empty (base case for the recursion)
() => {}; () => {};
@ -148,7 +149,10 @@ macro_rules! thread_local {
reason = "should not be necessary", reason = "should not be necessary",
issue = "0")] issue = "0")]
#[macro_export] #[macro_export]
#[allow_internal_unstable] #[cfg_attr(stage0, allow_internal_unstable)]
#[cfg_attr(not(stage0), allow_internal_unstable(
thread_local_internals, cfg_target_thread_local, thread_local,
))]
#[allow_internal_unsafe] #[allow_internal_unsafe]
macro_rules! __thread_local_inner { macro_rules! __thread_local_inner {
(@key $(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => { (@key $(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => {

View file

@ -621,7 +621,8 @@ pub enum SyntaxExtension {
/// A function-like procedural macro. TokenStream -> TokenStream. /// A function-like procedural macro. TokenStream -> TokenStream.
ProcMacro { ProcMacro {
expander: Box<dyn ProcMacro + sync::Sync + sync::Send>, expander: Box<dyn ProcMacro + sync::Sync + sync::Send>,
allow_internal_unstable: bool, /// Whitelist of unstable features that are treated as stable inside this macro
allow_internal_unstable: Option<Lrc<[Symbol]>>,
edition: Edition, edition: Edition,
}, },
@ -638,8 +639,10 @@ pub enum SyntaxExtension {
expander: Box<dyn TTMacroExpander + sync::Sync + sync::Send>, expander: Box<dyn TTMacroExpander + sync::Sync + sync::Send>,
def_info: Option<(ast::NodeId, Span)>, def_info: Option<(ast::NodeId, Span)>,
/// Whether the contents of the macro can /// Whether the contents of the macro can
/// directly use `#[unstable]` things (true == yes). /// directly use `#[unstable]` things.
allow_internal_unstable: bool, ///
/// Only allows things that require a feature gate in the given whitelist
allow_internal_unstable: Option<Lrc<[Symbol]>>,
/// Whether the contents of the macro can use `unsafe` /// Whether the contents of the macro can use `unsafe`
/// without triggering the `unsafe_code` lint. /// without triggering the `unsafe_code` lint.
allow_internal_unsafe: bool, allow_internal_unsafe: bool,
@ -654,8 +657,11 @@ pub enum SyntaxExtension {
/// 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 {
IdentTT(Box<dyn IdentMacroExpander + sync::Sync + sync::Send>, Option<Span>, bool), expander: Box<dyn IdentMacroExpander + sync::Sync + sync::Send>,
span: Option<Span>,
allow_internal_unstable: Option<Lrc<[Symbol]>>,
},
/// An attribute-like procedural macro. TokenStream -> TokenStream. /// An attribute-like procedural macro. TokenStream -> TokenStream.
/// The input is the annotated item. /// The input is the annotated item.
@ -682,7 +688,7 @@ impl SyntaxExtension {
match *self { match *self {
SyntaxExtension::DeclMacro { .. } | SyntaxExtension::DeclMacro { .. } |
SyntaxExtension::NormalTT { .. } | SyntaxExtension::NormalTT { .. } |
SyntaxExtension::IdentTT(..) | SyntaxExtension::IdentTT { .. } |
SyntaxExtension::ProcMacro { .. } => SyntaxExtension::ProcMacro { .. } =>
MacroKind::Bang, MacroKind::Bang,
SyntaxExtension::NonMacroAttr { .. } | SyntaxExtension::NonMacroAttr { .. } |
@ -716,7 +722,7 @@ impl SyntaxExtension {
SyntaxExtension::ProcMacroDerive(.., edition) => edition, SyntaxExtension::ProcMacroDerive(.., edition) => edition,
// Unstable legacy stuff // Unstable legacy stuff
SyntaxExtension::NonMacroAttr { .. } | SyntaxExtension::NonMacroAttr { .. } |
SyntaxExtension::IdentTT(..) | SyntaxExtension::IdentTT { .. } |
SyntaxExtension::MultiDecorator(..) | SyntaxExtension::MultiDecorator(..) |
SyntaxExtension::MultiModifier(..) | SyntaxExtension::MultiModifier(..) |
SyntaxExtension::BuiltinDerive(..) => hygiene::default_edition(), SyntaxExtension::BuiltinDerive(..) => hygiene::default_edition(),

View file

@ -58,7 +58,10 @@ pub fn add_derived_markers<T>(cx: &mut ExtCtxt<'_>, span: Span, traits: &[ast::P
call_site: span, call_site: span,
def_site: None, def_site: None,
format: ExpnFormat::MacroAttribute(Symbol::intern(&pretty_name)), format: ExpnFormat::MacroAttribute(Symbol::intern(&pretty_name)),
allow_internal_unstable: true, allow_internal_unstable: Some(vec![
Symbol::intern("rustc_attrs"),
Symbol::intern("structural_match"),
].into()),
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
edition: hygiene::default_edition(), edition: hygiene::default_edition(),

View file

@ -558,7 +558,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
call_site: attr.span, call_site: attr.span,
def_site: None, def_site: None,
format: MacroAttribute(Symbol::intern(&attr.path.to_string())), format: MacroAttribute(Symbol::intern(&attr.path.to_string())),
allow_internal_unstable: false, allow_internal_unstable: None,
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
edition: ext.edition(), edition: ext.edition(),
@ -725,7 +725,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
// don't stability-check macros in the same crate // don't stability-check macros in the same crate
// (the only time this is null is for syntax extensions registered as macros) // (the only time this is null is for syntax extensions registered as macros)
if def_site_span.map_or(false, |def_span| !crate_span.contains(def_span)) if def_site_span.map_or(false, |def_span| !crate_span.contains(def_span))
&& !span.allows_unstable() && this.cx.ecfg.features.map_or(true, |feats| { && !span.allows_unstable(&feature.as_str())
&& this.cx.ecfg.features.map_or(true, |feats| {
// macro features will count as lib features // macro features will count as lib features
!feats.declared_lib_features.iter().any(|&(feat, _)| feat == feature) !feats.declared_lib_features.iter().any(|&(feat, _)| feat == feature)
}) { }) {
@ -757,7 +758,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
let opt_expanded = match *ext { let opt_expanded = match *ext {
DeclMacro { ref expander, def_info, edition, .. } => { DeclMacro { ref expander, def_info, edition, .. } => {
if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s), if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s),
false, false, false, None, None, false, false, None,
edition) { edition) {
dummy_span dummy_span
} else { } else {
@ -768,14 +769,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
NormalTT { NormalTT {
ref expander, ref expander,
def_info, def_info,
allow_internal_unstable, ref allow_internal_unstable,
allow_internal_unsafe, allow_internal_unsafe,
local_inner_macros, local_inner_macros,
unstable_feature, unstable_feature,
edition, edition,
} => { } => {
if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s), if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s),
allow_internal_unstable, allow_internal_unstable.clone(),
allow_internal_unsafe, allow_internal_unsafe,
local_inner_macros, local_inner_macros,
unstable_feature, unstable_feature,
@ -791,7 +792,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
} }
} }
IdentTT(ref expander, tt_span, allow_internal_unstable) => { IdentTT { ref expander, span: tt_span, ref allow_internal_unstable } => {
if ident.name == keywords::Invalid.name() { if ident.name == keywords::Invalid.name() {
self.cx.span_err(path.span, self.cx.span_err(path.span,
&format!("macro {}! expects an ident argument", path)); &format!("macro {}! expects an ident argument", path));
@ -802,7 +803,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
call_site: span, call_site: span,
def_site: tt_span, def_site: tt_span,
format: macro_bang_format(path), format: macro_bang_format(path),
allow_internal_unstable, allow_internal_unstable: allow_internal_unstable.clone(),
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
edition: hygiene::default_edition(), edition: hygiene::default_edition(),
@ -827,7 +828,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
kind.dummy(span) kind.dummy(span)
} }
SyntaxExtension::ProcMacro { ref expander, allow_internal_unstable, edition } => { SyntaxExtension::ProcMacro { ref expander, ref allow_internal_unstable, edition } => {
if ident.name != keywords::Invalid.name() { if ident.name != keywords::Invalid.name() {
let msg = let msg =
format!("macro {}! expects no ident argument, given '{}'", path, ident); format!("macro {}! expects no ident argument, given '{}'", path, ident);
@ -843,7 +844,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
def_site: None, def_site: None,
format: macro_bang_format(path), format: macro_bang_format(path),
// FIXME probably want to follow macro_rules macros here. // FIXME probably want to follow macro_rules macros here.
allow_internal_unstable, allow_internal_unstable: allow_internal_unstable.clone(),
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
edition, edition,
@ -918,7 +919,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
call_site: span, call_site: span,
def_site: None, def_site: None,
format: MacroAttribute(pretty_name), format: MacroAttribute(pretty_name),
allow_internal_unstable: false, allow_internal_unstable: None,
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
edition: ext.edition(), edition: ext.edition(),
@ -937,7 +938,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
Some(invoc.fragment_kind.expect_from_annotatables(items)) Some(invoc.fragment_kind.expect_from_annotatables(items))
} }
BuiltinDerive(func) => { BuiltinDerive(func) => {
expn_info.allow_internal_unstable = true; expn_info.allow_internal_unstable = Some(vec![
Symbol::intern("rustc_attrs"),
Symbol::intern("derive_clone_copy"),
Symbol::intern("derive_eq"),
Symbol::intern("libstd_sys_internals"), // RustcDeserialize and RustcSerialize
].into());
invoc.expansion_data.mark.set_expn_info(expn_info); invoc.expansion_data.mark.set_expn_info(expn_info);
let span = span.with_ctxt(self.cx.backtrace()); let span = span.with_ctxt(self.cx.backtrace());
let mut items = Vec::new(); let mut items = Vec::new();

View file

@ -44,7 +44,7 @@ pub fn expand_column(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTr
/* __rust_unstable_column!(): expands to the current column number */ /* __rust_unstable_column!(): expands to the current column number */
pub fn expand_column_gated(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) pub fn expand_column_gated(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree])
-> Box<dyn base::MacResult+'static> { -> Box<dyn base::MacResult+'static> {
if sp.allows_unstable() { if sp.allows_unstable("__rust_unstable_column") {
expand_column(cx, sp, tts) expand_column(cx, sp, tts)
} else { } else {
cx.span_fatal(sp, "the __rust_unstable_column macro is unstable"); cx.span_fatal(sp, "the __rust_unstable_column macro is unstable");

View file

@ -376,7 +376,24 @@ pub fn compile(
}); });
if body.legacy { if body.legacy {
let allow_internal_unstable = attr::contains_name(&def.attrs, "allow_internal_unstable"); let allow_internal_unstable = attr::find_by_name(&def.attrs, "allow_internal_unstable")
.map(|attr| attr
.meta_item_list()
.map(|list| list.iter()
.map(|it| it.name().unwrap_or_else(|| sess.span_diagnostic.span_bug(
it.span, "allow internal unstable expects feature names",
)))
.collect::<Vec<Symbol>>().into()
)
.unwrap_or_else(|| {
sess.span_diagnostic.span_warn(
attr.span, "allow_internal_unstable expects list of feature names. In the \
future this will become a hard error. Please use `allow_internal_unstable(\
foo, bar)` to only allow the `foo` and `bar` features",
);
vec![Symbol::intern("allow_internal_unstable_backcompat_hack")].into()
})
);
let allow_internal_unsafe = attr::contains_name(&def.attrs, "allow_internal_unsafe"); let allow_internal_unsafe = attr::contains_name(&def.attrs, "allow_internal_unsafe");
let mut local_inner_macros = false; let mut local_inner_macros = false;
if let Some(macro_export) = attr::find_by_name(&def.attrs, "macro_export") { if let Some(macro_export) = attr::find_by_name(&def.attrs, "macro_export") {

View file

@ -1091,7 +1091,8 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu
stable", stable",
cfg_fn!(profiler_runtime))), cfg_fn!(profiler_runtime))),
("allow_internal_unstable", Normal, template!(Word), Gated(Stability::Unstable, ("allow_internal_unstable", Normal, template!(Word, List: "feat1, feat2, ..."),
Gated(Stability::Unstable,
"allow_internal_unstable", "allow_internal_unstable",
EXPLAIN_ALLOW_INTERNAL_UNSTABLE, EXPLAIN_ALLOW_INTERNAL_UNSTABLE,
cfg_fn!(allow_internal_unstable))), cfg_fn!(allow_internal_unstable))),
@ -1199,7 +1200,7 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu
("proc_macro", Normal, template!(Word), Ungated), ("proc_macro", Normal, template!(Word), Ungated),
("rustc_proc_macro_decls", Normal, template!(Word), Gated(Stability::Unstable, ("rustc_proc_macro_decls", Normal, template!(Word), Gated(Stability::Unstable,
"rustc_proc_macro_decls", "rustc_attrs",
"used internally by rustc", "used internally by rustc",
cfg_fn!(rustc_attrs))), cfg_fn!(rustc_attrs))),
@ -1284,7 +1285,7 @@ impl GatedCfg {
pub fn check_and_emit(&self, sess: &ParseSess, features: &Features) { pub fn check_and_emit(&self, sess: &ParseSess, features: &Features) {
let (cfg, feature, has_feature) = GATED_CFGS[self.index]; let (cfg, feature, has_feature) = GATED_CFGS[self.index];
if !has_feature(features) && !self.span.allows_unstable() { if !has_feature(features) && !self.span.allows_unstable(feature) {
let explain = format!("`cfg({})` is experimental and subject to change", cfg); let explain = format!("`cfg({})` is experimental and subject to change", cfg);
emit_feature_err(sess, feature, self.span, GateIssue::Language, &explain); emit_feature_err(sess, feature, self.span, GateIssue::Language, &explain);
} }
@ -1303,7 +1304,7 @@ macro_rules! gate_feature_fn {
name, explain, level) = ($cx, $has_feature, $span, $name, $explain, $level); name, explain, level) = ($cx, $has_feature, $span, $name, $explain, $level);
let has_feature: bool = has_feature(&$cx.features); let has_feature: bool = has_feature(&$cx.features);
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature); debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
if !has_feature && !span.allows_unstable() { if !has_feature && !span.allows_unstable($name) {
leveled_feature_err(cx.parse_sess, name, span, GateIssue::Language, explain, level) leveled_feature_err(cx.parse_sess, name, span, GateIssue::Language, explain, level)
.emit(); .emit();
} }
@ -1328,7 +1329,11 @@ impl<'a> Context<'a> {
for &(n, ty, _template, ref gateage) in BUILTIN_ATTRIBUTES { for &(n, ty, _template, ref gateage) in BUILTIN_ATTRIBUTES {
if name == n { if name == n {
if let Gated(_, name, desc, ref has_feature) = *gateage { if let Gated(_, name, desc, ref has_feature) = *gateage {
gate_feature_fn!(self, has_feature, attr.span, name, desc, GateStrength::Hard); if !attr.span.allows_unstable(name) {
gate_feature_fn!(
self, has_feature, attr.span, name, desc, GateStrength::Hard
);
}
} else if name == "doc" { } else if name == "doc" {
if let Some(content) = attr.meta_item_list() { if let Some(content) = attr.meta_item_list() {
if content.iter().any(|c| c.check_name("include")) { if content.iter().any(|c| c.check_name("include")) {
@ -1493,13 +1498,13 @@ struct PostExpansionVisitor<'a> {
macro_rules! gate_feature_post { macro_rules! gate_feature_post {
($cx: expr, $feature: ident, $span: expr, $explain: expr) => {{ ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {{
let (cx, span) = ($cx, $span); let (cx, span) = ($cx, $span);
if !span.allows_unstable() { if !span.allows_unstable(stringify!($feature)) {
gate_feature!(cx.context, $feature, span, $explain) gate_feature!(cx.context, $feature, span, $explain)
} }
}}; }};
($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {{ ($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {{
let (cx, span) = ($cx, $span); let (cx, span) = ($cx, $span);
if !span.allows_unstable() { if !span.allows_unstable(stringify!($feature)) {
gate_feature!(cx.context, $feature, span, $explain, $level) gate_feature!(cx.context, $feature, span, $explain, $level)
} }
}} }}
@ -1610,10 +1615,8 @@ impl<'a> PostExpansionVisitor<'a> {
impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
fn visit_attribute(&mut self, attr: &ast::Attribute) { fn visit_attribute(&mut self, attr: &ast::Attribute) {
if !attr.span.allows_unstable() {
// check for gated attributes // check for gated attributes
self.context.check_attribute(attr, false); self.context.check_attribute(attr, false);
}
if attr.check_name("doc") { if attr.check_name("doc") {
if let Some(content) = attr.meta_item_list() { if let Some(content) = attr.meta_item_list() {

View file

@ -20,7 +20,9 @@ fn ignored_span(sp: Span) -> Span {
call_site: DUMMY_SP, call_site: DUMMY_SP,
def_site: None, def_site: None,
format: MacroAttribute(Symbol::intern("std_inject")), format: MacroAttribute(Symbol::intern("std_inject")),
allow_internal_unstable: true, allow_internal_unstable: Some(vec![
Symbol::intern("prelude_import"),
].into()),
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
edition: hygiene::default_edition(), edition: hygiene::default_edition(),

View file

@ -285,7 +285,11 @@ fn generate_test_harness(sess: &ParseSess,
call_site: DUMMY_SP, call_site: DUMMY_SP,
def_site: None, def_site: None,
format: MacroAttribute(Symbol::intern("test_case")), format: MacroAttribute(Symbol::intern("test_case")),
allow_internal_unstable: true, allow_internal_unstable: Some(vec![
Symbol::intern("main"),
Symbol::intern("test"),
Symbol::intern("rustc_attrs"),
].into()),
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
edition: hygiene::default_edition(), edition: hygiene::default_edition(),

View file

@ -136,11 +136,16 @@ fn call_intrinsic(cx: &ExtCtxt<'_>,
intrinsic: &str, intrinsic: &str,
args: Vec<P<ast::Expr>>) args: Vec<P<ast::Expr>>)
-> P<ast::Expr> { -> P<ast::Expr> {
if cx.current_expansion.mark.expn_info().unwrap().allow_internal_unstable { let intrinsic_allowed_via_allow_internal_unstable = cx
.current_expansion.mark.expn_info().unwrap()
.allow_internal_unstable.map_or(false, |features| features.iter().any(|&s|
s == "core_intrinsics"
));
if intrinsic_allowed_via_allow_internal_unstable {
span = span.with_ctxt(cx.backtrace()); span = span.with_ctxt(cx.backtrace());
} else { // Avoid instability errors with user defined curstom derives, cc #36316 } else { // Avoid instability errors with user defined curstom derives, cc #36316
let mut info = cx.current_expansion.mark.expn_info().unwrap(); let mut info = cx.current_expansion.mark.expn_info().unwrap();
info.allow_internal_unstable = true; info.allow_internal_unstable = Some(vec![Symbol::intern("core_intrinsics")].into());
let mark = Mark::fresh(Mark::root()); let mark = Mark::fresh(Mark::root());
mark.set_expn_info(info); mark.set_expn_info(info);
span = span.with_ctxt(SyntaxContext::empty().apply_mark(mark)); span = span.with_ctxt(SyntaxContext::empty().apply_mark(mark));

View file

@ -711,7 +711,7 @@ pub fn expand_format_args_nl<'cx>(
//if !ecx.ecfg.enable_allow_internal_unstable() { //if !ecx.ecfg.enable_allow_internal_unstable() {
// For some reason, the only one that actually works for `println` is the first check // For some reason, the only one that actually works for `println` is the first check
if !sp.allows_unstable() // the enclosing span is marked as `#[allow_insternal_unsable]` if !sp.allows_unstable("format_args_nl") // the span is marked as `#[allow_insternal_unsable]`
&& !ecx.ecfg.enable_allow_internal_unstable() // NOTE: when is this enabled? && !ecx.ecfg.enable_allow_internal_unstable() // NOTE: when is this enabled?
&& !ecx.ecfg.enable_format_args_nl() // enabled using `#[feature(format_args_nl]` && !ecx.ecfg.enable_format_args_nl() // enabled using `#[feature(format_args_nl]`
{ {

View file

@ -61,7 +61,7 @@ pub fn register_builtins(resolver: &mut dyn syntax::ext::base::Resolver,
NormalTT { NormalTT {
expander: Box::new($f as MacroExpanderFn), expander: Box::new($f as MacroExpanderFn),
def_info: None, def_info: None,
allow_internal_unstable: false, allow_internal_unstable: None,
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
unstable_feature: None, unstable_feature: None,
@ -104,7 +104,9 @@ pub fn register_builtins(resolver: &mut dyn syntax::ext::base::Resolver,
NormalTT { NormalTT {
expander: Box::new(format::expand_format_args), expander: Box::new(format::expand_format_args),
def_info: None, def_info: None,
allow_internal_unstable: true, allow_internal_unstable: Some(vec![
Symbol::intern("fmt_internals"),
].into()),
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
unstable_feature: None, unstable_feature: None,
@ -114,7 +116,9 @@ pub fn register_builtins(resolver: &mut dyn syntax::ext::base::Resolver,
NormalTT { NormalTT {
expander: Box::new(format::expand_format_args_nl), expander: Box::new(format::expand_format_args_nl),
def_info: None, def_info: None,
allow_internal_unstable: true, allow_internal_unstable: Some(vec![
Symbol::intern("fmt_internals"),
].into()),
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
unstable_feature: None, unstable_feature: None,

View file

@ -333,7 +333,10 @@ fn mk_decls(
call_site: DUMMY_SP, call_site: DUMMY_SP,
def_site: None, def_site: None,
format: MacroAttribute(Symbol::intern("proc_macro")), format: MacroAttribute(Symbol::intern("proc_macro")),
allow_internal_unstable: true, allow_internal_unstable: Some(vec![
Symbol::intern("rustc_attrs"),
Symbol::intern("proc_macro_internals"),
].into()),
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
edition: hygiene::default_edition(), edition: hygiene::default_edition(),

View file

@ -66,7 +66,10 @@ pub fn expand_test_or_bench(
call_site: DUMMY_SP, call_site: DUMMY_SP,
def_site: None, def_site: None,
format: MacroAttribute(Symbol::intern("test")), format: MacroAttribute(Symbol::intern("test")),
allow_internal_unstable: true, allow_internal_unstable: Some(vec![
Symbol::intern("rustc_attrs"),
Symbol::intern("test"),
].into()),
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
edition: hygiene::default_edition(), edition: hygiene::default_edition(),

View file

@ -41,7 +41,10 @@ pub fn expand(
call_site: DUMMY_SP, call_site: DUMMY_SP,
def_site: None, def_site: None,
format: MacroAttribute(Symbol::intern("test_case")), format: MacroAttribute(Symbol::intern("test_case")),
allow_internal_unstable: true, allow_internal_unstable: Some(vec![
Symbol::intern("test"),
Symbol::intern("rustc_attrs"),
].into()),
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
edition: hygiene::default_edition(), edition: hygiene::default_edition(),

View file

@ -12,6 +12,7 @@ use crate::symbol::{keywords, Symbol};
use serialize::{Encodable, Decodable, Encoder, Decoder}; use serialize::{Encodable, Decodable, Encoder, Decoder};
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::Lrc;
use std::{fmt, mem}; use std::{fmt, mem};
/// A SyntaxContext represents a chain of macro expansions (represented by marks). /// A SyntaxContext represents a chain of macro expansions (represented by marks).
@ -550,10 +551,10 @@ pub struct ExpnInfo {
pub def_site: Option<Span>, pub def_site: Option<Span>,
/// The format with which the macro was invoked. /// The format with which the macro was invoked.
pub format: ExpnFormat, pub format: ExpnFormat,
/// Whether the macro is allowed to use #[unstable]/feature-gated /// List of #[unstable]/feature-gated features that the macro is allowed to use
/// features internally without forcing the whole crate to opt-in /// internally without forcing the whole crate to opt-in
/// to them. /// to them.
pub allow_internal_unstable: bool, pub allow_internal_unstable: Option<Lrc<[Symbol]>>,
/// Whether the macro is allowed to use `unsafe` internally /// Whether the macro is allowed to use `unsafe` internally
/// even if the user crate has `#![forbid(unsafe_code)]`. /// even if the user crate has `#![forbid(unsafe_code)]`.
pub allow_internal_unsafe: bool, pub allow_internal_unsafe: bool,

View file

@ -386,9 +386,13 @@ impl Span {
/// Check if a span is "internal" to a macro in which `#[unstable]` /// Check if a span is "internal" to a macro in which `#[unstable]`
/// items can be used (that is, a macro marked with /// items can be used (that is, a macro marked with
/// `#[allow_internal_unstable]`). /// `#[allow_internal_unstable]`).
pub fn allows_unstable(&self) -> bool { pub fn allows_unstable(&self, feature: &str) -> bool {
match self.ctxt().outer().expn_info() { match self.ctxt().outer().expn_info() {
Some(info) => info.allow_internal_unstable, Some(info) => info
.allow_internal_unstable
.map_or(false, |features| features.iter().any(|&f|
f == feature || f == "allow_internal_unstable_backcompat_hack"
)),
None => false, None => false,
} }
} }

View file

@ -43,7 +43,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
NormalTT { NormalTT {
expander: Box::new(Expander { args: args, }), expander: Box::new(Expander { args: args, }),
def_info: None, def_info: None,
allow_internal_unstable: false, allow_internal_unstable: None,
allow_internal_unsafe: false, allow_internal_unsafe: false,
local_inner_macros: false, local_inner_macros: false,
unstable_feature: None, unstable_feature: None,

View file

@ -5,7 +5,7 @@
macro_rules! bar { macro_rules! bar {
() => { () => {
// more layers don't help: // more layers don't help:
#[allow_internal_unstable] //~ ERROR allow_internal_unstable side-steps #[allow_internal_unstable()] //~ ERROR allow_internal_unstable side-steps
macro_rules! baz { macro_rules! baz {
() => {} () => {}
} }

View file

@ -1,8 +1,8 @@
error[E0658]: allow_internal_unstable side-steps feature gating and stability checks error[E0658]: allow_internal_unstable side-steps feature gating and stability checks
--> $DIR/feature-gate-allow-internal-unstable-nested-macro.rs:8:9 --> $DIR/feature-gate-allow-internal-unstable-nested-macro.rs:8:9
| |
LL | #[allow_internal_unstable] //~ ERROR allow_internal_unstable side-steps LL | #[allow_internal_unstable()] //~ ERROR allow_internal_unstable side-steps
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
... ...
LL | bar!(); LL | bar!();
| ------- in this macro invocation | ------- in this macro invocation

View file

@ -1,7 +1,7 @@
// checks that this attribute is caught on non-macro items. // checks that this attribute is caught on non-macro items.
// this needs a different test since this is done after expansion // this needs a different test since this is done after expansion
#[allow_internal_unstable] //~ ERROR allow_internal_unstable side-steps #[allow_internal_unstable()] //~ ERROR allow_internal_unstable side-steps
struct S; struct S;
fn main() {} fn main() {}

View file

@ -1,8 +1,8 @@
error[E0658]: allow_internal_unstable side-steps feature gating and stability checks error[E0658]: allow_internal_unstable side-steps feature gating and stability checks
--> $DIR/feature-gate-allow-internal-unstable-struct.rs:4:1 --> $DIR/feature-gate-allow-internal-unstable-struct.rs:4:1
| |
LL | #[allow_internal_unstable] //~ ERROR allow_internal_unstable side-steps LL | #[allow_internal_unstable()] //~ ERROR allow_internal_unstable side-steps
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: add #![feature(allow_internal_unstable)] to the crate attributes to enable = help: add #![feature(allow_internal_unstable)] to the crate attributes to enable

View file

@ -1,6 +1,6 @@
#![allow(unused_macros)] #![allow(unused_macros)]
#[allow_internal_unstable] //~ ERROR allow_internal_unstable side-steps #[allow_internal_unstable()] //~ ERROR allow_internal_unstable side-steps
macro_rules! foo { macro_rules! foo {
() => {} () => {}
} }

View file

@ -1,8 +1,8 @@
error[E0658]: allow_internal_unstable side-steps feature gating and stability checks error[E0658]: allow_internal_unstable side-steps feature gating and stability checks
--> $DIR/feature-gate-allow-internal-unstable.rs:3:1 --> $DIR/feature-gate-allow-internal-unstable.rs:3:1
| |
LL | #[allow_internal_unstable] //~ ERROR allow_internal_unstable side-steps LL | #[allow_internal_unstable()] //~ ERROR allow_internal_unstable side-steps
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: add #![feature(allow_internal_unstable)] to the crate attributes to enable = help: add #![feature(allow_internal_unstable)] to the crate attributes to enable

View file

@ -23,14 +23,14 @@ pub struct Bar {
} }
#[stable(feature = "stable", since = "1.0.0")] #[stable(feature = "stable", since = "1.0.0")]
#[allow_internal_unstable] #[allow_internal_unstable(function)]
#[macro_export] #[macro_export]
macro_rules! call_unstable_allow { macro_rules! call_unstable_allow {
() => { $crate::unstable() } () => { $crate::unstable() }
} }
#[stable(feature = "stable", since = "1.0.0")] #[stable(feature = "stable", since = "1.0.0")]
#[allow_internal_unstable] #[allow_internal_unstable(struct_field)]
#[macro_export] #[macro_export]
macro_rules! construct_unstable_allow { macro_rules! construct_unstable_allow {
($e: expr) => { ($e: expr) => {
@ -39,21 +39,21 @@ macro_rules! construct_unstable_allow {
} }
#[stable(feature = "stable", since = "1.0.0")] #[stable(feature = "stable", since = "1.0.0")]
#[allow_internal_unstable] #[allow_internal_unstable(method)]
#[macro_export] #[macro_export]
macro_rules! call_method_allow { macro_rules! call_method_allow {
($e: expr) => { $e.method() } ($e: expr) => { $e.method() }
} }
#[stable(feature = "stable", since = "1.0.0")] #[stable(feature = "stable", since = "1.0.0")]
#[allow_internal_unstable] #[allow_internal_unstable(struct_field, struct2_field)]
#[macro_export] #[macro_export]
macro_rules! access_field_allow { macro_rules! access_field_allow {
($e: expr) => { $e.x } ($e: expr) => { $e.x }
} }
#[stable(feature = "stable", since = "1.0.0")] #[stable(feature = "stable", since = "1.0.0")]
#[allow_internal_unstable] #[allow_internal_unstable()]
#[macro_export] #[macro_export]
macro_rules! pass_through_allow { macro_rules! pass_through_allow {
($e: expr) => { $e } ($e: expr) => { $e }

View file

@ -13,7 +13,7 @@ macro_rules! foo {
}} }}
} }
#[allow_internal_unstable] #[allow_internal_unstable(function)]
macro_rules! bar { macro_rules! bar {
($e: expr) => {{ ($e: expr) => {{
foo!($e, foo!($e,

View file

@ -58,6 +58,7 @@ const EXCEPTION_PATHS: &[&str] = &[
"src/libstd/sys_common/net.rs", "src/libstd/sys_common/net.rs",
"src/libterm", // Not sure how to make this crate portable, but test crate needs it. "src/libterm", // Not sure how to make this crate portable, but test crate needs it.
"src/libtest", // Probably should defer to unstable `std::sys` APIs. "src/libtest", // Probably should defer to unstable `std::sys` APIs.
"src/libstd/sync/mpsc", // some tests are only run on non-emscripten
// std testing crates, okay for now at least // std testing crates, okay for now at least
"src/libcore/tests", "src/libcore/tests",