Skip to content

Commit 3e826bb

Browse files
committedFeb 21, 2021
Auto merge of #82359 - JohnTitor:rollup-6puemik, r=JohnTitor
Rollup of 11 pull requests Successful merges: - #81300 (BTree: share panicky test code & test panic during clear, clone) - #81706 (Document BinaryHeap unsafe functions) - #81833 (parallelize x.py test tidy) - #81966 (Add new `rustc` target for Arm64 machines that can target the iphonesimulator) - #82154 (Update RELEASES.md 1.50 to include methods stabilized in #79342) - #82177 (Do not delete bootstrap.exe on Windows during clean) - #82181 (Add check for ES5 in CI) - #82229 (Add [A-diagnostics] bug report template) - #82233 (try-back-block-type test: Use TryFromSliceError for From test) - #82302 (Remove unsafe impl Send for CompletedTest & TestResult) - #82349 (test: Print test name only once on timeout) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
2 parents ef14688 + d7fb4de commit 3e826bb

File tree

24 files changed

+708
-311
lines changed

24 files changed

+708
-311
lines changed
 

‎.github/ISSUE_TEMPLATE/diagnostics.md

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
name: Diagnostic issue
3+
about: Create a bug report or feature request for a change to `rustc`'s error output
4+
labels: A-diagnostics, T-compiler
5+
---
6+
<!--
7+
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
8+
along with any information you feel relevant to replicating the bug.
9+
10+
If you cannot produce a minimal reproduction case (something that would work in
11+
isolation), please provide the steps or even link to a repository that causes
12+
the problematic output to occur.
13+
-->
14+
15+
Given the following code: <!-- Please provide a link to play.rust-lang.org -->
16+
17+
```rust
18+
<code>
19+
```
20+
21+
The current output is:
22+
23+
```
24+
<rustc output>
25+
```
26+
27+
<!-- The following is not always necessary. -->
28+
Ideally the output should look like:
29+
30+
```
31+
<proposed output>
32+
```
33+
34+
<!--
35+
If the problem is not self-explanatory, please provide a rationale for the
36+
change.
37+
-->
38+
39+
<!--
40+
If dramatically different output is caused by small changes, consider also
41+
adding them here.
42+
43+
If you're using the stable version of the compiler, you should also check if the
44+
bug also exists in the beta or nightly versions. The output might also be
45+
different depending on the Edition.
46+
-->

‎RELEASES.md

+34
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ The following previously stable methods are now `const`.
4343

4444
- [`IpAddr::is_ipv4`]
4545
- [`IpAddr::is_ipv6`]
46+
- [`IpAddr::is_unspecified`]
47+
- [`IpAddr::is_loopback`]
48+
- [`IpAddr::is_multicast`]
49+
- [`Ipv4Addr::octets`]
50+
- [`Ipv4Addr::is_loopback`]
51+
- [`Ipv4Addr::is_private`]
52+
- [`Ipv4Addr::is_link_local`]
53+
- [`Ipv4Addr::is_multicast`]
54+
- [`Ipv4Addr::is_broadcast`]
55+
- [`Ipv4Addr::is_documentation`]
56+
- [`Ipv4Addr::to_ipv6_compatible`]
57+
- [`Ipv4Addr::to_ipv6_mapped`]
58+
- [`Ipv6Addr::segments`]
59+
- [`Ipv6Addr::is_unspecified`]
60+
- [`Ipv6Addr::is_loopback`]
61+
- [`Ipv6Addr::is_multicast`]
62+
- [`Ipv6Addr::to_ipv4`]
4663
- [`Layout::size`]
4764
- [`Layout::align`]
4865
- [`Layout::from_size_align`]
@@ -104,6 +121,23 @@ Compatibility Notes
104121
[cargo/8725]: https://github.com/rust-lang/cargo/pull/8725
105122
[`IpAddr::is_ipv4`]: https://doc.rust-lang.org/stable/std/net/enum.IpAddr.html#method.is_ipv4
106123
[`IpAddr::is_ipv6`]: https://doc.rust-lang.org/stable/std/net/enum.IpAddr.html#method.is_ipv6
124+
[`IpAddr::is_unspecified`]: https://doc.rust-lang.org/stable/std/net/enum.IpAddr.html#method.is_unspecified
125+
[`IpAddr::is_loopback`]: https://doc.rust-lang.org/stable/std/net/enum.IpAddr.html#method.is_loopback
126+
[`IpAddr::is_multicast`]: https://doc.rust-lang.org/stable/std/net/enum.IpAddr.html#method.is_multicast
127+
[`Ipv4Addr::octets`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.octets
128+
[`Ipv4Addr::is_loopback`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.is_loopback
129+
[`Ipv4Addr::is_private`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.is_private
130+
[`Ipv4Addr::is_link_local`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.is_link_local
131+
[`Ipv4Addr::is_multicast`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.is_multicast
132+
[`Ipv4Addr::is_broadcast`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.is_broadcast
133+
[`Ipv4Addr::is_documentation`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.is_documentation
134+
[`Ipv4Addr::to_ipv6_compatible`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.to_ipv6_compatible
135+
[`Ipv4Addr::to_ipv6_mapped`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.to_ipv6_mapped
136+
[`Ipv6Addr::segments`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.segments
137+
[`Ipv6Addr::is_unspecified`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.is_unspecified
138+
[`Ipv6Addr::is_loopback`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.is_loopback
139+
[`Ipv6Addr::is_multicast`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.is_multicast
140+
[`Ipv6Addr::to_ipv4`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.to_ipv4
107141
[`Layout::align`]: https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.align
108142
[`Layout::from_size_align`]: https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.from_size_align
109143
[`Layout::size`]: https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.size

‎compiler/rustc_codegen_ssa/src/back/link.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2193,6 +2193,7 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
21932193
("x86_64", "tvos") => "appletvsimulator",
21942194
("arm", "ios") => "iphoneos",
21952195
("aarch64", "ios") if llvm_target.contains("macabi") => "macosx",
2196+
("aarch64", "ios") if llvm_target.contains("sim") => "iphonesimulator",
21962197
("aarch64", "ios") => "iphoneos",
21972198
("x86", "ios") => "iphonesimulator",
21982199
("x86_64", "ios") if llvm_target.contains("macabi") => "macosx",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use super::apple_sdk_base::{opts, Arch};
2+
use crate::spec::{Target, TargetOptions};
3+
4+
pub fn target() -> Target {
5+
let base = opts("ios", Arch::Arm64_sim);
6+
7+
// Clang automatically chooses a more specific target based on
8+
// IPHONEOS_DEPLOYMENT_TARGET.
9+
// This is required for the simulator target to pick the right
10+
// MACH-O commands, so we do too.
11+
let arch = "arm64";
12+
let llvm_target = super::apple_base::ios_sim_llvm_target(arch);
13+
14+
Target {
15+
llvm_target: llvm_target,
16+
pointer_width: 64,
17+
data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(),
18+
arch: "aarch64".to_string(),
19+
options: TargetOptions {
20+
features: "+neon,+fp-armv8,+apple-a7".to_string(),
21+
eliminate_frame_pointer: false,
22+
max_atomic_width: Some(128),
23+
unsupported_abis: super::arm_base::unsupported_abis(),
24+
forces_embed_bitcode: true,
25+
// Taken from a clang build on Xcode 11.4.1.
26+
// These arguments are not actually invoked - they just have
27+
// to look right to pass App Store validation.
28+
bitcode_llvm_cmdline: "-triple\0\
29+
arm64-apple-ios14.0-simulator\0\
30+
-emit-obj\0\
31+
-disable-llvm-passes\0\
32+
-target-abi\0\
33+
darwinpcs\0\
34+
-Os\0"
35+
.to_string(),
36+
..base
37+
},
38+
}
39+
}

‎compiler/rustc_target/src/spec/apple_base.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,16 @@ pub fn opts(os: &str) -> TargetOptions {
5454
}
5555
}
5656

57-
fn macos_deployment_target() -> (u32, u32) {
58-
let deployment_target = env::var("MACOSX_DEPLOYMENT_TARGET").ok();
59-
let version = deployment_target
57+
fn deployment_target(var_name: &str) -> Option<(u32, u32)> {
58+
let deployment_target = env::var(var_name).ok();
59+
deployment_target
6060
.as_ref()
6161
.and_then(|s| s.split_once('.'))
62-
.and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok());
62+
.and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok())
63+
}
6364

64-
version.unwrap_or((10, 7))
65+
fn macos_deployment_target() -> (u32, u32) {
66+
deployment_target("MACOSX_DEPLOYMENT_TARGET").unwrap_or((10, 7))
6567
}
6668

6769
pub fn macos_llvm_target(arch: &str) -> String {
@@ -84,3 +86,12 @@ pub fn macos_link_env_remove() -> Vec<String> {
8486
env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".to_string());
8587
env_remove
8688
}
89+
90+
fn ios_deployment_target() -> (u32, u32) {
91+
deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((7, 0))
92+
}
93+
94+
pub fn ios_sim_llvm_target(arch: &str) -> String {
95+
let (major, minor) = ios_deployment_target();
96+
format!("{}-apple-ios{}.{}.0-simulator", arch, major, minor)
97+
}

‎compiler/rustc_target/src/spec/apple_sdk_base.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub enum Arch {
1111
X86_64,
1212
X86_64_macabi,
1313
Arm64_macabi,
14+
Arm64_sim,
1415
}
1516

1617
fn target_cpu(arch: Arch) -> String {
@@ -22,13 +23,16 @@ fn target_cpu(arch: Arch) -> String {
2223
X86_64 => "core2",
2324
X86_64_macabi => "core2",
2425
Arm64_macabi => "apple-a12",
26+
Arm64_sim => "apple-a12",
2527
}
2628
.to_string()
2729
}
2830

2931
fn link_env_remove(arch: Arch) -> Vec<String> {
3032
match arch {
31-
Armv7 | Armv7s | Arm64 | I386 | X86_64 => vec!["MACOSX_DEPLOYMENT_TARGET".to_string()],
33+
Armv7 | Armv7s | Arm64 | I386 | X86_64 | Arm64_sim => {
34+
vec!["MACOSX_DEPLOYMENT_TARGET".to_string()]
35+
}
3236
X86_64_macabi | Arm64_macabi => vec!["IPHONEOS_DEPLOYMENT_TARGET".to_string()],
3337
}
3438
}

‎compiler/rustc_target/src/spec/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,7 @@ supported_targets! {
727727
("armv7s-apple-ios", armv7s_apple_ios),
728728
("x86_64-apple-ios-macabi", x86_64_apple_ios_macabi),
729729
("aarch64-apple-ios-macabi", aarch64_apple_ios_macabi),
730+
("aarch64-apple-ios-sim", aarch64_apple_ios_sim),
730731
("aarch64-apple-tvos", aarch64_apple_tvos),
731732
("x86_64-apple-tvos", x86_64_apple_tvos),
732733

‎library/alloc/src/collections/binary_heap.rs

+117-49
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,8 @@ impl<T: Ord + fmt::Debug> fmt::Debug for PeekMut<'_, T> {
275275
impl<T: Ord> Drop for PeekMut<'_, T> {
276276
fn drop(&mut self) {
277277
if self.sift {
278-
self.heap.sift_down(0);
278+
// SAFETY: PeekMut is only instantiated for non-empty heaps.
279+
unsafe { self.heap.sift_down(0) };
279280
}
280281
}
281282
}
@@ -431,7 +432,8 @@ impl<T: Ord> BinaryHeap<T> {
431432
self.data.pop().map(|mut item| {
432433
if !self.is_empty() {
433434
swap(&mut item, &mut self.data[0]);
434-
self.sift_down_to_bottom(0);
435+
// SAFETY: !self.is_empty() means that self.len() > 0
436+
unsafe { self.sift_down_to_bottom(0) };
435437
}
436438
item
437439
})
@@ -473,7 +475,9 @@ impl<T: Ord> BinaryHeap<T> {
473475
pub fn push(&mut self, item: T) {
474476
let old_len = self.len();
475477
self.data.push(item);
476-
self.sift_up(0, old_len);
478+
// SAFETY: Since we pushed a new item it means that
479+
// old_len = self.len() - 1 < self.len()
480+
unsafe { self.sift_up(0, old_len) };
477481
}
478482

479483
/// Consumes the `BinaryHeap` and returns a vector in sorted
@@ -506,7 +510,10 @@ impl<T: Ord> BinaryHeap<T> {
506510
let ptr = self.data.as_mut_ptr();
507511
ptr::swap(ptr, ptr.add(end));
508512
}
509-
self.sift_down_range(0, end);
513+
// SAFETY: `end` goes from `self.len() - 1` to 1 (both included) so:
514+
// 0 < 1 <= end <= self.len() - 1 < self.len()
515+
// Which means 0 < end and end < self.len().
516+
unsafe { self.sift_down_range(0, end) };
510517
}
511518
self.into_vec()
512519
}
@@ -519,78 +526,139 @@ impl<T: Ord> BinaryHeap<T> {
519526
// the hole is filled back at the end of its scope, even on panic.
520527
// Using a hole reduces the constant factor compared to using swaps,
521528
// which involves twice as many moves.
522-
fn sift_up(&mut self, start: usize, pos: usize) -> usize {
523-
unsafe {
524-
// Take out the value at `pos` and create a hole.
525-
let mut hole = Hole::new(&mut self.data, pos);
526-
527-
while hole.pos() > start {
528-
let parent = (hole.pos() - 1) / 2;
529-
if hole.element() <= hole.get(parent) {
530-
break;
531-
}
532-
hole.move_to(parent);
529+
530+
/// # Safety
531+
///
532+
/// The caller must guarantee that `pos < self.len()`.
533+
unsafe fn sift_up(&mut self, start: usize, pos: usize) -> usize {
534+
// Take out the value at `pos` and create a hole.
535+
// SAFETY: The caller guarantees that pos < self.len()
536+
let mut hole = unsafe { Hole::new(&mut self.data, pos) };
537+
538+
while hole.pos() > start {
539+
let parent = (hole.pos() - 1) / 2;
540+
541+
// SAFETY: hole.pos() > start >= 0, which means hole.pos() > 0
542+
// and so hole.pos() - 1 can't underflow.
543+
// This guarantees that parent < hole.pos() so
544+
// it's a valid index and also != hole.pos().
545+
if hole.element() <= unsafe { hole.get(parent) } {
546+
break;
533547
}
534-
hole.pos()
548+
549+
// SAFETY: Same as above
550+
unsafe { hole.move_to(parent) };
535551
}
552+
553+
hole.pos()
536554
}
537555

538556
/// Take an element at `pos` and move it down the heap,
539557
/// while its children are larger.
540-
fn sift_down_range(&mut self, pos: usize, end: usize) {
541-
unsafe {
542-
let mut hole = Hole::new(&mut self.data, pos);
543-
let mut child = 2 * pos + 1;
544-
while child < end - 1 {
545-
// compare with the greater of the two children
546-
child += (hole.get(child) <= hole.get(child + 1)) as usize;
547-
// if we are already in order, stop.
548-
if hole.element() >= hole.get(child) {
549-
return;
550-
}
551-
hole.move_to(child);
552-
child = 2 * hole.pos() + 1;
553-
}
554-
if child == end - 1 && hole.element() < hole.get(child) {
555-
hole.move_to(child);
558+
///
559+
/// # Safety
560+
///
561+
/// The caller must guarantee that `pos < end <= self.len()`.
562+
unsafe fn sift_down_range(&mut self, pos: usize, end: usize) {
563+
// SAFETY: The caller guarantees that pos < end <= self.len().
564+
let mut hole = unsafe { Hole::new(&mut self.data, pos) };
565+
let mut child = 2 * hole.pos() + 1;
566+
567+
// Loop invariant: child == 2 * hole.pos() + 1.
568+
while child < end - 1 {
569+
// compare with the greater of the two children
570+
// SAFETY: child < end - 1 < self.len() and
571+
// child + 1 < end <= self.len(), so they're valid indexes.
572+
// child == 2 * hole.pos() + 1 != hole.pos() and
573+
// child + 1 == 2 * hole.pos() + 2 != hole.pos().
574+
// FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow
575+
// if T is a ZST
576+
child += unsafe { hole.get(child) <= hole.get(child + 1) } as usize;
577+
578+
// if we are already in order, stop.
579+
// SAFETY: child is now either the old child or the old child+1
580+
// We already proven that both are < self.len() and != hole.pos()
581+
if hole.element() >= unsafe { hole.get(child) } {
582+
return;
556583
}
584+
585+
// SAFETY: same as above.
586+
unsafe { hole.move_to(child) };
587+
child = 2 * hole.pos() + 1;
588+
}
589+
590+
// SAFETY: && short circuit, which means that in the
591+
// second condition it's already true that child == end - 1 < self.len().
592+
if child == end - 1 && hole.element() < unsafe { hole.get(child) } {
593+
// SAFETY: child is already proven to be a valid index and
594+
// child == 2 * hole.pos() + 1 != hole.pos().
595+
unsafe { hole.move_to(child) };
557596
}
558597
}
559598

560-
fn sift_down(&mut self, pos: usize) {
599+
/// # Safety
600+
///
601+
/// The caller must guarantee that `pos < self.len()`.
602+
unsafe fn sift_down(&mut self, pos: usize) {
561603
let len = self.len();
562-
self.sift_down_range(pos, len);
604+
// SAFETY: pos < len is guaranteed by the caller and
605+
// obviously len = self.len() <= self.len().
606+
unsafe { self.sift_down_range(pos, len) };
563607
}
564608

565609
/// Take an element at `pos` and move it all the way down the heap,
566610
/// then sift it up to its position.
567611
///
568612
/// Note: This is faster when the element is known to be large / should
569613
/// be closer to the bottom.
570-
fn sift_down_to_bottom(&mut self, mut pos: usize) {
614+
///
615+
/// # Safety
616+
///
617+
/// The caller must guarantee that `pos < self.len()`.
618+
unsafe fn sift_down_to_bottom(&mut self, mut pos: usize) {
571619
let end = self.len();
572620
let start = pos;
573-
unsafe {
574-
let mut hole = Hole::new(&mut self.data, pos);
575-
let mut child = 2 * pos + 1;
576-
while child < end - 1 {
577-
child += (hole.get(child) <= hole.get(child + 1)) as usize;
578-
hole.move_to(child);
579-
child = 2 * hole.pos() + 1;
580-
}
581-
if child == end - 1 {
582-
hole.move_to(child);
583-
}
584-
pos = hole.pos;
621+
622+
// SAFETY: The caller guarantees that pos < self.len().
623+
let mut hole = unsafe { Hole::new(&mut self.data, pos) };
624+
let mut child = 2 * hole.pos() + 1;
625+
626+
// Loop invariant: child == 2 * hole.pos() + 1.
627+
while child < end - 1 {
628+
// SAFETY: child < end - 1 < self.len() and
629+
// child + 1 < end <= self.len(), so they're valid indexes.
630+
// child == 2 * hole.pos() + 1 != hole.pos() and
631+
// child + 1 == 2 * hole.pos() + 2 != hole.pos().
632+
// FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow
633+
// if T is a ZST
634+
child += unsafe { hole.get(child) <= hole.get(child + 1) } as usize;
635+
636+
// SAFETY: Same as above
637+
unsafe { hole.move_to(child) };
638+
child = 2 * hole.pos() + 1;
585639
}
586-
self.sift_up(start, pos);
640+
641+
if child == end - 1 {
642+
// SAFETY: child == end - 1 < self.len(), so it's a valid index
643+
// and child == 2 * hole.pos() + 1 != hole.pos().
644+
unsafe { hole.move_to(child) };
645+
}
646+
pos = hole.pos();
647+
drop(hole);
648+
649+
// SAFETY: pos is the position in the hole and was already proven
650+
// to be a valid index.
651+
unsafe { self.sift_up(start, pos) };
587652
}
588653

589654
fn rebuild(&mut self) {
590655
let mut n = self.len() / 2;
591656
while n > 0 {
592657
n -= 1;
593-
self.sift_down(n);
658+
// SAFETY: n starts from self.len() / 2 and goes down to 0.
659+
// The only case when !(n < self.len()) is if
660+
// self.len() == 0, but it's ruled out by the loop condition.
661+
unsafe { self.sift_down(n) };
594662
}
595663
}
596664

‎library/alloc/src/collections/btree/map/tests.rs

+154-131
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use super::super::{node, DeterministicRng};
1+
use super::super::testing::crash_test::{CrashTestDummy, Panic};
2+
use super::super::testing::ord_chaos::{Cyclic3, Governed, Governor};
3+
use super::super::testing::rng::DeterministicRng;
24
use super::Entry::{Occupied, Vacant};
35
use super::*;
46
use crate::boxed::Box;
@@ -15,9 +17,6 @@ use std::ops::RangeBounds;
1517
use std::panic::{catch_unwind, AssertUnwindSafe};
1618
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
1719

18-
mod ord_chaos;
19-
use ord_chaos::{Cyclic3, Governed, Governor};
20-
2120
// Capacity of a tree with a single level,
2221
// i.e., a tree who's root is a leaf node at height 0.
2322
const NODE_CAPACITY: usize = node::CAPACITY;
@@ -1136,103 +1135,78 @@ mod test_drain_filter {
11361135

11371136
#[test]
11381137
fn drop_panic_leak() {
1139-
static PREDS: AtomicUsize = AtomicUsize::new(0);
1140-
static DROPS: AtomicUsize = AtomicUsize::new(0);
1141-
1142-
struct D;
1143-
impl Drop for D {
1144-
fn drop(&mut self) {
1145-
if DROPS.fetch_add(1, SeqCst) == 1 {
1146-
panic!("panic in `drop`");
1147-
}
1148-
}
1149-
}
1150-
1151-
// Keys are multiples of 4, so that each key is counted by a hexadecimal digit.
1152-
let mut map = (0..3).map(|i| (i * 4, D)).collect::<BTreeMap<_, _>>();
1138+
let a = CrashTestDummy::new(0);
1139+
let b = CrashTestDummy::new(1);
1140+
let c = CrashTestDummy::new(2);
1141+
let mut map = BTreeMap::new();
1142+
map.insert(a.spawn(Panic::Never), ());
1143+
map.insert(b.spawn(Panic::InDrop), ());
1144+
map.insert(c.spawn(Panic::Never), ());
11531145

1154-
catch_unwind(move || {
1155-
drop(map.drain_filter(|i, _| {
1156-
PREDS.fetch_add(1usize << i, SeqCst);
1157-
true
1158-
}))
1159-
})
1160-
.unwrap_err();
1146+
catch_unwind(move || drop(map.drain_filter(|dummy, _| dummy.query(true)))).unwrap_err();
11611147

1162-
assert_eq!(PREDS.load(SeqCst), 0x011);
1163-
assert_eq!(DROPS.load(SeqCst), 3);
1148+
assert_eq!(a.queried(), 1);
1149+
assert_eq!(b.queried(), 1);
1150+
assert_eq!(c.queried(), 0);
1151+
assert_eq!(a.dropped(), 1);
1152+
assert_eq!(b.dropped(), 1);
1153+
assert_eq!(c.dropped(), 1);
11641154
}
11651155

11661156
#[test]
11671157
fn pred_panic_leak() {
1168-
static PREDS: AtomicUsize = AtomicUsize::new(0);
1169-
static DROPS: AtomicUsize = AtomicUsize::new(0);
1170-
1171-
struct D;
1172-
impl Drop for D {
1173-
fn drop(&mut self) {
1174-
DROPS.fetch_add(1, SeqCst);
1175-
}
1176-
}
1177-
1178-
// Keys are multiples of 4, so that each key is counted by a hexadecimal digit.
1179-
let mut map = (0..3).map(|i| (i * 4, D)).collect::<BTreeMap<_, _>>();
1180-
1181-
catch_unwind(AssertUnwindSafe(|| {
1182-
drop(map.drain_filter(|i, _| {
1183-
PREDS.fetch_add(1usize << i, SeqCst);
1184-
match i {
1185-
0 => true,
1186-
_ => panic!(),
1187-
}
1188-
}))
1189-
}))
1190-
.unwrap_err();
1191-
1192-
assert_eq!(PREDS.load(SeqCst), 0x011);
1193-
assert_eq!(DROPS.load(SeqCst), 1);
1158+
let a = CrashTestDummy::new(0);
1159+
let b = CrashTestDummy::new(1);
1160+
let c = CrashTestDummy::new(2);
1161+
let mut map = BTreeMap::new();
1162+
map.insert(a.spawn(Panic::Never), ());
1163+
map.insert(b.spawn(Panic::InQuery), ());
1164+
map.insert(c.spawn(Panic::InQuery), ());
1165+
1166+
catch_unwind(AssertUnwindSafe(|| drop(map.drain_filter(|dummy, _| dummy.query(true)))))
1167+
.unwrap_err();
1168+
1169+
assert_eq!(a.queried(), 1);
1170+
assert_eq!(b.queried(), 1);
1171+
assert_eq!(c.queried(), 0);
1172+
assert_eq!(a.dropped(), 1);
1173+
assert_eq!(b.dropped(), 0);
1174+
assert_eq!(c.dropped(), 0);
11941175
assert_eq!(map.len(), 2);
1195-
assert_eq!(map.first_entry().unwrap().key(), &4);
1196-
assert_eq!(map.last_entry().unwrap().key(), &8);
1176+
assert_eq!(map.first_entry().unwrap().key().id(), 1);
1177+
assert_eq!(map.last_entry().unwrap().key().id(), 2);
11971178
map.check();
11981179
}
11991180

12001181
// Same as above, but attempt to use the iterator again after the panic in the predicate
12011182
#[test]
12021183
fn pred_panic_reuse() {
1203-
static PREDS: AtomicUsize = AtomicUsize::new(0);
1204-
static DROPS: AtomicUsize = AtomicUsize::new(0);
1205-
1206-
struct D;
1207-
impl Drop for D {
1208-
fn drop(&mut self) {
1209-
DROPS.fetch_add(1, SeqCst);
1210-
}
1211-
}
1212-
1213-
// Keys are multiples of 4, so that each key is counted by a hexadecimal digit.
1214-
let mut map = (0..3).map(|i| (i * 4, D)).collect::<BTreeMap<_, _>>();
1184+
let a = CrashTestDummy::new(0);
1185+
let b = CrashTestDummy::new(1);
1186+
let c = CrashTestDummy::new(2);
1187+
let mut map = BTreeMap::new();
1188+
map.insert(a.spawn(Panic::Never), ());
1189+
map.insert(b.spawn(Panic::InQuery), ());
1190+
map.insert(c.spawn(Panic::InQuery), ());
12151191

12161192
{
1217-
let mut it = map.drain_filter(|i, _| {
1218-
PREDS.fetch_add(1usize << i, SeqCst);
1219-
match i {
1220-
0 => true,
1221-
_ => panic!(),
1222-
}
1223-
});
1193+
let mut it = map.drain_filter(|dummy, _| dummy.query(true));
12241194
catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err();
12251195
// Iterator behaviour after a panic is explicitly unspecified,
12261196
// so this is just the current implementation:
12271197
let result = catch_unwind(AssertUnwindSafe(|| it.next()));
12281198
assert!(matches!(result, Ok(None)));
12291199
}
12301200

1231-
assert_eq!(PREDS.load(SeqCst), 0x011);
1232-
assert_eq!(DROPS.load(SeqCst), 1);
1201+
assert_eq!(a.queried(), 1);
1202+
assert_eq!(b.queried(), 1);
1203+
assert_eq!(c.queried(), 0);
1204+
assert_eq!(a.dropped(), 1);
1205+
assert_eq!(b.dropped(), 0);
1206+
assert_eq!(c.dropped(), 0);
12331207
assert_eq!(map.len(), 2);
1234-
assert_eq!(map.first_entry().unwrap().key(), &4);
1235-
assert_eq!(map.last_entry().unwrap().key(), &8);
1208+
assert_eq!(map.first_entry().unwrap().key().id(), 1);
1209+
assert_eq!(map.last_entry().unwrap().key().id(), 2);
12361210
map.check();
12371211
}
12381212
}
@@ -1439,6 +1413,43 @@ fn test_bad_zst() {
14391413
m.check();
14401414
}
14411415

1416+
#[test]
1417+
fn test_clear() {
1418+
let mut map = BTreeMap::new();
1419+
for &len in &[MIN_INSERTS_HEIGHT_1, MIN_INSERTS_HEIGHT_2, 0, NODE_CAPACITY] {
1420+
for i in 0..len {
1421+
map.insert(i, ());
1422+
}
1423+
assert_eq!(map.len(), len);
1424+
map.clear();
1425+
map.check();
1426+
assert!(map.is_empty());
1427+
}
1428+
}
1429+
1430+
#[test]
1431+
fn test_clear_drop_panic_leak() {
1432+
let a = CrashTestDummy::new(0);
1433+
let b = CrashTestDummy::new(1);
1434+
let c = CrashTestDummy::new(2);
1435+
1436+
let mut map = BTreeMap::new();
1437+
map.insert(a.spawn(Panic::Never), ());
1438+
map.insert(b.spawn(Panic::InDrop), ());
1439+
map.insert(c.spawn(Panic::Never), ());
1440+
1441+
catch_unwind(AssertUnwindSafe(|| map.clear())).unwrap_err();
1442+
assert_eq!(a.dropped(), 1);
1443+
assert_eq!(b.dropped(), 1);
1444+
assert_eq!(c.dropped(), 1);
1445+
assert_eq!(map.len(), 0);
1446+
1447+
drop(map);
1448+
assert_eq!(a.dropped(), 1);
1449+
assert_eq!(b.dropped(), 1);
1450+
assert_eq!(c.dropped(), 1);
1451+
}
1452+
14421453
#[test]
14431454
fn test_clone() {
14441455
let mut map = BTreeMap::new();
@@ -1484,6 +1495,35 @@ fn test_clone() {
14841495
map.check();
14851496
}
14861497

1498+
#[test]
1499+
fn test_clone_panic_leak() {
1500+
let a = CrashTestDummy::new(0);
1501+
let b = CrashTestDummy::new(1);
1502+
let c = CrashTestDummy::new(2);
1503+
1504+
let mut map = BTreeMap::new();
1505+
map.insert(a.spawn(Panic::Never), ());
1506+
map.insert(b.spawn(Panic::InClone), ());
1507+
map.insert(c.spawn(Panic::Never), ());
1508+
1509+
catch_unwind(|| map.clone()).unwrap_err();
1510+
assert_eq!(a.cloned(), 1);
1511+
assert_eq!(b.cloned(), 1);
1512+
assert_eq!(c.cloned(), 0);
1513+
assert_eq!(a.dropped(), 1);
1514+
assert_eq!(b.dropped(), 0);
1515+
assert_eq!(c.dropped(), 0);
1516+
assert_eq!(map.len(), 3);
1517+
1518+
drop(map);
1519+
assert_eq!(a.cloned(), 1);
1520+
assert_eq!(b.cloned(), 1);
1521+
assert_eq!(c.cloned(), 0);
1522+
assert_eq!(a.dropped(), 2);
1523+
assert_eq!(b.dropped(), 1);
1524+
assert_eq!(c.dropped(), 1);
1525+
}
1526+
14871527
#[test]
14881528
fn test_clone_from() {
14891529
let mut map1 = BTreeMap::new();
@@ -1901,29 +1941,21 @@ create_append_test!(test_append_1700, 1700);
19011941

19021942
#[test]
19031943
fn test_append_drop_leak() {
1904-
static DROPS: AtomicUsize = AtomicUsize::new(0);
1905-
1906-
struct D;
1907-
1908-
impl Drop for D {
1909-
fn drop(&mut self) {
1910-
if DROPS.fetch_add(1, SeqCst) == 0 {
1911-
panic!("panic in `drop`");
1912-
}
1913-
}
1914-
}
1915-
1944+
let a = CrashTestDummy::new(0);
1945+
let b = CrashTestDummy::new(1);
1946+
let c = CrashTestDummy::new(2);
19161947
let mut left = BTreeMap::new();
19171948
let mut right = BTreeMap::new();
1918-
left.insert(0, D);
1919-
left.insert(1, D); // first to be dropped during append
1920-
left.insert(2, D);
1921-
right.insert(1, D);
1922-
right.insert(2, D);
1949+
left.insert(a.spawn(Panic::Never), ());
1950+
left.insert(b.spawn(Panic::InDrop), ()); // first duplicate key, dropped during append
1951+
left.insert(c.spawn(Panic::Never), ());
1952+
right.insert(b.spawn(Panic::Never), ());
1953+
right.insert(c.spawn(Panic::Never), ());
19231954

19241955
catch_unwind(move || left.append(&mut right)).unwrap_err();
1925-
1926-
assert_eq!(DROPS.load(SeqCst), 4); // Rust issue #47949 ate one little piggy
1956+
assert_eq!(a.dropped(), 1);
1957+
assert_eq!(b.dropped(), 1); // should be 2 were it not for Rust issue #47949
1958+
assert_eq!(c.dropped(), 2);
19271959
}
19281960

19291961
#[test]
@@ -2050,51 +2082,42 @@ fn test_split_off_large_random_sorted() {
20502082

20512083
#[test]
20522084
fn test_into_iter_drop_leak_height_0() {
2053-
static DROPS: AtomicUsize = AtomicUsize::new(0);
2054-
2055-
struct D;
2056-
2057-
impl Drop for D {
2058-
fn drop(&mut self) {
2059-
if DROPS.fetch_add(1, SeqCst) == 3 {
2060-
panic!("panic in `drop`");
2061-
}
2062-
}
2063-
}
2064-
2085+
let a = CrashTestDummy::new(0);
2086+
let b = CrashTestDummy::new(1);
2087+
let c = CrashTestDummy::new(2);
2088+
let d = CrashTestDummy::new(3);
2089+
let e = CrashTestDummy::new(4);
20652090
let mut map = BTreeMap::new();
2066-
map.insert("a", D);
2067-
map.insert("b", D);
2068-
map.insert("c", D);
2069-
map.insert("d", D);
2070-
map.insert("e", D);
2091+
map.insert("a", a.spawn(Panic::Never));
2092+
map.insert("b", b.spawn(Panic::Never));
2093+
map.insert("c", c.spawn(Panic::Never));
2094+
map.insert("d", d.spawn(Panic::InDrop));
2095+
map.insert("e", e.spawn(Panic::Never));
20712096

20722097
catch_unwind(move || drop(map.into_iter())).unwrap_err();
20732098

2074-
assert_eq!(DROPS.load(SeqCst), 5);
2099+
assert_eq!(a.dropped(), 1);
2100+
assert_eq!(b.dropped(), 1);
2101+
assert_eq!(c.dropped(), 1);
2102+
assert_eq!(d.dropped(), 1);
2103+
assert_eq!(e.dropped(), 1);
20752104
}
20762105

20772106
#[test]
20782107
fn test_into_iter_drop_leak_height_1() {
20792108
let size = MIN_INSERTS_HEIGHT_1;
2080-
static DROPS: AtomicUsize = AtomicUsize::new(0);
2081-
static PANIC_POINT: AtomicUsize = AtomicUsize::new(0);
2082-
2083-
struct D;
2084-
impl Drop for D {
2085-
fn drop(&mut self) {
2086-
if DROPS.fetch_add(1, SeqCst) == PANIC_POINT.load(SeqCst) {
2087-
panic!("panic in `drop`");
2088-
}
2089-
}
2090-
}
2091-
20922109
for panic_point in vec![0, 1, size - 2, size - 1] {
2093-
DROPS.store(0, SeqCst);
2094-
PANIC_POINT.store(panic_point, SeqCst);
2095-
let map: BTreeMap<_, _> = (0..size).map(|i| (i, D)).collect();
2110+
let dummies: Vec<_> = (0..size).map(|i| CrashTestDummy::new(i)).collect();
2111+
let map: BTreeMap<_, _> = (0..size)
2112+
.map(|i| {
2113+
let panic = if i == panic_point { Panic::InDrop } else { Panic::Never };
2114+
(dummies[i].spawn(Panic::Never), dummies[i].spawn(panic))
2115+
})
2116+
.collect();
20962117
catch_unwind(move || drop(map.into_iter())).unwrap_err();
2097-
assert_eq!(DROPS.load(SeqCst), size);
2118+
for i in 0..size {
2119+
assert_eq!(dummies[i].dropped(), 2);
2120+
}
20982121
}
20992122
}
21002123

‎library/alloc/src/collections/btree/mod.rs

+1-29
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,4 @@ trait Recover<Q: ?Sized> {
2020
}
2121

2222
#[cfg(test)]
23-
/// XorShiftRng
24-
struct DeterministicRng {
25-
count: usize,
26-
x: u32,
27-
y: u32,
28-
z: u32,
29-
w: u32,
30-
}
31-
32-
#[cfg(test)]
33-
impl DeterministicRng {
34-
fn new() -> Self {
35-
DeterministicRng { count: 0, x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb }
36-
}
37-
38-
/// Guarantees that each returned number is unique.
39-
fn next(&mut self) -> u32 {
40-
self.count += 1;
41-
assert!(self.count <= 70029);
42-
let x = self.x;
43-
let t = x ^ (x << 11);
44-
self.x = self.y;
45-
self.y = self.z;
46-
self.z = self.w;
47-
let w_ = self.w;
48-
self.w = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8));
49-
self.w
50-
}
51-
}
23+
mod testing;

‎library/alloc/src/collections/btree/set/tests.rs

+32-57
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use super::super::DeterministicRng;
1+
use super::super::testing::crash_test::{CrashTestDummy, Panic};
2+
use super::super::testing::rng::DeterministicRng;
23
use super::*;
34
use crate::vec::Vec;
45
use std::cmp::Ordering;
56
use std::iter::FromIterator;
67
use std::panic::{catch_unwind, AssertUnwindSafe};
7-
use std::sync::atomic::{AtomicU32, Ordering::SeqCst};
88

99
#[test]
1010
fn test_clone_eq() {
@@ -349,70 +349,45 @@ fn test_drain_filter() {
349349

350350
#[test]
351351
fn test_drain_filter_drop_panic_leak() {
352-
static PREDS: AtomicU32 = AtomicU32::new(0);
353-
static DROPS: AtomicU32 = AtomicU32::new(0);
354-
355-
#[derive(PartialEq, Eq, PartialOrd, Ord)]
356-
struct D(i32);
357-
impl Drop for D {
358-
fn drop(&mut self) {
359-
if DROPS.fetch_add(1, SeqCst) == 1 {
360-
panic!("panic in `drop`");
361-
}
362-
}
363-
}
364-
352+
let a = CrashTestDummy::new(0);
353+
let b = CrashTestDummy::new(1);
354+
let c = CrashTestDummy::new(2);
365355
let mut set = BTreeSet::new();
366-
set.insert(D(0));
367-
set.insert(D(4));
368-
set.insert(D(8));
356+
set.insert(a.spawn(Panic::Never));
357+
set.insert(b.spawn(Panic::InDrop));
358+
set.insert(c.spawn(Panic::Never));
369359

370-
catch_unwind(move || {
371-
drop(set.drain_filter(|d| {
372-
PREDS.fetch_add(1u32 << d.0, SeqCst);
373-
true
374-
}))
375-
})
376-
.ok();
360+
catch_unwind(move || drop(set.drain_filter(|dummy| dummy.query(true)))).ok();
377361

378-
assert_eq!(PREDS.load(SeqCst), 0x011);
379-
assert_eq!(DROPS.load(SeqCst), 3);
362+
assert_eq!(a.queried(), 1);
363+
assert_eq!(b.queried(), 1);
364+
assert_eq!(c.queried(), 0);
365+
assert_eq!(a.dropped(), 1);
366+
assert_eq!(b.dropped(), 1);
367+
assert_eq!(c.dropped(), 1);
380368
}
381369

382370
#[test]
383371
fn test_drain_filter_pred_panic_leak() {
384-
static PREDS: AtomicU32 = AtomicU32::new(0);
385-
static DROPS: AtomicU32 = AtomicU32::new(0);
386-
387-
#[derive(PartialEq, Eq, PartialOrd, Ord)]
388-
struct D(i32);
389-
impl Drop for D {
390-
fn drop(&mut self) {
391-
DROPS.fetch_add(1, SeqCst);
392-
}
393-
}
394-
372+
let a = CrashTestDummy::new(0);
373+
let b = CrashTestDummy::new(1);
374+
let c = CrashTestDummy::new(2);
395375
let mut set = BTreeSet::new();
396-
set.insert(D(0));
397-
set.insert(D(4));
398-
set.insert(D(8));
399-
400-
catch_unwind(AssertUnwindSafe(|| {
401-
drop(set.drain_filter(|d| {
402-
PREDS.fetch_add(1u32 << d.0, SeqCst);
403-
match d.0 {
404-
0 => true,
405-
_ => panic!(),
406-
}
407-
}))
408-
}))
409-
.ok();
410-
411-
assert_eq!(PREDS.load(SeqCst), 0x011);
412-
assert_eq!(DROPS.load(SeqCst), 1);
376+
set.insert(a.spawn(Panic::Never));
377+
set.insert(b.spawn(Panic::InQuery));
378+
set.insert(c.spawn(Panic::InQuery));
379+
380+
catch_unwind(AssertUnwindSafe(|| drop(set.drain_filter(|dummy| dummy.query(true))))).ok();
381+
382+
assert_eq!(a.queried(), 1);
383+
assert_eq!(b.queried(), 1);
384+
assert_eq!(c.queried(), 0);
385+
assert_eq!(a.dropped(), 1);
386+
assert_eq!(b.dropped(), 0);
387+
assert_eq!(c.dropped(), 0);
413388
assert_eq!(set.len(), 2);
414-
assert_eq!(set.first().unwrap().0, 4);
415-
assert_eq!(set.last().unwrap().0, 8);
389+
assert_eq!(set.first().unwrap().id(), 1);
390+
assert_eq!(set.last().unwrap().id(), 2);
416391
}
417392

418393
#[test]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use crate::fmt::Debug;
2+
use std::cmp::Ordering;
3+
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
4+
5+
/// A blueprint for crash test dummy instances that monitor particular events.
6+
/// Some instances may be configured to panic at some point.
7+
/// Events are `clone`, `drop` or some anonymous `query`.
8+
///
9+
/// Crash test dummies are identified and ordered by an id, so they can be used
10+
/// as keys in a BTreeMap. The implementation intentionally uses does not rely
11+
/// on anything defined in the crate, apart from the `Debug` trait.
12+
#[derive(Debug)]
13+
pub struct CrashTestDummy {
14+
id: usize,
15+
cloned: AtomicUsize,
16+
dropped: AtomicUsize,
17+
queried: AtomicUsize,
18+
}
19+
20+
impl CrashTestDummy {
21+
/// Creates a crash test dummy design. The `id` determines order and equality of instances.
22+
pub fn new(id: usize) -> CrashTestDummy {
23+
CrashTestDummy {
24+
id,
25+
cloned: AtomicUsize::new(0),
26+
dropped: AtomicUsize::new(0),
27+
queried: AtomicUsize::new(0),
28+
}
29+
}
30+
31+
/// Creates an instance of a crash test dummy that records what events it experiences
32+
/// and optionally panics.
33+
pub fn spawn(&self, panic: Panic) -> Instance<'_> {
34+
Instance { origin: self, panic }
35+
}
36+
37+
/// Returns how many times instances of the dummy have been cloned.
38+
pub fn cloned(&self) -> usize {
39+
self.cloned.load(SeqCst)
40+
}
41+
42+
/// Returns how many times instances of the dummy have been dropped.
43+
pub fn dropped(&self) -> usize {
44+
self.dropped.load(SeqCst)
45+
}
46+
47+
/// Returns how many times instances of the dummy have had their `query` member invoked.
48+
pub fn queried(&self) -> usize {
49+
self.queried.load(SeqCst)
50+
}
51+
}
52+
53+
#[derive(Debug)]
54+
pub struct Instance<'a> {
55+
origin: &'a CrashTestDummy,
56+
panic: Panic,
57+
}
58+
59+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
60+
pub enum Panic {
61+
Never,
62+
InClone,
63+
InDrop,
64+
InQuery,
65+
}
66+
67+
impl Instance<'_> {
68+
pub fn id(&self) -> usize {
69+
self.origin.id
70+
}
71+
72+
/// Some anonymous query, the result of which is already given.
73+
pub fn query<R>(&self, result: R) -> R {
74+
self.origin.queried.fetch_add(1, SeqCst);
75+
if self.panic == Panic::InQuery {
76+
panic!("panic in `query`");
77+
}
78+
result
79+
}
80+
}
81+
82+
impl Clone for Instance<'_> {
83+
fn clone(&self) -> Self {
84+
self.origin.cloned.fetch_add(1, SeqCst);
85+
if self.panic == Panic::InClone {
86+
panic!("panic in `clone`");
87+
}
88+
Self { origin: self.origin, panic: Panic::Never }
89+
}
90+
}
91+
92+
impl Drop for Instance<'_> {
93+
fn drop(&mut self) {
94+
self.origin.dropped.fetch_add(1, SeqCst);
95+
if self.panic == Panic::InDrop {
96+
panic!("panic in `drop`");
97+
}
98+
}
99+
}
100+
101+
impl PartialOrd for Instance<'_> {
102+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
103+
self.id().partial_cmp(&other.id())
104+
}
105+
}
106+
107+
impl Ord for Instance<'_> {
108+
fn cmp(&self, other: &Self) -> Ordering {
109+
self.id().cmp(&other.id())
110+
}
111+
}
112+
113+
impl PartialEq for Instance<'_> {
114+
fn eq(&self, other: &Self) -> bool {
115+
self.id().eq(&other.id())
116+
}
117+
}
118+
119+
impl Eq for Instance<'_> {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub mod crash_test;
2+
pub mod ord_chaos;
3+
pub mod rng;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/// XorShiftRng
2+
pub struct DeterministicRng {
3+
count: usize,
4+
x: u32,
5+
y: u32,
6+
z: u32,
7+
w: u32,
8+
}
9+
10+
impl DeterministicRng {
11+
pub fn new() -> Self {
12+
DeterministicRng { count: 0, x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb }
13+
}
14+
15+
/// Guarantees that each returned number is unique.
16+
pub fn next(&mut self) -> u32 {
17+
self.count += 1;
18+
assert!(self.count <= 70029);
19+
let x = self.x;
20+
let t = x ^ (x << 11);
21+
self.x = self.y;
22+
self.y = self.z;
23+
self.z = self.w;
24+
let w_ = self.w;
25+
self.w = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8));
26+
self.w
27+
}
28+
}

‎library/test/src/event.rs

-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ impl CompletedTest {
2424
}
2525
}
2626

27-
unsafe impl Send for CompletedTest {}
28-
2927
#[derive(Debug, Clone)]
3028
pub enum TestEvent {
3129
TeFiltered(Vec<TestDesc>),

‎library/test/src/formatters/pretty.rs

-4
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,6 @@ impl<T: Write> OutputFormatter for PrettyFormatter<T> {
222222
}
223223

224224
fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
225-
if self.is_multithreaded {
226-
self.write_test_name(desc)?;
227-
}
228-
229225
self.write_plain(&format!(
230226
"test {} has been running for over {} seconds\n",
231227
desc.name,

‎library/test/src/test_result.rs

-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ pub enum TestResult {
2424
TrTimedFail,
2525
}
2626

27-
unsafe impl Send for TestResult {}
28-
2927
/// Creates a `TestResult` depending on the raw result of test execution
3028
/// and associated data.
3129
pub fn calc_result<'a>(

‎src/bootstrap/clean.rs

+36-4
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,40 @@ fn rm_rf(path: &Path) {
5151
}
5252
Ok(metadata) => {
5353
if metadata.file_type().is_file() || metadata.file_type().is_symlink() {
54-
do_op(path, "remove file", |p| fs::remove_file(p));
54+
do_op(path, "remove file", |p| {
55+
fs::remove_file(p).or_else(|e| {
56+
// Work around the fact that we cannot
57+
// delete an executable while it runs on Windows.
58+
#[cfg(windows)]
59+
if e.kind() == std::io::ErrorKind::PermissionDenied
60+
&& p.file_name().and_then(std::ffi::OsStr::to_str)
61+
== Some("bootstrap.exe")
62+
{
63+
eprintln!("warning: failed to delete '{}'.", p.display());
64+
return Ok(());
65+
}
66+
Err(e)
67+
})
68+
});
5569
return;
5670
}
5771

5872
for file in t!(fs::read_dir(path)) {
5973
rm_rf(&t!(file).path());
6074
}
61-
do_op(path, "remove dir", |p| fs::remove_dir(p));
75+
do_op(path, "remove dir", |p| {
76+
fs::remove_dir(p).or_else(|e| {
77+
// Check for dir not empty on Windows
78+
#[cfg(windows)]
79+
if matches!(e.kind(), std::io::ErrorKind::Other)
80+
&& e.raw_os_error() == Some(145)
81+
{
82+
return Ok(());
83+
}
84+
85+
Err(e)
86+
})
87+
});
6288
}
6389
};
6490
}
@@ -73,12 +99,18 @@ where
7399
// As a result, we have some special logic to remove readonly files on windows.
74100
// This is also the reason that we can't use things like fs::remove_dir_all().
75101
Err(ref e) if cfg!(windows) && e.kind() == ErrorKind::PermissionDenied => {
76-
let mut p = t!(path.symlink_metadata()).permissions();
102+
let m = t!(path.symlink_metadata());
103+
let mut p = m.permissions();
77104
p.set_readonly(false);
78105
t!(fs::set_permissions(path, p));
79106
f(path).unwrap_or_else(|e| {
107+
// Delete symlinked directories on Windows
108+
#[cfg(windows)]
109+
if m.file_type().is_symlink() && path.is_dir() && fs::remove_dir(path).is_ok() {
110+
return;
111+
}
80112
panic!("failed to {} {}: {}", desc, path.display(), e);
81-
})
113+
});
82114
}
83115
Err(e) => {
84116
panic!("failed to {} {}: {}", desc, path.display(), e);

‎src/bootstrap/format.rs

+63-18
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
use crate::Build;
44
use build_helper::{output, t};
55
use ignore::WalkBuilder;
6-
use std::path::Path;
6+
use std::collections::VecDeque;
7+
use std::path::{Path, PathBuf};
78
use std::process::{Command, Stdio};
9+
use std::sync::mpsc::SyncSender;
810

9-
fn rustfmt(src: &Path, rustfmt: &Path, path: &Path, check: bool) {
11+
fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl FnMut() {
1012
let mut cmd = Command::new(&rustfmt);
1113
// avoid the submodule config paths from coming into play,
1214
// we only allow a single global config for the workspace for now
@@ -17,17 +19,21 @@ fn rustfmt(src: &Path, rustfmt: &Path, path: &Path, check: bool) {
1719
if check {
1820
cmd.arg("--check");
1921
}
20-
cmd.arg(&path);
22+
cmd.args(paths);
2123
let cmd_debug = format!("{:?}", cmd);
22-
let status = cmd.status().expect("executing rustfmt");
23-
if !status.success() {
24-
eprintln!(
25-
"Running `{}` failed.\nIf you're running `tidy`, \
26-
try again with `--bless`. Or, if you just want to format \
27-
code, run `./x.py fmt` instead.",
28-
cmd_debug,
29-
);
30-
std::process::exit(1);
24+
let mut cmd = cmd.spawn().expect("running rustfmt");
25+
// poor man's async: return a closure that'll wait for rustfmt's completion
26+
move || {
27+
let status = cmd.wait().unwrap();
28+
if !status.success() {
29+
eprintln!(
30+
"Running `{}` failed.\nIf you're running `tidy`, \
31+
try again with `--bless`. Or, if you just want to format \
32+
code, run `./x.py fmt` instead.",
33+
cmd_debug,
34+
);
35+
std::process::exit(1);
36+
}
3137
}
3238
}
3339

@@ -101,19 +107,58 @@ pub fn format(build: &Build, check: bool) {
101107
}
102108
let ignore_fmt = ignore_fmt.build().unwrap();
103109

104-
let rustfmt_path = build.config.initial_rustfmt.as_ref().unwrap_or_else(|| {
105-
eprintln!("./x.py fmt is not supported on this channel");
106-
std::process::exit(1);
110+
let rustfmt_path = build
111+
.config
112+
.initial_rustfmt
113+
.as_ref()
114+
.unwrap_or_else(|| {
115+
eprintln!("./x.py fmt is not supported on this channel");
116+
std::process::exit(1);
117+
})
118+
.to_path_buf();
119+
let src = build.src.clone();
120+
let (tx, rx): (SyncSender<PathBuf>, _) = std::sync::mpsc::sync_channel(128);
121+
let walker =
122+
WalkBuilder::new(src.clone()).types(matcher).overrides(ignore_fmt).build_parallel();
123+
124+
// there is a lot of blocking involved in spawning a child process and reading files to format.
125+
// spawn more processes than available concurrency to keep the CPU busy
126+
let max_processes = build.jobs() as usize * 2;
127+
128+
// spawn child processes on a separate thread so we can batch entries we have received from ignore
129+
let thread = std::thread::spawn(move || {
130+
let mut children = VecDeque::new();
131+
while let Ok(path) = rx.recv() {
132+
// try getting a few more paths from the channel to amortize the overhead of spawning processes
133+
let paths: Vec<_> = rx.try_iter().take(7).chain(std::iter::once(path)).collect();
134+
135+
let child = rustfmt(&src, &rustfmt_path, paths.as_slice(), check);
136+
children.push_back(child);
137+
138+
if children.len() >= max_processes {
139+
// await oldest child
140+
children.pop_front().unwrap()();
141+
}
142+
}
143+
144+
// await remaining children
145+
for mut child in children {
146+
child();
147+
}
107148
});
108-
let src = &build.src;
109-
let walker = WalkBuilder::new(src).types(matcher).overrides(ignore_fmt).build_parallel();
149+
110150
walker.run(|| {
151+
let tx = tx.clone();
111152
Box::new(move |entry| {
112153
let entry = t!(entry);
113154
if entry.file_type().map_or(false, |t| t.is_file()) {
114-
rustfmt(src, &rustfmt_path, &entry.path(), check);
155+
t!(tx.send(entry.into_path()));
115156
}
116157
ignore::WalkState::Continue
117158
})
118159
});
160+
161+
drop(tx);
162+
163+
thread.join().unwrap();
119164
}

‎src/ci/docker/host-x86_64/mingw-check/Dockerfile

+8-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
1717
pkg-config \
1818
mingw-w64
1919

20+
RUN curl -sL https://nodejs.org/dist/v14.4.0/node-v14.4.0-linux-x64.tar.xz | tar -xJ
21+
ENV PATH="/node-v14.4.0-linux-x64/bin:${PATH}"
22+
# Install es-check
23+
RUN npm install es-check -g
24+
2025
COPY scripts/sccache.sh /scripts/
2126
RUN sh /scripts/sccache.sh
2227

@@ -29,4 +34,6 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \
2934
python3 ../x.py test --stage 0 src/tools/compiletest && \
3035
python3 ../x.py test --stage 2 src/tools/tidy && \
3136
python3 ../x.py doc --stage 0 library/std && \
32-
/scripts/validate-toolstate.sh
37+
/scripts/validate-toolstate.sh && \
38+
# Runs checks to ensure that there are no ES5 issues in our JS code.
39+
es-check es5 ../src/librustdoc/html/static/*.js

‎src/doc/rustc/src/platform-support.md

+1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ not available.
153153
target | std | host | notes
154154
-------|-----|------|-------
155155
`aarch64-apple-ios-macabi` | ? | | Apple Catalyst on ARM64
156+
`aarch64-apple-ios-sim` | ? | | Apple iOS Simulator on ARM64
156157
`aarch64-apple-tvos` | * | | ARM64 tvOS
157158
`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
158159
`aarch64-unknown-hermit` | ? | |

‎src/test/ui/try-block/try-block-bad-type.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![feature(try_blocks)]
44

55
pub fn main() {
6-
let res: Result<u32, i32> = try {
6+
let res: Result<u32, std::array::TryFromSliceError> = try {
77
Err("")?; //~ ERROR `?` couldn't convert the error
88
5
99
};

‎src/test/ui/try-block/try-block-bad-type.stderr

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
1-
error[E0277]: `?` couldn't convert the error to `i32`
1+
error[E0277]: `?` couldn't convert the error to `TryFromSliceError`
22
--> $DIR/try-block-bad-type.rs:7:16
33
|
44
LL | Err("")?;
5-
| ^ the trait `From<&str>` is not implemented for `i32`
5+
| ^ the trait `From<&str>` is not implemented for `TryFromSliceError`
66
|
77
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
88
= help: the following implementations were found:
9-
<i32 as From<NonZeroI32>>
10-
<i32 as From<bool>>
11-
<i32 as From<i16>>
12-
<i32 as From<i8>>
13-
and 2 others
9+
<TryFromSliceError as From<Infallible>>
1410
= note: required by `from`
1511

1612
error[E0271]: type mismatch resolving `<Result<i32, i32> as Try>::Ok == &str`

0 commit comments

Comments
 (0)
Please sign in to comment.