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
This commit is contained in:
bors 2022-10-02 15:31:06 +00:00
commit 39323a5877
14 changed files with 239 additions and 94 deletions

View File

@ -941,9 +941,19 @@ impl<'hir> Map<'hir> {
let span = match self.find(hir_id)? {
// Function-like.
Node::Item(Item { kind: ItemKind::Fn(sig, ..), .. })
| Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, ..), .. })
| Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(sig, ..), .. }) => sig.span,
Node::Item(Item { kind: ItemKind::Fn(sig, ..), span: outer_span, .. })
| Node::TraitItem(TraitItem {
kind: TraitItemKind::Fn(sig, ..),
span: outer_span,
..
})
| Node::ImplItem(ImplItem {
kind: ImplItemKind::Fn(sig, ..), span: outer_span, ..
}) => {
// Ensure that the returned span has the item's SyntaxContext, and not the
// SyntaxContext of the visibility.
sig.span.find_ancestor_in_same_ctxt(*outer_span).unwrap_or(*outer_span)
}
// Constants and Statics.
Node::Item(Item {
kind:
@ -985,7 +995,11 @@ impl<'hir> Map<'hir> {
}
// Other cases.
Node::Item(item) => match &item.kind {
ItemKind::Use(path, _) => path.span,
ItemKind::Use(path, _) => {
// Ensure that the returned span has the item's SyntaxContext, and not the
// SyntaxContext of the path.
path.span.find_ancestor_in_same_ctxt(item.span).unwrap_or(item.span)
}
_ => named_span(item.span, item.ident, item.kind.generics()),
},
Node::Variant(variant) => named_span(variant.span, variant.ident, None),
@ -995,11 +1009,17 @@ impl<'hir> Map<'hir> {
_ => named_span(item.span, item.ident, None),
},
Node::Ctor(_) => return self.opt_span(self.get_parent_node(hir_id)),
Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl_span, .. }), .. }) => {
*fn_decl_span
Node::Expr(Expr {
kind: ExprKind::Closure(Closure { fn_decl_span, .. }),
span,
..
}) => {
// Ensure that the returned span has the item's SyntaxContext.
fn_decl_span.find_ancestor_in_same_ctxt(*span).unwrap_or(*span)
}
_ => self.span_with_body(hir_id),
};
debug_assert_eq!(span.ctxt(), self.span_with_body(hir_id).ctxt());
Some(span)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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.

View File

@ -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(|_| ())
}

View File

@ -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")
}

View File

@ -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);
}

View File

@ -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 {

View File

@ -1134,6 +1134,7 @@ impl Tester for Collector {
panic::resume_unwind(Box::new(()));
}
Ok(())
})),
});
}

View File

@ -0,0 +1,21 @@
// known-bug: #102498
#![feature(const_trait_impl, generic_const_exprs)]
pub trait Tr {
fn a() -> usize;
}
impl Tr for () {
fn a() -> usize {
1
}
}
const fn foo<T: ~const Tr>() -> [u8; T::a()] {
[0; T::a()]
}
fn main() {
foo::<()>();
}

View File

@ -0,0 +1,18 @@
warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/constifconst-call-in-const-position.rs:3:30
|
LL | #![feature(const_trait_impl, generic_const_exprs)]
| ^^^^^^^^^^^^^^^^^^^
|
= note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
= note: `#[warn(incomplete_features)]` on by default
error[E0080]: evaluation of `foo::<()>::{constant#0}` failed
--> $DIR/constifconst-call-in-const-position.rs:15:38
|
LL | const fn foo<T: ~const Tr>() -> [u8; T::a()] {
| ^^^^^^ calling non-const function `<() as Tr>::a`
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0080`.

View File

@ -13,7 +13,7 @@ LL | | }
note: required by a bound in `assert_test_result`
--> $SRC_DIR/test/src/lib.rs:LL:COL
|
LL | pub fn assert_test_result<T: Termination>(result: T) {
LL | pub fn assert_test_result<T: Termination>(result: T) -> Result<(), String> {
| ^^^^^^^^^^^ required by this bound in `assert_test_result`
= note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@ -797,7 +797,10 @@ fn make_test_closure(
let config = config.clone();
let testpaths = testpaths.clone();
let revision = revision.cloned();
test::DynTestFn(Box::new(move || runtest::run(config, &testpaths, revision.as_deref())))
test::DynTestFn(Box::new(move || {
runtest::run(config, &testpaths, revision.as_deref());
Ok(())
}))
}
/// Returns `true` if the given target is an Android target for the

View File

@ -538,7 +538,9 @@ fn map_lib_features(
becoming_feature = None;
if line.contains("rustc_const_unstable(") {
// `const fn` features are handled specially.
let feature_name = match find_attr_val(line, "feature") {
let feature_name = match find_attr_val(line, "feature").or_else(|| {
iter_lines.peek().and_then(|next| find_attr_val(next.1, "feature"))
}) {
Some(name) => name,
None => err!("malformed stability attribute: missing `feature` key"),
};