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:
bors 2021-02-21 12:23:45 +00:00
commit 3e826bb112
24 changed files with 703 additions and 306 deletions

46
.github/ISSUE_TEMPLATE/diagnostics.md vendored Normal file
View 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.
-->

View file

@ -43,6 +43,23 @@ The following previously stable methods are now `const`.
- [`IpAddr::is_ipv4`]
- [`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::align`]
- [`Layout::from_size_align`]
@ -104,6 +121,23 @@ Compatibility Notes
[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_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::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

View file

@ -2193,6 +2193,7 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
("x86_64", "tvos") => "appletvsimulator",
("arm", "ios") => "iphoneos",
("aarch64", "ios") if llvm_target.contains("macabi") => "macosx",
("aarch64", "ios") if llvm_target.contains("sim") => "iphonesimulator",
("aarch64", "ios") => "iphoneos",
("x86", "ios") => "iphonesimulator",
("x86_64", "ios") if llvm_target.contains("macabi") => "macosx",

View 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
},
}
}

View file

@ -54,14 +54,16 @@ pub fn opts(os: &str) -> TargetOptions {
}
}
fn macos_deployment_target() -> (u32, u32) {
let deployment_target = env::var("MACOSX_DEPLOYMENT_TARGET").ok();
let version = deployment_target
fn deployment_target(var_name: &str) -> Option<(u32, u32)> {
let deployment_target = env::var(var_name).ok();
deployment_target
.as_ref()
.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 {
@ -84,3 +86,12 @@ pub fn macos_link_env_remove() -> Vec<String> {
env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".to_string());
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)
}

View file

@ -11,6 +11,7 @@ pub enum Arch {
X86_64,
X86_64_macabi,
Arm64_macabi,
Arm64_sim,
}
fn target_cpu(arch: Arch) -> String {
@ -22,13 +23,16 @@ fn target_cpu(arch: Arch) -> String {
X86_64 => "core2",
X86_64_macabi => "core2",
Arm64_macabi => "apple-a12",
Arm64_sim => "apple-a12",
}
.to_string()
}
fn link_env_remove(arch: Arch) -> Vec<String> {
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()],
}
}

View file

@ -727,6 +727,7 @@ supported_targets! {
("armv7s-apple-ios", armv7s_apple_ios),
("x86_64-apple-ios-macabi", x86_64_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),
("x86_64-apple-tvos", x86_64_apple_tvos),

View file

@ -275,7 +275,8 @@ impl<T: Ord + fmt::Debug> fmt::Debug for PeekMut<'_, T> {
impl<T: Ord> Drop for PeekMut<'_, T> {
fn drop(&mut self) {
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| {
if !self.is_empty() {
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
})
@ -473,7 +475,9 @@ impl<T: Ord> BinaryHeap<T> {
pub fn push(&mut self, item: T) {
let old_len = self.len();
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
@ -506,7 +510,10 @@ impl<T: Ord> BinaryHeap<T> {
let ptr = self.data.as_mut_ptr();
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()
}
@ -519,47 +526,84 @@ impl<T: Ord> BinaryHeap<T> {
// 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,
// 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 {
let parent = (hole.pos() - 1) / 2;
if hole.element() <= hole.get(parent) {
break;
}
hole.move_to(parent);
/// # Safety
///
/// The caller must guarantee that `pos < self.len()`.
unsafe fn sift_up(&mut self, start: usize, pos: usize) -> usize {
// Take out the value at `pos` and create a hole.
// 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,
/// while its children are larger.
fn sift_down_range(&mut self, pos: usize, end: usize) {
unsafe {
let mut hole = Hole::new(&mut self.data, pos);
let mut child = 2 * pos + 1;
while child < end - 1 {
// compare with the greater of the two children
child += (hole.get(child) <= hole.get(child + 1)) as usize;
// if we are already in order, stop.
if hole.element() >= hole.get(child) {
return;
}
hole.move_to(child);
child = 2 * hole.pos() + 1;
}
if child == end - 1 && hole.element() < hole.get(child) {
hole.move_to(child);
///
/// # Safety
///
/// The caller must guarantee that `pos < end <= self.len()`.
unsafe fn sift_down_range(&mut self, pos: usize, end: usize) {
// SAFETY: The caller guarantees that pos < end <= self.len().
let mut hole = unsafe { Hole::new(&mut self.data, pos) };
let mut child = 2 * hole.pos() + 1;
// Loop invariant: child == 2 * hole.pos() + 1.
while child < end - 1 {
// compare with the greater of the two children
// SAFETY: child < end - 1 < self.len() and
// child + 1 < end <= self.len(), so they're valid indexes.
// child == 2 * hole.pos() + 1 != hole.pos() and
// 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();
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,
@ -567,30 +611,54 @@ impl<T: Ord> BinaryHeap<T> {
///
/// Note: This is faster when the element is known to be large / should
/// 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 start = pos;
unsafe {
let mut hole = Hole::new(&mut self.data, pos);
let mut child = 2 * pos + 1;
while child < end - 1 {
child += (hole.get(child) <= hole.get(child + 1)) as usize;
hole.move_to(child);
child = 2 * hole.pos() + 1;
}
if child == end - 1 {
hole.move_to(child);
}
pos = hole.pos;
// SAFETY: The caller guarantees that pos < self.len().
let mut hole = unsafe { Hole::new(&mut self.data, pos) };
let mut child = 2 * hole.pos() + 1;
// Loop invariant: child == 2 * hole.pos() + 1.
while child < end - 1 {
// SAFETY: child < end - 1 < self.len() and
// child + 1 < end <= self.len(), so they're valid indexes.
// child == 2 * hole.pos() + 1 != hole.pos() and
// 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;
// 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) {
let mut n = self.len() / 2;
while n > 0 {
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) };
}
}

View file

@ -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::*;
use crate::boxed::Box;
@ -15,9 +17,6 @@ use std::ops::RangeBounds;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
mod ord_chaos;
use ord_chaos::{Cyclic3, Governed, Governor};
// Capacity of a tree with a single level,
// i.e., a tree who's root is a leaf node at height 0.
const NODE_CAPACITY: usize = node::CAPACITY;
@ -1136,91 +1135,62 @@ mod test_drain_filter {
#[test]
fn drop_panic_leak() {
static PREDS: AtomicUsize = AtomicUsize::new(0);
static DROPS: AtomicUsize = AtomicUsize::new(0);
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), ());
struct D;
impl Drop for D {
fn drop(&mut self) {
if DROPS.fetch_add(1, SeqCst) == 1 {
panic!("panic in `drop`");
}
}
}
catch_unwind(move || drop(map.drain_filter(|dummy, _| dummy.query(true)))).unwrap_err();
// 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<_, _>>();
catch_unwind(move || {
drop(map.drain_filter(|i, _| {
PREDS.fetch_add(1usize << i, SeqCst);
true
}))
})
.unwrap_err();
assert_eq!(PREDS.load(SeqCst), 0x011);
assert_eq!(DROPS.load(SeqCst), 3);
assert_eq!(a.queried(), 1);
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]
fn pred_panic_leak() {
static PREDS: AtomicUsize = AtomicUsize::new(0);
static DROPS: AtomicUsize = AtomicUsize::new(0);
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::InQuery), ());
map.insert(c.spawn(Panic::InQuery), ());
struct D;
impl Drop for D {
fn drop(&mut self) {
DROPS.fetch_add(1, SeqCst);
}
}
catch_unwind(AssertUnwindSafe(|| drop(map.drain_filter(|dummy, _| dummy.query(true)))))
.unwrap_err();
// 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<_, _>>();
catch_unwind(AssertUnwindSafe(|| {
drop(map.drain_filter(|i, _| {
PREDS.fetch_add(1usize << i, SeqCst);
match i {
0 => true,
_ => panic!(),
}
}))
}))
.unwrap_err();
assert_eq!(PREDS.load(SeqCst), 0x011);
assert_eq!(DROPS.load(SeqCst), 1);
assert_eq!(a.queried(), 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.first_entry().unwrap().key(), &4);
assert_eq!(map.last_entry().unwrap().key(), &8);
assert_eq!(map.first_entry().unwrap().key().id(), 1);
assert_eq!(map.last_entry().unwrap().key().id(), 2);
map.check();
}
// Same as above, but attempt to use the iterator again after the panic in the predicate
#[test]
fn pred_panic_reuse() {
static PREDS: AtomicUsize = AtomicUsize::new(0);
static DROPS: AtomicUsize = AtomicUsize::new(0);
struct D;
impl Drop for D {
fn drop(&mut self) {
DROPS.fetch_add(1, SeqCst);
}
}
// 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 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::InQuery), ());
map.insert(c.spawn(Panic::InQuery), ());
{
let mut it = map.drain_filter(|i, _| {
PREDS.fetch_add(1usize << i, SeqCst);
match i {
0 => true,
_ => panic!(),
}
});
let mut it = map.drain_filter(|dummy, _| dummy.query(true));
catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err();
// Iterator behaviour after a panic is explicitly unspecified,
// so this is just the current implementation:
@ -1228,11 +1198,15 @@ mod test_drain_filter {
assert!(matches!(result, Ok(None)));
}
assert_eq!(PREDS.load(SeqCst), 0x011);
assert_eq!(DROPS.load(SeqCst), 1);
assert_eq!(a.queried(), 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.first_entry().unwrap().key(), &4);
assert_eq!(map.last_entry().unwrap().key(), &8);
assert_eq!(map.first_entry().unwrap().key().id(), 1);
assert_eq!(map.last_entry().unwrap().key().id(), 2);
map.check();
}
}
@ -1439,6 +1413,43 @@ fn test_bad_zst() {
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]
fn test_clone() {
let mut map = BTreeMap::new();
@ -1484,6 +1495,35 @@ fn test_clone() {
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]
fn test_clone_from() {
let mut map1 = BTreeMap::new();
@ -1901,29 +1941,21 @@ create_append_test!(test_append_1700, 1700);
#[test]
fn test_append_drop_leak() {
static DROPS: AtomicUsize = AtomicUsize::new(0);
struct D;
impl Drop for D {
fn drop(&mut self) {
if DROPS.fetch_add(1, SeqCst) == 0 {
panic!("panic in `drop`");
}
}
}
let a = CrashTestDummy::new(0);
let b = CrashTestDummy::new(1);
let c = CrashTestDummy::new(2);
let mut left = BTreeMap::new();
let mut right = BTreeMap::new();
left.insert(0, D);
left.insert(1, D); // first to be dropped during append
left.insert(2, D);
right.insert(1, D);
right.insert(2, D);
left.insert(a.spawn(Panic::Never), ());
left.insert(b.spawn(Panic::InDrop), ()); // first duplicate key, dropped during append
left.insert(c.spawn(Panic::Never), ());
right.insert(b.spawn(Panic::Never), ());
right.insert(c.spawn(Panic::Never), ());
catch_unwind(move || left.append(&mut right)).unwrap_err();
assert_eq!(DROPS.load(SeqCst), 4); // Rust issue #47949 ate one little piggy
assert_eq!(a.dropped(), 1);
assert_eq!(b.dropped(), 1); // should be 2 were it not for Rust issue #47949
assert_eq!(c.dropped(), 2);
}
#[test]
@ -2050,51 +2082,42 @@ fn test_split_off_large_random_sorted() {
#[test]
fn test_into_iter_drop_leak_height_0() {
static DROPS: AtomicUsize = AtomicUsize::new(0);
struct D;
impl Drop for D {
fn drop(&mut self) {
if DROPS.fetch_add(1, SeqCst) == 3 {
panic!("panic in `drop`");
}
}
}
let a = CrashTestDummy::new(0);
let b = CrashTestDummy::new(1);
let c = CrashTestDummy::new(2);
let d = CrashTestDummy::new(3);
let e = CrashTestDummy::new(4);
let mut map = BTreeMap::new();
map.insert("a", D);
map.insert("b", D);
map.insert("c", D);
map.insert("d", D);
map.insert("e", D);
map.insert("a", a.spawn(Panic::Never));
map.insert("b", b.spawn(Panic::Never));
map.insert("c", c.spawn(Panic::Never));
map.insert("d", d.spawn(Panic::InDrop));
map.insert("e", e.spawn(Panic::Never));
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]
fn test_into_iter_drop_leak_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] {
DROPS.store(0, SeqCst);
PANIC_POINT.store(panic_point, SeqCst);
let map: BTreeMap<_, _> = (0..size).map(|i| (i, D)).collect();
let dummies: Vec<_> = (0..size).map(|i| CrashTestDummy::new(i)).collect();
let map: BTreeMap<_, _> = (0..size)
.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();
assert_eq!(DROPS.load(SeqCst), size);
for i in 0..size {
assert_eq!(dummies[i].dropped(), 2);
}
}
}

View file

@ -20,32 +20,4 @@ trait Recover<Q: ?Sized> {
}
#[cfg(test)]
/// XorShiftRng
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
}
}
mod testing;

View file

@ -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 crate::vec::Vec;
use std::cmp::Ordering;
use std::iter::FromIterator;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::sync::atomic::{AtomicU32, Ordering::SeqCst};
#[test]
fn test_clone_eq() {
@ -349,70 +349,45 @@ fn test_drain_filter() {
#[test]
fn test_drain_filter_drop_panic_leak() {
static PREDS: AtomicU32 = AtomicU32::new(0);
static DROPS: AtomicU32 = AtomicU32::new(0);
#[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 a = CrashTestDummy::new(0);
let b = CrashTestDummy::new(1);
let c = CrashTestDummy::new(2);
let mut set = BTreeSet::new();
set.insert(D(0));
set.insert(D(4));
set.insert(D(8));
set.insert(a.spawn(Panic::Never));
set.insert(b.spawn(Panic::InDrop));
set.insert(c.spawn(Panic::Never));
catch_unwind(move || {
drop(set.drain_filter(|d| {
PREDS.fetch_add(1u32 << d.0, SeqCst);
true
}))
})
.ok();
catch_unwind(move || drop(set.drain_filter(|dummy| dummy.query(true)))).ok();
assert_eq!(PREDS.load(SeqCst), 0x011);
assert_eq!(DROPS.load(SeqCst), 3);
assert_eq!(a.queried(), 1);
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]
fn test_drain_filter_pred_panic_leak() {
static PREDS: AtomicU32 = AtomicU32::new(0);
static DROPS: AtomicU32 = AtomicU32::new(0);
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct D(i32);
impl Drop for D {
fn drop(&mut self) {
DROPS.fetch_add(1, SeqCst);
}
}
let a = CrashTestDummy::new(0);
let b = CrashTestDummy::new(1);
let c = CrashTestDummy::new(2);
let mut set = BTreeSet::new();
set.insert(D(0));
set.insert(D(4));
set.insert(D(8));
set.insert(a.spawn(Panic::Never));
set.insert(b.spawn(Panic::InQuery));
set.insert(c.spawn(Panic::InQuery));
catch_unwind(AssertUnwindSafe(|| {
drop(set.drain_filter(|d| {
PREDS.fetch_add(1u32 << d.0, SeqCst);
match d.0 {
0 => true,
_ => panic!(),
}
}))
}))
.ok();
catch_unwind(AssertUnwindSafe(|| drop(set.drain_filter(|dummy| dummy.query(true))))).ok();
assert_eq!(PREDS.load(SeqCst), 0x011);
assert_eq!(DROPS.load(SeqCst), 1);
assert_eq!(a.queried(), 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.first().unwrap().0, 4);
assert_eq!(set.last().unwrap().0, 8);
assert_eq!(set.first().unwrap().id(), 1);
assert_eq!(set.last().unwrap().id(), 2);
}
#[test]

View 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<'_> {}

View file

@ -0,0 +1,3 @@
pub mod crash_test;
pub mod ord_chaos;
pub mod rng;

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

View file

@ -24,8 +24,6 @@ impl CompletedTest {
}
}
unsafe impl Send for CompletedTest {}
#[derive(Debug, Clone)]
pub enum TestEvent {
TeFiltered(Vec<TestDesc>),

View file

@ -222,10 +222,6 @@ impl<T: Write> OutputFormatter for PrettyFormatter<T> {
}
fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
if self.is_multithreaded {
self.write_test_name(desc)?;
}
self.write_plain(&format!(
"test {} has been running for over {} seconds\n",
desc.name,

View file

@ -24,8 +24,6 @@ pub enum TestResult {
TrTimedFail,
}
unsafe impl Send for TestResult {}
/// Creates a `TestResult` depending on the raw result of test execution
/// and associated data.
pub fn calc_result<'a>(

View file

@ -51,14 +51,40 @@ fn rm_rf(path: &Path) {
}
Ok(metadata) => {
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;
}
for file in t!(fs::read_dir(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.
// 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 => {
let mut p = t!(path.symlink_metadata()).permissions();
let m = t!(path.symlink_metadata());
let mut p = m.permissions();
p.set_readonly(false);
t!(fs::set_permissions(path, p));
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);
})
});
}
Err(e) => {
panic!("failed to {} {}: {}", desc, path.display(), e);

View file

@ -3,10 +3,12 @@
use crate::Build;
use build_helper::{output, t};
use ignore::WalkBuilder;
use std::path::Path;
use std::collections::VecDeque;
use std::path::{Path, PathBuf};
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);
// avoid the submodule config paths from coming into play,
// 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 {
cmd.arg("--check");
}
cmd.arg(&path);
cmd.args(paths);
let cmd_debug = format!("{:?}", cmd);
let status = cmd.status().expect("executing rustfmt");
if !status.success() {
eprintln!(
"Running `{}` failed.\nIf you're running `tidy`, \
try again with `--bless`. Or, if you just want to format \
code, run `./x.py fmt` instead.",
cmd_debug,
);
std::process::exit(1);
let mut cmd = cmd.spawn().expect("running rustfmt");
// poor man's async: return a closure that'll wait for rustfmt's completion
move || {
let status = cmd.wait().unwrap();
if !status.success() {
eprintln!(
"Running `{}` failed.\nIf you're running `tidy`, \
try again with `--bless`. Or, if you just want to format \
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 rustfmt_path = build.config.initial_rustfmt.as_ref().unwrap_or_else(|| {
eprintln!("./x.py fmt is not supported on this channel");
std::process::exit(1);
let rustfmt_path = build
.config
.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(|| {
let tx = tx.clone();
Box::new(move |entry| {
let entry = t!(entry);
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
})
});
drop(tx);
thread.join().unwrap();
}

View file

@ -17,6 +17,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
pkg-config \
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/
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 2 src/tools/tidy && \
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

View file

@ -153,6 +153,7 @@ not available.
target | std | host | notes
-------|-----|------|-------
`aarch64-apple-ios-macabi` | ? | | Apple Catalyst on ARM64
`aarch64-apple-ios-sim` | ? | | Apple iOS Simulator on ARM64
`aarch64-apple-tvos` | * | | ARM64 tvOS
`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
`aarch64-unknown-hermit` | ? | |

View file

@ -3,7 +3,7 @@
#![feature(try_blocks)]
pub fn main() {
let res: Result<u32, i32> = try {
let res: Result<u32, std::array::TryFromSliceError> = try {
Err("")?; //~ ERROR `?` couldn't convert the error
5
};

View file

@ -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
|
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
= help: the following implementations were found:
<i32 as From<NonZeroI32>>
<i32 as From<bool>>
<i32 as From<i16>>
<i32 as From<i8>>
and 2 others
<TryFromSliceError as From<Infallible>>
= note: required by `from`
error[E0271]: type mismatch resolving `<Result<i32, i32> as Try>::Ok == &str`