From 5ab4c811caba9cfac1488919efae6c6675e68e4c Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:36:27 -0400 Subject: [PATCH 01/36] str/slice: factor out overflow error messages --- src/libcore/slice/mod.rs | 12 ++++++++---- src/libcore/str/mod.rs | 18 ++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 83e8a6e4b683a..93ebc23ac0b0e 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -2262,6 +2262,12 @@ fn slice_index_order_fail(index: usize, end: usize) -> ! { panic!("slice index starts at {} but ends at {}", index, end); } +#[inline(never)] +#[cold] +fn slice_index_overflow_fail() -> ! { + panic!("attempted to index slice up to maximum usize"); +} + /// A helper trait used for indexing operations. #[unstable(feature = "slice_get_slice", issue = "35729")] #[rustc_on_unimplemented = "slice indices are of type `usize` or ranges of `usize`"] @@ -2538,15 +2544,13 @@ impl SliceIndex<[T]> for ops::RangeInclusive { #[inline] fn index(self, slice: &[T]) -> &[T] { - assert!(self.end != usize::max_value(), - "attempted to index slice up to maximum usize"); + if self.end == usize::max_value() { slice_index_overflow_fail(); } (self.start..self.end + 1).index(slice) } #[inline] fn index_mut(self, slice: &mut [T]) -> &mut [T] { - assert!(self.end != usize::max_value(), - "attempted to index slice up to maximum usize"); + if self.end == usize::max_value() { slice_index_overflow_fail(); } (self.start..self.end + 1).index_mut(slice) } } diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index b39d9feb35b7e..f7555700ebc20 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1849,6 +1849,12 @@ mod traits { } } + #[inline(never)] + #[cold] + fn str_index_overflow_fail() -> ! { + panic!("attempted to index str up to maximum usize"); + } + #[stable(feature = "str_checked_slicing", since = "1.20.0")] impl SliceIndex for ops::RangeFull { type Output = str; @@ -2053,14 +2059,12 @@ mod traits { } #[inline] fn index(self, slice: &str) -> &Self::Output { - assert!(self.end != usize::max_value(), - "attempted to index str up to maximum usize"); + if self.end == usize::max_value() { str_index_overflow_fail(); } (self.start..self.end+1).index(slice) } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { - assert!(self.end != usize::max_value(), - "attempted to index str up to maximum usize"); + if self.end == usize::max_value() { str_index_overflow_fail(); } (self.start..self.end+1).index_mut(slice) } } @@ -2098,14 +2102,12 @@ mod traits { } #[inline] fn index(self, slice: &str) -> &Self::Output { - assert!(self.end != usize::max_value(), - "attempted to index str up to maximum usize"); + if self.end == usize::max_value() { str_index_overflow_fail(); } (..self.end+1).index(slice) } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { - assert!(self.end != usize::max_value(), - "attempted to index str up to maximum usize"); + if self.end == usize::max_value() { str_index_overflow_fail(); } (..self.end+1).index_mut(slice) } } From 6b749b011350830f47a93f1efce986b3c02c4342 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:36:46 -0400 Subject: [PATCH 02/36] Clean up the other Slice*Inclusive impls for str A previous PR fixed one method that was legitimately buggy; this cleans up the rest to be less diverse, mirroring the corresponding impls on [T] to the greatest extent possible without introducing any unnecessary UTF-8 boundary checks at 0. --- src/libcore/str/mod.rs | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index f7555700ebc20..df7b2f25a86df 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -2035,19 +2035,13 @@ mod traits { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { - if let Some(end) = self.end.checked_add(1) { - (self.start..end).get(slice) - } else { - None - } + if self.end == usize::max_value() { None } + else { (self.start..self.end+1).get(slice) } } #[inline] fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if let Some(end) = self.end.checked_add(1) { - (self.start..end).get_mut(slice) - } else { - None - } + if self.end == usize::max_value() { None } + else { (self.start..self.end+1).get_mut(slice) } } #[inline] unsafe fn get_unchecked(self, slice: &str) -> &Self::Output { @@ -2076,29 +2070,21 @@ mod traits { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { - if self.end < usize::max_value() && slice.is_char_boundary(self.end + 1) { - Some(unsafe { self.get_unchecked(slice) }) - } else { - None - } + if self.end == usize::max_value() { None } + else { (..self.end+1).get(slice) } } #[inline] fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if self.end < usize::max_value() && slice.is_char_boundary(self.end + 1) { - Some(unsafe { self.get_unchecked_mut(slice) }) - } else { - None - } + if self.end == usize::max_value() { None } + else { (..self.end+1).get_mut(slice) } } #[inline] unsafe fn get_unchecked(self, slice: &str) -> &Self::Output { - let ptr = slice.as_ptr(); - super::from_utf8_unchecked(slice::from_raw_parts(ptr, self.end + 1)) + (..self.end+1).get_unchecked(slice) } #[inline] unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output { - let ptr = slice.as_ptr(); - super::from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr as *mut u8, self.end + 1)) + (..self.end+1).get_unchecked_mut(slice) } #[inline] fn index(self, slice: &str) -> &Self::Output { From 4fab1674c3db2cf4b2d1546a7120d8d56a85c355 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:36:56 -0400 Subject: [PATCH 03/36] update libcore's comment about str tests --- src/libcore/tests/str.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/tests/str.rs b/src/libcore/tests/str.rs index 08daafccc5404..343c9596c5383 100644 --- a/src/libcore/tests/str.rs +++ b/src/libcore/tests/str.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// All `str` tests live in collectionstests::str +// All `str` tests live in liballoc/tests From 0842dc67238969e39b0a08d2c4314ceefd19caa2 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:02 -0400 Subject: [PATCH 04/36] collect str SliceIndex tests into a mod GitHub users: I think you can add ?w=1 to the url for a vastly cleaner whitespace-ignoring diff --- src/liballoc/tests/str.rs | 277 +++++++++++++++++++------------------- 1 file changed, 140 insertions(+), 137 deletions(-) diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index a03b61ec97e51..c9536cbe16855 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -291,113 +291,160 @@ fn test_replace_pattern() { assert_eq!(data.replace(|c| c == 'γ', "😺😺😺"), "abcdαβ😺😺😺δabcdαβ😺😺😺δ"); } -#[test] -fn test_slice() { - assert_eq!("ab", &"abc"[0..2]); - assert_eq!("bc", &"abc"[1..3]); - assert_eq!("", &"abc"[1..1]); - assert_eq!("\u{65e5}", &"\u{65e5}\u{672c}"[0..3]); - - let data = "ประเทศไทย中华"; - assert_eq!("ป", &data[0..3]); - assert_eq!("ร", &data[3..6]); - assert_eq!("", &data[3..3]); - assert_eq!("华", &data[30..33]); - - fn a_million_letter_x() -> String { - let mut i = 0; - let mut rs = String::new(); - while i < 100000 { - rs.push_str("华华华华华华华华华华"); - i += 1; +mod slice_index { + #[test] + fn test_slice() { + assert_eq!("ab", &"abc"[0..2]); + assert_eq!("bc", &"abc"[1..3]); + assert_eq!("", &"abc"[1..1]); + assert_eq!("\u{65e5}", &"\u{65e5}\u{672c}"[0..3]); + + let data = "ประเทศไทย中华"; + assert_eq!("ป", &data[0..3]); + assert_eq!("ร", &data[3..6]); + assert_eq!("", &data[3..3]); + assert_eq!("华", &data[30..33]); + + fn a_million_letter_x() -> String { + let mut i = 0; + let mut rs = String::new(); + while i < 100000 { + rs.push_str("华华华华华华华华华华"); + i += 1; + } + rs } - rs - } - fn half_a_million_letter_x() -> String { - let mut i = 0; - let mut rs = String::new(); - while i < 100000 { - rs.push_str("华华华华华"); - i += 1; + fn half_a_million_letter_x() -> String { + let mut i = 0; + let mut rs = String::new(); + while i < 100000 { + rs.push_str("华华华华华"); + i += 1; + } + rs } - rs + let letters = a_million_letter_x(); + assert_eq!(half_a_million_letter_x(), &letters[0..3 * 500000]); } - let letters = a_million_letter_x(); - assert_eq!(half_a_million_letter_x(), &letters[0..3 * 500000]); -} -#[test] -fn test_slice_2() { - let ss = "中华Việt Nam"; + #[test] + fn test_slice_2() { + let ss = "中华Việt Nam"; + + assert_eq!("华", &ss[3..6]); + assert_eq!("Việt Nam", &ss[6..16]); + + assert_eq!("ab", &"abc"[0..2]); + assert_eq!("bc", &"abc"[1..3]); + assert_eq!("", &"abc"[1..1]); + + assert_eq!("中", &ss[0..3]); + assert_eq!("华V", &ss[3..7]); + assert_eq!("", &ss[3..3]); + /*0: 中 + 3: 华 + 6: V + 7: i + 8: ệ + 11: t + 12: + 13: N + 14: a + 15: m */ + } - assert_eq!("华", &ss[3..6]); - assert_eq!("Việt Nam", &ss[6..16]); + #[test] + #[should_panic] + fn test_slice_fail() { + &"中华Việt Nam"[0..2]; + } - assert_eq!("ab", &"abc"[0..2]); - assert_eq!("bc", &"abc"[1..3]); - assert_eq!("", &"abc"[1..1]); + #[test] + #[should_panic] + fn test_str_slice_rangetoinclusive_max_panics() { + &"hello"[..=usize::max_value()]; + } - assert_eq!("中", &ss[0..3]); - assert_eq!("华V", &ss[3..7]); - assert_eq!("", &ss[3..3]); - /*0: 中 - 3: 华 - 6: V - 7: i - 8: ệ - 11: t - 12: - 13: N - 14: a - 15: m */ -} + #[test] + #[should_panic] + fn test_str_slice_rangeinclusive_max_panics() { + &"hello"[1..=usize::max_value()]; + } -#[test] -#[should_panic] -fn test_slice_fail() { - &"中华Việt Nam"[0..2]; -} + #[test] + #[should_panic] + fn test_str_slicemut_rangetoinclusive_max_panics() { + let mut s = "hello".to_owned(); + let s: &mut str = &mut s; + &mut s[..=usize::max_value()]; + } -#[test] -#[should_panic] -fn test_str_slice_rangetoinclusive_max_panics() { - &"hello"[..=usize::max_value()]; -} + #[test] + #[should_panic] + fn test_str_slicemut_rangeinclusive_max_panics() { + let mut s = "hello".to_owned(); + let s: &mut str = &mut s; + &mut s[1..=usize::max_value()]; + } -#[test] -#[should_panic] -fn test_str_slice_rangeinclusive_max_panics() { - &"hello"[1..=usize::max_value()]; -} + #[test] + fn test_str_get_maxinclusive() { + let mut s = "hello".to_owned(); + { + let s: &str = &s; + assert_eq!(s.get(..=usize::max_value()), None); + assert_eq!(s.get(1..=usize::max_value()), None); + } + { + let s: &mut str = &mut s; + assert_eq!(s.get(..=usize::max_value()), None); + assert_eq!(s.get(1..=usize::max_value()), None); + } + } -#[test] -#[should_panic] -fn test_str_slicemut_rangetoinclusive_max_panics() { - let mut s = "hello".to_owned(); - let s: &mut str = &mut s; - &mut s[..=usize::max_value()]; -} + const LOREM_PARAGRAPH: &'static str = "\ + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse quis lorem sit amet dolor \ + ultricies condimentum. Praesent iaculis purus elit, ac malesuada quam malesuada in. Duis sed orci \ + eros. Suspendisse sit amet magna mollis, mollis nunc luctus, imperdiet mi. Integer fringilla non \ + sem ut lacinia. Fusce varius tortor a risus porttitor hendrerit. Morbi mauris dui, ultricies nec \ + tempus vel, gravida nec quam."; + + // check the panic includes the prefix of the sliced string + #[test] + #[should_panic(expected="byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet")] + fn test_slice_fail_truncated_1() { + &LOREM_PARAGRAPH[..1024]; + } + // check the truncation in the panic message + #[test] + #[should_panic(expected="luctus, im`[...]")] + fn test_slice_fail_truncated_2() { + &LOREM_PARAGRAPH[..1024]; + } -#[test] -#[should_panic] -fn test_str_slicemut_rangeinclusive_max_panics() { - let mut s = "hello".to_owned(); - let s: &mut str = &mut s; - &mut s[1..=usize::max_value()]; -} + #[test] + #[should_panic(expected="byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of")] + fn test_slice_fail_boundary_1() { + &"abcαβγ"[4..]; + } -#[test] -fn test_str_get_maxinclusive() { - let mut s = "hello".to_owned(); - { - let s: &str = &s; - assert_eq!(s.get(..=usize::max_value()), None); - assert_eq!(s.get(1..=usize::max_value()), None); + #[test] + #[should_panic(expected="byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of")] + fn test_slice_fail_boundary_2() { + &"abcαβγ"[2..6]; } - { - let s: &mut str = &mut s; - assert_eq!(s.get(..=usize::max_value()), None); - assert_eq!(s.get(1..=usize::max_value()), None); + + #[test] + fn test_slice_from() { + assert_eq!(&"abcd"[0..], "abcd"); + assert_eq!(&"abcd"[2..], "cd"); + assert_eq!(&"abcd"[4..], ""); + } + #[test] + fn test_slice_to() { + assert_eq!(&"abcd"[..0], ""); + assert_eq!(&"abcd"[..2], "ab"); + assert_eq!(&"abcd"[..4], "abcd"); } } @@ -446,50 +493,6 @@ fn test_is_char_boundary() { } } } -const LOREM_PARAGRAPH: &'static str = "\ -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse quis lorem sit amet dolor \ -ultricies condimentum. Praesent iaculis purus elit, ac malesuada quam malesuada in. Duis sed orci \ -eros. Suspendisse sit amet magna mollis, mollis nunc luctus, imperdiet mi. Integer fringilla non \ -sem ut lacinia. Fusce varius tortor a risus porttitor hendrerit. Morbi mauris dui, ultricies nec \ -tempus vel, gravida nec quam."; - -// check the panic includes the prefix of the sliced string -#[test] -#[should_panic(expected="byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet")] -fn test_slice_fail_truncated_1() { - &LOREM_PARAGRAPH[..1024]; -} -// check the truncation in the panic message -#[test] -#[should_panic(expected="luctus, im`[...]")] -fn test_slice_fail_truncated_2() { - &LOREM_PARAGRAPH[..1024]; -} - -#[test] -#[should_panic(expected="byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of")] -fn test_slice_fail_boundary_1() { - &"abcαβγ"[4..]; -} - -#[test] -#[should_panic(expected="byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of")] -fn test_slice_fail_boundary_2() { - &"abcαβγ"[2..6]; -} - -#[test] -fn test_slice_from() { - assert_eq!(&"abcd"[0..], "abcd"); - assert_eq!(&"abcd"[2..], "cd"); - assert_eq!(&"abcd"[4..], ""); -} -#[test] -fn test_slice_to() { - assert_eq!(&"abcd"[..0], ""); - assert_eq!(&"abcd"[..2], "ab"); - assert_eq!(&"abcd"[..4], "abcd"); -} #[test] fn test_trim_left_matches() { From ce66f5d9185aa2b81159fa61597bbb6e4cf2847f Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:08 -0400 Subject: [PATCH 05/36] flesh out tests for SliceIndex m*n lines of implementation deserves m*n lines of tests --- src/liballoc/tests/str.rs | 477 +++++++++++++++++++++++++++++-------- src/libcore/tests/slice.rs | 282 +++++++++++++++++++--- 2 files changed, 622 insertions(+), 137 deletions(-) diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index c9536cbe16855..bfba9a6b39355 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -292,19 +292,194 @@ fn test_replace_pattern() { } mod slice_index { + // Test a slicing operation **that should succeed,** + // testing it on all of the indexing methods. + // + // DO NOT use this in `should_panic` tests, unless you are testing the macro itself. + macro_rules! assert_range_eq { + ($s:expr, $range:expr, $expected:expr) + => { + let mut s: String = $s.to_owned(); + let mut expected: String = $expected.to_owned(); + { + let s: &str = &s; + let expected: &str = &expected; + + assert_eq!(&s[$range], expected, "(in assertion for: index)"); + assert_eq!(s.get($range), Some(expected), "(in assertion for: get)"); + unsafe { + assert_eq!( + s.get_unchecked($range), expected, + "(in assertion for: get_unchecked)", + ); + } + } + { + let s: &mut str = &mut s; + let expected: &mut str = &mut expected; + + assert_eq!( + &mut s[$range], expected, + "(in assertion for: index_mut)", + ); + assert_eq!( + s.get_mut($range), Some(&mut expected[..]), + "(in assertion for: get_mut)", + ); + unsafe { + assert_eq!( + s.get_unchecked_mut($range), expected, + "(in assertion for: get_unchecked_mut)", + ); + } + } + } + } + + // Make sure the macro can actually detect bugs, + // because if it can't, then what are we even doing here? + // + // (Be aware this only demonstrates the ability to detect bugs + // in the FIRST method it calls, as the macro is not designed + // to be used in `should_panic`) + #[test] + #[should_panic(expected = "out of bounds")] + fn assert_range_eq_can_fail_by_panic() { + assert_range_eq!("abc", 0..5, "abc"); + } + + // (Be aware this only demonstrates the ability to detect bugs + // in the FIRST method it calls, as the macro is not designed + // to be used in `should_panic`) + #[test] + #[should_panic(expected = "==")] + fn assert_range_eq_can_fail_by_inequality() { + assert_range_eq!("abc", 0..2, "abc"); + } + + // Generates test cases for bad index operations. + // + // This generates `should_panic` test cases for Index/IndexMut + // and `None` test cases for get/get_mut. + macro_rules! panic_cases { + ($( + mod $case_name:ident { + let DATA = $data:expr; + + // optional: + // + // a similar input for which DATA[input] succeeds, and the corresponding + // output str. This helps validate "critical points" where an input range + // straddles the boundary between valid and invalid. + // (such as the input `len..len`, which is just barely valid) + $( + let GOOD_INPUT = $good:expr; + let GOOD_OUTPUT = $output:expr; + )* + + let BAD_INPUT = $bad:expr; + const EXPECT_MSG = $expect_msg:expr; // must be a literal + + !!generate_tests!! + } + )*) => {$( + mod $case_name { + #[test] + fn pass() { + let mut v: String = $data.into(); + + $( assert_range_eq!(v, $good, $output); )* + + { + let v: &str = &v; + assert_eq!(v.get($bad), None, "(in None assertion for get)"); + } + + { + let v: &mut str = &mut v; + assert_eq!(v.get_mut($bad), None, "(in None assertion for get_mut)"); + } + } + + #[test] + #[should_panic(expected = $expect_msg)] + fn index_fail() { + let v: String = $data.into(); + let v: &str = &v; + let _v = &v[$bad]; + } + + #[test] + #[should_panic(expected = $expect_msg)] + fn index_mut_fail() { + let mut v: String = $data.into(); + let v: &mut str = &mut v; + let _v = &mut v[$bad]; + } + } + )*}; + } + + #[test] + fn simple_ascii() { + assert_range_eq!("abc", .., "abc"); + + assert_range_eq!("abc", 0..2, "ab"); + assert_range_eq!("abc", 0..=1, "ab"); + assert_range_eq!("abc", ..2, "ab"); + assert_range_eq!("abc", ..=1, "ab"); + + assert_range_eq!("abc", 1..3, "bc"); + assert_range_eq!("abc", 1..=2, "bc"); + assert_range_eq!("abc", 1..1, ""); + assert_range_eq!("abc", 1..=0, ""); + } + #[test] - fn test_slice() { - assert_eq!("ab", &"abc"[0..2]); - assert_eq!("bc", &"abc"[1..3]); - assert_eq!("", &"abc"[1..1]); - assert_eq!("\u{65e5}", &"\u{65e5}\u{672c}"[0..3]); + fn simple_unicode() { + // 日本 + assert_range_eq!("\u{65e5}\u{672c}", .., "\u{65e5}\u{672c}"); + + assert_range_eq!("\u{65e5}\u{672c}", 0..3, "\u{65e5}"); + assert_range_eq!("\u{65e5}\u{672c}", 0..=2, "\u{65e5}"); + assert_range_eq!("\u{65e5}\u{672c}", ..3, "\u{65e5}"); + assert_range_eq!("\u{65e5}\u{672c}", ..=2, "\u{65e5}"); + + assert_range_eq!("\u{65e5}\u{672c}", 3..6, "\u{672c}"); + assert_range_eq!("\u{65e5}\u{672c}", 3..=5, "\u{672c}"); + assert_range_eq!("\u{65e5}\u{672c}", 3.., "\u{672c}"); let data = "ประเทศไทย中华"; - assert_eq!("ป", &data[0..3]); - assert_eq!("ร", &data[3..6]); - assert_eq!("", &data[3..3]); - assert_eq!("华", &data[30..33]); + assert_range_eq!(data, 0..3, "ป"); + assert_range_eq!(data, 3..6, "ร"); + assert_range_eq!(data, 3..3, ""); + assert_range_eq!(data, 30..33, "华"); + /*0: 中 + 3: 华 + 6: V + 7: i + 8: ệ + 11: t + 12: + 13: N + 14: a + 15: m */ + let ss = "中华Việt Nam"; + assert_range_eq!(ss, 3..6, "华"); + assert_range_eq!(ss, 6..16, "Việt Nam"); + assert_range_eq!(ss, 6..=15, "Việt Nam"); + assert_range_eq!(ss, 6.., "Việt Nam"); + + assert_range_eq!(ss, 0..3, "中"); + assert_range_eq!(ss, 3..7, "华V"); + assert_range_eq!(ss, 3..=6, "华V"); + assert_range_eq!(ss, 3..3, ""); + assert_range_eq!(ss, 3..=2, ""); + } + + #[test] + fn simple_big() { fn a_million_letter_x() -> String { let mut i = 0; let mut rs = String::new(); @@ -324,33 +499,7 @@ mod slice_index { rs } let letters = a_million_letter_x(); - assert_eq!(half_a_million_letter_x(), &letters[0..3 * 500000]); - } - - #[test] - fn test_slice_2() { - let ss = "中华Việt Nam"; - - assert_eq!("华", &ss[3..6]); - assert_eq!("Việt Nam", &ss[6..16]); - - assert_eq!("ab", &"abc"[0..2]); - assert_eq!("bc", &"abc"[1..3]); - assert_eq!("", &"abc"[1..1]); - - assert_eq!("中", &ss[0..3]); - assert_eq!("华V", &ss[3..7]); - assert_eq!("", &ss[3..3]); - /*0: 中 - 3: 华 - 6: V - 7: i - 8: ệ - 11: t - 12: - 13: N - 14: a - 15: m */ + assert_range_eq!(letters, 0..3 * 500000, half_a_million_letter_x()); } #[test] @@ -359,55 +508,210 @@ mod slice_index { &"中华Việt Nam"[0..2]; } - #[test] - #[should_panic] - fn test_str_slice_rangetoinclusive_max_panics() { - &"hello"[..=usize::max_value()]; - } + panic_cases! { + mod rangefrom_len { + let DATA = "abcdef"; - #[test] - #[should_panic] - fn test_str_slice_rangeinclusive_max_panics() { - &"hello"[1..=usize::max_value()]; - } + let GOOD_INPUT = 6..; + let GOOD_OUTPUT = ""; - #[test] - #[should_panic] - fn test_str_slicemut_rangetoinclusive_max_panics() { - let mut s = "hello".to_owned(); - let s: &mut str = &mut s; - &mut s[..=usize::max_value()]; + let BAD_INPUT = 7..; + const EXPECT_MSG = "out of bounds"; + + !!generate_tests!! + } + + mod rangeto_len { + let DATA = "abcdef"; + + let GOOD_INPUT = ..6; + let GOOD_OUTPUT = "abcdef"; + + let BAD_INPUT = ..7; + const EXPECT_MSG = "out of bounds"; + + !!generate_tests!! + } + + mod rangetoinclusive_len { + let DATA = "abcdef"; + + let GOOD_INPUT = ..=5; + let GOOD_OUTPUT = "abcdef"; + + let BAD_INPUT = ..=6; + const EXPECT_MSG = "out of bounds"; + + !!generate_tests!! + } + + mod range_len_len { + let DATA = "abcdef"; + + let GOOD_INPUT = 6..6; + let GOOD_OUTPUT = ""; + + let BAD_INPUT = 7..7; + const EXPECT_MSG = "out of bounds"; + + !!generate_tests!! + } + + mod rangeinclusive_len_len { + let DATA = "abcdef"; + + let GOOD_INPUT = 6..=5; + let GOOD_OUTPUT = ""; + + let BAD_INPUT = 7..=6; + const EXPECT_MSG = "out of bounds"; + + !!generate_tests!! + } } - #[test] - #[should_panic] - fn test_str_slicemut_rangeinclusive_max_panics() { - let mut s = "hello".to_owned(); - let s: &mut str = &mut s; - &mut s[1..=usize::max_value()]; + panic_cases! { + mod range_neg_width { + let DATA = "abcdef"; + + let GOOD_INPUT = 4..4; + let GOOD_OUTPUT = ""; + + let BAD_INPUT = 4..3; + const EXPECT_MSG = "begin <= end (4 <= 3)"; + + !!generate_tests!! + } + + mod rangeinclusive_neg_width { + let DATA = "abcdef"; + + let GOOD_INPUT = 4..=3; + let GOOD_OUTPUT = ""; + + let BAD_INPUT = 4..=2; + const EXPECT_MSG = "begin <= end (4 <= 3)"; + + !!generate_tests!! + } } - #[test] - fn test_str_get_maxinclusive() { - let mut s = "hello".to_owned(); - { - let s: &str = &s; - assert_eq!(s.get(..=usize::max_value()), None); - assert_eq!(s.get(1..=usize::max_value()), None); + mod overflow { + panic_cases! { + + mod rangeinclusive { + let DATA = "hello"; + + let BAD_INPUT = 1..=usize::max_value(); + const EXPECT_MSG = "maximum usize"; + + !!generate_tests!! + } + + mod rangetoinclusive { + let DATA = "hello"; + + let BAD_INPUT = ..=usize::max_value(); + const EXPECT_MSG = "maximum usize"; + + !!generate_tests!! + } } - { - let s: &mut str = &mut s; - assert_eq!(s.get(..=usize::max_value()), None); - assert_eq!(s.get(1..=usize::max_value()), None); + } + + mod boundary { + const DATA: &'static str = "abcαβγ"; + + const BAD_START: usize = 4; + const GOOD_START: usize = 3; + const BAD_END: usize = 6; + const GOOD_END: usize = 7; + const BAD_END_INCL: usize = BAD_END - 1; + const GOOD_END_INCL: usize = GOOD_END - 1; + + // it is especially important to test all of the different range types here + // because some of the logic may be duplicated as part of micro-optimizations + // to dodge unicode boundary checks on half-ranges. + panic_cases! { + mod range_1 { + let DATA = super::DATA; + + let BAD_INPUT = super::BAD_START..super::GOOD_END; + const EXPECT_MSG = + "byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; + + !!generate_tests!! + } + + mod range_2 { + let DATA = super::DATA; + + let BAD_INPUT = super::GOOD_START..super::BAD_END; + const EXPECT_MSG = + "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; + + !!generate_tests!! + } + + mod rangefrom { + let DATA = super::DATA; + + let BAD_INPUT = super::BAD_START..; + const EXPECT_MSG = + "byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; + + !!generate_tests!! + } + + mod rangeto { + let DATA = super::DATA; + + let BAD_INPUT = ..super::BAD_END; + const EXPECT_MSG = + "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; + + !!generate_tests!! + } + + mod rangeinclusive_1 { + let DATA = super::DATA; + + let BAD_INPUT = super::BAD_START..=super::GOOD_END_INCL; + const EXPECT_MSG = + "byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; + + !!generate_tests!! + } + + mod rangeinclusive_2 { + let DATA = super::DATA; + + let BAD_INPUT = super::GOOD_START..=super::BAD_END_INCL; + const EXPECT_MSG = + "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; + + !!generate_tests!! + } + + mod rangetoinclusive { + let DATA = super::DATA; + + let BAD_INPUT = ..=super::BAD_END_INCL; + const EXPECT_MSG = + "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; + + !!generate_tests!! + } } } const LOREM_PARAGRAPH: &'static str = "\ - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse quis lorem sit amet dolor \ - ultricies condimentum. Praesent iaculis purus elit, ac malesuada quam malesuada in. Duis sed orci \ - eros. Suspendisse sit amet magna mollis, mollis nunc luctus, imperdiet mi. Integer fringilla non \ - sem ut lacinia. Fusce varius tortor a risus porttitor hendrerit. Morbi mauris dui, ultricies nec \ - tempus vel, gravida nec quam."; + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse quis lorem \ + sit amet dolor ultricies condimentum. Praesent iaculis purus elit, ac malesuada \ + quam malesuada in. Duis sed orci eros. Suspendisse sit amet magna mollis, mollis \ + nunc luctus, imperdiet mi. Integer fringilla non sem ut lacinia. Fusce varius \ + tortor a risus porttitor hendrerit. Morbi mauris dui, ultricies nec tempus vel, \ + gravida nec quam."; // check the panic includes the prefix of the sliced string #[test] @@ -421,31 +725,6 @@ mod slice_index { fn test_slice_fail_truncated_2() { &LOREM_PARAGRAPH[..1024]; } - - #[test] - #[should_panic(expected="byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of")] - fn test_slice_fail_boundary_1() { - &"abcαβγ"[4..]; - } - - #[test] - #[should_panic(expected="byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of")] - fn test_slice_fail_boundary_2() { - &"abcαβγ"[2..6]; - } - - #[test] - fn test_slice_from() { - assert_eq!(&"abcd"[0..], "abcd"); - assert_eq!(&"abcd"[2..], "cd"); - assert_eq!(&"abcd"[4..], ""); - } - #[test] - fn test_slice_to() { - assert_eq!(&"abcd"[..0], ""); - assert_eq!(&"abcd"[..2], "ab"); - assert_eq!(&"abcd"[..4], "abcd"); - } } #[test] diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 53fdfa0682742..5272c7427d91d 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -376,48 +376,254 @@ fn test_windows_zip() { assert_eq!(res, [14, 18, 22, 26]); } -#[test] -fn get_range() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - assert_eq!(v.get(..), Some(&[0, 1, 2, 3, 4, 5][..])); - assert_eq!(v.get(..2), Some(&[0, 1][..])); - assert_eq!(v.get(2..), Some(&[2, 3, 4, 5][..])); - assert_eq!(v.get(1..4), Some(&[1, 2, 3][..])); - assert_eq!(v.get(7..), None); - assert_eq!(v.get(7..10), None); -} +mod slice_index { + // Test a slicing operation that should succeed, + // testing it on all of the indexing methods. + macro_rules! assert_range_eq { + ($arr:expr, $range:expr, $expected:expr) + => { + let mut arr = $arr; + let mut expected = $expected; + { + let s: &[_] = &arr; + let expected: &[_] = &expected; + + assert_eq!(&s[$range], expected, "(in assertion for: index)"); + assert_eq!(s.get($range), Some(expected), "(in assertion for: get)"); + unsafe { + assert_eq!( + s.get_unchecked($range), expected, + "(in assertion for: get_unchecked)", + ); + } + } + { + let s: &mut [_] = &mut arr; + let expected: &mut [_] = &mut expected; + + assert_eq!( + &mut s[$range], expected, + "(in assertion for: index_mut)", + ); + assert_eq!( + s.get_mut($range), Some(&mut expected[..]), + "(in assertion for: get_mut)", + ); + unsafe { + assert_eq!( + s.get_unchecked_mut($range), expected, + "(in assertion for: get_unchecked_mut)", + ); + } + } + } + } -#[test] -fn get_mut_range() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - assert_eq!(v.get_mut(..), Some(&mut [0, 1, 2, 3, 4, 5][..])); - assert_eq!(v.get_mut(..2), Some(&mut [0, 1][..])); - assert_eq!(v.get_mut(2..), Some(&mut [2, 3, 4, 5][..])); - assert_eq!(v.get_mut(1..4), Some(&mut [1, 2, 3][..])); - assert_eq!(v.get_mut(7..), None); - assert_eq!(v.get_mut(7..10), None); -} - -#[test] -fn get_unchecked_range() { - unsafe { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - assert_eq!(v.get_unchecked(..), &[0, 1, 2, 3, 4, 5][..]); - assert_eq!(v.get_unchecked(..2), &[0, 1][..]); - assert_eq!(v.get_unchecked(2..), &[2, 3, 4, 5][..]); - assert_eq!(v.get_unchecked(1..4), &[1, 2, 3][..]); + // Make sure the macro can actually detect bugs, + // because if it can't, then what are we even doing here? + // + // (Be aware this only demonstrates the ability to detect bugs + // in the FIRST method it calls, as the macro is not designed + // to be used in `should_panic`) + #[test] + #[should_panic(expected = "out of range")] + fn assert_range_eq_can_fail_by_panic() { + assert_range_eq!([0, 1, 2], 0..5, [0, 1, 2]); } -} -#[test] -fn get_unchecked_mut_range() { - unsafe { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - assert_eq!(v.get_unchecked_mut(..), &mut [0, 1, 2, 3, 4, 5][..]); - assert_eq!(v.get_unchecked_mut(..2), &mut [0, 1][..]); - assert_eq!(v.get_unchecked_mut(2..), &mut[2, 3, 4, 5][..]); - assert_eq!(v.get_unchecked_mut(1..4), &mut [1, 2, 3][..]); + // (Be aware this only demonstrates the ability to detect bugs + // in the FIRST method it calls, as the macro is not designed + // to be used in `should_panic`) + #[test] + #[should_panic(expected = "==")] + fn assert_range_eq_can_fail_by_inequality() { + assert_range_eq!([0, 1, 2], 0..2, [0, 1, 2]); + } + + // Test cases for bad index operations. + // + // This generates `should_panic` test cases for Index/IndexMut + // and `None` test cases for get/get_mut. + macro_rules! panic_cases { + ($( + mod $case_name:ident { + let DATA: $Data: ty = $data:expr; + + // optional: + // + // a similar input for which DATA[input] succeeds, and the corresponding + // output as an array. This helps validate "critical points" where an + // input range straddles the boundary between valid and invalid. + // (such as the input `len..len`, which is just barely valid) + $( + let GOOD_INPUT = $good:expr; + let GOOD_OUTPUT = $output:expr; + )* + + let BAD_INPUT = $bad:expr; + const EXPECT_MSG = $expect_msg:expr; + + !!generate_tests!! + } + )*) => {$( + mod $case_name { + #[test] + fn pass() { + let mut v: $Data = $data; + + $( assert_range_eq!($data, $good, $output); )* + + { + let v: &[_] = &v; + assert_eq!(v.get($bad), None, "(in None assertion for get)"); + } + + { + let v: &mut [_] = &mut v; + assert_eq!(v.get_mut($bad), None, "(in None assertion for get_mut)"); + } + } + + #[test] + #[should_panic(expected = $expect_msg)] + fn index_fail() { + let v: $Data = $data; + let v: &[_] = &v; + let _v = &v[$bad]; + } + + #[test] + #[should_panic(expected = $expect_msg)] + fn index_mut_fail() { + let mut v: $Data = $data; + let v: &mut [_] = &mut v; + let _v = &mut v[$bad]; + } + } + )*}; + } + + #[test] + fn simple() { + let v = [0, 1, 2, 3, 4, 5]; + + assert_range_eq!(v, .., [0, 1, 2, 3, 4, 5]); + assert_range_eq!(v, ..2, [0, 1]); + assert_range_eq!(v, ..=1, [0, 1]); + assert_range_eq!(v, 2.., [2, 3, 4, 5]); + assert_range_eq!(v, 1..4, [1, 2, 3]); + assert_range_eq!(v, 1..=3, [1, 2, 3]); + } + + panic_cases! { + mod rangefrom_len { + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = 6..; + let GOOD_OUTPUT = []; + + let BAD_INPUT = 7..; + const EXPECT_MSG = "but ends at"; // perhaps not ideal + + !!generate_tests!! + } + + mod rangeto_len { + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = ..6; + let GOOD_OUTPUT = [0, 1, 2, 3, 4, 5]; + + let BAD_INPUT = ..7; + const EXPECT_MSG = "out of range"; + + !!generate_tests!! + } + + mod rangetoinclusive_len { + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = ..=5; + let GOOD_OUTPUT = [0, 1, 2, 3, 4, 5]; + + let BAD_INPUT = ..=6; + const EXPECT_MSG = "out of range"; + + !!generate_tests!! + } + + mod range_len_len { + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = 6..6; + let GOOD_OUTPUT = []; + + let BAD_INPUT = 7..7; + const EXPECT_MSG = "out of range"; + + !!generate_tests!! + } + + mod rangeinclusive_len_len{ + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = 6..=5; + let GOOD_OUTPUT = []; + + let BAD_INPUT = 7..=6; + const EXPECT_MSG = "out of range"; + + !!generate_tests!! + } } + + panic_cases! { + mod range_neg_width { + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = 4..4; + let GOOD_OUTPUT = []; + + let BAD_INPUT = 4..3; + const EXPECT_MSG = "but ends at"; + + !!generate_tests!! + } + + mod rangeinclusive_neg_width { + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = 4..=3; + let GOOD_OUTPUT = []; + + let BAD_INPUT = 4..=2; + const EXPECT_MSG = "but ends at"; + + !!generate_tests!! + } + } + + panic_cases! { + mod rangeinclusive_overflow { + let DATA: [i32; 2] = [0, 1]; + + // note: using 0 specifically ensures that the result of overflowing is 0..0, + // so that `get` doesn't simply return None for the wrong reason. + let BAD_INPUT = 0 ..= ::std::usize::MAX; + const EXPECT_MSG = "maximum usize"; + + !!generate_tests!! + } + + mod rangetoinclusive_overflow { + let DATA: [i32; 2] = [0, 1]; + + let BAD_INPUT = ..= ::std::usize::MAX; + const EXPECT_MSG = "maximum usize"; + + !!generate_tests!! + } + } // panic_cases! } #[test] From 02b3da1200df47ea7343dd2cd960b8afe983ac9c Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:19 -0400 Subject: [PATCH 06/36] decrease false negatives for str overflow test --- src/liballoc/tests/str.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index bfba9a6b39355..696ce79f36920 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -602,7 +602,9 @@ mod slice_index { mod rangeinclusive { let DATA = "hello"; - let BAD_INPUT = 1..=usize::max_value(); + // note: using 0 specifically ensures that the result of overflowing is 0..0, + // so that `get` doesn't simply return None for the wrong reason. + let BAD_INPUT = 0..=usize::max_value(); const EXPECT_MSG = "maximum usize"; !!generate_tests!! From 030aa9b112b5b52207186b375d9fc09607bbed48 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:29 -0400 Subject: [PATCH 07/36] revise macro in slice tests --- src/libcore/tests/slice.rs | 154 +++++++++++++++---------------------- 1 file changed, 62 insertions(+), 92 deletions(-) diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 5272c7427d91d..d2bda65de5562 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -376,9 +376,12 @@ fn test_windows_zip() { assert_eq!(res, [14, 18, 22, 26]); } +// The current implementation of SliceIndex fails to handle methods +// orthogonally from range types; therefore, it is worth testing +// all of the indexing operations on each input. mod slice_index { - // Test a slicing operation that should succeed, - // testing it on all of the indexing methods. + // This checks all six indexing methods, given an input range that + // should succeed. (it is NOT suitable for testing invalid inputs) macro_rules! assert_range_eq { ($arr:expr, $range:expr, $expected:expr) => { @@ -423,7 +426,7 @@ mod slice_index { // because if it can't, then what are we even doing here? // // (Be aware this only demonstrates the ability to detect bugs - // in the FIRST method it calls, as the macro is not designed + // in the FIRST method that panics, as the macro is not designed // to be used in `should_panic`) #[test] #[should_panic(expected = "out of range")] @@ -446,30 +449,29 @@ mod slice_index { // and `None` test cases for get/get_mut. macro_rules! panic_cases { ($( - mod $case_name:ident { - let DATA: $Data: ty = $data:expr; + // each test case needs a unique name to namespace the tests + in mod $case_name:ident { + data: $data:expr; // optional: // - // a similar input for which DATA[input] succeeds, and the corresponding - // output as an array. This helps validate "critical points" where an - // input range straddles the boundary between valid and invalid. + // one or more similar inputs for which data[input] succeeds, + // and the corresponding output as an array. This helps validate + // "critical points" where an input range straddles the boundary + // between valid and invalid. // (such as the input `len..len`, which is just barely valid) $( - let GOOD_INPUT = $good:expr; - let GOOD_OUTPUT = $output:expr; + good: data[$good:expr] == $output:expr; )* - let BAD_INPUT = $bad:expr; - const EXPECT_MSG = $expect_msg:expr; - - !!generate_tests!! + bad: data[$bad:expr]; + message: $expect_msg:expr; } )*) => {$( mod $case_name { #[test] fn pass() { - let mut v: $Data = $data; + let mut v = $data; $( assert_range_eq!($data, $good, $output); )* @@ -487,7 +489,7 @@ mod slice_index { #[test] #[should_panic(expected = $expect_msg)] fn index_fail() { - let v: $Data = $data; + let v = $data; let v: &[_] = &v; let _v = &v[$bad]; } @@ -495,7 +497,7 @@ mod slice_index { #[test] #[should_panic(expected = $expect_msg)] fn index_mut_fail() { - let mut v: $Data = $data; + let mut v = $data; let v: &mut [_] = &mut v; let _v = &mut v[$bad]; } @@ -516,112 +518,80 @@ mod slice_index { } panic_cases! { - mod rangefrom_len { - let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; - - let GOOD_INPUT = 6..; - let GOOD_OUTPUT = []; + in mod rangefrom_len { + data: [0, 1, 2, 3, 4, 5]; - let BAD_INPUT = 7..; - const EXPECT_MSG = "but ends at"; // perhaps not ideal - - !!generate_tests!! + good: data[6..] == []; + bad: data[7..]; + message: "but ends at"; // perhaps not ideal } - mod rangeto_len { - let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; - - let GOOD_INPUT = ..6; - let GOOD_OUTPUT = [0, 1, 2, 3, 4, 5]; + in mod rangeto_len { + data: [0, 1, 2, 3, 4, 5]; - let BAD_INPUT = ..7; - const EXPECT_MSG = "out of range"; - - !!generate_tests!! + good: data[..6] == [0, 1, 2, 3, 4, 5]; + bad: data[..7]; + message: "out of range"; } - mod rangetoinclusive_len { - let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; - - let GOOD_INPUT = ..=5; - let GOOD_OUTPUT = [0, 1, 2, 3, 4, 5]; + in mod rangetoinclusive_len { + data: [0, 1, 2, 3, 4, 5]; - let BAD_INPUT = ..=6; - const EXPECT_MSG = "out of range"; - - !!generate_tests!! + good: data[..=5] == [0, 1, 2, 3, 4, 5]; + bad: data[..=6]; + message: "out of range"; } - mod range_len_len { - let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; - - let GOOD_INPUT = 6..6; - let GOOD_OUTPUT = []; + in mod range_len_len { + data: [0, 1, 2, 3, 4, 5]; - let BAD_INPUT = 7..7; - const EXPECT_MSG = "out of range"; - - !!generate_tests!! + good: data[6..6] == []; + bad: data[7..7]; + message: "out of range"; } - mod rangeinclusive_len_len{ - let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; - - let GOOD_INPUT = 6..=5; - let GOOD_OUTPUT = []; - - let BAD_INPUT = 7..=6; - const EXPECT_MSG = "out of range"; + in mod rangeinclusive_len_len { + data: [0, 1, 2, 3, 4, 5]; - !!generate_tests!! + good: data[6..=5] == []; + bad: data[7..=6]; + message: "out of range"; } } panic_cases! { - mod range_neg_width { - let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; - - let GOOD_INPUT = 4..4; - let GOOD_OUTPUT = []; - - let BAD_INPUT = 4..3; - const EXPECT_MSG = "but ends at"; + in mod range_neg_width { + data: [0, 1, 2, 3, 4, 5]; - !!generate_tests!! + good: data[4..4] == []; + bad: data[4..3]; + message: "but ends at"; } - mod rangeinclusive_neg_width { - let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + in mod rangeinclusive_neg_width { + data: [0, 1, 2, 3, 4, 5]; - let GOOD_INPUT = 4..=3; - let GOOD_OUTPUT = []; - - let BAD_INPUT = 4..=2; - const EXPECT_MSG = "but ends at"; - - !!generate_tests!! + good: data[4..=3] == []; + bad: data[4..=2]; + message: "but ends at"; } } panic_cases! { - mod rangeinclusive_overflow { - let DATA: [i32; 2] = [0, 1]; + in mod rangeinclusive_overflow { + data: [0, 1]; // note: using 0 specifically ensures that the result of overflowing is 0..0, // so that `get` doesn't simply return None for the wrong reason. - let BAD_INPUT = 0 ..= ::std::usize::MAX; - const EXPECT_MSG = "maximum usize"; - - !!generate_tests!! + bad: data[0 ..= ::std::usize::MAX]; + message: "maximum usize"; } - mod rangetoinclusive_overflow { - let DATA: [i32; 2] = [0, 1]; - - let BAD_INPUT = ..= ::std::usize::MAX; - const EXPECT_MSG = "maximum usize"; + in mod rangetoinclusive_overflow { + data: [0, 1]; - !!generate_tests!! + bad: data[..= ::std::usize::MAX]; + message: "maximum usize"; } } // panic_cases! } From f1d7b453fed6acefc68f90752922b37c6e3ac7a4 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:36 -0400 Subject: [PATCH 08/36] revise test gen macro for str --- src/liballoc/tests/str.rs | 225 ++++++++++++++------------------------ 1 file changed, 81 insertions(+), 144 deletions(-) diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 696ce79f36920..2edd41a70b945 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -291,11 +291,14 @@ fn test_replace_pattern() { assert_eq!(data.replace(|c| c == 'γ', "😺😺😺"), "abcdαβ😺😺😺δabcdαβ😺😺😺δ"); } +// The current implementation of SliceIndex fails to handle methods +// orthogonally from range types; therefore, it is worth testing +// all of the indexing operations on each input. mod slice_index { // Test a slicing operation **that should succeed,** // testing it on all of the indexing methods. // - // DO NOT use this in `should_panic` tests, unless you are testing the macro itself. + // This is not suitable for testing failure on invalid inputs. macro_rules! assert_range_eq { ($s:expr, $range:expr, $expected:expr) => { @@ -340,7 +343,7 @@ mod slice_index { // because if it can't, then what are we even doing here? // // (Be aware this only demonstrates the ability to detect bugs - // in the FIRST method it calls, as the macro is not designed + // in the FIRST method that panics, as the macro is not designed // to be used in `should_panic`) #[test] #[should_panic(expected = "out of bounds")] @@ -363,8 +366,8 @@ mod slice_index { // and `None` test cases for get/get_mut. macro_rules! panic_cases { ($( - mod $case_name:ident { - let DATA = $data:expr; + in mod $case_name:ident { + data: $data:expr; // optional: // @@ -373,14 +376,11 @@ mod slice_index { // straddles the boundary between valid and invalid. // (such as the input `len..len`, which is just barely valid) $( - let GOOD_INPUT = $good:expr; - let GOOD_OUTPUT = $output:expr; + good: data[$good:expr] == $output:expr; )* - let BAD_INPUT = $bad:expr; - const EXPECT_MSG = $expect_msg:expr; // must be a literal - - !!generate_tests!! + bad: data[$bad:expr]; + message: $expect_msg:expr; // must be a literal } )*) => {$( mod $case_name { @@ -509,114 +509,72 @@ mod slice_index { } panic_cases! { - mod rangefrom_len { - let DATA = "abcdef"; - - let GOOD_INPUT = 6..; - let GOOD_OUTPUT = ""; - - let BAD_INPUT = 7..; - const EXPECT_MSG = "out of bounds"; - - !!generate_tests!! + in mod rangefrom_len { + data: "abcdef"; + good: data[6..] == ""; + bad: data[7..]; + message: "out of bounds"; } - mod rangeto_len { - let DATA = "abcdef"; - - let GOOD_INPUT = ..6; - let GOOD_OUTPUT = "abcdef"; - - let BAD_INPUT = ..7; - const EXPECT_MSG = "out of bounds"; - - !!generate_tests!! + in mod rangeto_len { + data: "abcdef"; + good: data[..6] == "abcdef"; + bad: data[..7]; + message: "out of bounds"; } - mod rangetoinclusive_len { - let DATA = "abcdef"; - - let GOOD_INPUT = ..=5; - let GOOD_OUTPUT = "abcdef"; - - let BAD_INPUT = ..=6; - const EXPECT_MSG = "out of bounds"; - - !!generate_tests!! + in mod rangetoinclusive_len { + data: "abcdef"; + good: data[..=5] == "abcdef"; + bad: data[..=6]; + message: "out of bounds"; } - mod range_len_len { - let DATA = "abcdef"; - - let GOOD_INPUT = 6..6; - let GOOD_OUTPUT = ""; - - let BAD_INPUT = 7..7; - const EXPECT_MSG = "out of bounds"; - - !!generate_tests!! + in mod range_len_len { + data: "abcdef"; + good: data[6..6] == ""; + bad: data[7..7]; + message: "out of bounds"; } - mod rangeinclusive_len_len { - let DATA = "abcdef"; - - let GOOD_INPUT = 6..=5; - let GOOD_OUTPUT = ""; - - let BAD_INPUT = 7..=6; - const EXPECT_MSG = "out of bounds"; - - !!generate_tests!! + in mod rangeinclusive_len_len { + data: "abcdef"; + good: data[6..=5] == ""; + bad: data[7..=6]; + message: "out of bounds"; } } panic_cases! { - mod range_neg_width { - let DATA = "abcdef"; - - let GOOD_INPUT = 4..4; - let GOOD_OUTPUT = ""; - - let BAD_INPUT = 4..3; - const EXPECT_MSG = "begin <= end (4 <= 3)"; - - !!generate_tests!! + in mod range_neg_width { + data: "abcdef"; + good: data[4..4] == ""; + bad: data[4..3]; + message: "begin <= end (4 <= 3)"; } - mod rangeinclusive_neg_width { - let DATA = "abcdef"; - - let GOOD_INPUT = 4..=3; - let GOOD_OUTPUT = ""; - - let BAD_INPUT = 4..=2; - const EXPECT_MSG = "begin <= end (4 <= 3)"; - - !!generate_tests!! + in mod rangeinclusive_neg_width { + data: "abcdef"; + good: data[4..=3] == ""; + bad: data[4..=2]; + message: "begin <= end (4 <= 3)"; } } mod overflow { panic_cases! { - - mod rangeinclusive { - let DATA = "hello"; - + in mod rangeinclusive { + data: "hello"; // note: using 0 specifically ensures that the result of overflowing is 0..0, // so that `get` doesn't simply return None for the wrong reason. - let BAD_INPUT = 0..=usize::max_value(); - const EXPECT_MSG = "maximum usize"; - - !!generate_tests!! + bad: data[0..=usize::max_value()]; + message: "maximum usize"; } - mod rangetoinclusive { - let DATA = "hello"; - - let BAD_INPUT = ..=usize::max_value(); - const EXPECT_MSG = "maximum usize"; - - !!generate_tests!! + in mod rangetoinclusive { + data: "hello"; + bad: data[..=usize::max_value()]; + message: "maximum usize"; } } } @@ -635,74 +593,53 @@ mod slice_index { // because some of the logic may be duplicated as part of micro-optimizations // to dodge unicode boundary checks on half-ranges. panic_cases! { - mod range_1 { - let DATA = super::DATA; - - let BAD_INPUT = super::BAD_START..super::GOOD_END; - const EXPECT_MSG = + in mod range_1 { + data: super::DATA; + bad: data[super::BAD_START..super::GOOD_END]; + message: "byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; - - !!generate_tests!! } - mod range_2 { - let DATA = super::DATA; - - let BAD_INPUT = super::GOOD_START..super::BAD_END; - const EXPECT_MSG = + in mod range_2 { + data: super::DATA; + bad: data[super::GOOD_START..super::BAD_END]; + message: "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; - - !!generate_tests!! } - mod rangefrom { - let DATA = super::DATA; - - let BAD_INPUT = super::BAD_START..; - const EXPECT_MSG = + in mod rangefrom { + data: super::DATA; + bad: data[super::BAD_START..]; + message: "byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; - - !!generate_tests!! } - mod rangeto { - let DATA = super::DATA; - - let BAD_INPUT = ..super::BAD_END; - const EXPECT_MSG = + in mod rangeto { + data: super::DATA; + bad: data[..super::BAD_END]; + message: "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; - - !!generate_tests!! } - mod rangeinclusive_1 { - let DATA = super::DATA; - - let BAD_INPUT = super::BAD_START..=super::GOOD_END_INCL; - const EXPECT_MSG = + in mod rangeinclusive_1 { + data: super::DATA; + bad: data[super::BAD_START..=super::GOOD_END_INCL]; + message: "byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; - - !!generate_tests!! } - mod rangeinclusive_2 { - let DATA = super::DATA; - - let BAD_INPUT = super::GOOD_START..=super::BAD_END_INCL; - const EXPECT_MSG = + in mod rangeinclusive_2 { + data: super::DATA; + bad: data[super::GOOD_START..=super::BAD_END_INCL]; + message: "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; - - !!generate_tests!! } - mod rangetoinclusive { - let DATA = super::DATA; - - let BAD_INPUT = ..=super::BAD_END_INCL; - const EXPECT_MSG = + in mod rangetoinclusive { + data: super::DATA; + bad: data[..=super::BAD_END_INCL]; + message: "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; - - !!generate_tests!! } } } From b0fcb5f4408d59a0dcea4427394a04e72ab5aeff Mon Sep 17 00:00:00 2001 From: Tomas Gavenciak Date: Tue, 27 Mar 2018 19:45:44 +0200 Subject: [PATCH 09/36] Extend tests for RFC1598 (GAT) --- .../collections.rs | 85 +++++++++++++++++++ .../collections.stderr | 34 ++++++++ .../iterable.rs | 34 ++++++++ .../iterable.stderr | 26 +++++- .../shadowing.rs | 44 ++++++++++ .../shadowing.stdout | 0 .../streaming_iterator.rs | 44 ++++++++++ .../streaming_iterator.stderr | 14 ++- 8 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/rfc1598-generic-associated-types/collections.rs create mode 100644 src/test/ui/rfc1598-generic-associated-types/collections.stderr create mode 100644 src/test/ui/rfc1598-generic-associated-types/shadowing.rs create mode 100644 src/test/ui/rfc1598-generic-associated-types/shadowing.stdout diff --git a/src/test/ui/rfc1598-generic-associated-types/collections.rs b/src/test/ui/rfc1598-generic-associated-types/collections.rs new file mode 100644 index 0000000000000..4ea2c82883133 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/collections.rs @@ -0,0 +1,85 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] +#![feature(associated_type_defaults)] + +//FIXME(#44265): "lifetime parameters are not allowed on this type" errors will be addressed in a +//follow-up PR + +// A Collection trait and collection families. +// Based on http://smallcultfollowing.com/babysteps/blog/2016/11/03/associated-type-constructors-part-2-family-traits/ + +trait Collection { + fn empty() -> Self; + fn add(&mut self, value: T); + fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>; + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + type Iter<'iter>: Iterator; + type Family: CollectionFamily; + // Test associated type defaults with parameters + type Sibling: Collection = <>::Family as CollectionFamily>::Member; + //~^ ERROR type parameters are not allowed on this type [E0109] +} + +trait CollectionFamily { + type Member: Collection; +} + +struct VecFamily; + +impl CollectionFamily for VecFamily { + type Member = Vec; +} + +impl Collection for Vec { + fn empty() -> Self { + Vec::new() + } + fn add(&mut self, value: T) { + self.push(value) + } + fn iterate<'iter>(&'iter self) -> Self::Iter<'iter> { + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + self.iter() + } + type Iter<'iter> = std::slice::Iter<'iter, T>; + type Family = VecFamily; +} + +fn floatify(ints: &C) -> <>::Family as CollectionFamily>::Member + //~^ ERROR type parameters are not allowed on this type [E0109] + where C: Collection { + let mut res = C::Family::Member::::empty(); + for &v in ints.iterate() { + res.add(v as f32); + } + res +} + +fn floatify_sibling(ints: &C) -> >::Sibling + //~^ ERROR type parameters are not allowed on this type [E0109] + where C: Collection { + let mut res = C::Family::Member::::empty(); + for &v in ints.iterate() { + res.add(v as f32); + } + res +} + +fn use_floatify() { + let a = vec![1i32, 2, 3]; + let b = floatify(a); + println!("{}", b.iterate().next()); + let c = floatify_sibling(a); + println!("{}", c.iterate().next()); +} + +fn main() {} \ No newline at end of file diff --git a/src/test/ui/rfc1598-generic-associated-types/collections.stderr b/src/test/ui/rfc1598-generic-associated-types/collections.stderr new file mode 100644 index 0000000000000..6aa7aa993fd25 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/collections.stderr @@ -0,0 +1,34 @@ +error[E0109]: type parameters are not allowed on this type + --> $DIR/collections.rs:57:90 + | +LL | fn floatify(ints: &C) -> <>::Family as CollectionFamily>::Member + | ^^^ type parameter not allowed + +error[E0109]: type parameters are not allowed on this type + --> $DIR/collections.rs:67:69 + | +LL | fn floatify_sibling(ints: &C) -> >::Sibling + | ^^^ type parameter not allowed + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/collections.rs:23:50 + | +LL | fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>; + | ^^^^^ lifetime parameter not allowed on this type + +error[E0109]: type parameters are not allowed on this type + --> $DIR/collections.rs:28:100 + | +LL | type Sibling: Collection = <>::Family as CollectionFamily>::Member; + | ^ type parameter not allowed + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/collections.rs:49:50 + | +LL | fn iterate<'iter>(&'iter self) -> Self::Iter<'iter> { + | ^^^^^ lifetime parameter not allowed on this type + +error: aborting due to 5 previous errors + +Some errors occurred: E0109, E0110. +For more information about an error, try `rustc --explain E0109`. diff --git a/src/test/ui/rfc1598-generic-associated-types/iterable.rs b/src/test/ui/rfc1598-generic-associated-types/iterable.rs index 1287ddaf7f7fe..b79aa6179adfd 100644 --- a/src/test/ui/rfc1598-generic-associated-types/iterable.rs +++ b/src/test/ui/rfc1598-generic-associated-types/iterable.rs @@ -29,4 +29,38 @@ trait Iterable { //~^ ERROR lifetime parameters are not allowed on this type [E0110] } +// Impl for struct type +impl Iterable for Vec { + type Item<'a> = &'a T; + type Iter<'a> = std::slice::Iter<'a, T>; + type Iter2<'a> = &'a T; + // gavento: ^^^ Not 100% sure about the intention here + fn iter<'a>(&'a self) -> Self::Iter<'a> { + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + self.iter() + } +} + +// Impl for a primitive type +impl Iterable for [T] { + type Item<'a> = &'a T; + type Iter<'a> = std::slice::Iter<'a, T>; + type Iter2<'a> = &'a T; + // gavento: ^^^ Not 100% sure about the intention here + fn iter<'a>(&'a self) -> Self::Iter<'a> { + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + self.iter() + } +} + +fn make_iter<'a, I: Iterable>(it: &'a I) -> I::Iter<'a> { + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + it.iter() +} + +fn get_first<'a, I: Iterable>(it: &'a I) -> Option> { + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + it.iter().next() +} + fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/iterable.stderr b/src/test/ui/rfc1598-generic-associated-types/iterable.stderr index d33eebb42d607..34266dd3c512f 100644 --- a/src/test/ui/rfc1598-generic-associated-types/iterable.stderr +++ b/src/test/ui/rfc1598-generic-associated-types/iterable.stderr @@ -10,12 +10,36 @@ error[E0110]: lifetime parameters are not allowed on this type LL | type Iter2<'a>: Deref as Iterator>::Item>; | ^^ lifetime parameter not allowed on this type +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/iterable.rs:56:53 + | +LL | fn make_iter<'a, I: Iterable>(it: &'a I) -> I::Iter<'a> { + | ^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/iterable.rs:61:60 + | +LL | fn get_first<'a, I: Iterable>(it: &'a I) -> Option> { + | ^^ lifetime parameter not allowed on this type + error[E0110]: lifetime parameters are not allowed on this type --> $DIR/iterable.rs:28:41 | LL | fn iter<'a>(&'a self) -> Self::Iter<'a>; | ^^ lifetime parameter not allowed on this type -error: aborting due to 3 previous errors +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/iterable.rs:38:41 + | +LL | fn iter<'a>(&'a self) -> Self::Iter<'a> { + | ^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/iterable.rs:50:41 + | +LL | fn iter<'a>(&'a self) -> Self::Iter<'a> { + | ^^ lifetime parameter not allowed on this type + +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0110`. diff --git a/src/test/ui/rfc1598-generic-associated-types/shadowing.rs b/src/test/ui/rfc1598-generic-associated-types/shadowing.rs new file mode 100644 index 0000000000000..6e77ce2b3dd0c --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/shadowing.rs @@ -0,0 +1,44 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +//FIXME(#44265): The lifetime shadowing and type parameter shadowing +// should cause an error. This will be addressed by a future PR. +// For now this compiles: +// must-compile-successfully + +trait Shadow<'a> { + type Bar<'a>; // Error: shadowed lifetime +} + +trait NoShadow<'a> { + type Bar<'b>; // OK +} + +impl<'a> NoShadow<'a> for &'a u32 +{ + type Bar<'a> = i32; // Error: shadowed lifetime +} + +trait ShadowT { + type Bar; // Error: shadowed type parameter +} + +trait NoShadowT { + type Bar; // OK +} + +impl NoShadowT for Option +{ + type Bar = i32; // Error: shadowed type parameter +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/shadowing.stdout b/src/test/ui/rfc1598-generic-associated-types/shadowing.stdout new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs index f9e270ee92e22..522ddb5dc135e 100644 --- a/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs +++ b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs @@ -35,4 +35,48 @@ struct Foo { fn foo(iter: T) where T: StreamingIterator, for<'a> T::Item<'a>: Display { /* ... */ } //~^ ERROR lifetime parameters are not allowed on this type [E0110] +// Full example of enumerate iterator + +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +struct StreamEnumerate { + iter: I, + count: usize, +} + +impl StreamingIterator for StreamEnumerate { + type Item<'a> = (usize, I::Item<'a>); + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + fn next<'a>(&'a self) -> Option> { + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + match self.iter.next() { + None => None, + Some(val) => { + let r = Some((self.count, val)); + self.count += 1; + r + } + } + } +} + +impl StreamEnumerate { + pub fn new(iter: I) -> Self { + StreamEnumerate { + count: 0, + iter: iter, + } + } +} + +fn test_stream_enumerate() { + let v = vec!["a", "b", "c"]; + let se = StreamEnumerate::new(v.iter()); + let a: &str = se.next().unwrap().1; + for (i, s) in se { + println!("{} {}", i, s); + } + println!("{}", a); +} + + fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.stderr b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.stderr index 9ab80151a7ed3..607a4b8d57996 100644 --- a/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.stderr +++ b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.stderr @@ -16,6 +16,18 @@ error[E0110]: lifetime parameters are not allowed on this type LL | fn next<'a>(&'a self) -> Option>; | ^^ lifetime parameter not allowed on this type -error: aborting due to 3 previous errors +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/streaming_iterator.rs:47:37 + | +LL | type Item<'a> = (usize, I::Item<'a>); + | ^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/streaming_iterator.rs:49:48 + | +LL | fn next<'a>(&'a self) -> Option> { + | ^^ lifetime parameter not allowed on this type + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0110`. From e09d9ecbcdbd4563656162be6a69e47a11f67c52 Mon Sep 17 00:00:00 2001 From: Tomas Gavenciak Date: Tue, 27 Mar 2018 22:26:46 +0200 Subject: [PATCH 10/36] Tidy up the code --- .../ui/rfc1598-generic-associated-types/collections.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/ui/rfc1598-generic-associated-types/collections.rs b/src/test/ui/rfc1598-generic-associated-types/collections.rs index 4ea2c82883133..19f5756609678 100644 --- a/src/test/ui/rfc1598-generic-associated-types/collections.rs +++ b/src/test/ui/rfc1598-generic-associated-types/collections.rs @@ -14,8 +14,9 @@ //FIXME(#44265): "lifetime parameters are not allowed on this type" errors will be addressed in a //follow-up PR -// A Collection trait and collection families. -// Based on http://smallcultfollowing.com/babysteps/blog/2016/11/03/associated-type-constructors-part-2-family-traits/ +// A Collection trait and collection families. Based on +// http://smallcultfollowing.com/babysteps/blog/2016/11/03/ +// associated-type-constructors-part-2-family-traits/ trait Collection { fn empty() -> Self; @@ -25,7 +26,8 @@ trait Collection { type Iter<'iter>: Iterator; type Family: CollectionFamily; // Test associated type defaults with parameters - type Sibling: Collection = <>::Family as CollectionFamily>::Member; + type Sibling: Collection = <>::Family as CollectionFamily>:: + Member; //~^ ERROR type parameters are not allowed on this type [E0109] } @@ -82,4 +84,4 @@ fn use_floatify() { println!("{}", c.iterate().next()); } -fn main() {} \ No newline at end of file +fn main() {} From a66a0110de5258507c30e26d7e7d055ffc080cfc Mon Sep 17 00:00:00 2001 From: Tomas Gavenciak Date: Wed, 28 Mar 2018 09:15:36 +0200 Subject: [PATCH 11/36] Fix test stderr after tidying the source --- .../collections.stderr | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/test/ui/rfc1598-generic-associated-types/collections.stderr b/src/test/ui/rfc1598-generic-associated-types/collections.stderr index 6aa7aa993fd25..eda8fb5f93fba 100644 --- a/src/test/ui/rfc1598-generic-associated-types/collections.stderr +++ b/src/test/ui/rfc1598-generic-associated-types/collections.stderr @@ -1,29 +1,29 @@ error[E0109]: type parameters are not allowed on this type - --> $DIR/collections.rs:57:90 + --> $DIR/collections.rs:59:90 | LL | fn floatify(ints: &C) -> <>::Family as CollectionFamily>::Member | ^^^ type parameter not allowed error[E0109]: type parameters are not allowed on this type - --> $DIR/collections.rs:67:69 + --> $DIR/collections.rs:69:69 | LL | fn floatify_sibling(ints: &C) -> >::Sibling | ^^^ type parameter not allowed error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/collections.rs:23:50 + --> $DIR/collections.rs:24:50 | LL | fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>; | ^^^^^ lifetime parameter not allowed on this type error[E0109]: type parameters are not allowed on this type - --> $DIR/collections.rs:28:100 + --> $DIR/collections.rs:30:16 | -LL | type Sibling: Collection = <>::Family as CollectionFamily>::Member; - | ^ type parameter not allowed +LL | Member; + | ^ type parameter not allowed error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/collections.rs:49:50 + --> $DIR/collections.rs:51:50 | LL | fn iterate<'iter>(&'iter self) -> Self::Iter<'iter> { | ^^^^^ lifetime parameter not allowed on this type From 0617b925e83079ebebc53bc9a29a9e4105a39ec3 Mon Sep 17 00:00:00 2001 From: Tomas Gavenciak Date: Wed, 28 Mar 2018 09:22:44 +0200 Subject: [PATCH 12/36] Add tests for GAT parameter number and kindness --- .../parameter_number_and_kind.rs | 56 +++++++++++++++++++ .../parameter_number_and_kind.stderr | 34 +++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.rs create mode 100644 src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.stderr diff --git a/src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.rs b/src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.rs new file mode 100644 index 0000000000000..51527d4117c2c --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.rs @@ -0,0 +1,56 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] +#![feature(associated_type_defaults)] + +//FIXME(#44265): "lifetime parameters are not allowed on this type" errors will be addressed in a +//follow-up PR + +//FIXME(#44265): Update expected errors once E110 is resolved, now does not get past `trait Foo` + +trait Foo { + type A<'a>; + type B<'a, 'b>; + type C; + type D; + type E<'a, T>; + // Test parameters in default values + type FOk = Self::E<'static, T>; + //~^ ERROR type parameters are not allowed on this type [E0109] + //~| ERROR lifetime parameters are not allowed on this type [E0110] + type FErr1 = Self::E<'static, 'static>; // Error + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + type FErr2 = Self::E<'static, T, u32>; // Error + //~^ ERROR type parameters are not allowed on this type [E0109] + //~| ERROR lifetime parameters are not allowed on this type [E0110] +} + +struct Fooy; + +impl Foo for Fooy { + type A = u32; // Error: parameter expected + type B<'a, T> = Vec; // Error: lifetime param expected + type C<'a> = u32; // Error: no param expected + type D<'a> = u32; // Error: type param expected + type E = u32; // Error: lifetime expected as the first param +} + +struct Fooer; + +impl Foo for Fooer { + type A = u32; // Error: lifetime parameter expected + type B<'a> = u32; // Error: another lifetime param expected + type C = T; // Error: no param expected + type D<'b, T> = u32; // Error: unexpected lifetime param + type E<'a, 'b> = u32; // Error: type expected as the second param +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.stderr b/src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.stderr new file mode 100644 index 0000000000000..df83fdaad5bfa --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.stderr @@ -0,0 +1,34 @@ +error[E0109]: type parameters are not allowed on this type + --> $DIR/parameter_number_and_kind.rs:26:36 + | +LL | type FOk = Self::E<'static, T>; + | ^ type parameter not allowed + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/parameter_number_and_kind.rs:26:27 + | +LL | type FOk = Self::E<'static, T>; + | ^^^^^^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/parameter_number_and_kind.rs:29:26 + | +LL | type FErr1 = Self::E<'static, 'static>; // Error + | ^^^^^^^ lifetime parameter not allowed on this type + +error[E0109]: type parameters are not allowed on this type + --> $DIR/parameter_number_and_kind.rs:31:38 + | +LL | type FErr2 = Self::E<'static, T, u32>; // Error + | ^ type parameter not allowed + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/parameter_number_and_kind.rs:31:29 + | +LL | type FErr2 = Self::E<'static, T, u32>; // Error + | ^^^^^^^ lifetime parameter not allowed on this type + +error: aborting due to 5 previous errors + +Some errors occurred: E0109, E0110. +For more information about an error, try `rustc --explain E0109`. From 571337b3dd89a17930a686a66d815f69889e9b66 Mon Sep 17 00:00:00 2001 From: Tomas Gavenciak Date: Wed, 11 Apr 2018 23:41:20 +0200 Subject: [PATCH 13/36] Update tests with Nikos comments --- .../collections.rs | 26 +++++++++++++------ .../collections.stderr | 20 +++++++------- .../construct_with_other_type.rs | 13 ++++++++-- .../construct_with_other_type.stderr | 20 +++++++++++--- .../iterable.rs | 11 ++------ .../iterable.stderr | 18 +++++-------- .../shadowing.rs | 4 +-- 7 files changed, 65 insertions(+), 47 deletions(-) diff --git a/src/test/ui/rfc1598-generic-associated-types/collections.rs b/src/test/ui/rfc1598-generic-associated-types/collections.rs index 19f5756609678..24d756a83314e 100644 --- a/src/test/ui/rfc1598-generic-associated-types/collections.rs +++ b/src/test/ui/rfc1598-generic-associated-types/collections.rs @@ -19,16 +19,19 @@ // associated-type-constructors-part-2-family-traits/ trait Collection { - fn empty() -> Self; - fn add(&mut self, value: T); - fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>; - //~^ ERROR lifetime parameters are not allowed on this type [E0110] type Iter<'iter>: Iterator; type Family: CollectionFamily; // Test associated type defaults with parameters type Sibling: Collection = <>::Family as CollectionFamily>:: Member; //~^ ERROR type parameters are not allowed on this type [E0109] + + fn empty() -> Self; + + fn add(&mut self, value: T); + + fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>; + //~^ ERROR lifetime parameters are not allowed on this type [E0110] } trait CollectionFamily { @@ -42,23 +45,28 @@ impl CollectionFamily for VecFamily { } impl Collection for Vec { + type Iter<'iter> = std::slice::Iter<'iter, T>; + type Family = VecFamily; + fn empty() -> Self { Vec::new() } + fn add(&mut self, value: T) { self.push(value) } + fn iterate<'iter>(&'iter self) -> Self::Iter<'iter> { //~^ ERROR lifetime parameters are not allowed on this type [E0110] self.iter() } - type Iter<'iter> = std::slice::Iter<'iter, T>; - type Family = VecFamily; } fn floatify(ints: &C) -> <>::Family as CollectionFamily>::Member //~^ ERROR type parameters are not allowed on this type [E0109] - where C: Collection { +where + C: Collection, +{ let mut res = C::Family::Member::::empty(); for &v in ints.iterate() { res.add(v as f32); @@ -68,7 +76,9 @@ fn floatify(ints: &C) -> <>::Family as CollectionFamily> fn floatify_sibling(ints: &C) -> >::Sibling //~^ ERROR type parameters are not allowed on this type [E0109] - where C: Collection { +where + C: Collection, +{ let mut res = C::Family::Member::::empty(); for &v in ints.iterate() { res.add(v as f32); diff --git a/src/test/ui/rfc1598-generic-associated-types/collections.stderr b/src/test/ui/rfc1598-generic-associated-types/collections.stderr index eda8fb5f93fba..0a51bf56397df 100644 --- a/src/test/ui/rfc1598-generic-associated-types/collections.stderr +++ b/src/test/ui/rfc1598-generic-associated-types/collections.stderr @@ -1,29 +1,29 @@ error[E0109]: type parameters are not allowed on this type - --> $DIR/collections.rs:59:90 + --> $DIR/collections.rs:65:90 | LL | fn floatify(ints: &C) -> <>::Family as CollectionFamily>::Member | ^^^ type parameter not allowed error[E0109]: type parameters are not allowed on this type - --> $DIR/collections.rs:69:69 + --> $DIR/collections.rs:77:69 | LL | fn floatify_sibling(ints: &C) -> >::Sibling | ^^^ type parameter not allowed -error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/collections.rs:24:50 - | -LL | fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>; - | ^^^^^ lifetime parameter not allowed on this type - error[E0109]: type parameters are not allowed on this type - --> $DIR/collections.rs:30:16 + --> $DIR/collections.rs:26:16 | LL | Member; | ^ type parameter not allowed error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/collections.rs:51:50 + --> $DIR/collections.rs:33:50 + | +LL | fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>; + | ^^^^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/collections.rs:59:50 | LL | fn iterate<'iter>(&'iter self) -> Self::Iter<'iter> { | ^^^^^ lifetime parameter not allowed on this type diff --git a/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.rs b/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.rs index 0d9b487876e21..0429410031526 100644 --- a/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.rs +++ b/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.rs @@ -10,6 +10,8 @@ #![feature(generic_associated_types)] +use std::ops::Deref; + //FIXME(#44265): "lifetime parameters are not allowed on this type" errors will be addressed in a //follow-up PR @@ -18,11 +20,18 @@ trait Foo { } trait Baz { - type Quux<'a>; + type Quux<'a>: Foo; + + // This weird type tests that we can use universal function call syntax to access the Item on + type Baa<'a>: Deref as Foo>::Bar<'a, 'static>>; + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + //~| ERROR lifetime parameters are not allowed on this type [E0110] } impl Baz for T where T: Foo { - type Quux<'a> = ::Bar<'a, 'static>; + type Quux<'a> = T; + + type Baa<'a> = &'a ::Bar<'a, 'static>; //~^ ERROR lifetime parameters are not allowed on this type [E0110] } diff --git a/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.stderr b/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.stderr index 054530e24bd18..764a0db2478a8 100644 --- a/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.stderr +++ b/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.stderr @@ -1,9 +1,21 @@ error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/construct_with_other_type.rs:25:37 + --> $DIR/construct_with_other_type.rs:26:46 | -LL | type Quux<'a> = ::Bar<'a, 'static>; - | ^^ lifetime parameter not allowed on this type +LL | type Baa<'a>: Deref as Foo>::Bar<'a, 'static>>; + | ^^ lifetime parameter not allowed on this type -error: aborting due to previous error +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/construct_with_other_type.rs:26:63 + | +LL | type Baa<'a>: Deref as Foo>::Bar<'a, 'static>>; + | ^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/construct_with_other_type.rs:34:40 + | +LL | type Baa<'a> = &'a ::Bar<'a, 'static>; + | ^^ lifetime parameter not allowed on this type + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0110`. diff --git a/src/test/ui/rfc1598-generic-associated-types/iterable.rs b/src/test/ui/rfc1598-generic-associated-types/iterable.rs index b79aa6179adfd..38967dbbe4530 100644 --- a/src/test/ui/rfc1598-generic-associated-types/iterable.rs +++ b/src/test/ui/rfc1598-generic-associated-types/iterable.rs @@ -20,11 +20,6 @@ trait Iterable { type Iter<'a>: Iterator>; //~^ ERROR lifetime parameters are not allowed on this type [E0110] - // This weird type tests that we can use universal function call syntax to access the Item on - // Self::Iter which we have declared to be an Iterator - type Iter2<'a>: Deref as Iterator>::Item>; - //~^ ERROR lifetime parameters are not allowed on this type [E0110] - fn iter<'a>(&'a self) -> Self::Iter<'a>; //~^ ERROR lifetime parameters are not allowed on this type [E0110] } @@ -33,8 +28,7 @@ trait Iterable { impl Iterable for Vec { type Item<'a> = &'a T; type Iter<'a> = std::slice::Iter<'a, T>; - type Iter2<'a> = &'a T; - // gavento: ^^^ Not 100% sure about the intention here + fn iter<'a>(&'a self) -> Self::Iter<'a> { //~^ ERROR lifetime parameters are not allowed on this type [E0110] self.iter() @@ -45,8 +39,7 @@ impl Iterable for Vec { impl Iterable for [T] { type Item<'a> = &'a T; type Iter<'a> = std::slice::Iter<'a, T>; - type Iter2<'a> = &'a T; - // gavento: ^^^ Not 100% sure about the intention here + fn iter<'a>(&'a self) -> Self::Iter<'a> { //~^ ERROR lifetime parameters are not allowed on this type [E0110] self.iter() diff --git a/src/test/ui/rfc1598-generic-associated-types/iterable.stderr b/src/test/ui/rfc1598-generic-associated-types/iterable.stderr index 34266dd3c512f..0e251300e451f 100644 --- a/src/test/ui/rfc1598-generic-associated-types/iterable.stderr +++ b/src/test/ui/rfc1598-generic-associated-types/iterable.stderr @@ -5,41 +5,35 @@ LL | type Iter<'a>: Iterator>; | ^^ lifetime parameter not allowed on this type error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/iterable.rs:25:48 - | -LL | type Iter2<'a>: Deref as Iterator>::Item>; - | ^^ lifetime parameter not allowed on this type - -error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/iterable.rs:56:53 + --> $DIR/iterable.rs:49:53 | LL | fn make_iter<'a, I: Iterable>(it: &'a I) -> I::Iter<'a> { | ^^ lifetime parameter not allowed on this type error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/iterable.rs:61:60 + --> $DIR/iterable.rs:54:60 | LL | fn get_first<'a, I: Iterable>(it: &'a I) -> Option> { | ^^ lifetime parameter not allowed on this type error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/iterable.rs:28:41 + --> $DIR/iterable.rs:23:41 | LL | fn iter<'a>(&'a self) -> Self::Iter<'a>; | ^^ lifetime parameter not allowed on this type error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/iterable.rs:38:41 + --> $DIR/iterable.rs:32:41 | LL | fn iter<'a>(&'a self) -> Self::Iter<'a> { | ^^ lifetime parameter not allowed on this type error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/iterable.rs:50:41 + --> $DIR/iterable.rs:43:41 | LL | fn iter<'a>(&'a self) -> Self::Iter<'a> { | ^^ lifetime parameter not allowed on this type -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0110`. diff --git a/src/test/ui/rfc1598-generic-associated-types/shadowing.rs b/src/test/ui/rfc1598-generic-associated-types/shadowing.rs index 6e77ce2b3dd0c..6ed7380f3f87a 100644 --- a/src/test/ui/rfc1598-generic-associated-types/shadowing.rs +++ b/src/test/ui/rfc1598-generic-associated-types/shadowing.rs @@ -11,8 +11,8 @@ #![feature(generic_associated_types)] //FIXME(#44265): The lifetime shadowing and type parameter shadowing -// should cause an error. This will be addressed by a future PR. -// For now this compiles: +// should cause an error. Now it compiles (errorneously) and this will be addressed +// by a future PR. Then remove the following: // must-compile-successfully trait Shadow<'a> { From a43171a24257f015113ecf494286d39f47db88ac Mon Sep 17 00:00:00 2001 From: Tomas Gavenciak Date: Thu, 3 May 2018 00:34:34 +0200 Subject: [PATCH 14/36] Update tests to use compile-pass --- src/test/ui/rfc1598-generic-associated-types/shadowing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/rfc1598-generic-associated-types/shadowing.rs b/src/test/ui/rfc1598-generic-associated-types/shadowing.rs index 6ed7380f3f87a..f0e711e71e341 100644 --- a/src/test/ui/rfc1598-generic-associated-types/shadowing.rs +++ b/src/test/ui/rfc1598-generic-associated-types/shadowing.rs @@ -13,7 +13,7 @@ //FIXME(#44265): The lifetime shadowing and type parameter shadowing // should cause an error. Now it compiles (errorneously) and this will be addressed // by a future PR. Then remove the following: -// must-compile-successfully +// compile-pass trait Shadow<'a> { type Bar<'a>; // Error: shadowed lifetime From 9073c897459a8582fa61d87f41654e18f9869b46 Mon Sep 17 00:00:00 2001 From: Tomas Gavenciak Date: Thu, 3 May 2018 00:58:35 +0200 Subject: [PATCH 15/36] Minor fromatting for RFC 1598 tests --- .../ui/rfc1598-generic-associated-types/collections.rs | 8 ++++---- .../rfc1598-generic-associated-types/collections.stderr | 6 +++--- src/test/ui/rfc1598-generic-associated-types/shadowing.rs | 6 ++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/test/ui/rfc1598-generic-associated-types/collections.rs b/src/test/ui/rfc1598-generic-associated-types/collections.rs index 24d756a83314e..e71166ed65bba 100644 --- a/src/test/ui/rfc1598-generic-associated-types/collections.rs +++ b/src/test/ui/rfc1598-generic-associated-types/collections.rs @@ -22,8 +22,8 @@ trait Collection { type Iter<'iter>: Iterator; type Family: CollectionFamily; // Test associated type defaults with parameters - type Sibling: Collection = <>::Family as CollectionFamily>:: - Member; + type Sibling: Collection = + <>::Family as CollectionFamily>::Member; //~^ ERROR type parameters are not allowed on this type [E0109] fn empty() -> Self; @@ -63,7 +63,7 @@ impl Collection for Vec { } fn floatify(ints: &C) -> <>::Family as CollectionFamily>::Member - //~^ ERROR type parameters are not allowed on this type [E0109] +//~^ ERROR type parameters are not allowed on this type [E0109] where C: Collection, { @@ -75,7 +75,7 @@ where } fn floatify_sibling(ints: &C) -> >::Sibling - //~^ ERROR type parameters are not allowed on this type [E0109] +//~^ ERROR type parameters are not allowed on this type [E0109] where C: Collection, { diff --git a/src/test/ui/rfc1598-generic-associated-types/collections.stderr b/src/test/ui/rfc1598-generic-associated-types/collections.stderr index 0a51bf56397df..ed96570583f4f 100644 --- a/src/test/ui/rfc1598-generic-associated-types/collections.stderr +++ b/src/test/ui/rfc1598-generic-associated-types/collections.stderr @@ -11,10 +11,10 @@ LL | fn floatify_sibling(ints: &C) -> >::Sibling | ^^^ type parameter not allowed error[E0109]: type parameters are not allowed on this type - --> $DIR/collections.rs:26:16 + --> $DIR/collections.rs:26:71 | -LL | Member; - | ^ type parameter not allowed +LL | <>::Family as CollectionFamily>::Member; + | ^ type parameter not allowed error[E0110]: lifetime parameters are not allowed on this type --> $DIR/collections.rs:33:50 diff --git a/src/test/ui/rfc1598-generic-associated-types/shadowing.rs b/src/test/ui/rfc1598-generic-associated-types/shadowing.rs index f0e711e71e341..6cdcaf2568394 100644 --- a/src/test/ui/rfc1598-generic-associated-types/shadowing.rs +++ b/src/test/ui/rfc1598-generic-associated-types/shadowing.rs @@ -23,8 +23,7 @@ trait NoShadow<'a> { type Bar<'b>; // OK } -impl<'a> NoShadow<'a> for &'a u32 -{ +impl<'a> NoShadow<'a> for &'a u32 { type Bar<'a> = i32; // Error: shadowed lifetime } @@ -36,8 +35,7 @@ trait NoShadowT { type Bar; // OK } -impl NoShadowT for Option -{ +impl NoShadowT for Option { type Bar = i32; // Error: shadowed type parameter } From 000d3c97eeb286a1a1e9c2fa2a1fc8874ed93731 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Mon, 7 May 2018 22:30:44 -0400 Subject: [PATCH 16/36] Make DepGraph::previous_work_products immutable Fixes #50501 --- src/librustc/dep_graph/graph.rs | 26 ++---- src/librustc_driver/driver.rs | 19 ++-- src/librustc_incremental/persist/load.rs | 112 ++++++++++++----------- 3 files changed, 74 insertions(+), 83 deletions(-) diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index 22ab1b15c8b8e..61a7404b08526 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs @@ -77,7 +77,7 @@ struct DepGraphData { /// things available to us. If we find that they are not dirty, we /// load the path to the file storing those work-products here into /// this map. We can later look for and extract that data. - previous_work_products: RwLock>, + previous_work_products: FxHashMap, /// Work-products that we generate in this run. work_products: RwLock>, @@ -90,7 +90,8 @@ struct DepGraphData { impl DepGraph { - pub fn new(prev_graph: PreviousDepGraph) -> DepGraph { + pub fn new(prev_graph: PreviousDepGraph, + prev_work_products: FxHashMap) -> DepGraph { // Pre-allocate the fingerprints array. We over-allocate a little so // that we hopefully don't have to re-allocate during this compilation // session. @@ -100,7 +101,7 @@ impl DepGraph { (prev_graph_node_count * 115) / 100); DepGraph { data: Some(Lrc::new(DepGraphData { - previous_work_products: RwLock::new(FxHashMap()), + previous_work_products: prev_work_products, work_products: RwLock::new(FxHashMap()), dep_node_debug: Lock::new(FxHashMap()), current: Lock::new(CurrentDepGraph::new()), @@ -460,19 +461,6 @@ impl DepGraph { self.data.as_ref().unwrap().previous.node_to_index(dep_node) } - /// Indicates that a previous work product exists for `v`. This is - /// invoked during initial start-up based on what nodes are clean - /// (and what files exist in the incr. directory). - pub fn insert_previous_work_product(&self, v: &WorkProductId, data: WorkProduct) { - debug!("insert_previous_work_product({:?}, {:?})", v, data); - self.data - .as_ref() - .unwrap() - .previous_work_products - .borrow_mut() - .insert(v.clone(), data); - } - /// Indicates that we created the given work-product in this run /// for `v`. This record will be preserved and loaded in the next /// run. @@ -492,7 +480,7 @@ impl DepGraph { self.data .as_ref() .and_then(|data| { - data.previous_work_products.borrow().get(v).cloned() + data.previous_work_products.get(v).cloned() }) } @@ -504,8 +492,8 @@ impl DepGraph { /// Access the map of work-products created during the cached run. Only /// used during saving of the dep-graph. - pub fn previous_work_products(&self) -> ReadGuard> { - self.data.as_ref().unwrap().previous_work_products.borrow() + pub fn previous_work_products(&self) -> &FxHashMap { + &self.data.as_ref().unwrap().previous_work_products } #[inline(always)] diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 2fb811eba1e9a..1e74039503d51 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -980,15 +980,16 @@ where let dep_graph = match future_dep_graph { None => DepGraph::new_disabled(), Some(future) => { - let prev_graph = time(sess, "blocked while dep-graph loading finishes", || { - future - .open() - .unwrap_or_else(|e| rustc_incremental::LoadResult::Error { - message: format!("could not decode incremental cache: {:?}", e), - }) - .open(sess) - }); - DepGraph::new(prev_graph) + let (prev_graph, prev_work_products) = + time(sess, "blocked while dep-graph loading finishes", || { + future + .open() + .unwrap_or_else(|e| rustc_incremental::LoadResult::Error { + message: format!("could not decode incremental cache: {:?}", e), + }) + .open(sess) + }); + DepGraph::new(prev_graph, prev_work_products) } }; let hir_forest = time(sess, "lowering ast -> hir", || { diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index 44d6e532f79bb..01186483a6839 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -10,7 +10,8 @@ //! Code to save/load the dep-graph from files. -use rustc::dep_graph::{PreviousDepGraph, SerializedDepGraph}; +use rustc_data_structures::fx::FxHashMap; +use rustc::dep_graph::{PreviousDepGraph, SerializedDepGraph, WorkProduct, WorkProductId}; use rustc::session::Session; use rustc::ty::TyCtxt; use rustc::ty::maps::OnDiskCache; @@ -32,65 +33,22 @@ pub fn dep_graph_tcx_init<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.allocate_metadata_dep_nodes(); tcx.precompute_in_scope_traits_hashes(); - - if tcx.sess.incr_comp_session_dir_opt().is_none() { - // If we are only building with -Zquery-dep-graph but without an actual - // incr. comp. session directory, we exit here. Otherwise we'd fail - // when trying to load work products. - return - } - - let work_products_path = work_products_path(tcx.sess); - let load_result = load_data(tcx.sess.opts.debugging_opts.incremental_info, &work_products_path); - - if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result { - // Decode the list of work_products - let mut work_product_decoder = Decoder::new(&work_products_data[..], start_pos); - let work_products: Vec = - RustcDecodable::decode(&mut work_product_decoder).unwrap_or_else(|e| { - let msg = format!("Error decoding `work-products` from incremental \ - compilation session directory: {}", e); - tcx.sess.fatal(&msg[..]) - }); - - for swp in work_products { - let mut all_files_exist = true; - for &(_, ref file_name) in swp.work_product.saved_files.iter() { - let path = in_incr_comp_dir_sess(tcx.sess, file_name); - if !path.exists() { - all_files_exist = false; - - if tcx.sess.opts.debugging_opts.incremental_info { - eprintln!("incremental: could not find file for work \ - product: {}", path.display()); - } - } - } - - if all_files_exist { - debug!("reconcile_work_products: all files for {:?} exist", swp); - tcx.dep_graph.insert_previous_work_product(&swp.id, swp.work_product); - } else { - debug!("reconcile_work_products: some file for {:?} does not exist", swp); - delete_dirty_work_product(tcx, swp); - } - } - } } +type WorkProductMap = FxHashMap; + pub enum LoadResult { Ok { data: T }, DataOutOfDate, Error { message: String }, } - -impl LoadResult { - pub fn open(self, sess: &Session) -> PreviousDepGraph { +impl LoadResult<(PreviousDepGraph, WorkProductMap)> { + pub fn open(self, sess: &Session) -> (PreviousDepGraph, WorkProductMap) { match self { LoadResult::Error { message } => { sess.warn(&message); - PreviousDepGraph::new(SerializedDepGraph::new()) + (PreviousDepGraph::new(SerializedDepGraph::new()), FxHashMap()) }, LoadResult::DataOutOfDate => { if let Err(err) = delete_all_session_dir_contents(sess) { @@ -98,7 +56,7 @@ impl LoadResult { incremental compilation session directory contents `{}`: {}.", dep_graph_path(sess).display(), err)); } - PreviousDepGraph::new(SerializedDepGraph::new()) + (PreviousDepGraph::new(SerializedDepGraph::new()), FxHashMap()) } LoadResult::Ok { data } => data } @@ -125,10 +83,10 @@ fn load_data(report_incremental_info: bool, path: &Path) -> LoadResult<(Vec, } } -fn delete_dirty_work_product(tcx: TyCtxt, +fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) { debug!("delete_dirty_work_product({:?})", swp); - work_product::delete_workproduct_files(tcx.sess, &swp.work_product); + work_product::delete_workproduct_files(sess, &swp.work_product); } /// Either a result that has already be computed or a @@ -149,7 +107,7 @@ impl MaybeAsync { /// Launch a thread and load the dependency graph in the background. pub fn load_dep_graph(sess: &Session) -> - MaybeAsync> + MaybeAsync> { // Since `sess` isn't `Sync`, we perform all accesses to `sess` // before we fire the background thread. @@ -159,7 +117,7 @@ pub fn load_dep_graph(sess: &Session) -> if sess.opts.incremental.is_none() { // No incremental compilation. return MaybeAsync::Sync(LoadResult::Ok { - data: PreviousDepGraph::new(SerializedDepGraph::new()) + data: (PreviousDepGraph::new(SerializedDepGraph::new()), FxHashMap()) }); } @@ -169,6 +127,50 @@ pub fn load_dep_graph(sess: &Session) -> let report_incremental_info = sess.opts.debugging_opts.incremental_info; let expected_hash = sess.opts.dep_tracking_hash(); + let mut prev_work_products = FxHashMap(); + + // If we are only building with -Zquery-dep-graph but without an actual + // incr. comp. session directory, we exit here. Otherwise we'd fail + // when trying to load work products. + if sess.incr_comp_session_dir_opt().is_some() { + let work_products_path = work_products_path(sess); + let load_result = load_data(report_incremental_info, &work_products_path); + + if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result { + // Decode the list of work_products + let mut work_product_decoder = Decoder::new(&work_products_data[..], start_pos); + let work_products: Vec = + RustcDecodable::decode(&mut work_product_decoder).unwrap_or_else(|e| { + let msg = format!("Error decoding `work-products` from incremental \ + compilation session directory: {}", e); + sess.fatal(&msg[..]) + }); + + for swp in work_products { + let mut all_files_exist = true; + for &(_, ref file_name) in swp.work_product.saved_files.iter() { + let path = in_incr_comp_dir_sess(sess, file_name); + if !path.exists() { + all_files_exist = false; + + if sess.opts.debugging_opts.incremental_info { + eprintln!("incremental: could not find file for work \ + product: {}", path.display()); + } + } + } + + if all_files_exist { + debug!("reconcile_work_products: all files for {:?} exist", swp); + prev_work_products.insert(swp.id, swp.work_product); + } else { + debug!("reconcile_work_products: some file for {:?} does not exist", swp); + delete_dirty_work_product(sess, swp); + } + } + } + } + MaybeAsync::Async(std::thread::spawn(move || { time_ext(time_passes, None, "background load prev dep-graph", move || { match load_data(report_incremental_info, &path) { @@ -195,7 +197,7 @@ pub fn load_dep_graph(sess: &Session) -> let dep_graph = SerializedDepGraph::decode(&mut decoder) .expect("Error reading cached dep-graph"); - LoadResult::Ok { data: PreviousDepGraph::new(dep_graph) } + LoadResult::Ok { data: (PreviousDepGraph::new(dep_graph), prev_work_products) } } } }) From ea4942835d607ffa87eb7f50552e6cf023d27403 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 8 May 2018 14:32:31 +0200 Subject: [PATCH 17/36] Don't use Lock for heavily accessed CrateMetadata::cnum_map. --- src/librustc_metadata/creader.rs | 9 ++++++--- src/librustc_metadata/cstore.rs | 5 +++-- src/librustc_metadata/decoder.rs | 6 +++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index d0237071a6058..7efcedd73eddf 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -219,6 +219,8 @@ impl<'a> CrateLoader<'a> { let cnum_map = self.resolve_crate_deps(root, &crate_root, &metadata, cnum, span, dep_kind); + let dependencies: Vec = cnum_map.iter().cloned().collect(); + let def_path_table = record_time(&self.sess.perf_stats.decode_def_path_tables_time, || { crate_root.def_path_table.decode((&metadata, self.sess)) }); @@ -239,8 +241,9 @@ impl<'a> CrateLoader<'a> { }), root: crate_root, blob: metadata, - cnum_map: Lock::new(cnum_map), + cnum_map, cnum, + dependencies: Lock::new(dependencies), codemap_import_info: RwLock::new(vec![]), attribute_cache: Lock::new([Vec::new(), Vec::new()]), dep_kind: Lock::new(dep_kind), @@ -392,7 +395,7 @@ impl<'a> CrateLoader<'a> { // Propagate the extern crate info to dependencies. extern_crate.direct = false; - for &dep_cnum in cmeta.cnum_map.borrow().iter() { + for &dep_cnum in cmeta.dependencies.borrow().iter() { self.update_extern_crate(dep_cnum, extern_crate, visited); } } @@ -1040,7 +1043,7 @@ impl<'a> CrateLoader<'a> { } info!("injecting a dep from {} to {}", cnum, krate); - data.cnum_map.borrow_mut().push(krate); + data.dependencies.borrow_mut().push(krate); }); } } diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 64bbcf436cb9e..f4d4bd3589398 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -64,8 +64,9 @@ pub struct CrateMetadata { pub extern_crate: Lock>, pub blob: MetadataBlob, - pub cnum_map: Lock, + pub cnum_map: CrateNumMap, pub cnum: CrateNum, + pub dependencies: Lock>, pub codemap_import_info: RwLock>, pub attribute_cache: Lock<[Vec>>; 2]>, @@ -144,7 +145,7 @@ impl CStore { } let data = self.get_crate_data(krate); - for &dep in data.cnum_map.borrow().iter() { + for &dep in data.dependencies.borrow().iter() { if dep != krate { self.push_dependencies_in_postorder(ordering, dep); } diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 57f92707ccfe3..53d1ff156274e 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -246,7 +246,7 @@ impl<'a, 'tcx: 'a> TyDecoder<'a, 'tcx> for DecodeContext<'a, 'tcx> { if cnum == LOCAL_CRATE { self.cdata().cnum } else { - self.cdata().cnum_map.borrow()[cnum] + self.cdata().cnum_map[cnum] } } } @@ -932,7 +932,7 @@ impl<'a, 'tcx> CrateMetadata { // Translate a DefId from the current compilation environment to a DefId // for an external crate. fn reverse_translate_def_id(&self, did: DefId) -> Option { - for (local, &global) in self.cnum_map.borrow().iter_enumerated() { + for (local, &global) in self.cnum_map.iter_enumerated() { if global == did.krate { return Some(DefId { krate: local, @@ -1007,7 +1007,7 @@ impl<'a, 'tcx> CrateMetadata { .enumerate() .flat_map(|(i, link)| { let cnum = CrateNum::new(i + 1); - link.map(|link| (self.cnum_map.borrow()[cnum], link)) + link.map(|link| (self.cnum_map[cnum], link)) }) .collect() } From 868d2a18c11f97265b4aa4a0f0bd3f3f735bfc13 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Tue, 8 May 2018 09:13:18 -0400 Subject: [PATCH 18/36] Fix comment --- src/librustc_incremental/persist/load.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index 01186483a6839..f846759545eb9 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -130,7 +130,7 @@ pub fn load_dep_graph(sess: &Session) -> let mut prev_work_products = FxHashMap(); // If we are only building with -Zquery-dep-graph but without an actual - // incr. comp. session directory, we exit here. Otherwise we'd fail + // incr. comp. session directory, we skip this. Otherwise we'd fail // when trying to load work products. if sess.incr_comp_session_dir_opt().is_some() { let work_products_path = work_products_path(sess); From 5128affbc482e65cc678745cd529e7e055b9cf8b Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 4 May 2018 08:46:30 -0700 Subject: [PATCH 19/36] Fix update-references for tests within subdirectories. Fixes #50438. I'll make this more robust later for #49815. --- src/test/ui/update-references.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/ui/update-references.sh b/src/test/ui/update-references.sh index 47a85352b0044..00b4b5c5caa78 100755 --- a/src/test/ui/update-references.sh +++ b/src/test/ui/update-references.sh @@ -26,6 +26,7 @@ if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" fi +MYDIR=$(dirname $0) BUILD_DIR="$1" shift @@ -33,13 +34,13 @@ shift shopt -s nullglob while [[ "$1" != "" ]]; do - MYDIR=$(dirname $1) for EXT in "stderr" "stdout" "fixed"; do for OUT_NAME in $BUILD_DIR/${1%.rs}.*$EXT; do + OUT_DIR=`dirname "$1"` OUT_BASE=`basename "$OUT_NAME"` - if ! (diff $OUT_NAME $MYDIR/$OUT_BASE >& /dev/null); then - echo updating $MYDIR/$OUT_BASE - cp $OUT_NAME $MYDIR + if ! (diff $OUT_NAME $MYDIR/$OUT_DIR/$OUT_BASE >& /dev/null); then + echo updating $MYDIR/$OUT_DIR/$OUT_BASE + cp $OUT_NAME $MYDIR/$OUT_DIR fi done done From 31d2012ef7468a3da17299c9964e8b01d68a67fc Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 8 May 2018 16:50:48 +0200 Subject: [PATCH 20/36] Adapt some method visibilities in librustc_metadata::cstore. --- src/librustc_metadata/cstore.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 64bbcf436cb9e..abecee2277c4b 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -104,15 +104,15 @@ impl CStore { /// You cannot use this function to allocate a CrateNum in a thread-safe manner. /// It is currently only used in CrateLoader which is single-threaded code. - pub fn next_crate_num(&self) -> CrateNum { + pub(super) fn next_crate_num(&self) -> CrateNum { CrateNum::new(self.metas.borrow().len() + 1) } - pub fn get_crate_data(&self, cnum: CrateNum) -> Lrc { + pub(super) fn get_crate_data(&self, cnum: CrateNum) -> Lrc { self.metas.borrow()[cnum].clone().unwrap() } - pub fn set_crate_data(&self, cnum: CrateNum, data: Lrc) { + pub(super) fn set_crate_data(&self, cnum: CrateNum, data: Lrc) { use rustc_data_structures::indexed_vec::Idx; let mut met = self.metas.borrow_mut(); while met.len() <= cnum.index() { @@ -121,7 +121,7 @@ impl CStore { met[cnum] = Some(data); } - pub fn iter_crate_data(&self, mut i: I) + pub(super) fn iter_crate_data(&self, mut i: I) where I: FnMut(CrateNum, &Lrc) { for (k, v) in self.metas.borrow().iter_enumerated() { @@ -131,14 +131,16 @@ impl CStore { } } - pub fn crate_dependencies_in_rpo(&self, krate: CrateNum) -> Vec { + pub(super) fn crate_dependencies_in_rpo(&self, krate: CrateNum) -> Vec { let mut ordering = Vec::new(); self.push_dependencies_in_postorder(&mut ordering, krate); ordering.reverse(); ordering } - pub fn push_dependencies_in_postorder(&self, ordering: &mut Vec, krate: CrateNum) { + pub(super) fn push_dependencies_in_postorder(&self, + ordering: &mut Vec, + krate: CrateNum) { if ordering.contains(&krate) { return; } @@ -153,7 +155,7 @@ impl CStore { ordering.push(krate); } - pub fn do_postorder_cnums_untracked(&self) -> Vec { + pub(super) fn do_postorder_cnums_untracked(&self) -> Vec { let mut ordering = Vec::new(); for (num, v) in self.metas.borrow().iter_enumerated() { if let &Some(_) = v { @@ -163,11 +165,11 @@ impl CStore { return ordering } - pub fn add_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId, cnum: CrateNum) { + pub(super) fn add_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId, cnum: CrateNum) { self.extern_mod_crate_map.borrow_mut().insert(emod_id, cnum); } - pub fn do_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option { + pub(super) fn do_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option { self.extern_mod_crate_map.borrow().get(&emod_id).cloned() } } From 77c40f8c6f8cc472f6438f7724d60bf3b7718a0c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 9 May 2018 15:25:44 +1000 Subject: [PATCH 21/36] Inline `Span` methods. Because they are simple and hot. This change speeds up some incremental runs of a few rustc-perf benchmarks, the best by 3%. --- src/libsyntax_pos/span_encoding.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libsyntax_pos/span_encoding.rs b/src/libsyntax_pos/span_encoding.rs index b55fe4bcb2672..601a0273ae911 100644 --- a/src/libsyntax_pos/span_encoding.rs +++ b/src/libsyntax_pos/span_encoding.rs @@ -31,11 +31,13 @@ pub struct Span(u32); impl Copy for Span {} impl Clone for Span { + #[inline] fn clone(&self) -> Span { *self } } impl PartialEq for Span { + #[inline] fn eq(&self, other: &Span) -> bool { let a = self.0; let b = other.0; @@ -44,6 +46,7 @@ impl PartialEq for Span { } impl Eq for Span {} impl Hash for Span { + #[inline] fn hash(&self, state: &mut H) { let a = self.0; a.hash(state) From 78262e700dc6a7b57e376742f344e80115d2d3f2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 9 May 2018 12:21:48 +1000 Subject: [PATCH 22/36] Use SmallVec for DepNodeIndex within dep_graph. This avoids a decent number of allocations, enough to speed up incremental runs of many rustc-benchmarks, the best by 2%. --- src/librustc/dep_graph/graph.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index 22ab1b15c8b8e..e9400e17b86c0 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs @@ -12,6 +12,7 @@ use errors::DiagnosticBuilder; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::small_vec::SmallVec; use rustc_data_structures::sync::{Lrc, RwLock, ReadGuard, Lock}; use std::env; use std::hash::Hash; @@ -131,7 +132,7 @@ impl DepGraph { let mut edges = Vec::new(); for (index, edge_targets) in current_dep_graph.edges.iter_enumerated() { let from = current_dep_graph.nodes[index]; - for &edge_target in edge_targets { + for &edge_target in edge_targets.iter() { let to = current_dep_graph.nodes[edge_target]; edges.push((from, to)); } @@ -209,7 +210,7 @@ impl DepGraph { self.with_task_impl(key, cx, arg, false, task, |key| OpenTask::Regular(Lock::new(RegularOpenTask { node: key, - reads: Vec::new(), + reads: SmallVec::new(), read_set: FxHashSet(), })), |data, key, task| data.borrow_mut().complete_task(key, task)) @@ -230,7 +231,7 @@ impl DepGraph { self.with_task_impl(key, cx, input, true, identity_fn, |_| OpenTask::Ignore, - |data, key, _| data.borrow_mut().alloc_node(key, Vec::new())) + |data, key, _| data.borrow_mut().alloc_node(key, SmallVec::new())) } fn with_task_impl<'gcx, C, A, R>( @@ -353,7 +354,7 @@ impl DepGraph { if let Some(ref data) = self.data { let (result, open_task) = ty::tls::with_context(|icx| { let task = OpenTask::Anon(Lock::new(AnonOpenTask { - reads: Vec::new(), + reads: SmallVec::new(), read_set: FxHashSet(), })); @@ -626,7 +627,7 @@ impl DepGraph { debug_assert!(data.colors.borrow().get(prev_dep_node_index).is_none()); - let mut current_deps = Vec::new(); + let mut current_deps = SmallVec::new(); for &dep_dep_node_index in prev_deps { let dep_dep_node_color = data.colors.borrow().get(dep_dep_node_index); @@ -923,7 +924,7 @@ pub enum WorkProductFileKind { pub(super) struct CurrentDepGraph { nodes: IndexVec, - edges: IndexVec>, + edges: IndexVec>, node_to_node_index: FxHashMap, forbidden_edge: Option, @@ -1061,7 +1062,7 @@ impl CurrentDepGraph { } = task { debug_assert_eq!(node, key); let krate_idx = self.node_to_node_index[&DepNode::new_no_params(DepKind::Krate)]; - self.alloc_node(node, vec![krate_idx]) + self.alloc_node(node, SmallVec::one(krate_idx)) } else { bug!("complete_eval_always_task() - Expected eval always task to be popped"); } @@ -1107,7 +1108,7 @@ impl CurrentDepGraph { fn alloc_node(&mut self, dep_node: DepNode, - edges: Vec) + edges: SmallVec<[DepNodeIndex; 8]>) -> DepNodeIndex { debug_assert_eq!(self.edges.len(), self.nodes.len()); debug_assert_eq!(self.node_to_node_index.len(), self.nodes.len()); @@ -1122,12 +1123,12 @@ impl CurrentDepGraph { pub struct RegularOpenTask { node: DepNode, - reads: Vec, + reads: SmallVec<[DepNodeIndex; 8]>, read_set: FxHashSet, } pub struct AnonOpenTask { - reads: Vec, + reads: SmallVec<[DepNodeIndex; 8]>, read_set: FxHashSet, } From 0ba1c101dce22fbe30933a90efd237a09227e07d Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Wed, 9 May 2018 06:47:37 -0700 Subject: [PATCH 23/36] Clarify in the docs that `mul_add` is not always faster. Fixes https://github.com/rust-lang/rust/issues/49842. Other resources: - https://users.rust-lang.org/t/why-does-the-mul-add-method-produce-a-more-accurate-result-with-better-performance/1626 - https://en.wikipedia.org/wiki/Multiply%E2%80%93accumulate_operation --- src/libstd/f32.rs | 6 ++++-- src/libstd/f64.rs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 26644c769575c..4f4baf1e8cd91 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -195,8 +195,10 @@ impl f32 { } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding - /// error. This produces a more accurate result with better performance than - /// a separate multiplication operation followed by an add. + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` can be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. /// /// ``` /// use std::f32; diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index a7e63f59b1c67..e00ff60452dd0 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -173,8 +173,10 @@ impl f64 { } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding - /// error. This produces a more accurate result with better performance than - /// a separate multiplication operation followed by an add. + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` can be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. /// /// ``` /// let m = 10.0_f64; From b8b957d4792be619a3251c917199fe7e7c01997c Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 8 May 2018 17:15:42 +0200 Subject: [PATCH 24/36] Make CrateNum allocation more thread-safe. --- src/librustc_metadata/creader.rs | 5 +---- src/librustc_metadata/cstore.rs | 20 +++++++++----------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index d0237071a6058..d9038105da08b 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -51,7 +51,6 @@ pub struct Library { pub struct CrateLoader<'a> { pub sess: &'a Session, cstore: &'a CStore, - next_crate_num: CrateNum, local_crate_name: Symbol, } @@ -102,7 +101,6 @@ impl<'a> CrateLoader<'a> { CrateLoader { sess, cstore, - next_crate_num: cstore.next_crate_num(), local_crate_name: Symbol::intern(local_crate_name), } } @@ -198,8 +196,7 @@ impl<'a> CrateLoader<'a> { self.verify_no_symbol_conflicts(span, &crate_root); // Claim this crate number and cache it - let cnum = self.next_crate_num; - self.next_crate_num = CrateNum::from_u32(cnum.as_u32() + 1); + let cnum = self.cstore.alloc_new_crate_num(); // Stash paths for top-most crate locally if necessary. let crate_paths = if root.is_none() { diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index abecee2277c4b..4872d560d27f0 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -96,16 +96,17 @@ pub struct CStore { impl CStore { pub fn new(metadata_loader: Box) -> CStore { CStore { - metas: RwLock::new(IndexVec::new()), + metas: RwLock::new(IndexVec::from_elem_n(None, 1)), extern_mod_crate_map: Lock::new(FxHashMap()), metadata_loader, } } - /// You cannot use this function to allocate a CrateNum in a thread-safe manner. - /// It is currently only used in CrateLoader which is single-threaded code. - pub(super) fn next_crate_num(&self) -> CrateNum { - CrateNum::new(self.metas.borrow().len() + 1) + pub(super) fn alloc_new_crate_num(&self) -> CrateNum { + let mut metas = self.metas.borrow_mut(); + let cnum = CrateNum::new(metas.len()); + metas.push(None); + cnum } pub(super) fn get_crate_data(&self, cnum: CrateNum) -> Lrc { @@ -113,12 +114,9 @@ impl CStore { } pub(super) fn set_crate_data(&self, cnum: CrateNum, data: Lrc) { - use rustc_data_structures::indexed_vec::Idx; - let mut met = self.metas.borrow_mut(); - while met.len() <= cnum.index() { - met.push(None); - } - met[cnum] = Some(data); + let mut metas = self.metas.borrow_mut(); + assert!(metas[cnum].is_none(), "Overwriting crate metadata entry"); + metas[cnum] = Some(data); } pub(super) fn iter_crate_data(&self, mut i: I) From a9810899a290a7746e88382558454e31bb8f85b7 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 25 Apr 2018 15:45:04 +0200 Subject: [PATCH 25/36] Allow for specifying a linker plugin for cross-language LTO --- src/librustc/session/config.rs | 55 +++++++++++++++++++++-- src/librustc/session/mod.rs | 7 +++ src/librustc_trans/back/link.rs | 3 ++ src/librustc_trans/back/linker.rs | 52 ++++++++++++++++++++- src/librustc_trans/back/write.rs | 10 ++--- src/test/run-make/cross-lang-lto/Makefile | 4 +- 6 files changed, 118 insertions(+), 13 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 83dac033f9408..245663494ddef 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -95,6 +95,23 @@ pub enum Lto { Fat, } +#[derive(Clone, PartialEq, Hash)] +pub enum CrossLangLto { + LinkerPlugin(PathBuf), + NoLink, + Disabled +} + +impl CrossLangLto { + pub fn embed_bitcode(&self) -> bool { + match *self { + CrossLangLto::LinkerPlugin(_) | + CrossLangLto::NoLink => true, + CrossLangLto::Disabled => false, + } + } +} + #[derive(Clone, Copy, PartialEq, Hash)] pub enum DebugInfoLevel { NoDebugInfo, @@ -412,6 +429,7 @@ top_level_options!( // Remap source path prefixes in all output (messages, object files, debug, etc) remap_path_prefix: Vec<(PathBuf, PathBuf)> [UNTRACKED], + edition: Edition [TRACKED], } ); @@ -777,11 +795,15 @@ macro_rules! options { Some("`string` or `string=string`"); pub const parse_lto: Option<&'static str> = Some("one of `thin`, `fat`, or omitted"); + pub const parse_cross_lang_lto: Option<&'static str> = + Some("either a boolean (`yes`, `no`, `on`, `off`, etc), `no-link`, \ + or the path to the linker plugin"); } #[allow(dead_code)] mod $mod_set { - use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer, Lto}; + use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer, Lto, + CrossLangLto}; use rustc_target::spec::{LinkerFlavor, PanicStrategy, RelroLevel}; use std::path::PathBuf; @@ -986,6 +1008,26 @@ macro_rules! options { true } + fn parse_cross_lang_lto(slot: &mut CrossLangLto, v: Option<&str>) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { + CrossLangLto::NoLink + } else { + CrossLangLto::Disabled + }; + return true + } + } + + *slot = match v { + None | + Some("no-link") => CrossLangLto::NoLink, + Some(path) => CrossLangLto::LinkerPlugin(PathBuf::from(path)), + }; + true + } } ) } @@ -1295,7 +1337,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "make the current crate share its generic instantiations"), chalk: bool = (false, parse_bool, [TRACKED], "enable the experimental Chalk-based trait solving engine"), - cross_lang_lto: bool = (false, parse_bool, [TRACKED], + cross_lang_lto: CrossLangLto = (CrossLangLto::Disabled, parse_cross_lang_lto, [TRACKED], "generate build artifacts that are compatible with linker-based LTO."), } @@ -2327,7 +2369,7 @@ mod dep_tracking { use std::path::PathBuf; use std::collections::hash_map::DefaultHasher; use super::{CrateType, DebugInfoLevel, ErrorOutputType, Lto, OptLevel, OutputTypes, - Passes, Sanitizer}; + Passes, Sanitizer, CrossLangLto}; use syntax::feature_gate::UnstableFeatures; use rustc_target::spec::{PanicStrategy, RelroLevel, TargetTriple}; use syntax::edition::Edition; @@ -2391,6 +2433,7 @@ mod dep_tracking { impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(TargetTriple); impl_dep_tracking_hash_via_hash!(Edition); + impl_dep_tracking_hash_via_hash!(CrossLangLto); impl_dep_tracking_hash_for_sortable_vec_of!(String); impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf); @@ -2455,7 +2498,7 @@ mod tests { use lint; use middle::cstore; use session::config::{build_configuration, build_session_options_and_crate_config}; - use session::config::Lto; + use session::config::{Lto, CrossLangLto}; use session::build_session; use std::collections::{BTreeMap, BTreeSet}; use std::iter::FromIterator; @@ -3111,6 +3154,10 @@ mod tests { opts = reference.clone(); opts.debugging_opts.relro_level = Some(RelroLevel::Full); assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.cross_lang_lto = CrossLangLto::NoLink; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); } #[test] diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 2ab72ba20bf4f..23f84881c7980 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -657,6 +657,13 @@ impl Session { } } + pub fn target_cpu(&self) -> &str { + match self.opts.cg.target_cpu { + Some(ref s) => &**s, + None => &*self.target.target.options.cpu + } + } + pub fn must_not_eliminate_frame_pointers(&self) -> bool { if let Some(x) = self.opts.cg.force_frame_pointers { x diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 92f9a9e8ba974..d39556e9bb197 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -970,6 +970,9 @@ fn link_args(cmd: &mut Linker, out_filename: &Path, trans: &CrateTranslation) { + // Linker plugins should be specified early in the list of arguments + cmd.cross_lang_lto(); + // The default library location, we need this to find the runtime. // The location of crates will be determined as needed. let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index ea3f5b408604e..2a84ffe79b285 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -21,7 +21,8 @@ use back::symbol_export; use rustc::hir::def_id::{LOCAL_CRATE, CrateNum}; use rustc::middle::dependency_format::Linkage; use rustc::session::Session; -use rustc::session::config::{self, CrateType, OptLevel, DebugInfoLevel}; +use rustc::session::config::{self, CrateType, OptLevel, DebugInfoLevel, + CrossLangLto}; use rustc::ty::TyCtxt; use rustc_target::spec::{LinkerFlavor, LldFlavor}; use serialize::{json, Encoder}; @@ -127,6 +128,7 @@ pub trait Linker { fn subsystem(&mut self, subsystem: &str); fn group_start(&mut self); fn group_end(&mut self); + fn cross_lang_lto(&mut self); // Should have been finalize(self), but we don't support self-by-value on trait objects (yet?). fn finalize(&mut self) -> Command; } @@ -434,6 +436,42 @@ impl<'a> Linker for GccLinker<'a> { self.linker_arg("--end-group"); } } + + fn cross_lang_lto(&mut self) { + match self.sess.opts.debugging_opts.cross_lang_lto { + CrossLangLto::Disabled | + CrossLangLto::NoLink => { + // Nothing to do + } + CrossLangLto::LinkerPlugin(ref path) => { + self.linker_arg(&format!("-plugin={}", path.display())); + + let opt_level = match self.sess.opts.optimize { + config::OptLevel::No => "O0", + config::OptLevel::Less => "O1", + config::OptLevel::Default => "O2", + config::OptLevel::Aggressive => "O3", + config::OptLevel::Size => "Os", + config::OptLevel::SizeMin => "Oz", + }; + + self.linker_arg(&format!("-plugin-opt={}", opt_level)); + self.linker_arg(&format!("-plugin-opt=mcpu={}", self.sess.target_cpu())); + + match self.sess.opts.cg.lto { + config::Lto::Thin | + config::Lto::ThinLocal => { + self.linker_arg(&format!("-plugin-opt=thin")); + } + config::Lto::Fat | + config::Lto::Yes | + config::Lto::No => { + // default to regular LTO + } + } + } + } + } } pub struct MsvcLinker<'a> { @@ -666,6 +704,10 @@ impl<'a> Linker for MsvcLinker<'a> { // MSVC doesn't need group indicators fn group_start(&mut self) {} fn group_end(&mut self) {} + + fn cross_lang_lto(&mut self) { + // Do nothing + } } pub struct EmLinker<'a> { @@ -832,6 +874,10 @@ impl<'a> Linker for EmLinker<'a> { // Appears not necessary on Emscripten fn group_start(&mut self) {} fn group_end(&mut self) {} + + fn cross_lang_lto(&mut self) { + // Do nothing + } } fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec { @@ -984,4 +1030,8 @@ impl Linker for WasmLd { // Not needed for now with LLD fn group_start(&mut self) {} fn group_end(&mut self) {} + + fn cross_lang_lto(&mut self) { + // Do nothing for now + } } diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index b6fae3eaff23a..64876e82309f0 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -174,10 +174,7 @@ pub fn target_machine_factory(sess: &Session, find_features: bool) let triple = &sess.target.target.llvm_target; let triple = CString::new(triple.as_bytes()).unwrap(); - let cpu = match sess.opts.cg.target_cpu { - Some(ref s) => &**s, - None => &*sess.target.target.options.cpu - }; + let cpu = sess.target_cpu(); let cpu = CString::new(cpu.as_bytes()).unwrap(); let features = attributes::llvm_target_features(sess) .collect::>() @@ -294,7 +291,7 @@ impl ModuleConfig { self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode; let embed_bitcode = sess.target.target.options.embed_bitcode || sess.opts.debugging_opts.embed_bitcode || - sess.opts.debugging_opts.cross_lang_lto; + sess.opts.debugging_opts.cross_lang_lto.embed_bitcode(); if embed_bitcode { match sess.opts.optimize { config::OptLevel::No | @@ -1358,7 +1355,8 @@ fn execute_work_item(cgcx: &CodegenContext, // Don't run LTO passes when cross-lang LTO is enabled. The linker // will do that for us in this case. - let needs_lto = needs_lto && !cgcx.opts.debugging_opts.cross_lang_lto; + let needs_lto = needs_lto && + !cgcx.opts.debugging_opts.cross_lang_lto.embed_bitcode(); if needs_lto { Ok(WorkItemResult::NeedsLTO(mtrans)) diff --git a/src/test/run-make/cross-lang-lto/Makefile b/src/test/run-make/cross-lang-lto/Makefile index 98b509cd81f54..925f686fe1161 100644 --- a/src/test/run-make/cross-lang-lto/Makefile +++ b/src/test/run-make/cross-lang-lto/Makefile @@ -18,9 +18,9 @@ endif OBJDUMP=llvm-objdump SECTION_HEADERS=$(OBJDUMP) -section-headers -BUILD_LIB=$(RUSTC) lib.rs -Copt-level=2 -Z cross-lang-lto -Ccodegen-units=1 +BUILD_LIB=$(RUSTC) lib.rs -Copt-level=2 -Z cross-lang-lto=no-link -Ccodegen-units=1 -BUILD_EXE=$(RUSTC) main.rs -Copt-level=2 -Z cross-lang-lto -Ccodegen-units=1 --emit=obj +BUILD_EXE=$(RUSTC) main.rs -Copt-level=2 -Z cross-lang-lto=no-link -Ccodegen-units=1 --emit=obj all: staticlib staticlib-fat-lto staticlib-thin-lto rlib exe cdylib rdylib From 23aa4831026b96869d0e0c283d39ba5fb35e786f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20K=C3=B6ln?= Date: Wed, 9 May 2018 18:03:13 +0200 Subject: [PATCH 26/36] add fn `into_inner(self) -> (Idx, Idx)` to RangeInclusive (#49022) --- src/libcore/ops/range.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index b01a769eda7fd..697e6a3efde28 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -411,6 +411,21 @@ impl RangeInclusive { pub fn end(&self) -> &Idx { &self.end } + + /// Destructures the RangeInclusive into (lower bound, upper (inclusive) bound). + /// + /// # Examples + /// + /// ``` + /// #![feature(inclusive_range_methods)] + /// + /// assert_eq!((3..=5).into_inner(), (3, 5)); + /// ``` + #[unstable(feature = "inclusive_range_methods", issue = "49022")] + #[inline] + pub fn into_inner(self) -> (Idx, Idx) { + (self.start, self.end) + } } #[stable(feature = "inclusive_range", since = "1.26.0")] From 254b6014d20f51a3e91b88c24a8f19e31f17acc9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 9 May 2018 08:33:49 -0700 Subject: [PATCH 27/36] std: Avoid `ptr::copy` if unnecessary in `vec::Drain` This commit is spawned out of a performance regression investigation in #50496. In tracking down this regression it turned out that the `expand_statements` function in the compiler was taking quite a long time. Further investigation showed two key properties: * The function was "fast" on glibc 2.24 and slow on glibc 2.23 * The hottest function was memmove from glibc Combined together it looked like glibc gained an optimization to the memmove function in 2.24. Ideally we don't want to rely on this optimization, so I wanted to dig further to see what was happening. The hottest part of `expand_statements` was `Drop for Drain` in the call to `splice` where we insert new statements into the original vector. This *should* be a cheap operation because we're draining and replacing iterators of the exact same length, but under the hood memmove was being called a lot, causing a slowdown on glibc 2.23. It turns out that at least one of the optimizations in glibc 2.24 was that `memmove` where the src/dst are equal becomes much faster. [This program][prog] executes in ~2.5s against glibc 2.23 and ~0.3s against glibc 2.24, exhibiting how glibc 2.24 is optimizing `memmove` if the src/dst are equal. And all that brings us to what this commit itself is doing. The change here is purely to `Drop for Drain` to avoid the call to `ptr::copy` if the region being copied doesn't actually need to be copied. For normal usage of just `Drain` itself this check isn't really necessary, but because `Splice` internally contains `Drain` this provides a nice speed boost on glibc 2.23. Overall this should fix the regression seen in #50496 on glibc 2.23 and also fix the regression on Windows where `memmove` looks to not have this optimization. Note that the way `splice` was called in `expand_statements` would cause a quadratic number of elements to be copied via `memmove` which is likely why the tuple-stress benchmark showed such a severe regression. Closes #50496 [prog]: https://gist.github.com/alexcrichton/c05bc51c6771bba5ae5b57561a6c1cd3 --- src/liballoc/vec.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 35d0a69a05abe..690cbcb559bbf 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2533,9 +2533,11 @@ impl<'a, T> Drop for Drain<'a, T> { // memmove back untouched tail, update to new length let start = source_vec.len(); let tail = self.tail_start; - let src = source_vec.as_ptr().offset(tail as isize); - let dst = source_vec.as_mut_ptr().offset(start as isize); - ptr::copy(src, dst, self.tail_len); + if tail != start { + let src = source_vec.as_ptr().offset(tail as isize); + let dst = source_vec.as_mut_ptr().offset(start as isize); + ptr::copy(src, dst, self.tail_len); + } source_vec.set_len(start + self.tail_len); } } From 4537025c7193b4f1412807ab15f723f51ff0b01e Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 9 May 2018 21:32:18 +0200 Subject: [PATCH 28/36] Add comment about first element in CStore::metas. --- src/librustc_metadata/cstore.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 4872d560d27f0..0c54ec7c27ab2 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -96,6 +96,10 @@ pub struct CStore { impl CStore { pub fn new(metadata_loader: Box) -> CStore { CStore { + // We add an empty entry for LOCAL_CRATE (which maps to zero) in + // order to make array indices in `metas` match with the + // corresponding `CrateNum`. This first entry will always remain + // `None`. metas: RwLock::new(IndexVec::from_elem_n(None, 1)), extern_mod_crate_map: Lock::new(FxHashMap()), metadata_loader, From 8010604b2d888ac839147fe27de76cdcc713aa1b Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Wed, 9 May 2018 18:03:56 -0400 Subject: [PATCH 29/36] move See also links to top --- src/liballoc/slice.rs | 4 ++-- src/liballoc/str.rs | 4 ++-- src/libcore/num/f32.rs | 4 ++-- src/libcore/num/f64.rs | 4 ++-- src/libstd/f32.rs | 4 ++-- src/libstd/f64.rs | 4 ++-- src/libstd/primitive_docs.rs | 16 ++++++++-------- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index d50a3458f20d1..6caf12aa7eb81 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -10,6 +10,8 @@ //! A dynamically-sized view into a contiguous sequence, `[T]`. //! +//! *[See also the slice primitive type](../../std/primitive.slice.html).* +//! //! Slices are a view into a block of memory represented as a pointer and a //! length. //! @@ -78,8 +80,6 @@ //! * Further methods that return iterators are [`.split`], [`.splitn`], //! [`.chunks`], [`.windows`] and more. //! -//! *[See also the slice primitive type](../../std/primitive.slice.html).* -//! //! [`Clone`]: ../../std/clone/trait.Clone.html //! [`Eq`]: ../../std/cmp/trait.Eq.html //! [`Ord`]: ../../std/cmp/trait.Ord.html diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 9e693c89be90d..42efdea74b1ab 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -10,6 +10,8 @@ //! Unicode string slices. //! +//! *[See also the `str` primitive type](../../std/primitive.str.html).* +//! //! The `&str` type is one of the two main string types, the other being `String`. //! Unlike its `String` counterpart, its contents are borrowed. //! @@ -29,8 +31,6 @@ //! ``` //! let hello_world: &'static str = "Hello, world!"; //! ``` -//! -//! *[See also the `str` primitive type](../../std/primitive.str.html).* #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 672119eba7f9d..4a7dc13f0f2ca 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -11,9 +11,9 @@ //! This module provides constants which are specific to the implementation //! of the `f32` floating point data type. //! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! //! *[See also the `f32` primitive type](../../std/primitive.f32.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 220b23a1e6a01..801de5e87bd10 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -11,9 +11,9 @@ //! This module provides constants which are specific to the implementation //! of the `f64` floating point data type. //! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! //! *[See also the `f64` primitive type](../../std/primitive.f64.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 26644c769575c..f4d897b0111e7 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -11,9 +11,9 @@ //! This module provides constants which are specific to the implementation //! of the `f32` floating point data type. //! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! //! *[See also the `f32` primitive type](../../std/primitive.f32.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index a7e63f59b1c67..bd24e84dbedc5 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -11,9 +11,9 @@ //! This module provides constants which are specific to the implementation //! of the `f64` floating point data type. //! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! //! *[See also the `f64` primitive type](../../std/primitive.f64.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs index 437d7d74cae0d..6e329d85539b1 100644 --- a/src/libstd/primitive_docs.rs +++ b/src/libstd/primitive_docs.rs @@ -370,6 +370,8 @@ mod prim_unit { } // /// Raw, unsafe pointers, `*const T`, and `*mut T`. /// +/// *[See also the `std::ptr` module](ptr/index.html).* +/// /// Working with raw pointers in Rust is uncommon, /// typically limited to a few patterns. /// @@ -444,8 +446,6 @@ mod prim_unit { } /// but C APIs hand out a lot of pointers generally, so are a common source /// of raw pointers in Rust. /// -/// *[See also the `std::ptr` module](ptr/index.html).* -/// /// [`null`]: ../std/ptr/fn.null.html /// [`null_mut`]: ../std/ptr/fn.null_mut.html /// [`is_null`]: ../std/primitive.pointer.html#method.is_null @@ -563,6 +563,8 @@ mod prim_array { } // /// A dynamically-sized view into a contiguous sequence, `[T]`. /// +/// *[See also the `std::slice` module](slice/index.html).* +/// /// Slices are a view into a block of memory represented as a pointer and a /// length. /// @@ -585,8 +587,6 @@ mod prim_array { } /// assert_eq!(x, &[1, 7, 3]); /// ``` /// -/// *[See also the `std::slice` module](slice/index.html).* -/// #[stable(feature = "rust1", since = "1.0.0")] mod prim_slice { } @@ -862,11 +862,11 @@ mod prim_u128 { } // /// The pointer-sized signed integer type. /// +/// *[See also the `std::isize` module](isize/index.html).* +/// /// The size of this primitive is how many bytes it takes to reference any /// location in memory. For example, on a 32 bit target, this is 4 bytes /// and on a 64 bit target, this is 8 bytes. -/// -/// *[See also the `std::isize` module](isize/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_isize { } @@ -874,11 +874,11 @@ mod prim_isize { } // /// The pointer-sized unsigned integer type. /// +/// *[See also the `std::usize` module](usize/index.html).* +/// /// The size of this primitive is how many bytes it takes to reference any /// location in memory. For example, on a 32 bit target, this is 4 bytes /// and on a 64 bit target, this is 8 bytes. -/// -/// *[See also the `std::usize` module](usize/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_usize { } From b8eb91a5ade04804118d39a0f74ae908f33b6268 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Wed, 9 May 2018 18:05:36 -0400 Subject: [PATCH 30/36] make std::str link into See also link also make a drive-by typo fix --- src/libstd/primitive_docs.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs index 6e329d85539b1..7074928eaf6da 100644 --- a/src/libstd/primitive_docs.rs +++ b/src/libstd/primitive_docs.rs @@ -594,15 +594,13 @@ mod prim_slice { } // /// String slices. /// +/// *[See also the `std::str` module](str/index.html).* +/// /// The `str` type, also called a 'string slice', is the most primitive string /// type. It is usually seen in its borrowed form, `&str`. It is also the type /// of string literals, `&'static str`. /// -/// Strings slices are always valid UTF-8. -/// -/// This documentation describes a number of methods and trait implementations -/// on the `str` type. For technical reasons, there is additional, separate -/// documentation in the [`std::str`](str/index.html) module as well. +/// String slices are always valid UTF-8. /// /// # Examples /// From 9c4e5b3b6cbf9b31ac7bd74d760214e36e79bfff Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 10 May 2018 09:16:10 +0900 Subject: [PATCH 31/36] Restore RawVec::reserve* documentation When the RawVec::try_reserve* methods were added, they took the place of the ::reserve* methods in the source file, and new ::reserve* methods wrapping the new try_reserve* methods were created. But the documentation didn't move along, such that: - reserve_* methods are barely documented. - try_reserve_* methods have unmodified documentation from reserve_*, such that their documentation indicate they are panicking/aborting. This moves the documentation back to the right methods, with a placeholder documentation for the try_reserve* methods. --- src/liballoc/raw_vec.rs | 113 ++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 4d73d3aa07e66..5c6f6b22aae06 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -385,26 +385,7 @@ impl RawVec { } } - /// Ensures that the buffer contains at least enough space to hold - /// `used_cap + needed_extra_cap` elements. If it doesn't already, - /// will reallocate the minimum possible amount of memory necessary. - /// Generally this will be exactly the amount of memory necessary, - /// but in principle the allocator is free to give back more than - /// we asked for. - /// - /// If `used_cap` exceeds `self.cap()`, this may fail to actually allocate - /// the requested space. This is not really unsafe, but the unsafe - /// code *you* write that relies on the behavior of this function may break. - /// - /// # Panics - /// - /// * Panics if the requested capacity exceeds `usize::MAX` bytes. - /// * Panics on 32-bit platforms if the requested capacity exceeds - /// `isize::MAX` bytes. - /// - /// # Aborts - /// - /// Aborts on OOM + /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. pub fn try_reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) -> Result<(), CollectionAllocErr> { @@ -441,6 +422,26 @@ impl RawVec { } } + /// Ensures that the buffer contains at least enough space to hold + /// `used_cap + needed_extra_cap` elements. If it doesn't already, + /// will reallocate the minimum possible amount of memory necessary. + /// Generally this will be exactly the amount of memory necessary, + /// but in principle the allocator is free to give back more than + /// we asked for. + /// + /// If `used_cap` exceeds `self.cap()`, this may fail to actually allocate + /// the requested space. This is not really unsafe, but the unsafe + /// code *you* write that relies on the behavior of this function may break. + /// + /// # Panics + /// + /// * Panics if the requested capacity exceeds `usize::MAX` bytes. + /// * Panics on 32-bit platforms if the requested capacity exceeds + /// `isize::MAX` bytes. + /// + /// # Aborts + /// + /// Aborts on OOM pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve_exact(used_cap, needed_extra_cap) { Err(CapacityOverflow) => capacity_overflow(), @@ -463,6 +464,42 @@ impl RawVec { Ok(cmp::max(double_cap, required_cap)) } + /// The same as `reserve`, but returns on errors instead of panicking or aborting. + pub fn try_reserve(&mut self, used_cap: usize, needed_extra_cap: usize) + -> Result<(), CollectionAllocErr> { + unsafe { + // NOTE: we don't early branch on ZSTs here because we want this + // to actually catch "asking for more than usize::MAX" in that case. + // If we make it past the first branch then we are guaranteed to + // panic. + + // Don't actually need any more capacity. + // Wrapping in case they give a bad `used_cap` + if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { + return Ok(()); + } + + let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)?; + let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; + + // FIXME: may crash and burn on over-reserve + alloc_guard(new_layout.size())?; + + let res = match self.current_layout() { + Some(layout) => { + debug_assert!(new_layout.align() == layout.align()); + self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) + } + None => self.a.alloc(new_layout), + }; + + self.ptr = res?.cast().into(); + self.cap = new_cap; + + Ok(()) + } + } + /// Ensures that the buffer contains at least enough space to hold /// `used_cap + needed_extra_cap` elements. If it doesn't already have /// enough capacity, will reallocate enough space plus comfortable slack @@ -515,42 +552,6 @@ impl RawVec { /// # vector.push_all(&[1, 3, 5, 7, 9]); /// # } /// ``` - pub fn try_reserve(&mut self, used_cap: usize, needed_extra_cap: usize) - -> Result<(), CollectionAllocErr> { - unsafe { - // NOTE: we don't early branch on ZSTs here because we want this - // to actually catch "asking for more than usize::MAX" in that case. - // If we make it past the first branch then we are guaranteed to - // panic. - - // Don't actually need any more capacity. - // Wrapping in case they give a bad `used_cap` - if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { - return Ok(()); - } - - let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)?; - let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; - - // FIXME: may crash and burn on over-reserve - alloc_guard(new_layout.size())?; - - let res = match self.current_layout() { - Some(layout) => { - debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) - } - None => self.a.alloc(new_layout), - }; - - self.ptr = res?.cast().into(); - self.cap = new_cap; - - Ok(()) - } - } - - /// The same as try_reserve, but errors are lowered to a call to oom(). pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve(used_cap, needed_extra_cap) { Err(CapacityOverflow) => capacity_overflow(), From ae3feff0281b2f08f6dbb75dc2e50e338a711e5f Mon Sep 17 00:00:00 2001 From: Isaac Whitfield Date: Wed, 9 May 2018 23:36:57 -0700 Subject: [PATCH 32/36] Remove unnecessary mutable borrow and resizing --- src/librustc/dep_graph/graph.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index 22ab1b15c8b8e..b26281ad7ebf2 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs @@ -534,15 +534,9 @@ impl DepGraph { } pub fn serialize(&self) -> SerializedDepGraph { - let mut fingerprints = self.fingerprints.borrow_mut(); let current_dep_graph = self.data.as_ref().unwrap().current.borrow(); - // Make sure we don't run out of bounds below. - if current_dep_graph.nodes.len() > fingerprints.len() { - fingerprints.resize(current_dep_graph.nodes.len(), Fingerprint::ZERO); - } - - let fingerprints = fingerprints.clone().convert_index_type(); + let fingerprints = self.fingerprints.borrow().clone().convert_index_type(); let nodes = current_dep_graph.nodes.clone().convert_index_type(); let total_edge_count: usize = current_dep_graph.edges.iter() From 7def3f0c82a95ee9147c969e94665418bf77468c Mon Sep 17 00:00:00 2001 From: kennytm Date: Thu, 10 May 2018 20:00:29 +0800 Subject: [PATCH 33/36] Retry when downloading the Docker cache. Prevent spuriously needing to rebuild the docker image when the network was down. Also, adjusted the retry function to insert a sleep between retries, because retrying immediately will often just hit the same issue. --- src/ci/docker/run.sh | 4 +++- src/ci/shared.sh | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index c470ae7eb3030..3465e386cd925 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -36,8 +36,10 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then s3url="s3://$SCCACHE_BUCKET/docker/$cksum" url="https://s3-us-west-1.amazonaws.com/$SCCACHE_BUCKET/docker/$cksum" echo "Attempting to download $s3url" + rm -f /tmp/rustci_docker_cache set +e - loaded_images=$(curl $url | docker load | sed 's/.* sha/sha/') + retry curl -f -L -C - -o /tmp/rustci_docker_cache "$url" + loaded_images=$(docker load -i /tmp/rustci_docker_cache | sed 's/.* sha/sha/') set -e echo "Downloaded containers:\n$loaded_images" fi diff --git a/src/ci/shared.sh b/src/ci/shared.sh index 4a08683e3ee86..bb6945f0fd6bb 100644 --- a/src/ci/shared.sh +++ b/src/ci/shared.sh @@ -21,11 +21,12 @@ function retry { while true; do "$@" && break || { if [[ $n -lt $max ]]; then + sleep $n # don't retry immediately ((n++)) echo "Command failed. Attempt $n/$max:" else echo "The command has failed after $n attempts." - exit 1 + return 1 fi } done From 12446dd52cd078ff05e7d0d2fc341fb0a569fb5d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 7 May 2018 11:43:58 -0700 Subject: [PATCH 34/36] Pull in a wasm fix from LLVM upstream This pulls in a fix for https://bugs.llvm.org/show_bug.cgi?id=36564 which has already landed in upstream LLVM and should... Closes rust-lang-nursery/rust-wasm#168 --- src/llvm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm b/src/llvm index b6c1a03fb498f..fd7dd99edf371 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit b6c1a03fb498f6c03d1cbfd4404223a046f8c3b2 +Subproject commit fd7dd99edf371ac502ae4e70288c027f6692ace0 From 85f57389bf42c3a3ab648f4662580c84d9bfdd4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 9 May 2018 16:26:18 -0700 Subject: [PATCH 35/36] Fix tuple struct field spans --- src/libsyntax/parse/parser.rs | 2 +- src/test/ui/issue-3008-1.stderr | 2 +- src/test/ui/issue-32326.stderr | 2 +- src/test/ui/rfc-2093-infer-outlives/enum.rs | 4 ++-- .../ui/rfc-2093-infer-outlives/enum.stderr | 12 +++++------ src/test/ui/span/E0204.stderr | 2 +- src/test/ui/union/union-sized-field.stderr | 2 +- src/test/ui/unsized-enum2.stderr | 20 +++++++++---------- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index bf4a68679df55..49b30c6f460fe 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -5741,7 +5741,7 @@ impl<'a> Parser<'a> { let vis = p.parse_visibility(true)?; let ty = p.parse_ty()?; Ok(StructField { - span: lo.to(p.span), + span: lo.to(ty.span), vis, ident: None, id: ast::DUMMY_NODE_ID, diff --git a/src/test/ui/issue-3008-1.stderr b/src/test/ui/issue-3008-1.stderr index 05c1f2aae8141..fe3e294d9e32a 100644 --- a/src/test/ui/issue-3008-1.stderr +++ b/src/test/ui/issue-3008-1.stderr @@ -5,7 +5,7 @@ LL | enum Bar { | ^^^^^^^^ recursive type has infinite size ... LL | BarSome(Bar) - | ---- recursive without indirection + | --- recursive without indirection | = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Bar` representable diff --git a/src/test/ui/issue-32326.stderr b/src/test/ui/issue-32326.stderr index 1aa99c402ed8d..b7e359af64ad0 100644 --- a/src/test/ui/issue-32326.stderr +++ b/src/test/ui/issue-32326.stderr @@ -4,7 +4,7 @@ error[E0072]: recursive type `Expr` has infinite size LL | enum Expr { //~ ERROR E0072 | ^^^^^^^^^ recursive type has infinite size LL | Plus(Expr, Expr), - | ----- ----- recursive without indirection + | ---- ---- recursive without indirection | | | recursive without indirection | diff --git a/src/test/ui/rfc-2093-infer-outlives/enum.rs b/src/test/ui/rfc-2093-infer-outlives/enum.rs index 7d0427adb9f3e..5071465b5f647 100644 --- a/src/test/ui/rfc-2093-infer-outlives/enum.rs +++ b/src/test/ui/rfc-2093-infer-outlives/enum.rs @@ -20,14 +20,14 @@ enum Foo<'a, T> { // Type U needs to outlive lifetime 'b struct Bar<'b, U> { - field2: &'b U //~ ERROR 23:5: 23:18: the parameter type `U` may not live long enough [E0309] + field2: &'b U //~ ERROR the parameter type `U` may not live long enough [E0309] } // Type K needs to outlive lifetime 'c. enum Ying<'c, K> { - One(&'c Yang) //~ ERROR 30:9: 30:21: the parameter type `K` may not live long enough [E0309] + One(&'c Yang) //~ ERROR the parameter type `K` may not live long enough [E0309] } struct Yang { diff --git a/src/test/ui/rfc-2093-infer-outlives/enum.stderr b/src/test/ui/rfc-2093-infer-outlives/enum.stderr index e6eaf9b475450..604dd0b43c04a 100644 --- a/src/test/ui/rfc-2093-infer-outlives/enum.stderr +++ b/src/test/ui/rfc-2093-infer-outlives/enum.stderr @@ -3,13 +3,13 @@ error[E0309]: the parameter type `U` may not live long enough | LL | struct Bar<'b, U> { | - help: consider adding an explicit lifetime bound `U: 'b`... -LL | field2: &'b U //~ ERROR 23:5: 23:18: the parameter type `U` may not live long enough [E0309] +LL | field2: &'b U //~ ERROR the parameter type `U` may not live long enough [E0309] | ^^^^^^^^^^^^^ | note: ...so that the reference type `&'b U` does not outlive the data it points at --> $DIR/enum.rs:23:5 | -LL | field2: &'b U //~ ERROR 23:5: 23:18: the parameter type `U` may not live long enough [E0309] +LL | field2: &'b U //~ ERROR the parameter type `U` may not live long enough [E0309] | ^^^^^^^^^^^^^ error[E0309]: the parameter type `K` may not live long enough @@ -17,14 +17,14 @@ error[E0309]: the parameter type `K` may not live long enough | LL | enum Ying<'c, K> { | - help: consider adding an explicit lifetime bound `K: 'c`... -LL | One(&'c Yang) //~ ERROR 30:9: 30:21: the parameter type `K` may not live long enough [E0309] - | ^^^^^^^^^^^^ +LL | One(&'c Yang) //~ ERROR the parameter type `K` may not live long enough [E0309] + | ^^^^^^^^^^^ | note: ...so that the reference type `&'c Yang` does not outlive the data it points at --> $DIR/enum.rs:30:9 | -LL | One(&'c Yang) //~ ERROR 30:9: 30:21: the parameter type `K` may not live long enough [E0309] - | ^^^^^^^^^^^^ +LL | One(&'c Yang) //~ ERROR the parameter type `K` may not live long enough [E0309] + | ^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/span/E0204.stderr b/src/test/ui/span/E0204.stderr index 8be1af61d80b3..2949a22747b87 100644 --- a/src/test/ui/span/E0204.stderr +++ b/src/test/ui/span/E0204.stderr @@ -32,7 +32,7 @@ LL | #[derive(Copy)] //~ ERROR may not be implemented for this type | ^^^^ LL | enum EFoo2<'a> { LL | Bar(&'a mut bool), - | ------------- this field does not implement `Copy` + | ------------ this field does not implement `Copy` error: aborting due to 4 previous errors diff --git a/src/test/ui/union/union-sized-field.stderr b/src/test/ui/union/union-sized-field.stderr index fb81a7b695d4e..ba80af6c7e037 100644 --- a/src/test/ui/union/union-sized-field.stderr +++ b/src/test/ui/union/union-sized-field.stderr @@ -22,7 +22,7 @@ error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied --> $DIR/union-sized-field.rs:23:11 | LL | Value(T), //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied - | ^^ `T` does not have a constant size known at compile-time + | ^ `T` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `T` = help: consider adding a `where T: std::marker::Sized` bound diff --git a/src/test/ui/unsized-enum2.stderr b/src/test/ui/unsized-enum2.stderr index c05c1cfe41296..0e18efbf9da3e 100644 --- a/src/test/ui/unsized-enum2.stderr +++ b/src/test/ui/unsized-enum2.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `W: std::marker::Sized` is not satisfied --> $DIR/unsized-enum2.rs:33:8 | LL | VA(W), //~ ERROR `W: std::marker::Sized` is not satisfied - | ^^ `W` does not have a constant size known at compile-time + | ^ `W` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `W` = help: consider adding a `where W: std::marker::Sized` bound @@ -22,7 +22,7 @@ error[E0277]: the trait bound `Y: std::marker::Sized` is not satisfied --> $DIR/unsized-enum2.rs:35:15 | LL | VC(isize, Y), //~ ERROR `Y: std::marker::Sized` is not satisfied - | ^^ `Y` does not have a constant size known at compile-time + | ^ `Y` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `Y` = help: consider adding a `where Y: std::marker::Sized` bound @@ -42,7 +42,7 @@ error[E0277]: the trait bound `[u8]: std::marker::Sized` is not satisfied --> $DIR/unsized-enum2.rs:39:8 | LL | VE([u8]), //~ ERROR `[u8]: std::marker::Sized` is not satisfied - | ^^^^^ `[u8]` does not have a constant size known at compile-time + | ^^^^ `[u8]` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `[u8]` = note: no field of an enum variant may have a dynamically sized type @@ -60,7 +60,7 @@ error[E0277]: the trait bound `[f32]: std::marker::Sized` is not satisfied --> $DIR/unsized-enum2.rs:41:15 | LL | VG(isize, [f32]), //~ ERROR `[f32]: std::marker::Sized` is not satisfied - | ^^^^^^ `[f32]` does not have a constant size known at compile-time + | ^^^^^ `[f32]` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `[f32]` = note: no field of an enum variant may have a dynamically sized type @@ -78,7 +78,7 @@ error[E0277]: the trait bound `Foo + 'static: std::marker::Sized` is not satisfi --> $DIR/unsized-enum2.rs:51:8 | LL | VM(Foo), //~ ERROR `Foo + 'static: std::marker::Sized` is not satisfied - | ^^^^ `Foo + 'static` does not have a constant size known at compile-time + | ^^^ `Foo + 'static` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `Foo + 'static` = note: no field of an enum variant may have a dynamically sized type @@ -96,7 +96,7 @@ error[E0277]: the trait bound `FooBar + 'static: std::marker::Sized` is not sati --> $DIR/unsized-enum2.rs:53:15 | LL | VO(isize, FooBar), //~ ERROR `FooBar + 'static: std::marker::Sized` is not satisfied - | ^^^^^^^ `FooBar + 'static` does not have a constant size known at compile-time + | ^^^^^^ `FooBar + 'static` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `FooBar + 'static` = note: no field of an enum variant may have a dynamically sized type @@ -114,7 +114,7 @@ error[E0277]: the trait bound `[i8]: std::marker::Sized` is not satisfied --> $DIR/unsized-enum2.rs:57:8 | LL | VQ(<&'static [i8] as Deref>::Target), //~ ERROR `[i8]: std::marker::Sized` is not satisfied - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `[i8]` does not have a constant size known at compile-time + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `[i8]` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `[i8]` = note: no field of an enum variant may have a dynamically sized type @@ -132,7 +132,7 @@ error[E0277]: the trait bound `[f64]: std::marker::Sized` is not satisfied --> $DIR/unsized-enum2.rs:60:15 | LL | VS(isize, <&'static [f64] as Deref>::Target), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `[f64]` does not have a constant size known at compile-time + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `[f64]` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `[f64]` = note: no field of an enum variant may have a dynamically sized type @@ -150,7 +150,7 @@ error[E0277]: the trait bound `PathHelper1 + 'static: std::marker::Sized` is not --> $DIR/unsized-enum2.rs:45:8 | LL | VI(Path1), //~ ERROR `PathHelper1 + 'static: std::marker::Sized` is not satisfied - | ^^^^^^ `PathHelper1 + 'static` does not have a constant size known at compile-time + | ^^^^^ `PathHelper1 + 'static` does not have a constant size known at compile-time | = help: within `Path1`, the trait `std::marker::Sized` is not implemented for `PathHelper1 + 'static` = note: required because it appears within the type `Path1` @@ -170,7 +170,7 @@ error[E0277]: the trait bound `PathHelper3 + 'static: std::marker::Sized` is not --> $DIR/unsized-enum2.rs:47:15 | LL | VK(isize, Path3), //~ ERROR `PathHelper3 + 'static: std::marker::Sized` is not satisfied - | ^^^^^^ `PathHelper3 + 'static` does not have a constant size known at compile-time + | ^^^^^ `PathHelper3 + 'static` does not have a constant size known at compile-time | = help: within `Path3`, the trait `std::marker::Sized` is not implemented for `PathHelper3 + 'static` = note: required because it appears within the type `Path3` From 2c5d13dc9ca6897ef65de99107e08cc4de5b11dc Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 10 May 2018 13:51:51 -0700 Subject: [PATCH 36/36] Skip a memory-hungry test that OOMs Attempting to fix https://travis-ci.org/rust-lang/rust/jobs/377407894 via some selective ignoring tests --- src/liballoc/tests/str.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 2edd41a70b945..1a47e5433ea90 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -479,6 +479,7 @@ mod slice_index { } #[test] + #[cfg(not(target_arch = "asmjs"))] // hits an OOM fn simple_big() { fn a_million_letter_x() -> String { let mut i = 0;