From d0862ec9b0c4107b773dcb0197f1877468ce14ad Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 2 Oct 2019 12:22:37 +0000 Subject: [PATCH 01/79] Inline `ptr::null(_mut)` even in debug builds --- src/libcore/ptr/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/ptr/mod.rs b/src/libcore/ptr/mod.rs index 933919185956..3cc0a1cd75e8 100644 --- a/src/libcore/ptr/mod.rs +++ b/src/libcore/ptr/mod.rs @@ -188,7 +188,7 @@ unsafe fn real_drop_in_place(to_drop: &mut T) { /// let p: *const i32 = ptr::null(); /// assert!(p.is_null()); /// ``` -#[inline] +#[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] pub const fn null() -> *const T { 0 as *const T } @@ -203,7 +203,7 @@ pub const fn null() -> *const T { 0 as *const T } /// let p: *mut i32 = ptr::null_mut(); /// assert!(p.is_null()); /// ``` -#[inline] +#[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] pub const fn null_mut() -> *mut T { 0 as *mut T } From a12788a0d77f1ad906bc1d51c4684dd8723278e0 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Thu, 22 Aug 2019 19:57:48 +0200 Subject: [PATCH 02/79] make is_power_of_two a const function --- src/libcore/num/mod.rs | 4 ++-- src/test/ui/consts/const-int-pow-rpass.rs | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/consts/const-int-pow-rpass.rs diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index ebde82de8345..82c1943b7d05 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -3749,8 +3749,8 @@ assert!(!10", stringify!($SelfT), ".is_power_of_two());", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn is_power_of_two(self) -> bool { - (self.wrapping_sub(1)) & self == 0 && !(self == 0) + pub const fn is_power_of_two(self) -> bool { + ((self.wrapping_sub(1)) & self == 0) & !(self == 0) } } diff --git a/src/test/ui/consts/const-int-pow-rpass.rs b/src/test/ui/consts/const-int-pow-rpass.rs new file mode 100644 index 000000000000..8e84a900605f --- /dev/null +++ b/src/test/ui/consts/const-int-pow-rpass.rs @@ -0,0 +1,11 @@ +// run-pass + +const IS_POWER_OF_TWO_A: bool = 0u32.is_power_of_two(); +const IS_POWER_OF_TWO_B: bool = 32u32.is_power_of_two(); +const IS_POWER_OF_TWO_C: bool = 33u32.is_power_of_two(); + +fn main() { + assert!(!IS_POWER_OF_TWO_A); + assert!(IS_POWER_OF_TWO_B); + assert!(!IS_POWER_OF_TWO_C); +} From 0a08841bb075959874e2e29c538150c826a1401a Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 3 Jul 2019 12:23:26 +0200 Subject: [PATCH 03/79] Remove uses of `allow(unions_with_drop_fields)` in the standard library --- src/libstd/panicking.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 638ce1679b8e..296f2a59bb82 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -12,7 +12,7 @@ use core::panic::{BoxMeUp, PanicInfo, Location}; use crate::any::Any; use crate::fmt; use crate::intrinsics; -use crate::mem; +use crate::mem::{self, ManuallyDrop}; use crate::ptr; use crate::raw; use crate::sync::atomic::{AtomicBool, Ordering}; @@ -227,10 +227,9 @@ pub use realstd::rt::update_panic_count; /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. pub unsafe fn r#try R>(f: F) -> Result> { - #[allow(unions_with_drop_fields)] union Data { - f: F, - r: R, + f: ManuallyDrop, + r: ManuallyDrop, } // We do some sketchy operations with ownership here for the sake of @@ -261,7 +260,7 @@ pub unsafe fn r#try R>(f: F) -> Result> let mut any_data = 0; let mut any_vtable = 0; let mut data = Data { - f, + f: ManuallyDrop::new(f) }; let r = __rust_maybe_catch_panic(do_call::, @@ -271,7 +270,7 @@ pub unsafe fn r#try R>(f: F) -> Result> return if r == 0 { debug_assert!(update_panic_count(0) == 0); - Ok(data.r) + Ok(ManuallyDrop::into_inner(data.r)) } else { update_panic_count(-1); debug_assert!(update_panic_count(0) == 0); @@ -284,8 +283,8 @@ pub unsafe fn r#try R>(f: F) -> Result> fn do_call R, R>(data: *mut u8) { unsafe { let data = data as *mut Data; - let f = ptr::read(&mut (*data).f); - ptr::write(&mut (*data).r, f()); + let f = ptr::read(&mut *(*data).f); + ptr::write(&mut *(*data).r, f()); } } } From 84ca0a1cb47f71a43ee16da2f6bc173577b35cb9 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 3 Jul 2019 12:35:02 +0200 Subject: [PATCH 04/79] Remove most uses of `allow(unions_with_drop_fields)` in tests --- .../ui/associated-type-bounds/union-bounds.rs | 15 ++--- src/test/ui/drop/dynamic-drop.rs | 12 ++-- .../rfc-2093-infer-outlives/explicit-union.rs | 5 +- .../rfc-2093-infer-outlives/nested-union.rs | 5 +- src/test/ui/self/self-in-typedefs.rs | 11 +++- .../union/union-borrow-move-parent-sibling.rs | 61 +++++++++++++++---- .../union-borrow-move-parent-sibling.stderr | 34 ++++------- src/test/ui/union/union-derive-rpass.rs | 5 +- src/test/ui/union/union-drop-assign.rs | 11 ++-- src/test/ui/union/union-drop.rs | 11 +--- src/test/ui/union/union-generic-rpass.rs | 12 +--- src/test/ui/union/union-nodrop.rs | 11 ++-- src/test/ui/union/union-overwrite.rs | 14 +++-- .../union-with-drop-fields-lint-rpass.rs | 32 ---------- 14 files changed, 118 insertions(+), 121 deletions(-) delete mode 100644 src/test/ui/union/union-with-drop-fields-lint-rpass.rs diff --git a/src/test/ui/associated-type-bounds/union-bounds.rs b/src/test/ui/associated-type-bounds/union-bounds.rs index ce482fff401c..97c5acf1f72c 100644 --- a/src/test/ui/associated-type-bounds/union-bounds.rs +++ b/src/test/ui/associated-type-bounds/union-bounds.rs @@ -3,13 +3,13 @@ #![feature(associated_type_bounds)] #![feature(untagged_unions)] -#![allow(unions_with_drop_fields, unused_assignments)] +#![allow(unused_assignments)] -trait Tr1 { type As1; } -trait Tr2 { type As2; } -trait Tr3 { type As3; } -trait Tr4<'a> { type As4; } -trait Tr5 { type As5; } +trait Tr1: Copy { type As1: Copy; } +trait Tr2: Copy { type As2: Copy; } +trait Tr3: Copy { type As3: Copy; } +trait Tr4<'a>: Copy { type As4: Copy; } +trait Tr5: Copy { type As5: Copy; } impl Tr1 for &str { type As1 = bool; } impl Tr2 for bool { type As2 = u8; } @@ -71,7 +71,8 @@ where let _: &'a T = &x.f0; } -union UnSelf where Self: Tr1 { +#[derive(Copy, Clone)] +union UnSelf where Self: Tr1, T: Copy { f0: T, f1: ::As1, f2: <::As1 as Tr2>::As2, diff --git a/src/test/ui/drop/dynamic-drop.rs b/src/test/ui/drop/dynamic-drop.rs index 8516bc3d9642..29dcbfe9609a 100644 --- a/src/test/ui/drop/dynamic-drop.rs +++ b/src/test/ui/drop/dynamic-drop.rs @@ -8,6 +8,7 @@ #![feature(slice_patterns)] use std::cell::{Cell, RefCell}; +use std::mem::ManuallyDrop; use std::ops::Generator; use std::panic; use std::pin::Pin; @@ -152,17 +153,16 @@ fn assignment1(a: &Allocator, c0: bool) { _v = _w; } -#[allow(unions_with_drop_fields)] union Boxy { - a: T, - b: T, + a: ManuallyDrop, + b: ManuallyDrop, } fn union1(a: &Allocator) { unsafe { - let mut u = Boxy { a: a.alloc() }; - u.b = a.alloc(); - drop(u.a); + let mut u = Boxy { a: ManuallyDrop::new(a.alloc()) }; + *u.b = a.alloc(); // drops first alloc + drop(ManuallyDrop::into_inner(u.a)); } } diff --git a/src/test/ui/rfc-2093-infer-outlives/explicit-union.rs b/src/test/ui/rfc-2093-infer-outlives/explicit-union.rs index a16fb7670a60..ea8a3c177e9d 100644 --- a/src/test/ui/rfc-2093-infer-outlives/explicit-union.rs +++ b/src/test/ui/rfc-2093-infer-outlives/explicit-union.rs @@ -1,13 +1,12 @@ #![feature(rustc_attrs)] #![feature(untagged_unions)] -#![allow(unions_with_drop_fields)] #[rustc_outlives] -union Foo<'b, U> { //~ ERROR rustc_outlives +union Foo<'b, U: Copy> { //~ ERROR rustc_outlives bar: Bar<'b, U> } -union Bar<'a, T> where T: 'a { +union Bar<'a, T: Copy> where T: 'a { x: &'a (), y: T, } diff --git a/src/test/ui/rfc-2093-infer-outlives/nested-union.rs b/src/test/ui/rfc-2093-infer-outlives/nested-union.rs index 0ff667fbeba6..0da3cc2ba1b0 100644 --- a/src/test/ui/rfc-2093-infer-outlives/nested-union.rs +++ b/src/test/ui/rfc-2093-infer-outlives/nested-union.rs @@ -1,14 +1,13 @@ #![feature(rustc_attrs)] #![feature(untagged_unions)] -#![allow(unions_with_drop_fields)] #[rustc_outlives] -union Foo<'a, T> { //~ ERROR rustc_outlives +union Foo<'a, T: Copy> { //~ ERROR rustc_outlives field1: Bar<'a, T> } // Type U needs to outlive lifetime 'b -union Bar<'b, U> { +union Bar<'b, U: Copy> { field2: &'b U } diff --git a/src/test/ui/self/self-in-typedefs.rs b/src/test/ui/self/self-in-typedefs.rs index 73f23a9cc17c..3b1eb9e1dfa1 100644 --- a/src/test/ui/self/self-in-typedefs.rs +++ b/src/test/ui/self/self-in-typedefs.rs @@ -3,7 +3,8 @@ #![feature(untagged_unions)] #![allow(dead_code)] -#![allow(unions_with_drop_fields)] + +use std::mem::ManuallyDrop; enum A<'a, T: 'a> where @@ -24,6 +25,14 @@ where union C<'a, T: 'a> where Self: Send, T: PartialEq +{ + foo: &'a Self, + bar: ManuallyDrop, +} + +union D<'a, T: 'a> +where + Self: Send, T: PartialEq + Copy { foo: &'a Self, bar: T, diff --git a/src/test/ui/union/union-borrow-move-parent-sibling.rs b/src/test/ui/union/union-borrow-move-parent-sibling.rs index 1b6052f10ba4..edf08e6ca678 100644 --- a/src/test/ui/union/union-borrow-move-parent-sibling.rs +++ b/src/test/ui/union/union-borrow-move-parent-sibling.rs @@ -1,51 +1,90 @@ #![feature(untagged_unions)] #![allow(unused)] -#[allow(unions_with_drop_fields)] +use std::ops::{Deref, DerefMut}; + +#[derive(Default)] +struct MockBox { + value: [T; 1], +} + +impl MockBox { + fn new(value: T) -> Self { MockBox { value: [value] } } +} + +impl Deref for MockBox { + type Target = T; + fn deref(&self) -> &T { &self.value[0] } +} + +impl DerefMut for MockBox { + fn deref_mut(&mut self) -> &mut T { &mut self.value[0] } +} + +#[derive(Default)] +struct MockVec { + value: [T; 0], +} + +impl MockVec { + fn new() -> Self { MockVec { value: [] } } +} + +impl Deref for MockVec { + type Target = [T]; + fn deref(&self) -> &[T] { &self.value } +} + +impl DerefMut for MockVec { + fn deref_mut(&mut self) -> &mut [T] { &mut self.value } +} + + union U { - x: ((Vec, Vec), Vec), - y: Box>, + x: ((MockVec, MockVec), MockVec), + y: MockBox>, } fn use_borrow(_: &T) {} unsafe fn parent_sibling_borrow() { - let mut u = U { x: ((Vec::new(), Vec::new()), Vec::new()) }; + let mut u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) }; let a = &mut u.x.0; let b = &u.y; //~ ERROR cannot borrow `u` (via `u.y`) use_borrow(a); } unsafe fn parent_sibling_move() { - let u = U { x: ((Vec::new(), Vec::new()), Vec::new()) }; + let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) }; let a = u.x.0; let b = u.y; //~ ERROR use of moved value: `u` } unsafe fn grandparent_sibling_borrow() { - let mut u = U { x: ((Vec::new(), Vec::new()), Vec::new()) }; + let mut u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) }; let a = &mut (u.x.0).0; let b = &u.y; //~ ERROR cannot borrow `u` (via `u.y`) use_borrow(a); } unsafe fn grandparent_sibling_move() { - let u = U { x: ((Vec::new(), Vec::new()), Vec::new()) }; + let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) }; let a = (u.x.0).0; let b = u.y; //~ ERROR use of moved value: `u` } unsafe fn deref_sibling_borrow() { - let mut u = U { y: Box::default() }; + let mut u = U { y: MockBox::default() }; let a = &mut *u.y; let b = &u.x; //~ ERROR cannot borrow `u` (via `u.x`) use_borrow(a); } unsafe fn deref_sibling_move() { - let u = U { x: ((Vec::new(), Vec::new()), Vec::new()) }; - let a = *u.y; - let b = u.x; //~ ERROR use of moved value: `u` + let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) }; + // No way to test deref-move without Box in union + // let a = *u.y; + // let b = u.x; ERROR use of moved value: `u` } diff --git a/src/test/ui/union/union-borrow-move-parent-sibling.stderr b/src/test/ui/union/union-borrow-move-parent-sibling.stderr index 2f4c921ea08f..1a7a02690d3e 100644 --- a/src/test/ui/union/union-borrow-move-parent-sibling.stderr +++ b/src/test/ui/union/union-borrow-move-parent-sibling.stderr @@ -1,5 +1,5 @@ error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0`) - --> $DIR/union-borrow-move-parent-sibling.rs:15:13 + --> $DIR/union-borrow-move-parent-sibling.rs:54:13 | LL | let a = &mut u.x.0; | ---------- mutable borrow occurs here (via `u.x.0`) @@ -11,9 +11,9 @@ LL | use_borrow(a); = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0` error[E0382]: use of moved value: `u` - --> $DIR/union-borrow-move-parent-sibling.rs:22:13 + --> $DIR/union-borrow-move-parent-sibling.rs:61:13 | -LL | let u = U { x: ((Vec::new(), Vec::new()), Vec::new()) }; +LL | let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) }; | - move occurs because `u` has type `U`, which does not implement the `Copy` trait LL | let a = u.x.0; | ----- value moved here @@ -21,7 +21,7 @@ LL | let b = u.y; | ^^^ value used here after move error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0.0`) - --> $DIR/union-borrow-move-parent-sibling.rs:28:13 + --> $DIR/union-borrow-move-parent-sibling.rs:67:13 | LL | let a = &mut (u.x.0).0; | -------------- mutable borrow occurs here (via `u.x.0.0`) @@ -33,38 +33,28 @@ LL | use_borrow(a); = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0.0` error[E0382]: use of moved value: `u` - --> $DIR/union-borrow-move-parent-sibling.rs:35:13 + --> $DIR/union-borrow-move-parent-sibling.rs:74:13 | -LL | let u = U { x: ((Vec::new(), Vec::new()), Vec::new()) }; +LL | let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) }; | - move occurs because `u` has type `U`, which does not implement the `Copy` trait LL | let a = (u.x.0).0; | --------- value moved here LL | let b = u.y; | ^^^ value used here after move -error[E0502]: cannot borrow `u` (via `u.x`) as immutable because it is also borrowed as mutable (via `*u.y`) - --> $DIR/union-borrow-move-parent-sibling.rs:41:13 +error[E0502]: cannot borrow `u` (via `u.x`) as immutable because it is also borrowed as mutable (via `u.y`) + --> $DIR/union-borrow-move-parent-sibling.rs:80:13 | LL | let a = &mut *u.y; - | --------- mutable borrow occurs here (via `*u.y`) + | --- mutable borrow occurs here (via `u.y`) LL | let b = &u.x; - | ^^^^ immutable borrow of `u.x` -- which overlaps with `*u.y` -- occurs here + | ^^^^ immutable borrow of `u.x` -- which overlaps with `u.y` -- occurs here LL | use_borrow(a); | - mutable borrow later used here | - = note: `u.x` is a field of the union `U`, so it overlaps the field `*u.y` + = note: `u.x` is a field of the union `U`, so it overlaps the field `u.y` -error[E0382]: use of moved value: `u` - --> $DIR/union-borrow-move-parent-sibling.rs:48:13 - | -LL | let u = U { x: ((Vec::new(), Vec::new()), Vec::new()) }; - | - move occurs because `u` has type `U`, which does not implement the `Copy` trait -LL | let a = *u.y; - | ---- value moved here -LL | let b = u.x; - | ^^^ value used here after move - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0382, E0502. For more information about an error, try `rustc --explain E0382`. diff --git a/src/test/ui/union/union-derive-rpass.rs b/src/test/ui/union/union-derive-rpass.rs index d4b82cdb250b..b2f7ae679fd6 100644 --- a/src/test/ui/union/union-derive-rpass.rs +++ b/src/test/ui/union/union-derive-rpass.rs @@ -1,7 +1,6 @@ // run-pass #![allow(dead_code)] #![allow(unused_variables)] -#![allow(unions_with_drop_fields)] // Some traits can be derived for unions. @@ -24,11 +23,11 @@ impl PartialEq for U { fn eq(&self, rhs: &Self) -> bool { true } } Copy, Eq )] -union W { +union W { a: T, } -impl PartialEq for W { fn eq(&self, rhs: &Self) -> bool { true } } +impl PartialEq for W { fn eq(&self, rhs: &Self) -> bool { true } } fn main() { let u = U { b: 0 }; diff --git a/src/test/ui/union/union-drop-assign.rs b/src/test/ui/union/union-drop-assign.rs index c4349c454641..f1511b0a6018 100644 --- a/src/test/ui/union/union-drop-assign.rs +++ b/src/test/ui/union/union-drop-assign.rs @@ -1,15 +1,16 @@ // run-pass #![allow(unused_assignments)] -#![allow(unions_with_drop_fields)] // Drop works for union itself. #![feature(untagged_unions)] +use std::mem::ManuallyDrop; + struct S; union U { - a: S + a: ManuallyDrop } impl Drop for S { @@ -28,11 +29,11 @@ static mut CHECK: u8 = 0; fn main() { unsafe { - let mut u = U { a: S }; + let mut u = U { a: ManuallyDrop::new(S) }; assert_eq!(CHECK, 0); - u = U { a: S }; + u = U { a: ManuallyDrop::new(S) }; assert_eq!(CHECK, 1); // union itself is assigned, union is dropped, field is not dropped - u.a = S; + *u.a = S; assert_eq!(CHECK, 11); // union field is assigned, field is dropped } } diff --git a/src/test/ui/union/union-drop.rs b/src/test/ui/union/union-drop.rs index 2060950dda95..daa03ce6b6fd 100644 --- a/src/test/ui/union/union-drop.rs +++ b/src/test/ui/union/union-drop.rs @@ -1,7 +1,6 @@ // run-pass #![allow(dead_code)] #![allow(unused_variables)] -#![allow(unions_with_drop_fields)] // Drop works for union itself. @@ -21,12 +20,6 @@ union Y { a: S, } -impl Drop for S { - fn drop(&mut self) { - unsafe { CHECK += 10; } - } -} - impl Drop for U { fn drop(&mut self) { unsafe { CHECK += 1; } @@ -51,10 +44,10 @@ fn main() { { let w = W { a: S }; } - assert_eq!(CHECK, 2); // 2, not 11, dtor of S is not called + assert_eq!(CHECK, 2); // 2, dtor of W is called { let y = Y { a: S }; } - assert_eq!(CHECK, 2); // 2, not 12, dtor of S is not called + assert_eq!(CHECK, 2); // 2, dtor of Y is called } } diff --git a/src/test/ui/union/union-generic-rpass.rs b/src/test/ui/union/union-generic-rpass.rs index 6f2caf8dc5b1..5ca3bc0f722c 100644 --- a/src/test/ui/union/union-generic-rpass.rs +++ b/src/test/ui/union/union-generic-rpass.rs @@ -1,30 +1,24 @@ // run-pass #![allow(dead_code)] -#![allow(unions_with_drop_fields)] #![feature(untagged_unions)] -union MaybeItem { +union MaybeItem where T::Item: Copy { elem: T::Item, none: (), } -union U { +union U where A: Copy, B: Copy { a: A, b: B, } -unsafe fn union_transmute(a: A) -> B { +unsafe fn union_transmute(a: A) -> B where A: Copy, B: Copy { U { a: a }.b } fn main() { unsafe { - let u = U::> { a: String::from("abcd") }; - - assert_eq!(u.b.len(), 4); - assert_eq!(u.b[0], b'a'); - let b = union_transmute::<(u8, u8), u16>((1, 1)); assert_eq!(b, (1 << 8) + 1); diff --git a/src/test/ui/union/union-nodrop.rs b/src/test/ui/union/union-nodrop.rs index 4cd64ddb5d67..330f6f9593b1 100644 --- a/src/test/ui/union/union-nodrop.rs +++ b/src/test/ui/union/union-nodrop.rs @@ -1,12 +1,11 @@ // run-pass -#![feature(core_intrinsics)] #![feature(untagged_unions)] -#![allow(unions_with_drop_fields)] #![allow(dead_code)] -use std::intrinsics::needs_drop; +use std::mem::needs_drop; +use std::mem::ManuallyDrop; struct NeedDrop; @@ -16,10 +15,10 @@ impl Drop for NeedDrop { // Constant expressios allow `NoDrop` to go out of scope, // unlike a value of the interior type implementing `Drop`. -static X: () = (NoDrop { inner: NeedDrop }, ()).1; +static X: () = (NoDrop { inner: ManuallyDrop::new(NeedDrop) }, ()).1; // A union that scrubs the drop glue from its inner type -union NoDrop {inner: T} +union NoDrop { inner: ManuallyDrop } // Copy currently can't be implemented on drop-containing unions, // this may change later @@ -40,7 +39,7 @@ struct Baz { y: Box, } -union ActuallyDrop {inner: T} +union ActuallyDrop { inner: ManuallyDrop } impl Drop for ActuallyDrop { fn drop(&mut self) {} diff --git a/src/test/ui/union/union-overwrite.rs b/src/test/ui/union/union-overwrite.rs index 64c60604ba9f..8234beb74a82 100644 --- a/src/test/ui/union/union-overwrite.rs +++ b/src/test/ui/union/union-overwrite.rs @@ -1,21 +1,27 @@ // run-pass -#![allow(unions_with_drop_fields)] - #![feature(untagged_unions)] #[repr(C)] +#[derive(Copy, Clone)] struct Pair(T, U); #[repr(C)] +#[derive(Copy, Clone)] struct Triple(T, T, T); #[repr(C)] -union U { +union U +where + A: Copy, B: Copy +{ a: Pair, b: B, } #[repr(C)] -union W { +union W +where + A: Copy, B: Copy +{ a: A, b: B, } diff --git a/src/test/ui/union/union-with-drop-fields-lint-rpass.rs b/src/test/ui/union/union-with-drop-fields-lint-rpass.rs deleted file mode 100644 index 4dbeb7c1e7e9..000000000000 --- a/src/test/ui/union/union-with-drop-fields-lint-rpass.rs +++ /dev/null @@ -1,32 +0,0 @@ -// run-pass - -#![feature(untagged_unions)] -#![allow(dead_code)] -#![allow(unions_with_drop_fields)] - -union U { - a: u8, // OK -} - -union W { - a: String, // OK - b: String, // OK -} - -struct S(String); - -// `S` doesn't implement `Drop` trait, but still has non-trivial destructor -union Y { - a: S, // OK -} - -// We don't know if `T` is trivially-destructable or not until trans -union J { - a: T, // OK -} - -union H { - a: T, // OK -} - -fn main() {} From 2f0c821be9ba9cdf52a45c327b7d3f2831626225 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 19 Dec 2018 20:58:20 +0100 Subject: [PATCH 05/79] Change untagged_unions to not allow union fields with drop Union fields may now never have a type with attached destructor. This for example allows unions to use arbitrary field types only by wrapping them in ManuallyDrop. The stable rule remains, that union fields must be Copy. We use the new rule for the `untagged_union` feature. See RFC 2514. Note for ui tests: We can't test move out through Box's deref-move since we can't have a Box in a union anymore. --- src/librustc/Cargo.toml | 2 +- src/librustc/ty/util.rs | 2 + src/librustc_typeck/check/mod.rs | 29 ++++++++ src/librustc_typeck/error_codes.rs | 4 ++ src/test/run-pass/union/union-manuallydrop.rs | 42 ++++++++++++ .../feature-gate-associated_type_bounds.rs | 6 +- .../explicit-union.stderr | 4 +- .../nested-union.stderr | 4 +- .../union-borrow-move-parent-sibling.stderr | 10 +-- src/test/ui/union/union-derive-clone.rs | 13 +++- src/test/ui/union/union-derive-clone.stderr | 10 +-- src/test/ui/union/union-unsafe.rs | 34 ++++++---- src/test/ui/union/union-unsafe.stderr | 66 +++++++++++++++---- .../union/union-with-drop-fields-lint.stderr | 26 -------- ...elds-lint.rs => union-with-drop-fields.rs} | 7 +- .../ui/union/union-with-drop-fields.stderr | 39 +++++++++++ 16 files changed, 222 insertions(+), 76 deletions(-) create mode 100644 src/test/run-pass/union/union-manuallydrop.rs delete mode 100644 src/test/ui/union/union-with-drop-fields-lint.stderr rename src/test/ui/union/{union-with-drop-fields-lint.rs => union-with-drop-fields.rs} (60%) create mode 100644 src/test/ui/union/union-with-drop-fields.stderr diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index 9b3609eca3e6..8f191961fdd0 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -35,5 +35,5 @@ parking_lot = "0.9" byteorder = { version = "1.3" } chalk-engine = { version = "0.9.0", default-features=false } rustc_fs_util = { path = "../librustc_fs_util" } -smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } +smallvec = { version = "0.6.8", features = ["union", "may_dangle"] } measureme = "0.3" diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index d0e95a18c59f..bd0d5846d958 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -805,6 +805,8 @@ impl<'tcx> ty::TyS<'tcx> { /// /// (Note that this implies that if `ty` has a destructor attached, /// then `needs_drop` will definitely return `true` for `ty`.) + /// + /// Note that this method is used to check eligible types in unions. #[inline] pub fn needs_drop(&'tcx self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { tcx.needs_drop_raw(param_env.and(self)).0 diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 7475b9cc3b32..2f122e5edb1c 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1387,9 +1387,38 @@ fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { def.destructor(tcx); // force the destructor to be evaluated check_representable(tcx, span, def_id); check_transparent(tcx, span, def_id); + check_union_fields(tcx, span, def_id); check_packed(tcx, span, def_id); } +fn check_union_fields<'tcx>(tcx: TyCtxt<'tcx>, _sp: Span, item_def_id: DefId) -> bool { + // Without the feature we check Copy types only later + if !tcx.features().untagged_unions { + return true; + } + let t = tcx.type_of(item_def_id); + if let ty::Adt(def, substs) = t.sty { + if def.is_union() { + let fields = &def.non_enum_variant().fields; + for field in fields { + let field_ty = field.ty(tcx, substs); + // We are currently checking the type this field came from, so it must be local + let field_span = tcx.hir().span_if_local(field.did).unwrap(); + let param_env = tcx.param_env(field.did); + if field_ty.needs_drop(tcx, param_env) { + struct_span_err!(tcx.sess, field_span, E0740, + "unions may not contain fields that need dropping") + .span_note(field_span, + "`std::mem::ManuallyDrop` can be used to wrap the type") + .emit(); + return false; + } + } + } + } + return true; +} + /// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` /// projections that would result in "inheriting lifetimes". fn check_opaque<'tcx>( diff --git a/src/librustc_typeck/error_codes.rs b/src/librustc_typeck/error_codes.rs index ef08e8d4f0b7..a92a2c960039 100644 --- a/src/librustc_typeck/error_codes.rs +++ b/src/librustc_typeck/error_codes.rs @@ -4863,6 +4863,10 @@ assert_eq!(1, discriminant(&Enum::Struct{a: 7, b: 11})); ``` "##, +E0740: r##" +A `union` can not have fields with destructors. +"##, + E0733: r##" Recursion in an `async fn` requires boxing. For example, this will not compile: diff --git a/src/test/run-pass/union/union-manuallydrop.rs b/src/test/run-pass/union/union-manuallydrop.rs new file mode 100644 index 000000000000..a43a50508656 --- /dev/null +++ b/src/test/run-pass/union/union-manuallydrop.rs @@ -0,0 +1,42 @@ +#![feature(untagged_unions)] +#![allow(dead_code)] +// run-pass + +use std::mem::needs_drop; +use std::mem::ManuallyDrop; + +struct NeedDrop; + +impl Drop for NeedDrop { + fn drop(&mut self) {} +} + +union UnionOk1 { + empty: (), + value: ManuallyDrop, +} + +union UnionOk2 { + value: ManuallyDrop, +} + +#[allow(dead_code)] +union UnionOk3 { + empty: (), + value: T, +} + +trait Foo { } + +trait ImpliesCopy : Copy { } + +#[allow(dead_code)] +union UnionOk4 { + value: T, +} + +fn main() { + // NeedDrop should not make needs_drop true + assert!(!needs_drop::>()); + assert!(!needs_drop::>()); +} diff --git a/src/test/ui/feature-gates/feature-gate-associated_type_bounds.rs b/src/test/ui/feature-gates/feature-gate-associated_type_bounds.rs index 3d75251e1561..0faa9090f4eb 100644 --- a/src/test/ui/feature-gates/feature-gate-associated_type_bounds.rs +++ b/src/test/ui/feature-gates/feature-gate-associated_type_bounds.rs @@ -1,7 +1,7 @@ #![feature(untagged_unions)] -trait Tr1 { type As1; } -trait Tr2 { type As2; } +trait Tr1 { type As1: Copy; } +trait Tr2 { type As2: Copy; } struct S1; #[derive(Copy, Clone)] @@ -32,7 +32,7 @@ enum _En1> { union _Un1> { //~^ ERROR associated type bounds are unstable - outest: T, + outest: std::mem::ManuallyDrop, outer: T::As1, inner: ::As2, } diff --git a/src/test/ui/rfc-2093-infer-outlives/explicit-union.stderr b/src/test/ui/rfc-2093-infer-outlives/explicit-union.stderr index adf62651cacb..8aa246e8bfeb 100644 --- a/src/test/ui/rfc-2093-infer-outlives/explicit-union.stderr +++ b/src/test/ui/rfc-2093-infer-outlives/explicit-union.stderr @@ -1,7 +1,7 @@ error: rustc_outlives - --> $DIR/explicit-union.rs:6:1 + --> $DIR/explicit-union.rs:5:1 | -LL | / union Foo<'b, U> { +LL | / union Foo<'b, U: Copy> { LL | | bar: Bar<'b, U> LL | | } | |_^ diff --git a/src/test/ui/rfc-2093-infer-outlives/nested-union.stderr b/src/test/ui/rfc-2093-infer-outlives/nested-union.stderr index fc4b1a223294..a42285a56d08 100644 --- a/src/test/ui/rfc-2093-infer-outlives/nested-union.stderr +++ b/src/test/ui/rfc-2093-infer-outlives/nested-union.stderr @@ -1,7 +1,7 @@ error: rustc_outlives - --> $DIR/nested-union.rs:6:1 + --> $DIR/nested-union.rs:5:1 | -LL | / union Foo<'a, T> { +LL | / union Foo<'a, T: Copy> { LL | | field1: Bar<'a, T> LL | | } | |_^ diff --git a/src/test/ui/union/union-borrow-move-parent-sibling.stderr b/src/test/ui/union/union-borrow-move-parent-sibling.stderr index 1a7a02690d3e..8ba155bafb0b 100644 --- a/src/test/ui/union/union-borrow-move-parent-sibling.stderr +++ b/src/test/ui/union/union-borrow-move-parent-sibling.stderr @@ -1,5 +1,5 @@ error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0`) - --> $DIR/union-borrow-move-parent-sibling.rs:54:13 + --> $DIR/union-borrow-move-parent-sibling.rs:53:13 | LL | let a = &mut u.x.0; | ---------- mutable borrow occurs here (via `u.x.0`) @@ -11,7 +11,7 @@ LL | use_borrow(a); = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0` error[E0382]: use of moved value: `u` - --> $DIR/union-borrow-move-parent-sibling.rs:61:13 + --> $DIR/union-borrow-move-parent-sibling.rs:60:13 | LL | let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) }; | - move occurs because `u` has type `U`, which does not implement the `Copy` trait @@ -21,7 +21,7 @@ LL | let b = u.y; | ^^^ value used here after move error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0.0`) - --> $DIR/union-borrow-move-parent-sibling.rs:67:13 + --> $DIR/union-borrow-move-parent-sibling.rs:66:13 | LL | let a = &mut (u.x.0).0; | -------------- mutable borrow occurs here (via `u.x.0.0`) @@ -33,7 +33,7 @@ LL | use_borrow(a); = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0.0` error[E0382]: use of moved value: `u` - --> $DIR/union-borrow-move-parent-sibling.rs:74:13 + --> $DIR/union-borrow-move-parent-sibling.rs:73:13 | LL | let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) }; | - move occurs because `u` has type `U`, which does not implement the `Copy` trait @@ -43,7 +43,7 @@ LL | let b = u.y; | ^^^ value used here after move error[E0502]: cannot borrow `u` (via `u.x`) as immutable because it is also borrowed as mutable (via `u.y`) - --> $DIR/union-borrow-move-parent-sibling.rs:80:13 + --> $DIR/union-borrow-move-parent-sibling.rs:79:13 | LL | let a = &mut *u.y; | --- mutable borrow occurs here (via `u.y`) diff --git a/src/test/ui/union/union-derive-clone.rs b/src/test/ui/union/union-derive-clone.rs index 64c3caef4490..60e280f53f52 100644 --- a/src/test/ui/union/union-derive-clone.rs +++ b/src/test/ui/union/union-derive-clone.rs @@ -1,5 +1,7 @@ #![feature(untagged_unions)] +use std::mem::ManuallyDrop; + #[derive(Clone)] //~ ERROR the trait bound `U1: std::marker::Copy` is not satisfied union U1 { a: u8, @@ -18,14 +20,19 @@ union U3 { } #[derive(Clone, Copy)] -union U4 { +union U4 { a: T, // OK } +#[derive(Clone, Copy)] +union U5 { + a: ManuallyDrop, // OK +} + #[derive(Clone)] struct CloneNoCopy; fn main() { - let u = U4 { a: CloneNoCopy }; - let w = u.clone(); //~ ERROR no method named `clone` found for type `U4` + let u = U5 { a: ManuallyDrop::new(CloneNoCopy) }; + let w = u.clone(); //~ ERROR no method named `clone` found for type `U5` } diff --git a/src/test/ui/union/union-derive-clone.stderr b/src/test/ui/union/union-derive-clone.stderr index 4f4c779b12bb..a6dcd556a1fe 100644 --- a/src/test/ui/union/union-derive-clone.stderr +++ b/src/test/ui/union/union-derive-clone.stderr @@ -1,22 +1,22 @@ error[E0277]: the trait bound `U1: std::marker::Copy` is not satisfied - --> $DIR/union-derive-clone.rs:3:10 + --> $DIR/union-derive-clone.rs:5:10 | LL | #[derive(Clone)] | ^^^^^ the trait `std::marker::Copy` is not implemented for `U1` | = note: required by `std::clone::AssertParamIsCopy` -error[E0599]: no method named `clone` found for type `U4` in the current scope - --> $DIR/union-derive-clone.rs:30:15 +error[E0599]: no method named `clone` found for type `U5` in the current scope + --> $DIR/union-derive-clone.rs:37:15 | -LL | union U4 { +LL | union U5 { | ----------- method `clone` not found for this ... LL | let w = u.clone(); | ^^^^^ method not found in `U4` | = note: the method `clone` exists but the following trait bounds were not satisfied: - `U4 : std::clone::Clone` + `U5 : std::clone::Clone` = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: candidate #1: `std::clone::Clone` diff --git a/src/test/ui/union/union-unsafe.rs b/src/test/ui/union/union-unsafe.rs index 6cfde35fe4c3..8535cbd019ce 100644 --- a/src/test/ui/union/union-unsafe.rs +++ b/src/test/ui/union/union-unsafe.rs @@ -1,15 +1,16 @@ #![feature(untagged_unions)] +use std::mem::ManuallyDrop; union U1 { a: u8 } union U2 { - a: String + a: ManuallyDrop } union U3 { - a: T + a: ManuallyDrop } union U4 { @@ -17,13 +18,16 @@ union U4 { } fn generic_noncopy() { - let mut u3 = U3 { a: T::default() }; - u3.a = T::default(); //~ ERROR assignment to non-`Copy` union field is unsafe + let mut u3 = U3 { a: ManuallyDrop::new(T::default()) }; + u3.a = ManuallyDrop::new(T::default()); //~ ERROR assignment to non-`Copy` union field is unsafe + *u3.a = T::default(); //~ ERROR access to union field is unsafe } fn generic_copy() { - let mut u3 = U3 { a: T::default() }; - u3.a = T::default(); // OK + let mut u3 = U3 { a: ManuallyDrop::new(T::default()) }; + u3.a = ManuallyDrop::new(T::default()); // OK + *u3.a = T::default(); //~ ERROR access to union field is unsafe + let mut u4 = U4 { a: T::default() }; u4.a = T::default(); // OK } @@ -32,14 +36,20 @@ fn main() { let mut u1 = U1 { a: 10 }; // OK let a = u1.a; //~ ERROR access to union field is unsafe u1.a = 11; // OK + let U1 { a } = u1; //~ ERROR access to union field is unsafe if let U1 { a: 12 } = u1 {} //~ ERROR access to union field is unsafe // let U1 { .. } = u1; // OK - let mut u2 = U2 { a: String::from("old") }; // OK - u2.a = String::from("new"); //~ ERROR assignment to non-`Copy` union field is unsafe - let mut u3 = U3 { a: 0 }; // OK - u3.a = 1; // OK - let mut u3 = U3 { a: String::from("old") }; // OK - u3.a = String::from("new"); //~ ERROR assignment to non-`Copy` union field is unsafe + let mut u2 = U2 { a: ManuallyDrop::new(String::from("old")) }; // OK + u2.a = ManuallyDrop::new(String::from("new")); //~ ERROR assignment to non-`Copy` union + *u2.a = String::from("new"); //~ ERROR access to union field is unsafe + + let mut u3 = U3 { a: ManuallyDrop::new(0) }; // OK + u3.a = ManuallyDrop::new(1); // OK + *u3.a = 1; //~ ERROR access to union field is unsafe + + let mut u3 = U3 { a: ManuallyDrop::new(String::from("old")) }; // OK + u3.a = ManuallyDrop::new(String::from("new")); //~ ERROR assignment to non-`Copy` union + *u3.a = String::from("new"); //~ ERROR access to union field is unsafe } diff --git a/src/test/ui/union/union-unsafe.stderr b/src/test/ui/union/union-unsafe.stderr index ab62508fcf66..e020dab63f8f 100644 --- a/src/test/ui/union/union-unsafe.stderr +++ b/src/test/ui/union/union-unsafe.stderr @@ -1,13 +1,29 @@ error[E0133]: assignment to non-`Copy` union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:21:5 + --> $DIR/union-unsafe.rs:22:5 | -LL | u3.a = T::default(); - | ^^^^ assignment to non-`Copy` union field +LL | u3.a = ManuallyDrop::new(T::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to non-`Copy` union field | = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:33:13 + --> $DIR/union-unsafe.rs:23:6 + | +LL | *u3.a = T::default(); + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:29:6 + | +LL | *u3.a = T::default(); + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:37:13 | LL | let a = u1.a; | ^^^^ access to union field @@ -15,7 +31,7 @@ LL | let a = u1.a; = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:35:14 + --> $DIR/union-unsafe.rs:40:14 | LL | let U1 { a } = u1; | ^ access to union field @@ -23,7 +39,7 @@ LL | let U1 { a } = u1; = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:36:20 + --> $DIR/union-unsafe.rs:41:20 | LL | if let U1 { a: 12 } = u1 {} | ^^ access to union field @@ -31,21 +47,45 @@ LL | if let U1 { a: 12 } = u1 {} = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: assignment to non-`Copy` union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:40:5 + --> $DIR/union-unsafe.rs:45:5 | -LL | u2.a = String::from("new"); - | ^^^^ assignment to non-`Copy` union field +LL | u2.a = ManuallyDrop::new(String::from("new")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to non-`Copy` union field | = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:46:6 + | +LL | *u2.a = String::from("new"); + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:50:6 + | +LL | *u3.a = 1; + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + error[E0133]: assignment to non-`Copy` union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:44:5 + --> $DIR/union-unsafe.rs:53:5 | -LL | u3.a = String::from("new"); - | ^^^^ assignment to non-`Copy` union field +LL | u3.a = ManuallyDrop::new(String::from("new")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to non-`Copy` union field | = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized -error: aborting due to 6 previous errors +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:54:6 + | +LL | *u3.a = String::from("new"); + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/union/union-with-drop-fields-lint.stderr b/src/test/ui/union/union-with-drop-fields-lint.stderr deleted file mode 100644 index 2f90f240d2e1..000000000000 --- a/src/test/ui/union/union-with-drop-fields-lint.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error: union contains a field with possibly non-trivial drop code, drop code of union fields is ignored when dropping the union - --> $DIR/union-with-drop-fields-lint.rs:10:5 - | -LL | a: String, - | ^^^^^^^^^ - | -note: lint level defined here - --> $DIR/union-with-drop-fields-lint.rs:3:9 - | -LL | #![deny(unions_with_drop_fields)] - | ^^^^^^^^^^^^^^^^^^^^^^^ - -error: union contains a field with possibly non-trivial drop code, drop code of union fields is ignored when dropping the union - --> $DIR/union-with-drop-fields-lint.rs:18:5 - | -LL | a: S, - | ^^^^ - -error: union contains a field with possibly non-trivial drop code, drop code of union fields is ignored when dropping the union - --> $DIR/union-with-drop-fields-lint.rs:23:5 - | -LL | a: T, - | ^^^^ - -error: aborting due to 3 previous errors - diff --git a/src/test/ui/union/union-with-drop-fields-lint.rs b/src/test/ui/union/union-with-drop-fields.rs similarity index 60% rename from src/test/ui/union/union-with-drop-fields-lint.rs rename to src/test/ui/union/union-with-drop-fields.rs index 8e502aa55f95..e3c63a6d5b5a 100644 --- a/src/test/ui/union/union-with-drop-fields-lint.rs +++ b/src/test/ui/union/union-with-drop-fields.rs @@ -1,13 +1,12 @@ #![feature(untagged_unions)] #![allow(dead_code)] -#![deny(unions_with_drop_fields)] union U { a: u8, // OK } union W { - a: String, //~ ERROR union contains a field with possibly non-trivial drop code + a: String, //~ ERROR unions may not contain fields that need dropping b: String, // OK, only one field is reported } @@ -15,12 +14,12 @@ struct S(String); // `S` doesn't implement `Drop` trait, but still has non-trivial destructor union Y { - a: S, //~ ERROR union contains a field with possibly non-trivial drop code + a: S, //~ ERROR unions may not contain fields that need dropping } // We don't know if `T` is trivially-destructable or not until trans union J { - a: T, //~ ERROR union contains a field with possibly non-trivial drop code + a: T, //~ ERROR unions may not contain fields that need dropping } union H { diff --git a/src/test/ui/union/union-with-drop-fields.stderr b/src/test/ui/union/union-with-drop-fields.stderr new file mode 100644 index 000000000000..0e77279be616 --- /dev/null +++ b/src/test/ui/union/union-with-drop-fields.stderr @@ -0,0 +1,39 @@ +error[E0740]: unions may not contain fields that need dropping + --> $DIR/union-with-drop-fields.rs:9:5 + | +LL | a: String, + | ^^^^^^^^^ + | +note: `std::mem::ManuallyDrop` can be used to wrap the type + --> $DIR/union-with-drop-fields.rs:9:5 + | +LL | a: String, + | ^^^^^^^^^ + +error[E0740]: unions may not contain fields that need dropping + --> $DIR/union-with-drop-fields.rs:17:5 + | +LL | a: S, + | ^^^^ + | +note: `std::mem::ManuallyDrop` can be used to wrap the type + --> $DIR/union-with-drop-fields.rs:17:5 + | +LL | a: S, + | ^^^^ + +error[E0740]: unions may not contain fields that need dropping + --> $DIR/union-with-drop-fields.rs:22:5 + | +LL | a: T, + | ^^^^ + | +note: `std::mem::ManuallyDrop` can be used to wrap the type + --> $DIR/union-with-drop-fields.rs:22:5 + | +LL | a: T, + | ^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0740`. From fe13bbd0648c4f7ad8f7cdfe540ca13bc93ade60 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 3 Jul 2019 11:56:59 +0200 Subject: [PATCH 06/79] Remove unions_with_drop_fields lint Cases where it would trigger are now hard errors. --- .../src/lints/listing/warn-by-default.md | 24 --------------- src/librustc_lint/builtin.rs | 30 ------------------- src/librustc_lint/lib.rs | 3 -- 3 files changed, 57 deletions(-) diff --git a/src/doc/rustc/src/lints/listing/warn-by-default.md b/src/doc/rustc/src/lints/listing/warn-by-default.md index e486240fda89..813d7c4bafef 100644 --- a/src/doc/rustc/src/lints/listing/warn-by-default.md +++ b/src/doc/rustc/src/lints/listing/warn-by-default.md @@ -596,30 +596,6 @@ warning: function cannot return without recursing | ``` -## unions-with-drop-fields - -This lint detects use of unions that contain fields with possibly non-trivial drop code. Some -example code that triggers this lint: - -```rust -#![feature(untagged_unions)] - -union U { - s: String, -} -``` - -This will produce: - -```text -warning: union contains a field with possibly non-trivial drop code, drop code of union fields is ignored when dropping the union - --> src/main.rs:4:5 - | -4 | s: String, - | ^^^^^^^^^ - | -``` - ## unknown-lints This lint detects unrecognized lint attribute. Some diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index d0a7eab071c3..f00bcd41b601 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -979,35 +979,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnstableFeatures { } } -declare_lint! { - UNIONS_WITH_DROP_FIELDS, - Warn, - "use of unions that contain fields with possibly non-trivial drop code" -} - -declare_lint_pass!( - /// Lint for unions that contain fields with possibly non-trivial destructors. - UnionsWithDropFields => [UNIONS_WITH_DROP_FIELDS] -); - -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnionsWithDropFields { - fn check_item(&mut self, ctx: &LateContext<'_, '_>, item: &hir::Item) { - if let hir::ItemKind::Union(ref vdata, _) = item.kind { - for field in vdata.fields() { - let field_ty = ctx.tcx.type_of( - ctx.tcx.hir().local_def_id(field.hir_id)); - if field_ty.needs_drop(ctx.tcx, ctx.param_env) { - ctx.span_lint(UNIONS_WITH_DROP_FIELDS, - field.span, - "union contains a field with possibly non-trivial drop code, \ - drop code of union fields is ignored when dropping the union"); - return; - } - } - } - } -} - declare_lint! { pub UNREACHABLE_PUB, Allow, @@ -1287,7 +1258,6 @@ declare_lint_pass!( NO_MANGLE_GENERIC_ITEMS, MUTABLE_TRANSMUTES, UNSTABLE_FEATURES, - UNIONS_WITH_DROP_FIELDS, UNREACHABLE_PUB, TYPE_ALIAS_BOUNDS, TRIVIAL_BOUNDS diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 0e054013cd77..a898e1a3dcfa 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -164,9 +164,6 @@ macro_rules! late_lint_mod_passes { // Depends on referenced function signatures in expressions MutableTransmutes: MutableTransmutes, - // Depends on types of fields, checks if they implement Drop - UnionsWithDropFields: UnionsWithDropFields, - TypeAliasBounds: TypeAliasBounds, TrivialConstraints: TrivialConstraints, From e247a40a1c5f37cf76ca41d5bbee09985a809529 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 3 Jul 2019 13:02:47 +0200 Subject: [PATCH 07/79] Fixes #41073, it is no longer an ICE --- src/test/ui/union/issue-41073.rs | 24 ++++++++++++++++++++++++ src/test/ui/union/issue-41073.stderr | 15 +++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/test/ui/union/issue-41073.rs create mode 100644 src/test/ui/union/issue-41073.stderr diff --git a/src/test/ui/union/issue-41073.rs b/src/test/ui/union/issue-41073.rs new file mode 100644 index 000000000000..91e9a0d0b659 --- /dev/null +++ b/src/test/ui/union/issue-41073.rs @@ -0,0 +1,24 @@ +#![feature(untagged_unions)] + +union Test { + a: A, //~ ERROR unions may not contain fields that need dropping + b: B +} + +#[derive(Debug)] +struct A(i32); +impl Drop for A { + fn drop(&mut self) { println!("A"); } +} + +#[derive(Debug)] +struct B(f32); +impl Drop for B { + fn drop(&mut self) { println!("B"); } +} + +fn main() { + let mut test = Test { a: A(3) }; + println!("{:?}", unsafe { test.b }); + unsafe { test.b = B(0.5); } +} diff --git a/src/test/ui/union/issue-41073.stderr b/src/test/ui/union/issue-41073.stderr new file mode 100644 index 000000000000..2e9598b22712 --- /dev/null +++ b/src/test/ui/union/issue-41073.stderr @@ -0,0 +1,15 @@ +error[E0740]: unions may not contain fields that need dropping + --> $DIR/issue-41073.rs:4:5 + | +LL | a: A, + | ^^^^ + | +note: `std::mem::ManuallyDrop` can be used to wrap the type + --> $DIR/issue-41073.rs:4:5 + | +LL | a: A, + | ^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0740`. From 05a644e8257b20c3372f27420d28bef5fd6d6d73 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 4 Jul 2019 15:51:55 +0200 Subject: [PATCH 08/79] Update src/librustc_typeck/check/mod.rs Co-Authored-By: Mazdak Farrokhzad --- src/librustc_typeck/check/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 2f122e5edb1c..294d1992b9d9 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1402,7 +1402,7 @@ fn check_union_fields<'tcx>(tcx: TyCtxt<'tcx>, _sp: Span, item_def_id: DefId) -> let fields = &def.non_enum_variant().fields; for field in fields { let field_ty = field.ty(tcx, substs); - // We are currently checking the type this field came from, so it must be local + // We are currently checking the type this field came from, so it must be local. let field_span = tcx.hir().span_if_local(field.did).unwrap(); let param_env = tcx.param_env(field.did); if field_ty.needs_drop(tcx, param_env) { From 8c5ae86496d25ddd9df3486df46bcb671ce619ad Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 4 Jul 2019 15:52:37 +0200 Subject: [PATCH 09/79] Update src/librustc_typeck/check/mod.rs Co-Authored-By: Mazdak Farrokhzad --- src/librustc_typeck/check/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 294d1992b9d9..a8b3a0483899 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1391,7 +1391,9 @@ fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { check_packed(tcx, span, def_id); } -fn check_union_fields<'tcx>(tcx: TyCtxt<'tcx>, _sp: Span, item_def_id: DefId) -> bool { +/// When the `#![feature(untagged_unions)]` gate is active, +/// check that the fields of the `union` does not contain fields that need dropping. +fn check_union_fields(tcx: TyCtxt<'_>, _: Span, item_def_id: DefId) -> bool { // Without the feature we check Copy types only later if !tcx.features().untagged_unions { return true; From 0301eafd32a128b31cc7c551d5844f6fce965caa Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 4 Jul 2019 15:52:48 +0200 Subject: [PATCH 10/79] Update src/librustc_typeck/error_codes.rs Co-Authored-By: Mazdak Farrokhzad --- src/librustc_typeck/error_codes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_typeck/error_codes.rs b/src/librustc_typeck/error_codes.rs index a92a2c960039..78dedca8027f 100644 --- a/src/librustc_typeck/error_codes.rs +++ b/src/librustc_typeck/error_codes.rs @@ -4864,7 +4864,7 @@ assert_eq!(1, discriminant(&Enum::Struct{a: 7, b: 11})); "##, E0740: r##" -A `union` can not have fields with destructors. +A `union` cannot have fields with destructors. "##, E0733: r##" From bf25a9c82fb9d78447da885eb70d7234d135ecd5 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 4 Jul 2019 15:54:16 +0200 Subject: [PATCH 11/79] Update src/test/run-pass/union/union-nodrop.rs Co-Authored-By: Mazdak Farrokhzad --- src/test/ui/union/union-nodrop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/union/union-nodrop.rs b/src/test/ui/union/union-nodrop.rs index 330f6f9593b1..c99228d7d343 100644 --- a/src/test/ui/union/union-nodrop.rs +++ b/src/test/ui/union/union-nodrop.rs @@ -13,7 +13,7 @@ impl Drop for NeedDrop { fn drop(&mut self) {} } -// Constant expressios allow `NoDrop` to go out of scope, +// Constant expressions allow `NoDrop` to go out of scope, // unlike a value of the interior type implementing `Drop`. static X: () = (NoDrop { inner: ManuallyDrop::new(NeedDrop) }, ()).1; From fc512d2cdf5ab654e55e25725b7243812f4630d1 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 4 Jul 2019 18:21:06 +0200 Subject: [PATCH 12/79] More descriptive variable name --- src/librustc_typeck/check/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a8b3a0483899..ea27efcd0fbc 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1398,8 +1398,8 @@ fn check_union_fields(tcx: TyCtxt<'_>, _: Span, item_def_id: DefId) -> bool { if !tcx.features().untagged_unions { return true; } - let t = tcx.type_of(item_def_id); - if let ty::Adt(def, substs) = t.sty { + let item_type = tcx.type_of(item_def_id); + if let ty::Adt(def, substs) = item_type.sty { if def.is_union() { let fields = &def.non_enum_variant().fields; for field in fields { From 616cf52358a66f0d546aac67a58958e2b50c5f0b Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 4 Jul 2019 18:30:41 +0200 Subject: [PATCH 13/79] Extend union-nodrop.rs test --- src/test/ui/union/union-nodrop.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/ui/union/union-nodrop.rs b/src/test/ui/union/union-nodrop.rs index c99228d7d343..59282bec59b8 100644 --- a/src/test/ui/union/union-nodrop.rs +++ b/src/test/ui/union/union-nodrop.rs @@ -13,10 +13,14 @@ impl Drop for NeedDrop { fn drop(&mut self) {} } -// Constant expressions allow `NoDrop` to go out of scope, +// Constant expressios allow `NoDrop` to go out of scope, // unlike a value of the interior type implementing `Drop`. static X: () = (NoDrop { inner: ManuallyDrop::new(NeedDrop) }, ()).1; +const Y: () = (NoDrop { inner: ManuallyDrop::new(NeedDrop) }, ()).1; + +const fn _f() { (NoDrop { inner: ManuallyDrop::new(NeedDrop) }, ()).1 } + // A union that scrubs the drop glue from its inner type union NoDrop { inner: ManuallyDrop } From 50ec10e6059f923204c573b4004612e99aa6cdad Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 20 Sep 2019 10:59:29 +0200 Subject: [PATCH 14/79] rpass tests are now part of `ui` tests --- src/test/ui/union/union-derive-clone.stderr | 2 +- .../union/union-manuallydrop-rpass.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/test/{run-pass/union/union-manuallydrop.rs => ui/union/union-manuallydrop-rpass.rs} (100%) diff --git a/src/test/ui/union/union-derive-clone.stderr b/src/test/ui/union/union-derive-clone.stderr index a6dcd556a1fe..6893f9176f2d 100644 --- a/src/test/ui/union/union-derive-clone.stderr +++ b/src/test/ui/union/union-derive-clone.stderr @@ -13,7 +13,7 @@ LL | union U5 { | ----------- method `clone` not found for this ... LL | let w = u.clone(); - | ^^^^^ method not found in `U4` + | ^^^^^ method not found in `U5` | = note: the method `clone` exists but the following trait bounds were not satisfied: `U5 : std::clone::Clone` diff --git a/src/test/run-pass/union/union-manuallydrop.rs b/src/test/ui/union/union-manuallydrop-rpass.rs similarity index 100% rename from src/test/run-pass/union/union-manuallydrop.rs rename to src/test/ui/union/union-manuallydrop-rpass.rs From 9c1ad0ff2fe64c02a91c1daf0ce6670b1eaf75f6 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 20 Sep 2019 12:22:06 +0200 Subject: [PATCH 15/79] Preserve originally intended test semantics --- src/test/ui/union/union-generic-rpass.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/ui/union/union-generic-rpass.rs b/src/test/ui/union/union-generic-rpass.rs index 5ca3bc0f722c..eb169c516d2a 100644 --- a/src/test/ui/union/union-generic-rpass.rs +++ b/src/test/ui/union/union-generic-rpass.rs @@ -3,8 +3,10 @@ #![feature(untagged_unions)] -union MaybeItem where T::Item: Copy { - elem: T::Item, +use std::mem::ManuallyDrop; + +union MaybeItem { + elem: ManuallyDrop, none: (), } @@ -25,7 +27,7 @@ fn main() { let v: Vec = vec![1, 2, 3]; let mut i = v.iter(); i.next(); - let mi = MaybeItem::> { elem: i.next().unwrap() }; - assert_eq!(*mi.elem, 2); + let mi = MaybeItem::> { elem: ManuallyDrop::new(i.next().unwrap()) }; + assert_eq!(**mi.elem, 2); } } From 2fc257ca8144ad6a41b053a01f977afce7a23c95 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 20 Sep 2019 12:40:57 +0200 Subject: [PATCH 16/79] Prefer `ManuallyDrop::{take,new}` over `ptr::{read,write}` --- src/libstd/lib.rs | 1 + src/libstd/panicking.rs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 5ff32d7adafc..8e3e02586a62 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -275,6 +275,7 @@ #![feature(link_args)] #![feature(linkage)] #![feature(log_syntax)] +#![feature(manually_drop_take)] #![feature(maybe_uninit_ref)] #![feature(maybe_uninit_slice)] #![feature(mem_take)] diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 296f2a59bb82..2dde81bb0ecd 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -13,7 +13,6 @@ use crate::any::Any; use crate::fmt; use crate::intrinsics; use crate::mem::{self, ManuallyDrop}; -use crate::ptr; use crate::raw; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sys::stdio::panic_output; @@ -283,8 +282,9 @@ pub unsafe fn r#try R>(f: F) -> Result> fn do_call R, R>(data: *mut u8) { unsafe { let data = data as *mut Data; - let f = ptr::read(&mut *(*data).f); - ptr::write(&mut *(*data).r, f()); + let data = &mut (*data); + let f = ManuallyDrop::take(&mut data.f); + data.r = ManuallyDrop::new(f()); } } } From fb23a5cf3bda8d4d5ee89be4c1777d28a1061f9b Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 20 Sep 2019 12:46:41 +0200 Subject: [PATCH 17/79] Clarify a vague comment --- src/librustc_typeck/check/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index ea27efcd0fbc..d2a4b824439d 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1394,7 +1394,8 @@ fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { /// When the `#![feature(untagged_unions)]` gate is active, /// check that the fields of the `union` does not contain fields that need dropping. fn check_union_fields(tcx: TyCtxt<'_>, _: Span, item_def_id: DefId) -> bool { - // Without the feature we check Copy types only later + // Without the feature we check that all fields are `Copy` in our stability checking + // infrastructure. if !tcx.features().untagged_unions { return true; } From 7e1a65dce1d13d02de8e0ffafad15538556cb7b9 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 20 Sep 2019 13:53:46 +0200 Subject: [PATCH 18/79] Ensure we do not treat all unions as not having any drop glue. --- src/test/ui/union/union-custom-drop.rs | 19 +++++++++++++++ src/test/ui/union/union-custom-drop.stderr | 28 ++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/test/ui/union/union-custom-drop.rs create mode 100644 src/test/ui/union/union-custom-drop.stderr diff --git a/src/test/ui/union/union-custom-drop.rs b/src/test/ui/union/union-custom-drop.rs new file mode 100644 index 000000000000..8f816cc1b737 --- /dev/null +++ b/src/test/ui/union/union-custom-drop.rs @@ -0,0 +1,19 @@ +// test for a union with a field that's a union with a manual impl Drop +// Ensures we do not treat all unions as not having any drop glue. + +#![feature(untagged_unions)] + +union Foo { + bar: Bar, //~ ERROR unions may not contain fields that need dropping +} + +union Bar { + a: i32, + b: u32, +} + +impl Drop for Bar { + fn drop(&mut self) {} +} + +fn main() {} diff --git a/src/test/ui/union/union-custom-drop.stderr b/src/test/ui/union/union-custom-drop.stderr new file mode 100644 index 000000000000..8e4e5dd40fd3 --- /dev/null +++ b/src/test/ui/union/union-custom-drop.stderr @@ -0,0 +1,28 @@ +error[E0601]: `main` function not found in crate `union_custom_drop` + --> $DIR/union-custom-drop.rs:4:1 + | +LL | / #![feature(untagged_unions)] +LL | | +LL | | union Foo { +LL | | bar: Bar, +... | +LL | | } +LL | | } + | |_^ consider adding a `main` function to `$DIR/union-custom-drop.rs` + +error[E0740]: unions may not contain fields that need dropping + --> $DIR/union-custom-drop.rs:7:5 + | +LL | bar: Bar, + | ^^^^^^^^ + | +note: `std::mem::ManuallyDrop` can be used to wrap the type + --> $DIR/union-custom-drop.rs:7:5 + | +LL | bar: Bar, + | ^^^^^^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0601, E0740. +For more information about an error, try `rustc --explain E0601`. From f5669ebb45353bfb828650472af783331b7b90fe Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 20 Sep 2019 15:23:10 +0200 Subject: [PATCH 19/79] Update ui stderr --- src/test/ui/union/union-custom-drop.stderr | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/test/ui/union/union-custom-drop.stderr b/src/test/ui/union/union-custom-drop.stderr index 8e4e5dd40fd3..ee2333f905fb 100644 --- a/src/test/ui/union/union-custom-drop.stderr +++ b/src/test/ui/union/union-custom-drop.stderr @@ -1,15 +1,3 @@ -error[E0601]: `main` function not found in crate `union_custom_drop` - --> $DIR/union-custom-drop.rs:4:1 - | -LL | / #![feature(untagged_unions)] -LL | | -LL | | union Foo { -LL | | bar: Bar, -... | -LL | | } -LL | | } - | |_^ consider adding a `main` function to `$DIR/union-custom-drop.rs` - error[E0740]: unions may not contain fields that need dropping --> $DIR/union-custom-drop.rs:7:5 | @@ -22,7 +10,6 @@ note: `std::mem::ManuallyDrop` can be used to wrap the type LL | bar: Bar, | ^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0601, E0740. -For more information about an error, try `rustc --explain E0601`. +For more information about this error, try `rustc --explain E0740`. From 5db17335a15e5e290bd7505cc37b2177c86b51c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 11 Oct 2019 00:00:00 +0000 Subject: [PATCH 20/79] rustdoc: forward -Z options to rustc Currently rustdoc does not forward `-Z` options to rustc when building test executables. This makes impossible to use rustdoc to run test samples when crate under test is instrumented with one of sanitizers `-Zsanitizer=...`, since the final linking step will not include sanitizer runtime library. Forward `-Z` options to rustc to solve the issue. Helps with #43031. --- src/librustdoc/config.rs | 4 ++++ src/librustdoc/test.rs | 3 +++ .../failed-doctest-missing-codes.stdout | 14 +++++++------- .../rustdoc-ui/failed-doctest-output.stdout | 8 ++++---- src/test/rustdoc-ui/unparseable-doc-test.stdout | 8 ++++---- src/test/rustdoc/sanitizer-option.rs | 17 +++++++++++++++++ 6 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 src/test/rustdoc/sanitizer-option.rs diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index fe4e2bd09151..3bfc25e5115c 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -53,6 +53,8 @@ pub struct Options { pub codegen_options_strs: Vec, /// Debugging (`-Z`) options to pass to the compiler. pub debugging_options: DebuggingOptions, + /// Debugging (`-Z`) options strings to pass to the compiler. + pub debugging_options_strs: Vec, /// The target used to compile the crate against. pub target: TargetTriple, /// Edition used when reading the crate. Defaults to "2015". Also used by default when @@ -481,6 +483,7 @@ impl Options { let generate_redirect_pages = matches.opt_present("generate-redirect-pages"); let test_builder = matches.opt_str("test-builder").map(PathBuf::from); let codegen_options_strs = matches.opt_strs("C"); + let debugging_options_strs = matches.opt_strs("Z"); let lib_strs = matches.opt_strs("L"); let extern_strs = matches.opt_strs("extern"); let runtool = matches.opt_str("runtool"); @@ -502,6 +505,7 @@ impl Options { codegen_options, codegen_options_strs, debugging_options, + debugging_options_strs, target, edition, maybe_sysroot, diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 3e77ca47e8a6..f1d1694ca940 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -279,6 +279,9 @@ fn run_test( for codegen_options_str in &options.codegen_options_strs { compiler.arg("-C").arg(&codegen_options_str); } + for debugging_option_str in &options.debugging_options_strs { + compiler.arg("-Z").arg(&debugging_option_str); + } if no_run { compiler.arg("--emit=metadata"); } diff --git a/src/test/rustdoc-ui/failed-doctest-missing-codes.stdout b/src/test/rustdoc-ui/failed-doctest-missing-codes.stdout index d206b721765b..a8753d14de22 100644 --- a/src/test/rustdoc-ui/failed-doctest-missing-codes.stdout +++ b/src/test/rustdoc-ui/failed-doctest-missing-codes.stdout @@ -6,13 +6,13 @@ failures: ---- $DIR/failed-doctest-missing-codes.rs - Foo (line 8) stdout ---- error[E0308]: mismatched types - --> $DIR/failed-doctest-missing-codes.rs:9:13 - | -3 | let x: () = 5i32; - | ^^^^ expected (), found i32 - | - = note: expected type `()` - found type `i32` + --> $DIR/failed-doctest-missing-codes.rs:9:13 + | +LL | let x: () = 5i32; + | ^^^^ expected (), found i32 + | + = note: expected type `()` + found type `i32` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/failed-doctest-output.stdout b/src/test/rustdoc-ui/failed-doctest-output.stdout index ef1b419f5289..9887d07a3eb6 100644 --- a/src/test/rustdoc-ui/failed-doctest-output.stdout +++ b/src/test/rustdoc-ui/failed-doctest-output.stdout @@ -7,10 +7,10 @@ failures: ---- $DIR/failed-doctest-output.rs - OtherStruct (line 21) stdout ---- error[E0425]: cannot find value `no` in this scope - --> $DIR/failed-doctest-output.rs:22:1 - | -3 | no - | ^^ not found in this scope + --> $DIR/failed-doctest-output.rs:22:1 + | +LL | no + | ^^ not found in this scope error: aborting due to previous error diff --git a/src/test/rustdoc-ui/unparseable-doc-test.stdout b/src/test/rustdoc-ui/unparseable-doc-test.stdout index 0350c0164360..4ea6455d3aa4 100644 --- a/src/test/rustdoc-ui/unparseable-doc-test.stdout +++ b/src/test/rustdoc-ui/unparseable-doc-test.stdout @@ -6,10 +6,10 @@ failures: ---- $DIR/unparseable-doc-test.rs - foo (line 6) stdout ---- error: unterminated double quote string - --> $DIR/unparseable-doc-test.rs:8:1 - | -2 | "unterminated - | ^^^^^^^^^^^^^ + --> $DIR/unparseable-doc-test.rs:8:1 + | +LL | "unterminated + | ^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/rustdoc/sanitizer-option.rs b/src/test/rustdoc/sanitizer-option.rs new file mode 100644 index 000000000000..6af9ed3e33f6 --- /dev/null +++ b/src/test/rustdoc/sanitizer-option.rs @@ -0,0 +1,17 @@ +// needs-sanitizer-support +// compile-flags: --test -Z sanitizer=address +// +// #43031: Verify that rustdoc passes `-Z` options to rustc. Use an extern +// function that is provided by the sanitizer runtime, if flag is not passed +// correctly, then linking will fail. + +/// ``` +/// extern { +/// fn __sanitizer_print_stack_trace(); +/// } +/// +/// fn main() { +/// unsafe { __sanitizer_print_stack_trace() }; +/// } +/// ``` +pub fn z_flag_is_passed_to_rustc() {} From d5ffd36a6cd6fa03b09a60a3835ce6ca90c310ff Mon Sep 17 00:00:00 2001 From: Shotaro Yamada Date: Wed, 16 Oct 2019 13:46:44 +0900 Subject: [PATCH 21/79] Change HashSet element type to `DefId` --- src/librustc_mir/hair/pattern/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 58d741b9295a..72e6970ebba1 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -1217,7 +1217,7 @@ fn search_for_adt_without_structural_match<'tcx>(tcx: TyCtxt<'tcx>, // tracks ADT's previously encountered during search, so that // we will not recur on them again. - seen: FxHashSet<&'tcx AdtDef>, + seen: FxHashSet, } impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { @@ -1257,14 +1257,12 @@ fn search_for_adt_without_structural_match<'tcx>(tcx: TyCtxt<'tcx>, return true // Halt visiting! } - if self.seen.contains(adt_def) { + if !self.seen.insert(adt_def.did) { debug!("Search already seen adt_def: {:?}", adt_def); // let caller continue its search return false; } - self.seen.insert(adt_def); - // `#[structural_match]` does not care about the // instantiation of the generics in an ADT (it // instead looks directly at its fields outside From ac2f906a592135953ede70429efadd876e21cd09 Mon Sep 17 00:00:00 2001 From: Shotaro Yamada Date: Wed, 16 Oct 2019 13:48:20 +0900 Subject: [PATCH 22/79] Make use of the return value of `HashSet::insert` --- src/librustc/middle/stability.rs | 3 +-- src/librustc_codegen_llvm/debuginfo/metadata.rs | 4 +--- src/librustc_metadata/native_libs.rs | 4 +--- src/librustc_mir/borrow_check/conflict_errors.rs | 4 +--- src/librustc_mir/lints.rs | 4 +--- src/librustc_resolve/resolve_imports.rs | 3 +-- 6 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 302c11f309d9..b8d1a549ecda 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -905,11 +905,10 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { // Warn if the user has enabled an already-stable lang feature. unnecessary_stable_feature_lint(tcx, span, feature, since); } - if lang_features.contains(&feature) { + if !lang_features.insert(feature) { // Warn if the user enables a lang feature multiple times. duplicate_feature_err(tcx.sess, span, feature); } - lang_features.insert(feature); } let declared_lib_features = &tcx.features().declared_lib_features; diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 438a660b8a86..7bd82ced3c38 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -2069,11 +2069,9 @@ fn set_members_of_composite_type(cx: &CodegenCx<'ll, 'tcx>, { let mut composite_types_completed = debug_context(cx).composite_types_completed.borrow_mut(); - if composite_types_completed.contains(&composite_type_metadata) { + if !composite_types_completed.insert(&composite_type_metadata) { bug!("debuginfo::set_members_of_composite_type() - \ Already completed forward declaration re-encountered."); - } else { - composite_types_completed.insert(composite_type_metadata); } } diff --git a/src/librustc_metadata/native_libs.rs b/src/librustc_metadata/native_libs.rs index 9e4c2685f116..a58db6a903bb 100644 --- a/src/librustc_metadata/native_libs.rs +++ b/src/librustc_metadata/native_libs.rs @@ -198,12 +198,10 @@ impl Collector<'tcx> { self.tcx.sess.err(&format!("renaming of the library `{}` was specified, \ however this crate contains no `#[link(...)]` \ attributes referencing this library.", name)); - } else if renames.contains(name) { + } else if !renames.insert(name) { self.tcx.sess.err(&format!("multiple renamings were \ specified for library `{}` .", name)); - } else { - renames.insert(name); } } } diff --git a/src/librustc_mir/borrow_check/conflict_errors.rs b/src/librustc_mir/borrow_check/conflict_errors.rs index 098258994f4e..4c469a82ac3d 100644 --- a/src/librustc_mir/borrow_check/conflict_errors.rs +++ b/src/librustc_mir/borrow_check/conflict_errors.rs @@ -78,7 +78,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .last() .unwrap(); - if self.uninitialized_error_reported.contains(&root_place) { + if !self.uninitialized_error_reported.insert(root_place) { debug!( "report_use_of_moved_or_uninitialized place: error about {:?} suppressed", root_place @@ -86,8 +86,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return; } - self.uninitialized_error_reported.insert(root_place); - let item_msg = match self.describe_place_with_options(used_place, IncludingDowncast(true)) { Some(name) => format!("`{}`", name), diff --git a/src/librustc_mir/lints.rs b/src/librustc_mir/lints.rs index da3fead1f9dd..158b730b9bd4 100644 --- a/src/librustc_mir/lints.rs +++ b/src/librustc_mir/lints.rs @@ -72,13 +72,11 @@ fn check_fn_for_unconditional_recursion( let caller_substs = &InternalSubsts::identity_for_item(tcx, def_id)[..trait_substs_count]; while let Some(bb) = reachable_without_self_call_queue.pop() { - if visited.contains(bb) { + if !visited.insert(bb) { //already done continue; } - visited.insert(bb); - let block = &basic_blocks[bb]; if let Some(ref terminator) = block.terminator { diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 360343169bc3..0a734d881efe 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -670,13 +670,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> { self.throw_unresolved_import_error(errors, None); errors = vec![]; } - if !seen_spans.contains(&err.span) { + if seen_spans.insert(err.span) { let path = import_path_to_string( &import.module_path.iter().map(|seg| seg.ident).collect::>(), &import.subclass, err.span, ); - seen_spans.insert(err.span); errors.push((path, err)); prev_root_id = import.root_id; } From c4deea212d19b19fa11fb4e29dd5c7f1359543a5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 16 Oct 2019 17:22:35 +1100 Subject: [PATCH 23/79] Avoid unnecessary arena allocations in `expand_pattern()`. `expand_pattern()` has two callsites. One of them needs arena allocation, but the other does not. This commit moves the arena allocation out of the function. This avoids the allocation of many 4 KiB page arena chunks that only hold a single small allocation. It reduces the number of bytes allocated by up to 2% for various benchmarks, albeit without only a very small improvement in runtime. --- src/librustc_mir/hair/pattern/_match.rs | 4 ++-- src/librustc_mir/hair/pattern/check_match.rs | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 3ea580528772..bcbb14137c7d 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -188,8 +188,8 @@ use std::ops::RangeInclusive; use std::u128; use std::convert::TryInto; -pub fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> &'a Pat<'tcx> { - cx.pattern_arena.alloc(LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat)) +pub fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> { + LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat) } struct LiteralExpander<'tcx> { diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 9bed4fb66ea9..27ad56fd29b5 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -153,7 +153,8 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { self.tables ); patcx.include_lint_checks(); - let pattern = expand_pattern(cx, patcx.lower_pattern(&pat)); + let pattern = + cx.pattern_arena.alloc(expand_pattern(cx, patcx.lower_pattern(&pat))) as &_; if !patcx.errors.is_empty() { patcx.report_inlining_errors(pat.span); have_errors = true; @@ -252,8 +253,9 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { patcx.include_lint_checks(); let pattern = patcx.lower_pattern(pat); let pattern_ty = pattern.ty; + let pattern = expand_pattern(cx, pattern); let pats: Matrix<'_, '_> = vec![smallvec![ - expand_pattern(cx, pattern) + &pattern ]].into_iter().collect(); let witnesses = match check_not_useful(cx, pattern_ty, &pats) { From 01008e4cd68a92e585d7f9e718393568c800589e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Wed, 16 Oct 2019 12:50:59 +0200 Subject: [PATCH 24/79] Update libc to 0.2.64 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6d40b1989279..01637a212cba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1713,9 +1713,9 @@ checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" [[package]] name = "libc" -version = "0.2.62" +version = "0.2.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" +checksum = "74dfca3d9957906e8d1e6a0b641dc9a59848e793f1da2165889fd4f62d10d79c" dependencies = [ "rustc-std-workspace-core", ] From 6de4924b6c1ff5a99397ca1a3894c51f085f3e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Thu, 17 Oct 2019 12:09:11 +0200 Subject: [PATCH 25/79] Update emscripten functions declarations --- src/libstd/sys/unix/fd.rs | 34 ++-------------------------------- src/libstd/sys/unix/fs.rs | 2 -- 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index ac43526b50f2..ba611a6b7e7b 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -71,22 +71,7 @@ impl FileDesc { #[cfg(target_os = "android")] use super::android::cvt_pread64; - #[cfg(target_os = "emscripten")] - unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64) - -> io::Result - { - use crate::convert::TryInto; - use libc::pread64; - // pread64 on emscripten actually takes a 32 bit offset - if let Ok(o) = offset.try_into() { - cvt(pread64(fd, buf, count, o)) - } else { - Err(io::Error::new(io::ErrorKind::InvalidInput, - "cannot pread >2GB")) - } - } - - #[cfg(not(any(target_os = "android", target_os = "emscripten")))] + #[cfg(not(target_os = "android"))] unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64) -> io::Result { @@ -128,22 +113,7 @@ impl FileDesc { #[cfg(target_os = "android")] use super::android::cvt_pwrite64; - #[cfg(target_os = "emscripten")] - unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64) - -> io::Result - { - use crate::convert::TryInto; - use libc::pwrite64; - // pwrite64 on emscripten actually takes a 32 bit offset - if let Ok(o) = offset.try_into() { - cvt(pwrite64(fd, buf, count, o)) - } else { - Err(io::Error::new(io::ErrorKind::InvalidInput, - "cannot pwrite >2GB")) - } - } - - #[cfg(not(any(target_os = "android", target_os = "emscripten")))] + #[cfg(not(target_os = "android"))] unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64) -> io::Result { diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 3b1eb86b84fe..fdfc350995d0 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -602,8 +602,6 @@ impl File { SeekFrom::End(off) => (libc::SEEK_END, off), SeekFrom::Current(off) => (libc::SEEK_CUR, off), }; - #[cfg(target_os = "emscripten")] - let pos = pos as i32; let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?; Ok(n as u64) } From bb5a652361bb416e38fa4f41784dd5d08b232f59 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Thu, 17 Oct 2019 13:28:14 +0200 Subject: [PATCH 26/79] Rebase fallout --- src/librustc_typeck/check/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d2a4b824439d..3e0527393cd1 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1400,7 +1400,7 @@ fn check_union_fields(tcx: TyCtxt<'_>, _: Span, item_def_id: DefId) -> bool { return true; } let item_type = tcx.type_of(item_def_id); - if let ty::Adt(def, substs) = item_type.sty { + if let ty::Adt(def, substs) = item_type.kind { if def.is_union() { let fields = &def.non_enum_variant().fields; for field in fields { From a2feb9c6cc343a25ecca5594acff61bb92ab4e22 Mon Sep 17 00:00:00 2001 From: "Christian Holler (:decoder)" Date: Thu, 17 Oct 2019 14:09:49 +0200 Subject: [PATCH 27/79] Avoid injecting sanitizer runtimes into staticlibs (#64629). --- src/librustc_metadata/creader.rs | 2 +- .../sanitizer-staticlib-link/Makefile | 14 ++++++++++---- .../sanitizer-staticlib-link/program.rs | 10 ++++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 src/test/run-make-fulldeps/sanitizer-staticlib-link/program.rs diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 9850121d2ce5..20c8cf1e963d 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -738,10 +738,10 @@ impl<'a> CrateLoader<'a> { if !self.sess.crate_types.borrow().iter().all(|ct| { match *ct { // Link the runtime - config::CrateType::Staticlib | config::CrateType::Executable => true, // This crate will be compiled with the required // instrumentation pass + config::CrateType::Staticlib | config::CrateType::Rlib | config::CrateType::Dylib | config::CrateType::Cdylib => diff --git a/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile b/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile index 200dc1be4dee..f56475b441f1 100644 --- a/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile +++ b/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile @@ -4,12 +4,18 @@ -include ../tools.mk -# This test builds a staticlib, then an executable that links to it. -# The staticlib and executable both are compiled with address sanitizer, -# and we assert that a fault in the staticlib is correctly detected. +# This test first builds a staticlib with AddressSanitizer and checks that +# linking it to an executable fails due to the missing sanitizer runtime. +# It then builds an executable linking to the staticlib and checks that +# the fault in the staticlib is detected correctly. + +# Note that checking for the link failure actually checks two things at once: +# 1) That the library has the sanitizer intrumentation +# 2) and that library does not have the sanitizer runtime all: $(RUSTC) -g -Z sanitizer=address --crate-type staticlib --target $(TARGET) library.rs - $(CC) program.c $(call STATICLIB,library) $(call OUT_EXE,program) $(EXTRACFLAGS) $(EXTRACXXFLAGS) + ! $(CC) program.c $(call STATICLIB,library) $(call OUT_EXE,program) $(EXTRACFLAGS) $(EXTRACXXFLAGS) + $(RUSTC) -g -Z sanitizer=address --crate-type bin --target $(TARGET) -L . program.rs LD_LIBRARY_PATH=$(TMPDIR) $(TMPDIR)/program 2>&1 | $(CGREP) stack-buffer-overflow diff --git a/src/test/run-make-fulldeps/sanitizer-staticlib-link/program.rs b/src/test/run-make-fulldeps/sanitizer-staticlib-link/program.rs new file mode 100644 index 000000000000..21e1ade2cd52 --- /dev/null +++ b/src/test/run-make-fulldeps/sanitizer-staticlib-link/program.rs @@ -0,0 +1,10 @@ +#[link(name = "library")] +extern { + fn overflow(); +} + +fn main() { + unsafe { + overflow(); + } +} From 4d5052203d200474b1a9aacbb0d59666a576ee16 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Thu, 17 Oct 2019 09:47:43 +0300 Subject: [PATCH 28/79] Split libtest into several smaller modules --- src/libtest/bench.rs | 251 +++++ src/libtest/cli.rs | 384 ++++++++ src/libtest/formatters/json.rs | 4 +- src/libtest/formatters/mod.rs | 2 +- src/libtest/formatters/pretty.rs | 10 +- src/libtest/formatters/terse.rs | 4 +- src/libtest/helpers/concurrency.rs | 153 +++ src/libtest/helpers/isatty.rs | 33 + src/libtest/helpers/metrics.rs | 50 + src/libtest/helpers/mod.rs | 6 + src/libtest/lib.rs | 1417 +--------------------------- src/libtest/options.rs | 80 ++ src/libtest/test_result.rs | 102 ++ src/libtest/time.rs | 206 ++++ src/libtest/types.rs | 145 +++ 15 files changed, 1462 insertions(+), 1385 deletions(-) create mode 100644 src/libtest/bench.rs create mode 100644 src/libtest/cli.rs create mode 100644 src/libtest/helpers/concurrency.rs create mode 100644 src/libtest/helpers/isatty.rs create mode 100644 src/libtest/helpers/metrics.rs create mode 100644 src/libtest/helpers/mod.rs create mode 100644 src/libtest/options.rs create mode 100644 src/libtest/test_result.rs create mode 100644 src/libtest/time.rs create mode 100644 src/libtest/types.rs diff --git a/src/libtest/bench.rs b/src/libtest/bench.rs new file mode 100644 index 000000000000..055a74f691cd --- /dev/null +++ b/src/libtest/bench.rs @@ -0,0 +1,251 @@ +//! Benchmarking module. +use super::{ + BenchMode, MonitorMsg, Sender, Sink, TestDesc, TestResult +}; + +use crate::stats; +use std::time::{Duration, Instant}; +use std::cmp; +use std::io; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::sync::{Arc, Mutex}; +use std::hint::black_box; + +/// Manager of the benchmarking runs. +/// +/// This is fed into functions marked with `#[bench]` to allow for +/// set-up & tear-down before running a piece of code repeatedly via a +/// call to `iter`. +#[derive(Clone)] +pub struct Bencher { + mode: BenchMode, + summary: Option, + pub bytes: u64, +} + +impl Bencher { + /// Callback for benchmark functions to run in their body. + pub fn iter(&mut self, mut inner: F) + where + F: FnMut() -> T, + { + if self.mode == BenchMode::Single { + ns_iter_inner(&mut inner, 1); + return; + } + + self.summary = Some(iter(&mut inner)); + } + + pub fn bench(&mut self, mut f: F) -> Option + where + F: FnMut(&mut Bencher), + { + f(self); + return self.summary; + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct BenchSamples { + pub ns_iter_summ: stats::Summary, + pub mb_s: usize, +} + +pub fn fmt_bench_samples(bs: &BenchSamples) -> String { + use std::fmt::Write; + let mut output = String::new(); + + let median = bs.ns_iter_summ.median as usize; + let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize; + + output + .write_fmt(format_args!( + "{:>11} ns/iter (+/- {})", + fmt_thousands_sep(median, ','), + fmt_thousands_sep(deviation, ',') + )) + .unwrap(); + if bs.mb_s != 0 { + output + .write_fmt(format_args!(" = {} MB/s", bs.mb_s)) + .unwrap(); + } + output +} + +// Format a number with thousands separators +fn fmt_thousands_sep(mut n: usize, sep: char) -> String { + use std::fmt::Write; + let mut output = String::new(); + let mut trailing = false; + for &pow in &[9, 6, 3, 0] { + let base = 10_usize.pow(pow); + if pow == 0 || trailing || n / base != 0 { + if !trailing { + output.write_fmt(format_args!("{}", n / base)).unwrap(); + } else { + output.write_fmt(format_args!("{:03}", n / base)).unwrap(); + } + if pow != 0 { + output.push(sep); + } + trailing = true; + } + n %= base; + } + + output +} + +fn ns_from_dur(dur: Duration) -> u64 { + dur.as_secs() * 1_000_000_000 + (dur.subsec_nanos() as u64) +} + +fn ns_iter_inner(inner: &mut F, k: u64) -> u64 +where + F: FnMut() -> T, +{ + let start = Instant::now(); + for _ in 0..k { + black_box(inner()); + } + return ns_from_dur(start.elapsed()); +} + +pub fn iter(inner: &mut F) -> stats::Summary +where + F: FnMut() -> T, +{ + // Initial bench run to get ballpark figure. + let ns_single = ns_iter_inner(inner, 1); + + // Try to estimate iter count for 1ms falling back to 1m + // iterations if first run took < 1ns. + let ns_target_total = 1_000_000; // 1ms + let mut n = ns_target_total / cmp::max(1, ns_single); + + // if the first run took more than 1ms we don't want to just + // be left doing 0 iterations on every loop. The unfortunate + // side effect of not being able to do as many runs is + // automatically handled by the statistical analysis below + // (i.e., larger error bars). + n = cmp::max(1, n); + + let mut total_run = Duration::new(0, 0); + let samples: &mut [f64] = &mut [0.0_f64; 50]; + loop { + let loop_start = Instant::now(); + + for p in &mut *samples { + *p = ns_iter_inner(inner, n) as f64 / n as f64; + } + + stats::winsorize(samples, 5.0); + let summ = stats::Summary::new(samples); + + for p in &mut *samples { + let ns = ns_iter_inner(inner, 5 * n); + *p = ns as f64 / (5 * n) as f64; + } + + stats::winsorize(samples, 5.0); + let summ5 = stats::Summary::new(samples); + + let loop_run = loop_start.elapsed(); + + // If we've run for 100ms and seem to have converged to a + // stable median. + if loop_run > Duration::from_millis(100) + && summ.median_abs_dev_pct < 1.0 + && summ.median - summ5.median < summ5.median_abs_dev + { + return summ5; + } + + total_run = total_run + loop_run; + // Longest we ever run for is 3s. + if total_run > Duration::from_secs(3) { + return summ5; + } + + // If we overflow here just return the results so far. We check a + // multiplier of 10 because we're about to multiply by 2 and the + // next iteration of the loop will also multiply by 5 (to calculate + // the summ5 result) + n = match n.checked_mul(10) { + Some(_) => n * 2, + None => { + return summ5; + } + }; + } +} + +pub fn benchmark(desc: TestDesc, monitor_ch: Sender, nocapture: bool, f: F) +where + F: FnMut(&mut Bencher), +{ + let mut bs = Bencher { + mode: BenchMode::Auto, + summary: None, + bytes: 0, + }; + + let data = Arc::new(Mutex::new(Vec::new())); + let oldio = if !nocapture { + Some(( + io::set_print(Some(Box::new(Sink(data.clone())))), + io::set_panic(Some(Box::new(Sink(data.clone())))), + )) + } else { + None + }; + + let result = catch_unwind(AssertUnwindSafe(|| bs.bench(f))); + + if let Some((printio, panicio)) = oldio { + io::set_print(printio); + io::set_panic(panicio); + } + + let test_result = match result { + //bs.bench(f) { + Ok(Some(ns_iter_summ)) => { + let ns_iter = cmp::max(ns_iter_summ.median as u64, 1); + let mb_s = bs.bytes * 1000 / ns_iter; + + let bs = BenchSamples { + ns_iter_summ, + mb_s: mb_s as usize, + }; + TestResult::TrBench(bs) + } + Ok(None) => { + // iter not called, so no data. + // FIXME: error in this case? + let samples: &mut [f64] = &mut [0.0_f64; 1]; + let bs = BenchSamples { + ns_iter_summ: stats::Summary::new(samples), + mb_s: 0, + }; + TestResult::TrBench(bs) + } + Err(_) => TestResult::TrFailed, + }; + + let stdout = data.lock().unwrap().to_vec(); + monitor_ch.send((desc, test_result, None, stdout)).unwrap(); +} + +pub fn run_once(f: F) +where + F: FnMut(&mut Bencher), +{ + let mut bs = Bencher { + mode: BenchMode::Single, + summary: None, + bytes: 0, + }; + bs.bench(f); +} diff --git a/src/libtest/cli.rs b/src/libtest/cli.rs new file mode 100644 index 000000000000..b35193701d6e --- /dev/null +++ b/src/libtest/cli.rs @@ -0,0 +1,384 @@ +//! Module converting command-line arguments into test configuration. + +use std::env; +use std::path::PathBuf; +use getopts; + +use super::options::{RunIgnored, ColorConfig, OutputFormat, Options}; +use super::time::TestTimeOptions; +use super::helpers::isatty; + +#[derive(Debug)] +pub struct TestOpts { + pub list: bool, + pub filter: Option, + pub filter_exact: bool, + pub exclude_should_panic: bool, + pub run_ignored: RunIgnored, + pub run_tests: bool, + pub bench_benchmarks: bool, + pub logfile: Option, + pub nocapture: bool, + pub color: ColorConfig, + pub format: OutputFormat, + pub test_threads: Option, + pub skip: Vec, + pub time_options: Option, + pub options: Options, +} + +impl TestOpts { + pub fn use_color(&self) -> bool { + match self.color { + ColorConfig::AutoColor => !self.nocapture && isatty::stdout_isatty(), + ColorConfig::AlwaysColor => true, + ColorConfig::NeverColor => false, + } + } +} + +/// Result of parsing the options. +pub type OptRes = Result; +/// Result of parsing the option part. +type OptPartRes = Result, String>; + +fn optgroups() -> getopts::Options { + let mut opts = getopts::Options::new(); + opts.optflag("", "include-ignored", "Run ignored and not ignored tests") + .optflag("", "ignored", "Run only ignored tests") + .optflag("", "exclude-should-panic", "Excludes tests marked as should_panic") + .optflag("", "test", "Run tests and not benchmarks") + .optflag("", "bench", "Run benchmarks instead of tests") + .optflag("", "list", "List all tests and benchmarks") + .optflag("h", "help", "Display this message (longer with --help)") + .optopt( + "", + "logfile", + "Write logs to the specified file instead \ + of stdout", + "PATH", + ) + .optflag( + "", + "nocapture", + "don't capture stdout/stderr of each \ + task, allow printing directly", + ) + .optopt( + "", + "test-threads", + "Number of threads used for running tests \ + in parallel", + "n_threads", + ) + .optmulti( + "", + "skip", + "Skip tests whose names contain FILTER (this flag can \ + be used multiple times)", + "FILTER", + ) + .optflag( + "q", + "quiet", + "Display one character per test instead of one line. \ + Alias to --format=terse", + ) + .optflag( + "", + "exact", + "Exactly match filters rather than by substring", + ) + .optopt( + "", + "color", + "Configure coloring of output: + auto = colorize if stdout is a tty and tests are run on serially (default); + always = always colorize output; + never = never colorize output;", + "auto|always|never", + ) + .optopt( + "", + "format", + "Configure formatting of output: + pretty = Print verbose output; + terse = Display one character per test; + json = Output a json document", + "pretty|terse|json", + ) + .optflag( + "", + "show-output", + "Show captured stdout of successful tests" + ) + .optopt( + "Z", + "", + "Enable nightly-only flags: + unstable-options = Allow use of experimental features", + "unstable-options", + ) + .optflagopt( + "", + "report-time", + "Show execution time of each test. Awailable values: + plain = do not colorize the execution time (default); + colored = colorize output according to the `color` parameter value; + + Threshold values for colorized output can be configured via + `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and + `RUST_TEST_TIME_DOCTEST` environment variables. + + Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`. + + Not available for --format=terse", + "plain|colored" + ) + .optflag( + "", + "ensure-time", + "Treat excess of the test execution time limit as error. + + Threshold values for this option can be configured via + `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and + `RUST_TEST_TIME_DOCTEST` environment variables. + + Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`. + + `CRITICAL_TIME` here means the limit that should not be exceeded by test. + " + ); + return opts; +} + +fn usage(binary: &str, options: &getopts::Options) { + let message = format!("Usage: {} [OPTIONS] [FILTER]", binary); + println!( + r#"{usage} + +The FILTER string is tested against the name of all tests, and only those +tests whose names contain the filter are run. + +By default, all tests are run in parallel. This can be altered with the +--test-threads flag or the RUST_TEST_THREADS environment variable when running +tests (set it to 1). + +All tests have their standard output and standard error captured by default. +This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE +environment variable to a value other than "0". Logging is not captured by default. + +Test Attributes: + + `#[test]` - Indicates a function is a test to be run. This function + takes no arguments. + `#[bench]` - Indicates a function is a benchmark to be run. This + function takes one argument (test::Bencher). + `#[should_panic]` - This function (also labeled with `#[test]`) will only pass if + the code causes a panic (an assertion failure or panic!) + A message may be provided, which the failure string must + contain: #[should_panic(expected = "foo")]. + `#[ignore]` - When applied to a function which is already attributed as a + test, then the test runner will ignore these tests during + normal test runs. Running with --ignored or --include-ignored will run + these tests."#, + usage = options.usage(&message) + ); +} + +// FIXME: Copied from libsyntax until linkage errors are resolved. Issue #47566 +fn is_nightly() -> bool { + // Whether this is a feature-staged build, i.e., on the beta or stable channel + let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some(); + // Whether we should enable unstable features for bootstrapping + let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok(); + + bootstrap || !disable_unstable_features +} + +// Gets the option value and checks if unstable features are enabled. +macro_rules! unstable_optflag { + ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{ + let opt = $matches.opt_present($option_name); + if !$allow_unstable && opt { + return Some(Err(format!( + "The \"{}\" flag is only accepted on the nightly compiler", + $option_name + ))); + } + + opt + }}; +} + +// Gets the CLI options assotiated with `report-time` feature. +fn get_time_options( + matches: &getopts::Matches, + allow_unstable: bool) +-> Option> { + let report_time = unstable_optflag!(matches, allow_unstable, "report-time"); + let colored_opt_str = matches.opt_str("report-time"); + let mut report_time_colored = report_time && colored_opt_str == Some("colored".into()); + let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time"); + + // If `ensure-test-time` option is provided, time output is enforced, + // so user won't be confused if any of tests will silently fail. + let options = if report_time || ensure_test_time { + if ensure_test_time && !report_time { + report_time_colored = true; + } + Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored)) + } else { + None + }; + + Some(Ok(options)) +} + +// Parses command line arguments into test options +pub fn parse_opts(args: &[String]) -> Option { + let mut allow_unstable = false; + let opts = optgroups(); + let args = args.get(1..).unwrap_or(args); + let matches = match opts.parse(args) { + Ok(m) => m, + Err(f) => return Some(Err(f.to_string())), + }; + + if let Some(opt) = matches.opt_str("Z") { + if !is_nightly() { + return Some(Err( + "the option `Z` is only accepted on the nightly compiler".into(), + )); + } + + match &*opt { + "unstable-options" => { + allow_unstable = true; + } + _ => { + return Some(Err("Unrecognized option to `Z`".into())); + } + } + }; + + if matches.opt_present("h") { + usage(&args[0], &opts); + return None; + } + + let filter = if !matches.free.is_empty() { + Some(matches.free[0].clone()) + } else { + None + }; + + let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic"); + + let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored"); + + let run_ignored = match (include_ignored, matches.opt_present("ignored")) { + (true, true) => { + return Some(Err( + "the options --include-ignored and --ignored are mutually exclusive".into(), + )); + } + (true, false) => RunIgnored::Yes, + (false, true) => RunIgnored::Only, + (false, false) => RunIgnored::No, + }; + let quiet = matches.opt_present("quiet"); + let exact = matches.opt_present("exact"); + let list = matches.opt_present("list"); + + let logfile = matches.opt_str("logfile"); + let logfile = logfile.map(|s| PathBuf::from(&s)); + + let bench_benchmarks = matches.opt_present("bench"); + let run_tests = !bench_benchmarks || matches.opt_present("test"); + + let mut nocapture = matches.opt_present("nocapture"); + if !nocapture { + nocapture = match env::var("RUST_TEST_NOCAPTURE") { + Ok(val) => &val != "0", + Err(_) => false, + }; + } + + let time_options = match get_time_options(&matches, allow_unstable) { + Some(Ok(val)) => val, + Some(Err(e)) => return Some(Err(e)), + None => panic!("Unexpected output from `get_time_options`"), + }; + + let test_threads = match matches.opt_str("test-threads") { + Some(n_str) => match n_str.parse::() { + Ok(0) => return Some(Err("argument for --test-threads must not be 0".to_string())), + Ok(n) => Some(n), + Err(e) => { + return Some(Err(format!( + "argument for --test-threads must be a number > 0 \ + (error: {})", + e + ))); + } + }, + None => None, + }; + + let color = match matches.opt_str("color").as_ref().map(|s| &**s) { + Some("auto") | None => ColorConfig::AutoColor, + Some("always") => ColorConfig::AlwaysColor, + Some("never") => ColorConfig::NeverColor, + + Some(v) => { + return Some(Err(format!( + "argument for --color must be auto, always, or never (was \ + {})", + v + ))); + } + }; + + let format = match matches.opt_str("format").as_ref().map(|s| &**s) { + None if quiet => OutputFormat::Terse, + Some("pretty") | None => OutputFormat::Pretty, + Some("terse") => OutputFormat::Terse, + Some("json") => { + if !allow_unstable { + return Some(Err( + "The \"json\" format is only accepted on the nightly compiler".into(), + )); + } + OutputFormat::Json + } + + Some(v) => { + return Some(Err(format!( + "argument for --format must be pretty, terse, or json (was \ + {})", + v + ))); + } + }; + + let test_opts = TestOpts { + list, + filter, + filter_exact: exact, + exclude_should_panic, + run_ignored, + run_tests, + bench_benchmarks, + logfile, + nocapture, + color, + format, + test_threads, + skip: matches.opt_strs("skip"), + time_options, + options: Options::new().display_output(matches.opt_present("show-output")), + }; + + Some(Ok(test_opts)) +} diff --git a/src/libtest/formatters/json.rs b/src/libtest/formatters/json.rs index dcd733620bf9..ff756c456dae 100644 --- a/src/libtest/formatters/json.rs +++ b/src/libtest/formatters/json.rs @@ -27,7 +27,7 @@ impl JsonFormatter { ty: &str, name: &str, evt: &str, - exec_time: Option<&TestExecTime>, + exec_time: Option<&time::TestExecTime>, stdout: Option>, extra: Option<&str>, ) -> io::Result<()> { @@ -76,7 +76,7 @@ impl OutputFormatter for JsonFormatter { &mut self, desc: &TestDesc, result: &TestResult, - exec_time: Option<&TestExecTime>, + exec_time: Option<&time::TestExecTime>, stdout: &[u8], state: &ConsoleTestState, ) -> io::Result<()> { diff --git a/src/libtest/formatters/mod.rs b/src/libtest/formatters/mod.rs index dd202fb3ab6f..72432cd8e3c2 100644 --- a/src/libtest/formatters/mod.rs +++ b/src/libtest/formatters/mod.rs @@ -16,7 +16,7 @@ pub(crate) trait OutputFormatter { &mut self, desc: &TestDesc, result: &TestResult, - exec_time: Option<&TestExecTime>, + exec_time: Option<&time::TestExecTime>, stdout: &[u8], state: &ConsoleTestState, ) -> io::Result<()>; diff --git a/src/libtest/formatters/pretty.rs b/src/libtest/formatters/pretty.rs index 2935b4c99cec..84e1a44dab80 100644 --- a/src/libtest/formatters/pretty.rs +++ b/src/libtest/formatters/pretty.rs @@ -3,7 +3,7 @@ use super::*; pub(crate) struct PrettyFormatter { out: OutputLocation, use_color: bool, - time_options: Option, + time_options: Option, /// Number of columns to fill when aligning names max_name_len: usize, @@ -17,7 +17,7 @@ impl PrettyFormatter { use_color: bool, max_name_len: usize, is_multithreaded: bool, - time_options: Option, + time_options: Option, ) -> Self { PrettyFormatter { out, @@ -93,7 +93,7 @@ impl PrettyFormatter { fn write_time( &mut self, desc: &TestDesc, - exec_time: Option<&TestExecTime> + exec_time: Option<&time::TestExecTime> ) -> io::Result<()> { if let (Some(opts), Some(time)) = (self.time_options, exec_time) { let time_str = format!(" <{}>", time); @@ -194,7 +194,7 @@ impl OutputFormatter for PrettyFormatter { &mut self, desc: &TestDesc, result: &TestResult, - exec_time: Option<&TestExecTime>, + exec_time: Option<&time::TestExecTime>, _: &[u8], _: &ConsoleTestState, ) -> io::Result<()> { @@ -225,7 +225,7 @@ impl OutputFormatter for PrettyFormatter { self.write_plain(&format!( "test {} has been running for over {} seconds\n", - desc.name, TEST_WARN_TIMEOUT_S + desc.name, time::TEST_WARN_TIMEOUT_S )) } diff --git a/src/libtest/formatters/terse.rs b/src/libtest/formatters/terse.rs index 8914e7b6b568..50407d1130f8 100644 --- a/src/libtest/formatters/terse.rs +++ b/src/libtest/formatters/terse.rs @@ -174,7 +174,7 @@ impl OutputFormatter for TerseFormatter { &mut self, desc: &TestDesc, result: &TestResult, - _: Option<&TestExecTime>, + _: Option<&time::TestExecTime>, _: &[u8], _: &ConsoleTestState, ) -> io::Result<()> { @@ -196,7 +196,7 @@ impl OutputFormatter for TerseFormatter { fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> { self.write_plain(&format!( "test {} has been running for over {} seconds\n", - desc.name, TEST_WARN_TIMEOUT_S + desc.name, time::TEST_WARN_TIMEOUT_S )) } diff --git a/src/libtest/helpers/concurrency.rs b/src/libtest/helpers/concurrency.rs new file mode 100644 index 000000000000..f0292c2d2c79 --- /dev/null +++ b/src/libtest/helpers/concurrency.rs @@ -0,0 +1,153 @@ +//! Helper module which helps to determine amount of threads to be used +//! during tests execution. +use std::env; + +#[cfg(any(unix, target_os = "cloudabi"))] +use libc; + +#[allow(deprecated)] +pub fn get_concurrency() -> usize { + return match env::var("RUST_TEST_THREADS") { + Ok(s) => { + let opt_n: Option = s.parse().ok(); + match opt_n { + Some(n) if n > 0 => n, + _ => panic!( + "RUST_TEST_THREADS is `{}`, should be a positive integer.", + s + ), + } + } + Err(..) => num_cpus(), + }; + + #[cfg(windows)] + #[allow(nonstandard_style)] + fn num_cpus() -> usize { + #[repr(C)] + struct SYSTEM_INFO { + wProcessorArchitecture: u16, + wReserved: u16, + dwPageSize: u32, + lpMinimumApplicationAddress: *mut u8, + lpMaximumApplicationAddress: *mut u8, + dwActiveProcessorMask: *mut u8, + dwNumberOfProcessors: u32, + dwProcessorType: u32, + dwAllocationGranularity: u32, + wProcessorLevel: u16, + wProcessorRevision: u16, + } + extern "system" { + fn GetSystemInfo(info: *mut SYSTEM_INFO) -> i32; + } + unsafe { + let mut sysinfo = std::mem::zeroed(); + GetSystemInfo(&mut sysinfo); + sysinfo.dwNumberOfProcessors as usize + } + } + + #[cfg(target_os = "vxworks")] + fn num_cpus() -> usize { + // FIXME: Implement num_cpus on vxWorks + 1 + } + + #[cfg(target_os = "redox")] + fn num_cpus() -> usize { + // FIXME: Implement num_cpus on Redox + 1 + } + + #[cfg(any( + all(target_arch = "wasm32", not(target_os = "emscripten")), + all(target_vendor = "fortanix", target_env = "sgx") + ))] + fn num_cpus() -> usize { + 1 + } + + #[cfg(any( + target_os = "android", + target_os = "cloudabi", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + ))] + fn num_cpus() -> usize { + unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize } + } + + #[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "netbsd" + ))] + fn num_cpus() -> usize { + use std::ptr; + + let mut cpus: libc::c_uint = 0; + let mut cpus_size = std::mem::size_of_val(&cpus); + + unsafe { + cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint; + } + if cpus < 1 { + let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0]; + unsafe { + libc::sysctl( + mib.as_mut_ptr(), + 2, + &mut cpus as *mut _ as *mut _, + &mut cpus_size as *mut _ as *mut _, + ptr::null_mut(), + 0, + ); + } + if cpus < 1 { + cpus = 1; + } + } + cpus as usize + } + + #[cfg(target_os = "openbsd")] + fn num_cpus() -> usize { + use std::ptr; + + let mut cpus: libc::c_uint = 0; + let mut cpus_size = std::mem::size_of_val(&cpus); + let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0]; + + unsafe { + libc::sysctl( + mib.as_mut_ptr(), + 2, + &mut cpus as *mut _ as *mut _, + &mut cpus_size as *mut _ as *mut _, + ptr::null_mut(), + 0, + ); + } + if cpus < 1 { + cpus = 1; + } + cpus as usize + } + + #[cfg(target_os = "haiku")] + fn num_cpus() -> usize { + // FIXME: implement + 1 + } + + #[cfg(target_os = "l4re")] + fn num_cpus() -> usize { + // FIXME: implement + 1 + } +} diff --git a/src/libtest/helpers/isatty.rs b/src/libtest/helpers/isatty.rs new file mode 100644 index 000000000000..638328aea18c --- /dev/null +++ b/src/libtest/helpers/isatty.rs @@ -0,0 +1,33 @@ +//! Helper module which provides a function to test +//! if stdout is a tty. + +#[cfg(any( + target_os = "cloudabi", + all(target_arch = "wasm32", not(target_os = "emscripten")), + all(target_vendor = "fortanix", target_env = "sgx") +))] +pub fn stdout_isatty() -> bool { + // FIXME: Implement isatty on SGX + false +} +#[cfg(unix)] +pub fn stdout_isatty() -> bool { + unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 } +} +#[cfg(windows)] +pub fn stdout_isatty() -> bool { + type DWORD = u32; + type BOOL = i32; + type HANDLE = *mut u8; + type LPDWORD = *mut u32; + const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD; + extern "system" { + fn GetStdHandle(which: DWORD) -> HANDLE; + fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL; + } + unsafe { + let handle = GetStdHandle(STD_OUTPUT_HANDLE); + let mut out = 0; + GetConsoleMode(handle, &mut out) != 0 + } +} \ No newline at end of file diff --git a/src/libtest/helpers/metrics.rs b/src/libtest/helpers/metrics.rs new file mode 100644 index 000000000000..f77a23e6875b --- /dev/null +++ b/src/libtest/helpers/metrics.rs @@ -0,0 +1,50 @@ +//! Benchmark metrics. +use std::collections::BTreeMap; + +#[derive(Clone, PartialEq, Debug, Copy)] +pub struct Metric { + value: f64, + noise: f64, +} + +impl Metric { + pub fn new(value: f64, noise: f64) -> Metric { + Metric { value, noise } + } +} + +#[derive(Clone, PartialEq)] +pub struct MetricMap(BTreeMap); + +impl MetricMap { + pub fn new() -> MetricMap { + MetricMap(BTreeMap::new()) + } + + /// Insert a named `value` (+/- `noise`) metric into the map. The value + /// must be non-negative. The `noise` indicates the uncertainty of the + /// metric, which doubles as the "noise range" of acceptable + /// pairwise-regressions on this named value, when comparing from one + /// metric to the next using `compare_to_old`. + /// + /// If `noise` is positive, then it means this metric is of a value + /// you want to see grow smaller, so a change larger than `noise` in the + /// positive direction represents a regression. + /// + /// If `noise` is negative, then it means this metric is of a value + /// you want to see grow larger, so a change larger than `noise` in the + /// negative direction represents a regression. + pub fn insert_metric(&mut self, name: &str, value: f64, noise: f64) { + let m = Metric { value, noise }; + self.0.insert(name.to_owned(), m); + } + + pub fn fmt_metrics(&self) -> String { + let v = self + .0 + .iter() + .map(|(k, v)| format!("{}: {} (+/- {})", *k, v.value, v.noise)) + .collect::>(); + v.join(", ") + } +} diff --git a/src/libtest/helpers/mod.rs b/src/libtest/helpers/mod.rs new file mode 100644 index 000000000000..0bbe77b1c50a --- /dev/null +++ b/src/libtest/helpers/mod.rs @@ -0,0 +1,6 @@ +//! Module with common helpers not directly related to tests +//! but used in `libtest`. + +pub mod concurrency; +pub mod isatty; +pub mod metrics; diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 4c3cbeb4acce..f79994671114 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -30,33 +30,21 @@ #![feature(termination_trait_lib)] #![feature(test)] -use getopts; -#[cfg(any(unix, target_os = "cloudabi"))] -extern crate libc; use term; pub use self::ColorConfig::*; -use self::NamePadding::*; use self::OutputLocation::*; use self::TestEvent::*; -pub use self::TestFn::*; -pub use self::TestName::*; -pub use self::TestResult::*; +pub use self::types::TestName::*; -use std::any::Any; use std::borrow::Cow; -use std::cmp; -use std::collections::BTreeMap; use std::env; -use std::fmt; use std::fs::File; use std::io; use std::io::prelude::*; use std::panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo}; -use std::path::PathBuf; use std::process; use std::process::{ExitStatus, Command, Termination}; -use std::str::FromStr; use std::sync::mpsc::{channel, Sender}; use std::sync::{Arc, Mutex}; use std::thread; @@ -65,277 +53,49 @@ use std::time::{Duration, Instant}; #[cfg(test)] mod tests; -const TEST_WARN_TIMEOUT_S: u64 = 60; const QUIET_MODE_MAX_COLUMN: usize = 100; // insert a '\n' after 100 tests in quiet mode const SECONDARY_TEST_INVOKER_VAR: &'static str = "__RUST_TEST_INVOKE"; -// Return codes for secondary process. -// Start somewhere other than 0 so we know the return code means what we think -// it means. -const TR_OK: i32 = 50; -const TR_FAILED: i32 = 51; - -/// This small module contains constants used by `report-time` option. -/// Those constants values will be used if corresponding environment variables are not set. -/// -/// To override values for unit-tests, use a constant `RUST_TEST_TIME_UNIT`, -/// To override values for integration tests, use a constant `RUST_TEST_TIME_INTEGRATION`, -/// To override values for doctests, use a constant `RUST_TEST_TIME_DOCTEST`. -/// -/// Example of the expected format is `RUST_TEST_TIME_xxx=100,200`, where 100 means -/// warn time, and 200 means critical time. -pub mod time_constants { - use std::time::Duration; - use super::TEST_WARN_TIMEOUT_S; - - /// Environment variable for overriding default threshold for unit-tests. - pub const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; - - // Unit tests are supposed to be really quick. - pub const UNIT_WARN: Duration = Duration::from_millis(50); - pub const UNIT_CRITICAL: Duration = Duration::from_millis(100); - - /// Environment variable for overriding default threshold for unit-tests. - pub const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION"; - - // Integration tests may have a lot of work, so they can take longer to execute. - pub const INTEGRATION_WARN: Duration = Duration::from_millis(500); - pub const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); - - /// Environment variable for overriding default threshold for unit-tests. - pub const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST"; - - // Doctests are similar to integration tests, because they can include a lot of - // initialization code. - pub const DOCTEST_WARN: Duration = INTEGRATION_WARN; - pub const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; - - // Do not suppose anything about unknown tests, base limits on the - // `TEST_WARN_TIMEOUT_S` constant. - pub const UNKNOWN_WARN: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S); - pub const UNKNOWN_CRITICAL: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S * 2); -} - // to be used by rustc to compile tests in libtest pub mod test { pub use crate::{ - assert_test_result, filter_tests, parse_opts, run_test, test_main, test_main_static, - Bencher, DynTestFn, DynTestName, Metric, MetricMap, Options, RunIgnored, RunStrategy, - ShouldPanic, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, TestDescAndFn, TestName, - TestOpts, TestTimeOptions, TestType, TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk, + bench::Bencher, + cli::{parse_opts, TestOpts}, + helpers::metrics::{Metric, MetricMap}, + options::{ShouldPanic, Options, RunIgnored, RunStrategy}, + test_result::{TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk}, + time::TestTimeOptions, + types::{ + DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, TestDescAndFn, + TestName, TestType, + }, + assert_test_result, filter_tests, run_test, test_main, test_main_static, }; } +use bench::*; +use test_result::*; +use types::*; +use options::*; +use cli::*; + +use helpers::concurrency::get_concurrency; +use helpers::metrics::MetricMap; + mod formatters; pub mod stats; +mod cli; +mod helpers; +mod time; +mod types; +mod options; +mod bench; +mod test_result; + use crate::formatters::{JsonFormatter, OutputFormatter, PrettyFormatter, TerseFormatter}; -/// Whether to execute tests concurrently or not -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Concurrent { - Yes, - No, -} - -/// Type of the test according to the [rust book](https://doc.rust-lang.org/cargo/guide/tests.html) -/// conventions. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum TestType { - /// Unit-tests are expected to be in the `src` folder of the crate. - UnitTest, - /// Integration-style tests are expected to be in the `tests` folder of the crate. - IntegrationTest, - /// Doctests are created by the `librustdoc` manually, so it's a different type of test. - DocTest, - /// Tests for the sources that don't follow the project layout convention - /// (e.g. tests in raw `main.rs` compiled by calling `rustc --test` directly). - Unknown, -} - -// The name of a test. By convention this follows the rules for rust -// paths; i.e., it should be a series of identifiers separated by double -// colons. This way if some test runner wants to arrange the tests -// hierarchically it may. - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub enum TestName { - StaticTestName(&'static str), - DynTestName(String), - AlignedTestName(Cow<'static, str>, NamePadding), -} -impl TestName { - fn as_slice(&self) -> &str { - match *self { - StaticTestName(s) => s, - DynTestName(ref s) => s, - AlignedTestName(ref s, _) => &*s, - } - } - - fn padding(&self) -> NamePadding { - match self { - &AlignedTestName(_, p) => p, - _ => PadNone, - } - } - - fn with_padding(&self, padding: NamePadding) -> TestName { - let name = match self { - &TestName::StaticTestName(name) => Cow::Borrowed(name), - &TestName::DynTestName(ref name) => Cow::Owned(name.clone()), - &TestName::AlignedTestName(ref name, _) => name.clone(), - }; - - TestName::AlignedTestName(name, padding) - } -} -impl fmt::Display for TestName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.as_slice(), f) - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub enum NamePadding { - PadNone, - PadOnRight, -} - -impl TestDesc { - fn padded_name(&self, column_count: usize, align: NamePadding) -> String { - let mut name = String::from(self.name.as_slice()); - let fill = column_count.saturating_sub(name.len()); - let pad = " ".repeat(fill); - match align { - PadNone => name, - PadOnRight => { - name.push_str(&pad); - name - } - } - } -} - -/// Represents a benchmark function. -pub trait TDynBenchFn: Send { - fn run(&self, harness: &mut Bencher); -} - -// A function that runs a test. If the function returns successfully, -// the test succeeds; if the function panics then the test fails. We -// may need to come up with a more clever definition of test in order -// to support isolation of tests into threads. -pub enum TestFn { - StaticTestFn(fn()), - StaticBenchFn(fn(&mut Bencher)), - DynTestFn(Box), - DynBenchFn(Box), -} - -impl TestFn { - fn padding(&self) -> NamePadding { - match *self { - StaticTestFn(..) => PadNone, - StaticBenchFn(..) => PadOnRight, - DynTestFn(..) => PadNone, - DynBenchFn(..) => PadOnRight, - } - } -} - -impl fmt::Debug for TestFn { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match *self { - StaticTestFn(..) => "StaticTestFn(..)", - StaticBenchFn(..) => "StaticBenchFn(..)", - DynTestFn(..) => "DynTestFn(..)", - DynBenchFn(..) => "DynBenchFn(..)", - }) - } -} - -/// Manager of the benchmarking runs. -/// -/// This is fed into functions marked with `#[bench]` to allow for -/// set-up & tear-down before running a piece of code repeatedly via a -/// call to `iter`. -#[derive(Clone)] -pub struct Bencher { - mode: BenchMode, - summary: Option, - pub bytes: u64, -} - -#[derive(Clone, PartialEq, Eq)] -pub enum BenchMode { - Auto, - Single, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum ShouldPanic { - No, - Yes, - YesWithMessage(&'static str), -} - -// The definition of a single test. A test runner will run a list of -// these. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct TestDesc { - pub name: TestName, - pub ignore: bool, - pub should_panic: ShouldPanic, - pub allow_fail: bool, - pub test_type: TestType, -} - -#[derive(Debug)] -pub struct TestDescAndFn { - pub desc: TestDesc, - pub testfn: TestFn, -} - -#[derive(Clone, PartialEq, Debug, Copy)] -pub struct Metric { - value: f64, - noise: f64, -} - -impl Metric { - pub fn new(value: f64, noise: f64) -> Metric { - Metric { value, noise } - } -} - -/// In case we want to add other options as well, just add them in this struct. -#[derive(Copy, Clone, Debug)] -pub struct Options { - display_output: bool, - panic_abort: bool, -} - -impl Options { - pub fn new() -> Options { - Options { - display_output: false, - panic_abort: false, - } - } - - pub fn display_output(mut self, display_output: bool) -> Options { - self.display_output = display_output; - self - } - - pub fn panic_abort(mut self, panic_abort: bool) -> Options { - self.panic_abort = panic_abort; - self - } -} - // The default console test runner. It accepts the command line // arguments and a vector of test_descs. pub fn test_main(args: &[String], tests: Vec, options: Option) { @@ -440,556 +200,6 @@ pub fn assert_test_result(result: T) { ); } -#[derive(Copy, Clone, Debug)] -pub enum ColorConfig { - AutoColor, - AlwaysColor, - NeverColor, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum OutputFormat { - Pretty, - Terse, - Json, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum RunIgnored { - Yes, - No, - Only, -} - -/// Structure denoting time limits for test execution. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub struct TimeThreshold { - pub warn: Duration, - pub critical: Duration, -} - -impl TimeThreshold { - /// Creates a new `TimeThreshold` instance with provided durations. - pub fn new(warn: Duration, critical: Duration) -> Self { - Self { - warn, - critical, - } - } - - /// Attempts to create a `TimeThreshold` instance with values obtained - /// from the environment variable, and returns `None` if the variable - /// is not set. - /// Environment variable format is expected to match `\d+,\d+`. - /// - /// # Panics - /// - /// Panics if variable with provided name is set but contains inappropriate - /// value. - pub fn from_env_var(env_var_name: &str) -> Option { - let durations_str = env::var(env_var_name).ok()?; - - // Split string into 2 substrings by comma and try to parse numbers. - let mut durations = durations_str - .splitn(2, ',') - .map(|v| { - u64::from_str(v).unwrap_or_else(|_| { - panic!( - "Duration value in variable {} is expected to be a number, but got {}", - env_var_name, v - ) - }) - }); - - // Callback to be called if the environment variable has unexpected structure. - let panic_on_incorrect_value = || { - panic!( - "Duration variable {} expected to have 2 numbers separated by comma, but got {}", - env_var_name, durations_str - ); - }; - - let (warn, critical) = ( - durations.next().unwrap_or_else(panic_on_incorrect_value), - durations.next().unwrap_or_else(panic_on_incorrect_value) - ); - - if warn > critical { - panic!("Test execution warn time should be less or equal to the critical time"); - } - - Some(Self::new(Duration::from_millis(warn), Duration::from_millis(critical))) - } -} - -/// Structure with parameters for calculating test execution time. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub struct TestTimeOptions { - /// Denotes if the test critical execution time limit excess should be considered - /// a test failure. - pub error_on_excess: bool, - pub colored: bool, - pub unit_threshold: TimeThreshold, - pub integration_threshold: TimeThreshold, - pub doctest_threshold: TimeThreshold, -} - -impl TestTimeOptions { - pub fn new_from_env(error_on_excess: bool, colored: bool) -> Self { - let unit_threshold = - TimeThreshold::from_env_var(time_constants::UNIT_ENV_NAME) - .unwrap_or_else(Self::default_unit); - - let integration_threshold = - TimeThreshold::from_env_var(time_constants::INTEGRATION_ENV_NAME) - .unwrap_or_else(Self::default_integration); - - let doctest_threshold = - TimeThreshold::from_env_var(time_constants::DOCTEST_ENV_NAME) - .unwrap_or_else(Self::default_doctest); - - Self { - error_on_excess, - colored, - unit_threshold, - integration_threshold, - doctest_threshold, - } - } - - pub fn is_warn(&self, test: &TestDesc, exec_time: &TestExecTime) -> bool { - exec_time.0 >= self.warn_time(test) - } - - pub fn is_critical(&self, test: &TestDesc, exec_time: &TestExecTime) -> bool { - exec_time.0 >= self.critical_time(test) - } - - fn warn_time(&self, test: &TestDesc) -> Duration { - match test.test_type { - TestType::UnitTest => self.unit_threshold.warn, - TestType::IntegrationTest => self.integration_threshold.warn, - TestType::DocTest => self.doctest_threshold.warn, - TestType::Unknown => time_constants::UNKNOWN_WARN, - } - } - - fn critical_time(&self, test: &TestDesc) -> Duration { - match test.test_type { - TestType::UnitTest => self.unit_threshold.critical, - TestType::IntegrationTest => self.integration_threshold.critical, - TestType::DocTest => self.doctest_threshold.critical, - TestType::Unknown => time_constants::UNKNOWN_CRITICAL, - } - } - - fn default_unit() -> TimeThreshold { - TimeThreshold::new(time_constants::UNIT_WARN, time_constants::UNIT_CRITICAL) - } - - fn default_integration() -> TimeThreshold { - TimeThreshold::new(time_constants::INTEGRATION_WARN, time_constants::INTEGRATION_CRITICAL) - } - - fn default_doctest() -> TimeThreshold { - TimeThreshold::new(time_constants::DOCTEST_WARN, time_constants::DOCTEST_CRITICAL) - } -} - -#[derive(Debug)] -pub struct TestOpts { - pub list: bool, - pub filter: Option, - pub filter_exact: bool, - pub exclude_should_panic: bool, - pub run_ignored: RunIgnored, - pub run_tests: bool, - pub bench_benchmarks: bool, - pub logfile: Option, - pub nocapture: bool, - pub color: ColorConfig, - pub format: OutputFormat, - pub test_threads: Option, - pub skip: Vec, - pub time_options: Option, - pub options: Options, -} - -/// Result of parsing the options. -pub type OptRes = Result; -/// Result of parsing the option part. -type OptPartRes = Result, String>; - -fn optgroups() -> getopts::Options { - let mut opts = getopts::Options::new(); - opts.optflag("", "include-ignored", "Run ignored and not ignored tests") - .optflag("", "ignored", "Run only ignored tests") - .optflag("", "exclude-should-panic", "Excludes tests marked as should_panic") - .optflag("", "test", "Run tests and not benchmarks") - .optflag("", "bench", "Run benchmarks instead of tests") - .optflag("", "list", "List all tests and benchmarks") - .optflag("h", "help", "Display this message (longer with --help)") - .optopt( - "", - "logfile", - "Write logs to the specified file instead \ - of stdout", - "PATH", - ) - .optflag( - "", - "nocapture", - "don't capture stdout/stderr of each \ - task, allow printing directly", - ) - .optopt( - "", - "test-threads", - "Number of threads used for running tests \ - in parallel", - "n_threads", - ) - .optmulti( - "", - "skip", - "Skip tests whose names contain FILTER (this flag can \ - be used multiple times)", - "FILTER", - ) - .optflag( - "q", - "quiet", - "Display one character per test instead of one line. \ - Alias to --format=terse", - ) - .optflag( - "", - "exact", - "Exactly match filters rather than by substring", - ) - .optopt( - "", - "color", - "Configure coloring of output: - auto = colorize if stdout is a tty and tests are run on serially (default); - always = always colorize output; - never = never colorize output;", - "auto|always|never", - ) - .optopt( - "", - "format", - "Configure formatting of output: - pretty = Print verbose output; - terse = Display one character per test; - json = Output a json document", - "pretty|terse|json", - ) - .optflag( - "", - "show-output", - "Show captured stdout of successful tests" - ) - .optopt( - "Z", - "", - "Enable nightly-only flags: - unstable-options = Allow use of experimental features", - "unstable-options", - ) - .optflagopt( - "", - "report-time", - "Show execution time of each test. Awailable values: - plain = do not colorize the execution time (default); - colored = colorize output according to the `color` parameter value; - - Threshold values for colorized output can be configured via - `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and - `RUST_TEST_TIME_DOCTEST` environment variables. - - Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`. - - Not available for --format=terse", - "plain|colored" - ) - .optflag( - "", - "ensure-time", - "Treat excess of the test execution time limit as error. - - Threshold values for this option can be configured via - `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and - `RUST_TEST_TIME_DOCTEST` environment variables. - - Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`. - - `CRITICAL_TIME` here means the limit that should not be exceeded by test. - " - ); - return opts; -} - -fn usage(binary: &str, options: &getopts::Options) { - let message = format!("Usage: {} [OPTIONS] [FILTER]", binary); - println!( - r#"{usage} - -The FILTER string is tested against the name of all tests, and only those -tests whose names contain the filter are run. - -By default, all tests are run in parallel. This can be altered with the ---test-threads flag or the RUST_TEST_THREADS environment variable when running -tests (set it to 1). - -All tests have their standard output and standard error captured by default. -This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE -environment variable to a value other than "0". Logging is not captured by default. - -Test Attributes: - - `#[test]` - Indicates a function is a test to be run. This function - takes no arguments. - `#[bench]` - Indicates a function is a benchmark to be run. This - function takes one argument (test::Bencher). - `#[should_panic]` - This function (also labeled with `#[test]`) will only pass if - the code causes a panic (an assertion failure or panic!) - A message may be provided, which the failure string must - contain: #[should_panic(expected = "foo")]. - `#[ignore]` - When applied to a function which is already attributed as a - test, then the test runner will ignore these tests during - normal test runs. Running with --ignored or --include-ignored will run - these tests."#, - usage = options.usage(&message) - ); -} - -// FIXME: Copied from libsyntax until linkage errors are resolved. Issue #47566 -fn is_nightly() -> bool { - // Whether this is a feature-staged build, i.e., on the beta or stable channel - let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some(); - // Whether we should enable unstable features for bootstrapping - let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok(); - - bootstrap || !disable_unstable_features -} - -// Gets the option value and checks if unstable features are enabled. -macro_rules! unstable_optflag { - ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{ - let opt = $matches.opt_present($option_name); - if !$allow_unstable && opt { - return Some(Err(format!( - "The \"{}\" flag is only accepted on the nightly compiler", - $option_name - ))); - } - - opt - }}; -} - -// Gets the CLI options assotiated with `report-time` feature. -fn get_time_options( - matches: &getopts::Matches, - allow_unstable: bool) --> Option> { - let report_time = unstable_optflag!(matches, allow_unstable, "report-time"); - let colored_opt_str = matches.opt_str("report-time"); - let mut report_time_colored = report_time && colored_opt_str == Some("colored".into()); - let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time"); - - // If `ensure-test-time` option is provided, time output is enforced, - // so user won't be confused if any of tests will silently fail. - let options = if report_time || ensure_test_time { - if ensure_test_time && !report_time { - report_time_colored = true; - } - Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored)) - } else { - None - }; - - Some(Ok(options)) -} - -// Parses command line arguments into test options -pub fn parse_opts(args: &[String]) -> Option { - let mut allow_unstable = false; - let opts = optgroups(); - let args = args.get(1..).unwrap_or(args); - let matches = match opts.parse(args) { - Ok(m) => m, - Err(f) => return Some(Err(f.to_string())), - }; - - if let Some(opt) = matches.opt_str("Z") { - if !is_nightly() { - return Some(Err( - "the option `Z` is only accepted on the nightly compiler".into(), - )); - } - - match &*opt { - "unstable-options" => { - allow_unstable = true; - } - _ => { - return Some(Err("Unrecognized option to `Z`".into())); - } - } - }; - - if matches.opt_present("h") { - usage(&args[0], &opts); - return None; - } - - let filter = if !matches.free.is_empty() { - Some(matches.free[0].clone()) - } else { - None - }; - - let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic"); - - let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored"); - - let run_ignored = match (include_ignored, matches.opt_present("ignored")) { - (true, true) => { - return Some(Err( - "the options --include-ignored and --ignored are mutually exclusive".into(), - )); - } - (true, false) => RunIgnored::Yes, - (false, true) => RunIgnored::Only, - (false, false) => RunIgnored::No, - }; - let quiet = matches.opt_present("quiet"); - let exact = matches.opt_present("exact"); - let list = matches.opt_present("list"); - - let logfile = matches.opt_str("logfile"); - let logfile = logfile.map(|s| PathBuf::from(&s)); - - let bench_benchmarks = matches.opt_present("bench"); - let run_tests = !bench_benchmarks || matches.opt_present("test"); - - let mut nocapture = matches.opt_present("nocapture"); - if !nocapture { - nocapture = match env::var("RUST_TEST_NOCAPTURE") { - Ok(val) => &val != "0", - Err(_) => false, - }; - } - - let time_options = match get_time_options(&matches, allow_unstable) { - Some(Ok(val)) => val, - Some(Err(e)) => return Some(Err(e)), - None => panic!("Unexpected output from `get_time_options`"), - }; - - let test_threads = match matches.opt_str("test-threads") { - Some(n_str) => match n_str.parse::() { - Ok(0) => return Some(Err("argument for --test-threads must not be 0".to_string())), - Ok(n) => Some(n), - Err(e) => { - return Some(Err(format!( - "argument for --test-threads must be a number > 0 \ - (error: {})", - e - ))); - } - }, - None => None, - }; - - let color = match matches.opt_str("color").as_ref().map(|s| &**s) { - Some("auto") | None => AutoColor, - Some("always") => AlwaysColor, - Some("never") => NeverColor, - - Some(v) => { - return Some(Err(format!( - "argument for --color must be auto, always, or never (was \ - {})", - v - ))); - } - }; - - let format = match matches.opt_str("format").as_ref().map(|s| &**s) { - None if quiet => OutputFormat::Terse, - Some("pretty") | None => OutputFormat::Pretty, - Some("terse") => OutputFormat::Terse, - Some("json") => { - if !allow_unstable { - return Some(Err( - "The \"json\" format is only accepted on the nightly compiler".into(), - )); - } - OutputFormat::Json - } - - Some(v) => { - return Some(Err(format!( - "argument for --format must be pretty, terse, or json (was \ - {})", - v - ))); - } - }; - - let test_opts = TestOpts { - list, - filter, - filter_exact: exact, - exclude_should_panic, - run_ignored, - run_tests, - bench_benchmarks, - logfile, - nocapture, - color, - format, - test_threads, - skip: matches.opt_strs("skip"), - time_options, - options: Options::new().display_output(matches.opt_present("show-output")), - }; - - Some(Ok(test_opts)) -} - -#[derive(Debug, Clone, PartialEq)] -pub struct BenchSamples { - ns_iter_summ: stats::Summary, - mb_s: usize, -} - -#[derive(Debug, Clone, PartialEq)] -pub enum TestResult { - TrOk, - TrFailed, - TrFailedMsg(String), - TrIgnored, - TrAllowedFail, - TrBench(BenchSamples), - TrTimedFail, -} - -unsafe impl Send for TestResult {} - -/// The meassured execution time of a unit test. -#[derive(Clone, PartialEq)] -pub struct TestExecTime(Duration); - -impl fmt::Display for TestExecTime { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:.3}s", self.0.as_secs_f64()) - } -} - enum OutputLocation { Pretty(Box), Raw(T), @@ -1071,7 +281,7 @@ impl ConsoleTestState { pub fn write_log_result(&mut self,test: &TestDesc, result: &TestResult, - exec_time: Option<&TestExecTime>, + exec_time: Option<&time::TestExecTime>, ) -> io::Result<()> { self.write_log(|| format!( "{} {}", @@ -1097,52 +307,6 @@ impl ConsoleTestState { } } -// Format a number with thousands separators -fn fmt_thousands_sep(mut n: usize, sep: char) -> String { - use std::fmt::Write; - let mut output = String::new(); - let mut trailing = false; - for &pow in &[9, 6, 3, 0] { - let base = 10_usize.pow(pow); - if pow == 0 || trailing || n / base != 0 { - if !trailing { - output.write_fmt(format_args!("{}", n / base)).unwrap(); - } else { - output.write_fmt(format_args!("{:03}", n / base)).unwrap(); - } - if pow != 0 { - output.push(sep); - } - trailing = true; - } - n %= base; - } - - output -} - -pub fn fmt_bench_samples(bs: &BenchSamples) -> String { - use std::fmt::Write; - let mut output = String::new(); - - let median = bs.ns_iter_summ.median as usize; - let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize; - - output - .write_fmt(format_args!( - "{:>11} ns/iter (+/- {})", - fmt_thousands_sep(median, ','), - fmt_thousands_sep(deviation, ',') - )) - .unwrap(); - if bs.mb_s != 0 { - output - .write_fmt(format_args!(" = {} MB/s", bs.mb_s)) - .unwrap(); - } - output -} - // List the tests to console, and optionally to logfile. Filters are honored. pub fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { let mut output = match term::stdout() { @@ -1271,14 +435,14 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Resu let mut out: Box = match opts.format { OutputFormat::Pretty => Box::new(PrettyFormatter::new( output, - use_color(opts), + opts.use_color(), max_name_len, is_multithreaded, opts.time_options, )), OutputFormat::Terse => Box::new(TerseFormatter::new( output, - use_color(opts), + opts.use_color(), max_name_len, is_multithreaded, )), @@ -1299,55 +463,16 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Resu return out.write_run_finish(&st); } -fn use_color(opts: &TestOpts) -> bool { - match opts.color { - AutoColor => !opts.nocapture && stdout_isatty(), - AlwaysColor => true, - NeverColor => false, - } -} - -#[cfg(any( - target_os = "cloudabi", - all(target_arch = "wasm32", not(target_os = "emscripten")), - all(target_vendor = "fortanix", target_env = "sgx") -))] -fn stdout_isatty() -> bool { - // FIXME: Implement isatty on SGX - false -} -#[cfg(unix)] -fn stdout_isatty() -> bool { - unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 } -} -#[cfg(windows)] -fn stdout_isatty() -> bool { - type DWORD = u32; - type BOOL = i32; - type HANDLE = *mut u8; - type LPDWORD = *mut u32; - const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD; - extern "system" { - fn GetStdHandle(which: DWORD) -> HANDLE; - fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL; - } - unsafe { - let handle = GetStdHandle(STD_OUTPUT_HANDLE); - let mut out = 0; - GetConsoleMode(handle, &mut out) != 0 - } -} - #[derive(Clone)] pub enum TestEvent { TeFiltered(Vec), TeWait(TestDesc), - TeResult(TestDesc, TestResult, Option, Vec), + TeResult(TestDesc, TestResult, Option, Vec), TeTimeout(TestDesc), TeFilteredOut(usize), } -pub type MonitorMsg = (TestDesc, TestResult, Option, Vec); +pub type MonitorMsg = (TestDesc, TestResult, Option, Vec); struct Sink(Arc>>); impl Write for Sink { @@ -1359,18 +484,6 @@ impl Write for Sink { } } -#[derive(Clone, Copy)] -pub enum RunStrategy { - /// Runs the test in the current process, and sends the result back over the - /// supplied channel. - InProcess, - - /// Spawns a subprocess to run the test, and sends the result back over the - /// supplied channel. Requires `argv[0]` to exist and point to the binary - /// that's currently running. - SpawnPrimary, -} - pub fn run_tests(opts: &TestOpts, tests: Vec, mut callback: F) -> io::Result<()> where F: FnMut(TestEvent) -> io::Result<()>, @@ -1467,7 +580,7 @@ where while pending > 0 || !remaining.is_empty() { while pending < concurrency && !remaining.is_empty() { let test = remaining.pop().unwrap(); - let timeout = Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S); + let timeout = time::get_default_test_timeout(); running_tests.insert(test.desc.clone(), timeout); callback(TeWait(test.desc.clone()))?; //here no pad run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::Yes); @@ -1510,153 +623,6 @@ where Ok(()) } -#[allow(deprecated)] -fn get_concurrency() -> usize { - return match env::var("RUST_TEST_THREADS") { - Ok(s) => { - let opt_n: Option = s.parse().ok(); - match opt_n { - Some(n) if n > 0 => n, - _ => panic!( - "RUST_TEST_THREADS is `{}`, should be a positive integer.", - s - ), - } - } - Err(..) => num_cpus(), - }; - - #[cfg(windows)] - #[allow(nonstandard_style)] - fn num_cpus() -> usize { - #[repr(C)] - struct SYSTEM_INFO { - wProcessorArchitecture: u16, - wReserved: u16, - dwPageSize: u32, - lpMinimumApplicationAddress: *mut u8, - lpMaximumApplicationAddress: *mut u8, - dwActiveProcessorMask: *mut u8, - dwNumberOfProcessors: u32, - dwProcessorType: u32, - dwAllocationGranularity: u32, - wProcessorLevel: u16, - wProcessorRevision: u16, - } - extern "system" { - fn GetSystemInfo(info: *mut SYSTEM_INFO) -> i32; - } - unsafe { - let mut sysinfo = std::mem::zeroed(); - GetSystemInfo(&mut sysinfo); - sysinfo.dwNumberOfProcessors as usize - } - } - - #[cfg(target_os = "vxworks")] - fn num_cpus() -> usize { - // FIXME: Implement num_cpus on vxWorks - 1 - } - - #[cfg(target_os = "redox")] - fn num_cpus() -> usize { - // FIXME: Implement num_cpus on Redox - 1 - } - - #[cfg(any( - all(target_arch = "wasm32", not(target_os = "emscripten")), - all(target_vendor = "fortanix", target_env = "sgx") - ))] - fn num_cpus() -> usize { - 1 - } - - #[cfg(any( - target_os = "android", - target_os = "cloudabi", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "solaris", - ))] - fn num_cpus() -> usize { - unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize } - } - - #[cfg(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "netbsd" - ))] - fn num_cpus() -> usize { - use std::ptr; - - let mut cpus: libc::c_uint = 0; - let mut cpus_size = std::mem::size_of_val(&cpus); - - unsafe { - cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint; - } - if cpus < 1 { - let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0]; - unsafe { - libc::sysctl( - mib.as_mut_ptr(), - 2, - &mut cpus as *mut _ as *mut _, - &mut cpus_size as *mut _ as *mut _, - ptr::null_mut(), - 0, - ); - } - if cpus < 1 { - cpus = 1; - } - } - cpus as usize - } - - #[cfg(target_os = "openbsd")] - fn num_cpus() -> usize { - use std::ptr; - - let mut cpus: libc::c_uint = 0; - let mut cpus_size = std::mem::size_of_val(&cpus); - let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0]; - - unsafe { - libc::sysctl( - mib.as_mut_ptr(), - 2, - &mut cpus as *mut _ as *mut _, - &mut cpus_size as *mut _ as *mut _, - ptr::null_mut(), - 0, - ); - } - if cpus < 1 { - cpus = 1; - } - cpus as usize - } - - #[cfg(target_os = "haiku")] - fn num_cpus() -> usize { - // FIXME: implement - 1 - } - - #[cfg(target_os = "l4re")] - fn num_cpus() -> usize { - // FIXME: implement - 1 - } -} - pub fn filter_tests(opts: &TestOpts, tests: Vec) -> Vec { let mut filtered = tests; let matches_filter = |test: &TestDescAndFn, filter: &str| { @@ -1748,7 +714,7 @@ pub fn run_test( pub strategy: RunStrategy, pub nocapture: bool, pub concurrency: Concurrent, - pub time: Option, + pub time: Option, } fn run_test_inner( @@ -1835,86 +801,13 @@ fn __rust_begin_short_backtrace(f: F) { f() } -fn calc_result<'a>( - desc: &TestDesc, - task_result: Result<(), &'a (dyn Any + 'static + Send)>, - time_opts: &Option, - exec_time: &Option -) -> TestResult { - let result = match (&desc.should_panic, task_result) { - (&ShouldPanic::No, Ok(())) | (&ShouldPanic::Yes, Err(_)) => TrOk, - (&ShouldPanic::YesWithMessage(msg), Err(ref err)) => { - if err - .downcast_ref::() - .map(|e| &**e) - .or_else(|| err.downcast_ref::<&'static str>().map(|e| *e)) - .map(|e| e.contains(msg)) - .unwrap_or(false) - { - TrOk - } else { - if desc.allow_fail { - TrAllowedFail - } else { - TrFailedMsg(format!("panic did not include expected string '{}'", msg)) - } - } - } - (&ShouldPanic::Yes, Ok(())) => TrFailedMsg("test did not panic as expected".to_string()), - _ if desc.allow_fail => TrAllowedFail, - _ => TrFailed, - }; - - // If test is already failed (or allowed to fail), do not change the result. - if result != TrOk { - return result; - } - - // Check if test is failed due to timeout. - if let (Some(opts), Some(time)) = (time_opts, exec_time) { - if opts.error_on_excess && opts.is_critical(desc, time) { - return TrTimedFail; - } - } - - result -} - -fn get_result_from_exit_code( - desc: &TestDesc, - code: i32, - time_opts: &Option, - exec_time: &Option, -) -> TestResult { - let result = match (desc.allow_fail, code) { - (_, TR_OK) => TrOk, - (true, TR_FAILED) => TrAllowedFail, - (false, TR_FAILED) => TrFailed, - (_, _) => TrFailedMsg(format!("got unexpected return code {}", code)), - }; - - // If test is already failed (or allowed to fail), do not change the result. - if result != TrOk { - return result; - } - - // Check if test is failed due to timeout. - if let (Some(opts), Some(time)) = (time_opts, exec_time) { - if opts.error_on_excess && opts.is_critical(desc, time) { - return TrTimedFail; - } - } - - result -} - fn run_test_in_process( desc: TestDesc, nocapture: bool, report_time: bool, testfn: Box, monitor_ch: Sender, - time_opts: Option, + time_opts: Option, ) { // Buffer for capturing standard I/O let data = Arc::new(Mutex::new(Vec::new())); @@ -1936,7 +829,7 @@ fn run_test_in_process( let result = catch_unwind(AssertUnwindSafe(testfn)); let exec_time = start.map(|start| { let duration = start.elapsed(); - TestExecTime(duration) + time::TestExecTime(duration) }); if let Some((printio, panicio)) = oldio { @@ -1956,7 +849,7 @@ fn spawn_test_subprocess( desc: TestDesc, report_time: bool, monitor_ch: Sender, - time_opts: Option, + time_opts: Option, ) { let (result, test_output, exec_time) = (|| { let args = env::args().collect::>(); @@ -1978,7 +871,7 @@ fn spawn_test_subprocess( }; let exec_time = start.map(|start| { let duration = start.elapsed(); - TestExecTime(duration) + time::TestExecTime(duration) }); let std::process::Output { stdout, stderr, status } = output; @@ -2025,9 +918,9 @@ fn run_test_in_spawned_subprocess( } if let TrOk = test_result { - process::exit(TR_OK); + process::exit(test_result::TR_OK); } else { - process::exit(TR_FAILED); + process::exit(test_result::TR_FAILED); } }); let record_result2 = record_result.clone(); @@ -2053,229 +946,3 @@ fn get_exit_code(status: ExitStatus) -> Result { } } } - -#[derive(Clone, PartialEq)] -pub struct MetricMap(BTreeMap); - -impl MetricMap { - pub fn new() -> MetricMap { - MetricMap(BTreeMap::new()) - } - - /// Insert a named `value` (+/- `noise`) metric into the map. The value - /// must be non-negative. The `noise` indicates the uncertainty of the - /// metric, which doubles as the "noise range" of acceptable - /// pairwise-regressions on this named value, when comparing from one - /// metric to the next using `compare_to_old`. - /// - /// If `noise` is positive, then it means this metric is of a value - /// you want to see grow smaller, so a change larger than `noise` in the - /// positive direction represents a regression. - /// - /// If `noise` is negative, then it means this metric is of a value - /// you want to see grow larger, so a change larger than `noise` in the - /// negative direction represents a regression. - pub fn insert_metric(&mut self, name: &str, value: f64, noise: f64) { - let m = Metric { value, noise }; - self.0.insert(name.to_owned(), m); - } - - pub fn fmt_metrics(&self) -> String { - let v = self - .0 - .iter() - .map(|(k, v)| format!("{}: {} (+/- {})", *k, v.value, v.noise)) - .collect::>(); - v.join(", ") - } -} - -// Benchmarking - -pub use std::hint::black_box; - -impl Bencher { - /// Callback for benchmark functions to run in their body. - pub fn iter(&mut self, mut inner: F) - where - F: FnMut() -> T, - { - if self.mode == BenchMode::Single { - ns_iter_inner(&mut inner, 1); - return; - } - - self.summary = Some(iter(&mut inner)); - } - - pub fn bench(&mut self, mut f: F) -> Option - where - F: FnMut(&mut Bencher), - { - f(self); - return self.summary; - } -} - -fn ns_from_dur(dur: Duration) -> u64 { - dur.as_secs() * 1_000_000_000 + (dur.subsec_nanos() as u64) -} - -fn ns_iter_inner(inner: &mut F, k: u64) -> u64 -where - F: FnMut() -> T, -{ - let start = Instant::now(); - for _ in 0..k { - black_box(inner()); - } - return ns_from_dur(start.elapsed()); -} - -pub fn iter(inner: &mut F) -> stats::Summary -where - F: FnMut() -> T, -{ - // Initial bench run to get ballpark figure. - let ns_single = ns_iter_inner(inner, 1); - - // Try to estimate iter count for 1ms falling back to 1m - // iterations if first run took < 1ns. - let ns_target_total = 1_000_000; // 1ms - let mut n = ns_target_total / cmp::max(1, ns_single); - - // if the first run took more than 1ms we don't want to just - // be left doing 0 iterations on every loop. The unfortunate - // side effect of not being able to do as many runs is - // automatically handled by the statistical analysis below - // (i.e., larger error bars). - n = cmp::max(1, n); - - let mut total_run = Duration::new(0, 0); - let samples: &mut [f64] = &mut [0.0_f64; 50]; - loop { - let loop_start = Instant::now(); - - for p in &mut *samples { - *p = ns_iter_inner(inner, n) as f64 / n as f64; - } - - stats::winsorize(samples, 5.0); - let summ = stats::Summary::new(samples); - - for p in &mut *samples { - let ns = ns_iter_inner(inner, 5 * n); - *p = ns as f64 / (5 * n) as f64; - } - - stats::winsorize(samples, 5.0); - let summ5 = stats::Summary::new(samples); - - let loop_run = loop_start.elapsed(); - - // If we've run for 100ms and seem to have converged to a - // stable median. - if loop_run > Duration::from_millis(100) - && summ.median_abs_dev_pct < 1.0 - && summ.median - summ5.median < summ5.median_abs_dev - { - return summ5; - } - - total_run = total_run + loop_run; - // Longest we ever run for is 3s. - if total_run > Duration::from_secs(3) { - return summ5; - } - - // If we overflow here just return the results so far. We check a - // multiplier of 10 because we're about to multiply by 2 and the - // next iteration of the loop will also multiply by 5 (to calculate - // the summ5 result) - n = match n.checked_mul(10) { - Some(_) => n * 2, - None => { - return summ5; - } - }; - } -} - -pub mod bench { - use super::{ - BenchMode, BenchSamples, Bencher, MonitorMsg, Sender, Sink, TestDesc, TestResult - }; - use crate::stats; - use std::cmp; - use std::io; - use std::panic::{catch_unwind, AssertUnwindSafe}; - use std::sync::{Arc, Mutex}; - - pub fn benchmark(desc: TestDesc, monitor_ch: Sender, nocapture: bool, f: F) - where - F: FnMut(&mut Bencher), - { - let mut bs = Bencher { - mode: BenchMode::Auto, - summary: None, - bytes: 0, - }; - - let data = Arc::new(Mutex::new(Vec::new())); - let oldio = if !nocapture { - Some(( - io::set_print(Some(Box::new(Sink(data.clone())))), - io::set_panic(Some(Box::new(Sink(data.clone())))), - )) - } else { - None - }; - - let result = catch_unwind(AssertUnwindSafe(|| bs.bench(f))); - - if let Some((printio, panicio)) = oldio { - io::set_print(printio); - io::set_panic(panicio); - } - - let test_result = match result { - //bs.bench(f) { - Ok(Some(ns_iter_summ)) => { - let ns_iter = cmp::max(ns_iter_summ.median as u64, 1); - let mb_s = bs.bytes * 1000 / ns_iter; - - let bs = BenchSamples { - ns_iter_summ, - mb_s: mb_s as usize, - }; - TestResult::TrBench(bs) - } - Ok(None) => { - // iter not called, so no data. - // FIXME: error in this case? - let samples: &mut [f64] = &mut [0.0_f64; 1]; - let bs = BenchSamples { - ns_iter_summ: stats::Summary::new(samples), - mb_s: 0, - }; - TestResult::TrBench(bs) - } - Err(_) => TestResult::TrFailed, - }; - - let stdout = data.lock().unwrap().to_vec(); - monitor_ch.send((desc, test_result, None, stdout)).unwrap(); - } - - pub fn run_once(f: F) - where - F: FnMut(&mut Bencher), - { - let mut bs = Bencher { - mode: BenchMode::Single, - summary: None, - bytes: 0, - }; - bs.bench(f); - } -} diff --git a/src/libtest/options.rs b/src/libtest/options.rs new file mode 100644 index 000000000000..0a604cae0ca3 --- /dev/null +++ b/src/libtest/options.rs @@ -0,0 +1,80 @@ +//! Enums denoting options for test execution. + +/// Whether to execute tests concurrently or not +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Concurrent { + Yes, + No, +} + +#[derive(Clone, PartialEq, Eq)] +pub enum BenchMode { + Auto, + Single, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum ShouldPanic { + No, + Yes, + YesWithMessage(&'static str), +} + +#[derive(Copy, Clone, Debug)] +pub enum ColorConfig { + AutoColor, + AlwaysColor, + NeverColor, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum OutputFormat { + Pretty, + Terse, + Json, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum RunIgnored { + Yes, + No, + Only, +} + +#[derive(Clone, Copy)] +pub enum RunStrategy { + /// Runs the test in the current process, and sends the result back over the + /// supplied channel. + InProcess, + + /// Spawns a subprocess to run the test, and sends the result back over the + /// supplied channel. Requires `argv[0]` to exist and point to the binary + /// that's currently running. + SpawnPrimary, +} + +/// In case we want to add other options as well, just add them in this struct. +#[derive(Copy, Clone, Debug)] +pub struct Options { + pub display_output: bool, + pub panic_abort: bool, +} + +impl Options { + pub fn new() -> Options { + Options { + display_output: false, + panic_abort: false, + } + } + + pub fn display_output(mut self, display_output: bool) -> Options { + self.display_output = display_output; + self + } + + pub fn panic_abort(mut self, panic_abort: bool) -> Options { + self.panic_abort = panic_abort; + self + } +} diff --git a/src/libtest/test_result.rs b/src/libtest/test_result.rs new file mode 100644 index 000000000000..4eb3f93e2a42 --- /dev/null +++ b/src/libtest/test_result.rs @@ -0,0 +1,102 @@ + +use std::any::Any; + +use super::bench::BenchSamples; +use super::time; +use super::types::TestDesc; +use super::options::ShouldPanic; + +pub use self::TestResult::*; + +// Return codes for secondary process. +// Start somewhere other than 0 so we know the return code means what we think +// it means. +pub const TR_OK: i32 = 50; +pub const TR_FAILED: i32 = 51; + +#[derive(Debug, Clone, PartialEq)] +pub enum TestResult { + TrOk, + TrFailed, + TrFailedMsg(String), + TrIgnored, + TrAllowedFail, + TrBench(BenchSamples), + TrTimedFail, +} + +unsafe impl Send for TestResult {} + + +pub fn calc_result<'a>( + desc: &TestDesc, + task_result: Result<(), &'a (dyn Any + 'static + Send)>, + time_opts: &Option, + exec_time: &Option +) -> TestResult { + let result = match (&desc.should_panic, task_result) { + (&ShouldPanic::No, Ok(())) | (&ShouldPanic::Yes, Err(_)) => TestResult::TrOk, + (&ShouldPanic::YesWithMessage(msg), Err(ref err)) => { + if err + .downcast_ref::() + .map(|e| &**e) + .or_else(|| err.downcast_ref::<&'static str>().map(|e| *e)) + .map(|e| e.contains(msg)) + .unwrap_or(false) + { + TestResult::TrOk + } else { + if desc.allow_fail { + TestResult::TrAllowedFail + } else { + TestResult::TrFailedMsg(format!("panic did not include expected string '{}'", msg)) + } + } + } + (&ShouldPanic::Yes, Ok(())) => TestResult::TrFailedMsg("test did not panic as expected".to_string()), + _ if desc.allow_fail => TestResult::TrAllowedFail, + _ => TestResult::TrFailed, + }; + + // If test is already failed (or allowed to fail), do not change the result. + if result != TestResult::TrOk { + return result; + } + + // Check if test is failed due to timeout. + if let (Some(opts), Some(time)) = (time_opts, exec_time) { + if opts.error_on_excess && opts.is_critical(desc, time) { + return TestResult::TrTimedFail; + } + } + + result +} + +pub fn get_result_from_exit_code( + desc: &TestDesc, + code: i32, + time_opts: &Option, + exec_time: &Option, +) -> TestResult { + let result = match (desc.allow_fail, code) { + (_, TR_OK) => TestResult::TrOk, + (true, TR_FAILED) => TestResult::TrAllowedFail, + (false, TR_FAILED) => TestResult::TrFailed, + (_, _) => TestResult::TrFailedMsg(format!("got unexpected return code {}", code)), + }; + + // If test is already failed (or allowed to fail), do not change the result. + if result != TestResult::TrOk { + return result; + } + + // Check if test is failed due to timeout. + if let (Some(opts), Some(time)) = (time_opts, exec_time) { + if opts.error_on_excess && opts.is_critical(desc, time) { + return TestResult::TrTimedFail; + } + } + + result +} diff --git a/src/libtest/time.rs b/src/libtest/time.rs new file mode 100644 index 000000000000..b7ce764505bf --- /dev/null +++ b/src/libtest/time.rs @@ -0,0 +1,206 @@ +//! Module `time` contains everything related to the time measurement of unit tests +//! execution. +//! Two main purposes of this module: +//! - Check whether test is timed out. +//! - Provide helpers for `report-time` and `measure-time` options. + +use std::time::{Duration, Instant}; +use std::str::FromStr; +use std::fmt; +use std::env; + +use super::types::{TestDesc, TestType}; + +pub const TEST_WARN_TIMEOUT_S: u64 = 60; + +/// This small module contains constants used by `report-time` option. +/// Those constants values will be used if corresponding environment variables are not set. +/// +/// To override values for unit-tests, use a constant `RUST_TEST_TIME_UNIT`, +/// To override values for integration tests, use a constant `RUST_TEST_TIME_INTEGRATION`, +/// To override values for doctests, use a constant `RUST_TEST_TIME_DOCTEST`. +/// +/// Example of the expected format is `RUST_TEST_TIME_xxx=100,200`, where 100 means +/// warn time, and 200 means critical time. +pub mod time_constants { + use std::time::Duration; + use super::TEST_WARN_TIMEOUT_S; + + /// Environment variable for overriding default threshold for unit-tests. + pub const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; + + // Unit tests are supposed to be really quick. + pub const UNIT_WARN: Duration = Duration::from_millis(50); + pub const UNIT_CRITICAL: Duration = Duration::from_millis(100); + + /// Environment variable for overriding default threshold for unit-tests. + pub const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION"; + + // Integration tests may have a lot of work, so they can take longer to execute. + pub const INTEGRATION_WARN: Duration = Duration::from_millis(500); + pub const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); + + /// Environment variable for overriding default threshold for unit-tests. + pub const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST"; + + // Doctests are similar to integration tests, because they can include a lot of + // initialization code. + pub const DOCTEST_WARN: Duration = INTEGRATION_WARN; + pub const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; + + // Do not suppose anything about unknown tests, base limits on the + // `TEST_WARN_TIMEOUT_S` constant. + pub const UNKNOWN_WARN: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S); + pub const UNKNOWN_CRITICAL: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S * 2); +} + +/// Returns an `Instance` object denoting when the test should be considered +/// timed out. +pub fn get_default_test_timeout() -> Instant { + Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S) +} + +/// The meassured execution time of a unit test. +#[derive(Clone, PartialEq)] +pub struct TestExecTime(pub Duration); + +impl fmt::Display for TestExecTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:.3}s", self.0.as_secs_f64()) + } +} + +/// Structure denoting time limits for test execution. +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct TimeThreshold { + pub warn: Duration, + pub critical: Duration, +} + +impl TimeThreshold { + /// Creates a new `TimeThreshold` instance with provided durations. + pub fn new(warn: Duration, critical: Duration) -> Self { + Self { + warn, + critical, + } + } + + /// Attempts to create a `TimeThreshold` instance with values obtained + /// from the environment variable, and returns `None` if the variable + /// is not set. + /// Environment variable format is expected to match `\d+,\d+`. + /// + /// # Panics + /// + /// Panics if variable with provided name is set but contains inappropriate + /// value. + pub fn from_env_var(env_var_name: &str) -> Option { + let durations_str = env::var(env_var_name).ok()?; + + // Split string into 2 substrings by comma and try to parse numbers. + let mut durations = durations_str + .splitn(2, ',') + .map(|v| { + u64::from_str(v).unwrap_or_else(|_| { + panic!( + "Duration value in variable {} is expected to be a number, but got {}", + env_var_name, v + ) + }) + }); + + // Callback to be called if the environment variable has unexpected structure. + let panic_on_incorrect_value = || { + panic!( + "Duration variable {} expected to have 2 numbers separated by comma, but got {}", + env_var_name, durations_str + ); + }; + + let (warn, critical) = ( + durations.next().unwrap_or_else(panic_on_incorrect_value), + durations.next().unwrap_or_else(panic_on_incorrect_value) + ); + + if warn > critical { + panic!("Test execution warn time should be less or equal to the critical time"); + } + + Some(Self::new(Duration::from_millis(warn), Duration::from_millis(critical))) + } +} + +/// Structure with parameters for calculating test execution time. +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct TestTimeOptions { + /// Denotes if the test critical execution time limit excess should be considered + /// a test failure. + pub error_on_excess: bool, + pub colored: bool, + pub unit_threshold: TimeThreshold, + pub integration_threshold: TimeThreshold, + pub doctest_threshold: TimeThreshold, +} + +impl TestTimeOptions { + pub fn new_from_env(error_on_excess: bool, colored: bool) -> Self { + let unit_threshold = + TimeThreshold::from_env_var(time_constants::UNIT_ENV_NAME) + .unwrap_or_else(Self::default_unit); + + let integration_threshold = + TimeThreshold::from_env_var(time_constants::INTEGRATION_ENV_NAME) + .unwrap_or_else(Self::default_integration); + + let doctest_threshold = + TimeThreshold::from_env_var(time_constants::DOCTEST_ENV_NAME) + .unwrap_or_else(Self::default_doctest); + + Self { + error_on_excess, + colored, + unit_threshold, + integration_threshold, + doctest_threshold, + } + } + + pub fn is_warn(&self, test: &TestDesc, exec_time: &TestExecTime) -> bool { + exec_time.0 >= self.warn_time(test) + } + + pub fn is_critical(&self, test: &TestDesc, exec_time: &TestExecTime) -> bool { + exec_time.0 >= self.critical_time(test) + } + + fn warn_time(&self, test: &TestDesc) -> Duration { + match test.test_type { + TestType::UnitTest => self.unit_threshold.warn, + TestType::IntegrationTest => self.integration_threshold.warn, + TestType::DocTest => self.doctest_threshold.warn, + TestType::Unknown => time_constants::UNKNOWN_WARN, + } + } + + fn critical_time(&self, test: &TestDesc) -> Duration { + match test.test_type { + TestType::UnitTest => self.unit_threshold.critical, + TestType::IntegrationTest => self.integration_threshold.critical, + TestType::DocTest => self.doctest_threshold.critical, + TestType::Unknown => time_constants::UNKNOWN_CRITICAL, + } + } + + fn default_unit() -> TimeThreshold { + TimeThreshold::new(time_constants::UNIT_WARN, time_constants::UNIT_CRITICAL) + } + + fn default_integration() -> TimeThreshold { + TimeThreshold::new(time_constants::INTEGRATION_WARN, time_constants::INTEGRATION_CRITICAL) + } + + fn default_doctest() -> TimeThreshold { + TimeThreshold::new(time_constants::DOCTEST_WARN, time_constants::DOCTEST_CRITICAL) + } +} diff --git a/src/libtest/types.rs b/src/libtest/types.rs new file mode 100644 index 000000000000..89bcf2cf2853 --- /dev/null +++ b/src/libtest/types.rs @@ -0,0 +1,145 @@ +//! Common types used by `libtest`. + +use std::fmt; +use std::borrow::Cow; + +use super::options; +use super::bench::Bencher; + +pub use NamePadding::*; +pub use TestName::*; +pub use TestFn::*; + +/// Type of the test according to the [rust book](https://doc.rust-lang.org/cargo/guide/tests.html) +/// conventions. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum TestType { + /// Unit-tests are expected to be in the `src` folder of the crate. + UnitTest, + /// Integration-style tests are expected to be in the `tests` folder of the crate. + IntegrationTest, + /// Doctests are created by the `librustdoc` manually, so it's a different type of test. + DocTest, + /// Tests for the sources that don't follow the project layout convention + /// (e.g. tests in raw `main.rs` compiled by calling `rustc --test` directly). + Unknown, +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum NamePadding { + PadNone, + PadOnRight, +} + +// The name of a test. By convention this follows the rules for rust +// paths; i.e., it should be a series of identifiers separated by double +// colons. This way if some test runner wants to arrange the tests +// hierarchically it may. +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub enum TestName { + StaticTestName(&'static str), + DynTestName(String), + AlignedTestName(Cow<'static, str>, NamePadding), +} + +impl TestName { + pub fn as_slice(&self) -> &str { + match *self { + StaticTestName(s) => s, + DynTestName(ref s) => s, + AlignedTestName(ref s, _) => &*s, + } + } + + pub fn padding(&self) -> NamePadding { + match self { + &AlignedTestName(_, p) => p, + _ => PadNone, + } + } + + pub fn with_padding(&self, padding: NamePadding) -> TestName { + let name = match self { + &TestName::StaticTestName(name) => Cow::Borrowed(name), + &TestName::DynTestName(ref name) => Cow::Owned(name.clone()), + &TestName::AlignedTestName(ref name, _) => name.clone(), + }; + + TestName::AlignedTestName(name, padding) + } +} +impl fmt::Display for TestName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_slice(), f) + } +} + +/// Represents a benchmark function. +pub trait TDynBenchFn: Send { + fn run(&self, harness: &mut Bencher); +} + +// A function that runs a test. If the function returns successfully, +// the test succeeds; if the function panics then the test fails. We +// may need to come up with a more clever definition of test in order +// to support isolation of tests into threads. +pub enum TestFn { + StaticTestFn(fn()), + StaticBenchFn(fn(&mut Bencher)), + DynTestFn(Box), + DynBenchFn(Box), +} + +impl TestFn { + pub fn padding(&self) -> NamePadding { + match *self { + StaticTestFn(..) => PadNone, + StaticBenchFn(..) => PadOnRight, + DynTestFn(..) => PadNone, + DynBenchFn(..) => PadOnRight, + } + } +} + +impl fmt::Debug for TestFn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match *self { + StaticTestFn(..) => "StaticTestFn(..)", + StaticBenchFn(..) => "StaticBenchFn(..)", + DynTestFn(..) => "DynTestFn(..)", + DynBenchFn(..) => "DynBenchFn(..)", + }) + } +} + +// The definition of a single test. A test runner will run a list of +// these. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct TestDesc { + pub name: TestName, + pub ignore: bool, + pub should_panic: options::ShouldPanic, + pub allow_fail: bool, + pub test_type: TestType, +} + +impl TestDesc { + pub fn padded_name(&self, column_count: usize, align: NamePadding) -> String { + let mut name = String::from(self.name.as_slice()); + let fill = column_count.saturating_sub(name.len()); + let pad = " ".repeat(fill); + match align { + PadNone => name, + PadOnRight => { + name.push_str(&pad); + name + } + } + } +} + +#[derive(Debug)] +pub struct TestDescAndFn { + pub desc: TestDesc, + pub testfn: TestFn, +} From c951882c7364c672fe127f61d25b458e810572ff Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Thu, 17 Oct 2019 10:12:02 +0300 Subject: [PATCH 29/79] Extract ConsoleTestState --- src/libtest/console.rs | 288 +++++++++++++++++++++++++++++++ src/libtest/event.rs | 15 ++ src/libtest/formatters/json.rs | 1 + src/libtest/formatters/mod.rs | 1 + src/libtest/formatters/pretty.rs | 5 +- src/libtest/formatters/terse.rs | 5 +- src/libtest/lib.rs | 288 +------------------------------ 7 files changed, 317 insertions(+), 286 deletions(-) create mode 100644 src/libtest/console.rs create mode 100644 src/libtest/event.rs diff --git a/src/libtest/console.rs b/src/libtest/console.rs new file mode 100644 index 000000000000..851c0389ff37 --- /dev/null +++ b/src/libtest/console.rs @@ -0,0 +1,288 @@ +//! Module providing interface for running tests in the console. + +use std::fs::File; +use std::io::prelude::*; +use std::io; + +use term; + +use super::{ + helpers::{ + concurrency::get_concurrency, + metrics::MetricMap, + }, + types::{TestDesc, TestDescAndFn, NamePadding}, + options::{Options, OutputFormat}, + bench::fmt_bench_samples, + test_result::TestResult, + time::TestExecTime, + cli::TestOpts, + event::TestEvent, + run_tests, + filter_tests, +}; + +pub enum OutputLocation { + Pretty(Box), + Raw(T), +} + +impl Write for OutputLocation { + fn write(&mut self, buf: &[u8]) -> io::Result { + match *self { + OutputLocation::Pretty(ref mut term) => term.write(buf), + OutputLocation::Raw(ref mut stdout) => stdout.write(buf), + } + } + + fn flush(&mut self) -> io::Result<()> { + match *self { + OutputLocation::Pretty(ref mut term) => term.flush(), + OutputLocation::Raw(ref mut stdout) => stdout.flush(), + } + } +} + +use crate::formatters::{JsonFormatter, OutputFormatter, PrettyFormatter, TerseFormatter}; + +pub struct ConsoleTestState { + pub log_out: Option, + pub total: usize, + pub passed: usize, + pub failed: usize, + pub ignored: usize, + pub allowed_fail: usize, + pub filtered_out: usize, + pub measured: usize, + pub metrics: MetricMap, + pub failures: Vec<(TestDesc, Vec)>, + pub not_failures: Vec<(TestDesc, Vec)>, + pub time_failures: Vec<(TestDesc, Vec)>, + pub options: Options, +} + +impl ConsoleTestState { + pub fn new(opts: &TestOpts) -> io::Result { + let log_out = match opts.logfile { + Some(ref path) => Some(File::create(path)?), + None => None, + }; + + Ok(ConsoleTestState { + log_out, + total: 0, + passed: 0, + failed: 0, + ignored: 0, + allowed_fail: 0, + filtered_out: 0, + measured: 0, + metrics: MetricMap::new(), + failures: Vec::new(), + not_failures: Vec::new(), + time_failures: Vec::new(), + options: opts.options, + }) + } + + pub fn write_log( + &mut self, + msg: F, + ) -> io::Result<()> + where + S: AsRef, + F: FnOnce() -> S, + { + match self.log_out { + None => Ok(()), + Some(ref mut o) => { + let msg = msg(); + let msg = msg.as_ref(); + o.write_all(msg.as_bytes()) + }, + } + } + + pub fn write_log_result(&mut self,test: &TestDesc, + result: &TestResult, + exec_time: Option<&TestExecTime>, + ) -> io::Result<()> { + self.write_log(|| format!( + "{} {}", + match *result { + TestResult::TrOk => "ok".to_owned(), + TestResult::TrFailed => "failed".to_owned(), + TestResult::TrFailedMsg(ref msg) => format!("failed: {}", msg), + TestResult::TrIgnored => "ignored".to_owned(), + TestResult::TrAllowedFail => "failed (allowed)".to_owned(), + TestResult::TrBench(ref bs) => fmt_bench_samples(bs), + TestResult::TrTimedFail => "failed (time limit exceeded)".to_owned(), + }, + test.name, + ))?; + if let Some(exec_time) = exec_time { + self.write_log(|| format!(" <{}>", exec_time))?; + } + self.write_log(|| "\n") + } + + fn current_test_count(&self) -> usize { + self.passed + self.failed + self.ignored + self.measured + self.allowed_fail + } +} + +// List the tests to console, and optionally to logfile. Filters are honored. +pub fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { + let mut output = match term::stdout() { + None => OutputLocation::Raw(io::stdout()), + Some(t) => OutputLocation::Pretty(t), + }; + + let quiet = opts.format == OutputFormat::Terse; + let mut st = ConsoleTestState::new(opts)?; + + let mut ntest = 0; + let mut nbench = 0; + + for test in filter_tests(&opts, tests) { + use crate::TestFn::*; + + let TestDescAndFn { + desc: TestDesc { name, .. }, + testfn, + } = test; + + let fntype = match testfn { + StaticTestFn(..) | DynTestFn(..) => { + ntest += 1; + "test" + } + StaticBenchFn(..) | DynBenchFn(..) => { + nbench += 1; + "benchmark" + } + }; + + writeln!(output, "{}: {}", name, fntype)?; + st.write_log(|| format!("{} {}\n", fntype, name))?; + } + + fn plural(count: u32, s: &str) -> String { + match count { + 1 => format!("{} {}", 1, s), + n => format!("{} {}s", n, s), + } + } + + if !quiet { + if ntest != 0 || nbench != 0 { + writeln!(output, "")?; + } + + writeln!( + output, + "{}, {}", + plural(ntest, "test"), + plural(nbench, "benchmark") + )?; + } + + Ok(()) +} + +// A simple console test runner +pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Result { + fn callback( + event: &TestEvent, + st: &mut ConsoleTestState, + out: &mut dyn OutputFormatter, + ) -> io::Result<()> { + match (*event).clone() { + TestEvent::TeFiltered(ref filtered_tests) => { + st.total = filtered_tests.len(); + out.write_run_start(filtered_tests.len()) + } + TestEvent::TeFilteredOut(filtered_out) => Ok(st.filtered_out = filtered_out), + TestEvent::TeWait(ref test) => out.write_test_start(test), + TestEvent::TeTimeout(ref test) => out.write_timeout(test), + TestEvent::TeResult(test, result, exec_time, stdout) => { + st.write_log_result(&test, &result, exec_time.as_ref())?; + out.write_result(&test, &result, exec_time.as_ref(), &*stdout, &st)?; + match result { + TestResult::TrOk => { + st.passed += 1; + st.not_failures.push((test, stdout)); + } + TestResult::TrIgnored => st.ignored += 1, + TestResult::TrAllowedFail => st.allowed_fail += 1, + TestResult::TrBench(bs) => { + st.metrics.insert_metric( + test.name.as_slice(), + bs.ns_iter_summ.median, + bs.ns_iter_summ.max - bs.ns_iter_summ.min, + ); + st.measured += 1 + } + TestResult::TrFailed => { + st.failed += 1; + st.failures.push((test, stdout)); + } + TestResult::TrFailedMsg(msg) => { + st.failed += 1; + let mut stdout = stdout; + stdout.extend_from_slice(format!("note: {}", msg).as_bytes()); + st.failures.push((test, stdout)); + } + TestResult::TrTimedFail => { + st.failed += 1; + st.time_failures.push((test, stdout)); + } + } + Ok(()) + } + } + } + + let output = match term::stdout() { + None => OutputLocation::Raw(io::stdout()), + Some(t) => OutputLocation::Pretty(t), + }; + + let max_name_len = tests + .iter() + .max_by_key(|t| len_if_padded(*t)) + .map(|t| t.desc.name.as_slice().len()) + .unwrap_or(0); + + let is_multithreaded = opts.test_threads.unwrap_or_else(get_concurrency) > 1; + + let mut out: Box = match opts.format { + OutputFormat::Pretty => Box::new(PrettyFormatter::new( + output, + opts.use_color(), + max_name_len, + is_multithreaded, + opts.time_options, + )), + OutputFormat::Terse => Box::new(TerseFormatter::new( + output, + opts.use_color(), + max_name_len, + is_multithreaded, + )), + OutputFormat::Json => Box::new(JsonFormatter::new(output)), + }; + let mut st = ConsoleTestState::new(opts)?; + fn len_if_padded(t: &TestDescAndFn) -> usize { + match t.testfn.padding() { + NamePadding::PadNone => 0, + NamePadding::PadOnRight => t.desc.name.as_slice().len(), + } + } + + run_tests(opts, tests, |x| callback(&x, &mut st, &mut *out))?; + + assert!(st.current_test_count() == st.total); + + return out.write_run_finish(&st); +} diff --git a/src/libtest/event.rs b/src/libtest/event.rs new file mode 100644 index 000000000000..b84551826c69 --- /dev/null +++ b/src/libtest/event.rs @@ -0,0 +1,15 @@ +//! Module containing different events that can occur +//! during tests execution process. + +use super::types::TestDesc; +use super::test_result::TestResult; +use super::time::TestExecTime; + +#[derive(Clone)] +pub enum TestEvent { + TeFiltered(Vec), + TeWait(TestDesc), + TeResult(TestDesc, TestResult, Option, Vec), + TeTimeout(TestDesc), + TeFilteredOut(usize), +} diff --git a/src/libtest/formatters/json.rs b/src/libtest/formatters/json.rs index ff756c456dae..41a293195cc8 100644 --- a/src/libtest/formatters/json.rs +++ b/src/libtest/formatters/json.rs @@ -1,4 +1,5 @@ use super::*; +use super::console::{ConsoleTestState, OutputLocation}; pub(crate) struct JsonFormatter { out: OutputLocation, diff --git a/src/libtest/formatters/mod.rs b/src/libtest/formatters/mod.rs index 72432cd8e3c2..4f3ffdafe3fb 100644 --- a/src/libtest/formatters/mod.rs +++ b/src/libtest/formatters/mod.rs @@ -1,4 +1,5 @@ use super::*; +use super::console::ConsoleTestState; mod pretty; mod json; diff --git a/src/libtest/formatters/pretty.rs b/src/libtest/formatters/pretty.rs index 84e1a44dab80..6f2c56bdf45e 100644 --- a/src/libtest/formatters/pretty.rs +++ b/src/libtest/formatters/pretty.rs @@ -1,4 +1,5 @@ use super::*; +use super::console::{ConsoleTestState, OutputLocation}; pub(crate) struct PrettyFormatter { out: OutputLocation, @@ -67,7 +68,7 @@ impl PrettyFormatter { pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { match self.out { - Pretty(ref mut term) => { + OutputLocation::Pretty(ref mut term) => { if self.use_color { term.fg(color)?; } @@ -77,7 +78,7 @@ impl PrettyFormatter { } term.flush() } - Raw(ref mut stdout) => { + OutputLocation::Raw(ref mut stdout) => { stdout.write_all(word.as_bytes())?; stdout.flush() } diff --git a/src/libtest/formatters/terse.rs b/src/libtest/formatters/terse.rs index 50407d1130f8..96203d5ea427 100644 --- a/src/libtest/formatters/terse.rs +++ b/src/libtest/formatters/terse.rs @@ -1,4 +1,5 @@ use super::*; +use super::console::{ConsoleTestState, OutputLocation}; pub(crate) struct TerseFormatter { out: OutputLocation, @@ -68,7 +69,7 @@ impl TerseFormatter { pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { match self.out { - Pretty(ref mut term) => { + OutputLocation::Pretty(ref mut term) => { if self.use_color { term.fg(color)?; } @@ -78,7 +79,7 @@ impl TerseFormatter { } term.flush() } - Raw(ref mut stdout) => { + OutputLocation::Raw(ref mut stdout) => { stdout.write_all(word.as_bytes())?; stdout.flush() } diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index f79994671114..6221140f6069 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -30,16 +30,12 @@ #![feature(termination_trait_lib)] #![feature(test)] -use term; - pub use self::ColorConfig::*; -use self::OutputLocation::*; -use self::TestEvent::*; +use self::event::TestEvent::*; pub use self::types::TestName::*; use std::borrow::Cow; use std::env; -use std::fs::File; use std::io; use std::io::prelude::*; use std::panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo}; @@ -79,14 +75,16 @@ use test_result::*; use types::*; use options::*; use cli::*; +use event::*; use helpers::concurrency::get_concurrency; -use helpers::metrics::MetricMap; mod formatters; pub mod stats; mod cli; +mod console; +mod event; mod helpers; mod time; mod types; @@ -94,8 +92,6 @@ mod options; mod bench; mod test_result; -use crate::formatters::{JsonFormatter, OutputFormatter, PrettyFormatter, TerseFormatter}; - // The default console test runner. It accepts the command line // arguments and a vector of test_descs. pub fn test_main(args: &[String], tests: Vec, options: Option) { @@ -111,12 +107,12 @@ pub fn test_main(args: &[String], tests: Vec, options: Option {} Ok(false) => process::exit(101), Err(e) => { @@ -200,278 +196,6 @@ pub fn assert_test_result(result: T) { ); } -enum OutputLocation { - Pretty(Box), - Raw(T), -} - -impl Write for OutputLocation { - fn write(&mut self, buf: &[u8]) -> io::Result { - match *self { - Pretty(ref mut term) => term.write(buf), - Raw(ref mut stdout) => stdout.write(buf), - } - } - - fn flush(&mut self) -> io::Result<()> { - match *self { - Pretty(ref mut term) => term.flush(), - Raw(ref mut stdout) => stdout.flush(), - } - } -} - -struct ConsoleTestState { - log_out: Option, - total: usize, - passed: usize, - failed: usize, - ignored: usize, - allowed_fail: usize, - filtered_out: usize, - measured: usize, - metrics: MetricMap, - failures: Vec<(TestDesc, Vec)>, - not_failures: Vec<(TestDesc, Vec)>, - time_failures: Vec<(TestDesc, Vec)>, - options: Options, -} - -impl ConsoleTestState { - pub fn new(opts: &TestOpts) -> io::Result { - let log_out = match opts.logfile { - Some(ref path) => Some(File::create(path)?), - None => None, - }; - - Ok(ConsoleTestState { - log_out, - total: 0, - passed: 0, - failed: 0, - ignored: 0, - allowed_fail: 0, - filtered_out: 0, - measured: 0, - metrics: MetricMap::new(), - failures: Vec::new(), - not_failures: Vec::new(), - time_failures: Vec::new(), - options: opts.options, - }) - } - - pub fn write_log( - &mut self, - msg: F, - ) -> io::Result<()> - where - S: AsRef, - F: FnOnce() -> S, - { - match self.log_out { - None => Ok(()), - Some(ref mut o) => { - let msg = msg(); - let msg = msg.as_ref(); - o.write_all(msg.as_bytes()) - }, - } - } - - pub fn write_log_result(&mut self,test: &TestDesc, - result: &TestResult, - exec_time: Option<&time::TestExecTime>, - ) -> io::Result<()> { - self.write_log(|| format!( - "{} {}", - match *result { - TrOk => "ok".to_owned(), - TrFailed => "failed".to_owned(), - TrFailedMsg(ref msg) => format!("failed: {}", msg), - TrIgnored => "ignored".to_owned(), - TrAllowedFail => "failed (allowed)".to_owned(), - TrBench(ref bs) => fmt_bench_samples(bs), - TrTimedFail => "failed (time limit exceeded)".to_owned(), - }, - test.name, - ))?; - if let Some(exec_time) = exec_time { - self.write_log(|| format!(" <{}>", exec_time))?; - } - self.write_log(|| "\n") - } - - fn current_test_count(&self) -> usize { - self.passed + self.failed + self.ignored + self.measured + self.allowed_fail - } -} - -// List the tests to console, and optionally to logfile. Filters are honored. -pub fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { - let mut output = match term::stdout() { - None => Raw(io::stdout()), - Some(t) => Pretty(t), - }; - - let quiet = opts.format == OutputFormat::Terse; - let mut st = ConsoleTestState::new(opts)?; - - let mut ntest = 0; - let mut nbench = 0; - - for test in filter_tests(&opts, tests) { - use crate::TestFn::*; - - let TestDescAndFn { - desc: TestDesc { name, .. }, - testfn, - } = test; - - let fntype = match testfn { - StaticTestFn(..) | DynTestFn(..) => { - ntest += 1; - "test" - } - StaticBenchFn(..) | DynBenchFn(..) => { - nbench += 1; - "benchmark" - } - }; - - writeln!(output, "{}: {}", name, fntype)?; - st.write_log(|| format!("{} {}\n", fntype, name))?; - } - - fn plural(count: u32, s: &str) -> String { - match count { - 1 => format!("{} {}", 1, s), - n => format!("{} {}s", n, s), - } - } - - if !quiet { - if ntest != 0 || nbench != 0 { - writeln!(output, "")?; - } - - writeln!( - output, - "{}, {}", - plural(ntest, "test"), - plural(nbench, "benchmark") - )?; - } - - Ok(()) -} - -// A simple console test runner -pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Result { - fn callback( - event: &TestEvent, - st: &mut ConsoleTestState, - out: &mut dyn OutputFormatter, - ) -> io::Result<()> { - match (*event).clone() { - TeFiltered(ref filtered_tests) => { - st.total = filtered_tests.len(); - out.write_run_start(filtered_tests.len()) - } - TeFilteredOut(filtered_out) => Ok(st.filtered_out = filtered_out), - TeWait(ref test) => out.write_test_start(test), - TeTimeout(ref test) => out.write_timeout(test), - TeResult(test, result, exec_time, stdout) => { - st.write_log_result(&test, &result, exec_time.as_ref())?; - out.write_result(&test, &result, exec_time.as_ref(), &*stdout, &st)?; - match result { - TrOk => { - st.passed += 1; - st.not_failures.push((test, stdout)); - } - TrIgnored => st.ignored += 1, - TrAllowedFail => st.allowed_fail += 1, - TrBench(bs) => { - st.metrics.insert_metric( - test.name.as_slice(), - bs.ns_iter_summ.median, - bs.ns_iter_summ.max - bs.ns_iter_summ.min, - ); - st.measured += 1 - } - TrFailed => { - st.failed += 1; - st.failures.push((test, stdout)); - } - TrFailedMsg(msg) => { - st.failed += 1; - let mut stdout = stdout; - stdout.extend_from_slice(format!("note: {}", msg).as_bytes()); - st.failures.push((test, stdout)); - } - TrTimedFail => { - st.failed += 1; - st.time_failures.push((test, stdout)); - } - } - Ok(()) - } - } - } - - let output = match term::stdout() { - None => Raw(io::stdout()), - Some(t) => Pretty(t), - }; - - let max_name_len = tests - .iter() - .max_by_key(|t| len_if_padded(*t)) - .map(|t| t.desc.name.as_slice().len()) - .unwrap_or(0); - - let is_multithreaded = opts.test_threads.unwrap_or_else(get_concurrency) > 1; - - let mut out: Box = match opts.format { - OutputFormat::Pretty => Box::new(PrettyFormatter::new( - output, - opts.use_color(), - max_name_len, - is_multithreaded, - opts.time_options, - )), - OutputFormat::Terse => Box::new(TerseFormatter::new( - output, - opts.use_color(), - max_name_len, - is_multithreaded, - )), - OutputFormat::Json => Box::new(JsonFormatter::new(output)), - }; - let mut st = ConsoleTestState::new(opts)?; - fn len_if_padded(t: &TestDescAndFn) -> usize { - match t.testfn.padding() { - PadNone => 0, - PadOnRight => t.desc.name.as_slice().len(), - } - } - - run_tests(opts, tests, |x| callback(&x, &mut st, &mut *out))?; - - assert!(st.current_test_count() == st.total); - - return out.write_run_finish(&st); -} - -#[derive(Clone)] -pub enum TestEvent { - TeFiltered(Vec), - TeWait(TestDesc), - TeResult(TestDesc, TestResult, Option, Vec), - TeTimeout(TestDesc), - TeFilteredOut(usize), -} - pub type MonitorMsg = (TestDesc, TestResult, Option, Vec); struct Sink(Arc>>); From 12397e9dd5a97460d76c884d449ca1c2d26da8ed Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Thu, 17 Oct 2019 17:38:16 +0300 Subject: [PATCH 30/79] Make enum usage explicit and fix tests --- src/libtest/bench.rs | 16 ++- src/libtest/console.rs | 18 ++- src/libtest/event.rs | 25 ++++- src/libtest/formatters/json.rs | 31 ++++-- src/libtest/formatters/mod.rs | 13 ++- src/libtest/formatters/pretty.rs | 27 +++-- src/libtest/formatters/terse.rs | 31 ++++-- src/libtest/helpers/exit_code.rs | 20 ++++ src/libtest/helpers/mod.rs | 2 + src/libtest/helpers/sink.rs | 24 ++++ src/libtest/lib.rs | 184 +++++++++++++++---------------- src/libtest/stats/tests.rs | 2 +- src/libtest/tests.rs | 55 +++++---- src/libtest/time.rs | 2 +- 14 files changed, 289 insertions(+), 161 deletions(-) create mode 100644 src/libtest/helpers/exit_code.rs create mode 100644 src/libtest/helpers/sink.rs diff --git a/src/libtest/bench.rs b/src/libtest/bench.rs index 055a74f691cd..bb5b0d1da533 100644 --- a/src/libtest/bench.rs +++ b/src/libtest/bench.rs @@ -1,6 +1,11 @@ //! Benchmarking module. use super::{ - BenchMode, MonitorMsg, Sender, Sink, TestDesc, TestResult + event::CompletedTest, + helpers::sink::Sink, + options::BenchMode, + types::TestDesc, + test_result::TestResult, + Sender, }; use crate::stats; @@ -182,7 +187,7 @@ where } } -pub fn benchmark(desc: TestDesc, monitor_ch: Sender, nocapture: bool, f: F) +pub fn benchmark(desc: TestDesc, monitor_ch: Sender, nocapture: bool, f: F) where F: FnMut(&mut Bencher), { @@ -195,8 +200,8 @@ where let data = Arc::new(Mutex::new(Vec::new())); let oldio = if !nocapture { Some(( - io::set_print(Some(Box::new(Sink(data.clone())))), - io::set_panic(Some(Box::new(Sink(data.clone())))), + io::set_print(Some(Sink::new_boxed(&data))), + io::set_panic(Some(Sink::new_boxed(&data))), )) } else { None @@ -235,7 +240,8 @@ where }; let stdout = data.lock().unwrap().to_vec(); - monitor_ch.send((desc, test_result, None, stdout)).unwrap(); + let message = CompletedTest::new(desc, test_result, None, stdout); + monitor_ch.send(message).unwrap(); } pub fn run_once(f: F) diff --git a/src/libtest/console.rs b/src/libtest/console.rs index 851c0389ff37..2c14e9a1591c 100644 --- a/src/libtest/console.rs +++ b/src/libtest/console.rs @@ -1,7 +1,7 @@ //! Module providing interface for running tests in the console. use std::fs::File; -use std::io::prelude::*; +use std::io::prelude::Write; use std::io; use term; @@ -192,7 +192,8 @@ pub fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Res // A simple console test runner pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Result { - fn callback( + // A callback handling events that occure during test execution. + fn on_test_event( event: &TestEvent, st: &mut ConsoleTestState, out: &mut dyn OutputFormatter, @@ -205,9 +206,14 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Resu TestEvent::TeFilteredOut(filtered_out) => Ok(st.filtered_out = filtered_out), TestEvent::TeWait(ref test) => out.write_test_start(test), TestEvent::TeTimeout(ref test) => out.write_timeout(test), - TestEvent::TeResult(test, result, exec_time, stdout) => { - st.write_log_result(&test, &result, exec_time.as_ref())?; - out.write_result(&test, &result, exec_time.as_ref(), &*stdout, &st)?; + TestEvent::TeResult(completed_test) => { + let test = completed_test.desc; + let result = &completed_test.result; + let exec_time = &completed_test.exec_time; + let stdout = completed_test.stdout; + + st.write_log_result(&test, result, exec_time.as_ref())?; + out.write_result(&test, result, exec_time.as_ref(), &*stdout, st)?; match result { TestResult::TrOk => { st.passed += 1; @@ -280,7 +286,7 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Resu } } - run_tests(opts, tests, |x| callback(&x, &mut st, &mut *out))?; + run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?; assert!(st.current_test_count() == st.total); diff --git a/src/libtest/event.rs b/src/libtest/event.rs index b84551826c69..e1b606149c50 100644 --- a/src/libtest/event.rs +++ b/src/libtest/event.rs @@ -5,11 +5,32 @@ use super::types::TestDesc; use super::test_result::TestResult; use super::time::TestExecTime; -#[derive(Clone)] +#[derive(Debug, Clone)] +pub struct CompletedTest { + pub desc: TestDesc, + pub result: TestResult, + pub exec_time: Option, + pub stdout: Vec, +} + +impl CompletedTest { + pub fn new(desc: TestDesc, result: TestResult, exec_time: Option, stdout: Vec) -> Self { + Self { + desc, + result, + exec_time, + stdout, + } + } +} + +unsafe impl Send for CompletedTest {} + +#[derive(Debug, Clone)] pub enum TestEvent { TeFiltered(Vec), TeWait(TestDesc), - TeResult(TestDesc, TestResult, Option, Vec), + TeResult(CompletedTest), TeTimeout(TestDesc), TeFilteredOut(usize), } diff --git a/src/libtest/formatters/json.rs b/src/libtest/formatters/json.rs index 41a293195cc8..fc677036dabf 100644 --- a/src/libtest/formatters/json.rs +++ b/src/libtest/formatters/json.rs @@ -1,5 +1,16 @@ -use super::*; -use super::console::{ConsoleTestState, OutputLocation}; +use std::{ + io, + io::prelude::Write, + borrow::Cow, +}; + +use crate::{ + types::TestDesc, + time, + test_result::TestResult, + console::{ConsoleTestState, OutputLocation}, +}; +use super::OutputFormatter; pub(crate) struct JsonFormatter { out: OutputLocation, @@ -81,21 +92,21 @@ impl OutputFormatter for JsonFormatter { stdout: &[u8], state: &ConsoleTestState, ) -> io::Result<()> { - let stdout = if (state.options.display_output || *result != TrOk) && stdout.len() > 0 { + let stdout = if (state.options.display_output || *result != TestResult::TrOk) && stdout.len() > 0 { Some(String::from_utf8_lossy(stdout)) } else { None }; match *result { - TrOk => { + TestResult::TrOk => { self.write_event("test", desc.name.as_slice(), "ok", exec_time, stdout, None) } - TrFailed => { + TestResult::TrFailed => { self.write_event("test", desc.name.as_slice(), "failed", exec_time, stdout, None) } - TrTimedFail => self.write_event( + TestResult::TrTimedFail => self.write_event( "test", desc.name.as_slice(), "failed", @@ -104,7 +115,7 @@ impl OutputFormatter for JsonFormatter { Some(r#""reason": "time limit exceeded""#), ), - TrFailedMsg(ref m) => self.write_event( + TestResult::TrFailedMsg(ref m) => self.write_event( "test", desc.name.as_slice(), "failed", @@ -113,11 +124,11 @@ impl OutputFormatter for JsonFormatter { Some(&*format!(r#""message": "{}""#, EscapedString(m))), ), - TrIgnored => { + TestResult::TrIgnored => { self.write_event("test", desc.name.as_slice(), "ignored", exec_time, stdout, None) } - TrAllowedFail => self.write_event( + TestResult::TrAllowedFail => self.write_event( "test", desc.name.as_slice(), "allowed_failure", @@ -126,7 +137,7 @@ impl OutputFormatter for JsonFormatter { None, ), - TrBench(ref bs) => { + TestResult::TrBench(ref bs) => { let median = bs.ns_iter_summ.median as usize; let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize; diff --git a/src/libtest/formatters/mod.rs b/src/libtest/formatters/mod.rs index 4f3ffdafe3fb..b6649a3effc7 100644 --- a/src/libtest/formatters/mod.rs +++ b/src/libtest/formatters/mod.rs @@ -1,5 +1,14 @@ -use super::*; -use super::console::ConsoleTestState; +use std::{ + io, + io::prelude::Write, +}; + +use crate::{ + types::{TestDesc, TestName}, + time, + test_result::TestResult, + console::{ConsoleTestState}, +}; mod pretty; mod json; diff --git a/src/libtest/formatters/pretty.rs b/src/libtest/formatters/pretty.rs index 6f2c56bdf45e..2fdbc63d5133 100644 --- a/src/libtest/formatters/pretty.rs +++ b/src/libtest/formatters/pretty.rs @@ -1,5 +1,16 @@ -use super::*; -use super::console::{ConsoleTestState, OutputLocation}; +use std::{ + io, + io::prelude::Write, +}; + +use crate::{ + types::TestDesc, + time, + test_result::TestResult, + console::{ConsoleTestState, OutputLocation}, + bench::fmt_bench_samples, +}; +use super::OutputFormatter; pub(crate) struct PrettyFormatter { out: OutputLocation, @@ -204,15 +215,15 @@ impl OutputFormatter for PrettyFormatter { } match *result { - TrOk => self.write_ok()?, - TrFailed | TrFailedMsg(_) => self.write_failed()?, - TrIgnored => self.write_ignored()?, - TrAllowedFail => self.write_allowed_fail()?, - TrBench(ref bs) => { + TestResult::TrOk => self.write_ok()?, + TestResult::TrFailed | TestResult::TrFailedMsg(_) => self.write_failed()?, + TestResult::TrIgnored => self.write_ignored()?, + TestResult::TrAllowedFail => self.write_allowed_fail()?, + TestResult::TrBench(ref bs) => { self.write_bench()?; self.write_plain(&format!(": {}", fmt_bench_samples(bs)))?; } - TrTimedFail => self.write_time_failed()?, + TestResult::TrTimedFail => self.write_time_failed()?, } self.write_time(desc, exec_time)?; diff --git a/src/libtest/formatters/terse.rs b/src/libtest/formatters/terse.rs index 96203d5ea427..90eb62251fb4 100644 --- a/src/libtest/formatters/terse.rs +++ b/src/libtest/formatters/terse.rs @@ -1,5 +1,20 @@ -use super::*; -use super::console::{ConsoleTestState, OutputLocation}; +use std::{ + io, + io::prelude::Write, +}; + +use crate::{ + types::TestDesc, + time, + test_result::TestResult, + types::NamePadding, + console::{ConsoleTestState, OutputLocation}, + bench::fmt_bench_samples, +}; +use super::OutputFormatter; + +// insert a '\n' after 100 tests in quiet mode +const QUIET_MODE_MAX_COLUMN: usize = 100; pub(crate) struct TerseFormatter { out: OutputLocation, @@ -164,7 +179,7 @@ impl OutputFormatter for TerseFormatter { // in order to indicate benchmarks. // When running benchmarks, terse-mode should still print their name as if // it is the Pretty formatter. - if !self.is_multithreaded && desc.name.padding() == PadOnRight { + if !self.is_multithreaded && desc.name.padding() == NamePadding::PadOnRight { self.write_test_name(desc)?; } @@ -180,11 +195,11 @@ impl OutputFormatter for TerseFormatter { _: &ConsoleTestState, ) -> io::Result<()> { match *result { - TrOk => self.write_ok(), - TrFailed | TrFailedMsg(_) | TrTimedFail => self.write_failed(), - TrIgnored => self.write_ignored(), - TrAllowedFail => self.write_allowed_fail(), - TrBench(ref bs) => { + TestResult::TrOk => self.write_ok(), + TestResult::TrFailed | TestResult::TrFailedMsg(_) | TestResult::TrTimedFail => self.write_failed(), + TestResult::TrIgnored => self.write_ignored(), + TestResult::TrAllowedFail => self.write_allowed_fail(), + TestResult::TrBench(ref bs) => { if self.is_multithreaded { self.write_test_name(desc)?; } diff --git a/src/libtest/helpers/exit_code.rs b/src/libtest/helpers/exit_code.rs new file mode 100644 index 000000000000..831bef3b118a --- /dev/null +++ b/src/libtest/helpers/exit_code.rs @@ -0,0 +1,20 @@ +//! Helper module to detect subprocess exit code. + +use std::process::ExitStatus; + +#[cfg(not(unix))] +pub fn get_exit_code(status: ExitStatus) -> Result { + status.code().ok_or("received no exit code from child process".into()) +} + +#[cfg(unix)] +pub fn get_exit_code(status: ExitStatus) -> Result { + use std::os::unix::process::ExitStatusExt; + match status.code() { + Some(code) => Ok(code), + None => match status.signal() { + Some(signal) => Err(format!("child process exited with signal {}", signal)), + None => Err("child process exited with unknown signal".into()), + } + } +} diff --git a/src/libtest/helpers/mod.rs b/src/libtest/helpers/mod.rs index 0bbe77b1c50a..6a2ef6086cb9 100644 --- a/src/libtest/helpers/mod.rs +++ b/src/libtest/helpers/mod.rs @@ -4,3 +4,5 @@ pub mod concurrency; pub mod isatty; pub mod metrics; +pub mod sink; +pub mod exit_code; diff --git a/src/libtest/helpers/sink.rs b/src/libtest/helpers/sink.rs new file mode 100644 index 000000000000..aa7fe2487730 --- /dev/null +++ b/src/libtest/helpers/sink.rs @@ -0,0 +1,24 @@ +//! Module providing a helper structure to capture output in subprocesses. + +use std::{ + io, + io::prelude::Write, + sync::{Arc, Mutex}, +}; + +pub struct Sink(Arc>>); + +impl Sink { + pub fn new_boxed(data: &Arc>>) -> Box { + Box::new(Self(data.clone())) + } +} + +impl Write for Sink { + fn write(&mut self, data: &[u8]) -> io::Result { + Write::write(&mut *self.0.lock().unwrap(), data) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 6221140f6069..31da97b736a2 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -30,30 +30,13 @@ #![feature(termination_trait_lib)] #![feature(test)] +// Public reexports pub use self::ColorConfig::*; -use self::event::TestEvent::*; +pub use self::types::*; pub use self::types::TestName::*; +pub use self::options::{Options, ShouldPanic}; -use std::borrow::Cow; -use std::env; -use std::io; -use std::io::prelude::*; -use std::panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo}; -use std::process; -use std::process::{ExitStatus, Command, Termination}; -use std::sync::mpsc::{channel, Sender}; -use std::sync::{Arc, Mutex}; -use std::thread; -use std::time::{Duration, Instant}; - -#[cfg(test)] -mod tests; - -const QUIET_MODE_MAX_COLUMN: usize = 100; // insert a '\n' after 100 tests in quiet mode - -const SECONDARY_TEST_INVOKER_VAR: &'static str = "__RUST_TEST_INVOKE"; - -// to be used by rustc to compile tests in libtest +// Module to be used by rustc to compile tests in libtest pub mod test { pub use crate::{ bench::Bencher, @@ -61,7 +44,7 @@ pub mod test { helpers::metrics::{Metric, MetricMap}, options::{ShouldPanic, Options, RunIgnored, RunStrategy}, test_result::{TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk}, - time::TestTimeOptions, + time::{TestTimeOptions, TestExecTime}, types::{ DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, TestDescAndFn, TestName, TestType, @@ -70,18 +53,21 @@ pub mod test { }; } -use bench::*; -use test_result::*; -use types::*; -use options::*; -use cli::*; -use event::*; +use std::{ + env, + io, + io::prelude::Write, + panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo}, + process, + process::{Command, Termination}, + sync::mpsc::{channel, Sender}, + sync::{Arc, Mutex}, + thread, + time::{Duration, Instant}, +}; -use helpers::concurrency::get_concurrency; - -mod formatters; pub mod stats; - +mod formatters; mod cli; mod console; mod event; @@ -92,14 +78,31 @@ mod options; mod bench; mod test_result; +#[cfg(test)] +mod tests; + +use test_result::*; +use time::TestExecTime; +use options::{RunStrategy, Concurrent, RunIgnored, ColorConfig}; +use event::{CompletedTest, TestEvent}; +use cli::TestOpts; +use helpers::sink::Sink; +use helpers::concurrency::get_concurrency; +use helpers::exit_code::get_exit_code; + +// Process exit code to be used to indicate test failures. +const ERROR_EXIT_CODE: i32 = 101; + +const SECONDARY_TEST_INVOKER_VAR: &'static str = "__RUST_TEST_INVOKE"; + // The default console test runner. It accepts the command line // arguments and a vector of test_descs. pub fn test_main(args: &[String], tests: Vec, options: Option) { - let mut opts = match parse_opts(args) { + let mut opts = match cli::parse_opts(args) { Some(Ok(o)) => o, Some(Err(msg)) => { eprintln!("error: {}", msg); - process::exit(101); + process::exit(ERROR_EXIT_CODE); } None => return, }; @@ -109,15 +112,15 @@ pub fn test_main(args: &[String], tests: Vec, options: Option {} - Ok(false) => process::exit(101), + Ok(false) => process::exit(ERROR_EXIT_CODE), Err(e) => { eprintln!("error: io error when listing tests: {:?}", e); - process::exit(101); + process::exit(ERROR_EXIT_CODE); } } } @@ -196,19 +199,7 @@ pub fn assert_test_result(result: T) { ); } -pub type MonitorMsg = (TestDesc, TestResult, Option, Vec); - -struct Sink(Arc>>); -impl Write for Sink { - fn write(&mut self, data: &[u8]) -> io::Result { - Write::write(&mut *self.0.lock().unwrap(), data) - } - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub fn run_tests(opts: &TestOpts, tests: Vec, mut callback: F) -> io::Result<()> +pub fn run_tests(opts: &TestOpts, tests: Vec, mut notify_about_test_event: F) -> io::Result<()> where F: FnMut(TestEvent) -> io::Result<()>, { @@ -236,11 +227,13 @@ where }; let filtered_out = tests_len - filtered_tests.len(); - callback(TeFilteredOut(filtered_out))?; + let event = TestEvent::TeFilteredOut(filtered_out); + notify_about_test_event(event)?; let filtered_descs = filtered_tests.iter().map(|t| t.desc.clone()).collect(); - callback(TeFiltered(filtered_descs))?; + let event = TestEvent::TeFiltered(filtered_descs); + notify_about_test_event(event)?; let (filtered_tests, filtered_benchs): (Vec<_>, _) = filtered_tests.into_iter().partition(|e| match e.testfn { @@ -254,7 +247,7 @@ where remaining.reverse(); let mut pending = 0; - let (tx, rx) = channel::(); + let (tx, rx) = channel::(); let run_strategy = if opts.options.panic_abort { RunStrategy::SpawnPrimary } else { @@ -295,10 +288,13 @@ where if concurrency == 1 { while !remaining.is_empty() { let test = remaining.pop().unwrap(); - callback(TeWait(test.desc.clone()))?; + let event = TestEvent::TeWait(test.desc.clone()); + notify_about_test_event(event)?; run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::No); - let (test, result, exec_time, stdout) = rx.recv().unwrap(); - callback(TeResult(test, result, exec_time, stdout))?; + let completed_test = rx.recv().unwrap(); + + let event = TestEvent::TeResult(completed_test); + notify_about_test_event(event)?; } } else { while pending > 0 || !remaining.is_empty() { @@ -306,7 +302,9 @@ where let test = remaining.pop().unwrap(); let timeout = time::get_default_test_timeout(); running_tests.insert(test.desc.clone(), timeout); - callback(TeWait(test.desc.clone()))?; //here no pad + + let event = TestEvent::TeWait(test.desc.clone()); + notify_about_test_event(event)?; //here no pad run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::Yes); pending += 1; } @@ -316,10 +314,18 @@ where if let Some(timeout) = calc_timeout(&running_tests) { res = rx.recv_timeout(timeout); for test in get_timed_out_tests(&mut running_tests) { - callback(TeTimeout(test))?; + let event = TestEvent::TeTimeout(test); + notify_about_test_event(event)?; } - if res != Err(RecvTimeoutError::Timeout) { - break; + + match res { + Err(RecvTimeoutError::Timeout) => { + // Result is not yet ready, continue waiting. + } + _ => { + // We've got a result, stop the loop. + break; + } } } else { res = rx.recv().map_err(|_| RecvTimeoutError::Disconnected); @@ -327,10 +333,11 @@ where } } - let (desc, result, exec_time, stdout) = res.unwrap(); - running_tests.remove(&desc); + let completed_test = res.unwrap(); + running_tests.remove(&completed_test.desc); - callback(TeResult(desc, result, exec_time, stdout))?; + let event = TestEvent::TeResult(completed_test); + notify_about_test_event(event)?; pending -= 1; } } @@ -338,10 +345,13 @@ where if opts.bench_benchmarks { // All benchmarks run at the end, in serial. for b in filtered_benchs { - callback(TeWait(b.desc.clone()))?; + let event = TestEvent::TeWait(b.desc.clone()); + notify_about_test_event(event)?; run_test(opts, false, b, run_strategy, tx.clone(), Concurrent::No); - let (test, result, exec_time, stdout) = rx.recv().unwrap(); - callback(TeResult(test, result, exec_time, stdout))?; + let completed_test = rx.recv().unwrap(); + + let event = TestEvent::TeResult(completed_test); + notify_about_test_event(event)?; } } Ok(()) @@ -420,7 +430,7 @@ pub fn run_test( force_ignore: bool, test: TestDescAndFn, strategy: RunStrategy, - monitor_ch: Sender, + monitor_ch: Sender, concurrency: Concurrent, ) { let TestDescAndFn { desc, testfn } = test; @@ -430,7 +440,8 @@ pub fn run_test( && (cfg!(target_arch = "wasm32") || cfg!(target_os = "emscripten")); if force_ignore || desc.ignore || ignore_because_no_process_support { - monitor_ch.send((desc, TrIgnored, None, Vec::new())).unwrap(); + let message = CompletedTest::new(desc, TrIgnored, None, Vec::new()); + monitor_ch.send(message).unwrap(); return; } @@ -443,7 +454,7 @@ pub fn run_test( fn run_test_inner( desc: TestDesc, - monitor_ch: Sender, + monitor_ch: Sender, testfn: Box, opts: TestRunOpts, ) { @@ -530,7 +541,7 @@ fn run_test_in_process( nocapture: bool, report_time: bool, testfn: Box, - monitor_ch: Sender, + monitor_ch: Sender, time_opts: Option, ) { // Buffer for capturing standard I/O @@ -538,8 +549,8 @@ fn run_test_in_process( let oldio = if !nocapture { Some(( - io::set_print(Some(Box::new(Sink(data.clone())))), - io::set_panic(Some(Box::new(Sink(data.clone())))), + io::set_print(Some(Sink::new_boxed(&data))), + io::set_panic(Some(Sink::new_boxed(&data))), )) } else { None @@ -553,7 +564,7 @@ fn run_test_in_process( let result = catch_unwind(AssertUnwindSafe(testfn)); let exec_time = start.map(|start| { let duration = start.elapsed(); - time::TestExecTime(duration) + TestExecTime(duration) }); if let Some((printio, panicio)) = oldio { @@ -566,13 +577,14 @@ fn run_test_in_process( Err(e) => calc_result(&desc, Err(e.as_ref()), &time_opts, &exec_time), }; let stdout = data.lock().unwrap().to_vec(); - monitor_ch.send((desc.clone(), test_result, exec_time, stdout)).unwrap(); + let message = CompletedTest::new(desc.clone(), test_result, exec_time, stdout); + monitor_ch.send(message).unwrap(); } fn spawn_test_subprocess( desc: TestDesc, report_time: bool, - monitor_ch: Sender, + monitor_ch: Sender, time_opts: Option, ) { let (result, test_output, exec_time) = (|| { @@ -595,7 +607,7 @@ fn spawn_test_subprocess( }; let exec_time = start.map(|start| { let duration = start.elapsed(); - time::TestExecTime(duration) + TestExecTime(duration) }); let std::process::Output { stdout, stderr, status } = output; @@ -617,7 +629,8 @@ fn spawn_test_subprocess( (result, test_output, exec_time) })(); - monitor_ch.send((desc.clone(), result, exec_time, test_output)).unwrap(); + let message = CompletedTest::new(desc.clone(), result, exec_time, test_output); + monitor_ch.send(message).unwrap(); } fn run_test_in_spawned_subprocess( @@ -653,20 +666,3 @@ fn run_test_in_spawned_subprocess( record_result(None); unreachable!("panic=abort callback should have exited the process") } - -#[cfg(not(unix))] -fn get_exit_code(status: ExitStatus) -> Result { - status.code().ok_or("received no exit code from child process".into()) -} - -#[cfg(unix)] -fn get_exit_code(status: ExitStatus) -> Result { - use std::os::unix::process::ExitStatusExt; - match status.code() { - Some(code) => Ok(code), - None => match status.signal() { - Some(signal) => Err(format!("child process exited with signal {}", signal)), - None => Err("child process exited with unknown signal".into()), - } - } -} diff --git a/src/libtest/stats/tests.rs b/src/libtest/stats/tests.rs index 7d1d635186fb..eaf41bc9e225 100644 --- a/src/libtest/stats/tests.rs +++ b/src/libtest/stats/tests.rs @@ -4,7 +4,7 @@ extern crate test; use std::f64; use std::io::prelude::*; use std::io; -use self::test::Bencher; +use self::test::test::Bencher; // Test vectors generated from R, using the script src/etc/stat-test-vectors.r. diff --git a/src/libtest/tests.rs b/src/libtest/tests.rs index 5f7150a8eeba..f6470b40a391 100644 --- a/src/libtest/tests.rs +++ b/src/libtest/tests.rs @@ -1,11 +1,18 @@ use super::*; -use crate::test::{ - filter_tests, parse_opts, run_test, DynTestFn, DynTestName, MetricMap, RunIgnored, RunStrategy, - // ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts, TestTimeOptions, - // TestType, TrFailedMsg, TrIgnored, TrOk, - ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts, - TrIgnored, TrOk, +use crate::{ + bench::Bencher, + console::OutputLocation, + options::OutputFormat, + time::{TimeThreshold, TestTimeOptions}, + formatters::PrettyFormatter, + test::{ + filter_tests, parse_opts, run_test, DynTestFn, DynTestName, MetricMap, RunIgnored, RunStrategy, + // ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts, TestTimeOptions, + // TestType, TrFailedMsg, TrIgnored, TrOk, + ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts, + TrIgnored, TrOk, + }, }; use std::sync::mpsc::channel; use std::time::Duration; @@ -74,8 +81,8 @@ pub fn do_not_run_ignored_tests() { }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); - let (_, res, _, _) = rx.recv().unwrap(); - assert!(res != TrOk); + let result = rx.recv().unwrap().result; + assert!(result != TrOk); } #[test] @@ -93,8 +100,8 @@ pub fn ignored_tests_result_in_ignored() { }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); - let (_, res, _, _) = rx.recv().unwrap(); - assert!(res == TrIgnored); + let result = rx.recv().unwrap().result; + assert!(result == TrIgnored); } // FIXME: Re-enable emscripten once it can catch panics again @@ -116,8 +123,8 @@ fn test_should_panic() { }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); - let (_, res, _, _) = rx.recv().unwrap(); - assert!(res == TrOk); + let result = rx.recv().unwrap().result; + assert!(result == TrOk); } // FIXME: Re-enable emscripten once it can catch panics again @@ -139,8 +146,8 @@ fn test_should_panic_good_message() { }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); - let (_, res, _, _) = rx.recv().unwrap(); - assert!(res == TrOk); + let result = rx.recv().unwrap().result; + assert!(result == TrOk); } // FIXME: Re-enable emscripten once it can catch panics again @@ -165,8 +172,8 @@ fn test_should_panic_bad_message() { }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); - let (_, res, _, _) = rx.recv().unwrap(); - assert!(res == TrFailedMsg(format!("{} '{}'", failed_msg, expected))); + let result = rx.recv().unwrap().result; + assert!(result == TrFailedMsg(format!("{} '{}'", failed_msg, expected))); } // FIXME: Re-enable emscripten once it can catch panics again @@ -186,8 +193,8 @@ fn test_should_panic_but_succeeds() { }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); - let (_, res, _, _) = rx.recv().unwrap(); - assert!(res == TrFailedMsg("test did not panic as expected".to_string())); + let result = rx.recv().unwrap().result; + assert!(result == TrFailedMsg("test did not panic as expected".to_string())); } fn report_time_test_template(report_time: bool) -> Option { @@ -214,7 +221,7 @@ fn report_time_test_template(report_time: bool) -> Option { }; let (tx, rx) = channel(); run_test(&test_opts, false, desc, RunStrategy::InProcess, tx, Concurrent::No); - let (_, _, exec_time, _) = rx.recv().unwrap(); + let exec_time = rx.recv().unwrap().exec_time; exec_time } @@ -252,7 +259,7 @@ fn time_test_failure_template(test_type: TestType) -> TestResult { }; let (tx, rx) = channel(); run_test(&test_opts, false, desc, RunStrategy::InProcess, tx, Concurrent::No); - let (_, result, _, _) = rx.recv().unwrap(); + let result = rx.recv().unwrap().result; result } @@ -658,9 +665,9 @@ fn should_sort_failures_before_printing_them() { test_type: TestType::Unknown, }; - let mut out = PrettyFormatter::new(Raw(Vec::new()), false, 10, false, None); + let mut out = PrettyFormatter::new(OutputLocation::Raw(Vec::new()), false, 10, false, None); - let st = ConsoleTestState { + let st = console::ConsoleTestState { log_out: None, total: 0, passed: 0, @@ -678,8 +685,8 @@ fn should_sort_failures_before_printing_them() { out.write_failures(&st).unwrap(); let s = match out.output_location() { - &Raw(ref m) => String::from_utf8_lossy(&m[..]), - &Pretty(_) => unreachable!(), + &OutputLocation::Raw(ref m) => String::from_utf8_lossy(&m[..]), + &OutputLocation::Pretty(_) => unreachable!(), }; let apos = s.find("a").unwrap(); diff --git a/src/libtest/time.rs b/src/libtest/time.rs index b7ce764505bf..83a545470efa 100644 --- a/src/libtest/time.rs +++ b/src/libtest/time.rs @@ -61,7 +61,7 @@ pub fn get_default_test_timeout() -> Instant { } /// The meassured execution time of a unit test. -#[derive(Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct TestExecTime(pub Duration); impl fmt::Display for TestExecTime { From ddc6a5fd0e3e56f2ccbc576fdeed149579f141de Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Thu, 17 Oct 2019 19:10:17 +0300 Subject: [PATCH 31/79] Split options parsing into several functions --- src/libtest/cli.rs | 342 ++++++++++++++++++++++++++------------------- 1 file changed, 199 insertions(+), 143 deletions(-) diff --git a/src/libtest/cli.rs b/src/libtest/cli.rs index b35193701d6e..0c47bc8ae94a 100644 --- a/src/libtest/cli.rs +++ b/src/libtest/cli.rs @@ -40,7 +40,7 @@ impl TestOpts { /// Result of parsing the options. pub type OptRes = Result; /// Result of parsing the option part. -type OptPartRes = Result, String>; +type OptPartRes = Result; fn optgroups() -> getopts::Options { let mut opts = getopts::Options::new(); @@ -186,6 +186,96 @@ Test Attributes: ); } +/// Parses command line arguments into test options. +/// Returns `None` if help was requested (since we only show help message and don't run tests), +/// returns `Some(Err(..))` if provided arguments are incorrect, +/// otherwise creates a `TestOpts` object and returns it. +pub fn parse_opts(args: &[String]) -> Option { + // Parse matches. + let opts = optgroups(); + let args = args.get(1..).unwrap_or(args); + let matches = match opts.parse(args) { + Ok(m) => m, + Err(f) => return Some(Err(f.to_string())), + }; + + // Check if help was requested. + if matches.opt_present("h") { + // Show help and do nothing more. + usage(&args[0], &opts); + return None; + } + + // Actually parse the opts. + let opts_result = parse_opts_impl(matches); + + Some(opts_result) +} + +// Gets the option value and checks if unstable features are enabled. +macro_rules! unstable_optflag { + ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{ + let opt = $matches.opt_present($option_name); + if !$allow_unstable && opt { + return Err(format!( + "The \"{}\" flag is only accepted on the nightly compiler", + $option_name + )); + } + + opt + }}; +} + +// Implementation of `parse_opts` that doesn't care about help message +// and returns a `Result`. +fn parse_opts_impl(matches: getopts::Matches) -> OptRes { + let allow_unstable = get_allow_unstable(&matches)?; + + // Unstable flags + let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic"); + let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored"); + let time_options = get_time_options(&matches, allow_unstable)?; + + let quiet = matches.opt_present("quiet"); + let exact = matches.opt_present("exact"); + let list = matches.opt_present("list"); + let skip = matches.opt_strs("skip"); + + let bench_benchmarks = matches.opt_present("bench"); + let run_tests = !bench_benchmarks || matches.opt_present("test"); + + let logfile = get_log_file(&matches)?; + let run_ignored = get_run_ignored(&matches, include_ignored)?; + let filter = get_filter(&matches)?; + let nocapture = get_nocapture(&matches)?; + let test_threads = get_test_threads(&matches)?; + let color = get_color_config(&matches)?; + let format = get_format(&matches, quiet, allow_unstable)?; + + let options = Options::new().display_output(matches.opt_present("show-output")); + + let test_opts = TestOpts { + list, + filter, + filter_exact: exact, + exclude_should_panic, + run_ignored, + run_tests, + bench_benchmarks, + logfile, + nocapture, + color, + format, + test_threads, + skip, + time_options, + options, + }; + + Ok(test_opts) +} + // FIXME: Copied from libsyntax until linkage errors are resolved. Issue #47566 fn is_nightly() -> bool { // Whether this is a feature-staged build, i.e., on the beta or stable channel @@ -196,26 +286,11 @@ fn is_nightly() -> bool { bootstrap || !disable_unstable_features } -// Gets the option value and checks if unstable features are enabled. -macro_rules! unstable_optflag { - ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{ - let opt = $matches.opt_present($option_name); - if !$allow_unstable && opt { - return Some(Err(format!( - "The \"{}\" flag is only accepted on the nightly compiler", - $option_name - ))); - } - - opt - }}; -} - // Gets the CLI options assotiated with `report-time` feature. fn get_time_options( matches: &getopts::Matches, allow_unstable: bool) --> Option> { +-> OptPartRes> { let report_time = unstable_optflag!(matches, allow_unstable, "report-time"); let colored_opt_str = matches.opt_str("report-time"); let mut report_time_colored = report_time && colored_opt_str == Some("colored".into()); @@ -232,71 +307,73 @@ fn get_time_options( None }; - Some(Ok(options)) + Ok(options) } -// Parses command line arguments into test options -pub fn parse_opts(args: &[String]) -> Option { - let mut allow_unstable = false; - let opts = optgroups(); - let args = args.get(1..).unwrap_or(args); - let matches = match opts.parse(args) { - Ok(m) => m, - Err(f) => return Some(Err(f.to_string())), +fn get_test_threads(matches: &getopts::Matches) -> OptPartRes> { + let test_threads = match matches.opt_str("test-threads") { + Some(n_str) => match n_str.parse::() { + Ok(0) => return Err("argument for --test-threads must not be 0".to_string()), + Ok(n) => Some(n), + Err(e) => { + return Err(format!( + "argument for --test-threads must be a number > 0 \ + (error: {})", + e + )); + } + }, + None => None, }; - if let Some(opt) = matches.opt_str("Z") { - if !is_nightly() { - return Some(Err( - "the option `Z` is only accepted on the nightly compiler".into(), + Ok(test_threads) +} + +fn get_format(matches: &getopts::Matches, quiet: bool, allow_unstable: bool) -> OptPartRes { + let format = match matches.opt_str("format").as_ref().map(|s| &**s) { + None if quiet => OutputFormat::Terse, + Some("pretty") | None => OutputFormat::Pretty, + Some("terse") => OutputFormat::Terse, + Some("json") => { + if !allow_unstable { + return Err( + "The \"json\" format is only accepted on the nightly compiler".into(), + ); + } + OutputFormat::Json + } + + Some(v) => { + return Err(format!( + "argument for --format must be pretty, terse, or json (was \ + {})", + v )); } - - match &*opt { - "unstable-options" => { - allow_unstable = true; - } - _ => { - return Some(Err("Unrecognized option to `Z`".into())); - } - } }; - if matches.opt_present("h") { - usage(&args[0], &opts); - return None; - } + Ok(format) +} - let filter = if !matches.free.is_empty() { - Some(matches.free[0].clone()) - } else { - None - }; +fn get_color_config(matches: &getopts::Matches) -> OptPartRes { + let color = match matches.opt_str("color").as_ref().map(|s| &**s) { + Some("auto") | None => ColorConfig::AutoColor, + Some("always") => ColorConfig::AlwaysColor, + Some("never") => ColorConfig::NeverColor, - let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic"); - - let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored"); - - let run_ignored = match (include_ignored, matches.opt_present("ignored")) { - (true, true) => { - return Some(Err( - "the options --include-ignored and --ignored are mutually exclusive".into(), + Some(v) => { + return Err(format!( + "argument for --color must be auto, always, or never (was \ + {})", + v )); } - (true, false) => RunIgnored::Yes, - (false, true) => RunIgnored::Only, - (false, false) => RunIgnored::No, }; - let quiet = matches.opt_present("quiet"); - let exact = matches.opt_present("exact"); - let list = matches.opt_present("list"); - let logfile = matches.opt_str("logfile"); - let logfile = logfile.map(|s| PathBuf::from(&s)); - - let bench_benchmarks = matches.opt_present("bench"); - let run_tests = !bench_benchmarks || matches.opt_present("test"); + Ok(color) +} +fn get_nocapture(matches: &getopts::Matches) -> OptPartRes { let mut nocapture = matches.opt_present("nocapture"); if !nocapture { nocapture = match env::var("RUST_TEST_NOCAPTURE") { @@ -305,80 +382,59 @@ pub fn parse_opts(args: &[String]) -> Option { }; } - let time_options = match get_time_options(&matches, allow_unstable) { - Some(Ok(val)) => val, - Some(Err(e)) => return Some(Err(e)), - None => panic!("Unexpected output from `get_time_options`"), - }; - - let test_threads = match matches.opt_str("test-threads") { - Some(n_str) => match n_str.parse::() { - Ok(0) => return Some(Err("argument for --test-threads must not be 0".to_string())), - Ok(n) => Some(n), - Err(e) => { - return Some(Err(format!( - "argument for --test-threads must be a number > 0 \ - (error: {})", - e - ))); - } - }, - None => None, - }; - - let color = match matches.opt_str("color").as_ref().map(|s| &**s) { - Some("auto") | None => ColorConfig::AutoColor, - Some("always") => ColorConfig::AlwaysColor, - Some("never") => ColorConfig::NeverColor, - - Some(v) => { - return Some(Err(format!( - "argument for --color must be auto, always, or never (was \ - {})", - v - ))); - } - }; - - let format = match matches.opt_str("format").as_ref().map(|s| &**s) { - None if quiet => OutputFormat::Terse, - Some("pretty") | None => OutputFormat::Pretty, - Some("terse") => OutputFormat::Terse, - Some("json") => { - if !allow_unstable { - return Some(Err( - "The \"json\" format is only accepted on the nightly compiler".into(), - )); - } - OutputFormat::Json - } - - Some(v) => { - return Some(Err(format!( - "argument for --format must be pretty, terse, or json (was \ - {})", - v - ))); - } - }; - - let test_opts = TestOpts { - list, - filter, - filter_exact: exact, - exclude_should_panic, - run_ignored, - run_tests, - bench_benchmarks, - logfile, - nocapture, - color, - format, - test_threads, - skip: matches.opt_strs("skip"), - time_options, - options: Options::new().display_output(matches.opt_present("show-output")), - }; - - Some(Ok(test_opts)) + Ok(nocapture) +} + +fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPartRes { + let run_ignored = match (include_ignored, matches.opt_present("ignored")) { + (true, true) => { + return Err( + "the options --include-ignored and --ignored are mutually exclusive".into(), + ); + } + (true, false) => RunIgnored::Yes, + (false, true) => RunIgnored::Only, + (false, false) => RunIgnored::No, + }; + + Ok(run_ignored) +} + +fn get_filter(matches: &getopts::Matches) -> OptPartRes> { + let filter = if !matches.free.is_empty() { + Some(matches.free[0].clone()) + } else { + None + }; + + Ok(filter) +} + +fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes { + let mut allow_unstable = false; + + if let Some(opt) = matches.opt_str("Z") { + if !is_nightly() { + return Err( + "the option `Z` is only accepted on the nightly compiler".into(), + ); + } + + match &*opt { + "unstable-options" => { + allow_unstable = true; + } + _ => { + return Err("Unrecognized option to `Z`".into()); + } + } + }; + + Ok(allow_unstable) +} + +fn get_log_file(matches: &getopts::Matches) -> OptPartRes> { + let logfile = matches.opt_str("logfile").map(|s| PathBuf::from(&s)); + + Ok(logfile) } From 85628e80637cf21caa9d6fef31d9fed53e7156aa Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Thu, 17 Oct 2019 19:10:35 +0300 Subject: [PATCH 32/79] Add more explaining comments to the code --- src/libtest/console.rs | 146 ++++++++++++++++++++----------------- src/libtest/options.rs | 10 +++ src/libtest/test_result.rs | 4 +- 3 files changed, 93 insertions(+), 67 deletions(-) diff --git a/src/libtest/console.rs b/src/libtest/console.rs index 2c14e9a1591c..e17030726cea 100644 --- a/src/libtest/console.rs +++ b/src/libtest/console.rs @@ -7,21 +7,23 @@ use std::io; use term; use super::{ + bench::fmt_bench_samples, + cli::TestOpts, + event::{TestEvent, CompletedTest}, + formatters::{JsonFormatter, OutputFormatter, PrettyFormatter, TerseFormatter}, helpers::{ concurrency::get_concurrency, metrics::MetricMap, }, types::{TestDesc, TestDescAndFn, NamePadding}, options::{Options, OutputFormat}, - bench::fmt_bench_samples, test_result::TestResult, time::TestExecTime, - cli::TestOpts, - event::TestEvent, run_tests, filter_tests, }; +/// Generic wrapper over stdout. pub enum OutputLocation { Pretty(Box), Raw(T), @@ -43,8 +45,6 @@ impl Write for OutputLocation { } } -use crate::formatters::{JsonFormatter, OutputFormatter, PrettyFormatter, TerseFormatter}; - pub struct ConsoleTestState { pub log_out: Option, pub total: usize, @@ -190,65 +190,77 @@ pub fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Res Ok(()) } -// A simple console test runner -pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Result { - // A callback handling events that occure during test execution. - fn on_test_event( - event: &TestEvent, - st: &mut ConsoleTestState, - out: &mut dyn OutputFormatter, - ) -> io::Result<()> { - match (*event).clone() { - TestEvent::TeFiltered(ref filtered_tests) => { - st.total = filtered_tests.len(); - out.write_run_start(filtered_tests.len()) - } - TestEvent::TeFilteredOut(filtered_out) => Ok(st.filtered_out = filtered_out), - TestEvent::TeWait(ref test) => out.write_test_start(test), - TestEvent::TeTimeout(ref test) => out.write_timeout(test), - TestEvent::TeResult(completed_test) => { - let test = completed_test.desc; - let result = &completed_test.result; - let exec_time = &completed_test.exec_time; - let stdout = completed_test.stdout; +// Updates `ConsoleTestState` depending on result of the test execution. +fn handle_test_result(st: &mut ConsoleTestState, completed_test: CompletedTest) { + let test = completed_test.desc; + let stdout = completed_test.stdout; + match completed_test.result { + TestResult::TrOk => { + st.passed += 1; + st.not_failures.push((test, stdout)); + } + TestResult::TrIgnored => st.ignored += 1, + TestResult::TrAllowedFail => st.allowed_fail += 1, + TestResult::TrBench(bs) => { + st.metrics.insert_metric( + test.name.as_slice(), + bs.ns_iter_summ.median, + bs.ns_iter_summ.max - bs.ns_iter_summ.min, + ); + st.measured += 1 + } + TestResult::TrFailed => { + st.failed += 1; + st.failures.push((test, stdout)); + } + TestResult::TrFailedMsg(msg) => { + st.failed += 1; + let mut stdout = stdout; + stdout.extend_from_slice(format!("note: {}", msg).as_bytes()); + st.failures.push((test, stdout)); + } + TestResult::TrTimedFail => { + st.failed += 1; + st.time_failures.push((test, stdout)); + } + } +} - st.write_log_result(&test, result, exec_time.as_ref())?; - out.write_result(&test, result, exec_time.as_ref(), &*stdout, st)?; - match result { - TestResult::TrOk => { - st.passed += 1; - st.not_failures.push((test, stdout)); - } - TestResult::TrIgnored => st.ignored += 1, - TestResult::TrAllowedFail => st.allowed_fail += 1, - TestResult::TrBench(bs) => { - st.metrics.insert_metric( - test.name.as_slice(), - bs.ns_iter_summ.median, - bs.ns_iter_summ.max - bs.ns_iter_summ.min, - ); - st.measured += 1 - } - TestResult::TrFailed => { - st.failed += 1; - st.failures.push((test, stdout)); - } - TestResult::TrFailedMsg(msg) => { - st.failed += 1; - let mut stdout = stdout; - stdout.extend_from_slice(format!("note: {}", msg).as_bytes()); - st.failures.push((test, stdout)); - } - TestResult::TrTimedFail => { - st.failed += 1; - st.time_failures.push((test, stdout)); - } - } - Ok(()) - } +// Handler for events that occur during test execution. +// It is provided as a callback to the `run_tests` function. +fn on_test_event( + event: &TestEvent, + st: &mut ConsoleTestState, + out: &mut dyn OutputFormatter, +) -> io::Result<()> { + match (*event).clone() { + TestEvent::TeFiltered(ref filtered_tests) => { + st.total = filtered_tests.len(); + out.write_run_start(filtered_tests.len())?; + } + TestEvent::TeFilteredOut(filtered_out) => { + st.filtered_out = filtered_out; + } + TestEvent::TeWait(ref test) => out.write_test_start(test)?, + TestEvent::TeTimeout(ref test) => out.write_timeout(test)?, + TestEvent::TeResult(completed_test) => { + let test = &completed_test.desc; + let result = &completed_test.result; + let exec_time = &completed_test.exec_time; + let stdout = &completed_test.stdout; + + st.write_log_result(test, result, exec_time.as_ref())?; + out.write_result(test, result, exec_time.as_ref(), &*stdout, st)?; + handle_test_result(st, completed_test); } } + Ok(()) +} + +/// A simple console test runner. +/// Runs provided tests reporting process and results to the stdout. +pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Result { let output = match term::stdout() { None => OutputLocation::Raw(io::stdout()), Some(t) => OutputLocation::Pretty(t), @@ -279,12 +291,6 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Resu OutputFormat::Json => Box::new(JsonFormatter::new(output)), }; let mut st = ConsoleTestState::new(opts)?; - fn len_if_padded(t: &TestDescAndFn) -> usize { - match t.testfn.padding() { - NamePadding::PadNone => 0, - NamePadding::PadOnRight => t.desc.name.as_slice().len(), - } - } run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?; @@ -292,3 +298,11 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Resu return out.write_run_finish(&st); } + +// Calculates padding for given test description. +fn len_if_padded(t: &TestDescAndFn) -> usize { + match t.testfn.padding() { + NamePadding::PadNone => 0, + NamePadding::PadOnRight => t.desc.name.as_slice().len(), + } +} diff --git a/src/libtest/options.rs b/src/libtest/options.rs index 0a604cae0ca3..ec87b0fcd463 100644 --- a/src/libtest/options.rs +++ b/src/libtest/options.rs @@ -7,12 +7,14 @@ pub enum Concurrent { No, } +/// Number of times to run a benchmarked function #[derive(Clone, PartialEq, Eq)] pub enum BenchMode { Auto, Single, } +/// Whether test is expected to panic or not #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum ShouldPanic { No, @@ -20,6 +22,7 @@ pub enum ShouldPanic { YesWithMessage(&'static str), } +/// Whether should console output be colored or not #[derive(Copy, Clone, Debug)] pub enum ColorConfig { AutoColor, @@ -27,17 +30,23 @@ pub enum ColorConfig { NeverColor, } +/// Format of the test results output #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum OutputFormat { + /// Verbose output Pretty, + /// Quiet output Terse, + /// JSON output Json, } +/// Whether ignored test should be runned or not #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum RunIgnored { Yes, No, + /// Run only ignored tests Only, } @@ -53,6 +62,7 @@ pub enum RunStrategy { SpawnPrimary, } +/// Options for the test run defined by the caller (instead of CLI arguments). /// In case we want to add other options as well, just add them in this struct. #[derive(Copy, Clone, Debug)] pub struct Options { diff --git a/src/libtest/test_result.rs b/src/libtest/test_result.rs index 4eb3f93e2a42..dd4dfd9997f0 100644 --- a/src/libtest/test_result.rs +++ b/src/libtest/test_result.rs @@ -27,7 +27,8 @@ pub enum TestResult { unsafe impl Send for TestResult {} - +/// Creates a `TestResult` depending on the raw result of test execution +/// and assotiated data. pub fn calc_result<'a>( desc: &TestDesc, task_result: Result<(), &'a (dyn Any + 'static + Send)>, @@ -73,6 +74,7 @@ pub fn calc_result<'a>( result } +/// Creates a `TestResult` depending on the exit code of test subprocess. pub fn get_result_from_exit_code( desc: &TestDesc, code: i32, From cb5733de868600aab889730effe4077641a46981 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Thu, 17 Oct 2019 19:21:05 +0300 Subject: [PATCH 33/79] Improve code style --- src/libtest/cli.rs | 6 +++++- src/libtest/event.rs | 7 ++++++- src/libtest/formatters/json.rs | 3 ++- src/libtest/formatters/terse.rs | 4 +++- src/libtest/helpers/isatty.rs | 2 +- src/libtest/lib.rs | 12 ++++++++---- src/libtest/test_result.rs | 9 ++++++--- src/libtest/tests.rs | 15 ++++++++------- src/libtest/time.rs | 4 ++-- 9 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/libtest/cli.rs b/src/libtest/cli.rs index 0c47bc8ae94a..f95d5aad18a6 100644 --- a/src/libtest/cli.rs +++ b/src/libtest/cli.rs @@ -329,7 +329,11 @@ fn get_test_threads(matches: &getopts::Matches) -> OptPartRes> { Ok(test_threads) } -fn get_format(matches: &getopts::Matches, quiet: bool, allow_unstable: bool) -> OptPartRes { +fn get_format( + matches: &getopts::Matches, + quiet: bool, + allow_unstable: bool +) -> OptPartRes { let format = match matches.opt_str("format").as_ref().map(|s| &**s) { None if quiet => OutputFormat::Terse, Some("pretty") | None => OutputFormat::Pretty, diff --git a/src/libtest/event.rs b/src/libtest/event.rs index e1b606149c50..eefbd2d6a813 100644 --- a/src/libtest/event.rs +++ b/src/libtest/event.rs @@ -14,7 +14,12 @@ pub struct CompletedTest { } impl CompletedTest { - pub fn new(desc: TestDesc, result: TestResult, exec_time: Option, stdout: Vec) -> Self { + pub fn new( + desc: TestDesc, + result: TestResult, + exec_time: Option, + stdout: Vec + ) -> Self { Self { desc, result, diff --git a/src/libtest/formatters/json.rs b/src/libtest/formatters/json.rs index fc677036dabf..b73d7349678a 100644 --- a/src/libtest/formatters/json.rs +++ b/src/libtest/formatters/json.rs @@ -92,7 +92,8 @@ impl OutputFormatter for JsonFormatter { stdout: &[u8], state: &ConsoleTestState, ) -> io::Result<()> { - let stdout = if (state.options.display_output || *result != TestResult::TrOk) && stdout.len() > 0 { + let display_stdout = state.options.display_output || *result != TestResult::TrOk; + let stdout = if display_stdout && stdout.len() > 0 { Some(String::from_utf8_lossy(stdout)) } else { None diff --git a/src/libtest/formatters/terse.rs b/src/libtest/formatters/terse.rs index 90eb62251fb4..fe56157d9c10 100644 --- a/src/libtest/formatters/terse.rs +++ b/src/libtest/formatters/terse.rs @@ -196,7 +196,9 @@ impl OutputFormatter for TerseFormatter { ) -> io::Result<()> { match *result { TestResult::TrOk => self.write_ok(), - TestResult::TrFailed | TestResult::TrFailedMsg(_) | TestResult::TrTimedFail => self.write_failed(), + TestResult::TrFailed + | TestResult::TrFailedMsg(_) + | TestResult::TrTimedFail => self.write_failed(), TestResult::TrIgnored => self.write_ignored(), TestResult::TrAllowedFail => self.write_allowed_fail(), TestResult::TrBench(ref bs) => { diff --git a/src/libtest/helpers/isatty.rs b/src/libtest/helpers/isatty.rs index 638328aea18c..6e4954778e60 100644 --- a/src/libtest/helpers/isatty.rs +++ b/src/libtest/helpers/isatty.rs @@ -30,4 +30,4 @@ pub fn stdout_isatty() -> bool { let mut out = 0; GetConsoleMode(handle, &mut out) != 0 } -} \ No newline at end of file +} diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 31da97b736a2..89f527b6bd76 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -46,8 +46,8 @@ pub mod test { test_result::{TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk}, time::{TestTimeOptions, TestExecTime}, types::{ - DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, TestDescAndFn, - TestName, TestType, + DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, + TestDesc, TestDescAndFn, TestName, TestType, }, assert_test_result, filter_tests, run_test, test_main, test_main_static, }; @@ -199,7 +199,11 @@ pub fn assert_test_result(result: T) { ); } -pub fn run_tests(opts: &TestOpts, tests: Vec, mut notify_about_test_event: F) -> io::Result<()> +pub fn run_tests( + opts: &TestOpts, + tests: Vec, + mut notify_about_test_event: F +) -> io::Result<()> where F: FnMut(TestEvent) -> io::Result<()>, { @@ -325,7 +329,7 @@ where _ => { // We've got a result, stop the loop. break; - } + } } } else { res = rx.recv().map_err(|_| RecvTimeoutError::Disconnected); diff --git a/src/libtest/test_result.rs b/src/libtest/test_result.rs index dd4dfd9997f0..80ca9dea18f5 100644 --- a/src/libtest/test_result.rs +++ b/src/libtest/test_result.rs @@ -1,4 +1,3 @@ - use std::any::Any; use super::bench::BenchSamples; @@ -50,11 +49,15 @@ pub fn calc_result<'a>( if desc.allow_fail { TestResult::TrAllowedFail } else { - TestResult::TrFailedMsg(format!("panic did not include expected string '{}'", msg)) + TestResult::TrFailedMsg( + format!("panic did not include expected string '{}'", msg) + ) } } } - (&ShouldPanic::Yes, Ok(())) => TestResult::TrFailedMsg("test did not panic as expected".to_string()), + (&ShouldPanic::Yes, Ok(())) => { + TestResult::TrFailedMsg("test did not panic as expected".to_string()) + } _ if desc.allow_fail => TestResult::TrAllowedFail, _ => TestResult::TrFailed, }; diff --git a/src/libtest/tests.rs b/src/libtest/tests.rs index f6470b40a391..9de774555e9c 100644 --- a/src/libtest/tests.rs +++ b/src/libtest/tests.rs @@ -7,11 +7,12 @@ use crate::{ time::{TimeThreshold, TestTimeOptions}, formatters::PrettyFormatter, test::{ - filter_tests, parse_opts, run_test, DynTestFn, DynTestName, MetricMap, RunIgnored, RunStrategy, + filter_tests, parse_opts, run_test, DynTestFn, DynTestName, MetricMap, + RunIgnored, RunStrategy, ShouldPanic, StaticTestName, TestDesc, + TestDescAndFn, TestOpts, TrIgnored, TrOk, + // FIXME (introduced by #65251) // ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts, TestTimeOptions, // TestType, TrFailedMsg, TrIgnored, TrOk, - ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts, - TrIgnored, TrOk, }, }; use std::sync::mpsc::channel; @@ -104,7 +105,7 @@ pub fn ignored_tests_result_in_ignored() { assert!(result == TrIgnored); } -// FIXME: Re-enable emscripten once it can catch panics again +// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] #[cfg(not(target_os = "emscripten"))] fn test_should_panic() { @@ -127,7 +128,7 @@ fn test_should_panic() { assert!(result == TrOk); } -// FIXME: Re-enable emscripten once it can catch panics again +// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] #[cfg(not(target_os = "emscripten"))] fn test_should_panic_good_message() { @@ -150,7 +151,7 @@ fn test_should_panic_good_message() { assert!(result == TrOk); } -// FIXME: Re-enable emscripten once it can catch panics again +// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] #[cfg(not(target_os = "emscripten"))] fn test_should_panic_bad_message() { @@ -176,7 +177,7 @@ fn test_should_panic_bad_message() { assert!(result == TrFailedMsg(format!("{} '{}'", failed_msg, expected))); } -// FIXME: Re-enable emscripten once it can catch panics again +// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] #[cfg(not(target_os = "emscripten"))] fn test_should_panic_but_succeeds() { diff --git a/src/libtest/time.rs b/src/libtest/time.rs index 83a545470efa..f4d4b17b620b 100644 --- a/src/libtest/time.rs +++ b/src/libtest/time.rs @@ -2,7 +2,7 @@ //! execution. //! Two main purposes of this module: //! - Check whether test is timed out. -//! - Provide helpers for `report-time` and `measure-time` options. +//! - Provide helpers for `report-time` and `measure-time` options. use std::time::{Duration, Instant}; use std::str::FromStr; @@ -55,7 +55,7 @@ pub mod time_constants { } /// Returns an `Instance` object denoting when the test should be considered -/// timed out. +/// timed out. pub fn get_default_test_timeout() -> Instant { Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S) } From a06b205177e714fc68fec2a2b0efe5b57d4eb865 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Fri, 18 Oct 2019 08:32:44 +0300 Subject: [PATCH 34/79] Add public re-exports for benches --- src/libtest/bench.rs | 3 ++- src/libtest/lib.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libtest/bench.rs b/src/libtest/bench.rs index bb5b0d1da533..c142c5213d2e 100644 --- a/src/libtest/bench.rs +++ b/src/libtest/bench.rs @@ -1,4 +1,6 @@ //! Benchmarking module. +pub use std::hint::black_box; + use super::{ event::CompletedTest, helpers::sink::Sink, @@ -14,7 +16,6 @@ use std::cmp; use std::io; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::sync::{Arc, Mutex}; -use std::hint::black_box; /// Manager of the benchmarking runs. /// diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 89f527b6bd76..179558e8f9a1 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -35,6 +35,7 @@ pub use self::ColorConfig::*; pub use self::types::*; pub use self::types::TestName::*; pub use self::options::{Options, ShouldPanic}; +pub use self::bench::{Bencher, black_box}; // Module to be used by rustc to compile tests in libtest pub mod test { @@ -67,6 +68,7 @@ use std::{ }; pub mod stats; +pub mod bench; mod formatters; mod cli; mod console; @@ -75,7 +77,6 @@ mod helpers; mod time; mod types; mod options; -mod bench; mod test_result; #[cfg(test)] From 23cb1d520bbe943b9dfae54c6a8f2f1fe4748872 Mon Sep 17 00:00:00 2001 From: Shotaro Yamada Date: Fri, 18 Oct 2019 16:10:13 +0900 Subject: [PATCH 35/79] Avoid realloc in `CString::new` --- src/librustc_codegen_llvm/back/lto.rs | 4 +--- src/libstd/ffi/c_str.rs | 26 +++++++++++++++++++++++++- src/libstd/lib.rs | 1 + 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index 7437b1e3c8a3..b3be3d09f17d 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -53,9 +53,7 @@ fn prepare_lto(cgcx: &CodegenContext, let symbol_filter = &|&(ref name, level): &(String, SymbolExportLevel)| { if level.is_below_threshold(export_threshold) { - let mut bytes = Vec::with_capacity(name.len() + 1); - bytes.extend(name.bytes()); - Some(CString::new(bytes).unwrap()) + Some(CString::new(name.as_str()).unwrap()) } else { None } diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 483f2ba52eca..6dcda9863101 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -327,7 +327,31 @@ impl CString { /// [`NulError`]: struct.NulError.html #[stable(feature = "rust1", since = "1.0.0")] pub fn new>>(t: T) -> Result { - Self::_new(t.into()) + trait SpecIntoVec { + fn into_vec(self) -> Vec; + } + impl>> SpecIntoVec for T { + default fn into_vec(self) -> Vec { + self.into() + } + } + // Specialization for avoiding reallocation. + impl SpecIntoVec for &'_ [u8] { + fn into_vec(self) -> Vec { + let mut v = Vec::with_capacity(self.len() + 1); + v.extend(self); + v + } + } + impl SpecIntoVec for &'_ str { + fn into_vec(self) -> Vec { + let mut v = Vec::with_capacity(self.len() + 1); + v.extend(self.as_bytes()); + v + } + } + + Self::_new(SpecIntoVec::into_vec(t)) } fn _new(bytes: Vec) -> Result { diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index af6cb656444d..50a1226cc1d0 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -297,6 +297,7 @@ #![feature(slice_concat_ext)] #![feature(slice_internals)] #![feature(slice_patterns)] +#![feature(specialization)] #![feature(staged_api)] #![feature(std_internals)] #![feature(stdsimd)] From 52c771377f865f04940c294815309e6005663e88 Mon Sep 17 00:00:00 2001 From: Yves Dorfsman Date: Thu, 17 Oct 2019 23:07:29 -0600 Subject: [PATCH 36/79] Added doc on keyword break --- src/libstd/keyword_docs.rs | 66 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs index a8dfe924fdf0..7d678ee3879a 100644 --- a/src/libstd/keyword_docs.rs +++ b/src/libstd/keyword_docs.rs @@ -33,9 +33,71 @@ mod as_keyword { } // /// Exit early from a loop. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// When `break` is encountered, execution of the associated loop body is +/// immediately terminated. +/// +/// ```rust +/// let mut last = 0; +/// +/// for x in 1..100 { +/// if x > 12 { +/// break; +/// } +/// last = x; +/// } +/// +/// assert_eq!(last, 12); +/// println!("{}", last); +/// ``` +/// +/// A break expression is normally associated with the innermost loop enclosing the +/// `break` but a label can be used to specify which enclosing loop is affected. +/// +///```rust +/// 'outer: for i in 1..=5 { +/// println!("outer iteration (i): {}", i); +/// +/// 'inner: for j in 1..=200 { +/// println!(" inner iteration (j): {}", j); +/// if j >= 3 { +/// // breaks from inner loop, let's outer loop continue. +/// break; +/// } +/// if i >= 2 { +/// // breaks from outer loop, and directly to "Bye". +/// break 'outer; +/// } +/// } +/// } +/// println!("Bye."); +///``` +/// +/// When associated with `loop`, but not with any other kind of loop expression, +/// `break` can return a value. When no value is specified, `break;` returns `()`. +/// Every `break` within a loop must return the same type. +/// +/// ```rust +/// let (mut a, mut b) = (1, 1); +/// let result = loop { +/// if b > 10 { +/// break b; +/// } +/// let c = a + b; +/// a = b; +/// b = c; +/// }; +/// // first number in Fibonacci sequence over 10: +/// assert_eq!(result, 13); +/// println!("{}", result); +/// ``` +/// +/// For more details consult the [Reference on "break expression"] and the [Reference on "break and +/// loop values"]. +/// +/// [Reference on "break expression"]: ../reference/expressions/loop-expr.html#break-expressions +/// [Reference on "break and loop values"]: +/// ../reference/expressions/loop-expr.html#break-and-loop-values /// -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 mod break_keyword { } #[doc(keyword = "const")] From 11214a63125efc187ef0c3acd90f8f8cf7c93dc6 Mon Sep 17 00:00:00 2001 From: Yves Dorfsman Date: Fri, 18 Oct 2019 19:49:09 -0600 Subject: [PATCH 37/79] reworded loop value sentence --- src/libstd/keyword_docs.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs index 7d678ee3879a..ab8a55660cb0 100644 --- a/src/libstd/keyword_docs.rs +++ b/src/libstd/keyword_docs.rs @@ -72,8 +72,9 @@ mod as_keyword { } /// println!("Bye."); ///``` /// -/// When associated with `loop`, but not with any other kind of loop expression, -/// `break` can return a value. When no value is specified, `break;` returns `()`. +/// When associated with `loop`, a break expression may be used to return a value from that loop. +/// This is only valid with `loop` and not with any other type of loop. +/// If no value is specified, `break;` returns `()`. /// Every `break` within a loop must return the same type. /// /// ```rust From d945f9860f7ccfe45516c0ee2085e31c8767b0a4 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Fri, 11 Oct 2019 23:48:16 +0200 Subject: [PATCH 38/79] move parse_cfgspecs to rustc_interface --- Cargo.lock | 1 + src/librustc/session/config.rs | 65 +--------------- src/librustc_driver/lib.rs | 2 +- src/librustc_interface/Cargo.toml | 1 + src/librustc_interface/interface.rs | 63 ++++++++++++++- src/librustc_interface/lib.rs | 3 + .../config => librustc_interface}/tests.rs | 76 +++++++++---------- src/librustdoc/core.rs | 2 +- src/librustdoc/test.rs | 2 +- 9 files changed, 108 insertions(+), 107 deletions(-) rename src/{librustc/session/config => librustc_interface}/tests.rs (93%) diff --git a/Cargo.lock b/Cargo.lock index a5a7521abde1..ebe0807611ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3556,6 +3556,7 @@ dependencies = [ "rustc_plugin_impl", "rustc_privacy", "rustc_resolve", + "rustc_target", "rustc_traits", "rustc_typeck", "serialize", diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 675e3bbd002b..651f9393a31b 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -7,24 +7,19 @@ use crate::session::{early_error, early_warn, Session}; use crate::session::search_paths::SearchPath; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync::Lrc; use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel}; use rustc_target::spec::{Target, TargetTriple}; use syntax; -use syntax::ast::{self, IntTy, UintTy, MetaItemKind}; +use syntax::ast::{self, IntTy, UintTy}; use syntax::source_map::{FileName, FilePathMapping}; use syntax::edition::{Edition, EDITION_NAME_LIST, DEFAULT_EDITION}; -use syntax::parse::new_parser_from_source_str; -use syntax::parse::token; -use syntax::sess::ParseSess; use syntax::symbol::{sym, Symbol}; use syntax::feature_gate::UnstableFeatures; -use syntax::source_map::SourceMap; use errors::emitter::HumanReadableErrorType; -use errors::{ColorConfig, FatalError, Handler, SourceMapperDyn}; +use errors::{ColorConfig, FatalError, Handler}; use getopts; @@ -1854,59 +1849,6 @@ pub fn rustc_optgroups() -> Vec { opts } -struct NullEmitter; - -impl errors::emitter::Emitter for NullEmitter { - fn emit_diagnostic(&mut self, _: &errors::Diagnostic) {} - fn source_map(&self) -> Option<&Lrc> { None } -} - -// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`. -pub fn parse_cfgspecs(cfgspecs: Vec) -> FxHashSet<(String, Option)> { - syntax::with_default_globals(move || { - let cfg = cfgspecs.into_iter().map(|s| { - - let cm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let handler = Handler::with_emitter(false, None, Box::new(NullEmitter)); - let sess = ParseSess::with_span_handler(handler, cm); - let filename = FileName::cfg_spec_source_code(&s); - let mut parser = new_parser_from_source_str(&sess, filename, s.to_string()); - - macro_rules! error {($reason: expr) => { - early_error(ErrorOutputType::default(), - &format!(concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), s)); - }} - - match &mut parser.parse_meta_item() { - Ok(meta_item) if parser.token == token::Eof => { - if meta_item.path.segments.len() != 1 { - error!("argument key must be an identifier"); - } - match &meta_item.kind { - MetaItemKind::List(..) => { - error!(r#"expected `key` or `key="value"`"#); - } - MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { - error!("argument value must be a string"); - } - MetaItemKind::NameValue(..) | MetaItemKind::Word => { - let ident = meta_item.ident().expect("multi-segment cfg key"); - return (ident.name, meta_item.value_str()); - } - } - } - Ok(..) => {} - Err(err) => err.cancel(), - } - - error!(r#"expected `key` or `key="value"`"#); - }).collect::(); - cfg.into_iter().map(|(a, b)| { - (a.to_string(), b.map(|b| b.to_string())) - }).collect() - }) -} - pub fn get_cmd_lint_options(matches: &getopts::Matches, error_format: ErrorOutputType) -> (Vec<(String, lint::Level)>, bool, Option) { @@ -2877,6 +2819,3 @@ mod dep_tracking { } } } - -#[cfg(test)] -mod tests; diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index f33cb4e215d3..bfcbec8b78f3 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -167,7 +167,7 @@ pub fn run_compiler( }; let sopts = config::build_session_options(&matches); - let cfg = config::parse_cfgspecs(matches.opt_strs("cfg")); + let cfg = interface::parse_cfgspecs(matches.opt_strs("cfg")); let mut dummy_config = |sopts, cfg, diagnostic_output| { let mut config = interface::Config { diff --git a/src/librustc_interface/Cargo.toml b/src/librustc_interface/Cargo.toml index 7a0d931ca732..0d8d765a572c 100644 --- a/src/librustc_interface/Cargo.toml +++ b/src/librustc_interface/Cargo.toml @@ -27,6 +27,7 @@ rustc_codegen_utils = { path = "../librustc_codegen_utils" } rustc_metadata = { path = "../librustc_metadata" } rustc_mir = { path = "../librustc_mir" } rustc_passes = { path = "../librustc_passes" } +rustc_target = { path = "../librustc_target" } rustc_typeck = { path = "../librustc_typeck" } rustc_lint = { path = "../librustc_lint" } rustc_errors = { path = "../librustc_errors" } diff --git a/src/librustc_interface/interface.rs b/src/librustc_interface/interface.rs index b26bd75c974c..5e1ad3e61dd1 100644 --- a/src/librustc_interface/interface.rs +++ b/src/librustc_interface/interface.rs @@ -3,7 +3,8 @@ use crate::util; pub use crate::passes::BoxedResolver; use rustc::lint; -use rustc::session::config::{self, Input}; +use rustc::session::early_error; +use rustc::session::config::{self, Input, ErrorOutputType}; use rustc::session::{DiagnosticOutput, Session}; use rustc::util::common::ErrorReported; use rustc_codegen_utils::codegen_backend::CodegenBackend; @@ -14,9 +15,13 @@ use rustc_metadata::cstore::CStore; use std::path::PathBuf; use std::result; use std::sync::{Arc, Mutex}; -use syntax; -use syntax::source_map::{FileLoader, SourceMap}; +use syntax::{self, parse}; +use syntax::ast::{self, MetaItemKind}; +use syntax::parse::token; +use syntax::source_map::{FileName, FilePathMapping, FileLoader, SourceMap}; +use syntax::sess::ParseSess; use syntax_pos::edition; +use rustc_errors::{Diagnostic, emitter::Emitter, Handler, SourceMapperDyn}; pub type Result = result::Result; @@ -60,6 +65,58 @@ impl Compiler { } } +/// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`. +pub fn parse_cfgspecs(cfgspecs: Vec) -> FxHashSet<(String, Option)> { + struct NullEmitter; + impl Emitter for NullEmitter { + fn emit_diagnostic(&mut self, _: &Diagnostic) {} + fn source_map(&self) -> Option<&Lrc> { None } + } + + syntax::with_default_globals(move || { + let cfg = cfgspecs.into_iter().map(|s| { + + let cm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let handler = Handler::with_emitter(false, None, Box::new(NullEmitter)); + let sess = ParseSess::with_span_handler(handler, cm); + let filename = FileName::cfg_spec_source_code(&s); + let mut parser = parse::new_parser_from_source_str(&sess, filename, s.to_string()); + + macro_rules! error {($reason: expr) => { + early_error(ErrorOutputType::default(), + &format!(concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), s)); + }} + + match &mut parser.parse_meta_item() { + Ok(meta_item) if parser.token == token::Eof => { + if meta_item.path.segments.len() != 1 { + error!("argument key must be an identifier"); + } + match &meta_item.kind { + MetaItemKind::List(..) => { + error!(r#"expected `key` or `key="value"`"#); + } + MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { + error!("argument value must be a string"); + } + MetaItemKind::NameValue(..) | MetaItemKind::Word => { + let ident = meta_item.ident().expect("multi-segment cfg key"); + return (ident.name, meta_item.value_str()); + } + } + } + Ok(..) => {} + Err(err) => err.cancel(), + } + + error!(r#"expected `key` or `key="value"`"#); + }).collect::(); + cfg.into_iter().map(|(a, b)| { + (a.to_string(), b.map(|b| b.to_string())) + }).collect() + }) +} + /// The compiler configuration pub struct Config { /// Command line options diff --git a/src/librustc_interface/lib.rs b/src/librustc_interface/lib.rs index 6be36e9b9001..53baf6556fb5 100644 --- a/src/librustc_interface/lib.rs +++ b/src/librustc_interface/lib.rs @@ -18,3 +18,6 @@ pub mod util; mod proc_macro_decls; pub use interface::{run_compiler, Config}; + +#[cfg(test)] +mod tests; diff --git a/src/librustc/session/config/tests.rs b/src/librustc_interface/tests.rs similarity index 93% rename from src/librustc/session/config/tests.rs rename to src/librustc_interface/tests.rs index 061bbdc307fc..7a57605da58b 100644 --- a/src/librustc/session/config/tests.rs +++ b/src/librustc_interface/tests.rs @@ -1,25 +1,24 @@ -use getopts; -use crate::lint; -use crate::middle::cstore; -use crate::session::config::{ - build_configuration, - build_session_options, - to_crate_config, - parse_cfgspecs, -}; -use crate::session::config::{LtoCli, LinkerPluginLto, SwitchWithOptPath, ExternEntry}; -use crate::session::build_session; -use crate::session::search_paths::SearchPath; +extern crate getopts; + +use crate::interface::parse_cfgspecs; + +use rustc::lint; +use rustc::middle::cstore; +use rustc::session::config::{build_configuration, build_session_options, to_crate_config}; +use rustc::session::config::{LtoCli, LinkerPluginLto, SwitchWithOptPath, ExternEntry}; +use rustc::session::config::{Externs, OutputType, OutputTypes, SymbolManglingVersion}; +use rustc::session::config::{rustc_optgroups, Options, ErrorOutputType, Passes}; +use rustc::session::build_session; +use rustc::session::search_paths::SearchPath; use std::collections::{BTreeMap, BTreeSet}; use std::iter::FromIterator; use std::path::PathBuf; -use super::{Externs, OutputType, OutputTypes, SymbolManglingVersion}; use rustc_target::spec::{MergeFunctions, PanicStrategy, RelroLevel}; use syntax::symbol::sym; use syntax::edition::{Edition, DEFAULT_EDITION}; use syntax; -use super::Options; use rustc_data_structures::fx::FxHashSet; +use rustc_errors::{ColorConfig, emitter::HumanReadableErrorType, registry}; pub fn build_session_options_and_crate_config( matches: &getopts::Matches, @@ -30,22 +29,23 @@ pub fn build_session_options_and_crate_config( ) } -impl ExternEntry { - fn new_public, - I: IntoIterator>>(locations: I) -> ExternEntry { - let locations: BTreeSet<_> = locations.into_iter().map(|o| o.map(|s| s.into())) - .collect(); +fn new_public_extern_entry(locations: I) -> ExternEntry +where + S: Into, + I: IntoIterator>, +{ + let locations: BTreeSet<_> = locations.into_iter().map(|o| o.map(|s| s.into())) + .collect(); - ExternEntry { - locations, - is_private_dep: false - } + ExternEntry { + locations, + is_private_dep: false } } fn optgroups() -> getopts::Options { let mut opts = getopts::Options::new(); - for group in super::rustc_optgroups() { + for group in rustc_optgroups() { (group.apply)(&mut opts); } return opts; @@ -63,7 +63,7 @@ fn test_switch_implies_cfg_test() { Ok(m) => m, Err(f) => panic!("test_switch_implies_cfg_test: {}", f), }; - let registry = errors::registry::Registry::new(&[]); + let registry = registry::Registry::new(&[]); let (sessopts, cfg) = build_session_options_and_crate_config(matches); let sess = build_session(sessopts, None, registry); let cfg = build_configuration(&sess, to_crate_config(cfg)); @@ -81,7 +81,7 @@ fn test_switch_implies_cfg_test_unless_cfg_test() { Ok(m) => m, Err(f) => panic!("test_switch_implies_cfg_test_unless_cfg_test: {}", f), }; - let registry = errors::registry::Registry::new(&[]); + let registry = registry::Registry::new(&[]); let (sessopts, cfg) = build_session_options_and_crate_config(matches); let sess = build_session(sessopts, None, registry); let cfg = build_configuration(&sess, to_crate_config(cfg)); @@ -95,7 +95,7 @@ fn test_switch_implies_cfg_test_unless_cfg_test() { fn test_can_print_warnings() { syntax::with_default_globals(|| { let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap(); - let registry = errors::registry::Registry::new(&[]); + let registry = registry::Registry::new(&[]); let (sessopts, _) = build_session_options_and_crate_config(&matches); let sess = build_session(sessopts, None, registry); assert!(!sess.diagnostic().can_emit_warnings()); @@ -105,7 +105,7 @@ fn test_can_print_warnings() { let matches = optgroups() .parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]) .unwrap(); - let registry = errors::registry::Registry::new(&[]); + let registry = registry::Registry::new(&[]); let (sessopts, _) = build_session_options_and_crate_config(&matches); let sess = build_session(sessopts, None, registry); assert!(sess.diagnostic().can_emit_warnings()); @@ -113,7 +113,7 @@ fn test_can_print_warnings() { syntax::with_default_globals(|| { let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap(); - let registry = errors::registry::Registry::new(&[]); + let registry = registry::Registry::new(&[]); let (sessopts, _) = build_session_options_and_crate_config(&matches); let sess = build_session(sessopts, None, registry); assert!(sess.diagnostic().can_emit_warnings()); @@ -172,33 +172,33 @@ fn test_externs_tracking_hash_different_construction_order() { v1.externs = Externs::new(mk_map(vec![ ( String::from("a"), - ExternEntry::new_public(vec![Some("b"), Some("c")]) + new_public_extern_entry(vec![Some("b"), Some("c")]) ), ( String::from("d"), - ExternEntry::new_public(vec![Some("e"), Some("f")]) + new_public_extern_entry(vec![Some("e"), Some("f")]) ), ])); v2.externs = Externs::new(mk_map(vec![ ( String::from("d"), - ExternEntry::new_public(vec![Some("e"), Some("f")]) + new_public_extern_entry(vec![Some("e"), Some("f")]) ), ( String::from("a"), - ExternEntry::new_public(vec![Some("b"), Some("c")]) + new_public_extern_entry(vec![Some("b"), Some("c")]) ), ])); v3.externs = Externs::new(mk_map(vec![ ( String::from("a"), - ExternEntry::new_public(vec![Some("b"), Some("c")]) + new_public_extern_entry(vec![Some("b"), Some("c")]) ), ( String::from("d"), - ExternEntry::new_public(vec![Some("f"), Some("e")]) + new_public_extern_entry(vec![Some("f"), Some("e")]) ), ])); @@ -282,9 +282,9 @@ fn test_search_paths_tracking_hash_different_order() { let mut v3 = Options::default(); let mut v4 = Options::default(); - const JSON: super::ErrorOutputType = super::ErrorOutputType::Json { + const JSON: ErrorOutputType = ErrorOutputType::Json { pretty: false, - json_rendered: super::HumanReadableErrorType::Default(super::ColorConfig::Never), + json_rendered: HumanReadableErrorType::Default(ColorConfig::Never), }; // Reference @@ -455,7 +455,7 @@ fn test_codegen_options_tracking_hash() { opts.cg.codegen_units = Some(42); assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.cg.remark = super::Passes::Some(vec![String::from("pass1"), String::from("pass2")]); + opts.cg.remark = Passes::Some(vec![String::from("pass1"), String::from("pass2")]); assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.cg.save_temps = true; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 00265caa965e..2337ec5a52cc 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -329,7 +329,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let config = interface::Config { opts: sessopts, - crate_cfg: config::parse_cfgspecs(cfgs), + crate_cfg: interface::parse_cfgspecs(cfgs), input, input_path: cpath, output_file: None, diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 0be6340df96e..01b897ab3644 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -67,7 +67,7 @@ pub fn run(options: Options) -> i32 { cfgs.push("doctest".to_owned()); let config = interface::Config { opts: sessopts, - crate_cfg: config::parse_cfgspecs(cfgs), + crate_cfg: interface::parse_cfgspecs(cfgs), input, input_path: None, output_file: None, From 282403e6bd0f858474c47fe2b9efd50645023c7c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Oct 2019 11:46:56 +0200 Subject: [PATCH 39/79] clarify const_prop ICE protection comment --- src/librustc_mir/transform/const_prop.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index f0c0e5734438..223330a3ecb4 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -518,12 +518,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - // Work around: avoid ICE in miri. - // FIXME(wesleywiser) we don't currently handle the case where we try to make a ref - // from a function argument that hasn't been assigned to in this function. The main - // issue is if an arg is a fat-pointer, miri `expects()` to be able to read the value - // of that pointer to get size info. However, since this is `ConstProp`, that argument - // doesn't actually have a backing value and so this causes an ICE. + // Work around: avoid ICE in miri. FIXME(wesleywiser) + // The Miri engine ICEs when taking a reference to an uninitialized unsized + // local. There's nothing it can do here: taking a reference needs an allocation + // which needs to know the size. Normally that's okay as during execution + // (e.g. for CTFE) it can never happen. But here in const_prop + // we leave function arguments uninitialized, so if one of these is unsized + // and has a reference taken, we get an ICE. Rvalue::Ref(_, _, Place { base: PlaceBase::Local(local), projection: box [] }) => { trace!("checking Ref({:?})", place); let alive = @@ -531,14 +532,15 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { true } else { false }; + // local 0 is the return place; locals 1..=arg_count are the arguments. if local.as_usize() <= self.ecx.frame().body.arg_count && !alive { trace!("skipping Ref({:?})", place); return None; } } - // Work around: avoid extra unnecessary locals. - // FIXME(wesleywiser): const eval will turn this into a `const Scalar()` that + // Work around: avoid extra unnecessary locals. FIXME(wesleywiser) + // Const eval will turn this into a `const Scalar()` that // `SimplifyLocals` doesn't know it can remove. Rvalue::Aggregate(_, operands) if operands.len() == 0 => { return None; From 7f1ce451401b21155923174480f8be9e0b8cf499 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Oct 2019 13:13:31 +0200 Subject: [PATCH 40/79] add test for calling non-const fn --- src/test/ui/consts/miri_unleashed/non_const_fn.rs | 12 ++++++++++++ .../ui/consts/miri_unleashed/non_const_fn.stderr | 15 +++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/test/ui/consts/miri_unleashed/non_const_fn.rs create mode 100644 src/test/ui/consts/miri_unleashed/non_const_fn.stderr diff --git a/src/test/ui/consts/miri_unleashed/non_const_fn.rs b/src/test/ui/consts/miri_unleashed/non_const_fn.rs new file mode 100644 index 000000000000..01ce6b904e37 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/non_const_fn.rs @@ -0,0 +1,12 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you +#![allow(const_err)] + +// A test demonstrating that we prevent calling non-const fn during CTFE. + +fn foo() {} + +const C: () = foo(); //~ WARN: skipping const checks + +fn main() { + println!("{:?}", C); //~ ERROR: evaluation of constant expression failed +} diff --git a/src/test/ui/consts/miri_unleashed/non_const_fn.stderr b/src/test/ui/consts/miri_unleashed/non_const_fn.stderr new file mode 100644 index 000000000000..ad8154bd2c68 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/non_const_fn.stderr @@ -0,0 +1,15 @@ +warning: skipping const checks + --> $DIR/non_const_fn.rs:8:15 + | +LL | const C: () = foo(); + | ^^^^^ + +error[E0080]: evaluation of constant expression failed + --> $DIR/non_const_fn.rs:11:22 + | +LL | println!("{:?}", C); + | ^ referenced constant has errors + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. From 38fd74f22af7eec30d11d421bcbeaa891c7f7436 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Oct 2019 15:52:08 +0200 Subject: [PATCH 41/79] show the proper diagnostics --- .../ui/consts/miri_unleashed/non_const_fn.rs | 3 ++- .../ui/consts/miri_unleashed/non_const_fn.stderr | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/test/ui/consts/miri_unleashed/non_const_fn.rs b/src/test/ui/consts/miri_unleashed/non_const_fn.rs index 01ce6b904e37..e1ac4306575d 100644 --- a/src/test/ui/consts/miri_unleashed/non_const_fn.rs +++ b/src/test/ui/consts/miri_unleashed/non_const_fn.rs @@ -1,11 +1,12 @@ // compile-flags: -Zunleash-the-miri-inside-of-you -#![allow(const_err)] +#![warn(const_err)] // A test demonstrating that we prevent calling non-const fn during CTFE. fn foo() {} const C: () = foo(); //~ WARN: skipping const checks +//~^ WARN any use of this value will cause an error fn main() { println!("{:?}", C); //~ ERROR: evaluation of constant expression failed diff --git a/src/test/ui/consts/miri_unleashed/non_const_fn.stderr b/src/test/ui/consts/miri_unleashed/non_const_fn.stderr index ad8154bd2c68..7a574b34304c 100644 --- a/src/test/ui/consts/miri_unleashed/non_const_fn.stderr +++ b/src/test/ui/consts/miri_unleashed/non_const_fn.stderr @@ -4,8 +4,22 @@ warning: skipping const checks LL | const C: () = foo(); | ^^^^^ +warning: any use of this value will cause an error + --> $DIR/non_const_fn.rs:8:15 + | +LL | const C: () = foo(); + | --------------^^^^^- + | | + | calling non-const function `foo` + | +note: lint level defined here + --> $DIR/non_const_fn.rs:2:9 + | +LL | #![warn(const_err)] + | ^^^^^^^^^ + error[E0080]: evaluation of constant expression failed - --> $DIR/non_const_fn.rs:11:22 + --> $DIR/non_const_fn.rs:12:22 | LL | println!("{:?}", C); | ^ referenced constant has errors From f704c099ebd51e2fe34209d5a733501f317e37b8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Oct 2019 17:15:12 +0200 Subject: [PATCH 42/79] bump miri --- src/tools/miri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri b/src/tools/miri index 2adc39f27b7f..fccb23982488 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit 2adc39f27b7fd2d06b3d1d470827928766731a1d +Subproject commit fccb2398248802a268fcda544ff3945247ef2119 From 5192daf43e8f913ed1c49322630fbb84ac00d8df Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Oct 2019 17:17:02 +0200 Subject: [PATCH 43/79] adjust miri sysroot determination --- src/bootstrap/test.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index e09e25de64a3..0b7aa0750a19 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -427,7 +427,7 @@ impl Step for Miri { // (We do this separately from the above so that when the setup actually // happens we get some output.) // We re-use the `cargo` from above. - cargo.arg("--env"); + cargo.arg("--print-sysroot"); // FIXME: Is there a way in which we can re-use the usual `run` helpers? let miri_sysroot = if builder.config.dry_run { @@ -437,13 +437,11 @@ impl Step for Miri { let out = cargo.output() .expect("We already ran `cargo miri setup` before and that worked"); assert!(out.status.success(), "`cargo miri setup` returned with non-0 exit code"); - // Output is "MIRI_SYSROOT=\n". + // Output is "\n". let stdout = String::from_utf8(out.stdout) .expect("`cargo miri setup` stdout is not valid UTF-8"); - let stdout = stdout.trim(); - builder.verbose(&format!("`cargo miri setup --env` returned: {:?}", stdout)); - let sysroot = stdout.splitn(2, '=') - .nth(1).expect("`cargo miri setup` stdout did not contain '='"); + let sysroot = stdout.trim_end(); + builder.verbose(&format!("`cargo miri setup --print-sysroot` said: {:?}", sysroot)); sysroot.to_owned() }; From 4ea1a1983e228959c72e3e121c56808160ef795d Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" <193874+carols10cents@users.noreply.github.com> Date: Sat, 19 Oct 2019 11:20:05 -0400 Subject: [PATCH 44/79] Remove unneeded `ref` from docs Will reduce confusion like in https://users.rust-lang.org/t/help-understanding-the-ref-t-syntax/33779 since match ergonomics means you (almost) never have to say `ref` anymore! --- src/libcore/option.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 47e3a0d21676..09d4076c7fbc 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -64,7 +64,7 @@ //! //! fn check_optional(optional: Option>) { //! match optional { -//! Some(ref p) => println!("has value {}", p), +//! Some(p) => println!("has value {}", p), //! None => println!("has no value"), //! } //! } @@ -83,7 +83,7 @@ //! let msg = Some("howdy"); //! //! // Take a reference to the contained string -//! if let Some(ref m) = msg { +//! if let Some(m) = &msg { //! println!("{}", *m); //! } //! From f042687959ef15f9c45d7093cd6b01f137da743f Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 19 Oct 2019 18:08:54 +0100 Subject: [PATCH 45/79] Fix plural mistake in emitter.rs --- src/librustc_errors/emitter.rs | 5 +++-- src/test/ui/issues/issue-17546.stderr | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 1e486786f686..e3b55a141338 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -12,7 +12,7 @@ use Destination::*; use syntax_pos::{SourceFile, Span, MultiSpan}; use crate::{ - Level, CodeSuggestion, Diagnostic, SubDiagnostic, + Level, CodeSuggestion, Diagnostic, SubDiagnostic, pluralise, SuggestionStyle, SourceMapper, SourceMapperDyn, DiagnosticId, }; use crate::Level::Error; @@ -1572,7 +1572,8 @@ impl EmitterWriter { } } if suggestions.len() > MAX_SUGGESTIONS { - let msg = format!("and {} other candidates", suggestions.len() - MAX_SUGGESTIONS); + let others = suggestions.len() - MAX_SUGGESTIONS; + let msg = format!("and {} other candidate{}", others, pluralise!(others)); buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle); } else if notice_capitalization { let msg = "notice the capitalization difference"; diff --git a/src/test/ui/issues/issue-17546.stderr b/src/test/ui/issues/issue-17546.stderr index 4a0fb186e49f..291086d4f694 100644 --- a/src/test/ui/issues/issue-17546.stderr +++ b/src/test/ui/issues/issue-17546.stderr @@ -27,7 +27,7 @@ LL | use std::prelude::v1::Result; | LL | use std::result::Result; | - and 1 other candidates + and 1 other candidate error[E0573]: expected type, found variant `Result` --> $DIR/issue-17546.rs:28:13 @@ -44,7 +44,7 @@ LL | use std::prelude::v1::Result; | LL | use std::result::Result; | - and 1 other candidates + and 1 other candidate error[E0573]: expected type, found variant `NoResult` --> $DIR/issue-17546.rs:33:15 From 9be0bd8aa110bc580fb5d0f59e4f4fc96068c616 Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Sat, 19 Oct 2019 13:05:46 -0400 Subject: [PATCH 46/79] Avoid ICE when include! is used by stdin crate This should also eliminate the ICE when using `include_bytes!`, `include_str!` and `#[doc(include = "...")]`. Fixes #63900 --- src/libsyntax_expand/base.rs | 15 +++++++++++---- src/libsyntax_expand/expand.rs | 9 ++++++++- src/libsyntax_ext/source_util.rs | 24 +++++++++++++++++++++--- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/libsyntax_expand/base.rs b/src/libsyntax_expand/base.rs index c222e7357ac7..58edf23a5b1e 100644 --- a/src/libsyntax_expand/base.rs +++ b/src/libsyntax_expand/base.rs @@ -1072,7 +1072,11 @@ impl<'a> ExtCtxt<'a> { /// This unifies the logic used for resolving `include_X!`, and `#[doc(include)]` file paths. /// /// Returns an absolute path to the file that `path` refers to. - pub fn resolve_path(&self, path: impl Into, span: Span) -> PathBuf { + pub fn resolve_path( + &self, + path: impl Into, + span: Span, + ) -> Result> { let path = path.into(); // Relative paths are resolved relative to the file in which they are found @@ -1082,13 +1086,16 @@ impl<'a> ExtCtxt<'a> { let mut result = match self.source_map().span_to_unmapped_path(callsite) { FileName::Real(path) => path, FileName::DocTest(path, _) => path, - other => panic!("cannot resolve relative path in non-file source `{}`", other), + other => return Err(self.struct_span_err( + span, + &format!("cannot resolve relative path in non-file source `{}`", other), + )), }; result.pop(); result.push(path); - result + Ok(result) } else { - path + Ok(path) } } } diff --git a/src/libsyntax_expand/expand.rs b/src/libsyntax_expand/expand.rs index f03d464eafb9..fc521e5edc06 100644 --- a/src/libsyntax_expand/expand.rs +++ b/src/libsyntax_expand/expand.rs @@ -1418,7 +1418,14 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { return noop_visit_attribute(at, self); } - let filename = self.cx.resolve_path(&*file.as_str(), it.span()); + let filename = match self.cx.resolve_path(&*file.as_str(), it.span()) { + Ok(filename) => filename, + Err(mut err) => { + err.emit(); + continue; + } + }; + match self.cx.source_map().load_file(&filename) { Ok(source_file) => { let src = source_file.src.as_ref() diff --git a/src/libsyntax_ext/source_util.rs b/src/libsyntax_ext/source_util.rs index 438e199ebdb8..f6c58fcdfa1c 100644 --- a/src/libsyntax_ext/source_util.rs +++ b/src/libsyntax_ext/source_util.rs @@ -76,7 +76,13 @@ pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream) None => return DummyResult::any(sp), }; // The file will be added to the code map by the parser - let file = cx.resolve_path(file, sp); + let file = match cx.resolve_path(file, sp) { + Ok(f) => f, + Err(mut err) => { + err.emit(); + return DummyResult::any(sp); + }, + }; let directory_ownership = DirectoryOwnership::Owned { relative: None }; let p = parse::new_sub_parser_from_file(cx.parse_sess(), &file, directory_ownership, None, sp); @@ -122,7 +128,13 @@ pub fn expand_include_str(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream) Some(f) => f, None => return DummyResult::any(sp) }; - let file = cx.resolve_path(file, sp); + let file = match cx.resolve_path(file, sp) { + Ok(f) => f, + Err(mut err) => { + err.emit(); + return DummyResult::any(sp); + }, + }; match cx.source_map().load_binary_file(&file) { Ok(bytes) => match std::str::from_utf8(&bytes) { Ok(src) => { @@ -147,7 +159,13 @@ pub fn expand_include_bytes(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream) Some(f) => f, None => return DummyResult::any(sp) }; - let file = cx.resolve_path(file, sp); + let file = match cx.resolve_path(file, sp) { + Ok(f) => f, + Err(mut err) => { + err.emit(); + return DummyResult::any(sp); + }, + }; match cx.source_map().load_binary_file(&file) { Ok(bytes) => { base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Lrc::new(bytes)))) From 2e36c84ebd48648b9c808f6e8dba5d8e49f47f3c Mon Sep 17 00:00:00 2001 From: Caleb Behunin Date: Sun, 8 Sep 2019 22:17:10 -0700 Subject: [PATCH 47/79] Remove unreachable unit tuple compare binop codegen --- src/librustc_codegen_ssa/mir/rvalue.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs index 27442bb6bff8..7e662ea37dbb 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/src/librustc_codegen_ssa/mir/rvalue.rs @@ -556,7 +556,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) -> Bx::Value { let is_float = input_ty.is_floating_point(); let is_signed = input_ty.is_signed(); - let is_unit = input_ty.is_unit(); match op { mir::BinOp::Add => if is_float { bx.fadd(lhs, rhs) @@ -594,13 +593,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::BinOp::Shl => common::build_unchecked_lshift(bx, lhs, rhs), mir::BinOp::Shr => common::build_unchecked_rshift(bx, input_ty, lhs, rhs), mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt | - mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => if is_unit { - bx.cx().const_bool(match op { - mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt => false, - mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => true, - _ => unreachable!() - }) - } else if is_float { + mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => if is_float { bx.fcmp( base::bin_op_to_fcmp_predicate(op.to_hir_binop()), lhs, rhs From 925e3042f659c4a79bb742503cdbbd4ee4a731a2 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 19 Oct 2019 11:57:48 +1300 Subject: [PATCH 48/79] Fix resolve_type_vars_with_obligations not resolving const inference variables. --- src/librustc/infer/resolve.rs | 6 +++--- src/librustc/ty/fold.rs | 3 +++ src/librustc_typeck/check/mod.rs | 4 ++-- .../const-argument-cross-crate-mismatch.stderr | 16 ++++++++-------- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index 2db18674e2f5..7c3a338366c9 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs @@ -1,7 +1,7 @@ use super::{InferCtxt, FixupError, FixupResult, Span}; use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::mir::interpret::ConstValue; -use crate::ty::{self, Ty, Const, TyCtxt, TypeFoldable, InferConst, TypeFlags}; +use crate::ty::{self, Ty, Const, TyCtxt, TypeFoldable, InferConst}; use crate::ty::fold::{TypeFolder, TypeVisitor}; /////////////////////////////////////////////////////////////////////////// @@ -29,7 +29,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.has_infer_types() { + if !t.has_infer_types() && !t.has_infer_consts() { t // micro-optimize -- if there is nothing in this type that this fold affects... } else { let t = self.infcx.shallow_resolve(t); @@ -38,7 +38,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { } fn fold_const(&mut self, ct: &'tcx Const<'tcx>) -> &'tcx Const<'tcx> { - if !ct.has_type_flags(TypeFlags::HAS_CT_INFER) { + if !ct.has_infer_consts() { ct // micro-optimize -- if there is nothing in this const that this fold affects... } else { let ct = self.infcx.shallow_resolve(ct); diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 5192075c26e9..a95ed589c3e2 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -88,6 +88,9 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn has_infer_types(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_INFER) } + fn has_infer_consts(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_CT_INFER) + } fn has_local_value(&self) -> bool { self.has_type_flags(TypeFlags::KEEP_IN_LOCAL_TCX) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 152edf8dd0e5..013282848226 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2448,14 +2448,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("resolve_type_vars_with_obligations(ty={:?})", ty); // No Infer()? Nothing needs doing. - if !ty.has_infer_types() { + if !ty.has_infer_types() && !ty.has_infer_consts() { debug!("resolve_type_vars_with_obligations: ty={:?}", ty); return ty; } // If `ty` is a type variable, see whether we already know what it is. ty = self.resolve_vars_if_possible(&ty); - if !ty.has_infer_types() { + if !ty.has_infer_types() && !ty.has_infer_consts() { debug!("resolve_type_vars_with_obligations: ty={:?}", ty); return ty; } diff --git a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.stderr b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.stderr index b7fd29ce7067..7090cb880fd4 100644 --- a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.stderr +++ b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.stderr @@ -1,20 +1,20 @@ error[E0308]: mismatched types - --> $DIR/const-argument-cross-crate-mismatch.rs:6:41 + --> $DIR/const-argument-cross-crate-mismatch.rs:6:67 | LL | let _ = const_generic_lib::function(const_generic_lib::Struct([0u8, 1u8])); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `3usize`, found `2usize` + | ^^^^^^^^^^ expected an array with a fixed size of 3 elements, found one with 2 elements | - = note: expected type `const_generic_lib::Struct<3usize>` - found type `const_generic_lib::Struct<_: usize>` + = note: expected type `[u8; 3]` + found type `[u8; 2]` error[E0308]: mismatched types - --> $DIR/const-argument-cross-crate-mismatch.rs:8:39 + --> $DIR/const-argument-cross-crate-mismatch.rs:8:65 | LL | let _: const_generic_lib::Alias = const_generic_lib::Struct([0u8, 1u8, 2u8]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2usize`, found `3usize` + | ^^^^^^^^^^^^^^^ expected an array with a fixed size of 2 elements, found one with 3 elements | - = note: expected type `const_generic_lib::Struct<2usize>` - found type `const_generic_lib::Struct<_: usize>` + = note: expected type `[u8; 2]` + found type `[u8; 3]` error: aborting due to 2 previous errors From 9cefcd3051ac7f4ea3c924bd7542c70c59ac5dfd Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 19 Oct 2019 12:04:16 +1300 Subject: [PATCH 49/79] Rename resolve_type_vars_with_obligations to resolve_vars_with_obligations, as it now also resolves const variables. --- src/librustc_typeck/check/coercion.rs | 10 +++++----- src/librustc_typeck/check/demand.rs | 4 ++-- src/librustc_typeck/check/expr.rs | 2 +- src/librustc_typeck/check/method/suggest.rs | 2 +- src/librustc_typeck/check/mod.rs | 18 +++++++++--------- src/librustc_typeck/check/op.rs | 4 ++-- src/librustc_typeck/check/pat.rs | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 3a89cddda236..bfccb032458f 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -811,7 +811,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { target: Ty<'tcx>, allow_two_phase: AllowTwoPhase, ) -> RelateResult<'tcx, Ty<'tcx>> { - let source = self.resolve_type_vars_with_obligations(expr_ty); + let source = self.resolve_vars_with_obligations(expr_ty); debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable); @@ -829,7 +829,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Same as `try_coerce()`, but without side-effects. pub fn can_coerce(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool { - let source = self.resolve_type_vars_with_obligations(expr_ty); + let source = self.resolve_vars_with_obligations(expr_ty); debug!("coercion::can({:?} -> {:?})", source, target); let cause = self.cause(syntax_pos::DUMMY_SP, ObligationCauseCode::ExprAssignable); @@ -853,8 +853,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { -> RelateResult<'tcx, Ty<'tcx>> where E: AsCoercionSite { - let prev_ty = self.resolve_type_vars_with_obligations(prev_ty); - let new_ty = self.resolve_type_vars_with_obligations(new_ty); + let prev_ty = self.resolve_vars_with_obligations(prev_ty); + let new_ty = self.resolve_vars_with_obligations(new_ty); debug!("coercion::try_find_coercion_lub({:?}, {:?})", prev_ty, new_ty); // Special-case that coercion alone cannot handle: @@ -1333,7 +1333,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { err.span_label(return_sp, "expected because this return type..."); err.span_label( *sp, format!( "...is found to be `{}` here", - fcx.resolve_type_vars_with_obligations(expected), + fcx.resolve_vars_with_obligations(expected), )); } err diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 677e2ea35662..3509d6566ec9 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -108,7 +108,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, allow_two_phase: AllowTwoPhase) -> (Ty<'tcx>, Option>) { - let expected = self.resolve_type_vars_with_obligations(expected); + let expected = self.resolve_vars_with_obligations(expected); let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase) { Ok(ty) => return (ty, None), @@ -117,7 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr = expr.peel_drop_temps(); let cause = self.misc(expr.span); - let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); + let expr_ty = self.resolve_vars_with_obligations(checked_ty); let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); if self.is_assign_to_bool(expr, expected) { diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index ad46a443b8ff..f5f85bbcb100 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1010,7 +1010,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr, ) -> Ty<'tcx> { let flds = expected.only_has_type(self).and_then(|ty| { - let ty = self.resolve_type_vars_with_obligations(ty); + let ty = self.resolve_vars_with_obligations(ty); match ty.kind { ty::Tuple(ref flds) => Some(&flds[..]), _ => None diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index f2d001eadedd..d90ed2a790bb 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -919,7 +919,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This occurs for UFCS desugaring of `T::method`, where there is no // receiver expression for the method call, and thus no autoderef. if let SelfSource::QPath(_) = source { - return is_local(self.resolve_type_vars_with_obligations(rcvr_ty)); + return is_local(self.resolve_vars_with_obligations(rcvr_ty)); } self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty)) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 013282848226..73f35dde4f6e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2440,23 +2440,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.cause(span, ObligationCauseCode::MiscObligation) } - /// Resolves type variables in `ty` if possible. Unlike the infcx + /// Resolves type and const variables in `ty` if possible. Unlike the infcx /// version (resolve_vars_if_possible), this version will /// also select obligations if it seems useful, in an effort /// to get more type information. - fn resolve_type_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> { - debug!("resolve_type_vars_with_obligations(ty={:?})", ty); + fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> { + debug!("resolve_vars_with_obligations(ty={:?})", ty); // No Infer()? Nothing needs doing. if !ty.has_infer_types() && !ty.has_infer_consts() { - debug!("resolve_type_vars_with_obligations: ty={:?}", ty); + debug!("resolve_vars_with_obligations: ty={:?}", ty); return ty; } // If `ty` is a type variable, see whether we already know what it is. ty = self.resolve_vars_if_possible(&ty); if !ty.has_infer_types() && !ty.has_infer_consts() { - debug!("resolve_type_vars_with_obligations: ty={:?}", ty); + debug!("resolve_vars_with_obligations: ty={:?}", ty); return ty; } @@ -2467,7 +2467,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.select_obligations_where_possible(false, |_| {}); ty = self.resolve_vars_if_possible(&ty); - debug!("resolve_type_vars_with_obligations: ty={:?}", ty); + debug!("resolve_vars_with_obligations: ty={:?}", ty); ty } @@ -3668,7 +3668,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { formal_ret: Ty<'tcx>, formal_args: &[Ty<'tcx>]) -> Vec> { - let formal_ret = self.resolve_type_vars_with_obligations(formal_ret); + let formal_ret = self.resolve_vars_with_obligations(formal_ret); let ret_ty = match expected_ret.only_has_type(self) { Some(ret) => ret, None => return Vec::new() @@ -4517,7 +4517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_suggestion( span, "try adding a return type", - format!("-> {} ", self.resolve_type_vars_with_obligations(found)), + format!("-> {} ", self.resolve_vars_with_obligations(found)), Applicability::MachineApplicable); true } @@ -4993,7 +4993,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If no resolution is possible, then an error is reported. // Numeric inference variables may be left unresolved. pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty = self.resolve_type_vars_with_obligations(ty); + let ty = self.resolve_vars_with_obligations(ty); if !ty.is_ty_var() { ty } else { diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index f9df2d1d848f..819c347d3ae9 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -179,7 +179,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_with_needs(lhs_expr, Needs::MutPlace) } }; - let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty); + let lhs_ty = self.resolve_vars_with_obligations(lhs_ty); // N.B., as we have not yet type-checked the RHS, we don't have the // type at hand. Make a variable to represent it. The whole reason @@ -196,7 +196,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // see `NB` above let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var); - let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty); + let rhs_ty = self.resolve_vars_with_obligations(rhs_ty); let return_ty = match result { Ok(method) => { diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs index 53ee0777c7c1..97c30f208f5e 100644 --- a/src/librustc_typeck/check/pat.rs +++ b/src/librustc_typeck/check/pat.rs @@ -251,7 +251,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, mut def_bm: BindingMode, ) -> (Ty<'tcx>, BindingMode) { - let mut expected = self.resolve_type_vars_with_obligations(&expected); + let mut expected = self.resolve_vars_with_obligations(&expected); // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example, // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches From 2f7c9a28964f8ddb1fca675bbc567144c893d6e6 Mon Sep 17 00:00:00 2001 From: varkor Date: Sun, 20 Oct 2019 01:11:56 +0100 Subject: [PATCH 50/79] Improve error message for APIT with explicit generic parameters This is disallowed with type or const generics. --- src/librustc_typeck/astconv.rs | 4 ++-- src/librustc_typeck/error_codes.rs | 4 ++-- src/test/ui/impl-trait/issues/universal-issue-48703.rs | 2 +- src/test/ui/impl-trait/issues/universal-issue-48703.stderr | 2 +- .../issues/universal-turbofish-in-method-issue-50950.rs | 2 +- .../issues/universal-turbofish-in-method-issue-50950.stderr | 2 +- src/test/ui/synthetic-param.rs | 6 +++--- src/test/ui/synthetic-param.stderr | 6 +++--- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 7e0a9bc4011c..8eab9c4e67e3 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -232,8 +232,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx.sess, span, E0632, - "cannot provide explicit type parameters when `impl Trait` is \ - used in argument position." + "cannot provide explicit generic arguments when `impl Trait` is \ + used in argument position" }; err.emit(); diff --git a/src/librustc_typeck/error_codes.rs b/src/librustc_typeck/error_codes.rs index ef08e8d4f0b7..8321fdd400fe 100644 --- a/src/librustc_typeck/error_codes.rs +++ b/src/librustc_typeck/error_codes.rs @@ -5048,8 +5048,8 @@ the future, [RFC 2091] prohibits their implementation without a follow-up RFC. // E0612, // merged into E0609 // E0613, // Removed (merged with E0609) E0627, // yield statement outside of generator literal - E0632, // cannot provide explicit type parameters when `impl Trait` is used - // in argument position. + E0632, // cannot provide explicit generic arguments when `impl Trait` is + // used in argument position E0634, // type has conflicting packed representaton hints E0640, // infer outlives requirements E0641, // cannot cast to/from a pointer with an unknown kind diff --git a/src/test/ui/impl-trait/issues/universal-issue-48703.rs b/src/test/ui/impl-trait/issues/universal-issue-48703.rs index e434e10bf89d..f661c62c9e44 100644 --- a/src/test/ui/impl-trait/issues/universal-issue-48703.rs +++ b/src/test/ui/impl-trait/issues/universal-issue-48703.rs @@ -5,5 +5,5 @@ use std::fmt::Debug; fn foo(x: impl Debug) { } fn main() { - foo::('a'); //~ ERROR cannot provide explicit type parameters + foo::('a'); //~ ERROR cannot provide explicit generic arguments } diff --git a/src/test/ui/impl-trait/issues/universal-issue-48703.stderr b/src/test/ui/impl-trait/issues/universal-issue-48703.stderr index 527bbd5f30fe..a51302dce296 100644 --- a/src/test/ui/impl-trait/issues/universal-issue-48703.stderr +++ b/src/test/ui/impl-trait/issues/universal-issue-48703.stderr @@ -1,4 +1,4 @@ -error[E0632]: cannot provide explicit type parameters when `impl Trait` is used in argument position. +error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position --> $DIR/universal-issue-48703.rs:8:5 | LL | foo::('a'); diff --git a/src/test/ui/impl-trait/issues/universal-turbofish-in-method-issue-50950.rs b/src/test/ui/impl-trait/issues/universal-turbofish-in-method-issue-50950.rs index d3d561621fc2..4ac0a694cb14 100644 --- a/src/test/ui/impl-trait/issues/universal-turbofish-in-method-issue-50950.rs +++ b/src/test/ui/impl-trait/issues/universal-turbofish-in-method-issue-50950.rs @@ -12,6 +12,6 @@ struct TestEvent(i32); fn main() { let mut evt = EventHandler {}; evt.handle_event::(|_evt| { - //~^ ERROR cannot provide explicit type parameters + //~^ ERROR cannot provide explicit generic arguments }); } diff --git a/src/test/ui/impl-trait/issues/universal-turbofish-in-method-issue-50950.stderr b/src/test/ui/impl-trait/issues/universal-turbofish-in-method-issue-50950.stderr index e2e6581fcf91..f09aa166ef50 100644 --- a/src/test/ui/impl-trait/issues/universal-turbofish-in-method-issue-50950.stderr +++ b/src/test/ui/impl-trait/issues/universal-turbofish-in-method-issue-50950.stderr @@ -1,4 +1,4 @@ -error[E0632]: cannot provide explicit type parameters when `impl Trait` is used in argument position. +error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position --> $DIR/universal-turbofish-in-method-issue-50950.rs:14:9 | LL | evt.handle_event::(|_evt| { diff --git a/src/test/ui/synthetic-param.rs b/src/test/ui/synthetic-param.rs index e53e3ba06e0d..e14697f5c3e9 100644 --- a/src/test/ui/synthetic-param.rs +++ b/src/test/ui/synthetic-param.rs @@ -17,12 +17,12 @@ impl Bar { } fn main() { - func::(42); //~ ERROR cannot provide explicit type parameters + func::(42); //~ ERROR cannot provide explicit generic arguments func(42); // Ok - Foo::func::(42); //~ ERROR cannot provide explicit type parameters + Foo::func::(42); //~ ERROR cannot provide explicit generic arguments Foo::func(42); // Ok - Bar::::func::(42); //~ ERROR cannot provide explicit type parameters + Bar::::func::(42); //~ ERROR cannot provide explicit generic arguments Bar::::func(42); // Ok } diff --git a/src/test/ui/synthetic-param.stderr b/src/test/ui/synthetic-param.stderr index bfafd8cbd72e..f8d14f26f32d 100644 --- a/src/test/ui/synthetic-param.stderr +++ b/src/test/ui/synthetic-param.stderr @@ -1,16 +1,16 @@ -error[E0632]: cannot provide explicit type parameters when `impl Trait` is used in argument position. +error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position --> $DIR/synthetic-param.rs:20:5 | LL | func::(42); | ^^^^^^^^^^ -error[E0632]: cannot provide explicit type parameters when `impl Trait` is used in argument position. +error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position --> $DIR/synthetic-param.rs:23:5 | LL | Foo::func::(42); | ^^^^^^^^^^^^^^^ -error[E0632]: cannot provide explicit type parameters when `impl Trait` is used in argument position. +error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position --> $DIR/synthetic-param.rs:26:5 | LL | Bar::::func::(42); From f907fbe1a6676a26cfc893b78f0fffb4285f1e6c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Oct 2019 18:16:22 +0200 Subject: [PATCH 51/79] skip all refs-to-uninit-local, not just arguments --- src/librustc_mir/transform/const_prop.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 223330a3ecb4..780b49cd9db0 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -523,18 +523,19 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // local. There's nothing it can do here: taking a reference needs an allocation // which needs to know the size. Normally that's okay as during execution // (e.g. for CTFE) it can never happen. But here in const_prop - // we leave function arguments uninitialized, so if one of these is unsized + // unknown data is uninitialized, so if e.g. a function argument is unsized // and has a reference taken, we get an ICE. Rvalue::Ref(_, _, Place { base: PlaceBase::Local(local), projection: box [] }) => { trace!("checking Ref({:?})", place); let alive = if let LocalValue::Live(_) = self.ecx.frame().locals[*local].value { true - } else { false }; + } else { + false + }; - // local 0 is the return place; locals 1..=arg_count are the arguments. - if local.as_usize() <= self.ecx.frame().body.arg_count && !alive { - trace!("skipping Ref({:?})", place); + if !alive { + trace!("skipping Ref({:?}) to uninitialized local", place); return None; } } From 5719f57fb1628e33765393d886f1638bfa0da059 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 20 Oct 2019 12:02:35 +0200 Subject: [PATCH 52/79] miri add write_bytes method to Memory doing bounds-checks and supporting iterators --- src/librustc/mir/interpret/allocation.rs | 9 +++++++-- src/librustc_mir/interpret/memory.rs | 21 ++++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 15e6cb6bcaba..3bcde8defdfa 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -346,11 +346,16 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { &mut self, cx: &impl HasDataLayout, ptr: Pointer, - src: &[u8], + src: impl IntoIterator, ) -> InterpResult<'tcx> { + let mut src = src.into_iter(); let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(src.len() as u64))?; - bytes.clone_from_slice(src); + // `zip` would stop when the first iterator ends; we want to definitely + // cover all of `bytes`. + for dest in bytes { + *dest = src.next().expect("iterator was shorter than it said it would be"); + } Ok(()) } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 924474c53175..0b65e9742b6a 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -7,7 +7,7 @@ //! short-circuiting the empty case! use std::collections::VecDeque; -use std::ptr; +use std::{ptr, iter}; use std::borrow::Cow; use rustc::ty::{self, Instance, ParamEnv, query::TyCtxtAt}; @@ -785,6 +785,25 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { self.get(ptr.alloc_id)?.read_c_str(self, ptr) } + /// Writes the given stream of bytes into memory. + /// + /// Performs appropriate bounds checks. + pub fn write_bytes( + &mut self, + ptr: Scalar, + src: impl IntoIterator, + ) -> InterpResult<'tcx> + { + let src = src.into_iter(); + let size = Size::from_bytes(src.len() as u64); + let ptr = match self.check_ptr_access(ptr, size, Align::from_bytes(1).unwrap())? { + Some(ptr) => ptr, + None => return Ok(()), // zero-sized access + }; + let tcx = self.tcx.tcx; + self.get_mut(ptr.alloc_id)?.write_bytes(&tcx, ptr, src) + } + /// Expects the caller to have checked bounds and alignment. pub fn copy( &mut self, From 50ddcbb2f5004da5b2d805c079e7c9699b6b7bea Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 20 Oct 2019 12:06:03 +0200 Subject: [PATCH 53/79] also check the iterator is not too long --- src/librustc/lib.rs | 1 + src/librustc/mir/interpret/allocation.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 8943fc342c02..3b0ac9ada8f6 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -43,6 +43,7 @@ #![feature(nll)] #![feature(non_exhaustive)] #![feature(optin_builtin_traits)] +#![feature(option_expect_none)] #![feature(range_is_empty)] #![feature(slice_patterns)] #![feature(specialization)] diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 3bcde8defdfa..87e9b91a86c7 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -356,6 +356,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { for dest in bytes { *dest = src.next().expect("iterator was shorter than it said it would be"); } + src.next().expect_none("iterator was longer than it said it would be"); Ok(()) } From 379733e8a02ca6fb500c2641e587ad96fb2ee9a5 Mon Sep 17 00:00:00 2001 From: guanqun Date: Sun, 20 Oct 2019 23:03:33 +0800 Subject: [PATCH 54/79] typo fix --- src/libsyntax/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index a68b7fdf931a..3fa13f08d3ab 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -67,7 +67,7 @@ pub struct Globals { impl Globals { fn new(edition: Edition) -> Globals { Globals { - // We have no idea how many attributes their will be, so just + // We have no idea how many attributes there will be, so just // initiate the vectors with 0 bits. We'll grow them as necessary. used_attrs: Lock::new(GrowableBitSet::new_empty()), known_attrs: Lock::new(GrowableBitSet::new_empty()), From 51a5b49bdafe24cd59d1f38b54e82b181b37aae8 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sun, 20 Oct 2019 09:36:04 -0700 Subject: [PATCH 55/79] Remove `borrowck_graphviz_postflow` from test --- src/test/ui/consts/const-eval/issue-65394.rs | 3 --- src/test/ui/consts/const-eval/issue-65394.stderr | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/test/ui/consts/const-eval/issue-65394.rs b/src/test/ui/consts/const-eval/issue-65394.rs index 978e227bcc81..8cf527f0429f 100644 --- a/src/test/ui/consts/const-eval/issue-65394.rs +++ b/src/test/ui/consts/const-eval/issue-65394.rs @@ -1,8 +1,5 @@ // Test for absence of validation mismatch ICE in #65394 -#![feature(rustc_attrs)] - -#[rustc_mir(borrowck_graphviz_postflow="hello.dot")] const _: Vec = { let mut x = Vec::::new(); let r = &mut x; //~ ERROR references in constants may only refer to immutable values diff --git a/src/test/ui/consts/const-eval/issue-65394.stderr b/src/test/ui/consts/const-eval/issue-65394.stderr index f48c551cb50f..15df813836e5 100644 --- a/src/test/ui/consts/const-eval/issue-65394.stderr +++ b/src/test/ui/consts/const-eval/issue-65394.stderr @@ -1,11 +1,11 @@ error[E0017]: references in constants may only refer to immutable values - --> $DIR/issue-65394.rs:8:13 + --> $DIR/issue-65394.rs:5:13 | LL | let r = &mut x; | ^^^^^^ constants require immutable values -[ERROR rustc_mir::transform::qualify_consts] old validator: [($DIR/issue-65394.rs:8:13: 8:19, "MutBorrow(Mut { allow_two_phase_borrow: false })")] -[ERROR rustc_mir::transform::qualify_consts] new validator: [($DIR/issue-65394.rs:8:13: 8:19, "MutBorrow(Mut { allow_two_phase_borrow: false })"), ($DIR/issue-65394.rs:7:9: 7:14, "LiveDrop")] +[ERROR rustc_mir::transform::qualify_consts] old validator: [($DIR/issue-65394.rs:5:13: 5:19, "MutBorrow(Mut { allow_two_phase_borrow: false })")] +[ERROR rustc_mir::transform::qualify_consts] new validator: [($DIR/issue-65394.rs:5:13: 5:19, "MutBorrow(Mut { allow_two_phase_borrow: false })"), ($DIR/issue-65394.rs:4:9: 4:14, "LiveDrop")] error: aborting due to previous error For more information about this error, try `rustc --explain E0017`. From c9e361fcd138eb131d045e53a094e1f3827b38dd Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sun, 20 Oct 2019 10:46:49 -0700 Subject: [PATCH 56/79] Check all files in `src/test` for `borrowck_graphviz_postflow` This attribute causes DOT files to be generated in the top-level directory. It is intended to be used only temporarily and should never appear on master. This will prevent #65071 from occurring again. --- src/tools/tidy/src/debug_artifacts.rs | 24 ++++++++++++++++++++++++ src/tools/tidy/src/lib.rs | 1 + src/tools/tidy/src/main.rs | 1 + 3 files changed, 26 insertions(+) create mode 100644 src/tools/tidy/src/debug_artifacts.rs diff --git a/src/tools/tidy/src/debug_artifacts.rs b/src/tools/tidy/src/debug_artifacts.rs new file mode 100644 index 000000000000..ee555a3e5bda --- /dev/null +++ b/src/tools/tidy/src/debug_artifacts.rs @@ -0,0 +1,24 @@ +//! Tidy check to prevent creation of unnecessary debug artifacts. + +use std::path::{Path, PathBuf}; + +const GRAPHVIZ_POSTFLOW_MSG: &'static str = + "`borrowck_graphviz_postflow` attribute in test"; + +pub fn check(path: &Path, bad: &mut bool) { + let test_dir: PathBuf = path.join("test"); + + super::walk(&test_dir, &mut super::filter_dirs, &mut |entry, contents| { + let filename = entry.path(); + let is_rust = filename.extension().map_or(false, |ext| ext == "rs"); + if !is_rust { + return; + } + + for (i, line) in contents.lines().enumerate() { + if line.contains("borrowck_graphviz_postflow") { + tidy_error!(bad, "{}:{}: {}", filename.display(), i + 1, GRAPHVIZ_POSTFLOW_MSG); + } + } + }); +} diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index eb93eb297479..c76cc4cff7f8 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -31,6 +31,7 @@ macro_rules! tidy_error { pub mod bins; pub mod style; +pub mod debug_artifacts; pub mod errors; pub mod features; pub mod cargo; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index e08c23c01fe2..de6b0c5b28d9 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -22,6 +22,7 @@ fn main() { let verbose = args.iter().any(|s| *s == "--verbose"); bins::check(&path, &mut bad); style::check(&path, &mut bad); + debug_artifacts::check(&path, &mut bad); errors::check(&path, &mut bad); cargo::check(&path, &mut bad); edition::check(&path, &mut bad); From efcae577bf5ebf503a702c7e66c2924ddb53cdb8 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sun, 20 Oct 2019 11:03:13 -0700 Subject: [PATCH 57/79] Ignore DOT files in .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 81a472451d77..1c47ed4c0248 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,6 @@ config.stamp Session.vim .cargo no_llvm_build +# Generated when dumping Graphviz output for debugging: +/mir_dump/ +/*.dot From 040d88dda1c65d6cb3cd5df5e81075a281f90da4 Mon Sep 17 00:00:00 2001 From: Mikko Rantanen Date: Sun, 20 Oct 2019 21:13:47 +0300 Subject: [PATCH 58/79] Remove leading :: from paths in doc examples --- src/libcore/iter/traits/collect.rs | 2 +- src/libcore/ops/unsize.rs | 2 +- src/libcore/str/mod.rs | 4 ++-- src/libstd/net/udp.rs | 2 +- src/libstd/process.rs | 12 ++++++------ 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libcore/iter/traits/collect.rs b/src/libcore/iter/traits/collect.rs index 25439136b853..00a864170583 100644 --- a/src/libcore/iter/traits/collect.rs +++ b/src/libcore/iter/traits/collect.rs @@ -167,7 +167,7 @@ pub trait FromIterator: Sized { /// // and we'll implement IntoIterator /// impl IntoIterator for MyCollection { /// type Item = i32; -/// type IntoIter = ::std::vec::IntoIter; +/// type IntoIter = std::vec::IntoIter; /// /// fn into_iter(self) -> Self::IntoIter { /// self.0.into_iter() diff --git a/src/libcore/ops/unsize.rs b/src/libcore/ops/unsize.rs index 8e4683008464..d29147645f7e 100644 --- a/src/libcore/ops/unsize.rs +++ b/src/libcore/ops/unsize.rs @@ -76,7 +76,7 @@ impl, U: ?Sized> CoerceUnsized<*const U> for *const T {} /// ``` /// # #![feature(dispatch_from_dyn, unsize)] /// # use std::{ops::DispatchFromDyn, marker::Unsize}; -/// # struct Rc(::std::rc::Rc); +/// # struct Rc(std::rc::Rc); /// impl DispatchFromDyn> for Rc /// where /// T: Unsize, diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 885696e5acf4..f67012d8f2fc 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -176,7 +176,7 @@ Section: Creating a string /// ``` /// fn from_utf8_lossy(mut input: &[u8], mut push: F) where F: FnMut(&str) { /// loop { -/// match ::std::str::from_utf8(input) { +/// match std::str::from_utf8(input) { /// Ok(valid) => { /// push(valid); /// break @@ -184,7 +184,7 @@ Section: Creating a string /// Err(error) => { /// let (valid, after_valid) = input.split_at(error.valid_up_to()); /// unsafe { -/// push(::std::str::from_utf8_unchecked(valid)) +/// push(std::str::from_utf8_unchecked(valid)) /// } /// push("\u{FFFD}"); /// diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index 46bbd8855ded..a9e4457f4237 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -202,7 +202,7 @@ impl UdpSocket { /// /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); /// assert_eq!(socket.peer_addr().unwrap_err().kind(), - /// ::std::io::ErrorKind::NotConnected); + /// std::io::ErrorKind::NotConnected); /// ``` #[stable(feature = "udp_peer_addr", since = "1.40.0")] pub fn peer_addr(&self) -> io::Result { diff --git a/src/libstd/process.rs b/src/libstd/process.rs index da136ca6bf68..4b0cf8312f18 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -1488,12 +1488,12 @@ impl Child { /// } /// /// fn main() { -/// ::std::process::exit(match run_app() { -/// Ok(_) => 0, -/// Err(err) => { -/// eprintln!("error: {:?}", err); -/// 1 -/// } +/// std::process::exit(match run_app() { +/// Ok(_) => 0, +/// Err(err) => { +/// eprintln!("error: {:?}", err); +/// 1 +/// } /// }); /// } /// ``` From 2a5c31ca51a5d7c1c3d580e1214094dc2ef5d8fe Mon Sep 17 00:00:00 2001 From: David Sinclair Date: Sun, 20 Oct 2019 22:22:33 +0200 Subject: [PATCH 59/79] Rename the default argument 'def' to 'default' Fixes: #65492 --- src/libcore/option.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 09d4076c7fbc..9eb29eae7f75 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -395,10 +395,10 @@ impl Option { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn unwrap_or(self, def: T) -> T { + pub fn unwrap_or(self, default: T) -> T { match self { Some(x) => x, - None => def, + None => default, } } From e697ffbbcb41559c8de3ca3b94f3b9467e662a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Heine=20n=C3=A9=20Lang?= Date: Sun, 20 Oct 2019 23:13:41 +0200 Subject: [PATCH 60/79] Fix parameter name in documentation --- src/libstd/io/stdio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index c798ee0e2209..6574ef13db95 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -762,7 +762,7 @@ pub fn set_print(sink: Option>) -> Option Date: Sun, 20 Oct 2019 15:40:44 +0900 Subject: [PATCH 61/79] Correctly note code as Ok not error for E0573 --- src/librustc_resolve/error_codes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_resolve/error_codes.rs b/src/librustc_resolve/error_codes.rs index cd6189c681da..b82cba8c83dc 100644 --- a/src/librustc_resolve/error_codes.rs +++ b/src/librustc_resolve/error_codes.rs @@ -1682,7 +1682,7 @@ enum Wizard { } trait Isengard { - fn wizard(w: Wizard) { // error! + fn wizard(w: Wizard) { // ok! match w { Wizard::Saruman => { // do something From 77c50dc508b8a0ea4277db6860c8d86288fd8538 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 20 Oct 2019 10:35:51 +1100 Subject: [PATCH 62/79] Remove unnecessary trait bounds from `keys::Keys`. --- src/librustc/ty/query/keys.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/librustc/ty/query/keys.rs b/src/librustc/ty/query/keys.rs index 30a3e53dddfb..f61801cd2327 100644 --- a/src/librustc/ty/query/keys.rs +++ b/src/librustc/ty/query/keys.rs @@ -8,14 +8,12 @@ use crate::ty::subst::SubstsRef; use crate::ty::fast_reject::SimplifiedType; use crate::mir; -use std::fmt::Debug; -use std::hash::Hash; use syntax_pos::{Span, DUMMY_SP}; use syntax_pos::symbol::InternedString; /// The `Key` trait controls what types can legally be used as the key /// for a query. -pub(super) trait Key: Clone + Hash + Eq + Debug { +pub(super) trait Key { /// Given an instance of this key, what crate is it referring to? /// This is used to find the provider. fn query_crate(&self) -> CrateNum; @@ -201,10 +199,7 @@ impl Key for InternedString { /// Canonical query goals correspond to abstract trait operations that /// are not tied to any crate in particular. -impl<'tcx, T> Key for Canonical<'tcx, T> -where - T: Debug + Hash + Clone + Eq, -{ +impl<'tcx, T> Key for Canonical<'tcx, T> { fn query_crate(&self) -> CrateNum { LOCAL_CRATE } From c3b3a861244f5e61d5912ba507e229d978ec1c1c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 20 Oct 2019 10:29:11 +1100 Subject: [PATCH 63/79] Remove unnecessary `Hash` bounds from various types. --- src/bootstrap/cache.rs | 2 +- src/librustc/ty/fast_reject.rs | 8 ++++---- src/librustc_data_structures/sharded.rs | 2 +- src/librustc_data_structures/snapshot_map/mod.rs | 2 +- src/librustc_data_structures/stable_hasher.rs | 6 +++--- src/librustc_mir/borrow_check/nll/member_constraints.rs | 4 ++-- src/libserialize/collection_impls.rs | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/bootstrap/cache.rs b/src/bootstrap/cache.rs index 53071df85529..4310f2c6fa14 100644 --- a/src/bootstrap/cache.rs +++ b/src/bootstrap/cache.rs @@ -161,7 +161,7 @@ impl Ord for Interned { } } -struct TyIntern { +struct TyIntern { items: Vec, set: HashMap>, } diff --git a/src/librustc/ty/fast_reject.rs b/src/librustc/ty/fast_reject.rs index 038b54f1f26d..27a09b394b8f 100644 --- a/src/librustc/ty/fast_reject.rs +++ b/src/librustc/ty/fast_reject.rs @@ -19,7 +19,7 @@ pub type SimplifiedType = SimplifiedTypeGen; /// the non-stable but fast to construct DefId-version is the better choice. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, RustcEncodable, RustcDecodable)] pub enum SimplifiedTypeGen - where D: Copy + Debug + Ord + Eq + Hash + where D: Copy + Debug + Ord + Eq { BoolSimplifiedType, CharSimplifiedType, @@ -123,10 +123,10 @@ pub fn simplify_type( } } -impl SimplifiedTypeGen { +impl SimplifiedTypeGen { pub fn map_def(self, map: F) -> SimplifiedTypeGen where F: Fn(D) -> U, - U: Copy + Debug + Ord + Eq + Hash, + U: Copy + Debug + Ord + Eq, { match self { BoolSimplifiedType => BoolSimplifiedType, @@ -155,7 +155,7 @@ impl SimplifiedTypeGen { impl<'a, D> HashStable> for SimplifiedTypeGen where - D: Copy + Debug + Ord + Eq + Hash + HashStable>, + D: Copy + Debug + Ord + Eq + HashStable>, { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); diff --git a/src/librustc_data_structures/sharded.rs b/src/librustc_data_structures/sharded.rs index d0ff6108d6ea..2f972eeccdcb 100644 --- a/src/librustc_data_structures/sharded.rs +++ b/src/librustc_data_structures/sharded.rs @@ -90,7 +90,7 @@ impl Sharded { pub type ShardedHashMap = Sharded>; -impl ShardedHashMap { +impl ShardedHashMap { pub fn len(&self) -> usize { self.lock_shards().iter().map(|shard| shard.len()).sum() } diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/src/librustc_data_structures/snapshot_map/mod.rs index ce0aa07cc282..bdd3dc966562 100644 --- a/src/librustc_data_structures/snapshot_map/mod.rs +++ b/src/librustc_data_structures/snapshot_map/mod.rs @@ -7,7 +7,7 @@ use std::mem; mod tests; pub struct SnapshotMap - where K: Hash + Clone + Eq + where K: Clone + Eq { map: FxHashMap, undo_log: Vec>, diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index ee4f6a5e785e..78494401c0ba 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs @@ -460,7 +460,7 @@ impl_stable_hash_via_hash!(::std::path::Path); impl_stable_hash_via_hash!(::std::path::PathBuf); impl HashStable for ::std::collections::HashMap - where K: ToStableHashKey + Eq + Hash, + where K: ToStableHashKey + Eq, V: HashStable, R: BuildHasher, { @@ -471,7 +471,7 @@ impl HashStable for ::std::collections::HashMap } impl HashStable for ::std::collections::HashSet - where K: ToStableHashKey + Eq + Hash, + where K: ToStableHashKey + Eq, R: BuildHasher, { fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { @@ -513,7 +513,7 @@ pub fn hash_stable_hashmap( hasher: &mut StableHasher, map: &::std::collections::HashMap, to_stable_hash_key: F) - where K: Eq + Hash, + where K: Eq, V: HashStable, R: BuildHasher, SK: HashStable + Ord + Clone, diff --git a/src/librustc_mir/borrow_check/nll/member_constraints.rs b/src/librustc_mir/borrow_check/nll/member_constraints.rs index fd195873a55e..75213d30982f 100644 --- a/src/librustc_mir/borrow_check/nll/member_constraints.rs +++ b/src/librustc_mir/borrow_check/nll/member_constraints.rs @@ -11,7 +11,7 @@ use syntax_pos::Span; /// indexed by the region `R0`. crate struct MemberConstraintSet<'tcx, R> where - R: Copy + Hash + Eq, + R: Copy + Eq, { /// Stores the first "member" constraint for a given `R0`. This is an /// index into the `constraints` vector below. @@ -191,7 +191,7 @@ where impl<'tcx, R> Index for MemberConstraintSet<'tcx, R> where - R: Copy + Hash + Eq, + R: Copy + Eq, { type Output = NllMemberConstraint<'tcx>; diff --git a/src/libserialize/collection_impls.rs b/src/libserialize/collection_impls.rs index d981740780e6..f2e9be14c8d1 100644 --- a/src/libserialize/collection_impls.rs +++ b/src/libserialize/collection_impls.rs @@ -143,7 +143,7 @@ impl Decodable for BTreeSet } impl Encodable for HashMap - where K: Encodable + Hash + Eq, + where K: Encodable + Eq, V: Encodable, S: BuildHasher, { @@ -180,7 +180,7 @@ impl Decodable for HashMap } impl Encodable for HashSet - where T: Encodable + Hash + Eq, + where T: Encodable + Eq, S: BuildHasher, { fn encode(&self, s: &mut E) -> Result<(), E::Error> { From 4592a9eb3f240cef2994ee76f6975c4946d4d269 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Sat, 12 Oct 2019 08:21:51 -0400 Subject: [PATCH 64/79] Cleanup `ConstProp::visit_statement()` --- src/librustc_mir/transform/const_prop.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 780b49cd9db0..4a3a182cea20 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -431,7 +431,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { place_layout: TyLayout<'tcx>, source_info: SourceInfo, place: &Place<'tcx>, - ) -> Option> { + ) -> Option<()> { let span = source_info.span; let overflow_check = self.tcx.sess.overflow_checks(); @@ -553,7 +553,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { self.use_ecx(source_info, |this| { trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place); this.ecx.eval_rvalue_into_place(rvalue, place)?; - this.ecx.eval_place_to_op(place, Some(place_layout)) + Ok(()) }) } @@ -717,16 +717,15 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { base: PlaceBase::Local(local), projection: box [], } = *place { - if let Some(value) = self.const_prop(rval, - place_layout, - statement.source_info, - place) { - trace!("checking whether {:?} can be stored to {:?}", value, local); + let source = statement.source_info; + if let Some(()) = self.const_prop(rval, place_layout, source, place) { if self.can_const_prop[local] { - trace!("stored {:?} to {:?}", value, local); - assert_eq!(self.get_const(local), Some(value)); + trace!("propagated into {:?}", local); if self.should_const_prop() { + let value = + self.get_const(local).expect("local was dead/uninitialized"); + trace!("replacing {:?} with {:?}", rval, value); self.replace_with_const( rval, value, @@ -734,7 +733,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { ); } } else { - trace!("can't propagate {:?} to {:?}", value, local); + trace!("can't propagate into {:?}", local); self.remove_const(local); } } From 2ec73395b985a028062cc40faed6ace50be0c67d Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Thu, 17 Oct 2019 06:46:51 -0400 Subject: [PATCH 65/79] Improve SimplifyLocals pass so it can remove unused consts The `ConstProp` can cause many locals to be initialized to a constant value and then never read from. `ConstProp` can also evaluate ZSTs into constant values. Previously, many of these would be removed by other parts of the MIR optimization pipeline. However, evaluating ZSTs (especially `()`) into constant values defeated those parts of the optimizer and so in a2e3ed5c054b544df6ceeb9e612d39af819f4aae, I added a hack to `ConstProp` that skips evaluating ZSTs to avoid that regression. This commit changes `SimplifyLocals` so that it doesn't consider writes of const values to a local to be a use of that local. In doing so, `SimplifyLocals` is able to remove otherwise unused locals left behind by other optimization passes (`ConstProp` in particular). --- src/librustc_mir/transform/const_prop.rs | 7 -- src/librustc_mir/transform/simplify.rs | 78 +++++++++++----- src/test/incremental/hashes/for_loops.rs | 2 +- .../incremental/hashes/let_expressions.rs | 8 +- .../incremental/hashes/loop_expressions.rs | 2 +- .../incremental/hashes/while_let_loops.rs | 2 +- src/test/incremental/hashes/while_loops.rs | 2 +- .../simplify-locals-removes-unused-consts.rs | 89 +++++++++++++++++++ src/test/mir-opt/slice-drop-shim.rs | 4 +- 9 files changed, 155 insertions(+), 39 deletions(-) create mode 100644 src/test/mir-opt/simplify-locals-removes-unused-consts.rs diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 4a3a182cea20..108c6c9786b2 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -540,13 +540,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - // Work around: avoid extra unnecessary locals. FIXME(wesleywiser) - // Const eval will turn this into a `const Scalar()` that - // `SimplifyLocals` doesn't know it can remove. - Rvalue::Aggregate(_, operands) if operands.len() == 0 => { - return None; - } - _ => { } } diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index 606c1a3a1cc0..e41b4678dbd0 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -31,7 +31,7 @@ use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; use rustc::ty::TyCtxt; use rustc::mir::*; -use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext}; +use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext, MutatingUseContext}; use rustc::session::config::DebugInfo; use std::borrow::Cow; use crate::transform::{MirPass, MirSource}; @@ -293,23 +293,31 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) { pub struct SimplifyLocals; impl<'tcx> MirPass<'tcx> for SimplifyLocals { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { - let mut marker = DeclMarker { locals: BitSet::new_empty(body.local_decls.len()) }; - marker.visit_body(body); - // Return pointer and arguments are always live - marker.locals.insert(RETURN_PLACE); - for arg in body.args_iter() { - marker.locals.insert(arg); - } - - // We may need to keep dead user variables live for debuginfo. - if tcx.sess.opts.debuginfo == DebugInfo::Full { - for local in body.vars_iter() { - marker.locals.insert(local); + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + trace!("running SimplifyLocals on {:?}", source); + let locals = { + let mut marker = DeclMarker { + locals: BitSet::new_empty(body.local_decls.len()), + body, + }; + marker.visit_body(body); + // Return pointer and arguments are always live + marker.locals.insert(RETURN_PLACE); + for arg in body.args_iter() { + marker.locals.insert(arg); } - } - let map = make_local_map(&mut body.local_decls, marker.locals); + // We may need to keep dead user variables live for debuginfo. + if tcx.sess.opts.debuginfo == DebugInfo::Full { + for local in body.vars_iter() { + marker.locals.insert(local); + } + } + + marker.locals + }; + + let map = make_local_map(&mut body.local_decls, locals); // Update references to all vars and tmps now LocalUpdater { map }.visit_body(body); body.local_decls.shrink_to_fit(); @@ -334,18 +342,35 @@ fn make_local_map( map } -struct DeclMarker { +struct DeclMarker<'a, 'tcx> { pub locals: BitSet, + pub body: &'a Body<'tcx>, } -impl<'tcx> Visitor<'tcx> for DeclMarker { - fn visit_local(&mut self, local: &Local, ctx: PlaceContext, _: Location) { +impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> { + fn visit_local(&mut self, local: &Local, ctx: PlaceContext, location: Location) { // Ignore storage markers altogether, they get removed along with their otherwise unused // decls. // FIXME: Extend this to all non-uses. - if !ctx.is_storage_marker() { - self.locals.insert(*local); + if ctx.is_storage_marker() { + return; } + + // Ignore stores of constants because `ConstProp` and `CopyProp` can remove uses of many + // of these locals. However, if the local is still needed, then it will be referenced in + // another place and we'll mark it as being used there. + if ctx == PlaceContext::MutatingUse(MutatingUseContext::Store) { + let stmt = + &self.body.basic_blocks()[location.block].statements[location.statement_index]; + if let StatementKind::Assign(box (p, Rvalue::Use(Operand::Constant(c)))) = &stmt.kind { + if p.as_local().is_some() { + trace!("skipping store of const value {:?} to {:?}", c, local); + return; + } + } + } + + self.locals.insert(*local); } } @@ -357,9 +382,16 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater { fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { // Remove unnecessary StorageLive and StorageDead annotations. data.statements.retain(|stmt| { - match stmt.kind { + match &stmt.kind { StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => { - self.map[l].is_some() + self.map[*l].is_some() + } + StatementKind::Assign(box (place, _)) => { + if let Some(local) = place.as_local() { + self.map[local].is_some() + } else { + true + } } _ => true } diff --git a/src/test/incremental/hashes/for_loops.rs b/src/test/incremental/hashes/for_loops.rs index 70820dfaea4a..8e134ad14fc1 100644 --- a/src/test/incremental/hashes/for_loops.rs +++ b/src/test/incremental/hashes/for_loops.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; diff --git a/src/test/incremental/hashes/let_expressions.rs b/src/test/incremental/hashes/let_expressions.rs index 68545b7daaa5..4e8ba5a209df 100644 --- a/src/test/incremental/hashes/let_expressions.rs +++ b/src/test/incremental/hashes/let_expressions.rs @@ -22,7 +22,7 @@ pub fn change_name() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir")] + except="HirBody,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_name() { let _y = 2u64; @@ -86,7 +86,7 @@ pub fn change_mutability_of_slot() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,typeck_tables_of,mir_built,optimized_mir")] + except="HirBody,typeck_tables_of,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_slot() { let _x: u64 = 0; @@ -182,7 +182,7 @@ pub fn add_initializer() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,typeck_tables_of,mir_built,optimized_mir")] + except="HirBody,typeck_tables_of,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn add_initializer() { let _x: i16 = 3i16; @@ -198,7 +198,7 @@ pub fn change_initializer() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir")] + except="HirBody,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_initializer() { let _x = 5u16; diff --git a/src/test/incremental/hashes/loop_expressions.rs b/src/test/incremental/hashes/loop_expressions.rs index a2222db4c59a..ca85ee39e367 100644 --- a/src/test/incremental/hashes/loop_expressions.rs +++ b/src/test/incremental/hashes/loop_expressions.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; diff --git a/src/test/incremental/hashes/while_let_loops.rs b/src/test/incremental/hashes/while_let_loops.rs index da3c957741fb..1e628d019196 100644 --- a/src/test/incremental/hashes/while_let_loops.rs +++ b/src/test/incremental/hashes/while_let_loops.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; diff --git a/src/test/incremental/hashes/while_loops.rs b/src/test/incremental/hashes/while_loops.rs index 3be42e7a4ee7..295c2244879f 100644 --- a/src/test/incremental/hashes/while_loops.rs +++ b/src/test/incremental/hashes/while_loops.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; diff --git a/src/test/mir-opt/simplify-locals-removes-unused-consts.rs b/src/test/mir-opt/simplify-locals-removes-unused-consts.rs new file mode 100644 index 000000000000..6f03438ff723 --- /dev/null +++ b/src/test/mir-opt/simplify-locals-removes-unused-consts.rs @@ -0,0 +1,89 @@ +// compile-flags: -C overflow-checks=no + +fn use_zst(_: ((), ())) { } + +struct Temp { + x: u8 +} + +fn use_u8(_: u8) { } + +fn main() { + let ((), ()) = ((), ()); + use_zst(((), ())); + + use_u8((Temp { x : 40 }).x + 2); +} + +// END RUST SOURCE + +// START rustc.main.SimplifyLocals.before.mir +// let mut _0: (); +// let mut _1: ((), ()); +// let mut _2: (); +// let mut _3: (); +// let _4: (); +// let mut _5: ((), ()); +// let mut _6: (); +// let mut _7: (); +// let _8: (); +// let mut _9: u8; +// let mut _10: u8; +// let mut _11: Temp; +// scope 1 { +// } +// bb0: { +// StorageLive(_1); +// StorageLive(_2); +// _2 = const Scalar() : (); +// StorageLive(_3); +// _3 = const Scalar() : (); +// _1 = const Scalar() : ((), ()); +// StorageDead(_3); +// StorageDead(_2); +// StorageDead(_1); +// StorageLive(_4); +// StorageLive(_6); +// _6 = const Scalar() : (); +// StorageLive(_7); +// _7 = const Scalar() : (); +// StorageDead(_7); +// StorageDead(_6); +// _4 = const use_zst(const Scalar() : ((), ())) -> bb1; +// } +// bb1: { +// StorageDead(_4); +// StorageLive(_8); +// StorageLive(_10); +// StorageLive(_11); +// _11 = const Scalar(0x28) : Temp; +// _10 = const 40u8; +// StorageDead(_10); +// _8 = const use_u8(const 42u8) -> bb2; +// } +// bb2: { +// StorageDead(_11); +// StorageDead(_8); +// return; +// } +// END rustc.main.SimplifyLocals.before.mir +// START rustc.main.SimplifyLocals.after.mir +// let mut _0: (); +// let _1: (); +// let _2: (); +// scope 1 { +// } +// bb0: { +// StorageLive(_1); +// _1 = const use_zst(const Scalar() : ((), ())) -> bb1; +// } +// bb1: { +// StorageDead(_1); +// StorageLive(_2); +// _2 = const use_u8(const 42u8) -> bb2; +// } +// bb2: { +// StorageDead(_2); +// return; +// } +// END rustc.main.SimplifyLocals.after.mir diff --git a/src/test/mir-opt/slice-drop-shim.rs b/src/test/mir-opt/slice-drop-shim.rs index 754fad51b21e..f270dec5fe23 100644 --- a/src/test/mir-opt/slice-drop-shim.rs +++ b/src/test/mir-opt/slice-drop-shim.rs @@ -1,5 +1,7 @@ +// compile-flags: -Zmir-opt-level=0 + fn main() { - std::ptr::drop_in_place::<[String]> as unsafe fn(_); + let _fn = std::ptr::drop_in_place::<[String]> as unsafe fn(_); } // END RUST SOURCE From ae04dc8473f9ea53b71123eb4eb0fcec71e6d797 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Mon, 21 Oct 2019 09:05:40 +0300 Subject: [PATCH 66/79] Remove unneccessary use under cfg(unix) --- src/libtest/helpers/concurrency.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/libtest/helpers/concurrency.rs b/src/libtest/helpers/concurrency.rs index f0292c2d2c79..61651a927c5f 100644 --- a/src/libtest/helpers/concurrency.rs +++ b/src/libtest/helpers/concurrency.rs @@ -2,9 +2,6 @@ //! during tests execution. use std::env; -#[cfg(any(unix, target_os = "cloudabi"))] -use libc; - #[allow(deprecated)] pub fn get_concurrency() -> usize { return match env::var("RUST_TEST_THREADS") { @@ -12,10 +9,7 @@ pub fn get_concurrency() -> usize { let opt_n: Option = s.parse().ok(); match opt_n { Some(n) if n > 0 => n, - _ => panic!( - "RUST_TEST_THREADS is `{}`, should be a positive integer.", - s - ), + _ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", s), } } Err(..) => num_cpus(), @@ -82,11 +76,7 @@ pub fn get_concurrency() -> usize { unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize } } - #[cfg(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "netbsd" - ))] + #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))] fn num_cpus() -> usize { use std::ptr; From 642da6e12ae337928fcc0dc9f0c84436a0954f85 Mon Sep 17 00:00:00 2001 From: Guanqun Lu Date: Mon, 21 Oct 2019 14:18:09 +0800 Subject: [PATCH 67/79] use unwrap_or in lint code --- src/librustc/lint/levels.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/librustc/lint/levels.rs b/src/librustc/lint/levels.rs index 60b1b192d10d..8ed06cbdc762 100644 --- a/src/librustc/lint/levels.rs +++ b/src/librustc/lint/levels.rs @@ -202,11 +202,7 @@ impl<'a> LintLevelsBuilder<'a> { let meta = unwrap_or!(attr.meta(), continue); attr::mark_used(attr); - let mut metas = if let Some(metas) = meta.meta_item_list() { - metas - } else { - continue; - }; + let mut metas = unwrap_or!(meta.meta_item_list(), continue); if metas.is_empty() { // FIXME (#55112): issue unused-attributes lint for `#[level()]` From aa3d28f9a8e0fa2915e5321648d57af6c922799f Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 21 Oct 2019 14:42:54 +1300 Subject: [PATCH 68/79] Fix `canonicalize_const_var` from leaking inference variables through it's type. --- src/librustc/infer/canonical/canonicalizer.rs | 2 +- .../incremental/const-generics/issue-61338.rs | 14 ++++++++++++++ .../incremental/const-generics/issue-61516.rs | 16 ++++++++++++++++ .../incremental/const-generics/issue-62536.rs | 12 ++++++++++++ .../incremental/const-generics/issue-64087.rs | 11 +++++++++++ .../incremental/const-generics/issue-65623.rs | 14 ++++++++++++++ 6 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/test/incremental/const-generics/issue-61338.rs create mode 100644 src/test/incremental/const-generics/issue-61516.rs create mode 100644 src/test/incremental/const-generics/issue-62536.rs create mode 100644 src/test/incremental/const-generics/issue-64087.rs create mode 100644 src/test/incremental/const-generics/issue-65623.rs diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index b9474f869ee2..e69719806a81 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -701,7 +701,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { self.tcx().mk_const( ty::Const { val: ConstValue::Infer(InferConst::Canonical(self.binder_index, var.into())), - ty: const_var.ty, + ty: self.fold_ty(const_var.ty), } ) } diff --git a/src/test/incremental/const-generics/issue-61338.rs b/src/test/incremental/const-generics/issue-61338.rs new file mode 100644 index 000000000000..00b3b29698be --- /dev/null +++ b/src/test/incremental/const-generics/issue-61338.rs @@ -0,0 +1,14 @@ +// revisions:rpass1 + +#![feature(const_generics)] + +struct Struct(T); + +impl Struct<[T; N]> { + fn f() {} + fn g() { Self::f(); } +} + +fn main() { + Struct::<[u32; 3]>::g(); +} diff --git a/src/test/incremental/const-generics/issue-61516.rs b/src/test/incremental/const-generics/issue-61516.rs new file mode 100644 index 000000000000..a7465b77267a --- /dev/null +++ b/src/test/incremental/const-generics/issue-61516.rs @@ -0,0 +1,16 @@ +// revisions:rpass1 + +#![feature(const_generics)] + +struct FakeArray(T); + +impl FakeArray { + fn len(&self) -> usize { + N + } +} + +fn main() { + let fa = FakeArray::(1); + assert_eq!(fa.len(), 32); +} diff --git a/src/test/incremental/const-generics/issue-62536.rs b/src/test/incremental/const-generics/issue-62536.rs new file mode 100644 index 000000000000..90e279bfc743 --- /dev/null +++ b/src/test/incremental/const-generics/issue-62536.rs @@ -0,0 +1,12 @@ +// revisions:cfail1 +#![feature(const_generics)] +//[cfail1]~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +struct S([T; N]); + +fn f(x: T) -> S { panic!() } + +fn main() { + f(0u8); + //[cfail1]~^ ERROR type annotations needed +} diff --git a/src/test/incremental/const-generics/issue-64087.rs b/src/test/incremental/const-generics/issue-64087.rs new file mode 100644 index 000000000000..b3c12fbb6e81 --- /dev/null +++ b/src/test/incremental/const-generics/issue-64087.rs @@ -0,0 +1,11 @@ +// revisions:cfail1 +#![feature(const_generics)] +//[cfail1]~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +fn combinator() -> [T; S] {} +//[cfail1]~^ ERROR mismatched types + +fn main() { + combinator().into_iter(); + //[cfail1]~^ ERROR type annotations needed +} diff --git a/src/test/incremental/const-generics/issue-65623.rs b/src/test/incremental/const-generics/issue-65623.rs new file mode 100644 index 000000000000..353e323e67b4 --- /dev/null +++ b/src/test/incremental/const-generics/issue-65623.rs @@ -0,0 +1,14 @@ +// revisions:rpass1 +#![feature(const_generics)] + +pub struct Foo([T; 0]); + +impl Foo { + pub fn new() -> Self { + Foo([]) + } +} + +fn main() { + let _: Foo = Foo::new(); +} From 0653694fdc46a2bca119b9790d1dfd62e1b4901e Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 21 Oct 2019 09:08:05 +0200 Subject: [PATCH 69/79] Don't silently do nothing on mis_use of `check_union_fields` --- src/librustc_typeck/check/mod.rs | 33 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 3e0527393cd1..d5182d69c3e7 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1393,7 +1393,7 @@ fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { /// When the `#![feature(untagged_unions)]` gate is active, /// check that the fields of the `union` does not contain fields that need dropping. -fn check_union_fields(tcx: TyCtxt<'_>, _: Span, item_def_id: DefId) -> bool { +fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: DefId) -> bool { // Without the feature we check that all fields are `Copy` in our stability checking // infrastructure. if !tcx.features().untagged_unions { @@ -1401,23 +1401,24 @@ fn check_union_fields(tcx: TyCtxt<'_>, _: Span, item_def_id: DefId) -> bool { } let item_type = tcx.type_of(item_def_id); if let ty::Adt(def, substs) = item_type.kind { - if def.is_union() { - let fields = &def.non_enum_variant().fields; - for field in fields { - let field_ty = field.ty(tcx, substs); - // We are currently checking the type this field came from, so it must be local. - let field_span = tcx.hir().span_if_local(field.did).unwrap(); - let param_env = tcx.param_env(field.did); - if field_ty.needs_drop(tcx, param_env) { - struct_span_err!(tcx.sess, field_span, E0740, - "unions may not contain fields that need dropping") - .span_note(field_span, - "`std::mem::ManuallyDrop` can be used to wrap the type") - .emit(); - return false; - } + assert!(def.is_union()); + let fields = &def.non_enum_variant().fields; + for field in fields { + let field_ty = field.ty(tcx, substs); + // We are currently checking the type this field came from, so it must be local. + let field_span = tcx.hir().span_if_local(field.did).unwrap(); + let param_env = tcx.param_env(field.did); + if field_ty.needs_drop(tcx, param_env) { + struct_span_err!(tcx.sess, field_span, E0740, + "unions may not contain fields that need dropping") + .span_note(field_span, + "`std::mem::ManuallyDrop` can be used to wrap the type") + .emit(); + return false; } } + } else { + span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind); } return true; } From 875bdd5dbe663a6dafd785b86c8964a90653eeb7 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 21 Oct 2019 10:12:09 +0200 Subject: [PATCH 70/79] Report even duplilcate errors in case the feature gat is not active --- src/librustc_typeck/check/mod.rs | 5 ---- .../feature-gate-untagged_unions.rs | 4 +-- .../feature-gate-untagged_unions.stderr | 29 +++++++++++++++++-- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d5182d69c3e7..c5b809ad380f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1394,11 +1394,6 @@ fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { /// When the `#![feature(untagged_unions)]` gate is active, /// check that the fields of the `union` does not contain fields that need dropping. fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: DefId) -> bool { - // Without the feature we check that all fields are `Copy` in our stability checking - // infrastructure. - if !tcx.features().untagged_unions { - return true; - } let item_type = tcx.type_of(item_def_id); if let ty::Adt(def, substs) = item_type.kind { assert!(def.is_union()); diff --git a/src/test/ui/feature-gates/feature-gate-untagged_unions.rs b/src/test/ui/feature-gates/feature-gate-untagged_unions.rs index 3bac3d853e90..9ee0e6f681dc 100644 --- a/src/test/ui/feature-gates/feature-gate-untagged_unions.rs +++ b/src/test/ui/feature-gates/feature-gate-untagged_unions.rs @@ -7,11 +7,11 @@ union U2 { // OK } union U3 { //~ ERROR unions with non-`Copy` fields are unstable - a: String, + a: String, //~ ERROR unions may not contain fields that need dropping } union U4 { //~ ERROR unions with non-`Copy` fields are unstable - a: T, + a: T, //~ ERROR unions may not contain fields that need dropping } union U5 { //~ ERROR unions with `Drop` implementations are unstable diff --git a/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr b/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr index f59a34e2c81f..1885518a4585 100644 --- a/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr +++ b/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr @@ -31,6 +31,31 @@ LL | | } = note: for more information, see https://github.com/rust-lang/rust/issues/32836 = help: add `#![feature(untagged_unions)]` to the crate attributes to enable -error: aborting due to 3 previous errors +error[E0740]: unions may not contain fields that need dropping + --> $DIR/feature-gate-untagged_unions.rs:10:5 + | +LL | a: String, + | ^^^^^^^^^ + | +note: `std::mem::ManuallyDrop` can be used to wrap the type + --> $DIR/feature-gate-untagged_unions.rs:10:5 + | +LL | a: String, + | ^^^^^^^^^ -For more information about this error, try `rustc --explain E0658`. +error[E0740]: unions may not contain fields that need dropping + --> $DIR/feature-gate-untagged_unions.rs:14:5 + | +LL | a: T, + | ^^^^ + | +note: `std::mem::ManuallyDrop` can be used to wrap the type + --> $DIR/feature-gate-untagged_unions.rs:14:5 + | +LL | a: T, + | ^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0658, E0740. +For more information about an error, try `rustc --explain E0658`. From 55b787e675e26a3b0e7513ec17936210abc42fa1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 21 Oct 2019 10:30:35 +0200 Subject: [PATCH 71/79] keep the root dir clean from debugging --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 1c47ed4c0248..81a472451d77 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,3 @@ config.stamp Session.vim .cargo no_llvm_build -# Generated when dumping Graphviz output for debugging: -/mir_dump/ -/*.dot From d4b365429ddaeb18b42ff47456bd9865fd1d732e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 20 Oct 2019 14:57:21 +0200 Subject: [PATCH 72/79] points the user away from the Allocation type and towards the Memory type --- src/librustc/mir/interpret/allocation.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 87e9b91a86c7..33c53323d96a 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -245,6 +245,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// as a slice. /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods + /// on `InterpCx` instead. #[inline] pub fn get_bytes( &self, @@ -275,6 +277,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// so be sure to actually put data there! /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods + /// on `InterpCx` instead. pub fn get_bytes_mut( &mut self, cx: &impl HasDataLayout, @@ -297,6 +301,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Reads bytes until a `0` is encountered. Will error if the end of the allocation is reached /// before a `0` is found. + /// + /// Most likely, you want to call `Memory::read_c_str` instead of this method. pub fn read_c_str( &self, cx: &impl HasDataLayout, @@ -342,6 +348,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Writes `src` to the memory starting at `ptr.offset`. /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to call `Memory::write_bytes` instead of this method. pub fn write_bytes( &mut self, cx: &impl HasDataLayout, @@ -363,6 +370,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Sets `count` bytes starting at `ptr.offset` with `val`. Basically `memset`. /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to call `Memory::write_bytes` instead of this method. pub fn write_repeat( &mut self, cx: &impl HasDataLayout, @@ -386,6 +394,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// pointers being valid for ZSTs. /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to call `InterpCx::read_scalar` instead of this method. pub fn read_scalar( &self, cx: &impl HasDataLayout, @@ -424,6 +433,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Reads a pointer-sized scalar. /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to call `InterpCx::read_scalar` instead of this method. pub fn read_ptr_sized( &self, cx: &impl HasDataLayout, @@ -441,6 +451,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// pointers being valid for ZSTs. /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to call `InterpCx::write_scalar` instead of this method. pub fn write_scalar( &mut self, cx: &impl HasDataLayout, @@ -483,6 +494,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Writes a pointer-sized scalar. /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to call `InterpCx::write_scalar` instead of this method. pub fn write_ptr_sized( &mut self, cx: &impl HasDataLayout, From f6d70b42b8d6d299fe5621ac9170f69c511c8ddc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 21 Oct 2019 11:08:37 +0200 Subject: [PATCH 73/79] remove write_repeat; it is subsumed by the new write_bytes --- src/librustc/mir/interpret/allocation.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 33c53323d96a..796d293e2c63 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -367,25 +367,6 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { Ok(()) } - /// Sets `count` bytes starting at `ptr.offset` with `val`. Basically `memset`. - /// - /// It is the caller's responsibility to check bounds and alignment beforehand. - /// Most likely, you want to call `Memory::write_bytes` instead of this method. - pub fn write_repeat( - &mut self, - cx: &impl HasDataLayout, - ptr: Pointer, - val: u8, - count: Size - ) -> InterpResult<'tcx> - { - let bytes = self.get_bytes_mut(cx, ptr, count)?; - for b in bytes { - *b = val; - } - Ok(()) - } - /// Reads a *non-ZST* scalar. /// /// ZSTs can't be read for two reasons: From ac6daed384d17abd31f84fc8205c21eee6a248be Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 20 Oct 2019 15:54:53 +1100 Subject: [PATCH 74/79] Remove many unnecessary trait derivations. --- src/librustc/dep_graph/graph.rs | 2 +- src/librustc/hir/map/definitions.rs | 1 - src/librustc/hir/mod.rs | 10 ++++------ src/librustc/hir/ptr.rs | 2 +- src/librustc/infer/canonical/mod.rs | 2 +- src/librustc/infer/combine.rs | 2 +- src/librustc/infer/mod.rs | 2 +- src/librustc/infer/nll_relate/mod.rs | 2 +- src/librustc/infer/region_constraints/mod.rs | 2 +- src/librustc/middle/cstore.rs | 2 +- src/librustc/middle/mem_categorization.rs | 8 ++++---- src/librustc/middle/stability.rs | 2 +- src/librustc/mir/interpret/value.rs | 4 ++-- src/librustc/mir/mod.rs | 20 +++++++++---------- src/librustc/mir/mono.rs | 6 +++--- src/librustc/mir/visit.rs | 2 +- src/librustc/session/config.rs | 8 ++++---- src/librustc/session/search_paths.rs | 3 +-- src/librustc/traits/query/mod.rs | 2 +- .../query/type_op/implied_outlives_bounds.rs | 2 +- src/librustc/ty/binding.rs | 2 +- src/librustc/ty/context.rs | 4 ++-- src/librustc/ty/error.rs | 1 - src/librustc/ty/fold.rs | 1 - src/librustc/ty/mod.rs | 8 ++++---- src/librustc/ty/query/plumbing.rs | 4 ++-- src/librustc/ty/sty.rs | 8 +++----- src/librustc_codegen_llvm/llvm/ffi.rs | 3 +-- .../graph/implementation/mod.rs | 4 ++-- src/librustc_data_structures/stable_hasher.rs | 4 ++-- src/librustc_data_structures/sync.rs | 2 +- src/librustc_data_structures/thin_vec.rs | 2 +- src/librustc_data_structures/tiny_list.rs | 4 ++-- .../transitive_relation.rs | 6 +++--- .../borrow_check/nll/constraints/mod.rs | 2 +- .../borrow_check/nll/region_infer/values.rs | 2 +- .../borrow_check/nll/universal_regions.rs | 2 +- src/librustc_mir/interpret/eval_context.rs | 4 ++-- src/librustc_mir/interpret/memory.rs | 2 +- src/librustc_mir/interpret/operand.rs | 6 +++--- src/librustc_mir/monomorphize/collector.rs | 2 +- src/librustc_target/spec/mod.rs | 6 ++---- src/librustdoc/clean/mod.rs | 7 +++---- src/libsyntax/ast.rs | 6 +++--- src/libsyntax/attr/builtin.rs | 9 ++++----- src/libsyntax/ptr.rs | 1 - src/libsyntax/source_map.rs | 2 +- src/libsyntax_expand/mbe.rs | 2 +- src/libsyntax_pos/lib.rs | 2 +- 49 files changed, 89 insertions(+), 103 deletions(-) diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index 337cdddc432c..0104507f7020 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs @@ -35,7 +35,7 @@ impl DepNodeIndex { pub const INVALID: DepNodeIndex = DepNodeIndex::MAX; } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(PartialEq)] pub enum DepNodeColor { Red, Green(DepNodeIndex) diff --git a/src/librustc/hir/map/definitions.rs b/src/librustc/hir/map/definitions.rs index d2732c92d268..d95637c3b986 100644 --- a/src/librustc/hir/map/definitions.rs +++ b/src/librustc/hir/map/definitions.rs @@ -599,7 +599,6 @@ macro_rules! define_global_metadata_kind { (pub enum GlobalMetaDataKind { $($variant:ident),* }) => ( - #[derive(Clone, Copy, Debug, Hash, RustcEncodable, RustcDecodable)] pub enum GlobalMetaDataKind { $($variant),* } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 568e051aaf08..364a8ace1aac 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1077,7 +1077,7 @@ impl Mutability { } } -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Hash, HashStable)] +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable)] pub enum BinOpKind { /// The `+` operator (addition). Add, @@ -1211,7 +1211,7 @@ impl Into for BinOpKind { pub type BinOp = Spanned; -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Hash, HashStable)] +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable)] pub enum UnOp { /// The `*` operator (deferencing). UnDeref, @@ -1388,8 +1388,7 @@ impl Body { } /// The type of source expression that caused this generator to be created. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, HashStable, - RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +#[derive(Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable, Debug, Copy)] pub enum GeneratorKind { /// An explicit `async` block or the body of an async function. Async(AsyncGeneratorKind), @@ -1412,8 +1411,7 @@ impl fmt::Display for GeneratorKind { /// /// This helps error messages but is also used to drive coercions in /// type-checking (see #60424). -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, HashStable, - RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +#[derive(Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable, Debug, Copy)] pub enum AsyncGeneratorKind { /// An explicit `async` block written by the user. Block, diff --git a/src/librustc/hir/ptr.rs b/src/librustc/hir/ptr.rs index 8cdcf5202fcd..7ee461a859bd 100644 --- a/src/librustc/hir/ptr.rs +++ b/src/librustc/hir/ptr.rs @@ -11,7 +11,7 @@ use rustc_serialize::{Encodable, Decodable, Encoder, Decoder}; use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; /// An owned smart pointer. -#[derive(Hash, PartialEq, Eq)] +#[derive(PartialEq, Eq)] pub struct P { ptr: Box } diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index 562a463ded86..de1867596c8c 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -73,7 +73,7 @@ pub struct CanonicalVarValues<'tcx> { /// various parts of it with canonical variables. This struct stores /// those replaced bits to remember for when we process the query /// result. -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] +#[derive(Clone, Debug)] pub struct OriginalQueryValues<'tcx> { /// Map from the universes that appear in the query to the /// universes in the caller context. For the time being, we only diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 6f73275d455f..f06dbc72cd96 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -53,7 +53,7 @@ pub struct CombineFields<'infcx, 'tcx> { pub obligations: PredicateObligations<'tcx>, } -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +#[derive(Copy, Clone, Debug)] pub enum RelationDir { SubtypeOf, SupertypeOf, EqTo } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index af74d1357243..f4ed7dac1f7c 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -407,7 +407,7 @@ pub enum RegionVariableOrigin { NLL(NLLRegionVariableOrigin), } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug)] pub enum NLLRegionVariableOrigin { /// During NLL region processing, we create variables for free /// regions that we encounter in the function signature and diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs index 4649f3f9567e..9a835cf5c093 100644 --- a/src/librustc/infer/nll_relate/mod.rs +++ b/src/librustc/infer/nll_relate/mod.rs @@ -32,7 +32,7 @@ use crate::mir::interpret::ConstValue; use rustc_data_structures::fx::FxHashMap; use std::fmt::Debug; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(PartialEq)] pub enum NormalizationStrategy { Lazy, Eager, diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index b4b4d1fe3e1f..8c6a7c9a376a 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -116,7 +116,7 @@ pub struct RegionConstraintData<'tcx> { } /// Represents a constraint that influences the inference process. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] pub enum Constraint<'tcx> { /// A region variable is a subregion of another. VarSubVar(RegionVid, RegionVid), diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 2170a288c922..ec1e32988a60 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -117,7 +117,7 @@ pub struct NativeLibrary { pub wasm_import_module: Option, } -#[derive(Clone, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)] pub struct ForeignModule { pub foreign_items: Vec, pub def_id: DefId, diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 8f79b8aa2952..cbf336fdbe2f 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -102,7 +102,7 @@ pub struct Upvar { } // different kinds of pointers: -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum PointerKind<'tcx> { /// `Box` Unique, @@ -116,7 +116,7 @@ pub enum PointerKind<'tcx> { // We use the term "interior" to mean "something reachable from the // base without a pointer dereference", e.g., a field -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, PartialEq)] pub enum InteriorKind { InteriorField(FieldIndex), InteriorElement(InteriorOffsetKind), @@ -139,13 +139,13 @@ impl Hash for FieldIndex { } } -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, PartialEq)] pub enum InteriorOffsetKind { Index, // e.g., `array_expr[index_expr]` Pattern, // e.g., `fn foo([_, a, _, _]: [A; 4]) { ... }` } -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Debug)] pub enum MutabilityCategory { McImmutable, // Immutable. McDeclared, // Directly declared as mutable. diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 302c11f309d9..0095f89337c4 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -25,7 +25,7 @@ use crate::util::nodemap::{FxHashSet, FxHashMap}; use std::mem::replace; use std::cmp::Ordering; -#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Copy, Debug, Eq, Hash)] +#[derive(PartialEq, Clone, Copy, Debug)] pub enum StabilityLevel { Unstable, Stable, diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index bbf00cc23ae8..04b1a86d4d02 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -9,7 +9,7 @@ use crate::hir::def_id::DefId; use super::{InterpResult, Pointer, PointerArithmetic, Allocation, AllocId, sign_extend, truncate}; /// Represents the result of a raw const operation, pre-validation. -#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash, HashStable)] +#[derive(Clone, HashStable)] pub struct RawConst<'tcx> { // the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory` // (so you can use `AllocMap::unwrap_memory`). @@ -487,7 +487,7 @@ impl From> for Scalar { } } -#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, Eq, PartialEq, RustcEncodable, RustcDecodable)] pub enum ScalarMaybeUndef { Scalar(Scalar), Undef, diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 9ac1465cb0ba..ccf64c51e133 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -468,7 +468,7 @@ impl rustc_serialize::UseSpecializedDecodable for ClearCrossCrate< /// Grouped information about the source code origin of a MIR entity. /// Intended to be inspected by diagnostics and debuginfo. /// Most passes can work with it as a whole, within a single function. -#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, HashStable)] +#[derive(Copy, Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable)] pub struct SourceInfo { /// The source span for the AST pertaining to this MIR entity. pub span: Span, @@ -608,7 +608,7 @@ pub enum LocalKind { ReturnPointer, } -#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct VarBindingForm<'tcx> { /// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`? pub binding_mode: ty::BindingMode, @@ -630,7 +630,7 @@ pub struct VarBindingForm<'tcx> { pub pat_span: Span, } -#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub enum BindingForm<'tcx> { /// This is a binding for a non-`self` binding, or a `self` that has an explicit type. Var(VarBindingForm<'tcx>), @@ -641,7 +641,7 @@ pub enum BindingForm<'tcx> { } /// Represents what type of implicit self a function has, if any. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable)] pub enum ImplicitSelfKind { /// Represents a `fn x(self);`. Imm, @@ -2392,7 +2392,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { /// this does not necessarily mean that they are "==" in Rust -- in /// particular one must be wary of `NaN`! -#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] pub struct Constant<'tcx> { pub span: Span, @@ -2438,7 +2438,7 @@ pub struct Constant<'tcx> { /// The first will lead to the constraint `w: &'1 str` (for some /// inferred region `'1`). The second will lead to the constraint `w: /// &'static str`. -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct UserTypeProjections { pub(crate) contents: Vec<(UserTypeProjection, Span)>, } @@ -2515,7 +2515,7 @@ impl<'tcx> UserTypeProjections { /// * `let (x, _): T = ...` -- here, the `projs` vector would contain /// `field[0]` (aka `.0`), indicating that the type of `s` is /// determined by finding the type of the `.0` field from `T`. -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct UserTypeProjection { pub base: UserTypeAnnotationIndex, pub projs: Vec, @@ -2724,7 +2724,7 @@ impl Location { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] pub enum UnsafetyViolationKind { General, /// Permitted both in `const fn`s and regular `fn`s. @@ -2733,7 +2733,7 @@ pub enum UnsafetyViolationKind { BorrowPacked(hir::HirId), } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] pub struct UnsafetyViolation { pub source_info: SourceInfo, pub description: InternedString, @@ -2741,7 +2741,7 @@ pub struct UnsafetyViolation { pub kind: UnsafetyViolationKind, } -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)] pub struct UnsafetyCheckResult { /// Violations that are propagated *upwards* from this function. pub violations: Lrc<[UnsafetyViolation]>, diff --git a/src/librustc/mir/mono.rs b/src/librustc/mir/mono.rs index 265ac975ed7a..eeb997d75ca5 100644 --- a/src/librustc/mir/mono.rs +++ b/src/librustc/mir/mono.rs @@ -15,7 +15,7 @@ use std::fmt; use std::hash::Hash; /// Describes how a monomorphization will be instantiated in object files. -#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] +#[derive(PartialEq)] pub enum InstantiationMode { /// There will be exactly one instance of the given MonoItem. It will have /// external linkage so that it can be linked to from other codegen units. @@ -251,7 +251,7 @@ pub struct CodegenUnit<'tcx> { size_estimate: Option, } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)] pub enum Linkage { External, AvailableExternally, @@ -280,7 +280,7 @@ impl_stable_hash_for!(enum self::Linkage { Common }); -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum Visibility { Default, Hidden, diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index fef406e89878..427540d72758 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -947,7 +947,7 @@ impl<'tcx> MirVisitable<'tcx> for Option> { /// Extra information passed to `visit_ty` and friends to give context /// about where the type etc appears. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Debug)] pub enum TyContext { LocalDecl { /// The index of the local variable we are visiting. diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 675e3bbd002b..491c636374bc 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -67,7 +67,7 @@ impl_stable_hash_via_hash!(OptLevel); /// This is what the `LtoCli` values get mapped to after resolving defaults and /// and taking other command line options into account. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] +#[derive(Clone, PartialEq)] pub enum Lto { /// Don't do any LTO whatsoever No, @@ -301,10 +301,10 @@ impl OutputTypes { /// Use tree-based collections to cheaply get a deterministic `Hash` implementation. /// *Do not* switch `BTreeMap` or `BTreeSet` out for an unsorted container type! That /// would break dependency tracking for command-line arguments. -#[derive(Clone, Hash)] +#[derive(Clone)] pub struct Externs(BTreeMap); -#[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct ExternEntry { pub locations: BTreeSet>, pub is_private_dep: bool @@ -464,7 +464,7 @@ pub enum PrintRequest { NativeStaticLibs, } -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone)] pub enum BorrowckMode { Mir, Migrate, diff --git a/src/librustc/session/search_paths.rs b/src/librustc/session/search_paths.rs index 3695f0a82f4b..949dad751a10 100644 --- a/src/librustc/session/search_paths.rs +++ b/src/librustc/session/search_paths.rs @@ -1,5 +1,4 @@ use std::path::{Path, PathBuf}; -use rustc_macros::HashStable; use crate::session::{early_error, config}; use crate::session::filesearch::make_target_lib_path; @@ -10,7 +9,7 @@ pub struct SearchPath { pub files: Vec, } -#[derive(Eq, PartialEq, Clone, Copy, Debug, PartialOrd, Ord, Hash, HashStable)] +#[derive(PartialEq, Clone, Copy, Debug, HashStable)] pub enum PathKind { Native, Crate, diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index 112a1d0e09c9..f6ea77dc5cc6 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs @@ -40,7 +40,7 @@ pub type CanonicalTypeOpProvePredicateGoal<'tcx> = pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::normalize::Normalize>>; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug)] pub struct NoSolution; pub type Fallible = Result; diff --git a/src/librustc/traits/query/type_op/implied_outlives_bounds.rs b/src/librustc/traits/query/type_op/implied_outlives_bounds.rs index 12a834fbda6b..7aa98703411b 100644 --- a/src/librustc/traits/query/type_op/implied_outlives_bounds.rs +++ b/src/librustc/traits/query/type_op/implied_outlives_bounds.rs @@ -3,7 +3,7 @@ use crate::traits::query::outlives_bounds::OutlivesBound; use crate::traits::query::Fallible; use crate::ty::{ParamEnvAnd, Ty, TyCtxt}; -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Clone, Debug)] pub struct ImpliedOutlivesBounds<'tcx> { pub ty: Ty<'tcx>, } diff --git a/src/librustc/ty/binding.rs b/src/librustc/ty/binding.rs index 1290141b0a6b..5570144489cd 100644 --- a/src/librustc/ty/binding.rs +++ b/src/librustc/ty/binding.rs @@ -2,7 +2,7 @@ use crate::hir::BindingAnnotation::*; use crate::hir::BindingAnnotation; use crate::hir::Mutability; -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)] pub enum BindingMode { BindByReference(Mutability), BindByValue(Mutability), diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 665d4c2d0696..1027cbd8322b 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -827,7 +827,7 @@ rustc_index::newtype_index! { pub type CanonicalUserTypeAnnotations<'tcx> = IndexVec>; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct CanonicalUserTypeAnnotation<'tcx> { pub user_ty: CanonicalUserType<'tcx>, pub span: Span, @@ -899,7 +899,7 @@ impl CanonicalUserType<'tcx> { /// A user-given type annotation attached to a constant. These arise /// from constants that are named via paths, like `Foo::::new` and /// so forth. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable)] pub enum UserType<'tcx> { Ty(Ty<'tcx>), diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index 5851a48a8d37..3395715f67ff 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -51,7 +51,6 @@ pub enum TypeError<'tcx> { IntrinsicCast, } -#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] pub enum UnconstrainedNumeric { UnconstrainedFloat, UnconstrainedInt, diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 5192075c26e9..0f8061ef3dd4 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -718,7 +718,6 @@ impl<'tcx> TyCtxt<'tcx> { // vars. See comment on `shift_vars_through_binders` method in // `subst.rs` for more details. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] enum Direction { In, Out, diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index d46ab3769ad5..d377b7328e80 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -159,7 +159,7 @@ impl AssocItemContainer { /// The "header" of an impl is everything outside the body: a Self type, a trait /// ref (in the case of a trait impl), and a set of predicates (from the /// bounds / where-clauses). -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Debug)] pub struct ImplHeader<'tcx> { pub impl_def_id: DefId, pub self_ty: Ty<'tcx>, @@ -195,7 +195,7 @@ pub struct AssocItem { pub method_has_self_argument: bool, } -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, Debug, HashStable)] pub enum AssocKind { Const, Method, @@ -331,7 +331,7 @@ impl Visibility { } } -#[derive(Copy, Clone, PartialEq, Eq, RustcDecodable, RustcEncodable, Hash, HashStable)] +#[derive(Copy, Clone, PartialEq, RustcDecodable, RustcEncodable, HashStable)] pub enum Variance { Covariant, // T <: T iff A <: B -- e.g., function return type Invariant, // T <: T iff B == A -- e.g., type of mutable cell @@ -752,7 +752,7 @@ pub struct UpvarId { pub closure_expr_id: LocalDefId, } -#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable, Copy, HashStable)] +#[derive(Clone, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, HashStable)] pub enum BorrowKind { /// Data must be immutable and is aliasable. ImmBorrow, diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index 7f05e553bc97..41b4883793b5 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -801,7 +801,7 @@ macro_rules! define_queries_inner { } #[allow(nonstandard_style)] - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + #[derive(Clone, Copy)] pub enum QueryName { $($name),* } @@ -819,7 +819,7 @@ macro_rules! define_queries_inner { } #[allow(nonstandard_style)] - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] + #[derive(Clone, Debug)] pub enum Query<$tcx> { $($(#[$attr])* $name($K)),* } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 4af73fa389a7..1e872eb8e456 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -304,8 +304,7 @@ static_assert_size!(TyKind<'_>, 24); /// type parameters is similar, but the role of CK and CS are /// different. CK represents the "yield type" and CS represents the /// "return type" of the generator. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, - RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, Debug)] pub struct ClosureSubsts<'tcx> { /// Lifetime and type parameters from the enclosing function, /// concatenated with the types of the upvars. @@ -392,8 +391,7 @@ impl<'tcx> ClosureSubsts<'tcx> { } /// Similar to `ClosureSubsts`; see the above documentation for more. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, - RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, Debug)] pub struct GeneratorSubsts<'tcx> { pub substs: SubstsRef<'tcx>, } @@ -1035,7 +1033,7 @@ impl<'tcx> ProjectionTy<'tcx> { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, Debug)] pub struct GenSig<'tcx> { pub yield_ty: Ty<'tcx>, pub return_ty: Ty<'tcx>, diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index a2313b933a6c..c69942ef3f2d 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -50,7 +50,7 @@ pub enum CallConv { } /// LLVMRustLinkage -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(PartialEq)] #[repr(C)] pub enum Linkage { ExternalLinkage = 0, @@ -67,7 +67,6 @@ pub enum Linkage { } // LLVMRustVisibility -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[repr(C)] pub enum Visibility { Default = 0, diff --git a/src/librustc_data_structures/graph/implementation/mod.rs b/src/librustc_data_structures/graph/implementation/mod.rs index c438a8558a70..9fdcea6df88c 100644 --- a/src/librustc_data_structures/graph/implementation/mod.rs +++ b/src/librustc_data_structures/graph/implementation/mod.rs @@ -60,10 +60,10 @@ impl SnapshotVecDelegate for Edge { fn reverse(_: &mut Vec>, _: ()) {} } -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Debug)] pub struct NodeIndex(pub usize); -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Debug)] pub struct EdgeIndex(pub usize); pub const INVALID_EDGE_INDEX: EdgeIndex = EdgeIndex(usize::MAX); diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index 78494401c0ba..092208cfe1db 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs @@ -169,7 +169,7 @@ pub trait HashStable { /// example, for DefId that can be converted to a DefPathHash. This is used for /// bringing maps into a predictable order before hashing them. pub trait ToStableHashKey { - type KeyType: Ord + Clone + Sized + HashStable; + type KeyType: Ord + Sized + HashStable; fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType; } @@ -516,7 +516,7 @@ pub fn hash_stable_hashmap( where K: Eq, V: HashStable, R: BuildHasher, - SK: HashStable + Ord + Clone, + SK: HashStable + Ord, F: Fn(&K, &HCX) -> SK, { let mut entries: Vec<_> = map.iter() diff --git a/src/librustc_data_structures/sync.rs b/src/librustc_data_structures/sync.rs index 9622c290039d..f09474ff4d34 100644 --- a/src/librustc_data_structures/sync.rs +++ b/src/librustc_data_structures/sync.rs @@ -738,7 +738,7 @@ impl Clone for RwLock { /// A type which only allows its inner value to be used in one thread. /// It will panic if it is used on multiple threads. -#[derive(Copy, Clone, Hash, Debug, Eq, PartialEq)] +#[derive(Debug)] pub struct OneThread { #[cfg(parallel_compiler)] thread: thread::ThreadId, diff --git a/src/librustc_data_structures/thin_vec.rs b/src/librustc_data_structures/thin_vec.rs index 93a8b7f525ff..d97da489db8d 100644 --- a/src/librustc_data_structures/thin_vec.rs +++ b/src/librustc_data_structures/thin_vec.rs @@ -3,7 +3,7 @@ use crate::stable_hasher::{StableHasher, HashStable}; /// A vector type optimized for cases where this size is usually 0 (cf. `SmallVector`). /// The `Option>` wrapping allows us to represent a zero sized vector with `None`, /// which uses only a single (null) pointer. -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct ThinVec(Option>>); impl ThinVec { diff --git a/src/librustc_data_structures/tiny_list.rs b/src/librustc_data_structures/tiny_list.rs index ea771d9f20f8..371f0f6fa0b4 100644 --- a/src/librustc_data_structures/tiny_list.rs +++ b/src/librustc_data_structures/tiny_list.rs @@ -14,7 +14,7 @@ #[cfg(test)] mod tests; -#[derive(Clone, Hash, Debug, PartialEq)] +#[derive(Clone)] pub struct TinyList { head: Option> } @@ -80,7 +80,7 @@ impl TinyList { } } -#[derive(Clone, Hash, Debug, PartialEq)] +#[derive(Clone)] struct Element { data: T, next: Option>>, diff --git a/src/librustc_data_structures/transitive_relation.rs b/src/librustc_data_structures/transitive_relation.rs index f0a9c3afc68b..a3926c155517 100644 --- a/src/librustc_data_structures/transitive_relation.rs +++ b/src/librustc_data_structures/transitive_relation.rs @@ -11,7 +11,7 @@ use std::mem; mod tests; #[derive(Clone, Debug)] -pub struct TransitiveRelation { +pub struct TransitiveRelation { // List of elements. This is used to map from a T to a usize. elements: Vec, @@ -35,7 +35,7 @@ pub struct TransitiveRelation { } // HACK(eddyb) manual impl avoids `Default` bound on `T`. -impl Default for TransitiveRelation { +impl Default for TransitiveRelation { fn default() -> Self { TransitiveRelation { elements: Default::default(), @@ -46,7 +46,7 @@ impl Default for TransitiveRelation { } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, RustcEncodable, RustcDecodable, Debug)] struct Index(usize); #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)] diff --git a/src/librustc_mir/borrow_check/nll/constraints/mod.rs b/src/librustc_mir/borrow_check/nll/constraints/mod.rs index 93113753c630..8a242b7ee25b 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/mod.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/mod.rs @@ -71,7 +71,7 @@ impl Index for OutlivesConstraintSet { } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct OutlivesConstraint { // NB. The ordering here is not significant for correctness, but // it is for convenience. Before we dump the constraints in the diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs index 6acbff76bdc9..7a86536573dc 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs @@ -129,7 +129,7 @@ rustc_index::newtype_index! { /// An individual element in a region value -- the value of a /// particular region variable consists of a set of these elements. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Debug)] crate enum RegionElement { /// A point in the control-flow graph. Location(Location), diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 5f6951856434..fd1f333010ad 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -146,7 +146,7 @@ struct UniversalRegionIndices<'tcx> { indices: FxHashMap, RegionVid>, } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Debug, PartialEq)] pub enum RegionClassification { /// A **global** region is one that can be named from /// anywhere. There is only one, `'static`. diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 06fdd407951c..2ab7c41bb787 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -91,7 +91,7 @@ pub struct Frame<'mir, 'tcx, Tag=(), Extra=()> { pub extra: Extra, } -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Eq, PartialEq)] pub enum StackPopCleanup { /// Jump to the next block in the caller, or cause UB if None (that's a function /// that may never return). Also store layout of return place so @@ -113,7 +113,7 @@ pub struct LocalState<'tcx, Tag=(), Id=AllocId> { } /// Current value of a local variable -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, PartialEq, Eq)] pub enum LocalValue { /// This local is not currently alive, and cannot be used at all. Dead, diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 924474c53175..269e7a5d6efe 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -22,7 +22,7 @@ use super::{ Machine, AllocMap, MayLeak, ErrorHandled, CheckInAllocMsg, }; -#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] +#[derive(Debug, PartialEq, Copy, Clone)] pub enum MemoryKind { /// Error if deallocated except during a stack pop Stack, diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 4d9be55945e0..a0f50f21352b 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -26,7 +26,7 @@ pub use rustc::mir::interpret::ScalarMaybeUndef; /// operations and fat pointers. This idea was taken from rustc's codegen. /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely /// defined on `Immediate`, and do not have to work with a `Place`. -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Immediate { Scalar(ScalarMaybeUndef), ScalarPair(ScalarMaybeUndef, ScalarMaybeUndef), @@ -123,7 +123,7 @@ impl<'tcx, Tag> ::std::ops::Deref for ImmTy<'tcx, Tag> { /// An `Operand` is the result of computing a `mir::Operand`. It can be immediate, /// or still in memory. The latter is an optimization, to delay reading that chunk of /// memory and to avoid having to store arbitrary-sized data here. -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Operand { Immediate(Immediate), Indirect(MemPlace), @@ -153,7 +153,7 @@ impl Operand { } } -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct OpTy<'tcx, Tag=()> { op: Operand, // Keep this private, it helps enforce invariants pub layout: TyLayout<'tcx>, diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index ee7452d3e8b4..5e31b80bec6d 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -199,7 +199,7 @@ use rustc_data_structures::sync::{MTRef, MTLock, ParallelIterator, par_iter}; use std::iter; -#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] +#[derive(PartialEq)] pub enum MonoItemCollectionMode { Eager, Lazy diff --git a/src/librustc_target/spec/mod.rs b/src/librustc_target/spec/mod.rs index c5277c4f90e7..d91588db1834 100644 --- a/src/librustc_target/spec/mod.rs +++ b/src/librustc_target/spec/mod.rs @@ -71,8 +71,7 @@ mod riscv_base; mod wasm32_base; mod vxworks_base; -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash, - RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum LinkerFlavor { Em, Gcc, @@ -82,8 +81,7 @@ pub enum LinkerFlavor { PtxLinker, } -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash, - RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum LldFlavor { Wasm, Ld64, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c355f661410e..09c9757dc4d0 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1307,7 +1307,7 @@ impl Clean> for ty::RegionKind { } } -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, Debug)] pub enum WherePredicate { BoundPredicate { ty: Type, bounds: Vec }, RegionPredicate { lifetime: Lifetime, bounds: Vec }, @@ -1589,7 +1589,7 @@ impl Clean for hir::GenericParam { } // maybe use a Generic enum and use Vec? -#[derive(Clone, PartialEq, Eq, Debug, Default, Hash)] +#[derive(Clone, Debug, Default)] pub struct Generics { pub params: Vec, pub where_predicates: Vec, @@ -3847,7 +3847,7 @@ impl Clean for hir::Mutability { } } -#[derive(Clone, PartialEq, Eq, Copy, Debug, Hash)] +#[derive(Clone, PartialEq, Debug)] pub enum ImplPolarity { Positive, Negative, @@ -4506,7 +4506,6 @@ struct RegionDeps<'tcx> { smaller: FxHashSet> } -#[derive(Eq, PartialEq, Hash, Debug)] enum SimpleBound { TraitBound(Vec, Vec, Vec, hir::TraitBoundModifier), Outlives(Lifetime), diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 0408d7d1bc2a..8be7f4478fa0 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1305,7 +1305,7 @@ impl MacroDef { } } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy, Hash, PartialEq)] +#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy)] pub enum StrStyle { /// A regular string, like `"foo"`. Cooked, @@ -1327,7 +1327,7 @@ pub struct Lit { pub span: Span, } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy, Hash, PartialEq)] +#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy)] pub enum LitIntType { Signed(IntTy), Unsigned(UintTy), @@ -1337,7 +1337,7 @@ pub enum LitIntType { /// Literal kind. /// /// E.g., `"foo"`, `42`, `12.34`, or `bool`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Hash, PartialEq)] +#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub enum LitKind { /// A string literal (`"foo"`). Str(Symbol, StrStyle), diff --git a/src/libsyntax/attr/builtin.rs b/src/libsyntax/attr/builtin.rs index 28b61c5aa77c..84c86c9651fc 100644 --- a/src/libsyntax/attr/builtin.rs +++ b/src/libsyntax/attr/builtin.rs @@ -84,7 +84,7 @@ fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) { } } -#[derive(Copy, Clone, Hash, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)] pub enum InlineAttr { None, Hint, @@ -92,7 +92,7 @@ pub enum InlineAttr { Never, } -#[derive(Copy, Clone, Hash, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Clone, RustcEncodable, RustcDecodable)] pub enum OptimizeAttr { None, Speed, @@ -624,8 +624,7 @@ pub fn eval_condition(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F) } } - -#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)] +#[derive(RustcEncodable, RustcDecodable, Clone)] pub struct Deprecation { pub since: Option, pub note: Option, @@ -749,7 +748,7 @@ pub enum ReprAttr { ReprAlign(u32), } -#[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)] +#[derive(Eq, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)] pub enum IntType { SignedInt(ast::IntTy), UnsignedInt(ast::UintTy) diff --git a/src/libsyntax/ptr.rs b/src/libsyntax/ptr.rs index 7300ce249548..d987dc855b61 100644 --- a/src/libsyntax/ptr.rs +++ b/src/libsyntax/ptr.rs @@ -35,7 +35,6 @@ use rustc_serialize::{Encodable, Decodable, Encoder, Decoder}; use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; /// An owned smart pointer. -#[derive(Hash, PartialEq, Eq)] pub struct P { ptr: Box } diff --git a/src/libsyntax/source_map.rs b/src/libsyntax/source_map.rs index 1501adc59710..a1d147637e27 100644 --- a/src/libsyntax/source_map.rs +++ b/src/libsyntax/source_map.rs @@ -41,7 +41,7 @@ pub fn original_sp(sp: Span, enclosing_sp: Span) -> Span { } } -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy)] pub struct Spanned { pub node: T, pub span: Span, diff --git a/src/libsyntax_expand/mbe.rs b/src/libsyntax_expand/mbe.rs index 453fe94f1de1..d0f790638efa 100644 --- a/src/libsyntax_expand/mbe.rs +++ b/src/libsyntax_expand/mbe.rs @@ -73,7 +73,7 @@ impl KleeneToken { /// A Kleene-style [repetition operator](http://en.wikipedia.org/wiki/Kleene_star) /// for token sequences. -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)] enum KleeneOp { /// Kleene star (`*`) for zero or more repetitions ZeroOrMore, diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 7f7c5cb2e454..7e42b931961f 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -1311,7 +1311,7 @@ pub struct BytePos(pub u32); /// A character offset. Because of multibyte UTF-8 characters, a byte offset /// is not equivalent to a character offset. The `SourceMap` will convert `BytePos` /// values to `CharPos` values as necessary. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct CharPos(pub usize); // FIXME: lots of boilerplate in these impls, but so far my attempts to fix From 3de7698e124f5fc2bbde48fdfce41410747bcc00 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Mon, 21 Oct 2019 13:13:43 +0100 Subject: [PATCH 75/79] Fix typo from #65214 --- src/libstd/panic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 24c693790e84..577673b7e405 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -264,7 +264,7 @@ impl RefUnwindSafe for atomic::AtomicI128 {} #[cfg(target_has_atomic_load_store = "ptr")] #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] impl RefUnwindSafe for atomic::AtomicUsize {} -#[cfg(target_hastarget_has_atomic_load_store_atomic = "8")] +#[cfg(target_has_atomic_load_store = "8")] #[unstable(feature = "integer_atomics", issue = "32976")] impl RefUnwindSafe for atomic::AtomicU8 {} #[cfg(target_has_atomic_load_store = "16")] From d689c709ea6e942de1687d918eaee541610aa86d Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Mon, 21 Oct 2019 15:35:54 +0200 Subject: [PATCH 76/79] improve readability of is_power_of_two --- src/libcore/num/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 82c1943b7d05..e14c886aec39 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -3750,7 +3750,7 @@ assert!(!10", stringify!($SelfT), ".is_power_of_two());", $EndFeature, " #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub const fn is_power_of_two(self) -> bool { - ((self.wrapping_sub(1)) & self == 0) & !(self == 0) + self.count_ones() == 1 } } From ebc9a1ab10ff813664778e572eaeef4a9c6fcf4f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 21 Oct 2019 10:36:49 +0200 Subject: [PATCH 77/79] expand comment --- .gitignore | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 81a472451d77..487867c375d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ -# This file should only ignore things that are generated during a build, -# generated by common IDEs, and optional files controlled by the user -# that affect the build (such as config.toml). +# This file should only ignore things that are generated during a `x.py` build, +# generated by common IDEs, and optional files controlled by the user that +# affect the build (such as config.toml). +# In particular, things like `mir_dump` should not be listed here; they are only +# created during manual debugging and many people like to clean up instead of +# having git ignore such leftovers. You can use `.git/info/exclude` to +# configure your local ignore list. # FIXME: This needs cleanup. *~ .#* @@ -52,3 +56,4 @@ config.stamp Session.vim .cargo no_llvm_build +# Before adding new lines, see the comment at the top. From e9c2685167c4d0f207714d436de8bb95538b7254 Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 21 Oct 2019 11:22:45 +0100 Subject: [PATCH 78/79] Rename `ConstValue::Infer(InferConst::Canonical(..))` to `ConstValue::Bound(..)` --- src/librustc/infer/canonical/canonicalizer.rs | 4 ++-- src/librustc/infer/canonical/mod.rs | 6 ++--- .../infer/canonical/query_response.rs | 7 ++---- src/librustc/infer/freshen.rs | 2 +- src/librustc/infer/nll_relate/mod.rs | 6 ++--- src/librustc/mir/interpret/value.rs | 7 +++++- src/librustc/ty/context.rs | 2 +- src/librustc/ty/flags.rs | 2 +- src/librustc/ty/fold.rs | 23 +++++++++---------- src/librustc/ty/structural_impls.rs | 14 ++++------- src/librustc/ty/sty.rs | 2 -- src/librustc/ty/subst.rs | 6 ++--- src/librustc_codegen_ssa/mir/operand.rs | 1 + src/librustc_mir/interpret/operand.rs | 4 ++-- src/librustc_traits/chalk_context/mod.rs | 4 ++-- .../chalk_context/resolvent_ops.rs | 17 ++++---------- src/test/ui/symbol-names/impl1.legacy.stderr | 4 ++-- 17 files changed, 47 insertions(+), 64 deletions(-) diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index e69719806a81..49a2c90bdbf6 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -468,7 +468,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { ConstValue::Infer(InferConst::Fresh(_)) => { bug!("encountered a fresh const during canonicalization") } - ConstValue::Infer(InferConst::Canonical(debruijn, _)) => { + ConstValue::Bound(debruijn, _) => { if debruijn >= self.binder_index { bug!("escaping bound type during canonicalization") } else { @@ -700,7 +700,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { let var = self.canonical_var(info, const_var.into()); self.tcx().mk_const( ty::Const { - val: ConstValue::Infer(InferConst::Canonical(self.binder_index, var.into())), + val: ConstValue::Bound(self.binder_index, var.into()), ty: self.fold_ty(const_var.ty), } ) diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index 562a463ded86..ef53fc12eed7 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -33,7 +33,7 @@ use std::ops::Index; use syntax::source_map::Span; use crate::ty::fold::TypeFoldable; use crate::ty::subst::GenericArg; -use crate::ty::{self, BoundVar, InferConst, Lift, List, Region, TyCtxt}; +use crate::ty::{self, BoundVar, Lift, List, Region, TyCtxt}; mod canonicalizer; @@ -510,9 +510,7 @@ impl<'tcx> CanonicalVarValues<'tcx> { GenericArgKind::Const(ct) => { tcx.mk_const(ty::Const { ty: ct.ty, - val: ConstValue::Infer( - InferConst::Canonical(ty::INNERMOST, ty::BoundVar::from_u32(i)) - ), + val: ConstValue::Bound(ty::INNERMOST, ty::BoundVar::from_u32(i)), }).into() } }) diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index 95b6a8bc8434..7ad6006012f4 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -26,7 +26,7 @@ use crate::traits::TraitEngine; use crate::traits::{Obligation, ObligationCause, PredicateObligation}; use crate::ty::fold::TypeFoldable; use crate::ty::subst::{GenericArg, GenericArgKind}; -use crate::ty::{self, BoundVar, InferConst, Ty, TyCtxt}; +use crate::ty::{self, BoundVar, Ty, TyCtxt}; use crate::util::captures::Captures; impl<'tcx> InferCtxtBuilder<'tcx> { @@ -493,10 +493,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { } } GenericArgKind::Const(result_value) => { - if let ty::Const { - val: ConstValue::Infer(InferConst::Canonical(debrujin, b)), - .. - } = result_value { + if let ty::Const { val: ConstValue::Bound(debrujin, b), .. } = result_value { // ...in which case we would set `canonical_vars[0]` to `Some(const X)`. // We only allow a `ty::INNERMOST` index in substitutions. diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index 9e9220cc3d8c..1841bd9ea642 100644 --- a/src/librustc/infer/freshen.rs +++ b/src/librustc/infer/freshen.rs @@ -252,7 +252,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { return ct; } - ConstValue::Infer(ty::InferConst::Canonical(..)) | + ConstValue::Bound(..) | ConstValue::Placeholder(_) => { bug!("unexpected const {:?}", ct) } diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs index 4649f3f9567e..a032d0df028d 100644 --- a/src/librustc/infer/nll_relate/mod.rs +++ b/src/librustc/infer/nll_relate/mod.rs @@ -27,7 +27,7 @@ use crate::ty::error::TypeError; use crate::ty::fold::{TypeFoldable, TypeVisitor}; use crate::ty::relate::{self, Relate, RelateResult, TypeRelation}; use crate::ty::subst::GenericArg; -use crate::ty::{self, Ty, TyCtxt, InferConst}; +use crate::ty::{self, Ty, TyCtxt}; use crate::mir::interpret::ConstValue; use rustc_data_structures::fx::FxHashMap; use std::fmt::Debug; @@ -618,7 +618,7 @@ where a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>, ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { - if let ty::Const { val: ConstValue::Infer(InferConst::Canonical(_, _)), .. } = a { + if let ty::Const { val: ConstValue::Bound(..), .. } = a { // FIXME(const_generics): I'm unsure how this branch should actually be handled, // so this is probably not correct. self.infcx.super_combine_consts(self, a, b) @@ -993,7 +993,7 @@ where ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { debug!("TypeGeneralizer::consts(a={:?})", a); - if let ty::Const { val: ConstValue::Infer(InferConst::Canonical(_, _)), .. } = a { + if let ty::Const { val: ConstValue::Bound(..), .. } = a { bug!( "unexpected inference variable encountered in NLL generalization: {:?}", a diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index bbf00cc23ae8..377f9448941f 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -5,6 +5,7 @@ use rustc_apfloat::{Float, ieee::{Double, Single}}; use crate::ty::{Ty, InferConst, ParamConst, layout::{HasDataLayout, Size}, subst::SubstsRef}; use crate::ty::PlaceholderConst; use crate::hir::def_id::DefId; +use crate::ty::{BoundVar, DebruijnIndex}; use super::{InterpResult, Pointer, PointerArithmetic, Allocation, AllocId, sign_extend, truncate}; @@ -28,6 +29,9 @@ pub enum ConstValue<'tcx> { /// Infer the value of the const. Infer(InferConst<'tcx>), + /// Bound const variable, used only when preparing a trait query. + Bound(DebruijnIndex, BoundVar), + /// A placeholder const - universally quantified higher-ranked const. Placeholder(PlaceholderConst), @@ -66,8 +70,9 @@ impl<'tcx> ConstValue<'tcx> { match *self { ConstValue::Param(_) | ConstValue::Infer(_) | + ConstValue::Bound(..) | ConstValue::Placeholder(_) | - ConstValue::ByRef{ .. } | + ConstValue::ByRef { .. } | ConstValue::Unevaluated(..) | ConstValue::Slice { .. } => None, ConstValue::Scalar(val) => Some(val), diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 665d4c2d0696..2674d0bcdb45 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -882,7 +882,7 @@ impl CanonicalUserType<'tcx> { }, GenericArgKind::Const(ct) => match ct.val { - ConstValue::Infer(InferConst::Canonical(debruijn, b)) => { + ConstValue::Bound(debruijn, b) => { // We only allow a `ty::INNERMOST` index in substitutions. assert_eq!(debruijn, ty::INNERMOST); cvar == b diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index d3a3f51cfa47..cb1fb4f685de 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -240,10 +240,10 @@ impl FlagComputation { self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES | TypeFlags::HAS_CT_INFER); match infer { InferConst::Fresh(_) => {} - InferConst::Canonical(debruijn, _) => self.add_binder(debruijn), InferConst::Var(_) => self.add_flags(TypeFlags::KEEP_IN_LOCAL_TCX), } } + ConstValue::Bound(debruijn, _) => self.add_binder(debruijn), ConstValue::Param(_) => { self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES | TypeFlags::HAS_PARAMS); } diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index a95ed589c3e2..a69f1a623b69 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -521,10 +521,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> { } fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - if let ty::Const { - val: ConstValue::Infer(ty::InferConst::Canonical(debruijn, bound_const)), - ty, - } = *ct { + if let ty::Const { val: ConstValue::Bound(debruijn, bound_const), ty } = *ct { if debruijn == self.current_index { let fld_c = &mut self.fld_c; let ct = fld_c(bound_const, ty); @@ -570,7 +567,10 @@ impl<'tcx> TyCtxt<'tcx> { // identity for bound types and consts let fld_t = |bound_ty| self.mk_ty(ty::Bound(ty::INNERMOST, bound_ty)); let fld_c = |bound_ct, ty| { - self.mk_const_infer(ty::InferConst::Canonical(ty::INNERMOST, bound_ct), ty) + self.mk_const(ty::Const { + val: ConstValue::Bound(ty::INNERMOST, bound_ct), + ty, + }) }; self.replace_escaping_bound_vars(value.skip_binder(), fld_r, fld_t, fld_c) } @@ -802,10 +802,7 @@ impl TypeFolder<'tcx> for Shifter<'tcx> { } fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - if let ty::Const { - val: ConstValue::Infer(ty::InferConst::Canonical(debruijn, bound_const)), - ty, - } = *ct { + if let ty::Const { val: ConstValue::Bound(debruijn, bound_ct), ty } = *ct { if self.amount == 0 || debruijn < self.current_index { ct } else { @@ -816,7 +813,10 @@ impl TypeFolder<'tcx> for Shifter<'tcx> { debruijn.shifted_out(self.amount) } }; - self.tcx.mk_const_infer(ty::InferConst::Canonical(debruijn, bound_const), ty) + self.tcx.mk_const(ty::Const { + val: ConstValue::Bound(debruijn, bound_ct), + ty, + }) } } else { ct.super_fold_with(self) @@ -920,8 +920,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { // const, as it has types/regions embedded in a lot of other // places. match ct.val { - ConstValue::Infer(ty::InferConst::Canonical(debruijn, _)) - if debruijn >= self.outer_index => true, + ConstValue::Bound(debruijn, _) if debruijn >= self.outer_index => true, _ => ct.super_visit_with(self), } } diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 83ec98f9ddd2..6b1f10bdb21c 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -1379,27 +1379,23 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> { impl<'tcx> TypeFoldable<'tcx> for ConstValue<'tcx> { fn super_fold_with>(&self, folder: &mut F) -> Self { match *self { - ConstValue::ByRef { alloc, offset } => - ConstValue::ByRef { alloc, offset }, ConstValue::Infer(ic) => ConstValue::Infer(ic.fold_with(folder)), ConstValue::Param(p) => ConstValue::Param(p.fold_with(folder)), - ConstValue::Placeholder(p) => ConstValue::Placeholder(p), - ConstValue::Scalar(a) => ConstValue::Scalar(a), - ConstValue::Slice { data, start, end } => ConstValue::Slice { data, start, end }, ConstValue::Unevaluated(did, substs) => ConstValue::Unevaluated(did, substs.fold_with(folder)), + ConstValue::ByRef { .. } | ConstValue::Bound(..) | ConstValue::Placeholder(..) + | ConstValue::Scalar(..) | ConstValue::Slice { .. } => *self, + } } fn super_visit_with>(&self, visitor: &mut V) -> bool { match *self { - ConstValue::ByRef { .. } => false, ConstValue::Infer(ic) => ic.visit_with(visitor), ConstValue::Param(p) => p.visit_with(visitor), - ConstValue::Placeholder(_) => false, - ConstValue::Scalar(_) => false, - ConstValue::Slice { .. } => false, ConstValue::Unevaluated(_, substs) => substs.visit_with(visitor), + ConstValue::ByRef { .. } | ConstValue::Bound(..) | ConstValue::Placeholder(_) + | ConstValue::Scalar(_) | ConstValue::Slice { .. } => false, } } } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 4af73fa389a7..618d7d0b7801 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -2373,6 +2373,4 @@ pub enum InferConst<'tcx> { Var(ConstVid<'tcx>), /// A fresh const variable. See `infer::freshen` for more details. Fresh(u32), - /// Canonicalized const variable, used only when preparing a trait query. - Canonical(DebruijnIndex, BoundVar), } diff --git a/src/librustc/ty/subst.rs b/src/librustc/ty/subst.rs index 4081c02a33ca..29721979099d 100644 --- a/src/librustc/ty/subst.rs +++ b/src/librustc/ty/subst.rs @@ -2,7 +2,7 @@ use crate::hir::def_id::DefId; use crate::infer::canonical::Canonical; -use crate::ty::{self, Lift, List, Ty, TyCtxt, InferConst, ParamConst}; +use crate::ty::{self, Lift, List, Ty, TyCtxt, ParamConst}; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use crate::mir::interpret::ConstValue; use crate::ty::sty::{ClosureSubsts, GeneratorSubsts}; @@ -234,9 +234,7 @@ impl<'a, 'tcx> InternalSubsts<'tcx> { ty::GenericParamDefKind::Const => { tcx.mk_const(ty::Const { - val: ConstValue::Infer( - InferConst::Canonical(ty::INNERMOST, ty::BoundVar::from(param.index)) - ), + val: ConstValue::Bound(ty::INNERMOST, ty::BoundVar::from(param.index)), ty: tcx.type_of(def_id), }).into() } diff --git a/src/librustc_codegen_ssa/mir/operand.rs b/src/librustc_codegen_ssa/mir/operand.rs index daa25b2ea059..ba5e47aeede1 100644 --- a/src/librustc_codegen_ssa/mir/operand.rs +++ b/src/librustc_codegen_ssa/mir/operand.rs @@ -79,6 +79,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { ConstValue::Unevaluated(..) => bug!("unevaluated constant in `OperandRef::from_const`"), ConstValue::Param(_) => bug!("encountered a ConstValue::Param in codegen"), ConstValue::Infer(_) => bug!("encountered a ConstValue::Infer in codegen"), + ConstValue::Bound(..) => bug!("encountered a ConstValue::Bound in codegen"), ConstValue::Placeholder(_) => bug!("encountered a ConstValue::Placeholder in codegen"), ConstValue::Scalar(x) => { let scalar = match layout.abi { diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 4d9be55945e0..b225d66aec56 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -589,8 +589,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let ptr = self.tag_static_base_pointer(Pointer::new(id, offset)); Operand::Indirect(MemPlace::from_ptr(ptr, layout.align.abi)) }, - ConstValue::Scalar(x) => - Operand::Immediate(tag_scalar(x).into()), + ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x).into()), ConstValue::Slice { data, start, end } => { // We rely on mutability being set correctly in `data` to prevent writes // where none should happen. @@ -606,6 +605,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } ConstValue::Param(..) | ConstValue::Infer(..) | + ConstValue::Bound(..) | ConstValue::Placeholder(..) | ConstValue::Unevaluated(..) => bug!("eval_const_to_op: Unexpected ConstValue {:?}", val), diff --git a/src/librustc_traits/chalk_context/mod.rs b/src/librustc_traits/chalk_context/mod.rs index 54d580ec05d7..8d136a1b65c7 100644 --- a/src/librustc_traits/chalk_context/mod.rs +++ b/src/librustc_traits/chalk_context/mod.rs @@ -33,7 +33,7 @@ use rustc::traits::{ InEnvironment, ChalkCanonicalGoal, }; -use rustc::ty::{self, TyCtxt, InferConst}; +use rustc::ty::{self, TyCtxt}; use rustc::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use rustc::ty::query::Providers; use rustc::ty::subst::{GenericArg, GenericArgKind}; @@ -286,7 +286,7 @@ impl context::ContextOps> for ChalkContext<'tcx> { _ => false, }, GenericArgKind::Const(ct) => match ct.val { - ConstValue::Infer(InferConst::Canonical(debruijn, bound_ct)) => { + ConstValue::Bound(debruijn, bound_ct) => { debug_assert_eq!(debruijn, ty::INNERMOST); cvar == bound_ct } diff --git a/src/librustc_traits/chalk_context/resolvent_ops.rs b/src/librustc_traits/chalk_context/resolvent_ops.rs index 8facec1e9e35..49d76681196a 100644 --- a/src/librustc_traits/chalk_context/resolvent_ops.rs +++ b/src/librustc_traits/chalk_context/resolvent_ops.rs @@ -16,7 +16,7 @@ use rustc::traits::{ Environment, InEnvironment, }; -use rustc::ty::{self, Ty, TyCtxt, InferConst}; +use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::GenericArg; use rustc::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc::mir::interpret::ConstValue; @@ -287,10 +287,7 @@ impl TypeRelation<'tcx> for AnswerSubstitutor<'cx, 'tcx> { a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>, ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { - if let ty::Const { - val: ConstValue::Infer(InferConst::Canonical(debruijn, bound_ct)), - .. - } = a { + if let ty::Const { val: ConstValue::Bound(debruijn, bound_ct), .. } = a { if *debruijn == self.binder_index { self.unify_free_answer_var(*bound_ct, b.into())?; return Ok(b); @@ -299,14 +296,8 @@ impl TypeRelation<'tcx> for AnswerSubstitutor<'cx, 'tcx> { match (a, b) { ( - ty::Const { - val: ConstValue::Infer(InferConst::Canonical(a_debruijn, a_bound)), - .. - }, - ty::Const { - val: ConstValue::Infer(InferConst::Canonical(b_debruijn, b_bound)), - .. - }, + ty::Const { val: ConstValue::Bound(a_debruijn, a_bound), .. }, + ty::Const { val: ConstValue::Bound(b_debruijn, b_bound), .. }, ) => { assert_eq!(a_debruijn, b_debruijn); assert_eq!(a_bound, b_bound); diff --git a/src/test/ui/symbol-names/impl1.legacy.stderr b/src/test/ui/symbol-names/impl1.legacy.stderr index a3d966bb0b03..610937739c19 100644 --- a/src/test/ui/symbol-names/impl1.legacy.stderr +++ b/src/test/ui/symbol-names/impl1.legacy.stderr @@ -46,13 +46,13 @@ error: def-path(bar::::baz) LL | #[rustc_def_path] | ^^^^^^^^^^^^^^^^^ -error: symbol-name(_ZN209_$LT$$u5b$$RF$dyn$u20$impl1..Foo$u2b$Assoc$u20$$u3d$$u20$extern$u20$$u22$C$u22$$u20$fn$LP$$RF$u8$C$$u20$...$RP$$u2b$impl1..AutoTrait$u3b$$u20$_$u5d$$u20$as$u20$impl1..main..$u7b$$u7b$closure$u7d$$u7d$..Bar$GT$6method17h059bf53000885489E) +error: symbol-name(_ZN209_$LT$$u5b$$RF$dyn$u20$impl1..Foo$u2b$Assoc$u20$$u3d$$u20$extern$u20$$u22$C$u22$$u20$fn$LP$$RF$u8$C$$u20$...$RP$$u2b$impl1..AutoTrait$u3b$$u20$_$u5d$$u20$as$u20$impl1..main..$u7b$$u7b$closure$u7d$$u7d$..Bar$GT$6method17h636bc933fc62ee2fE) --> $DIR/impl1.rs:61:13 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8, ::.)+impl1::AutoTrait; _] as impl1::main::{{closure}}::Bar>::method::h059bf53000885489) +error: demangling(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8, ::.)+impl1::AutoTrait; _] as impl1::main::{{closure}}::Bar>::method::h636bc933fc62ee2f) --> $DIR/impl1.rs:61:13 | LL | #[rustc_symbol_name] From c7d285b78136a5bccf8419afa4c57428b83b8bec Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 17 Oct 2019 07:04:39 -0700 Subject: [PATCH 79/79] Remove `src/llvm-emscripten` submodule With #65251 landed there's no need to build two LLVM backends and ship them with rustc, every target we have now uses the same LLVM backend! This removes the `src/llvm-emscripten` submodule and additionally removes all support from rustbuild for building the emscripten LLVM backend. Multiple codegen backend support is left in place for now, and this is intended to be an easy 10-15 minute win on CI times by avoiding having to build LLVM twice. --- .gitmodules | 3 - config.toml.example | 5 +- src/bootstrap/bootstrap.py | 4 -- src/bootstrap/compile.rs | 27 +++------ src/bootstrap/config.rs | 1 - src/bootstrap/configure.py | 5 +- src/bootstrap/dist.rs | 4 -- src/bootstrap/lib.rs | 7 --- src/bootstrap/native.rs | 75 +++++++++---------------- src/bootstrap/test.rs | 3 +- src/ci/docker/dist-various-1/Dockerfile | 1 - src/ci/init_repo.sh | 2 +- src/librustc_codegen_llvm/Cargo.toml | 6 -- src/llvm-emscripten | 1 - src/tools/tidy/src/lib.rs | 1 - 15 files changed, 36 insertions(+), 109 deletions(-) delete mode 160000 src/llvm-emscripten diff --git a/.gitmodules b/.gitmodules index 3ff5af78097f..1dcf9ed319f1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,9 +28,6 @@ [submodule "src/doc/rust-by-example"] path = src/doc/rust-by-example url = https://github.com/rust-lang/rust-by-example.git -[submodule "src/llvm-emscripten"] - path = src/llvm-emscripten - url = https://github.com/rust-lang/llvm.git [submodule "src/stdarch"] path = src/stdarch url = https://github.com/rust-lang/stdarch.git diff --git a/config.toml.example b/config.toml.example index 2e3b714f9228..be9770244260 100644 --- a/config.toml.example +++ b/config.toml.example @@ -374,10 +374,7 @@ # This is an array of the codegen backends that will be compiled for the rustc # that's being compiled. The default is to only build the LLVM codegen backend, -# but you can also optionally enable the "emscripten" backend for asm.js or -# make this an empty array (but that probably won't get too far in the -# bootstrap) -# FIXME: remove the obsolete emscripten backend option. +# and currently the only standard option supported is `"llvm"` #codegen-backends = ["llvm"] # This is the name of the directory in which codegen backends will get installed diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 65129eeeec50..4caf36a6f2a5 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -734,10 +734,6 @@ class RustBuild(object): if module.endswith("llvm-project"): if self.get_toml('llvm-config') and self.get_toml('lld') != 'true': continue - if module.endswith("llvm-emscripten"): - backends = self.get_toml('codegen-backends') - if backends is None or not 'emscripten' in backends: - continue check = self.check_submodule(module, slow_submodules) filtered_submodules.append((module, check)) submodules_names.append(module) diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 6ea32edfb208..5074b035789a 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -210,7 +210,6 @@ pub fn std_cargo(builder: &Builder<'_>, // config.toml equivalent) is used let llvm_config = builder.ensure(native::Llvm { target: builder.config.build, - emscripten: false, }); cargo.env("LLVM_CONFIG", llvm_config); cargo.env("RUSTC_BUILD_SANITIZERS", "1"); @@ -615,36 +614,27 @@ pub fn build_codegen_backend(builder: &Builder<'_>, compiler: &Compiler, target: Interned, backend: Interned) -> String { - let mut features = String::new(); - match &*backend { - "llvm" | "emscripten" => { + "llvm" => { // Build LLVM for our target. This will implicitly build the // host LLVM if necessary. let llvm_config = builder.ensure(native::Llvm { target, - emscripten: backend == "emscripten", }); - if backend == "emscripten" { - features.push_str(" emscripten"); - } - builder.info(&format!("Building stage{} codegen artifacts ({} -> {}, {})", compiler.stage, &compiler.host, target, backend)); // Pass down configuration from the LLVM build into the build of // librustc_llvm and librustc_codegen_llvm. - if builder.is_rust_llvm(target) && backend != "emscripten" { + if builder.is_rust_llvm(target) { cargo.env("LLVM_RUSTLLVM", "1"); } cargo.env("LLVM_CONFIG", &llvm_config); - if backend != "emscripten" { - let target_config = builder.config.target_config.get(&target); - if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { - cargo.env("CFG_LLVM_ROOT", s); - } + let target_config = builder.config.target_config.get(&target); + if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { + cargo.env("CFG_LLVM_ROOT", s); } // Some LLVM linker flags (-L and -l) may be needed to link librustc_llvm. if let Some(ref s) = builder.config.llvm_ldflags { @@ -662,9 +652,7 @@ pub fn build_codegen_backend(builder: &Builder<'_>, "libstdc++.a"); cargo.env("LLVM_STATIC_STDCPP", file); } - if builder.config.llvm_link_shared || - (builder.config.llvm_thin_lto && backend != "emscripten") - { + if builder.config.llvm_link_shared || builder.config.llvm_thin_lto { cargo.env("LLVM_LINK_SHARED", "1"); } if builder.config.llvm_use_libcxx { @@ -676,8 +664,7 @@ pub fn build_codegen_backend(builder: &Builder<'_>, } _ => panic!("unknown backend: {}", backend), } - - features + String::new() } /// Creates the `codegen-backends` folder for a compiler that's about to be diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 52b5cd888df9..441bb8d68faf 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -668,7 +668,6 @@ impl Config { pub fn llvm_enabled(&self) -> bool { self.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) - || self.rust_codegen_backends.contains(&INTERNER.intern_str("emscripten")) } } diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 346f0cb2039c..76509134f7cc 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -55,7 +55,6 @@ o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, m o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball") o("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo") o("profiler", "build.profiler", "build the profiler runtime") -o("emscripten", None, "compile the emscripten backend as well as LLVM") o("full-tools", None, "enable all tools") o("lld", "rust.lld", "build lld") o("lldb", "rust.lldb", "build lldb") @@ -335,10 +334,8 @@ for key in known_args: set('build.host', value.split(',')) elif option.name == 'target': set('build.target', value.split(',')) - elif option.name == 'emscripten': - set('rust.codegen-backends', ['llvm', 'emscripten']) elif option.name == 'full-tools': - set('rust.codegen-backends', ['llvm', 'emscripten']) + set('rust.codegen-backends', ['llvm']) set('rust.lld', True) set('rust.llvm-tools', True) set('build.extended', True) diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index d9dff77a30e6..514ad1144491 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -826,7 +826,6 @@ fn copy_src_dirs(builder: &Builder<'_>, src_dirs: &[&str], exclude_dirs: &[&str] const LLVM_TEST: &[&str] = &[ "llvm-project/llvm/test", "llvm-project\\llvm\\test", - "llvm-emscripten/test", "llvm-emscripten\\test", ]; if LLVM_TEST.iter().any(|path| spath.contains(path)) && (spath.ends_with(".ll") || @@ -834,9 +833,6 @@ fn copy_src_dirs(builder: &Builder<'_>, src_dirs: &[&str], exclude_dirs: &[&str] spath.ends_with(".s")) { return false } - if spath.contains("test/emscripten") || spath.contains("test\\emscripten") { - return false - } let full_path = Path::new(dir).join(path); if exclude_dirs.iter().any(|excl| full_path == Path::new(excl)) { diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 6fc72fa0b2a3..a182405f3b2d 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -232,7 +232,6 @@ pub struct Build { miri_info: channel::GitInfo, rustfmt_info: channel::GitInfo, in_tree_llvm_info: channel::GitInfo, - emscripten_llvm_info: channel::GitInfo, local_rebuild: bool, fail_fast: bool, doc_tests: DocTests, @@ -351,7 +350,6 @@ impl Build { // we always try to use git for LLVM builds let in_tree_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-project")); - let emscripten_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-emscripten")); let mut build = Build { initial_rustc: config.initial_rustc.clone(), @@ -376,7 +374,6 @@ impl Build { miri_info, rustfmt_info, in_tree_llvm_info, - emscripten_llvm_info, cc: HashMap::new(), cxx: HashMap::new(), ar: HashMap::new(), @@ -553,10 +550,6 @@ impl Build { self.out.join(&*target).join("llvm") } - fn emscripten_llvm_out(&self, target: Interned) -> PathBuf { - self.out.join(&*target).join("llvm-emscripten") - } - fn lld_out(&self, target: Interned) -> PathBuf { self.out.join(&*target).join("lld") } diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index fb308bc35ebc..97cdd2568016 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -28,7 +28,6 @@ use crate::GitRepo; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Llvm { pub target: Interned, - pub emscripten: bool, } impl Step for Llvm { @@ -40,46 +39,35 @@ impl Step for Llvm { run.path("src/llvm-project") .path("src/llvm-project/llvm") .path("src/llvm") - .path("src/llvm-emscripten") } fn make_run(run: RunConfig<'_>) { - let emscripten = run.path.ends_with("llvm-emscripten"); run.builder.ensure(Llvm { target: run.target, - emscripten, }); } /// Compile LLVM for `target`. fn run(self, builder: &Builder<'_>) -> PathBuf { let target = self.target; - let emscripten = self.emscripten; // If we're using a custom LLVM bail out here, but we can only use a // custom LLVM for the build triple. - if !self.emscripten { - if let Some(config) = builder.config.target_config.get(&target) { - if let Some(ref s) = config.llvm_config { - check_llvm_version(builder, s); - return s.to_path_buf() - } + if let Some(config) = builder.config.target_config.get(&target) { + if let Some(ref s) = config.llvm_config { + check_llvm_version(builder, s); + return s.to_path_buf() } } - let (llvm_info, root, out_dir, llvm_config_ret_dir) = if emscripten { - let info = &builder.emscripten_llvm_info; - let dir = builder.emscripten_llvm_out(target); - let config_dir = dir.join("bin"); - (info, "src/llvm-emscripten", dir, config_dir) - } else { - let info = &builder.in_tree_llvm_info; - let mut dir = builder.llvm_out(builder.config.build); - if !builder.config.build.contains("msvc") || builder.config.ninja { - dir.push("build"); - } - (info, "src/llvm-project/llvm", builder.llvm_out(target), dir.join("bin")) - }; + let llvm_info = &builder.in_tree_llvm_info; + let root = "src/llvm-project/llvm"; + let out_dir = builder.llvm_out(target); + let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build); + if !builder.config.build.contains("msvc") || builder.config.ninja { + llvm_config_ret_dir.push("build"); + } + llvm_config_ret_dir.push("bin"); let build_llvm_config = llvm_config_ret_dir .join(exe("llvm-config", &*builder.config.build)); @@ -107,8 +95,7 @@ impl Step for Llvm { } } - let descriptor = if emscripten { "Emscripten " } else { "" }; - builder.info(&format!("Building {}LLVM for {}", descriptor, target)); + builder.info(&format!("Building LLVM for {}", target)); let _time = util::timeit(&builder); t!(fs::create_dir_all(&out_dir)); @@ -123,23 +110,15 @@ impl Step for Llvm { // NOTE: remember to also update `config.toml.example` when changing the // defaults! - let llvm_targets = if self.emscripten { - "JSBackend" - } else { - match builder.config.llvm_targets { - Some(ref s) => s, - None => "AArch64;ARM;Hexagon;MSP430;Mips;NVPTX;PowerPC;RISCV;\ - Sparc;SystemZ;WebAssembly;X86", - } + let llvm_targets = match &builder.config.llvm_targets { + Some(s) => s, + None => "AArch64;ARM;Hexagon;MSP430;Mips;NVPTX;PowerPC;RISCV;\ + Sparc;SystemZ;WebAssembly;X86", }; - let llvm_exp_targets = if self.emscripten { - "" - } else { - match builder.config.llvm_experimental_targets { - Some(ref s) => s, - None => "", - } + let llvm_exp_targets = match builder.config.llvm_experimental_targets { + Some(ref s) => s, + None => "", }; let assertions = if builder.config.llvm_assertions {"ON"} else {"OFF"}; @@ -163,25 +142,23 @@ impl Step for Llvm { .define("LLVM_TARGET_ARCH", target.split('-').next().unwrap()) .define("LLVM_DEFAULT_TARGET_TRIPLE", target); - if builder.config.llvm_thin_lto && !emscripten { + if builder.config.llvm_thin_lto { cfg.define("LLVM_ENABLE_LTO", "Thin"); if !target.contains("apple") { cfg.define("LLVM_ENABLE_LLD", "ON"); } } - let want_lldb = builder.config.lldb_enabled && !self.emscripten; - // This setting makes the LLVM tools link to the dynamic LLVM library, // which saves both memory during parallel links and overall disk space // for the tools. We don't do this on every platform as it doesn't work // equally well everywhere. - if builder.llvm_link_tools_dynamically(target) && !emscripten { + if builder.llvm_link_tools_dynamically(target) { cfg.define("LLVM_LINK_LLVM_DYLIB", "ON"); } // For distribution we want the LLVM tools to be *statically* linked to libstdc++ - if builder.config.llvm_tools_enabled || want_lldb { + if builder.config.llvm_tools_enabled || builder.config.lldb_enabled { if !target.contains("windows") { if target.contains("apple") { cfg.define("CMAKE_EXE_LINKER_FLAGS", "-static-libstdc++"); @@ -209,7 +186,7 @@ impl Step for Llvm { enabled_llvm_projects.push("compiler-rt"); } - if want_lldb { + if builder.config.lldb_enabled { enabled_llvm_projects.push("clang"); enabled_llvm_projects.push("lldb"); // For the time being, disable code signing. @@ -234,10 +211,9 @@ impl Step for Llvm { } // http://llvm.org/docs/HowToCrossCompileLLVM.html - if target != builder.config.build && !emscripten { + if target != builder.config.build { builder.ensure(Llvm { target: builder.config.build, - emscripten: false, }); // FIXME: if the llvm root for the build triple is overridden then we // should use llvm-tblgen from there, also should verify that it @@ -481,7 +457,6 @@ impl Step for Lld { let llvm_config = builder.ensure(Llvm { target: self.target, - emscripten: false, }); let out_dir = builder.lld_out(target); diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 0b7aa0750a19..7ed67c6c7c5d 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1163,7 +1163,7 @@ impl Step for Compiletest { }).to_string() }) }; - let lldb_exe = if builder.config.lldb_enabled && !target.contains("emscripten") { + let lldb_exe = if builder.config.lldb_enabled { // Test against the lldb that was just built. builder.llvm_out(target).join("bin").join("lldb") } else { @@ -1232,7 +1232,6 @@ impl Step for Compiletest { if builder.config.llvm_enabled() { let llvm_config = builder.ensure(native::Llvm { target: builder.config.build, - emscripten: false, }); if !builder.config.dry_run { let llvm_version = output(Command::new(&llvm_config).arg("--version")); diff --git a/src/ci/docker/dist-various-1/Dockerfile b/src/ci/docker/dist-various-1/Dockerfile index 105791194628..fab3824a20ae 100644 --- a/src/ci/docker/dist-various-1/Dockerfile +++ b/src/ci/docker/dist-various-1/Dockerfile @@ -139,7 +139,6 @@ ENV RUST_CONFIGURE_ARGS \ --musl-root-aarch64=/musl-aarch64 \ --musl-root-mips=/musl-mips \ --musl-root-mipsel=/musl-mipsel \ - --enable-emscripten \ --disable-docs ENV SCRIPT \ diff --git a/src/ci/init_repo.sh b/src/ci/init_repo.sh index c7c3b0a5fbf5..92c6e546a389 100755 --- a/src/ci/init_repo.sh +++ b/src/ci/init_repo.sh @@ -47,7 +47,7 @@ function fetch_github_commit_archive { rm $cached } -included="src/llvm-project src/llvm-emscripten src/doc/book src/doc/rust-by-example" +included="src/llvm-project src/doc/book src/doc/rust-by-example" modules="$(git config --file .gitmodules --get-regexp '\.path$' | cut -d' ' -f2)" modules=($modules) use_git="" diff --git a/src/librustc_codegen_llvm/Cargo.toml b/src/librustc_codegen_llvm/Cargo.toml index 98efa6a5804b..867bbd22cfbb 100644 --- a/src/librustc_codegen_llvm/Cargo.toml +++ b/src/librustc_codegen_llvm/Cargo.toml @@ -12,9 +12,3 @@ test = false [dependencies] rustc_llvm = { path = "../librustc_llvm" } - -[features] -# This is used to convince Cargo to separately cache builds of `rustc_codegen_llvm` -# when this option is enabled or not. That way we can build two, cache two -# artifacts, and have nice speedy rebuilds. -emscripten = ["rustc_llvm/emscripten"] diff --git a/src/llvm-emscripten b/src/llvm-emscripten deleted file mode 160000 index 7f23313edff8..000000000000 --- a/src/llvm-emscripten +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7f23313edff8beccb3fe44b815714269c5124c15 diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index c76cc4cff7f8..d9db68ff66ef 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -46,7 +46,6 @@ pub mod error_codes_check; fn filter_dirs(path: &Path) -> bool { let skip = [ - "src/llvm-emscripten", "src/llvm-project", "src/stdarch", "src/tools/cargo",