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:
commit
c84e797642
41 changed files with 622 additions and 512 deletions
|
@ -34,7 +34,8 @@
|
|||
#[cfg(not(test))]
|
||||
#[macro_export]
|
||||
#[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 {
|
||||
($elem:expr; $n:expr) => (
|
||||
$crate::vec::from_elem($elem, $n)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/// Entry point of thread panic, for details, see std::macros
|
||||
#[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")]
|
||||
macro_rules! panic {
|
||||
() => (
|
||||
|
@ -409,7 +410,8 @@ macro_rules! write {
|
|||
/// ```
|
||||
#[macro_export]
|
||||
#[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 {
|
||||
($dst:expr) => (
|
||||
write!($dst, "\n")
|
||||
|
|
|
@ -44,6 +44,7 @@ use crate::middle::cstore::CrateStore;
|
|||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use rustc_data_structures::thin_vec::ThinVec;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use crate::session::Session;
|
||||
use crate::session::config::nightly_options;
|
||||
use crate::util::common::FN_OUTPUT_NAME;
|
||||
|
@ -681,13 +682,20 @@ impl<'a> LoweringContext<'a> {
|
|||
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());
|
||||
mark.set_expn_info(source_map::ExpnInfo {
|
||||
call_site: span,
|
||||
def_site: Some(span),
|
||||
format: source_map::CompilerDesugaring(reason),
|
||||
allow_internal_unstable: true,
|
||||
allow_internal_unstable,
|
||||
allow_internal_unsafe: false,
|
||||
local_inner_macros: false,
|
||||
edition: source_map::hygiene::default_edition(),
|
||||
|
@ -964,7 +972,13 @@ impl<'a> LoweringContext<'a> {
|
|||
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(
|
||||
unstable_span, &["future", "from_generator"], None, ThinVec::new());
|
||||
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.
|
||||
// Not tracking it makes lints in rustc and clippy very fragile as
|
||||
// frequently opened issues show.
|
||||
let exist_ty_span = self.allow_internal_unstable(
|
||||
let exist_ty_span = self.mark_span_with_reason(
|
||||
CompilerDesugaringKind::ExistentialReturnType,
|
||||
span,
|
||||
None,
|
||||
);
|
||||
|
||||
let exist_ty_def_index = self
|
||||
|
@ -3927,8 +3942,13 @@ impl<'a> LoweringContext<'a> {
|
|||
}),
|
||||
ExprKind::TryBlock(ref body) => {
|
||||
self.with_catch_scope(body.id, |this| {
|
||||
let unstable_span =
|
||||
this.allow_internal_unstable(CompilerDesugaringKind::TryBlock, body.span);
|
||||
let unstable_span = this.mark_span_with_reason(
|
||||
CompilerDesugaringKind::TryBlock,
|
||||
body.span,
|
||||
Some(vec![
|
||||
Symbol::intern("try_trait"),
|
||||
].into()),
|
||||
);
|
||||
let mut block = this.lower_block(body, true).into_inner();
|
||||
let tail = block.expr.take().map_or_else(
|
||||
|| {
|
||||
|
@ -4360,9 +4380,10 @@ impl<'a> LoweringContext<'a> {
|
|||
// expand <head>
|
||||
let head = self.lower_expr(head);
|
||||
let head_sp = head.span;
|
||||
let desugared_span = self.allow_internal_unstable(
|
||||
let desugared_span = self.mark_span_with_reason(
|
||||
CompilerDesugaringKind::ForLoop,
|
||||
head_sp,
|
||||
None,
|
||||
);
|
||||
|
||||
let iter = self.str_to_ident("iter");
|
||||
|
@ -4525,8 +4546,13 @@ impl<'a> LoweringContext<'a> {
|
|||
// return Try::from_error(From::from(err)),
|
||||
// }
|
||||
|
||||
let unstable_span =
|
||||
self.allow_internal_unstable(CompilerDesugaringKind::QuestionMark, e.span);
|
||||
let unstable_span = self.mark_span_with_reason(
|
||||
CompilerDesugaringKind::QuestionMark,
|
||||
e.span,
|
||||
Some(vec![
|
||||
Symbol::intern("try_trait")
|
||||
].into()),
|
||||
);
|
||||
|
||||
// `Try::into_result(<expr>)`
|
||||
let discr = {
|
||||
|
|
|
@ -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
|
||||
/// `id`.
|
||||
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,
|
||||
id: NodeId,
|
||||
note: Option<Symbol>,
|
||||
|
@ -694,6 +689,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
|||
|
||||
match stability {
|
||||
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) {
|
||||
return EvalResult::Allow;
|
||||
}
|
||||
|
|
|
@ -91,7 +91,9 @@ impl MutVisitor for ExpandAllocatorDirectives<'_> {
|
|||
call_site: item.span, // use the call site of the static
|
||||
def_site: None,
|
||||
format: MacroAttribute(Symbol::intern(name)),
|
||||
allow_internal_unstable: true,
|
||||
allow_internal_unstable: Some(vec![
|
||||
Symbol::intern("rustc_attrs"),
|
||||
].into()),
|
||||
allow_internal_unsafe: false,
|
||||
local_inner_macros: false,
|
||||
edition: hygiene::default_edition(),
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/// A simple static assertion macro. The first argument should be a unique
|
||||
/// ALL_CAPS identifier that describes the condition.
|
||||
#[macro_export]
|
||||
#[allow_internal_unstable]
|
||||
#[cfg_attr(stage0, allow_internal_unstable)]
|
||||
#[cfg_attr(not(stage0), allow_internal_unstable(type_ascription))]
|
||||
macro_rules! static_assert {
|
||||
($name:ident: $test:expr) => {
|
||||
// Use the bool to access an array such that if the bool is false, the access
|
||||
|
|
|
@ -570,7 +570,7 @@ impl<'a> CrateLoader<'a> {
|
|||
ProcMacro::Bang { name, client } => {
|
||||
(name, SyntaxExtension::ProcMacro {
|
||||
expander: Box::new(BangProcMacro { client }),
|
||||
allow_internal_unstable: false,
|
||||
allow_internal_unstable: None,
|
||||
edition: root.edition,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -425,7 +425,9 @@ impl cstore::CStore {
|
|||
let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote);
|
||||
let ext = SyntaxExtension::ProcMacro {
|
||||
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,
|
||||
};
|
||||
return LoadedMacro::ProcMacro(Lrc::new(ext));
|
||||
|
|
|
@ -909,7 +909,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
|||
// Check `#[unstable]` const fns or `#[rustc_const_unstable]`
|
||||
// functions without the feature gate active in this crate in
|
||||
// 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.
|
||||
is_const_fn = true;
|
||||
} else {
|
||||
|
|
|
@ -110,8 +110,8 @@ impl<'a> Registry<'a> {
|
|||
edition,
|
||||
}
|
||||
}
|
||||
IdentTT(ext, _, allow_internal_unstable) => {
|
||||
IdentTT(ext, Some(self.krate_span), allow_internal_unstable)
|
||||
IdentTT { expander, span: _, allow_internal_unstable } => {
|
||||
IdentTT { expander, span: Some(self.krate_span), allow_internal_unstable }
|
||||
}
|
||||
_ => extension,
|
||||
}));
|
||||
|
@ -126,7 +126,7 @@ impl<'a> Registry<'a> {
|
|||
self.register_syntax_extension(Symbol::intern(name), NormalTT {
|
||||
expander: Box::new(expander),
|
||||
def_info: None,
|
||||
allow_internal_unstable: false,
|
||||
allow_internal_unstable: None,
|
||||
allow_internal_unsafe: false,
|
||||
local_inner_macros: false,
|
||||
unstable_feature: None,
|
||||
|
|
|
@ -53,7 +53,8 @@
|
|||
/// ```
|
||||
#[macro_export]
|
||||
#[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 {
|
||||
() => ({
|
||||
panic!("explicit panic")
|
||||
|
@ -111,7 +112,8 @@ macro_rules! panic {
|
|||
/// ```
|
||||
#[macro_export]
|
||||
#[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 {
|
||||
($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)));
|
||||
}
|
||||
|
@ -143,7 +145,8 @@ macro_rules! print {
|
|||
/// ```
|
||||
#[macro_export]
|
||||
#[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 {
|
||||
() => (print!("\n"));
|
||||
($($arg:tt)*) => ({
|
||||
|
@ -174,7 +177,8 @@ macro_rules! println {
|
|||
/// ```
|
||||
#[macro_export]
|
||||
#[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 {
|
||||
($($arg:tt)*) => ($crate::io::_eprint(format_args!($($arg)*)));
|
||||
}
|
||||
|
@ -202,7 +206,8 @@ macro_rules! eprint {
|
|||
/// ```
|
||||
#[macro_export]
|
||||
#[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 {
|
||||
() => (eprint!("\n"));
|
||||
($($arg:tt)*) => ({
|
||||
|
@ -325,7 +330,8 @@ macro_rules! dbg {
|
|||
/// A macro to await on an async call.
|
||||
#[macro_export]
|
||||
#[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]
|
||||
macro_rules! await {
|
||||
($e:expr) => { {
|
||||
|
|
|
@ -279,6 +279,9 @@ use self::select::StartResult;
|
|||
use self::select::StartResult::*;
|
||||
use self::blocking::SignalToken;
|
||||
|
||||
#[cfg(all(test, not(target_os = "emscripten")))]
|
||||
mod select_tests;
|
||||
|
||||
mod blocking;
|
||||
mod oneshot;
|
||||
mod select;
|
||||
|
|
|
@ -158,7 +158,7 @@ impl Select {
|
|||
}
|
||||
|
||||
/// 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
|
||||
// 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
|
||||
|
@ -352,417 +352,3 @@ impl<'rx, T:Send+'rx> fmt::Debug for Handle<'rx, T> {
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
413
src/libstd/sync/mpsc/select_tests.rs
Normal file
413
src/libstd/sync/mpsc/select_tests.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -126,7 +126,8 @@ impl<T: 'static> fmt::Debug for LocalKey<T> {
|
|||
/// [`std::thread::LocalKey`]: ../std/thread/struct.LocalKey.html
|
||||
#[macro_export]
|
||||
#[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 {
|
||||
// empty (base case for the recursion)
|
||||
() => {};
|
||||
|
@ -148,7 +149,10 @@ macro_rules! thread_local {
|
|||
reason = "should not be necessary",
|
||||
issue = "0")]
|
||||
#[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]
|
||||
macro_rules! __thread_local_inner {
|
||||
(@key $(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => {
|
||||
|
|
|
@ -621,7 +621,8 @@ pub enum SyntaxExtension {
|
|||
/// A function-like procedural macro. TokenStream -> TokenStream.
|
||||
ProcMacro {
|
||||
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,
|
||||
},
|
||||
|
||||
|
@ -638,8 +639,10 @@ pub enum SyntaxExtension {
|
|||
expander: Box<dyn TTMacroExpander + sync::Sync + sync::Send>,
|
||||
def_info: Option<(ast::NodeId, Span)>,
|
||||
/// Whether the contents of the macro can
|
||||
/// directly use `#[unstable]` things (true == yes).
|
||||
allow_internal_unstable: bool,
|
||||
/// directly use `#[unstable]` things.
|
||||
///
|
||||
/// 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`
|
||||
/// without triggering the `unsafe_code` lint.
|
||||
allow_internal_unsafe: bool,
|
||||
|
@ -654,8 +657,11 @@ pub enum SyntaxExtension {
|
|||
|
||||
/// A function-like syntax extension that has an extra ident before
|
||||
/// the block.
|
||||
///
|
||||
IdentTT(Box<dyn IdentMacroExpander + sync::Sync + sync::Send>, Option<Span>, bool),
|
||||
IdentTT {
|
||||
expander: Box<dyn IdentMacroExpander + sync::Sync + sync::Send>,
|
||||
span: Option<Span>,
|
||||
allow_internal_unstable: Option<Lrc<[Symbol]>>,
|
||||
},
|
||||
|
||||
/// An attribute-like procedural macro. TokenStream -> TokenStream.
|
||||
/// The input is the annotated item.
|
||||
|
@ -682,7 +688,7 @@ impl SyntaxExtension {
|
|||
match *self {
|
||||
SyntaxExtension::DeclMacro { .. } |
|
||||
SyntaxExtension::NormalTT { .. } |
|
||||
SyntaxExtension::IdentTT(..) |
|
||||
SyntaxExtension::IdentTT { .. } |
|
||||
SyntaxExtension::ProcMacro { .. } =>
|
||||
MacroKind::Bang,
|
||||
SyntaxExtension::NonMacroAttr { .. } |
|
||||
|
@ -716,7 +722,7 @@ impl SyntaxExtension {
|
|||
SyntaxExtension::ProcMacroDerive(.., edition) => edition,
|
||||
// Unstable legacy stuff
|
||||
SyntaxExtension::NonMacroAttr { .. } |
|
||||
SyntaxExtension::IdentTT(..) |
|
||||
SyntaxExtension::IdentTT { .. } |
|
||||
SyntaxExtension::MultiDecorator(..) |
|
||||
SyntaxExtension::MultiModifier(..) |
|
||||
SyntaxExtension::BuiltinDerive(..) => hygiene::default_edition(),
|
||||
|
|
|
@ -58,7 +58,10 @@ pub fn add_derived_markers<T>(cx: &mut ExtCtxt<'_>, span: Span, traits: &[ast::P
|
|||
call_site: span,
|
||||
def_site: None,
|
||||
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,
|
||||
local_inner_macros: false,
|
||||
edition: hygiene::default_edition(),
|
||||
|
|
|
@ -558,7 +558,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
call_site: attr.span,
|
||||
def_site: None,
|
||||
format: MacroAttribute(Symbol::intern(&attr.path.to_string())),
|
||||
allow_internal_unstable: false,
|
||||
allow_internal_unstable: None,
|
||||
allow_internal_unsafe: false,
|
||||
local_inner_macros: false,
|
||||
edition: ext.edition(),
|
||||
|
@ -725,7 +725,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
// don't stability-check macros in the same crate
|
||||
// (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))
|
||||
&& !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
|
||||
!feats.declared_lib_features.iter().any(|&(feat, _)| feat == feature)
|
||||
}) {
|
||||
|
@ -757,7 +758,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
let opt_expanded = match *ext {
|
||||
DeclMacro { ref expander, def_info, edition, .. } => {
|
||||
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) {
|
||||
dummy_span
|
||||
} else {
|
||||
|
@ -768,14 +769,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
NormalTT {
|
||||
ref expander,
|
||||
def_info,
|
||||
allow_internal_unstable,
|
||||
ref allow_internal_unstable,
|
||||
allow_internal_unsafe,
|
||||
local_inner_macros,
|
||||
unstable_feature,
|
||||
edition,
|
||||
} => {
|
||||
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,
|
||||
local_inner_macros,
|
||||
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() {
|
||||
self.cx.span_err(path.span,
|
||||
&format!("macro {}! expects an ident argument", path));
|
||||
|
@ -802,7 +803,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
call_site: span,
|
||||
def_site: tt_span,
|
||||
format: macro_bang_format(path),
|
||||
allow_internal_unstable,
|
||||
allow_internal_unstable: allow_internal_unstable.clone(),
|
||||
allow_internal_unsafe: false,
|
||||
local_inner_macros: false,
|
||||
edition: hygiene::default_edition(),
|
||||
|
@ -827,7 +828,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
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() {
|
||||
let msg =
|
||||
format!("macro {}! expects no ident argument, given '{}'", path, ident);
|
||||
|
@ -843,7 +844,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
def_site: None,
|
||||
format: macro_bang_format(path),
|
||||
// FIXME probably want to follow macro_rules macros here.
|
||||
allow_internal_unstable,
|
||||
allow_internal_unstable: allow_internal_unstable.clone(),
|
||||
allow_internal_unsafe: false,
|
||||
local_inner_macros: false,
|
||||
edition,
|
||||
|
@ -918,7 +919,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
call_site: span,
|
||||
def_site: None,
|
||||
format: MacroAttribute(pretty_name),
|
||||
allow_internal_unstable: false,
|
||||
allow_internal_unstable: None,
|
||||
allow_internal_unsafe: false,
|
||||
local_inner_macros: false,
|
||||
edition: ext.edition(),
|
||||
|
@ -937,7 +938,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
Some(invoc.fragment_kind.expect_from_annotatables(items))
|
||||
}
|
||||
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);
|
||||
let span = span.with_ctxt(self.cx.backtrace());
|
||||
let mut items = Vec::new();
|
||||
|
|
|
@ -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 */
|
||||
pub fn expand_column_gated(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree])
|
||||
-> Box<dyn base::MacResult+'static> {
|
||||
if sp.allows_unstable() {
|
||||
if sp.allows_unstable("__rust_unstable_column") {
|
||||
expand_column(cx, sp, tts)
|
||||
} else {
|
||||
cx.span_fatal(sp, "the __rust_unstable_column macro is unstable");
|
||||
|
|
|
@ -376,7 +376,24 @@ pub fn compile(
|
|||
});
|
||||
|
||||
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 mut local_inner_macros = false;
|
||||
if let Some(macro_export) = attr::find_by_name(&def.attrs, "macro_export") {
|
||||
|
|
|
@ -1091,7 +1091,8 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu
|
|||
stable",
|
||||
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",
|
||||
EXPLAIN_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),
|
||||
|
||||
("rustc_proc_macro_decls", Normal, template!(Word), Gated(Stability::Unstable,
|
||||
"rustc_proc_macro_decls",
|
||||
"rustc_attrs",
|
||||
"used internally by rustc",
|
||||
cfg_fn!(rustc_attrs))),
|
||||
|
||||
|
@ -1284,7 +1285,7 @@ impl GatedCfg {
|
|||
|
||||
pub fn check_and_emit(&self, sess: &ParseSess, features: &Features) {
|
||||
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);
|
||||
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);
|
||||
let has_feature: bool = has_feature(&$cx.features);
|
||||
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)
|
||||
.emit();
|
||||
}
|
||||
|
@ -1328,7 +1329,11 @@ impl<'a> Context<'a> {
|
|||
for &(n, ty, _template, ref gateage) in BUILTIN_ATTRIBUTES {
|
||||
if name == n {
|
||||
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" {
|
||||
if let Some(content) = attr.meta_item_list() {
|
||||
if content.iter().any(|c| c.check_name("include")) {
|
||||
|
@ -1493,13 +1498,13 @@ struct PostExpansionVisitor<'a> {
|
|||
macro_rules! gate_feature_post {
|
||||
($cx: expr, $feature: ident, $span: expr, $explain: expr) => {{
|
||||
let (cx, span) = ($cx, $span);
|
||||
if !span.allows_unstable() {
|
||||
if !span.allows_unstable(stringify!($feature)) {
|
||||
gate_feature!(cx.context, $feature, span, $explain)
|
||||
}
|
||||
}};
|
||||
($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {{
|
||||
let (cx, span) = ($cx, $span);
|
||||
if !span.allows_unstable() {
|
||||
if !span.allows_unstable(stringify!($feature)) {
|
||||
gate_feature!(cx.context, $feature, span, $explain, $level)
|
||||
}
|
||||
}}
|
||||
|
@ -1610,10 +1615,8 @@ impl<'a> PostExpansionVisitor<'a> {
|
|||
|
||||
impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
fn visit_attribute(&mut self, attr: &ast::Attribute) {
|
||||
if !attr.span.allows_unstable() {
|
||||
// check for gated attributes
|
||||
self.context.check_attribute(attr, false);
|
||||
}
|
||||
|
||||
if attr.check_name("doc") {
|
||||
if let Some(content) = attr.meta_item_list() {
|
||||
|
|
|
@ -20,7 +20,9 @@ fn ignored_span(sp: Span) -> Span {
|
|||
call_site: DUMMY_SP,
|
||||
def_site: None,
|
||||
format: MacroAttribute(Symbol::intern("std_inject")),
|
||||
allow_internal_unstable: true,
|
||||
allow_internal_unstable: Some(vec![
|
||||
Symbol::intern("prelude_import"),
|
||||
].into()),
|
||||
allow_internal_unsafe: false,
|
||||
local_inner_macros: false,
|
||||
edition: hygiene::default_edition(),
|
||||
|
|
|
@ -285,7 +285,11 @@ fn generate_test_harness(sess: &ParseSess,
|
|||
call_site: DUMMY_SP,
|
||||
def_site: None,
|
||||
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,
|
||||
local_inner_macros: false,
|
||||
edition: hygiene::default_edition(),
|
||||
|
|
|
@ -136,11 +136,16 @@ fn call_intrinsic(cx: &ExtCtxt<'_>,
|
|||
intrinsic: &str,
|
||||
args: Vec<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());
|
||||
} else { // Avoid instability errors with user defined curstom derives, cc #36316
|
||||
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());
|
||||
mark.set_expn_info(info);
|
||||
span = span.with_ctxt(SyntaxContext::empty().apply_mark(mark));
|
||||
|
|
|
@ -711,7 +711,7 @@ pub fn expand_format_args_nl<'cx>(
|
|||
//if !ecx.ecfg.enable_allow_internal_unstable() {
|
||||
|
||||
// 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_format_args_nl() // enabled using `#[feature(format_args_nl]`
|
||||
{
|
||||
|
|
|
@ -61,7 +61,7 @@ pub fn register_builtins(resolver: &mut dyn syntax::ext::base::Resolver,
|
|||
NormalTT {
|
||||
expander: Box::new($f as MacroExpanderFn),
|
||||
def_info: None,
|
||||
allow_internal_unstable: false,
|
||||
allow_internal_unstable: None,
|
||||
allow_internal_unsafe: false,
|
||||
local_inner_macros: false,
|
||||
unstable_feature: None,
|
||||
|
@ -104,7 +104,9 @@ pub fn register_builtins(resolver: &mut dyn syntax::ext::base::Resolver,
|
|||
NormalTT {
|
||||
expander: Box::new(format::expand_format_args),
|
||||
def_info: None,
|
||||
allow_internal_unstable: true,
|
||||
allow_internal_unstable: Some(vec![
|
||||
Symbol::intern("fmt_internals"),
|
||||
].into()),
|
||||
allow_internal_unsafe: false,
|
||||
local_inner_macros: false,
|
||||
unstable_feature: None,
|
||||
|
@ -114,7 +116,9 @@ pub fn register_builtins(resolver: &mut dyn syntax::ext::base::Resolver,
|
|||
NormalTT {
|
||||
expander: Box::new(format::expand_format_args_nl),
|
||||
def_info: None,
|
||||
allow_internal_unstable: true,
|
||||
allow_internal_unstable: Some(vec![
|
||||
Symbol::intern("fmt_internals"),
|
||||
].into()),
|
||||
allow_internal_unsafe: false,
|
||||
local_inner_macros: false,
|
||||
unstable_feature: None,
|
||||
|
|
|
@ -333,7 +333,10 @@ fn mk_decls(
|
|||
call_site: DUMMY_SP,
|
||||
def_site: None,
|
||||
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,
|
||||
local_inner_macros: false,
|
||||
edition: hygiene::default_edition(),
|
||||
|
|
|
@ -66,7 +66,10 @@ pub fn expand_test_or_bench(
|
|||
call_site: DUMMY_SP,
|
||||
def_site: None,
|
||||
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,
|
||||
local_inner_macros: false,
|
||||
edition: hygiene::default_edition(),
|
||||
|
|
|
@ -41,7 +41,10 @@ pub fn expand(
|
|||
call_site: DUMMY_SP,
|
||||
def_site: None,
|
||||
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,
|
||||
local_inner_macros: false,
|
||||
edition: hygiene::default_edition(),
|
||||
|
|
|
@ -12,6 +12,7 @@ use crate::symbol::{keywords, Symbol};
|
|||
|
||||
use serialize::{Encodable, Decodable, Encoder, Decoder};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use std::{fmt, mem};
|
||||
|
||||
/// A SyntaxContext represents a chain of macro expansions (represented by marks).
|
||||
|
@ -550,10 +551,10 @@ pub struct ExpnInfo {
|
|||
pub def_site: Option<Span>,
|
||||
/// The format with which the macro was invoked.
|
||||
pub format: ExpnFormat,
|
||||
/// Whether the macro is allowed to use #[unstable]/feature-gated
|
||||
/// features internally without forcing the whole crate to opt-in
|
||||
/// List of #[unstable]/feature-gated features that the macro is allowed to use
|
||||
/// internally without forcing the whole crate to opt-in
|
||||
/// to them.
|
||||
pub allow_internal_unstable: bool,
|
||||
pub allow_internal_unstable: Option<Lrc<[Symbol]>>,
|
||||
/// Whether the macro is allowed to use `unsafe` internally
|
||||
/// even if the user crate has `#![forbid(unsafe_code)]`.
|
||||
pub allow_internal_unsafe: bool,
|
||||
|
|
|
@ -386,9 +386,13 @@ impl Span {
|
|||
/// Check if a span is "internal" to a macro in which `#[unstable]`
|
||||
/// items can be used (that is, a macro marked with
|
||||
/// `#[allow_internal_unstable]`).
|
||||
pub fn allows_unstable(&self) -> bool {
|
||||
pub fn allows_unstable(&self, feature: &str) -> bool {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
|
|||
NormalTT {
|
||||
expander: Box::new(Expander { args: args, }),
|
||||
def_info: None,
|
||||
allow_internal_unstable: false,
|
||||
allow_internal_unstable: None,
|
||||
allow_internal_unsafe: false,
|
||||
local_inner_macros: false,
|
||||
unstable_feature: None,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
macro_rules! bar {
|
||||
() => {
|
||||
// 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 {
|
||||
() => {}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error[E0658]: allow_internal_unstable side-steps feature gating and stability checks
|
||||
--> $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!();
|
||||
| ------- in this macro invocation
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// checks that this attribute is caught on non-macro items.
|
||||
// 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;
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error[E0658]: allow_internal_unstable side-steps feature gating and stability checks
|
||||
--> $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
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(unused_macros)]
|
||||
|
||||
#[allow_internal_unstable] //~ ERROR allow_internal_unstable side-steps
|
||||
#[allow_internal_unstable()] //~ ERROR allow_internal_unstable side-steps
|
||||
macro_rules! foo {
|
||||
() => {}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error[E0658]: allow_internal_unstable side-steps feature gating and stability checks
|
||||
--> $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
|
||||
|
||||
|
|
|
@ -23,14 +23,14 @@ pub struct Bar {
|
|||
}
|
||||
|
||||
#[stable(feature = "stable", since = "1.0.0")]
|
||||
#[allow_internal_unstable]
|
||||
#[allow_internal_unstable(function)]
|
||||
#[macro_export]
|
||||
macro_rules! call_unstable_allow {
|
||||
() => { $crate::unstable() }
|
||||
}
|
||||
|
||||
#[stable(feature = "stable", since = "1.0.0")]
|
||||
#[allow_internal_unstable]
|
||||
#[allow_internal_unstable(struct_field)]
|
||||
#[macro_export]
|
||||
macro_rules! construct_unstable_allow {
|
||||
($e: expr) => {
|
||||
|
@ -39,21 +39,21 @@ macro_rules! construct_unstable_allow {
|
|||
}
|
||||
|
||||
#[stable(feature = "stable", since = "1.0.0")]
|
||||
#[allow_internal_unstable]
|
||||
#[allow_internal_unstable(method)]
|
||||
#[macro_export]
|
||||
macro_rules! call_method_allow {
|
||||
($e: expr) => { $e.method() }
|
||||
}
|
||||
|
||||
#[stable(feature = "stable", since = "1.0.0")]
|
||||
#[allow_internal_unstable]
|
||||
#[allow_internal_unstable(struct_field, struct2_field)]
|
||||
#[macro_export]
|
||||
macro_rules! access_field_allow {
|
||||
($e: expr) => { $e.x }
|
||||
}
|
||||
|
||||
#[stable(feature = "stable", since = "1.0.0")]
|
||||
#[allow_internal_unstable]
|
||||
#[allow_internal_unstable()]
|
||||
#[macro_export]
|
||||
macro_rules! pass_through_allow {
|
||||
($e: expr) => { $e }
|
||||
|
|
|
@ -13,7 +13,7 @@ macro_rules! foo {
|
|||
}}
|
||||
}
|
||||
|
||||
#[allow_internal_unstable]
|
||||
#[allow_internal_unstable(function)]
|
||||
macro_rules! bar {
|
||||
($e: expr) => {{
|
||||
foo!($e,
|
||||
|
|
|
@ -58,6 +58,7 @@ const EXCEPTION_PATHS: &[&str] = &[
|
|||
"src/libstd/sys_common/net.rs",
|
||||
"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/libstd/sync/mpsc", // some tests are only run on non-emscripten
|
||||
|
||||
// std testing crates, okay for now at least
|
||||
"src/libcore/tests",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue