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
This commit is contained in:
commit
3e826bb112
24 changed files with 703 additions and 306 deletions
46
.github/ISSUE_TEMPLATE/diagnostics.md
vendored
Normal file
46
.github/ISSUE_TEMPLATE/diagnostics.md
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
---
|
||||||
|
name: Diagnostic issue
|
||||||
|
about: Create a bug report or feature request for a change to `rustc`'s error output
|
||||||
|
labels: A-diagnostics, T-compiler
|
||||||
|
---
|
||||||
|
<!--
|
||||||
|
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
|
||||||
|
along with any information you feel relevant to replicating the bug.
|
||||||
|
|
||||||
|
If you cannot produce a minimal reproduction case (something that would work in
|
||||||
|
isolation), please provide the steps or even link to a repository that causes
|
||||||
|
the problematic output to occur.
|
||||||
|
-->
|
||||||
|
|
||||||
|
Given the following code: <!-- Please provide a link to play.rust-lang.org -->
|
||||||
|
|
||||||
|
```rust
|
||||||
|
<code>
|
||||||
|
```
|
||||||
|
|
||||||
|
The current output is:
|
||||||
|
|
||||||
|
```
|
||||||
|
<rustc output>
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- The following is not always necessary. -->
|
||||||
|
Ideally the output should look like:
|
||||||
|
|
||||||
|
```
|
||||||
|
<proposed output>
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If the problem is not self-explanatory, please provide a rationale for the
|
||||||
|
change.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If dramatically different output is caused by small changes, consider also
|
||||||
|
adding them here.
|
||||||
|
|
||||||
|
If you're using the stable version of the compiler, you should also check if the
|
||||||
|
bug also exists in the beta or nightly versions. The output might also be
|
||||||
|
different depending on the Edition.
|
||||||
|
-->
|
34
RELEASES.md
34
RELEASES.md
|
@ -43,6 +43,23 @@ The following previously stable methods are now `const`.
|
||||||
|
|
||||||
- [`IpAddr::is_ipv4`]
|
- [`IpAddr::is_ipv4`]
|
||||||
- [`IpAddr::is_ipv6`]
|
- [`IpAddr::is_ipv6`]
|
||||||
|
- [`IpAddr::is_unspecified`]
|
||||||
|
- [`IpAddr::is_loopback`]
|
||||||
|
- [`IpAddr::is_multicast`]
|
||||||
|
- [`Ipv4Addr::octets`]
|
||||||
|
- [`Ipv4Addr::is_loopback`]
|
||||||
|
- [`Ipv4Addr::is_private`]
|
||||||
|
- [`Ipv4Addr::is_link_local`]
|
||||||
|
- [`Ipv4Addr::is_multicast`]
|
||||||
|
- [`Ipv4Addr::is_broadcast`]
|
||||||
|
- [`Ipv4Addr::is_documentation`]
|
||||||
|
- [`Ipv4Addr::to_ipv6_compatible`]
|
||||||
|
- [`Ipv4Addr::to_ipv6_mapped`]
|
||||||
|
- [`Ipv6Addr::segments`]
|
||||||
|
- [`Ipv6Addr::is_unspecified`]
|
||||||
|
- [`Ipv6Addr::is_loopback`]
|
||||||
|
- [`Ipv6Addr::is_multicast`]
|
||||||
|
- [`Ipv6Addr::to_ipv4`]
|
||||||
- [`Layout::size`]
|
- [`Layout::size`]
|
||||||
- [`Layout::align`]
|
- [`Layout::align`]
|
||||||
- [`Layout::from_size_align`]
|
- [`Layout::from_size_align`]
|
||||||
|
@ -104,6 +121,23 @@ Compatibility Notes
|
||||||
[cargo/8725]: https://github.com/rust-lang/cargo/pull/8725
|
[cargo/8725]: https://github.com/rust-lang/cargo/pull/8725
|
||||||
[`IpAddr::is_ipv4`]: https://doc.rust-lang.org/stable/std/net/enum.IpAddr.html#method.is_ipv4
|
[`IpAddr::is_ipv4`]: https://doc.rust-lang.org/stable/std/net/enum.IpAddr.html#method.is_ipv4
|
||||||
[`IpAddr::is_ipv6`]: https://doc.rust-lang.org/stable/std/net/enum.IpAddr.html#method.is_ipv6
|
[`IpAddr::is_ipv6`]: https://doc.rust-lang.org/stable/std/net/enum.IpAddr.html#method.is_ipv6
|
||||||
|
[`IpAddr::is_unspecified`]: https://doc.rust-lang.org/stable/std/net/enum.IpAddr.html#method.is_unspecified
|
||||||
|
[`IpAddr::is_loopback`]: https://doc.rust-lang.org/stable/std/net/enum.IpAddr.html#method.is_loopback
|
||||||
|
[`IpAddr::is_multicast`]: https://doc.rust-lang.org/stable/std/net/enum.IpAddr.html#method.is_multicast
|
||||||
|
[`Ipv4Addr::octets`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.octets
|
||||||
|
[`Ipv4Addr::is_loopback`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.is_loopback
|
||||||
|
[`Ipv4Addr::is_private`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.is_private
|
||||||
|
[`Ipv4Addr::is_link_local`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.is_link_local
|
||||||
|
[`Ipv4Addr::is_multicast`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.is_multicast
|
||||||
|
[`Ipv4Addr::is_broadcast`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.is_broadcast
|
||||||
|
[`Ipv4Addr::is_documentation`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.is_documentation
|
||||||
|
[`Ipv4Addr::to_ipv6_compatible`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.to_ipv6_compatible
|
||||||
|
[`Ipv4Addr::to_ipv6_mapped`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.to_ipv6_mapped
|
||||||
|
[`Ipv6Addr::segments`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.segments
|
||||||
|
[`Ipv6Addr::is_unspecified`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.is_unspecified
|
||||||
|
[`Ipv6Addr::is_loopback`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.is_loopback
|
||||||
|
[`Ipv6Addr::is_multicast`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.is_multicast
|
||||||
|
[`Ipv6Addr::to_ipv4`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.to_ipv4
|
||||||
[`Layout::align`]: https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.align
|
[`Layout::align`]: https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.align
|
||||||
[`Layout::from_size_align`]: https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.from_size_align
|
[`Layout::from_size_align`]: https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.from_size_align
|
||||||
[`Layout::size`]: https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.size
|
[`Layout::size`]: https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.size
|
||||||
|
|
|
@ -2193,6 +2193,7 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
|
||||||
("x86_64", "tvos") => "appletvsimulator",
|
("x86_64", "tvos") => "appletvsimulator",
|
||||||
("arm", "ios") => "iphoneos",
|
("arm", "ios") => "iphoneos",
|
||||||
("aarch64", "ios") if llvm_target.contains("macabi") => "macosx",
|
("aarch64", "ios") if llvm_target.contains("macabi") => "macosx",
|
||||||
|
("aarch64", "ios") if llvm_target.contains("sim") => "iphonesimulator",
|
||||||
("aarch64", "ios") => "iphoneos",
|
("aarch64", "ios") => "iphoneos",
|
||||||
("x86", "ios") => "iphonesimulator",
|
("x86", "ios") => "iphonesimulator",
|
||||||
("x86_64", "ios") if llvm_target.contains("macabi") => "macosx",
|
("x86_64", "ios") if llvm_target.contains("macabi") => "macosx",
|
||||||
|
|
39
compiler/rustc_target/src/spec/aarch64_apple_ios_sim.rs
Normal file
39
compiler/rustc_target/src/spec/aarch64_apple_ios_sim.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use super::apple_sdk_base::{opts, Arch};
|
||||||
|
use crate::spec::{Target, TargetOptions};
|
||||||
|
|
||||||
|
pub fn target() -> Target {
|
||||||
|
let base = opts("ios", Arch::Arm64_sim);
|
||||||
|
|
||||||
|
// Clang automatically chooses a more specific target based on
|
||||||
|
// IPHONEOS_DEPLOYMENT_TARGET.
|
||||||
|
// This is required for the simulator target to pick the right
|
||||||
|
// MACH-O commands, so we do too.
|
||||||
|
let arch = "arm64";
|
||||||
|
let llvm_target = super::apple_base::ios_sim_llvm_target(arch);
|
||||||
|
|
||||||
|
Target {
|
||||||
|
llvm_target: llvm_target,
|
||||||
|
pointer_width: 64,
|
||||||
|
data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(),
|
||||||
|
arch: "aarch64".to_string(),
|
||||||
|
options: TargetOptions {
|
||||||
|
features: "+neon,+fp-armv8,+apple-a7".to_string(),
|
||||||
|
eliminate_frame_pointer: false,
|
||||||
|
max_atomic_width: Some(128),
|
||||||
|
unsupported_abis: super::arm_base::unsupported_abis(),
|
||||||
|
forces_embed_bitcode: true,
|
||||||
|
// Taken from a clang build on Xcode 11.4.1.
|
||||||
|
// These arguments are not actually invoked - they just have
|
||||||
|
// to look right to pass App Store validation.
|
||||||
|
bitcode_llvm_cmdline: "-triple\0\
|
||||||
|
arm64-apple-ios14.0-simulator\0\
|
||||||
|
-emit-obj\0\
|
||||||
|
-disable-llvm-passes\0\
|
||||||
|
-target-abi\0\
|
||||||
|
darwinpcs\0\
|
||||||
|
-Os\0"
|
||||||
|
.to_string(),
|
||||||
|
..base
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,14 +54,16 @@ pub fn opts(os: &str) -> TargetOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn macos_deployment_target() -> (u32, u32) {
|
fn deployment_target(var_name: &str) -> Option<(u32, u32)> {
|
||||||
let deployment_target = env::var("MACOSX_DEPLOYMENT_TARGET").ok();
|
let deployment_target = env::var(var_name).ok();
|
||||||
let version = deployment_target
|
deployment_target
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|s| s.split_once('.'))
|
.and_then(|s| s.split_once('.'))
|
||||||
.and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok());
|
.and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok())
|
||||||
|
}
|
||||||
|
|
||||||
version.unwrap_or((10, 7))
|
fn macos_deployment_target() -> (u32, u32) {
|
||||||
|
deployment_target("MACOSX_DEPLOYMENT_TARGET").unwrap_or((10, 7))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn macos_llvm_target(arch: &str) -> String {
|
pub fn macos_llvm_target(arch: &str) -> String {
|
||||||
|
@ -84,3 +86,12 @@ pub fn macos_link_env_remove() -> Vec<String> {
|
||||||
env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".to_string());
|
env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".to_string());
|
||||||
env_remove
|
env_remove
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ios_deployment_target() -> (u32, u32) {
|
||||||
|
deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((7, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ios_sim_llvm_target(arch: &str) -> String {
|
||||||
|
let (major, minor) = ios_deployment_target();
|
||||||
|
format!("{}-apple-ios{}.{}.0-simulator", arch, major, minor)
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ pub enum Arch {
|
||||||
X86_64,
|
X86_64,
|
||||||
X86_64_macabi,
|
X86_64_macabi,
|
||||||
Arm64_macabi,
|
Arm64_macabi,
|
||||||
|
Arm64_sim,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn target_cpu(arch: Arch) -> String {
|
fn target_cpu(arch: Arch) -> String {
|
||||||
|
@ -22,13 +23,16 @@ fn target_cpu(arch: Arch) -> String {
|
||||||
X86_64 => "core2",
|
X86_64 => "core2",
|
||||||
X86_64_macabi => "core2",
|
X86_64_macabi => "core2",
|
||||||
Arm64_macabi => "apple-a12",
|
Arm64_macabi => "apple-a12",
|
||||||
|
Arm64_sim => "apple-a12",
|
||||||
}
|
}
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_env_remove(arch: Arch) -> Vec<String> {
|
fn link_env_remove(arch: Arch) -> Vec<String> {
|
||||||
match arch {
|
match arch {
|
||||||
Armv7 | Armv7s | Arm64 | I386 | X86_64 => vec!["MACOSX_DEPLOYMENT_TARGET".to_string()],
|
Armv7 | Armv7s | Arm64 | I386 | X86_64 | Arm64_sim => {
|
||||||
|
vec!["MACOSX_DEPLOYMENT_TARGET".to_string()]
|
||||||
|
}
|
||||||
X86_64_macabi | Arm64_macabi => vec!["IPHONEOS_DEPLOYMENT_TARGET".to_string()],
|
X86_64_macabi | Arm64_macabi => vec!["IPHONEOS_DEPLOYMENT_TARGET".to_string()],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -727,6 +727,7 @@ supported_targets! {
|
||||||
("armv7s-apple-ios", armv7s_apple_ios),
|
("armv7s-apple-ios", armv7s_apple_ios),
|
||||||
("x86_64-apple-ios-macabi", x86_64_apple_ios_macabi),
|
("x86_64-apple-ios-macabi", x86_64_apple_ios_macabi),
|
||||||
("aarch64-apple-ios-macabi", aarch64_apple_ios_macabi),
|
("aarch64-apple-ios-macabi", aarch64_apple_ios_macabi),
|
||||||
|
("aarch64-apple-ios-sim", aarch64_apple_ios_sim),
|
||||||
("aarch64-apple-tvos", aarch64_apple_tvos),
|
("aarch64-apple-tvos", aarch64_apple_tvos),
|
||||||
("x86_64-apple-tvos", x86_64_apple_tvos),
|
("x86_64-apple-tvos", x86_64_apple_tvos),
|
||||||
|
|
||||||
|
|
|
@ -275,7 +275,8 @@ impl<T: Ord + fmt::Debug> fmt::Debug for PeekMut<'_, T> {
|
||||||
impl<T: Ord> Drop for PeekMut<'_, T> {
|
impl<T: Ord> Drop for PeekMut<'_, T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.sift {
|
if self.sift {
|
||||||
self.heap.sift_down(0);
|
// SAFETY: PeekMut is only instantiated for non-empty heaps.
|
||||||
|
unsafe { self.heap.sift_down(0) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -431,7 +432,8 @@ impl<T: Ord> BinaryHeap<T> {
|
||||||
self.data.pop().map(|mut item| {
|
self.data.pop().map(|mut item| {
|
||||||
if !self.is_empty() {
|
if !self.is_empty() {
|
||||||
swap(&mut item, &mut self.data[0]);
|
swap(&mut item, &mut self.data[0]);
|
||||||
self.sift_down_to_bottom(0);
|
// SAFETY: !self.is_empty() means that self.len() > 0
|
||||||
|
unsafe { self.sift_down_to_bottom(0) };
|
||||||
}
|
}
|
||||||
item
|
item
|
||||||
})
|
})
|
||||||
|
@ -473,7 +475,9 @@ impl<T: Ord> BinaryHeap<T> {
|
||||||
pub fn push(&mut self, item: T) {
|
pub fn push(&mut self, item: T) {
|
||||||
let old_len = self.len();
|
let old_len = self.len();
|
||||||
self.data.push(item);
|
self.data.push(item);
|
||||||
self.sift_up(0, old_len);
|
// SAFETY: Since we pushed a new item it means that
|
||||||
|
// old_len = self.len() - 1 < self.len()
|
||||||
|
unsafe { self.sift_up(0, old_len) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes the `BinaryHeap` and returns a vector in sorted
|
/// Consumes the `BinaryHeap` and returns a vector in sorted
|
||||||
|
@ -506,7 +510,10 @@ impl<T: Ord> BinaryHeap<T> {
|
||||||
let ptr = self.data.as_mut_ptr();
|
let ptr = self.data.as_mut_ptr();
|
||||||
ptr::swap(ptr, ptr.add(end));
|
ptr::swap(ptr, ptr.add(end));
|
||||||
}
|
}
|
||||||
self.sift_down_range(0, end);
|
// SAFETY: `end` goes from `self.len() - 1` to 1 (both included) so:
|
||||||
|
// 0 < 1 <= end <= self.len() - 1 < self.len()
|
||||||
|
// Which means 0 < end and end < self.len().
|
||||||
|
unsafe { self.sift_down_range(0, end) };
|
||||||
}
|
}
|
||||||
self.into_vec()
|
self.into_vec()
|
||||||
}
|
}
|
||||||
|
@ -519,47 +526,84 @@ impl<T: Ord> BinaryHeap<T> {
|
||||||
// the hole is filled back at the end of its scope, even on panic.
|
// the hole is filled back at the end of its scope, even on panic.
|
||||||
// Using a hole reduces the constant factor compared to using swaps,
|
// Using a hole reduces the constant factor compared to using swaps,
|
||||||
// which involves twice as many moves.
|
// which involves twice as many moves.
|
||||||
fn sift_up(&mut self, start: usize, pos: usize) -> usize {
|
|
||||||
unsafe {
|
|
||||||
// Take out the value at `pos` and create a hole.
|
|
||||||
let mut hole = Hole::new(&mut self.data, pos);
|
|
||||||
|
|
||||||
while hole.pos() > start {
|
/// # Safety
|
||||||
let parent = (hole.pos() - 1) / 2;
|
///
|
||||||
if hole.element() <= hole.get(parent) {
|
/// The caller must guarantee that `pos < self.len()`.
|
||||||
break;
|
unsafe fn sift_up(&mut self, start: usize, pos: usize) -> usize {
|
||||||
}
|
// Take out the value at `pos` and create a hole.
|
||||||
hole.move_to(parent);
|
// SAFETY: The caller guarantees that pos < self.len()
|
||||||
|
let mut hole = unsafe { Hole::new(&mut self.data, pos) };
|
||||||
|
|
||||||
|
while hole.pos() > start {
|
||||||
|
let parent = (hole.pos() - 1) / 2;
|
||||||
|
|
||||||
|
// SAFETY: hole.pos() > start >= 0, which means hole.pos() > 0
|
||||||
|
// and so hole.pos() - 1 can't underflow.
|
||||||
|
// This guarantees that parent < hole.pos() so
|
||||||
|
// it's a valid index and also != hole.pos().
|
||||||
|
if hole.element() <= unsafe { hole.get(parent) } {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
hole.pos()
|
|
||||||
|
// SAFETY: Same as above
|
||||||
|
unsafe { hole.move_to(parent) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hole.pos()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take an element at `pos` and move it down the heap,
|
/// Take an element at `pos` and move it down the heap,
|
||||||
/// while its children are larger.
|
/// while its children are larger.
|
||||||
fn sift_down_range(&mut self, pos: usize, end: usize) {
|
///
|
||||||
unsafe {
|
/// # Safety
|
||||||
let mut hole = Hole::new(&mut self.data, pos);
|
///
|
||||||
let mut child = 2 * pos + 1;
|
/// The caller must guarantee that `pos < end <= self.len()`.
|
||||||
while child < end - 1 {
|
unsafe fn sift_down_range(&mut self, pos: usize, end: usize) {
|
||||||
// compare with the greater of the two children
|
// SAFETY: The caller guarantees that pos < end <= self.len().
|
||||||
child += (hole.get(child) <= hole.get(child + 1)) as usize;
|
let mut hole = unsafe { Hole::new(&mut self.data, pos) };
|
||||||
// if we are already in order, stop.
|
let mut child = 2 * hole.pos() + 1;
|
||||||
if hole.element() >= hole.get(child) {
|
|
||||||
return;
|
// Loop invariant: child == 2 * hole.pos() + 1.
|
||||||
}
|
while child < end - 1 {
|
||||||
hole.move_to(child);
|
// compare with the greater of the two children
|
||||||
child = 2 * hole.pos() + 1;
|
// SAFETY: child < end - 1 < self.len() and
|
||||||
}
|
// child + 1 < end <= self.len(), so they're valid indexes.
|
||||||
if child == end - 1 && hole.element() < hole.get(child) {
|
// child == 2 * hole.pos() + 1 != hole.pos() and
|
||||||
hole.move_to(child);
|
// child + 1 == 2 * hole.pos() + 2 != hole.pos().
|
||||||
|
// FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow
|
||||||
|
// if T is a ZST
|
||||||
|
child += unsafe { hole.get(child) <= hole.get(child + 1) } as usize;
|
||||||
|
|
||||||
|
// if we are already in order, stop.
|
||||||
|
// SAFETY: child is now either the old child or the old child+1
|
||||||
|
// We already proven that both are < self.len() and != hole.pos()
|
||||||
|
if hole.element() >= unsafe { hole.get(child) } {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: same as above.
|
||||||
|
unsafe { hole.move_to(child) };
|
||||||
|
child = 2 * hole.pos() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: && short circuit, which means that in the
|
||||||
|
// second condition it's already true that child == end - 1 < self.len().
|
||||||
|
if child == end - 1 && hole.element() < unsafe { hole.get(child) } {
|
||||||
|
// SAFETY: child is already proven to be a valid index and
|
||||||
|
// child == 2 * hole.pos() + 1 != hole.pos().
|
||||||
|
unsafe { hole.move_to(child) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sift_down(&mut self, pos: usize) {
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller must guarantee that `pos < self.len()`.
|
||||||
|
unsafe fn sift_down(&mut self, pos: usize) {
|
||||||
let len = self.len();
|
let len = self.len();
|
||||||
self.sift_down_range(pos, len);
|
// SAFETY: pos < len is guaranteed by the caller and
|
||||||
|
// obviously len = self.len() <= self.len().
|
||||||
|
unsafe { self.sift_down_range(pos, len) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take an element at `pos` and move it all the way down the heap,
|
/// Take an element at `pos` and move it all the way down the heap,
|
||||||
|
@ -567,30 +611,54 @@ impl<T: Ord> BinaryHeap<T> {
|
||||||
///
|
///
|
||||||
/// Note: This is faster when the element is known to be large / should
|
/// Note: This is faster when the element is known to be large / should
|
||||||
/// be closer to the bottom.
|
/// be closer to the bottom.
|
||||||
fn sift_down_to_bottom(&mut self, mut pos: usize) {
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller must guarantee that `pos < self.len()`.
|
||||||
|
unsafe fn sift_down_to_bottom(&mut self, mut pos: usize) {
|
||||||
let end = self.len();
|
let end = self.len();
|
||||||
let start = pos;
|
let start = pos;
|
||||||
unsafe {
|
|
||||||
let mut hole = Hole::new(&mut self.data, pos);
|
// SAFETY: The caller guarantees that pos < self.len().
|
||||||
let mut child = 2 * pos + 1;
|
let mut hole = unsafe { Hole::new(&mut self.data, pos) };
|
||||||
while child < end - 1 {
|
let mut child = 2 * hole.pos() + 1;
|
||||||
child += (hole.get(child) <= hole.get(child + 1)) as usize;
|
|
||||||
hole.move_to(child);
|
// Loop invariant: child == 2 * hole.pos() + 1.
|
||||||
child = 2 * hole.pos() + 1;
|
while child < end - 1 {
|
||||||
}
|
// SAFETY: child < end - 1 < self.len() and
|
||||||
if child == end - 1 {
|
// child + 1 < end <= self.len(), so they're valid indexes.
|
||||||
hole.move_to(child);
|
// child == 2 * hole.pos() + 1 != hole.pos() and
|
||||||
}
|
// child + 1 == 2 * hole.pos() + 2 != hole.pos().
|
||||||
pos = hole.pos;
|
// FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow
|
||||||
|
// if T is a ZST
|
||||||
|
child += unsafe { hole.get(child) <= hole.get(child + 1) } as usize;
|
||||||
|
|
||||||
|
// SAFETY: Same as above
|
||||||
|
unsafe { hole.move_to(child) };
|
||||||
|
child = 2 * hole.pos() + 1;
|
||||||
}
|
}
|
||||||
self.sift_up(start, pos);
|
|
||||||
|
if child == end - 1 {
|
||||||
|
// SAFETY: child == end - 1 < self.len(), so it's a valid index
|
||||||
|
// and child == 2 * hole.pos() + 1 != hole.pos().
|
||||||
|
unsafe { hole.move_to(child) };
|
||||||
|
}
|
||||||
|
pos = hole.pos();
|
||||||
|
drop(hole);
|
||||||
|
|
||||||
|
// SAFETY: pos is the position in the hole and was already proven
|
||||||
|
// to be a valid index.
|
||||||
|
unsafe { self.sift_up(start, pos) };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rebuild(&mut self) {
|
fn rebuild(&mut self) {
|
||||||
let mut n = self.len() / 2;
|
let mut n = self.len() / 2;
|
||||||
while n > 0 {
|
while n > 0 {
|
||||||
n -= 1;
|
n -= 1;
|
||||||
self.sift_down(n);
|
// SAFETY: n starts from self.len() / 2 and goes down to 0.
|
||||||
|
// The only case when !(n < self.len()) is if
|
||||||
|
// self.len() == 0, but it's ruled out by the loop condition.
|
||||||
|
unsafe { self.sift_down(n) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use super::super::{node, DeterministicRng};
|
use super::super::testing::crash_test::{CrashTestDummy, Panic};
|
||||||
|
use super::super::testing::ord_chaos::{Cyclic3, Governed, Governor};
|
||||||
|
use super::super::testing::rng::DeterministicRng;
|
||||||
use super::Entry::{Occupied, Vacant};
|
use super::Entry::{Occupied, Vacant};
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::boxed::Box;
|
use crate::boxed::Box;
|
||||||
|
@ -15,9 +17,6 @@ use std::ops::RangeBounds;
|
||||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||||
|
|
||||||
mod ord_chaos;
|
|
||||||
use ord_chaos::{Cyclic3, Governed, Governor};
|
|
||||||
|
|
||||||
// Capacity of a tree with a single level,
|
// Capacity of a tree with a single level,
|
||||||
// i.e., a tree who's root is a leaf node at height 0.
|
// i.e., a tree who's root is a leaf node at height 0.
|
||||||
const NODE_CAPACITY: usize = node::CAPACITY;
|
const NODE_CAPACITY: usize = node::CAPACITY;
|
||||||
|
@ -1136,91 +1135,62 @@ mod test_drain_filter {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn drop_panic_leak() {
|
fn drop_panic_leak() {
|
||||||
static PREDS: AtomicUsize = AtomicUsize::new(0);
|
let a = CrashTestDummy::new(0);
|
||||||
static DROPS: AtomicUsize = AtomicUsize::new(0);
|
let b = CrashTestDummy::new(1);
|
||||||
|
let c = CrashTestDummy::new(2);
|
||||||
|
let mut map = BTreeMap::new();
|
||||||
|
map.insert(a.spawn(Panic::Never), ());
|
||||||
|
map.insert(b.spawn(Panic::InDrop), ());
|
||||||
|
map.insert(c.spawn(Panic::Never), ());
|
||||||
|
|
||||||
struct D;
|
catch_unwind(move || drop(map.drain_filter(|dummy, _| dummy.query(true)))).unwrap_err();
|
||||||
impl Drop for D {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if DROPS.fetch_add(1, SeqCst) == 1 {
|
|
||||||
panic!("panic in `drop`");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keys are multiples of 4, so that each key is counted by a hexadecimal digit.
|
assert_eq!(a.queried(), 1);
|
||||||
let mut map = (0..3).map(|i| (i * 4, D)).collect::<BTreeMap<_, _>>();
|
assert_eq!(b.queried(), 1);
|
||||||
|
assert_eq!(c.queried(), 0);
|
||||||
catch_unwind(move || {
|
assert_eq!(a.dropped(), 1);
|
||||||
drop(map.drain_filter(|i, _| {
|
assert_eq!(b.dropped(), 1);
|
||||||
PREDS.fetch_add(1usize << i, SeqCst);
|
assert_eq!(c.dropped(), 1);
|
||||||
true
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
.unwrap_err();
|
|
||||||
|
|
||||||
assert_eq!(PREDS.load(SeqCst), 0x011);
|
|
||||||
assert_eq!(DROPS.load(SeqCst), 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pred_panic_leak() {
|
fn pred_panic_leak() {
|
||||||
static PREDS: AtomicUsize = AtomicUsize::new(0);
|
let a = CrashTestDummy::new(0);
|
||||||
static DROPS: AtomicUsize = AtomicUsize::new(0);
|
let b = CrashTestDummy::new(1);
|
||||||
|
let c = CrashTestDummy::new(2);
|
||||||
|
let mut map = BTreeMap::new();
|
||||||
|
map.insert(a.spawn(Panic::Never), ());
|
||||||
|
map.insert(b.spawn(Panic::InQuery), ());
|
||||||
|
map.insert(c.spawn(Panic::InQuery), ());
|
||||||
|
|
||||||
struct D;
|
catch_unwind(AssertUnwindSafe(|| drop(map.drain_filter(|dummy, _| dummy.query(true)))))
|
||||||
impl Drop for D {
|
.unwrap_err();
|
||||||
fn drop(&mut self) {
|
|
||||||
DROPS.fetch_add(1, SeqCst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keys are multiples of 4, so that each key is counted by a hexadecimal digit.
|
assert_eq!(a.queried(), 1);
|
||||||
let mut map = (0..3).map(|i| (i * 4, D)).collect::<BTreeMap<_, _>>();
|
assert_eq!(b.queried(), 1);
|
||||||
|
assert_eq!(c.queried(), 0);
|
||||||
catch_unwind(AssertUnwindSafe(|| {
|
assert_eq!(a.dropped(), 1);
|
||||||
drop(map.drain_filter(|i, _| {
|
assert_eq!(b.dropped(), 0);
|
||||||
PREDS.fetch_add(1usize << i, SeqCst);
|
assert_eq!(c.dropped(), 0);
|
||||||
match i {
|
|
||||||
0 => true,
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}))
|
|
||||||
.unwrap_err();
|
|
||||||
|
|
||||||
assert_eq!(PREDS.load(SeqCst), 0x011);
|
|
||||||
assert_eq!(DROPS.load(SeqCst), 1);
|
|
||||||
assert_eq!(map.len(), 2);
|
assert_eq!(map.len(), 2);
|
||||||
assert_eq!(map.first_entry().unwrap().key(), &4);
|
assert_eq!(map.first_entry().unwrap().key().id(), 1);
|
||||||
assert_eq!(map.last_entry().unwrap().key(), &8);
|
assert_eq!(map.last_entry().unwrap().key().id(), 2);
|
||||||
map.check();
|
map.check();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same as above, but attempt to use the iterator again after the panic in the predicate
|
// Same as above, but attempt to use the iterator again after the panic in the predicate
|
||||||
#[test]
|
#[test]
|
||||||
fn pred_panic_reuse() {
|
fn pred_panic_reuse() {
|
||||||
static PREDS: AtomicUsize = AtomicUsize::new(0);
|
let a = CrashTestDummy::new(0);
|
||||||
static DROPS: AtomicUsize = AtomicUsize::new(0);
|
let b = CrashTestDummy::new(1);
|
||||||
|
let c = CrashTestDummy::new(2);
|
||||||
struct D;
|
let mut map = BTreeMap::new();
|
||||||
impl Drop for D {
|
map.insert(a.spawn(Panic::Never), ());
|
||||||
fn drop(&mut self) {
|
map.insert(b.spawn(Panic::InQuery), ());
|
||||||
DROPS.fetch_add(1, SeqCst);
|
map.insert(c.spawn(Panic::InQuery), ());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keys are multiples of 4, so that each key is counted by a hexadecimal digit.
|
|
||||||
let mut map = (0..3).map(|i| (i * 4, D)).collect::<BTreeMap<_, _>>();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut it = map.drain_filter(|i, _| {
|
let mut it = map.drain_filter(|dummy, _| dummy.query(true));
|
||||||
PREDS.fetch_add(1usize << i, SeqCst);
|
|
||||||
match i {
|
|
||||||
0 => true,
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err();
|
catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err();
|
||||||
// Iterator behaviour after a panic is explicitly unspecified,
|
// Iterator behaviour after a panic is explicitly unspecified,
|
||||||
// so this is just the current implementation:
|
// so this is just the current implementation:
|
||||||
|
@ -1228,11 +1198,15 @@ mod test_drain_filter {
|
||||||
assert!(matches!(result, Ok(None)));
|
assert!(matches!(result, Ok(None)));
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(PREDS.load(SeqCst), 0x011);
|
assert_eq!(a.queried(), 1);
|
||||||
assert_eq!(DROPS.load(SeqCst), 1);
|
assert_eq!(b.queried(), 1);
|
||||||
|
assert_eq!(c.queried(), 0);
|
||||||
|
assert_eq!(a.dropped(), 1);
|
||||||
|
assert_eq!(b.dropped(), 0);
|
||||||
|
assert_eq!(c.dropped(), 0);
|
||||||
assert_eq!(map.len(), 2);
|
assert_eq!(map.len(), 2);
|
||||||
assert_eq!(map.first_entry().unwrap().key(), &4);
|
assert_eq!(map.first_entry().unwrap().key().id(), 1);
|
||||||
assert_eq!(map.last_entry().unwrap().key(), &8);
|
assert_eq!(map.last_entry().unwrap().key().id(), 2);
|
||||||
map.check();
|
map.check();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1439,6 +1413,43 @@ fn test_bad_zst() {
|
||||||
m.check();
|
m.check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_clear() {
|
||||||
|
let mut map = BTreeMap::new();
|
||||||
|
for &len in &[MIN_INSERTS_HEIGHT_1, MIN_INSERTS_HEIGHT_2, 0, NODE_CAPACITY] {
|
||||||
|
for i in 0..len {
|
||||||
|
map.insert(i, ());
|
||||||
|
}
|
||||||
|
assert_eq!(map.len(), len);
|
||||||
|
map.clear();
|
||||||
|
map.check();
|
||||||
|
assert!(map.is_empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_clear_drop_panic_leak() {
|
||||||
|
let a = CrashTestDummy::new(0);
|
||||||
|
let b = CrashTestDummy::new(1);
|
||||||
|
let c = CrashTestDummy::new(2);
|
||||||
|
|
||||||
|
let mut map = BTreeMap::new();
|
||||||
|
map.insert(a.spawn(Panic::Never), ());
|
||||||
|
map.insert(b.spawn(Panic::InDrop), ());
|
||||||
|
map.insert(c.spawn(Panic::Never), ());
|
||||||
|
|
||||||
|
catch_unwind(AssertUnwindSafe(|| map.clear())).unwrap_err();
|
||||||
|
assert_eq!(a.dropped(), 1);
|
||||||
|
assert_eq!(b.dropped(), 1);
|
||||||
|
assert_eq!(c.dropped(), 1);
|
||||||
|
assert_eq!(map.len(), 0);
|
||||||
|
|
||||||
|
drop(map);
|
||||||
|
assert_eq!(a.dropped(), 1);
|
||||||
|
assert_eq!(b.dropped(), 1);
|
||||||
|
assert_eq!(c.dropped(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_clone() {
|
fn test_clone() {
|
||||||
let mut map = BTreeMap::new();
|
let mut map = BTreeMap::new();
|
||||||
|
@ -1484,6 +1495,35 @@ fn test_clone() {
|
||||||
map.check();
|
map.check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_clone_panic_leak() {
|
||||||
|
let a = CrashTestDummy::new(0);
|
||||||
|
let b = CrashTestDummy::new(1);
|
||||||
|
let c = CrashTestDummy::new(2);
|
||||||
|
|
||||||
|
let mut map = BTreeMap::new();
|
||||||
|
map.insert(a.spawn(Panic::Never), ());
|
||||||
|
map.insert(b.spawn(Panic::InClone), ());
|
||||||
|
map.insert(c.spawn(Panic::Never), ());
|
||||||
|
|
||||||
|
catch_unwind(|| map.clone()).unwrap_err();
|
||||||
|
assert_eq!(a.cloned(), 1);
|
||||||
|
assert_eq!(b.cloned(), 1);
|
||||||
|
assert_eq!(c.cloned(), 0);
|
||||||
|
assert_eq!(a.dropped(), 1);
|
||||||
|
assert_eq!(b.dropped(), 0);
|
||||||
|
assert_eq!(c.dropped(), 0);
|
||||||
|
assert_eq!(map.len(), 3);
|
||||||
|
|
||||||
|
drop(map);
|
||||||
|
assert_eq!(a.cloned(), 1);
|
||||||
|
assert_eq!(b.cloned(), 1);
|
||||||
|
assert_eq!(c.cloned(), 0);
|
||||||
|
assert_eq!(a.dropped(), 2);
|
||||||
|
assert_eq!(b.dropped(), 1);
|
||||||
|
assert_eq!(c.dropped(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_clone_from() {
|
fn test_clone_from() {
|
||||||
let mut map1 = BTreeMap::new();
|
let mut map1 = BTreeMap::new();
|
||||||
|
@ -1901,29 +1941,21 @@ create_append_test!(test_append_1700, 1700);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_append_drop_leak() {
|
fn test_append_drop_leak() {
|
||||||
static DROPS: AtomicUsize = AtomicUsize::new(0);
|
let a = CrashTestDummy::new(0);
|
||||||
|
let b = CrashTestDummy::new(1);
|
||||||
struct D;
|
let c = CrashTestDummy::new(2);
|
||||||
|
|
||||||
impl Drop for D {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if DROPS.fetch_add(1, SeqCst) == 0 {
|
|
||||||
panic!("panic in `drop`");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut left = BTreeMap::new();
|
let mut left = BTreeMap::new();
|
||||||
let mut right = BTreeMap::new();
|
let mut right = BTreeMap::new();
|
||||||
left.insert(0, D);
|
left.insert(a.spawn(Panic::Never), ());
|
||||||
left.insert(1, D); // first to be dropped during append
|
left.insert(b.spawn(Panic::InDrop), ()); // first duplicate key, dropped during append
|
||||||
left.insert(2, D);
|
left.insert(c.spawn(Panic::Never), ());
|
||||||
right.insert(1, D);
|
right.insert(b.spawn(Panic::Never), ());
|
||||||
right.insert(2, D);
|
right.insert(c.spawn(Panic::Never), ());
|
||||||
|
|
||||||
catch_unwind(move || left.append(&mut right)).unwrap_err();
|
catch_unwind(move || left.append(&mut right)).unwrap_err();
|
||||||
|
assert_eq!(a.dropped(), 1);
|
||||||
assert_eq!(DROPS.load(SeqCst), 4); // Rust issue #47949 ate one little piggy
|
assert_eq!(b.dropped(), 1); // should be 2 were it not for Rust issue #47949
|
||||||
|
assert_eq!(c.dropped(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2050,51 +2082,42 @@ fn test_split_off_large_random_sorted() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_into_iter_drop_leak_height_0() {
|
fn test_into_iter_drop_leak_height_0() {
|
||||||
static DROPS: AtomicUsize = AtomicUsize::new(0);
|
let a = CrashTestDummy::new(0);
|
||||||
|
let b = CrashTestDummy::new(1);
|
||||||
struct D;
|
let c = CrashTestDummy::new(2);
|
||||||
|
let d = CrashTestDummy::new(3);
|
||||||
impl Drop for D {
|
let e = CrashTestDummy::new(4);
|
||||||
fn drop(&mut self) {
|
|
||||||
if DROPS.fetch_add(1, SeqCst) == 3 {
|
|
||||||
panic!("panic in `drop`");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut map = BTreeMap::new();
|
let mut map = BTreeMap::new();
|
||||||
map.insert("a", D);
|
map.insert("a", a.spawn(Panic::Never));
|
||||||
map.insert("b", D);
|
map.insert("b", b.spawn(Panic::Never));
|
||||||
map.insert("c", D);
|
map.insert("c", c.spawn(Panic::Never));
|
||||||
map.insert("d", D);
|
map.insert("d", d.spawn(Panic::InDrop));
|
||||||
map.insert("e", D);
|
map.insert("e", e.spawn(Panic::Never));
|
||||||
|
|
||||||
catch_unwind(move || drop(map.into_iter())).unwrap_err();
|
catch_unwind(move || drop(map.into_iter())).unwrap_err();
|
||||||
|
|
||||||
assert_eq!(DROPS.load(SeqCst), 5);
|
assert_eq!(a.dropped(), 1);
|
||||||
|
assert_eq!(b.dropped(), 1);
|
||||||
|
assert_eq!(c.dropped(), 1);
|
||||||
|
assert_eq!(d.dropped(), 1);
|
||||||
|
assert_eq!(e.dropped(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_into_iter_drop_leak_height_1() {
|
fn test_into_iter_drop_leak_height_1() {
|
||||||
let size = MIN_INSERTS_HEIGHT_1;
|
let size = MIN_INSERTS_HEIGHT_1;
|
||||||
static DROPS: AtomicUsize = AtomicUsize::new(0);
|
|
||||||
static PANIC_POINT: AtomicUsize = AtomicUsize::new(0);
|
|
||||||
|
|
||||||
struct D;
|
|
||||||
impl Drop for D {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if DROPS.fetch_add(1, SeqCst) == PANIC_POINT.load(SeqCst) {
|
|
||||||
panic!("panic in `drop`");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for panic_point in vec![0, 1, size - 2, size - 1] {
|
for panic_point in vec![0, 1, size - 2, size - 1] {
|
||||||
DROPS.store(0, SeqCst);
|
let dummies: Vec<_> = (0..size).map(|i| CrashTestDummy::new(i)).collect();
|
||||||
PANIC_POINT.store(panic_point, SeqCst);
|
let map: BTreeMap<_, _> = (0..size)
|
||||||
let map: BTreeMap<_, _> = (0..size).map(|i| (i, D)).collect();
|
.map(|i| {
|
||||||
|
let panic = if i == panic_point { Panic::InDrop } else { Panic::Never };
|
||||||
|
(dummies[i].spawn(Panic::Never), dummies[i].spawn(panic))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
catch_unwind(move || drop(map.into_iter())).unwrap_err();
|
catch_unwind(move || drop(map.into_iter())).unwrap_err();
|
||||||
assert_eq!(DROPS.load(SeqCst), size);
|
for i in 0..size {
|
||||||
|
assert_eq!(dummies[i].dropped(), 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,32 +20,4 @@ trait Recover<Q: ?Sized> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// XorShiftRng
|
mod testing;
|
||||||
struct DeterministicRng {
|
|
||||||
count: usize,
|
|
||||||
x: u32,
|
|
||||||
y: u32,
|
|
||||||
z: u32,
|
|
||||||
w: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl DeterministicRng {
|
|
||||||
fn new() -> Self {
|
|
||||||
DeterministicRng { count: 0, x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Guarantees that each returned number is unique.
|
|
||||||
fn next(&mut self) -> u32 {
|
|
||||||
self.count += 1;
|
|
||||||
assert!(self.count <= 70029);
|
|
||||||
let x = self.x;
|
|
||||||
let t = x ^ (x << 11);
|
|
||||||
self.x = self.y;
|
|
||||||
self.y = self.z;
|
|
||||||
self.z = self.w;
|
|
||||||
let w_ = self.w;
|
|
||||||
self.w = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8));
|
|
||||||
self.w
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use super::super::DeterministicRng;
|
use super::super::testing::crash_test::{CrashTestDummy, Panic};
|
||||||
|
use super::super::testing::rng::DeterministicRng;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::vec::Vec;
|
use crate::vec::Vec;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||||
use std::sync::atomic::{AtomicU32, Ordering::SeqCst};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_clone_eq() {
|
fn test_clone_eq() {
|
||||||
|
@ -349,70 +349,45 @@ fn test_drain_filter() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_drain_filter_drop_panic_leak() {
|
fn test_drain_filter_drop_panic_leak() {
|
||||||
static PREDS: AtomicU32 = AtomicU32::new(0);
|
let a = CrashTestDummy::new(0);
|
||||||
static DROPS: AtomicU32 = AtomicU32::new(0);
|
let b = CrashTestDummy::new(1);
|
||||||
|
let c = CrashTestDummy::new(2);
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
struct D(i32);
|
|
||||||
impl Drop for D {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if DROPS.fetch_add(1, SeqCst) == 1 {
|
|
||||||
panic!("panic in `drop`");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut set = BTreeSet::new();
|
let mut set = BTreeSet::new();
|
||||||
set.insert(D(0));
|
set.insert(a.spawn(Panic::Never));
|
||||||
set.insert(D(4));
|
set.insert(b.spawn(Panic::InDrop));
|
||||||
set.insert(D(8));
|
set.insert(c.spawn(Panic::Never));
|
||||||
|
|
||||||
catch_unwind(move || {
|
catch_unwind(move || drop(set.drain_filter(|dummy| dummy.query(true)))).ok();
|
||||||
drop(set.drain_filter(|d| {
|
|
||||||
PREDS.fetch_add(1u32 << d.0, SeqCst);
|
|
||||||
true
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
assert_eq!(PREDS.load(SeqCst), 0x011);
|
assert_eq!(a.queried(), 1);
|
||||||
assert_eq!(DROPS.load(SeqCst), 3);
|
assert_eq!(b.queried(), 1);
|
||||||
|
assert_eq!(c.queried(), 0);
|
||||||
|
assert_eq!(a.dropped(), 1);
|
||||||
|
assert_eq!(b.dropped(), 1);
|
||||||
|
assert_eq!(c.dropped(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_drain_filter_pred_panic_leak() {
|
fn test_drain_filter_pred_panic_leak() {
|
||||||
static PREDS: AtomicU32 = AtomicU32::new(0);
|
let a = CrashTestDummy::new(0);
|
||||||
static DROPS: AtomicU32 = AtomicU32::new(0);
|
let b = CrashTestDummy::new(1);
|
||||||
|
let c = CrashTestDummy::new(2);
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
struct D(i32);
|
|
||||||
impl Drop for D {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
DROPS.fetch_add(1, SeqCst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut set = BTreeSet::new();
|
let mut set = BTreeSet::new();
|
||||||
set.insert(D(0));
|
set.insert(a.spawn(Panic::Never));
|
||||||
set.insert(D(4));
|
set.insert(b.spawn(Panic::InQuery));
|
||||||
set.insert(D(8));
|
set.insert(c.spawn(Panic::InQuery));
|
||||||
|
|
||||||
catch_unwind(AssertUnwindSafe(|| {
|
catch_unwind(AssertUnwindSafe(|| drop(set.drain_filter(|dummy| dummy.query(true))))).ok();
|
||||||
drop(set.drain_filter(|d| {
|
|
||||||
PREDS.fetch_add(1u32 << d.0, SeqCst);
|
|
||||||
match d.0 {
|
|
||||||
0 => true,
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}))
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
assert_eq!(PREDS.load(SeqCst), 0x011);
|
assert_eq!(a.queried(), 1);
|
||||||
assert_eq!(DROPS.load(SeqCst), 1);
|
assert_eq!(b.queried(), 1);
|
||||||
|
assert_eq!(c.queried(), 0);
|
||||||
|
assert_eq!(a.dropped(), 1);
|
||||||
|
assert_eq!(b.dropped(), 0);
|
||||||
|
assert_eq!(c.dropped(), 0);
|
||||||
assert_eq!(set.len(), 2);
|
assert_eq!(set.len(), 2);
|
||||||
assert_eq!(set.first().unwrap().0, 4);
|
assert_eq!(set.first().unwrap().id(), 1);
|
||||||
assert_eq!(set.last().unwrap().0, 8);
|
assert_eq!(set.last().unwrap().id(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
119
library/alloc/src/collections/btree/testing/crash_test.rs
Normal file
119
library/alloc/src/collections/btree/testing/crash_test.rs
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
use crate::fmt::Debug;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||||
|
|
||||||
|
/// A blueprint for crash test dummy instances that monitor particular events.
|
||||||
|
/// Some instances may be configured to panic at some point.
|
||||||
|
/// Events are `clone`, `drop` or some anonymous `query`.
|
||||||
|
///
|
||||||
|
/// Crash test dummies are identified and ordered by an id, so they can be used
|
||||||
|
/// as keys in a BTreeMap. The implementation intentionally uses does not rely
|
||||||
|
/// on anything defined in the crate, apart from the `Debug` trait.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CrashTestDummy {
|
||||||
|
id: usize,
|
||||||
|
cloned: AtomicUsize,
|
||||||
|
dropped: AtomicUsize,
|
||||||
|
queried: AtomicUsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CrashTestDummy {
|
||||||
|
/// Creates a crash test dummy design. The `id` determines order and equality of instances.
|
||||||
|
pub fn new(id: usize) -> CrashTestDummy {
|
||||||
|
CrashTestDummy {
|
||||||
|
id,
|
||||||
|
cloned: AtomicUsize::new(0),
|
||||||
|
dropped: AtomicUsize::new(0),
|
||||||
|
queried: AtomicUsize::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an instance of a crash test dummy that records what events it experiences
|
||||||
|
/// and optionally panics.
|
||||||
|
pub fn spawn(&self, panic: Panic) -> Instance<'_> {
|
||||||
|
Instance { origin: self, panic }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns how many times instances of the dummy have been cloned.
|
||||||
|
pub fn cloned(&self) -> usize {
|
||||||
|
self.cloned.load(SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns how many times instances of the dummy have been dropped.
|
||||||
|
pub fn dropped(&self) -> usize {
|
||||||
|
self.dropped.load(SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns how many times instances of the dummy have had their `query` member invoked.
|
||||||
|
pub fn queried(&self) -> usize {
|
||||||
|
self.queried.load(SeqCst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Instance<'a> {
|
||||||
|
origin: &'a CrashTestDummy,
|
||||||
|
panic: Panic,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Panic {
|
||||||
|
Never,
|
||||||
|
InClone,
|
||||||
|
InDrop,
|
||||||
|
InQuery,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance<'_> {
|
||||||
|
pub fn id(&self) -> usize {
|
||||||
|
self.origin.id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Some anonymous query, the result of which is already given.
|
||||||
|
pub fn query<R>(&self, result: R) -> R {
|
||||||
|
self.origin.queried.fetch_add(1, SeqCst);
|
||||||
|
if self.panic == Panic::InQuery {
|
||||||
|
panic!("panic in `query`");
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Instance<'_> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
self.origin.cloned.fetch_add(1, SeqCst);
|
||||||
|
if self.panic == Panic::InClone {
|
||||||
|
panic!("panic in `clone`");
|
||||||
|
}
|
||||||
|
Self { origin: self.origin, panic: Panic::Never }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Instance<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.origin.dropped.fetch_add(1, SeqCst);
|
||||||
|
if self.panic == Panic::InDrop {
|
||||||
|
panic!("panic in `drop`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Instance<'_> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
self.id().partial_cmp(&other.id())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Instance<'_> {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.id().cmp(&other.id())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Instance<'_> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id().eq(&other.id())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Instance<'_> {}
|
3
library/alloc/src/collections/btree/testing/mod.rs
Normal file
3
library/alloc/src/collections/btree/testing/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod crash_test;
|
||||||
|
pub mod ord_chaos;
|
||||||
|
pub mod rng;
|
28
library/alloc/src/collections/btree/testing/rng.rs
Normal file
28
library/alloc/src/collections/btree/testing/rng.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/// XorShiftRng
|
||||||
|
pub struct DeterministicRng {
|
||||||
|
count: usize,
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
z: u32,
|
||||||
|
w: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeterministicRng {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
DeterministicRng { count: 0, x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Guarantees that each returned number is unique.
|
||||||
|
pub fn next(&mut self) -> u32 {
|
||||||
|
self.count += 1;
|
||||||
|
assert!(self.count <= 70029);
|
||||||
|
let x = self.x;
|
||||||
|
let t = x ^ (x << 11);
|
||||||
|
self.x = self.y;
|
||||||
|
self.y = self.z;
|
||||||
|
self.z = self.w;
|
||||||
|
let w_ = self.w;
|
||||||
|
self.w = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8));
|
||||||
|
self.w
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,8 +24,6 @@ impl CompletedTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for CompletedTest {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TestEvent {
|
pub enum TestEvent {
|
||||||
TeFiltered(Vec<TestDesc>),
|
TeFiltered(Vec<TestDesc>),
|
||||||
|
|
|
@ -222,10 +222,6 @@ impl<T: Write> OutputFormatter for PrettyFormatter<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
|
fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
|
||||||
if self.is_multithreaded {
|
|
||||||
self.write_test_name(desc)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.write_plain(&format!(
|
self.write_plain(&format!(
|
||||||
"test {} has been running for over {} seconds\n",
|
"test {} has been running for over {} seconds\n",
|
||||||
desc.name,
|
desc.name,
|
||||||
|
|
|
@ -24,8 +24,6 @@ pub enum TestResult {
|
||||||
TrTimedFail,
|
TrTimedFail,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for TestResult {}
|
|
||||||
|
|
||||||
/// Creates a `TestResult` depending on the raw result of test execution
|
/// Creates a `TestResult` depending on the raw result of test execution
|
||||||
/// and associated data.
|
/// and associated data.
|
||||||
pub fn calc_result<'a>(
|
pub fn calc_result<'a>(
|
||||||
|
|
|
@ -51,14 +51,40 @@ fn rm_rf(path: &Path) {
|
||||||
}
|
}
|
||||||
Ok(metadata) => {
|
Ok(metadata) => {
|
||||||
if metadata.file_type().is_file() || metadata.file_type().is_symlink() {
|
if metadata.file_type().is_file() || metadata.file_type().is_symlink() {
|
||||||
do_op(path, "remove file", |p| fs::remove_file(p));
|
do_op(path, "remove file", |p| {
|
||||||
|
fs::remove_file(p).or_else(|e| {
|
||||||
|
// Work around the fact that we cannot
|
||||||
|
// delete an executable while it runs on Windows.
|
||||||
|
#[cfg(windows)]
|
||||||
|
if e.kind() == std::io::ErrorKind::PermissionDenied
|
||||||
|
&& p.file_name().and_then(std::ffi::OsStr::to_str)
|
||||||
|
== Some("bootstrap.exe")
|
||||||
|
{
|
||||||
|
eprintln!("warning: failed to delete '{}'.", p.display());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e)
|
||||||
|
})
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for file in t!(fs::read_dir(path)) {
|
for file in t!(fs::read_dir(path)) {
|
||||||
rm_rf(&t!(file).path());
|
rm_rf(&t!(file).path());
|
||||||
}
|
}
|
||||||
do_op(path, "remove dir", |p| fs::remove_dir(p));
|
do_op(path, "remove dir", |p| {
|
||||||
|
fs::remove_dir(p).or_else(|e| {
|
||||||
|
// Check for dir not empty on Windows
|
||||||
|
#[cfg(windows)]
|
||||||
|
if matches!(e.kind(), std::io::ErrorKind::Other)
|
||||||
|
&& e.raw_os_error() == Some(145)
|
||||||
|
{
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(e)
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -73,12 +99,18 @@ where
|
||||||
// As a result, we have some special logic to remove readonly files on windows.
|
// As a result, we have some special logic to remove readonly files on windows.
|
||||||
// This is also the reason that we can't use things like fs::remove_dir_all().
|
// This is also the reason that we can't use things like fs::remove_dir_all().
|
||||||
Err(ref e) if cfg!(windows) && e.kind() == ErrorKind::PermissionDenied => {
|
Err(ref e) if cfg!(windows) && e.kind() == ErrorKind::PermissionDenied => {
|
||||||
let mut p = t!(path.symlink_metadata()).permissions();
|
let m = t!(path.symlink_metadata());
|
||||||
|
let mut p = m.permissions();
|
||||||
p.set_readonly(false);
|
p.set_readonly(false);
|
||||||
t!(fs::set_permissions(path, p));
|
t!(fs::set_permissions(path, p));
|
||||||
f(path).unwrap_or_else(|e| {
|
f(path).unwrap_or_else(|e| {
|
||||||
|
// Delete symlinked directories on Windows
|
||||||
|
#[cfg(windows)]
|
||||||
|
if m.file_type().is_symlink() && path.is_dir() && fs::remove_dir(path).is_ok() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
panic!("failed to {} {}: {}", desc, path.display(), e);
|
panic!("failed to {} {}: {}", desc, path.display(), e);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
panic!("failed to {} {}: {}", desc, path.display(), e);
|
panic!("failed to {} {}: {}", desc, path.display(), e);
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
use crate::Build;
|
use crate::Build;
|
||||||
use build_helper::{output, t};
|
use build_helper::{output, t};
|
||||||
use ignore::WalkBuilder;
|
use ignore::WalkBuilder;
|
||||||
use std::path::Path;
|
use std::collections::VecDeque;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
use std::sync::mpsc::SyncSender;
|
||||||
|
|
||||||
fn rustfmt(src: &Path, rustfmt: &Path, path: &Path, check: bool) {
|
fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl FnMut() {
|
||||||
let mut cmd = Command::new(&rustfmt);
|
let mut cmd = Command::new(&rustfmt);
|
||||||
// avoid the submodule config paths from coming into play,
|
// avoid the submodule config paths from coming into play,
|
||||||
// we only allow a single global config for the workspace for now
|
// 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) {
|
||||||
if check {
|
if check {
|
||||||
cmd.arg("--check");
|
cmd.arg("--check");
|
||||||
}
|
}
|
||||||
cmd.arg(&path);
|
cmd.args(paths);
|
||||||
let cmd_debug = format!("{:?}", cmd);
|
let cmd_debug = format!("{:?}", cmd);
|
||||||
let status = cmd.status().expect("executing rustfmt");
|
let mut cmd = cmd.spawn().expect("running rustfmt");
|
||||||
if !status.success() {
|
// poor man's async: return a closure that'll wait for rustfmt's completion
|
||||||
eprintln!(
|
move || {
|
||||||
"Running `{}` failed.\nIf you're running `tidy`, \
|
let status = cmd.wait().unwrap();
|
||||||
try again with `--bless`. Or, if you just want to format \
|
if !status.success() {
|
||||||
code, run `./x.py fmt` instead.",
|
eprintln!(
|
||||||
cmd_debug,
|
"Running `{}` failed.\nIf you're running `tidy`, \
|
||||||
);
|
try again with `--bless`. Or, if you just want to format \
|
||||||
std::process::exit(1);
|
code, run `./x.py fmt` instead.",
|
||||||
|
cmd_debug,
|
||||||
|
);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,19 +107,58 @@ pub fn format(build: &Build, check: bool) {
|
||||||
}
|
}
|
||||||
let ignore_fmt = ignore_fmt.build().unwrap();
|
let ignore_fmt = ignore_fmt.build().unwrap();
|
||||||
|
|
||||||
let rustfmt_path = build.config.initial_rustfmt.as_ref().unwrap_or_else(|| {
|
let rustfmt_path = build
|
||||||
eprintln!("./x.py fmt is not supported on this channel");
|
.config
|
||||||
std::process::exit(1);
|
.initial_rustfmt
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
eprintln!("./x.py fmt is not supported on this channel");
|
||||||
|
std::process::exit(1);
|
||||||
|
})
|
||||||
|
.to_path_buf();
|
||||||
|
let src = build.src.clone();
|
||||||
|
let (tx, rx): (SyncSender<PathBuf>, _) = std::sync::mpsc::sync_channel(128);
|
||||||
|
let walker =
|
||||||
|
WalkBuilder::new(src.clone()).types(matcher).overrides(ignore_fmt).build_parallel();
|
||||||
|
|
||||||
|
// there is a lot of blocking involved in spawning a child process and reading files to format.
|
||||||
|
// spawn more processes than available concurrency to keep the CPU busy
|
||||||
|
let max_processes = build.jobs() as usize * 2;
|
||||||
|
|
||||||
|
// spawn child processes on a separate thread so we can batch entries we have received from ignore
|
||||||
|
let thread = std::thread::spawn(move || {
|
||||||
|
let mut children = VecDeque::new();
|
||||||
|
while let Ok(path) = rx.recv() {
|
||||||
|
// try getting a few more paths from the channel to amortize the overhead of spawning processes
|
||||||
|
let paths: Vec<_> = rx.try_iter().take(7).chain(std::iter::once(path)).collect();
|
||||||
|
|
||||||
|
let child = rustfmt(&src, &rustfmt_path, paths.as_slice(), check);
|
||||||
|
children.push_back(child);
|
||||||
|
|
||||||
|
if children.len() >= max_processes {
|
||||||
|
// await oldest child
|
||||||
|
children.pop_front().unwrap()();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// await remaining children
|
||||||
|
for mut child in children {
|
||||||
|
child();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
let src = &build.src;
|
|
||||||
let walker = WalkBuilder::new(src).types(matcher).overrides(ignore_fmt).build_parallel();
|
|
||||||
walker.run(|| {
|
walker.run(|| {
|
||||||
|
let tx = tx.clone();
|
||||||
Box::new(move |entry| {
|
Box::new(move |entry| {
|
||||||
let entry = t!(entry);
|
let entry = t!(entry);
|
||||||
if entry.file_type().map_or(false, |t| t.is_file()) {
|
if entry.file_type().map_or(false, |t| t.is_file()) {
|
||||||
rustfmt(src, &rustfmt_path, &entry.path(), check);
|
t!(tx.send(entry.into_path()));
|
||||||
}
|
}
|
||||||
ignore::WalkState::Continue
|
ignore::WalkState::Continue
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
drop(tx);
|
||||||
|
|
||||||
|
thread.join().unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
mingw-w64
|
mingw-w64
|
||||||
|
|
||||||
|
RUN curl -sL https://nodejs.org/dist/v14.4.0/node-v14.4.0-linux-x64.tar.xz | tar -xJ
|
||||||
|
ENV PATH="/node-v14.4.0-linux-x64/bin:${PATH}"
|
||||||
|
# Install es-check
|
||||||
|
RUN npm install es-check -g
|
||||||
|
|
||||||
COPY scripts/sccache.sh /scripts/
|
COPY scripts/sccache.sh /scripts/
|
||||||
RUN sh /scripts/sccache.sh
|
RUN sh /scripts/sccache.sh
|
||||||
|
|
||||||
|
@ -29,4 +34,6 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \
|
||||||
python3 ../x.py test --stage 0 src/tools/compiletest && \
|
python3 ../x.py test --stage 0 src/tools/compiletest && \
|
||||||
python3 ../x.py test --stage 2 src/tools/tidy && \
|
python3 ../x.py test --stage 2 src/tools/tidy && \
|
||||||
python3 ../x.py doc --stage 0 library/std && \
|
python3 ../x.py doc --stage 0 library/std && \
|
||||||
/scripts/validate-toolstate.sh
|
/scripts/validate-toolstate.sh && \
|
||||||
|
# Runs checks to ensure that there are no ES5 issues in our JS code.
|
||||||
|
es-check es5 ../src/librustdoc/html/static/*.js
|
||||||
|
|
|
@ -153,6 +153,7 @@ not available.
|
||||||
target | std | host | notes
|
target | std | host | notes
|
||||||
-------|-----|------|-------
|
-------|-----|------|-------
|
||||||
`aarch64-apple-ios-macabi` | ? | | Apple Catalyst on ARM64
|
`aarch64-apple-ios-macabi` | ? | | Apple Catalyst on ARM64
|
||||||
|
`aarch64-apple-ios-sim` | ? | | Apple iOS Simulator on ARM64
|
||||||
`aarch64-apple-tvos` | * | | ARM64 tvOS
|
`aarch64-apple-tvos` | * | | ARM64 tvOS
|
||||||
`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
|
`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
|
||||||
`aarch64-unknown-hermit` | ? | |
|
`aarch64-unknown-hermit` | ? | |
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#![feature(try_blocks)]
|
#![feature(try_blocks)]
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let res: Result<u32, i32> = try {
|
let res: Result<u32, std::array::TryFromSliceError> = try {
|
||||||
Err("")?; //~ ERROR `?` couldn't convert the error
|
Err("")?; //~ ERROR `?` couldn't convert the error
|
||||||
5
|
5
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
error[E0277]: `?` couldn't convert the error to `i32`
|
error[E0277]: `?` couldn't convert the error to `TryFromSliceError`
|
||||||
--> $DIR/try-block-bad-type.rs:7:16
|
--> $DIR/try-block-bad-type.rs:7:16
|
||||||
|
|
|
|
||||||
LL | Err("")?;
|
LL | Err("")?;
|
||||||
| ^ the trait `From<&str>` is not implemented for `i32`
|
| ^ the trait `From<&str>` is not implemented for `TryFromSliceError`
|
||||||
|
|
|
|
||||||
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
|
||||||
= help: the following implementations were found:
|
= help: the following implementations were found:
|
||||||
<i32 as From<NonZeroI32>>
|
<TryFromSliceError as From<Infallible>>
|
||||||
<i32 as From<bool>>
|
|
||||||
<i32 as From<i16>>
|
|
||||||
<i32 as From<i8>>
|
|
||||||
and 2 others
|
|
||||||
= note: required by `from`
|
= note: required by `from`
|
||||||
|
|
||||||
error[E0271]: type mismatch resolving `<Result<i32, i32> as Try>::Ok == &str`
|
error[E0271]: type mismatch resolving `<Result<i32, i32> as Try>::Ok == &str`
|
||||||
|
|
Loading…
Add table
Reference in a new issue