about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_interface/src/util.rs5
-rw-r--r--library/std/src/io/impls.rs6
-rw-r--r--library/std/src/io/mod.rs4
-rw-r--r--library/std/src/io/stdio.rs38
-rw-r--r--library/std/src/panicking.rs2
-rw-r--r--library/std/src/thread/mod.rs5
-rw-r--r--library/test/src/helpers/sink.rs7
-rw-r--r--src/test/ui/panic-while-printing.rs19
-rw-r--r--src/test/ui/test-thread-capture.rs31
-rw-r--r--src/test/ui/test-thread-capture.run.stdout21
-rw-r--r--src/test/ui/test-thread-nocapture.rs31
-rw-r--r--src/test/ui/test-thread-nocapture.run.stderr2
-rw-r--r--src/test/ui/test-thread-nocapture.run.stdout20
-rw-r--r--src/test/ui/threads-sendsync/task-stderr.rs5
14 files changed, 184 insertions, 12 deletions
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 7658ffb0e3d..46a6c5861d5 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -115,6 +115,11 @@ impl Write for Sink {
         Ok(())
     }
 }
+impl io::LocalOutput for Sink {
+    fn clone_box(&self) -> Box<dyn io::LocalOutput> {
+        Box::new(Self(self.0.clone()))
+    }
+}
 
 /// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need
 /// for `'static` bounds.
diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs
index e09e7ba978e..66426101d27 100644
--- a/library/std/src/io/impls.rs
+++ b/library/std/src/io/impls.rs
@@ -213,13 +213,13 @@ impl<B: BufRead + ?Sized> BufRead for Box<B> {
 #[cfg(test)]
 /// This impl is only used by printing logic, so any error returned is always
 /// of kind `Other`, and should be ignored.
-impl Write for Box<dyn (::realstd::io::Write) + Send> {
+impl Write for dyn ::realstd::io::LocalOutput {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        (**self).write(buf).map_err(|_| ErrorKind::Other.into())
+        (*self).write(buf).map_err(|_| ErrorKind::Other.into())
     }
 
     fn flush(&mut self) -> io::Result<()> {
-        (**self).flush().map_err(|_| ErrorKind::Other.into())
+        (*self).flush().map_err(|_| ErrorKind::Other.into())
     }
 }
 
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index d9d03807819..e6efe6ec57e 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -277,10 +277,12 @@ pub use self::stdio::{StderrLock, StdinLock, StdoutLock};
 pub use self::stdio::{_eprint, _print};
 #[unstable(feature = "libstd_io_internals", issue = "42788")]
 #[doc(no_inline, hidden)]
-pub use self::stdio::{set_panic, set_print};
+pub use self::stdio::{set_panic, set_print, LocalOutput};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::util::{copy, empty, repeat, sink, Empty, Repeat, Sink};
 
+pub(crate) use self::stdio::clone_io;
+
 mod buffered;
 mod cursor;
 mod error;
diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs
index 36b49401591..2eb5fb45286 100644
--- a/library/std/src/io/stdio.rs
+++ b/library/std/src/io/stdio.rs
@@ -18,14 +18,14 @@ use crate::thread::LocalKey;
 
 thread_local! {
     /// Used by the test crate to capture the output of the print! and println! macros.
-    static LOCAL_STDOUT: RefCell<Option<Box<dyn Write + Send>>> = {
+    static LOCAL_STDOUT: RefCell<Option<Box<dyn LocalOutput>>> = {
         RefCell::new(None)
     }
 }
 
 thread_local! {
     /// Used by the test crate to capture the output of the eprint! and eprintln! macros, and panics.
-    static LOCAL_STDERR: RefCell<Option<Box<dyn Write + Send>>> = {
+    static LOCAL_STDERR: RefCell<Option<Box<dyn LocalOutput>>> = {
         RefCell::new(None)
     }
 }
@@ -888,6 +888,18 @@ impl fmt::Debug for StderrLock<'_> {
     }
 }
 
+/// A writer than can be cloned to new threads.
+#[unstable(
+    feature = "set_stdio",
+    reason = "this trait may disappear completely or be replaced \
+                     with a more general mechanism",
+    issue = "none"
+)]
+#[doc(hidden)]
+pub trait LocalOutput: Write + Send {
+    fn clone_box(&self) -> Box<dyn LocalOutput>;
+}
+
 /// Resets the thread-local stderr handle to the specified writer
 ///
 /// This will replace the current thread's stderr handle, returning the old
@@ -903,7 +915,7 @@ impl fmt::Debug for StderrLock<'_> {
     issue = "none"
 )]
 #[doc(hidden)]
-pub fn set_panic(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write + Send>> {
+pub fn set_panic(sink: Option<Box<dyn LocalOutput>>) -> Option<Box<dyn LocalOutput>> {
     use crate::mem;
     if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) {
         // LOCAL_STDERR is definitely None since LOCAL_STREAMS is false.
@@ -934,7 +946,7 @@ pub fn set_panic(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write +
     issue = "none"
 )]
 #[doc(hidden)]
-pub fn set_print(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write + Send>> {
+pub fn set_print(sink: Option<Box<dyn LocalOutput>>) -> Option<Box<dyn LocalOutput>> {
     use crate::mem;
     if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) {
         // LOCAL_STDOUT is definitely None since LOCAL_STREAMS is false.
@@ -950,6 +962,22 @@ pub fn set_print(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write +
     s
 }
 
+pub(crate) fn clone_io() -> (Option<Box<dyn LocalOutput>>, Option<Box<dyn LocalOutput>>) {
+    // Don't waste time when LOCAL_{STDOUT,STDERR} are definitely None.
+    if !LOCAL_STREAMS.load(Ordering::Relaxed) {
+        return (None, None);
+    }
+
+    LOCAL_STDOUT.with(|stdout| {
+        LOCAL_STDERR.with(|stderr| {
+            (
+                stdout.borrow().as_ref().map(|o| o.clone_box()),
+                stderr.borrow().as_ref().map(|o| o.clone_box()),
+            )
+        })
+    })
+}
+
 /// Write `args` to output stream `local_s` if possible, `global_s`
 /// otherwise. `label` identifies the stream in a panic message.
 ///
@@ -962,7 +990,7 @@ pub fn set_print(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write +
 /// However, if the actual I/O causes an error, this function does panic.
 fn print_to<T>(
     args: fmt::Arguments<'_>,
-    local_s: &'static LocalKey<RefCell<Option<Box<dyn Write + Send>>>>,
+    local_s: &'static LocalKey<RefCell<Option<Box<dyn LocalOutput>>>>,
     global_s: fn() -> T,
     label: &str,
 ) where
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 221ae809e23..fbbc61f4e60 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -220,7 +220,7 @@ fn default_hook(info: &PanicInfo<'_>) {
 
     if let Some(mut local) = set_panic(None) {
         // NB. In `cfg(test)` this uses the forwarding impl
-        // for `Box<dyn (::realstd::io::Write) + Send>`.
+        // for `dyn ::realstd::io::LocalOutput`.
         write(&mut local);
         set_panic(Some(local));
     } else if let Some(mut out) = panic_output() {
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 45c10266ba2..bdb8fc7807b 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -457,11 +457,16 @@ impl Builder {
         let my_packet: Arc<UnsafeCell<Option<Result<T>>>> = Arc::new(UnsafeCell::new(None));
         let their_packet = my_packet.clone();
 
+        let (stdout, stderr) = crate::io::clone_io();
+
         let main = move || {
             if let Some(name) = their_thread.cname() {
                 imp::Thread::set_name(name);
             }
 
+            crate::io::set_print(stdout);
+            crate::io::set_panic(stderr);
+
             // SAFETY: the stack guard passed is the one for the current thread.
             // This means the current thread's stack and the new thread's stack
             // are properly set and protected from each other.
diff --git a/library/test/src/helpers/sink.rs b/library/test/src/helpers/sink.rs
index aa7fe248773..dfbf0a3b72f 100644
--- a/library/test/src/helpers/sink.rs
+++ b/library/test/src/helpers/sink.rs
@@ -6,6 +6,7 @@ use std::{
     sync::{Arc, Mutex},
 };
 
+#[derive(Clone)]
 pub struct Sink(Arc<Mutex<Vec<u8>>>);
 
 impl Sink {
@@ -14,6 +15,12 @@ impl Sink {
     }
 }
 
+impl io::LocalOutput for Sink {
+    fn clone_box(&self) -> Box<dyn io::LocalOutput> {
+        Box::new(self.clone())
+    }
+}
+
 impl Write for Sink {
     fn write(&mut self, data: &[u8]) -> io::Result<usize> {
         Write::write(&mut *self.0.lock().unwrap(), data)
diff --git a/src/test/ui/panic-while-printing.rs b/src/test/ui/panic-while-printing.rs
index 7e9fa16b084..21fc12759f8 100644
--- a/src/test/ui/panic-while-printing.rs
+++ b/src/test/ui/panic-while-printing.rs
@@ -5,7 +5,7 @@
 
 use std::fmt;
 use std::fmt::{Display, Formatter};
-use std::io::set_panic;
+use std::io::{self, set_panic, LocalOutput, Write};
 
 pub struct A;
 
@@ -15,8 +15,23 @@ impl Display for A {
     }
 }
 
+struct Sink;
+impl Write for Sink {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        Ok(buf.len())
+    }
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+impl LocalOutput for Sink {
+    fn clone_box(&self) -> Box<dyn LocalOutput> {
+        Box::new(Sink)
+    }
+}
+
 fn main() {
-    set_panic(Some(Box::new(Vec::new())));
+    set_panic(Some(Box::new(Sink)));
     assert!(std::panic::catch_unwind(|| {
         eprintln!("{}", A);
     })
diff --git a/src/test/ui/test-thread-capture.rs b/src/test/ui/test-thread-capture.rs
new file mode 100644
index 00000000000..6bec48cd816
--- /dev/null
+++ b/src/test/ui/test-thread-capture.rs
@@ -0,0 +1,31 @@
+// compile-flags: --test
+// run-fail
+// run-flags: --test-threads=1
+// check-run-results
+// exec-env:RUST_BACKTRACE=0
+// ignore-emscripten no threads support
+
+#[test]
+fn thready_pass() {
+    println!("fee");
+    std::thread::spawn(|| {
+        println!("fie");
+        println!("foe");
+    })
+    .join()
+    .unwrap();
+    println!("fum");
+}
+
+#[test]
+fn thready_fail() {
+    println!("fee");
+    std::thread::spawn(|| {
+        println!("fie");
+        println!("foe");
+    })
+    .join()
+    .unwrap();
+    println!("fum");
+    panic!();
+}
diff --git a/src/test/ui/test-thread-capture.run.stdout b/src/test/ui/test-thread-capture.run.stdout
new file mode 100644
index 00000000000..1102aadab02
--- /dev/null
+++ b/src/test/ui/test-thread-capture.run.stdout
@@ -0,0 +1,21 @@
+
+running 2 tests
+test thready_fail ... FAILED
+test thready_pass ... ok
+
+failures:
+
+---- thready_fail stdout ----
+fee
+fie
+foe
+fum
+thread 'main' panicked at 'explicit panic', $DIR/test-thread-capture.rs:30:5
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+
+
+failures:
+    thready_fail
+
+test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
+
diff --git a/src/test/ui/test-thread-nocapture.rs b/src/test/ui/test-thread-nocapture.rs
new file mode 100644
index 00000000000..82df6e77cb1
--- /dev/null
+++ b/src/test/ui/test-thread-nocapture.rs
@@ -0,0 +1,31 @@
+// compile-flags: --test
+// run-fail
+// run-flags: --test-threads=1 --nocapture
+// check-run-results
+// exec-env:RUST_BACKTRACE=0
+// ignore-emscripten no threads support
+
+#[test]
+fn thready_pass() {
+    println!("fee");
+    std::thread::spawn(|| {
+        println!("fie");
+        println!("foe");
+    })
+    .join()
+    .unwrap();
+    println!("fum");
+}
+
+#[test]
+fn thready_fail() {
+    println!("fee");
+    std::thread::spawn(|| {
+        println!("fie");
+        println!("foe");
+    })
+    .join()
+    .unwrap();
+    println!("fum");
+    panic!();
+}
diff --git a/src/test/ui/test-thread-nocapture.run.stderr b/src/test/ui/test-thread-nocapture.run.stderr
new file mode 100644
index 00000000000..98bd96d0abe
--- /dev/null
+++ b/src/test/ui/test-thread-nocapture.run.stderr
@@ -0,0 +1,2 @@
+thread 'main' panicked at 'explicit panic', $DIR/test-thread-nocapture.rs:30:5
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
diff --git a/src/test/ui/test-thread-nocapture.run.stdout b/src/test/ui/test-thread-nocapture.run.stdout
new file mode 100644
index 00000000000..77b42ed88d6
--- /dev/null
+++ b/src/test/ui/test-thread-nocapture.run.stdout
@@ -0,0 +1,20 @@
+
+running 2 tests
+test thready_fail ... fee
+fie
+foe
+fum
+FAILED
+test thready_pass ... fee
+fie
+foe
+fum
+ok
+
+failures:
+
+failures:
+    thready_fail
+
+test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
+
diff --git a/src/test/ui/threads-sendsync/task-stderr.rs b/src/test/ui/threads-sendsync/task-stderr.rs
index d474084bf20..bc4bedac196 100644
--- a/src/test/ui/threads-sendsync/task-stderr.rs
+++ b/src/test/ui/threads-sendsync/task-stderr.rs
@@ -16,6 +16,11 @@ impl Write for Sink {
     }
     fn flush(&mut self) -> io::Result<()> { Ok(()) }
 }
+impl io::LocalOutput for Sink {
+    fn clone_box(&self) -> Box<dyn io::LocalOutput> {
+        Box::new(Sink(self.0.clone()))
+    }
+}
 
 fn main() {
     let data = Arc::new(Mutex::new(Vec::new()));