diff --git a/src/liballoc/macros.rs b/src/liballoc/macros.rs index db91b07fa71b4..7ae57a8dc7984 100644 --- a/src/liballoc/macros.rs +++ b/src/liballoc/macros.rs @@ -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) diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index 664490c1997ef..6b5fe84ff61df 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -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") diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 3de41b1665d6d..43c756def88be 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -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>, + ) -> 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 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()` let discr = { diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 34c77d08f5a7e..5d606abb3cd79 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -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, 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, @@ -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; } diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index d302e7646d168..758a0d63886b1 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -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(), diff --git a/src/librustc_data_structures/macros.rs b/src/librustc_data_structures/macros.rs index a661f0c78ab5d..af1f2910461ec 100644 --- a/src/librustc_data_structures/macros.rs +++ b/src/librustc_data_structures/macros.rs @@ -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 diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 0b4c8a5367c15..90b0fb249aa61 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -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, }) } diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index b248c6bf6565a..f49b88f14e60e 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -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)); diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index b116c327f5bab..c553398b9b1c5 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -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 { diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs index b53d956a9c032..2da520d396948 100644 --- a/src/librustc_plugin/registry.rs +++ b/src/librustc_plugin/registry.rs @@ -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, diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index b87257188df10..506b6d4e8e0a8 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -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) => { { diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index 446c164965d6f..a8843549b8abc 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -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; diff --git a/src/libstd/sync/mpsc/select.rs b/src/libstd/sync/mpsc/select.rs index 8f41680a818b9..e34fc5487cf90 100644 --- a/src/libstd/sync/mpsc/select.rs +++ b/src/libstd/sync/mpsc/select.rs @@ -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::(); - let (tx2, rx2) = channel::(); - 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::(); - let (_tx2, rx2) = channel::(); - let (_tx3, rx3) = channel::(); - let (_tx4, rx4) = channel::(); - let (tx5, rx5) = channel::(); - 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::(); - let (tx2, rx2) = channel::(); - drop(tx2); - - select! { - _a1 = rx1.recv() => { panic!() }, - a2 = rx2.recv() => { assert!(a2.is_err()); } - } - } - - #[test] - fn unblocks() { - let (tx1, rx1) = channel::(); - let (_tx2, rx2) = channel::(); - let (tx3, rx3) = channel::(); - - 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::(); - let (tx2, rx2) = channel::(); - 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::(); - let (tx2, rx2) = channel::(); - 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::(); - let (_tx2, rx2) = channel::(); - 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::(); - let (_tx2, rx2) = channel::(); - 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::(1); - tx.send(1).unwrap(); - select! { - n = rx.recv() => { assert_eq!(n.unwrap(), 1); } - } - } - - #[test] - fn sync2() { - let (tx, rx) = sync_channel::(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::(0); - let (tx2, rx2): (Sender, Receiver) = 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); - } - } - } -} diff --git a/src/libstd/sync/mpsc/select_tests.rs b/src/libstd/sync/mpsc/select_tests.rs new file mode 100644 index 0000000000000..be048511caaec --- /dev/null +++ b/src/libstd/sync/mpsc/select_tests.rs @@ -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::(); + let (tx2, rx2) = channel::(); + 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::(); + let (_tx2, rx2) = channel::(); + let (_tx3, rx3) = channel::(); + let (_tx4, rx4) = channel::(); + let (tx5, rx5) = channel::(); + 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::(); + let (tx2, rx2) = channel::(); + drop(tx2); + + select! { + _a1 = rx1.recv() => { panic!() }, + a2 = rx2.recv() => { assert!(a2.is_err()); } + } +} + +#[test] +fn unblocks() { + let (tx1, rx1) = channel::(); + let (_tx2, rx2) = channel::(); + let (tx3, rx3) = channel::(); + + 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::(); + let (tx2, rx2) = channel::(); + 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::(); + let (tx2, rx2) = channel::(); + 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::(); + let (_tx2, rx2) = channel::(); + 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::(); + let (_tx2, rx2) = channel::(); + 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::(1); + tx.send(1).unwrap(); + select! { + n = rx.recv() => { assert_eq!(n.unwrap(), 1); } + } +} + +#[test] +fn sync2() { + let (tx, rx) = sync_channel::(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::(0); + let (tx2, rx2): (Sender, Receiver) = 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); + } + } +} diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 5d2eb5f8e7320..8207709e1f9f0 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -126,7 +126,8 @@ impl fmt::Debug for LocalKey { /// [`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) => { diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 465b53184dcd7..f7225810aca74 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -621,7 +621,8 @@ pub enum SyntaxExtension { /// A function-like procedural macro. TokenStream -> TokenStream. ProcMacro { expander: Box, - allow_internal_unstable: bool, + /// Whitelist of unstable features that are treated as stable inside this macro + allow_internal_unstable: Option>, edition: Edition, }, @@ -638,8 +639,10 @@ pub enum SyntaxExtension { expander: Box, 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>, /// 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, Option, bool), + IdentTT { + expander: Box, + span: Option, + allow_internal_unstable: Option>, + }, /// 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(), diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index 50cec9e7908c1..6df369133d01d 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -58,7 +58,10 @@ pub fn add_derived_markers(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(), diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 89d59478a5df1..60359531b7f63 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -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(); diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index 31a134b856d82..549de1628eb55 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -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 { - 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"); diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 33ea675f9d1bb..cc5531c401046 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -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::>().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") { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 0853b4399d2c1..4d24c7bfb4038 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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); - } + // check for gated attributes + self.context.check_attribute(attr, false); if attr.check_name("doc") { if let Some(content) = attr.meta_item_list() { diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 5b904fa86ad09..b9758bd655c15 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -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(), diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 703c4f2db347b..f45bf034ba2f8 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -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(), diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index 2c8a996cdb0cb..fff54814a38c4 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -136,11 +136,16 @@ fn call_intrinsic(cx: &ExtCtxt<'_>, intrinsic: &str, args: Vec>) -> P { - 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)); diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index 6bb7ee1d5ddfd..1b17fc0d040a5 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -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]` { diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 7d7fd03085912..05c8084a51ed2 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -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, diff --git a/src/libsyntax_ext/proc_macro_decls.rs b/src/libsyntax_ext/proc_macro_decls.rs index 24d095145201e..5730081ce018a 100644 --- a/src/libsyntax_ext/proc_macro_decls.rs +++ b/src/libsyntax_ext/proc_macro_decls.rs @@ -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(), diff --git a/src/libsyntax_ext/test.rs b/src/libsyntax_ext/test.rs index 832bebb6113e9..371862465487b 100644 --- a/src/libsyntax_ext/test.rs +++ b/src/libsyntax_ext/test.rs @@ -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(), diff --git a/src/libsyntax_ext/test_case.rs b/src/libsyntax_ext/test_case.rs index 63417b702d569..1ed1ab0a07b96 100644 --- a/src/libsyntax_ext/test_case.rs +++ b/src/libsyntax_ext/test_case.rs @@ -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(), diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 0c645fc678caf..d5c0a2ca85f64 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -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, /// 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>, /// Whether the macro is allowed to use `unsafe` internally /// even if the user crate has `#![forbid(unsafe_code)]`. pub allow_internal_unsafe: bool, diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index dbb4f8f8159dc..042005ea53889 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -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, } } diff --git a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs index eab612c5dfb45..309acb25184a8 100644 --- a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs +++ b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs @@ -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, diff --git a/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-nested-macro.rs b/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-nested-macro.rs index cf320b2747c36..ee48f9516299a 100644 --- a/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-nested-macro.rs +++ b/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-nested-macro.rs @@ -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 { () => {} } diff --git a/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-nested-macro.stderr b/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-nested-macro.stderr index 7cdf743b279c2..802c74239d719 100644 --- a/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-nested-macro.stderr +++ b/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-nested-macro.stderr @@ -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 diff --git a/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-struct.rs b/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-struct.rs index c9ea6c338b5cc..ede969097d5ae 100644 --- a/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-struct.rs +++ b/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-struct.rs @@ -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() {} diff --git a/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-struct.stderr b/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-struct.stderr index 485c00ad42bd6..d619f1e3239ca 100644 --- a/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-struct.stderr +++ b/src/test/ui/feature-gates/feature-gate-allow-internal-unstable-struct.stderr @@ -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 diff --git a/src/test/ui/feature-gates/feature-gate-allow-internal-unstable.rs b/src/test/ui/feature-gates/feature-gate-allow-internal-unstable.rs index bea60fc012e3f..0a1b6acd9bff2 100644 --- a/src/test/ui/feature-gates/feature-gate-allow-internal-unstable.rs +++ b/src/test/ui/feature-gates/feature-gate-allow-internal-unstable.rs @@ -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 { () => {} } diff --git a/src/test/ui/feature-gates/feature-gate-allow-internal-unstable.stderr b/src/test/ui/feature-gates/feature-gate-allow-internal-unstable.stderr index 0533d071de3ac..aa4f6648c4fbd 100644 --- a/src/test/ui/feature-gates/feature-gate-allow-internal-unstable.stderr +++ b/src/test/ui/feature-gates/feature-gate-allow-internal-unstable.stderr @@ -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 diff --git a/src/test/ui/internal/auxiliary/internal_unstable.rs b/src/test/ui/internal/auxiliary/internal_unstable.rs index 7cf54c3e6a69f..7c79dcb752272 100644 --- a/src/test/ui/internal/auxiliary/internal_unstable.rs +++ b/src/test/ui/internal/auxiliary/internal_unstable.rs @@ -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 } diff --git a/src/test/ui/internal/internal-unstable.rs b/src/test/ui/internal/internal-unstable.rs index 34fef33bfeb97..e09a5d89172e8 100644 --- a/src/test/ui/internal/internal-unstable.rs +++ b/src/test/ui/internal/internal-unstable.rs @@ -13,7 +13,7 @@ macro_rules! foo { }} } -#[allow_internal_unstable] +#[allow_internal_unstable(function)] macro_rules! bar { ($e: expr) => {{ foo!($e, diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 0f722945c49e6..ed2218f09d26b 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -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",