From 644550f3de4805b3dec6ade4a60ecf48c1d2487b Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Sat, 27 Jul 2024 16:47:10 +0200 Subject: [PATCH 01/23] Improve panic message and surrounding documentation for Ord violations The new sort implementations have the ability to detect Ord violations in many cases. This commit improves the message in a way that should help users realize what went wrong in their program. --- .../core/src/slice/sort/shared/smallsort.rs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 5111ed8756bf1..f3b0bf8b018e6 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -834,9 +834,10 @@ unsafe fn bidirectional_merge bool>( right = right.add((!left_nonempty) as usize); } - // We now should have consumed the full input exactly once. This can - // only fail if the comparison operator fails to be Ord, in which case - // we will panic and never access the inconsistent state in dst. + // We now should have consumed the full input exactly once. This can only fail if the + // user-provided comparison operator fails implements a strict weak ordering as required by + // Ord incorrectly, in which case we will panic and never access the inconsistent state in + // dst. if left != left_end || right != right_end { panic_on_ord_violation(); } @@ -845,7 +846,21 @@ unsafe fn bidirectional_merge bool>( #[inline(never)] fn panic_on_ord_violation() -> ! { - panic!("Ord violation"); + // This is indicative of a logic bug in the user-provided comparison function or Ord + // implementation. They are expected to implement a total order as explained in the Ord + // documentation. + // + // By raising this panic we inform the user, that they have a logic bug in their program. If a + // strict weak ordering is not given, the concept of comparison based sorting makes no sense. + // E.g.: a < b < c < a + // + // The Ord documentation requires users to implement a total order, arguably that's + // unnecessarily strict in the context of sorting. Issues only arise if the weaker requirement + // of a strict weak ordering is violated. + // + // The panic message talks about a total order because that's what the Ord documentation talks + // about and requires, so as to not confuse users. + panic!("user-provided comparison function does not correctly implement a total order"); } #[must_use] From 00ce238885825e5e987dbcd4fef266e96157303d Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Sat, 27 Jul 2024 16:48:42 +0200 Subject: [PATCH 02/23] Improve panic sections for sort*, sort_unstable* and select_nth_unstable* - Move panic information into # Panics section - Fix mentions of T: Ord that should be compare - Add missing information --- library/alloc/src/slice.rs | 16 +++++++++++++--- library/core/src/slice/mod.rs | 27 +++++++++++++++------------ 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index c7960b3fb49c3..9e222c6072fd7 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -199,7 +199,9 @@ impl [T] { /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it /// clamps at `self.len() / 2`. /// - /// If `T: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if `T: Ord` does not implement a total order. /// /// # Examples /// @@ -258,7 +260,9 @@ impl [T] { /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it /// clamps at `self.len() / 2`. /// - /// If `T: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if `compare` does not implement a total order. /// /// # Examples /// @@ -304,7 +308,9 @@ impl [T] { /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it /// clamps at `self.len() / 2`. /// - /// If `K: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if `K: Ord` does not implement a total order. /// /// # Examples /// @@ -356,6 +362,10 @@ impl [T] { /// In the worst case, the algorithm allocates temporary storage in a `Vec<(K, usize)>` the /// length of the slice. /// + /// # Panics + /// + /// May panic if `K: Ord` does not implement a total order. + /// /// # Examples /// /// ``` diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 6d3e625bef428..8916d43a8f236 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2898,7 +2898,9 @@ impl [T] { /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. /// - /// If `T: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if `T: Ord` does not implement a total order. /// /// # Examples /// @@ -2955,7 +2957,9 @@ impl [T] { /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. /// - /// If `T: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if `compare` does not implement a total order. /// /// # Examples /// @@ -2999,7 +3003,9 @@ impl [T] { /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. /// - /// If `K: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if `K: Ord` does not implement a total order. /// /// # Examples /// @@ -3042,15 +3048,14 @@ impl [T] { /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime /// for all inputs. /// - /// It is typically faster than stable sorting, except in a few special cases, e.g., when the - /// slice is nearly fully sorted, where `slice::sort` may be faster. - /// /// [`sort_unstable`]: slice::sort_unstable /// /// # Panics /// /// Panics when `index >= len()`, meaning it always panics on empty slices. /// + /// May panic if `T: Ord` does not implement a total order. + /// /// # Examples /// /// ``` @@ -3103,15 +3108,14 @@ impl [T] { /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime /// for all inputs. /// - /// It is typically faster than stable sorting, except in a few special cases, e.g., when the - /// slice is nearly fully sorted, where `slice::sort` may be faster. - /// /// [`sort_unstable`]: slice::sort_unstable /// /// # Panics /// /// Panics when `index >= len()`, meaning it always panics on empty slices. /// + /// May panic if `compare` does not implement a total order. + /// /// # Examples /// /// ``` @@ -3168,15 +3172,14 @@ impl [T] { /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime /// for all inputs. /// - /// It is typically faster than stable sorting, except in a few special cases, e.g., when the - /// slice is nearly fully sorted, where `slice::sort` may be faster. - /// /// [`sort_unstable`]: slice::sort_unstable /// /// # Panics /// /// Panics when `index >= len()`, meaning it always panics on empty slices. /// + /// May panic if `K: Ord` does not implement a total order. + /// /// # Examples /// /// ``` From afc404fdfc1559b4ba2846a054347e806bb8f465 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Wed, 31 Jul 2024 11:28:11 +0200 Subject: [PATCH 03/23] Apply review comments - Use if the implementation of [`Ord`] for `T` language - Link to total order wiki page - Rework total order help and examples - Improve language to be more precise and less prone to misunderstandings. - Fix usage of `sort_unstable_by` in `sort_by` example - Fix missing author mention - Use more consistent example input for sort - Use more idiomatic assert_eq! in examples - Use more natural "comparison function" language instead of "comparator function" --- library/alloc/src/slice.rs | 88 ++++++++++--------- library/core/src/slice/mod.rs | 80 +++++++++-------- .../core/src/slice/sort/shared/smallsort.rs | 13 ++- library/core/src/slice/sort/unstable/mod.rs | 2 +- 4 files changed, 97 insertions(+), 86 deletions(-) diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 9e222c6072fd7..21270dbed0c61 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -179,15 +179,25 @@ impl [T] { /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) /// worst-case. /// - /// If `T: Ord` does not implement a total order the resulting order is unspecified. All - /// original elements will remain in the slice and any possible modifications via interior - /// mutability are observed in the input. Same is true if `T: Ord` panics. + /// If the implementation of [`Ord`] for `T` does not implement a [total order] the resulting + /// order of elements in the slice is unspecified. All original elements will remain in the + /// slice and any possible modifications via interior mutability are observed in the input. Same + /// is true if the implementation of [`Ord`] for `T` panics. /// /// When applicable, unstable sorting is preferred because it is generally faster than stable /// sorting and it doesn't allocate auxiliary memory. See /// [`sort_unstable`](slice::sort_unstable). The exception are partially sorted slices, which /// may be better served with `slice::sort`. /// + /// Sorting types that only implement [`PartialOrd`] such as [`f32`] and [`f64`] requires + /// additional precautions. For example Rust defines `NaN != NaN`, which doesn't fulfill the + /// reflexivity requirement posed by [`Ord`]. By using an alternative comparison function with + /// [`slice::sort_by`] such as [`f32::total_cmp`] or [`f64::total_cmp`] that defines a [total + /// order] users can sort slices containing floating point numbers. Alternatively, if one can + /// guarantee that all values in the slice are comparable with [`PartialOrd::partial_cmp`] *and* + /// the implementation forms a [total order], it's possible to sort the slice with `sort_by(|a, + /// b| a.partial_cmp(b).unwrap())`. + /// /// # Current implementation /// /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which @@ -201,18 +211,19 @@ impl [T] { /// /// # Panics /// - /// May panic if `T: Ord` does not implement a total order. + /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [-5, 4, 1, -3, 2]; + /// let mut v = [4, -5, 1, -3, 2]; /// /// v.sort(); - /// assert!(v == [-5, -3, 1, 2, 4]); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); /// ``` /// /// [driftsort]: https://github.com/Voultapher/driftsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] @@ -224,30 +235,19 @@ impl [T] { stable_sort(self, T::lt); } - /// Sorts the slice with a comparator function, preserving initial order of equal elements. + /// Sorts the slice with a comparison function, preserving initial order of equal elements. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) /// worst-case. /// - /// The comparator function should define a total ordering for the elements in the slice. If the - /// ordering is not total, the order of the elements is unspecified. - /// - /// If the comparator function does not implement a total order the resulting order is - /// unspecified. All original elements will remain in the slice and any possible modifications - /// via interior mutability are observed in the input. Same is true if the comparator function - /// panics. A total order (for all `a`, `b` and `c`): - /// - /// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and - /// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. + /// If the comparison function `compare` does not implement a [total order] the resulting order + /// of elements in the slice is unspecified. All original elements will remain in the slice and + /// any possible modifications via interior mutability are observed in the input. Same is true + /// if `compare` panics. /// - /// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use - /// `partial_cmp` as our sort function when we know the slice doesn't contain a `NaN`. - /// - /// ``` - /// let mut floats = [5f64, 4.0, 1.0, 3.0, 2.0]; - /// floats.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); - /// assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 5.0]); - /// ``` + /// For example `|a, b| (a - b).cmp(a)` is a comparison function that is neither transitive nor + /// reflexive nor total, `a < b < c < a` with `a = 1, b = 2, c = 3`. For more information and + /// examples see the [`Ord`] documentation. /// /// # Current implementation /// @@ -262,21 +262,22 @@ impl [T] { /// /// # Panics /// - /// May panic if `compare` does not implement a total order. + /// May panic if `compare` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [5, 4, 1, 3, 2]; + /// let mut v = [4, -5, 1, -3, 2]; /// v.sort_by(|a, b| a.cmp(b)); - /// assert!(v == [1, 2, 3, 4, 5]); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); /// /// // reverse sorting /// v.sort_by(|a, b| b.cmp(a)); - /// assert!(v == [5, 4, 3, 2, 1]); + /// assert_eq!(v, [4, 2, 1, -3, -5]); /// ``` /// /// [driftsort]: https://github.com/Voultapher/driftsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] @@ -293,9 +294,10 @@ impl [T] { /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* \* log(*n*)) /// worst-case, where the key function is *O*(*m*). /// - /// If `K: Ord` does not implement a total order the resulting order is unspecified. - /// All original elements will remain in the slice and any possible modifications via interior - /// mutability are observed in the input. Same is true if `K: Ord` panics. + /// If the implementation of [`Ord`] for `K` does not implement a [total order] the resulting + /// order of elements in the slice is unspecified. All original elements will remain in the + /// slice and any possible modifications via interior mutability are observed in the input. Same + /// is true if the implementation of [`Ord`] for `K` panics. /// /// # Current implementation /// @@ -310,18 +312,19 @@ impl [T] { /// /// # Panics /// - /// May panic if `K: Ord` does not implement a total order. + /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [-5i32, 4, 1, -3, 2]; + /// let mut v = [4i32, -5, 1, -3, 2]; /// /// v.sort_by_key(|k| k.abs()); - /// assert!(v == [1, 2, -3, 4, -5]); + /// assert_eq!(v, [1, 2, -3, 4, -5]); /// ``` /// /// [driftsort]: https://github.com/Voultapher/driftsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "slice_sort_by_key", since = "1.7.0")] @@ -343,9 +346,10 @@ impl [T] { /// storage to remember the results of key evaluation. The order of calls to the key function is /// unspecified and may change in future versions of the standard library. /// - /// If `K: Ord` does not implement a total order the resulting order is unspecified. - /// All original elements will remain in the slice and any possible modifications via interior - /// mutability are observed in the input. Same is true if `K: Ord` panics. + /// If the implementation of [`Ord`] for `K` does not implement a [total order] the resulting + /// order of elements in the slice is unspecified. All original elements will remain in the + /// slice and any possible modifications via interior mutability are observed in the input. Same + /// is true if the implementation of [`Ord`] for `K` panics. /// /// For simple key functions (e.g., functions that are property accesses or basic operations), /// [`sort_by_key`](slice::sort_by_key) is likely to be faster. @@ -364,18 +368,20 @@ impl [T] { /// /// # Panics /// - /// May panic if `K: Ord` does not implement a total order. + /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [-5i32, 4, 32, -3, 2]; + /// let mut v = [4i32, -5, 1, -3, 2, 10]; /// + /// // Strings are sorted by lexicographical order. /// v.sort_by_cached_key(|k| k.to_string()); - /// assert!(v == [-3, -5, 2, 32, 4]); + /// assert_eq!(v, [-3, -5, 1, 10, 2, 4]); /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "slice_sort_by_cached_key", since = "1.34.0")] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 8916d43a8f236..223ba96c4755b 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2884,9 +2884,19 @@ impl [T] { /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not /// allocate), and *O*(*n* \* log(*n*)) worst-case. /// - /// If `T: Ord` does not implement a total order the resulting order is unspecified. All - /// original elements will remain in the slice and any possible modifications via interior - /// mutability are observed in the input. Same is true if `T: Ord` panics. + /// If the implementation of [`Ord`] for `T` does not implement a [total order] the resulting + /// order of elements in the slice is unspecified. All original elements will remain in the + /// slice and any possible modifications via interior mutability are observed in the input. Same + /// is true if the implementation of [`Ord`] for `T` panics. + /// + /// Sorting types that only implement [`PartialOrd`] such as [`f32`] and [`f64`] requires + /// additional precautions. For example Rust defines `NaN != NaN`, which doesn't fulfill the + /// reflexivity requirement posed by [`Ord`]. By using an alternative comparison function with + /// [`slice::sort_unstable_by`] such as [`f32::total_cmp`] or [`f64::total_cmp`] that defines a + /// [total order] users can sort slices containing floating point numbers. Alternatively, if one + /// can guarantee that all values in the slice are comparable with [`PartialOrd::partial_cmp`] + /// *and* the implementation forms a [total order], it's possible to sort the slice with + /// `sort_unstable_by(|a, b| a.partial_cmp(b).unwrap())`. /// /// # Current implementation /// @@ -2900,18 +2910,19 @@ impl [T] { /// /// # Panics /// - /// May panic if `T: Ord` does not implement a total order. + /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [-5, 4, 1, -3, 2]; + /// let mut v = [4, -5, 1, -3, 2]; /// /// v.sort_unstable(); - /// assert!(v == [-5, -3, 1, 2, 4]); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable(&mut self) @@ -2921,31 +2932,20 @@ impl [T] { sort::unstable::sort(self, &mut T::lt); } - /// Sorts the slice with a comparator function, **without** preserving the initial order of + /// Sorts the slice with a comparison function, **without** preserving the initial order of /// equal elements. /// /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not /// allocate), and *O*(*n* \* log(*n*)) worst-case. /// - /// The comparator function should define a total ordering for the elements in the slice. If the - /// ordering is not total, the order of the elements is unspecified. + /// If the comparison function `compare` does not implement a [total order] the resulting order + /// of elements in the slice is unspecified. All original elements will remain in the slice and + /// any possible modifications via interior mutability are observed in the input. Same is true + /// if `compare` panics. /// - /// If the comparator function does not implement a total order the resulting order is - /// unspecified. All original elements will remain in the slice and any possible modifications - /// via interior mutability are observed in the input. Same is true if the comparator function - /// panics. A total order (for all `a`, `b` and `c`): - /// - /// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and - /// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. - /// - /// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use - /// `partial_cmp` as our sort function when we know the slice doesn't contain a `NaN`. - /// - /// ``` - /// let mut floats = [5f64, 4.0, 1.0, 3.0, 2.0]; - /// floats.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); - /// assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 5.0]); - /// ``` + /// For example `|a, b| (a - b).cmp(a)` is a comparison function that is neither transitive nor + /// reflexive nor total, `a < b < c < a` with `a = 1, b = 2, c = 3`. For more information and + /// examples see the [`Ord`] documentation. /// /// # Current implementation /// @@ -2959,21 +2959,22 @@ impl [T] { /// /// # Panics /// - /// May panic if `compare` does not implement a total order. + /// May panic if `compare` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [5, 4, 1, 3, 2]; + /// let mut v = [4, -5, 1, -3, 2]; /// v.sort_unstable_by(|a, b| a.cmp(b)); - /// assert!(v == [1, 2, 3, 4, 5]); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); /// /// // reverse sorting /// v.sort_unstable_by(|a, b| b.cmp(a)); - /// assert!(v == [5, 4, 3, 2, 1]); + /// assert_eq!(v, [4, 2, 1, -3, -5]); /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable_by(&mut self, mut compare: F) @@ -2989,9 +2990,10 @@ impl [T] { /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not /// allocate), and *O*(*n* \* log(*n*)) worst-case. /// - /// If `K: Ord` does not implement a total order the resulting order is unspecified. - /// All original elements will remain in the slice and any possible modifications via interior - /// mutability are observed in the input. Same is true if `K: Ord` panics. + /// If the implementation of [`Ord`] for `K` does not implement a [total order] the resulting + /// order of elements in the slice is unspecified. All original elements will remain in the + /// slice and any possible modifications via interior mutability are observed in the input. Same + /// is true if the implementation of [`Ord`] for `K` panics. /// /// # Current implementation /// @@ -3005,18 +3007,19 @@ impl [T] { /// /// # Panics /// - /// May panic if `K: Ord` does not implement a total order. + /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [-5i32, 4, 1, -3, 2]; + /// let mut v = [4i32, -5, 1, -3, 2]; /// /// v.sort_unstable_by_key(|k| k.abs()); - /// assert!(v == [1, 2, -3, 4, -5]); + /// assert_eq!(v, [1, 2, -3, 4, -5]); /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable_by_key(&mut self, mut f: F) @@ -3054,7 +3057,7 @@ impl [T] { /// /// Panics when `index >= len()`, meaning it always panics on empty slices. /// - /// May panic if `T: Ord` does not implement a total order. + /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order]. /// /// # Examples /// @@ -3078,6 +3081,7 @@ impl [T] { /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] pub fn select_nth_unstable(&mut self, index: usize) -> (&mut [T], &mut T, &mut [T]) @@ -3114,7 +3118,7 @@ impl [T] { /// /// Panics when `index >= len()`, meaning it always panics on empty slices. /// - /// May panic if `compare` does not implement a total order. + /// May panic if `compare` does not implement a [total order]. /// /// # Examples /// @@ -3138,6 +3142,7 @@ impl [T] { /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] pub fn select_nth_unstable_by( @@ -3202,6 +3207,7 @@ impl [T] { /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] pub fn select_nth_unstable_by_key( diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index f3b0bf8b018e6..e168e8a4478b8 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -835,9 +835,8 @@ unsafe fn bidirectional_merge bool>( } // We now should have consumed the full input exactly once. This can only fail if the - // user-provided comparison operator fails implements a strict weak ordering as required by - // Ord incorrectly, in which case we will panic and never access the inconsistent state in - // dst. + // user-provided comparison function fails to implement a strict weak ordering. In that case + // we panic and never access the inconsistent state in dst. if left != left_end || right != right_end { panic_on_ord_violation(); } @@ -850,11 +849,11 @@ fn panic_on_ord_violation() -> ! { // implementation. They are expected to implement a total order as explained in the Ord // documentation. // - // By raising this panic we inform the user, that they have a logic bug in their program. If a - // strict weak ordering is not given, the concept of comparison based sorting makes no sense. - // E.g.: a < b < c < a + // By panicking we inform the user, that they have a logic bug in their program. If a strict + // weak ordering is not given, the concept of comparison based sorting cannot yield a sorted + // result. E.g.: a < b < c < a // - // The Ord documentation requires users to implement a total order, arguably that's + // The Ord documentation requires users to implement a total order. Arguably that's // unnecessarily strict in the context of sorting. Issues only arise if the weaker requirement // of a strict weak ordering is violated. // diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index 692c2d8f7c7ba..17aa3994bf20d 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -9,7 +9,7 @@ use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; pub(crate) mod heapsort; pub(crate) mod quicksort; -/// Unstable sort called ipnsort by Lukas Bergdoll. +/// Unstable sort called ipnsort by Lukas Bergdoll and Orson Peters. /// Design document: /// /// From eae7a186b221bb2b4a4bedeb19d5aca658e91c25 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Thu, 1 Aug 2024 17:37:07 +0200 Subject: [PATCH 04/23] Hide internal sort module --- library/core/src/slice/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 223ba96c4755b..862a18c3f4a4f 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -31,6 +31,7 @@ pub mod memchr; issue = "none", reason = "exposed from core to be reused in std;" )] +#[doc(hidden)] pub mod sort; mod ascii; From c2c46e3e305f82b7f4ce0afa4ff7977075421318 Mon Sep 17 00:00:00 2001 From: B I Mohammed Abbas Date: Sat, 3 Aug 2024 08:00:39 +0530 Subject: [PATCH 05/23] Forbid unsafe_op_in_unsafe_fn in vxworks specific os and sys files --- library/std/src/os/vxworks/mod.rs | 1 + library/std/src/sys/pal/unix/process/process_vxworks.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/library/std/src/os/vxworks/mod.rs b/library/std/src/os/vxworks/mod.rs index 0a7ac641dd3e1..b09aa72f72693 100644 --- a/library/std/src/os/vxworks/mod.rs +++ b/library/std/src/os/vxworks/mod.rs @@ -1,6 +1,7 @@ //! VxWorks-specific definitions #![stable(feature = "raw_ext", since = "1.1.0")] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod fs; pub mod raw; diff --git a/library/std/src/sys/pal/unix/process/process_vxworks.rs b/library/std/src/sys/pal/unix/process/process_vxworks.rs index 6a9d8fab1d412..0477b3d9a70da 100644 --- a/library/std/src/sys/pal/unix/process/process_vxworks.rs +++ b/library/std/src/sys/pal/unix/process/process_vxworks.rs @@ -1,3 +1,4 @@ +#![forbid(unsafe_op_in_unsafe_fn)] use libc::{self, c_char, c_int, RTP_ID}; use crate::io::{self, ErrorKind}; From 613155c96ab32daf12514e76476132e6b84adaf8 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Sat, 3 Aug 2024 15:10:27 +0200 Subject: [PATCH 06/23] Apply review comments to PartialOrd section --- library/alloc/src/slice.rs | 14 +++++++------- library/core/src/slice/mod.rs | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 21270dbed0c61..aaa6a2abbd904 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -189,14 +189,14 @@ impl [T] { /// [`sort_unstable`](slice::sort_unstable). The exception are partially sorted slices, which /// may be better served with `slice::sort`. /// - /// Sorting types that only implement [`PartialOrd`] such as [`f32`] and [`f64`] requires - /// additional precautions. For example Rust defines `NaN != NaN`, which doesn't fulfill the - /// reflexivity requirement posed by [`Ord`]. By using an alternative comparison function with + /// Sorting types that only implement [`PartialOrd`] such as [`f32`] and [`f64`] require + /// additional precautions. For example, `f32::NAN != f32::NAN`, which doesn't fulfill the + /// reflexivity requirement of [`Ord`]. By using an alternative comparison function with /// [`slice::sort_by`] such as [`f32::total_cmp`] or [`f64::total_cmp`] that defines a [total - /// order] users can sort slices containing floating point numbers. Alternatively, if one can - /// guarantee that all values in the slice are comparable with [`PartialOrd::partial_cmp`] *and* - /// the implementation forms a [total order], it's possible to sort the slice with `sort_by(|a, - /// b| a.partial_cmp(b).unwrap())`. + /// order] users can sort slices containing floating-point values. Alternatively, if all values + /// in the slice are guaranteed to be in a subset for which [`PartialOrd::partial_cmp`] forms a + /// [total order], it's possible to sort the slice with `sort_by(|a, b| + /// a.partial_cmp(b).unwrap())`. /// /// # Current implementation /// diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 862a18c3f4a4f..e75a9a88045e9 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2890,14 +2890,14 @@ impl [T] { /// slice and any possible modifications via interior mutability are observed in the input. Same /// is true if the implementation of [`Ord`] for `T` panics. /// - /// Sorting types that only implement [`PartialOrd`] such as [`f32`] and [`f64`] requires - /// additional precautions. For example Rust defines `NaN != NaN`, which doesn't fulfill the - /// reflexivity requirement posed by [`Ord`]. By using an alternative comparison function with + /// Sorting types that only implement [`PartialOrd`] such as [`f32`] and [`f64`] require + /// additional precautions. For example, `f32::NAN != f32::NAN`, which doesn't fulfill the + /// reflexivity requirement of [`Ord`]. By using an alternative comparison function with /// [`slice::sort_unstable_by`] such as [`f32::total_cmp`] or [`f64::total_cmp`] that defines a - /// [total order] users can sort slices containing floating point numbers. Alternatively, if one - /// can guarantee that all values in the slice are comparable with [`PartialOrd::partial_cmp`] - /// *and* the implementation forms a [total order], it's possible to sort the slice with - /// `sort_unstable_by(|a, b| a.partial_cmp(b).unwrap())`. + /// [total order] users can sort slices containing floating-point values. Alternatively, if all + /// values in the slice are guaranteed to be in a subset for which [`PartialOrd::partial_cmp`] + /// forms a [total order], it's possible to sort the slice with `sort_unstable_by(|a, b| + /// a.partial_cmp(b).unwrap())`. /// /// # Current implementation /// From 9717dadaa19235798a2d64b0d6f8f50ede944878 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 20 Mar 2024 19:24:42 +0100 Subject: [PATCH 07/23] Stabilize `min_exhaustive_patterns` --- compiler/rustc_feature/src/accepted.rs | 2 ++ compiler/rustc_feature/src/unstable.rs | 3 --- .../src/build/matches/match_pair.rs | 13 +++++-------- .../src/thir/pattern/check_match.rs | 6 ++---- compiler/rustc_pattern_analysis/src/lib.rs | 1 - compiler/rustc_pattern_analysis/src/rustc.rs | 7 +------ .../rustc_pattern_analysis/src/usefulness.rs | 19 +++++++------------ .../tests/common/mod.rs | 4 ---- .../src/collections/vec_deque/into_iter.rs | 2 ++ tests/ui/pattern/usefulness/empty-types.rs | 1 - 10 files changed, 19 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index e42a655531b5d..44286cfeeefaa 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -267,6 +267,8 @@ declare_features! ( (accepted, min_const_generics, "1.51.0", Some(74878)), /// Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions. (accepted, min_const_unsafe_fn, "1.33.0", Some(55607)), + /// Allows exhaustive pattern matching on uninhabited types when matched by value. + (accepted, min_exhaustive_patterns, "CURRENT_RUSTC_VERSION", Some(119612)), /// Allows using `Self` and associated types in struct expressions and patterns. (accepted, more_struct_aliases, "1.16.0", Some(37544)), /// Allows using the MOVBE target feature. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 88a4b5a838246..47810bc9165ef 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -519,9 +519,6 @@ declare_features! ( (unstable, macro_metavar_expr_concat, "1.81.0", Some(124225)), /// Allows `#[marker]` on certain traits allowing overlapping implementations. (unstable, marker_trait_attr, "1.30.0", Some(29864)), - /// Allows exhaustive pattern matching on types that contain uninhabited types in cases that are - /// unambiguously sound. - (unstable, min_exhaustive_patterns, "1.77.0", Some(119612)), /// A minimal, sound subset of specialization intended to be used by the /// standard library until the soundness issues with specialization /// are fixed. diff --git a/compiler/rustc_mir_build/src/build/matches/match_pair.rs b/compiler/rustc_mir_build/src/build/matches/match_pair.rs index 95fec154918a5..ab2bfcbca3aa6 100644 --- a/compiler/rustc_mir_build/src/build/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/build/matches/match_pair.rs @@ -208,14 +208,11 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { subpairs = cx.field_match_pairs(downcast_place, subpatterns); let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| { - i == variant_index || { - (cx.tcx.features().exhaustive_patterns - || cx.tcx.features().min_exhaustive_patterns) - && !v - .inhabited_predicate(cx.tcx, adt_def) - .instantiate(cx.tcx, args) - .apply_ignore_module(cx.tcx, cx.param_env) - } + i == variant_index + || !v + .inhabited_predicate(cx.tcx, adt_def) + .instantiate(cx.tcx, args) + .apply_ignore_module(cx.tcx, cx.param_env) }) && (adt_def.did().is_local() || !adt_def.is_variant_list_non_exhaustive()); if irrefutable { diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 64c6ff952c686..efaa75800fca4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -695,9 +695,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { // Emit an extra note if the first uncovered witness would be uninhabited // if we disregard visibility. - let witness_1_is_privately_uninhabited = if (self.tcx.features().exhaustive_patterns - || self.tcx.features().min_exhaustive_patterns) - && let Some(witness_1) = witnesses.get(0) + let witness_1_is_privately_uninhabited = if let Some(witness_1) = witnesses.get(0) && let ty::Adt(adt, args) = witness_1.ty().kind() && adt.is_enum() && let Constructor::Variant(variant_index) = witness_1.ctor() @@ -1059,7 +1057,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary"); } else if cx.is_foreign_non_exhaustive_enum(ty) { err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively")); - } else if cx.is_uninhabited(ty.inner()) && cx.tcx.features().min_exhaustive_patterns { + } else if cx.is_uninhabited(ty.inner()) { // The type is uninhabited yet there is a witness: we must be in the `MaybeInvalid` // case. err.note(format!("`{ty}` is uninhabited but is not being matched by value, so a wildcard `_` is required")); diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index a5c0b13c90be1..e37fa072b6d65 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -54,7 +54,6 @@ pub trait PatCx: Sized + fmt::Debug { type PatData: Clone; fn is_exhaustive_patterns_feature_on(&self) -> bool; - fn is_min_exhaustive_patterns_feature_on(&self) -> bool; /// The number of fields for this constructor. fn ctor_arity(&self, ctor: &Constructor, ty: &Self::Ty) -> usize; diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 6290aeb252312..25f7cc17c11ff 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -237,9 +237,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| { let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); - let is_uninhabited = (cx.tcx.features().exhaustive_patterns - || cx.tcx.features().min_exhaustive_patterns) - && cx.is_uninhabited(*ty); + let is_uninhabited = cx.is_uninhabited(*ty); let skip = is_uninhabited && (!is_visible || is_non_exhaustive); (ty, PrivateUninhabitedField(skip)) }); @@ -925,9 +923,6 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { fn is_exhaustive_patterns_feature_on(&self) -> bool { self.tcx.features().exhaustive_patterns } - fn is_min_exhaustive_patterns_feature_on(&self) -> bool { - self.tcx.features().min_exhaustive_patterns - } fn ctor_arity(&self, ctor: &crate::constructor::Constructor, ty: &Self::Ty) -> usize { self.ctor_arity(ctor, *ty) diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 9710c9e1303dd..6535afcc39862 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -543,13 +543,11 @@ //! recurse into subpatterns. That second part is done through [`PlaceValidity`], most notably //! [`PlaceValidity::specialize`]. //! -//! Having said all that, in practice we don't fully follow what's been presented in this section. -//! Let's call "toplevel exception" the case where the match scrutinee itself has type `!` or -//! `EmptyEnum`. First, on stable rust, we require `_` patterns for empty types in all cases apart -//! from the toplevel exception. The `exhaustive_patterns` and `min_exaustive_patterns` allow -//! omitting patterns in the cases described above. There's a final detail: in the toplevel -//! exception or with the `exhaustive_patterns` feature, we ignore place validity when checking -//! whether a pattern is required for exhaustiveness. I (Nadrieril) hope to deprecate this behavior. +//! Having said all that, we don't fully follow what's been presented in this section. For +//! backwards-compatibility, we ignore place validity when checking whether a pattern is required +//! for exhaustiveness in two cases: when the `exhaustive_patterns` feature gate is on, or when the +//! match scrutinee itself has type `!` or `EmptyEnum`. I (Nadrieril) hope to deprecate this +//! exception. //! //! //! @@ -953,13 +951,10 @@ impl PlaceInfo { self.is_scrutinee && matches!(ctors_for_ty, ConstructorSet::NoConstructors); // Whether empty patterns are counted as useful or not. We only warn an empty arm unreachable if // it is guaranteed unreachable by the opsem (i.e. if the place is `known_valid`). - let empty_arms_are_unreachable = self.validity.is_known_valid() - && (is_toplevel_exception - || cx.is_exhaustive_patterns_feature_on() - || cx.is_min_exhaustive_patterns_feature_on()); + let empty_arms_are_unreachable = self.validity.is_known_valid(); // Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the // toplevel exception and `exhaustive_patterns` cases for backwards compatibility. - let can_omit_empty_arms = empty_arms_are_unreachable + let can_omit_empty_arms = self.validity.is_known_valid() || is_toplevel_exception || cx.is_exhaustive_patterns_feature_on(); diff --git a/compiler/rustc_pattern_analysis/tests/common/mod.rs b/compiler/rustc_pattern_analysis/tests/common/mod.rs index 01a56eaa78fcc..ec0bcd43ad24d 100644 --- a/compiler/rustc_pattern_analysis/tests/common/mod.rs +++ b/compiler/rustc_pattern_analysis/tests/common/mod.rs @@ -152,10 +152,6 @@ impl PatCx for Cx { false } - fn is_min_exhaustive_patterns_feature_on(&self) -> bool { - true - } - fn ctor_arity(&self, ctor: &Constructor, ty: &Self::Ty) -> usize { ty.sub_tys(ctor).len() } diff --git a/library/alloc/src/collections/vec_deque/into_iter.rs b/library/alloc/src/collections/vec_deque/into_iter.rs index 2d283dac9a97a..7be3de16b2da7 100644 --- a/library/alloc/src/collections/vec_deque/into_iter.rs +++ b/library/alloc/src/collections/vec_deque/into_iter.rs @@ -121,6 +121,7 @@ impl Iterator for IntoIter { { match self.try_fold(init, |b, item| Ok::(f(b, item))) { Ok(b) => b, + #[cfg(bootstrap)] Err(e) => match e {}, } } @@ -242,6 +243,7 @@ impl DoubleEndedIterator for IntoIter { { match self.try_rfold(init, |b, item| Ok::(f(b, item))) { Ok(b) => b, + #[cfg(bootstrap)] Err(e) => match e {}, } } diff --git a/tests/ui/pattern/usefulness/empty-types.rs b/tests/ui/pattern/usefulness/empty-types.rs index cc71f67831dd8..46024b1caebc7 100644 --- a/tests/ui/pattern/usefulness/empty-types.rs +++ b/tests/ui/pattern/usefulness/empty-types.rs @@ -1,5 +1,4 @@ //@ revisions: normal min_exh_pats exhaustive_patterns never_pats -// gate-test-min_exhaustive_patterns // // This tests correct handling of empty types in exhaustiveness checking. // From a10b8230ebef45123b4a0e84039d9f4c8f06a22f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 27 Jul 2024 11:08:16 +0200 Subject: [PATCH 08/23] Update tests --- ...atterns.opt1.SimplifyCfg-initial.after.mir | 10 +- ...atterns.opt2.SimplifyCfg-initial.after.mir | 15 - ...atterns.opt3.SimplifyCfg-initial.after.mir | 17 +- ...ch.UnreachablePropagation.panic-abort.diff | 16 +- ...h.UnreachablePropagation.panic-unwind.diff | 16 +- tests/mir-opt/unreachable.rs | 10 +- tests/mir-opt/unreachable_enum_branching.rs | 2 +- ....UnreachableEnumBranching.panic-abort.diff | 24 +- ...UnreachableEnumBranching.panic-unwind.diff | 24 +- .../run_pass/multivariant.rs | 3 +- .../huge_multispan_highlight.svg | 2 +- .../write-to-uninhabited-enum-variant.rs | 4 +- tests/ui/enum-discriminant/issue-61696.rs | 1 + tests/ui/enum-discriminant/niche.rs | 1 + .../feature-gate-exhaustive-patterns.rs | 2 +- .../feature-gate-exhaustive-patterns.stderr | 10 +- tests/ui/never_type/never-result.rs | 5 +- ...bited-union-ref.exhaustive_patterns.stderr | 6 +- ... always-inhabited-union-ref.normal.stderr} | 6 +- .../usefulness/always-inhabited-union-ref.rs | 3 +- ...tch-check-notes.exhaustive_patterns.stderr | 6 +- .../empty-match-check-notes.normal.stderr | 7 +- .../usefulness/empty-match-check-notes.rs | 6 +- .../empty-match.exhaustive_patterns.stderr | 1 + .../usefulness/empty-match.normal.stderr | 1 + .../empty-types.exhaustive_patterns.stderr | 100 ++--- .../usefulness/empty-types.never_pats.stderr | 357 ++++++++++-------- .../usefulness/empty-types.normal.stderr | 355 +++++++++-------- tests/ui/pattern/usefulness/empty-types.rs | 91 ++--- .../usefulness/explain-unreachable-pats.rs | 1 - .../explain-unreachable-pats.stderr | 24 +- tests/ui/pattern/usefulness/impl-trait.rs | 1 - tests/ui/pattern/usefulness/impl-trait.stderr | 32 +- ...privately-empty.exhaustive_patterns.stderr | 2 +- .../match-privately-empty.normal.stderr | 21 ++ .../usefulness/match-privately-empty.rs | 3 +- .../slice_of_empty.exhaustive_patterns.stderr | 2 +- .../usefulness/slice_of_empty.normal.stderr | 30 ++ tests/ui/pattern/usefulness/slice_of_empty.rs | 7 +- tests/ui/pattern/usefulness/uninhabited.rs | 1 - .../ui/reachable/unreachable-loop-patterns.rs | 2 - .../unreachable-loop-patterns.stderr | 4 +- .../ui/rfcs/rfc-0000-never_patterns/check.rs | 8 +- .../rfcs/rfc-0000-never_patterns/check.stderr | 22 +- .../typeck.fail.stderr | 16 +- .../ui/rfcs/rfc-0000-never_patterns/typeck.rs | 1 - .../unreachable.exh_pats.stderr | 14 +- .../unreachable.normal.stderr | 44 +++ .../rfc-0000-never_patterns/unreachable.rs | 15 +- .../unreachable.stderr | 55 +++ .../uninhabited/indirect_match_same_crate.rs | 11 +- .../indirect_match_same_crate.stderr | 79 ---- ...indirect_match_with_exhaustive_patterns.rs | 1 - ...rect_match_with_exhaustive_patterns.stderr | 8 +- ...tch_with_exhaustive_patterns_same_crate.rs | 1 - .../uninhabited/match_same_crate.rs | 7 +- .../uninhabited/match_same_crate.stderr | 64 ---- .../match_with_exhaustive_patterns.rs | 1 - .../match_with_exhaustive_patterns.stderr | 8 +- ...tch_with_exhaustive_patterns_same_crate.rs | 1 - .../uninhabited/patterns.rs | 1 - .../uninhabited/patterns_same_crate.rs | 1 - .../uninhabited/patterns_same_crate.stderr | 10 +- tests/ui/try-trait/try-operator-custom.rs | 3 - .../exhaustive-wo-nevertype-issue-51221.rs | 2 - ...ted-irrefutable.exhaustive_patterns.stderr | 4 +- .../uninhabited-irrefutable.normal.stderr | 26 ++ .../ui/uninhabited/uninhabited-irrefutable.rs | 3 +- .../uninhabited-matches-feature-gated.rs | 7 +- .../uninhabited-matches-feature-gated.stderr | 66 +--- tests/ui/uninhabited/uninhabited-patterns.rs | 1 - .../uninhabited/uninhabited-patterns.stderr | 8 +- 72 files changed, 878 insertions(+), 841 deletions(-) rename tests/ui/pattern/usefulness/{always-inhabited-union-ref.min_exhaustive_patterns.stderr => always-inhabited-union-ref.normal.stderr} (87%) create mode 100644 tests/ui/pattern/usefulness/match-privately-empty.normal.stderr create mode 100644 tests/ui/pattern/usefulness/slice_of_empty.normal.stderr create mode 100644 tests/ui/rfcs/rfc-0000-never_patterns/unreachable.normal.stderr create mode 100644 tests/ui/rfcs/rfc-0000-never_patterns/unreachable.stderr delete mode 100644 tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr delete mode 100644 tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr create mode 100644 tests/ui/uninhabited/uninhabited-irrefutable.normal.stderr diff --git a/tests/mir-opt/building/match/never_patterns.opt1.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/never_patterns.opt1.SimplifyCfg-initial.after.mir index 78356a90743a6..bba4d9c0149a1 100644 --- a/tests/mir-opt/building/match/never_patterns.opt1.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/never_patterns.opt1.SimplifyCfg-initial.after.mir @@ -13,17 +13,17 @@ fn opt1(_1: &Result) -> &u32 { bb0: { PlaceMention(_1); - _2 = discriminant((*_1)); - switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1]; + falseEdge -> [real: bb4, imaginary: bb1]; } bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; + _2 = discriminant((*_1)); + switchInt(move _2) -> [1: bb3, otherwise: bb2]; } bb2: { - falseEdge -> [real: bb4, imaginary: bb3]; + FakeRead(ForMatchedPlace(None), _1); + unreachable; } bb3: { diff --git a/tests/mir-opt/building/match/never_patterns.opt2.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/never_patterns.opt2.SimplifyCfg-initial.after.mir index 979fbb2860dcb..fc0769d6f7dcc 100644 --- a/tests/mir-opt/building/match/never_patterns.opt2.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/never_patterns.opt2.SimplifyCfg-initial.after.mir @@ -11,25 +11,10 @@ fn opt2(_1: &Result) -> &u32 { bb0: { PlaceMention(_1); - _2 = discriminant((*_1)); - switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1]; - } - - bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } - - bb2: { StorageLive(_3); _3 = &(((*_1) as Ok).0: u32); _0 = &(*_3); StorageDead(_3); return; } - - bb3: { - FakeRead(ForMatchedPlace(None), (((*_1) as Err).0: Void)); - unreachable; - } } diff --git a/tests/mir-opt/building/match/never_patterns.opt3.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/never_patterns.opt3.SimplifyCfg-initial.after.mir index 93ebe600b3ff7..86347db4d92eb 100644 --- a/tests/mir-opt/building/match/never_patterns.opt3.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/never_patterns.opt3.SimplifyCfg-initial.after.mir @@ -12,24 +12,19 @@ fn opt3(_1: &Result) -> &u32 { bb0: { PlaceMention(_1); _2 = discriminant((*_1)); - switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1]; + switchInt(move _2) -> [1: bb2, otherwise: bb1]; } bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } - - bb2: { - FakeRead(ForMatchedPlace(None), (((*_1) as Err).0: Void)); - unreachable; - } - - bb3: { StorageLive(_3); _3 = &(((*_1) as Ok).0: u32); _0 = &(*_3); StorageDead(_3); return; } + + bb2: { + FakeRead(ForMatchedPlace(None), (((*_1) as Err).0: Void)); + unreachable; + } } diff --git a/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-abort.diff b/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-abort.diff index da7a2bd10e01a..1e1ddfae0eb9e 100644 --- a/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-abort.diff +++ b/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-abort.diff @@ -19,14 +19,16 @@ bb1: { _2 = discriminant(_1); -- switchInt(move _2) -> [0: bb4, 1: bb3, otherwise: bb2]; -+ _5 = Eq(_2, const 0_isize); +- switchInt(move _2) -> [1: bb3, otherwise: bb2]; ++ _5 = Ne(_2, const 1_isize); + assume(move _5); -+ goto -> bb4; ++ goto -> bb2; } bb2: { - unreachable; + _0 = const (); + StorageDead(_1); + return; } bb3: { @@ -35,11 +37,5 @@ - StorageLive(_4); unreachable; } - - bb4: { - _0 = const (); - StorageDead(_1); - return; - } } diff --git a/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-unwind.diff b/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-unwind.diff index a2121fc684f5b..809d24aa15a13 100644 --- a/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-unwind.diff +++ b/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-unwind.diff @@ -19,14 +19,16 @@ bb1: { _2 = discriminant(_1); -- switchInt(move _2) -> [0: bb4, 1: bb3, otherwise: bb2]; -+ _5 = Eq(_2, const 0_isize); +- switchInt(move _2) -> [1: bb3, otherwise: bb2]; ++ _5 = Ne(_2, const 1_isize); + assume(move _5); -+ goto -> bb4; ++ goto -> bb2; } bb2: { - unreachable; + _0 = const (); + StorageDead(_1); + return; } bb3: { @@ -35,11 +37,5 @@ - StorageLive(_4); unreachable; } - - bb4: { - _0 = const (); - StorageDead(_1); - return; - } } diff --git a/tests/mir-opt/unreachable.rs b/tests/mir-opt/unreachable.rs index 881e3542f0ac9..f7f4815ae7ce1 100644 --- a/tests/mir-opt/unreachable.rs +++ b/tests/mir-opt/unreachable.rs @@ -45,18 +45,16 @@ fn as_match() { // CHECK: bb0: { // CHECK: {{_.*}} = empty() // CHECK: bb1: { - // CHECK: [[eq:_.*]] = Eq({{.*}}, const 0_isize); + // CHECK: [[eq:_.*]] = Ne({{.*}}, const 1_isize); // CHECK-NEXT: assume(move [[eq]]); - // CHECK-NEXT: goto -> bb4; + // CHECK-NEXT: goto -> bb2; // CHECK: bb2: { - // CHECK-NEXT: unreachable; + // CHECK: return; // CHECK: bb3: { // CHECK-NEXT: unreachable; - // CHECK: bb4: { - // CHECK: return; match empty() { - None => {} Some(_x) => match _x {}, + None => {} } } diff --git a/tests/mir-opt/unreachable_enum_branching.rs b/tests/mir-opt/unreachable_enum_branching.rs index fac14042b1075..7647f9bf0779a 100644 --- a/tests/mir-opt/unreachable_enum_branching.rs +++ b/tests/mir-opt/unreachable_enum_branching.rs @@ -49,7 +49,7 @@ struct Plop { fn simple() { // CHECK-LABEL: fn simple( // CHECK: [[discr:_.*]] = discriminant( - // CHECK: switchInt(move [[discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: bb2, otherwise: [[unreachable]]]; + // CHECK: switchInt(move [[discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: bb1, otherwise: [[unreachable]]]; // CHECK: [[unreachable]]: { // CHECK-NEXT: unreachable; match Test1::C { diff --git a/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-abort.diff b/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-abort.diff index 8aef991493672..5c08648fac3bb 100644 --- a/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-abort.diff +++ b/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-abort.diff @@ -14,40 +14,40 @@ StorageLive(_2); _2 = Test1::C; _3 = discriminant(_2); -- switchInt(move _3) -> [0: bb4, 1: bb3, 2: bb2, otherwise: bb1]; -+ switchInt(move _3) -> [0: bb1, 1: bb1, 2: bb2, otherwise: bb1]; +- switchInt(move _3) -> [0: bb3, 1: bb2, otherwise: bb1]; ++ switchInt(move _3) -> [0: bb5, 1: bb5, 2: bb1, otherwise: bb5]; } bb1: { - unreachable; - } - - bb2: { StorageLive(_5); _5 = const "C"; _1 = &(*_5); StorageDead(_5); - goto -> bb5; + goto -> bb4; } - bb3: { + bb2: { StorageLive(_4); _4 = const "B(Empty)"; _1 = &(*_4); StorageDead(_4); - goto -> bb5; + goto -> bb4; } - bb4: { + bb3: { _1 = const "A(Empty)"; - goto -> bb5; + goto -> bb4; } - bb5: { + bb4: { StorageDead(_2); StorageDead(_1); _0 = const (); return; ++ } ++ ++ bb5: { ++ unreachable; } } diff --git a/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-unwind.diff b/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-unwind.diff index 8aef991493672..5c08648fac3bb 100644 --- a/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-unwind.diff +++ b/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-unwind.diff @@ -14,40 +14,40 @@ StorageLive(_2); _2 = Test1::C; _3 = discriminant(_2); -- switchInt(move _3) -> [0: bb4, 1: bb3, 2: bb2, otherwise: bb1]; -+ switchInt(move _3) -> [0: bb1, 1: bb1, 2: bb2, otherwise: bb1]; +- switchInt(move _3) -> [0: bb3, 1: bb2, otherwise: bb1]; ++ switchInt(move _3) -> [0: bb5, 1: bb5, 2: bb1, otherwise: bb5]; } bb1: { - unreachable; - } - - bb2: { StorageLive(_5); _5 = const "C"; _1 = &(*_5); StorageDead(_5); - goto -> bb5; + goto -> bb4; } - bb3: { + bb2: { StorageLive(_4); _4 = const "B(Empty)"; _1 = &(*_4); StorageDead(_4); - goto -> bb5; + goto -> bb4; } - bb4: { + bb3: { _1 = const "A(Empty)"; - goto -> bb5; + goto -> bb4; } - bb5: { + bb4: { StorageDead(_2); StorageDead(_1); _0 = const (); return; ++ } ++ ++ bb5: { ++ unreachable; } } diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/multivariant.rs b/tests/ui/closures/2229_closure_analysis/run_pass/multivariant.rs index 52ed008137fce..74f37b514e4a4 100644 --- a/tests/ui/closures/2229_closure_analysis/run_pass/multivariant.rs +++ b/tests/ui/closures/2229_closure_analysis/run_pass/multivariant.rs @@ -1,10 +1,9 @@ // Test precise capture of a multi-variant enum (when remaining variants are // visibly uninhabited). -//@ revisions: min_exhaustive_patterns exhaustive_patterns +//@ revisions: normal exhaustive_patterns //@ edition:2021 //@ run-pass #![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))] -#![cfg_attr(min_exhaustive_patterns, feature(min_exhaustive_patterns))] #![feature(never_type)] pub fn main() { diff --git a/tests/ui/codemap_tests/huge_multispan_highlight.svg b/tests/ui/codemap_tests/huge_multispan_highlight.svg index 7b6dbb17c6f95..12058176dc0f1 100644 --- a/tests/ui/codemap_tests/huge_multispan_highlight.svg +++ b/tests/ui/codemap_tests/huge_multispan_highlight.svg @@ -1,4 +1,4 @@ - + { fn fold_with>(self, folder: &mut F) -> Self { match self.try_fold_with(folder) { Ok(t) => t, + #[cfg(bootstrap)] Err(e) => match e {}, } } @@ -115,6 +116,7 @@ pub trait TypeSuperFoldable: TypeFoldable { fn super_fold_with>(self, folder: &mut F) -> Self { match self.try_super_fold_with(folder) { Ok(t) => t, + #[cfg(bootstrap)] Err(e) => match e {}, } } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index e74900ff7471b..514ae6f30cffd 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -192,6 +192,7 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(min_exhaustive_patterns))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] @@ -225,7 +226,6 @@ #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] #![feature(marker_trait_attr)] -#![feature(min_exhaustive_patterns)] #![feature(min_specialization)] #![feature(multiple_supertrait_upcastable)] #![feature(must_not_suspend)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 05e33d47bac39..321d91cf380f0 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -272,6 +272,7 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(min_exhaustive_patterns))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] @@ -299,7 +300,6 @@ #![feature(link_cfg)] #![feature(linkage)] #![feature(macro_metavar_expr_concat)] -#![feature(min_exhaustive_patterns)] #![feature(min_specialization)] #![feature(must_not_suspend)] #![feature(needs_panic_runtime)] From a1d0cdb2d63a224920c91c0bfe14d70aa2b76248 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 30 Mar 2024 12:21:59 +0100 Subject: [PATCH 10/23] Test that 0/unknown-length arrays are nonempty --- .../empty-match.exhaustive_patterns.stderr | 92 ++++++++++++++----- .../usefulness/empty-match.normal.stderr | 92 ++++++++++++++----- tests/ui/pattern/usefulness/empty-match.rs | 7 +- 3 files changed, 144 insertions(+), 47 deletions(-) diff --git a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr index bc12327a1b2a3..f2067f0341fcd 100644 --- a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/empty-match.rs:46:20 + --> $DIR/empty-match.rs:47:20 | LL | match_no_arms!(0u8); | ^^^ @@ -8,7 +8,7 @@ LL | match_no_arms!(0u8); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `i8` is non-empty - --> $DIR/empty-match.rs:47:20 + --> $DIR/empty-match.rs:48:20 | LL | match_no_arms!(0i8); | ^^^ @@ -17,7 +17,7 @@ LL | match_no_arms!(0i8); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `usize` is non-empty - --> $DIR/empty-match.rs:48:20 + --> $DIR/empty-match.rs:49:20 | LL | match_no_arms!(0usize); | ^^^^^^ @@ -26,7 +26,7 @@ LL | match_no_arms!(0usize); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `isize` is non-empty - --> $DIR/empty-match.rs:49:20 + --> $DIR/empty-match.rs:50:20 | LL | match_no_arms!(0isize); | ^^^^^^ @@ -35,7 +35,7 @@ LL | match_no_arms!(0isize); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty - --> $DIR/empty-match.rs:50:20 + --> $DIR/empty-match.rs:51:20 | LL | match_no_arms!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | struct NonEmptyStruct1; = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty - --> $DIR/empty-match.rs:51:20 + --> $DIR/empty-match.rs:52:20 | LL | match_no_arms!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | struct NonEmptyStruct2(bool); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty - --> $DIR/empty-match.rs:52:20 + --> $DIR/empty-match.rs:53:20 | LL | match_no_arms!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | union NonEmptyUnion1 { = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty - --> $DIR/empty-match.rs:53:20 + --> $DIR/empty-match.rs:54:20 | LL | match_no_arms!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -91,7 +91,7 @@ LL | union NonEmptyUnion2 { = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:54:20 + --> $DIR/empty-match.rs:55:20 | LL | match_no_arms!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -107,7 +107,7 @@ LL | Foo(bool), = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:55:20 + --> $DIR/empty-match.rs:56:20 | LL | match_no_arms!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -125,7 +125,7 @@ LL | Bar, = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:56:20 + --> $DIR/empty-match.rs:57:20 | LL | match_no_arms!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered @@ -148,8 +148,26 @@ LL | V5, = note: the matched value is of type `NonEmptyEnum5` = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms +error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty + --> $DIR/empty-match.rs:58:20 + | +LL | match_no_arms!(array0_of_empty); + | ^^^^^^^^^^^^^^^ + | + = note: the matched value is of type `[!; 0]` + = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern + +error[E0004]: non-exhaustive patterns: type `[!; N]` is non-empty + --> $DIR/empty-match.rs:59:20 + | +LL | match_no_arms!(arrayN_of_empty); + | ^^^^^^^^^^^^^^^ + | + = note: the matched value is of type `[!; N]` + = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern + error[E0004]: non-exhaustive patterns: `0_u8..=u8::MAX` not covered - --> $DIR/empty-match.rs:58:24 + --> $DIR/empty-match.rs:61:24 | LL | match_guarded_arm!(0u8); | ^^^ pattern `0_u8..=u8::MAX` not covered @@ -163,7 +181,7 @@ LL + 0_u8..=u8::MAX => todo!() | error[E0004]: non-exhaustive patterns: `i8::MIN..=i8::MAX` not covered - --> $DIR/empty-match.rs:59:24 + --> $DIR/empty-match.rs:62:24 | LL | match_guarded_arm!(0i8); | ^^^ pattern `i8::MIN..=i8::MAX` not covered @@ -177,7 +195,7 @@ LL + i8::MIN..=i8::MAX => todo!() | error[E0004]: non-exhaustive patterns: `0_usize..` not covered - --> $DIR/empty-match.rs:60:24 + --> $DIR/empty-match.rs:63:24 | LL | match_guarded_arm!(0usize); | ^^^^^^ pattern `0_usize..` not covered @@ -191,7 +209,7 @@ LL + 0_usize.. => todo!() | error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/empty-match.rs:61:24 + --> $DIR/empty-match.rs:64:24 | LL | match_guarded_arm!(0isize); | ^^^^^^ pattern `_` not covered @@ -205,7 +223,7 @@ LL + _ => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered - --> $DIR/empty-match.rs:62:24 + --> $DIR/empty-match.rs:65:24 | LL | match_guarded_arm!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered @@ -224,7 +242,7 @@ LL + NonEmptyStruct1 => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered - --> $DIR/empty-match.rs:63:24 + --> $DIR/empty-match.rs:66:24 | LL | match_guarded_arm!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered @@ -243,7 +261,7 @@ LL + NonEmptyStruct2(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered - --> $DIR/empty-match.rs:64:24 + --> $DIR/empty-match.rs:67:24 | LL | match_guarded_arm!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered @@ -262,7 +280,7 @@ LL + NonEmptyUnion1 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered - --> $DIR/empty-match.rs:65:24 + --> $DIR/empty-match.rs:68:24 | LL | match_guarded_arm!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered @@ -282,7 +300,7 @@ LL + NonEmptyUnion2 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:66:24 + --> $DIR/empty-match.rs:69:24 | LL | match_guarded_arm!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -303,7 +321,7 @@ LL + NonEmptyEnum1::Foo(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:67:24 + --> $DIR/empty-match.rs:70:24 | LL | match_guarded_arm!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -326,7 +344,7 @@ LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:68:24 + --> $DIR/empty-match.rs:71:24 | LL | match_guarded_arm!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered @@ -354,6 +372,34 @@ LL ~ _ if false => {}, LL + _ => todo!() | -error: aborting due to 22 previous errors +error[E0004]: non-exhaustive patterns: `[]` not covered + --> $DIR/empty-match.rs:72:24 + | +LL | match_guarded_arm!(array0_of_empty); + | ^^^^^^^^^^^^^^^ pattern `[]` not covered + | + = note: the matched value is of type `[!; 0]` + = note: match arms with guards don't count towards exhaustivity +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ _ if false => {}, +LL + [] => todo!() + | + +error[E0004]: non-exhaustive patterns: `[]` not covered + --> $DIR/empty-match.rs:73:24 + | +LL | match_guarded_arm!(arrayN_of_empty); + | ^^^^^^^^^^^^^^^ pattern `[]` not covered + | + = note: the matched value is of type `[!; N]` + = note: match arms with guards don't count towards exhaustivity +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ _ if false => {}, +LL + [] => todo!() + | + +error: aborting due to 26 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/empty-match.normal.stderr b/tests/ui/pattern/usefulness/empty-match.normal.stderr index bc12327a1b2a3..f2067f0341fcd 100644 --- a/tests/ui/pattern/usefulness/empty-match.normal.stderr +++ b/tests/ui/pattern/usefulness/empty-match.normal.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/empty-match.rs:46:20 + --> $DIR/empty-match.rs:47:20 | LL | match_no_arms!(0u8); | ^^^ @@ -8,7 +8,7 @@ LL | match_no_arms!(0u8); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `i8` is non-empty - --> $DIR/empty-match.rs:47:20 + --> $DIR/empty-match.rs:48:20 | LL | match_no_arms!(0i8); | ^^^ @@ -17,7 +17,7 @@ LL | match_no_arms!(0i8); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `usize` is non-empty - --> $DIR/empty-match.rs:48:20 + --> $DIR/empty-match.rs:49:20 | LL | match_no_arms!(0usize); | ^^^^^^ @@ -26,7 +26,7 @@ LL | match_no_arms!(0usize); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `isize` is non-empty - --> $DIR/empty-match.rs:49:20 + --> $DIR/empty-match.rs:50:20 | LL | match_no_arms!(0isize); | ^^^^^^ @@ -35,7 +35,7 @@ LL | match_no_arms!(0isize); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty - --> $DIR/empty-match.rs:50:20 + --> $DIR/empty-match.rs:51:20 | LL | match_no_arms!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | struct NonEmptyStruct1; = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty - --> $DIR/empty-match.rs:51:20 + --> $DIR/empty-match.rs:52:20 | LL | match_no_arms!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | struct NonEmptyStruct2(bool); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty - --> $DIR/empty-match.rs:52:20 + --> $DIR/empty-match.rs:53:20 | LL | match_no_arms!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | union NonEmptyUnion1 { = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty - --> $DIR/empty-match.rs:53:20 + --> $DIR/empty-match.rs:54:20 | LL | match_no_arms!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -91,7 +91,7 @@ LL | union NonEmptyUnion2 { = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:54:20 + --> $DIR/empty-match.rs:55:20 | LL | match_no_arms!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -107,7 +107,7 @@ LL | Foo(bool), = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:55:20 + --> $DIR/empty-match.rs:56:20 | LL | match_no_arms!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -125,7 +125,7 @@ LL | Bar, = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:56:20 + --> $DIR/empty-match.rs:57:20 | LL | match_no_arms!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered @@ -148,8 +148,26 @@ LL | V5, = note: the matched value is of type `NonEmptyEnum5` = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms +error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty + --> $DIR/empty-match.rs:58:20 + | +LL | match_no_arms!(array0_of_empty); + | ^^^^^^^^^^^^^^^ + | + = note: the matched value is of type `[!; 0]` + = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern + +error[E0004]: non-exhaustive patterns: type `[!; N]` is non-empty + --> $DIR/empty-match.rs:59:20 + | +LL | match_no_arms!(arrayN_of_empty); + | ^^^^^^^^^^^^^^^ + | + = note: the matched value is of type `[!; N]` + = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern + error[E0004]: non-exhaustive patterns: `0_u8..=u8::MAX` not covered - --> $DIR/empty-match.rs:58:24 + --> $DIR/empty-match.rs:61:24 | LL | match_guarded_arm!(0u8); | ^^^ pattern `0_u8..=u8::MAX` not covered @@ -163,7 +181,7 @@ LL + 0_u8..=u8::MAX => todo!() | error[E0004]: non-exhaustive patterns: `i8::MIN..=i8::MAX` not covered - --> $DIR/empty-match.rs:59:24 + --> $DIR/empty-match.rs:62:24 | LL | match_guarded_arm!(0i8); | ^^^ pattern `i8::MIN..=i8::MAX` not covered @@ -177,7 +195,7 @@ LL + i8::MIN..=i8::MAX => todo!() | error[E0004]: non-exhaustive patterns: `0_usize..` not covered - --> $DIR/empty-match.rs:60:24 + --> $DIR/empty-match.rs:63:24 | LL | match_guarded_arm!(0usize); | ^^^^^^ pattern `0_usize..` not covered @@ -191,7 +209,7 @@ LL + 0_usize.. => todo!() | error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/empty-match.rs:61:24 + --> $DIR/empty-match.rs:64:24 | LL | match_guarded_arm!(0isize); | ^^^^^^ pattern `_` not covered @@ -205,7 +223,7 @@ LL + _ => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered - --> $DIR/empty-match.rs:62:24 + --> $DIR/empty-match.rs:65:24 | LL | match_guarded_arm!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered @@ -224,7 +242,7 @@ LL + NonEmptyStruct1 => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered - --> $DIR/empty-match.rs:63:24 + --> $DIR/empty-match.rs:66:24 | LL | match_guarded_arm!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered @@ -243,7 +261,7 @@ LL + NonEmptyStruct2(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered - --> $DIR/empty-match.rs:64:24 + --> $DIR/empty-match.rs:67:24 | LL | match_guarded_arm!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered @@ -262,7 +280,7 @@ LL + NonEmptyUnion1 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered - --> $DIR/empty-match.rs:65:24 + --> $DIR/empty-match.rs:68:24 | LL | match_guarded_arm!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered @@ -282,7 +300,7 @@ LL + NonEmptyUnion2 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:66:24 + --> $DIR/empty-match.rs:69:24 | LL | match_guarded_arm!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -303,7 +321,7 @@ LL + NonEmptyEnum1::Foo(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:67:24 + --> $DIR/empty-match.rs:70:24 | LL | match_guarded_arm!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -326,7 +344,7 @@ LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:68:24 + --> $DIR/empty-match.rs:71:24 | LL | match_guarded_arm!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered @@ -354,6 +372,34 @@ LL ~ _ if false => {}, LL + _ => todo!() | -error: aborting due to 22 previous errors +error[E0004]: non-exhaustive patterns: `[]` not covered + --> $DIR/empty-match.rs:72:24 + | +LL | match_guarded_arm!(array0_of_empty); + | ^^^^^^^^^^^^^^^ pattern `[]` not covered + | + = note: the matched value is of type `[!; 0]` + = note: match arms with guards don't count towards exhaustivity +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ _ if false => {}, +LL + [] => todo!() + | + +error[E0004]: non-exhaustive patterns: `[]` not covered + --> $DIR/empty-match.rs:73:24 + | +LL | match_guarded_arm!(arrayN_of_empty); + | ^^^^^^^^^^^^^^^ pattern `[]` not covered + | + = note: the matched value is of type `[!; N]` + = note: match arms with guards don't count towards exhaustivity +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ _ if false => {}, +LL + [] => todo!() + | + +error: aborting due to 26 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/empty-match.rs b/tests/ui/pattern/usefulness/empty-match.rs index 9b22b47a12b1f..b34427a7c2388 100644 --- a/tests/ui/pattern/usefulness/empty-match.rs +++ b/tests/ui/pattern/usefulness/empty-match.rs @@ -5,7 +5,7 @@ #![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))] #![deny(unreachable_patterns)] -fn nonempty() { +fn nonempty(arrayN_of_empty: [!; N]) { macro_rules! match_no_arms { ($e:expr) => { match $e {} @@ -42,6 +42,7 @@ fn nonempty() { V4, V5, } + let array0_of_empty: [!; 0] = []; match_no_arms!(0u8); //~ ERROR type `u8` is non-empty match_no_arms!(0i8); //~ ERROR type `i8` is non-empty @@ -54,6 +55,8 @@ fn nonempty() { match_no_arms!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered match_no_arms!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered match_no_arms!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered + match_no_arms!(array0_of_empty); //~ ERROR type `[!; 0]` is non-empty + match_no_arms!(arrayN_of_empty); //~ ERROR type `[!; N]` is non-empty match_guarded_arm!(0u8); //~ ERROR `0_u8..=u8::MAX` not covered match_guarded_arm!(0i8); //~ ERROR `i8::MIN..=i8::MAX` not covered @@ -66,6 +69,8 @@ fn nonempty() { match_guarded_arm!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered match_guarded_arm!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered match_guarded_arm!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered + match_guarded_arm!(array0_of_empty); //~ ERROR `[]` not covered + match_guarded_arm!(arrayN_of_empty); //~ ERROR `[]` not covered } fn main() {} From 48c7ebe6ecfb16f9d17658eb53fe69e1b133f7d1 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 20 Mar 2024 22:51:29 +0100 Subject: [PATCH 11/23] Fixes in various places --- .../rustc_codegen_cranelift/example/mini_core_hello_world.rs | 1 + compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs | 1 + src/tools/clippy/clippy_utils/src/lib.rs | 1 + src/tools/clippy/tests/ui/single_match_else.fixed | 2 +- src/tools/clippy/tests/ui/single_match_else.rs | 2 +- src/tools/clippy/tests/ui/single_match_else.stderr | 4 ++-- src/tools/miri/src/eval.rs | 1 + src/tools/miri/tests/pass/async-fn.rs | 1 + src/tools/miri/tests/pass/enums.rs | 1 + 9 files changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 7d361a9ab2bb6..e603ac566f4ec 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -585,6 +585,7 @@ pub enum E2 { V4, } +#[allow(unreachable_patterns)] fn check_niche_behavior() { if let E1::V2 { .. } = (E1::V1 { f: true }) { intrinsics::abort(); diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index 5a7ddc4cd7fa5..9f096e9022012 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -430,6 +430,7 @@ pub enum E2 { V4, } +#[allow(unreachable_patterns)] fn check_niche_behavior () { if let E1::V2 { .. } = (E1::V1 { f: true }) { intrinsics::abort(); diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 1d5f1a2a2bb13..163e70f9fe54a 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -2927,6 +2927,7 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprU moved_before_use, same_ctxt, }, + #[allow(unreachable_patterns)] Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow"), None => ExprUseCtxt { node: Node::Crate(cx.tcx.hir().root_module()), diff --git a/src/tools/clippy/tests/ui/single_match_else.fixed b/src/tools/clippy/tests/ui/single_match_else.fixed index e840adf0fa34b..163be16ad8be7 100644 --- a/src/tools/clippy/tests/ui/single_match_else.fixed +++ b/src/tools/clippy/tests/ui/single_match_else.fixed @@ -89,7 +89,7 @@ fn main() { // lint here use std::convert::Infallible; - if let Ok(a) = Result::::Ok(1) { println!("${:?}", a) } else { + if let Ok(a) = Result::::Ok(1) { println!("${:?}", a) } else { println!("else block"); return; } diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs index 430c4da20f12a..3f1fd2b31832f 100644 --- a/src/tools/clippy/tests/ui/single_match_else.rs +++ b/src/tools/clippy/tests/ui/single_match_else.rs @@ -98,7 +98,7 @@ fn main() { // lint here use std::convert::Infallible; - match Result::::Ok(1) { + match Result::::Ok(1) { Ok(a) => println!("${:?}", a), Err(_) => { println!("else block"); diff --git a/src/tools/clippy/tests/ui/single_match_else.stderr b/src/tools/clippy/tests/ui/single_match_else.stderr index f8f88379d6d12..61c348260d05e 100644 --- a/src/tools/clippy/tests/ui/single_match_else.stderr +++ b/src/tools/clippy/tests/ui/single_match_else.stderr @@ -64,7 +64,7 @@ LL + } error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> tests/ui/single_match_else.rs:101:5 | -LL | / match Result::::Ok(1) { +LL | / match Result::::Ok(1) { LL | | Ok(a) => println!("${:?}", a), LL | | Err(_) => { LL | | println!("else block"); @@ -75,7 +75,7 @@ LL | | } | help: try | -LL ~ if let Ok(a) = Result::::Ok(1) { println!("${:?}", a) } else { +LL ~ if let Ok(a) = Result::::Ok(1) { println!("${:?}", a) } else { LL + println!("else block"); LL + return; LL + } diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 53e877517089c..b5d13e9b32479 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -460,6 +460,7 @@ pub fn eval_entry<'tcx>( let res = match res { Err(res) => res, // `Ok` can never happen + #[cfg(bootstrap)] Ok(never) => match never {}, }; diff --git a/src/tools/miri/tests/pass/async-fn.rs b/src/tools/miri/tests/pass/async-fn.rs index 13400c88c7112..67ec2e26b3068 100644 --- a/src/tools/miri/tests/pass/async-fn.rs +++ b/src/tools/miri/tests/pass/async-fn.rs @@ -59,6 +59,7 @@ async fn hello_world() { } // This example comes from https://github.com/rust-lang/rust/issues/115145 +#[allow(unreachable_patterns)] async fn uninhabited_variant() { async fn unreachable(_: Never) {} diff --git a/src/tools/miri/tests/pass/enums.rs b/src/tools/miri/tests/pass/enums.rs index ac7aafc1bb2e3..1dafef025e958 100644 --- a/src/tools/miri/tests/pass/enums.rs +++ b/src/tools/miri/tests/pass/enums.rs @@ -43,6 +43,7 @@ fn discriminant_overflow() { } } +#[allow(unreachable_patterns)] fn more_discriminant_overflow() { pub enum Infallible {} From c369c2034f7b4fb4af2c5e28806aaec65f38adc9 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 5 Aug 2024 12:39:35 -0500 Subject: [PATCH 12/23] Add a triagebot mention for `library/Cargo.lock` Since [1], `Cargo.lock` was split into `Cargo.lock` and `library/Cargo.lock`. Update Triagebot to give the same warning for both. [1]: https://github.com/rust-lang/rust/pull/128534 --- triagebot.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 2795f93728470..33108f743cb94 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -778,6 +778,14 @@ If this was unintentional then you should revert the changes before this PR is m Otherwise, you can ignore this comment. """ +[mentions."library/Cargo.lock"] +message = """ +These commits modify the `library/Cargo.lock` file. Unintentional changes to `library/Cargo.lock` can be introduced when switching branches and rebasing PRs. + +If this was unintentional then you should revert the changes before this PR is merged. +Otherwise, you can ignore this comment. +""" + [mentions."src/tools/x"] message = "`src/tools/x` was changed. Bump version of Cargo.toml in `src/tools/x` so tidy will suggest installing the new version." From 4af77dfea5290b0053efcb31cad774cabf71af59 Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 30 Jul 2024 14:27:20 -0400 Subject: [PATCH 13/23] implement BufReader::peek --- library/std/src/io/buffered/bufreader.rs | 34 +++++++++++++++++++ .../std/src/io/buffered/bufreader/buffer.rs | 21 ++++++++++++ 2 files changed, 55 insertions(+) diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index f11dd50c5e2b7..0b12e5777c840 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -94,6 +94,40 @@ impl BufReader { pub fn with_capacity(capacity: usize, inner: R) -> BufReader { BufReader { inner, buf: Buffer::with_capacity(capacity) } } + + /// Attempt to look ahead `n` bytes. + /// + /// `n` must be less than `capacity`. + /// + /// ## Examples + /// + /// ```rust + /// #![feature(bufreader_peek)] + /// use std::io::{Read, BufReader}; + /// + /// let mut bytes = &b"oh, hello"[..]; + /// let mut rdr = BufReader::with_capacity(6, &mut bytes); + /// assert_eq!(rdr.peek(2).unwrap(), b"oh"); + /// let mut buf = [0; 4]; + /// rdr.read(&mut buf[..]).unwrap(); + /// assert_eq!(&buf, b"oh, "); + /// assert_eq!(rdr.peek(2).unwrap(), b"he"); + /// let mut s = String::new(); + /// rdr.read_to_string(&mut s).unwrap(); + /// assert_eq!(&s, "hello"); + /// ``` + #[unstable(feature = "bufreader_peek", issue = "128405")] + pub fn peek(&mut self, n: usize) -> io::Result<&[u8]> { + assert!(n <= self.capacity()); + while n > self.buf.buffer().len() { + if self.buf.pos() > 0 { + self.buf.backshift(); + } + self.buf.read_more(&mut self.inner)?; + debug_assert_eq!(self.buf.pos(), 0); + } + Ok(&self.buf.buffer()[..n]) + } } impl BufReader { diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 796137c0123e7..ccd67fafb45b4 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -97,6 +97,27 @@ impl Buffer { self.pos = self.pos.saturating_sub(amt); } + /// Read more bytes into the buffer without discarding any of its contents + pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<()> { + let mut buf = BorrowedBuf::from(&mut self.buf[self.pos..]); + let old_init = self.initialized - self.pos; + unsafe { + buf.set_init(old_init); + } + reader.read_buf(buf.unfilled())?; + self.filled += buf.len(); + self.initialized += buf.init_len() - old_init; + Ok(()) + } + + /// Remove bytes that have already been read from the buffer. + pub fn backshift(&mut self) { + self.buf.copy_within(self.pos.., 0); + self.initialized -= self.pos; + self.filled -= self.pos; + self.pos = 0; + } + #[inline] pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> { // If we've reached the end of our internal buffer then we need to fetch From c8d50ef2ee71b6b586e52f0834657a7fd4b42800 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Mon, 5 Aug 2024 22:13:12 +0000 Subject: [PATCH 14/23] Windows: Test if `\\.\NUL` works as an input file --- tests/run-make/dos-device-input/rmake.rs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/run-make/dos-device-input/rmake.rs diff --git a/tests/run-make/dos-device-input/rmake.rs b/tests/run-make/dos-device-input/rmake.rs new file mode 100644 index 0000000000000..cc34dd551071d --- /dev/null +++ b/tests/run-make/dos-device-input/rmake.rs @@ -0,0 +1,9 @@ +//@ only-windows +// Reason: dos devices are a Windows thing + +use run_make_support::rustc; + +fn main() { + rustc().input(r"\\.\NUL").crate_type("staticlib").run(); + rustc().input(r"\\?\NUL").crate_type("staticlib").run(); +} From c6d94821f06320108b9fd35219a13c5ec4094ed4 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Mon, 5 Aug 2024 22:30:13 +0000 Subject: [PATCH 15/23] Don't ICE if getting the input's file_stem fails --- compiler/rustc_session/src/config.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f58a991a616da..a60af3f2d7137 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -838,10 +838,14 @@ pub enum Input { impl Input { pub fn filestem(&self) -> &str { - match *self { - Input::File(ref ifile) => ifile.file_stem().unwrap().to_str().unwrap(), - Input::Str { .. } => "rust_out", + if let Input::File(ifile) = self { + // If for some reason getting the file stem as a UTF-8 string fails, + // then fallback to a fixed name. + if let Some(name) = ifile.file_stem().and_then(OsStr::to_str) { + return name; + } } + "rust_out" } pub fn source_name(&self) -> FileName { From 63bf9e13c2f3d9812c4be80402343b563faf50e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 9 May 2024 20:55:32 +0000 Subject: [PATCH 16/23] Maintain highlighting in `note` and `help` even when they have a span --- compiler/rustc_errors/src/diagnostic.rs | 21 +++++++++++++++++++++ compiler/rustc_errors/src/emitter.rs | 7 +++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index e1dcbf5b2805b..67ca6d50cca4b 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -741,6 +741,16 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + #[rustc_lint_diagnostics] + pub fn highlighted_span_note( + &mut self, + span: impl Into, + msg: Vec, + ) -> &mut Self { + self.sub_with_highlights(Level::Note, msg, span.into()); + self + } + /// This is like [`Diag::note()`], but it's only printed once. #[rustc_lint_diagnostics] pub fn note_once(&mut self, msg: impl Into) -> &mut Self { @@ -815,6 +825,17 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + /// Add a help message attached to this diagnostic with a customizable highlighted message. + #[rustc_lint_diagnostics] + pub fn highlighted_span_help( + &mut self, + span: impl Into, + msg: Vec, + ) -> &mut Self { + self.sub_with_highlights(Level::Help, msg, span.into()); + self + } + /// Prints the span with some help above it. /// This is like [`Diag::help()`], but it gets its own span. #[rustc_lint_diagnostics] diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 8963b009c31ec..556d77da80fe3 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -1346,7 +1346,7 @@ impl HumanEmitter { buffer.append(0, ": ", header_style); label_width += 2; } - for (text, _) in msgs.iter() { + for (text, style) in msgs.iter() { let text = self.translate_message(text, args).map_err(Report::new).unwrap(); // Account for newlines to align output to its label. for (line, text) in normalize_whitespace(&text).lines().enumerate() { @@ -1357,7 +1357,10 @@ impl HumanEmitter { if line == 0 { String::new() } else { " ".repeat(label_width) }, text ), - header_style, + match style { + Style::Highlight => *style, + _ => header_style, + }, ); } } From c9170a1b7cc9900e0f89fa77caec6057acb9dfe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 9 May 2024 21:05:05 +0000 Subject: [PATCH 17/23] On trait bound mismatch, detect multiple crate versions in dep tree When encountering an E0277, if the type and the trait both come from a crate with the same name but different crate number, we explain that there are multiple crate versions in the dependency tree. If there's a type that fulfills the bound, and it has the same name as the passed in type and has the same crate name, we explain that the same type in two different versions of the same crate *are different*. ``` error[E0277]: the trait bound `Type: dependency::Trait` is not satisfied --> src/main.rs:4:18 | 4 | do_something(Type); | ------------ ^^^^ the trait `dependency::Trait` is not implemented for `Type` | | | required by a bound introduced by this call | help: you have multiple different versions of crate `dependency` in your dependency graph --> src/main.rs:1:5 | 1 | use bar::do_something; | ^^^ one version of crate `dependency` is used here, as a dependency of crate `bar` 2 | use dependency::Type; | ^^^^^^^^^^ one version of crate `dependency` is used here, as a direct dependency of the current crate note: two types coming from two different versions of the same crate are different types even if they look the same --> /home/gh-estebank/crate_versions/baz-2/src/lib.rs:1:1 | 1 | pub struct Type; | ^^^^^^^^^^^^^^^ this type doesn't implement the required trait | ::: /home/gh-estebank/crate_versions/baz/src/lib.rs:1:1 | 1 | pub struct Type; | ^^^^^^^^^^^^^^^ this type implements the required trait 2 | pub trait Trait {} | --------------- this is the required trait note: required by a bound in `bar::do_something` --> /home/gh-estebank/crate_versions/baz/src/lib.rs:4:24 | 4 | pub fn do_something(_: X) {} | ^^^^^ required by this bound in `do_something` ``` Address #22750. --- .../traits/fulfillment_errors.rs | 163 +++++++++++++----- 1 file changed, 124 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 2ce1b955af521..03b93f1c139b5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1624,9 +1624,130 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { other: bool, param_env: ty::ParamEnv<'tcx>, ) -> bool { - // If we have a single implementation, try to unify it with the trait ref - // that failed. This should uncover a better hint for what *is* implemented. + let alternative_candidates = |def_id: DefId| { + let mut impl_candidates: Vec<_> = self + .tcx + .all_impls(def_id) + // ignore `do_not_recommend` items + .filter(|def_id| { + !self + .tcx + .has_attrs_with_path(*def_id, &[sym::diagnostic, sym::do_not_recommend]) + }) + // Ignore automatically derived impls and `!Trait` impls. + .filter_map(|def_id| self.tcx.impl_trait_header(def_id)) + .filter_map(|header| { + (header.polarity != ty::ImplPolarity::Negative + || self.tcx.is_automatically_derived(def_id)) + .then(|| header.trait_ref.instantiate_identity()) + }) + .filter(|trait_ref| { + let self_ty = trait_ref.self_ty(); + // Avoid mentioning type parameters. + if let ty::Param(_) = self_ty.kind() { + false + } + // Avoid mentioning types that are private to another crate + else if let ty::Adt(def, _) = self_ty.peel_refs().kind() { + // FIXME(compiler-errors): This could be generalized, both to + // be more granular, and probably look past other `#[fundamental]` + // types, too. + self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) + } else { + true + } + }) + .collect(); + + impl_candidates.sort_by_key(|tr| tr.to_string()); + impl_candidates.dedup(); + impl_candidates + }; + + // We'll check for the case where the reason for the mismatch is that the trait comes from + // one crate version and the type comes from another crate version, even though they both + // are from the same crate. + let trait_def_id = trait_ref.def_id(); + if let ty::Adt(def, _) = trait_ref.self_ty().skip_binder().peel_refs().kind() + && let found_type = def.did() + && trait_def_id.krate != found_type.krate + && self.tcx.crate_name(trait_def_id.krate) == self.tcx.crate_name(found_type.krate) + { + let name = self.tcx.crate_name(trait_def_id.krate); + let spans: Vec<_> = [trait_def_id, found_type] + .into_iter() + .filter_map(|def_id| self.tcx.extern_crate(def_id)) + .map(|data| { + let dependency = if data.dependency_of == LOCAL_CRATE { + "direct dependency of the current crate".to_string() + } else { + let dep = self.tcx.crate_name(data.dependency_of); + format!("dependency of crate `{dep}`") + }; + ( + data.span, + format!("one version of crate `{name}` is used here, as a {dependency}"), + ) + }) + .collect(); + let mut span: MultiSpan = spans.iter().map(|(sp, _)| *sp).collect::>().into(); + for (sp, label) in spans.into_iter() { + span.push_span_label(sp, label); + } + err.highlighted_span_help( + span, + vec![ + StringPart::normal("you have ".to_string()), + StringPart::highlighted("multiple different versions".to_string()), + StringPart::normal(" of crate `".to_string()), + StringPart::highlighted(format!("{name}")), + StringPart::normal("` in your dependency graph".to_string()), + ], + ); + let candidates = if impl_candidates.is_empty() { + alternative_candidates(trait_def_id) + } else { + impl_candidates.into_iter().map(|cand| cand.trait_ref).collect() + }; + if let Some((sp_candidate, sp_found)) = candidates.iter().find_map(|trait_ref| { + if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind() + && let candidate_def_id = def.did() + && let Some(name) = self.tcx.opt_item_name(candidate_def_id) + && let Some(found) = self.tcx.opt_item_name(found_type) + && name == found + && candidate_def_id.krate != found_type.krate + && self.tcx.crate_name(candidate_def_id.krate) + == self.tcx.crate_name(found_type.krate) + { + // A candidate was found of an item with the same name, from two separate + // versions of the same crate, let's clarify. + Some((self.tcx.def_span(candidate_def_id), self.tcx.def_span(found_type))) + } else { + None + } + }) { + let mut span: MultiSpan = vec![sp_candidate, sp_found].into(); + span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait"); + span.push_span_label(sp_candidate, "this type implements the required trait"); + span.push_span_label(sp_found, "this type doesn't implement the required trait"); + err.highlighted_span_note( + span, + vec![ + StringPart::normal( + "two types coming from two different versions of the same crate are \ + different types " + .to_string(), + ), + StringPart::highlighted("even if they look the same".to_string()), + ], + ); + } + return true; + } + if let [single] = &impl_candidates { + // If we have a single implementation, try to unify it with the trait ref + // that failed. This should uncover a better hint for what *is* implemented. if self.probe(|_| { let ocx = ObligationCtxt::new(self); @@ -1798,43 +1919,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Mentioning implementers of `Copy`, `Debug` and friends is not useful. return false; } - let mut impl_candidates: Vec<_> = self - .tcx - .all_impls(def_id) - // ignore `do_not_recommend` items - .filter(|def_id| { - !self - .tcx - .has_attrs_with_path(*def_id, &[sym::diagnostic, sym::do_not_recommend]) - }) - // Ignore automatically derived impls and `!Trait` impls. - .filter_map(|def_id| self.tcx.impl_trait_header(def_id)) - .filter_map(|header| { - (header.polarity != ty::ImplPolarity::Negative - || self.tcx.is_automatically_derived(def_id)) - .then(|| header.trait_ref.instantiate_identity()) - }) - .filter(|trait_ref| { - let self_ty = trait_ref.self_ty(); - // Avoid mentioning type parameters. - if let ty::Param(_) = self_ty.kind() { - false - } - // Avoid mentioning types that are private to another crate - else if let ty::Adt(def, _) = self_ty.peel_refs().kind() { - // FIXME(compiler-errors): This could be generalized, both to - // be more granular, and probably look past other `#[fundamental]` - // types, too. - self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) - } else { - true - } - }) - .collect(); - - impl_candidates.sort_by_key(|tr| tr.to_string()); - impl_candidates.dedup(); - return report(impl_candidates, err); + return report(alternative_candidates(def_id), err); } // Sort impl candidates so that ordering is consistent for UI tests. From 089d4475274dcf47ef511408b148b78938ab945c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 May 2024 04:19:18 +0000 Subject: [PATCH 18/23] Add test for mixing types from two incompatible crate versions --- .../crate-loading/auxiliary/dep-2-reexport.rs | 4 + .../auxiliary/multiple-dep-versions-1.rs | 7 ++ .../auxiliary/multiple-dep-versions-2.rs | 7 ++ .../ui/crate-loading/multiple-dep-versions.rs | 14 +++ .../crate-loading/multiple-dep-versions.svg | 101 ++++++++++++++++++ 5 files changed, 133 insertions(+) create mode 100644 tests/ui/crate-loading/auxiliary/dep-2-reexport.rs create mode 100644 tests/ui/crate-loading/auxiliary/multiple-dep-versions-1.rs create mode 100644 tests/ui/crate-loading/auxiliary/multiple-dep-versions-2.rs create mode 100644 tests/ui/crate-loading/multiple-dep-versions.rs create mode 100644 tests/ui/crate-loading/multiple-dep-versions.svg diff --git a/tests/ui/crate-loading/auxiliary/dep-2-reexport.rs b/tests/ui/crate-loading/auxiliary/dep-2-reexport.rs new file mode 100644 index 0000000000000..3a793bbbb106e --- /dev/null +++ b/tests/ui/crate-loading/auxiliary/dep-2-reexport.rs @@ -0,0 +1,4 @@ +//@ edition:2021 +//@ aux-build:multiple-dep-versions-2.rs +extern crate dependency; +pub use dependency::do_something; diff --git a/tests/ui/crate-loading/auxiliary/multiple-dep-versions-1.rs b/tests/ui/crate-loading/auxiliary/multiple-dep-versions-1.rs new file mode 100644 index 0000000000000..b96bb6d9e6443 --- /dev/null +++ b/tests/ui/crate-loading/auxiliary/multiple-dep-versions-1.rs @@ -0,0 +1,7 @@ +#![crate_name="dependency"] +//@ edition:2021 +//@ compile-flags: -C metadata=1 -C extra-filename=-1 +pub struct Type; +pub trait Trait {} +impl Trait for Type {} +pub fn do_something(_: X) { } diff --git a/tests/ui/crate-loading/auxiliary/multiple-dep-versions-2.rs b/tests/ui/crate-loading/auxiliary/multiple-dep-versions-2.rs new file mode 100644 index 0000000000000..c79157fa4634f --- /dev/null +++ b/tests/ui/crate-loading/auxiliary/multiple-dep-versions-2.rs @@ -0,0 +1,7 @@ +#![crate_name="dependency"] +//@ edition:2021 +//@ compile-flags: -C metadata=2 -C extra-filename=-2 +pub struct Type(pub i32); +pub trait Trait {} +impl Trait for Type {} +pub fn do_something(_: X) {} diff --git a/tests/ui/crate-loading/multiple-dep-versions.rs b/tests/ui/crate-loading/multiple-dep-versions.rs new file mode 100644 index 0000000000000..9e0a5dc5c5dc9 --- /dev/null +++ b/tests/ui/crate-loading/multiple-dep-versions.rs @@ -0,0 +1,14 @@ +//@ aux-build:dep-2-reexport.rs +//@ aux-build:multiple-dep-versions-1.rs +//@ edition:2021 +//@ compile-flags: --error-format=human --color=always --crate-type bin --extern dependency={{build-base}}/crate-loading/multiple-dep-versions/auxiliary/libdependency-1.so --extern dep_2_reexport={{build-base}}/crate-loading/multiple-dep-versions/auxiliary/libdep_2_reexport.so +//@ ignore-windows + +extern crate dependency; +extern crate dep_2_reexport; +use dependency::Type; +use dep_2_reexport::do_something; + +fn main() { + do_something(Type); +} diff --git a/tests/ui/crate-loading/multiple-dep-versions.svg b/tests/ui/crate-loading/multiple-dep-versions.svg new file mode 100644 index 0000000000000..6b89ca691e0bc --- /dev/null +++ b/tests/ui/crate-loading/multiple-dep-versions.svg @@ -0,0 +1,101 @@ + + + + + + + error[E0277]: the trait bound `Type: dependency::Trait` is not satisfied + + --> $DIR/multiple-dep-versions.rs:13:18 + + | + + LL | do_something(Type); + + | ------------ ^^^^ the trait `dependency::Trait` is not implemented for `Type` + + | | + + | required by a bound introduced by this call + + | + + help: you have multiple different versions of crate `dependency` in your dependency graph + + --> $DIR/multiple-dep-versions.rs:7:1 + + | + + LL | extern crate dependency; + + | ^^^^^^^^^^^^^^^^^^^^^^^^ one version of crate `dependency` is used here, as a direct dependency of the current crate + + LL | extern crate dep_2_reexport; + + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one version of crate `dependency` is used here, as a dependency of crate `dep_2_reexport` + + note: two types coming from two different versions of the same crate are different types even if they look the same + + --> $DIR/auxiliary/multiple-dep-versions-1.rs:4:1 + + | + + LL | pub struct Type; + + | ^^^^^^^^^^^^^^^ this type doesn't implement the required trait + + | + + ::: $DIR/auxiliary/multiple-dep-versions-2.rs:4:1 + + | + + LL | pub struct Type(pub i32); + + | ^^^^^^^^^^^^^^^ this type implements the required trait + + LL | pub trait Trait {} + + | --------------- this is the required trait + + note: required by a bound in `dep_2_reexport::do_something` + + --> $DIR/auxiliary/multiple-dep-versions-2.rs:7:24 + + | + + LL | pub fn do_something<X: Trait>(_: X) {} + + | ^^^^^ required by this bound in `do_something` + + + + error: aborting due to 1 previous error + + + + For more information about this error, try `rustc --explain E0277`. + + + + + + From 829561f37c1decbd8207cf5e0d4b785079ddbd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 May 2024 04:30:14 +0000 Subject: [PATCH 19/23] Add `help` about using `cargo tree` --- .../traits/fulfillment_errors.rs | 1 + .../crate-loading/multiple-dep-versions.svg | 24 ++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 03b93f1c139b5..5a25d9a7ecdac 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1742,6 +1742,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ], ); } + err.help("you can use `cargo tree` to explore your dependency tree"); return true; } diff --git a/tests/ui/crate-loading/multiple-dep-versions.svg b/tests/ui/crate-loading/multiple-dep-versions.svg index 6b89ca691e0bc..671f38d3eb013 100644 --- a/tests/ui/crate-loading/multiple-dep-versions.svg +++ b/tests/ui/crate-loading/multiple-dep-versions.svg @@ -1,4 +1,4 @@ - +