about summary refs log tree commit diff
path: root/library
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-10-02 15:31:06 +0000
committerbors <bors@rust-lang.org>2022-10-02 15:31:06 +0000
commit39323a5877ee6b85e75c652c2518a97a1433a2dc (patch)
tree27348d8ceb2103ee0e7332e0385233435ad1ff7f /library
parent91931ec2fcb67a8e61080a97345c828a875c86ec (diff)
parent0b2596723bade820e3d5ba58d3e142dedeff566d (diff)
downloadrust-39323a5877ee6b85e75c652c2518a97a1433a2dc.tar.gz
rust-39323a5877ee6b85e75c652c2518a97a1433a2dc.zip
Auto merge of #102586 - Dylan-DPC:rollup-g107h6z, r=Dylan-DPC
Rollup of 5 pull requests

Successful merges:

 - #100451 (Do not panic when a test function returns Result::Err.)
 - #102098 (Use fetch_update in sync::Weak::upgrade)
 - #102538 (Give `def_span` the same SyntaxContext as `span_with_body`.)
 - #102556 (Make `feature(const_btree_len)` implied by `feature(const_btree_new)`)
 - #102566 (Add a known-bug test for #102498)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'library')
-rw-r--r--library/alloc/src/collections/btree/map.rs12
-rw-r--r--library/alloc/src/collections/btree/set.rs12
-rw-r--r--library/alloc/src/sync.rs39
-rw-r--r--library/test/src/bench.rs21
-rw-r--r--library/test/src/lib.rs61
-rw-r--r--library/test/src/tests.rs90
-rw-r--r--library/test/src/types.rs15
7 files changed, 165 insertions, 85 deletions
diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs
index 3018d1c9125..3687f84b1bd 100644
--- a/library/alloc/src/collections/btree/map.rs
+++ b/library/alloc/src/collections/btree/map.rs
@@ -2392,7 +2392,11 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
     /// ```
     #[must_use]
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[rustc_const_unstable(feature = "const_btree_len", issue = "71835")]
+    #[rustc_const_unstable(
+        feature = "const_btree_len",
+        issue = "71835",
+        implied_by = "const_btree_new"
+    )]
     pub const fn len(&self) -> usize {
         self.length
     }
@@ -2413,7 +2417,11 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
     /// ```
     #[must_use]
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[rustc_const_unstable(feature = "const_btree_len", issue = "71835")]
+    #[rustc_const_unstable(
+        feature = "const_btree_len",
+        issue = "71835",
+        implied_by = "const_btree_new"
+    )]
     pub const fn is_empty(&self) -> bool {
         self.len() == 0
     }
diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs
index 3caaf521240..5783d836e10 100644
--- a/library/alloc/src/collections/btree/set.rs
+++ b/library/alloc/src/collections/btree/set.rs
@@ -1174,7 +1174,11 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
     /// ```
     #[must_use]
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[rustc_const_unstable(feature = "const_btree_len", issue = "71835")]
+    #[rustc_const_unstable(
+        feature = "const_btree_len",
+        issue = "71835",
+        implied_by = "const_btree_new"
+    )]
     pub const fn len(&self) -> usize {
         self.map.len()
     }
@@ -1193,7 +1197,11 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
     /// ```
     #[must_use]
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[rustc_const_unstable(feature = "const_btree_len", issue = "71835")]
+    #[rustc_const_unstable(
+        feature = "const_btree_len",
+        issue = "71835",
+        implied_by = "const_btree_new"
+    )]
     pub const fn is_empty(&self) -> bool {
         self.len() == 0
     }
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index a5322953d49..d0e5b6f4d82 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -1980,33 +1980,26 @@ impl<T: ?Sized> Weak<T> {
         // We use a CAS loop to increment the strong count instead of a
         // fetch_add as this function should never take the reference count
         // from zero to one.
-        let inner = self.inner()?;
-
-        // Relaxed load because any write of 0 that we can observe
-        // leaves the field in a permanently zero state (so a
-        // "stale" read of 0 is fine), and any other value is
-        // confirmed via the CAS below.
-        let mut n = inner.strong.load(Relaxed);
-
-        loop {
-            if n == 0 {
-                return None;
-            }
-
-            // See comments in `Arc::clone` for why we do this (for `mem::forget`).
-            if n > MAX_REFCOUNT {
-                abort();
-            }
-
+        self.inner()?
+            .strong
             // Relaxed is fine for the failure case because we don't have any expectations about the new state.
             // Acquire is necessary for the success case to synchronise with `Arc::new_cyclic`, when the inner
             // value can be initialized after `Weak` references have already been created. In that case, we
             // expect to observe the fully initialized value.
-            match inner.strong.compare_exchange_weak(n, n + 1, Acquire, Relaxed) {
-                Ok(_) => return Some(unsafe { Arc::from_inner(self.ptr) }), // null checked above
-                Err(old) => n = old,
-            }
-        }
+            .fetch_update(Acquire, Relaxed, |n| {
+                // Any write of 0 we can observe leaves the field in permanently zero state.
+                if n == 0 {
+                    return None;
+                }
+                // See comments in `Arc::clone` for why we do this (for `mem::forget`).
+                if n > MAX_REFCOUNT {
+                    abort();
+                }
+                Some(n + 1)
+            })
+            .ok()
+            // null checked above
+            .map(|_| unsafe { Arc::from_inner(self.ptr) })
     }
 
     /// Gets the number of strong (`Arc`) pointers pointing to this allocation.
diff --git a/library/test/src/bench.rs b/library/test/src/bench.rs
index 7869ba2c041..23925e6ea72 100644
--- a/library/test/src/bench.rs
+++ b/library/test/src/bench.rs
@@ -49,12 +49,12 @@ impl Bencher {
         self.summary = Some(iter(&mut inner));
     }
 
-    pub fn bench<F>(&mut self, mut f: F) -> Option<stats::Summary>
+    pub fn bench<F>(&mut self, mut f: F) -> Result<Option<stats::Summary>, String>
     where
-        F: FnMut(&mut Bencher),
+        F: FnMut(&mut Bencher) -> Result<(), String>,
     {
-        f(self);
-        self.summary
+        let result = f(self);
+        result.map(|_| self.summary)
     }
 }
 
@@ -195,7 +195,7 @@ pub fn benchmark<F>(
     nocapture: bool,
     f: F,
 ) where
-    F: FnMut(&mut Bencher),
+    F: FnMut(&mut Bencher) -> Result<(), String>,
 {
     let mut bs = Bencher { mode: BenchMode::Auto, summary: None, bytes: 0 };
 
@@ -211,14 +211,14 @@ pub fn benchmark<F>(
 
     let test_result = match result {
         //bs.bench(f) {
-        Ok(Some(ns_iter_summ)) => {
+        Ok(Ok(Some(ns_iter_summ))) => {
             let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
             let mb_s = bs.bytes * 1000 / ns_iter;
 
             let bs = BenchSamples { ns_iter_summ, mb_s: mb_s as usize };
             TestResult::TrBench(bs)
         }
-        Ok(None) => {
+        Ok(Ok(None)) => {
             // iter not called, so no data.
             // FIXME: error in this case?
             let samples: &mut [f64] = &mut [0.0_f64; 1];
@@ -226,6 +226,7 @@ pub fn benchmark<F>(
             TestResult::TrBench(bs)
         }
         Err(_) => TestResult::TrFailed,
+        Ok(Err(_)) => TestResult::TrFailed,
     };
 
     let stdout = data.lock().unwrap().to_vec();
@@ -233,10 +234,10 @@ pub fn benchmark<F>(
     monitor_ch.send(message).unwrap();
 }
 
-pub fn run_once<F>(f: F)
+pub fn run_once<F>(f: F) -> Result<(), String>
 where
-    F: FnMut(&mut Bencher),
+    F: FnMut(&mut Bencher) -> Result<(), String>,
 {
     let mut bs = Bencher { mode: BenchMode::Single, summary: None, bytes: 0 };
-    bs.bench(f);
+    bs.bench(f).map(|_| ())
 }
diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs
index 33c6ea58532..c30257fc792 100644
--- a/library/test/src/lib.rs
+++ b/library/test/src/lib.rs
@@ -6,7 +6,8 @@
 //! benchmarks themselves) should be done via the `#[test]` and
 //! `#[bench]` attributes.
 //!
-//! See the [Testing Chapter](../book/ch11-00-testing.html) of the book for more details.
+//! See the [Testing Chapter](../book/ch11-00-testing.html) of the book for more
+//! details.
 
 // Currently, not much of this is meant for users. It is intended to
 // support the simplest interface possible for representing and
@@ -76,6 +77,7 @@ mod types;
 #[cfg(test)]
 mod tests;
 
+use core::any::Any;
 use event::{CompletedTest, TestEvent};
 use helpers::concurrency::get_concurrency;
 use helpers::exit_code::get_exit_code;
@@ -175,17 +177,20 @@ fn make_owned_test(test: &&TestDescAndFn) -> TestDescAndFn {
     }
 }
 
-/// Invoked when unit tests terminate. Should panic if the unit
-/// Tests is considered a failure. By default, invokes `report()`
-/// and checks for a `0` result.
-pub fn assert_test_result<T: Termination>(result: T) {
+/// Invoked when unit tests terminate. Returns `Result::Err` if the test is
+/// considered a failure. By default, invokes `report() and checks for a `0`
+/// result.
+pub fn assert_test_result<T: Termination>(result: T) -> Result<(), String> {
     let code = result.report().to_i32();
-    assert_eq!(
-        code, 0,
-        "the test returned a termination value with a non-zero status code ({}) \
-         which indicates a failure",
-        code
-    );
+    if code == 0 {
+        Ok(())
+    } else {
+        Err(format!(
+            "the test returned a termination value with a non-zero status code \
+             ({}) which indicates a failure",
+            code
+        ))
+    }
 }
 
 pub fn run_tests<F>(
@@ -478,7 +483,7 @@ pub fn run_test(
         id: TestId,
         desc: TestDesc,
         monitor_ch: Sender<CompletedTest>,
-        testfn: Box<dyn FnOnce() + Send>,
+        testfn: Box<dyn FnOnce() -> Result<(), String> + Send>,
         opts: TestRunOpts,
     ) -> Option<thread::JoinHandle<()>> {
         let concurrency = opts.concurrency;
@@ -567,11 +572,11 @@ pub fn run_test(
 
 /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
 #[inline(never)]
-fn __rust_begin_short_backtrace<F: FnOnce()>(f: F) {
-    f();
+fn __rust_begin_short_backtrace<T, F: FnOnce() -> T>(f: F) -> T {
+    let result = f();
 
     // prevent this frame from being tail-call optimised away
-    black_box(());
+    black_box(result)
 }
 
 fn run_test_in_process(
@@ -579,7 +584,7 @@ fn run_test_in_process(
     desc: TestDesc,
     nocapture: bool,
     report_time: bool,
-    testfn: Box<dyn FnOnce() + Send>,
+    testfn: Box<dyn FnOnce() -> Result<(), String> + Send>,
     monitor_ch: Sender<CompletedTest>,
     time_opts: Option<time::TestTimeOptions>,
 ) {
@@ -591,7 +596,7 @@ fn run_test_in_process(
     }
 
     let start = report_time.then(Instant::now);
-    let result = catch_unwind(AssertUnwindSafe(testfn));
+    let result = fold_err(catch_unwind(AssertUnwindSafe(testfn)));
     let exec_time = start.map(|start| {
         let duration = start.elapsed();
         TestExecTime(duration)
@@ -608,6 +613,19 @@ fn run_test_in_process(
     monitor_ch.send(message).unwrap();
 }
 
+fn fold_err<T, E>(
+    result: Result<Result<T, E>, Box<dyn Any + Send>>,
+) -> Result<T, Box<dyn Any + Send>>
+where
+    E: Send + 'static,
+{
+    match result {
+        Ok(Err(e)) => Err(Box::new(e)),
+        Ok(Ok(v)) => Ok(v),
+        Err(e) => Err(e),
+    }
+}
+
 fn spawn_test_subprocess(
     id: TestId,
     desc: TestDesc,
@@ -663,7 +681,10 @@ fn spawn_test_subprocess(
     monitor_ch.send(message).unwrap();
 }
 
-fn run_test_in_spawned_subprocess(desc: TestDesc, testfn: Box<dyn FnOnce() + Send>) -> ! {
+fn run_test_in_spawned_subprocess(
+    desc: TestDesc,
+    testfn: Box<dyn FnOnce() -> Result<(), String> + Send>,
+) -> ! {
     let builtin_panic_hook = panic::take_hook();
     let record_result = Arc::new(move |panic_info: Option<&'_ PanicInfo<'_>>| {
         let test_result = match panic_info {
@@ -689,7 +710,9 @@ fn run_test_in_spawned_subprocess(desc: TestDesc, testfn: Box<dyn FnOnce() + Sen
     });
     let record_result2 = record_result.clone();
     panic::set_hook(Box::new(move |info| record_result2(Some(&info))));
-    testfn();
+    if let Err(message) = testfn() {
+        panic!("{}", message);
+    }
     record_result(None);
     unreachable!("panic=abort callback should have exited the process")
 }
diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs
index 0b81aff5907..278cfb15bb1 100644
--- a/library/test/src/tests.rs
+++ b/library/test/src/tests.rs
@@ -67,7 +67,7 @@ fn one_ignored_one_unignored_test() -> Vec<TestDescAndFn> {
                 no_run: false,
                 test_type: TestType::Unknown,
             },
-            testfn: DynTestFn(Box::new(move || {})),
+            testfn: DynTestFn(Box::new(move || Ok(()))),
         },
         TestDescAndFn {
             desc: TestDesc {
@@ -79,14 +79,14 @@ fn one_ignored_one_unignored_test() -> Vec<TestDescAndFn> {
                 no_run: false,
                 test_type: TestType::Unknown,
             },
-            testfn: DynTestFn(Box::new(move || {})),
+            testfn: DynTestFn(Box::new(move || Ok(()))),
         },
     ]
 }
 
 #[test]
 pub fn do_not_run_ignored_tests() {
-    fn f() {
+    fn f() -> Result<(), String> {
         panic!();
     }
     let desc = TestDescAndFn {
@@ -109,7 +109,9 @@ pub fn do_not_run_ignored_tests() {
 
 #[test]
 pub fn ignored_tests_result_in_ignored() {
-    fn f() {}
+    fn f() -> Result<(), String> {
+        Ok(())
+    }
     let desc = TestDescAndFn {
         desc: TestDesc {
             name: StaticTestName("whatever"),
@@ -132,7 +134,7 @@ pub fn ignored_tests_result_in_ignored() {
 #[test]
 #[cfg(not(target_os = "emscripten"))]
 fn test_should_panic() {
-    fn f() {
+    fn f() -> Result<(), String> {
         panic!();
     }
     let desc = TestDescAndFn {
@@ -157,7 +159,7 @@ fn test_should_panic() {
 #[test]
 #[cfg(not(target_os = "emscripten"))]
 fn test_should_panic_good_message() {
-    fn f() {
+    fn f() -> Result<(), String> {
         panic!("an error message");
     }
     let desc = TestDescAndFn {
@@ -183,7 +185,7 @@ fn test_should_panic_good_message() {
 #[cfg(not(target_os = "emscripten"))]
 fn test_should_panic_bad_message() {
     use crate::tests::TrFailedMsg;
-    fn f() {
+    fn f() -> Result<(), String> {
         panic!("an error message");
     }
     let expected = "foobar";
@@ -214,7 +216,7 @@ fn test_should_panic_bad_message() {
 fn test_should_panic_non_string_message_type() {
     use crate::tests::TrFailedMsg;
     use std::any::TypeId;
-    fn f() {
+    fn f() -> Result<(), String> {
         std::panic::panic_any(1i32);
     }
     let expected = "foobar";
@@ -249,7 +251,9 @@ fn test_should_panic_but_succeeds() {
     let should_panic_variants = [ShouldPanic::Yes, ShouldPanic::YesWithMessage("error message")];
 
     for &should_panic in should_panic_variants.iter() {
-        fn f() {}
+        fn f() -> Result<(), String> {
+            Ok(())
+        }
         let desc = TestDescAndFn {
             desc: TestDesc {
                 name: StaticTestName("whatever"),
@@ -283,7 +287,9 @@ fn test_should_panic_but_succeeds() {
 }
 
 fn report_time_test_template(report_time: bool) -> Option<TestExecTime> {
-    fn f() {}
+    fn f() -> Result<(), String> {
+        Ok(())
+    }
     let desc = TestDescAndFn {
         desc: TestDesc {
             name: StaticTestName("whatever"),
@@ -318,7 +324,9 @@ fn test_should_report_time() {
 }
 
 fn time_test_failure_template(test_type: TestType) -> TestResult {
-    fn f() {}
+    fn f() -> Result<(), String> {
+        Ok(())
+    }
     let desc = TestDescAndFn {
         desc: TestDesc {
             name: StaticTestName("whatever"),
@@ -480,7 +488,7 @@ pub fn exclude_should_panic_option() {
             no_run: false,
             test_type: TestType::Unknown,
         },
-        testfn: DynTestFn(Box::new(move || {})),
+        testfn: DynTestFn(Box::new(move || Ok(()))),
     });
 
     let filtered = filter_tests(&opts, tests);
@@ -504,7 +512,7 @@ pub fn exact_filter_match() {
                     no_run: false,
                     test_type: TestType::Unknown,
                 },
-                testfn: DynTestFn(Box::new(move || {})),
+                testfn: DynTestFn(Box::new(move || Ok(()))),
             })
             .collect()
     }
@@ -580,7 +588,9 @@ fn sample_tests() -> Vec<TestDescAndFn> {
         "test::run_include_ignored_option".to_string(),
         "test::sort_tests".to_string(),
     ];
-    fn testfn() {}
+    fn testfn() -> Result<(), String> {
+        Ok(())
+    }
     let mut tests = Vec::new();
     for name in &names {
         let test = TestDescAndFn {
@@ -717,21 +727,26 @@ pub fn test_metricmap_compare() {
 
 #[test]
 pub fn test_bench_once_no_iter() {
-    fn f(_: &mut Bencher) {}
-    bench::run_once(f);
+    fn f(_: &mut Bencher) -> Result<(), String> {
+        Ok(())
+    }
+    bench::run_once(f).unwrap();
 }
 
 #[test]
 pub fn test_bench_once_iter() {
-    fn f(b: &mut Bencher) {
-        b.iter(|| {})
+    fn f(b: &mut Bencher) -> Result<(), String> {
+        b.iter(|| {});
+        Ok(())
     }
-    bench::run_once(f);
+    bench::run_once(f).unwrap();
 }
 
 #[test]
 pub fn test_bench_no_iter() {
-    fn f(_: &mut Bencher) {}
+    fn f(_: &mut Bencher) -> Result<(), String> {
+        Ok(())
+    }
 
     let (tx, rx) = channel();
 
@@ -751,8 +766,9 @@ pub fn test_bench_no_iter() {
 
 #[test]
 pub fn test_bench_iter() {
-    fn f(b: &mut Bencher) {
-        b.iter(|| {})
+    fn f(b: &mut Bencher) -> Result<(), String> {
+        b.iter(|| {});
+        Ok(())
     }
 
     let (tx, rx) = channel();
@@ -821,3 +837,33 @@ fn should_sort_failures_before_printing_them() {
     let bpos = s.find("b").unwrap();
     assert!(apos < bpos);
 }
+
+#[test]
+#[cfg(not(target_os = "emscripten"))]
+fn test_dyn_bench_returning_err_fails_when_run_as_test() {
+    fn f(_: &mut Bencher) -> Result<(), String> {
+        Result::Err("An error".into())
+    }
+    let desc = TestDescAndFn {
+        desc: TestDesc {
+            name: StaticTestName("whatever"),
+            ignore: false,
+            ignore_message: None,
+            should_panic: ShouldPanic::No,
+            compile_fail: false,
+            no_run: false,
+            test_type: TestType::Unknown,
+        },
+        testfn: DynBenchFn(Box::new(f)),
+    };
+    let (tx, rx) = channel();
+    let notify = move |event: TestEvent| {
+        if let TestEvent::TeResult(result) = event {
+            tx.send(result).unwrap();
+        }
+        Ok(())
+    };
+    run_tests(&TestOpts { run_tests: true, ..TestOpts::new() }, vec![desc], notify).unwrap();
+    let result = rx.recv().unwrap().result;
+    assert_eq!(result, TrFailed);
+}
diff --git a/library/test/src/types.rs b/library/test/src/types.rs
index ffb1efe18cc..888afff7921 100644
--- a/library/test/src/types.rs
+++ b/library/test/src/types.rs
@@ -75,14 +75,15 @@ impl fmt::Display for TestName {
 }
 
 // A function that runs a test. If the function returns successfully,
-// the test succeeds; if the function panics then the test fails. We
-// may need to come up with a more clever definition of test in order
-// to support isolation of tests into threads.
+// the test succeeds; if the function panics or returns Result::Err
+// then the test fails. We may need to come up with a more clever
+// definition of test in order to support isolation of tests into
+// threads.
 pub enum TestFn {
-    StaticTestFn(fn()),
-    StaticBenchFn(fn(&mut Bencher)),
-    DynTestFn(Box<dyn FnOnce() + Send>),
-    DynBenchFn(Box<dyn Fn(&mut Bencher) + Send>),
+    StaticTestFn(fn() -> Result<(), String>),
+    StaticBenchFn(fn(&mut Bencher) -> Result<(), String>),
+    DynTestFn(Box<dyn FnOnce() -> Result<(), String> + Send>),
+    DynBenchFn(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>),
 }
 
 impl TestFn {