From 012a458dcabc6438ee070c074435860690475370 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Mon, 13 May 2024 17:42:44 +0530 Subject: [PATCH 001/147] Suggest removing unused tuple fields if they are the last fields --- compiler/rustc_passes/messages.ftl | 9 +++ compiler/rustc_passes/src/dead.rs | 56 +++++++++++++++---- compiler/rustc_passes/src/errors.rs | 19 +++++-- tests/ui/lint/dead-code/tuple-struct-field.rs | 35 +++++++----- .../lint/dead-code/tuple-struct-field.stderr | 39 +++++++------ 5 files changed, 111 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index a545c170297d..70d314913d2a 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -597,6 +597,15 @@ passes_pass_by_value = passes_proc_macro_bad_sig = {$kind} has incorrect signature +passes_remove_fields = + consider removing { $num -> + [one] this + *[other] these + } { $num -> + [one] field + *[other] fields + } + passes_repr_conflicting = conflicting representation hints diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 51f288b3c959..f977f90e8ace 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -23,8 +23,7 @@ use rustc_target::abi::FieldIdx; use std::mem; use crate::errors::{ - ChangeFieldsToBeOfUnitType, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, - UselessAssignment, + ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UselessAssignment, }; // Any local node that may call something in its body block should be @@ -993,17 +992,50 @@ impl<'tcx> DeadVisitor<'tcx> { }; let diag = match report_on { - ReportOn::TupleField => MultipleDeadCodes::UnusedTupleStructFields { - multiple, - num, - descr, - participle, - name_list, - change_fields_suggestion: ChangeFieldsToBeOfUnitType { num, spans: spans.clone() }, - parent_info, - ignored_derived_impls, - }, + ReportOn::TupleField => { + let tuple_fields = if let Some(parent_id) = parent_item + && let node = tcx.hir_node_by_def_id(parent_id) + && let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(hir::VariantData::Tuple(fields, _, _), _), + .. + }) = node + { + *fields + } else { + &[] + }; + let trailing_tuple_fields = if tuple_fields.len() >= dead_codes.len() { + LocalDefIdSet::from_iter( + tuple_fields + .iter() + .skip(tuple_fields.len() - dead_codes.len()) + .map(|f| f.def_id), + ) + } else { + LocalDefIdSet::default() + }; + + let fields_suggestion = + // Suggest removal if all tuple fields are at the end. + // Otherwise suggest removal or changing to unit type + if dead_codes.iter().all(|dc| trailing_tuple_fields.contains(&dc.def_id)) { + ChangeFields::Remove { num } + } else { + ChangeFields::ChangeToUnitTypeOrRemove { num, spans: spans.clone() } + }; + + MultipleDeadCodes::UnusedTupleStructFields { + multiple, + num, + descr, + participle, + name_list, + change_fields_suggestion: fields_suggestion, + parent_info, + ignored_derived_impls, + } + } ReportOn::NamedField => MultipleDeadCodes::DeadCodes { multiple, num, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 03a607348e88..a590ef8f3a1e 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1589,7 +1589,7 @@ pub enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] - change_fields_suggestion: ChangeFieldsToBeOfUnitType, + change_fields_suggestion: ChangeFields, #[subdiagnostic] parent_info: Option>, #[subdiagnostic] @@ -1616,11 +1616,18 @@ pub struct IgnoredDerivedImpls { } #[derive(Subdiagnostic)] -#[multipart_suggestion(passes_change_fields_to_be_of_unit_type, applicability = "has-placeholders")] -pub struct ChangeFieldsToBeOfUnitType { - pub num: usize, - #[suggestion_part(code = "()")] - pub spans: Vec, +pub enum ChangeFields { + #[multipart_suggestion( + passes_change_fields_to_be_of_unit_type, + applicability = "has-placeholders" + )] + ChangeToUnitTypeOrRemove { + num: usize, + #[suggestion_part(code = "()")] + spans: Vec, + }, + #[help(passes_remove_fields)] + Remove { num: usize }, } #[derive(Diagnostic)] diff --git a/tests/ui/lint/dead-code/tuple-struct-field.rs b/tests/ui/lint/dead-code/tuple-struct-field.rs index d13fe029289a..ff3da4105000 100644 --- a/tests/ui/lint/dead-code/tuple-struct-field.rs +++ b/tests/ui/lint/dead-code/tuple-struct-field.rs @@ -5,15 +5,20 @@ use std::marker::PhantomData; const LEN: usize = 4; -struct SingleUnused(i32, [u8; LEN], String); -//~^ ERROR: field `1` is never read -//~| NOTE: field in this struct -//~| HELP: consider changing the field to be of unit type - -struct MultipleUnused(i32, f32, String, u8); -//~^ ERROR: fields `0`, `1`, `2`, and `3` are never read +struct UnusedAtTheEnd(i32, f32, [u8; LEN], String, u8); +//~^ ERROR:fields `1`, `2`, `3`, and `4` are never read //~| NOTE: fields in this struct -//~| HELP: consider changing the fields to be of unit type +//~| HELP: consider removing these fields + +struct UnusedJustOneField(i32); +//~^ ERROR: field `0` is never read +//~| NOTE: field in this struct +//~| HELP: consider removing this field + +struct UnusedInTheMiddle(i32, f32, String, u8, u32); +//~^ ERROR: fields `1`, `2`, and `4` are never read +//~| NOTE: fields in this struct +//~| HELP: consider changing the fields to be of unit type to suppress this warning while preserving the field numbering, or remove the fields struct GoodUnit(()); @@ -23,15 +28,19 @@ struct Void; struct GoodVoid(Void); fn main() { - let w = SingleUnused(42, [0, 1, 2, 3], "abc".to_string()); - let _ = w.0; - let _ = w.2; + let u1 = UnusedAtTheEnd(42, 3.14, [0, 1, 2, 3], "def".to_string(), 4u8); + let _ = u1.0; + + let _ = UnusedJustOneField(42); + + let u2 = UnusedInTheMiddle(42, 3.14, "def".to_string(), 4u8, 5); + let _ = u2.0; + let _ = u2.3; - let m = MultipleUnused(42, 3.14, "def".to_string(), 4u8); let gu = GoodUnit(()); let gp = GoodPhantom(PhantomData); let gv = GoodVoid(Void); - let _ = (gu, gp, gv, m); + let _ = (gu, gp, gv); } diff --git a/tests/ui/lint/dead-code/tuple-struct-field.stderr b/tests/ui/lint/dead-code/tuple-struct-field.stderr index 0154d5489f9f..434554d7ae5e 100644 --- a/tests/ui/lint/dead-code/tuple-struct-field.stderr +++ b/tests/ui/lint/dead-code/tuple-struct-field.stderr @@ -1,33 +1,40 @@ -error: field `1` is never read - --> $DIR/tuple-struct-field.rs:8:26 +error: fields `1`, `2`, `3`, and `4` are never read + --> $DIR/tuple-struct-field.rs:8:28 | -LL | struct SingleUnused(i32, [u8; LEN], String); - | ------------ ^^^^^^^^^ +LL | struct UnusedAtTheEnd(i32, f32, [u8; LEN], String, u8); + | -------------- ^^^ ^^^^^^^^^ ^^^^^^ ^^ | | - | field in this struct + | fields in this struct | + = help: consider removing these fields note: the lint level is defined here --> $DIR/tuple-struct-field.rs:1:9 | LL | #![deny(dead_code)] | ^^^^^^^^^ -help: consider changing the field to be of unit type to suppress this warning while preserving the field numbering, or remove the field - | -LL | struct SingleUnused(i32, (), String); - | ~~ -error: fields `0`, `1`, `2`, and `3` are never read - --> $DIR/tuple-struct-field.rs:13:23 +error: field `0` is never read + --> $DIR/tuple-struct-field.rs:13:27 | -LL | struct MultipleUnused(i32, f32, String, u8); - | -------------- ^^^ ^^^ ^^^^^^ ^^ +LL | struct UnusedJustOneField(i32); + | ------------------ ^^^ + | | + | field in this struct + | + = help: consider removing this field + +error: fields `1`, `2`, and `4` are never read + --> $DIR/tuple-struct-field.rs:18:31 + | +LL | struct UnusedInTheMiddle(i32, f32, String, u8, u32); + | ----------------- ^^^ ^^^^^^ ^^^ | | | fields in this struct | help: consider changing the fields to be of unit type to suppress this warning while preserving the field numbering, or remove the fields | -LL | struct MultipleUnused((), (), (), ()); - | ~~ ~~ ~~ ~~ +LL | struct UnusedInTheMiddle(i32, (), (), u8, ()); + | ~~ ~~ ~~ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors From e49be415cdc24d876403e8493e28ec1bfee62427 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Tue, 16 Apr 2024 20:24:21 +0200 Subject: [PATCH 002/147] Replace sort implementations - `slice::sort` -> driftsort https://github.com/Voultapher/sort-research-rs/blob/main/writeup/driftsort_introduction/text.md - `slice::sort_unstable` -> ipnsort https://github.com/Voultapher/sort-research-rs/blob/main/writeup/ipnsort_introduction/text.md Replaces the sort implementations with tailor made ones that strike a balance of run-time, compile-time and binary-size, yielding run-time and compile-time improvements. Regressing binary-size for `slice::sort` while improving it for `slice::sort_unstable`. All while upholding the existing soft and hard safety guarantees, and even extending the soft guarantees, detecting strict weak ordering violations with a high chance and reporting it to users via a panic. In addition the implementation of `select_nth_unstable` is also adapted as it uses `slice::sort_unstable` internals. --- library/alloc/src/slice.rs | 215 ++- library/alloc/src/slice/tests.rs | 22 +- library/core/src/slice/mod.rs | 193 ++- library/core/src/slice/sort.rs | 1383 ----------------- library/core/src/slice/sort/mod.rs | 8 + library/core/src/slice/{ => sort}/select.rs | 148 +- library/core/src/slice/sort/shared/mod.rs | 45 + library/core/src/slice/sort/shared/pivot.rs | 88 ++ .../core/src/slice/sort/shared/smallsort.rs | 843 ++++++++++ library/core/src/slice/sort/stable/drift.rs | 300 ++++ library/core/src/slice/sort/stable/merge.rs | 151 ++ library/core/src/slice/sort/stable/mod.rs | 116 ++ .../core/src/slice/sort/stable/quicksort.rs | 267 ++++ .../core/src/slice/sort/unstable/heapsort.rs | 80 + library/core/src/slice/sort/unstable/mod.rs | 76 + .../core/src/slice/sort/unstable/quicksort.rs | 347 +++++ library/core/tests/lib.rs | 1 - library/core/tests/slice.rs | 26 +- 18 files changed, 2621 insertions(+), 1688 deletions(-) delete mode 100644 library/core/src/slice/sort.rs create mode 100644 library/core/src/slice/sort/mod.rs rename library/core/src/slice/{ => sort}/select.rs (77%) create mode 100644 library/core/src/slice/sort/shared/mod.rs create mode 100644 library/core/src/slice/sort/shared/pivot.rs create mode 100644 library/core/src/slice/sort/shared/smallsort.rs create mode 100644 library/core/src/slice/sort/stable/drift.rs create mode 100644 library/core/src/slice/sort/stable/merge.rs create mode 100644 library/core/src/slice/sort/stable/mod.rs create mode 100644 library/core/src/slice/sort/stable/quicksort.rs create mode 100644 library/core/src/slice/sort/unstable/heapsort.rs create mode 100644 library/core/src/slice/sort/unstable/mod.rs create mode 100644 library/core/src/slice/sort/unstable/quicksort.rs diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index ebe6f7e7caa9..104f4ee753b1 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -16,7 +16,7 @@ use core::borrow::{Borrow, BorrowMut}; #[cfg(not(no_global_oom_handling))] use core::cmp::Ordering::{self, Less}; #[cfg(not(no_global_oom_handling))] -use core::mem::{self, SizedTypeProperties}; +use core::mem::{self, MaybeUninit}; #[cfg(not(no_global_oom_handling))] use core::ptr; #[cfg(not(no_global_oom_handling))] @@ -24,7 +24,7 @@ use core::slice::sort; use crate::alloc::Allocator; #[cfg(not(no_global_oom_handling))] -use crate::alloc::{self, Global}; +use crate::alloc::Global; #[cfg(not(no_global_oom_handling))] use crate::borrow::ToOwned; use crate::boxed::Box; @@ -174,23 +174,32 @@ pub(crate) mod hack { #[cfg(not(test))] impl [T] { - /// Sorts the slice. + /// Sorts the slice, preserving initial order of equal elements. /// - /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) worst-case. + /// 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. /// /// 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). + /// 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`. /// /// # Current implementation /// - /// The current algorithm is an adaptive, iterative merge sort inspired by - /// [timsort](https://en.wikipedia.org/wiki/Timsort). - /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of - /// two or more sorted sequences concatenated one after another. + /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which + /// combines the fast average case of quicksort with the fast worst case and partial run + /// detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs + /// with k distinct elements, the expected time to sort the data is *O(*n* log(*k*))*. /// - /// Also, it allocates temporary storage half the size of `self`, but for short slices a - /// non-allocating insertion sort is used instead. + /// The auxiliary memory allocation behavior depends on the input length. Short slices are + /// 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. /// /// # Examples /// @@ -200,6 +209,8 @@ impl [T] { /// v.sort(); /// assert!(v == [-5, -3, 1, 2, 4]); /// ``` + /// + /// [driftsort]: https://github.com/Voultapher/driftsort #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] @@ -211,13 +222,18 @@ impl [T] { stable_sort(self, T::lt); } - /// Sorts the slice with a comparator function. + /// Sorts the slice with a comparator 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. + /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) + /// worst-case. /// - /// The comparator function must define a total ordering for the elements in the slice. If - /// the ordering is not total, the order of the elements is unspecified. An order is a - /// total order if it is (for all `a`, `b` and `c`): + /// 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 `>`. @@ -227,23 +243,22 @@ impl [T] { /// /// ``` /// let mut floats = [5f64, 4.0, 1.0, 3.0, 2.0]; - /// floats.sort_by(|a, b| a.partial_cmp(b).unwrap()); + /// floats.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); /// assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 5.0]); /// ``` /// - /// When applicable, unstable sorting is preferred because it is generally faster than stable - /// sorting and it doesn't allocate auxiliary memory. - /// See [`sort_unstable_by`](slice::sort_unstable_by). - /// /// # Current implementation /// - /// The current algorithm is an adaptive, iterative merge sort inspired by - /// [timsort](https://en.wikipedia.org/wiki/Timsort). - /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of - /// two or more sorted sequences concatenated one after another. + /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which + /// combines the fast average case of quicksort with the fast worst case and partial run + /// detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs + /// with k distinct elements, the expected time to sort the data is *O(*n* log(*k*))*. /// - /// Also, it allocates temporary storage half the size of `self`, but for short slices a - /// non-allocating insertion sort is used instead. + /// The auxiliary memory allocation behavior depends on the input length. Short slices are + /// 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. /// /// # Examples /// @@ -256,6 +271,8 @@ impl [T] { /// v.sort_by(|a, b| b.cmp(a)); /// assert!(v == [5, 4, 3, 2, 1]); /// ``` + /// + /// [driftsort]: https://github.com/Voultapher/driftsort #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] @@ -267,28 +284,27 @@ impl [T] { stable_sort(self, |a, b| compare(a, b) == Less); } - /// Sorts the slice with a key extraction function. + /// Sorts the slice with a key extraction function, preserving initial order of equal elements. /// /// 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*). /// - /// For expensive key functions (e.g. functions that are not simple property accesses or - /// basic operations), [`sort_by_cached_key`](slice::sort_by_cached_key) is likely to be - /// significantly faster, as it does not recompute element keys. - /// - /// When applicable, unstable sorting is preferred because it is generally faster than stable - /// sorting and it doesn't allocate auxiliary memory. - /// See [`sort_unstable_by_key`](slice::sort_unstable_by_key). + /// 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. /// /// # Current implementation /// - /// The current algorithm is an adaptive, iterative merge sort inspired by - /// [timsort](https://en.wikipedia.org/wiki/Timsort). - /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of - /// two or more sorted sequences concatenated one after another. + /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which + /// combines the fast average case of quicksort with the fast worst case and partial run + /// detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs + /// with k distinct elements, the expected time to sort the data is *O(*n* log(*k*))*. /// - /// Also, it allocates temporary storage half the size of `self`, but for short slices a - /// non-allocating insertion sort is used instead. + /// The auxiliary memory allocation behavior depends on the input length. Short slices are + /// 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. /// /// # Examples /// @@ -298,6 +314,8 @@ impl [T] { /// v.sort_by_key(|k| k.abs()); /// assert!(v == [1, 2, -3, 4, -5]); /// ``` + /// + /// [driftsort]: https://github.com/Voultapher/driftsort #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "slice_sort_by_key", since = "1.7.0")] @@ -310,27 +328,30 @@ impl [T] { stable_sort(self, |a, b| f(a).lt(&f(b))); } - /// Sorts the slice with a key extraction function. + /// Sorts the slice with a key extraction function, preserving initial order of equal elements. /// - /// During sorting, the key function is called at most once per element, by using - /// temporary 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. + /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* + *n* \* + /// log(*n*)) worst-case, where the key function is *O*(*m*). /// - /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* + *n* \* log(*n*)) - /// worst-case, where the key function is *O*(*m*). + /// During sorting, the key function is called at most once per element, by using temporary + /// 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. /// - /// 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. + /// 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. + /// + /// 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. /// /// # Current implementation /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. + /// The current implementation is based on [instruction-parallel-network sort][ipnsort] by Lukas + /// Bergdoll, which combines the fast average case of randomized quicksort with the fast worst + /// case of heapsort, while achieving linear time on fully sorted and reversed inputs. And + /// *O*(*k* \* log(*n*)) where *k* is the number of distinct elements in the input. It leverages + /// superscalar out-of-order execution capabilities commonly found in CPUs, to efficiently + /// perform the operation. /// /// In the worst case, the algorithm allocates temporary storage in a `Vec<(K, usize)>` the /// length of the slice. @@ -344,7 +365,7 @@ impl [T] { /// assert!(v == [-3, -5, 2, 32, 4]); /// ``` /// - /// [pdqsort]: https://github.com/orlp/pdqsort + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "slice_sort_by_cached_key", since = "1.34.0")] @@ -361,7 +382,7 @@ impl [T] { $slice.iter().map($f).enumerate().map(|(i, k)| (k, i as $t)).collect(); // The elements of `indices` are unique, as they are indexed, so any sort will be // stable with respect to the original slice. We use `sort_unstable` here because - // it requires less memory allocation. + // it requires no memory allocation. indices.sort_unstable(); for i in 0..$slice.len() { let mut index = indices[i].1; @@ -374,24 +395,24 @@ impl [T] { }}; } - let sz_u8 = mem::size_of::<(K, u8)>(); - let sz_u16 = mem::size_of::<(K, u16)>(); - let sz_u32 = mem::size_of::<(K, u32)>(); - let sz_usize = mem::size_of::<(K, usize)>(); - let len = self.len(); if len < 2 { return; } - if sz_u8 < sz_u16 && len <= (u8::MAX as usize) { - return sort_by_key!(u8, self, f); - } - if sz_u16 < sz_u32 && len <= (u16::MAX as usize) { - return sort_by_key!(u16, self, f); - } - if sz_u32 < sz_usize && len <= (u32::MAX as usize) { + + // Avoids binary-size usage in cases where the alignment doesn't work out to make this + // beneficial or on 32-bit platforms. + let is_using_u32_as_idx_type_helpful = + const { mem::size_of::<(K, u32)>() < mem::size_of::<(K, usize)>() }; + + // It's possible to instantiate this for u8 and u16 but, doing so is very wasteful in terms + // of compile-times and binary-size, the peak saved heap memory for u16 is (u8 + u16) -> 4 + // bytes * u16::MAX vs (u8 + u32) -> 8 bytes * u16::MAX, the saved heap memory is at peak + // ~262KB. + if is_using_u32_as_idx_type_helpful && len <= (u32::MAX as usize) { return sort_by_key!(u32, self, f); } + sort_by_key!(usize, self, f) } @@ -843,46 +864,18 @@ fn stable_sort(v: &mut [T], mut is_less: F) where F: FnMut(&T, &T) -> bool, { - if T::IS_ZST { - // Sorting has no meaningful behavior on zero-sized types. Do nothing. - return; + use sort::stable::BufGuard; + + #[unstable(issue = "none", feature = "std_internals")] + impl BufGuard for Vec { + fn with_capacity(capacity: usize) -> Self { + Vec::with_capacity(capacity) + } + + fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit] { + self.spare_capacity_mut() + } } - let elem_alloc_fn = |len: usize| -> *mut T { - // SAFETY: Creating the layout is safe as long as merge_sort never calls this with len > - // v.len(). Alloc in general will only be used as 'shadow-region' to store temporary swap - // elements. - unsafe { alloc::alloc(alloc::Layout::array::(len).unwrap_unchecked()) as *mut T } - }; - - let elem_dealloc_fn = |buf_ptr: *mut T, len: usize| { - // SAFETY: Creating the layout is safe as long as merge_sort never calls this with len > - // v.len(). The caller must ensure that buf_ptr was created by elem_alloc_fn with the same - // len. - unsafe { - alloc::dealloc(buf_ptr as *mut u8, alloc::Layout::array::(len).unwrap_unchecked()); - } - }; - - let run_alloc_fn = |len: usize| -> *mut sort::TimSortRun { - // SAFETY: Creating the layout is safe as long as merge_sort never calls this with an - // obscene length or 0. - unsafe { - alloc::alloc(alloc::Layout::array::(len).unwrap_unchecked()) - as *mut sort::TimSortRun - } - }; - - let run_dealloc_fn = |buf_ptr: *mut sort::TimSortRun, len: usize| { - // SAFETY: The caller must ensure that buf_ptr was created by elem_alloc_fn with the same - // len. - unsafe { - alloc::dealloc( - buf_ptr as *mut u8, - alloc::Layout::array::(len).unwrap_unchecked(), - ); - } - }; - - sort::merge_sort(v, &mut is_less, elem_alloc_fn, elem_dealloc_fn, run_alloc_fn, run_dealloc_fn); + sort::stable::sort::>(v, &mut is_less); } diff --git a/library/alloc/src/slice/tests.rs b/library/alloc/src/slice/tests.rs index 54bc4e77b16f..63a176a5ace3 100644 --- a/library/alloc/src/slice/tests.rs +++ b/library/alloc/src/slice/tests.rs @@ -34,7 +34,7 @@ macro_rules! do_test { } let v = $input.to_owned(); - let _ = std::panic::catch_unwind(move || { + let _ = panic::catch_unwind(move || { let mut v = v; let mut panic_countdown = panic_countdown; v.$func(|a, b| { @@ -197,8 +197,7 @@ fn panic_safe() { let mut rng = test_rng(); - // Miri is too slow (but still need to `chain` to make the types match) - let lens = if cfg!(miri) { (1..10).chain(0..0) } else { (1..20).chain(70..MAX_LEN) }; + let lens = if cfg!(miri) { (1..10).chain(30..36) } else { (1..20).chain(70..MAX_LEN) }; let moduli: &[u32] = if cfg!(miri) { &[5] } else { &[5, 20, 50] }; for len in lens { @@ -294,15 +293,20 @@ fn test_sort() { } } - // Sort using a completely random comparison function. - // This will reorder the elements *somehow*, but won't panic. - let mut v = [0; 500]; - for i in 0..v.len() { + const ORD_VIOLATION_MAX_LEN: usize = 500; + let mut v = [0; ORD_VIOLATION_MAX_LEN]; + for i in 0..ORD_VIOLATION_MAX_LEN { v[i] = i as i32; } - v.sort_by(|_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); + + // Sort using a completely random comparison function. This will reorder the elements *somehow*, + // it may panic but the original elements must still be present. + let _ = panic::catch_unwind(move || { + v.sort_by(|_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); + }); + v.sort(); - for i in 0..v.len() { + for i in 0..ORD_VIOLATION_MAX_LEN { assert_eq!(v[i], i as i32); } diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 9bee50424b3d..4e6070cdb1b5 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -39,7 +39,6 @@ pub(crate) mod index; mod iter; mod raw; mod rotate; -mod select; mod specialize; #[unstable(feature = "str_internals", issue = "none")] @@ -83,10 +82,6 @@ pub use raw::{from_mut, from_ref}; #[unstable(feature = "slice_from_ptr_range", issue = "89792")] pub use raw::{from_mut_ptr_range, from_ptr_range}; -// This function is public only because there is no other way to unit test heapsort. -#[unstable(feature = "sort_internals", reason = "internal to sort module", issue = "none")] -pub use sort::heapsort; - #[stable(feature = "slice_get_slice", since = "1.28.0")] pub use index::SliceIndex; @@ -2884,21 +2879,26 @@ impl [T] { self.binary_search_by(|k| f(k).cmp(b)) } - /// Sorts the slice, but might not preserve the order of equal elements. + /// Sorts the slice **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. + /// 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. /// /// # Current implementation /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. + /// The current implementation is based on [ipnsort] by Lukas Bergdoll and Orson Peters, which + /// combines the fast average case of quicksort with the fast worst case of heapsort, achieving + /// linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the + /// expected time to sort the data is *O(*n* log(*k*))*. /// /// It is typically faster than stable sorting, except in a few special cases, e.g., when the - /// slice consists of several concatenated sorted sequences. + /// slice is partially sorted. + /// + /// If `T: Ord` does not implement a total order, the implementation may panic. /// /// # Examples /// @@ -2909,25 +2909,29 @@ impl [T] { /// assert!(v == [-5, -3, 1, 2, 4]); /// ``` /// - /// [pdqsort]: https://github.com/orlp/pdqsort + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable(&mut self) where T: Ord, { - sort::quicksort(self, T::lt); + sort::unstable::sort(self, &mut T::lt); } - /// Sorts the slice with a comparator function, but might not preserve the order of equal - /// elements. + /// Sorts the slice with a comparator 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. + /// 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 must define a total ordering for the elements in the slice. If - /// the ordering is not total, the order of the elements is unspecified. An order is a - /// total order if it is (for all `a`, `b` and `c`): + /// 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 `>`. @@ -2943,14 +2947,15 @@ impl [T] { /// /// # Current implementation /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. + /// The current implementation is based on [ipnsort] by Lukas Bergdoll and Orson Peters, which + /// combines the fast average case of quicksort with the fast worst case of heapsort, achieving + /// linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the + /// expected time to sort the data is *O(*n* log(*k*))*. /// /// It is typically faster than stable sorting, except in a few special cases, e.g., when the - /// slice consists of several concatenated sorted sequences. + /// slice is partially sorted. + /// + /// If `T: Ord` does not implement a total order, the implementation may panic. /// /// # Examples /// @@ -2964,34 +2969,37 @@ impl [T] { /// assert!(v == [5, 4, 3, 2, 1]); /// ``` /// - /// [pdqsort]: https://github.com/orlp/pdqsort + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable_by(&mut self, mut compare: F) where F: FnMut(&T, &T) -> Ordering, { - sort::quicksort(self, |a, b| compare(a, b) == Ordering::Less); + sort::unstable::sort(self, &mut |a, b| compare(a, b) == Ordering::Less); } - /// Sorts the slice with a key extraction function, but might not preserve the order of equal - /// elements. + /// Sorts the slice with a key extraction 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*(*m* \* *n* \* log(*n*)) worst-case, where the key function is - /// *O*(*m*). + /// 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. /// /// # Current implementation /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. + /// The current implementation is based on [ipnsort] by Lukas Bergdoll and Orson Peters, which + /// combines the fast average case of quicksort with the fast worst case of heapsort, achieving + /// linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the + /// expected time to sort the data is *O(*n* log(*k*))*. /// - /// Due to its key calling strategy, [`sort_unstable_by_key`](#method.sort_unstable_by_key) - /// is likely to be slower than [`sort_by_cached_key`](#method.sort_by_cached_key) in - /// cases where the key function is expensive. + /// 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. /// /// # Examples /// @@ -3002,7 +3010,7 @@ impl [T] { /// assert!(v == [1, 2, -3, 4, -5]); /// ``` /// - /// [pdqsort]: https://github.com/orlp/pdqsort + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable_by_key(&mut self, mut f: F) @@ -3010,27 +3018,32 @@ impl [T] { F: FnMut(&T) -> K, K: Ord, { - sort::quicksort(self, |a, b| f(a).lt(&f(b))); + sort::unstable::sort(self, &mut |a, b| f(a).lt(&f(b))); } - /// Reorder the slice such that the element at `index` after the reordering is at its final sorted position. + /// Reorder the slice such that the element at `index` after the reordering is at its final + /// sorted position. /// /// This reordering has the additional property that any value at position `i < index` will be /// less than or equal to any value at a position `j > index`. Additionally, this reordering is - /// unstable (i.e. any number of equal elements may end up at position `index`), in-place - /// (i.e. does not allocate), and runs in *O*(*n*) time. - /// This function is also known as "kth element" in other libraries. + /// unstable (i.e. any number of equal elements may end up at position `index`), in-place (i.e. + /// does not allocate), and runs in *O*(*n*) time. This function is also known as "kth element" + /// in other libraries. /// - /// It returns a triplet of the following from the reordered slice: - /// the subslice prior to `index`, the element at `index`, and the subslice after `index`; - /// accordingly, the values in those two subslices will respectively all be less-than-or-equal-to - /// and greater-than-or-equal-to the value of the element at `index`. + /// It returns a triplet of the following from the reordered slice: the subslice prior to + /// `index`, the element at `index`, and the subslice after `index`; accordingly, the values in + /// those two subslices will respectively all be less-than-or-equal-to and + /// greater-than-or-equal-to the value of the element at `index`. /// /// # Current implementation /// - /// The current algorithm is an introselect implementation based on Pattern Defeating Quicksort, which is also - /// the basis for [`sort_unstable`]. The fallback algorithm is Median of Medians using Tukey's Ninther for - /// pivot selection, which guarantees linear runtime for all inputs. + /// The current algorithm is an introselect implementation based on [ipnsort] by Lukas Bergdoll + /// and Orson Peters, which is also the basis for [`sort_unstable`]. The fallback algorithm is + /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime + /// for all inputs. + /// + /// It is typically faster than 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 /// @@ -3058,35 +3071,40 @@ impl [T] { /// v == [-3, -5, 1, 4, 2] || /// v == [-5, -3, 1, 4, 2]); /// ``` + /// + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[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]) where T: Ord, { - select::partition_at_index(self, index, T::lt) + sort::select::partition_at_index(self, index, T::lt) } - /// Reorder the slice with a comparator function such that the element at `index` after the reordering is at - /// its final sorted position. + /// Reorder the slice with a comparator function such that the element at `index` after the + /// reordering is at its final sorted position. /// /// This reordering has the additional property that any value at position `i < index` will be /// less than or equal to any value at a position `j > index` using the comparator function. /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at - /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. - /// This function is also known as "kth element" in other libraries. + /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This + /// function is also known as "kth element" in other libraries. /// - /// It returns a triplet of the following from - /// the slice reordered according to the provided comparator function: the subslice prior to - /// `index`, the element at `index`, and the subslice after `index`; accordingly, the values in - /// those two subslices will respectively all be less-than-or-equal-to and greater-than-or-equal-to - /// the value of the element at `index`. + /// It returns a triplet of the following from the slice reordered according to the provided + /// comparator function: the subslice prior to `index`, the element at `index`, and the subslice + /// after `index`; accordingly, the values in those two subslices will respectively all be + /// less-than-or-equal-to and greater-than-or-equal-to the value of the element at `index`. /// /// # Current implementation /// - /// The current algorithm is an introselect implementation based on Pattern Defeating Quicksort, which is also - /// the basis for [`sort_unstable`]. The fallback algorithm is Median of Medians using Tukey's Ninther for - /// pivot selection, which guarantees linear runtime for all inputs. + /// The current algorithm is an introselect implementation based on [ipnsort] by Lukas Bergdoll + /// and Orson Peters, which is also the basis for [`sort_unstable`]. The fallback algorithm is + /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime + /// for all inputs. + /// + /// It is typically faster than 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 /// @@ -3114,6 +3132,8 @@ impl [T] { /// v == [4, 2, 1, -5, -3] || /// v == [4, 2, 1, -3, -5]); /// ``` + /// + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] pub fn select_nth_unstable_by( @@ -3124,29 +3144,32 @@ impl [T] { where F: FnMut(&T, &T) -> Ordering, { - select::partition_at_index(self, index, |a: &T, b: &T| compare(a, b) == Less) + sort::select::partition_at_index(self, index, |a: &T, b: &T| compare(a, b) == Less) } - /// Reorder the slice with a key extraction function such that the element at `index` after the reordering is - /// at its final sorted position. + /// Reorder the slice with a key extraction function such that the element at `index` after the + /// reordering is at its final sorted position. /// /// This reordering has the additional property that any value at position `i < index` will be /// less than or equal to any value at a position `j > index` using the key extraction function. /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at - /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. - /// This function is also known as "kth element" in other libraries. + /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This + /// function is also known as "kth element" in other libraries. /// - /// It returns a triplet of the following from - /// the slice reordered according to the provided key extraction function: the subslice prior to - /// `index`, the element at `index`, and the subslice after `index`; accordingly, the values in - /// those two subslices will respectively all be less-than-or-equal-to and greater-than-or-equal-to - /// the value of the element at `index`. + /// It returns a triplet of the following from the slice reordered according to the provided key + /// extraction function: the subslice prior to `index`, the element at `index`, and the subslice + /// after `index`; accordingly, the values in those two subslices will respectively all be + /// less-than-or-equal-to and greater-than-or-equal-to the value of the element at `index`. /// /// # Current implementation /// - /// The current algorithm is an introselect implementation based on Pattern Defeating Quicksort, which is also - /// the basis for [`sort_unstable`]. The fallback algorithm is Median of Medians using Tukey's Ninther for - /// pivot selection, which guarantees linear runtime for all inputs. + /// The current algorithm is an introselect implementation based on [ipnsort] by Lukas Bergdoll + /// and Orson Peters, which is also the basis for [`sort_unstable`]. The fallback algorithm is + /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime + /// for all inputs. + /// + /// It is typically faster than 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 /// @@ -3174,6 +3197,8 @@ impl [T] { /// v == [2, 1, -3, 4, -5] || /// v == [2, 1, -3, -5, 4]); /// ``` + /// + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] pub fn select_nth_unstable_by_key( @@ -3185,7 +3210,7 @@ impl [T] { F: FnMut(&T) -> K, K: Ord, { - select::partition_at_index(self, index, |a: &T, b: &T| f(a).lt(&f(b))) + sort::select::partition_at_index(self, index, |a: &T, b: &T| f(a).lt(&f(b))) } /// Moves all consecutive repeated elements to the end of the slice according to the diff --git a/library/core/src/slice/sort.rs b/library/core/src/slice/sort.rs deleted file mode 100644 index 993a608f42b6..000000000000 --- a/library/core/src/slice/sort.rs +++ /dev/null @@ -1,1383 +0,0 @@ -//! Slice sorting -//! -//! This module contains a sorting algorithm based on Orson Peters' pattern-defeating quicksort, -//! published at: -//! -//! Unstable sorting is compatible with core because it doesn't allocate memory, unlike our -//! stable sorting implementation. -//! -//! In addition it also contains the core logic of the stable sort used by `slice::sort` based on -//! TimSort. - -use crate::cmp; -use crate::mem::{self, MaybeUninit, SizedTypeProperties}; -use crate::ptr; - -// When dropped, copies from `src` into `dest`. -struct InsertionHole { - src: *const T, - dest: *mut T, -} - -impl Drop for InsertionHole { - fn drop(&mut self) { - // SAFETY: This is a helper class. Please refer to its usage for correctness. Namely, one - // must be sure that `src` and `dst` does not overlap as required by - // `ptr::copy_nonoverlapping` and are both valid for writes. - unsafe { - ptr::copy_nonoverlapping(self.src, self.dest, 1); - } - } -} - -/// Inserts `v[v.len() - 1]` into pre-sorted sequence `v[..v.len() - 1]` so that whole `v[..]` -/// becomes sorted. -unsafe fn insert_tail(v: &mut [T], is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - debug_assert!(v.len() >= 2); - - let arr_ptr = v.as_mut_ptr(); - let i = v.len() - 1; - - // SAFETY: caller must ensure v is at least len 2. - unsafe { - // See insert_head which talks about why this approach is beneficial. - let i_ptr = arr_ptr.add(i); - - // It's important that we use i_ptr here. If this check is positive and we continue, - // We want to make sure that no other copy of the value was seen by is_less. - // Otherwise we would have to copy it back. - if is_less(&*i_ptr, &*i_ptr.sub(1)) { - // It's important, that we use tmp for comparison from now on. As it is the value that - // will be copied back. And notionally we could have created a divergence if we copy - // back the wrong value. - let tmp = mem::ManuallyDrop::new(ptr::read(i_ptr)); - // Intermediate state of the insertion process is always tracked by `hole`, which - // serves two purposes: - // 1. Protects integrity of `v` from panics in `is_less`. - // 2. Fills the remaining hole in `v` in the end. - // - // Panic safety: - // - // If `is_less` panics at any point during the process, `hole` will get dropped and - // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it - // initially held exactly once. - let mut hole = InsertionHole { src: &*tmp, dest: i_ptr.sub(1) }; - ptr::copy_nonoverlapping(hole.dest, i_ptr, 1); - - // SAFETY: We know i is at least 1. - for j in (0..(i - 1)).rev() { - let j_ptr = arr_ptr.add(j); - if !is_less(&*tmp, &*j_ptr) { - break; - } - - ptr::copy_nonoverlapping(j_ptr, hole.dest, 1); - hole.dest = j_ptr; - } - // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`. - } - } -} - -/// Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted. -/// -/// This is the integral subroutine of insertion sort. -unsafe fn insert_head(v: &mut [T], is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - debug_assert!(v.len() >= 2); - - // SAFETY: caller must ensure v is at least len 2. - unsafe { - if is_less(v.get_unchecked(1), v.get_unchecked(0)) { - let arr_ptr = v.as_mut_ptr(); - - // There are three ways to implement insertion here: - // - // 1. Swap adjacent elements until the first one gets to its final destination. - // However, this way we copy data around more than is necessary. If elements are big - // structures (costly to copy), this method will be slow. - // - // 2. Iterate until the right place for the first element is found. Then shift the - // elements succeeding it to make room for it and finally place it into the - // remaining hole. This is a good method. - // - // 3. Copy the first element into a temporary variable. Iterate until the right place - // for it is found. As we go along, copy every traversed element into the slot - // preceding it. Finally, copy data from the temporary variable into the remaining - // hole. This method is very good. Benchmarks demonstrated slightly better - // performance than with the 2nd method. - // - // All methods were benchmarked, and the 3rd showed best results. So we chose that one. - let tmp = mem::ManuallyDrop::new(ptr::read(arr_ptr)); - - // Intermediate state of the insertion process is always tracked by `hole`, which - // serves two purposes: - // 1. Protects integrity of `v` from panics in `is_less`. - // 2. Fills the remaining hole in `v` in the end. - // - // Panic safety: - // - // If `is_less` panics at any point during the process, `hole` will get dropped and - // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it - // initially held exactly once. - let mut hole = InsertionHole { src: &*tmp, dest: arr_ptr.add(1) }; - ptr::copy_nonoverlapping(arr_ptr.add(1), arr_ptr.add(0), 1); - - for i in 2..v.len() { - if !is_less(&v.get_unchecked(i), &*tmp) { - break; - } - ptr::copy_nonoverlapping(arr_ptr.add(i), arr_ptr.add(i - 1), 1); - hole.dest = arr_ptr.add(i); - } - // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`. - } - } -} - -/// Sort `v` assuming `v[..offset]` is already sorted. -/// -/// Never inline this function to avoid code bloat. It still optimizes nicely and has practically no -/// performance impact. Even improving performance in some cases. -#[inline(never)] -pub(super) fn insertion_sort_shift_left(v: &mut [T], offset: usize, is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - let len = v.len(); - - // Using assert here improves performance. - assert!(offset != 0 && offset <= len); - - // Shift each element of the unsorted region v[i..] as far left as is needed to make v sorted. - for i in offset..len { - // SAFETY: we tested that `offset` must be at least 1, so this loop is only entered if len - // >= 2. The range is exclusive and we know `i` must be at least 1 so this slice has at - // >least len 2. - unsafe { - insert_tail(&mut v[..=i], is_less); - } - } -} - -/// Sort `v` assuming `v[offset..]` is already sorted. -/// -/// Never inline this function to avoid code bloat. It still optimizes nicely and has practically no -/// performance impact. Even improving performance in some cases. -#[inline(never)] -fn insertion_sort_shift_right(v: &mut [T], offset: usize, is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - let len = v.len(); - - // Using assert here improves performance. - assert!(offset != 0 && offset <= len && len >= 2); - - // Shift each element of the unsorted region v[..i] as far left as is needed to make v sorted. - for i in (0..offset).rev() { - // SAFETY: we tested that `offset` must be at least 1, so this loop is only entered if len - // >= 2.We ensured that the slice length is always at least 2 long. We know that start_found - // will be at least one less than end, and the range is exclusive. Which gives us i always - // <= (end - 2). - unsafe { - insert_head(&mut v[i..len], is_less); - } - } -} - -/// Partially sorts a slice by shifting several out-of-order elements around. -/// -/// Returns `true` if the slice is sorted at the end. This function is *O*(*n*) worst-case. -#[cold] -fn partial_insertion_sort(v: &mut [T], is_less: &mut F) -> bool -where - F: FnMut(&T, &T) -> bool, -{ - // Maximum number of adjacent out-of-order pairs that will get shifted. - const MAX_STEPS: usize = 5; - // If the slice is shorter than this, don't shift any elements. - const SHORTEST_SHIFTING: usize = 50; - - let len = v.len(); - let mut i = 1; - - for _ in 0..MAX_STEPS { - // SAFETY: We already explicitly did the bound checking with `i < len`. - // All our subsequent indexing is only in the range `0 <= index < len` - unsafe { - // Find the next pair of adjacent out-of-order elements. - while i < len && !is_less(v.get_unchecked(i), v.get_unchecked(i - 1)) { - i += 1; - } - } - - // Are we done? - if i == len { - return true; - } - - // Don't shift elements on short arrays, that has a performance cost. - if len < SHORTEST_SHIFTING { - return false; - } - - // Swap the found pair of elements. This puts them in correct order. - v.swap(i - 1, i); - - if i >= 2 { - // Shift the smaller element to the left. - insertion_sort_shift_left(&mut v[..i], i - 1, is_less); - - // Shift the greater element to the right. - insertion_sort_shift_right(&mut v[..i], 1, is_less); - } - } - - // Didn't manage to sort the slice in the limited number of steps. - false -} - -/// Sorts `v` using heapsort, which guarantees *O*(*n* \* log(*n*)) worst-case. -#[cold] -#[unstable(feature = "sort_internals", reason = "internal to sort module", issue = "none")] -pub fn heapsort(v: &mut [T], mut is_less: F) -where - F: FnMut(&T, &T) -> bool, -{ - // This binary heap respects the invariant `parent >= child`. - let mut sift_down = |v: &mut [T], mut node| { - loop { - // Children of `node`. - let mut child = 2 * node + 1; - if child >= v.len() { - break; - } - - // Choose the greater child. - if child + 1 < v.len() { - // We need a branch to be sure not to out-of-bounds index, - // but it's highly predictable. The comparison, however, - // is better done branchless, especially for primitives. - child += is_less(&v[child], &v[child + 1]) as usize; - } - - // Stop if the invariant holds at `node`. - if !is_less(&v[node], &v[child]) { - break; - } - - // Swap `node` with the greater child, move one step down, and continue sifting. - v.swap(node, child); - node = child; - } - }; - - // Build the heap in linear time. - for i in (0..v.len() / 2).rev() { - sift_down(v, i); - } - - // Pop maximal elements from the heap. - for i in (1..v.len()).rev() { - v.swap(0, i); - sift_down(&mut v[..i], 0); - } -} - -/// Partitions `v` into elements smaller than `pivot`, followed by elements greater than or equal -/// to `pivot`. -/// -/// Returns the number of elements smaller than `pivot`. -/// -/// Partitioning is performed block-by-block in order to minimize the cost of branching operations. -/// This idea is presented in the [BlockQuicksort][pdf] paper. -/// -/// [pdf]: https://drops.dagstuhl.de/opus/volltexte/2016/6389/pdf/LIPIcs-ESA-2016-38.pdf -fn partition_in_blocks(v: &mut [T], pivot: &T, is_less: &mut F) -> usize -where - F: FnMut(&T, &T) -> bool, -{ - // Number of elements in a typical block. - const BLOCK: usize = 128; - - // The partitioning algorithm repeats the following steps until completion: - // - // 1. Trace a block from the left side to identify elements greater than or equal to the pivot. - // 2. Trace a block from the right side to identify elements smaller than the pivot. - // 3. Exchange the identified elements between the left and right side. - // - // We keep the following variables for a block of elements: - // - // 1. `block` - Number of elements in the block. - // 2. `start` - Start pointer into the `offsets` array. - // 3. `end` - End pointer into the `offsets` array. - // 4. `offsets` - Indices of out-of-order elements within the block. - - // The current block on the left side (from `l` to `l.add(block_l)`). - let mut l = v.as_mut_ptr(); - let mut block_l = BLOCK; - let mut start_l = ptr::null_mut(); - let mut end_l = ptr::null_mut(); - let mut offsets_l = [MaybeUninit::::uninit(); BLOCK]; - - // The current block on the right side (from `r.sub(block_r)` to `r`). - // SAFETY: The documentation for .add() specifically mention that `vec.as_ptr().add(vec.len())` is always safe - let mut r = unsafe { l.add(v.len()) }; - let mut block_r = BLOCK; - let mut start_r = ptr::null_mut(); - let mut end_r = ptr::null_mut(); - let mut offsets_r = [MaybeUninit::::uninit(); BLOCK]; - - // FIXME: When we get VLAs, try creating one array of length `min(v.len(), 2 * BLOCK)` rather - // than two fixed-size arrays of length `BLOCK`. VLAs might be more cache-efficient. - - // Returns the number of elements between pointers `l` (inclusive) and `r` (exclusive). - fn width(l: *mut T, r: *mut T) -> usize { - assert!(mem::size_of::() > 0); - // FIXME: this should *likely* use `offset_from`, but more - // investigation is needed (including running tests in miri). - (r.addr() - l.addr()) / mem::size_of::() - } - - loop { - // We are done with partitioning block-by-block when `l` and `r` get very close. Then we do - // some patch-up work in order to partition the remaining elements in between. - let is_done = width(l, r) <= 2 * BLOCK; - - if is_done { - // Number of remaining elements (still not compared to the pivot). - let mut rem = width(l, r); - if start_l < end_l || start_r < end_r { - rem -= BLOCK; - } - - // Adjust block sizes so that the left and right block don't overlap, but get perfectly - // aligned to cover the whole remaining gap. - if start_l < end_l { - block_r = rem; - } else if start_r < end_r { - block_l = rem; - } else { - // There were the same number of elements to switch on both blocks during the last - // iteration, so there are no remaining elements on either block. Cover the remaining - // items with roughly equally-sized blocks. - block_l = rem / 2; - block_r = rem - block_l; - } - debug_assert!(block_l <= BLOCK && block_r <= BLOCK); - debug_assert!(width(l, r) == block_l + block_r); - } - - if start_l == end_l { - // Trace `block_l` elements from the left side. - start_l = MaybeUninit::slice_as_mut_ptr(&mut offsets_l); - end_l = start_l; - let mut elem = l; - - for i in 0..block_l { - // SAFETY: The unsafety operations below involve the usage of the `offset`. - // According to the conditions required by the function, we satisfy them because: - // 1. `offsets_l` is stack-allocated, and thus considered separate allocated object. - // 2. The function `is_less` returns a `bool`. - // Casting a `bool` will never overflow `isize`. - // 3. We have guaranteed that `block_l` will be `<= BLOCK`. - // Plus, `end_l` was initially set to the begin pointer of `offsets_` which was declared on the stack. - // Thus, we know that even in the worst case (all invocations of `is_less` returns false) we will only be at most 1 byte pass the end. - // Another unsafety operation here is dereferencing `elem`. - // However, `elem` was initially the begin pointer to the slice which is always valid. - unsafe { - // Branchless comparison. - *end_l = i as u8; - end_l = end_l.add(!is_less(&*elem, pivot) as usize); - elem = elem.add(1); - } - } - } - - if start_r == end_r { - // Trace `block_r` elements from the right side. - start_r = MaybeUninit::slice_as_mut_ptr(&mut offsets_r); - end_r = start_r; - let mut elem = r; - - for i in 0..block_r { - // SAFETY: The unsafety operations below involve the usage of the `offset`. - // According to the conditions required by the function, we satisfy them because: - // 1. `offsets_r` is stack-allocated, and thus considered separate allocated object. - // 2. The function `is_less` returns a `bool`. - // Casting a `bool` will never overflow `isize`. - // 3. We have guaranteed that `block_r` will be `<= BLOCK`. - // Plus, `end_r` was initially set to the begin pointer of `offsets_` which was declared on the stack. - // Thus, we know that even in the worst case (all invocations of `is_less` returns true) we will only be at most 1 byte pass the end. - // Another unsafety operation here is dereferencing `elem`. - // However, `elem` was initially `1 * sizeof(T)` past the end and we decrement it by `1 * sizeof(T)` before accessing it. - // Plus, `block_r` was asserted to be less than `BLOCK` and `elem` will therefore at most be pointing to the beginning of the slice. - unsafe { - // Branchless comparison. - elem = elem.sub(1); - *end_r = i as u8; - end_r = end_r.add(is_less(&*elem, pivot) as usize); - } - } - } - - // Number of out-of-order elements to swap between the left and right side. - let count = cmp::min(width(start_l, end_l), width(start_r, end_r)); - - if count > 0 { - macro_rules! left { - () => { - l.add(usize::from(*start_l)) - }; - } - macro_rules! right { - () => { - r.sub(usize::from(*start_r) + 1) - }; - } - - // Instead of swapping one pair at the time, it is more efficient to perform a cyclic - // permutation. This is not strictly equivalent to swapping, but produces a similar - // result using fewer memory operations. - - // SAFETY: The use of `ptr::read` is valid because there is at least one element in - // both `offsets_l` and `offsets_r`, so `left!` is a valid pointer to read from. - // - // The uses of `left!` involve calls to `offset` on `l`, which points to the - // beginning of `v`. All the offsets pointed-to by `start_l` are at most `block_l`, so - // these `offset` calls are safe as all reads are within the block. The same argument - // applies for the uses of `right!`. - // - // The calls to `start_l.offset` are valid because there are at most `count-1` of them, - // plus the final one at the end of the unsafe block, where `count` is the minimum number - // of collected offsets in `offsets_l` and `offsets_r`, so there is no risk of there not - // being enough elements. The same reasoning applies to the calls to `start_r.offset`. - // - // The calls to `copy_nonoverlapping` are safe because `left!` and `right!` are guaranteed - // not to overlap, and are valid because of the reasoning above. - unsafe { - let tmp = ptr::read(left!()); - ptr::copy_nonoverlapping(right!(), left!(), 1); - - for _ in 1..count { - start_l = start_l.add(1); - ptr::copy_nonoverlapping(left!(), right!(), 1); - start_r = start_r.add(1); - ptr::copy_nonoverlapping(right!(), left!(), 1); - } - - ptr::copy_nonoverlapping(&tmp, right!(), 1); - mem::forget(tmp); - start_l = start_l.add(1); - start_r = start_r.add(1); - } - } - - if start_l == end_l { - // All out-of-order elements in the left block were moved. Move to the next block. - - // block-width-guarantee - // SAFETY: if `!is_done` then the slice width is guaranteed to be at least `2*BLOCK` wide. There - // are at most `BLOCK` elements in `offsets_l` because of its size, so the `offset` operation is - // safe. Otherwise, the debug assertions in the `is_done` case guarantee that - // `width(l, r) == block_l + block_r`, namely, that the block sizes have been adjusted to account - // for the smaller number of remaining elements. - l = unsafe { l.add(block_l) }; - } - - if start_r == end_r { - // All out-of-order elements in the right block were moved. Move to the previous block. - - // SAFETY: Same argument as [block-width-guarantee]. Either this is a full block `2*BLOCK`-wide, - // or `block_r` has been adjusted for the last handful of elements. - r = unsafe { r.sub(block_r) }; - } - - if is_done { - break; - } - } - - // All that remains now is at most one block (either the left or the right) with out-of-order - // elements that need to be moved. Such remaining elements can be simply shifted to the end - // within their block. - - if start_l < end_l { - // The left block remains. - // Move its remaining out-of-order elements to the far right. - debug_assert_eq!(width(l, r), block_l); - while start_l < end_l { - // remaining-elements-safety - // SAFETY: while the loop condition holds there are still elements in `offsets_l`, so it - // is safe to point `end_l` to the previous element. - // - // The `ptr::swap` is safe if both its arguments are valid for reads and writes: - // - Per the debug assert above, the distance between `l` and `r` is `block_l` - // elements, so there can be at most `block_l` remaining offsets between `start_l` - // and `end_l`. This means `r` will be moved at most `block_l` steps back, which - // makes the `r.offset` calls valid (at that point `l == r`). - // - `offsets_l` contains valid offsets into `v` collected during the partitioning of - // the last block, so the `l.offset` calls are valid. - unsafe { - end_l = end_l.sub(1); - ptr::swap(l.add(usize::from(*end_l)), r.sub(1)); - r = r.sub(1); - } - } - width(v.as_mut_ptr(), r) - } else if start_r < end_r { - // The right block remains. - // Move its remaining out-of-order elements to the far left. - debug_assert_eq!(width(l, r), block_r); - while start_r < end_r { - // SAFETY: See the reasoning in [remaining-elements-safety]. - unsafe { - end_r = end_r.sub(1); - ptr::swap(l, r.sub(usize::from(*end_r) + 1)); - l = l.add(1); - } - } - width(v.as_mut_ptr(), l) - } else { - // Nothing else to do, we're done. - width(v.as_mut_ptr(), l) - } -} - -/// Partitions `v` into elements smaller than `v[pivot]`, followed by elements greater than or -/// equal to `v[pivot]`. -/// -/// Returns a tuple of: -/// -/// 1. Number of elements smaller than `v[pivot]`. -/// 2. True if `v` was already partitioned. -pub(super) fn partition(v: &mut [T], pivot: usize, is_less: &mut F) -> (usize, bool) -where - F: FnMut(&T, &T) -> bool, -{ - let (mid, was_partitioned) = { - // Place the pivot at the beginning of slice. - v.swap(0, pivot); - let (pivot, v) = v.split_at_mut(1); - let pivot = &mut pivot[0]; - - // Read the pivot into a stack-allocated variable for efficiency. If a following comparison - // operation panics, the pivot will be automatically written back into the slice. - - // SAFETY: `pivot` is a reference to the first element of `v`, so `ptr::read` is safe. - let tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) }); - let _pivot_guard = InsertionHole { src: &*tmp, dest: pivot }; - let pivot = &*tmp; - - // Find the first pair of out-of-order elements. - let mut l = 0; - let mut r = v.len(); - - // SAFETY: The unsafety below involves indexing an array. - // For the first one: We already do the bounds checking here with `l < r`. - // For the second one: We initially have `l == 0` and `r == v.len()` and we checked that `l < r` at every indexing operation. - // From here we know that `r` must be at least `r == l` which was shown to be valid from the first one. - unsafe { - // Find the first element greater than or equal to the pivot. - while l < r && is_less(v.get_unchecked(l), pivot) { - l += 1; - } - - // Find the last element smaller that the pivot. - while l < r && !is_less(v.get_unchecked(r - 1), pivot) { - r -= 1; - } - } - - (l + partition_in_blocks(&mut v[l..r], pivot, is_less), l >= r) - - // `_pivot_guard` goes out of scope and writes the pivot (which is a stack-allocated - // variable) back into the slice where it originally was. This step is critical in ensuring - // safety! - }; - - // Place the pivot between the two partitions. - v.swap(0, mid); - - (mid, was_partitioned) -} - -/// Partitions `v` into elements equal to `v[pivot]` followed by elements greater than `v[pivot]`. -/// -/// Returns the number of elements equal to the pivot. It is assumed that `v` does not contain -/// elements smaller than the pivot. -pub(super) fn partition_equal(v: &mut [T], pivot: usize, is_less: &mut F) -> usize -where - F: FnMut(&T, &T) -> bool, -{ - // Place the pivot at the beginning of slice. - v.swap(0, pivot); - let (pivot, v) = v.split_at_mut(1); - let pivot = &mut pivot[0]; - - // Read the pivot into a stack-allocated variable for efficiency. If a following comparison - // operation panics, the pivot will be automatically written back into the slice. - // SAFETY: The pointer here is valid because it is obtained from a reference to a slice. - let tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) }); - let _pivot_guard = InsertionHole { src: &*tmp, dest: pivot }; - let pivot = &*tmp; - - let len = v.len(); - if len == 0 { - return 0; - } - - // Now partition the slice. - let mut l = 0; - let mut r = len; - loop { - // SAFETY: The unsafety below involves indexing an array. - // For the first one: We already do the bounds checking here with `l < r`. - // For the second one: We initially have `l == 0` and `r == v.len()` and we checked that `l < r` at every indexing operation. - // From here we know that `r` must be at least `r == l` which was shown to be valid from the first one. - unsafe { - // Find the first element greater than the pivot. - while l < r && !is_less(pivot, v.get_unchecked(l)) { - l += 1; - } - - // Find the last element equal to the pivot. - loop { - r -= 1; - if l >= r || !is_less(pivot, v.get_unchecked(r)) { - break; - } - } - - // Are we done? - if l >= r { - break; - } - - // Swap the found pair of out-of-order elements. - let ptr = v.as_mut_ptr(); - ptr::swap(ptr.add(l), ptr.add(r)); - l += 1; - } - } - - // We found `l` elements equal to the pivot. Add 1 to account for the pivot itself. - l + 1 - - // `_pivot_guard` goes out of scope and writes the pivot (which is a stack-allocated variable) - // back into the slice where it originally was. This step is critical in ensuring safety! -} - -/// Scatters some elements around in an attempt to break patterns that might cause imbalanced -/// partitions in quicksort. -#[cold] -pub(super) fn break_patterns(v: &mut [T]) { - let len = v.len(); - if len >= 8 { - let mut seed = len; - let mut gen_usize = || { - // Pseudorandom number generator from the "Xorshift RNGs" paper by George Marsaglia. - if usize::BITS <= 32 { - let mut r = seed as u32; - r ^= r << 13; - r ^= r >> 17; - r ^= r << 5; - seed = r as usize; - seed - } else { - let mut r = seed as u64; - r ^= r << 13; - r ^= r >> 7; - r ^= r << 17; - seed = r as usize; - seed - } - }; - - // Take random numbers modulo this number. - // The number fits into `usize` because `len` is not greater than `isize::MAX`. - let modulus = len.next_power_of_two(); - - // Some pivot candidates will be in the nearby of this index. Let's randomize them. - let pos = len / 4 * 2; - - for i in 0..3 { - // Generate a random number modulo `len`. However, in order to avoid costly operations - // we first take it modulo a power of two, and then decrease by `len` until it fits - // into the range `[0, len - 1]`. - let mut other = gen_usize() & (modulus - 1); - - // `other` is guaranteed to be less than `2 * len`. - if other >= len { - other -= len; - } - - v.swap(pos - 1 + i, other); - } - } -} - -/// Chooses a pivot in `v` and returns the index and `true` if the slice is likely already sorted. -/// -/// Elements in `v` might be reordered in the process. -pub(super) fn choose_pivot(v: &mut [T], is_less: &mut F) -> (usize, bool) -where - F: FnMut(&T, &T) -> bool, -{ - // Minimum length to choose the median-of-medians method. - // Shorter slices use the simple median-of-three method. - const SHORTEST_MEDIAN_OF_MEDIANS: usize = 50; - // Maximum number of swaps that can be performed in this function. - const MAX_SWAPS: usize = 4 * 3; - - let len = v.len(); - - // Three indices near which we are going to choose a pivot. - let mut a = len / 4 * 1; - let mut b = len / 4 * 2; - let mut c = len / 4 * 3; - - // Counts the total number of swaps we are about to perform while sorting indices. - let mut swaps = 0; - - if len >= 8 { - // Swaps indices so that `v[a] <= v[b]`. - // SAFETY: `len >= 8` so there are at least two elements in the neighborhoods of - // `a`, `b` and `c`. This means the three calls to `sort_adjacent` result in - // corresponding calls to `sort3` with valid 3-item neighborhoods around each - // pointer, which in turn means the calls to `sort2` are done with valid - // references. Thus the `v.get_unchecked` calls are safe, as is the `ptr::swap` - // call. - let mut sort2 = |a: &mut usize, b: &mut usize| unsafe { - if is_less(v.get_unchecked(*b), v.get_unchecked(*a)) { - ptr::swap(a, b); - swaps += 1; - } - }; - - // Swaps indices so that `v[a] <= v[b] <= v[c]`. - let mut sort3 = |a: &mut usize, b: &mut usize, c: &mut usize| { - sort2(a, b); - sort2(b, c); - sort2(a, b); - }; - - if len >= SHORTEST_MEDIAN_OF_MEDIANS { - // Finds the median of `v[a - 1], v[a], v[a + 1]` and stores the index into `a`. - let mut sort_adjacent = |a: &mut usize| { - let tmp = *a; - sort3(&mut (tmp - 1), a, &mut (tmp + 1)); - }; - - // Find medians in the neighborhoods of `a`, `b`, and `c`. - sort_adjacent(&mut a); - sort_adjacent(&mut b); - sort_adjacent(&mut c); - } - - // Find the median among `a`, `b`, and `c`. - sort3(&mut a, &mut b, &mut c); - } - - if swaps < MAX_SWAPS { - (b, swaps == 0) - } else { - // The maximum number of swaps was performed. Chances are the slice is descending or mostly - // descending, so reversing will probably help sort it faster. - v.reverse(); - (len - 1 - b, true) - } -} - -/// Sorts `v` recursively. -/// -/// If the slice had a predecessor in the original array, it is specified as `pred`. -/// -/// `limit` is the number of allowed imbalanced partitions before switching to `heapsort`. If zero, -/// this function will immediately switch to heapsort. -fn recurse<'a, T, F>(mut v: &'a mut [T], is_less: &mut F, mut pred: Option<&'a T>, mut limit: u32) -where - F: FnMut(&T, &T) -> bool, -{ - // Slices of up to this length get sorted using insertion sort. - const MAX_INSERTION: usize = 20; - - // True if the last partitioning was reasonably balanced. - let mut was_balanced = true; - // True if the last partitioning didn't shuffle elements (the slice was already partitioned). - let mut was_partitioned = true; - - loop { - let len = v.len(); - - // Very short slices get sorted using insertion sort. - if len <= MAX_INSERTION { - if len >= 2 { - insertion_sort_shift_left(v, 1, is_less); - } - return; - } - - // If too many bad pivot choices were made, simply fall back to heapsort in order to - // guarantee `O(n * log(n))` worst-case. - if limit == 0 { - heapsort(v, is_less); - return; - } - - // If the last partitioning was imbalanced, try breaking patterns in the slice by shuffling - // some elements around. Hopefully we'll choose a better pivot this time. - if !was_balanced { - break_patterns(v); - limit -= 1; - } - - // Choose a pivot and try guessing whether the slice is already sorted. - let (pivot, likely_sorted) = choose_pivot(v, is_less); - - // If the last partitioning was decently balanced and didn't shuffle elements, and if pivot - // selection predicts the slice is likely already sorted... - if was_balanced && was_partitioned && likely_sorted { - // Try identifying several out-of-order elements and shifting them to correct - // positions. If the slice ends up being completely sorted, we're done. - if partial_insertion_sort(v, is_less) { - return; - } - } - - // If the chosen pivot is equal to the predecessor, then it's the smallest element in the - // slice. Partition the slice into elements equal to and elements greater than the pivot. - // This case is usually hit when the slice contains many duplicate elements. - if let Some(p) = pred { - if !is_less(p, &v[pivot]) { - let mid = partition_equal(v, pivot, is_less); - - // Continue sorting elements greater than the pivot. - v = &mut v[mid..]; - continue; - } - } - - // Partition the slice. - let (mid, was_p) = partition(v, pivot, is_less); - was_balanced = cmp::min(mid, len - mid) >= len / 8; - was_partitioned = was_p; - - // Split the slice into `left`, `pivot`, and `right`. - let (left, right) = v.split_at_mut(mid); - let (pivot, right) = right.split_at_mut(1); - let pivot = &pivot[0]; - - // Recurse into the shorter side only in order to minimize the total number of recursive - // calls and consume less stack space. Then just continue with the longer side (this is - // akin to tail recursion). - if left.len() < right.len() { - recurse(left, is_less, pred, limit); - v = right; - pred = Some(pivot); - } else { - recurse(right, is_less, Some(pivot), limit); - v = left; - } - } -} - -/// Sorts `v` using pattern-defeating quicksort, which is *O*(*n* \* log(*n*)) worst-case. -pub fn quicksort(v: &mut [T], mut is_less: F) -where - F: FnMut(&T, &T) -> bool, -{ - // Sorting has no meaningful behavior on zero-sized types. - if T::IS_ZST { - return; - } - - // Limit the number of imbalanced partitions to `floor(log2(len)) + 1`. - let limit = usize::BITS - v.len().leading_zeros(); - - recurse(v, &mut is_less, None, limit); -} - -/// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and -/// stores the result into `v[..]`. -/// -/// # Safety -/// -/// The two slices must be non-empty and `mid` must be in bounds. Buffer `buf` must be long enough -/// to hold a copy of the shorter slice. Also, `T` must not be a zero-sized type. -unsafe fn merge(v: &mut [T], mid: usize, buf: *mut T, is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - let len = v.len(); - let v = v.as_mut_ptr(); - - // SAFETY: mid and len must be in-bounds of v. - let (v_mid, v_end) = unsafe { (v.add(mid), v.add(len)) }; - - // The merge process first copies the shorter run into `buf`. Then it traces the newly copied - // run and the longer run forwards (or backwards), comparing their next unconsumed elements and - // copying the lesser (or greater) one into `v`. - // - // As soon as the shorter run is fully consumed, the process is done. If the longer run gets - // consumed first, then we must copy whatever is left of the shorter run into the remaining - // hole in `v`. - // - // Intermediate state of the process is always tracked by `hole`, which serves two purposes: - // 1. Protects integrity of `v` from panics in `is_less`. - // 2. Fills the remaining hole in `v` if the longer run gets consumed first. - // - // Panic safety: - // - // If `is_less` panics at any point during the process, `hole` will get dropped and fill the - // hole in `v` with the unconsumed range in `buf`, thus ensuring that `v` still holds every - // object it initially held exactly once. - let mut hole; - - if mid <= len - mid { - // The left run is shorter. - - // SAFETY: buf must have enough capacity for `v[..mid]`. - unsafe { - ptr::copy_nonoverlapping(v, buf, mid); - hole = MergeHole { start: buf, end: buf.add(mid), dest: v }; - } - - // Initially, these pointers point to the beginnings of their arrays. - let left = &mut hole.start; - let mut right = v_mid; - let out = &mut hole.dest; - - while *left < hole.end && right < v_end { - // Consume the lesser side. - // If equal, prefer the left run to maintain stability. - - // SAFETY: left and right must be valid and part of v same for out. - unsafe { - let is_l = is_less(&*right, &**left); - let to_copy = if is_l { right } else { *left }; - ptr::copy_nonoverlapping(to_copy, *out, 1); - *out = out.add(1); - right = right.add(is_l as usize); - *left = left.add(!is_l as usize); - } - } - } else { - // The right run is shorter. - - // SAFETY: buf must have enough capacity for `v[mid..]`. - unsafe { - ptr::copy_nonoverlapping(v_mid, buf, len - mid); - hole = MergeHole { start: buf, end: buf.add(len - mid), dest: v_mid }; - } - - // Initially, these pointers point past the ends of their arrays. - let left = &mut hole.dest; - let right = &mut hole.end; - let mut out = v_end; - - while v < *left && buf < *right { - // Consume the greater side. - // If equal, prefer the right run to maintain stability. - - // SAFETY: left and right must be valid and part of v same for out. - unsafe { - let is_l = is_less(&*right.sub(1), &*left.sub(1)); - *left = left.sub(is_l as usize); - *right = right.sub(!is_l as usize); - let to_copy = if is_l { *left } else { *right }; - out = out.sub(1); - ptr::copy_nonoverlapping(to_copy, out, 1); - } - } - } - // Finally, `hole` gets dropped. If the shorter run was not fully consumed, whatever remains of - // it will now be copied into the hole in `v`. - - // When dropped, copies the range `start..end` into `dest..`. - struct MergeHole { - start: *mut T, - end: *mut T, - dest: *mut T, - } - - impl Drop for MergeHole { - fn drop(&mut self) { - // SAFETY: `T` is not a zero-sized type, and these are pointers into a slice's elements. - unsafe { - let len = self.end.sub_ptr(self.start); - ptr::copy_nonoverlapping(self.start, self.dest, len); - } - } - } -} - -/// This merge sort borrows some (but not all) ideas from TimSort, which used to be described in -/// detail [here](https://github.com/python/cpython/blob/main/Objects/listsort.txt). However Python -/// has switched to a Powersort based implementation. -/// -/// The algorithm identifies strictly descending and non-descending subsequences, which are called -/// natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed -/// onto the stack, and then some pairs of adjacent runs are merged until these two invariants are -/// satisfied: -/// -/// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len` -/// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len` -/// -/// The invariants ensure that the total running time is *O*(*n* \* log(*n*)) worst-case. -pub fn merge_sort( - v: &mut [T], - is_less: &mut CmpF, - elem_alloc_fn: ElemAllocF, - elem_dealloc_fn: ElemDeallocF, - run_alloc_fn: RunAllocF, - run_dealloc_fn: RunDeallocF, -) where - CmpF: FnMut(&T, &T) -> bool, - ElemAllocF: Fn(usize) -> *mut T, - ElemDeallocF: Fn(*mut T, usize), - RunAllocF: Fn(usize) -> *mut TimSortRun, - RunDeallocF: Fn(*mut TimSortRun, usize), -{ - // Slices of up to this length get sorted using insertion sort. - const MAX_INSERTION: usize = 20; - - // The caller should have already checked that. - debug_assert!(!T::IS_ZST); - - let len = v.len(); - - // Short arrays get sorted in-place via insertion sort to avoid allocations. - if len <= MAX_INSERTION { - if len >= 2 { - insertion_sort_shift_left(v, 1, is_less); - } - return; - } - - // Allocate a buffer to use as scratch memory. We keep the length 0 so we can keep in it - // shallow copies of the contents of `v` without risking the dtors running on copies if - // `is_less` panics. When merging two sorted runs, this buffer holds a copy of the shorter run, - // which will always have length at most `len / 2`. - let buf = BufGuard::new(len / 2, elem_alloc_fn, elem_dealloc_fn); - let buf_ptr = buf.buf_ptr.as_ptr(); - - let mut runs = RunVec::new(run_alloc_fn, run_dealloc_fn); - - let mut end = 0; - let mut start = 0; - - // Scan forward. Memory pre-fetching prefers forward scanning vs backwards scanning, and the - // code-gen is usually better. For the most sensitive types such as integers, these are merged - // bidirectionally at once. So there is no benefit in scanning backwards. - while end < len { - let (streak_end, was_reversed) = find_streak(&v[start..], is_less); - end += streak_end; - if was_reversed { - v[start..end].reverse(); - } - - // Insert some more elements into the run if it's too short. Insertion sort is faster than - // merge sort on short sequences, so this significantly improves performance. - end = provide_sorted_batch(v, start, end, is_less); - - // Push this run onto the stack. - runs.push(TimSortRun { start, len: end - start }); - start = end; - - // Merge some pairs of adjacent runs to satisfy the invariants. - while let Some(r) = collapse(runs.as_slice(), len) { - let left = runs[r]; - let right = runs[r + 1]; - let merge_slice = &mut v[left.start..right.start + right.len]; - // SAFETY: `buf_ptr` must hold enough capacity for the shorter of the two sides, and - // neither side may be on length 0. - unsafe { - merge(merge_slice, left.len, buf_ptr, is_less); - } - runs[r + 1] = TimSortRun { start: left.start, len: left.len + right.len }; - runs.remove(r); - } - } - - // Finally, exactly one run must remain in the stack. - debug_assert!(runs.len() == 1 && runs[0].start == 0 && runs[0].len == len); - - // Examines the stack of runs and identifies the next pair of runs to merge. More specifically, - // if `Some(r)` is returned, that means `runs[r]` and `runs[r + 1]` must be merged next. If the - // algorithm should continue building a new run instead, `None` is returned. - // - // TimSort is infamous for its buggy implementations, as described here: - // http://envisage-project.eu/timsort-specification-and-verification/ - // - // The gist of the story is: we must enforce the invariants on the top four runs on the stack. - // Enforcing them on just top three is not sufficient to ensure that the invariants will still - // hold for *all* runs in the stack. - // - // This function correctly checks invariants for the top four runs. Additionally, if the top - // run starts at index 0, it will always demand a merge operation until the stack is fully - // collapsed, in order to complete the sort. - #[inline] - fn collapse(runs: &[TimSortRun], stop: usize) -> Option { - let n = runs.len(); - if n >= 2 - && (runs[n - 1].start + runs[n - 1].len == stop - || runs[n - 2].len <= runs[n - 1].len - || (n >= 3 && runs[n - 3].len <= runs[n - 2].len + runs[n - 1].len) - || (n >= 4 && runs[n - 4].len <= runs[n - 3].len + runs[n - 2].len)) - { - if n >= 3 && runs[n - 3].len < runs[n - 1].len { Some(n - 3) } else { Some(n - 2) } - } else { - None - } - } - - // Extremely basic versions of Vec. - // Their use is super limited and by having the code here, it allows reuse between the sort - // implementations. - struct BufGuard - where - ElemDeallocF: Fn(*mut T, usize), - { - buf_ptr: ptr::NonNull, - capacity: usize, - elem_dealloc_fn: ElemDeallocF, - } - - impl BufGuard - where - ElemDeallocF: Fn(*mut T, usize), - { - fn new( - len: usize, - elem_alloc_fn: ElemAllocF, - elem_dealloc_fn: ElemDeallocF, - ) -> Self - where - ElemAllocF: Fn(usize) -> *mut T, - { - Self { - buf_ptr: ptr::NonNull::new(elem_alloc_fn(len)).unwrap(), - capacity: len, - elem_dealloc_fn, - } - } - } - - impl Drop for BufGuard - where - ElemDeallocF: Fn(*mut T, usize), - { - fn drop(&mut self) { - (self.elem_dealloc_fn)(self.buf_ptr.as_ptr(), self.capacity); - } - } - - struct RunVec - where - RunAllocF: Fn(usize) -> *mut TimSortRun, - RunDeallocF: Fn(*mut TimSortRun, usize), - { - buf_ptr: ptr::NonNull, - capacity: usize, - len: usize, - run_alloc_fn: RunAllocF, - run_dealloc_fn: RunDeallocF, - } - - impl RunVec - where - RunAllocF: Fn(usize) -> *mut TimSortRun, - RunDeallocF: Fn(*mut TimSortRun, usize), - { - fn new(run_alloc_fn: RunAllocF, run_dealloc_fn: RunDeallocF) -> Self { - // Most slices can be sorted with at most 16 runs in-flight. - const START_RUN_CAPACITY: usize = 16; - - Self { - buf_ptr: ptr::NonNull::new(run_alloc_fn(START_RUN_CAPACITY)).unwrap(), - capacity: START_RUN_CAPACITY, - len: 0, - run_alloc_fn, - run_dealloc_fn, - } - } - - fn push(&mut self, val: TimSortRun) { - if self.len == self.capacity { - let old_capacity = self.capacity; - let old_buf_ptr = self.buf_ptr.as_ptr(); - - self.capacity = self.capacity * 2; - self.buf_ptr = ptr::NonNull::new((self.run_alloc_fn)(self.capacity)).unwrap(); - - // SAFETY: buf_ptr new and old were correctly allocated and old_buf_ptr has - // old_capacity valid elements. - unsafe { - ptr::copy_nonoverlapping(old_buf_ptr, self.buf_ptr.as_ptr(), old_capacity); - } - - (self.run_dealloc_fn)(old_buf_ptr, old_capacity); - } - - // SAFETY: The invariant was just checked. - unsafe { - self.buf_ptr.as_ptr().add(self.len).write(val); - } - self.len += 1; - } - - fn remove(&mut self, index: usize) { - if index >= self.len { - panic!("Index out of bounds"); - } - - // SAFETY: buf_ptr needs to be valid and len invariant upheld. - unsafe { - // the place we are taking from. - let ptr = self.buf_ptr.as_ptr().add(index); - - // Shift everything down to fill in that spot. - ptr::copy(ptr.add(1), ptr, self.len - index - 1); - } - self.len -= 1; - } - - fn as_slice(&self) -> &[TimSortRun] { - // SAFETY: Safe as long as buf_ptr is valid and len invariant was upheld. - unsafe { &*ptr::slice_from_raw_parts(self.buf_ptr.as_ptr(), self.len) } - } - - fn len(&self) -> usize { - self.len - } - } - - impl core::ops::Index for RunVec - where - RunAllocF: Fn(usize) -> *mut TimSortRun, - RunDeallocF: Fn(*mut TimSortRun, usize), - { - type Output = TimSortRun; - - fn index(&self, index: usize) -> &Self::Output { - if index < self.len { - // SAFETY: buf_ptr and len invariant must be upheld. - unsafe { - return &*(self.buf_ptr.as_ptr().add(index)); - } - } - - panic!("Index out of bounds"); - } - } - - impl core::ops::IndexMut for RunVec - where - RunAllocF: Fn(usize) -> *mut TimSortRun, - RunDeallocF: Fn(*mut TimSortRun, usize), - { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - if index < self.len { - // SAFETY: buf_ptr and len invariant must be upheld. - unsafe { - return &mut *(self.buf_ptr.as_ptr().add(index)); - } - } - - panic!("Index out of bounds"); - } - } - - impl Drop for RunVec - where - RunAllocF: Fn(usize) -> *mut TimSortRun, - RunDeallocF: Fn(*mut TimSortRun, usize), - { - fn drop(&mut self) { - // As long as TimSortRun is Copy we don't need to drop them individually but just the - // whole allocation. - (self.run_dealloc_fn)(self.buf_ptr.as_ptr(), self.capacity); - } - } -} - -/// Internal type used by merge_sort. -#[derive(Clone, Copy, Debug)] -pub struct TimSortRun { - len: usize, - start: usize, -} - -/// Takes a range as denoted by start and end, that is already sorted and extends it to the right if -/// necessary with sorts optimized for smaller ranges such as insertion sort. -fn provide_sorted_batch(v: &mut [T], start: usize, mut end: usize, is_less: &mut F) -> usize -where - F: FnMut(&T, &T) -> bool, -{ - let len = v.len(); - assert!(end >= start && end <= len); - - // This value is a balance between least comparisons and best performance, as - // influenced by for example cache locality. - const MIN_INSERTION_RUN: usize = 10; - - // Insert some more elements into the run if it's too short. Insertion sort is faster than - // merge sort on short sequences, so this significantly improves performance. - let start_end_diff = end - start; - - if start_end_diff < MIN_INSERTION_RUN && end < len { - // v[start_found..end] are elements that are already sorted in the input. We want to extend - // the sorted region to the left, so we push up MIN_INSERTION_RUN - 1 to the right. Which is - // more efficient that trying to push those already sorted elements to the left. - end = cmp::min(start + MIN_INSERTION_RUN, len); - let presorted_start = cmp::max(start_end_diff, 1); - - insertion_sort_shift_left(&mut v[start..end], presorted_start, is_less); - } - - end -} - -/// Finds a streak of presorted elements starting at the beginning of the slice. Returns the first -/// value that is not part of said streak, and a bool denoting whether the streak was reversed. -/// Streaks can be increasing or decreasing. -fn find_streak(v: &[T], is_less: &mut F) -> (usize, bool) -where - F: FnMut(&T, &T) -> bool, -{ - let len = v.len(); - - if len < 2 { - return (len, false); - } - - let mut end = 2; - - // SAFETY: See below specific. - unsafe { - // SAFETY: We checked that len >= 2, so 0 and 1 are valid indices. - let assume_reverse = is_less(v.get_unchecked(1), v.get_unchecked(0)); - - // SAFETY: We know end >= 2 and check end < len. - // From that follows that accessing v at end and end - 1 is safe. - if assume_reverse { - while end < len && is_less(v.get_unchecked(end), v.get_unchecked(end - 1)) { - end += 1; - } - - (end, true) - } else { - while end < len && !is_less(v.get_unchecked(end), v.get_unchecked(end - 1)) { - end += 1; - } - (end, false) - } - } -} diff --git a/library/core/src/slice/sort/mod.rs b/library/core/src/slice/sort/mod.rs new file mode 100644 index 000000000000..79852708b81e --- /dev/null +++ b/library/core/src/slice/sort/mod.rs @@ -0,0 +1,8 @@ +//! This module and the contained sub-modules contains the code for efficient and robust sort +//! implementations, as well as the domain adjacent implementation of `select_nth_unstable`. + +pub mod stable; +pub mod unstable; + +pub(crate) mod select; +pub(crate) mod shared; diff --git a/library/core/src/slice/select.rs b/library/core/src/slice/sort/select.rs similarity index 77% rename from library/core/src/slice/select.rs rename to library/core/src/slice/sort/select.rs index ffc193578e07..e0c1085916e5 100644 --- a/library/core/src/slice/select.rs +++ b/library/core/src/slice/sort/select.rs @@ -1,45 +1,78 @@ -//! Slice selection -//! //! This module contains the implementation for `slice::select_nth_unstable`. -//! It uses an introselect algorithm based on Orson Peters' pattern-defeating quicksort, -//! published at: +//! It uses an introselect algorithm based on ipnsort by Lukas Bergdoll and Orson Peters, +//! published at: //! //! The fallback algorithm used for introselect is Median of Medians using Tukey's Ninther //! for pivot selection. Using this as a fallback ensures O(n) worst case running time with //! better performance than one would get using heapsort as fallback. -use crate::cmp; use crate::mem::{self, SizedTypeProperties}; -use crate::slice::sort::{ - break_patterns, choose_pivot, insertion_sort_shift_left, partition, partition_equal, -}; -// For slices of up to this length it's probably faster to simply sort them. -// Defined at the module scope because it's used in multiple functions. -const MAX_INSERTION: usize = 10; +use crate::slice::sort::shared::pivot::choose_pivot; +use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; +use crate::slice::sort::unstable::quicksort::partition; + +/// Reorder the slice such that the element at `index` is at its final sorted position. +pub(crate) fn partition_at_index( + v: &mut [T], + index: usize, + mut is_less: F, +) -> (&mut [T], &mut T, &mut [T]) +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + + // Puts a lower limit of 1 on `len`. + if index >= len { + panic!("partition_at_index index {} greater than length of slice {}", index, len); + } + + if T::IS_ZST { + // Sorting has no meaningful behavior on zero-sized types. Do nothing. + } else if index == len - 1 { + // Find max element and place it in the last position of the array. We're free to use + // `unwrap()` here because we checked that `v` is not empty. + let max_idx = max_index(v, &mut is_less).unwrap(); + v.swap(max_idx, index); + } else if index == 0 { + // Find min element and place it in the first position of the array. We're free to use + // `unwrap()` here because we checked that `v` is not empty. + let min_idx = min_index(v, &mut is_less).unwrap(); + v.swap(min_idx, index); + } else { + partition_at_index_loop(v, index, None, &mut is_less); + } + + let (left, right) = v.split_at_mut(index); + let (pivot, right) = right.split_at_mut(1); + let pivot = &mut pivot[0]; + (left, pivot, right) +} + +// For small sub-slices it's faster to use a dedicated small-sort, but because it is only called at +// most once, it doesn't make sense to use something more sophisticated than insertion-sort. +const INSERTION_SORT_THRESHOLD: usize = 16; fn partition_at_index_loop<'a, T, F>( mut v: &'a mut [T], mut index: usize, + mut ancestor_pivot: Option<&'a T>, is_less: &mut F, - mut pred: Option<&'a T>, ) where F: FnMut(&T, &T) -> bool, { - // Limit the amount of iterations and fall back to fast deterministic selection - // to ensure O(n) worst case running time. This limit needs to be constant, because - // using `ilog2(len)` like in `sort` would result in O(n log n) time complexity. - // The exact value of the limit is chosen somewhat arbitrarily, but for most inputs bad pivot - // selections should be relatively rare, so the limit usually shouldn't be reached - // anyways. + // Limit the amount of iterations and fall back to fast deterministic selection to ensure O(n) + // worst case running time. This limit needs to be constant, because using `ilog2(len)` like in + // `sort` would result in O(n log n) time complexity. The exact value of the limit is chosen + // somewhat arbitrarily, but for most inputs bad pivot selections should be relatively rare, so + // the limit is reached for sub-slices len / (2^limit or less). Which makes the remaining work + // with the fallback minimal in relative terms. let mut limit = 16; - // True if the last partitioning was reasonably balanced. - let mut was_balanced = true; - loop { - if v.len() <= MAX_INSERTION { - if v.len() > 1 { + if v.len() <= INSERTION_SORT_THRESHOLD { + if v.len() >= 2 { insertion_sort_shift_left(v, 1, is_less); } return; @@ -50,38 +83,35 @@ fn partition_at_index_loop<'a, T, F>( return; } - // If the last partitioning was imbalanced, try breaking patterns in the slice by shuffling - // some elements around. Hopefully we'll choose a better pivot this time. - if !was_balanced { - break_patterns(v); - limit -= 1; - } + limit -= 1; // Choose a pivot - let (pivot, _) = choose_pivot(v, is_less); + let pivot_pos = choose_pivot(v, is_less); // If the chosen pivot is equal to the predecessor, then it's the smallest element in the // slice. Partition the slice into elements equal to and elements greater than the pivot. // This case is usually hit when the slice contains many duplicate elements. - if let Some(p) = pred { - if !is_less(p, &v[pivot]) { - let mid = partition_equal(v, pivot, is_less); + if let Some(p) = ancestor_pivot { + if !is_less(p, unsafe { v.get_unchecked(pivot_pos) }) { + let num_lt = partition(v, pivot_pos, &mut |a, b| !is_less(b, a)); + + // Continue sorting elements greater than the pivot. We know that `mid` contains + // the pivot. So we can continue after `mid`. + let mid = num_lt + 1; // If we've passed our index, then we're good. if mid > index { return; } - // Otherwise, continue sorting elements greater than the pivot. v = &mut v[mid..]; index = index - mid; - pred = None; + ancestor_pivot = None; continue; } } - let (mid, _) = partition(v, pivot, is_less); - was_balanced = cmp::min(mid, v.len() - mid) >= v.len() / 8; + let mid = partition(v, pivot_pos, is_less); // Split the slice into `left`, `pivot`, and `right`. let (left, right) = v.split_at_mut(mid); @@ -91,7 +121,7 @@ fn partition_at_index_loop<'a, T, F>( if mid < index { v = right; index = index - mid - 1; - pred = Some(pivot); + ancestor_pivot = Some(pivot); } else if mid > index { v = left; } else { @@ -122,41 +152,6 @@ fn max_index bool>(slice: &[T], is_less: &mut F) -> Optio .map(|(i, _)| i) } -/// Reorder the slice such that the element at `index` is at its final sorted position. -pub fn partition_at_index( - v: &mut [T], - index: usize, - mut is_less: F, -) -> (&mut [T], &mut T, &mut [T]) -where - F: FnMut(&T, &T) -> bool, -{ - if index >= v.len() { - panic!("partition_at_index index {} greater than length of slice {}", index, v.len()); - } - - if T::IS_ZST { - // Sorting has no meaningful behavior on zero-sized types. Do nothing. - } else if index == v.len() - 1 { - // Find max element and place it in the last position of the array. We're free to use - // `unwrap()` here because we know v must not be empty. - let max_idx = max_index(v, &mut is_less).unwrap(); - v.swap(max_idx, index); - } else if index == 0 { - // Find min element and place it in the first position of the array. We're free to use - // `unwrap()` here because we know v must not be empty. - let min_idx = min_index(v, &mut is_less).unwrap(); - v.swap(min_idx, index); - } else { - partition_at_index_loop(v, index, &mut is_less, None); - } - - let (left, right) = v.split_at_mut(index); - let (pivot, right) = right.split_at_mut(1); - let pivot = &mut pivot[0]; - (left, pivot, right) -} - /// Selection algorithm to select the k-th element from the slice in guaranteed O(n) time. /// This is essentially a quickselect that uses Tukey's Ninther for pivot selection fn median_of_medians bool>(mut v: &mut [T], is_less: &mut F, mut k: usize) { @@ -168,8 +163,8 @@ fn median_of_medians bool>(mut v: &mut [T], is_less: &mut // We now know that `k < v.len() <= isize::MAX` loop { - if v.len() <= MAX_INSERTION { - if v.len() > 1 { + if v.len() <= INSERTION_SORT_THRESHOLD { + if v.len() >= 2 { insertion_sort_shift_left(v, 1, is_less); } return; @@ -232,7 +227,8 @@ fn median_of_ninthers bool>(v: &mut [T], is_less: &mut F) } median_of_medians(&mut v[lo..lo + frac], is_less, pivot); - partition(v, lo + pivot, is_less).0 + + partition(v, lo + pivot, is_less) } /// Moves around the 9 elements at the indices a..i, such that diff --git a/library/core/src/slice/sort/shared/mod.rs b/library/core/src/slice/sort/shared/mod.rs new file mode 100644 index 000000000000..ad1171bfc6a0 --- /dev/null +++ b/library/core/src/slice/sort/shared/mod.rs @@ -0,0 +1,45 @@ +use crate::marker::Freeze; + +pub(crate) mod pivot; +pub(crate) mod smallsort; + +/// SAFETY: this is safety relevant, how does this interact with the soundness holes in +/// specialization? +#[rustc_unsafe_specialization_marker] +pub(crate) trait FreezeMarker {} + +impl FreezeMarker for T {} + +/// Finds a run of sorted elements starting at the beginning of the slice. +/// +/// Returns the length of the run, and a bool that is false when the run +/// is ascending, and true if the run strictly descending. +#[inline(always)] +pub(crate) fn find_existing_run bool>( + v: &[T], + is_less: &mut F, +) -> (usize, bool) { + let len = v.len(); + if len < 2 { + return (len, false); + } + + // SAFETY: We checked that len >= 2, so 0 and 1 are valid indices. + // This also means that run_len < len implies run_len and run_len - 1 + // are valid indices as well. + unsafe { + let mut run_len = 2; + let strictly_descending = is_less(v.get_unchecked(1), v.get_unchecked(0)); + if strictly_descending { + while run_len < len && is_less(v.get_unchecked(run_len), v.get_unchecked(run_len - 1)) { + run_len += 1; + } + } else { + while run_len < len && !is_less(v.get_unchecked(run_len), v.get_unchecked(run_len - 1)) + { + run_len += 1; + } + } + (run_len, strictly_descending) + } +} diff --git a/library/core/src/slice/sort/shared/pivot.rs b/library/core/src/slice/sort/shared/pivot.rs new file mode 100644 index 000000000000..255a1eb6c88a --- /dev/null +++ b/library/core/src/slice/sort/shared/pivot.rs @@ -0,0 +1,88 @@ +//! This module contains the logic for pivot selection. + +use crate::intrinsics; + +// Recursively select a pseudomedian if above this threshold. +const PSEUDO_MEDIAN_REC_THRESHOLD: usize = 64; + +/// Selects a pivot from `v`. Algorithm taken from glidesort by Orson Peters. +/// +/// This chooses a pivot by sampling an adaptive amount of points, approximating +/// the quality of a median of sqrt(n) elements. +pub fn choose_pivot bool>(v: &[T], is_less: &mut F) -> usize { + // We use unsafe code and raw pointers here because we're dealing with + // heavy recursion. Passing safe slices around would involve a lot of + // branches and function call overhead. + + let len = v.len(); + if len < 8 { + intrinsics::abort(); + } + + // SAFETY: a, b, c point to initialized regions of len_div_8 elements, + // satisfying median3 and median3_rec's preconditions as v_base points + // to an initialized region of n = len elements. + unsafe { + let v_base = v.as_ptr(); + let len_div_8 = len / 8; + + let a = v_base; // [0, floor(n/8)) + let b = v_base.add(len_div_8 * 4); // [4*floor(n/8), 5*floor(n/8)) + let c = v_base.add(len_div_8 * 7); // [7*floor(n/8), 8*floor(n/8)) + + if len < PSEUDO_MEDIAN_REC_THRESHOLD { + median3(&*a, &*b, &*c, is_less).sub_ptr(v_base) + } else { + median3_rec(a, b, c, len_div_8, is_less).sub_ptr(v_base) + } + } +} + +/// Calculates an approximate median of 3 elements from sections a, b, c, or +/// recursively from an approximation of each, if they're large enough. By +/// dividing the size of each section by 8 when recursing we have logarithmic +/// recursion depth and overall sample from f(n) = 3*f(n/8) -> f(n) = +/// O(n^(log(3)/log(8))) ~= O(n^0.528) elements. +/// +/// SAFETY: a, b, c must point to the start of initialized regions of memory of +/// at least n elements. +unsafe fn median3_rec bool>( + mut a: *const T, + mut b: *const T, + mut c: *const T, + n: usize, + is_less: &mut F, +) -> *const T { + // SAFETY: a, b, c still point to initialized regions of n / 8 elements, + // by the exact same logic as in choose_pivot. + unsafe { + if n * 8 >= PSEUDO_MEDIAN_REC_THRESHOLD { + let n8 = n / 8; + a = median3_rec(a, a.add(n8 * 4), a.add(n8 * 7), n8, is_less); + b = median3_rec(b, b.add(n8 * 4), b.add(n8 * 7), n8, is_less); + c = median3_rec(c, c.add(n8 * 4), c.add(n8 * 7), n8, is_less); + } + median3(&*a, &*b, &*c, is_less) + } +} + +/// Calculates the median of 3 elements. +/// +/// SAFETY: a, b, c must be valid initialized elements. +#[inline(always)] +fn median3 bool>(a: &T, b: &T, c: &T, is_less: &mut F) -> *const T { + // Compiler tends to make this branchless when sensible, and avoids the + // third comparison when not. + let x = is_less(a, b); + let y = is_less(a, c); + if x == y { + // If x=y=0 then b, c <= a. In this case we want to return max(b, c). + // If x=y=1 then a < b, c. In this case we want to return min(b, c). + // By toggling the outcome of b < c using XOR x we get this behavior. + let z = is_less(b, c); + if z ^ x { c } else { b } + } else { + // Either c <= a < b or b <= a < c, thus a is our median. + a + } +} diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs new file mode 100644 index 000000000000..8dbd45a389ca --- /dev/null +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -0,0 +1,843 @@ +//! This module contains a variety of sort implementations that are optimized for small lengths. + +use crate::intrinsics; +use crate::mem::{self, ManuallyDrop, MaybeUninit}; +use crate::ptr; +use crate::slice; + +use crate::slice::sort::shared::FreezeMarker; + +// It's important to differentiate between SMALL_SORT_THRESHOLD performance for +// small slices and small-sort performance sorting small sub-slices as part of +// the main quicksort loop. For the former, testing showed that the +// representative benchmarks for real-world performance are cold CPU state and +// not single-size hot benchmarks. For the latter the CPU will call them many +// times, so hot benchmarks are fine and more realistic. And it's worth it to +// optimize sorting small sub-slices with more sophisticated solutions than +// insertion sort. + +/// Using a trait allows us to specialize on `Freeze` which in turn allows us to make safe +/// abstractions. +pub(crate) trait StableSmallSortTypeImpl: Sized { + /// For which input length <= return value of this function, is it valid to call `small_sort`. + fn small_sort_threshold() -> usize; + + /// Sorts `v` using strategies optimized for small sizes. + fn small_sort bool>( + v: &mut [Self], + scratch: &mut [MaybeUninit], + is_less: &mut F, + ); +} + +impl StableSmallSortTypeImpl for T { + #[inline(always)] + default fn small_sort_threshold() -> usize { + // Optimal number of comparisons, and good perf. + SMALL_SORT_FALLBACK_THRESHOLD + } + + #[inline(always)] + default fn small_sort bool>( + v: &mut [T], + _scratch: &mut [MaybeUninit], + is_less: &mut F, + ) { + if v.len() >= 2 { + insertion_sort_shift_left(v, 1, is_less); + } + } +} + +impl StableSmallSortTypeImpl for T { + #[inline(always)] + fn small_sort_threshold() -> usize { + SMALL_SORT_GENERAL_THRESHOLD + } + + #[inline(always)] + fn small_sort bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + is_less: &mut F, + ) { + small_sort_general_with_scratch(v, scratch, is_less); + } +} + +/// Using a trait allows us to specialize on `Freeze` which in turn allows us to make safe +/// abstractions. +pub(crate) trait UnstableSmallSortTypeImpl: Sized { + /// For which input length <= return value of this function, is it valid to call `small_sort`. + fn small_sort_threshold() -> usize; + + /// Sorts `v` using strategies optimized for small sizes. + fn small_sort bool>(v: &mut [Self], is_less: &mut F); +} + +impl UnstableSmallSortTypeImpl for T { + #[inline(always)] + default fn small_sort_threshold() -> usize { + SMALL_SORT_FALLBACK_THRESHOLD + } + + #[inline(always)] + default fn small_sort(v: &mut [T], is_less: &mut F) + where + F: FnMut(&T, &T) -> bool, + { + small_sort_fallback(v, is_less); + } +} + +impl UnstableSmallSortTypeImpl for T { + #[inline(always)] + fn small_sort_threshold() -> usize { + match const { choose_unstable_small_sort::() } { + UnstalbeSmallSort::Fallback => SMALL_SORT_FALLBACK_THRESHOLD, + UnstalbeSmallSort::General => SMALL_SORT_GENERAL_THRESHOLD, + UnstalbeSmallSort::Network => SMALL_SORT_NETWORK_THRESHOLD, + } + } + + #[inline(always)] + fn small_sort(v: &mut [T], is_less: &mut F) + where + F: FnMut(&T, &T) -> bool, + { + // This construct is used to limit the LLVM IR generated, which saves large amounts of + // compile-time by only instantiating the code that is needed. Idea by Frank Steffahn. + (const { inst_unstable_small_sort::() })(v, is_less); + } +} + +/// Optimal number of comparisons, and good perf. +const SMALL_SORT_FALLBACK_THRESHOLD: usize = 16; + +/// From a comparison perspective 20 was ~2% more efficient for fully random input, but for +/// wall-clock performance choosing 32 yielded better performance overall. +/// +/// SAFETY: If you change this value, you have to adjust [`small_sort_general`] ! +const SMALL_SORT_GENERAL_THRESHOLD: usize = 32; + +/// [`small_sort_general`] uses [`sort8_stable`] as primitive and does a kind of ping-pong merge, +/// where the output of the first two [`sort8_stable`] calls is stored at the end of the scratch +/// buffer. This simplifies panic handling and avoids additional copies. This affects the required +/// scratch buffer size. +/// +/// SAFETY: If you change this value, you have to adjust [`small_sort_general`] ! +pub(crate) const SMALL_SORT_GENERAL_SCRATCH_LEN: usize = SMALL_SORT_GENERAL_THRESHOLD + 16; + +/// SAFETY: If you change this value, you have to adjust [`small_sort_network`] ! +const SMALL_SORT_NETWORK_THRESHOLD: usize = 32; +const SMALL_SORT_NETWORK_SCRATCH_LEN: usize = SMALL_SORT_NETWORK_THRESHOLD; + +/// Using a stack array, could cause a stack overflow if the type `T` is very large. To be +/// conservative we limit the usage of small-sorts that require a stack array to types that fit +/// within this limit. +const MAX_STACK_ARRAY_SIZE: usize = 4096; + +enum UnstalbeSmallSort { + Fallback, + General, + Network, +} + +const fn choose_unstable_small_sort() -> UnstalbeSmallSort { + if T::is_copy() + && has_efficient_in_place_swap::() + && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE + { + // Heuristic for int like types. + return UnstalbeSmallSort::Network; + } + + if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + return UnstalbeSmallSort::General; + } + + UnstalbeSmallSort::Fallback +} + +const fn inst_unstable_small_sort bool>() +-> fn(&mut [T], &mut F) { + match const { choose_unstable_small_sort::() } { + UnstalbeSmallSort::Fallback => small_sort_fallback::, + UnstalbeSmallSort::General => small_sort_general::, + UnstalbeSmallSort::Network => small_sort_network::, + } +} + +fn small_sort_fallback bool>(v: &mut [T], is_less: &mut F) { + if v.len() >= 2 { + insertion_sort_shift_left(v, 1, is_less); + } +} + +fn small_sort_general bool>(v: &mut [T], is_less: &mut F) { + let mut stack_array = MaybeUninit::<[T; SMALL_SORT_GENERAL_SCRATCH_LEN]>::uninit(); + + let scratch = unsafe { + slice::from_raw_parts_mut( + stack_array.as_mut_ptr() as *mut MaybeUninit, + SMALL_SORT_GENERAL_SCRATCH_LEN, + ) + }; + + small_sort_general_with_scratch(v, scratch, is_less); +} + +fn small_sort_general_with_scratch bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + is_less: &mut F, +) { + let len = v.len(); + if len < 2 { + return; + } + + if scratch.len() < len + 16 { + intrinsics::abort(); + } + + let v_base = v.as_mut_ptr(); + let len_div_2 = len / 2; + + // SAFETY: See individual comments. + unsafe { + let scratch_base = scratch.as_mut_ptr() as *mut T; + + let presorted_len = if const { mem::size_of::() <= 16 } && len >= 16 { + // SAFETY: scratch_base is valid and has enough space. + sort8_stable(v_base, scratch_base, scratch_base.add(len), is_less); + sort8_stable( + v_base.add(len_div_2), + scratch_base.add(len_div_2), + scratch_base.add(len + 8), + is_less, + ); + + 8 + } else if len >= 8 { + // SAFETY: scratch_base is valid and has enough space. + sort4_stable(v_base, scratch_base, is_less); + sort4_stable(v_base.add(len_div_2), scratch_base.add(len_div_2), is_less); + + 4 + } else { + ptr::copy_nonoverlapping(v_base, scratch_base, 1); + ptr::copy_nonoverlapping(v_base.add(len_div_2), scratch_base.add(len_div_2), 1); + + 1 + }; + + for offset in [0, len_div_2] { + // SAFETY: at this point dst is initialized with presorted_len elements. + // We extend this to desired_len, src is valid for desired_len elements. + let src = v_base.add(offset); + let dst = scratch_base.add(offset); + let desired_len = if offset == 0 { len_div_2 } else { len - len_div_2 }; + + for i in presorted_len..desired_len { + ptr::copy_nonoverlapping(src.add(i), dst.add(i), 1); + insert_tail(dst, dst.add(i), is_less); + } + } + + // SAFETY: see comment in `CopyOnDrop::drop`. + let drop_guard = CopyOnDrop { src: scratch_base, dst: v_base, len }; + + // SAFETY: at this point scratch_base is fully initialized, allowing us + // to use it as the source of our merge back into the original array. + // If a panic occurs we ensure the original array is restored to a valid + // permutation of the input through drop_guard. This technique is similar + // to ping-pong merging. + bidirectional_merge( + &*ptr::slice_from_raw_parts(drop_guard.src, drop_guard.len), + drop_guard.dst, + is_less, + ); + mem::forget(drop_guard); + } +} + +struct CopyOnDrop { + src: *const T, + dst: *mut T, + len: usize, +} + +impl Drop for CopyOnDrop { + fn drop(&mut self) { + // SAFETY: `src` must contain `len` initialized elements, and dst must + // be valid to write `len` elements. + unsafe { + ptr::copy_nonoverlapping(self.src, self.dst, self.len); + } + } +} + +fn small_sort_network(v: &mut [T], is_less: &mut F) +where + T: FreezeMarker, + F: FnMut(&T, &T) -> bool, +{ + // This implementation is tuned to be efficient for integer types. + + let len = v.len(); + if len < 2 { + return; + } + + if len > SMALL_SORT_NETWORK_SCRATCH_LEN { + intrinsics::abort(); + } + + let mut stack_array = MaybeUninit::<[T; SMALL_SORT_NETWORK_SCRATCH_LEN]>::uninit(); + + let len_div_2 = len / 2; + let no_merge = len < 18; + + let v_base = v.as_mut_ptr(); + let initial_region_len = if no_merge { len } else { len_div_2 }; + // SAFETY: Both possible values of `initial_region_len` are in-bounds. + let mut region = unsafe { &mut *ptr::slice_from_raw_parts_mut(v_base, initial_region_len) }; + + // Avoid compiler unrolling, we *really* don't want that to happen here for binary-size reasons. + loop { + let presorted_len = if region.len() >= 13 { + sort13_optimal(region, is_less); + 13 + } else if region.len() >= 9 { + sort9_optimal(region, is_less); + 9 + } else { + 1 + }; + + insertion_sort_shift_left(region, presorted_len, is_less); + + if no_merge { + return; + } + + if region.as_ptr() != v_base { + break; + } + + // SAFETY: The right side of `v` based on `len_div_2` is guaranteed in-bounds. + region = + unsafe { &mut *ptr::slice_from_raw_parts_mut(v_base.add(len_div_2), len - len_div_2) }; + } + + // SAFETY: We checked that T is Freeze and thus observation safe. + // Should is_less panic v was not modified in parity_merge and retains it's original input. + // scratch and v must not alias and scratch has v.len() space. + unsafe { + let scratch_base = stack_array.as_mut_ptr() as *mut T; + bidirectional_merge( + &mut *ptr::slice_from_raw_parts_mut(v_base, len), + scratch_base, + is_less, + ); + ptr::copy_nonoverlapping(scratch_base, v_base, len); + } +} + +/// Swap two values in the slice pointed to by `v_base` at the position `a_pos` and `b_pos` if the +/// value at position `b_pos` is less than the one at position `a_pos`. +pub unsafe fn swap_if_less(v_base: *mut T, a_pos: usize, b_pos: usize, is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + // SAFETY: the caller must guarantee that `a` and `b` each added to `v_base` yield valid + // pointers into `v_base`, and are properly aligned, and part of the same allocation. + unsafe { + let v_a = v_base.add(a_pos); + let v_b = v_base.add(b_pos); + + // PANIC SAFETY: if is_less panics, no scratch memory was created and the slice should still be + // in a well defined state, without duplicates. + + // Important to only swap if it is more and not if it is equal. is_less should return false for + // equal, so we don't swap. + let should_swap = is_less(&*v_b, &*v_a); + + // This is a branchless version of swap if. + // The equivalent code with a branch would be: + // + // if should_swap { + // ptr::swap(left, right, 1); + // } + + // The goal is to generate cmov instructions here. + let left_swap = if should_swap { v_b } else { v_a }; + let right_swap = if should_swap { v_a } else { v_b }; + + let right_swap_tmp = ManuallyDrop::new(ptr::read(right_swap)); + ptr::copy(left_swap, v_a, 1); + ptr::copy_nonoverlapping(&*right_swap_tmp, v_b, 1); + } +} + +// Never inline this function to avoid code bloat. It still optimizes nicely and has practically no +// performance impact. +fn sort9_optimal(v: &mut [T], is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + if v.len() < 9 { + intrinsics::abort(); + } + + let v_base = v.as_mut_ptr(); + + // Optimal sorting network see: + // https://bertdobbelaere.github.io/sorting_networks.html. + + // SAFETY: We checked the len. + unsafe { + swap_if_less(v_base, 0, 3, is_less); + swap_if_less(v_base, 1, 7, is_less); + swap_if_less(v_base, 2, 5, is_less); + swap_if_less(v_base, 4, 8, is_less); + swap_if_less(v_base, 0, 7, is_less); + swap_if_less(v_base, 2, 4, is_less); + swap_if_less(v_base, 3, 8, is_less); + swap_if_less(v_base, 5, 6, is_less); + swap_if_less(v_base, 0, 2, is_less); + swap_if_less(v_base, 1, 3, is_less); + swap_if_less(v_base, 4, 5, is_less); + swap_if_less(v_base, 7, 8, is_less); + swap_if_less(v_base, 1, 4, is_less); + swap_if_less(v_base, 3, 6, is_less); + swap_if_less(v_base, 5, 7, is_less); + swap_if_less(v_base, 0, 1, is_less); + swap_if_less(v_base, 2, 4, is_less); + swap_if_less(v_base, 3, 5, is_less); + swap_if_less(v_base, 6, 8, is_less); + swap_if_less(v_base, 2, 3, is_less); + swap_if_less(v_base, 4, 5, is_less); + swap_if_less(v_base, 6, 7, is_less); + swap_if_less(v_base, 1, 2, is_less); + swap_if_less(v_base, 3, 4, is_less); + swap_if_less(v_base, 5, 6, is_less); + } +} + +// Never inline this function to avoid code bloat. It still optimizes nicely and has practically no +// performance impact. +fn sort13_optimal(v: &mut [T], is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + if v.len() < 13 { + intrinsics::abort(); + } + + let v_base = v.as_mut_ptr(); + + // Optimal sorting network see: + // https://bertdobbelaere.github.io/sorting_networks.html. + + // SAFETY: We checked the len. + unsafe { + swap_if_less(v_base, 0, 12, is_less); + swap_if_less(v_base, 1, 10, is_less); + swap_if_less(v_base, 2, 9, is_less); + swap_if_less(v_base, 3, 7, is_less); + swap_if_less(v_base, 5, 11, is_less); + swap_if_less(v_base, 6, 8, is_less); + swap_if_less(v_base, 1, 6, is_less); + swap_if_less(v_base, 2, 3, is_less); + swap_if_less(v_base, 4, 11, is_less); + swap_if_less(v_base, 7, 9, is_less); + swap_if_less(v_base, 8, 10, is_less); + swap_if_less(v_base, 0, 4, is_less); + swap_if_less(v_base, 1, 2, is_less); + swap_if_less(v_base, 3, 6, is_less); + swap_if_less(v_base, 7, 8, is_less); + swap_if_less(v_base, 9, 10, is_less); + swap_if_less(v_base, 11, 12, is_less); + swap_if_less(v_base, 4, 6, is_less); + swap_if_less(v_base, 5, 9, is_less); + swap_if_less(v_base, 8, 11, is_less); + swap_if_less(v_base, 10, 12, is_less); + swap_if_less(v_base, 0, 5, is_less); + swap_if_less(v_base, 3, 8, is_less); + swap_if_less(v_base, 4, 7, is_less); + swap_if_less(v_base, 6, 11, is_less); + swap_if_less(v_base, 9, 10, is_less); + swap_if_less(v_base, 0, 1, is_less); + swap_if_less(v_base, 2, 5, is_less); + swap_if_less(v_base, 6, 9, is_less); + swap_if_less(v_base, 7, 8, is_less); + swap_if_less(v_base, 10, 11, is_less); + swap_if_less(v_base, 1, 3, is_less); + swap_if_less(v_base, 2, 4, is_less); + swap_if_less(v_base, 5, 6, is_less); + swap_if_less(v_base, 9, 10, is_less); + swap_if_less(v_base, 1, 2, is_less); + swap_if_less(v_base, 3, 4, is_less); + swap_if_less(v_base, 5, 7, is_less); + swap_if_less(v_base, 6, 8, is_less); + swap_if_less(v_base, 2, 3, is_less); + swap_if_less(v_base, 4, 5, is_less); + swap_if_less(v_base, 6, 7, is_less); + swap_if_less(v_base, 8, 9, is_less); + swap_if_less(v_base, 3, 4, is_less); + swap_if_less(v_base, 5, 6, is_less); + } +} + +/// Sorts range [begin, tail] assuming [begin, tail) is already sorted. +/// +/// # Safety +/// begin < tail and p must be valid and initialized for all begin <= p <= tail. +unsafe fn insert_tail bool>(begin: *mut T, tail: *mut T, is_less: &mut F) { + // SAFETY: see individual comments. + unsafe { + // SAFETY: in-bounds as tail > begin. + let mut sift = tail.sub(1); + if !is_less(&*tail, &*sift) { + return; + } + + // SAFETY: after this read tail is never read from again, as we only ever + // read from sift, sift < tail and we only ever decrease sift. Thus this is + // effectively a move, not a copy. Should a panic occur, or we have found + // the correct insertion position, gap_guard ensures the element is moved + // back into the array. + let tmp = ManuallyDrop::new(tail.read()); + let mut gap_guard = CopyOnDrop { src: &*tmp, dst: tail, len: 1 }; + + loop { + // SAFETY: we move sift into the gap (which is valid), and point the + // gap guard destination at sift, ensuring that if a panic occurs the + // gap is once again filled. + ptr::copy_nonoverlapping(sift, gap_guard.dst, 1); + gap_guard.dst = sift; + + if sift == begin { + break; + } + + // SAFETY: we checked that sift != begin, thus this is in-bounds. + sift = sift.sub(1); + if !is_less(&tmp, &*sift) { + break; + } + } + } +} + +/// Sort `v` assuming `v[..offset]` is already sorted. +pub fn insertion_sort_shift_left bool>( + v: &mut [T], + offset: usize, + is_less: &mut F, +) { + let len = v.len(); + if offset == 0 || offset > len { + intrinsics::abort(); + } + + // SAFETY: see individual comments. + unsafe { + // We write this basic loop directly using pointers, as when we use a + // for loop LLVM likes to unroll this loop which we do not want. + // SAFETY: v_end is the one-past-end pointer, and we checked that + // offset <= len, thus tail is also in-bounds. + let v_base = v.as_mut_ptr(); + let v_end = v_base.add(len); + let mut tail = v_base.add(offset); + while tail != v_end { + // SAFETY: v_base and tail are both valid pointers to elements, and + // v_base < tail since we checked offset != 0. + insert_tail(v_base, tail, is_less); + + // SAFETY: we checked that tail is not yet the one-past-end pointer. + tail = tail.add(1); + } + } +} + +/// SAFETY: The caller MUST guarantee that `v_base` is valid for 4 reads and +/// `dst` is valid for 4 writes. The result will be stored in `dst[0..4]`. +pub unsafe fn sort4_stable bool>( + v_base: *const T, + dst: *mut T, + is_less: &mut F, +) { + // By limiting select to picking pointers, we are guaranteed good cmov code-gen + // regardless of type T's size. Further this only does 5 instead of 6 + // comparisons compared to a stable transposition 4 element sorting-network, + // and always copies each element exactly once. + + // SAFETY: all pointers have offset at most 3 from v_base and dst, and are + // thus in-bounds by the precondition. + unsafe { + // Stably create two pairs a <= b and c <= d. + let c1 = is_less(&*v_base.add(1), &*v_base); + let c2 = is_less(&*v_base.add(3), &*v_base.add(2)); + let a = v_base.add(c1 as usize); + let b = v_base.add(!c1 as usize); + let c = v_base.add(2 + c2 as usize); + let d = v_base.add(2 + (!c2 as usize)); + + // Compare (a, c) and (b, d) to identify max/min. We're left with two + // unknown elements, but because we are a stable sort we must know which + // one is leftmost and which one is rightmost. + // c3, c4 | min max unknown_left unknown_right + // 0, 0 | a d b c + // 0, 1 | a b c d + // 1, 0 | c d a b + // 1, 1 | c b a d + let c3 = is_less(&*c, &*a); + let c4 = is_less(&*d, &*b); + let min = select(c3, c, a); + let max = select(c4, b, d); + let unknown_left = select(c3, a, select(c4, c, b)); + let unknown_right = select(c4, d, select(c3, b, c)); + + // Sort the last two unknown elements. + let c5 = is_less(&*unknown_right, &*unknown_left); + let lo = select(c5, unknown_right, unknown_left); + let hi = select(c5, unknown_left, unknown_right); + + ptr::copy_nonoverlapping(min, dst, 1); + ptr::copy_nonoverlapping(lo, dst.add(1), 1); + ptr::copy_nonoverlapping(hi, dst.add(2), 1); + ptr::copy_nonoverlapping(max, dst.add(3), 1); + } + + #[inline(always)] + fn select(cond: bool, if_true: *const T, if_false: *const T) -> *const T { + if cond { if_true } else { if_false } + } +} + +/// SAFETY: The caller MUST guarantee that `v_base` is valid for 8 reads and +/// writes, `scratch_base` and `dst` MUST be valid for 8 writes. The result will +/// be stored in `dst[0..8]`. +unsafe fn sort8_stable bool>( + v_base: *mut T, + dst: *mut T, + scratch_base: *mut T, + is_less: &mut F, +) { + // SAFETY: these pointers are all in-bounds by the precondition of our function. + unsafe { + sort4_stable(v_base, scratch_base, is_less); + sort4_stable(v_base.add(4), scratch_base.add(4), is_less); + } + + // SAFETY: scratch_base[0..8] is now initialized, allowing us to merge back + // into dst. + unsafe { + bidirectional_merge(&*ptr::slice_from_raw_parts(scratch_base, 8), dst, is_less); + } +} + +#[inline(always)] +unsafe fn merge_up bool>( + mut left_src: *const T, + mut right_src: *const T, + mut dst: *mut T, + is_less: &mut F, +) -> (*const T, *const T, *mut T) { + // This is a branchless merge utility function. + // The equivalent code with a branch would be: + // + // if !is_less(&*right_src, &*left_src) { + // ptr::copy_nonoverlapping(left_src, dst, 1); + // left_src = left_src.add(1); + // } else { + // ptr::copy_nonoverlapping(right_src, dst, 1); + // right_src = right_src.add(1); + // } + // dst = dst.add(1); + + // SAFETY: The caller must guarantee that `left_src`, `right_src` are valid + // to read and `dst` is valid to write, while not aliasing. + unsafe { + let is_l = !is_less(&*right_src, &*left_src); + let src = if is_l { left_src } else { right_src }; + ptr::copy_nonoverlapping(src, dst, 1); + right_src = right_src.add(!is_l as usize); + left_src = left_src.add(is_l as usize); + dst = dst.add(1); + } + + (left_src, right_src, dst) +} + +#[inline(always)] +unsafe fn merge_down bool>( + mut left_src: *const T, + mut right_src: *const T, + mut dst: *mut T, + is_less: &mut F, +) -> (*const T, *const T, *mut T) { + // This is a branchless merge utility function. + // The equivalent code with a branch would be: + // + // if !is_less(&*right_src, &*left_src) { + // ptr::copy_nonoverlapping(right_src, dst, 1); + // right_src = right_src.wrapping_sub(1); + // } else { + // ptr::copy_nonoverlapping(left_src, dst, 1); + // left_src = left_src.wrapping_sub(1); + // } + // dst = dst.sub(1); + + // SAFETY: The caller must guarantee that `left_src`, `right_src` are valid + // to read and `dst` is valid to write, while not aliasing. + unsafe { + let is_l = !is_less(&*right_src, &*left_src); + let src = if is_l { right_src } else { left_src }; + ptr::copy_nonoverlapping(src, dst, 1); + right_src = right_src.wrapping_sub(is_l as usize); + left_src = left_src.wrapping_sub(!is_l as usize); + dst = dst.sub(1); + } + + (left_src, right_src, dst) +} + +/// Merge v assuming v[..len / 2] and v[len / 2..] are sorted. +/// +/// Original idea for bi-directional merging by Igor van den Hoven (quadsort), +/// adapted to only use merge up and down. In contrast to the original +/// parity_merge function, it performs 2 writes instead of 4 per iteration. +/// +/// # Safety +/// The caller must guarantee that `dst` is valid for v.len() writes. +/// Also `v.as_ptr()` and `dst` must not alias and v.len() must be >= 2. +/// +/// Note that T must be Freeze, the comparison function is evaluated on outdated +/// temporary 'copies' that may not end up in the final array. +unsafe fn bidirectional_merge bool>( + v: &[T], + dst: *mut T, + is_less: &mut F, +) { + // It helps to visualize the merge: + // + // Initial: + // + // |dst (in dst) + // |left |right + // v v + // [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] + // ^ ^ + // |left_rev |right_rev + // |dst_rev (in dst) + // + // After: + // + // |dst (in dst) + // |left | |right + // v v v + // [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] + // ^ ^ ^ + // |left_rev | |right_rev + // |dst_rev (in dst) + // + // In each iteration one of left or right moves up one position, and one of + // left_rev or right_rev moves down one position, whereas dst always moves + // up one position and dst_rev always moves down one position. Assuming + // the input was sorted and the comparison function is correctly implemented + // at the end we will have left == left_rev + 1, and right == right_rev + 1, + // fully consuming the input having written it to dst. + + let len = v.len(); + let src = v.as_ptr(); + + let len_div_2 = len / 2; + + // SAFETY: The caller has to ensure that len >= 2. + unsafe { + intrinsics::assume(len_div_2 != 0); // This can avoid useless code-gen. + } + + // SAFETY: no matter what the result of the user-provided comparison function + // is, all 4 read pointers will always be in-bounds. Writing `dst` and `dst_rev` + // will always be in bounds if the caller guarantees that `dst` is valid for + // `v.len()` writes. + unsafe { + let mut left = src; + let mut right = src.add(len_div_2); + let mut dst = dst; + + let mut left_rev = src.add(len_div_2 - 1); + let mut right_rev = src.add(len - 1); + let mut dst_rev = dst.add(len - 1); + + for _ in 0..len_div_2 { + (left, right, dst) = merge_up(left, right, dst, is_less); + (left_rev, right_rev, dst_rev) = merge_down(left_rev, right_rev, dst_rev, is_less); + } + + let left_end = left_rev.wrapping_add(1); + let right_end = right_rev.wrapping_add(1); + + // Odd length, so one element is left unconsumed in the input. + if len % 2 != 0 { + let left_nonempty = left < left_end; + let last_src = if left_nonempty { left } else { right }; + ptr::copy_nonoverlapping(last_src, dst, 1); + left = left.add(left_nonempty as usize); + 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. + if left != left_end || right != right_end { + panic_on_ord_violation(); + } + } +} + +#[inline(never)] +fn panic_on_ord_violation() -> ! { + panic!("Ord violation"); +} + +#[must_use] +pub(crate) const fn has_efficient_in_place_swap() -> bool { + // Heuristic that holds true on all tested 64-bit capable architectures. + mem::size_of::() <= 8 // mem::size_of::() +} + +#[test] +fn type_info() { + assert!(has_efficient_in_place_swap::()); + assert!(has_efficient_in_place_swap::()); + assert!(!has_efficient_in_place_swap::()); + assert!(!has_efficient_in_place_swap::()); +} + +/// SAFETY: Only used for run-time optimization heuristic. +#[rustc_unsafe_specialization_marker] +trait CopyMarker {} + +impl CopyMarker for T {} + +#[const_trait] +trait IsCopy { + fn is_copy() -> bool; +} + +impl const IsCopy for T { + default fn is_copy() -> bool { + false + } +} +impl const IsCopy for T { + fn is_copy() -> bool { + true + } +} diff --git a/library/core/src/slice/sort/stable/drift.rs b/library/core/src/slice/sort/stable/drift.rs new file mode 100644 index 000000000000..4008639095bd --- /dev/null +++ b/library/core/src/slice/sort/stable/drift.rs @@ -0,0 +1,300 @@ +//! This module contains the hybrid top-level loop combining bottom-up Mergesort with top-down +//! Quicksort. + +use crate::cmp; +use crate::intrinsics; +use crate::mem::MaybeUninit; + +use crate::slice::sort::shared::find_existing_run; +use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl; +use crate::slice::sort::stable::merge::merge; +use crate::slice::sort::stable::quicksort::quicksort; + +/// Sorts `v` based on comparison function `is_less`. If `eager_sort` is true, +/// it will only do small-sorts and physical merges, ensuring O(N * log(N)) +/// worst-case complexity. `scratch.len()` must be at least `max(v.len() / 2, +/// MIN_SMALL_SORT_SCRATCH_LEN)` otherwise the implementation may abort. +/// Fully ascending and descending inputs will be sorted with exactly N - 1 +/// comparisons. +/// +/// This is the main loop for driftsort, which uses powersort's heuristic to +/// determine in which order to merge runs, see below for details. +pub fn sort bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + eager_sort: bool, + is_less: &mut F, +) { + let len = v.len(); + if len < 2 { + return; // Removing this length check *increases* code size. + } + let scale_factor = merge_tree_scale_factor(len); + + // It's important to have a relatively high entry barrier for pre-sorted + // runs, as the presence of a single such run will force on average several + // merge operations and shrink the maximum quicksort size a lot. For that + // reason we use sqrt(len) as our pre-sorted run threshold. + const MIN_SQRT_RUN_LEN: usize = 64; + let min_good_run_len = if len <= (MIN_SQRT_RUN_LEN * MIN_SQRT_RUN_LEN) { + // For small input length `MIN_SQRT_RUN_LEN` would break pattern + // detection of full or nearly sorted inputs. + cmp::min(len - len / 2, MIN_SQRT_RUN_LEN) + } else { + sqrt_approx(len) + }; + + // (stack_len, runs, desired_depths) together form a stack maintaining run + // information for the powersort heuristic. desired_depths[i] is the desired + // depth of the merge node that merges runs[i] with the run that comes after + // it. + let mut stack_len = 0; + let mut run_storage = MaybeUninit::<[DriftsortRun; 66]>::uninit(); + let runs: *mut DriftsortRun = run_storage.as_mut_ptr().cast(); + let mut desired_depth_storage = MaybeUninit::<[u8; 66]>::uninit(); + let desired_depths: *mut u8 = desired_depth_storage.as_mut_ptr().cast(); + + let mut scan_idx = 0; + let mut prev_run = DriftsortRun::new_sorted(0); // Initial dummy run. + loop { + // Compute the next run and the desired depth of the merge node between + // prev_run and next_run. On the last iteration we create a dummy run + // with root-level desired depth to fully collapse the merge tree. + let (next_run, desired_depth); + if scan_idx < len { + next_run = + create_run(&mut v[scan_idx..], scratch, min_good_run_len, eager_sort, is_less); + desired_depth = merge_tree_depth( + scan_idx - prev_run.len(), + scan_idx, + scan_idx + next_run.len(), + scale_factor, + ); + } else { + next_run = DriftsortRun::new_sorted(0); + desired_depth = 0; + }; + + // Process the merge nodes between earlier runs[i] that have a desire to + // be deeper in the merge tree than the merge node for the splitpoint + // between prev_run and next_run. + // + // SAFETY: first note that this is the only place we modify stack_len, + // runs or desired depths. We maintain the following invariants: + // 1. The first stack_len elements of runs/desired_depths are initialized. + // 2. For all valid i > 0, desired_depths[i] < desired_depths[i+1]. + // 3. The sum of all valid runs[i].len() plus prev_run.len() equals + // scan_idx. + unsafe { + while stack_len > 1 && *desired_depths.add(stack_len - 1) >= desired_depth { + // Desired depth greater than the upcoming desired depth, pop + // left neighbor run from stack and merge into prev_run. + let left = *runs.add(stack_len - 1); + let merged_len = left.len() + prev_run.len(); + let merge_start_idx = scan_idx - merged_len; + let merge_slice = v.get_unchecked_mut(merge_start_idx..scan_idx); + prev_run = logical_merge(merge_slice, scratch, left, prev_run, is_less); + stack_len -= 1; + } + + // We now know that desired_depths[stack_len - 1] < desired_depth, + // maintaining our invariant. This also guarantees we don't overflow + // the stack as merge_tree_depth(..) <= 64 and thus we can only have + // 64 distinct values on the stack before pushing, plus our initial + // dummy run, while our capacity is 66. + *runs.add(stack_len) = prev_run; + *desired_depths.add(stack_len) = desired_depth; + stack_len += 1; + } + + // Break before overriding the last run with our dummy run. + if scan_idx >= len { + break; + } + + scan_idx += next_run.len(); + prev_run = next_run; + } + + if !prev_run.sorted() { + stable_quicksort(v, scratch, is_less); + } +} + +// Nearly-Optimal Mergesorts: Fast, Practical Sorting Methods That Optimally +// Adapt to Existing Runs by J. Ian Munro and Sebastian Wild. +// +// This method forms a binary merge tree, where each internal node corresponds +// to a splitting point between the adjacent runs that have to be merged. If we +// visualize our array as the number line from 0 to 1, we want to find the +// dyadic fraction with smallest denominator that lies between the midpoints of +// our to-be-merged slices. The exponent in the dyadic fraction indicates the +// desired depth in the binary merge tree this internal node wishes to have. +// This does not always correspond to the actual depth due to the inherent +// imbalance in runs, but we follow it as closely as possible. +// +// As an optimization we rescale the number line from [0, 1) to [0, 2^62). Then +// finding the simplest dyadic fraction between midpoints corresponds to finding +// the most significant bit difference of the midpoints. We save scale_factor = +// ceil(2^62 / n) to perform this rescaling using a multiplication, avoiding +// having to repeatedly do integer divides. This rescaling isn't exact when n is +// not a power of two since we use integers and not reals, but the result is +// very close, and in fact when n < 2^30 the resulting tree is equivalent as the +// approximation errors stay entirely in the lower order bits. +// +// Thus for the splitting point between two adjacent slices [a, b) and [b, c) +// the desired depth of the corresponding merge node is CLZ((a+b)*f ^ (b+c)*f), +// where CLZ counts the number of leading zeros in an integer and f is our scale +// factor. Note that we omitted the division by two in the midpoint +// calculations, as this simply shifts the bits by one position (and thus always +// adds one to the result), and we only care about the relative depths. +// +// Finally, if we try to upper bound x = (a+b)*f giving x = (n-1 + n) * ceil(2^62 / n) then +// x < (2^62 / n + 1) * 2n +// x < 2^63 + 2n +// So as long as n < 2^62 we find that x < 2^64, meaning our operations do not +// overflow. +#[inline(always)] +fn merge_tree_scale_factor(n: usize) -> u64 { + if usize::BITS > u64::BITS { + panic!("Platform not supported"); + } + + ((1 << 62) + n as u64 - 1) / n as u64 +} + +// Note: merge_tree_depth output is < 64 when left < right as f*x and f*y must +// differ in some bit, and is <= 64 always. +#[inline(always)] +fn merge_tree_depth(left: usize, mid: usize, right: usize, scale_factor: u64) -> u8 { + let x = left as u64 + mid as u64; + let y = mid as u64 + right as u64; + ((scale_factor * x) ^ (scale_factor * y)).leading_zeros() as u8 +} + +fn sqrt_approx(n: usize) -> usize { + // Note that sqrt(n) = n^(1/2), and that 2^log2(n) = n. We combine these + // two facts to approximate sqrt(n) as 2^(log2(n) / 2). Because our integer + // log floors we want to add 0.5 to compensate for this on average, so our + // initial approximation is 2^((1 + floor(log2(n))) / 2). + // + // We then apply an iteration of Newton's method to improve our + // approximation, which for sqrt(n) is a1 = (a0 + n / a0) / 2. + // + // Finally we note that the exponentiation / division can be done directly + // with shifts. We OR with 1 to avoid zero-checks in the integer log. + let ilog = (n | 1).ilog2(); + let shift = (1 + ilog) / 2; + ((1 << shift) + (n >> shift)) / 2 +} + +// Lazy logical runs as in Glidesort. +#[inline(always)] +fn logical_merge bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + left: DriftsortRun, + right: DriftsortRun, + is_less: &mut F, +) -> DriftsortRun { + // If one or both of the runs are sorted do a physical merge, using + // quicksort to sort the unsorted run if present. We also *need* to + // physically merge if the combined runs would not fit in the scratch space + // anymore (as this would mean we are no longer able to to quicksort them). + let len = v.len(); + let can_fit_in_scratch = len <= scratch.len(); + if !can_fit_in_scratch || left.sorted() || right.sorted() { + if !left.sorted() { + stable_quicksort(&mut v[..left.len()], scratch, is_less); + } + if !right.sorted() { + stable_quicksort(&mut v[left.len()..], scratch, is_less); + } + merge(v, scratch, left.len(), is_less); + + DriftsortRun::new_sorted(len) + } else { + DriftsortRun::new_unsorted(len) + } +} + +/// Creates a new logical run. +/// +/// A logical run can either be sorted or unsorted. If there is a pre-existing +/// run that clears the `min_good_run_len` threshold it is returned as a sorted +/// run. If not, the result depends on the value of `eager_sort`. If it is true, +/// then a sorted run of length `T::SMALL_SORT_THRESHOLD` is returned, and if it +/// is false an unsorted run of length `min_good_run_len` is returned. +fn create_run bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + min_good_run_len: usize, + eager_sort: bool, + is_less: &mut F, +) -> DriftsortRun { + let len = v.len(); + if len >= min_good_run_len { + let (run_len, was_reversed) = find_existing_run(v, is_less); + + // SAFETY: find_existing_run promises to return a valid run_len. + unsafe { intrinsics::assume(run_len <= len) }; + + if run_len >= min_good_run_len { + if was_reversed { + v[..run_len].reverse(); + } + + return DriftsortRun::new_sorted(run_len); + } + } + + if eager_sort { + // We call quicksort with a len that will immediately call small-sort. + // By not calling the small-sort directly here it can always be inlined into + // the quicksort itself, making the recursive base case faster and is generally + // more binary-size efficient. + let eager_run_len = cmp::min(T::small_sort_threshold(), len); + quicksort(&mut v[..eager_run_len], scratch, 0, None, is_less); + DriftsortRun::new_sorted(eager_run_len) + } else { + DriftsortRun::new_unsorted(cmp::min(min_good_run_len, len)) + } +} + +fn stable_quicksort bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + is_less: &mut F, +) { + // Limit the number of imbalanced partitions to `2 * floor(log2(len))`. + // The binary OR by one is used to eliminate the zero-check in the logarithm. + let limit = 2 * (v.len() | 1).ilog2(); + quicksort(v, scratch, limit, None, is_less); +} + +/// Compactly stores the length of a run, and whether or not it is sorted. This +/// can always fit in a usize because the maximum slice length is isize::MAX. +#[derive(Copy, Clone)] +struct DriftsortRun(usize); + +impl DriftsortRun { + #[inline(always)] + fn new_sorted(length: usize) -> Self { + Self((length << 1) | 1) + } + + #[inline(always)] + fn new_unsorted(length: usize) -> Self { + Self(length << 1) + } + + #[inline(always)] + fn sorted(self) -> bool { + self.0 & 1 == 1 + } + + #[inline(always)] + fn len(self) -> usize { + self.0 >> 1 + } +} diff --git a/library/core/src/slice/sort/stable/merge.rs b/library/core/src/slice/sort/stable/merge.rs new file mode 100644 index 000000000000..4f6a98bc7b89 --- /dev/null +++ b/library/core/src/slice/sort/stable/merge.rs @@ -0,0 +1,151 @@ +//! This module contains logic for performing a merge of two sorted sub-slices. + +use crate::cmp; +use crate::mem::MaybeUninit; +use crate::ptr; + +/// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `scratch` as +/// temporary storage, and stores the result into `v[..]`. +pub fn merge bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + mid: usize, + is_less: &mut F, +) { + let len = v.len(); + + if mid == 0 || mid >= len || scratch.len() < cmp::min(mid, len - mid) { + return; + } + + // SAFETY: We checked that the two slices are non-empty and `mid` is in-bounds. + // We checked that the buffer `scratch` has enough capacity to hold a copy of + // the shorter slice. `merge_up` and `merge_down` are written in such a way that + // they uphold the contract described in `MergeState::drop`. + unsafe { + // The merge process first copies the shorter run into `buf`. Then it traces + // the newly copied run and the longer run forwards (or backwards), comparing + // their next unconsumed elements and copying the lesser (or greater) one into `v`. + // + // As soon as the shorter run is fully consumed, the process is done. If the + // longer run gets consumed first, then we must copy whatever is left of the + // shorter run into the remaining gap in `v`. + // + // Intermediate state of the process is always tracked by `gap`, which serves + // two purposes: + // 1. Protects integrity of `v` from panics in `is_less`. + // 2. Fills the remaining gap in `v` if the longer run gets consumed first. + + let buf = MaybeUninit::slice_as_mut_ptr(scratch); + + let v_base = v.as_mut_ptr(); + let v_mid = v_base.add(mid); + let v_end = v_base.add(len); + + let left_len = mid; + let right_len = len - mid; + + let left_is_shorter = left_len <= right_len; + let save_base = if left_is_shorter { v_base } else { v_mid }; + let save_len = if left_is_shorter { left_len } else { right_len }; + + ptr::copy_nonoverlapping(save_base, buf, save_len); + + let mut merge_state = MergeState { start: buf, end: buf.add(save_len), dst: save_base }; + + if left_is_shorter { + merge_state.merge_up(v_mid, v_end, is_less); + } else { + merge_state.merge_down(v_base, buf, v_end, is_less); + } + // Finally, `merge_state` gets dropped. If the shorter run was not fully + // consumed, whatever remains of it will now be copied into the hole in `v`. + } + + // When dropped, copies the range `start..end` into `dst..`. + struct MergeState { + start: *mut T, + end: *mut T, + dst: *mut T, + } + + impl MergeState { + /// # Safety + /// The caller MUST guarantee that `self` is initialized in a way where `start -> end` is + /// the longer sub-slice and so that `dst` can be written to at least the shorter sub-slice + /// length times. In addition `start -> end` and `right -> right_end` MUST be valid to be + /// read. This function MUST only be called once. + unsafe fn merge_up bool>( + &mut self, + mut right: *const T, + right_end: *const T, + is_less: &mut F, + ) { + // SAFETY: See function safety comment. + unsafe { + let left = &mut self.start; + let out = &mut self.dst; + + while *left != self.end && right as *const T != right_end { + let consume_left = !is_less(&*right, &**left); + + let src = if consume_left { *left } else { right }; + ptr::copy_nonoverlapping(src, *out, 1); + + *left = left.add(consume_left as usize); + right = right.add(!consume_left as usize); + + *out = out.add(1); + } + } + } + + /// # Safety + /// The caller MUST guarantee that `self` is initialized in a way where `left_end <- dst` is + /// the shorter sub-slice and so that `out` can be written to at least the shorter sub-slice + /// length times. In addition `left_end <- dst` and `right_end <- end` MUST be valid to be + /// read. This function MUST only be called once. + unsafe fn merge_down bool>( + &mut self, + left_end: *const T, + right_end: *const T, + mut out: *mut T, + is_less: &mut F, + ) { + // SAFETY: See function safety comment. + unsafe { + loop { + let left = self.dst.sub(1); + let right = self.end.sub(1); + out = out.sub(1); + + let consume_left = is_less(&*right, &*left); + + let src = if consume_left { left } else { right }; + ptr::copy_nonoverlapping(src, out, 1); + + self.dst = left.add(!consume_left as usize); + self.end = right.add(consume_left as usize); + + if self.dst as *const T == left_end || self.end as *const T == right_end { + break; + } + } + } + } + } + + impl Drop for MergeState { + fn drop(&mut self) { + // SAFETY: The user of MergeState MUST ensure, that at any point this drop + // impl MAY run, for example when the user provided `is_less` panics, that + // copying the contiguous region between `start` and `end` to `dst` will + // leave the input slice `v` with each original element and all possible + // modifications observed. + unsafe { + let len = self.end.sub_ptr(self.start); + ptr::copy_nonoverlapping(self.start, self.dst, len); + } + } + } +} diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs new file mode 100644 index 000000000000..f9034e1b4d8e --- /dev/null +++ b/library/core/src/slice/sort/stable/mod.rs @@ -0,0 +1,116 @@ +//! This module contains the entry points for `slice::sort`. + +use crate::cmp; +use crate::intrinsics; +use crate::mem::{self, MaybeUninit, SizedTypeProperties}; + +use crate::slice::sort::shared::smallsort::{ + insertion_sort_shift_left, StableSmallSortTypeImpl, SMALL_SORT_GENERAL_SCRATCH_LEN, +}; + +pub(crate) mod drift; +pub(crate) mod merge; +pub(crate) mod quicksort; + +/// Stable sort called driftsort by Orson Peters and Lukas Bergdoll. +/// Design document: +/// https://github.com/Voultapher/sort-research-rs/blob/main/writeup/driftsort_introduction/text.md +/// +/// Upholds all safety properties outlined here: +/// https://github.com/Voultapher/sort-research-rs/blob/main/writeup/sort_safety/text.md +#[inline(always)] +pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less: &mut F) { + // Arrays of zero-sized types are always all-equal, and thus sorted. + if T::IS_ZST { + return; + } + + // Instrumenting the standard library showed that 90+% of the calls to sort + // by rustc are either of size 0 or 1. + let len = v.len(); + if intrinsics::likely(len < 2) { + return; + } + + // More advanced sorting methods than insertion sort are faster if called in + // a hot loop for small inputs, but for general-purpose code the small + // binary size of insertion sort is more important. The instruction cache in + // modern processors is very valuable, and for a single sort call in general + // purpose code any gains from an advanced method are cancelled by i-cache + // misses during the sort, and thrashing the i-cache for surrounding code. + const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; + if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { + insertion_sort_shift_left(v, 1, is_less); + return; + } + + driftsort_main::(v, is_less); +} + +/// See [`sort`] +/// +/// Deliberately don't inline the main sorting routine entrypoint to ensure the +/// inlined insertion sort i-cache footprint remains minimal. +#[inline(never)] +fn driftsort_main bool, BufT: BufGuard>(v: &mut [T], is_less: &mut F) { + // By allocating n elements of memory we can ensure the entire input can + // be sorted using stable quicksort, which allows better performance on + // random and low-cardinality distributions. However, we still want to + // reduce our memory usage to n / 2 for large inputs. We do this by scaling + // our allocation as max(n / 2, min(n, 8MB)), ensuring we scale like n for + // small inputs and n / 2 for large inputs, without a sudden drop off. We + // also need to ensure our alloc >= MIN_SMALL_SORT_SCRATCH_LEN, as the + // small-sort always needs this much memory. + const MAX_FULL_ALLOC_BYTES: usize = 8_000_000; // 8MB + let max_full_alloc = MAX_FULL_ALLOC_BYTES / mem::size_of::(); + let len = v.len(); + let alloc_len = + cmp::max(cmp::max(len / 2, cmp::min(len, max_full_alloc)), SMALL_SORT_GENERAL_SCRATCH_LEN); + + // For small inputs 4KiB of stack storage suffices, which allows us to avoid + // calling the (de-)allocator. Benchmarks showed this was quite beneficial. + let mut stack_buf = AlignedStorage::::new(); + let stack_scratch = stack_buf.as_uninit_slice_mut(); + let mut heap_buf; + let scratch = if stack_scratch.len() >= alloc_len { + stack_scratch + } else { + heap_buf = BufT::with_capacity(alloc_len); + heap_buf.as_uninit_slice_mut() + }; + + // For small inputs using quicksort is not yet beneficial, and a single + // small-sort or two small-sorts plus a single merge outperforms it, so use + // eager mode. + let eager_sort = len <= T::small_sort_threshold() * 2; + crate::slice::sort::stable::drift::sort(v, scratch, eager_sort, is_less); +} + +#[doc(hidden)] +/// Abstracts owned memory buffer, so that sort code can live in core where no allocation is +/// possible. This trait can then be implemented in a place that has access to allocation. +pub trait BufGuard { + /// Creates new buffer that holds at least `capacity` memory. + fn with_capacity(capacity: usize) -> Self; + /// Returns mutable access to uninitialized memory owned by the buffer. + fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit]; +} + +#[repr(C)] +struct AlignedStorage { + _align: [T; 0], + storage: [MaybeUninit; N], +} + +impl AlignedStorage { + fn new() -> Self { + Self { _align: [], storage: MaybeUninit::uninit_array() } + } + + fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit] { + let len = N / mem::size_of::(); + + // SAFETY: `_align` ensures we are correctly aligned. + unsafe { core::slice::from_raw_parts_mut(self.storage.as_mut_ptr().cast(), len) } + } +} diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs new file mode 100644 index 000000000000..b6d6d3ec8ea7 --- /dev/null +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -0,0 +1,267 @@ +//! This module contains a stable quicksort and partition implementation. + +use crate::intrinsics; +use crate::mem::{self, ManuallyDrop, MaybeUninit}; +use crate::ptr; + +use crate::slice::sort::shared::pivot::choose_pivot; +use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl; +use crate::slice::sort::shared::FreezeMarker; + +/// Sorts `v` recursively using quicksort. +/// +/// `limit` when initialized with `c*log(v.len())` for some c ensures we do not +/// overflow the stack or go quadratic. +#[inline(never)] +pub fn quicksort bool>( + mut v: &mut [T], + scratch: &mut [MaybeUninit], + mut limit: u32, + mut left_ancestor_pivot: Option<&T>, + is_less: &mut F, +) { + loop { + let len = v.len(); + + if len <= T::small_sort_threshold() { + T::small_sort(v, scratch, is_less); + return; + } + + if limit == 0 { + // We have had too many bad pivots, switch to O(n log n) fallback + // algorithm. In our case that is driftsort in eager mode. + crate::slice::sort::stable::drift::sort(v, scratch, true, is_less); + return; + } + limit -= 1; + + let pivot_pos = choose_pivot(v, is_less); + // SAFETY: choose_pivot promises to return a valid pivot index. + unsafe { + intrinsics::assume(pivot_pos < v.len()); + } + + // SAFETY: We only access the temporary copy for Freeze types, otherwise + // self-modifications via `is_less` would not be observed and this would + // be unsound. Our temporary copy does not escape this scope. + let pivot_copy = unsafe { ManuallyDrop::new(ptr::read(&v[pivot_pos])) }; + let pivot_ref = (!has_direct_interior_mutability::()).then_some(&*pivot_copy); + + // We choose a pivot, and check if this pivot is equal to our left + // ancestor. If true, we do a partition putting equal elements on the + // left and do not recurse on it. This gives O(n log k) sorting for k + // distinct values, a strategy borrowed from pdqsort. For types with + // interior mutability we can't soundly create a temporary copy of the + // ancestor pivot, and use left_partition_len == 0 as our method for + // detecting when we re-use a pivot, which means we do at most three + // partition operations with pivot p instead of the optimal two. + let mut perform_equal_partition = false; + if let Some(la_pivot) = left_ancestor_pivot { + perform_equal_partition = !is_less(la_pivot, &v[pivot_pos]); + } + + let mut left_partition_len = 0; + if !perform_equal_partition { + left_partition_len = stable_partition(v, scratch, pivot_pos, false, is_less); + perform_equal_partition = left_partition_len == 0; + } + + if perform_equal_partition { + let mid_eq = stable_partition(v, scratch, pivot_pos, true, &mut |a, b| !is_less(b, a)); + v = &mut v[mid_eq..]; + left_ancestor_pivot = None; + continue; + } + + // Process left side with the next loop iter, right side with recursion. + let (left, right) = v.split_at_mut(left_partition_len); + quicksort(right, scratch, limit, pivot_ref, is_less); + v = left; + } +} + +/// Partitions `v` using pivot `p = v[pivot_pos]` and returns the number of +/// elements less than `p`. The relative order of elements that compare < p and +/// those that compare >= p is preserved - it is a stable partition. +/// +/// If `is_less` is not a strict total order or panics, `scratch.len() < v.len()`, +/// or `pivot_pos >= v.len()`, the result and `v`'s state is sound but unspecified. +fn stable_partition bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + pivot_pos: usize, + pivot_goes_left: bool, + is_less: &mut F, +) -> usize { + let len = v.len(); + + if intrinsics::unlikely(scratch.len() < len || pivot_pos >= len) { + core::intrinsics::abort() + } + + let v_base = v.as_ptr(); + let scratch_base = MaybeUninit::slice_as_mut_ptr(scratch); + + // The core idea is to write the values that compare as less-than to the left + // side of `scratch`, while the values that compared as greater or equal than + // `v[pivot_pos]` go to the right side of `scratch` in reverse. See + // PartitionState for details. + + // SAFETY: see individual comments. + unsafe { + // SAFETY: we made sure the scratch has length >= len and that pivot_pos + // is in-bounds. v and scratch are disjoint slices. + let pivot = v_base.add(pivot_pos); + let mut state = PartitionState::new(v_base, scratch_base, len); + + let mut pivot_in_scratch = ptr::null_mut(); + let mut loop_end_pos = pivot_pos; + + // SAFETY: this loop is equivalent to calling state.partition_one + // exactly len times. + loop { + // Ideally the outer loop won't be unrolled, to save binary size, + // but we do want the inner loop to be unrolled for small types, as + // this gave significant performance boosts in benchmarks. Unrolling + // through for _ in 0..UNROLL_LEN { .. } instead of manually improves + // compile times but has a ~10-20% performance penalty on opt-level=s. + if const { mem::size_of::() <= 16 } { + const UNROLL_LEN: usize = 4; + let unroll_end = v_base.add(loop_end_pos.saturating_sub(UNROLL_LEN - 1)); + while state.scan < unroll_end { + state.partition_one(is_less(&*state.scan, &*pivot)); + state.partition_one(is_less(&*state.scan, &*pivot)); + state.partition_one(is_less(&*state.scan, &*pivot)); + state.partition_one(is_less(&*state.scan, &*pivot)); + } + } + + let loop_end = v_base.add(loop_end_pos); + while state.scan < loop_end { + state.partition_one(is_less(&*state.scan, &*pivot)); + } + + if loop_end_pos == len { + break; + } + + // We avoid comparing pivot with itself, as this could create deadlocks for + // certain comparison operators. We also store its location later for later. + pivot_in_scratch = state.partition_one(pivot_goes_left); + + loop_end_pos = len; + } + + // `pivot` must be copied into its correct position again, because a + // comparison operator might have modified it. + if has_direct_interior_mutability::() { + ptr::copy_nonoverlapping(pivot, pivot_in_scratch, 1); + } + + // SAFETY: partition_one being called exactly len times guarantees that scratch + // is initialized with a permuted copy of `v`, and that num_left <= v.len(). + // Copying scratch[0..num_left] and scratch[num_left..v.len()] back is thus + // sound, as the values in scratch will never be read again, meaning our copies + // semantically act as moves, permuting `v`. + + // Copy all the elements < p directly from swap to v. + let v_base = v.as_mut_ptr(); + ptr::copy_nonoverlapping(scratch_base, v_base, state.num_left); + + // Copy the elements >= p in reverse order. + for i in 0..len - state.num_left { + ptr::copy_nonoverlapping( + scratch_base.add(len - 1 - i), + v_base.add(state.num_left + i), + 1, + ); + } + + state.num_left + } +} + +struct PartitionState { + // The start of the scratch auxiliary memory. + scratch_base: *mut T, + // The current element that is being looked at, scans left to right through slice. + scan: *const T, + // Counts the number of elements that went to the left side, also works around: + // https://github.com/rust-lang/rust/issues/117128 + num_left: usize, + // Reverse scratch output pointer. + scratch_rev: *mut T, +} + +impl PartitionState { + /// # Safety + /// scan and scratch must point to valid disjoint buffers of length len. The + /// scan buffer must be initialized. + unsafe fn new(scan: *const T, scratch: *mut T, len: usize) -> Self { + // SAFETY: See function safety comment. + unsafe { Self { scratch_base: scratch, scan, num_left: 0, scratch_rev: scratch.add(len) } } + } + + /// Depending on the value of `towards_left` this function will write a value + /// to the growing left or right side of the scratch memory. This forms the + /// branchless core of the partition. + /// + /// # Safety + /// This function may be called at most `len` times. If it is called exactly + /// `len` times the scratch buffer then contains a copy of each element from + /// the scan buffer exactly once - a permutation, and num_left <= len. + unsafe fn partition_one(&mut self, towards_left: bool) -> *mut T { + // SAFETY: see individual comments. + unsafe { + // SAFETY: in-bounds because this function is called at most len times, and thus + // right now is incremented at most len - 1 times. Similarly, num_left < len and + // num_right < len, where num_right == i - num_left at the start of the ith + // iteration (zero-indexed). + self.scratch_rev = self.scratch_rev.sub(1); + + // SAFETY: now we have scratch_rev == base + len - (i + 1). This means + // scratch_rev + num_left == base + len - 1 - num_right < base + len. + let dst_base = if towards_left { self.scratch_base } else { self.scratch_rev }; + let dst = dst_base.add(self.num_left); + ptr::copy_nonoverlapping(self.scan, dst, 1); + + self.num_left += towards_left as usize; + self.scan = self.scan.add(1); + dst + } + } +} + +#[const_trait] +trait IsFreeze { + fn is_freeze() -> bool; +} + +impl const IsFreeze for T { + default fn is_freeze() -> bool { + false + } +} +impl const IsFreeze for T { + fn is_freeze() -> bool { + true + } +} + +#[must_use] +const fn has_direct_interior_mutability() -> bool { + // If a type has interior mutability it may alter itself during comparison + // in a way that must be preserved after the sort operation concludes. + // Otherwise a type like Mutex>> could lead to double free. + !T::is_freeze() +} + +#[test] +fn freeze_check() { + assert!(!has_direct_interior_mutability::()); + assert!(!has_direct_interior_mutability::<[u128; 2]>()); + + assert!(has_direct_interior_mutability::>()); + assert!(has_direct_interior_mutability::>()); +} diff --git a/library/core/src/slice/sort/unstable/heapsort.rs b/library/core/src/slice/sort/unstable/heapsort.rs new file mode 100644 index 000000000000..559605ef4b6b --- /dev/null +++ b/library/core/src/slice/sort/unstable/heapsort.rs @@ -0,0 +1,80 @@ +//! This module contains a branchless heapsort as fallback for unstable quicksort. + +use crate::intrinsics; +use crate::ptr; + +/// Sorts `v` using heapsort, which guarantees *O*(*n* \* log(*n*)) worst-case. +/// +/// Never inline this, it sits the main hot-loop in `recurse` and is meant as unlikely algorithmic +/// fallback. +/// +/// SAFETY: The caller has to guarantee that `v.len()` >= 2. +#[inline(never)] +pub(crate) unsafe fn heapsort(v: &mut [T], is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + // SAFETY: See function safety. + unsafe { + intrinsics::assume(v.len() >= 2); + + // Build the heap in linear time. + for i in (0..v.len() / 2).rev() { + sift_down(v, i, is_less); + } + + // Pop maximal elements from the heap. + for i in (1..v.len()).rev() { + v.swap(0, i); + sift_down(&mut v[..i], 0, is_less); + } + } +} + +// This binary heap respects the invariant `parent >= child`. +// +// SAFETY: The caller has to guarantee that node < `v.len()`. +#[inline(never)] +unsafe fn sift_down(v: &mut [T], mut node: usize, is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + // SAFETY: See function safety. + unsafe { + intrinsics::assume(node < v.len()); + } + + let len = v.len(); + + let v_base = v.as_mut_ptr(); + + loop { + // Children of `node`. + let mut child = 2 * node + 1; + if child >= len { + break; + } + + // SAFETY: The invariants and checks guarantee that both node and child are in-bounds. + unsafe { + // Choose the greater child. + if child + 1 < len { + // We need a branch to be sure not to out-of-bounds index, + // but it's highly predictable. The comparison, however, + // is better done branchless, especially for primitives. + child += is_less(&*v_base.add(child), &*v_base.add(child + 1)) as usize; + } + + // Stop if the invariant holds at `node`. + if !is_less(&*v_base.add(node), &*v_base.add(child)) { + break; + } + + // Swap `node` with the greater child, move one step down, and continue sifting. This + // could be ptr::swap_nonoverlapping but that adds a significant amount of binary-size. + ptr::swap(v_base.add(node), v_base.add(child)); + } + + node = child; + } +} diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs new file mode 100644 index 000000000000..0d0fbd74b388 --- /dev/null +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -0,0 +1,76 @@ +//! This module contains the entry points for `slice::sort_unstable`. + +use crate::intrinsics; +use crate::mem::SizedTypeProperties; + +use crate::slice::sort::shared::find_existing_run; +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. +/// Design document: +/// https://github.com/Voultapher/sort-research-rs/blob/main/writeup/ipnsort_introduction/text.md +/// +/// Upholds all safety properties outlined here: +/// https://github.com/Voultapher/sort-research-rs/blob/main/writeup/sort_safety/text.md +#[inline(always)] +pub fn sort bool>(v: &mut [T], is_less: &mut F) { + // Arrays of zero-sized types are always all-equal, and thus sorted. + if T::IS_ZST { + return; + } + + // Instrumenting the standard library showed that 90+% of the calls to sort + // by rustc are either of size 0 or 1. + let len = v.len(); + if intrinsics::likely(len < 2) { + return; + } + + // More advanced sorting methods than insertion sort are faster if called in + // a hot loop for small inputs, but for general-purpose code the small + // binary size of insertion sort is more important. The instruction cache in + // modern processors is very valuable, and for a single sort call in general + // purpose code any gains from an advanced method are cancelled by i-cache + // misses during the sort, and thrashing the i-cache for surrounding code. + const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; + if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { + insertion_sort_shift_left(v, 1, is_less); + return; + } + + ipnsort(v, is_less); +} + +/// See [`sort`] +/// +/// Deliberately don't inline the main sorting routine entrypoint to ensure the +/// inlined insertion sort i-cache footprint remains minimal. +#[inline(never)] +fn ipnsort(v: &mut [T], is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + let (run_len, was_reversed) = find_existing_run(v, is_less); + + // SAFETY: find_existing_run promises to return a valid run_len. + unsafe { intrinsics::assume(run_len <= len) }; + + if run_len == len { + if was_reversed { + v.reverse(); + } + + // It would be possible to a do in-place merging here for a long existing streak. But that + // makes the implementation a lot bigger, users can use `slice::sort` for that use-case. + return; + } + + // Limit the number of imbalanced partitions to `2 * floor(log2(len))`. + // The binary OR by one is used to eliminate the zero-check in the logarithm. + let limit = 2 * (len | 1).ilog2(); + crate::slice::sort::unstable::quicksort::quicksort(v, None, limit, is_less); +} diff --git a/library/core/src/slice/sort/unstable/quicksort.rs b/library/core/src/slice/sort/unstable/quicksort.rs new file mode 100644 index 000000000000..c3b4339d7047 --- /dev/null +++ b/library/core/src/slice/sort/unstable/quicksort.rs @@ -0,0 +1,347 @@ +//! This module contains an unstable quicksort and two partition implementations. + +use crate::intrinsics; +use crate::mem::{self, ManuallyDrop}; +use crate::ptr; + +use crate::slice::sort::shared::pivot::choose_pivot; +use crate::slice::sort::shared::smallsort::UnstableSmallSortTypeImpl; + +/// Sorts `v` recursively. +/// +/// If the slice had a predecessor in the original array, it is specified as `ancestor_pivot`. +/// +/// `limit` is the number of allowed imbalanced partitions before switching to `heapsort`. If zero, +/// this function will immediately switch to heapsort. +pub(crate) fn quicksort<'a, T, F>( + mut v: &'a mut [T], + mut ancestor_pivot: Option<&'a T>, + mut limit: u32, + is_less: &mut F, +) where + F: FnMut(&T, &T) -> bool, +{ + loop { + if v.len() <= T::small_sort_threshold() { + T::small_sort(v, is_less); + return; + } + + // If too many bad pivot choices were made, simply fall back to heapsort in order to + // guarantee `O(N x log(N))` worst-case. + if limit == 0 { + // SAFETY: We assume the `small_sort` threshold is at least 1. + unsafe { + crate::slice::sort::unstable::heapsort::heapsort(v, is_less); + } + return; + } + + limit -= 1; + + // Choose a pivot and try guessing whether the slice is already sorted. + let pivot_pos = choose_pivot(v, is_less); + + // If the chosen pivot is equal to the predecessor, then it's the smallest element in the + // slice. Partition the slice into elements equal to and elements greater than the pivot. + // This case is usually hit when the slice contains many duplicate elements. + if let Some(p) = ancestor_pivot { + // SAFETY: We assume choose_pivot yields an in-bounds position. + if !is_less(p, unsafe { v.get_unchecked(pivot_pos) }) { + let num_lt = partition(v, pivot_pos, &mut |a, b| !is_less(b, a)); + + // Continue sorting elements greater than the pivot. We know that `num_lt` contains + // the pivot. So we can continue after `num_lt`. + v = &mut v[(num_lt + 1)..]; + ancestor_pivot = None; + continue; + } + } + + // Partition the slice. + let num_lt = partition(v, pivot_pos, is_less); + // SAFETY: partition ensures that `num_lt` will be in-bounds. + unsafe { intrinsics::assume(num_lt < v.len()) }; + + // Split the slice into `left`, `pivot`, and `right`. + let (left, right) = v.split_at_mut(num_lt); + let (pivot, right) = right.split_at_mut(1); + let pivot = &pivot[0]; + + // Recurse into the left side. We have a fixed recursion limit, testing shows no real + // benefit for recursing into the shorter side. + quicksort(left, ancestor_pivot, limit, is_less); + + // Continue with the right side. + v = right; + ancestor_pivot = Some(pivot); + } +} + +/// Takes the input slice `v` and re-arranges elements such that when the call returns normally +/// all elements that compare true for `is_less(elem, pivot)` where `pivot == v[pivot_pos]` are +/// on the left side of `v` followed by the other elements, notionally considered greater or +/// equal to `pivot`. +/// +/// Returns the number of elements that are compared true for `is_less(elem, pivot)`. +/// +/// If `is_less` does not implement a total order the resulting order and return value are +/// unspecified. All original elements will remain in `v` and any possible modifications via +/// interior mutability will be observable. Same is true if `is_less` panics or `v.len()` +/// exceeds `scratch.len()`. +pub(crate) fn partition(v: &mut [T], pivot: usize, is_less: &mut F) -> usize +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + + // Allows for panic-free code-gen by proving this property to the compiler. + if len == 0 { + return 0; + } + + // Allows for panic-free code-gen by proving this property to the compiler. + if pivot >= len { + intrinsics::abort(); + } + + // Place the pivot at the beginning of slice. + v.swap(0, pivot); + let (pivot, v_without_pivot) = v.split_at_mut(1); + + // Assuming that Rust generates noalias LLVM IR we can be sure that a partition function + // signature of the form `(v: &mut [T], pivot: &T)` guarantees that pivot and v can't alias. + // Having this guarantee is crucial for optimizations. It's possible to copy the pivot value + // into a stack value, but this creates issues for types with interior mutability mandating + // a drop guard. + let pivot = &mut pivot[0]; + + // This construct is used to limit the LLVM IR generated, which saves large amounts of + // compile-time by only instantiating the code that is needed. Idea by Frank Steffahn. + let num_lt = (const { inst_partition::() })(v_without_pivot, pivot, is_less); + + // Place the pivot between the two partitions. + v.swap(0, num_lt); + + num_lt +} + +const fn inst_partition bool>() -> fn(&mut [T], &T, &mut F) -> usize { + const MAX_BRANCHLESS_PARTITION_SIZE: usize = 96; + if mem::size_of::() <= MAX_BRANCHLESS_PARTITION_SIZE { + // Specialize for types that are relatively cheap to copy, where branchless optimizations + // have large leverage e.g. `u64` and `String`. + partition_lomuto_branchless_cyclic:: + } else { + partition_hoare_branchy_cyclic:: + } +} + +/// See [`partition`]. +fn partition_hoare_branchy_cyclic(v: &mut [T], pivot: &T, is_less: &mut F) -> usize +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + + if len == 0 { + return 0; + } + + // Optimized for large types that are expensive to move. Not optimized for integers. Optimized + // for small code-gen, assuming that is_less is an expensive operation that generates + // substantial amounts of code or a call. And that copying elements will likely be a call to + // memcpy. Using 2 `ptr::copy_nonoverlapping` has the chance to be faster than + // `ptr::swap_nonoverlapping` because `memcpy` can use wide SIMD based on runtime feature + // detection. Benchmarks support this analysis. + + let mut gap_opt: Option> = None; + + // SAFETY: The left-to-right scanning loop performs a bounds check, where we know that `left >= + // v_base && left < right && right <= v_base.add(len)`. The right-to-left scanning loop performs + // a bounds check ensuring that `right` is in-bounds. We checked that `len` is more than zero, + // which means that unconditional `right = right.sub(1)` is safe to do. The exit check makes + // sure that `left` and `right` never alias, making `ptr::copy_nonoverlapping` safe. The + // drop-guard `gap` ensures that should `is_less` panic we always overwrite the duplicate in the + // input. `gap.pos` stores the previous value of `right` and starts at `right` and so it too is + // in-bounds. We never pass the saved `gap.value` to `is_less` while it is inside the `GapGuard` + // thus any changes via interior mutability will be observed. + unsafe { + let v_base = v.as_mut_ptr(); + + let mut left = v_base; + let mut right = v_base.add(len); + + loop { + // Find the first element greater than the pivot. + while left < right && is_less(&*left, pivot) { + left = left.add(1); + } + + // Find the last element equal to the pivot. + loop { + right = right.sub(1); + if left >= right || is_less(&*right, pivot) { + break; + } + } + + if left >= right { + break; + } + + // Swap the found pair of out-of-order elements via cyclic permutation. + let is_first_swap_pair = gap_opt.is_none(); + + if is_first_swap_pair { + gap_opt = Some(GapGuard { pos: right, value: ManuallyDrop::new(ptr::read(left)) }); + } + + let gap = gap_opt.as_mut().unwrap_unchecked(); + + // Single place where we instantiate ptr::copy_nonoverlapping in the partition. + if !is_first_swap_pair { + ptr::copy_nonoverlapping(left, gap.pos, 1); + } + gap.pos = right; + ptr::copy_nonoverlapping(right, left, 1); + + left = left.add(1); + } + + left.sub_ptr(v_base) + + // `gap_opt` goes out of scope and overwrites the last wrong-side element on the right side + // with the first wrong-side element of the left side that was initially overwritten by the + // first wrong-side element on the right side element. + } +} + +struct PartitionState { + // The current element that is being looked at, scans left to right through slice. + right: *mut T, + // Counts the number of elements that compared less-than, also works around: + // https://github.com/rust-lang/rust/issues/117128 + num_lt: usize, + // Gap guard that tracks the temporary duplicate in the input. + gap: GapGuardRaw, +} + +fn partition_lomuto_branchless_cyclic(v: &mut [T], pivot: &T, is_less: &mut F) -> usize +where + F: FnMut(&T, &T) -> bool, +{ + // Novel partition implementation by Lukas Bergdoll and Orson Peters. Branchless Lomuto + // partition paired with a cyclic permutation. + // https://github.com/Voultapher/sort-research-rs/blob/main/writeup/lomcyc_partition/text.md + + let len = v.len(); + let v_base = v.as_mut_ptr(); + + if len == 0 { + return 0; + } + + // SAFETY: We checked that `len` is more than zero, which means that reading `v_base` is safe to + // do. From there we have a bounded loop where `v_base.add(i)` is guaranteed in-bounds. `v` and + // `pivot` can't alias because of type system rules. The drop-guard `gap` ensures that should + // `is_less` panic we always overwrite the duplicate in the input. `gap.pos` stores the previous + // value of `right` and starts at `v_base` and so it too is in-bounds. Given `UNROLL_LEN == 2` + // after the main loop we either have A) the last element in `v` that has not yet been processed + // because `len % 2 != 0`, or B) all elements have been processed except the gap value that was + // saved at the beginning with `ptr::read(v_base)`. In the case A) the loop will iterate twice, + // first performing loop_body to take care of the last element that didn't fit into the unroll. + // After that the behavior is the same as for B) where we use the saved value as `right` to + // overwrite the duplicate. If this very last call to `is_less` panics the saved value will be + // copied back including all possible changes via interior mutability. If `is_less` does not + // panic and the code continues we overwrite the duplicate and do `right = right.add(1)`, this + // is safe to do with `&mut *gap.value` because `T` is the same as `[T; 1]` and generating a + // pointer one past the allocation is safe. + unsafe { + let mut loop_body = |state: &mut PartitionState| { + let right_is_lt = is_less(&*state.right, pivot); + let left = v_base.add(state.num_lt); + + ptr::copy(left, state.gap.pos, 1); + ptr::copy_nonoverlapping(state.right, left, 1); + + state.gap.pos = state.right; + state.num_lt += right_is_lt as usize; + + state.right = state.right.add(1); + }; + + // Ideally we could just use GapGuard in PartitionState, but the reference that is + // materialized with `&mut state` when calling `loop_body` would create a mutable reference + // to the parent struct that contains the gap value, invalidating the reference pointer + // created from a reference to the gap value in the cleanup loop. This is only an issue + // under Stacked Borrows, Tree Borrows accepts the intuitive code using GapGuard as valid. + let mut gap_value = ManuallyDrop::new(ptr::read(v_base)); + + let mut state = PartitionState { + num_lt: 0, + right: v_base.add(1), + + gap: GapGuardRaw { pos: v_base, value: &mut *gap_value }, + }; + + // Manual unrolling that works well on x86, Arm and with opt-level=s without murdering + // compile-times. Leaving this to the compiler yields ok to bad results. + let unroll_len = const { if mem::size_of::() <= 16 { 2 } else { 1 } }; + + let unroll_end = v_base.add(len - (unroll_len - 1)); + while state.right < unroll_end { + if unroll_len == 2 { + loop_body(&mut state); + loop_body(&mut state); + } else { + loop_body(&mut state); + } + } + + // Single instantiate `loop_body` for both the unroll cleanup and cyclic permutation + // cleanup. Optimizes binary-size and compile-time. + let end = v_base.add(len); + loop { + let is_done = state.right == end; + state.right = if is_done { state.gap.value } else { state.right }; + + loop_body(&mut state); + + if is_done { + mem::forget(state.gap); + break; + } + } + + state.num_lt + } +} + +struct GapGuard { + pos: *mut T, + value: ManuallyDrop, +} + +impl Drop for GapGuard { + fn drop(&mut self) { + unsafe { + ptr::copy_nonoverlapping(&*self.value, self.pos, 1); + } + } +} + +/// Ideally this wouldn't be needed and we could just use the regular GapGuard. +/// See comment in [`partition_lomuto_branchless_cyclic`]. +struct GapGuardRaw { + pos: *mut T, + value: *mut T, +} + +impl Drop for GapGuardRaw { + fn drop(&mut self) { + unsafe { + ptr::copy_nonoverlapping(self.value, self.pos, 1); + } + } +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 797108a8425d..87e48354dc12 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -49,7 +49,6 @@ #![feature(is_sorted)] #![feature(layout_for_ptr)] #![feature(pattern)] -#![feature(sort_internals)] #![feature(slice_take)] #![feature(slice_from_ptr_range)] #![feature(slice_split_once)] diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index ffe8ffcc7f2f..3870fcb5f0f5 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -1803,9 +1803,8 @@ fn brute_force_rotate_test_1() { #[test] #[cfg(not(target_arch = "wasm32"))] fn sort_unstable() { - use core::cmp::Ordering::{Equal, Greater, Less}; - use core::slice::heapsort; - use rand::{seq::SliceRandom, Rng}; + // use core::cmp::Ordering::{Equal, Greater, Less}; + use rand::Rng; // Miri is too slow (but still need to `chain` to make the types match) let lens = if cfg!(miri) { (2..20).chain(0..0) } else { (2..25).chain(500..510) }; @@ -1839,31 +1838,10 @@ fn sort_unstable() { tmp.copy_from_slice(v); tmp.sort_unstable_by(|a, b| b.cmp(a)); assert!(tmp.windows(2).all(|w| w[0] >= w[1])); - - // Test heapsort using `<` operator. - tmp.copy_from_slice(v); - heapsort(tmp, |a, b| a < b); - assert!(tmp.windows(2).all(|w| w[0] <= w[1])); - - // Test heapsort using `>` operator. - tmp.copy_from_slice(v); - heapsort(tmp, |a, b| a > b); - assert!(tmp.windows(2).all(|w| w[0] >= w[1])); } } } - // Sort using a completely random comparison function. - // This will reorder the elements *somehow*, but won't panic. - for i in 0..v.len() { - v[i] = i as i32; - } - v.sort_unstable_by(|_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); - v.sort_unstable(); - for i in 0..v.len() { - assert_eq!(v[i], i as i32); - } - // Should not panic. [0i32; 0].sort_unstable(); [(); 10].sort_unstable(); From 1a6b0e410e3b32f8641b9ee2bcf992712c8fa72f Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Tue, 16 Apr 2024 20:44:01 +0200 Subject: [PATCH 003/147] Fix tidy errors --- library/core/src/slice/sort/select.rs | 5 ++++- library/core/src/slice/sort/shared/smallsort.rs | 15 +++++---------- library/core/src/slice/sort/stable/quicksort.rs | 9 --------- library/core/src/slice/sort/unstable/quicksort.rs | 4 ++++ library/core/tests/slice.rs | 1 - .../src/library-features/sort-internals.md | 5 ----- 6 files changed, 13 insertions(+), 26 deletions(-) delete mode 100644 src/doc/unstable-book/src/library-features/sort-internals.md diff --git a/library/core/src/slice/sort/select.rs b/library/core/src/slice/sort/select.rs index e0c1085916e5..6212def30416 100644 --- a/library/core/src/slice/sort/select.rs +++ b/library/core/src/slice/sort/select.rs @@ -92,7 +92,10 @@ fn partition_at_index_loop<'a, T, F>( // slice. Partition the slice into elements equal to and elements greater than the pivot. // This case is usually hit when the slice contains many duplicate elements. if let Some(p) = ancestor_pivot { - if !is_less(p, unsafe { v.get_unchecked(pivot_pos) }) { + // SAFETY: choose_pivot promises to return a valid pivot position. + let pivot = unsafe { v.get_unchecked(pivot_pos) }; + + if !is_less(p, pivot) { let num_lt = partition(v, pivot_pos, &mut |a, b| !is_less(b, a)); // Continue sorting elements greater than the pivot. We know that `mid` contains diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 8dbd45a389ca..6e4424310e80 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -177,6 +177,8 @@ fn small_sort_fallback bool>(v: &mut [T], is_less: &mut F fn small_sort_general bool>(v: &mut [T], is_less: &mut F) { let mut stack_array = MaybeUninit::<[T; SMALL_SORT_GENERAL_SCRATCH_LEN]>::uninit(); + // SAFETY: The memory is backed by `stack_array`, and the operation is safe as long as the len + // is the same. let scratch = unsafe { slice::from_raw_parts_mut( stack_array.as_mut_ptr() as *mut MaybeUninit, @@ -327,8 +329,9 @@ where } // SAFETY: The right side of `v` based on `len_div_2` is guaranteed in-bounds. - region = - unsafe { &mut *ptr::slice_from_raw_parts_mut(v_base.add(len_div_2), len - len_div_2) }; + unsafe { + region = &mut *ptr::slice_from_raw_parts_mut(v_base.add(len_div_2), len - len_div_2) + }; } // SAFETY: We checked that T is Freeze and thus observation safe. @@ -812,14 +815,6 @@ pub(crate) const fn has_efficient_in_place_swap() -> bool { mem::size_of::() <= 8 // mem::size_of::() } -#[test] -fn type_info() { - assert!(has_efficient_in_place_swap::()); - assert!(has_efficient_in_place_swap::()); - assert!(!has_efficient_in_place_swap::()); - assert!(!has_efficient_in_place_swap::()); -} - /// SAFETY: Only used for run-time optimization heuristic. #[rustc_unsafe_specialization_marker] trait CopyMarker {} diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs index b6d6d3ec8ea7..e1734ce8d8b4 100644 --- a/library/core/src/slice/sort/stable/quicksort.rs +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -256,12 +256,3 @@ const fn has_direct_interior_mutability() -> bool { // Otherwise a type like Mutex>> could lead to double free. !T::is_freeze() } - -#[test] -fn freeze_check() { - assert!(!has_direct_interior_mutability::()); - assert!(!has_direct_interior_mutability::<[u128; 2]>()); - - assert!(has_direct_interior_mutability::>()); - assert!(has_direct_interior_mutability::>()); -} diff --git a/library/core/src/slice/sort/unstable/quicksort.rs b/library/core/src/slice/sort/unstable/quicksort.rs index c3b4339d7047..533b5b0eec76 100644 --- a/library/core/src/slice/sort/unstable/quicksort.rs +++ b/library/core/src/slice/sort/unstable/quicksort.rs @@ -325,6 +325,8 @@ struct GapGuard { impl Drop for GapGuard { fn drop(&mut self) { + // SAFETY: `self` MUST be constructed in a way that makes copying the gap value into + // `self.pos` sound. unsafe { ptr::copy_nonoverlapping(&*self.value, self.pos, 1); } @@ -340,6 +342,8 @@ struct GapGuardRaw { impl Drop for GapGuardRaw { fn drop(&mut self) { + // SAFETY: `self` MUST be constructed in a way that makes copying the gap value into + // `self.pos` sound. unsafe { ptr::copy_nonoverlapping(self.value, self.pos, 1); } diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index 3870fcb5f0f5..1fe92afb6bb4 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -1803,7 +1803,6 @@ fn brute_force_rotate_test_1() { #[test] #[cfg(not(target_arch = "wasm32"))] fn sort_unstable() { - // use core::cmp::Ordering::{Equal, Greater, Less}; use rand::Rng; // Miri is too slow (but still need to `chain` to make the types match) diff --git a/src/doc/unstable-book/src/library-features/sort-internals.md b/src/doc/unstable-book/src/library-features/sort-internals.md deleted file mode 100644 index 6f2385e53008..000000000000 --- a/src/doc/unstable-book/src/library-features/sort-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `sort_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- From d20b1190cfb92faab93e0528555753c3823d28d0 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Tue, 16 Apr 2024 20:59:17 +0200 Subject: [PATCH 004/147] Move BufGuard impl outside of function --- library/alloc/src/slice.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 104f4ee753b1..79540fcd3955 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -864,18 +864,17 @@ fn stable_sort(v: &mut [T], mut is_less: F) where F: FnMut(&T, &T) -> bool, { - use sort::stable::BufGuard; - - #[unstable(issue = "none", feature = "std_internals")] - impl BufGuard for Vec { - fn with_capacity(capacity: usize) -> Self { - Vec::with_capacity(capacity) - } - - fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit] { - self.spare_capacity_mut() - } - } - sort::stable::sort::>(v, &mut is_less); } + +#[cfg(not(no_global_oom_handling))] +#[unstable(issue = "none", feature = "std_internals")] +impl sort::stable::BufGuard for Vec { + fn with_capacity(capacity: usize) -> Self { + Vec::with_capacity(capacity) + } + + fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit] { + self.spare_capacity_mut() + } +} From 8300f67c6e6b4fd29e04dcea7d0c011ee4a6decc Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Tue, 16 Apr 2024 21:31:27 +0200 Subject: [PATCH 005/147] Turn bare links into automatic links --- library/core/src/slice/sort/stable/mod.rs | 4 ++-- library/core/src/slice/sort/unstable/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index f9034e1b4d8e..3d3fe2e70b7b 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -14,10 +14,10 @@ pub(crate) mod quicksort; /// Stable sort called driftsort by Orson Peters and Lukas Bergdoll. /// Design document: -/// https://github.com/Voultapher/sort-research-rs/blob/main/writeup/driftsort_introduction/text.md +/// /// /// Upholds all safety properties outlined here: -/// https://github.com/Voultapher/sort-research-rs/blob/main/writeup/sort_safety/text.md +/// #[inline(always)] pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less: &mut F) { // Arrays of zero-sized types are always all-equal, and thus sorted. diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index 0d0fbd74b388..692c2d8f7c7b 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -11,10 +11,10 @@ pub(crate) mod quicksort; /// Unstable sort called ipnsort by Lukas Bergdoll. /// Design document: -/// https://github.com/Voultapher/sort-research-rs/blob/main/writeup/ipnsort_introduction/text.md +/// /// /// Upholds all safety properties outlined here: -/// https://github.com/Voultapher/sort-research-rs/blob/main/writeup/sort_safety/text.md +/// #[inline(always)] pub fn sort bool>(v: &mut [T], is_less: &mut F) { // Arrays of zero-sized types are always all-equal, and thus sorted. From 88fb5edb6525bd198208e0b459f70c9ddfb6e558 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Wed, 17 Apr 2024 17:50:05 +0200 Subject: [PATCH 006/147] Fix linkchecker doc errors Also includes small doc fixes. --- library/alloc/src/slice.rs | 6 +++--- library/core/src/slice/mod.rs | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 79540fcd3955..846f8ba6c6e4 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -193,7 +193,7 @@ impl [T] { /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which /// combines the fast average case of quicksort with the fast worst case and partial run /// detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs - /// with k distinct elements, the expected time to sort the data is *O(*n* log(*k*))*. + /// with k distinct elements, the expected time to sort the data is *O(*n* \* log(*k*))*. /// /// The auxiliary memory allocation behavior depends on the input length. Short slices are /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it @@ -252,7 +252,7 @@ impl [T] { /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which /// combines the fast average case of quicksort with the fast worst case and partial run /// detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs - /// with k distinct elements, the expected time to sort the data is *O(*n* log(*k*))*. + /// with k distinct elements, the expected time to sort the data is *O(*n* \* log(*k*))*. /// /// The auxiliary memory allocation behavior depends on the input length. Short slices are /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it @@ -298,7 +298,7 @@ impl [T] { /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which /// combines the fast average case of quicksort with the fast worst case and partial run /// detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs - /// with k distinct elements, the expected time to sort the data is *O(*n* log(*k*))*. + /// with k distinct elements, the expected time to sort the data is *O(*n* \* log(*k*))*. /// /// The auxiliary memory allocation behavior depends on the input length. Short slices are /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 4e6070cdb1b5..3c818eacca31 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2893,7 +2893,7 @@ impl [T] { /// The current implementation is based on [ipnsort] by Lukas Bergdoll and Orson Peters, which /// combines the fast average case of quicksort with the fast worst case of heapsort, achieving /// linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the - /// expected time to sort the data is *O(*n* log(*k*))*. + /// expected time to sort the data is *O(*n* \* log(*k*))*. /// /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. @@ -2950,7 +2950,7 @@ impl [T] { /// The current implementation is based on [ipnsort] by Lukas Bergdoll and Orson Peters, which /// combines the fast average case of quicksort with the fast worst case of heapsort, achieving /// linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the - /// expected time to sort the data is *O(*n* log(*k*))*. + /// expected time to sort the data is *O(*n* \* log(*k*))*. /// /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. @@ -2994,7 +2994,7 @@ impl [T] { /// The current implementation is based on [ipnsort] by Lukas Bergdoll and Orson Peters, which /// combines the fast average case of quicksort with the fast worst case of heapsort, achieving /// linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the - /// expected time to sort the data is *O(*n* log(*k*))*. + /// expected time to sort the data is *O(*n* \* log(*k*))*. /// /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. @@ -3042,8 +3042,8 @@ impl [T] { /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime /// for all inputs. /// - /// It is typically faster than sorting, except in a few special cases, e.g., when the slice is - /// nearly fully sorted, where [`slice::sort`] may be faster. + /// 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 /// @@ -3103,8 +3103,8 @@ impl [T] { /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime /// for all inputs. /// - /// It is typically faster than sorting, except in a few special cases, e.g., when the slice is - /// nearly fully sorted, where [`slice::sort`] may be faster. + /// 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 /// @@ -3168,8 +3168,8 @@ impl [T] { /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime /// for all inputs. /// - /// It is typically faster than sorting, except in a few special cases, e.g., when the slice is - /// nearly fully sorted, where [`slice::sort`] may be faster. + /// 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 /// From 586821eacd7389a4a30b60633ceb53596d06e238 Mon Sep 17 00:00:00 2001 From: bohan Date: Sat, 1 Jun 2024 17:43:48 +0800 Subject: [PATCH 007/147] tip for inaccessible traits --- .../rustc_hir_typeck/src/method/suggest.rs | 189 +++++++++++++----- tests/ui/traits/item-privacy.stderr | 15 +- 2 files changed, 145 insertions(+), 59 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index daaaf630f2c4..e2b60bd52b26 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -284,14 +284,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if !candidates.is_empty() { let help = format!( - "{an}other candidate{s} {were} found in the following trait{s}, perhaps \ - add a `use` for {one_of_them}:", + "{an}other candidate{s} {were} found in the following trait{s}", an = if candidates.len() == 1 { "an" } else { "" }, s = pluralize!(candidates.len()), were = pluralize!("was", candidates.len()), - one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" }, ); - self.suggest_use_candidates(&mut err, help, candidates); + self.suggest_use_candidates( + candidates, + |accessible_sugg, inaccessible_sugg, span| { + let suggest_for_access = + |err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| { + msg += &format!( + ", perhaps add a `use` for {one_of_them}:", + one_of_them = + if sugg.len() == 1 { "it" } else { "one_of_them" }, + ); + err.span_suggestions( + span, + msg, + sugg, + Applicability::MaybeIncorrect, + ); + }; + let suggest_for_privacy = + |err: &mut Diag<'_>, mut msg: String, sugg: Vec| { + if sugg.len() == 1 { + let msg = format!("\ + trait `{}` provides `{item_name}` is implemented but not reachable", + sugg[0].trim() + ); + err.help(msg); + } else { + msg += &format!(" but {} not reachable", pluralize!("is", sugg.len())); + err.span_suggestions( + span, + msg, + sugg, + Applicability::MaybeIncorrect, + ); + } + }; + if accessible_sugg.is_empty() { + // `inaccessible_sugg` must not be empty + suggest_for_privacy(&mut err, help, inaccessible_sugg); + } else if inaccessible_sugg.is_empty() { + suggest_for_access(&mut err, help, accessible_sugg); + } else { + suggest_for_access(&mut err, help.clone(), accessible_sugg); + suggest_for_privacy(&mut err, help, inaccessible_sugg); + } + }, + ); } if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() { if needs_mut { @@ -3069,49 +3112,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn suggest_use_candidates(&self, err: &mut Diag<'_>, msg: String, candidates: Vec) { + fn suggest_use_candidates(&self, candidates: Vec, handle_candidates: F) + where + F: FnOnce(Vec, Vec, Span), + { let parent_map = self.tcx.visible_parent_map(()); - // Separate out candidates that must be imported with a glob, because they are named `_` - // and cannot be referred with their identifier. - let (candidates, globs): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|trait_did| { - if let Some(parent_did) = parent_map.get(trait_did) { - // If the item is re-exported as `_`, we should suggest a glob-import instead. - if *parent_did != self.tcx.parent(*trait_did) - && self - .tcx - .module_children(*parent_did) - .iter() - .filter(|child| child.res.opt_def_id() == Some(*trait_did)) - .all(|child| child.ident.name == kw::Underscore) - { - return false; - } - } + let scope = self.tcx.parent_module_from_def_id(self.body_id); + let (accessible_candidates, inaccessible_candidates): (Vec<_>, Vec<_>) = + candidates.into_iter().partition(|id| { + let vis = self.tcx.visibility(*id); + vis.is_accessible_from(scope, self.tcx) + }); - true - }); + let sugg = |candidates: Vec<_>, visible| { + // Separate out candidates that must be imported with a glob, because they are named `_` + // and cannot be referred with their identifier. + let (candidates, globs): (Vec<_>, Vec<_>) = + candidates.into_iter().partition(|trait_did| { + if let Some(parent_did) = parent_map.get(trait_did) { + // If the item is re-exported as `_`, we should suggest a glob-import instead. + if *parent_did != self.tcx.parent(*trait_did) + && self + .tcx + .module_children(*parent_did) + .iter() + .filter(|child| child.res.opt_def_id() == Some(*trait_did)) + .all(|child| child.ident.name == kw::Underscore) + { + return false; + } + } - let module_did = self.tcx.parent_module_from_def_id(self.body_id); - let (module, _, _) = self.tcx.hir().get_module(module_did); + true + }); + + let prefix = if visible { "use " } else { "" }; + let postfix = if visible { ";" } else { "" }; + let path_strings = candidates.iter().map(|trait_did| { + format!( + "{prefix}{}{postfix}\n", + with_crate_prefix!(self.tcx.def_path_str(*trait_did)), + ) + }); + + let glob_path_strings = globs.iter().map(|trait_did| { + let parent_did = parent_map.get(trait_did).unwrap(); + format!( + "{prefix}{}::*{postfix} // trait {}\n", + with_crate_prefix!(self.tcx.def_path_str(*parent_did)), + self.tcx.item_name(*trait_did), + ) + }); + let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect(); + sugg.sort(); + sugg + }; + + let accessible_sugg = sugg(accessible_candidates, true); + let inaccessible_sugg = sugg(inaccessible_candidates, false); + + let (module, _, _) = self.tcx.hir().get_module(scope); let span = module.spans.inject_use_span; - - let path_strings = candidates.iter().map(|trait_did| { - format!("use {};\n", with_crate_prefix!(self.tcx.def_path_str(*trait_did)),) - }); - - let glob_path_strings = globs.iter().map(|trait_did| { - let parent_did = parent_map.get(trait_did).unwrap(); - format!( - "use {}::*; // trait {}\n", - with_crate_prefix!(self.tcx.def_path_str(*parent_did)), - self.tcx.item_name(*trait_did), - ) - }); - let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect(); - sugg.sort(); - - err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect); + handle_candidates(accessible_sugg, inaccessible_sugg, span); } fn suggest_valid_traits( @@ -3135,9 +3198,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if explain { err.help("items from traits can only be used if the trait is in scope"); } + let msg = format!( - "{this_trait_is} implemented but not in scope; perhaps you want to import \ - {one_of_them}", + "{this_trait_is} implemented but not in scope", this_trait_is = if candidates.len() == 1 { format!( "trait `{}` which provides `{item_name}` is", @@ -3145,11 +3208,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } else { format!("the following traits which provide `{item_name}` are") - }, - one_of_them = if candidates.len() == 1 { "it" } else { "one of them" }, + } ); - self.suggest_use_candidates(err, msg, candidates); + self.suggest_use_candidates(candidates, |accessible_sugg, inaccessible_sugg, span| { + let suggest_for_access = |err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| { + msg += &format!( + "; perhaps you want to import {one_of}", + one_of = if sugg.len() == 1 { "it" } else { "one of them" }, + ); + err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect); + }; + let suggest_for_privacy = |err: &mut Diag<'_>, sugg: Vec| { + let msg = format!( + "{this_trait_is} implemented but not reachable", + this_trait_is = if sugg.len() == 1 { + format!("trait `{}` which provides `{item_name}` is", sugg[0].trim()) + } else { + format!("the following traits which provide `{item_name}` are") + } + ); + if sugg.len() == 1 { + err.help(msg); + } else { + err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect); + } + }; + if accessible_sugg.is_empty() { + // `inaccessible_sugg` must not be empty + suggest_for_privacy(err, inaccessible_sugg); + } else if inaccessible_sugg.is_empty() { + suggest_for_access(err, msg, accessible_sugg); + } else { + suggest_for_access(err, msg, accessible_sugg); + suggest_for_privacy(err, inaccessible_sugg); + } + }); + if let Some(did) = edition_fix { err.note(format!( "'{}' is included in the prelude starting in Edition 2021", diff --git a/tests/ui/traits/item-privacy.stderr b/tests/ui/traits/item-privacy.stderr index d08bb4745bf5..fca7473f3bc3 100644 --- a/tests/ui/traits/item-privacy.stderr +++ b/tests/ui/traits/item-privacy.stderr @@ -8,10 +8,7 @@ LL | S.a(); | ^ | = help: items from traits can only be used if the trait is implemented and in scope -help: trait `A` which provides `a` is implemented but not in scope; perhaps you want to import it - | -LL + use method::A; - | + = help: trait `method::A` which provides `a` is implemented but not reachable help: there is a method `b` with a similar name | LL | S.b(); @@ -58,15 +55,12 @@ LL | S::a(&S); | ^ function or associated item not found in `S` | = help: items from traits can only be used if the trait is implemented and in scope + = help: trait `method::A` which provides `a` is implemented but not reachable help: there is an associated constant `B` with a similar name --> $DIR/item-privacy.rs:29:9 | LL | const B: u8 = 0; | ^^^^^^^^^^^ -help: trait `A` which provides `a` is implemented but not in scope; perhaps you want to import it - | -LL + use method::A; - | error[E0599]: no function or associated item named `b` found for struct `S` in the current scope --> $DIR/item-privacy.rs:80:8 @@ -107,10 +101,7 @@ LL | S::A; | ^ associated item not found in `S` | = help: items from traits can only be used if the trait is implemented and in scope -help: trait `A` which provides `A` is implemented but not in scope; perhaps you want to import it - | -LL + use assoc_const::A; - | + = help: trait `assoc_const::A` which provides `A` is implemented but not reachable help: there is an associated constant `B` with a similar name | LL | S::B; From acd6e4c0fd10ac356d2bddbf47595748d0e3f2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Fri, 3 May 2024 10:28:09 +0000 Subject: [PATCH 008/147] tests(coverage): Add mcdc_non_control_flow tests --- tests/coverage/mcdc_non_control_flow.cov-map | 199 ++++++++++++++++++ tests/coverage/mcdc_non_control_flow.coverage | 98 +++++++++ tests/coverage/mcdc_non_control_flow.rs | 72 +++++++ 3 files changed, 369 insertions(+) create mode 100644 tests/coverage/mcdc_non_control_flow.cov-map create mode 100644 tests/coverage/mcdc_non_control_flow.coverage create mode 100644 tests/coverage/mcdc_non_control_flow.rs diff --git a/tests/coverage/mcdc_non_control_flow.cov-map b/tests/coverage/mcdc_non_control_flow.cov-map new file mode 100644 index 000000000000..3f422d85e799 --- /dev/null +++ b/tests/coverage/mcdc_non_control_flow.cov-map @@ -0,0 +1,199 @@ +Function name: mcdc_non_control_flow::assign_3 +Raw bytes (73): 0x[01, 01, 09, 05, 07, 0b, 11, 09, 0d, 01, 05, 01, 05, 22, 11, 01, 05, 22, 11, 01, 05, 09, 01, 16, 01, 00, 28, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 22, 00, 0d, 00, 0e, 22, 00, 12, 00, 13, 20, 1e, 11, 00, 12, 00, 13, 1e, 00, 17, 00, 18, 20, 09, 0d, 00, 17, 00, 18, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 9 +- expression 0 operands: lhs = Counter(1), rhs = Expression(1, Add) +- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(4) +- expression 2 operands: lhs = Counter(2), rhs = Counter(3) +- expression 3 operands: lhs = Counter(0), rhs = Counter(1) +- expression 4 operands: lhs = Counter(0), rhs = Counter(1) +- expression 5 operands: lhs = Expression(8, Sub), rhs = Counter(4) +- expression 6 operands: lhs = Counter(0), rhs = Counter(1) +- expression 7 operands: lhs = Expression(8, Sub), rhs = Counter(4) +- expression 8 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 9 +- Code(Counter(0)) at (prev + 22, 1) to (start + 0, 40) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = (c1 + ((c2 + c3) + c4)) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(1), false: Expression(8, Sub) } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Expression(8, Sub)) at (prev + 0, 18) to (start + 0, 19) + = (c0 - c1) +- Branch { true: Expression(7, Sub), false: Counter(4) } at (prev + 0, 18) to (start + 0, 19) + true = ((c0 - c1) - c4) + false = c4 +- Code(Expression(7, Sub)) at (prev + 0, 23) to (start + 0, 24) + = ((c0 - c1) - c4) +- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 23) to (start + 0, 24) + true = c2 + false = c3 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = (c1 + ((c2 + c3) + c4)) + +Function name: mcdc_non_control_flow::assign_3_bis +Raw bytes (82): 0x[01, 01, 07, 07, 11, 09, 0d, 01, 05, 05, 09, 16, 1a, 05, 09, 01, 05, 0a, 01, 1b, 01, 00, 2c, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 02, 00, 0d, 00, 13, 30, 05, 1a, 01, 02, 00, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 30, 09, 16, 02, 00, 00, 00, 12, 00, 13, 13, 00, 17, 00, 18, 20, 0d, 11, 00, 17, 00, 18, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 7 +- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(4) +- expression 1 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +- expression 3 operands: lhs = Counter(1), rhs = Counter(2) +- expression 4 operands: lhs = Expression(5, Sub), rhs = Expression(6, Sub) +- expression 5 operands: lhs = Counter(1), rhs = Counter(2) +- expression 6 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 10 +- Code(Counter(0)) at (prev + 27, 1) to (start + 0, 44) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = ((c2 + c3) + c4) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 0, 13) to (start + 0, 19) +- MCDCBranch { true: Counter(1), false: Expression(6, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 18) to (start + 0, 19) +- MCDCBranch { true: Counter(2), false: Expression(5, Sub), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 18) to (start + 0, 19) + true = c2 + false = (c1 - c2) +- Code(Expression(4, Add)) at (prev + 0, 23) to (start + 0, 24) + = ((c1 - c2) + (c0 - c1)) +- Branch { true: Counter(3), false: Counter(4) } at (prev + 0, 23) to (start + 0, 24) + true = c3 + false = c4 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = ((c2 + c3) + c4) + +Function name: mcdc_non_control_flow::assign_and +Raw bytes (51): 0x[01, 01, 04, 07, 0e, 09, 0d, 01, 05, 01, 05, 07, 01, 0c, 01, 00, 21, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 0e, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 20, 09, 0d, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Expression(1, Add), rhs = Expression(3, Sub) +- expression 1 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +- expression 3 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 7 +- Code(Counter(0)) at (prev + 12, 1) to (start + 0, 33) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = ((c2 + c3) + (c0 - c1)) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(1), false: Expression(3, Sub) } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 18) to (start + 0, 19) +- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 18) to (start + 0, 19) + true = c2 + false = c3 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = ((c2 + c3) + (c0 - c1)) + +Function name: mcdc_non_control_flow::assign_or +Raw bytes (51): 0x[01, 01, 04, 07, 0d, 05, 09, 01, 05, 01, 05, 07, 01, 11, 01, 00, 20, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 0e, 00, 0d, 00, 0e, 0e, 00, 12, 00, 13, 20, 09, 0d, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(3) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +- expression 3 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 7 +- Code(Counter(0)) at (prev + 17, 1) to (start + 0, 32) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = ((c1 + c2) + c3) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(1), false: Expression(3, Sub) } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Expression(3, Sub)) at (prev + 0, 18) to (start + 0, 19) + = (c0 - c1) +- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 18) to (start + 0, 19) + true = c2 + false = c3 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = ((c1 + c2) + c3) + +Function name: mcdc_non_control_flow::foo +Raw bytes (9): 0x[01, 01, 00, 01, 01, 25, 01, 02, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 37, 1) to (start + 2, 2) + +Function name: mcdc_non_control_flow::func_call +Raw bytes (39): 0x[01, 01, 03, 01, 05, 0b, 02, 09, 0d, 05, 01, 29, 01, 01, 0a, 20, 05, 02, 01, 09, 00, 0a, 05, 00, 0e, 00, 0f, 20, 09, 0d, 00, 0e, 00, 0f, 07, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 3 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Expression(2, Add), rhs = Expression(0, Sub) +- expression 2 operands: lhs = Counter(2), rhs = Counter(3) +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 41, 1) to (start + 1, 10) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 9) to (start + 0, 10) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 14) to (start + 0, 15) +- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 14) to (start + 0, 15) + true = c2 + false = c3 +- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) + = ((c2 + c3) + (c0 - c1)) + +Function name: mcdc_non_control_flow::right_comb_tree +Raw bytes (117): 0x[01, 01, 13, 07, 1a, 0b, 19, 0f, 15, 13, 11, 09, 0d, 01, 05, 01, 05, 05, 19, 05, 19, 4a, 15, 05, 19, 4a, 15, 05, 19, 46, 11, 4a, 15, 05, 19, 46, 11, 4a, 15, 05, 19, 0d, 01, 20, 01, 00, 41, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 1a, 00, 0d, 00, 0e, 05, 00, 13, 00, 14, 20, 4a, 19, 00, 13, 00, 14, 4a, 00, 19, 00, 1a, 20, 46, 15, 00, 19, 00, 1a, 46, 00, 1f, 00, 20, 20, 42, 11, 00, 1f, 00, 20, 42, 00, 24, 00, 27, 20, 09, 0d, 00, 24, 00, 27, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 19 +- expression 0 operands: lhs = Expression(1, Add), rhs = Expression(6, Sub) +- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(6) +- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(5) +- expression 3 operands: lhs = Expression(4, Add), rhs = Counter(4) +- expression 4 operands: lhs = Counter(2), rhs = Counter(3) +- expression 5 operands: lhs = Counter(0), rhs = Counter(1) +- expression 6 operands: lhs = Counter(0), rhs = Counter(1) +- expression 7 operands: lhs = Counter(1), rhs = Counter(6) +- expression 8 operands: lhs = Counter(1), rhs = Counter(6) +- expression 9 operands: lhs = Expression(18, Sub), rhs = Counter(5) +- expression 10 operands: lhs = Counter(1), rhs = Counter(6) +- expression 11 operands: lhs = Expression(18, Sub), rhs = Counter(5) +- expression 12 operands: lhs = Counter(1), rhs = Counter(6) +- expression 13 operands: lhs = Expression(17, Sub), rhs = Counter(4) +- expression 14 operands: lhs = Expression(18, Sub), rhs = Counter(5) +- expression 15 operands: lhs = Counter(1), rhs = Counter(6) +- expression 16 operands: lhs = Expression(17, Sub), rhs = Counter(4) +- expression 17 operands: lhs = Expression(18, Sub), rhs = Counter(5) +- expression 18 operands: lhs = Counter(1), rhs = Counter(6) +Number of file 0 mappings: 13 +- Code(Counter(0)) at (prev + 32, 1) to (start + 0, 65) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = (((((c2 + c3) + c4) + c5) + c6) + (c0 - c1)) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(1), false: Expression(6, Sub) } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 19) to (start + 0, 20) +- Branch { true: Expression(18, Sub), false: Counter(6) } at (prev + 0, 19) to (start + 0, 20) + true = (c1 - c6) + false = c6 +- Code(Expression(18, Sub)) at (prev + 0, 25) to (start + 0, 26) + = (c1 - c6) +- Branch { true: Expression(17, Sub), false: Counter(5) } at (prev + 0, 25) to (start + 0, 26) + true = ((c1 - c6) - c5) + false = c5 +- Code(Expression(17, Sub)) at (prev + 0, 31) to (start + 0, 32) + = ((c1 - c6) - c5) +- Branch { true: Expression(16, Sub), false: Counter(4) } at (prev + 0, 31) to (start + 0, 32) + true = (((c1 - c6) - c5) - c4) + false = c4 +- Code(Expression(16, Sub)) at (prev + 0, 36) to (start + 0, 39) + = (((c1 - c6) - c5) - c4) +- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 36) to (start + 0, 39) + true = c2 + false = c3 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = (((((c2 + c3) + c4) + c5) + c6) + (c0 - c1)) + diff --git a/tests/coverage/mcdc_non_control_flow.coverage b/tests/coverage/mcdc_non_control_flow.coverage new file mode 100644 index 000000000000..4e53a7a0d8e8 --- /dev/null +++ b/tests/coverage/mcdc_non_control_flow.coverage @@ -0,0 +1,98 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ min-llvm-version: 18 + LL| |//@ compile-flags: -Zcoverage-options=mcdc + LL| |//@ llvm-cov-flags: --show-mcdc + LL| | + LL| |// This test ensures that boolean expressions that are not inside control flow + LL| |// decisions are correctly instrumented. + LL| | + LL| |use core::hint::black_box; + LL| | + LL| 3|fn assign_and(a: bool, b: bool) { + LL| 3| let x = a && b; + ^2 + LL| 3| black_box(x); + LL| 3|} + LL| | + LL| 3|fn assign_or(a: bool, b: bool) { + LL| 3| let x = a || b; + ^1 + LL| 3| black_box(x); + LL| 3|} + LL| | + LL| 4|fn assign_3(a: bool, b: bool, c: bool) { + LL| 4| let x = a || b && c; + ^2 ^1 + LL| 4| black_box(x); + LL| 4|} + LL| | + LL| 4|fn assign_3_bis(a: bool, b: bool, c: bool) { + LL| 4| let x = a && b || c; + ^2 ^3 + ------------------ + |---> MC/DC Decision Region (LL:13) to (LL:19) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:13) + | Condition C2 --> (LL:18) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, F = F } + | 3 { T, T = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (2,3) + | MC/DC Coverage for Decision: 100.00% + | + ------------------ + LL| 4| black_box(x); + LL| 4|} + LL| | + LL| 3|fn right_comb_tree(a: bool, b: bool, c: bool, d: bool, e: bool) { + LL| 3| let x = a && (b && (c && (d && (e)))); + ^2 ^1 ^1 ^1 + LL| 3| black_box(x); + LL| 3|} + LL| | + LL| 3|fn foo(a: bool) -> bool { + LL| 3| black_box(a) + LL| 3|} + LL| | + LL| 3|fn func_call(a: bool, b: bool) { + LL| 3| foo(a && b); + ^2 + LL| 3|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | assign_and(true, false); + LL| | assign_and(true, true); + LL| | assign_and(false, false); + LL| | + LL| | assign_or(true, false); + LL| | assign_or(true, true); + LL| | assign_or(false, false); + LL| | + LL| | assign_3(true, false, false); + LL| | assign_3(true, true, false); + LL| | assign_3(false, false, true); + LL| | assign_3(false, true, true); + LL| | + LL| | assign_3_bis(true, false, false); + LL| | assign_3_bis(true, true, false); + LL| | assign_3_bis(false, false, true); + LL| | assign_3_bis(false, true, true); + LL| | + LL| | right_comb_tree(false, false, false, true, true); + LL| | right_comb_tree(true, false, false, true, true); + LL| | right_comb_tree(true, true, true, true, true); + LL| | + LL| | func_call(true, false); + LL| | func_call(true, true); + LL| | func_call(false, false); + LL| |} + diff --git a/tests/coverage/mcdc_non_control_flow.rs b/tests/coverage/mcdc_non_control_flow.rs new file mode 100644 index 000000000000..85c0a6c6ae58 --- /dev/null +++ b/tests/coverage/mcdc_non_control_flow.rs @@ -0,0 +1,72 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ min-llvm-version: 18 +//@ compile-flags: -Zcoverage-options=mcdc +//@ llvm-cov-flags: --show-mcdc + +// This test ensures that boolean expressions that are not inside control flow +// decisions are correctly instrumented. + +use core::hint::black_box; + +fn assign_and(a: bool, b: bool) { + let x = a && b; + black_box(x); +} + +fn assign_or(a: bool, b: bool) { + let x = a || b; + black_box(x); +} + +fn assign_3(a: bool, b: bool, c: bool) { + let x = a || b && c; + black_box(x); +} + +fn assign_3_bis(a: bool, b: bool, c: bool) { + let x = a && b || c; + black_box(x); +} + +fn right_comb_tree(a: bool, b: bool, c: bool, d: bool, e: bool) { + let x = a && (b && (c && (d && (e)))); + black_box(x); +} + +fn foo(a: bool) -> bool { + black_box(a) +} + +fn func_call(a: bool, b: bool) { + foo(a && b); +} + +#[coverage(off)] +fn main() { + assign_and(true, false); + assign_and(true, true); + assign_and(false, false); + + assign_or(true, false); + assign_or(true, true); + assign_or(false, false); + + assign_3(true, false, false); + assign_3(true, true, false); + assign_3(false, false, true); + assign_3(false, true, true); + + assign_3_bis(true, false, false); + assign_3_bis(true, true, false); + assign_3_bis(false, false, true); + assign_3_bis(false, true, true); + + right_comb_tree(false, false, false, true, true); + right_comb_tree(true, false, false, true, true); + right_comb_tree(true, true, true, true, true); + + func_call(true, false); + func_call(true, true); + func_call(false, false); +} From 05b7b46e65207bdc33bd86c4ffda3516059e4de9 Mon Sep 17 00:00:00 2001 From: Lin Yihai Date: Mon, 13 May 2024 22:42:18 +0800 Subject: [PATCH 009/147] fix: break inside async closure has incorrect span for enclosing closure --- compiler/rustc_ast_lowering/src/item.rs | 10 +++++++++- .../async-closures/wrong-fn-kind.stderr | 9 +++++---- .../break-inside-coroutine-issue-124495.rs | 1 + .../break-inside-coroutine-issue-124495.stderr | 14 +++++++------- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index a15449409df3..44e07c299a5f 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1311,6 +1311,14 @@ impl<'hir> LoweringContext<'_, 'hir> { CoroutineKind::AsyncGen { .. } => hir::CoroutineDesugaring::AsyncGen, }; let closure_id = coroutine_kind.closure_id(); + + let span = if let FnRetTy::Default(span) = decl.output + && matches!(coroutine_source, rustc_hir::CoroutineSource::Closure) + { + body_span.with_lo(span.lo()) + } else { + body_span + }; let coroutine_expr = self.make_desugared_coroutine_expr( // The default capture mode here is by-ref. Later on during upvar analysis, // we will force the captured arguments to by-move, but for async closures, @@ -1319,7 +1327,7 @@ impl<'hir> LoweringContext<'_, 'hir> { CaptureBy::Ref, closure_id, None, - body_span, + span, desugaring_kind, coroutine_source, mkbody, diff --git a/tests/ui/async-await/async-closures/wrong-fn-kind.stderr b/tests/ui/async-await/async-closures/wrong-fn-kind.stderr index e56389b32027..6f07a6feed35 100644 --- a/tests/ui/async-await/async-closures/wrong-fn-kind.stderr +++ b/tests/ui/async-await/async-closures/wrong-fn-kind.stderr @@ -20,15 +20,16 @@ LL | fn needs_async_fn(_: impl async Fn()) {} | ^^^^^^^^^^ required by this bound in `needs_async_fn` error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure - --> $DIR/wrong-fn-kind.rs:9:29 + --> $DIR/wrong-fn-kind.rs:9:20 | LL | fn needs_async_fn(_: impl async Fn()) {} | --------------- change this to accept `FnMut` instead of `Fn` ... LL | needs_async_fn(async || { - | _____--------------_--------_^ - | | | | - | | | in this closure + | -------------- ^------- + | | | + | _____|______________in this closure + | | | | | expects `Fn` instead of `FnMut` LL | | LL | | x += 1; diff --git a/tests/ui/coroutine/break-inside-coroutine-issue-124495.rs b/tests/ui/coroutine/break-inside-coroutine-issue-124495.rs index 5d93db56722b..97c3d06c023e 100644 --- a/tests/ui/coroutine/break-inside-coroutine-issue-124495.rs +++ b/tests/ui/coroutine/break-inside-coroutine-issue-124495.rs @@ -18,6 +18,7 @@ async gen fn async_gen_fn() { fn main() { let _ = async { break; }; //~ ERROR `break` inside `async` block + let _ = async || { break; }; //~ ERROR `break` inside `async` closure let _ = gen { break; }; //~ ERROR `break` inside `gen` block diff --git a/tests/ui/coroutine/break-inside-coroutine-issue-124495.stderr b/tests/ui/coroutine/break-inside-coroutine-issue-124495.stderr index a7f37fad35ea..f030961b7edd 100644 --- a/tests/ui/coroutine/break-inside-coroutine-issue-124495.stderr +++ b/tests/ui/coroutine/break-inside-coroutine-issue-124495.stderr @@ -38,16 +38,16 @@ LL | let _ = async { break; }; | enclosing `async` block error[E0267]: `break` inside `async` closure - --> $DIR/break-inside-coroutine-issue-124495.rs:21:24 + --> $DIR/break-inside-coroutine-issue-124495.rs:22:24 | LL | let _ = async || { break; }; - | --^^^^^--- - | | | - | | cannot `break` inside `async` closure - | enclosing `async` closure + | -----------^^^^^--- + | | | + | | cannot `break` inside `async` closure + | enclosing `async` closure error[E0267]: `break` inside `gen` block - --> $DIR/break-inside-coroutine-issue-124495.rs:23:19 + --> $DIR/break-inside-coroutine-issue-124495.rs:24:19 | LL | let _ = gen { break; }; | ------^^^^^--- @@ -56,7 +56,7 @@ LL | let _ = gen { break; }; | enclosing `gen` block error[E0267]: `break` inside `async gen` block - --> $DIR/break-inside-coroutine-issue-124495.rs:25:25 + --> $DIR/break-inside-coroutine-issue-124495.rs:26:25 | LL | let _ = async gen { break; }; | ------------^^^^^--- From 9b05e154f39f1a413ebc09c8f1dd751f991b3bba Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 8 Jun 2024 12:04:38 +0200 Subject: [PATCH 010/147] StorageLive: refresh storage (instead of UB) when local is already live --- compiler/rustc_const_eval/messages.ftl | 2 -- .../rustc_const_eval/src/interpret/eval_context.rs | 8 +++----- compiler/rustc_middle/src/mir/syntax.rs | 11 +++++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 2dbeb7d5e0ca..14c036b8df9b 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -73,8 +73,6 @@ const_eval_division_by_zero = dividing by zero const_eval_division_overflow = overflow in signed division (dividing MIN by -1) -const_eval_double_storage_live = - StorageLive on a local that was already live const_eval_dyn_call_not_a_method = `dyn` call trying to call something that is not a method diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 7c2100fcbe38..c1daa2fac338 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1103,11 +1103,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Operand::Immediate(Immediate::Uninit) }); - // StorageLive expects the local to be dead, and marks it live. + // If the local is already live, deallocate its old memory. let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val); - if !matches!(old, LocalValue::Dead) { - throw_ub_custom!(fluent::const_eval_double_storage_live); - } + self.deallocate_local(old)?; Ok(()) } @@ -1121,7 +1119,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { assert!(local != mir::RETURN_PLACE, "Cannot make return place dead"); trace!("{:?} is now dead", local); - // It is entirely okay for this local to be already dead (at least that's how we currently generate MIR) + // If the local is already dead, this is a NOP. let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead); self.deallocate_local(old)?; Ok(()) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index ebe77a1abfd8..9855c72197ed 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -361,16 +361,19 @@ pub enum StatementKind<'tcx> { /// At any point during the execution of a function, each local is either allocated or /// unallocated. Except as noted below, all locals except function parameters are initially /// unallocated. `StorageLive` statements cause memory to be allocated for the local while - /// `StorageDead` statements cause the memory to be freed. Using a local in any way (not only - /// reading/writing from it) while it is unallocated is UB. + /// `StorageDead` statements cause the memory to be freed. In other words, + /// `StorageLive`/`StorageDead` act like the heap operations `allocate`/`deallocate`, but for + /// stack-allocated local variables. Using a local in any way (not only reading/writing from it) + /// while it is unallocated is UB. /// /// Some locals have no `StorageLive` or `StorageDead` statements within the entire MIR body. /// These locals are implicitly allocated for the full duration of the function. There is a /// convenience method at `rustc_mir_dataflow::storage::always_storage_live_locals` for /// computing these locals. /// - /// If the local is already allocated, calling `StorageLive` again is UB. However, for an - /// unallocated local an additional `StorageDead` all is simply a nop. + /// If the local is already allocated, calling `StorageLive` again will implicitly free the + /// local and then allocate fresh uninitilized memory. If a local is already deallocated, + /// calling `StorageDead` again is a NOP. StorageLive(Local), /// See `StorageLive` above. From 3954b744cc77bd5150854b33127211bf66202b45 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Thu, 13 Jun 2024 09:04:10 +0200 Subject: [PATCH 011/147] feat: Add std Xtensa targets support --- compiler/rustc_target/src/spec/mod.rs | 3 ++ .../src/spec/targets/xtensa_esp32_espidf.rs | 36 ++++++++++++++++ .../src/spec/targets/xtensa_esp32s2_espidf.rs | 43 +++++++++++++++++++ .../src/spec/targets/xtensa_esp32s3_espidf.rs | 36 ++++++++++++++++ src/doc/rustc/src/platform-support.md | 3 ++ src/doc/rustc/src/platform-support/xtensa.md | 12 +++++- src/tools/tidy/src/target_policy.rs | 3 ++ tests/assembly/targets/targets-elf.rs | 9 ++++ 8 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs create mode 100644 compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs create mode 100644 compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index adea2caabbe7..16b7e148ba71 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1767,8 +1767,11 @@ supported_targets! { ("nvptx64-nvidia-cuda", nvptx64_nvidia_cuda), ("xtensa-esp32-none-elf", xtensa_esp32_none_elf), + ("xtensa-esp32-espidf", xtensa_esp32_espidf), ("xtensa-esp32s2-none-elf", xtensa_esp32s2_none_elf), + ("xtensa-esp32s2-espidf", xtensa_esp32s2_espidf), ("xtensa-esp32s3-none-elf", xtensa_esp32s3_none_elf), + ("xtensa-esp32s3-espidf", xtensa_esp32s3_espidf), ("i686-wrs-vxworks", i686_wrs_vxworks), ("x86_64-wrs-vxworks", x86_64_wrs_vxworks), diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs new file mode 100644 index 000000000000..1b66fdbd2afd --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs @@ -0,0 +1,36 @@ +use crate::abi::Endian; +use crate::spec::{base::xtensa, cvs, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "xtensa-none-elf".into(), + pointer_width: 32, + data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), + arch: "xtensa".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + + options: TargetOptions { + endian: Endian::Little, + c_int_width: "32".into(), + families: cvs!["unix"], + os: "espidf".into(), + env: "newlib".into(), + vendor: "espressif".into(), + + executables: true, + cpu: "esp32".into(), + linker: Some("xtensa-esp32-elf-gcc".into()), + + // The esp32 only supports native 32bit atomics. + max_atomic_width: Some(32), + atomic_cas: true, + + ..xtensa::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs new file mode 100644 index 000000000000..ad5fda8a4ae1 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs @@ -0,0 +1,43 @@ +use crate::abi::Endian; +use crate::spec::{base::xtensa, cvs, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "xtensa-none-elf".into(), + pointer_width: 32, + data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), + arch: "xtensa".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + + options: TargetOptions { + endian: Endian::Little, + c_int_width: "32".into(), + families: cvs!["unix"], + os: "espidf".into(), + env: "newlib".into(), + vendor: "espressif".into(), + + executables: true, + cpu: "esp32-s2".into(), + linker: Some("xtensa-esp32s2-elf-gcc".into()), + + // See https://github.com/espressif/rust-esp32-example/issues/3#issuecomment-861054477 + // + // While the ESP32-S2 chip does not natively support atomics, ESP-IDF does support + // the __atomic* and __sync* compiler builtins. Setting `max_atomic_width` and `atomic_cas` + // and `atomic_cas: true` will cause the compiler to emit libcalls to these builtins. On the + // ESP32-S2, these are guaranteed to be lock-free. + // + // Support for atomics is necessary for the Rust STD library, which is supported by ESP-IDF. + max_atomic_width: Some(32), + atomic_cas: true, + + ..xtensa::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs new file mode 100644 index 000000000000..ab1d1df43dd9 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs @@ -0,0 +1,36 @@ +use crate::abi::Endian; +use crate::spec::{base::xtensa, cvs, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "xtensa-none-elf".into(), + pointer_width: 32, + data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), + arch: "xtensa".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + + options: TargetOptions { + endian: Endian::Little, + c_int_width: "32".into(), + families: cvs!["unix"], + os: "espidf".into(), + env: "newlib".into(), + vendor: "espressif".into(), + + executables: true, + cpu: "esp32-s3".into(), + linker: Some("xtensa-esp32s3-elf-gcc".into()), + + // The esp32s3 only supports native 32bit atomics. + max_atomic_width: Some(32), + atomic_cas: true, + + ..xtensa::opts() + }, + } +} diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index c672cff74526..e0ace5db2aac 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -384,7 +384,10 @@ target | std | host | notes [`x86_64h-apple-darwin`](platform-support/x86_64h-apple-darwin.md) | ✓ | ✓ | macOS with late-gen Intel (at least Haswell) [`x86_64-unknown-linux-none`](platform-support/x86_64-unknown-linux-none.md) | * | | 64-bit Linux with no libc `xtensa-esp32-none-elf` | | | Xtensa ESP32 +`xtensa-esp32-espidf` | | | Xtensa ESP32 `xtensa-esp32s2-none-elf` | | | Xtensa ESP32-S2 +`xtensa-esp32s2-espidf` | | | Xtensa ESP32-S2 `xtensa-esp32s3-none-elf` | | | Xtensa ESP32-S3 +`xtensa-esp32s3-espidf` | | | Xtensa ESP32-S3 [runs on NVIDIA GPUs]: https://github.com/japaric-archived/nvptx#targets diff --git a/src/doc/rustc/src/platform-support/xtensa.md b/src/doc/rustc/src/platform-support/xtensa.md index 7785977466e4..050ffcc8a41b 100644 --- a/src/doc/rustc/src/platform-support/xtensa.md +++ b/src/doc/rustc/src/platform-support/xtensa.md @@ -13,13 +13,23 @@ Targets for Xtensa CPUs. The target names follow this format: `xtensa-$CPU`, where `$CPU` specifies the target chip. The following targets are currently defined: + +### `no_std` + | Target name | Target CPU(s) | | ------------------------- | --------------------------------------------------------------- | | `xtensa-esp32-none-elf` | [ESP32](https://www.espressif.com/en/products/socs/esp32) | | `xtensa-esp32s2-none-elf` | [ESP32-S2](https://www.espressif.com/en/products/socs/esp32-s2) | | `xtensa-esp32s3-none-elf` | [ESP32-S3](https://www.espressif.com/en/products/socs/esp32-s3) | +### `std` -## Building the target +| Target name | Target CPU(s) | +| ----------------------- | --------------------------------------------------------------- | +| `xtensa-esp32-espidf` | [ESP32](https://www.espressif.com/en/products/socs/esp32) | +| `xtensa-esp32s2-espidf` | [ESP32-S2](https://www.espressif.com/en/products/socs/esp32-s2) | +| `xtensa-esp32s3-espidf` | [ESP32-S3](https://www.espressif.com/en/products/socs/esp32-s3) | + +## Building the targets The targets can be built by installing the [Xtensa enabled Rust channel](https://github.com/esp-rs/rust/). See instructions in the [RISC-V and Xtensa Targets section of the The Rust on ESP Book](https://docs.esp-rs.org/book/installation/riscv-and-xtensa.html). diff --git a/src/tools/tidy/src/target_policy.rs b/src/tools/tidy/src/target_policy.rs index 06210c8cdb20..cb9a0c1c7f4e 100644 --- a/src/tools/tidy/src/target_policy.rs +++ b/src/tools/tidy/src/target_policy.rs @@ -14,8 +14,11 @@ const EXCEPTIONS: &[&str] = &[ "csky_unknown_linux_gnuabiv2hf", // FIXME: disabled since it requires a custom LLVM until the upstream LLVM adds support for the target (https://github.com/espressif/llvm-project/issues/4) "xtensa_esp32_none_elf", + "xtensa_esp32_espidf", "xtensa_esp32s2_none_elf", + "xtensa_esp32s2_espidf", "xtensa_esp32s3_none_elf", + "xtensa_esp32s3_espidf", ]; pub fn check(root_path: &Path, bad: &mut bool) { diff --git a/tests/assembly/targets/targets-elf.rs b/tests/assembly/targets/targets-elf.rs index 4c54fe639e3e..6c96ece5568d 100644 --- a/tests/assembly/targets/targets-elf.rs +++ b/tests/assembly/targets/targets-elf.rs @@ -578,12 +578,21 @@ revisions: xtensa_esp32_none_elf [xtensa_esp32_none_elf] compile-flags: --target xtensa-esp32-none-elf [xtensa_esp32_none_elf] needs-llvm-components: xtensa + revisions: xtensa_esp32_espidf + [xtensa_esp32_espidf] compile-flags: --target xtensa-esp32s2-espidf + [xtensa_esp32_espidf] needs-llvm-components: xtensa revisions: xtensa_esp32s2_none_elf [xtensa_esp32s2_none_elf] compile-flags: --target xtensa-esp32s2-none-elf [xtensa_esp32s2_none_elf] needs-llvm-components: xtensa + revisions: xtensa_esp32s2_espidf + [xtensa_esp32s2_espidf] compile-flags: --target xtensa-esp32s2-espidf + [xtensa_esp32s2_espidf] needs-llvm-components: xtensa revisions: xtensa_esp32s3_none_elf [xtensa_esp32s3_none_elf] compile-flags: --target xtensa-esp32s3-none-elf [xtensa_esp32s3_none_elf] needs-llvm-components: xtensa + revisions: xtensa_esp32s3_espidf + [xtensa_esp32s3_espidf] compile-flags: --target xtensa-esp32s3-espidf + [xtensa_esp32s3_espidf] needs-llvm-components: xtensa */ // Sanity-check that each target can produce assembly code. From b092798128a9c957952037638ea6e834be405e13 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Thu, 13 Jun 2024 10:47:55 +0200 Subject: [PATCH 012/147] docs: Update platform support --- src/doc/rustc/src/SUMMARY.md | 3 ++- src/doc/rustc/src/platform-support.md | 12 +++++------ src/doc/rustc/src/platform-support/esp-idf.md | 20 +++++++++++-------- src/doc/rustc/src/platform-support/xtensa.md | 12 ++--------- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 201ace5ce3a7..a8814f7203b1 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -47,7 +47,7 @@ - [\*-linux-ohos](platform-support/openharmony.md) - [\*-hurd-gnu](platform-support/hurd.md) - [aarch64-unknown-teeos](platform-support/aarch64-unknown-teeos.md) - - [\*-esp-espidf](platform-support/esp-idf.md) + - [\*-espidf](platform-support/esp-idf.md) - [\*-unknown-fuchsia](platform-support/fuchsia.md) - [\*-kmc-solid_\*](platform-support/kmc-solid.md) - [csky-unknown-linux-gnuabiv2\*](platform-support/csky-unknown-linux-gnuabiv2.md) @@ -80,6 +80,7 @@ - [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md) - [x86_64-unknown-linux-none.md](platform-support/x86_64-unknown-linux-none.md) - [x86_64-unknown-none](platform-support/x86_64-unknown-none.md) + - [xtensa-\*-none-elf](platform-support/xtensa.md) - [Targets](targets/index.md) - [Built-in Targets](targets/built-in.md) - [Custom Targets](targets/custom.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index e0ace5db2aac..fadf7466cd00 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -383,11 +383,11 @@ target | std | host | notes `x86_64-wrs-vxworks` | ? | | [`x86_64h-apple-darwin`](platform-support/x86_64h-apple-darwin.md) | ✓ | ✓ | macOS with late-gen Intel (at least Haswell) [`x86_64-unknown-linux-none`](platform-support/x86_64-unknown-linux-none.md) | * | | 64-bit Linux with no libc -`xtensa-esp32-none-elf` | | | Xtensa ESP32 -`xtensa-esp32-espidf` | | | Xtensa ESP32 -`xtensa-esp32s2-none-elf` | | | Xtensa ESP32-S2 -`xtensa-esp32s2-espidf` | | | Xtensa ESP32-S2 -`xtensa-esp32s3-none-elf` | | | Xtensa ESP32-S3 -`xtensa-esp32s3-espidf` | | | Xtensa ESP32-S3 +[`xtensa-esp32-none-elf`](platform-support/xtensa.md) | * | | Xtensa ESP32 +[`xtensa-esp32-espidf`](platform-support/esp-idf.md) | ✓ | | Xtensa ESP32 +[`xtensa-esp32s2-none-elf`](platform-support/xtensa.md) | * | | Xtensa ESP32-S2 +[`xtensa-esp32s2-espidf`](platform-support/esp-idf.md) | ✓ | | Xtensa ESP32-S2 +[`xtensa-esp32s3-none-elf`](platform-support/xtensa.md) | * | | Xtensa ESP32-S3 +[`xtensa-esp32s3-espidf`](platform-support/esp-idf.md) | ✓ | | Xtensa ESP32-S3 [runs on NVIDIA GPUs]: https://github.com/japaric-archived/nvptx#targets diff --git a/src/doc/rustc/src/platform-support/esp-idf.md b/src/doc/rustc/src/platform-support/esp-idf.md index 1f8d98598099..91d7d66627d0 100644 --- a/src/doc/rustc/src/platform-support/esp-idf.md +++ b/src/doc/rustc/src/platform-support/esp-idf.md @@ -1,4 +1,4 @@ -# `*-esp-espidf` +# `*-espidf` **Tier: 3** @@ -8,18 +8,22 @@ Targets for the [ESP-IDF](https://github.com/espressif/esp-idf) development fram - Ivan Markov [@ivmarkov](https://github.com/ivmarkov) - Scott Mabin [@MabezDev](https://github.com/MabezDev) +- Sergio Gasquez [@SergioGasquez](https://github.com/SergioGasquez) ## Requirements The target names follow this format: `$ARCH-esp-espidf`, where `$ARCH` specifies the target processor architecture. The following targets are currently defined: -| Target name | Target CPU(s) | Minimum ESP-IDF version | -| ------------------------ | --------------------------------------------------------------- | ----------------------- | -| `riscv32imc-esp-espidf` | [ESP32-C2](https://www.espressif.com/en/products/socs/esp32-c2) | `v5.0` | -| `riscv32imc-esp-espidf` | [ESP32-C3](https://www.espressif.com/en/products/socs/esp32-c3) | `v4.3` | -| `riscv32imac-esp-espidf` | [ESP32-C6](https://www.espressif.com/en/products/socs/esp32-c6) | `v5.1` | -| `riscv32imac-esp-espidf` | [ESP32-H2](https://www.espressif.com/en/products/socs/esp32-h2) | `v5.1` | -| `riscv32imafc-esp-espidf`| [ESP32-P4](https://www.espressif.com/en/news/ESP32-P4) | `v5.2` | +| Target name | Target CPU(s) | Minimum ESP-IDF version | +| ------------------------- | --------------------------------------------------------------- | ----------------------- | +| `riscv32imc-esp-espidf` | [ESP32-C2](https://www.espressif.com/en/products/socs/esp32-c2) | `v5.0` | +| `riscv32imc-esp-espidf` | [ESP32-C3](https://www.espressif.com/en/products/socs/esp32-c3) | `v4.4` | +| `riscv32imac-esp-espidf` | [ESP32-C6](https://www.espressif.com/en/products/socs/esp32-c6) | `v5.1` | +| `riscv32imac-esp-espidf` | [ESP32-H2](https://www.espressif.com/en/products/socs/esp32-h2) | `v5.1` | +| `riscv32imafc-esp-espidf` | [ESP32-P4](https://www.espressif.com/en/news/ESP32-P4) | `v5.2` | +| `xtensa-esp32-espidf` | [ESP32](https://www.espressif.com/en/products/socs/esp32) | `v4.4` | +| `xtensa-esp32s2-espidf` | [ESP32-S2](https://www.espressif.com/en/products/socs/esp32-s2) | `v4.4` | +| `xtensa-esp32s3-espidf` | [ESP32-S3](https://www.espressif.com/en/products/socs/esp32-s3) | `v4.4` | It is recommended to use the latest ESP-IDF stable release if possible. diff --git a/src/doc/rustc/src/platform-support/xtensa.md b/src/doc/rustc/src/platform-support/xtensa.md index 050ffcc8a41b..332b8ee9c154 100644 --- a/src/doc/rustc/src/platform-support/xtensa.md +++ b/src/doc/rustc/src/platform-support/xtensa.md @@ -1,4 +1,4 @@ -# `xtensa-*` +# `xtensa-*-none-elf` **Tier: 3** @@ -13,22 +13,14 @@ Targets for Xtensa CPUs. The target names follow this format: `xtensa-$CPU`, where `$CPU` specifies the target chip. The following targets are currently defined: - -### `no_std` - | Target name | Target CPU(s) | | ------------------------- | --------------------------------------------------------------- | | `xtensa-esp32-none-elf` | [ESP32](https://www.espressif.com/en/products/socs/esp32) | | `xtensa-esp32s2-none-elf` | [ESP32-S2](https://www.espressif.com/en/products/socs/esp32-s2) | | `xtensa-esp32s3-none-elf` | [ESP32-S3](https://www.espressif.com/en/products/socs/esp32-s3) | -### `std` -| Target name | Target CPU(s) | -| ----------------------- | --------------------------------------------------------------- | -| `xtensa-esp32-espidf` | [ESP32](https://www.espressif.com/en/products/socs/esp32) | -| `xtensa-esp32s2-espidf` | [ESP32-S2](https://www.espressif.com/en/products/socs/esp32-s2) | -| `xtensa-esp32s3-espidf` | [ESP32-S3](https://www.espressif.com/en/products/socs/esp32-s3) | +Xtensa targets that support `std` are documented in the [ESP-IDF platform support document](esp-idf.md) ## Building the targets From 408c8eb983b2c460e56e1e71db26d6b7e76aa695 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 14 Jun 2024 12:14:32 +0200 Subject: [PATCH 013/147] add Miri tests --- .../miri/tests/fail/storage-live-dead-var.rs | 14 ++++++++++++++ .../tests/fail/storage-live-dead-var.stderr | 15 +++++++++++++++ .../miri/tests/fail/storage-live-resets-var.rs | 17 +++++++++++++++++ .../tests/fail/storage-live-resets-var.stderr | 15 +++++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 src/tools/miri/tests/fail/storage-live-dead-var.rs create mode 100644 src/tools/miri/tests/fail/storage-live-dead-var.stderr create mode 100644 src/tools/miri/tests/fail/storage-live-resets-var.rs create mode 100644 src/tools/miri/tests/fail/storage-live-resets-var.stderr diff --git a/src/tools/miri/tests/fail/storage-live-dead-var.rs b/src/tools/miri/tests/fail/storage-live-dead-var.rs new file mode 100644 index 000000000000..83ab98d79d1a --- /dev/null +++ b/src/tools/miri/tests/fail/storage-live-dead-var.rs @@ -0,0 +1,14 @@ +#![feature(core_intrinsics, custom_mir)] +use std::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime")] +fn main() { + mir! { + let val: i32; + { + val = 42; //~ERROR: accessing a dead local variable + StorageLive(val); // too late... (but needs to be here to make `val` not implicitly live) + Return() + } + } +} diff --git a/src/tools/miri/tests/fail/storage-live-dead-var.stderr b/src/tools/miri/tests/fail/storage-live-dead-var.stderr new file mode 100644 index 000000000000..ccc77b1c978d --- /dev/null +++ b/src/tools/miri/tests/fail/storage-live-dead-var.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: accessing a dead local variable + --> $DIR/storage-live-dead-var.rs:LL:CC + | +LL | val = 42; + | ^^^^^^^^ accessing a dead local variable + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/storage-live-dead-var.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/storage-live-resets-var.rs b/src/tools/miri/tests/fail/storage-live-resets-var.rs new file mode 100644 index 000000000000..bfdd9e78943e --- /dev/null +++ b/src/tools/miri/tests/fail/storage-live-resets-var.rs @@ -0,0 +1,17 @@ +#![feature(core_intrinsics, custom_mir)] +use std::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime")] +fn main() { + mir! { + let val: i32; + let _val2: i32; + { + StorageLive(val); + val = 42; + StorageLive(val); // reset val to `uninit` + _val2 = val; //~ERROR: uninitialized + Return() + } + } +} diff --git a/src/tools/miri/tests/fail/storage-live-resets-var.stderr b/src/tools/miri/tests/fail/storage-live-resets-var.stderr new file mode 100644 index 000000000000..07d39cc9d6b3 --- /dev/null +++ b/src/tools/miri/tests/fail/storage-live-resets-var.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: constructing invalid value: encountered uninitialized memory, but expected an integer + --> $DIR/storage-live-resets-var.rs:LL:CC + | +LL | _val2 = val; + | ^^^^^^^^^^^ constructing invalid value: encountered uninitialized memory, but expected an integer + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/storage-live-resets-var.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From 9b221109bf8c0b24e7114f32add87863ed7253a6 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Fri, 14 Jun 2024 14:57:14 +0000 Subject: [PATCH 014/147] Add powerpc-unknown-openbsd maintaince status --- src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 2 +- src/doc/rustc/src/platform-support/powerpc-unknown-openbsd.md | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 src/doc/rustc/src/platform-support/powerpc-unknown-openbsd.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 201ace5ce3a7..8277cc75537c 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -60,6 +60,7 @@ - [mipsel-sony-psx](platform-support/mipsel-sony-psx.md) - [mipsisa\*r6\*-unknown-linux-gnu\*](platform-support/mips-release-6.md) - [nvptx64-nvidia-cuda](platform-support/nvptx64-nvidia-cuda.md) + - [powerpc-unknown-openbsd](platform-support/powerpc-unknown-openbsd.md) - [powerpc64-ibm-aix](platform-support/aix.md) - [riscv32im-risc0-zkvm-elf](platform-support/riscv32im-risc0-zkvm-elf.md) - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index c672cff74526..894e404da58f 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -329,7 +329,7 @@ target | std | host | notes `powerpc-unknown-linux-gnuspe` | ✓ | | PowerPC SPE Linux `powerpc-unknown-linux-musl` | ? | | PowerPC Linux with musl 1.2.3 [`powerpc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD 32-bit powerpc systems -`powerpc-unknown-openbsd` | ? | | +[`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * | | `powerpc-wrs-vxworks-spe` | ? | | `powerpc-wrs-vxworks` | ? | | `powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2) diff --git a/src/doc/rustc/src/platform-support/powerpc-unknown-openbsd.md b/src/doc/rustc/src/platform-support/powerpc-unknown-openbsd.md new file mode 100644 index 000000000000..b1600c71f1f3 --- /dev/null +++ b/src/doc/rustc/src/platform-support/powerpc-unknown-openbsd.md @@ -0,0 +1,3 @@ +## Designated maintainers + +`powerpc-unknown-openbsd` is not maintained by OpenBSD developers and there are currently no active rustc maintainers. From 22d0b1ee18520ba32d3bc41ad3e85d91a5c73c24 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 15 Mar 2024 14:21:03 +0300 Subject: [PATCH 015/147] delegation: Implement glob delegation --- compiler/rustc_ast/src/ast.rs | 9 +- compiler/rustc_ast/src/mut_visit.rs | 38 +++-- compiler/rustc_ast/src/visit.rs | 38 +++-- .../rustc_ast_pretty/src/pprust/state/item.rs | 51 +++--- compiler/rustc_expand/messages.ftl | 7 +- compiler/rustc_expand/src/base.rs | 47 +++++- compiler/rustc_expand/src/errors.rs | 12 +- compiler/rustc_expand/src/expand.rs | 152 +++++++++++++----- compiler/rustc_parse/src/errors.rs | 3 +- compiler/rustc_parse/src/parser/item.rs | 24 ++- .../rustc_resolve/src/build_reduced_graph.rs | 28 +++- compiler/rustc_resolve/src/def_collector.rs | 10 +- compiler/rustc_resolve/src/late.rs | 22 +-- compiler/rustc_resolve/src/lib.rs | 14 +- compiler/rustc_resolve/src/macros.rs | 152 +++++++++++++++--- tests/ui/delegation/body-identity-glob.rs | 32 ++++ tests/ui/delegation/empty-glob.rs | 11 ++ tests/ui/delegation/empty-glob.stderr | 8 + tests/ui/delegation/glob-bad-path.rs | 12 ++ tests/ui/delegation/glob-bad-path.stderr | 15 ++ tests/ui/delegation/glob-glob-conflict.rs | 33 ++++ tests/ui/delegation/glob-glob-conflict.stderr | 25 +++ tests/ui/delegation/glob-glob.rs | 36 +++++ tests/ui/delegation/glob-non-fn.rs | 38 +++++ tests/ui/delegation/glob-non-fn.stderr | 62 +++++++ tests/ui/delegation/glob-non-impl.rs | 20 +++ tests/ui/delegation/glob-non-impl.stderr | 26 +++ tests/ui/delegation/glob-override.rs | 37 +++++ tests/ui/delegation/glob.rs | 35 ++++ tests/ui/delegation/macro-inside-glob.rs | 26 +++ tests/ui/delegation/macro-inside-list.rs | 4 +- 31 files changed, 892 insertions(+), 135 deletions(-) create mode 100644 tests/ui/delegation/body-identity-glob.rs create mode 100644 tests/ui/delegation/empty-glob.rs create mode 100644 tests/ui/delegation/empty-glob.stderr create mode 100644 tests/ui/delegation/glob-bad-path.rs create mode 100644 tests/ui/delegation/glob-bad-path.stderr create mode 100644 tests/ui/delegation/glob-glob-conflict.rs create mode 100644 tests/ui/delegation/glob-glob-conflict.stderr create mode 100644 tests/ui/delegation/glob-glob.rs create mode 100644 tests/ui/delegation/glob-non-fn.rs create mode 100644 tests/ui/delegation/glob-non-fn.stderr create mode 100644 tests/ui/delegation/glob-non-impl.rs create mode 100644 tests/ui/delegation/glob-non-impl.stderr create mode 100644 tests/ui/delegation/glob-override.rs create mode 100644 tests/ui/delegation/glob.rs create mode 100644 tests/ui/delegation/macro-inside-glob.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 9cb193b4a678..b9b8d0f67ee7 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3158,13 +3158,16 @@ pub struct Delegation { pub path: Path, pub rename: Option, pub body: Option>, + /// The item was expanded from a glob delegation item. + pub from_glob: bool, } #[derive(Clone, Encodable, Decodable, Debug)] pub struct DelegationMac { pub qself: Option>, pub prefix: Path, - pub suffixes: ThinVec<(Ident, Option)>, + // Some for list delegation, and None for glob delegation. + pub suffixes: Option)>>, pub body: Option>, } @@ -3291,7 +3294,7 @@ pub enum ItemKind { /// /// E.g. `reuse ::name { target_expr_template }`. Delegation(Box), - /// A list delegation item (`reuse prefix::{a, b, c}`). + /// A list or glob delegation item (`reuse prefix::{a, b, c}`, `reuse prefix::*`). /// Treated similarly to a macro call and expanded early. DelegationMac(Box), } @@ -3372,7 +3375,7 @@ pub enum AssocItemKind { MacCall(P), /// An associated delegation item. Delegation(Box), - /// An associated delegation item list. + /// An associated list or glob delegation item. DelegationMac(Box), } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index cc33ce2cb562..8497be53c068 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1162,7 +1162,14 @@ impl NoopVisitItemKind for ItemKind { } ItemKind::MacCall(m) => vis.visit_mac_call(m), ItemKind::MacroDef(def) => vis.visit_macro_def(def), - ItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => { + ItemKind::Delegation(box Delegation { + id, + qself, + path, + rename, + body, + from_glob: _, + }) => { vis.visit_id(id); vis.visit_qself(qself); vis.visit_path(path); @@ -1176,10 +1183,12 @@ impl NoopVisitItemKind for ItemKind { ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { vis.visit_qself(qself); vis.visit_path(prefix); - for (ident, rename) in suffixes { - vis.visit_ident(ident); - if let Some(rename) = rename { - vis.visit_ident(rename); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + vis.visit_ident(ident); + if let Some(rename) = rename { + vis.visit_ident(rename); + } } } if let Some(body) = body { @@ -1218,7 +1227,14 @@ impl NoopVisitItemKind for AssocItemKind { visit_opt(ty, |ty| visitor.visit_ty(ty)); } AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac), - AssocItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => { + AssocItemKind::Delegation(box Delegation { + id, + qself, + path, + rename, + body, + from_glob: _, + }) => { visitor.visit_id(id); visitor.visit_qself(qself); visitor.visit_path(path); @@ -1232,10 +1248,12 @@ impl NoopVisitItemKind for AssocItemKind { AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { visitor.visit_qself(qself); visitor.visit_path(prefix); - for (ident, rename) in suffixes { - visitor.visit_ident(ident); - if let Some(rename) = rename { - visitor.visit_ident(rename); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + visitor.visit_ident(ident); + if let Some(rename) = rename { + visitor.visit_ident(rename); + } } } if let Some(body) = body { diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index fa97c8db3267..30ab852c4aa9 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -398,7 +398,14 @@ impl WalkItemKind for ItemKind { } ItemKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)), ItemKind::MacroDef(ts) => try_visit!(visitor.visit_mac_def(ts, item.id)), - ItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => { + ItemKind::Delegation(box Delegation { + id, + qself, + path, + rename, + body, + from_glob: _, + }) => { if let Some(qself) = qself { try_visit!(visitor.visit_ty(&qself.ty)); } @@ -411,10 +418,12 @@ impl WalkItemKind for ItemKind { try_visit!(visitor.visit_ty(&qself.ty)); } try_visit!(visitor.visit_path(prefix, item.id)); - for (ident, rename) in suffixes { - visitor.visit_ident(*ident); - if let Some(rename) = rename { - visitor.visit_ident(*rename); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + visitor.visit_ident(*ident); + if let Some(rename) = rename { + visitor.visit_ident(*rename); + } } } visit_opt!(visitor, visit_block, body); @@ -828,7 +837,14 @@ impl WalkItemKind for AssocItemKind { AssocItemKind::MacCall(mac) => { try_visit!(visitor.visit_mac_call(mac)); } - AssocItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => { + AssocItemKind::Delegation(box Delegation { + id, + qself, + path, + rename, + body, + from_glob: _, + }) => { if let Some(qself) = qself { try_visit!(visitor.visit_ty(&qself.ty)); } @@ -841,10 +857,12 @@ impl WalkItemKind for AssocItemKind { try_visit!(visitor.visit_ty(&qself.ty)); } try_visit!(visitor.visit_path(prefix, item.id)); - for (ident, rename) in suffixes { - visitor.visit_ident(*ident); - if let Some(rename) = rename { - visitor.visit_ident(*rename); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + visitor.visit_ident(*ident); + if let Some(rename) = rename { + visitor.visit_ident(*rename); + } } } visit_opt!(visitor, visit_block, body); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 474741fb0677..49ac5ece337f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -9,6 +9,12 @@ use rustc_ast::ptr::P; use rustc_ast::ModKind; use rustc_span::symbol::Ident; +enum DelegationKind<'a> { + Single, + List(&'a [(Ident, Option)]), + Glob, +} + fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String { format!("{}{}", State::to_string(|s| s.print_visibility(vis)), s) } @@ -387,7 +393,7 @@ impl<'a> State<'a> { &item.vis, &deleg.qself, &deleg.path, - None, + DelegationKind::Single, &deleg.body, ), ast::ItemKind::DelegationMac(deleg) => self.print_delegation( @@ -395,7 +401,7 @@ impl<'a> State<'a> { &item.vis, &deleg.qself, &deleg.prefix, - Some(&deleg.suffixes), + deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)), &deleg.body, ), } @@ -579,7 +585,7 @@ impl<'a> State<'a> { vis, &deleg.qself, &deleg.path, - None, + DelegationKind::Single, &deleg.body, ), ast::AssocItemKind::DelegationMac(deleg) => self.print_delegation( @@ -587,20 +593,20 @@ impl<'a> State<'a> { vis, &deleg.qself, &deleg.prefix, - Some(&deleg.suffixes), + deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)), &deleg.body, ), } self.ann.post(self, AnnNode::SubItem(id)) } - pub(crate) fn print_delegation( + fn print_delegation( &mut self, attrs: &[ast::Attribute], vis: &ast::Visibility, qself: &Option>, path: &ast::Path, - suffixes: Option<&[(Ident, Option)]>, + kind: DelegationKind<'_>, body: &Option>, ) { if body.is_some() { @@ -614,21 +620,28 @@ impl<'a> State<'a> { } else { self.print_path(path, false, 0); } - if let Some(suffixes) = suffixes { - self.word("::"); - self.word("{"); - for (i, (ident, rename)) in suffixes.iter().enumerate() { - self.print_ident(*ident); - if let Some(rename) = rename { - self.nbsp(); - self.word_nbsp("as"); - self.print_ident(*rename); - } - if i != suffixes.len() - 1 { - self.word_space(","); + match kind { + DelegationKind::Single => {} + DelegationKind::List(suffixes) => { + self.word("::"); + self.word("{"); + for (i, (ident, rename)) in suffixes.iter().enumerate() { + self.print_ident(*ident); + if let Some(rename) = rename { + self.nbsp(); + self.word_nbsp("as"); + self.print_ident(*rename); + } + if i != suffixes.len() - 1 { + self.word_space(","); + } } + self.word("}"); + } + DelegationKind::Glob => { + self.word("::"); + self.word("*"); } - self.word("}"); } if let Some(body) = body { self.nbsp(); diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 2e150f7bb279..6113580491ef 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -35,8 +35,8 @@ expand_duplicate_matcher_binding = duplicate matcher binding .label = duplicate binding .label2 = previous binding -expand_empty_delegation_list = - empty list delegation is not supported +expand_empty_delegation_mac = + empty {$kind} delegation is not supported expand_expected_paren_or_brace = expected `(` or `{"{"}`, found `{$token}` @@ -58,6 +58,9 @@ expand_feature_removed = .label = feature has been removed .reason = {$reason} +expand_glob_delegation_outside_impls = + glob delegation is only supported in impls + expand_helper_attribute_name_invalid = `{$name}` cannot be a name of derive helper attribute diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index b3f6a35f3a4b..aab493b9db5a 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -357,6 +357,10 @@ where } } +pub trait GlobDelegationExpander { + fn expand(&self, ecx: &mut ExtCtxt<'_>) -> ExpandResult)>, ()>; +} + // Use a macro because forwarding to a simple function has type system issues macro_rules! make_stmts_default { ($me:expr) => { @@ -714,6 +718,9 @@ pub enum SyntaxExtensionKind { /// The produced AST fragment is appended to the input AST fragment. Box, ), + + /// A glob delegation. + GlobDelegation(Box), } /// A struct representing a macro definition in "lowered" form ready for expansion. @@ -748,7 +755,9 @@ impl SyntaxExtension { /// Returns which kind of macro calls this syntax extension. pub fn macro_kind(&self) -> MacroKind { match self.kind { - SyntaxExtensionKind::Bang(..) | SyntaxExtensionKind::LegacyBang(..) => MacroKind::Bang, + SyntaxExtensionKind::Bang(..) + | SyntaxExtensionKind::LegacyBang(..) + | SyntaxExtensionKind::GlobDelegation(..) => MacroKind::Bang, SyntaxExtensionKind::Attr(..) | SyntaxExtensionKind::LegacyAttr(..) | SyntaxExtensionKind::NonMacroAttr => MacroKind::Attr, @@ -922,6 +931,32 @@ impl SyntaxExtension { SyntaxExtension::default(SyntaxExtensionKind::NonMacroAttr, edition) } + pub fn glob_delegation( + trait_def_id: DefId, + impl_def_id: LocalDefId, + edition: Edition, + ) -> SyntaxExtension { + struct GlobDelegationExpanderImpl { + trait_def_id: DefId, + impl_def_id: LocalDefId, + } + impl GlobDelegationExpander for GlobDelegationExpanderImpl { + fn expand( + &self, + ecx: &mut ExtCtxt<'_>, + ) -> ExpandResult)>, ()> { + match ecx.resolver.glob_delegation_suffixes(self.trait_def_id, self.impl_def_id) { + Ok(suffixes) => ExpandResult::Ready(suffixes), + Err(Indeterminate) if ecx.force_mode => ExpandResult::Ready(Vec::new()), + Err(Indeterminate) => ExpandResult::Retry(()), + } + } + } + + let expander = GlobDelegationExpanderImpl { trait_def_id, impl_def_id }; + SyntaxExtension::default(SyntaxExtensionKind::GlobDelegation(Box::new(expander)), edition) + } + pub fn expn_data( &self, parent: LocalExpnId, @@ -1030,6 +1065,16 @@ pub trait ResolverExpand { /// Tools registered with `#![register_tool]` and used by tool attributes and lints. fn registered_tools(&self) -> &RegisteredTools; + + /// Mark this invocation id as a glob delegation. + fn register_glob_delegation(&mut self, invoc_id: LocalExpnId); + + /// Names of specific methods to which glob delegation expands. + fn glob_delegation_suffixes( + &mut self, + trait_def_id: DefId, + impl_def_id: LocalDefId, + ) -> Result)>, Indeterminate>; } pub trait LintStoreExpand { diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 3f8b4661e5f5..c883121fb406 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -435,8 +435,16 @@ pub struct ExpectedParenOrBrace<'a> { } #[derive(Diagnostic)] -#[diag(expand_empty_delegation_list)] -pub(crate) struct EmptyDelegationList { +#[diag(expand_empty_delegation_mac)] +pub(crate) struct EmptyDelegationMac { + #[primary_span] + pub span: Span, + pub kind: String, +} + +#[derive(Diagnostic)] +#[diag(expand_glob_delegation_outside_impls)] +pub(crate) struct GlobDelegationOutsideImpls { #[primary_span] pub span: Span, } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index c28a09eb57c7..716bfc8c26b1 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1,8 +1,8 @@ use crate::base::*; use crate::config::StripUnconfigured; use crate::errors::{ - EmptyDelegationList, IncompleteParse, RecursionLimitReached, RemoveExprNotSupported, - RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind, + EmptyDelegationMac, GlobDelegationOutsideImpls, IncompleteParse, RecursionLimitReached, + RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind, }; use crate::mbe::diagnostics::annotate_err_with_kind; use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod}; @@ -343,6 +343,9 @@ pub enum InvocationKind { is_const: bool, item: Annotatable, }, + GlobDelegation { + item: P, + }, } impl InvocationKind { @@ -370,6 +373,7 @@ impl Invocation { InvocationKind::Bang { span, .. } => *span, InvocationKind::Attr { attr, .. } => attr.span, InvocationKind::Derive { path, .. } => path.span, + InvocationKind::GlobDelegation { item } => item.span, } } @@ -378,6 +382,7 @@ impl Invocation { InvocationKind::Bang { span, .. } => span, InvocationKind::Attr { attr, .. } => &mut attr.span, InvocationKind::Derive { path, .. } => &mut path.span, + InvocationKind::GlobDelegation { item } => &mut item.span, } } } @@ -800,6 +805,36 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } _ => unreachable!(), }, + InvocationKind::GlobDelegation { item } => { + let AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() }; + let suffixes = match ext { + SyntaxExtensionKind::GlobDelegation(expander) => match expander.expand(self.cx) + { + ExpandResult::Ready(suffixes) => suffixes, + ExpandResult::Retry(()) => { + // Reassemble the original invocation for retrying. + return ExpandResult::Retry(Invocation { + kind: InvocationKind::GlobDelegation { item }, + ..invoc + }); + } + }, + SyntaxExtensionKind::LegacyBang(..) => { + let msg = "expanded a dummy glob delegation"; + let guar = self.cx.dcx().span_delayed_bug(span, msg); + return ExpandResult::Ready(fragment_kind.dummy(span, guar)); + } + _ => unreachable!(), + }; + + type Node = AstNodeWrapper, ImplItemTag>; + let single_delegations = build_single_delegations::( + self.cx, deleg, &item, &suffixes, item.span, true, + ); + fragment_kind.expect_from_annotatables( + single_delegations.map(|item| Annotatable::ImplItem(P(item))), + ) + } }) } @@ -1067,7 +1102,7 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { unreachable!() } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { None } fn delegation_item_kind(_deleg: Box) -> Self::ItemKind { @@ -1126,7 +1161,7 @@ impl InvocationCollectorNode for P { _ => unreachable!(), } } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { match &self.kind { ItemKind::DelegationMac(deleg) => Some((deleg, self)), _ => None, @@ -1270,7 +1305,7 @@ impl InvocationCollectorNode for AstNodeWrapper, TraitItemTag> _ => unreachable!(), } } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { match &self.wrapped.kind { AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)), _ => None, @@ -1311,7 +1346,7 @@ impl InvocationCollectorNode for AstNodeWrapper, ImplItemTag> _ => unreachable!(), } } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { match &self.wrapped.kind { AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)), _ => None, @@ -1487,7 +1522,7 @@ impl InvocationCollectorNode for ast::Stmt { }; (mac, attrs, if add_semicolon { AddSemicolon::Yes } else { AddSemicolon::No }) } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { match &self.kind { StmtKind::Item(item) => match &item.kind { ItemKind::DelegationMac(deleg) => Some((deleg, item)), @@ -1684,6 +1719,44 @@ impl InvocationCollectorNode for AstNodeWrapper, MethodReceiverTag> } } +fn build_single_delegations<'a, Node: InvocationCollectorNode>( + ecx: &ExtCtxt<'_>, + deleg: &'a ast::DelegationMac, + item: &'a ast::Item, + suffixes: &'a [(Ident, Option)], + item_span: Span, + from_glob: bool, +) -> impl Iterator> + 'a { + if suffixes.is_empty() { + // Report an error for now, to avoid keeping stem for resolution and + // stability checks. + let kind = String::from(if from_glob { "glob" } else { "list" }); + ecx.dcx().emit_err(EmptyDelegationMac { span: item.span, kind }); + } + + suffixes.iter().map(move |&(ident, rename)| { + let mut path = deleg.prefix.clone(); + path.segments.push(ast::PathSegment { ident, id: ast::DUMMY_NODE_ID, args: None }); + + ast::Item { + attrs: item.attrs.clone(), + id: ast::DUMMY_NODE_ID, + span: if from_glob { item_span } else { ident.span }, + vis: item.vis.clone(), + ident: rename.unwrap_or(ident), + kind: Node::delegation_item_kind(Box::new(ast::Delegation { + id: ast::DUMMY_NODE_ID, + qself: deleg.qself.clone(), + path, + rename, + body: deleg.body.clone(), + from_glob, + })), + tokens: None, + } + }) +} + struct InvocationCollector<'a, 'b> { cx: &'a mut ExtCtxt<'b>, invocations: Vec<(Invocation, Option>)>, @@ -1702,6 +1775,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect(&mut self, fragment_kind: AstFragmentKind, kind: InvocationKind) -> AstFragment { let expn_id = LocalExpnId::fresh_empty(); + if matches!(kind, InvocationKind::GlobDelegation { .. }) { + // In resolver we need to know which invocation ids are delegations early, + // before their `ExpnData` is filled. + self.cx.resolver.register_glob_delegation(expn_id); + } let vis = kind.placeholder_visibility(); self.invocations.push(( Invocation { @@ -1734,6 +1812,14 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.collect(kind, InvocationKind::Attr { attr, pos, item, derives }) } + fn collect_glob_delegation( + &mut self, + item: P, + kind: AstFragmentKind, + ) -> AstFragment { + self.collect(kind, InvocationKind::GlobDelegation { item }) + } + /// If `item` is an attribute invocation, remove the attribute and return it together with /// its position and derives following it. We have to collect the derives in order to resolve /// legacy derive helpers (helpers written before derives that introduce them). @@ -1901,37 +1987,27 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { Node::post_flat_map_node_collect_bang(&mut res, add_semicolon); res } - None if let Some((deleg, item)) = node.delegation_list() => { - if deleg.suffixes.is_empty() { - // Report an error for now, to avoid keeping stem for resolution and - // stability checks. - self.cx.dcx().emit_err(EmptyDelegationList { span: item.span }); - } - - Node::flatten_outputs(deleg.suffixes.iter().map(|&(ident, rename)| { - let mut path = deleg.prefix.clone(); - path.segments.push(ast::PathSegment { - ident, - id: ast::DUMMY_NODE_ID, - args: None, - }); - - let mut item = Node::from_item(ast::Item { - attrs: item.attrs.clone(), - id: ast::DUMMY_NODE_ID, - span: ident.span, - vis: item.vis.clone(), - ident: rename.unwrap_or(ident), - kind: Node::delegation_item_kind(Box::new(ast::Delegation { - id: ast::DUMMY_NODE_ID, - qself: deleg.qself.clone(), - path, - rename, - body: deleg.body.clone(), - })), - tokens: None, - }); + None if let Some((deleg, item)) = node.delegation() => { + let Some(suffixes) = &deleg.suffixes else { + let item = match node.to_annotatable() { + Annotatable::ImplItem(item) => item, + ann @ (Annotatable::Item(_) + | Annotatable::TraitItem(_) + | Annotatable::Stmt(_)) => { + let span = ann.span(); + self.cx.dcx().emit_err(GlobDelegationOutsideImpls { span }); + return Default::default(); + } + _ => unreachable!(), + }; + return self.collect_glob_delegation(item, Node::KIND).make_ast::(); + }; + let single_delegations = build_single_delegations::( + self.cx, deleg, item, suffixes, item.span, false, + ); + Node::flatten_outputs(single_delegations.map(|item| { + let mut item = Node::from_item(item); assign_id!(self, item.node_id_mut(), || item.noop_flat_map(self)) })) } @@ -1983,7 +2059,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.collect_bang(mac, Node::KIND).make_ast::() }) } - None if node.delegation_list().is_some() => unreachable!(), + None if node.delegation().is_some() => unreachable!(), None => { assign_id!(self, node.node_id_mut(), || node.noop_visit(self)) } diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 6c1fcbe06fc5..314f3f17dab0 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2699,12 +2699,13 @@ pub(crate) struct SingleColonImportPath { #[derive(Diagnostic)] #[diag(parse_bad_item_kind)] -#[help] pub(crate) struct BadItemKind { #[primary_span] pub span: Span, pub descr: &'static str, pub ctx: &'static str, + #[help] + pub help: Option<()>, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 3f5a4afdad8a..9df1431f6570 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -707,15 +707,25 @@ impl<'a> Parser<'a> { }; let (ident, item_kind) = if self.eat(&token::PathSep) { - let (suffixes, _) = self.parse_delim_comma_seq(Delimiter::Brace, |p| { - Ok((p.parse_path_segment_ident()?, rename(p)?)) - })?; + let suffixes = if self.eat(&token::BinOp(token::Star)) { + None + } else { + let parse_suffix = |p: &mut Self| Ok((p.parse_path_segment_ident()?, rename(p)?)); + Some(self.parse_delim_comma_seq(Delimiter::Brace, parse_suffix)?.0) + }; let deleg = DelegationMac { qself, prefix: path, suffixes, body: body(self)? }; (Ident::empty(), ItemKind::DelegationMac(Box::new(deleg))) } else { let rename = rename(self)?; let ident = rename.unwrap_or_else(|| path.segments.last().unwrap().ident); - let deleg = Delegation { id: DUMMY_NODE_ID, qself, path, rename, body: body(self)? }; + let deleg = Delegation { + id: DUMMY_NODE_ID, + qself, + path, + rename, + body: body(self)?, + from_glob: false, + }; (ident, ItemKind::Delegation(Box::new(deleg))) }; @@ -1237,7 +1247,11 @@ impl<'a> Parser<'a> { // FIXME(#100717): needs variant for each `ItemKind` (instead of using `ItemKind::descr()`) let span = self.psess.source_map().guess_head_span(span); let descr = kind.descr(); - self.dcx().emit_err(errors::BadItemKind { span, descr, ctx }); + let help = match kind { + ItemKind::DelegationMac(deleg) if deleg.suffixes.is_none() => None, + _ => Some(()), + }; + self.dcx().emit_err(errors::BadItemKind { span, descr, ctx, help }); None } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 689109b2840f..e035749fc39e 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -19,6 +19,7 @@ use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind}; use rustc_ast::{Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId}; use rustc_attr as attr; use rustc_data_structures::sync::Lrc; +use rustc_expand::base::ResolverExpand; use rustc_expand::expand::AstFragment; use rustc_hir::def::{self, *}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; @@ -1358,6 +1359,14 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.visit_invoc_in_module(item.id); } AssocCtxt::Impl => { + let invoc_id = item.id.placeholder_to_expn_id(); + if !self.r.glob_delegation_invoc_ids.contains(&invoc_id) { + self.r + .impl_unexpanded_invocations + .entry(self.r.invocation_parent(invoc_id)) + .or_default() + .insert(invoc_id); + } self.visit_invoc(item.id); } } @@ -1379,18 +1388,21 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.r.feed_visibility(feed, vis); } + let ns = match item.kind { + AssocItemKind::Const(..) | AssocItemKind::Delegation(..) | AssocItemKind::Fn(..) => { + ValueNS + } + AssocItemKind::Type(..) => TypeNS, + AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(..) => bug!(), // handled above + }; if ctxt == AssocCtxt::Trait { - let ns = match item.kind { - AssocItemKind::Const(..) - | AssocItemKind::Delegation(..) - | AssocItemKind::Fn(..) => ValueNS, - AssocItemKind::Type(..) => TypeNS, - AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(..) => bug!(), // handled above - }; - let parent = self.parent_scope.module; let expansion = self.parent_scope.expansion; self.r.define(parent, item.ident, ns, (self.res(def_id), vis, item.span, expansion)); + } else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob) { + let impl_def_id = self.r.tcx.local_parent(local_def_id); + let key = BindingKey::new(item.ident.normalize_to_macros_2_0(), ns); + self.r.impl_binding_keys.entry(impl_def_id).or_default().insert(key); } visit::walk_assoc_item(self, item, ctxt); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index ca97d10617b6..fb6e55f2b7bd 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -144,8 +144,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { } ItemKind::GlobalAsm(..) => DefKind::GlobalAsm, ItemKind::Use(..) => return visit::walk_item(self, i), - ItemKind::MacCall(..) => return self.visit_macro_invoc(i.id), - ItemKind::DelegationMac(..) => unreachable!(), + ItemKind::MacCall(..) | ItemKind::DelegationMac(..) => { + return self.visit_macro_invoc(i.id); + } }; let def_id = self.create_def(i.id, i.ident.name, def_kind, i.span); @@ -294,8 +295,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { AssocItemKind::Fn(..) | AssocItemKind::Delegation(..) => DefKind::AssocFn, AssocItemKind::Const(..) => DefKind::AssocConst, AssocItemKind::Type(..) => DefKind::AssocTy, - AssocItemKind::MacCall(..) => return self.visit_macro_invoc(i.id), - AssocItemKind::DelegationMac(..) => unreachable!(), + AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => { + return self.visit_macro_invoc(i.id); + } }; let def = self.create_def(i.id, i.ident.name, def_kind, i.span); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b0adc3775f66..fa711d932b63 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3281,17 +3281,19 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } self.visit_path(&delegation.path, delegation.id); if let Some(body) = &delegation.body { - // `PatBoundCtx` is not necessary in this context - let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; + self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { + // `PatBoundCtx` is not necessary in this context + let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - let span = delegation.path.segments.last().unwrap().ident.span; - self.fresh_binding( - Ident::new(kw::SelfLower, span), - delegation.id, - PatternSource::FnParam, - &mut bindings, - ); - self.visit_block(body); + let span = delegation.path.segments.last().unwrap().ident.span; + this.fresh_binding( + Ident::new(kw::SelfLower, span), + delegation.id, + PatternSource::FnParam, + &mut bindings, + ); + this.visit_block(body); + }); } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 9557b0f5ebc0..3a831a7f19e0 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1089,7 +1089,7 @@ pub struct Resolver<'a, 'tcx> { single_segment_macro_resolutions: Vec<(Ident, MacroKind, ParentScope<'a>, Option>)>, multi_segment_macro_resolutions: - Vec<(Vec, Span, MacroKind, ParentScope<'a>, Option)>, + Vec<(Vec, Span, MacroKind, ParentScope<'a>, Option, Namespace)>, builtin_attrs: Vec<(Ident, ParentScope<'a>)>, /// `derive(Copy)` marks items they are applied to so they are treated specially later. /// Derive macros cannot modify the item themselves and have to store the markers in the global @@ -1163,6 +1163,15 @@ pub struct Resolver<'a, 'tcx> { doc_link_resolutions: FxHashMap, doc_link_traits_in_scope: FxHashMap>, all_macro_rules: FxHashMap, + + /// Invocation ids of all glob delegations. + glob_delegation_invoc_ids: FxHashSet, + /// Analogue of module `unexpanded_invocations` but in trait impls, excluding glob delegations. + /// Needed because glob delegations wait for all other neighboring macros to expand. + impl_unexpanded_invocations: FxHashMap>, + /// Simplified analogue of module `resolutions` but in trait impls, excluding glob delegations. + /// Needed because glob delegations exclude explicitly defined names. + impl_binding_keys: FxHashMap>, } /// Nothing really interesting here; it just provides memory for the rest of the crate. @@ -1504,6 +1513,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { doc_link_traits_in_scope: Default::default(), all_macro_rules: Default::default(), delegation_fn_sigs: Default::default(), + glob_delegation_invoc_ids: Default::default(), + impl_unexpanded_invocations: Default::default(), + impl_binding_keys: Default::default(), }; let root_parent_scope = ParentScope::module(graph_root, &resolver); diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 268e7f06d042..87794d11cea9 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -5,7 +5,7 @@ use crate::errors::CannotDetermineMacroResolution; use crate::errors::{self, AddAsNonDerive, CannotFindIdentInThisScope}; use crate::errors::{MacroExpectedFound, RemoveSurroundingDerive}; use crate::Namespace::*; -use crate::{BuiltinMacroState, Determinacy, MacroData, Used}; +use crate::{BindingKey, BuiltinMacroState, Determinacy, MacroData, Used}; use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet}; use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding}; use rustc_ast::expand::StrippedCfgItem; @@ -198,6 +198,11 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope); parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion); + if let Some(unexpanded_invocations) = + self.impl_unexpanded_invocations.get_mut(&self.invocation_parent(expansion)) + { + unexpanded_invocations.remove(&expansion); + } } fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind) { @@ -262,15 +267,21 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { } }; - let (path, kind, inner_attr, derives) = match invoc.kind { - InvocationKind::Attr { ref attr, ref derives, .. } => ( - &attr.get_normal_item().path, - MacroKind::Attr, - attr.style == ast::AttrStyle::Inner, - self.arenas.alloc_ast_paths(derives), - ), - InvocationKind::Bang { ref mac, .. } => (&mac.path, MacroKind::Bang, false, &[][..]), - InvocationKind::Derive { ref path, .. } => (path, MacroKind::Derive, false, &[][..]), + let (mut derives, mut inner_attr, mut deleg_impl) = (&[][..], false, None); + let (path, kind) = match invoc.kind { + InvocationKind::Attr { ref attr, derives: ref attr_derives, .. } => { + derives = self.arenas.alloc_ast_paths(attr_derives); + inner_attr = attr.style == ast::AttrStyle::Inner; + (&attr.get_normal_item().path, MacroKind::Attr) + } + InvocationKind::Bang { ref mac, .. } => (&mac.path, MacroKind::Bang), + InvocationKind::Derive { ref path, .. } => (path, MacroKind::Derive), + InvocationKind::GlobDelegation { ref item } => { + let ast::AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() }; + deleg_impl = Some(self.invocation_parent(invoc_id)); + // It is sufficient to consider glob delegation a bang macro for now. + (&deleg.prefix, MacroKind::Bang) + } }; // Derives are not included when `invocations` are collected, so we have to add them here. @@ -286,10 +297,11 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { node_id, force, soft_custom_inner_attributes_gate(path, invoc), + deleg_impl, )?; let span = invoc.span(); - let def_id = res.opt_def_id(); + let def_id = if deleg_impl.is_some() { None } else { res.opt_def_id() }; invoc_id.set_expn_data( ext.expn_data( parent_scope.expansion, @@ -452,6 +464,45 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { fn registered_tools(&self) -> &RegisteredTools { self.registered_tools } + + fn register_glob_delegation(&mut self, invoc_id: LocalExpnId) { + self.glob_delegation_invoc_ids.insert(invoc_id); + } + + fn glob_delegation_suffixes( + &mut self, + trait_def_id: DefId, + impl_def_id: LocalDefId, + ) -> Result)>, Indeterminate> { + let target_trait = self.expect_module(trait_def_id); + if !target_trait.unexpanded_invocations.borrow().is_empty() { + return Err(Indeterminate); + } + // FIXME: Instead of waiting try generating all trait methods, and pruning + // the shadowed ones a bit later, e.g. when all macro expansion completes. + // Pros: expansion will be stuck less (but only in exotic cases), the implementation may be + // less hacky. + // Cons: More code is generated just to be deleted later, deleting already created `DefId`s + // may be nontrivial. + if let Some(unexpanded_invocations) = self.impl_unexpanded_invocations.get(&impl_def_id) + && !unexpanded_invocations.is_empty() + { + return Err(Indeterminate); + } + + let mut idents = Vec::new(); + target_trait.for_each_child(self, |this, ident, ns, _binding| { + // FIXME: Adjust hygiene for idents from globs, like for glob imports. + if let Some(overriding_keys) = this.impl_binding_keys.get(&impl_def_id) + && overriding_keys.contains(&BindingKey::new(ident.normalize_to_macros_2_0(), ns)) + { + // The name is overridden, do not produce it from the glob delegation. + } else { + idents.push((ident, None)); + } + }); + Ok(idents) + } } impl<'a, 'tcx> Resolver<'a, 'tcx> { @@ -468,15 +519,40 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { node_id: NodeId, force: bool, soft_custom_inner_attributes_gate: bool, + deleg_impl: Option, ) -> Result<(Lrc, Res), Indeterminate> { - let (ext, res) = match self.resolve_macro_path(path, Some(kind), parent_scope, true, force) - { + let (ext, res) = match self.resolve_macro_or_delegation_path( + path, + Some(kind), + parent_scope, + true, + force, + deleg_impl, + ) { Ok((Some(ext), res)) => (ext, res), Ok((None, res)) => (self.dummy_ext(kind), res), Err(Determinacy::Determined) => (self.dummy_ext(kind), Res::Err), Err(Determinacy::Undetermined) => return Err(Indeterminate), }; + // Everything below is irrelevant to glob delegation, take a shortcut. + if deleg_impl.is_some() { + if !matches!(res, Res::Err | Res::Def(DefKind::Trait, _)) { + self.dcx().emit_err(MacroExpectedFound { + span: path.span, + expected: "trait", + article: "a", + found: res.descr(), + macro_path: &pprust::path_to_string(path), + remove_surrounding_derive: None, + add_as_non_derive: None, + }); + return Ok((self.dummy_ext(kind), Res::Err)); + } + + return Ok((ext, res)); + } + // Report errors for the resolved macro. for segment in &path.segments { if let Some(args) = &segment.args { @@ -605,12 +681,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, trace: bool, force: bool, + ) -> Result<(Option>, Res), Determinacy> { + self.resolve_macro_or_delegation_path(path, kind, parent_scope, trace, force, None) + } + + fn resolve_macro_or_delegation_path( + &mut self, + path: &ast::Path, + kind: Option, + parent_scope: &ParentScope<'a>, + trace: bool, + force: bool, + deleg_impl: Option, ) -> Result<(Option>, Res), Determinacy> { let path_span = path.span; let mut path = Segment::from_path(path); // Possibly apply the macro helper hack - if kind == Some(MacroKind::Bang) + if deleg_impl.is_none() + && kind == Some(MacroKind::Bang) && path.len() == 1 && path[0].ident.span.ctxt().outer_expn_data().local_inner_macros { @@ -618,13 +707,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { path.insert(0, Segment::from_ident(root)); } - let res = if path.len() > 1 { - let res = match self.maybe_resolve_path(&path, Some(MacroNS), parent_scope) { + let res = if deleg_impl.is_some() || path.len() > 1 { + let ns = if deleg_impl.is_some() { TypeNS } else { MacroNS }; + let res = match self.maybe_resolve_path(&path, Some(ns), parent_scope) { PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => Ok(res), PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined), PathResult::NonModule(..) | PathResult::Indeterminate | PathResult::Failed { .. } => Err(Determinacy::Determined), + PathResult::Module(ModuleOrUniformRoot::Module(module)) => { + Ok(module.res().unwrap()) + } PathResult::Module(..) => unreachable!(), }; @@ -636,6 +729,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { kind, *parent_scope, res.ok(), + ns, )); } @@ -670,7 +764,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { res }; - res.map(|res| (self.get_macro(res).map(|macro_data| macro_data.ext.clone()), res)) + let res = res?; + let ext = match deleg_impl { + Some(impl_def_id) => match res { + def::Res::Def(DefKind::Trait, def_id) => { + let edition = self.tcx.sess.edition(); + Some(Lrc::new(SyntaxExtension::glob_delegation(def_id, impl_def_id, edition))) + } + _ => None, + }, + None => self.get_macro(res).map(|macro_data| macro_data.ext.clone()), + }; + Ok((ext, res)) } pub(crate) fn finalize_macro_resolutions(&mut self, krate: &Crate) { @@ -706,14 +811,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }; let macro_resolutions = mem::take(&mut self.multi_segment_macro_resolutions); - for (mut path, path_span, kind, parent_scope, initial_res) in macro_resolutions { + for (mut path, path_span, kind, parent_scope, initial_res, ns) in macro_resolutions { // FIXME: Path resolution will ICE if segment IDs present. for seg in &mut path { seg.id = None; } match self.resolve_path( &path, - Some(MacroNS), + Some(ns), &parent_scope, Some(Finalize::new(ast::CRATE_NODE_ID, path_span)), None, @@ -721,6 +826,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => { check_consistency(self, &path, path_span, kind, initial_res, res) } + // This may be a trait for glob delegation expansions. + PathResult::Module(ModuleOrUniformRoot::Module(module)) => check_consistency( + self, + &path, + path_span, + kind, + initial_res, + module.res().unwrap(), + ), path_res @ (PathResult::NonModule(..) | PathResult::Failed { .. }) => { let mut suggestion = None; let (span, label, module) = diff --git a/tests/ui/delegation/body-identity-glob.rs b/tests/ui/delegation/body-identity-glob.rs new file mode 100644 index 000000000000..58b644f46d61 --- /dev/null +++ b/tests/ui/delegation/body-identity-glob.rs @@ -0,0 +1,32 @@ +//@ check-pass + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) {} + fn bar(&self) {} +} + +impl Trait for u8 {} + +struct S(u8); + +mod to_import { + pub fn check(arg: &u8) -> &u8 { arg } +} + +impl Trait for S { + reuse Trait::* { + use to_import::check; + + let _arr = Some(self.0).map(|x| [x * 2; 3]); + check(&self.0) + } +} + +fn main() { + let s = S(0); + s.foo(); + s.bar(); +} diff --git a/tests/ui/delegation/empty-glob.rs b/tests/ui/delegation/empty-glob.rs new file mode 100644 index 000000000000..d98579d89725 --- /dev/null +++ b/tests/ui/delegation/empty-glob.rs @@ -0,0 +1,11 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait {} + +struct S; +impl S { + reuse Trait::*; //~ ERROR empty glob delegation is not supported +} + +fn main() {} diff --git a/tests/ui/delegation/empty-glob.stderr b/tests/ui/delegation/empty-glob.stderr new file mode 100644 index 000000000000..f4d282f6a0f6 --- /dev/null +++ b/tests/ui/delegation/empty-glob.stderr @@ -0,0 +1,8 @@ +error: empty glob delegation is not supported + --> $DIR/empty-glob.rs:8:5 + | +LL | reuse Trait::*; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/delegation/glob-bad-path.rs b/tests/ui/delegation/glob-bad-path.rs new file mode 100644 index 000000000000..7bc4f0153a30 --- /dev/null +++ b/tests/ui/delegation/glob-bad-path.rs @@ -0,0 +1,12 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait {} +struct S; + +impl Trait for u8 { + reuse unresolved::*; //~ ERROR failed to resolve: use of undeclared crate or module `unresolved` + reuse S::*; //~ ERROR expected trait, found struct `S` +} + +fn main() {} diff --git a/tests/ui/delegation/glob-bad-path.stderr b/tests/ui/delegation/glob-bad-path.stderr new file mode 100644 index 000000000000..0c06364b3f0a --- /dev/null +++ b/tests/ui/delegation/glob-bad-path.stderr @@ -0,0 +1,15 @@ +error: expected trait, found struct `S` + --> $DIR/glob-bad-path.rs:9:11 + | +LL | reuse S::*; + | ^ not a trait + +error[E0433]: failed to resolve: use of undeclared crate or module `unresolved` + --> $DIR/glob-bad-path.rs:8:11 + | +LL | reuse unresolved::*; + | ^^^^^^^^^^ use of undeclared crate or module `unresolved` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/delegation/glob-glob-conflict.rs b/tests/ui/delegation/glob-glob-conflict.rs new file mode 100644 index 000000000000..2843bf8c4934 --- /dev/null +++ b/tests/ui/delegation/glob-glob-conflict.rs @@ -0,0 +1,33 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait1 { + fn method(&self) -> u8; +} +trait Trait2 { + fn method(&self) -> u8; +} +trait Trait { + fn method(&self) -> u8; +} + +impl Trait1 for u8 { + fn method(&self) -> u8 { 0 } +} +impl Trait1 for u16 { + fn method(&self) -> u8 { 1 } +} +impl Trait2 for u8 { + fn method(&self) -> u8 { 2 } +} + +impl Trait for u8 { + reuse Trait1::*; + reuse Trait2::*; //~ ERROR duplicate definitions with name `method` +} +impl Trait for u16 { + reuse Trait1::*; + reuse Trait1::*; //~ ERROR duplicate definitions with name `method` +} + +fn main() {} diff --git a/tests/ui/delegation/glob-glob-conflict.stderr b/tests/ui/delegation/glob-glob-conflict.stderr new file mode 100644 index 000000000000..8c7e5a4b023c --- /dev/null +++ b/tests/ui/delegation/glob-glob-conflict.stderr @@ -0,0 +1,25 @@ +error[E0201]: duplicate definitions with name `method`: + --> $DIR/glob-glob-conflict.rs:26:5 + | +LL | fn method(&self) -> u8; + | ----------------------- item in trait +... +LL | reuse Trait1::*; + | ---------------- previous definition here +LL | reuse Trait2::*; + | ^^^^^^^^^^^^^^^^ duplicate definition + +error[E0201]: duplicate definitions with name `method`: + --> $DIR/glob-glob-conflict.rs:30:5 + | +LL | fn method(&self) -> u8; + | ----------------------- item in trait +... +LL | reuse Trait1::*; + | ---------------- previous definition here +LL | reuse Trait1::*; + | ^^^^^^^^^^^^^^^^ duplicate definition + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0201`. diff --git a/tests/ui/delegation/glob-glob.rs b/tests/ui/delegation/glob-glob.rs new file mode 100644 index 000000000000..ef7f9a15e195 --- /dev/null +++ b/tests/ui/delegation/glob-glob.rs @@ -0,0 +1,36 @@ +//@ check-pass + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +mod inner { + pub trait TraitFoo { + fn foo(&self) -> u8; + } + pub trait TraitBar { + fn bar(&self) -> u8; + } + + impl TraitFoo for u8 { + fn foo(&self) -> u8 { 0 } + } + impl TraitBar for u8 { + fn bar(&self) -> u8 { 1 } + } +} + +trait Trait { + fn foo(&self) -> u8; + fn bar(&self) -> u8; +} + +impl Trait for u8 { + reuse inner::TraitFoo::*; + reuse inner::TraitBar::*; +} + +fn main() { + let u = 0u8; + u.foo(); + u.bar(); +} diff --git a/tests/ui/delegation/glob-non-fn.rs b/tests/ui/delegation/glob-non-fn.rs new file mode 100644 index 000000000000..ab312d51f498 --- /dev/null +++ b/tests/ui/delegation/glob-non-fn.rs @@ -0,0 +1,38 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn method(&self); + const CONST: u8; + type Type; + #[allow(non_camel_case_types)] + type method; +} + +impl Trait for u8 { + fn method(&self) {} + const CONST: u8 = 0; + type Type = u8; + type method = u8; +} + +struct Good(u8); +impl Trait for Good { + reuse Trait::* { &self.0 } + // Explicit definitions for non-delegatable items. + const CONST: u8 = 0; + type Type = u8; + type method = u8; +} + +struct Bad(u8); +impl Trait for Bad { //~ ERROR not all trait items implemented, missing: `CONST`, `Type`, `method` + reuse Trait::* { &self.0 } + //~^ ERROR item `CONST` is an associated method, which doesn't match its trait `Trait` + //~| ERROR item `Type` is an associated method, which doesn't match its trait `Trait` + //~| ERROR duplicate definitions with name `method` + //~| ERROR expected function, found associated constant `Trait::CONST` + //~| ERROR expected function, found associated type `Trait::Type` +} + +fn main() {} diff --git a/tests/ui/delegation/glob-non-fn.stderr b/tests/ui/delegation/glob-non-fn.stderr new file mode 100644 index 000000000000..4b918c53b848 --- /dev/null +++ b/tests/ui/delegation/glob-non-fn.stderr @@ -0,0 +1,62 @@ +error[E0324]: item `CONST` is an associated method, which doesn't match its trait `Trait` + --> $DIR/glob-non-fn.rs:30:5 + | +LL | const CONST: u8; + | ---------------- item in trait +... +LL | reuse Trait::* { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait + +error[E0324]: item `Type` is an associated method, which doesn't match its trait `Trait` + --> $DIR/glob-non-fn.rs:30:5 + | +LL | type Type; + | ---------- item in trait +... +LL | reuse Trait::* { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait + +error[E0201]: duplicate definitions with name `method`: + --> $DIR/glob-non-fn.rs:30:5 + | +LL | fn method(&self); + | ----------------- item in trait +... +LL | reuse Trait::* { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | duplicate definition + | previous definition here + +error[E0423]: expected function, found associated constant `Trait::CONST` + --> $DIR/glob-non-fn.rs:30:11 + | +LL | reuse Trait::* { &self.0 } + | ^^^^^ not a function + +error[E0423]: expected function, found associated type `Trait::Type` + --> $DIR/glob-non-fn.rs:30:11 + | +LL | reuse Trait::* { &self.0 } + | ^^^^^ + | + = note: can't use a type alias as a constructor + +error[E0046]: not all trait items implemented, missing: `CONST`, `Type`, `method` + --> $DIR/glob-non-fn.rs:29:1 + | +LL | const CONST: u8; + | --------------- `CONST` from trait +LL | type Type; + | --------- `Type` from trait +LL | #[allow(non_camel_case_types)] +LL | type method; + | ----------- `method` from trait +... +LL | impl Trait for Bad { + | ^^^^^^^^^^^^^^^^^^ missing `CONST`, `Type`, `method` in implementation + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0046, E0201, E0324, E0423. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/delegation/glob-non-impl.rs b/tests/ui/delegation/glob-non-impl.rs new file mode 100644 index 000000000000..d523731eeb35 --- /dev/null +++ b/tests/ui/delegation/glob-non-impl.rs @@ -0,0 +1,20 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn method() {} +} + +reuse Trait::*; //~ ERROR glob delegation is only supported in impls + +trait OtherTrait { + reuse Trait::*; //~ ERROR glob delegation is only supported in impls +} + +extern { + reuse Trait::*; //~ ERROR delegation is not supported in `extern` blocks +} + +fn main() { + reuse Trait::*; //~ ERROR glob delegation is only supported in impls +} diff --git a/tests/ui/delegation/glob-non-impl.stderr b/tests/ui/delegation/glob-non-impl.stderr new file mode 100644 index 000000000000..ea458fd5e90f --- /dev/null +++ b/tests/ui/delegation/glob-non-impl.stderr @@ -0,0 +1,26 @@ +error: delegation is not supported in `extern` blocks + --> $DIR/glob-non-impl.rs:15:5 + | +LL | reuse Trait::*; + | ^^^^^^^^^^^^^^^ + +error: glob delegation is only supported in impls + --> $DIR/glob-non-impl.rs:8:1 + | +LL | reuse Trait::*; + | ^^^^^^^^^^^^^^^ + +error: glob delegation is only supported in impls + --> $DIR/glob-non-impl.rs:11:5 + | +LL | reuse Trait::*; + | ^^^^^^^^^^^^^^^ + +error: glob delegation is only supported in impls + --> $DIR/glob-non-impl.rs:19:5 + | +LL | reuse Trait::*; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/delegation/glob-override.rs b/tests/ui/delegation/glob-override.rs new file mode 100644 index 000000000000..1d0dcf1df6e6 --- /dev/null +++ b/tests/ui/delegation/glob-override.rs @@ -0,0 +1,37 @@ +//@ check-pass + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) -> u8; + fn bar(&self) -> u8; +} + +impl Trait for u8 { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } +} + +struct S(u8); +struct Z(u8); + +impl Trait for S { + reuse Trait::* { &self.0 } + fn bar(&self) -> u8 { 2 } +} + +impl Trait for Z { + reuse Trait::* { &self.0 } + reuse Trait::bar { &self.0 } +} + +fn main() { + let s = S(2); + s.foo(); + s.bar(); + + let z = Z(2); + z.foo(); + z.bar(); +} diff --git a/tests/ui/delegation/glob.rs b/tests/ui/delegation/glob.rs new file mode 100644 index 000000000000..5bc80c166489 --- /dev/null +++ b/tests/ui/delegation/glob.rs @@ -0,0 +1,35 @@ +//@ check-pass + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) -> u8; + fn bar(&self) -> u8; +} + +impl Trait for u8 { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } +} + +struct S(u8); +struct Z(u8); + +impl Trait for S { + reuse Trait::* { &self.0 } +} + +impl Trait for Z { + reuse ::* { &self.0 } +} + +fn main() { + let s = S(2); + s.foo(); + s.bar(); + + let z = Z(3); + z.foo(); + z.bar(); +} diff --git a/tests/ui/delegation/macro-inside-glob.rs b/tests/ui/delegation/macro-inside-glob.rs new file mode 100644 index 000000000000..1d529341c5b4 --- /dev/null +++ b/tests/ui/delegation/macro-inside-glob.rs @@ -0,0 +1,26 @@ +//@ check-pass + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } +} + +impl Trait for u8 {} + +struct S(u8); + +// Macro expansion works inside delegation items. +macro_rules! u8 { () => { u8 } } +macro_rules! self_0 { ($self:ident) => { &$self.0 } } +impl Trait for S { + reuse ::* { self_0!(self) } +} + +fn main() { + let s = S(2); + s.foo(); + s.bar(); +} diff --git a/tests/ui/delegation/macro-inside-list.rs b/tests/ui/delegation/macro-inside-list.rs index 16c74d396ef0..d07a4e47dd4c 100644 --- a/tests/ui/delegation/macro-inside-list.rs +++ b/tests/ui/delegation/macro-inside-list.rs @@ -14,9 +14,9 @@ struct S(u8); // Macro expansion works inside delegation items. macro_rules! u8 { () => { u8 } } -macro_rules! self_0 { () => { &self.0 } } +macro_rules! self_0 { ($self:ident) => { &$self.0 } } impl Trait for S { - reuse ::{foo, bar} { self_0!() } + reuse ::{foo, bar} { self_0!(self) } } fn main() { From df1d6168f4deef4954f3b74a23fe1e923d9b95b6 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 14 Jun 2024 17:02:07 +0000 Subject: [PATCH 016/147] safe transmute: support non-ZST, variantful, uninhabited enums Previously, `Tree::from_enum`'s implementation branched into three disjoint cases: 1. enums that uninhabited 2. enums for which all but one variant is uninhabited 3. enums with multiple inhabited variants This branching (incorrectly) did not differentiate between variantful and variantless uninhabited enums. In both cases, we assumed (and asserted) that uninhabited enums are zero-sized types. This assumption is false for enums like: enum Uninhabited { A(!, u128) } ...which, currently, has the same size as `u128`. This faulty assumption manifested as the ICE reported in #126460. In this PR, we revise the first case of `Tree::from_enum` to consider only the narrow category of "enums that are uninhabited ZSTs". These enums, whose layouts are described with `Variants::Single { index }`, are special in their layouts otherwise resemble the `!` type and cannot be descended into like typical enums. This first case captures uninhabited enums like: enum Uninhabited { A(!, !), B(!) } The second case is revised to consider the broader category of "enums that defer their layout to one of their variants"; i.e., enums whose layouts are described with `Variants::Single { index }` and that do have a variant at `index`. This second case captures uninhabited enums that are not ZSTs, like: enum Uninhabited { A(!, u128) } ...which represent their variants with `Variants::Single`. Finally, the third case is revised to cover the broader category of "enums with multiple variants", which captures uninhabited, non-ZST enums like: enum Uninhabited { A(u8, !), B(!, u32) } ...which represent their variants with `Variants::Multiple`. This PR also adds a comment requested by RalfJung in his review of #126358 to `compiler/rustc_const_eval/src/interpret/discriminant.rs`. Fixes #126460 --- .../src/interpret/discriminant.rs | 11 ++++- compiler/rustc_transmute/src/layout/tree.rs | 36 +++++++--------- .../enums/uninhabited_optimization.rs | 6 +++ tests/ui/transmutability/uninhabited.rs | 24 ++++++++++- tests/ui/transmutability/uninhabited.stderr | 42 ++++++++++++++++--- 5 files changed, 89 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index a50b50d231d7..b3a139d553ad 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -241,7 +241,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { variant_index: VariantIdx, ) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> { match self.layout_of(ty)?.variants { - abi::Variants::Single { .. } => Ok(None), + abi::Variants::Single { .. } => { + // The tag of a `Single` enum is like the tag of the niched + // variant: there's no tag as the discriminant is encoded + // entirely implicitly. If `write_discriminant` ever hits this + // case, we do a "validation read" to ensure the the right + // discriminant is encoded implicitly, so any attempt to write + // the wrong discriminant for a `Single` enum will reliably + // result in UB. + Ok(None) + } abi::Variants::Multiple { tag_encoding: TagEncoding::Direct, diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 241381f5875e..865f9487213f 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -341,37 +341,29 @@ pub(crate) mod rustc { // We consider three kinds of enums, each demanding a different // treatment of their layout computation: - // 1. enums that are uninhabited - // 2. enums for which all but one variant is uninhabited - // 3. enums with multiple inhabited variants + // 1. enums that are uninhabited ZSTs + // 2. enums that delegate their layout to a variant + // 3. enums with multiple variants match layout.variants() { - _ if layout.abi.is_uninhabited() => { - // Uninhabited enums are usually (always?) zero-sized. In - // the (unlikely?) event that an uninhabited enum is - // non-zero-sized, this assert will trigger an ICE, and this - // code should be modified such that a `layout.size` amount - // of uninhabited bytes is returned instead. - // - // Uninhabited enums are currently implemented such that - // their layout is described with `Variants::Single`, even - // though they don't necessarily have a 'single' variant to - // defer to. That said, we don't bother specifically - // matching on `Variants::Single` in this arm because the - // behavioral principles here remain true even if, for - // whatever reason, the compiler describes an uninhabited - // enum with `Variants::Multiple`. - assert_eq!(layout.size, Size::ZERO); + Variants::Single { .. } + if layout.abi.is_uninhabited() && layout.size == Size::ZERO => + { + // The layout representation of uninhabited, ZST enums is + // defined to be like that of the `!` type, as opposed of a + // typical enum. Consequently, they cannot be descended into + // as if they typical enums. We therefore special-case this + // scenario and simply return an uninhabited `Tree`. Ok(Self::uninhabited()) } Variants::Single { index } => { - // `Variants::Single` on non-uninhabited enums denotes that + // `Variants::Single` on enums with variants denotes that // the enum delegates its layout to the variant at `index`. layout_of_variant(*index) } Variants::Multiple { tag_field, .. } => { // `Variants::Multiple` denotes an enum with multiple - // inhabited variants. The layout of such an enum is the - // disjunction of the layouts of its tagged variants. + // variants. The layout of such an enum is the disjunction + // of the layouts of its tagged variants. // For enums (but not coroutines), the tag field is // currently always the first field of the layout. diff --git a/tests/ui/transmutability/enums/uninhabited_optimization.rs b/tests/ui/transmutability/enums/uninhabited_optimization.rs index 04a8eb40c8b8..c2d5b67ab2ce 100644 --- a/tests/ui/transmutability/enums/uninhabited_optimization.rs +++ b/tests/ui/transmutability/enums/uninhabited_optimization.rs @@ -19,8 +19,14 @@ enum SingleUninhabited { Y(Uninhabited), } +enum MultipleUninhabited { + X(u8, Uninhabited), + Y(Uninhabited, u16), +} + fn main() { assert_transmutable::(); assert_transmutable::(); assert_transmutable::(); + assert_transmutable::(); } diff --git a/tests/ui/transmutability/uninhabited.rs b/tests/ui/transmutability/uninhabited.rs index b61b110f6a11..7524922c16a7 100644 --- a/tests/ui/transmutability/uninhabited.rs +++ b/tests/ui/transmutability/uninhabited.rs @@ -30,7 +30,7 @@ fn void() { } // Non-ZST uninhabited types are, nonetheless, uninhabited. -fn yawning_void() { +fn yawning_void_struct() { enum Void {} struct YawningVoid(Void, u128); @@ -49,6 +49,28 @@ fn yawning_void() { assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted } +// Non-ZST uninhabited types are, nonetheless, uninhabited. +fn yawning_void_enum() { + enum Void {} + + enum YawningVoid { + A(Void, u128), + } + + const _: () = { + assert!(std::mem::size_of::() == std::mem::size_of::()); + // Just to be sure the above constant actually evaluated: + assert!(false); //~ ERROR: evaluation of constant value failed + }; + + // This transmutation is vacuously acceptable; since one cannot construct a + // `Void`, unsoundness cannot directly arise from transmuting a void into + // anything else. + assert::is_maybe_transmutable::(); + + assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted +} + // References to uninhabited types are, logically, uninhabited, but for layout // purposes are not ZSTs, and aren't treated as uninhabited when they appear in // enum variants. diff --git a/tests/ui/transmutability/uninhabited.stderr b/tests/ui/transmutability/uninhabited.stderr index 60219b0f263c..88a98c798fc3 100644 --- a/tests/ui/transmutability/uninhabited.stderr +++ b/tests/ui/transmutability/uninhabited.stderr @@ -7,10 +7,18 @@ LL | assert!(false); = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/uninhabited.rs:65:9 + --> $DIR/uninhabited.rs:63:9 | LL | assert!(false); - | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:65:9 + | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:63:9 + | + = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/uninhabited.rs:87:9 + | +LL | assert!(false); + | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:87:9 | = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -36,11 +44,33 @@ LL | | } LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` -error[E0277]: `()` cannot be safely transmuted into `yawning_void::Void` +error[E0277]: `()` cannot be safely transmuted into `yawning_void_struct::Void` --> $DIR/uninhabited.rs:49:41 | LL | assert::is_maybe_transmutable::<(), Void>(); - | ^^^^ `yawning_void::Void` is uninhabited + | ^^^^ `yawning_void_struct::Void` is uninhabited + | +note: required by a bound in `is_maybe_transmutable` + --> $DIR/uninhabited.rs:10:14 + | +LL | pub fn is_maybe_transmutable() + | --------------------- required by a bound in this function +LL | where +LL | Dst: BikeshedIntrinsicFrom + | |__________^ required by this bound in `is_maybe_transmutable` + +error[E0277]: `()` cannot be safely transmuted into `yawning_void_enum::Void` + --> $DIR/uninhabited.rs:71:41 + | +LL | assert::is_maybe_transmutable::<(), Void>(); + | ^^^^ `yawning_void_enum::Void` is uninhabited | note: required by a bound in `is_maybe_transmutable` --> $DIR/uninhabited.rs:10:14 @@ -59,7 +89,7 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0277]: `u128` cannot be safely transmuted into `DistantVoid` - --> $DIR/uninhabited.rs:70:43 + --> $DIR/uninhabited.rs:92:43 | LL | assert::is_maybe_transmutable::(); | ^^^^^^^^^^^ at least one value of `u128` isn't a bit-valid value of `DistantVoid` @@ -80,7 +110,7 @@ LL | | } LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0080, E0277. For more information about an error, try `rustc --explain E0080`. From ab0e72781f6bccb1c033a7c79edaebc6124ac2b5 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 13 Jun 2024 17:08:04 +0200 Subject: [PATCH 017/147] Suggest standalone doctest for non-local impl defs --- compiler/rustc_lint/messages.ftl | 1 + compiler/rustc_lint/src/lints.rs | 5 ++ compiler/rustc_lint/src/non_local_def.rs | 84 ++++++++++--------- .../rustdoc-ui/doctest/auxiliary/pub_trait.rs | 1 + .../rustdoc-ui/doctest/non-local-defs-impl.rs | 31 +++++++ .../doctest/non-local-defs-impl.stdout | 37 ++++++++ 6 files changed, 121 insertions(+), 38 deletions(-) create mode 100644 tests/rustdoc-ui/doctest/auxiliary/pub_trait.rs create mode 100644 tests/rustdoc-ui/doctest/non-local-defs-impl.rs create mode 100644 tests/rustdoc-ui/doctest/non-local-defs-impl.stdout diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 733c73bc3d07..7430f1fc13ae 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -548,6 +548,7 @@ lint_non_local_definitions_impl = non-local `impl` definition, `impl` blocks sho .without_trait = methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` .with_trait = an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` .bounds = `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + .doctest = make this doc-test a standalone test with its own `fn main() {"{"} ... {"}"}` .exception = items in an anonymous const item (`const _: () = {"{"} ... {"}"}`) are treated as in the same scope as the anonymous const's declaration .const_anon = use a const-anon item to suppress this lint diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index b377da31a581..6cc0a81aa4f6 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1358,6 +1358,7 @@ pub enum NonLocalDefinitionsDiag { cargo_update: Option, const_anon: Option>, move_to: Option<(Span, Vec)>, + doctest: bool, may_remove: Option<(Span, String)>, has_trait: bool, self_ty_str: String, @@ -1383,6 +1384,7 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag { cargo_update, const_anon, move_to, + doctest, may_remove, has_trait, self_ty_str, @@ -1411,6 +1413,9 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag { } diag.span_help(ms, fluent::lint_non_local_definitions_impl_move_help); } + if doctest { + diag.help(fluent::lint_doctest); + } if let Some((span, part)) = may_remove { diag.arg("may_remove_part", part); diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index d7ffc34d824f..423862dcdbae 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -111,6 +111,12 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { } }; + // determining if we are in a doctest context can't currently be determined + // by the code itself (there are no specific attributes), but fortunately rustdoc + // sets a perma-unstable env var for libtest so we just reuse that for now + let is_at_toplevel_doctest = + || self.body_depth == 2 && std::env::var("UNSTABLE_RUSTDOC_TEST_PATH").is_ok(); + match item.kind { ItemKind::Impl(impl_) => { // The RFC states: @@ -191,29 +197,6 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { None }; - let mut collector = PathCollector { paths: Vec::new() }; - collector.visit_ty(&impl_.self_ty); - if let Some(of_trait) = &impl_.of_trait { - collector.visit_trait_ref(of_trait); - } - collector.visit_generics(&impl_.generics); - - let mut may_move: Vec = collector - .paths - .into_iter() - .filter_map(|path| { - if let Some(did) = path.res.opt_def_id() - && did_has_local_parent(did, cx.tcx, parent, parent_parent) - { - Some(cx.tcx.def_span(did)) - } else { - None - } - }) - .collect(); - may_move.sort(); - may_move.dedup(); - let const_anon = matches!(parent_def_kind, DefKind::Const | DefKind::Static { .. }) .then_some(span_for_const_anon_suggestion); @@ -248,14 +231,44 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { } else { None }; - let move_to = if may_move.is_empty() { - ms.push_span_label( - cx.tcx.def_span(parent), - fluent::lint_non_local_definitions_impl_move_help, - ); - None + + let (doctest, move_to) = if is_at_toplevel_doctest() { + (true, None) } else { - Some((cx.tcx.def_span(parent), may_move)) + let mut collector = PathCollector { paths: Vec::new() }; + collector.visit_ty(&impl_.self_ty); + if let Some(of_trait) = &impl_.of_trait { + collector.visit_trait_ref(of_trait); + } + collector.visit_generics(&impl_.generics); + + let mut may_move: Vec = collector + .paths + .into_iter() + .filter_map(|path| { + if let Some(did) = path.res.opt_def_id() + && did_has_local_parent(did, cx.tcx, parent, parent_parent) + { + Some(cx.tcx.def_span(did)) + } else { + None + } + }) + .collect(); + may_move.sort(); + may_move.dedup(); + + let move_to = if may_move.is_empty() { + ms.push_span_label( + cx.tcx.def_span(parent), + fluent::lint_non_local_definitions_impl_move_help, + ); + None + } else { + Some((cx.tcx.def_span(parent), may_move)) + }; + + (false, move_to) }; cx.emit_span_lint( @@ -272,6 +285,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { self_ty_str, of_trait_str, move_to, + doctest, may_remove, has_trait: impl_.of_trait.is_some(), }, @@ -280,12 +294,6 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { ItemKind::Macro(_macro, MacroKind::Bang) if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) => { - // determining we if are in a doctest context can't currently be determined - // by the code it-self (no specific attrs), but fortunatly rustdoc sets a - // perma-unstable env for libtest so we just re-use that env for now - let is_at_toplevel_doctest = - self.body_depth == 2 && std::env::var("UNSTABLE_RUSTDOC_TEST_PATH").is_ok(); - cx.emit_span_lint( NON_LOCAL_DEFINITIONS, item.span, @@ -296,8 +304,8 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { .map(|s| s.to_ident_string()) .unwrap_or_else(|| "".to_string()), cargo_update: cargo_update(), - help: (!is_at_toplevel_doctest).then_some(()), - doctest_help: is_at_toplevel_doctest.then_some(()), + help: (!is_at_toplevel_doctest()).then_some(()), + doctest_help: is_at_toplevel_doctest().then_some(()), }, ) } diff --git a/tests/rustdoc-ui/doctest/auxiliary/pub_trait.rs b/tests/rustdoc-ui/doctest/auxiliary/pub_trait.rs new file mode 100644 index 000000000000..0a47fdc74d72 --- /dev/null +++ b/tests/rustdoc-ui/doctest/auxiliary/pub_trait.rs @@ -0,0 +1 @@ +pub trait Trait {} diff --git a/tests/rustdoc-ui/doctest/non-local-defs-impl.rs b/tests/rustdoc-ui/doctest/non-local-defs-impl.rs new file mode 100644 index 000000000000..c984e097c046 --- /dev/null +++ b/tests/rustdoc-ui/doctest/non-local-defs-impl.rs @@ -0,0 +1,31 @@ +//@ check-fail +//@ edition:2018 +//@ failure-status: 101 +//@ aux-build:pub_trait.rs +//@ compile-flags: --test --test-args --test-threads=1 +//@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" + +#![doc(test(attr(deny(non_local_definitions))))] +#![doc(test(attr(allow(dead_code))))] + +/// This will produce a warning: +/// ```rust,no_run +/// # extern crate pub_trait; +/// # use pub_trait::Trait; +/// +/// struct Local; +/// impl Trait for &Local {} +/// ``` +/// +/// But this shoudln't produce a warning: +/// ```rust,no_run +/// # extern crate pub_trait; +/// # use pub_trait::Trait; +/// +/// struct Local; +/// impl Trait for &Local {} +/// +/// # fn main() {} +/// ``` +pub fn doctest() {} diff --git a/tests/rustdoc-ui/doctest/non-local-defs-impl.stdout b/tests/rustdoc-ui/doctest/non-local-defs-impl.stdout new file mode 100644 index 000000000000..27797e22f8ec --- /dev/null +++ b/tests/rustdoc-ui/doctest/non-local-defs-impl.stdout @@ -0,0 +1,37 @@ + +running 2 tests +test $DIR/non-local-defs-impl.rs - doctest (line 13) - compile ... FAILED +test $DIR/non-local-defs-impl.rs - doctest (line 22) - compile ... ok + +failures: + +---- $DIR/non-local-defs-impl.rs - doctest (line 13) stdout ---- +error: non-local `impl` definition, `impl` blocks should be written at the same level as their item + --> $DIR/non-local-defs-impl.rs:18:1 + | +LL | impl Trait for &Local {} + | ^^^^^-----^^^^^------ + | | | + | | `&'_ Local` is not local + | | help: remove `&` to make the `impl` local + | `Trait` is not local + | + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` + = help: make this doc-test a standalone test with its own `fn main() { ... }` + = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue +note: the lint level is defined here + --> $DIR/non-local-defs-impl.rs:11:9 + | +LL | #![deny(non_local_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +Couldn't compile the test. + +failures: + $DIR/non-local-defs-impl.rs - doctest (line 13) + +test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + From 94c282197d7081370d3fd538c66976fb41554cf6 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 13 Jun 2024 19:23:30 +0200 Subject: [PATCH 018/147] Also simplify macro_rules doctest code --- compiler/rustc_lint/src/lints.rs | 13 +++++-------- compiler/rustc_lint/src/non_local_def.rs | 3 +-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 6cc0a81aa4f6..1c983a516c85 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1368,8 +1368,7 @@ pub enum NonLocalDefinitionsDiag { depth: u32, body_kind_descr: &'static str, body_name: String, - help: Option<()>, - doctest_help: Option<()>, + doctest: bool, cargo_update: Option, }, } @@ -1448,8 +1447,7 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag { depth, body_kind_descr, body_name, - help, - doctest_help, + doctest, cargo_update, } => { diag.primary_message(fluent::lint_non_local_definitions_macro_rules); @@ -1457,11 +1455,10 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag { diag.arg("body_kind_descr", body_kind_descr); diag.arg("body_name", body_name); - if let Some(()) = help { - diag.help(fluent::lint_help); - } - if let Some(()) = doctest_help { + if doctest { diag.help(fluent::lint_help_doctest); + } else { + diag.help(fluent::lint_help); } diag.note(fluent::lint_non_local); diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 423862dcdbae..48d791a38596 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -304,8 +304,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { .map(|s| s.to_ident_string()) .unwrap_or_else(|| "".to_string()), cargo_update: cargo_update(), - help: (!is_at_toplevel_doctest()).then_some(()), - doctest_help: is_at_toplevel_doctest().then_some(()), + doctest: is_at_toplevel_doctest(), }, ) } From f7496f5f85fccadc4cb81704b78a5ffe3db16ea2 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Sun, 16 Jun 2024 08:27:49 +0200 Subject: [PATCH 019/147] Apply review comments --- .../core/src/slice/sort/shared/smallsort.rs | 38 +++-- library/core/src/slice/sort/stable/merge.rs | 148 +++++++++--------- 2 files changed, 97 insertions(+), 89 deletions(-) diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 6e4424310e80..7cd351c9f251 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -94,9 +94,9 @@ impl UnstableSmallSortTypeImpl for T { #[inline(always)] fn small_sort_threshold() -> usize { match const { choose_unstable_small_sort::() } { - UnstalbeSmallSort::Fallback => SMALL_SORT_FALLBACK_THRESHOLD, - UnstalbeSmallSort::General => SMALL_SORT_GENERAL_THRESHOLD, - UnstalbeSmallSort::Network => SMALL_SORT_NETWORK_THRESHOLD, + UnstableSmallSort::Fallback => SMALL_SORT_FALLBACK_THRESHOLD, + UnstableSmallSort::General => SMALL_SORT_GENERAL_THRESHOLD, + UnstableSmallSort::Network => SMALL_SORT_NETWORK_THRESHOLD, } } @@ -137,34 +137,34 @@ const SMALL_SORT_NETWORK_SCRATCH_LEN: usize = SMALL_SORT_NETWORK_THRESHOLD; /// within this limit. const MAX_STACK_ARRAY_SIZE: usize = 4096; -enum UnstalbeSmallSort { +enum UnstableSmallSort { Fallback, General, Network, } -const fn choose_unstable_small_sort() -> UnstalbeSmallSort { +const fn choose_unstable_small_sort() -> UnstableSmallSort { if T::is_copy() && has_efficient_in_place_swap::() && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { // Heuristic for int like types. - return UnstalbeSmallSort::Network; + return UnstableSmallSort::Network; } if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { - return UnstalbeSmallSort::General; + return UnstableSmallSort::General; } - UnstalbeSmallSort::Fallback + UnstableSmallSort::Fallback } const fn inst_unstable_small_sort bool>() -> fn(&mut [T], &mut F) { match const { choose_unstable_small_sort::() } { - UnstalbeSmallSort::Fallback => small_sort_fallback::, - UnstalbeSmallSort::General => small_sort_general::, - UnstalbeSmallSort::Network => small_sort_network::, + UnstableSmallSort::Fallback => small_sort_fallback::, + UnstableSmallSort::General => small_sort_general::, + UnstableSmallSort::Network => small_sort_network::, } } @@ -384,8 +384,12 @@ where } } -// Never inline this function to avoid code bloat. It still optimizes nicely and has practically no -// performance impact. +/// Sorts the first 9 elements of `v` with a fast fixed function. +/// +/// Should `is_less` generate substantial amounts of code the compiler can choose to not inline +/// `swap_if_less`. If the code of a sort impl changes so as to call this function in multiple +/// places, `#[inline(never)]` is recommended to keep binary-size in check. The current design of +/// `small_sort_network` makes sure to only call this once. fn sort9_optimal(v: &mut [T], is_less: &mut F) where F: FnMut(&T, &T) -> bool, @@ -429,8 +433,12 @@ where } } -// Never inline this function to avoid code bloat. It still optimizes nicely and has practically no -// performance impact. +/// Sorts the first 13 elements of `v` with a fast fixed function. +/// +/// Should `is_less` generate substantial amounts of code the compiler can choose to not inline +/// `swap_if_less`. If the code of a sort impl changes so as to call this function in multiple +/// places, `#[inline(never)]` is recommended to keep binary-size in check. The current design of +/// `small_sort_network` makes sure to only call this once. fn sort13_optimal(v: &mut [T], is_less: &mut F) where F: FnMut(&T, &T) -> bool, diff --git a/library/core/src/slice/sort/stable/merge.rs b/library/core/src/slice/sort/stable/merge.rs index 4f6a98bc7b89..6739e114b130 100644 --- a/library/core/src/slice/sort/stable/merge.rs +++ b/library/core/src/slice/sort/stable/merge.rs @@ -61,91 +61,91 @@ pub fn merge bool>( // Finally, `merge_state` gets dropped. If the shorter run was not fully // consumed, whatever remains of it will now be copied into the hole in `v`. } +} - // When dropped, copies the range `start..end` into `dst..`. - struct MergeState { - start: *mut T, - end: *mut T, - dst: *mut T, - } +// When dropped, copies the range `start..end` into `dst..`. +struct MergeState { + start: *mut T, + end: *mut T, + dst: *mut T, +} - impl MergeState { - /// # Safety - /// The caller MUST guarantee that `self` is initialized in a way where `start -> end` is - /// the longer sub-slice and so that `dst` can be written to at least the shorter sub-slice - /// length times. In addition `start -> end` and `right -> right_end` MUST be valid to be - /// read. This function MUST only be called once. - unsafe fn merge_up bool>( - &mut self, - mut right: *const T, - right_end: *const T, - is_less: &mut F, - ) { - // SAFETY: See function safety comment. - unsafe { - let left = &mut self.start; - let out = &mut self.dst; +impl MergeState { + /// # Safety + /// The caller MUST guarantee that `self` is initialized in a way where `start -> end` is + /// the longer sub-slice and so that `dst` can be written to at least the shorter sub-slice + /// length times. In addition `start -> end` and `right -> right_end` MUST be valid to be + /// read. This function MUST only be called once. + unsafe fn merge_up bool>( + &mut self, + mut right: *const T, + right_end: *const T, + is_less: &mut F, + ) { + // SAFETY: See function safety comment. + unsafe { + let left = &mut self.start; + let out = &mut self.dst; - while *left != self.end && right as *const T != right_end { - let consume_left = !is_less(&*right, &**left); + while *left != self.end && right as *const T != right_end { + let consume_left = !is_less(&*right, &**left); - let src = if consume_left { *left } else { right }; - ptr::copy_nonoverlapping(src, *out, 1); + let src = if consume_left { *left } else { right }; + ptr::copy_nonoverlapping(src, *out, 1); - *left = left.add(consume_left as usize); - right = right.add(!consume_left as usize); + *left = left.add(consume_left as usize); + right = right.add(!consume_left as usize); - *out = out.add(1); - } - } - } - - /// # Safety - /// The caller MUST guarantee that `self` is initialized in a way where `left_end <- dst` is - /// the shorter sub-slice and so that `out` can be written to at least the shorter sub-slice - /// length times. In addition `left_end <- dst` and `right_end <- end` MUST be valid to be - /// read. This function MUST only be called once. - unsafe fn merge_down bool>( - &mut self, - left_end: *const T, - right_end: *const T, - mut out: *mut T, - is_less: &mut F, - ) { - // SAFETY: See function safety comment. - unsafe { - loop { - let left = self.dst.sub(1); - let right = self.end.sub(1); - out = out.sub(1); - - let consume_left = is_less(&*right, &*left); - - let src = if consume_left { left } else { right }; - ptr::copy_nonoverlapping(src, out, 1); - - self.dst = left.add(!consume_left as usize); - self.end = right.add(consume_left as usize); - - if self.dst as *const T == left_end || self.end as *const T == right_end { - break; - } - } + *out = out.add(1); } } } - impl Drop for MergeState { - fn drop(&mut self) { - // SAFETY: The user of MergeState MUST ensure, that at any point this drop - // impl MAY run, for example when the user provided `is_less` panics, that - // copying the contiguous region between `start` and `end` to `dst` will - // leave the input slice `v` with each original element and all possible - // modifications observed. - unsafe { - let len = self.end.sub_ptr(self.start); - ptr::copy_nonoverlapping(self.start, self.dst, len); + /// # Safety + /// The caller MUST guarantee that `self` is initialized in a way where `left_end <- dst` is + /// the shorter sub-slice and so that `out` can be written to at least the shorter sub-slice + /// length times. In addition `left_end <- dst` and `right_end <- end` MUST be valid to be + /// read. This function MUST only be called once. + unsafe fn merge_down bool>( + &mut self, + left_end: *const T, + right_end: *const T, + mut out: *mut T, + is_less: &mut F, + ) { + // SAFETY: See function safety comment. + unsafe { + loop { + let left = self.dst.sub(1); + let right = self.end.sub(1); + out = out.sub(1); + + let consume_left = is_less(&*right, &*left); + + let src = if consume_left { left } else { right }; + ptr::copy_nonoverlapping(src, out, 1); + + self.dst = left.add(!consume_left as usize); + self.end = right.add(consume_left as usize); + + if self.dst as *const T == left_end || self.end as *const T == right_end { + break; + } } } } } + +impl Drop for MergeState { + fn drop(&mut self) { + // SAFETY: The user of MergeState MUST ensure, that at any point this drop + // impl MAY run, for example when the user provided `is_less` panics, that + // copying the contiguous region between `start` and `end` to `dst` will + // leave the input slice `v` with each original element and all possible + // modifications observed. + unsafe { + let len = self.end.sub_ptr(self.start); + ptr::copy_nonoverlapping(self.start, self.dst, len); + } + } +} From 9889ab1f6032d4ab38aa2aeffac002093e4f6559 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Sun, 16 Jun 2024 17:17:36 +0200 Subject: [PATCH 020/147] Remove reliance on const_trait in sort implementations const_trait in conjunction with specialization was deemed not ready for usage in this scenario. So instead a two-stage trait specialization approach is used. This approach is likely worse for compile-times. Future work that enables const_trait can revert back to the previous version as outlined in the comment marked FIXME(effects). --- .../core/src/slice/sort/shared/smallsort.rs | 123 +++++++++--------- .../core/src/slice/sort/stable/quicksort.rs | 7 +- 2 files changed, 66 insertions(+), 64 deletions(-) diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 7cd351c9f251..9a0f7bf2f51d 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -93,10 +93,62 @@ impl UnstableSmallSortTypeImpl for T { impl UnstableSmallSortTypeImpl for T { #[inline(always)] fn small_sort_threshold() -> usize { - match const { choose_unstable_small_sort::() } { - UnstableSmallSort::Fallback => SMALL_SORT_FALLBACK_THRESHOLD, - UnstableSmallSort::General => SMALL_SORT_GENERAL_THRESHOLD, - UnstableSmallSort::Network => SMALL_SORT_NETWORK_THRESHOLD, + ::small_sort_threshold() + } + + #[inline(always)] + fn small_sort(v: &mut [T], is_less: &mut F) + where + F: FnMut(&T, &T) -> bool, + { + ::small_sort(v, is_less); + } +} + +/// FIXME(effects) use original ipnsort approach with choose_unstable_small_sort, +/// as found here https://github.com/Voultapher/sort-research-rs/blob/438fad5d0495f65d4b72aa87f0b62fc96611dff3/ipnsort/src/smallsort.rs#L83C10-L83C36. +pub(crate) trait UnstableSmallSortFreezeTypeImpl: Sized + FreezeMarker { + fn small_sort_threshold() -> usize; + + fn small_sort bool>(v: &mut [Self], is_less: &mut F); +} + +impl UnstableSmallSortFreezeTypeImpl for T { + #[inline(always)] + default fn small_sort_threshold() -> usize { + if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + SMALL_SORT_GENERAL_THRESHOLD + } else { + SMALL_SORT_FALLBACK_THRESHOLD + } + } + + #[inline(always)] + default fn small_sort(v: &mut [T], is_less: &mut F) + where + F: FnMut(&T, &T) -> bool, + { + if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + small_sort_general(v, is_less); + } else { + small_sort_fallback(v, is_less); + } + } +} + +/// SAFETY: Only used for run-time optimization heuristic. +#[rustc_unsafe_specialization_marker] +trait CopyMarker {} + +impl UnstableSmallSortFreezeTypeImpl for T { + #[inline(always)] + fn small_sort_threshold() -> usize { + if has_efficient_in_place_swap::() + && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE + { + SMALL_SORT_NETWORK_SCRATCH_LEN + } else { + SMALL_SORT_FALLBACK_THRESHOLD } } @@ -105,9 +157,13 @@ impl UnstableSmallSortTypeImpl for T { where F: FnMut(&T, &T) -> bool, { - // This construct is used to limit the LLVM IR generated, which saves large amounts of - // compile-time by only instantiating the code that is needed. Idea by Frank Steffahn. - (const { inst_unstable_small_sort::() })(v, is_less); + if has_efficient_in_place_swap::() + && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE + { + small_sort_network(v, is_less); + } else { + small_sort_fallback(v, is_less); + } } } @@ -137,37 +193,6 @@ const SMALL_SORT_NETWORK_SCRATCH_LEN: usize = SMALL_SORT_NETWORK_THRESHOLD; /// within this limit. const MAX_STACK_ARRAY_SIZE: usize = 4096; -enum UnstableSmallSort { - Fallback, - General, - Network, -} - -const fn choose_unstable_small_sort() -> UnstableSmallSort { - if T::is_copy() - && has_efficient_in_place_swap::() - && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE - { - // Heuristic for int like types. - return UnstableSmallSort::Network; - } - - if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { - return UnstableSmallSort::General; - } - - UnstableSmallSort::Fallback -} - -const fn inst_unstable_small_sort bool>() --> fn(&mut [T], &mut F) { - match const { choose_unstable_small_sort::() } { - UnstableSmallSort::Fallback => small_sort_fallback::, - UnstableSmallSort::General => small_sort_general::, - UnstableSmallSort::Network => small_sort_network::, - } -} - fn small_sort_fallback bool>(v: &mut [T], is_less: &mut F) { if v.len() >= 2 { insertion_sort_shift_left(v, 1, is_less); @@ -822,25 +847,3 @@ pub(crate) const fn has_efficient_in_place_swap() -> bool { // Heuristic that holds true on all tested 64-bit capable architectures. mem::size_of::() <= 8 // mem::size_of::() } - -/// SAFETY: Only used for run-time optimization heuristic. -#[rustc_unsafe_specialization_marker] -trait CopyMarker {} - -impl CopyMarker for T {} - -#[const_trait] -trait IsCopy { - fn is_copy() -> bool; -} - -impl const IsCopy for T { - default fn is_copy() -> bool { - false - } -} -impl const IsCopy for T { - fn is_copy() -> bool { - true - } -} diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs index e1734ce8d8b4..181fe603d232 100644 --- a/library/core/src/slice/sort/stable/quicksort.rs +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -233,24 +233,23 @@ impl PartitionState { } } -#[const_trait] trait IsFreeze { fn is_freeze() -> bool; } -impl const IsFreeze for T { +impl IsFreeze for T { default fn is_freeze() -> bool { false } } -impl const IsFreeze for T { +impl IsFreeze for T { fn is_freeze() -> bool { true } } #[must_use] -const fn has_direct_interior_mutability() -> bool { +fn has_direct_interior_mutability() -> bool { // If a type has interior mutability it may alter itself during comparison // in a way that must be preserved after the sort operation concludes. // Otherwise a type like Mutex>> could lead to double free. From 6b84d7566eaeefe521fdba12c65a9e0b137e34a6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 3 Mar 2024 02:22:37 +0100 Subject: [PATCH 021/147] Add tests --- tests/mir-opt/or_pattern.rs | 24 +++++ ...ut_second_or.SimplifyCfg-initial.after.mir | 100 ++++++++++++++++++ ...le_switchint.SimplifyCfg-initial.after.mir | 79 ++++++++++++++ tests/ui/or-patterns/bindings-runpass-2.rs | 1 + tests/ui/or-patterns/inner-or-pat.or3.stderr | 2 +- tests/ui/or-patterns/inner-or-pat.or4.stderr | 2 +- tests/ui/or-patterns/inner-or-pat.rs | 4 +- ...ssue-70413-no-unreachable-pat-and-guard.rs | 21 ++-- tests/ui/or-patterns/search-via-bindings.rs | 22 ++++ .../or-patterns/simplification_subtleties.rs | 11 ++ 10 files changed, 250 insertions(+), 16 deletions(-) create mode 100644 tests/mir-opt/or_pattern.rs create mode 100644 tests/mir-opt/or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir create mode 100644 tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir create mode 100644 tests/ui/or-patterns/simplification_subtleties.rs diff --git a/tests/mir-opt/or_pattern.rs b/tests/mir-opt/or_pattern.rs new file mode 100644 index 000000000000..0ad0ce8ead1e --- /dev/null +++ b/tests/mir-opt/or_pattern.rs @@ -0,0 +1,24 @@ +// skip-filecheck + +// EMIT_MIR or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir +fn shortcut_second_or() { + // Check that after matching `0`, failing to match `2 | 3` skips trying to match `(1, 2 | 3)`. + match ((0, 0), 0) { + (x @ (0, _) | x @ (_, 1), y @ 2 | y @ 3) => {} + _ => {} + } +} + +// EMIT_MIR or_pattern.single_switchint.SimplifyCfg-initial.after.mir +fn single_switchint() { + // Check how many `SwitchInt`s we do. In theory a single one is necessary. + match (1, true) { + (1, true) => 1, + (2, false) => 2, + (1 | 2, true | false) => 3, + (3 | 4, true | false) => 4, + _ => 5, + }; +} + +fn main() {} diff --git a/tests/mir-opt/or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir b/tests/mir-opt/or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir new file mode 100644 index 000000000000..56edd38a6da4 --- /dev/null +++ b/tests/mir-opt/or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir @@ -0,0 +1,100 @@ +// MIR for `shortcut_second_or` after SimplifyCfg-initial + +fn shortcut_second_or() -> () { + let mut _0: (); + let mut _1: ((i32, i32), i32); + let mut _2: (i32, i32); + let _3: (i32, i32); + let _4: i32; + scope 1 { + debug x => _3; + debug y => _4; + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + _2 = (const 0_i32, const 0_i32); + _1 = (move _2, const 0_i32); + StorageDead(_2); + PlaceMention(_1); + switchInt(((_1.0: (i32, i32)).0: i32)) -> [0: bb4, otherwise: bb2]; + } + + bb1: { + _0 = const (); + goto -> bb14; + } + + bb2: { + switchInt(((_1.0: (i32, i32)).1: i32)) -> [1: bb3, otherwise: bb1]; + } + + bb3: { + switchInt((_1.1: i32)) -> [2: bb7, 3: bb8, otherwise: bb1]; + } + + bb4: { + switchInt((_1.1: i32)) -> [2: bb5, 3: bb6, otherwise: bb1]; + } + + bb5: { + falseEdge -> [real: bb10, imaginary: bb6]; + } + + bb6: { + falseEdge -> [real: bb11, imaginary: bb2]; + } + + bb7: { + falseEdge -> [real: bb12, imaginary: bb8]; + } + + bb8: { + falseEdge -> [real: bb13, imaginary: bb1]; + } + + bb9: { + _0 = const (); + StorageDead(_4); + StorageDead(_3); + goto -> bb14; + } + + bb10: { + StorageLive(_3); + _3 = (_1.0: (i32, i32)); + StorageLive(_4); + _4 = (_1.1: i32); + goto -> bb9; + } + + bb11: { + StorageLive(_3); + _3 = (_1.0: (i32, i32)); + StorageLive(_4); + _4 = (_1.1: i32); + goto -> bb9; + } + + bb12: { + StorageLive(_3); + _3 = (_1.0: (i32, i32)); + StorageLive(_4); + _4 = (_1.1: i32); + goto -> bb9; + } + + bb13: { + StorageLive(_3); + _3 = (_1.0: (i32, i32)); + StorageLive(_4); + _4 = (_1.1: i32); + goto -> bb9; + } + + bb14: { + StorageDead(_1); + return; + } +} diff --git a/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir b/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir new file mode 100644 index 000000000000..72b1a835cf08 --- /dev/null +++ b/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir @@ -0,0 +1,79 @@ +// MIR for `single_switchint` after SimplifyCfg-initial + +fn single_switchint() -> () { + let mut _0: (); + let _1: i32; + let mut _2: (i32, bool); + + bb0: { + StorageLive(_1); + StorageLive(_2); + _2 = (const 1_i32, const true); + PlaceMention(_2); + switchInt((_2.0: i32)) -> [1: bb6, 2: bb8, otherwise: bb1]; + } + + bb1: { + switchInt((_2.0: i32)) -> [1: bb3, 2: bb3, otherwise: bb2]; + } + + bb2: { + switchInt((_2.0: i32)) -> [3: bb5, 4: bb5, otherwise: bb4]; + } + + bb3: { + falseEdge -> [real: bb12, imaginary: bb2]; + } + + bb4: { + _1 = const 5_i32; + goto -> bb14; + } + + bb5: { + falseEdge -> [real: bb13, imaginary: bb4]; + } + + bb6: { + switchInt((_2.1: bool)) -> [0: bb1, otherwise: bb7]; + } + + bb7: { + falseEdge -> [real: bb10, imaginary: bb8]; + } + + bb8: { + switchInt((_2.1: bool)) -> [0: bb9, otherwise: bb1]; + } + + bb9: { + falseEdge -> [real: bb11, imaginary: bb1]; + } + + bb10: { + _1 = const 1_i32; + goto -> bb14; + } + + bb11: { + _1 = const 2_i32; + goto -> bb14; + } + + bb12: { + _1 = const 3_i32; + goto -> bb14; + } + + bb13: { + _1 = const 4_i32; + goto -> bb14; + } + + bb14: { + StorageDead(_2); + StorageDead(_1); + _0 = const (); + return; + } +} diff --git a/tests/ui/or-patterns/bindings-runpass-2.rs b/tests/ui/or-patterns/bindings-runpass-2.rs index 657d7f1ed189..a9ae99810840 100644 --- a/tests/ui/or-patterns/bindings-runpass-2.rs +++ b/tests/ui/or-patterns/bindings-runpass-2.rs @@ -26,5 +26,6 @@ fn main() { assert_eq!(or_at(Err(7)), 207); assert_eq!(or_at(Err(8)), 8); assert_eq!(or_at(Err(20)), 220); + assert_eq!(or_at(Err(34)), 134); assert_eq!(or_at(Err(50)), 500); } diff --git a/tests/ui/or-patterns/inner-or-pat.or3.stderr b/tests/ui/or-patterns/inner-or-pat.or3.stderr index 10ec7c202e4d..5c522a97ccef 100644 --- a/tests/ui/or-patterns/inner-or-pat.or3.stderr +++ b/tests/ui/or-patterns/inner-or-pat.or3.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/inner-or-pat.rs:38:54 + --> $DIR/inner-or-pat.rs:36:54 | LL | match x { | - this expression has type `&str` diff --git a/tests/ui/or-patterns/inner-or-pat.or4.stderr b/tests/ui/or-patterns/inner-or-pat.or4.stderr index 97800161d82f..508520c82379 100644 --- a/tests/ui/or-patterns/inner-or-pat.or4.stderr +++ b/tests/ui/or-patterns/inner-or-pat.or4.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `x` is not bound in all patterns - --> $DIR/inner-or-pat.rs:53:37 + --> $DIR/inner-or-pat.rs:51:37 | LL | (x @ "red" | (x @ "blue" | "red")) => { | - ^^^^^ pattern doesn't bind `x` diff --git a/tests/ui/or-patterns/inner-or-pat.rs b/tests/ui/or-patterns/inner-or-pat.rs index ceb0a8b3f796..4d136de00535 100644 --- a/tests/ui/or-patterns/inner-or-pat.rs +++ b/tests/ui/or-patterns/inner-or-pat.rs @@ -1,7 +1,5 @@ -//@ revisions: or1 or2 or3 or4 or5 +//@ revisions: or1 or3 or4 //@ [or1] run-pass -//@ [or2] run-pass -//@ [or5] run-pass #![allow(unreachable_patterns)] #![allow(unused_variables)] diff --git a/tests/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs b/tests/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs index 7d62364a6aee..76dc298a5c85 100644 --- a/tests/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs +++ b/tests/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs @@ -1,21 +1,20 @@ -//@ check-pass +//@ run-pass #![deny(unreachable_patterns)] fn main() { - match (3,42) { - (a,_) | (_,a) if a > 10 => {println!("{}", a)} - _ => () + match (3, 42) { + (a, _) | (_, a) if a > 10 => {} + _ => unreachable!(), } - match Some((3,42)) { - Some((a, _)) | Some((_, a)) if a > 10 => {println!("{}", a)} - _ => () - + match Some((3, 42)) { + Some((a, _)) | Some((_, a)) if a > 10 => {} + _ => unreachable!(), } - match Some((3,42)) { - Some((a, _) | (_, a)) if a > 10 => {println!("{}", a)} - _ => () + match Some((3, 42)) { + Some((a, _) | (_, a)) if a > 10 => {} + _ => unreachable!(), } } diff --git a/tests/ui/or-patterns/search-via-bindings.rs b/tests/ui/or-patterns/search-via-bindings.rs index a760112f1d42..42174bd7cef7 100644 --- a/tests/ui/or-patterns/search-via-bindings.rs +++ b/tests/ui/or-patterns/search-via-bindings.rs @@ -42,6 +42,23 @@ fn search_old_style(target: (bool, bool, bool)) -> u32 { } } +// Check that a dummy or-pattern also leads to running the guard multiple times. +fn search_with_dummy(target: (bool, bool)) -> u32 { + let x = ((false, true), (false, true), ()); + let mut guard_count = 0; + match x { + ((a, _) | (_, a), (b, _) | (_, b), _ | _) + if { + guard_count += 1; + (a, b) == target + } => + { + guard_count + } + _ => unreachable!(), + } +} + fn main() { assert_eq!(search((false, false, false)), 1); assert_eq!(search((false, false, true)), 2); @@ -60,4 +77,9 @@ fn main() { assert_eq!(search_old_style((true, false, true)), 6); assert_eq!(search_old_style((true, true, false)), 7); assert_eq!(search_old_style((true, true, true)), 8); + + assert_eq!(search_with_dummy((false, false)), 1); + assert_eq!(search_with_dummy((false, true)), 3); + assert_eq!(search_with_dummy((true, false)), 5); + assert_eq!(search_with_dummy((true, true)), 7); } diff --git a/tests/ui/or-patterns/simplification_subtleties.rs b/tests/ui/or-patterns/simplification_subtleties.rs new file mode 100644 index 000000000000..a932bd531e6d --- /dev/null +++ b/tests/ui/or-patterns/simplification_subtleties.rs @@ -0,0 +1,11 @@ +//@ run-pass + +#[allow(unreachable_patterns)] +fn main() { + // Test that we don't naively sort the two `2`s together and confuse the failure paths. + match (1, true) { + (1 | 2, false | false) => unreachable!(), + (2, _) => unreachable!(), + _ => {} + } +} From e74b30e3a9f75f8854fb04a6f9b528077c5e9ec5 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 20 Mar 2024 15:53:25 +0100 Subject: [PATCH 022/147] Tweak simple or-pattern expansion --- .../rustc_mir_build/src/build/matches/mod.rs | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 544f27b84e90..13e208524f15 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1364,9 +1364,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise_block: BasicBlock, candidates: &mut [&mut Candidate<'pat, 'tcx>], ) { - let mut split_or_candidate = false; - for candidate in &mut *candidates { - if let [MatchPair { test_case: TestCase::Or { .. }, .. }] = &*candidate.match_pairs { + let expand_or_pats = candidates.iter().any(|candidate| { + matches!(&*candidate.match_pairs, [MatchPair { test_case: TestCase::Or { .. }, .. }]) + }); + + ensure_sufficient_stack(|| { + if expand_or_pats { // Split a candidate in which the only match-pair is an or-pattern into multiple // candidates. This is so that // @@ -1376,30 +1379,32 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // } // // only generates a single switch. - let match_pair = candidate.match_pairs.pop().unwrap(); - self.create_or_subcandidates(candidate, match_pair); - split_or_candidate = true; - } - } - - ensure_sufficient_stack(|| { - if split_or_candidate { - // At least one of the candidates has been split into subcandidates. - // We need to change the candidate list to include those. let mut new_candidates = Vec::new(); for candidate in candidates.iter_mut() { - candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate)); + if let [MatchPair { test_case: TestCase::Or { .. }, .. }] = + &*candidate.match_pairs + { + let match_pair = candidate.match_pairs.pop().unwrap(); + self.create_or_subcandidates(candidate, match_pair); + for subcandidate in candidate.subcandidates.iter_mut() { + new_candidates.push(subcandidate); + } + } else { + new_candidates.push(candidate); + } } self.match_candidates( span, scrutinee_span, start_block, otherwise_block, - &mut *new_candidates, + new_candidates.as_mut_slice(), ); for candidate in candidates { - self.merge_trivial_subcandidates(candidate); + if !candidate.subcandidates.is_empty() { + self.merge_trivial_subcandidates(candidate); + } } } else { self.match_simplified_candidates( From 5fe2ca65cf5b2696d4d48c261cb7f6cecb2f7099 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 3 Mar 2024 01:42:18 +0100 Subject: [PATCH 023/147] Always set `otherwise_block`s --- .../rustc_mir_build/src/build/matches/mod.rs | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 13e208524f15..a67c59138cd2 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1074,12 +1074,9 @@ struct Candidate<'pat, 'tcx> { // because that would break binding consistency. subcandidates: Vec>, - /// ...and the guard must be evaluated if there is one. + /// ...and if there is a guard it must be evaluated; if it's `false` then branch to `otherwise_block`. has_guard: bool, - /// If the guard is `false` then branch to `otherwise_block`. - otherwise_block: Option, - /// If the candidate matches, bindings and ascriptions must be established. extra_data: PatternExtraData<'tcx>, @@ -1090,6 +1087,9 @@ struct Candidate<'pat, 'tcx> { /// The block before the `bindings` have been established. pre_binding_block: Option, + /// The block to branch to if the guard or a nested candidate fails to match. + otherwise_block: Option, + /// The earliest block that has only candidates >= this one as descendents. Used for false /// edges, see the doc for [`Builder::match_expr`]. false_edge_start_block: Option, @@ -1500,11 +1500,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate.pre_binding_block = Some(start_block); let otherwise_block = self.cfg.start_new_block(); - if candidate.has_guard { - // Create the otherwise block for this candidate, which is the - // pre-binding block for the next candidate. - candidate.otherwise_block = Some(otherwise_block); - } + // Create the otherwise block for this candidate, which is the + // pre-binding block for the next candidate. + candidate.otherwise_block = Some(otherwise_block); otherwise_block } @@ -1591,10 +1589,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { assert!(leaf_candidate.match_pairs.is_empty()); leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned()); let or_start = leaf_candidate.pre_binding_block.unwrap(); - // In a case like `(a | b, c | d)`, if `a` succeeds and `c | d` fails, we know `(b, - // c | d)` will fail too. If there is no guard, we skip testing of `b` by branching - // directly to `remainder_start`. If there is a guard, we have to try `(b, c | d)`. - let or_otherwise = leaf_candidate.otherwise_block.unwrap_or(remainder_start); + // In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q, + // R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching + // directly to `remainder_start`. If there is a guard, `or_otherwise` can be reached + // by guard failure as well, so we can't skip `Q`. + let or_otherwise = if leaf_candidate.has_guard { + leaf_candidate.otherwise_block.unwrap() + } else { + remainder_start + }; self.test_candidates_with_or( span, scrutinee_span, @@ -1669,6 +1672,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty() }); if can_merge { + let mut last_otherwise = None; let any_matches = self.cfg.start_new_block(); let or_span = candidate.or_span.take().unwrap(); let source_info = self.source_info(or_span); @@ -1679,8 +1683,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for subcandidate in mem::take(&mut candidate.subcandidates) { let or_block = subcandidate.pre_binding_block.unwrap(); self.cfg.goto(or_block, source_info, any_matches); + last_otherwise = subcandidate.otherwise_block; } candidate.pre_binding_block = Some(any_matches); + assert!(last_otherwise.is_some()); + candidate.otherwise_block = last_otherwise; } else { // Never subcandidates may have a set of bindings inconsistent with their siblings, // which would break later code. So we filter them out. Note that we can't filter out From 764f086f2cd7cc4e2ca9c5fd9f53ba4c0b3a1740 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 4 Mar 2024 01:41:37 +0100 Subject: [PATCH 024/147] Use `otherwise_block` for or-pattern shortcutting --- compiler/rustc_mir_build/src/build/matches/mod.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index a67c59138cd2..a5f24ba3e5b2 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1585,18 +1585,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // We could add them to the or-candidates before the call to `test_or_pattern` but this // would make it impossible to detect simplifiable or-patterns. That would guarantee // exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`. + let mut last_otherwise = None; + first_candidate.visit_leaves(|leaf_candidate| { + last_otherwise = leaf_candidate.otherwise_block; + }); first_candidate.visit_leaves(|leaf_candidate| { assert!(leaf_candidate.match_pairs.is_empty()); leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned()); let or_start = leaf_candidate.pre_binding_block.unwrap(); // In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q, // R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching - // directly to `remainder_start`. If there is a guard, `or_otherwise` can be reached - // by guard failure as well, so we can't skip `Q`. + // directly to `last_otherwise`. If there is a guard, + // `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we + // can't skip `Q`. let or_otherwise = if leaf_candidate.has_guard { leaf_candidate.otherwise_block.unwrap() } else { - remainder_start + last_otherwise.unwrap() }; self.test_candidates_with_or( span, From ce374fcbc16f6bd9fcbb03ce1894ae13e5e3d67b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 20 Mar 2024 01:56:24 +0100 Subject: [PATCH 025/147] Factor out `finalize_or_candidate` --- .../rustc_mir_build/src/build/matches/mod.rs | 60 +++++++++++++------ 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index a5f24ba3e5b2..70ba83c55d7c 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1575,21 +1575,52 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } let first_match_pair = first_candidate.match_pairs.remove(0); - let remaining_match_pairs = mem::take(&mut first_candidate.match_pairs); let remainder_start = self.cfg.start_new_block(); // Test the alternatives of this or-pattern. - self.test_or_pattern(first_candidate, start_block, remainder_start, first_match_pair); + self.test_or_pattern( + span, + scrutinee_span, + first_candidate, + start_block, + remainder_start, + first_match_pair, + ); - if !remaining_match_pairs.is_empty() { + // Test the remaining candidates. + self.match_candidates( + span, + scrutinee_span, + remainder_start, + otherwise_block, + remaining_candidates, + ); + } + + /// Simplify subcandidates and process any leftover match pairs. The candidate should have been + /// expanded with `create_or_subcandidates`. + fn finalize_or_candidate( + &mut self, + span: Span, + scrutinee_span: Span, + candidate: &mut Candidate<'_, 'tcx>, + ) { + if candidate.subcandidates.is_empty() { + return; + } + + self.merge_trivial_subcandidates(candidate); + + if !candidate.match_pairs.is_empty() { // If more match pairs remain, test them after each subcandidate. // We could add them to the or-candidates before the call to `test_or_pattern` but this // would make it impossible to detect simplifiable or-patterns. That would guarantee // exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`. let mut last_otherwise = None; - first_candidate.visit_leaves(|leaf_candidate| { + candidate.visit_leaves(|leaf_candidate| { last_otherwise = leaf_candidate.otherwise_block; }); - first_candidate.visit_leaves(|leaf_candidate| { + let remaining_match_pairs = mem::take(&mut candidate.match_pairs); + candidate.visit_leaves(|leaf_candidate| { assert!(leaf_candidate.match_pairs.is_empty()); leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned()); let or_start = leaf_candidate.pre_binding_block.unwrap(); @@ -1612,20 +1643,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); }); } - - // Test the remaining candidates. - self.match_candidates( - span, - scrutinee_span, - remainder_start, - otherwise_block, - remaining_candidates, - ); } #[instrument(skip(self, start_block, otherwise_block, candidate, match_pair), level = "debug")] fn test_or_pattern<'pat>( &mut self, + span: Span, + scrutinee_span: Span, candidate: &mut Candidate<'pat, 'tcx>, start_block: BasicBlock, otherwise_block: BasicBlock, @@ -1641,12 +1665,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise_block, &mut or_candidate_refs, ); - self.merge_trivial_subcandidates(candidate); + self.finalize_or_candidate(span, scrutinee_span, candidate); } /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new /// subcandidate. Any candidate that has been expanded that way should be passed to - /// `merge_trivial_subcandidates` after its subcandidates have been processed. + /// `finalize_or_candidate` after its subcandidates have been processed. fn create_or_subcandidates<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, @@ -1664,8 +1688,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Try to merge all of the subcandidates of the given candidate into one. This avoids - /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The or-pattern should have - /// been expanded with `create_or_subcandidates`. + /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The candidate should have been + /// expanded with `create_or_subcandidates`. fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { if candidate.subcandidates.is_empty() || candidate.has_guard { // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. From 7b764be9f1dd4cde35c393ceaad891b7c22b2ebb Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 20 Mar 2024 15:57:51 +0100 Subject: [PATCH 026/147] Expand or-candidates mixed with candidates above We can't mix them with candidates below them, but we can mix them with candidates above. --- .../rustc_mir_build/src/build/matches/mod.rs | 176 ++++++++---------- ...le_switchint.SimplifyCfg-initial.after.mir | 56 +++--- 2 files changed, 105 insertions(+), 127 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 70ba83c55d7c..68244136d1ad 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1364,61 +1364,105 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise_block: BasicBlock, candidates: &mut [&mut Candidate<'pat, 'tcx>], ) { - let expand_or_pats = candidates.iter().any(|candidate| { - matches!(&*candidate.match_pairs, [MatchPair { test_case: TestCase::Or { .. }, .. }]) - }); + // We process or-patterns here. If any candidate starts with an or-pattern, we have to + // expand the or-pattern before we can proceed further. + // + // We can't expand them freely however. The rule is: if the candidate has an or-pattern as + // its only remaining match pair, we can expand it freely. If it has other match pairs, we + // can expand it but we can't process more candidates after it. + // + // If we didn't stop, the `otherwise` cases could get mixed up. E.g. in the following, + // or-pattern simplification (in `merge_trivial_subcandidates`) makes it so the `1` and `2` + // cases branch to a same block (which then tests `false`). If we took `(2, _)` in the same + // set of candidates, when we reach the block that tests `false` we don't know whether we + // came from `1` or `2`, hence we can't know where to branch on failure. + // ```ignore(illustrative) + // match (1, true) { + // (1 | 2, false) => {}, + // (2, _) => {}, + // _ => {} + // } + // ``` + // + // We therefore split the `candidates` slice in two, expand or-patterns in the first half, + // and process both halves separately. + let mut expand_until = 0; + for (i, candidate) in candidates.iter().enumerate() { + if matches!( + &*candidate.match_pairs, + [MatchPair { test_case: TestCase::Or { .. }, .. }, ..] + ) { + expand_until = i + 1; + if candidate.match_pairs.len() > 1 { + break; + } + } + } + let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until); ensure_sufficient_stack(|| { - if expand_or_pats { - // Split a candidate in which the only match-pair is an or-pattern into multiple - // candidates. This is so that - // - // match x { - // 0 | 1 => { ... }, - // 2 | 3 => { ... }, - // } - // - // only generates a single switch. - let mut new_candidates = Vec::new(); - for candidate in candidates.iter_mut() { - if let [MatchPair { test_case: TestCase::Or { .. }, .. }] = + if candidates_to_expand.is_empty() { + // No candidates start with an or-pattern, we can continue. + self.match_expanded_candidates( + span, + scrutinee_span, + start_block, + otherwise_block, + remaining_candidates, + ); + } else { + // Expand one level of or-patterns for each candidate in `candidates_to_expand`. + let mut expanded_candidates = Vec::new(); + for candidate in candidates_to_expand.iter_mut() { + if let [MatchPair { test_case: TestCase::Or { .. }, .. }, ..] = &*candidate.match_pairs { - let match_pair = candidate.match_pairs.pop().unwrap(); - self.create_or_subcandidates(candidate, match_pair); + let or_match_pair = candidate.match_pairs.remove(0); + // Expand the or-pattern into subcandidates. + self.create_or_subcandidates(candidate, or_match_pair); + // Collect the newly created subcandidates. for subcandidate in candidate.subcandidates.iter_mut() { - new_candidates.push(subcandidate); + expanded_candidates.push(subcandidate); } } else { - new_candidates.push(candidate); + expanded_candidates.push(candidate); } } + + // Process the expanded candidates. + let remainder_start = self.cfg.start_new_block(); + // There might be new or-patterns obtained from expanding the old ones, so we call + // `match_candidates` again. self.match_candidates( span, scrutinee_span, start_block, - otherwise_block, - new_candidates.as_mut_slice(), + remainder_start, + expanded_candidates.as_mut_slice(), ); - for candidate in candidates { + // Simplify subcandidates and process any leftover match pairs. + for candidate in candidates_to_expand { if !candidate.subcandidates.is_empty() { - self.merge_trivial_subcandidates(candidate); + self.finalize_or_candidate(span, scrutinee_span, candidate); } } - } else { - self.match_simplified_candidates( + + // Process the remaining candidates. + self.match_candidates( span, scrutinee_span, - start_block, + remainder_start, otherwise_block, - candidates, + remaining_candidates, ); } }); } - fn match_simplified_candidates( + /// Construct the decision tree for `candidates`. Caller must ensure that no candidate in + /// `candidates` starts with an or-pattern. + fn match_expanded_candidates( &mut self, span: Span, scrutinee_span: Span, @@ -1443,7 +1487,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // The first candidate has satisfied all its match pairs; we link it up and continue // with the remaining candidates. start_block = self.select_matched_candidate(first, start_block); - self.match_simplified_candidates( + self.match_expanded_candidates( span, scrutinee_span, start_block, @@ -1453,7 +1497,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } candidates => { // The first candidate has some unsatisfied match pairs; we proceed to do more tests. - self.test_candidates_with_or( + self.test_candidates( span, scrutinee_span, candidates, @@ -1506,8 +1550,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise_block } - /// Tests a candidate where there are only or-patterns left to test, or - /// forwards to [Builder::test_candidates]. + /// Simplify subcandidates and process any leftover match pairs. The candidate should have been + /// expanded with `create_or_subcandidates`. /// /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like /// so: @@ -1559,45 +1603,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// | /// ... /// ``` - fn test_candidates_with_or( - &mut self, - span: Span, - scrutinee_span: Span, - candidates: &mut [&mut Candidate<'_, 'tcx>], - start_block: BasicBlock, - otherwise_block: BasicBlock, - ) { - let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap(); - assert!(first_candidate.subcandidates.is_empty()); - if !matches!(first_candidate.match_pairs[0].test_case, TestCase::Or { .. }) { - self.test_candidates(span, scrutinee_span, candidates, start_block, otherwise_block); - return; - } - - let first_match_pair = first_candidate.match_pairs.remove(0); - let remainder_start = self.cfg.start_new_block(); - // Test the alternatives of this or-pattern. - self.test_or_pattern( - span, - scrutinee_span, - first_candidate, - start_block, - remainder_start, - first_match_pair, - ); - - // Test the remaining candidates. - self.match_candidates( - span, - scrutinee_span, - remainder_start, - otherwise_block, - remaining_candidates, - ); - } - - /// Simplify subcandidates and process any leftover match pairs. The candidate should have been - /// expanded with `create_or_subcandidates`. fn finalize_or_candidate( &mut self, span: Span, @@ -1634,40 +1639,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { last_otherwise.unwrap() }; - self.test_candidates_with_or( + self.match_candidates( span, scrutinee_span, - &mut [leaf_candidate], or_start, or_otherwise, + &mut [leaf_candidate], ); }); } } - #[instrument(skip(self, start_block, otherwise_block, candidate, match_pair), level = "debug")] - fn test_or_pattern<'pat>( - &mut self, - span: Span, - scrutinee_span: Span, - candidate: &mut Candidate<'pat, 'tcx>, - start_block: BasicBlock, - otherwise_block: BasicBlock, - match_pair: MatchPair<'pat, 'tcx>, - ) { - let or_span = match_pair.pattern.span; - self.create_or_subcandidates(candidate, match_pair); - let mut or_candidate_refs: Vec<_> = candidate.subcandidates.iter_mut().collect(); - self.match_candidates( - or_span, - or_span, - start_block, - otherwise_block, - &mut or_candidate_refs, - ); - self.finalize_or_candidate(span, scrutinee_span, candidate); - } - /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new /// subcandidate. Any candidate that has been expanded that way should be passed to /// `finalize_or_candidate` after its subcandidates have been processed. diff --git a/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir b/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir index 72b1a835cf08..eafe95b4a11e 100644 --- a/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir @@ -10,67 +10,63 @@ fn single_switchint() -> () { StorageLive(_2); _2 = (const 1_i32, const true); PlaceMention(_2); - switchInt((_2.0: i32)) -> [1: bb6, 2: bb8, otherwise: bb1]; + switchInt((_2.0: i32)) -> [1: bb2, 2: bb4, otherwise: bb1]; } bb1: { - switchInt((_2.0: i32)) -> [1: bb3, 2: bb3, otherwise: bb2]; + switchInt((_2.0: i32)) -> [3: bb8, 4: bb8, otherwise: bb7]; } bb2: { - switchInt((_2.0: i32)) -> [3: bb5, 4: bb5, otherwise: bb4]; + switchInt((_2.1: bool)) -> [0: bb6, otherwise: bb3]; } bb3: { - falseEdge -> [real: bb12, imaginary: bb2]; + falseEdge -> [real: bb9, imaginary: bb4]; } bb4: { - _1 = const 5_i32; - goto -> bb14; + switchInt((_2.1: bool)) -> [0: bb5, otherwise: bb6]; } bb5: { - falseEdge -> [real: bb13, imaginary: bb4]; + falseEdge -> [real: bb10, imaginary: bb6]; } bb6: { - switchInt((_2.1: bool)) -> [0: bb1, otherwise: bb7]; - } - - bb7: { - falseEdge -> [real: bb10, imaginary: bb8]; - } - - bb8: { - switchInt((_2.1: bool)) -> [0: bb9, otherwise: bb1]; - } - - bb9: { falseEdge -> [real: bb11, imaginary: bb1]; } - bb10: { + bb7: { + _1 = const 5_i32; + goto -> bb13; + } + + bb8: { + falseEdge -> [real: bb12, imaginary: bb7]; + } + + bb9: { _1 = const 1_i32; - goto -> bb14; + goto -> bb13; + } + + bb10: { + _1 = const 2_i32; + goto -> bb13; } bb11: { - _1 = const 2_i32; - goto -> bb14; + _1 = const 3_i32; + goto -> bb13; } bb12: { - _1 = const 3_i32; - goto -> bb14; + _1 = const 4_i32; + goto -> bb13; } bb13: { - _1 = const 4_i32; - goto -> bb14; - } - - bb14: { StorageDead(_2); StorageDead(_1); _0 = const (); From 24697ac66b1cb7e314cad5166872ae54d039856c Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Sun, 16 Jun 2024 20:26:33 +0200 Subject: [PATCH 027/147] Fix doc-link issue --- library/core/src/slice/sort/shared/smallsort.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 9a0f7bf2f51d..e208f7143d44 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -106,7 +106,7 @@ impl UnstableSmallSortTypeImpl for T { } /// FIXME(effects) use original ipnsort approach with choose_unstable_small_sort, -/// as found here https://github.com/Voultapher/sort-research-rs/blob/438fad5d0495f65d4b72aa87f0b62fc96611dff3/ipnsort/src/smallsort.rs#L83C10-L83C36. +/// as found here . pub(crate) trait UnstableSmallSortFreezeTypeImpl: Sized + FreezeMarker { fn small_sort_threshold() -> usize; From 032ad4c4c6b6fe94947209a7f5cee595a3f225d1 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Mon, 17 Jun 2024 07:28:18 +0200 Subject: [PATCH 028/147] Fix unintended regression for Freeze + Copy types Freeze + Copy types should be allowed to choose between all three small-sort variants. With the recent changes to small-sort selection, a regression was added that only let such types choose between network and fallback. It can now also choose general where appropriate. --- library/core/src/slice/sort/shared/smallsort.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index e208f7143d44..567a7090ecd7 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -146,7 +146,9 @@ impl UnstableSmallSortFreezeTypeImpl for T { if has_efficient_in_place_swap::() && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { - SMALL_SORT_NETWORK_SCRATCH_LEN + SMALL_SORT_NETWORK_THRESHOLD + } else if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + SMALL_SORT_GENERAL_THRESHOLD } else { SMALL_SORT_FALLBACK_THRESHOLD } @@ -161,6 +163,8 @@ impl UnstableSmallSortFreezeTypeImpl for T { && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { small_sort_network(v, is_less); + } else if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + small_sort_general(v, is_less); } else { small_sort_fallback(v, is_less); } From 207c5bc5a9007acd5bf3dbd58ef361bbb3ff3866 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 17 Jun 2024 08:48:49 +0300 Subject: [PATCH 029/147] override user defined channel when using precompiled rustc Signed-off-by: onur-ozkan --- src/bootstrap/src/core/config/config.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index a1d8ca3cbcaa..0438dee7241f 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1718,7 +1718,23 @@ impl Config { config.omit_git_hash = omit_git_hash.unwrap_or(default); config.rust_info = GitInfo::new(config.omit_git_hash, &config.src); - if config.rust_info.is_from_tarball() && !is_user_configured_rust_channel { + // We need to override `rust.channel` if it's manually specified when using the CI rustc. + // This is because if the compiler uses a different channel than the one specified in config.toml, + // tests may fail due to using a different channel than the one used by the compiler during tests. + if let Some(commit) = &config.download_rustc_commit { + if is_user_configured_rust_channel { + println!( + "WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel." + ); + + let channel = config + .read_file_by_commit(&PathBuf::from("src/ci/channel"), commit) + .trim() + .to_owned(); + + config.channel = channel; + } + } else if config.rust_info.is_from_tarball() && !is_user_configured_rust_channel { ci_channel.clone_into(&mut config.channel); } From 5ae2446109ccdc64921561228eb983f795705b17 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 17 Jun 2024 08:49:36 +0300 Subject: [PATCH 030/147] simplify `Builder::doc_rust_lang_org_channel` This is already handled at the config parsing level, so we can simplify it. Signed-off-by: onur-ozkan --- src/bootstrap/src/core/builder.rs | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index d9e4cbae17d7..73e2cebc21c1 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1036,23 +1036,12 @@ impl<'a> Builder<'a> { } pub fn doc_rust_lang_org_channel(&self) -> String { - // When using precompiled compiler from CI, we need to use CI rustc's channel and - // ignore `rust.channel` from the configuration. Otherwise most of the rustdoc tests - // will fail due to incompatible `DOC_RUST_LANG_ORG_CHANNEL`. - let channel = if let Some(commit) = self.config.download_rustc_commit() { - self.config - .read_file_by_commit(&PathBuf::from("src/ci/channel"), commit) - .trim() - .to_owned() - } else { - match &*self.config.channel { - "stable" => &self.version, - "beta" => "beta", - "nightly" | "dev" => "nightly", - // custom build of rustdoc maybe? link to the latest stable docs just in case - _ => "stable", - } - .to_owned() + let channel = match &*self.config.channel { + "stable" => &self.version, + "beta" => "beta", + "nightly" | "dev" => "nightly", + // custom build of rustdoc maybe? link to the latest stable docs just in case + _ => "stable", }; format!("https://doc.rust-lang.org/{channel}") From 6f5b0efb28593d355a6f8bcf2eea78ff396ff1aa Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 17 Jun 2024 13:15:09 +0200 Subject: [PATCH 031/147] Add badboy and BlackHoleFox as Mac Catalyst maintainers --- src/doc/rustc/src/platform-support/apple-ios-macabi.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/doc/rustc/src/platform-support/apple-ios-macabi.md b/src/doc/rustc/src/platform-support/apple-ios-macabi.md index 278ee94b6d4e..15ba31e0f064 100644 --- a/src/doc/rustc/src/platform-support/apple-ios-macabi.md +++ b/src/doc/rustc/src/platform-support/apple-ios-macabi.md @@ -9,6 +9,8 @@ Apple Mac Catalyst targets. ## Target maintainers +- [@badboy](https://github.com/badboy) +- [@BlackHoleFox](https://github.com/BlackHoleFox) - [@madsmtm](https://github.com/madsmtm) ## Requirements From b7c23761d87c593d239118b647a2cfb8acd3aa28 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 31 May 2024 20:08:29 +0000 Subject: [PATCH 032/147] Sync fuchsia test runner with clang test runner This synchronizes the fuchsia test running code with the clang test runner. This brings with it: * Improved logging * Uses the fuchsia image from the SDK version * Caches the product bundle across test runs * Strips the binaries to reduce the data sent to the emulator --- src/ci/docker/scripts/fuchsia-test-runner.py | 1012 +++++++++++------- 1 file changed, 627 insertions(+), 385 deletions(-) diff --git a/src/ci/docker/scripts/fuchsia-test-runner.py b/src/ci/docker/scripts/fuchsia-test-runner.py index d791550a8db8..115ee69a5891 100755 --- a/src/ci/docker/scripts/fuchsia-test-runner.py +++ b/src/ci/docker/scripts/fuchsia-test-runner.py @@ -8,34 +8,137 @@ https://doc.rust-lang.org/stable/rustc/platform-support/fuchsia.html#aarch64-unk """ import argparse +from concurrent.futures import ThreadPoolExecutor from dataclasses import dataclass import glob -import hashlib +import io import json +import logging import os import platform +import shlex import shutil import subprocess import sys -from typing import ClassVar, List +from pathlib import Path +from typing import ClassVar, List, Optional -@dataclass +def check_call_with_logging( + args, *, stdout_handler, stderr_handler, check=True, text=True, **kwargs +): + stdout_handler(f"Subprocess: {shlex.join(str(arg) for arg in args)}") + + with subprocess.Popen( + args, + text=text, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + **kwargs, + ) as process: + with ThreadPoolExecutor(max_workers=2) as executor: + + def exhaust_pipe(handler, pipe): + for line in pipe: + handler(line.rstrip()) + + executor_out = executor.submit( + exhaust_pipe, stdout_handler, process.stdout + ) + executor_err = executor.submit( + exhaust_pipe, stderr_handler, process.stderr + ) + executor_out.result() + executor_err.result() + retcode = process.poll() + if check and retcode: + raise subprocess.CalledProcessError(retcode, process.args) + return subprocess.CompletedProcess(process.args, retcode) + + +def check_output_with_logging( + args, *, stdout_handler, stderr_handler, check=True, text=True, **kwargs +): + stdout_handler(f"Subprocess: {shlex.join(str(arg) for arg in args)}") + + buf = io.StringIO() + + with subprocess.Popen( + args, + text=text, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + **kwargs, + ) as process: + with ThreadPoolExecutor(max_workers=2) as executor: + + def exhaust_stdout(handler, buf, pipe): + for line in pipe: + handler(line.rstrip()) + buf.write(line) + buf.write("\n") + + def exhaust_stderr(handler, pipe): + for line in pipe: + handler(line.rstrip()) + + executor_out = executor.submit( + exhaust_stdout, stdout_handler, buf, process.stdout + ) + executor_err = executor.submit( + exhaust_stderr, stderr_handler, process.stderr + ) + executor_out.result() + executor_err.result() + retcode = process.poll() + if check and retcode: + raise subprocess.CalledProcessError(retcode, process.args) + + return buf.getvalue() + + +def atomic_link(link: Path, target: Path): + link_dir = link.parent + os.makedirs(link_dir, exist_ok=True) + link_file = link.name + tmp_file = link_dir.joinpath(link_file + "_tmp") + os.link(target, tmp_file) + try: + os.rename(tmp_file, link) + except Exception as e: + raise e + finally: + if tmp_file.exists(): + os.remove(tmp_file) + + +@dataclass(kw_only=True) class TestEnvironment: - rust_build_dir: str - sdk_dir: str + rust_build_dir: Path + sdk_dir: Path target: str + toolchain_dir: Path + local_pb_path: Optional[Path] + use_local_pb: bool verbose: bool = False - @staticmethod - def tmp_dir(): - tmp_dir = os.environ.get("TEST_TOOLCHAIN_TMP_DIR") - if tmp_dir is not None: - return os.path.abspath(tmp_dir) - return os.path.join(os.path.dirname(__file__), "tmp~") + env_logger = logging.getLogger("env") + subprocess_logger = logging.getLogger("env.subprocess") + __tmp_dir = None @staticmethod - def triple_to_arch(triple): + def tmp_dir() -> Path: + if TestEnvironment.__tmp_dir: + return TestEnvironment.__tmp_dir + tmp_dir = os.environ.get("TEST_TOOLCHAIN_TMP_DIR") + if tmp_dir is not None: + TestEnvironment.__tmp_dir = Path(tmp_dir).absolute() + else: + TestEnvironment.__tmp_dir = Path(__file__).parent.joinpath("tmp~") + return TestEnvironment.__tmp_dir + + @staticmethod + def triple_to_arch(triple) -> str: if "x86_64" in triple: return "x64" elif "aarch64" in triple: @@ -44,61 +147,175 @@ class TestEnvironment: raise Exception(f"Unrecognized target triple {triple}") @classmethod - def env_file_path(cls): - return os.path.join(cls.tmp_dir(), "test_env.json") + def env_file_path(cls) -> Path: + return cls.tmp_dir().joinpath("test_env.json") @classmethod def from_args(cls, args): + local_pb_path = args.local_product_bundle_path + if local_pb_path is not None: + local_pb_path = Path(local_pb_path).absolute() + return cls( - os.path.abspath(args.rust_build), - os.path.abspath(args.sdk), - args.target, + rust_build_dir=Path(args.rust_build).absolute(), + sdk_dir=Path(args.sdk).absolute(), + target=args.target, + toolchain_dir=Path(args.toolchain_dir).absolute(), + local_pb_path=local_pb_path, + use_local_pb=args.use_local_product_bundle_if_exists, verbose=args.verbose, ) @classmethod def read_from_file(cls): with open(cls.env_file_path(), encoding="utf-8") as f: - test_env = json.loads(f.read()) + test_env = json.load(f) + local_pb_path = test_env["local_pb_path"] + if local_pb_path is not None: + local_pb_path = Path(local_pb_path) + return cls( - test_env["rust_build_dir"], - test_env["sdk_dir"], - test_env["target"], + rust_build_dir=Path(test_env["rust_build_dir"]), + sdk_dir=Path(test_env["sdk_dir"]), + target=test_env["target"], + toolchain_dir=Path(test_env["toolchain_dir"]), + local_pb_path=local_pb_path, + use_local_pb=test_env["use_local_pb"], verbose=test_env["verbose"], ) + def build_id(self, binary): + llvm_readelf = Path(self.toolchain_dir).joinpath("bin", "llvm-readelf") + process = subprocess.run( + args=[ + llvm_readelf, + "-n", + "--elf-output-style=JSON", + binary, + ], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + if process.returncode: + self.env_logger.error( + f"llvm-readelf failed for binary {binary} with output {process.stdout}" + ) + raise Exception(f"Unreadable build-id for binary {binary}") + data = json.loads(process.stdout) + if len(data) != 1: + raise Exception( + f"Unreadable output from llvm-readelf for binary {binary}" + ) + notes = data[0]["Notes"] + for note in notes: + note_section = note["NoteSection"] + if note_section["Name"] == ".note.gnu.build-id": + return note_section["Note"]["Build ID"] + raise Exception(f"Build ID not found for binary {binary}") + + def generate_buildid_dir( + self, + binary: Path, + build_id_dir: Path, + build_id: str, + log_handler: logging.Logger, + ): + os.makedirs(build_id_dir, exist_ok=True) + suffix = ".debug" + # Hardlink the original binary + build_id_prefix_dir = build_id_dir.joinpath(build_id[:2]) + unstripped_binary = build_id_prefix_dir.joinpath(build_id[2:] + suffix) + build_id_prefix_dir.mkdir(parents=True, exist_ok=True) + atomic_link(unstripped_binary, binary) + assert unstripped_binary.exists() + stripped_binary = unstripped_binary.with_suffix("") + llvm_objcopy = Path(self.toolchain_dir).joinpath("bin", "llvm-objcopy") + strip_mode = "--strip-sections" + check_call_with_logging( + [ + llvm_objcopy, + strip_mode, + unstripped_binary, + stripped_binary, + ], + stdout_handler=log_handler.info, + stderr_handler=log_handler.error, + ) + return stripped_binary + def write_to_file(self): with open(self.env_file_path(), "w", encoding="utf-8") as f: - f.write(json.dumps(self.__dict__)) + local_pb_path = self.local_pb_path + if local_pb_path is not None: + local_pb_path = str(local_pb_path) - def package_server_log_path(self): - return os.path.join(self.tmp_dir(), "package_server_log") + json.dump( + { + "rust_build_dir": str(self.rust_build_dir), + "sdk_dir": str(self.sdk_dir), + "target": self.target, + "toolchain_dir": str(self.toolchain_dir), + "local_pb_path": local_pb_path, + "use_local_pb": self.use_local_pb, + "verbose": self.verbose, + }, + f, + ) - def emulator_log_path(self): - return os.path.join(self.tmp_dir(), "emulator_log") + def setup_logging(self, log_to_file=False): + fs = logging.Formatter("%(asctime)s %(levelname)s:%(name)s:%(message)s") + if log_to_file: + logfile_handler = logging.FileHandler( + self.tmp_dir().joinpath("log") + ) + logfile_handler.setLevel(logging.DEBUG) + logfile_handler.setFormatter(fs) + logging.getLogger().addHandler(logfile_handler) + stream_handler = logging.StreamHandler(sys.stdout) + stream_handler.setFormatter(fs) + if self.verbose: + stream_handler.setLevel(logging.DEBUG) + else: + stream_handler.setLevel(logging.INFO) + logging.getLogger().addHandler(stream_handler) + logging.getLogger().setLevel(logging.DEBUG) - def packages_dir(self): - return os.path.join(self.tmp_dir(), "packages") + @property + def package_server_log_path(self) -> Path: + return self.tmp_dir().joinpath("package_server_log") - def output_dir(self): - return os.path.join(self.tmp_dir(), "output") + @property + def emulator_log_path(self) -> Path: + return self.tmp_dir().joinpath("emulator_log") + + @property + def packages_dir(self) -> Path: + return self.tmp_dir().joinpath("packages") + + @property + def output_dir(self) -> Path: + return self.tmp_dir().joinpath("output") + + def read_sdk_version(self): + meta_json_path = Path(self.sdk_dir).joinpath("meta", "manifest.json") + with open(meta_json_path, encoding="utf-8") as f: + meta_json = json.load(f) + return meta_json["id"] TEST_REPO_NAME: ClassVar[str] = "rust-testing" - def repo_dir(self): - return os.path.join(self.tmp_dir(), self.TEST_REPO_NAME) + def repo_dir(self) -> Path: + return self.tmp_dir().joinpath(self.TEST_REPO_NAME) - def libs_dir(self): - return os.path.join( - self.rust_build_dir, + def libs_dir(self) -> Path: + return self.rust_build_dir.joinpath( "host", "stage2", "lib", ) - def rustlibs_dir(self): - return os.path.join( - self.libs_dir(), + def rustlibs_dir(self) -> Path: + return self.libs_dir().joinpath( "rustlib", self.target, "lib", @@ -112,8 +329,8 @@ class TestEnvironment: return "a64" raise Exception(f"Unrecognized host architecture {machine}") - def tool_path(self, tool): - return os.path.join(self.sdk_dir, "tools", self.sdk_arch(), tool) + def tool_path(self, tool) -> Path: + return Path(self.sdk_dir).joinpath("tools", self.sdk_arch(), tool) def host_arch_triple(self): machine = platform.machine() @@ -123,45 +340,25 @@ class TestEnvironment: return "aarch64-unknown-linux-gnu" raise Exception(f"Unrecognized host architecture {machine}") - def zxdb_script_path(self): - return os.path.join(self.tmp_dir(), "zxdb_script") - - def pm_lockfile_path(self): - return os.path.join(self.tmp_dir(), "pm.lock") - - def log_info(self, msg): - print(msg) - - def log_debug(self, msg): - if self.verbose: - print(msg) - - def subprocess_output(self): - if self.verbose: - return sys.stdout - return subprocess.DEVNULL - - def check_call(self, args, **kwargs): - self.log_info(f"Running: {' '.join(args)}") - return subprocess.check_call(args, **kwargs) - - def check_output(self, args, **kwargs): - self.log_info(f"Running: {' '.join(args)}") - return subprocess.check_output(args, **kwargs) + def zxdb_script_path(self) -> Path: + return Path(self.tmp_dir(), "zxdb_script") + @property def ffx_daemon_log_path(self): - return os.path.join(self.tmp_dir(), "ffx_daemon_log") + return self.tmp_dir().joinpath("ffx_daemon_log") + @property def ffx_isolate_dir(self): - return os.path.join(self.tmp_dir(), "ffx_isolate") + return self.tmp_dir().joinpath("ffx_isolate") + @property def home_dir(self): - return os.path.join(self.tmp_dir(), "user-home") + return self.tmp_dir().joinpath("user-home") def start_ffx_isolation(self): # Most of this is translated directly from ffx's isolate library - os.mkdir(self.ffx_isolate_dir()) - os.mkdir(self.home_dir()) + os.mkdir(self.ffx_isolate_dir) + os.mkdir(self.home_dir) ffx_path = self.tool_path("ffx") ffx_env = self.ffx_cmd_env() @@ -170,7 +367,7 @@ class TestEnvironment: # We want this to be a long-running process that persists after the script finishes # pylint: disable=consider-using-with with open( - self.ffx_daemon_log_path(), "w", encoding="utf-8" + self.ffx_daemon_log_path, "w", encoding="utf-8" ) as ffx_daemon_log_file: subprocess.Popen( [ @@ -184,7 +381,7 @@ class TestEnvironment: ) # Disable analytics - self.check_call( + check_call_with_logging( [ ffx_path, "config", @@ -192,8 +389,8 @@ class TestEnvironment: "disable", ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) # Set configs @@ -203,7 +400,7 @@ class TestEnvironment: "test.experimental_structured_output": "true", } for key, value in configs.items(): - self.check_call( + check_call_with_logging( [ ffx_path, "config", @@ -212,14 +409,14 @@ class TestEnvironment: value, ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) def ffx_cmd_env(self): return { - "HOME": self.home_dir(), - "FFX_ISOLATE_DIR": self.ffx_isolate_dir(), + "HOME": self.home_dir, + "FFX_ISOLATE_DIR": self.ffx_isolate_dir, # We want to use our own specified temp directory "TMP": self.tmp_dir(), "TEMP": self.tmp_dir(), @@ -228,16 +425,15 @@ class TestEnvironment: } def stop_ffx_isolation(self): - self.check_call( + check_call_with_logging( [ self.tool_path("ffx"), "daemon", "stop", - "-w", ], env=self.ffx_cmd_env(), - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) def start(self): @@ -256,22 +452,23 @@ class TestEnvironment: """ # Initialize temp directory - if not os.path.exists(self.tmp_dir()): - os.mkdir(self.tmp_dir()) - elif len(os.listdir(self.tmp_dir())) != 0: - raise Exception(f"Temp directory is not clean (in {self.tmp_dir()})") - - os.mkdir(self.output_dir()) + os.makedirs(self.tmp_dir(), exist_ok=True) + if len(os.listdir(self.tmp_dir())) != 0: + raise Exception( + f"Temp directory is not clean (in {self.tmp_dir()})" + ) + self.setup_logging(log_to_file=True) + os.mkdir(self.output_dir) ffx_path = self.tool_path("ffx") ffx_env = self.ffx_cmd_env() # Start ffx isolation - self.log_info("Starting ffx isolation...") + self.env_logger.info("Starting ffx isolation...") self.start_ffx_isolation() # Stop any running emulators (there shouldn't be any) - self.check_call( + check_call_with_logging( [ ffx_path, "emu", @@ -279,79 +476,95 @@ class TestEnvironment: "--all", ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) - # Look up the product bundle transfer manifest. - self.log_info("Looking up the product bundle transfer manifest...") - product_name = "minimal." + self.triple_to_arch(self.target) - fuchsia_version = "21.20240610.2.1" + if not self.local_pb_path: + self.local_pb_path = os.path.join(self.tmp_dir(), "local_pb") + else: + self.local_pb_path = os.path.abspath(self.local_pb_path) - out = self.check_output( - [ - ffx_path, - "--machine", - "json", - "product", - "lookup", - product_name, - fuchsia_version, - "--base-url", - "gs://fuchsia/development/" + fuchsia_version, - ], - env=ffx_env, - stderr=self.subprocess_output(), - ) + if self.use_local_pb and os.path.exists(self.local_pb_path): + self.env_logger.info( + 'Using existing emulator image at "%s"' % self.local_pb_path + ) + else: + shutil.rmtree(self.local_pb_path, ignore_errors=True) - self.log_debug(out) + # Look up the product bundle transfer manifest. + self.env_logger.info( + "Looking up the product bundle transfer manifest..." + ) + product_name = "minimal." + self.triple_to_arch(self.target) + sdk_version = self.read_sdk_version() - try: - transfer_manifest_url = json.loads(out)["transfer_manifest_url"] - except Exception as e: - print(e) - raise Exception("Unable to parse transfer manifest") from e + output = check_output_with_logging( + [ + ffx_path, + "--machine", + "json", + "product", + "lookup", + product_name, + sdk_version, + "--base-url", + "gs://fuchsia/development/" + sdk_version, + ], + env=ffx_env, + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, + ) - # Download the product bundle. - product_bundle_dir = os.path.join(self.tmp_dir(), 'product-bundle') - self.check_call( - [ - ffx_path, - "product", - "download", - transfer_manifest_url, - product_bundle_dir, - "--force", - ], - env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), - ) + try: + transfer_manifest_url = json.loads(output)[ + "transfer_manifest_url" + ] + except Exception as e: + print(e) + raise Exception("Unable to parse transfer manifest") from e + + # Download the product bundle. + self.env_logger.info("Downloading the product bundle...") + check_call_with_logging( + [ + ffx_path, + "product", + "download", + transfer_manifest_url, + self.local_pb_path, + ], + env=ffx_env, + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, + ) # Start emulator + self.env_logger.info("Starting emulator...") + # FIXME: condition --accel hyper on target arch matching host arch - self.check_call( + check_call_with_logging( [ ffx_path, "emu", "start", - product_bundle_dir, + self.local_pb_path, "--headless", "--log", - self.emulator_log_path(), + self.emulator_log_path, "--net", - "tap", + "auto", "--accel", - "hyper", + "auto", ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) # Create new package repo - self.log_info("Creating package repo...") - self.check_call( + self.env_logger.info("Creating package repo...") + check_call_with_logging( [ ffx_path, "repository", @@ -359,11 +572,12 @@ class TestEnvironment: self.repo_dir(), ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) - self.check_call( + # Add repository + check_call_with_logging( [ ffx_path, "repository", @@ -373,15 +587,12 @@ class TestEnvironment: self.repo_dir(), ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) - # Write to file - self.write_to_file() - # Start repository server - self.check_call( + check_call_with_logging( [ ffx_path, "repository", @@ -391,12 +602,12 @@ class TestEnvironment: "[::]:0", ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) # Register with newly-started emulator - self.check_call( + check_call_with_logging( [ ffx_path, "target", @@ -406,11 +617,14 @@ class TestEnvironment: self.TEST_REPO_NAME, ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) - self.log_info("Success! Your environment is ready to run tests.") + # Write to file + self.write_to_file() + + self.env_logger.info("Success! Your environment is ready to run tests.") # FIXME: shardify this # `facet` statement required for TCP testing via @@ -481,7 +695,7 @@ class TestEnvironment: - Forward the test's stdout and stderr as this script's stdout and stderr """ - bin_path = os.path.abspath(args.bin_path) + bin_path = Path(args.bin_path).absolute() # Find libstd and libtest libstd_paths = glob.glob(os.path.join(self.rustlibs_dir(), "libstd-*.so")) @@ -490,233 +704,240 @@ class TestEnvironment: if not libstd_paths: raise Exception(f"Failed to locate libstd (in {self.rustlibs_dir()})") - # Build a unique, deterministic name for the test using the name of the - # binary and the last 6 hex digits of the hash of the full path - def path_checksum(path): - m = hashlib.sha256() - m.update(path.encode("utf-8")) - return m.hexdigest()[0:6] - base_name = os.path.basename(os.path.dirname(args.bin_path)) exe_name = base_name.lower().replace(".", "_") - package_name = f"{exe_name}_{path_checksum(bin_path)}" + build_id = self.build_id(bin_path) + package_name = f"{exe_name}_{build_id}" - package_dir = os.path.join(self.packages_dir(), package_name) - cml_path = os.path.join(package_dir, "meta", f"{package_name}.cml") - cm_path = os.path.join(package_dir, "meta", f"{package_name}.cm") - manifest_path = os.path.join(package_dir, f"{package_name}.manifest") - manifest_json_path = os.path.join(package_dir, "package_manifest.json") - far_path = os.path.join(package_dir, f"{package_name}-0.far") + package_dir = self.packages_dir.joinpath(package_name) + package_dir.mkdir(parents=True, exist_ok=True) + meta_dir = package_dir.joinpath("meta") + meta_dir.mkdir(parents=True, exist_ok=True) + meta_package_path = meta_dir.joinpath("package") + cml_path = meta_dir.joinpath(f"{package_name}.cml") + cm_path = meta_dir.joinpath(f"{package_name}.cm") + manifest_path = package_dir.joinpath(f"{package_name}.manifest") shared_libs = args.shared_libs[: args.n] arguments = args.shared_libs[args.n :] - test_output_dir = os.path.join(self.output_dir(), package_name) + test_output_dir = self.output_dir.joinpath(package_name) # Clean and create temporary output directory - if os.path.exists(test_output_dir): + if test_output_dir.exists(): shutil.rmtree(test_output_dir) - - os.mkdir(test_output_dir) + test_output_dir.mkdir(parents=True) # Open log file - log_path = os.path.join(test_output_dir, "log") - with open(log_path, "w", encoding="utf-8") as log_file: + runner_logger = logging.getLogger(f"env.package.{package_name}") + runner_logger.setLevel(logging.DEBUG) + logfile_handler = logging.FileHandler(test_output_dir.joinpath("log")) + logfile_handler.setLevel(logging.DEBUG) + logfile_handler.setFormatter( + logging.Formatter("%(levelname)s:%(name)s:%(message)s") + ) + runner_logger.addHandler(logfile_handler) - def log(msg): - print(msg, file=log_file) - log_file.flush() + runner_logger.info(f"Bin path: {bin_path}") + runner_logger.info("Setting up package...") - log(f"Bin path: {bin_path}") + # Link binary to build-id dir and strip it. + build_id_dir = self.tmp_dir().joinpath(".build-id") + stripped_binary = self.generate_buildid_dir( + binary=bin_path, + build_id_dir=build_id_dir, + build_id=build_id, + log_handler=runner_logger, + ) + runner_logger.info(f"Stripped Bin path: {stripped_binary}") - log("Writing CML...") + runner_logger.info("Writing CML...") - # Write and compile CML - with open(cml_path, "w", encoding="utf-8") as cml: - # Collect environment variables - env_vars = "" - for var_name in self.TEST_ENV_VARS: - var_value = os.getenv(var_name) - if var_value is not None: - env_vars += f'\n "{var_name}={var_value}",' + # Write and compile CML + with open(cml_path, "w", encoding="utf-8") as cml: + # Collect environment variables + env_vars = "" + for var_name in self.TEST_ENV_VARS: + var_value = os.getenv(var_name) + if var_value is not None: + env_vars += f'\n "{var_name}={var_value}",' - # Default to no backtrace for test suite - if os.getenv("RUST_BACKTRACE") is None: - env_vars += '\n "RUST_BACKTRACE=0",' + # Default to no backtrace for test suite + if os.getenv("RUST_BACKTRACE") is None: + env_vars += '\n "RUST_BACKTRACE=0",' - # Use /tmp as the test temporary directory - env_vars += '\n "RUST_TEST_TMPDIR=/tmp",' + # Use /tmp as the test temporary directory + env_vars += '\n "RUST_TEST_TMPDIR=/tmp",' - cml.write( - self.CML_TEMPLATE.format(env_vars=env_vars, exe_name=exe_name) - ) - - log("Compiling CML...") - - self.check_call( - [ - self.tool_path("cmc"), - "compile", - cml_path, - "--includepath", - ".", - "--output", - cm_path, - ], - stdout=log_file, - stderr=log_file, + cml.write( + self.CML_TEMPLATE.format(env_vars=env_vars, exe_name=exe_name) ) - log("Writing manifest...") + runner_logger.info("Compiling CML...") - # Write, build, and archive manifest - with open(manifest_path, "w", encoding="utf-8") as manifest: + check_call_with_logging( + [ + self.tool_path("cmc"), + "compile", + cml_path, + "--includepath", + ".", + "--output", + cm_path, + ], + stdout_handler=runner_logger.info, + stderr_handler=runner_logger.warning, + ) + + runner_logger.info("Writing meta/package...") + with open(meta_package_path, "w", encoding="utf-8") as f: + json.dump({"name": package_name, "version": "0"}, f) + + runner_logger.info("Writing manifest...") + + # Write package manifest + with open(manifest_path, "w", encoding="utf-8") as manifest: + manifest.write( + self.MANIFEST_TEMPLATE.format( + bin_path=stripped_binary, + exe_name=exe_name, + package_dir=package_dir, + package_name=package_name, + target=self.target, + sdk_dir=self.sdk_dir, + libstd_name=os.path.basename(libstd_paths[0]), + libstd_path=libstd_paths[0], + target_arch=self.triple_to_arch(self.target), + ) + ) + # `libtest`` was historically a shared library, but now seems to be (sometimes?) + # statically linked. If we find it as a shared library, include it in the manifest. + if libtest_paths: manifest.write( - self.MANIFEST_TEMPLATE.format( - bin_path=bin_path, - exe_name=exe_name, - package_dir=package_dir, - package_name=package_name, - target=self.target, - sdk_dir=self.sdk_dir, - libstd_name=os.path.basename(libstd_paths[0]), - libstd_path=libstd_paths[0], - target_arch=self.triple_to_arch(self.target), - ) + f"lib/{os.path.basename(libtest_paths[0])}={libtest_paths[0]}\n" ) - # `libtest`` was historically a shared library, but now seems to be (sometimes?) - # statically linked. If we find it as a shared library, include it in the manifest. - if libtest_paths: - manifest.write( - f"lib/{os.path.basename(libtest_paths[0])}={libtest_paths[0]}\n" - ) - for shared_lib in shared_libs: - manifest.write(f"lib/{os.path.basename(shared_lib)}={shared_lib}\n") + for shared_lib in shared_libs: + manifest.write(f"lib/{os.path.basename(shared_lib)}={shared_lib}\n") - log("Determining API level...") - out = self.check_output( - [ - self.tool_path("ffx"), - "--machine", - "json", - "version", - ], - env=self.ffx_cmd_env(), - stderr=log_file, - ) - api_level = json.loads(out)["tool_version"]["api_level"] + runner_logger.info("Determining API level...") + out = check_output_with_logging( + [ + self.tool_path("ffx"), + "--machine", + "json", + "version", + ], + env=self.ffx_cmd_env(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, + ) + api_level = json.loads(out)["tool_version"]["api_level"] - log("Compiling and archiving manifest...") + runner_logger.info("Compiling manifest...") - self.check_call( - [ - self.tool_path("ffx"), - "package", - "build", - manifest_path, - "-o", - package_dir, - "--api-level", - str(api_level), - ], - env=self.ffx_cmd_env(), - stdout=log_file, - stderr=log_file, - ) + check_call_with_logging( + [ + self.tool_path("ffx"), + "package", + "build", + manifest_path, + "-o", + package_dir, + "--api-level", + str(api_level), + ], + env=self.ffx_cmd_env(), + stdout_handler=runner_logger.info, + stderr_handler=runner_logger.warning, + ) - self.check_call( - [ - self.tool_path("ffx"), - "package", - "archive", - "create", - "-o", - far_path, - manifest_json_path, - ], - env=self.ffx_cmd_env(), - stdout=log_file, - stderr=log_file, - ) + runner_logger.info("Publishing package to repo...") - log("Publishing package to repo...") + # Publish package to repo + check_call_with_logging( + [ + self.tool_path("ffx"), + "repository", + "publish", + "--package", + os.path.join(package_dir, "package_manifest.json"), + self.repo_dir(), + ], + env=self.ffx_cmd_env(), + stdout_handler=runner_logger.info, + stderr_handler=runner_logger.warning, + ) - # Publish package to repo - self.check_call( - [ - self.tool_path("ffx"), - "repository", - "publish", - "--package", - os.path.join(package_dir, "package_manifest.json"), - self.repo_dir(), - ], - stdout=log_file, - stderr=log_file, - ) + runner_logger.info("Running ffx test...") - log("Running ffx test...") + # Run test on emulator + check_call_with_logging( + [ + self.tool_path("ffx"), + "test", + "run", + f"fuchsia-pkg://{self.TEST_REPO_NAME}/{package_name}#meta/{package_name}.cm", + "--min-severity-logs", + "TRACE", + "--output-directory", + test_output_dir, + "--", + ] + + arguments, + env=self.ffx_cmd_env(), + check=False, + stdout_handler=runner_logger.info, + stderr_handler=runner_logger.warning, + ) - # Run test on emulator - subprocess.run( - [ - self.tool_path("ffx"), - "test", - "run", - f"fuchsia-pkg://{self.TEST_REPO_NAME}/{package_name}#meta/{package_name}.cm", - "--min-severity-logs", - "TRACE", - "--output-directory", - test_output_dir, - "--", - ] - + arguments, - env=self.ffx_cmd_env(), - check=False, - stdout=log_file, - stderr=log_file, - ) + runner_logger.info("Reporting test suite output...") - log("Reporting test suite output...") + # Read test suite output + run_summary_path = test_output_dir.joinpath("run_summary.json") + if not run_summary_path.exists(): + runner_logger.error("Failed to open test run summary") + return 254 - # Read test suite output - run_summary_path = os.path.join(test_output_dir, "run_summary.json") - if os.path.exists(run_summary_path): - with open(run_summary_path, encoding="utf-8") as f: - run_summary = json.loads(f.read()) + with open(run_summary_path, encoding="utf-8") as f: + run_summary = json.load(f) - suite = run_summary["data"]["suites"][0] - case = suite["cases"][0] + suite = run_summary["data"]["suites"][0] + case = suite["cases"][0] - return_code = 0 if case["outcome"] == "PASSED" else 1 + return_code = 0 if case["outcome"] == "PASSED" else 1 - artifacts = case["artifacts"] - artifact_dir = case["artifact_dir"] - stdout_path = None - stderr_path = None + artifacts = case["artifacts"] + artifact_dir = case["artifact_dir"] + stdout_path = None + stderr_path = None - for path, artifact in artifacts.items(): - artifact_path = os.path.join(test_output_dir, artifact_dir, path) - artifact_type = artifact["artifact_type"] + for path, artifact in artifacts.items(): + artifact_path = os.path.join(test_output_dir, artifact_dir, path) + artifact_type = artifact["artifact_type"] - if artifact_type == "STDERR": - stderr_path = artifact_path - elif artifact_type == "STDOUT": - stdout_path = artifact_path + if artifact_type == "STDERR": + stderr_path = artifact_path + elif artifact_type == "STDOUT": + stdout_path = artifact_path - if stdout_path is not None and os.path.exists(stdout_path): - with open(stdout_path, encoding="utf-8") as f: - print(f.read(), file=sys.stdout, end="") - - if stderr_path is not None and os.path.exists(stderr_path): - with open(stderr_path, encoding="utf-8") as f: - print(f.read(), file=sys.stderr, end="") + if stdout_path is not None: + if not os.path.exists(stdout_path): + runner_logger.error( + f"stdout file {stdout_path} does not exist." + ) else: - log("Failed to open test run summary") - return_code = 254 - - log("Done!") + with open(stdout_path, encoding="utf-8", errors="ignore") as f: + runner_logger.info(f.read()) + if stderr_path is not None: + if not os.path.exists(stderr_path): + runner_logger.error( + f"stderr file {stderr_path} does not exist." + ) + else: + with open(stderr_path, encoding="utf-8", errors="ignore") as f: + runner_logger.error(f.read()) + runner_logger.info("Done!") return return_code def stop(self): @@ -730,65 +951,65 @@ class TestEnvironment: During cleanup, this function will stop the emulator, package server, and update server, then delete all temporary files. If an error is encountered while stopping any running processes, the temporary files will not be deleted. - Passing --delete-tmp will force the process to delete the files anyway. + Passing --cleanup will force the process to delete the files anyway. """ - self.log_debug("Reporting logs...") + self.env_logger.debug("Reporting logs...") # Print test log files - for test_dir in os.listdir(self.output_dir()): - log_path = os.path.join(self.output_dir(), test_dir, "log") - self.log_debug(f"\n---- Logs for test '{test_dir}' ----\n") + for test_dir in os.listdir(self.output_dir): + log_path = os.path.join(self.output_dir, test_dir, "log") + self.env_logger.debug(f"\n---- Logs for test '{test_dir}' ----\n") if os.path.exists(log_path): - with open(log_path, encoding="utf-8") as log: - self.log_debug(log.read()) + with open(log_path, encoding="utf-8", errors="ignore") as log: + self.env_logger.debug(log.read()) else: - self.log_debug("No logs found") + self.env_logger.debug("No logs found") # Print the emulator log - self.log_debug("\n---- Emulator logs ----\n") - if os.path.exists(self.emulator_log_path()): - with open(self.emulator_log_path(), encoding="utf-8") as log: - self.log_debug(log.read()) + self.env_logger.debug("\n---- Emulator logs ----\n") + if os.path.exists(self.emulator_log_path): + with open(self.emulator_log_path, encoding="utf-8") as log: + self.env_logger.debug(log.read()) else: - self.log_debug("No emulator logs found") + self.env_logger.debug("No emulator logs found") # Print the package server log - self.log_debug("\n---- Package server log ----\n") - if os.path.exists(self.package_server_log_path()): - with open(self.package_server_log_path(), encoding="utf-8") as log: - self.log_debug(log.read()) + self.env_logger.debug("\n---- Package server log ----\n") + if os.path.exists(self.package_server_log_path): + with open(self.package_server_log_path, encoding="utf-8") as log: + self.env_logger.debug(log.read()) else: - self.log_debug("No package server log found") + self.env_logger.debug("No package server log found") # Print the ffx daemon log - self.log_debug("\n---- ffx daemon log ----\n") - if os.path.exists(self.ffx_daemon_log_path()): - with open(self.ffx_daemon_log_path(), encoding="utf-8") as log: - self.log_debug(log.read()) + self.env_logger.debug("\n---- ffx daemon log ----\n") + if os.path.exists(self.ffx_daemon_log_path): + with open(self.ffx_daemon_log_path, encoding="utf-8") as log: + self.env_logger.debug(log.read()) else: - self.log_debug("No ffx daemon log found") + self.env_logger.debug("No ffx daemon log found") # Shut down the emulator - self.log_info("Stopping emulator...") - self.check_call( + self.env_logger.info("Stopping emulator...") + check_call_with_logging( [ self.tool_path("ffx"), "emu", "stop", ], env=self.ffx_cmd_env(), - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) # Stop ffx isolation - self.log_info("Stopping ffx isolation...") + self.env_logger.info("Stopping ffx isolation...") self.stop_ffx_isolation() - def delete_tmp(self): + def cleanup(self): # Remove temporary files - self.log_info("Deleting temporary files...") + self.env_logger.info("Deleting temporary files...") shutil.rmtree(self.tmp_dir(), ignore_errors=True) def debug(self, args): @@ -816,7 +1037,7 @@ class TestEnvironment: f"--symbol-path={self.rust_dir}/lib/rustlib/{self.target}/lib", ] - # Add rust source if it's available + # Add rust source if it's available rust_src_map = None if args.rust_src is not None: # This matches the remapped prefix used by compiletest. There's no @@ -908,21 +1129,24 @@ def start(args): def run(args): test_env = TestEnvironment.read_from_file() + test_env.setup_logging(log_to_file=True) return test_env.run(args) def stop(args): test_env = TestEnvironment.read_from_file() + test_env.setup_logging(log_to_file=False) test_env.stop() - if not args.no_delete: - test_env.delete_tmp() + if not args.no_cleanup: + test_env.cleanup() return 0 -def delete_tmp(args): +def cleanup(args): del args test_env = TestEnvironment.read_from_file() - test_env.delete_tmp() + test_env.setup_logging(log_to_file=False) + test_env.cleanup() return 0 @@ -934,6 +1158,7 @@ def debug(args): def syslog(args): test_env = TestEnvironment.read_from_file() + test_env.setup_logging(log_to_file=True) test_env.syslog(args) return 0 @@ -973,6 +1198,21 @@ def main(): help="the target platform to test", required=True, ) + start_parser.add_argument( + "--toolchain-dir", + help="the toolchain directory", + required=True, + ) + start_parser.add_argument( + "--local-product-bundle-path", + help="the path where the product-bundle should be downloaded to", + ) + start_parser.add_argument( + "--use-local-product-bundle-if-exists", + help="if the product bundle already exists in the local path, use " + "it instead of downloading it again", + action="store_true", + ) start_parser.set_defaults(func=start) run_parser = subparsers.add_parser( @@ -993,18 +1233,23 @@ def main(): "stop", help="shuts down and cleans up the testing environment" ) stop_parser.add_argument( - "--no-delete", + "--no-cleanup", default=False, action="store_true", help="don't delete temporary files after stopping", ) stop_parser.set_defaults(func=stop) - delete_parser = subparsers.add_parser( - "delete-tmp", + cleanup_parser = subparsers.add_parser( + "cleanup", help="deletes temporary files after the testing environment has been manually cleaned up", ) - delete_parser.set_defaults(func=delete_tmp) + cleanup_parser.set_defaults(func=cleanup) + + syslog_parser = subparsers.add_parser( + "syslog", help="prints the device syslog" + ) + syslog_parser.set_defaults(func=syslog) debug_parser = subparsers.add_parser( "debug", @@ -1033,9 +1278,6 @@ def main(): ) debug_parser.set_defaults(func=debug) - syslog_parser = subparsers.add_parser("syslog", help="prints the device syslog") - syslog_parser.set_defaults(func=syslog) - args = parser.parse_args() return args.func(args) From 732616998c06d8d9024db3e1f62e64ad0b50ed30 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Mon, 17 Jun 2024 22:05:35 +0200 Subject: [PATCH 033/147] Revert panic_safe test changes The changes made only a limited improvement for the current small miri coverage and in general test coverage of the sort implementations. But they exploded test times from ~13s to ~240s, which is not deemed worth it. --- library/alloc/src/slice/tests.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/slice/tests.rs b/library/alloc/src/slice/tests.rs index 63a176a5ace3..0156b9928dab 100644 --- a/library/alloc/src/slice/tests.rs +++ b/library/alloc/src/slice/tests.rs @@ -197,7 +197,8 @@ fn panic_safe() { let mut rng = test_rng(); - let lens = if cfg!(miri) { (1..10).chain(30..36) } else { (1..20).chain(70..MAX_LEN) }; + // Miri is too slow (but still need to `chain` to make the types match) + let lens = if cfg!(miri) { (1..10).chain(0..0) } else { (1..20).chain(70..MAX_LEN) }; let moduli: &[u32] = if cfg!(miri) { &[5] } else { &[5, 20, 50] }; for len in lens { From 0f8c3f78825a1b6eb765b97f658b92252b55c5df Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Fri, 12 Apr 2024 23:24:45 +0800 Subject: [PATCH 034/147] tail expression behind terminating scope --- compiler/rustc_ast_lowering/src/expr.rs | 56 +++++---- compiler/rustc_feature/src/unstable.rs | 2 + .../rustc_hir_analysis/src/check/region.rs | 10 +- compiler/rustc_span/src/symbol.rs | 1 + .../ui/drop/auxiliary/edition-2021-macros.rs | 8 ++ .../ui/drop/auxiliary/edition-2024-macros.rs | 9 ++ ...xpr-drop-order-negative.edition2024.stderr | 16 +++ .../ui/drop/tail-expr-drop-order-negative.rs | 17 +++ tests/ui/drop/tail-expr-drop-order.rs | 108 ++++++++++++++++++ .../feature-gate-shorter_tail_lifetimes.rs | 8 ++ ...feature-gate-shorter_tail_lifetimes.stderr | 26 +++++ .../refcell-in-tail-expr.edition2021.stderr | 26 +++++ tests/ui/lifetimes/refcell-in-tail-expr.rs | 16 +++ ...rter-tail-expr-lifetime.edition2021.stderr | 26 +++++ .../lifetimes/shorter-tail-expr-lifetime.rs | 15 +++ .../ui/lifetimes/tail-expr-in-nested-expr.rs | 9 ++ .../lifetimes/tail-expr-in-nested-expr.stderr | 15 +++ .../ui/lifetimes/tail-expr-lock-poisoning.rs | 29 +++++ tests/ui/nll/issue-52534-1.rs | 4 +- 19 files changed, 377 insertions(+), 24 deletions(-) create mode 100644 tests/ui/drop/auxiliary/edition-2021-macros.rs create mode 100644 tests/ui/drop/auxiliary/edition-2024-macros.rs create mode 100644 tests/ui/drop/tail-expr-drop-order-negative.edition2024.stderr create mode 100644 tests/ui/drop/tail-expr-drop-order-negative.rs create mode 100644 tests/ui/drop/tail-expr-drop-order.rs create mode 100644 tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.rs create mode 100644 tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.stderr create mode 100644 tests/ui/lifetimes/refcell-in-tail-expr.edition2021.stderr create mode 100644 tests/ui/lifetimes/refcell-in-tail-expr.rs create mode 100644 tests/ui/lifetimes/shorter-tail-expr-lifetime.edition2021.stderr create mode 100644 tests/ui/lifetimes/shorter-tail-expr-lifetime.rs create mode 100644 tests/ui/lifetimes/tail-expr-in-nested-expr.rs create mode 100644 tests/ui/lifetimes/tail-expr-in-nested-expr.stderr create mode 100644 tests/ui/lifetimes/tail-expr-lock-poisoning.rs diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 77f95869e9da..79cff0fbcd2e 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1716,24 +1716,28 @@ impl<'hir> LoweringContext<'_, 'hir> { // `mut iter => { ... }` let iter_arm = self.arm(iter_pat, loop_expr); - let into_iter_expr = match loop_kind { + let match_expr = match loop_kind { ForLoopKind::For => { // `::std::iter::IntoIterator::into_iter()` - self.expr_call_lang_item_fn( + let into_iter_expr = self.expr_call_lang_item_fn( head_span, hir::LangItem::IntoIterIntoIter, arena_vec![self; head], - ) - } - // ` unsafe { Pin::new_unchecked(&mut into_async_iter()) }` - ForLoopKind::ForAwait => { - // `::core::async_iter::IntoAsyncIterator::into_async_iter()` - let iter = self.expr_call_lang_item_fn( - head_span, - hir::LangItem::IntoAsyncIterIntoIter, - arena_vec![self; head], ); - let iter = self.expr_mut_addr_of(head_span, iter); + + self.arena.alloc(self.expr_match( + for_span, + into_iter_expr, + arena_vec![self; iter_arm], + hir::MatchSource::ForLoopDesugar, + )) + } + // `match into_async_iter() { ref mut iter => match unsafe { Pin::new_unchecked(iter) } { ... } }` + ForLoopKind::ForAwait => { + let iter_ident = iter; + let (async_iter_pat, async_iter_pat_id) = + self.pat_ident_binding_mode(head_span, iter_ident, hir::BindingMode::REF_MUT); + let iter = self.expr_ident_mut(head_span, iter_ident, async_iter_pat_id); // `Pin::new_unchecked(...)` let iter = self.arena.alloc(self.expr_call_lang_item_fn_mut( head_span, @@ -1742,17 +1746,29 @@ impl<'hir> LoweringContext<'_, 'hir> { )); // `unsafe { ... }` let iter = self.arena.alloc(self.expr_unsafe(iter)); - iter + let inner_match_expr = self.arena.alloc(self.expr_match( + for_span, + iter, + arena_vec![self; iter_arm], + hir::MatchSource::ForLoopDesugar, + )); + + // `::core::async_iter::IntoAsyncIterator::into_async_iter()` + let iter = self.expr_call_lang_item_fn( + head_span, + hir::LangItem::IntoAsyncIterIntoIter, + arena_vec![self; head], + ); + let iter_arm = self.arm(async_iter_pat, inner_match_expr); + self.arena.alloc(self.expr_match( + for_span, + iter, + arena_vec![self; iter_arm], + hir::MatchSource::ForLoopDesugar, + )) } }; - let match_expr = self.arena.alloc(self.expr_match( - for_span, - into_iter_expr, - arena_vec![self; iter_arm], - hir::MatchSource::ForLoopDesugar, - )); - // This is effectively `{ let _result = ...; _result }`. // The construct was introduced in #21984 and is necessary to make sure that // temporaries in the `head` expression are dropped and do not leak to the diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 58832cb10875..77e72b5f4fb6 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -588,6 +588,8 @@ declare_features! ( (incomplete, return_type_notation, "1.70.0", Some(109417)), /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), + /// Shortern the tail expression lifetime + (unstable, shorter_tail_lifetimes, "1.79.0", Some(123739)), /// Allows the use of SIMD types in functions declared in `extern` blocks. (unstable, simd_ffi, "1.0.0", Some(27731)), /// Allows specialization of implementations (RFC 1210). diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 72e431926ca3..2b5efd3b2f6f 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -6,7 +6,6 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html -use rustc_ast::visit::visit_opt; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -168,7 +167,14 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement), } } - visit_opt!(visitor, visit_expr, &blk.expr); + if let Some(tail_expr) = blk.expr { + if visitor.tcx.features().shorter_tail_lifetimes + && blk.span.edition().at_least_rust_2024() + { + visitor.terminating_scopes.insert(tail_expr.hir_id.local_id); + } + visitor.visit_expr(tail_expr); + } } visitor.cx = prev_cx; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f44fa1bcb4fd..9fa8086c8fad 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1678,6 +1678,7 @@ symbols! { shadow_call_stack, shl, shl_assign, + shorter_tail_lifetimes, should_panic, shr, shr_assign, diff --git a/tests/ui/drop/auxiliary/edition-2021-macros.rs b/tests/ui/drop/auxiliary/edition-2021-macros.rs new file mode 100644 index 000000000000..8a6444f8614d --- /dev/null +++ b/tests/ui/drop/auxiliary/edition-2021-macros.rs @@ -0,0 +1,8 @@ +//@ edition:2021 + +#[macro_export] +macro_rules! edition_2021_block { + ($($c:tt)*) => { + { $($c)* } + } +} diff --git a/tests/ui/drop/auxiliary/edition-2024-macros.rs b/tests/ui/drop/auxiliary/edition-2024-macros.rs new file mode 100644 index 000000000000..236340bfed4f --- /dev/null +++ b/tests/ui/drop/auxiliary/edition-2024-macros.rs @@ -0,0 +1,9 @@ +//@ edition:2024 +//@ compile-flags: -Zunstable-options + +#[macro_export] +macro_rules! edition_2024_block { + ($($c:tt)*) => { + { $($c)* } + } +} diff --git a/tests/ui/drop/tail-expr-drop-order-negative.edition2024.stderr b/tests/ui/drop/tail-expr-drop-order-negative.edition2024.stderr new file mode 100644 index 000000000000..75fc34e409b5 --- /dev/null +++ b/tests/ui/drop/tail-expr-drop-order-negative.edition2024.stderr @@ -0,0 +1,16 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/tail-expr-drop-order-negative.rs:11:15 + | +LL | x.replace(std::cell::RefCell::new(123).borrow()).is_some() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement + | | + | creates a temporary value which is freed while still in use +LL | +LL | } + | - borrow might be used here, when `x` is dropped and runs the destructor for type `Option>` + | + = note: consider using a `let` binding to create a longer lived value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/drop/tail-expr-drop-order-negative.rs b/tests/ui/drop/tail-expr-drop-order-negative.rs new file mode 100644 index 000000000000..c570b3a1ee23 --- /dev/null +++ b/tests/ui/drop/tail-expr-drop-order-negative.rs @@ -0,0 +1,17 @@ +//@ revisions: edition2021 edition2024 +//@ [edition2024] compile-flags: -Zunstable-options +//@ [edition2024] edition: 2024 +//@ [edition2021] check-pass + +#![feature(shorter_tail_lifetimes)] + +fn why_would_you_do_this() -> bool { + let mut x = None; + // Make a temporary `RefCell` and put a `Ref` that borrows it in `x`. + x.replace(std::cell::RefCell::new(123).borrow()).is_some() + //[edition2024]~^ ERROR: temporary value dropped while borrowed +} + +fn main() { + why_would_you_do_this(); +} diff --git a/tests/ui/drop/tail-expr-drop-order.rs b/tests/ui/drop/tail-expr-drop-order.rs new file mode 100644 index 000000000000..5d87f980b156 --- /dev/null +++ b/tests/ui/drop/tail-expr-drop-order.rs @@ -0,0 +1,108 @@ +//@ aux-build:edition-2021-macros.rs +//@ aux-build:edition-2024-macros.rs +//@ compile-flags: -Z validate-mir -Zunstable-options +//@ edition: 2024 +//@ run-pass + +#![feature(shorter_tail_lifetimes)] +#![allow(unused_imports)] +#![allow(dead_code)] +#![allow(unused_variables)] + +#[macro_use] +extern crate edition_2021_macros; +#[macro_use] +extern crate edition_2024_macros; +use std::cell::RefCell; +use std::convert::TryInto; + +#[derive(Default)] +struct DropOrderCollector(RefCell>); + +struct LoudDrop<'a>(&'a DropOrderCollector, u32); + +impl Drop for LoudDrop<'_> { + fn drop(&mut self) { + println!("{}", self.1); + self.0.0.borrow_mut().push(self.1); + } +} + +impl DropOrderCollector { + fn option_loud_drop(&self, n: u32) -> Option { + Some(LoudDrop(self, n)) + } + + fn loud_drop(&self, n: u32) -> LoudDrop { + LoudDrop(self, n) + } + + fn assert_sorted(&self, expected: usize) { + let result = self.0.borrow(); + assert_eq!(result.len(), expected); + for i in 1..result.len() { + assert!( + result[i - 1] < result[i], + "inversion at {} ({} followed by {})", + i - 1, + result[i - 1], + result[i] + ); + } + } +} + +fn edition_2021_around_2021() { + let c = DropOrderCollector::default(); + let _ = edition_2021_block! { + let a = c.loud_drop(1); + edition_2021_block! { + let b = c.loud_drop(0); + c.loud_drop(2).1 + } + }; + c.assert_sorted(3); +} + +fn edition_2021_around_2024() { + let c = DropOrderCollector::default(); + let _ = edition_2021_block! { + let a = c.loud_drop(2); + edition_2024_block! { + let b = c.loud_drop(1); + c.loud_drop(0).1 + } + }; + c.assert_sorted(3); +} + +fn edition_2024_around_2021() { + let c = DropOrderCollector::default(); + let _ = edition_2024_block! { + let a = c.loud_drop(2); + edition_2021_block! { + let b = c.loud_drop(0); + c.loud_drop(1).1 + } + }; + c.assert_sorted(3); +} + +fn edition_2024_around_2024() { + let c = DropOrderCollector::default(); + let _ = edition_2024_block! { + let a = c.loud_drop(2); + edition_2024_block! { + let b = c.loud_drop(1); + c.loud_drop(0).1 + } + }; + c.assert_sorted(3); +} + +fn main() { + edition_2021_around_2021(); + edition_2021_around_2024(); + edition_2024_around_2021(); + edition_2024_around_2024(); +} diff --git a/tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.rs b/tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.rs new file mode 100644 index 000000000000..5292c44bb2d4 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.rs @@ -0,0 +1,8 @@ +fn f() -> usize { + let c = std::cell::RefCell::new(".."); + c.borrow().len() //~ ERROR: `c` does not live long enough +} + +fn main() { + let _ = f(); +} diff --git a/tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.stderr b/tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.stderr new file mode 100644 index 000000000000..648c3d5daa1c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.stderr @@ -0,0 +1,26 @@ +error[E0597]: `c` does not live long enough + --> $DIR/feature-gate-shorter_tail_lifetimes.rs:3:5 + | +LL | let c = std::cell::RefCell::new(".."); + | - binding `c` declared here +LL | c.borrow().len() + | ^--------- + | | + | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... +LL | } + | - + | | + | `c` dropped here while still borrowed + | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, &str>` + | + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | let x = c.borrow().len(); x + | +++++++ +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/lifetimes/refcell-in-tail-expr.edition2021.stderr b/tests/ui/lifetimes/refcell-in-tail-expr.edition2021.stderr new file mode 100644 index 000000000000..858be42d5409 --- /dev/null +++ b/tests/ui/lifetimes/refcell-in-tail-expr.edition2021.stderr @@ -0,0 +1,26 @@ +error[E0597]: `cell` does not live long enough + --> $DIR/refcell-in-tail-expr.rs:12:27 + | +LL | let cell = std::cell::RefCell::new(0u8); + | ---- binding `cell` declared here +LL | +LL | if let Ok(mut byte) = cell.try_borrow_mut() { + | ^^^^----------------- + | | + | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... +... +LL | } + | - + | | + | `cell` dropped here while still borrowed + | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Result, BorrowMutError>` + | +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | }; + | + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/lifetimes/refcell-in-tail-expr.rs b/tests/ui/lifetimes/refcell-in-tail-expr.rs new file mode 100644 index 000000000000..b1814c1e3271 --- /dev/null +++ b/tests/ui/lifetimes/refcell-in-tail-expr.rs @@ -0,0 +1,16 @@ +//@ revisions: edition2021 edition2024 +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 +//@ [edition2024] compile-flags: -Zunstable-options +//@ [edition2024] check-pass + +#![cfg_attr(edition2024, feature(shorter_tail_lifetimes))] + +fn main() { + let cell = std::cell::RefCell::new(0u8); + + if let Ok(mut byte) = cell.try_borrow_mut() { + //[edition2021]~^ ERROR: `cell` does not live long enough + *byte = 1; + } +} diff --git a/tests/ui/lifetimes/shorter-tail-expr-lifetime.edition2021.stderr b/tests/ui/lifetimes/shorter-tail-expr-lifetime.edition2021.stderr new file mode 100644 index 000000000000..ad28ae2f80d6 --- /dev/null +++ b/tests/ui/lifetimes/shorter-tail-expr-lifetime.edition2021.stderr @@ -0,0 +1,26 @@ +error[E0597]: `c` does not live long enough + --> $DIR/shorter-tail-expr-lifetime.rs:10:5 + | +LL | let c = std::cell::RefCell::new(".."); + | - binding `c` declared here +LL | c.borrow().len() + | ^--------- + | | + | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... +LL | } + | - + | | + | `c` dropped here while still borrowed + | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, &str>` + | + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | let x = c.borrow().len(); x + | +++++++ +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/lifetimes/shorter-tail-expr-lifetime.rs b/tests/ui/lifetimes/shorter-tail-expr-lifetime.rs new file mode 100644 index 000000000000..0392b6c6d9ad --- /dev/null +++ b/tests/ui/lifetimes/shorter-tail-expr-lifetime.rs @@ -0,0 +1,15 @@ +//@ revisions: edition2021 edition2024 +//@ [edition2024] compile-flags: -Zunstable-options +//@ [edition2024] edition: 2024 +//@ [edition2024] run-pass + +#![cfg_attr(edition2024, feature(shorter_tail_lifetimes))] + +fn f() -> usize { + let c = std::cell::RefCell::new(".."); + c.borrow().len() //[edition2021]~ ERROR: `c` does not live long enough +} + +fn main() { + let _ = f(); +} diff --git a/tests/ui/lifetimes/tail-expr-in-nested-expr.rs b/tests/ui/lifetimes/tail-expr-in-nested-expr.rs new file mode 100644 index 000000000000..a8989f22f4b5 --- /dev/null +++ b/tests/ui/lifetimes/tail-expr-in-nested-expr.rs @@ -0,0 +1,9 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options + +#![feature(shorter_tail_lifetimes)] + +fn main() { + let _ = { String::new().as_str() }.len(); + //~^ ERROR temporary value dropped while borrowed +} diff --git a/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr b/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr new file mode 100644 index 000000000000..f699d184bdb1 --- /dev/null +++ b/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr @@ -0,0 +1,15 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/tail-expr-in-nested-expr.rs:7:15 + | +LL | let _ = { String::new().as_str() }.len(); + | ^^^^^^^^^^^^^--------- + | | | + | | temporary value is freed at the end of this statement + | creates a temporary value which is freed while still in use + | borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/lifetimes/tail-expr-lock-poisoning.rs b/tests/ui/lifetimes/tail-expr-lock-poisoning.rs new file mode 100644 index 000000000000..69b8f286d774 --- /dev/null +++ b/tests/ui/lifetimes/tail-expr-lock-poisoning.rs @@ -0,0 +1,29 @@ +//@ revisions: edition2021 edition2024 +//@ ignore-wasm no panic or subprocess support +//@ [edition2024] compile-flags: -Zunstable-options +//@ [edition2024] edition: 2024 +//@ run-pass +#![cfg_attr(edition2024, feature(shorter_tail_lifetimes))] + +use std::sync::Mutex; + +struct PanicOnDrop; +impl Drop for PanicOnDrop { + fn drop(&mut self) { + panic!() + } +} + +fn f(m: &Mutex) -> i32 { + let _x = PanicOnDrop; + *m.lock().unwrap() +} + +fn main() { + let m = Mutex::new(0); + let _ = std::panic::catch_unwind(|| f(&m)); + #[cfg(edition2024)] + assert!(m.lock().is_ok()); + #[cfg(edition2021)] + assert!(m.lock().is_err()); +} diff --git a/tests/ui/nll/issue-52534-1.rs b/tests/ui/nll/issue-52534-1.rs index d9ea3ae42c49..526b81bb2d05 100644 --- a/tests/ui/nll/issue-52534-1.rs +++ b/tests/ui/nll/issue-52534-1.rs @@ -17,14 +17,14 @@ fn foo(x: &u32) -> &u32 { fn baz(x: &u32) -> &&u32 { let x = 22; &&x -//~^ ERROR cannot return value referencing local variable +//~^ ERROR cannot return value referencing local variable `x` //~| ERROR cannot return reference to temporary value } fn foobazbar<'a>(x: u32, y: &'a u32) -> &'a u32 { let x = 22; &x -//~^ ERROR cannot return reference to local variable +//~^ ERROR cannot return reference to local variable `x` } fn foobar<'a>(x: &'a u32) -> &'a u32 { From b7deff3a0de56941b554e6a116f08c697e59ee51 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Mon, 17 Jun 2024 22:32:01 +0200 Subject: [PATCH 035/147] Add missing CopyMarker impl Due to refactoring the const_trait usage, the CopyMarker impl was accidentally deleted, which had the consequence that the Copy specialization for the small-sort was never picked. --- library/core/src/slice/sort/shared/smallsort.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 567a7090ecd7..5111ed8756bf 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -140,6 +140,8 @@ impl UnstableSmallSortFreezeTypeImpl for T { #[rustc_unsafe_specialization_marker] trait CopyMarker {} +impl CopyMarker for T {} + impl UnstableSmallSortFreezeTypeImpl for T { #[inline(always)] fn small_sort_threshold() -> usize { From c4ddc863ae168dcba9afa3fe4cc83a4f4a78359f Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 17 Jun 2024 19:19:41 -0500 Subject: [PATCH 036/147] Print the tested value in int_log tests --- library/core/tests/num/int_log.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/library/core/tests/num/int_log.rs b/library/core/tests/num/int_log.rs index a1edb1a51863..2320a7acc35a 100644 --- a/library/core/tests/num/int_log.rs +++ b/library/core/tests/num/int_log.rs @@ -24,15 +24,15 @@ fn checked_ilog() { #[cfg(not(miri))] // Miri is too slow for i in i16::MIN..=0 { - assert_eq!(i.checked_ilog(4), None); + assert_eq!(i.checked_ilog(4), None, "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=i16::MAX { - assert_eq!(i.checked_ilog(13), Some((i as f32).log(13.0) as u32)); + assert_eq!(i.checked_ilog(13), Some((i as f32).log(13.0) as u32), "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=u16::MAX { - assert_eq!(i.checked_ilog(13), Some((i as f32).log(13.0) as u32)); + assert_eq!(i.checked_ilog(13), Some((i as f32).log(13.0) as u32), "checking {i}"); } } @@ -49,30 +49,30 @@ fn checked_ilog2() { assert_eq!(0i16.checked_ilog2(), None); for i in 1..=u8::MAX { - assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32)); + assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=u16::MAX { // Guard against Android's imprecise f32::ilog2 implementation. if i != 8192 && i != 32768 { - assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32)); + assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}"); } } for i in i8::MIN..=0 { - assert_eq!(i.checked_ilog2(), None); + assert_eq!(i.checked_ilog2(), None, "checking {i}"); } for i in 1..=i8::MAX { - assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32)); + assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in i16::MIN..=0 { - assert_eq!(i.checked_ilog2(), None); + assert_eq!(i.checked_ilog2(), None, "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=i16::MAX { // Guard against Android's imprecise f32::ilog2 implementation. if i != 8192 { - assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32)); + assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}"); } } } @@ -95,19 +95,19 @@ fn checked_ilog10() { #[cfg(not(miri))] // Miri is too slow for i in i16::MIN..=0 { - assert_eq!(i.checked_ilog10(), None); + assert_eq!(i.checked_ilog10(), None, "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=i16::MAX { - assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32)); + assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32), "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=u16::MAX { - assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32)); + assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32), "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=100_000u32 { - assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32)); + assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32), "checking {i}"); } } From 475b63fa283efc4b5aafd8ddfde69229204dd059 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 17 Jun 2024 22:56:12 -0500 Subject: [PATCH 037/147] Add `/rustc-ice*/ to `.gitignore` --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 485968d9c56f..87d02563ed04 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,7 @@ build/ /src/tools/x/target # Created by default with `src/ci/docker/run.sh` /obj/ +/rustc-ice* ## Temporary files *~ From af10880f6b4a56e5f9a3c458878772f8eda19544 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Mon, 17 Jun 2024 17:00:45 +0300 Subject: [PATCH 038/147] Make async drop code more consistent with regular drop code Fixes #126573 --- .../src/back/symbol_export.rs | 19 ++++++++--- compiler/rustc_middle/src/query/mod.rs | 33 +++++++++++++++---- compiler/rustc_middle/src/ty/instance.rs | 28 ++++++++++++++-- .../cfi/typeid/itanium_cxx_abi/transform.rs | 1 + 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 1d61c1564091..c868904c5511 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -371,7 +371,7 @@ fn exported_symbols_provider_local( }) => { // A little sanity-check debug_assert_eq!( - args.non_erasable_generics(tcx, def_id).skip(1).next(), + args.non_erasable_generics(tcx, def_id).next(), Some(GenericArgKind::Type(ty)) ); symbols.push(( @@ -422,10 +422,7 @@ fn upstream_monomorphizations_provider( } ExportedSymbol::AsyncDropGlueCtorShim(ty) => { if let Some(async_drop_in_place_fn_def_id) = async_drop_in_place_fn_def_id { - ( - async_drop_in_place_fn_def_id, - tcx.mk_args(&[tcx.lifetimes.re_erased.into(), ty.into()]), - ) + (async_drop_in_place_fn_def_id, tcx.mk_args(&[ty.into()])) } else { // `drop_in_place` in place does not exist, don't try // to use it. @@ -480,6 +477,17 @@ fn upstream_drop_glue_for_provider<'tcx>( } } +fn upstream_async_drop_glue_for_provider<'tcx>( + tcx: TyCtxt<'tcx>, + args: GenericArgsRef<'tcx>, +) -> Option { + if let Some(def_id) = tcx.lang_items().async_drop_in_place_fn() { + tcx.upstream_monomorphizations_for(def_id).and_then(|monos| monos.get(&args).cloned()) + } else { + None + } +} + fn is_unreachable_local_definition_provider(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { !tcx.reachable_set(()).contains(&def_id) } @@ -491,6 +499,7 @@ pub fn provide(providers: &mut Providers) { providers.upstream_monomorphizations = upstream_monomorphizations_provider; providers.is_unreachable_local_definition = is_unreachable_local_definition_provider; providers.upstream_drop_glue_for = upstream_drop_glue_for_provider; + providers.upstream_async_drop_glue_for = upstream_async_drop_glue_for_provider; providers.wasm_import_module_map = wasm_import_module_map; providers.extern_queries.is_reachable_non_generic = is_reachable_non_generic_provider_extern; providers.extern_queries.upstream_monomorphizations_for = diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0f08694c4eab..c5afecffb07a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1554,12 +1554,13 @@ rustc_queries! { } } - /// The entire set of monomorphizations the local crate can safely link - /// to because they are exported from upstream crates. Do not depend on - /// this directly, as its value changes anytime a monomorphization gets - /// added or removed in any upstream crate. Instead use the narrower - /// `upstream_monomorphizations_for`, `upstream_drop_glue_for`, or, even - /// better, `Instance::upstream_monomorphization()`. + /// The entire set of monomorphizations the local crate can safely + /// link to because they are exported from upstream crates. Do + /// not depend on this directly, as its value changes anytime + /// a monomorphization gets added or removed in any upstream + /// crate. Instead use the narrower `upstream_monomorphizations_for`, + /// `upstream_drop_glue_for`, `upstream_async_drop_glue_for`, or, + /// even better, `Instance::upstream_monomorphization()`. query upstream_monomorphizations(_: ()) -> &'tcx DefIdMap, CrateNum>> { arena_cache desc { "collecting available upstream monomorphizations" } @@ -1601,6 +1602,26 @@ rustc_queries! { desc { "available upstream drop-glue for `{:?}`", args } } + /// Returns the upstream crate that exports async-drop-glue for + /// the given type (`args` is expected to be a single-item list + /// containing the type one wants async-drop-glue for). + /// + /// This is a subset of `upstream_monomorphizations_for` in order + /// to increase dep-tracking granularity. Otherwise adding or + /// removing any type with async-drop-glue in any upstream crate + /// would invalidate all functions calling async-drop-glue of an + /// upstream type. + /// + /// You likely want to call `Instance::upstream_monomorphization()` + /// instead of invoking this query directly. + /// + /// NOTE: This query could easily be extended to also support other + /// common functions that have are large set of monomorphizations + /// (like `Clone::clone` for example). + query upstream_async_drop_glue_for(args: GenericArgsRef<'tcx>) -> Option { + desc { "available upstream async-drop-glue for `{:?}`", args } + } + /// Returns a list of all `extern` blocks of a crate. query foreign_modules(_: CrateNum) -> &'tcx FxIndexMap { arena_cache diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 5718264c9446..099b14cca9ce 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -219,8 +219,9 @@ impl<'tcx> Instance<'tcx> { InstanceKind::Item(def) => tcx .upstream_monomorphizations_for(def) .and_then(|monos| monos.get(&self.args).cloned()), - InstanceKind::DropGlue(_, Some(_)) | InstanceKind::AsyncDropGlueCtorShim(_, _) => { - tcx.upstream_drop_glue_for(self.args) + InstanceKind::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.args), + InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => { + tcx.upstream_async_drop_glue_for(self.args) } _ => None, } @@ -256,7 +257,7 @@ impl<'tcx> InstanceKind<'tcx> { match self { ty::InstanceKind::Item(def) => Some(def), ty::InstanceKind::DropGlue(def_id, Some(_)) - | InstanceKind::AsyncDropGlueCtorShim(def_id, _) + | InstanceKind::AsyncDropGlueCtorShim(def_id, Some(_)) | InstanceKind::ThreadLocalShim(def_id) => Some(def_id), InstanceKind::VTableShim(..) | InstanceKind::ReifyShim(..) @@ -267,6 +268,7 @@ impl<'tcx> InstanceKind<'tcx> { | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } | ty::InstanceKind::CoroutineKindShim { .. } | InstanceKind::DropGlue(..) + | InstanceKind::AsyncDropGlueCtorShim(..) | InstanceKind::CloneShim(..) | InstanceKind::FnPtrAddrShim(..) => None, } @@ -332,6 +334,26 @@ impl<'tcx> InstanceKind<'tcx> { .map_or_else(|| adt_def.is_enum(), |dtor| tcx.cross_crate_inlinable(dtor.did)) }); } + if let ty::InstanceKind::AsyncDropGlueCtorShim(.., Some(ty)) = *self { + // Async drop glue generally wants to be instantiated at + // every codegen unit, but without an #[inline] hint. We + // should make this available to normal end-users. + if tcx.sess.opts.incremental.is_none() { + return true; + } + // When compiling with incremental, we can generate a *lot* of + // codegen units. Including drop glue into all of them has a + // considerable compile time cost. + // + // We include enums without destructors to allow, say, optimizing + // drops of `Option::None` before LTO. We also respect the intent of + // `#[inline]` on `Drop::drop` implementations. + return ty.ty_adt_def().map_or(true, |adt_def| { + adt_def + .async_destructor(tcx) + .map_or_else(|| adt_def.is_enum(), |dtor| tcx.cross_crate_inlinable(dtor.ctor)) + }); + } if let ty::InstanceKind::ThreadLocalShim(..) = *self { return false; } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 832efb119992..742ec4c377cc 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -288,6 +288,7 @@ pub fn transform_instance<'tcx>( mut instance: Instance<'tcx>, options: TransformTyOptions, ) -> Instance<'tcx> { + // FIXME: account for async-drop-glue if (matches!(instance.def, ty::InstanceKind::Virtual(..)) && tcx.is_lang_item(instance.def_id(), LangItem::DropInPlace)) || matches!(instance.def, ty::InstanceKind::DropGlue(..)) From 1a8eae1aba998af1b0fdc597c0e263d4d391b169 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Tue, 18 Jun 2024 14:28:00 +0300 Subject: [PATCH 039/147] Apply suggestions from oli-obk's review Co-authored-by: Oli Scherer --- .../src/back/symbol_export.rs | 14 +++----- compiler/rustc_middle/src/ty/instance.rs | 35 ++++++------------- 2 files changed, 15 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index c868904c5511..6abe4fa1c380 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -470,22 +470,16 @@ fn upstream_drop_glue_for_provider<'tcx>( tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>, ) -> Option { - if let Some(def_id) = tcx.lang_items().drop_in_place_fn() { - tcx.upstream_monomorphizations_for(def_id).and_then(|monos| monos.get(&args).cloned()) - } else { - None - } + let def_id = tcx.lang_items().drop_in_place_fn()?; + tcx.upstream_monomorphizations_for(def_id)?.get(&args).cloned() } fn upstream_async_drop_glue_for_provider<'tcx>( tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>, ) -> Option { - if let Some(def_id) = tcx.lang_items().async_drop_in_place_fn() { - tcx.upstream_monomorphizations_for(def_id).and_then(|monos| monos.get(&args).cloned()) - } else { - None - } + let def_id = tcx.lang_items().async_drop_in_place_fn()?; + tcx.upstream_monomorphizations_for(def_id)?.get(&args).cloned() } fn is_unreachable_local_definition_provider(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 099b14cca9ce..efaf9c7231bb 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -314,7 +314,9 @@ impl<'tcx> InstanceKind<'tcx> { if self.requires_inline(tcx) { return true; } - if let ty::InstanceKind::DropGlue(.., Some(ty)) = *self { + if let ty::InstanceKind::DropGlue(.., Some(ty)) + | ty::InstanceKind::AsyncDropGlueCtorShim(.., Some(ty)) = *self + { // Drop glue generally wants to be instantiated at every codegen // unit, but without an #[inline] hint. We should make this // available to normal end-users. @@ -329,29 +331,14 @@ impl<'tcx> InstanceKind<'tcx> { // drops of `Option::None` before LTO. We also respect the intent of // `#[inline]` on `Drop::drop` implementations. return ty.ty_adt_def().map_or(true, |adt_def| { - adt_def - .destructor(tcx) - .map_or_else(|| adt_def.is_enum(), |dtor| tcx.cross_crate_inlinable(dtor.did)) - }); - } - if let ty::InstanceKind::AsyncDropGlueCtorShim(.., Some(ty)) = *self { - // Async drop glue generally wants to be instantiated at - // every codegen unit, but without an #[inline] hint. We - // should make this available to normal end-users. - if tcx.sess.opts.incremental.is_none() { - return true; - } - // When compiling with incremental, we can generate a *lot* of - // codegen units. Including drop glue into all of them has a - // considerable compile time cost. - // - // We include enums without destructors to allow, say, optimizing - // drops of `Option::None` before LTO. We also respect the intent of - // `#[inline]` on `Drop::drop` implementations. - return ty.ty_adt_def().map_or(true, |adt_def| { - adt_def - .async_destructor(tcx) - .map_or_else(|| adt_def.is_enum(), |dtor| tcx.cross_crate_inlinable(dtor.ctor)) + match *self { + ty::InstanceKind::DropGlue(..) => adt_def.destructor(tcx).map(|dtor| dtor.did), + ty::InstanceKind::AsyncDropGlueCtorShim(..) => { + adt_def.async_destructor(tcx).map(|dtor| dtor.ctor) + } + _ => unreachable!(), + } + .map_or_else(|| adt_def.is_enum(), |did| tcx.cross_crate_inlinable(did)) }); } if let ty::InstanceKind::ThreadLocalShim(..) = *self { From 7321e791141ddf2ec4d5f8a9893146832118bb19 Mon Sep 17 00:00:00 2001 From: Vonr Date: Tue, 18 Jun 2024 23:25:08 +0800 Subject: [PATCH 040/147] Replace `move||` with `move ||` in `compiler/` and `library/` Edit from #126631 to revert changes on ui tests --- .../rustc_hir_typeck/src/typeck_root_ctxt.rs | 2 +- library/core/src/sync/atomic.rs | 2 +- library/std/src/sync/barrier.rs | 4 ++-- library/std/src/sync/condvar.rs | 16 ++++++++-------- library/std/src/sync/mpsc/mod.rs | 10 +++++----- library/std/src/thread/local.rs | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index 28745af3a530..b6e9000ef950 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -22,7 +22,7 @@ use std::ops::Deref; /// e.g. closures defined within the function. For example: /// ```ignore (illustrative) /// fn foo() { -/// bar(move|| { ... }) +/// bar(move || { ... }) /// } /// ``` /// Here, the function `foo()` and the closure passed to diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 482bd19705c2..c709ea2a15db 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -183,7 +183,7 @@ //! //! let spinlock_clone = Arc::clone(&spinlock); //! -//! let thread = thread::spawn(move|| { +//! let thread = thread::spawn(move || { //! spinlock_clone.store(0, Ordering::Release); //! }); //! diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index b4bac081e7ab..82cc13a74b7f 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -20,7 +20,7 @@ use crate::sync::{Condvar, Mutex}; /// let c = Arc::clone(&barrier); /// // The same messages will be printed together. /// // You will NOT see any interleaving. -/// handles.push(thread::spawn(move|| { +/// handles.push(thread::spawn(move || { /// println!("before wait"); /// c.wait(); /// println!("after wait"); @@ -115,7 +115,7 @@ impl Barrier { /// let c = Arc::clone(&barrier); /// // The same messages will be printed together. /// // You will NOT see any interleaving. - /// handles.push(thread::spawn(move|| { + /// handles.push(thread::spawn(move || { /// println!("before wait"); /// c.wait(); /// println!("after wait"); diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs index b20574e4f149..f9f83fb4f63c 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/condvar.rs @@ -88,7 +88,7 @@ impl WaitTimeoutResult { /// let pair2 = Arc::clone(&pair); /// /// // Inside of our lock, spawn a new thread, and then wait for it to start. -/// thread::spawn(move|| { +/// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -166,7 +166,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -221,7 +221,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(true), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut pending = lock.lock().unwrap(); /// *pending = false; @@ -280,7 +280,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -352,7 +352,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -420,7 +420,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(true), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut pending = lock.lock().unwrap(); /// *pending = false; @@ -484,7 +484,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -524,7 +524,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index d353c7bd5de9..feee6948db4f 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -51,7 +51,7 @@ //! //! // Create a simple streaming channel //! let (tx, rx) = channel(); -//! thread::spawn(move|| { +//! thread::spawn(move || { //! tx.send(10).unwrap(); //! }); //! assert_eq!(rx.recv().unwrap(), 10); @@ -69,7 +69,7 @@ //! let (tx, rx) = channel(); //! for i in 0..10 { //! let tx = tx.clone(); -//! thread::spawn(move|| { +//! thread::spawn(move || { //! tx.send(i).unwrap(); //! }); //! } @@ -99,7 +99,7 @@ //! use std::sync::mpsc::sync_channel; //! //! let (tx, rx) = sync_channel::(0); -//! thread::spawn(move|| { +//! thread::spawn(move || { //! // This will wait for the parent thread to start receiving //! tx.send(53).unwrap(); //! }); @@ -510,7 +510,7 @@ pub enum TrySendError { /// let (sender, receiver) = channel(); /// /// // Spawn off an expensive computation -/// thread::spawn(move|| { +/// thread::spawn(move || { /// # fn expensive_computation() {} /// sender.send(expensive_computation()).unwrap(); /// }); @@ -561,7 +561,7 @@ pub fn channel() -> (Sender, Receiver) { /// // this returns immediately /// sender.send(1).unwrap(); /// -/// thread::spawn(move|| { +/// thread::spawn(move || { /// // this will block until the previous message has been received /// sender.send(2).unwrap(); /// }); diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index aed185637fd1..f147c5fdcd14 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -62,7 +62,7 @@ use crate::fmt; /// FOO.set(2); /// /// // each thread starts out with the initial value of 1 -/// let t = thread::spawn(move|| { +/// let t = thread::spawn(move || { /// assert_eq!(FOO.get(), 1); /// FOO.set(3); /// }); From de473a5a2b7e15aa45e4c2b0d070c22f7140316a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 11 Apr 2024 08:56:10 +0000 Subject: [PATCH 041/147] Test that opaque types can't have themselves as a hidden type with incompatible lifetimes --- .../different_args_considered_equal.rs | 14 ++++++++++++ .../different_args_considered_equal.stderr | 15 +++++++++++++ .../different_args_considered_equal2.rs | 14 ++++++++++++ .../different_args_considered_equal2.stderr | 20 +++++++++++++++++ .../different_args_considered_equal3.rs | 22 +++++++++++++++++++ .../different_args_considered_equal3.stderr | 10 +++++++++ 6 files changed, 95 insertions(+) create mode 100644 tests/ui/type-alias-impl-trait/different_args_considered_equal.rs create mode 100644 tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr create mode 100644 tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs create mode 100644 tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr create mode 100644 tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs create mode 100644 tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal.rs b/tests/ui/type-alias-impl-trait/different_args_considered_equal.rs new file mode 100644 index 000000000000..8ce471e39568 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal.rs @@ -0,0 +1,14 @@ +#![feature(type_alias_impl_trait)] + +pub type Opaque<'a> = impl Sized; + +fn get_one<'a>(a: *mut &'a str) -> Opaque<'a> { + a +} + +fn get_iter<'a>() -> impl IntoIterator> { + //~^ ERROR: item does not constrain + None::> +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr b/tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr new file mode 100644 index 000000000000..f27f22345252 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr @@ -0,0 +1,15 @@ +error: item does not constrain `Opaque::{opaque#0}`, but has it in its signature + --> $DIR/different_args_considered_equal.rs:9:4 + | +LL | fn get_iter<'a>() -> impl IntoIterator> { + | ^^^^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/different_args_considered_equal.rs:3:23 + | +LL | pub type Opaque<'a> = impl Sized; + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs new file mode 100644 index 000000000000..43dfea97e6db --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs @@ -0,0 +1,14 @@ +#![feature(type_alias_impl_trait)] + +pub type Opaque<'a> = impl Sized; + +fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator> { + if a.is_null() { + Some(a) + } else { + None::> + //~^ ERROR hidden type for `Opaque<'static>` captures lifetime that does not appear in bounds + } +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr new file mode 100644 index 000000000000..1104c2c498a5 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr @@ -0,0 +1,20 @@ +error[E0700]: hidden type for `Opaque<'static>` captures lifetime that does not appear in bounds + --> $DIR/different_args_considered_equal2.rs:9:9 + | +LL | pub type Opaque<'a> = impl Sized; + | ---------- opaque type defined here +LL | +LL | fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator> { + | -- hidden type `*mut &'a str` captures the lifetime `'a` as defined here +... +LL | None::> + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to declare that `impl IntoIterator>` captures `'a`, you can add an explicit `'a` lifetime bound + | +LL | fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator> + 'a { + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs new file mode 100644 index 000000000000..ea69175ba310 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs @@ -0,0 +1,22 @@ +//! Test that we don't allow coercing an opaque type with a non-static +//! lifetime to one with a static lifetime. While `get_iter` looks like +//! it would be doing the opposite, the way we're handling projections +//! makes `Opaque<'a>` the hidden type of `Opaque<'static>`. + +#![feature(type_alias_impl_trait)] + +mod defining_scope { + pub type Opaque<'a> = impl Sized; + + fn get_one<'a>(a: *mut &'a str) -> Opaque<'a> { + a + } +} +use defining_scope::Opaque; + +fn get_iter<'a>() -> impl IntoIterator> { + None::> + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr new file mode 100644 index 000000000000..d8f70e3d7782 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/different_args_considered_equal3.rs:18:5 + | +LL | fn get_iter<'a>() -> impl IntoIterator> { + | -- lifetime `'a` defined here +LL | None::> + | ^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to 1 previous error + From a183989e88f2154770de1afd860bffb387c4b525 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 17 Jun 2024 08:15:43 +0000 Subject: [PATCH 042/147] Only check locally for reported errors --- compiler/rustc_hir_typeck/src/writeback.rs | 2 +- ...dings-in-pattern-with-ty-err-doesnt-ice.rs | 1 + ...s-in-pattern-with-ty-err-doesnt-ice.stderr | 15 +++++++++-- tests/ui/wf/conflicting-impls.rs | 2 ++ tests/ui/wf/conflicting-impls.stderr | 27 ++++++++++++++++--- 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index b67d29fce921..2714be1f9b4f 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -793,7 +793,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { } fn report_error(&self, p: impl Into>) -> ErrorGuaranteed { - if let Some(guar) = self.fcx.dcx().has_errors() { + if let Some(guar) = self.fcx.tainted_by_errors() { guar } else { self.fcx diff --git a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs index 196da30b8647..d0bf5078165b 100644 --- a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs +++ b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs @@ -4,4 +4,5 @@ fn main() { //~| ERROR cannot find type `T` in this scope //~| ERROR const and type arguments are not allowed on builtin type `str` //~| ERROR expected unit struct, unit variant or constant, found associated function `str< +//~| ERROR type annotations needed } diff --git a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr index 99f8dbd9a6c8..fc431eb14127 100644 --- a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr +++ b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr @@ -32,7 +32,18 @@ error[E0533]: expected unit struct, unit variant or constant, found associated f LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a unit struct, unit variant or constant -error: aborting due to 4 previous errors +error[E0282]: type annotations needed + --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:31 + | +LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; + | ^^^^^^^^^^^^^^^^^^ + | +help: consider giving this pattern a type + | +LL | let str::<{fn str() { let str::T>>::as_bytes: /* Type */; }}, T>::as_bytes; + | ++++++++++++ -Some errors have detailed explanations: E0109, E0412, E0533. +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0109, E0282, E0412, E0533. For more information about an error, try `rustc --explain E0109`. diff --git a/tests/ui/wf/conflicting-impls.rs b/tests/ui/wf/conflicting-impls.rs index 8054eb7c5943..c387199a8bf1 100644 --- a/tests/ui/wf/conflicting-impls.rs +++ b/tests/ui/wf/conflicting-impls.rs @@ -5,6 +5,7 @@ struct Ty; impl TryFrom for u8 { type Error = Ty; fn try_from(_: Ty) -> Result { + //~^ ERROR type annotations needed loop {} } } @@ -13,6 +14,7 @@ impl TryFrom for u8 { //~^ ERROR conflicting implementations of trait type Error = Ty; fn try_from(_: Ty) -> Result { + //~^ ERROR type annotations needed loop {} } } diff --git a/tests/ui/wf/conflicting-impls.stderr b/tests/ui/wf/conflicting-impls.stderr index d31ae17aa8f7..15222a9b2ccd 100644 --- a/tests/ui/wf/conflicting-impls.stderr +++ b/tests/ui/wf/conflicting-impls.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `TryFrom` for type `u8` - --> $DIR/conflicting-impls.rs:12:1 + --> $DIR/conflicting-impls.rs:13:1 | LL | impl TryFrom for u8 { | ----------------------- first implementation here @@ -7,6 +7,27 @@ LL | impl TryFrom for u8 { LL | impl TryFrom for u8 { | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u8` -error: aborting due to 1 previous error +error[E0282]: type annotations needed + --> $DIR/conflicting-impls.rs:7:53 + | +LL | fn try_from(_: Ty) -> Result { + | _____________________________________________________^ +LL | | +LL | | loop {} +LL | | } + | |_____^ cannot infer type for enum `Result` -For more information about this error, try `rustc --explain E0119`. +error[E0282]: type annotations needed + --> $DIR/conflicting-impls.rs:16:53 + | +LL | fn try_from(_: Ty) -> Result { + | _____________________________________________________^ +LL | | +LL | | loop {} +LL | | } + | |_____^ cannot infer type for enum `Result` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0119, E0282. +For more information about an error, try `rustc --explain E0119`. From fa2b6122135ad18640e2e9b47450f50fa873c54a Mon Sep 17 00:00:00 2001 From: Oneirical Date: Thu, 6 Jun 2024 11:55:24 -0400 Subject: [PATCH 043/147] Rewrite link-args-order to rmake --- src/doc/book | 2 +- src/tools/cargo | 2 +- src/tools/run-make-support/src/rustc.rs | 24 ++++++++++++ .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/link-args-order/Makefile | 10 ----- tests/run-make/link-args-order/rmake.rs | 39 +++++++++++++++++++ 6 files changed, 65 insertions(+), 13 deletions(-) delete mode 100644 tests/run-make/link-args-order/Makefile create mode 100644 tests/run-make/link-args-order/rmake.rs diff --git a/src/doc/book b/src/doc/book index 45c1a6d69edf..5228bfac8267 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 45c1a6d69edfd1fc91fb7504cb73958dbd09441e +Subproject commit 5228bfac8267ad24659a81b92ec5417976b5edbc diff --git a/src/tools/cargo b/src/tools/cargo index a1f47ec3f7cd..4dcbca118ab7 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit a1f47ec3f7cd076986f1bfcd7061f2e8cb1a726e +Subproject commit 4dcbca118ab7f9ffac4728004c983754bc6a04ff diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index 331e1a5ab8b3..7e5a637e20e1 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -230,6 +230,24 @@ impl Rustc { self } + /// Add multiple extra arguments to the linker invocation, via `-Clink-args`. + pub fn link_args(&mut self, link_args: &str) -> &mut Self { + self.cmd.arg(format!("-Clink-args={link_args}")); + self + } + + /// Add an extra argument to prepend the linker invocation, via `-Zpre-link-arg`. + pub fn pre_link_arg(&mut self, link_arg: &str) -> &mut Self { + self.cmd.arg(format!("-Zpre-link-arg={link_arg}")); + self + } + + /// Add multiple extra arguments to the linker invocation, via `-Zpre-link-args`. + pub fn pre_link_args(&mut self, link_args: &str) -> &mut Self { + self.cmd.arg(format!("-Zpre-link-args={link_args}")); + self + } + /// Specify a stdin input pub fn stdin>(&mut self, input: I) -> &mut Self { self.cmd.set_stdin(input.as_ref().to_vec().into_boxed_slice()); @@ -248,4 +266,10 @@ impl Rustc { self.cmd.arg(format!("-Clinker={linker}")); self } + + /// Specify the linker flavor + pub fn linker_flavor(&mut self, linker_flavor: &str) -> &mut Self { + self.cmd.arg(format!("-Clinker-flavor={linker_flavor}")); + self + } } diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 1596257747fa..feb31e25e5f8 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -100,7 +100,6 @@ run-make/libtest-json/Makefile run-make/libtest-junit/Makefile run-make/libtest-padding/Makefile run-make/libtest-thread-limit/Makefile -run-make/link-args-order/Makefile run-make/link-cfg/Makefile run-make/link-framework/Makefile run-make/link-path-order/Makefile diff --git a/tests/run-make/link-args-order/Makefile b/tests/run-make/link-args-order/Makefile deleted file mode 100644 index c562cc1b396f..000000000000 --- a/tests/run-make/link-args-order/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# ignore-msvc - -include ../tools.mk - -RUSTC_FLAGS = -C linker-flavor=ld -C link-arg=a -C link-args="b c" -C link-args="d e" -C link-arg=f -RUSTC_FLAGS_PRE = -C linker-flavor=ld -Z pre-link-arg=a -Z pre-link-args="b c" -Z pre-link-args="d e" -Z pre-link-arg=f - -all: - $(RUSTC) $(RUSTC_FLAGS) empty.rs 2>&1 | $(CGREP) '"a" "b" "c" "d" "e" "f"' - $(RUSTC) $(RUSTC_FLAGS_PRE) empty.rs 2>&1 | $(CGREP) '"a" "b" "c" "d" "e" "f"' diff --git a/tests/run-make/link-args-order/rmake.rs b/tests/run-make/link-args-order/rmake.rs new file mode 100644 index 000000000000..cd921a031292 --- /dev/null +++ b/tests/run-make/link-args-order/rmake.rs @@ -0,0 +1,39 @@ +// Passing linker arguments to the compiler used to be lost or reordered in a messy way +// as they were passed further to the linker. This was fixed in #70665, and this test +// checks that linker arguments remain intact and in the order they were originally passed in. +// See https://github.com/rust-lang/rust/pull/70665 + +use run_make_support::rustc; + +fn main() { + assert!( + String::from_utf8( + rustc() + .input("empty.rs") + .linker_flavor("ld") + .link_arg("a") + .link_args("\"b c\"") + .link_args("\"d e\"") + .link_arg("f") + .run_fail() + .stderr + ) + .unwrap() + .contains("\"a\" \"b\" \"c\" \"d\" \"e\" \"f\"") + ); + assert!( + String::from_utf8( + rustc() + .input("empty.rs") + .linker_flavor("ld") + .pre_link_arg("a") + .pre_link_args("\"b c\"") + .pre_link_args("\"d e\"") + .pre_link_arg("f") + .run_fail() + .stderr + ) + .unwrap() + .contains("\"a\" \"b\" \"c\" \"d\" \"e\" \"f\"") + ); +} From 594135ea370085bfd80ce56f7f08b146cb97f3ca Mon Sep 17 00:00:00 2001 From: Oneirical Date: Thu, 6 Jun 2024 14:51:55 -0400 Subject: [PATCH 044/147] Rewrite ls-metadata to rmake --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/ls-metadata/Makefile | 8 -------- tests/run-make/ls-metadata/rmake.rs | 17 +++++++++++++++++ 3 files changed, 17 insertions(+), 9 deletions(-) delete mode 100644 tests/run-make/ls-metadata/Makefile create mode 100644 tests/run-make/ls-metadata/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index feb31e25e5f8..57bb326ac954 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -108,7 +108,6 @@ run-make/llvm-ident/Makefile run-make/long-linker-command-lines-cmd-exe/Makefile run-make/long-linker-command-lines/Makefile run-make/longjmp-across-rust/Makefile -run-make/ls-metadata/Makefile run-make/lto-dylib-dep/Makefile run-make/lto-empty/Makefile run-make/lto-linkage-used-attr/Makefile diff --git a/tests/run-make/ls-metadata/Makefile b/tests/run-make/ls-metadata/Makefile deleted file mode 100644 index f03569baef7f..000000000000 --- a/tests/run-make/ls-metadata/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) foo.rs - $(RUSTC) -Z ls=root $(TMPDIR)/foo - touch $(TMPDIR)/bar - $(RUSTC) -Z ls=root $(TMPDIR)/bar diff --git a/tests/run-make/ls-metadata/rmake.rs b/tests/run-make/ls-metadata/rmake.rs new file mode 100644 index 000000000000..61dcd3774a19 --- /dev/null +++ b/tests/run-make/ls-metadata/rmake.rs @@ -0,0 +1,17 @@ +// Passing invalid files to -Z ls (which lists the symbols +// defined by a library crate) used to cause a segmentation fault. +// As this was fixed in #11262, this test checks that no segfault +// occurs when passing the invalid file `bar` to -Z ls. +// See https://github.com/rust-lang/rust/issues/11259 + +//@ ignore-cross-compile + +use run_make_support::fs_wrapper; +use run_make_support::{rmake_out_path, rustc}; + +fn main() { + rustc().input("foo.rs"); + rustc().arg("-Zls=root").input(rmake_out_path("foo")); + fs_wrapper::create_file(rmake_out_path("bar")); + rustc().arg("-Zls=root").input(rmake_out_path("bar")); +} From 03a4259c8b925330d5425e61e7553351c21e55be Mon Sep 17 00:00:00 2001 From: Oneirical Date: Thu, 6 Jun 2024 15:20:42 -0400 Subject: [PATCH 045/147] Rewrite lto-readonly-lib to rmake --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/link-args-order/rmake.rs | 48 +++++++------------ tests/run-make/ls-metadata/rmake.rs | 10 ++-- tests/run-make/lto-readonly-lib/Makefile | 13 ----- tests/run-make/lto-readonly-lib/rmake.rs | 25 ++++++++++ 5 files changed, 48 insertions(+), 49 deletions(-) delete mode 100644 tests/run-make/lto-readonly-lib/Makefile create mode 100644 tests/run-make/lto-readonly-lib/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 57bb326ac954..0fdc6de0b644 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -112,7 +112,6 @@ run-make/lto-dylib-dep/Makefile run-make/lto-empty/Makefile run-make/lto-linkage-used-attr/Makefile run-make/lto-no-link-whole-rlib/Makefile -run-make/lto-readonly-lib/Makefile run-make/lto-smoke-c/Makefile run-make/macos-deployment-target/Makefile run-make/macos-fat-archive/Makefile diff --git a/tests/run-make/link-args-order/rmake.rs b/tests/run-make/link-args-order/rmake.rs index cd921a031292..4530251c7a58 100644 --- a/tests/run-make/link-args-order/rmake.rs +++ b/tests/run-make/link-args-order/rmake.rs @@ -6,34 +6,22 @@ use run_make_support::rustc; fn main() { - assert!( - String::from_utf8( - rustc() - .input("empty.rs") - .linker_flavor("ld") - .link_arg("a") - .link_args("\"b c\"") - .link_args("\"d e\"") - .link_arg("f") - .run_fail() - .stderr - ) - .unwrap() - .contains("\"a\" \"b\" \"c\" \"d\" \"e\" \"f\"") - ); - assert!( - String::from_utf8( - rustc() - .input("empty.rs") - .linker_flavor("ld") - .pre_link_arg("a") - .pre_link_args("\"b c\"") - .pre_link_args("\"d e\"") - .pre_link_arg("f") - .run_fail() - .stderr - ) - .unwrap() - .contains("\"a\" \"b\" \"c\" \"d\" \"e\" \"f\"") - ); + rustc() + .input("empty.rs") + .linker_flavor("ld") + .link_arg("a") + .link_args("\"b c\"") + .link_args("\"d e\"") + .link_arg("f") + .run_fail() + .assert_stderr_contains("\"a\" \"b\" \"c\" \"d\" \"e\" \"f\""); + rustc() + .input("empty.rs") + .linker_flavor("ld") + .pre_link_arg("a") + .pre_link_args("\"b c\"") + .pre_link_args("\"d e\"") + .pre_link_arg("f") + .run_fail() + .assert_stderr_contains("\"a\" \"b\" \"c\" \"d\" \"e\" \"f\""); } diff --git a/tests/run-make/ls-metadata/rmake.rs b/tests/run-make/ls-metadata/rmake.rs index 61dcd3774a19..0e60f2c46787 100644 --- a/tests/run-make/ls-metadata/rmake.rs +++ b/tests/run-make/ls-metadata/rmake.rs @@ -7,11 +7,11 @@ //@ ignore-cross-compile use run_make_support::fs_wrapper; -use run_make_support::{rmake_out_path, rustc}; +use run_make_support::rustc; fn main() { - rustc().input("foo.rs"); - rustc().arg("-Zls=root").input(rmake_out_path("foo")); - fs_wrapper::create_file(rmake_out_path("bar")); - rustc().arg("-Zls=root").input(rmake_out_path("bar")); + rustc().input("foo.rs").run(); + rustc().arg("-Zls=root").input("foo").run(); + fs_wrapper::create_file("bar"); + rustc().arg("-Zls=root").input("bar").run(); } diff --git a/tests/run-make/lto-readonly-lib/Makefile b/tests/run-make/lto-readonly-lib/Makefile deleted file mode 100644 index 11d944e3e3d4..000000000000 --- a/tests/run-make/lto-readonly-lib/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) lib.rs - - # the compiler needs to copy and modify the rlib file when performing - # LTO, so we should ensure that it can cope with the original rlib - # being read-only. - chmod 444 $(TMPDIR)/*.rlib - - $(RUSTC) main.rs -C lto - $(call RUN,main) diff --git a/tests/run-make/lto-readonly-lib/rmake.rs b/tests/run-make/lto-readonly-lib/rmake.rs new file mode 100644 index 000000000000..bf1dafee697d --- /dev/null +++ b/tests/run-make/lto-readonly-lib/rmake.rs @@ -0,0 +1,25 @@ +// When the compiler is performing link time optimization, it will +// need to copy the original rlib file, set the copy's permissions to read/write, +// and modify that copy - even if the original +// file is read-only. This test creates a read-only rlib, and checks that +// compilation with LTO succeeds. +// See https://github.com/rust-lang/rust/pull/17619 + +//@ ignore-cross-compile + +use run_make_support::fs_wrapper; +use run_make_support::{cwd, run, rustc}; + +fn main() { + rustc().input("lib.rs").run(); + let entries = fs_wrapper::read_dir(cwd()); + for entry in entries { + if entry.path().extension().and_then(|s| s.to_str()) == Some("rlib") { + let mut perms = fs_wrapper::metadata(entry.path()).permissions(); + perms.set_readonly(true); + fs_wrapper::set_permissions(entry.path(), perms); + } + } + rustc().input("main.rs").arg("-Clto").run(); + run("main"); +} From 83cb760e2c2b9fa3f0bb90ad2941f4cbceba2255 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Thu, 30 May 2024 16:20:49 -0400 Subject: [PATCH 046/147] run_make_support nm implementation + bin-emit-no-symbols rmake rewrite --- library/backtrace | 2 +- src/doc/book | 2 +- src/doc/edition-guide | 2 +- src/doc/reference | 2 +- src/doc/rust-by-example | 2 +- src/doc/rustc-dev-guide | 2 +- src/tools/cargo | 2 +- src/tools/run-make-support/src/nm/mod.rs | 48 +++++++++++++++++++ .../tidy/src/allowed_run_make_makefiles.txt | 1 - .../app.rs | 0 tests/run-make/bin-emit-no-symbols/rmake.rs | 19 ++++++++ tests/run-make/issue-51671/Makefile | 9 ---- 12 files changed, 74 insertions(+), 17 deletions(-) create mode 100644 src/tools/run-make-support/src/nm/mod.rs rename tests/run-make/{issue-51671 => bin-emit-no-symbols}/app.rs (100%) create mode 100644 tests/run-make/bin-emit-no-symbols/rmake.rs delete mode 100644 tests/run-make/issue-51671/Makefile diff --git a/library/backtrace b/library/backtrace index 72265bea2108..5e05efa87905 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit 72265bea210891ae47bbe6d4f17b493ef0606619 +Subproject commit 5e05efa87905fb5b351a2bc5644d60c57d6d9327 diff --git a/src/doc/book b/src/doc/book index 45c1a6d69edf..5e9051f71638 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 45c1a6d69edfd1fc91fb7504cb73958dbd09441e +Subproject commit 5e9051f71638aa941cd5dda465e25c61cde9594f diff --git a/src/doc/edition-guide b/src/doc/edition-guide index cb58c430b4e8..bbaabbe088e2 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit cb58c430b4e8054c2cb81d2d4434092c482a93d8 +Subproject commit bbaabbe088e21a81a0d9ae6757705020d5d7b416 diff --git a/src/doc/reference b/src/doc/reference index 0b805c658040..6019b76f5b28 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 0b805c65804019b0ac8f2fe3117afad82a6069b8 +Subproject commit 6019b76f5b28938565b251bbba0bf5cc5c43d863 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index b1d97bd6113a..4840dca06cad 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit b1d97bd6113aba732b2091ce093c76f2d05bb8a0 +Subproject commit 4840dca06cadf48b305d3ce0aeafde7f80933f80 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index aec82168dd31..6a7374bd87cb 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit aec82168dd3121289a194b381f56076fc789a4d2 +Subproject commit 6a7374bd87cbac0f8be4fd4877d8186d9c313985 diff --git a/src/tools/cargo b/src/tools/cargo index a1f47ec3f7cd..431db31d0dbe 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit a1f47ec3f7cd076986f1bfcd7061f2e8cb1a726e +Subproject commit 431db31d0dbeda320caf8ef8535ea48eb3093407 diff --git a/src/tools/run-make-support/src/nm/mod.rs b/src/tools/run-make-support/src/nm/mod.rs new file mode 100644 index 000000000000..c304877eba19 --- /dev/null +++ b/src/tools/run-make-support/src/nm/mod.rs @@ -0,0 +1,48 @@ +use crate::{fs_wrapper, object}; +use object::{Object, ObjectSection}; +use std::path::Path; + +#[derive(Debug)] +pub struct Nm { + file: Option, +} + +pub fn nm() -> Nm { + Nm::new() +} + +impl Nm { + /// Construct a bare `nm` invocation. + pub fn new() -> Self { + Self { file: None } + } + + /// Specify the file to analyze the symbols of. + pub fn input>(&mut self, path: P) -> &mut Self { + &mut Self { + file: Some( + object::File::parse(fs_wrapper::read(path)) + .expect(format!("Failed to parse ELF file at {:?}", path.as_ref().display())), + ), + } + } + + /// Collect all symbols of an object file into a String. + pub fn collect_symbols(&self) -> String { + let object_file = self.file; + let mut symbols_str = String::new(); + for section in object_file.sections() { + if let Ok(ObjectSection::SymbolTable(st)) = section.parse::() { + for symbol in st.symbols() { + symbols_str.push_str(&format!( + "{:016x} {:?} {}\n", + symbol.address(), + symbol.kind(), + symbol.name() + )); + } + } + } + symbols_str + } +} diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 1596257747fa..98438e8004dc 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -84,7 +84,6 @@ run-make/issue-37839/Makefile run-make/issue-40535/Makefile run-make/issue-47384/Makefile run-make/issue-47551/Makefile -run-make/issue-51671/Makefile run-make/issue-68794-textrel-on-minimal-lib/Makefile run-make/issue-69368/Makefile run-make/issue-83045/Makefile diff --git a/tests/run-make/issue-51671/app.rs b/tests/run-make/bin-emit-no-symbols/app.rs similarity index 100% rename from tests/run-make/issue-51671/app.rs rename to tests/run-make/bin-emit-no-symbols/app.rs diff --git a/tests/run-make/bin-emit-no-symbols/rmake.rs b/tests/run-make/bin-emit-no-symbols/rmake.rs new file mode 100644 index 000000000000..6d6d6b329674 --- /dev/null +++ b/tests/run-make/bin-emit-no-symbols/rmake.rs @@ -0,0 +1,19 @@ +// When setting the crate type as a "bin" (in app.rs), +// this could cause a bug where some symbols would not be +// emitted in the object files. This has been fixed, and +// this test checks that the correct symbols have been successfully +// emitted inside the object files. +// See https://github.com/rust-lang/rust/issues/51671 + +use run_make_support::{nm, rustc, tmp_dir}; + +fn main() { + rustc().emit("obj").input("app.rs").run(); + //FIXME(Oneirical): This should eventually be rmake_out_path + let nm = nm(tmp_dir().join("app.o")); + assert!( + nm.contains("rust_begin_unwind") + && nm.contains("rust_eh_personality") + && nm.contains("__rg_oom") + ); +} diff --git a/tests/run-make/issue-51671/Makefile b/tests/run-make/issue-51671/Makefile deleted file mode 100644 index c93645369928..000000000000 --- a/tests/run-make/issue-51671/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -include ../tools.mk - -# ignore-windows-msvc - -all: - $(RUSTC) --emit=obj app.rs - nm $(TMPDIR)/app.o | $(CGREP) rust_begin_unwind - nm $(TMPDIR)/app.o | $(CGREP) rust_eh_personality - nm $(TMPDIR)/app.o | $(CGREP) __rg_oom From c1597f90396c0cf38b196b584be49a17e6e881a6 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Wed, 5 Jun 2024 16:29:42 -0400 Subject: [PATCH 047/147] try implementing suggestions --- library/backtrace | 2 +- src/doc/book | 2 +- src/doc/edition-guide | 2 +- src/doc/reference | 2 +- src/doc/rust-by-example | 2 +- src/doc/rustc-dev-guide | 2 +- src/tools/cargo | 2 +- src/tools/run-make-support/src/nm/mod.rs | 8 +++++--- 8 files changed, 12 insertions(+), 10 deletions(-) diff --git a/library/backtrace b/library/backtrace index 5e05efa87905..72265bea2108 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit 5e05efa87905fb5b351a2bc5644d60c57d6d9327 +Subproject commit 72265bea210891ae47bbe6d4f17b493ef0606619 diff --git a/src/doc/book b/src/doc/book index 5e9051f71638..45c1a6d69edf 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 5e9051f71638aa941cd5dda465e25c61cde9594f +Subproject commit 45c1a6d69edfd1fc91fb7504cb73958dbd09441e diff --git a/src/doc/edition-guide b/src/doc/edition-guide index bbaabbe088e2..cb58c430b4e8 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit bbaabbe088e21a81a0d9ae6757705020d5d7b416 +Subproject commit cb58c430b4e8054c2cb81d2d4434092c482a93d8 diff --git a/src/doc/reference b/src/doc/reference index 6019b76f5b28..0b805c658040 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 6019b76f5b28938565b251bbba0bf5cc5c43d863 +Subproject commit 0b805c65804019b0ac8f2fe3117afad82a6069b8 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 4840dca06cad..b1d97bd6113a 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 4840dca06cadf48b305d3ce0aeafde7f80933f80 +Subproject commit b1d97bd6113aba732b2091ce093c76f2d05bb8a0 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index 6a7374bd87cb..aec82168dd31 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit 6a7374bd87cbac0f8be4fd4877d8186d9c313985 +Subproject commit aec82168dd3121289a194b381f56076fc789a4d2 diff --git a/src/tools/cargo b/src/tools/cargo index 431db31d0dbe..a1f47ec3f7cd 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 431db31d0dbeda320caf8ef8535ea48eb3093407 +Subproject commit a1f47ec3f7cd076986f1bfcd7061f2e8cb1a726e diff --git a/src/tools/run-make-support/src/nm/mod.rs b/src/tools/run-make-support/src/nm/mod.rs index c304877eba19..1f41792921d6 100644 --- a/src/tools/run-make-support/src/nm/mod.rs +++ b/src/tools/run-make-support/src/nm/mod.rs @@ -3,8 +3,8 @@ use object::{Object, ObjectSection}; use std::path::Path; #[derive(Debug)] -pub struct Nm { - file: Option, +pub struct Nm<'a> { + file: Option>, } pub fn nm() -> Nm { @@ -32,7 +32,9 @@ impl Nm { let object_file = self.file; let mut symbols_str = String::new(); for section in object_file.sections() { - if let Ok(ObjectSection::SymbolTable(st)) = section.parse::() { + if let Ok(object::read::elf::SymbolTable(st)) = + section.parse::>() + { for symbol in st.symbols() { symbols_str.push_str(&format!( "{:016x} {:?} {}\n", From 977d3f6f96728b6cd68bb4c36f34fcb659989dcc Mon Sep 17 00:00:00 2001 From: Oneirical Date: Tue, 18 Jun 2024 14:57:00 -0400 Subject: [PATCH 048/147] use llvm_readobj in run-make test instead of nm --- src/tools/run-make-support/src/llvm.rs | 28 +++++++++--- src/tools/run-make-support/src/nm/mod.rs | 50 --------------------- tests/run-make/bin-emit-no-symbols/rmake.rs | 13 +++--- 3 files changed, 28 insertions(+), 63 deletions(-) delete mode 100644 src/tools/run-make-support/src/nm/mod.rs diff --git a/src/tools/run-make-support/src/llvm.rs b/src/tools/run-make-support/src/llvm.rs index fe4131819bae..99bce08fc238 100644 --- a/src/tools/run-make-support/src/llvm.rs +++ b/src/tools/run-make-support/src/llvm.rs @@ -2,8 +2,8 @@ use std::path::{Path, PathBuf}; use crate::{env_var, Command}; -/// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available -/// at `$LLVM_BIN_DIR/llvm-readobj`. +/// Construct a new `llvm-readobj` invocation with the `GNU` output style. +/// This assumes that `llvm-readobj` is available at `$LLVM_BIN_DIR/llvm-readobj`. #[track_caller] pub fn llvm_readobj() -> LlvmReadobj { LlvmReadobj::new() @@ -70,13 +70,24 @@ pub fn llvm_bin_dir() -> PathBuf { } impl LlvmReadobj { - /// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available - /// at `$LLVM_BIN_DIR/llvm-readobj`. + /// Construct a new `llvm-readobj` invocation with the `GNU` output style. + /// This assumes that `llvm-readobj` is available at `$LLVM_BIN_DIR/llvm-readobj`. #[track_caller] pub fn new() -> Self { let llvm_readobj = llvm_bin_dir().join("llvm-readobj"); let cmd = Command::new(llvm_readobj); - Self { cmd } + let mut readobj = Self { cmd }; + readobj.elf_output_style("GNU"); + readobj + } + + /// Specify the format of the ELF information. + /// + /// Valid options are `LLVM` (default), `GNU`, and `JSON`. + pub fn elf_output_style(&mut self, style: &str) -> &mut Self { + self.cmd.arg("--elf-output-style"); + self.cmd.arg(style); + self } /// Provide an input file. @@ -90,6 +101,13 @@ impl LlvmReadobj { self.cmd.arg("--file-header"); self } + + /// Specify the section to display. + pub fn section(&mut self, section: &str) -> &mut Self { + self.cmd.arg("--string-dump"); + self.cmd.arg(section); + self + } } impl LlvmProfdata { diff --git a/src/tools/run-make-support/src/nm/mod.rs b/src/tools/run-make-support/src/nm/mod.rs deleted file mode 100644 index 1f41792921d6..000000000000 --- a/src/tools/run-make-support/src/nm/mod.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::{fs_wrapper, object}; -use object::{Object, ObjectSection}; -use std::path::Path; - -#[derive(Debug)] -pub struct Nm<'a> { - file: Option>, -} - -pub fn nm() -> Nm { - Nm::new() -} - -impl Nm { - /// Construct a bare `nm` invocation. - pub fn new() -> Self { - Self { file: None } - } - - /// Specify the file to analyze the symbols of. - pub fn input>(&mut self, path: P) -> &mut Self { - &mut Self { - file: Some( - object::File::parse(fs_wrapper::read(path)) - .expect(format!("Failed to parse ELF file at {:?}", path.as_ref().display())), - ), - } - } - - /// Collect all symbols of an object file into a String. - pub fn collect_symbols(&self) -> String { - let object_file = self.file; - let mut symbols_str = String::new(); - for section in object_file.sections() { - if let Ok(object::read::elf::SymbolTable(st)) = - section.parse::>() - { - for symbol in st.symbols() { - symbols_str.push_str(&format!( - "{:016x} {:?} {}\n", - symbol.address(), - symbol.kind(), - symbol.name() - )); - } - } - } - symbols_str - } -} diff --git a/tests/run-make/bin-emit-no-symbols/rmake.rs b/tests/run-make/bin-emit-no-symbols/rmake.rs index 6d6d6b329674..5586e53c0508 100644 --- a/tests/run-make/bin-emit-no-symbols/rmake.rs +++ b/tests/run-make/bin-emit-no-symbols/rmake.rs @@ -5,15 +5,12 @@ // emitted inside the object files. // See https://github.com/rust-lang/rust/issues/51671 -use run_make_support::{nm, rustc, tmp_dir}; +use run_make_support::{llvm_readobj, rustc}; fn main() { rustc().emit("obj").input("app.rs").run(); - //FIXME(Oneirical): This should eventually be rmake_out_path - let nm = nm(tmp_dir().join("app.o")); - assert!( - nm.contains("rust_begin_unwind") - && nm.contains("rust_eh_personality") - && nm.contains("__rg_oom") - ); + let out = llvm_readobj().input("app.o").arg("--symbols").run(); + out.assert_stdout_contains("rust_begin_unwind"); + out.assert_stdout_contains("rust_eh_personality"); + out.assert_stdout_contains("__rg_oom"); } From 62431b73e00eaae0788b53f4ccf6cecb9f7f03ca Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 18 Jun 2024 15:13:40 +0200 Subject: [PATCH 049/147] Migrate `run-make/compressed-debuginfo` to `rmake.rs` --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/compressed-debuginfo/Makefile | 14 -------- tests/run-make/compressed-debuginfo/rmake.rs | 36 +++++++++++++++++++ 3 files changed, 36 insertions(+), 15 deletions(-) delete mode 100644 tests/run-make/compressed-debuginfo/Makefile create mode 100644 tests/run-make/compressed-debuginfo/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 24e51b8fee51..61303669ee41 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -15,7 +15,6 @@ run-make/comment-section/Makefile run-make/compiler-lookup-paths-2/Makefile run-make/compiler-lookup-paths/Makefile run-make/compiler-rt-works-on-mingw/Makefile -run-make/compressed-debuginfo/Makefile run-make/crate-hash-rustc-version/Makefile run-make/crate-name-priority/Makefile run-make/cross-lang-lto-clang/Makefile diff --git a/tests/run-make/compressed-debuginfo/Makefile b/tests/run-make/compressed-debuginfo/Makefile deleted file mode 100644 index d2f24dde00d7..000000000000 --- a/tests/run-make/compressed-debuginfo/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# only-linux -# -# This tests debuginfo-compression. - -all: zlib zstandard - -zlib: - test "`$(RUSTC) --crate-name=foo --crate-type=lib --emit=obj -C debuginfo=full -Z debuginfo-compression=zlib foo.rs 2>&1 | sed 's/.*unknown.*zlib.*/missing/' | head -n 1`" = missing || readelf -t $(TMPDIR)/foo.o | grep -q ZLIB - -zstandard: - test "`$(RUSTC) --crate-name=foo --crate-type=lib --emit=obj -C debuginfo=full -Z debuginfo-compression=zstd foo.rs 2>&1 | sed 's/.*unknown.*zstd.*/missing/' | head -n 1`" = missing || readelf -t $(TMPDIR)/foo.o | grep -q ZST diff --git a/tests/run-make/compressed-debuginfo/rmake.rs b/tests/run-make/compressed-debuginfo/rmake.rs new file mode 100644 index 000000000000..9c6d50ab243c --- /dev/null +++ b/tests/run-make/compressed-debuginfo/rmake.rs @@ -0,0 +1,36 @@ +// Checks the `debuginfo-compression` option. + +//@ only-linux +//@ ignore-cross-compile + +// FIXME: This test isn't comprehensive and isn't covering all possible combinations. + +use run_make_support::{assert_contains, cmd, run_in_tmpdir, rustc}; + +fn check_compression(compression: &str, to_find: &str) { + run_in_tmpdir(|| { + let out = rustc() + .crate_name("foo") + .crate_type("lib") + .emit("obj") + .arg("-Cdebuginfo=full") + .arg(&format!("-Zdebuginfo-compression={compression}")) + .input("foo.rs") + .run(); + let stderr = out.stderr_utf8(); + if stderr.is_empty() { + // FIXME: `readelf` might need to be replaced with `llvm-readelf`. + cmd("readelf").arg("-t").arg("foo.o").run().assert_stdout_contains(to_find); + } else { + assert_contains( + &stderr, + &format!("unknown debuginfo compression algorithm {compression}"), + ); + } + }); +} + +fn main() { + check_compression("zlib", "ZLIB"); + check_compression("zstd", "ZSTD"); +} From 060a13e9fd56b1d186ea016070e390067b9e893d Mon Sep 17 00:00:00 2001 From: Oneirical Date: Tue, 18 Jun 2024 15:59:33 -0400 Subject: [PATCH 050/147] rewrite extern-flag-rename-transitive to rmake --- src/tools/tidy/src/allowed_run_make_makefiles.txt | 1 - .../extern-flag-rename-transitive/Makefile | 7 ------- .../extern-flag-rename-transitive/rmake.rs | 14 ++++++++++++++ 3 files changed, 14 insertions(+), 8 deletions(-) delete mode 100644 tests/run-make/extern-flag-rename-transitive/Makefile create mode 100644 tests/run-make/extern-flag-rename-transitive/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 1596257747fa..6f57cb717ace 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -40,7 +40,6 @@ run-make/export-executable-symbols/Makefile run-make/extern-diff-internal-name/Makefile run-make/extern-flag-disambiguates/Makefile run-make/extern-flag-pathless/Makefile -run-make/extern-flag-rename-transitive/Makefile run-make/extern-fn-explicit-align/Makefile run-make/extern-fn-generic/Makefile run-make/extern-fn-mangle/Makefile diff --git a/tests/run-make/extern-flag-rename-transitive/Makefile b/tests/run-make/extern-flag-rename-transitive/Makefile deleted file mode 100644 index d16a8e20868e..000000000000 --- a/tests/run-make/extern-flag-rename-transitive/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -include ../tools.mk - -all: - $(RUSTC) foo.rs - $(RUSTC) bar.rs - $(RUSTC) baz.rs --extern a=$(TMPDIR)/libfoo.rlib - diff --git a/tests/run-make/extern-flag-rename-transitive/rmake.rs b/tests/run-make/extern-flag-rename-transitive/rmake.rs new file mode 100644 index 000000000000..0090d487f03d --- /dev/null +++ b/tests/run-make/extern-flag-rename-transitive/rmake.rs @@ -0,0 +1,14 @@ +// In this test, baz.rs is looking for an extern crate "a" which +// does not exist, and can only run through the --extern rustc flag +// defining that the "a" crate is in fact just "foo". This test +// checks that the --extern flag takes precedence over the extern +// crate statement in the code. +// See https://github.com/rust-lang/rust/pull/52723 + +use run_make_support::{rust_lib_name, rustc}; + +fn main() { + rustc().input("foo.rs").run(); + rustc().input("bar.rs").run(); + rustc().input("baz.rs").extern_("a", rust_lib_name("foo")).run(); +} From 9e2ace85f9d2a9fd24af197f2da81eb3a5db51b9 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Tue, 18 Jun 2024 16:05:56 -0400 Subject: [PATCH 051/147] rewrite debugger-visualizer-dep-info to rmake --- src/tools/tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/debugger-visualizer-dep-info/Makefile | 9 --------- tests/run-make/debugger-visualizer-dep-info/rmake.rs | 11 +++++++++++ 3 files changed, 11 insertions(+), 10 deletions(-) delete mode 100644 tests/run-make/debugger-visualizer-dep-info/Makefile create mode 100644 tests/run-make/debugger-visualizer-dep-info/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 6f57cb717ace..4091cbbbcb89 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -23,7 +23,6 @@ run-make/cross-lang-lto-pgo-smoketest/Makefile run-make/cross-lang-lto-upstream-rlibs/Makefile run-make/cross-lang-lto/Makefile run-make/debug-assertions/Makefile -run-make/debugger-visualizer-dep-info/Makefile run-make/dep-info-doesnt-run-much/Makefile run-make/dep-info-spaces/Makefile run-make/dep-info/Makefile diff --git a/tests/run-make/debugger-visualizer-dep-info/Makefile b/tests/run-make/debugger-visualizer-dep-info/Makefile deleted file mode 100644 index 0877998a74fe..000000000000 --- a/tests/run-make/debugger-visualizer-dep-info/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -include ../tools.mk - -# This test makes sure that files referenced via #[debugger_visualizer] are -# included in `--emit dep-info` output. - -all: - $(RUSTC) --emit dep-info main.rs - $(CGREP) "foo.py" < $(TMPDIR)/main.d - $(CGREP) "my_visualizers/bar.natvis" < $(TMPDIR)/main.d diff --git a/tests/run-make/debugger-visualizer-dep-info/rmake.rs b/tests/run-make/debugger-visualizer-dep-info/rmake.rs new file mode 100644 index 000000000000..65ffb2373e7c --- /dev/null +++ b/tests/run-make/debugger-visualizer-dep-info/rmake.rs @@ -0,0 +1,11 @@ +// This test checks that files referenced via #[debugger_visualizer] are +// included in `--emit dep-info` output. +// See https://github.com/rust-lang/rust/pull/111641 + +use run_make_support::{invalid_utf8_contains, rustc}; + +fn main() { + rustc().emit("dep-info").input("main.rs").run(); + invalid_utf8_contains("main.d", "foo.py"); + invalid_utf8_contains("main.d", "my_visualizers/bar.natvis"); +} From dff354e57fe030f8fa0467f092b75e8979a3cce9 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Tue, 18 Jun 2024 16:20:32 -0400 Subject: [PATCH 052/147] rewrite metadata-flag-frobs-symbols to rmake --- src/tools/run-make-support/src/rustc.rs | 6 ++++++ .../tidy/src/allowed_run_make_makefiles.txt | 1 - .../metadata-flag-frobs-symbols/Makefile | 11 ---------- .../metadata-flag-frobs-symbols/rmake.rs | 21 +++++++++++++++++++ 4 files changed, 27 insertions(+), 12 deletions(-) delete mode 100644 tests/run-make/metadata-flag-frobs-symbols/Makefile create mode 100644 tests/run-make/metadata-flag-frobs-symbols/rmake.rs diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index 331e1a5ab8b3..125f4b4bd629 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -73,6 +73,12 @@ impl Rustc { self } + /// Incorporate a hashed string to mangled symbols. + pub fn metadata(&mut self, meta: &str) -> &mut Self { + self.cmd.arg(format!("-Cmetadata={meta}")); + self + } + /// Add a suffix in each output filename. pub fn extra_filename(&mut self, suffix: &str) -> &mut Self { self.cmd.arg(format!("-Cextra-filename={suffix}")); diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 4091cbbbcb89..691248ea260b 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -119,7 +119,6 @@ run-make/macos-fat-archive/Makefile run-make/manual-link/Makefile run-make/many-crates-but-no-match/Makefile run-make/metadata-dep-info/Makefile -run-make/metadata-flag-frobs-symbols/Makefile run-make/min-global-align/Makefile run-make/mingw-export-call-convention/Makefile run-make/mismatching-target-triples/Makefile diff --git a/tests/run-make/metadata-flag-frobs-symbols/Makefile b/tests/run-make/metadata-flag-frobs-symbols/Makefile deleted file mode 100644 index 53d7d0657695..000000000000 --- a/tests/run-make/metadata-flag-frobs-symbols/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) foo.rs -C metadata=a -C extra-filename=-a - $(RUSTC) foo.rs -C metadata=b -C extra-filename=-b - $(RUSTC) bar.rs \ - --extern foo1=$(TMPDIR)/libfoo-a.rlib \ - --extern foo2=$(TMPDIR)/libfoo-b.rlib \ - --print link-args - $(call RUN,bar) diff --git a/tests/run-make/metadata-flag-frobs-symbols/rmake.rs b/tests/run-make/metadata-flag-frobs-symbols/rmake.rs new file mode 100644 index 000000000000..12c07f9e3aee --- /dev/null +++ b/tests/run-make/metadata-flag-frobs-symbols/rmake.rs @@ -0,0 +1,21 @@ +// In this test, foo.rs is compiled twice with different hashes tied to its +// symbols thanks to the metadata flag. bar.rs then ensures that the memory locations +// of foo's symbols are different even though they came from the same original source code. +// This checks that the metadata flag is doing its job. +// See https://github.com/rust-lang/rust/issues/14471 + +//@ ignore-cross-compile + +use run_make_support::{run, rust_lib_name, rustc}; + +fn main() { + rustc().input("foo.rs").metadata("a").extra_filename("-a").run(); + rustc().input("foo.rs").metadata("b").extra_filename("-b").run(); + rustc() + .input("bar.rs") + .extern_("foo1", rust_lib_name("foo-a")) + .extern_("foo2", rust_lib_name("foo-b")) + .print("link-args") + .run(); + run("bar"); +} From d1e8c6bc7e93b6649e9dab0604dcc591dde03af0 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Tue, 18 Jun 2024 16:30:26 -0400 Subject: [PATCH 053/147] rewrite extern-overrides-distribution to rmake --- src/tools/tidy/src/allowed_run_make_makefiles.txt | 1 - .../extern-overrides-distribution/Makefile | 6 ------ .../extern-overrides-distribution/rmake.rs | 14 ++++++++++++++ 3 files changed, 14 insertions(+), 7 deletions(-) delete mode 100644 tests/run-make/extern-overrides-distribution/Makefile create mode 100644 tests/run-make/extern-overrides-distribution/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 691248ea260b..8eee16e87a88 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -49,7 +49,6 @@ run-make/extern-fn-with-packed-struct/Makefile run-make/extern-fn-with-union/Makefile run-make/extern-multiple-copies/Makefile run-make/extern-multiple-copies2/Makefile -run-make/extern-overrides-distribution/Makefile run-make/extra-filename-with-temp-outputs/Makefile run-make/fmt-write-bloat/Makefile run-make/forced-unwind-terminate-pof/Makefile diff --git a/tests/run-make/extern-overrides-distribution/Makefile b/tests/run-make/extern-overrides-distribution/Makefile deleted file mode 100644 index bfd0dd6991ba..000000000000 --- a/tests/run-make/extern-overrides-distribution/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) libc.rs -Cmetadata=foo - $(RUSTC) main.rs --extern libc=$(TMPDIR)/liblibc.rlib diff --git a/tests/run-make/extern-overrides-distribution/rmake.rs b/tests/run-make/extern-overrides-distribution/rmake.rs new file mode 100644 index 000000000000..bd2553d4134a --- /dev/null +++ b/tests/run-make/extern-overrides-distribution/rmake.rs @@ -0,0 +1,14 @@ +// The --extern flag should override any "crate_type" declarations in the +// Rust files themselves. In this test, libc is compiled as "lib", but +// main.rs will only run with an rlib, which checks if the --extern flag +// is successfully overriding the default behaviour. +// See https://github.com/rust-lang/rust/pull/21782 + +//@ ignore-cross-compile + +use run_make_support::{rust_lib_name, rustc}; + +fn main() { + rustc().input("libc.rs").metadata("foo").run(); + rustc().input("main.rs").extern_("libc", rust_lib_name("libc")).run(); +} From bfe6a26af6669485b4661877583ebe3d22cd0317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Tue, 18 Jun 2024 21:05:25 +0000 Subject: [PATCH 054/147] hir_typeck: be more conservative in making "note caller chooses ty param" note - Avoid "caller chooses ty for type param" note if the found type a.k.a. the return expression type *contains* the type parameter, because e.g. `&T` will always be different from `T` (i.e. "well duh"). - Rename `note_caller_chooses_ty_for_ty_param` to `try_note_caller_chooses_ty_for_ty_param` because the note is not always made. Issue: https://github.com/rust-lang/rust/issues/126547 --- .../src/fn_ctxt/suggestions.rs | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 337a92c0d012..9dd82868adc5 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -859,10 +859,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // Only point to return type if the expected type is the return type, as if they // are not, the expectation must have been caused by something else. - debug!("return type {:?}", hir_ty); + debug!(?hir_ty, "return type"); let ty = self.lowerer().lower_ty(hir_ty); - debug!("return type {:?}", ty); - debug!("expected type {:?}", expected); + debug!(?ty, "return type (lowered)"); + debug!(?expected, "expected type"); let bound_vars = self.tcx.late_bound_vars(hir_ty.hir_id.owner.into()); let ty = Binder::bind_with_vars(ty, bound_vars); let ty = self.normalize(hir_ty.span, ty); @@ -873,7 +873,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected, }); self.try_suggest_return_impl_trait(err, expected, found, fn_id); - self.note_caller_chooses_ty_for_ty_param(err, expected, found); + self.try_note_caller_chooses_ty_for_ty_param(err, expected, found); return true; } } @@ -883,18 +883,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - fn note_caller_chooses_ty_for_ty_param( + fn try_note_caller_chooses_ty_for_ty_param( &self, diag: &mut Diag<'_>, expected: Ty<'tcx>, found: Ty<'tcx>, ) { - if let ty::Param(expected_ty_as_param) = expected.kind() { - diag.subdiagnostic(errors::NoteCallerChoosesTyForTyParam { - ty_param_name: expected_ty_as_param.name, - found_ty: found, - }); + // Only show the note if: + // 1. `expected` ty is a type parameter; + // 2. The `expected` type parameter does *not* occur in the return expression type. This can + // happen for e.g. `fn foo(t: &T) -> T { t }`, where `expected` is `T` but `found` is + // `&T`. Saying "the caller chooses a type for `T` which can be different from `&T`" is + // "well duh" and is only confusing and not helpful. + let ty::Param(expected_ty_as_param) = expected.kind() else { + return; + }; + + if found.contains(expected) { + return; } + + diag.subdiagnostic(errors::NoteCallerChoosesTyForTyParam { + ty_param_name: expected_ty_as_param.name, + found_ty: found, + }); } /// check whether the return type is a generic type with a trait bound From 939026c8fba0d6b9edc2e2cdfbbf6dd6fb575662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Sun, 16 Jun 2024 17:57:01 +0000 Subject: [PATCH 055/147] tests: update tests for more conservative return ty mismatch note --- tests/ui/return/return-ty-mismatch-note.rs | 11 +++++++++- .../ui/return/return-ty-mismatch-note.stderr | 21 +++++++++++++++---- ...n-unconstrained-borrowed-type-param.stderr | 1 - 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/tests/ui/return/return-ty-mismatch-note.rs b/tests/ui/return/return-ty-mismatch-note.rs index 352bc2a16376..36d590519fba 100644 --- a/tests/ui/return/return-ty-mismatch-note.rs +++ b/tests/ui/return/return-ty-mismatch-note.rs @@ -1,4 +1,5 @@ -// Checks existence of a note for "a caller chooses ty for ty param" upon return ty mismatch. +// Checks existence or absence of a note for "a caller chooses ty for ty param" upon return ty +// mismatch. fn f() -> (T,) { (0,) //~ ERROR mismatched types @@ -14,6 +15,14 @@ fn h() -> u8 { 0u8 } +// This case was reported in where it doesn't +// make sense to make the "note caller chooses ty for ty param" note if the found type contains +// the ty param... +fn k(_t: &T) -> T { + _t + //~^ ERROR mismatched types +} + fn main() { f::<()>(); g::<(), ()>; diff --git a/tests/ui/return/return-ty-mismatch-note.stderr b/tests/ui/return/return-ty-mismatch-note.stderr index 135903da5c26..47ef6863063c 100644 --- a/tests/ui/return/return-ty-mismatch-note.stderr +++ b/tests/ui/return/return-ty-mismatch-note.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/return-ty-mismatch-note.rs:4:6 + --> $DIR/return-ty-mismatch-note.rs:5:6 | LL | fn f() -> (T,) { | - expected this type parameter @@ -10,7 +10,7 @@ LL | (0,) found type `{integer}` error[E0308]: mismatched types - --> $DIR/return-ty-mismatch-note.rs:8:6 + --> $DIR/return-ty-mismatch-note.rs:9:6 | LL | fn g() -> (U, V) { | - expected this type parameter @@ -21,7 +21,7 @@ LL | (0, "foo") found type `{integer}` error[E0308]: mismatched types - --> $DIR/return-ty-mismatch-note.rs:8:9 + --> $DIR/return-ty-mismatch-note.rs:9:9 | LL | fn g() -> (U, V) { | - expected this type parameter @@ -31,6 +31,19 @@ LL | (0, "foo") = note: expected type parameter `V` found reference `&'static str` -error: aborting due to 3 previous errors +error[E0308]: mismatched types + --> $DIR/return-ty-mismatch-note.rs:22:5 + | +LL | fn k(_t: &T) -> T { + | - - expected `T` because of return type + | | + | expected this type parameter +LL | _t + | ^^ expected type parameter `T`, found `&T` + | + = note: expected type parameter `_` + found reference `&_` + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr b/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr index 2c4be26a82b7..afbb9c32d516 100644 --- a/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr +++ b/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr @@ -10,7 +10,6 @@ LL | t.clone() | = note: expected type parameter `_` found reference `&_` - = note: the caller chooses a type for `T` which can be different from `&T` note: `T` does not implement `Clone`, so `&T` was cloned instead --> $DIR/clone-on-unconstrained-borrowed-type-param.rs:3:5 | From 1ba5c9808cddfd4b566451d60f54b242293f7f98 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 6 May 2024 11:10:42 +0200 Subject: [PATCH 056/147] Migrate `run-make/rustdoc-io-error` to `rmake.rs` --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/rustdoc-io-error/Makefile | 20 ------------ tests/run-make/rustdoc-io-error/rmake.rs | 31 +++++++++++++++++++ 3 files changed, 31 insertions(+), 21 deletions(-) delete mode 100644 tests/run-make/rustdoc-io-error/Makefile create mode 100644 tests/run-make/rustdoc-io-error/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 24e51b8fee51..cfa23ffd0dc9 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -189,7 +189,6 @@ run-make/rlib-format-packed-bundled-libs-3/Makefile run-make/rlib-format-packed-bundled-libs/Makefile run-make/rmeta-preferred/Makefile run-make/rustc-macro-dep-files/Makefile -run-make/rustdoc-io-error/Makefile run-make/sanitizer-cdylib-link/Makefile run-make/sanitizer-dylib-link/Makefile run-make/sanitizer-staticlib-link/Makefile diff --git a/tests/run-make/rustdoc-io-error/Makefile b/tests/run-make/rustdoc-io-error/Makefile deleted file mode 100644 index 27f5ecf94aba..000000000000 --- a/tests/run-make/rustdoc-io-error/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -include ../tools.mk - -# This test verifies that rustdoc doesn't ICE when it encounters an IO error -# while generating files. Ideally this would be a rustdoc-ui test, so we could -# verify the error message as well. - -# ignore-windows -# The test uses `chmod`. - -OUTPUT_DIR := "$(TMPDIR)/rustdoc-io-error" - -# This test operates by creating a temporary directory and modifying its -# permissions so that it is not writable. We have to take special care to set -# the permissions back to normal so that it's able to be deleted later. -all: - mkdir -p $(OUTPUT_DIR) - chmod u-w $(OUTPUT_DIR) - -$(shell $(RUSTDOC) -o $(OUTPUT_DIR) foo.rs) - chmod u+w $(OUTPUT_DIR) - exit $($(.SHELLSTATUS) -eq 1) diff --git a/tests/run-make/rustdoc-io-error/rmake.rs b/tests/run-make/rustdoc-io-error/rmake.rs new file mode 100644 index 000000000000..d47d1cfe7274 --- /dev/null +++ b/tests/run-make/rustdoc-io-error/rmake.rs @@ -0,0 +1,31 @@ +// This test verifies that rustdoc doesn't ICE when it encounters an IO error +// while generating files. Ideally this would be a rustdoc-ui test, so we could +// verify the error message as well. +// +// It operates by creating a temporary directory and modifying its +// permissions so that it is not writable. We have to take special care to set +// the permissions back to normal so that it's able to be deleted later. + +// On windows, the `set_readonly` functions doesn't work on folders. +//@ ignore-windows + +use run_make_support::{path, rustdoc}; +use std::fs; + +fn main() { + let out_dir = path("rustdoc-io-error"); + let output = fs::create_dir(&out_dir).unwrap(); + let mut permissions = fs::metadata(&out_dir).unwrap().permissions(); + let original_permissions = permissions.clone(); + + permissions.set_readonly(true); + fs::set_permissions(&out_dir, permissions).unwrap(); + + let output = rustdoc().input("foo.rs").output(&out_dir).env("RUST_BACKTRACE", "1").run_fail(); + + fs::set_permissions(&out_dir, original_permissions).unwrap(); + + output + .assert_exit_code(1) + .assert_stderr_contains("error: couldn't generate documentation: Permission denied"); +} From d51b4462ec2d4a7d8d647afcf86353ca4e971b70 Mon Sep 17 00:00:00 2001 From: ardi Date: Fri, 7 Jun 2024 16:25:43 +0200 Subject: [PATCH 057/147] Improve conflict marker recovery --- .../rustc_parse/src/parser/diagnostics.rs | 57 +++++++++++++------ tests/ui/parser/diff-markers/enum-2.stderr | 19 ++++--- tests/ui/parser/diff-markers/enum.stderr | 17 ++++-- tests/ui/parser/diff-markers/fn-arg.stderr | 17 ++++-- .../parser/diff-markers/item-with-attr.stderr | 17 ++++-- tests/ui/parser/diff-markers/item.stderr | 17 ++++-- tests/ui/parser/diff-markers/statement.stderr | 17 ++++-- .../ui/parser/diff-markers/struct-expr.stderr | 17 ++++-- tests/ui/parser/diff-markers/struct.stderr | 17 ++++-- .../ui/parser/diff-markers/trait-item.stderr | 17 ++++-- .../parser/diff-markers/tuple-struct.stderr | 17 ++++-- .../unclosed-delims-in-macro.stderr | 17 ++++-- .../ui/parser/diff-markers/unclosed-delims.rs | 10 +++- .../diff-markers/unclosed-delims.stderr | 17 ++++-- .../parser/diff-markers/use-statement.stderr | 17 ++++-- 15 files changed, 192 insertions(+), 98 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index c1aac84bcaec..e4630bbceb9b 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2990,14 +2990,18 @@ impl<'a> Parser<'a> { } pub(crate) fn err_vcs_conflict_marker(&mut self) -> PResult<'a, ()> { + // <<<<<<< let Some(start) = self.conflict_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) else { return Ok(()); }; let mut spans = Vec::with_capacity(3); spans.push(start); + // ||||||| let mut middlediff3 = None; + // ======= let mut middle = None; + // >>>>>>> let mut end = None; loop { if self.token.kind == TokenKind::Eof { @@ -3018,29 +3022,50 @@ impl<'a> Parser<'a> { } self.bump(); } + let mut err = self.dcx().struct_span_err(spans, "encountered diff marker"); - err.span_label(start, "after this is the code before the merge"); - if let Some(middle) = middlediff3 { - err.span_label(middle, ""); - } + match middlediff3 { + // We're using diff3 + Some(middlediff3) => { + err.span_label( + start, + "between this marker and `|||||||` is the code that we're merging into", + ); + err.span_label(middlediff3, "between this marker and `=======` is the base code (what the two refs diverged from)"); + } + None => { + err.span_label( + start, + "between this marker and `=======` is the code that we're merging into", + ); + } + }; + if let Some(middle) = middle { - err.span_label(middle, ""); + err.span_label(middle, "between this marker and `>>>>>>>` is the incoming code"); } if let Some(end) = end { - err.span_label(end, "above this are the incoming code changes"); + err.span_label(end, "this marker concludes the conflict region"); } - err.help( - "if you're having merge conflicts after pulling new code, the top section is the code \ - you already had and the bottom section is the remote code", - ); - err.help( - "if you're in the middle of a rebase, the top section is the code being rebased onto \ - and the bottom section is the code coming from the current commit being rebased", - ); err.note( - "for an explanation on these markers from the `git` documentation, visit \ - ", + "conflict markers indicate that a merge was started but could not be completed due \ + to merge conflicts\n\ + to resolve a conflict, keep only the code you want and then delete the lines \ + containing conflict markers", ); + err.help( + "if you're having merge conflicts after pulling new code:\n\ + the top section is the code you already had and the bottom section is the remote code\n\ + if you're in the middle of a rebase:\n\ + the top section is the code being rebased onto and the bottom section is the code \ + coming from the current commit being rebased", + ); + + err.note( + "for an explanation on these markers from the `git` documentation:\n\ + visit ", + ); + Err(err) } diff --git a/tests/ui/parser/diff-markers/enum-2.stderr b/tests/ui/parser/diff-markers/enum-2.stderr index 20e551c2f959..b76cf5d5a01e 100644 --- a/tests/ui/parser/diff-markers/enum-2.stderr +++ b/tests/ui/parser/diff-markers/enum-2.stderr @@ -2,20 +2,25 @@ error: encountered diff marker --> $DIR/enum-2.rs:3:1 | LL | <<<<<<< HEAD - | ^^^^^^^ after this is the code before the merge + | ^^^^^^^ between this marker and `|||||||` is the code that we're merging into LL | x: u8, LL | ||||||| - | ------- + | ------- between this marker and `=======` is the base code (what the two refs diverged from) LL | z: (), LL | ======= - | ------- + | ------- between this marker and `>>>>>>>` is the incoming code LL | y: i8, LL | >>>>>>> branch - | ^^^^^^^ above this are the incoming code changes + | ^^^^^^^ this marker concludes the conflict region | - = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code - = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased - = note: for an explanation on these markers from the `git` documentation, visit + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you're having merge conflicts after pulling new code: + the top section is the code you already had and the bottom section is the remote code + if you're in the middle of a rebase: + the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation: + visit error: aborting due to 1 previous error diff --git a/tests/ui/parser/diff-markers/enum.stderr b/tests/ui/parser/diff-markers/enum.stderr index be94331dce52..0ce473bc7023 100644 --- a/tests/ui/parser/diff-markers/enum.stderr +++ b/tests/ui/parser/diff-markers/enum.stderr @@ -2,17 +2,22 @@ error: encountered diff marker --> $DIR/enum.rs:2:1 | LL | <<<<<<< HEAD - | ^^^^^^^ after this is the code before the merge + | ^^^^^^^ between this marker and `=======` is the code that we're merging into LL | Foo(u8), LL | ======= - | ------- + | ------- between this marker and `>>>>>>>` is the incoming code LL | Bar(i8), LL | >>>>>>> branch - | ^^^^^^^ above this are the incoming code changes + | ^^^^^^^ this marker concludes the conflict region | - = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code - = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased - = note: for an explanation on these markers from the `git` documentation, visit + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you're having merge conflicts after pulling new code: + the top section is the code you already had and the bottom section is the remote code + if you're in the middle of a rebase: + the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation: + visit error: aborting due to 1 previous error diff --git a/tests/ui/parser/diff-markers/fn-arg.stderr b/tests/ui/parser/diff-markers/fn-arg.stderr index aabcb826c128..24521ffa6262 100644 --- a/tests/ui/parser/diff-markers/fn-arg.stderr +++ b/tests/ui/parser/diff-markers/fn-arg.stderr @@ -2,17 +2,22 @@ error: encountered diff marker --> $DIR/fn-arg.rs:3:1 | LL | <<<<<<< HEAD - | ^^^^^^^ after this is the code before the merge + | ^^^^^^^ between this marker and `=======` is the code that we're merging into LL | x: u8, LL | ======= - | ------- + | ------- between this marker and `>>>>>>>` is the incoming code LL | x: i8, LL | >>>>>>> branch - | ^^^^^^^ above this are the incoming code changes + | ^^^^^^^ this marker concludes the conflict region | - = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code - = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased - = note: for an explanation on these markers from the `git` documentation, visit + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you're having merge conflicts after pulling new code: + the top section is the code you already had and the bottom section is the remote code + if you're in the middle of a rebase: + the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation: + visit error: aborting due to 1 previous error diff --git a/tests/ui/parser/diff-markers/item-with-attr.stderr b/tests/ui/parser/diff-markers/item-with-attr.stderr index eefb2792e90b..432673cd5518 100644 --- a/tests/ui/parser/diff-markers/item-with-attr.stderr +++ b/tests/ui/parser/diff-markers/item-with-attr.stderr @@ -2,17 +2,22 @@ error: encountered diff marker --> $DIR/item-with-attr.rs:2:1 | LL | <<<<<<< HEAD - | ^^^^^^^ after this is the code before the merge + | ^^^^^^^ between this marker and `=======` is the code that we're merging into LL | fn foo() {} LL | ======= - | ------- + | ------- between this marker and `>>>>>>>` is the incoming code LL | fn bar() {} LL | >>>>>>> branch - | ^^^^^^^ above this are the incoming code changes + | ^^^^^^^ this marker concludes the conflict region | - = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code - = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased - = note: for an explanation on these markers from the `git` documentation, visit + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you're having merge conflicts after pulling new code: + the top section is the code you already had and the bottom section is the remote code + if you're in the middle of a rebase: + the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation: + visit error: aborting due to 1 previous error diff --git a/tests/ui/parser/diff-markers/item.stderr b/tests/ui/parser/diff-markers/item.stderr index a3092ebfcfd3..180c74e5d696 100644 --- a/tests/ui/parser/diff-markers/item.stderr +++ b/tests/ui/parser/diff-markers/item.stderr @@ -2,17 +2,22 @@ error: encountered diff marker --> $DIR/item.rs:1:1 | LL | <<<<<<< HEAD - | ^^^^^^^ after this is the code before the merge + | ^^^^^^^ between this marker and `=======` is the code that we're merging into LL | fn foo() {} LL | ======= - | ------- + | ------- between this marker and `>>>>>>>` is the incoming code LL | fn bar() {} LL | >>>>>>> branch - | ^^^^^^^ above this are the incoming code changes + | ^^^^^^^ this marker concludes the conflict region | - = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code - = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased - = note: for an explanation on these markers from the `git` documentation, visit + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you're having merge conflicts after pulling new code: + the top section is the code you already had and the bottom section is the remote code + if you're in the middle of a rebase: + the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation: + visit error: aborting due to 1 previous error diff --git a/tests/ui/parser/diff-markers/statement.stderr b/tests/ui/parser/diff-markers/statement.stderr index c6c6cae87659..6dccce4a48ee 100644 --- a/tests/ui/parser/diff-markers/statement.stderr +++ b/tests/ui/parser/diff-markers/statement.stderr @@ -2,17 +2,22 @@ error: encountered diff marker --> $DIR/statement.rs:10:1 | LL | <<<<<<< HEAD - | ^^^^^^^ after this is the code before the merge + | ^^^^^^^ between this marker and `=======` is the code that we're merging into LL | S::foo(); LL | ======= - | ------- + | ------- between this marker and `>>>>>>>` is the incoming code LL | S::bar(); LL | >>>>>>> branch - | ^^^^^^^ above this are the incoming code changes + | ^^^^^^^ this marker concludes the conflict region | - = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code - = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased - = note: for an explanation on these markers from the `git` documentation, visit + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you're having merge conflicts after pulling new code: + the top section is the code you already had and the bottom section is the remote code + if you're in the middle of a rebase: + the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation: + visit error: aborting due to 1 previous error diff --git a/tests/ui/parser/diff-markers/struct-expr.stderr b/tests/ui/parser/diff-markers/struct-expr.stderr index bdea8c841c63..3733cdd34964 100644 --- a/tests/ui/parser/diff-markers/struct-expr.stderr +++ b/tests/ui/parser/diff-markers/struct-expr.stderr @@ -2,17 +2,22 @@ error: encountered diff marker --> $DIR/struct-expr.rs:6:1 | LL | <<<<<<< HEAD - | ^^^^^^^ after this is the code before the merge + | ^^^^^^^ between this marker and `=======` is the code that we're merging into LL | x: 42, LL | ======= - | ------- + | ------- between this marker and `>>>>>>>` is the incoming code LL | x: 0, LL | >>>>>>> branch - | ^^^^^^^ above this are the incoming code changes + | ^^^^^^^ this marker concludes the conflict region | - = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code - = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased - = note: for an explanation on these markers from the `git` documentation, visit + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you're having merge conflicts after pulling new code: + the top section is the code you already had and the bottom section is the remote code + if you're in the middle of a rebase: + the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation: + visit error: aborting due to 1 previous error diff --git a/tests/ui/parser/diff-markers/struct.stderr b/tests/ui/parser/diff-markers/struct.stderr index 749941290cb8..44f8346613e6 100644 --- a/tests/ui/parser/diff-markers/struct.stderr +++ b/tests/ui/parser/diff-markers/struct.stderr @@ -2,17 +2,22 @@ error: encountered diff marker --> $DIR/struct.rs:2:1 | LL | <<<<<<< HEAD - | ^^^^^^^ after this is the code before the merge + | ^^^^^^^ between this marker and `=======` is the code that we're merging into LL | x: u8, LL | ======= - | ------- + | ------- between this marker and `>>>>>>>` is the incoming code LL | x: i8, LL | >>>>>>> branch - | ^^^^^^^ above this are the incoming code changes + | ^^^^^^^ this marker concludes the conflict region | - = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code - = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased - = note: for an explanation on these markers from the `git` documentation, visit + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you're having merge conflicts after pulling new code: + the top section is the code you already had and the bottom section is the remote code + if you're in the middle of a rebase: + the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation: + visit error: aborting due to 1 previous error diff --git a/tests/ui/parser/diff-markers/trait-item.stderr b/tests/ui/parser/diff-markers/trait-item.stderr index f01bbe8ba034..4361542c7743 100644 --- a/tests/ui/parser/diff-markers/trait-item.stderr +++ b/tests/ui/parser/diff-markers/trait-item.stderr @@ -2,17 +2,22 @@ error: encountered diff marker --> $DIR/trait-item.rs:2:1 | LL | <<<<<<< HEAD - | ^^^^^^^ after this is the code before the merge + | ^^^^^^^ between this marker and `=======` is the code that we're merging into LL | fn foo() {} LL | ======= - | ------- + | ------- between this marker and `>>>>>>>` is the incoming code LL | fn bar() {} LL | >>>>>>> branch - | ^^^^^^^ above this are the incoming code changes + | ^^^^^^^ this marker concludes the conflict region | - = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code - = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased - = note: for an explanation on these markers from the `git` documentation, visit + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you're having merge conflicts after pulling new code: + the top section is the code you already had and the bottom section is the remote code + if you're in the middle of a rebase: + the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation: + visit error: aborting due to 1 previous error diff --git a/tests/ui/parser/diff-markers/tuple-struct.stderr b/tests/ui/parser/diff-markers/tuple-struct.stderr index 8dae123c96dc..7fda24ba4853 100644 --- a/tests/ui/parser/diff-markers/tuple-struct.stderr +++ b/tests/ui/parser/diff-markers/tuple-struct.stderr @@ -2,17 +2,22 @@ error: encountered diff marker --> $DIR/tuple-struct.rs:2:1 | LL | <<<<<<< HEAD - | ^^^^^^^ after this is the code before the merge + | ^^^^^^^ between this marker and `=======` is the code that we're merging into LL | u8, LL | ======= - | ------- + | ------- between this marker and `>>>>>>>` is the incoming code LL | i8, LL | >>>>>>> branch - | ^^^^^^^ above this are the incoming code changes + | ^^^^^^^ this marker concludes the conflict region | - = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code - = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased - = note: for an explanation on these markers from the `git` documentation, visit + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you're having merge conflicts after pulling new code: + the top section is the code you already had and the bottom section is the remote code + if you're in the middle of a rebase: + the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation: + visit error: aborting due to 1 previous error diff --git a/tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr b/tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr index 6995b8e6f237..927821ddfaed 100644 --- a/tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr +++ b/tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr @@ -2,17 +2,22 @@ error: encountered diff marker --> $DIR/unclosed-delims-in-macro.rs:2:1 | LL | <<<<<<< HEAD - | ^^^^^^^ after this is the code before the merge + | ^^^^^^^ between this marker and `=======` is the code that we're merging into ... LL | ======= - | ------- + | ------- between this marker and `>>>>>>>` is the incoming code LL | () { // LL | >>>>>>> 7a4f13c blah blah blah - | ^^^^^^^ above this are the incoming code changes + | ^^^^^^^ this marker concludes the conflict region | - = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code - = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased - = note: for an explanation on these markers from the `git` documentation, visit + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you're having merge conflicts after pulling new code: + the top section is the code you already had and the bottom section is the remote code + if you're in the middle of a rebase: + the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation: + visit error: aborting due to 1 previous error diff --git a/tests/ui/parser/diff-markers/unclosed-delims.rs b/tests/ui/parser/diff-markers/unclosed-delims.rs index 653a605c28c6..7d400c3827bb 100644 --- a/tests/ui/parser/diff-markers/unclosed-delims.rs +++ b/tests/ui/parser/diff-markers/unclosed-delims.rs @@ -2,13 +2,17 @@ mod tests { #[test] <<<<<<< HEAD //~^ ERROR encountered diff marker -//~| NOTE after this is the code before the merge +//~| NOTE between this marker and `=======` + +//~| NOTE conflict markers indicate that +//~| HELP if you're having merge conflicts //~| NOTE for an explanation on these markers + fn test1() { ======= -//~^ NOTE +//~^ NOTE between this marker and `>>>>>>>` fn test2() { >>>>>>> 7a4f13c blah blah blah -//~^ NOTE above this are the incoming code changes +//~^ NOTE this marker concludes the conflict region } } diff --git a/tests/ui/parser/diff-markers/unclosed-delims.stderr b/tests/ui/parser/diff-markers/unclosed-delims.stderr index d4636150e66b..1eab96442b4f 100644 --- a/tests/ui/parser/diff-markers/unclosed-delims.stderr +++ b/tests/ui/parser/diff-markers/unclosed-delims.stderr @@ -2,17 +2,22 @@ error: encountered diff marker --> $DIR/unclosed-delims.rs:3:1 | LL | <<<<<<< HEAD - | ^^^^^^^ after this is the code before the merge + | ^^^^^^^ between this marker and `=======` is the code that we're merging into ... LL | ======= - | ------- + | ------- between this marker and `>>>>>>>` is the incoming code ... LL | >>>>>>> 7a4f13c blah blah blah - | ^^^^^^^ above this are the incoming code changes + | ^^^^^^^ this marker concludes the conflict region | - = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code - = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased - = note: for an explanation on these markers from the `git` documentation, visit + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you're having merge conflicts after pulling new code: + the top section is the code you already had and the bottom section is the remote code + if you're in the middle of a rebase: + the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation: + visit error: aborting due to 1 previous error diff --git a/tests/ui/parser/diff-markers/use-statement.stderr b/tests/ui/parser/diff-markers/use-statement.stderr index 6d376166a7f7..3eac7bebb5af 100644 --- a/tests/ui/parser/diff-markers/use-statement.stderr +++ b/tests/ui/parser/diff-markers/use-statement.stderr @@ -2,17 +2,22 @@ error: encountered diff marker --> $DIR/use-statement.rs:2:1 | LL | <<<<<<< HEAD - | ^^^^^^^ after this is the code before the merge + | ^^^^^^^ between this marker and `=======` is the code that we're merging into LL | bar, LL | ======= - | ------- + | ------- between this marker and `>>>>>>>` is the incoming code LL | baz, LL | >>>>>>> branch - | ^^^^^^^ above this are the incoming code changes + | ^^^^^^^ this marker concludes the conflict region | - = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code - = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased - = note: for an explanation on these markers from the `git` documentation, visit + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you're having merge conflicts after pulling new code: + the top section is the code you already had and the bottom section is the remote code + if you're in the middle of a rebase: + the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation: + visit error: aborting due to 1 previous error From 9f6371236f516ac2a883eb8707839ab80fc7f9f7 Mon Sep 17 00:00:00 2001 From: ardi Date: Mon, 10 Jun 2024 10:16:51 +0200 Subject: [PATCH 058/147] make this comment correct --- compiler/rustc_parse/src/parser/diagnostics.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index e4630bbceb9b..db23ce6fb7b3 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2958,9 +2958,10 @@ impl<'a> Parser<'a> { /// This checks if this is a conflict marker, depending of the parameter passed. /// - /// * `>>>>>` - /// * `=====` - /// * `<<<<<` + /// * `<<<<<<<` + /// * `|||||||` + /// * `=======` + /// * `>>>>>>>` /// pub(super) fn is_vcs_conflict_marker( &mut self, From c20d909701379ac932a49a946fb478477d907041 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 18 Jun 2024 12:49:43 -0400 Subject: [PATCH 059/147] Make rustc_type_ir nightly again --- compiler/rustc_type_ir/Cargo.toml | 2 +- compiler/rustc_type_ir/src/binder.rs | 6 +++++- compiler/rustc_type_ir/src/data_structures.rs | 19 +++++++++++++++++++ compiler/rustc_type_ir/src/error.rs | 2 +- compiler/rustc_type_ir/src/fold.rs | 3 ++- compiler/rustc_type_ir/src/generic_arg.rs | 1 + compiler/rustc_type_ir/src/inherent.rs | 4 ++-- compiler/rustc_type_ir/src/lib.rs | 10 +--------- compiler/rustc_type_ir/src/opaque_ty.rs | 1 + compiler/rustc_type_ir/src/relate.rs | 9 +++++---- compiler/rustc_type_ir/src/visit.rs | 3 ++- 11 files changed, 40 insertions(+), 20 deletions(-) create mode 100644 compiler/rustc_type_ir/src/data_structures.rs diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index 18a09067a2c9..02d163531b48 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" # tidy-alphabetical-start bitflags = "2.4.1" derivative = "2.2.0" -rustc_ast_ir = { path = "../rustc_ast_ir" } +rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false } rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index e50d59ba5f0e..39ea61f0393d 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -5,14 +5,16 @@ use std::ops::{ControlFlow, Deref}; #[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +#[cfg(feature = "nightly")] use rustc_serialize::Decodable; use tracing::debug; +use crate::data_structures::SsoHashSet; use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; use crate::inherent::*; use crate::lift::Lift; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; -use crate::{self as ty, Interner, SsoHashSet}; +use crate::{self as ty, Interner}; /// Binder is a binder for higher-ranked lifetimes or types. It is part of the /// compiler's representation for things like `for<'a> Fn(&'a isize)` @@ -55,6 +57,7 @@ where } } +#[cfg(feature = "nightly")] macro_rules! impl_binder_encode_decode { ($($t:ty),+ $(,)?) => { $( @@ -82,6 +85,7 @@ macro_rules! impl_binder_encode_decode { } } +#[cfg(feature = "nightly")] impl_binder_encode_decode! { ty::FnSig, ty::TraitPredicate, diff --git a/compiler/rustc_type_ir/src/data_structures.rs b/compiler/rustc_type_ir/src/data_structures.rs new file mode 100644 index 000000000000..86d7b31d2161 --- /dev/null +++ b/compiler/rustc_type_ir/src/data_structures.rs @@ -0,0 +1,19 @@ +#[cfg(feature = "nightly")] +mod impl_ { + pub use rustc_data_structures::fx::FxHashMap as HashMap; + pub use rustc_data_structures::fx::FxHashSet as HashSet; + pub use rustc_data_structures::sso::SsoHashMap as SsoHashMap; + pub use rustc_data_structures::sso::SsoHashSet as SsoHashSet; + pub use rustc_data_structures::sync::Lrc; +} + +#[cfg(not(feature = "nightly"))] +mod impl_ { + pub use std::collections::HashMap; + pub use std::collections::HashSet; + pub use std::collections::HashMap as SsoHashMap; + pub use std::collections::HashSet as SsoHashSet; + pub use std::sync::Arc as Lrc; +} + +pub use impl_::*; \ No newline at end of file diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index 27623ea9cacb..8b59e9a6f481 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -30,7 +30,7 @@ impl ExpectedFound { Debug(bound = "") )] #[derive(TypeVisitable_Generic)] -#[rustc_pass_by_value] +#[cfg_attr(feature = "nightly", rustc_pass_by_value)] pub enum TypeError { Mismatch, ConstnessMismatch(ExpectedFound), diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index ee3e5ce66d03..26e365f64faa 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -49,9 +49,10 @@ use rustc_index::{Idx, IndexVec}; use std::mem; use tracing::debug; +use crate::data_structures::Lrc; use crate::inherent::*; use crate::visit::{TypeVisitable, TypeVisitableExt as _}; -use crate::{self as ty, Interner, Lrc}; +use crate::{self as ty, Interner}; #[cfg(feature = "nightly")] type Never = !; diff --git a/compiler/rustc_type_ir/src/generic_arg.rs b/compiler/rustc_type_ir/src/generic_arg.rs index cc8c44446579..b158f0f5eee9 100644 --- a/compiler/rustc_type_ir/src/generic_arg.rs +++ b/compiler/rustc_type_ir/src/generic_arg.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use crate::Interner; diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 64d3400976a8..be6deee011b9 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -8,8 +8,8 @@ use std::hash::Hash; use std::ops::Deref; use rustc_ast_ir::Mutability; -use rustc_data_structures::fx::FxHashSet; +use crate::data_structures::HashSet; use crate::fold::{TypeFoldable, TypeSuperFoldable}; use crate::relate::Relate; use crate::solve::{CacheData, CanonicalInput, QueryResult, Reveal}; @@ -530,7 +530,7 @@ pub trait EvaluationCache { proof_tree: Option, additional_depth: usize, encountered_overflow: bool, - cycle_participants: FxHashSet>, + cycle_participants: HashSet>, dep_node: I::DepNodeIndex, result: QueryResult, ); diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 130ea231bf7e..9b8ca5efdda8 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -7,27 +7,19 @@ #![cfg_attr(feature = "nightly", allow(internal_features))] // tidy-alphabetical-end -#[cfg(feature = "nightly")] extern crate self as rustc_type_ir; -#[cfg(feature = "nightly")] -use rustc_data_structures::sso::SsoHashSet; -#[cfg(feature = "nightly")] -use rustc_data_structures::sync::Lrc; #[cfg(feature = "nightly")] use rustc_macros::{Decodable, Encodable, HashStable_NoContext}; -#[cfg(not(feature = "nightly"))] -use std::collections::HashSet as SsoHashSet; use std::fmt; use std::hash::Hash; -#[cfg(not(feature = "nightly"))] -use std::sync::Arc as Lrc; // These modules are `pub` since they are not glob-imported. #[macro_use] pub mod visit; #[cfg(feature = "nightly")] pub mod codec; +pub mod data_structures; pub mod error; pub mod fold; pub mod inherent; diff --git a/compiler/rustc_type_ir/src/opaque_ty.rs b/compiler/rustc_type_ir/src/opaque_ty.rs index 607370665975..738350f1b34f 100644 --- a/compiler/rustc_type_ir/src/opaque_ty.rs +++ b/compiler/rustc_type_ir/src/opaque_ty.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 8a6ba87b60e0..429bc3197d44 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -1,12 +1,13 @@ use std::iter; use rustc_ast_ir::Mutability; -use rustc_type_ir::error::{ExpectedFound, TypeError}; -use rustc_type_ir::fold::TypeFoldable; -use rustc_type_ir::inherent::*; -use rustc_type_ir::{self as ty, Interner}; use tracing::{debug, instrument}; +use crate::error::{ExpectedFound, TypeError}; +use crate::fold::TypeFoldable; +use crate::inherent::*; +use crate::{self as ty, Interner}; + pub type RelateResult = Result>; /// Extra information about why we ended up with a particular variance. diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 6880c7b8cefc..473a0aa250ff 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -47,8 +47,9 @@ use rustc_index::{Idx, IndexVec}; use std::fmt; use std::ops::ControlFlow; +use crate::data_structures::Lrc; use crate::inherent::*; -use crate::{self as ty, Interner, Lrc, TypeFlags}; +use crate::{self as ty, Interner, TypeFlags}; /// This trait is implemented for every type that can be visited, /// providing the skeleton of the traversal. From 9b0f9ef42eaef33282c05e698668aff0502885db Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 18 Jun 2024 13:21:48 -0400 Subject: [PATCH 060/147] Make rustc_next_trait_solver nightly again --- Cargo.lock | 1 + compiler/rustc_next_trait_solver/Cargo.toml | 2 +- compiler/rustc_next_trait_solver/src/lib.rs | 2 - .../src/solve/assembly/structural_traits.rs | 62 ++++++++++--------- .../src/solve/eval_ctxt/mod.rs | 41 +++++++----- .../src/solve/search_graph.rs | 17 +++-- .../src/solve/trait_goals.rs | 4 +- compiler/rustc_type_ir/Cargo.toml | 1 + compiler/rustc_type_ir/src/data_structures.rs | 18 ++++-- 9 files changed, 88 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 14ee031ad047..2120b1ed50cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4913,6 +4913,7 @@ version = "0.0.0" dependencies = [ "bitflags 2.5.0", "derivative", + "indexmap", "rustc_ast_ir", "rustc_data_structures", "rustc_index", diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index 3a5f438b4325..07cd4ae68d9a 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" # tidy-alphabetical-start bitflags = "2.4.1" derivative = "2.2.0" -rustc_ast_ir = { path = "../rustc_ast_ir" } +rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false } rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index 79c6925221e1..ea3e18872fa4 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -4,8 +4,6 @@ //! but were uplifted in the process of making the new trait solver generic. //! So if you got to this crate from the old solver, it's totally normal. -#![feature(let_chains)] - pub mod canonicalizer; pub mod infcx; pub mod resolve; diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 202af76565a8..dbe3dfd4a1b5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -2,7 +2,7 @@ //! traits, `Copy`/`Clone`. use rustc_ast_ir::{Movability, Mutability}; -use rustc_data_structures::fx::FxHashMap; +use rustc_type_ir::data_structures::HashMap; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; @@ -304,9 +304,10 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable, I: Interner> { ecx: &'a EvalCtxt<'a, Infcx>, param_env: I::ParamEnv, - mapping: FxHashMap>>, + mapping: HashMap>>, nested: Vec>, } @@ -725,24 +727,28 @@ impl, I: Interner> TypeFolder } fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { - if let ty::Alias(ty::Projection, alias_ty) = ty.kind() - && let Some(replacement) = self.mapping.get(&alias_ty.def_id) - { - // We may have a case where our object type's projection bound is higher-ranked, - // but the where clauses we instantiated are not. We can solve this by instantiating - // the binder at the usage site. - let proj = self.ecx.instantiate_binder_with_infer(*replacement); - // FIXME: Technically this equate could be fallible... - self.nested.extend( - self.ecx - .eq_and_get_goals( - self.param_env, - alias_ty, - proj.projection_term.expect_ty(self.ecx.interner()), - ) - .expect("expected to be able to unify goal projection with dyn's projection"), - ); - proj.term.expect_ty() + if let ty::Alias(ty::Projection, alias_ty) = ty.kind() { + if let Some(replacement) = self.mapping.get(&alias_ty.def_id) { + // We may have a case where our object type's projection bound is higher-ranked, + // but the where clauses we instantiated are not. We can solve this by instantiating + // the binder at the usage site. + let proj = self.ecx.instantiate_binder_with_infer(*replacement); + // FIXME: Technically this equate could be fallible... + self.nested.extend( + self.ecx + .eq_and_get_goals( + self.param_env, + alias_ty, + proj.projection_term.expect_ty(self.ecx.interner()), + ) + .expect( + "expected to be able to unify goal projection with dyn's projection", + ), + ); + proj.term.expect_ty() + } else { + ty.super_fold_with(self) + } } else { ty.super_fold_with(self) } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 485758b91a28..8548c647b6b1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1,7 +1,8 @@ use std::ops::ControlFlow; -use rustc_data_structures::stack::ensure_sufficient_stack; +#[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_type_ir::data_structures::ensure_sufficient_stack; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; @@ -88,7 +89,7 @@ where #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""), Debug(bound = ""), Default(bound = ""))] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[derive(TyDecodable, TyEncodable, HashStable_NoContext)] +#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] // FIXME: This can be made crate-private once `EvalCtxt` also lives in this crate. pub struct NestedGoals { /// These normalizes-to goals are treated specially during the evaluation @@ -116,7 +117,8 @@ impl NestedGoals { } } -#[derive(PartialEq, Eq, Debug, Hash, HashStable_NoContext, Clone, Copy)] +#[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum GenerateProofTree { Yes, No, @@ -689,14 +691,15 @@ where fn visit_ty(&mut self, t: I::Ty) -> Self::Result { match t.kind() { ty::Infer(ty::TyVar(vid)) => { - if let ty::TermKind::Ty(term) = self.term.kind() - && let ty::Infer(ty::TyVar(term_vid)) = term.kind() - && self.infcx.root_ty_var(vid) == self.infcx.root_ty_var(term_vid) - { - ControlFlow::Break(()) - } else { - self.check_nameable(self.infcx.universe_of_ty(vid).unwrap()) + if let ty::TermKind::Ty(term) = self.term.kind() { + if let ty::Infer(ty::TyVar(term_vid)) = term.kind() { + if self.infcx.root_ty_var(vid) == self.infcx.root_ty_var(term_vid) { + return ControlFlow::Break(()); + } + } } + + self.check_nameable(self.infcx.universe_of_ty(vid).unwrap()) } ty::Placeholder(p) => self.check_nameable(p.universe()), _ => { @@ -712,14 +715,18 @@ where fn visit_const(&mut self, c: I::Const) -> Self::Result { match c.kind() { ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { - if let ty::TermKind::Const(term) = self.term.kind() - && let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind() - && self.infcx.root_const_var(vid) == self.infcx.root_const_var(term_vid) - { - ControlFlow::Break(()) - } else { - self.check_nameable(self.infcx.universe_of_ct(vid).unwrap()) + if let ty::TermKind::Const(term) = self.term.kind() { + if let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind() + { + if self.infcx.root_const_var(vid) + == self.infcx.root_const_var(term_vid) + { + return ControlFlow::Break(()); + } + } } + + self.check_nameable(self.infcx.universe_of_ct(vid).unwrap()) } ty::ConstKind::Placeholder(p) => self.check_nameable(p.universe()), _ => { diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index b923a121d814..fc78a864f81a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -1,7 +1,7 @@ use std::mem; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::{Idx, IndexVec}; +use rustc_type_ir::data_structures::{HashMap, HashSet}; use rustc_type_ir::inherent::*; use rustc_type_ir::Interner; use tracing::debug; @@ -17,6 +17,7 @@ pub struct SolverLimit(usize); rustc_index::newtype_index! { #[orderable] + #[gate_rustc_only] pub struct StackDepth {} } @@ -70,7 +71,7 @@ struct StackEntry { /// C :- D /// D :- C /// ``` - cycle_participants: FxHashSet>, + cycle_participants: HashSet>, /// Starts out as `None` and gets set when rerunning this /// goal in case we encounter a cycle. provisional_result: Option>, @@ -126,7 +127,7 @@ pub(super) struct SearchGraph { /// /// An element is *deeper* in the stack if its index is *lower*. stack: IndexVec>, - provisional_cache: FxHashMap, ProvisionalCacheEntry>, + provisional_cache: HashMap, ProvisionalCacheEntry>, } impl SearchGraph { @@ -227,13 +228,17 @@ impl SearchGraph { } fn clear_dependent_provisional_results( - provisional_cache: &mut FxHashMap, ProvisionalCacheEntry>, + provisional_cache: &mut HashMap, ProvisionalCacheEntry>, head: StackDepth, ) { #[allow(rustc::potential_query_instability)] provisional_cache.retain(|_, entry| { - entry.with_coinductive_stack.take_if(|p| p.head == head); - entry.with_inductive_stack.take_if(|p| p.head == head); + if entry.with_coinductive_stack.as_ref().is_some_and(|p| p.head == head) { + entry.with_coinductive_stack.take(); + } + if entry.with_inductive_stack.as_ref().is_some_and(|p| p.head == head) { + entry.with_inductive_stack.take(); + } !entry.is_empty() }); } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index d1419bf5db9a..c0353f92bf8a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1,7 +1,7 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. use rustc_ast_ir::Movability; -use rustc_data_structures::fx::FxIndexSet; +use rustc_type_ir::data_structures::IndexSet; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::visit::TypeVisitableExt as _; @@ -821,7 +821,7 @@ where // We may upcast to auto traits that are either explicitly listed in // the object type's bounds, or implied by the principal trait ref's // supertraits. - let a_auto_traits: FxIndexSet = a_data + let a_auto_traits: IndexSet = a_data .auto_traits() .into_iter() .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| { diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index 02d163531b48..e4bf6069caf3 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" # tidy-alphabetical-start bitflags = "2.4.1" derivative = "2.2.0" +indexmap = "2.0.0" rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false } rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } diff --git a/compiler/rustc_type_ir/src/data_structures.rs b/compiler/rustc_type_ir/src/data_structures.rs index 86d7b31d2161..6d8ab61b722f 100644 --- a/compiler/rustc_type_ir/src/data_structures.rs +++ b/compiler/rustc_type_ir/src/data_structures.rs @@ -2,18 +2,28 @@ mod impl_ { pub use rustc_data_structures::fx::FxHashMap as HashMap; pub use rustc_data_structures::fx::FxHashSet as HashSet; - pub use rustc_data_structures::sso::SsoHashMap as SsoHashMap; - pub use rustc_data_structures::sso::SsoHashSet as SsoHashSet; + pub use rustc_data_structures::fx::FxIndexMap as IndexMap; + pub use rustc_data_structures::fx::FxIndexSet as IndexSet; + pub use rustc_data_structures::sso::SsoHashMap; + pub use rustc_data_structures::sso::SsoHashSet; + pub use rustc_data_structures::stack::ensure_sufficient_stack; pub use rustc_data_structures::sync::Lrc; } #[cfg(not(feature = "nightly"))] mod impl_ { + pub use indexmap::IndexMap; + pub use indexmap::IndexSet; pub use std::collections::HashMap; - pub use std::collections::HashSet; pub use std::collections::HashMap as SsoHashMap; + pub use std::collections::HashSet; pub use std::collections::HashSet as SsoHashSet; pub use std::sync::Arc as Lrc; + + #[inline] + pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { + f() + } } -pub use impl_::*; \ No newline at end of file +pub use impl_::*; From 1299aef92112945e9587c168caed8d6486ffbf30 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 18 Jun 2024 19:33:21 -0500 Subject: [PATCH 061/147] Make pretty printing for `f16` and `f128` consistent Currently the docs show e.g. {transmute(0xfffeffffffffffffffffffffffffffff): f128} for f128 constants. This should fix that to instead use apfloat for printing, as is done for `f32` and `f64`. --- compiler/rustc_middle/src/ty/print/pretty.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index de1796d48002..72cb3e134027 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -7,7 +7,7 @@ use crate::ty::{ ConstInt, Expr, ParamConst, ScalarInt, Term, TermKind, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; -use rustc_apfloat::ieee::{Double, Single}; +use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::Float; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::unord::UnordMap; @@ -1710,6 +1710,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::Bool if int == ScalarInt::FALSE => p!("false"), ty::Bool if int == ScalarInt::TRUE => p!("true"), // Float + ty::Float(ty::FloatTy::F16) => { + let val = Half::try_from(int).unwrap(); + p!(write("{}{}f16", val, if val.is_finite() { "" } else { "_" })) + } ty::Float(ty::FloatTy::F32) => { let val = Single::try_from(int).unwrap(); p!(write("{}{}f32", val, if val.is_finite() { "" } else { "_" })) @@ -1718,6 +1722,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let val = Double::try_from(int).unwrap(); p!(write("{}{}f64", val, if val.is_finite() { "" } else { "_" })) } + ty::Float(ty::FloatTy::F128) => { + let val = Quad::try_from(int).unwrap(); + p!(write("{}{}f128", val, if val.is_finite() { "" } else { "_" })) + } // Int ty::Uint(_) | ty::Int(_) => { let int = From 2126c1d44682e7fca5ea8f49e394e92a6aca0843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 19 Jun 2024 03:08:34 +0200 Subject: [PATCH 062/147] rustc_type_ir: Omit some struct fields from Debug output --- compiler/rustc_type_ir/src/binder.rs | 1 + compiler/rustc_type_ir/src/predicate.rs | 4 ++-- compiler/rustc_type_ir/src/ty_kind.rs | 4 ++-- .../occurs-check/associated-type.next.stderr | 8 ++++---- .../occurs-check/associated-type.old.stderr | 16 ++++++++-------- .../structually-relate-aliases.stderr | 4 ++-- .../next-solver/issue-118950-root-region.stderr | 8 ++++---- 7 files changed, 23 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index e50d59ba5f0e..5042a38fdb8e 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -361,6 +361,7 @@ impl TypeVisitor for ValidateBoundVars { #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub struct EarlyBinder { value: T, + #[derivative(Debug = "ignore")] _tcx: PhantomData, } diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index c0713dc50d26..d7201239d5f9 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -479,8 +479,8 @@ pub struct AliasTerm { /// aka. `interner.parent(def_id)`. pub def_id: I::DefId, - /// This field exists to prevent the creation of `AliasTerm` without using - /// [AliasTerm::new]. + /// This field exists to prevent the creation of `AliasTerm` without using [`AliasTerm::new`]. + #[derivative(Debug = "ignore")] _use_alias_term_new_instead: (), } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 8b4ad2f5ed0f..915888ab59b9 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -452,8 +452,8 @@ pub struct AliasTy { /// aka. `interner.parent(def_id)`. pub def_id: I::DefId, - /// This field exists to prevent the creation of `AliasTy` without using - /// [AliasTy::new]. + /// This field exists to prevent the creation of `AliasTy` without using [`AliasTy::new`]. + #[derivative(Debug = "ignore")] pub(crate) _use_alias_ty_new_instead: (), } diff --git a/tests/ui/coherence/occurs-check/associated-type.next.stderr b/tests/ui/coherence/occurs-check/associated-type.next.stderr index bd65cee58d9b..4f15be4c7c88 100644 --- a/tests/ui/coherence/occurs-check/associated-type.next.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.next.stderr @@ -1,7 +1,7 @@ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())` --> $DIR/associated-type.rs:31:1 | diff --git a/tests/ui/coherence/occurs-check/associated-type.old.stderr b/tests/ui/coherence/occurs-check/associated-type.old.stderr index d26f7665ee73..329086ab7dfd 100644 --- a/tests/ui/coherence/occurs-check/associated-type.old.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.old.stderr @@ -1,11 +1,11 @@ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), _)>` for type `for<'a> fn(&'a (), _)` --> $DIR/associated-type.rs:31:1 | diff --git a/tests/ui/higher-ranked/structually-relate-aliases.stderr b/tests/ui/higher-ranked/structually-relate-aliases.stderr index e1cffa0fc376..2f1dfd19c483 100644 --- a/tests/ui/higher-ranked/structually-relate-aliases.stderr +++ b/tests/ui/higher-ranked/structually-relate-aliases.stderr @@ -1,5 +1,5 @@ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, !2_0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, !2_0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied --> $DIR/structually-relate-aliases.rs:13:36 | diff --git a/tests/ui/traits/next-solver/issue-118950-root-region.stderr b/tests/ui/traits/next-solver/issue-118950-root-region.stderr index 04c44276195d..17da1f524796 100644 --- a/tests/ui/traits/next-solver/issue-118950-root-region.stderr +++ b/tests/ui/traits/next-solver/issue-118950-root-region.stderr @@ -25,10 +25,10 @@ help: this trait has no implementations, consider adding one LL | trait ToUnit<'a> { | ^^^^^^^^^^^^^^^^ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), _use_alias_ty_new_instead: () } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } error[E0119]: conflicting implementations of trait `Overlap` for type `fn(_)` --> $DIR/issue-118950-root-region.rs:19:1 | From 087add3cb294ccb8cd07bc11f80f3877bc304d27 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 18 Jun 2024 22:04:58 -0400 Subject: [PATCH 063/147] Update cargo --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index a1f47ec3f7cd..3ed207e416fb 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit a1f47ec3f7cd076986f1bfcd7061f2e8cb1a726e +Subproject commit 3ed207e416fb2f678a40cc79c02dcf4f936a21ce From e003a12ad540835083bcefdb77f65bb94ca24279 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 18 Jun 2024 20:54:11 -0700 Subject: [PATCH 064/147] Unconditionally warn on usage of `wasm32-wasi` This commit is a continuation of the work originally proposed in rust-lang/compiler-team#607 and later amended in rust-lang/compiler-team#695. The end goal is to rename `wasm32-wasi` to `wasm32-wasip1` to reflect WASI's development and distinguish the preexisting target from the `wasm32-wasip2` target that WASI is now developing. Work for this transition began in #120468 which landed in Rust 1.78 which became stable on 2024-05-02. This implements the next phase of the transition plan to warn on usage of `wasm32-wasi`. This is intended to help alert users that a removal is pending and all release channels have the replacement available as well. This will reach stable on 2024-09-05. The next stage of the plan is to remove the `wasm32-wasi` target some time in October 2024 which means that the removal will reach stable on 2025-01-09. For reference a full schedule of this transition is listed [here]. Currently this implementation is a simple unconditional warning whenever `rustc --target wasm32-wasi` is invoked. As-implemented there's no way to turn off the warning other than to switch to the `wasm32-wasip1` target. [here]: https://github.com/rust-lang/rust/pull/120468#issuecomment-1977878747 --- compiler/rustc_session/src/config.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 5f9c3a14d603..24143808ef49 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1311,6 +1311,20 @@ pub fn build_target_config(early_dcx: &EarlyDiagCtxt, opts: &Options, sysroot: & for warning in warnings.warning_messages() { early_dcx.early_warn(warning) } + + // The `wasm32-wasi` target is being renamed to `wasm32-wasip1` as + // part of rust-lang/compiler-team#607 and + // rust-lang/compiler-team#695. Warn unconditionally on usage to + // raise awareness of the renaming. This code will be deleted in + // October 2024. + if opts.target_triple.triple() == "wasm32-wasi" { + early_dcx.early_warn( + "the `wasm32-wasi` target is being renamed to \ + `wasm32-wasip1` and the `wasm32-wasi` target will be \ + removed from nightly in October 2024 and removed from \ + stable Rust in January 2025", + ) + } if !matches!(target.pointer_width, 16 | 32 | 64) { early_dcx.early_fatal(format!( "target specification was invalid: unrecognized target-pointer-width {}", From 3594a19f2ae792cf99a1a78a5454e52cfd7affde Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 17 Jun 2024 08:34:26 +0000 Subject: [PATCH 065/147] Taint infcx when reporting errors --- .../error_reporting/type_err_ctxt_ext.rs | 23 ++--- tests/crashes/122044.rs | 38 -------- tests/crashes/123255.rs | 13 --- .../incremental/const-generics/issue-64087.rs | 2 + .../const-generics/defaults/doesnt_infer.rs | 1 + .../defaults/doesnt_infer.stderr | 31 ++++++- .../defaults/rp_impl_trait_fail.stderr | 9 +- .../generic_arg_infer/issue-91614.rs | 1 + .../generic_arg_infer/issue-91614.stderr | 28 +++--- .../issue-62504.full.stderr | 33 ++++++- .../issue-62504.min.stderr | 33 ++++++- .../generic_const_exprs/issue-62504.rs | 1 + .../object-safety-ok-infer-err.rs | 1 + .../object-safety-ok-infer-err.stderr | 27 +++++- .../infer/cannot-infer-const-args.stderr | 9 +- tests/ui/const-generics/infer/issue-77092.rs | 5 +- .../const-generics/infer/issue-77092.stderr | 29 +++++- .../const-generics/infer/method-chain.stderr | 9 +- .../infer/one-param-uninferred.stderr | 9 +- .../const-generics/infer/uninferred-consts.rs | 1 + .../infer/uninferred-consts.stderr | 27 +++++- .../const-generics/mistyped_const_in_pat.rs} | 4 +- .../mistyped_const_in_pat.stderr | 12 +++ ...ent_generics_of_encoding_impl_trait.stderr | 4 +- .../unify_with_nested_expr.stderr | 7 +- tests/ui/impl-trait/upvar_captures.rs | 16 ++++ tests/ui/impl-trait/upvar_captures.stderr | 14 +++ tests/ui/inference/issue-83606.stderr | 11 ++- tests/ui/issues/issue-98299.rs | 2 + tests/ui/issues/issue-98299.stderr | 55 ++++++++++- .../const_derives/derive-const-use.stderr | 34 ++++++- .../trait-default-body-stability.stderr | 92 ++++++++++++++++++- tests/ui/wf/conflicting-impls.rs | 2 - tests/ui/wf/conflicting-impls.stderr | 27 +----- .../normalization-of-unknown-type.rs} | 8 +- .../normalization-of-unknown-type.stderr | 9 ++ 36 files changed, 470 insertions(+), 157 deletions(-) delete mode 100644 tests/crashes/122044.rs delete mode 100644 tests/crashes/123255.rs rename tests/{crashes/125799.rs => ui/const-generics/mistyped_const_in_pat.rs} (64%) create mode 100644 tests/ui/const-generics/mistyped_const_in_pat.stderr create mode 100644 tests/ui/impl-trait/upvar_captures.rs create mode 100644 tests/ui/impl-trait/upvar_captures.stderr rename tests/{crashes/123276.rs => ui/where-clauses/normalization-of-unknown-type.rs} (59%) create mode 100644 tests/ui/where-clauses/normalization-of-unknown-type.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index e38f79511977..b5f2d34852fc 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -179,6 +179,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { for (error, suppressed) in iter::zip(&errors, &is_suppressed) { if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion { let guar = self.report_fulfillment_error(error); + self.infcx.set_tainted_by_errors(guar); reported = Some(guar); // We want to ignore desugarings here: spans are equivalent even // if one is the result of a desugaring and the other is not. @@ -2686,22 +2687,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - // Given some `ConstArgHasType(?x, usize)`, we should not emit an error such as - // "type annotations needed: cannot satisfy the constant `_` has type `usize`" - // Instead we should emit a normal error suggesting the user to turbofish the - // const parameter that is currently being inferred. Unfortunately we cannot - // nicely emit such an error so we delay an ICE incase nobody else reports it - // for us. - ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { - return self.tcx.sess.dcx().span_delayed_bug( + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ..)) => self + .emit_inference_failure_err( + obligation.cause.body_id, span, - format!( - "`ambiguous ConstArgHasType({:?}, {:?}) unaccompanied by inference error`", - ct, ty - ), - ); - } - + ct.into(), + ErrorCode::E0284, + true, + ), ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) if term.is_infer() => { diff --git a/tests/crashes/122044.rs b/tests/crashes/122044.rs deleted file mode 100644 index 4c1d0de5719e..000000000000 --- a/tests/crashes/122044.rs +++ /dev/null @@ -1,38 +0,0 @@ -//@ known-bug: #122044 -use std::hint::black_box; - -trait Func { - type Ret: Id; -} - -trait Id { - type Assoc; -} -impl Id for u32 {} -impl Id for u32 {} - -impl R, R: Id> Func for F { - type Ret = R; -} - -fn bar() -> impl Copy + Id { - 0u32 -} - -struct Foo { - _func: T, - value: Option<<::Ret as Id>::Assoc>, -} - -fn main() { - let mut fn_def = black_box(Foo { - _func: bar, - value: None, - }); - let fn_ptr = black_box(Foo { - _func: bar as fn() -> _, - value: None, - }); - - fn_def.value = fn_ptr.value; -} diff --git a/tests/crashes/123255.rs b/tests/crashes/123255.rs deleted file mode 100644 index a94a2a0422e7..000000000000 --- a/tests/crashes/123255.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ known-bug: rust-lang/rust#123255 -//@ edition:2021 -#![crate_type = "lib"] - -pub fn a() {} - -mod handlers { - pub struct C(&()); - pub fn c() -> impl Fn() -> C { - let a1 = (); - || C((crate::a(), a1).into()) - } -} diff --git a/tests/incremental/const-generics/issue-64087.rs b/tests/incremental/const-generics/issue-64087.rs index 97f212b3fbba..787f2af8aa39 100644 --- a/tests/incremental/const-generics/issue-64087.rs +++ b/tests/incremental/const-generics/issue-64087.rs @@ -6,4 +6,6 @@ fn combinator() -> [T; S] {} fn main() { combinator().into_iter(); //[cfail1]~^ ERROR type annotations needed + //[cfail1]~| ERROR type annotations needed + //[cfail1]~| ERROR type annotations needed } diff --git a/tests/ui/const-generics/defaults/doesnt_infer.rs b/tests/ui/const-generics/defaults/doesnt_infer.rs index 016685eee9df..f62088210fed 100644 --- a/tests/ui/const-generics/defaults/doesnt_infer.rs +++ b/tests/ui/const-generics/defaults/doesnt_infer.rs @@ -12,4 +12,5 @@ fn main() { let foo = Foo::<1>::foo(); let foo = Foo::foo(); //~^ ERROR type annotations needed for `Foo<_>` + //~| ERROR type annotations needed } diff --git a/tests/ui/const-generics/defaults/doesnt_infer.stderr b/tests/ui/const-generics/defaults/doesnt_infer.stderr index 1e779f75ce08..e099289827e9 100644 --- a/tests/ui/const-generics/defaults/doesnt_infer.stderr +++ b/tests/ui/const-generics/defaults/doesnt_infer.stderr @@ -1,14 +1,37 @@ -error[E0282]: type annotations needed for `Foo<_>` +error[E0284]: type annotations needed for `Foo<_>` --> $DIR/doesnt_infer.rs:13:9 | LL | let foo = Foo::foo(); - | ^^^ + | ^^^ ---------- type must be known at this point | +note: required by a bound in `Foo::::foo` + --> $DIR/doesnt_infer.rs:5:6 + | +LL | impl Foo { + | ^^^^^^^^^^^^ required by this bound in `Foo::::foo` +LL | fn foo() -> Self { + | --- required by a bound in this associated function help: consider giving `foo` an explicit type, where the value of const parameter `N` is specified | LL | let foo: Foo = Foo::foo(); | ++++++++ -error: aborting due to 1 previous error +error[E0284]: type annotations needed for `Foo<_>` + --> $DIR/doesnt_infer.rs:13:9 + | +LL | let foo = Foo::foo(); + | ^^^ --- type must be known at this point + | +note: required by a bound in `Foo` + --> $DIR/doesnt_infer.rs:3:12 + | +LL | struct Foo; + | ^^^^^^^^^^^^^^^^ required by this bound in `Foo` +help: consider giving `foo` an explicit type, where the value of const parameter `N` is specified + | +LL | let foo: Foo = Foo::foo(); + | ++++++++ -For more information about this error, try `rustc --explain E0282`. +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/defaults/rp_impl_trait_fail.stderr b/tests/ui/const-generics/defaults/rp_impl_trait_fail.stderr index 4ed1c0ded9f8..d91e717b3ebe 100644 --- a/tests/ui/const-generics/defaults/rp_impl_trait_fail.stderr +++ b/tests/ui/const-generics/defaults/rp_impl_trait_fail.stderr @@ -31,12 +31,17 @@ LL | 1_u64 | = help: the trait `Traitor<1, 2>` is implemented for `u64` -error[E0282]: type annotations needed +error[E0284]: type annotations needed --> $DIR/rp_impl_trait_fail.rs:28:5 | LL | uwu(); | ^^^ cannot infer the value of the const parameter `N` declared on the function `uwu` | +note: required by a bound in `uwu` + --> $DIR/rp_impl_trait_fail.rs:16:8 + | +LL | fn uwu() -> impl Traitor { + | ^^^^^^^^^^^ required by this bound in `uwu` help: consider specifying the generic argument | LL | uwu::(); @@ -44,5 +49,5 @@ LL | uwu::(); error: aborting due to 4 previous errors -Some errors have detailed explanations: E0277, E0282. +Some errors have detailed explanations: E0277, E0284. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/generic_arg_infer/issue-91614.rs b/tests/ui/const-generics/generic_arg_infer/issue-91614.rs index cfbc5faecd9c..a386b1e5c2bf 100644 --- a/tests/ui/const-generics/generic_arg_infer/issue-91614.rs +++ b/tests/ui/const-generics/generic_arg_infer/issue-91614.rs @@ -5,4 +5,5 @@ use std::simd::Mask; fn main() { let y = Mask::<_, _>::splat(false); //~^ ERROR: type annotations needed + //~| ERROR type annotations needed } diff --git a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr index 563406ad5eaa..b6982e05b889 100644 --- a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr +++ b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr @@ -1,23 +1,29 @@ -error[E0283]: type annotations needed for `Mask<_, _>` +error[E0284]: type annotations needed for `Mask<_, _>` --> $DIR/issue-91614.rs:6:9 | LL | let y = Mask::<_, _>::splat(false); | ^ -------------------------- type must be known at this point | - = note: cannot satisfy `_: MaskElement` - = help: the following types implement trait `MaskElement`: - i16 - i32 - i64 - i8 - isize note: required by a bound in `Mask::::splat` --> $SRC_DIR/core/src/../../portable-simd/crates/core_simd/src/masks.rs:LL:COL -help: consider giving `y` an explicit type, where the type for type parameter `T` is specified +help: consider giving `y` an explicit type, where the value of const parameter `N` is specified | LL | let y: Mask = Mask::<_, _>::splat(false); | ++++++++++++ -error: aborting due to 1 previous error +error[E0284]: type annotations needed for `Mask<_, _>` + --> $DIR/issue-91614.rs:6:9 + | +LL | let y = Mask::<_, _>::splat(false); + | ^ ------------ type must be known at this point + | +note: required by a bound in `Mask` + --> $SRC_DIR/core/src/../../portable-simd/crates/core_simd/src/masks.rs:LL:COL +help: consider giving `y` an explicit type, where the value of const parameter `N` is specified + | +LL | let y: Mask = Mask::<_, _>::splat(false); + | ++++++++++++ -For more information about this error, try `rustc --explain E0283`. +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr b/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr index 5cda4681b5c8..27411646cb72 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr @@ -18,18 +18,41 @@ help: try adding a `where` bound LL | pub const fn new() -> Self where [(); Self::SIZE]: { | +++++++++++++++++++++++ -error[E0282]: type annotations needed for `ArrayHolder<_>` +error[E0284]: type annotations needed for `ArrayHolder<_>` --> $DIR/issue-62504.rs:26:9 | LL | let mut array = ArrayHolder::new(); - | ^^^^^^^^^ + | ^^^^^^^^^ ------------------ type must be known at this point | +note: required by a bound in `ArrayHolder::::new` + --> $DIR/issue-62504.rs:16:6 + | +LL | impl ArrayHolder { + | ^^^^^^^^^^^^^^ required by this bound in `ArrayHolder::::new` +LL | pub const fn new() -> Self { + | --- required by a bound in this associated function help: consider giving `array` an explicit type, where the value of const parameter `X` is specified | LL | let mut array: ArrayHolder = ArrayHolder::new(); | ++++++++++++++++ -error: aborting due to 3 previous errors +error[E0284]: type annotations needed for `ArrayHolder<_>` + --> $DIR/issue-62504.rs:26:9 + | +LL | let mut array = ArrayHolder::new(); + | ^^^^^^^^^ ----------- type must be known at this point + | +note: required by a bound in `ArrayHolder` + --> $DIR/issue-62504.rs:14:20 + | +LL | struct ArrayHolder([u32; X]); + | ^^^^^^^^^^^^^^ required by this bound in `ArrayHolder` +help: consider giving `array` an explicit type, where the value of const parameter `X` is specified + | +LL | let mut array: ArrayHolder = ArrayHolder::new(); + | ++++++++++++++++ -Some errors have detailed explanations: E0282, E0308. -For more information about an error, try `rustc --explain E0282`. +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0284, E0308. +For more information about an error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr b/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr index beb159779ff5..036b9001c03d 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr @@ -22,18 +22,41 @@ note: tuple struct defined here LL | struct ArrayHolder([u32; X]); | ^^^^^^^^^^^ -error[E0282]: type annotations needed for `ArrayHolder<_>` +error[E0284]: type annotations needed for `ArrayHolder<_>` --> $DIR/issue-62504.rs:26:9 | LL | let mut array = ArrayHolder::new(); - | ^^^^^^^^^ + | ^^^^^^^^^ ------------------ type must be known at this point | +note: required by a bound in `ArrayHolder::::new` + --> $DIR/issue-62504.rs:16:6 + | +LL | impl ArrayHolder { + | ^^^^^^^^^^^^^^ required by this bound in `ArrayHolder::::new` +LL | pub const fn new() -> Self { + | --- required by a bound in this associated function help: consider giving `array` an explicit type, where the value of const parameter `X` is specified | LL | let mut array: ArrayHolder = ArrayHolder::new(); | ++++++++++++++++ -error: aborting due to 3 previous errors +error[E0284]: type annotations needed for `ArrayHolder<_>` + --> $DIR/issue-62504.rs:26:9 + | +LL | let mut array = ArrayHolder::new(); + | ^^^^^^^^^ ----------- type must be known at this point + | +note: required by a bound in `ArrayHolder` + --> $DIR/issue-62504.rs:14:20 + | +LL | struct ArrayHolder([u32; X]); + | ^^^^^^^^^^^^^^ required by this bound in `ArrayHolder` +help: consider giving `array` an explicit type, where the value of const parameter `X` is specified + | +LL | let mut array: ArrayHolder = ArrayHolder::new(); + | ++++++++++++++++ -Some errors have detailed explanations: E0282, E0308. -For more information about an error, try `rustc --explain E0282`. +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0284, E0308. +For more information about an error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/generic_const_exprs/issue-62504.rs b/tests/ui/const-generics/generic_const_exprs/issue-62504.rs index b6a6a277843a..f021e1036143 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-62504.rs +++ b/tests/ui/const-generics/generic_const_exprs/issue-62504.rs @@ -25,4 +25,5 @@ impl ArrayHolder { fn main() { let mut array = ArrayHolder::new(); //~^ ERROR: type annotations needed + //~| ERROR type annotations needed } diff --git a/tests/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.rs b/tests/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.rs index 79e9834b54ed..298cfb512e41 100644 --- a/tests/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.rs +++ b/tests/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.rs @@ -18,4 +18,5 @@ fn use_dyn(v: &dyn Foo) where [u8; N + 1]: Sized { fn main() { use_dyn(&()); //~^ ERROR type annotations needed + //~| ERROR type annotations needed } diff --git a/tests/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.stderr b/tests/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.stderr index e800c5d059f4..0c290448fc5a 100644 --- a/tests/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.stderr +++ b/tests/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.stderr @@ -5,15 +5,36 @@ LL | use_dyn(&()); | ^^^^^^^ cannot infer the value of the const parameter `N` declared on the function `use_dyn` | note: required by a bound in `use_dyn` - --> $DIR/object-safety-ok-infer-err.rs:14:55 + --> $DIR/object-safety-ok-infer-err.rs:14:12 | LL | fn use_dyn(v: &dyn Foo) where [u8; N + 1]: Sized { - | ^^^^^ required by this bound in `use_dyn` + | ^^^^^^^^^^^^^^ required by this bound in `use_dyn` help: consider specifying the generic argument | LL | use_dyn::(&()); | +++++ -error: aborting due to 1 previous error +error[E0284]: type annotations needed + --> $DIR/object-safety-ok-infer-err.rs:19:5 + | +LL | use_dyn(&()); + | ^^^^^^^ --- type must be known at this point + | | + | cannot infer the value of the const parameter `N` declared on the function `use_dyn` + | +note: required for `()` to implement `Foo<_>` + --> $DIR/object-safety-ok-infer-err.rs:8:22 + | +LL | impl Foo for () { + | -------------- ^^^^^^ ^^ + | | + | unsatisfied trait bound introduced here + = note: required for the cast from `&()` to `&dyn Foo<_>` +help: consider specifying the generic argument + | +LL | use_dyn::(&()); + | +++++ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/infer/cannot-infer-const-args.stderr b/tests/ui/const-generics/infer/cannot-infer-const-args.stderr index e3caefef10f8..2180ba2f8095 100644 --- a/tests/ui/const-generics/infer/cannot-infer-const-args.stderr +++ b/tests/ui/const-generics/infer/cannot-infer-const-args.stderr @@ -1,9 +1,14 @@ -error[E0282]: type annotations needed +error[E0284]: type annotations needed --> $DIR/cannot-infer-const-args.rs:6:5 | LL | foo(); | ^^^ cannot infer the value of the const parameter `X` declared on the function `foo` | +note: required by a bound in `foo` + --> $DIR/cannot-infer-const-args.rs:1:8 + | +LL | fn foo() -> usize { + | ^^^^^^^^^^^^^^ required by this bound in `foo` help: consider specifying the generic argument | LL | foo::(); @@ -11,4 +16,4 @@ LL | foo::(); error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/infer/issue-77092.rs b/tests/ui/const-generics/infer/issue-77092.rs index fcf7d3282b43..47c594e5b11e 100644 --- a/tests/ui/const-generics/infer/issue-77092.rs +++ b/tests/ui/const-generics/infer/issue-77092.rs @@ -1,14 +1,15 @@ use std::convert::TryInto; fn take_array_from_mut(data: &mut [T], start: usize) -> &mut [T; N] { - (&mut data[start .. start + N]).try_into().unwrap() + (&mut data[start..start + N]).try_into().unwrap() } fn main() { let mut arr = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - for i in 1 .. 4 { + for i in 1..4 { println!("{:?}", take_array_from_mut(&mut arr, i)); //~^ ERROR type annotations needed + //~| ERROR type annotations needed } } diff --git a/tests/ui/const-generics/infer/issue-77092.stderr b/tests/ui/const-generics/infer/issue-77092.stderr index 5b411269862a..1579d217c2d4 100644 --- a/tests/ui/const-generics/infer/issue-77092.stderr +++ b/tests/ui/const-generics/infer/issue-77092.stderr @@ -1,14 +1,37 @@ -error[E0282]: type annotations needed +error[E0284]: type annotations needed --> $DIR/issue-77092.rs:11:26 | LL | println!("{:?}", take_array_from_mut(&mut arr, i)); | ^^^^^^^^^^^^^^^^^^^ cannot infer the value of the const parameter `N` declared on the function `take_array_from_mut` | +note: required by a bound in `take_array_from_mut` + --> $DIR/issue-77092.rs:3:27 + | +LL | fn take_array_from_mut(data: &mut [T], start: usize) -> &mut [T; N] { + | ^^^^^^^^^^^^^^ required by this bound in `take_array_from_mut` help: consider specifying the generic arguments | LL | println!("{:?}", take_array_from_mut::(&mut arr, i)); | ++++++++++ -error: aborting due to 1 previous error +error[E0284]: type annotations needed + --> $DIR/issue-77092.rs:11:26 + | +LL | println!("{:?}", take_array_from_mut(&mut arr, i)); + | ---- ^^^^^^^^^^^^^^^^^^^ cannot infer the value of the const parameter `N` declared on the function `take_array_from_mut` + | | + | type must be known at this point + | + = note: required for `[i32; _]` to implement `Debug` + = note: 1 redundant requirement hidden + = note: required for `&mut [i32; _]` to implement `Debug` +note: required by a bound in `core::fmt::rt::Argument::<'a>::new_debug` + --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL +help: consider specifying the generic arguments + | +LL | println!("{:?}", take_array_from_mut::(&mut arr, i)); + | ++++++++++ -For more information about this error, try `rustc --explain E0282`. +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/infer/method-chain.stderr b/tests/ui/const-generics/infer/method-chain.stderr index 2def9e85ab75..08c664e82f7e 100644 --- a/tests/ui/const-generics/infer/method-chain.stderr +++ b/tests/ui/const-generics/infer/method-chain.stderr @@ -1,9 +1,14 @@ -error[E0282]: type annotations needed +error[E0284]: type annotations needed --> $DIR/method-chain.rs:15:33 | LL | Foo.bar().bar().bar().bar().baz(); | ^^^ cannot infer the value of the const parameter `N` declared on the method `baz` | +note: required by a bound in `Foo::baz` + --> $DIR/method-chain.rs:8:12 + | +LL | fn baz(self) -> Foo { + | ^^^^^^^^^^^^^^ required by this bound in `Foo::baz` help: consider specifying the generic argument | LL | Foo.bar().bar().bar().bar().baz::(); @@ -11,4 +16,4 @@ LL | Foo.bar().bar().bar().bar().baz::(); error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/infer/one-param-uninferred.stderr b/tests/ui/const-generics/infer/one-param-uninferred.stderr index 3e33fec9cefe..68bec93e8262 100644 --- a/tests/ui/const-generics/infer/one-param-uninferred.stderr +++ b/tests/ui/const-generics/infer/one-param-uninferred.stderr @@ -1,9 +1,14 @@ -error[E0282]: type annotations needed +error[E0284]: type annotations needed --> $DIR/one-param-uninferred.rs:9:23 | LL | let _: [u8; 17] = foo(); | ^^^ cannot infer the value of the const parameter `M` declared on the function `foo` | +note: required by a bound in `foo` + --> $DIR/one-param-uninferred.rs:2:24 + | +LL | fn foo() -> [u8; N] { + | ^^^^^^^^^^^^^^ required by this bound in `foo` help: consider specifying the generic arguments | LL | let _: [u8; 17] = foo::<17, M>(); @@ -11,4 +16,4 @@ LL | let _: [u8; 17] = foo::<17, M>(); error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/infer/uninferred-consts.rs b/tests/ui/const-generics/infer/uninferred-consts.rs index 657f4b513042..552e2a15c23b 100644 --- a/tests/ui/const-generics/infer/uninferred-consts.rs +++ b/tests/ui/const-generics/infer/uninferred-consts.rs @@ -8,4 +8,5 @@ impl Foo { fn main() { Foo.foo(); //~^ ERROR type annotations needed + //~| ERROR type annotations needed } diff --git a/tests/ui/const-generics/infer/uninferred-consts.stderr b/tests/ui/const-generics/infer/uninferred-consts.stderr index 0ec6ac9c22e5..e8ad336af70c 100644 --- a/tests/ui/const-generics/infer/uninferred-consts.stderr +++ b/tests/ui/const-generics/infer/uninferred-consts.stderr @@ -1,14 +1,35 @@ -error[E0282]: type annotations needed +error[E0284]: type annotations needed --> $DIR/uninferred-consts.rs:9:9 | LL | Foo.foo(); | ^^^ cannot infer the value of the const parameter `A` declared on the method `foo` | +note: required by a bound in `Foo::foo` + --> $DIR/uninferred-consts.rs:6:12 + | +LL | fn foo(self) {} + | ^^^^^^^^^^^^^^ required by this bound in `Foo::foo` help: consider specifying the generic arguments | LL | Foo.foo::(); | ++++++++ -error: aborting due to 1 previous error +error[E0284]: type annotations needed + --> $DIR/uninferred-consts.rs:9:9 + | +LL | Foo.foo(); + | ^^^ cannot infer the value of the const parameter `B` declared on the method `foo` + | +note: required by a bound in `Foo::foo` + --> $DIR/uninferred-consts.rs:6:28 + | +LL | fn foo(self) {} + | ^^^^^^^^^^^^^^ required by this bound in `Foo::foo` +help: consider specifying the generic arguments + | +LL | Foo.foo::(); + | ++++++++ -For more information about this error, try `rustc --explain E0282`. +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/crashes/125799.rs b/tests/ui/const-generics/mistyped_const_in_pat.rs similarity index 64% rename from tests/crashes/125799.rs rename to tests/ui/const-generics/mistyped_const_in_pat.rs index 62d5438b4e40..6f1dd52872f6 100644 --- a/tests/crashes/125799.rs +++ b/tests/ui/const-generics/mistyped_const_in_pat.rs @@ -1,4 +1,5 @@ -//@ known-bug: rust-lang/rust#125799 +//! Used to ICE rust-lang/rust#125799 due to `isize` != `()` +//! not being detected early due to the conflicting impls. //@ only-x86_64 trait Trait { @@ -10,6 +11,7 @@ impl Trait for Vec { } impl Trait for Vec {} +//~^ ERROR: conflicting implementations const BAR: as Trait>::Assoc = 3; diff --git a/tests/ui/const-generics/mistyped_const_in_pat.stderr b/tests/ui/const-generics/mistyped_const_in_pat.stderr new file mode 100644 index 000000000000..de7516fa37f1 --- /dev/null +++ b/tests/ui/const-generics/mistyped_const_in_pat.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `Trait` for type `Vec` + --> $DIR/mistyped_const_in_pat.rs:13:1 + | +LL | impl Trait for Vec { + | --------------------------- first implementation here +... +LL | impl Trait for Vec {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Vec` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/const-generics/parent_generics_of_encoding_impl_trait.stderr b/tests/ui/const-generics/parent_generics_of_encoding_impl_trait.stderr index 5bef6f3c795e..cb8f56c6bb08 100644 --- a/tests/ui/const-generics/parent_generics_of_encoding_impl_trait.stderr +++ b/tests/ui/const-generics/parent_generics_of_encoding_impl_trait.stderr @@ -5,10 +5,10 @@ LL | generics_of_parent_impl_trait::foo([()]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer the value of const parameter `N` declared on the function `foo` | note: required by a bound in `foo` - --> $DIR/auxiliary/generics_of_parent_impl_trait.rs:5:48 + --> $DIR/auxiliary/generics_of_parent_impl_trait.rs:5:12 | LL | pub fn foo(foo: impl Into<[(); N + 1]>) { - | ^^^^^ required by this bound in `foo` + | ^^^^^^^^^^^^^^ required by this bound in `foo` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/unify_with_nested_expr.stderr b/tests/ui/const-generics/unify_with_nested_expr.stderr index e050254a3e9d..87610db67ae4 100644 --- a/tests/ui/const-generics/unify_with_nested_expr.stderr +++ b/tests/ui/const-generics/unify_with_nested_expr.stderr @@ -5,13 +5,10 @@ LL | bar(); | ^^^ cannot infer the value of the const parameter `N` declared on the function `bar` | note: required by a bound in `bar` - --> $DIR/unify_with_nested_expr.rs:14:10 + --> $DIR/unify_with_nested_expr.rs:12:8 | LL | fn bar() - | --- required by a bound in this function -LL | where -LL | [(); N + 1]:, - | ^^^^^ required by this bound in `bar` + | ^^^^^^^^^^^^^^ required by this bound in `bar` help: consider specifying the generic argument | LL | bar::(); diff --git a/tests/ui/impl-trait/upvar_captures.rs b/tests/ui/impl-trait/upvar_captures.rs new file mode 100644 index 000000000000..61e3cda66f57 --- /dev/null +++ b/tests/ui/impl-trait/upvar_captures.rs @@ -0,0 +1,16 @@ +//! This test used to ICE: rust-lang/rust#123255 +//! Because the errors on `C` were ignored when trying +//! to compute the MIR of the closure, which thus ended +//! up with broken upvars. +//@ edition:2021 +#![crate_type = "lib"] + +pub fn a() {} + +mod handlers { + pub struct C(&()); //~ ERROR missing lifetime specifier + pub fn c() -> impl Fn() -> C { + let a1 = (); + || C((crate::a(), a1).into()) + } +} diff --git a/tests/ui/impl-trait/upvar_captures.stderr b/tests/ui/impl-trait/upvar_captures.stderr new file mode 100644 index 000000000000..b87a16606cc5 --- /dev/null +++ b/tests/ui/impl-trait/upvar_captures.stderr @@ -0,0 +1,14 @@ +error[E0106]: missing lifetime specifier + --> $DIR/upvar_captures.rs:11:18 + | +LL | pub struct C(&()); + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL | pub struct C<'a>(&'a ()); + | ++++ ++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/inference/issue-83606.stderr b/tests/ui/inference/issue-83606.stderr index 8e6ff6d568de..2d74024bb287 100644 --- a/tests/ui/inference/issue-83606.stderr +++ b/tests/ui/inference/issue-83606.stderr @@ -1,9 +1,14 @@ -error[E0282]: type annotations needed for `[usize; _]` +error[E0284]: type annotations needed for `[usize; _]` --> $DIR/issue-83606.rs:8:9 | LL | let _ = foo("foo"); - | ^ + | ^ ---------- type must be known at this point | +note: required by a bound in `foo` + --> $DIR/issue-83606.rs:3:8 + | +LL | fn foo(_: impl std::fmt::Display) -> [usize; N] { + | ^^^^^^^^^^^^^^ required by this bound in `foo` help: consider giving this pattern a type, where the value of const parameter `N` is specified | LL | let _: [usize; N] = foo("foo"); @@ -11,4 +16,4 @@ LL | let _: [usize; N] = foo("foo"); error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/issues/issue-98299.rs b/tests/ui/issues/issue-98299.rs index 63c058f91fc7..ba63d963475a 100644 --- a/tests/ui/issues/issue-98299.rs +++ b/tests/ui/issues/issue-98299.rs @@ -3,6 +3,8 @@ use std::convert::TryFrom; pub fn test_usage(p: ()) { SmallCString::try_from(p).map(|cstr| cstr); //~^ ERROR: type annotations needed + //~| ERROR: type annotations needed + //~| ERROR: type annotations needed } pub struct SmallCString {} diff --git a/tests/ui/issues/issue-98299.stderr b/tests/ui/issues/issue-98299.stderr index becf16d1db91..f4b40cbbd195 100644 --- a/tests/ui/issues/issue-98299.stderr +++ b/tests/ui/issues/issue-98299.stderr @@ -1,14 +1,61 @@ -error[E0282]: type annotations needed for `SmallCString<_>` +error[E0284]: type annotations needed for `SmallCString<_>` --> $DIR/issue-98299.rs:4:36 | LL | SmallCString::try_from(p).map(|cstr| cstr); - | ^^^^ + | ------------ ^^^^ + | | + | type must be known at this point | +note: required by a bound in `SmallCString` + --> $DIR/issue-98299.rs:10:25 + | +LL | pub struct SmallCString {} + | ^^^^^^^^^^^^^^ required by this bound in `SmallCString` help: consider giving this closure parameter an explicit type, where the value of const parameter `N` is specified | LL | SmallCString::try_from(p).map(|cstr: SmallCString| cstr); | +++++++++++++++++ -error: aborting due to 1 previous error +error[E0284]: type annotations needed for `SmallCString<_>` + --> $DIR/issue-98299.rs:4:36 + | +LL | SmallCString::try_from(p).map(|cstr| cstr); + | ------------ ^^^^ + | | + | type must be known at this point + | +note: required for `SmallCString<_>` to implement `TryFrom<()>` + --> $DIR/issue-98299.rs:12:22 + | +LL | impl TryFrom<()> for SmallCString { + | -------------- ^^^^^^^^^^^ ^^^^^^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here +help: consider giving this closure parameter an explicit type, where the value of const parameter `N` is specified + | +LL | SmallCString::try_from(p).map(|cstr: SmallCString| cstr); + | +++++++++++++++++ -For more information about this error, try `rustc --explain E0282`. +error[E0284]: type annotations needed for `SmallCString<_>` + --> $DIR/issue-98299.rs:4:36 + | +LL | SmallCString::try_from(p).map(|cstr| cstr); + | ------------------------- ^^^^ + | | + | type must be known at this point + | +note: required for `SmallCString<_>` to implement `TryFrom<()>` + --> $DIR/issue-98299.rs:12:22 + | +LL | impl TryFrom<()> for SmallCString { + | -------------- ^^^^^^^^^^^ ^^^^^^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here +help: consider giving this closure parameter an explicit type, where the value of const parameter `N` is specified + | +LL | SmallCString::try_from(p).map(|cstr: SmallCString| cstr); + | +++++++++++++++++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr index e45c1a1f46fb..86dfc521fea7 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr @@ -37,6 +37,19 @@ error[E0207]: the const parameter `host` is not constrained by the impl trait, s = note: expressions using a const parameter must map each value to a distinct output value = note: proving the result of expressions other than the parameter are unique is not supported +error[E0284]: type annotations needed + --> $DIR/derive-const-use.rs:18:35 + | +LL | const _: () = assert!(S((), A) == S::default()); + | ^^^^^^^^^^^^ cannot infer the value of the constant `_` + | +note: required for `S` to implement `Default` + --> $DIR/derive-const-use.rs:15:16 + | +LL | #[derive_const(Default, PartialEq)] + | ^^^^^^^ unsatisfied trait bound introduced in this `derive` macro + = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0308]: mismatched types --> $DIR/derive-const-use.rs:16:14 | @@ -49,7 +62,24 @@ LL | pub struct S((), A); found constant `true` = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 6 previous errors +error[E0284]: type annotations needed + --> $DIR/derive-const-use.rs:16:18 + | +LL | #[derive_const(Default, PartialEq)] + | ------- in this derive macro expansion +LL | pub struct S((), A); + | ^ cannot infer the value of the constant `_` + | +note: required for `A` to implement `Default` + --> $DIR/derive-const-use.rs:7:12 + | +LL | impl const Default for A { + | ----- ^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) -Some errors have detailed explanations: E0207, E0308, E0635. +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0207, E0284, E0308, E0635. For more information about an error, try `rustc --explain E0207`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-default-body-stability.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-default-body-stability.stderr index 62c4bc3b7ae6..b58af6bb9d00 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-default-body-stability.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-default-body-stability.stderr @@ -34,6 +34,94 @@ LL | impl const FromResidual for T { = note: expressions using a const parameter must map each value to a distinct output value = note: proving the result of expressions other than the parameter are unique is not supported -error: aborting due to 4 previous errors +error[E0284]: type annotations needed + --> $DIR/trait-default-body-stability.rs:33:6 + | +LL | impl const FromResidual for T { + | ^^^^^ cannot infer the value of the constant `_` + | +note: required for `T` to implement `Try` + --> $DIR/trait-default-body-stability.rs:18:12 + | +LL | impl const Try for T { + | ----- ^^^ ^ + | | + | unsatisfied trait bound introduced here -For more information about this error, try `rustc --explain E0207`. +error[E0284]: type annotations needed + --> $DIR/trait-default-body-stability.rs:44:9 + | +LL | T? + | ^^ cannot infer the value of the constant `_` + | +note: required for `T` to implement `Try` + --> $DIR/trait-default-body-stability.rs:18:12 + | +LL | impl const Try for T { + | ----- ^^^ ^ + | | + | unsatisfied trait bound introduced here + +error[E0284]: type annotations needed + --> $DIR/trait-default-body-stability.rs:44:9 + | +LL | T? + | ^^ cannot infer the value of the constant `_` + | +note: required for `T` to implement `FromResidual` + --> $DIR/trait-default-body-stability.rs:33:12 + | +LL | impl const FromResidual for T { + | ----- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + +error[E0284]: type annotations needed + --> $DIR/trait-default-body-stability.rs:44:9 + | +LL | T? + | ^^ cannot infer the value of the constant `_` + | +note: required for `T` to implement `Try` + --> $DIR/trait-default-body-stability.rs:18:12 + | +LL | impl const Try for T { + | ----- ^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0284]: type annotations needed + --> $DIR/trait-default-body-stability.rs:44:9 + | +LL | T? + | ^^ cannot infer the value of the constant `_` + | +note: required for `T` to implement `FromResidual` + --> $DIR/trait-default-body-stability.rs:33:12 + | +LL | impl const FromResidual for T { + | ----- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0284]: type annotations needed + --> $DIR/trait-default-body-stability.rs:44:9 + | +LL | T? + | ^^ cannot infer the value of the constant `_` + | +note: required for `T` to implement `Try` + --> $DIR/trait-default-body-stability.rs:18:12 + | +LL | impl const Try for T { + | ----- ^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0207, E0284. +For more information about an error, try `rustc --explain E0207`. diff --git a/tests/ui/wf/conflicting-impls.rs b/tests/ui/wf/conflicting-impls.rs index c387199a8bf1..8054eb7c5943 100644 --- a/tests/ui/wf/conflicting-impls.rs +++ b/tests/ui/wf/conflicting-impls.rs @@ -5,7 +5,6 @@ struct Ty; impl TryFrom for u8 { type Error = Ty; fn try_from(_: Ty) -> Result { - //~^ ERROR type annotations needed loop {} } } @@ -14,7 +13,6 @@ impl TryFrom for u8 { //~^ ERROR conflicting implementations of trait type Error = Ty; fn try_from(_: Ty) -> Result { - //~^ ERROR type annotations needed loop {} } } diff --git a/tests/ui/wf/conflicting-impls.stderr b/tests/ui/wf/conflicting-impls.stderr index 15222a9b2ccd..d31ae17aa8f7 100644 --- a/tests/ui/wf/conflicting-impls.stderr +++ b/tests/ui/wf/conflicting-impls.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `TryFrom` for type `u8` - --> $DIR/conflicting-impls.rs:13:1 + --> $DIR/conflicting-impls.rs:12:1 | LL | impl TryFrom for u8 { | ----------------------- first implementation here @@ -7,27 +7,6 @@ LL | impl TryFrom for u8 { LL | impl TryFrom for u8 { | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u8` -error[E0282]: type annotations needed - --> $DIR/conflicting-impls.rs:7:53 - | -LL | fn try_from(_: Ty) -> Result { - | _____________________________________________________^ -LL | | -LL | | loop {} -LL | | } - | |_____^ cannot infer type for enum `Result` +error: aborting due to 1 previous error -error[E0282]: type annotations needed - --> $DIR/conflicting-impls.rs:16:53 - | -LL | fn try_from(_: Ty) -> Result { - | _____________________________________________________^ -LL | | -LL | | loop {} -LL | | } - | |_____^ cannot infer type for enum `Result` - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0119, E0282. -For more information about an error, try `rustc --explain E0119`. +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/crashes/123276.rs b/tests/ui/where-clauses/normalization-of-unknown-type.rs similarity index 59% rename from tests/crashes/123276.rs rename to tests/ui/where-clauses/normalization-of-unknown-type.rs index d2246f595838..c3ff7ed125ac 100644 --- a/tests/crashes/123276.rs +++ b/tests/ui/where-clauses/normalization-of-unknown-type.rs @@ -1,4 +1,6 @@ -//@ known-bug: rust-lang/rust#123276 +//! This test used to ICE: rust-lang/rust#123276 because we did +//! not taint when failing to find the `Foo` type and then tried +//! to normalize it. //@ edition:2021 async fn create_task() { @@ -19,7 +21,9 @@ struct AndThen; impl Filter for AndThen where - Foo: Filter, + Foo: Filter, //~ ERROR: cannot find type `Foo` { type Future = (); } + +fn main() {} diff --git a/tests/ui/where-clauses/normalization-of-unknown-type.stderr b/tests/ui/where-clauses/normalization-of-unknown-type.stderr new file mode 100644 index 000000000000..11b83224352b --- /dev/null +++ b/tests/ui/where-clauses/normalization-of-unknown-type.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `Foo` in this scope + --> $DIR/normalization-of-unknown-type.rs:24:5 + | +LL | Foo: Filter, + | ^^^ not found in this scope + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0412`. From 1cb75dc4a92ef131b2b88bd9faeaa1d39c3d5f4e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 17 Jun 2024 08:53:45 +0000 Subject: [PATCH 066/147] Remove a hack that isn't needed anymore --- compiler/rustc_hir_analysis/src/check/wfcheck.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index b206d8046ee9..a188c1b12aeb 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -119,16 +119,7 @@ where let errors = wfcx.select_all_or_error(); if !errors.is_empty() { - let err = infcx.err_ctxt().report_fulfillment_errors(errors); - if tcx.dcx().has_errors().is_some() { - return Err(err); - } else { - // HACK(oli-obk): tests/ui/specialization/min_specialization/specialize_on_type_error.rs - // causes an delayed bug during normalization, without reporting an error, so we need - // to act as if no error happened, in order to let our callers continue and report an - // error later in check_impl_items_against_trait. - return Ok(()); - } + return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } debug!(?assumed_wf_types); From b6d20d1a1fcd37dcc47c4f2839d80bea4b5cdd85 Mon Sep 17 00:00:00 2001 From: Sayantan Chakraborty <142906350+sayantn@users.noreply.github.com> Date: Tue, 18 Jun 2024 13:03:14 +0530 Subject: [PATCH 067/147] Add the target-features --- compiler/rustc_target/src/target_features.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index d9812540e497..017fd3072fdb 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -208,6 +208,11 @@ const X86_ALLOWED_FEATURES: &[(&str, Stability)] = &[ ("avx512vnni", Unstable(sym::avx512_target_feature)), ("avx512vp2intersect", Unstable(sym::avx512_target_feature)), ("avx512vpopcntdq", Unstable(sym::avx512_target_feature)), + ("avxifma", Unstable(sym::avx512_target_feature)), + ("avxneconvert", Unstable(sym::avx512_target_feature)), + ("avxvnni", Unstable(sym::avx512_target_feature)), + ("avxvnniint16", Unstable(sym::avx512_target_feature)), + ("avxvnniint8", Unstable(sym::avx512_target_feature)), ("bmi1", Stable), ("bmi2", Stable), ("cmpxchg16b", Stable), From 1e1b3fcadafdb28f25f7e19bd05652bd8aa98084 Mon Sep 17 00:00:00 2001 From: sayantn Date: Wed, 19 Jun 2024 00:51:45 +0530 Subject: [PATCH 068/147] Fix stderr cases --- tests/ui/check-cfg/mix.stderr | 2 +- tests/ui/check-cfg/well-known-values.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/check-cfg/mix.stderr b/tests/ui/check-cfg/mix.stderr index b3d0046fc178..cc63466585a6 100644 --- a/tests/ui/check-cfg/mix.stderr +++ b/tests/ui/check-cfg/mix.stderr @@ -251,7 +251,7 @@ warning: unexpected `cfg` condition value: `zebra` LL | cfg!(target_feature = "zebra"); | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, and `bulk-memory` and 186 more + = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `avxifma`, `avxneconvert`, `avxvnni`, `avxvnniint16`, and `avxvnniint8` and 191 more = note: see for more information about checking conditional configuration warning: 27 warnings emitted diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index d2026a68450e..8a99ace75d85 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -165,7 +165,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_feature = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `extended-const`, `f`, `f16c`, `f32mm`, `f64mm`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frecipe`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lahfsahf`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `prfchw`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `unaligned-scalar-mem`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, and `zkt` + = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `avxifma`, `avxneconvert`, `avxvnni`, `avxvnniint16`, `avxvnniint8`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `extended-const`, `f`, `f16c`, `f32mm`, `f64mm`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frecipe`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lahfsahf`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `prfchw`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `unaligned-scalar-mem`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, and `zkt` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` From c894ece8fe607664fbdf2c081da9c997de5905f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 19 Jun 2024 09:19:09 +0200 Subject: [PATCH 069/147] Remove now NOP attrs `#[rustc_dump{,_env}_program_clauses]` --- compiler/rustc_feature/src/builtin_attrs.rs | 8 -------- compiler/rustc_span/src/symbol.rs | 2 -- 2 files changed, 10 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 9b5e4e50d3ca..c165620f657b 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1088,14 +1088,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ErrorFollowing, EncodeCrossCrate::No, "the `#[custom_mir]` attribute is just used for the Rust test suite", ), - rustc_attr!( - TEST, rustc_dump_program_clauses, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::No - ), - rustc_attr!( - TEST, rustc_dump_env_program_clauses, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::No - ), rustc_attr!( TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f44fa1bcb4fd..4719de9d4c03 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1592,8 +1592,6 @@ symbols! { rustc_do_not_const_check, rustc_doc_primitive, rustc_dummy, - rustc_dump_env_program_clauses, - rustc_dump_program_clauses, rustc_dump_user_args, rustc_dump_vtable, rustc_effective_visibility, From 6c7c824767e2e9c992da74e449da27889bbd4bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Fri, 3 May 2024 09:56:13 +0000 Subject: [PATCH 070/147] coverage: Make MCDC take in account last RHS of condition-coverage Condition coverage extends branch coverage to treat the specific case of last operands of boolean decisions not involved in control flow. This is ultimately made for MCDC to be exhaustive on all boolean expressions. This patch adds a call to `visit_branch_coverage_operation` to track the top-level operand of the said decisions, and changes `visit_coverage_standalone_condition` so MCDC branch registration is called when enabled on these _last RHS_ cases. --- .../rustc_mir_build/src/build/coverageinfo.rs | 66 ++++++++++++------- .../rustc_mir_build/src/build/expr/into.rs | 3 + 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs index 855dcbbcb346..876faca5172a 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -118,17 +118,35 @@ impl BranchInfoBuilder { } } - fn add_two_way_branch<'tcx>( + fn register_two_way_branch<'tcx>( &mut self, + tcx: TyCtxt<'tcx>, cfg: &mut CFG<'tcx>, source_info: SourceInfo, true_block: BasicBlock, false_block: BasicBlock, ) { - let true_marker = self.markers.inject_block_marker(cfg, source_info, true_block); - let false_marker = self.markers.inject_block_marker(cfg, source_info, false_block); + // Separate path for handling branches when MC/DC is enabled. + if let Some(mcdc_info) = self.mcdc_info.as_mut() { + let inject_block_marker = + |source_info, block| self.markers.inject_block_marker(cfg, source_info, block); + mcdc_info.visit_evaluated_condition( + tcx, + source_info, + true_block, + false_block, + inject_block_marker, + ); + } else { + let true_marker = self.markers.inject_block_marker(cfg, source_info, true_block); + let false_marker = self.markers.inject_block_marker(cfg, source_info, false_block); - self.branch_spans.push(BranchSpan { span: source_info.span, true_marker, false_marker }); + self.branch_spans.push(BranchSpan { + span: source_info.span, + true_marker, + false_marker, + }); + } } pub(crate) fn into_done(self) -> Option> { @@ -205,7 +223,14 @@ impl<'tcx> Builder<'_, 'tcx> { mir::TerminatorKind::if_(mir::Operand::Copy(place), true_block, false_block), ); - branch_info.add_two_way_branch(&mut self.cfg, source_info, true_block, false_block); + // Separate path for handling branches when MC/DC is enabled. + branch_info.register_two_way_branch( + self.tcx, + &mut self.cfg, + source_info, + true_block, + false_block, + ); let join_block = self.cfg.start_new_block(); self.cfg.goto(true_block, source_info, join_block); @@ -236,22 +261,13 @@ impl<'tcx> Builder<'_, 'tcx> { let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope }; - // Separate path for handling branches when MC/DC is enabled. - if let Some(mcdc_info) = branch_info.mcdc_info.as_mut() { - let inject_block_marker = |source_info, block| { - branch_info.markers.inject_block_marker(&mut self.cfg, source_info, block) - }; - mcdc_info.visit_evaluated_condition( - self.tcx, - source_info, - then_block, - else_block, - inject_block_marker, - ); - return; - } - - branch_info.add_two_way_branch(&mut self.cfg, source_info, then_block, else_block); + branch_info.register_two_way_branch( + self.tcx, + &mut self.cfg, + source_info, + then_block, + else_block, + ); } /// If branch coverage is enabled, inject marker statements into `true_block` @@ -270,6 +286,12 @@ impl<'tcx> Builder<'_, 'tcx> { // FIXME(#124144) This may need special handling when MC/DC is enabled. let source_info = SourceInfo { span: pattern.span, scope: self.source_scope }; - branch_info.add_two_way_branch(&mut self.cfg, source_info, true_block, false_block); + branch_info.register_two_way_branch( + self.tcx, + &mut self.cfg, + source_info, + true_block, + false_block, + ); } } diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index c08a3b6691af..6b4dd8b64a42 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -150,6 +150,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::LogicalOp { op, lhs, rhs } => { let condition_scope = this.local_scope(); let source_info = this.source_info(expr.span); + + this.visit_coverage_branch_operation(op, expr.span); + // We first evaluate the left-hand side of the predicate ... let (then_block, else_block) = this.in_if_then_scope(condition_scope, expr.span, |this| { From e15adef457b25b91906fde1510405fa4f3992188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Fri, 3 May 2024 10:37:02 +0000 Subject: [PATCH 071/147] tests(coverage): Bless mcdc_non_control_flow tests --- tests/coverage/mcdc_non_control_flow.cov-map | 63 ++++---- tests/coverage/mcdc_non_control_flow.coverage | 140 +++++++++++++++--- 2 files changed, 156 insertions(+), 47 deletions(-) diff --git a/tests/coverage/mcdc_non_control_flow.cov-map b/tests/coverage/mcdc_non_control_flow.cov-map index 3f422d85e799..937c36e1f16c 100644 --- a/tests/coverage/mcdc_non_control_flow.cov-map +++ b/tests/coverage/mcdc_non_control_flow.cov-map @@ -1,5 +1,5 @@ Function name: mcdc_non_control_flow::assign_3 -Raw bytes (73): 0x[01, 01, 09, 05, 07, 0b, 11, 09, 0d, 01, 05, 01, 05, 22, 11, 01, 05, 22, 11, 01, 05, 09, 01, 16, 01, 00, 28, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 22, 00, 0d, 00, 0e, 22, 00, 12, 00, 13, 20, 1e, 11, 00, 12, 00, 13, 1e, 00, 17, 00, 18, 20, 09, 0d, 00, 17, 00, 18, 03, 01, 05, 01, 02] +Raw bytes (89): 0x[01, 01, 09, 05, 07, 0b, 11, 09, 0d, 01, 05, 01, 05, 22, 11, 01, 05, 22, 11, 01, 05, 0a, 01, 16, 01, 00, 28, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 03, 00, 0d, 00, 18, 30, 05, 22, 01, 00, 02, 00, 0d, 00, 0e, 22, 00, 12, 00, 13, 30, 1e, 11, 02, 03, 00, 00, 12, 00, 13, 1e, 00, 17, 00, 18, 30, 09, 0d, 03, 00, 00, 00, 17, 00, 18, 03, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 9 @@ -12,29 +12,30 @@ Number of expressions: 9 - expression 6 operands: lhs = Counter(0), rhs = Counter(1) - expression 7 operands: lhs = Expression(8, Sub), rhs = Counter(4) - expression 8 operands: lhs = Counter(0), rhs = Counter(1) -Number of file 0 mappings: 9 +Number of file 0 mappings: 10 - Code(Counter(0)) at (prev + 22, 1) to (start + 0, 40) - Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) = (c1 + ((c2 + c3) + c4)) - Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) -- Branch { true: Counter(1), false: Expression(8, Sub) } at (prev + 0, 13) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 0, conditions_num: 3 } at (prev + 0, 13) to (start + 0, 24) +- MCDCBranch { true: Counter(1), false: Expression(8, Sub), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 13) to (start + 0, 14) true = c1 false = (c0 - c1) - Code(Expression(8, Sub)) at (prev + 0, 18) to (start + 0, 19) = (c0 - c1) -- Branch { true: Expression(7, Sub), false: Counter(4) } at (prev + 0, 18) to (start + 0, 19) +- MCDCBranch { true: Expression(7, Sub), false: Counter(4), condition_id: 2, true_next_id: 3, false_next_id: 0 } at (prev + 0, 18) to (start + 0, 19) true = ((c0 - c1) - c4) false = c4 - Code(Expression(7, Sub)) at (prev + 0, 23) to (start + 0, 24) = ((c0 - c1) - c4) -- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 23) to (start + 0, 24) +- MCDCBranch { true: Counter(2), false: Counter(3), condition_id: 3, true_next_id: 0, false_next_id: 0 } at (prev + 0, 23) to (start + 0, 24) true = c2 false = c3 - Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) = (c1 + ((c2 + c3) + c4)) Function name: mcdc_non_control_flow::assign_3_bis -Raw bytes (82): 0x[01, 01, 07, 07, 11, 09, 0d, 01, 05, 05, 09, 16, 1a, 05, 09, 01, 05, 0a, 01, 1b, 01, 00, 2c, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 02, 00, 0d, 00, 13, 30, 05, 1a, 01, 02, 00, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 30, 09, 16, 02, 00, 00, 00, 12, 00, 13, 13, 00, 17, 00, 18, 20, 0d, 11, 00, 17, 00, 18, 03, 01, 05, 01, 02] +Raw bytes (85): 0x[01, 01, 07, 07, 11, 09, 0d, 01, 05, 05, 09, 16, 1a, 05, 09, 01, 05, 0a, 01, 1b, 01, 00, 2c, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 03, 00, 0d, 00, 18, 30, 05, 1a, 01, 03, 02, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 30, 09, 16, 03, 00, 02, 00, 12, 00, 13, 13, 00, 17, 00, 18, 30, 0d, 11, 02, 00, 00, 00, 17, 00, 18, 03, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 7 @@ -50,24 +51,24 @@ Number of file 0 mappings: 10 - Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) = ((c2 + c3) + c4) - Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) -- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 0, 13) to (start + 0, 19) -- MCDCBranch { true: Counter(1), false: Expression(6, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 0, conditions_num: 3 } at (prev + 0, 13) to (start + 0, 24) +- MCDCBranch { true: Counter(1), false: Expression(6, Sub), condition_id: 1, true_next_id: 3, false_next_id: 2 } at (prev + 0, 13) to (start + 0, 14) true = c1 false = (c0 - c1) - Code(Counter(1)) at (prev + 0, 18) to (start + 0, 19) -- MCDCBranch { true: Counter(2), false: Expression(5, Sub), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 18) to (start + 0, 19) +- MCDCBranch { true: Counter(2), false: Expression(5, Sub), condition_id: 3, true_next_id: 0, false_next_id: 2 } at (prev + 0, 18) to (start + 0, 19) true = c2 false = (c1 - c2) - Code(Expression(4, Add)) at (prev + 0, 23) to (start + 0, 24) = ((c1 - c2) + (c0 - c1)) -- Branch { true: Counter(3), false: Counter(4) } at (prev + 0, 23) to (start + 0, 24) +- MCDCBranch { true: Counter(3), false: Counter(4), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 23) to (start + 0, 24) true = c3 false = c4 - Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) = ((c2 + c3) + c4) Function name: mcdc_non_control_flow::assign_and -Raw bytes (51): 0x[01, 01, 04, 07, 0e, 09, 0d, 01, 05, 01, 05, 07, 01, 0c, 01, 00, 21, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 0e, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 20, 09, 0d, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Raw bytes (64): 0x[01, 01, 04, 07, 0e, 09, 0d, 01, 05, 01, 05, 08, 01, 0c, 01, 00, 21, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 02, 00, 0d, 00, 13, 30, 05, 0e, 01, 02, 00, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 30, 09, 0d, 02, 00, 00, 00, 12, 00, 13, 03, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 4 @@ -75,23 +76,24 @@ Number of expressions: 4 - expression 1 operands: lhs = Counter(2), rhs = Counter(3) - expression 2 operands: lhs = Counter(0), rhs = Counter(1) - expression 3 operands: lhs = Counter(0), rhs = Counter(1) -Number of file 0 mappings: 7 +Number of file 0 mappings: 8 - Code(Counter(0)) at (prev + 12, 1) to (start + 0, 33) - Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) = ((c2 + c3) + (c0 - c1)) - Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) -- Branch { true: Counter(1), false: Expression(3, Sub) } at (prev + 0, 13) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 0, 13) to (start + 0, 19) +- MCDCBranch { true: Counter(1), false: Expression(3, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14) true = c1 false = (c0 - c1) - Code(Counter(1)) at (prev + 0, 18) to (start + 0, 19) -- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 18) to (start + 0, 19) +- MCDCBranch { true: Counter(2), false: Counter(3), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 18) to (start + 0, 19) true = c2 false = c3 - Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) = ((c2 + c3) + (c0 - c1)) Function name: mcdc_non_control_flow::assign_or -Raw bytes (51): 0x[01, 01, 04, 07, 0d, 05, 09, 01, 05, 01, 05, 07, 01, 11, 01, 00, 20, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 0e, 00, 0d, 00, 0e, 0e, 00, 12, 00, 13, 20, 09, 0d, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Raw bytes (64): 0x[01, 01, 04, 07, 0d, 05, 09, 01, 05, 01, 05, 08, 01, 11, 01, 00, 20, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 02, 00, 0d, 00, 13, 30, 05, 0e, 01, 00, 02, 00, 0d, 00, 0e, 0e, 00, 12, 00, 13, 30, 09, 0d, 02, 00, 00, 00, 12, 00, 13, 03, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 4 @@ -99,17 +101,18 @@ Number of expressions: 4 - expression 1 operands: lhs = Counter(1), rhs = Counter(2) - expression 2 operands: lhs = Counter(0), rhs = Counter(1) - expression 3 operands: lhs = Counter(0), rhs = Counter(1) -Number of file 0 mappings: 7 +Number of file 0 mappings: 8 - Code(Counter(0)) at (prev + 17, 1) to (start + 0, 32) - Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) = ((c1 + c2) + c3) - Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) -- Branch { true: Counter(1), false: Expression(3, Sub) } at (prev + 0, 13) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 0, 13) to (start + 0, 19) +- MCDCBranch { true: Counter(1), false: Expression(3, Sub), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 13) to (start + 0, 14) true = c1 false = (c0 - c1) - Code(Expression(3, Sub)) at (prev + 0, 18) to (start + 0, 19) = (c0 - c1) -- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 18) to (start + 0, 19) +- MCDCBranch { true: Counter(2), false: Counter(3), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 18) to (start + 0, 19) true = c2 false = c3 - Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) @@ -124,27 +127,28 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 37, 1) to (start + 2, 2) Function name: mcdc_non_control_flow::func_call -Raw bytes (39): 0x[01, 01, 03, 01, 05, 0b, 02, 09, 0d, 05, 01, 29, 01, 01, 0a, 20, 05, 02, 01, 09, 00, 0a, 05, 00, 0e, 00, 0f, 20, 09, 0d, 00, 0e, 00, 0f, 07, 01, 01, 00, 02] +Raw bytes (52): 0x[01, 01, 03, 01, 05, 0b, 02, 09, 0d, 06, 01, 29, 01, 01, 0a, 28, 00, 02, 01, 09, 00, 0f, 30, 05, 02, 01, 02, 00, 00, 09, 00, 0a, 05, 00, 0e, 00, 0f, 30, 09, 0d, 02, 00, 00, 00, 0e, 00, 0f, 07, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 3 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) - expression 1 operands: lhs = Expression(2, Add), rhs = Expression(0, Sub) - expression 2 operands: lhs = Counter(2), rhs = Counter(3) -Number of file 0 mappings: 5 +Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 41, 1) to (start + 1, 10) -- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 9) to (start + 0, 10) +- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 15) +- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 9) to (start + 0, 10) true = c1 false = (c0 - c1) - Code(Counter(1)) at (prev + 0, 14) to (start + 0, 15) -- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 14) to (start + 0, 15) +- MCDCBranch { true: Counter(2), false: Counter(3), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 14) to (start + 0, 15) true = c2 false = c3 - Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) = ((c2 + c3) + (c0 - c1)) Function name: mcdc_non_control_flow::right_comb_tree -Raw bytes (117): 0x[01, 01, 13, 07, 1a, 0b, 19, 0f, 15, 13, 11, 09, 0d, 01, 05, 01, 05, 05, 19, 05, 19, 4a, 15, 05, 19, 4a, 15, 05, 19, 46, 11, 4a, 15, 05, 19, 46, 11, 4a, 15, 05, 19, 0d, 01, 20, 01, 00, 41, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 1a, 00, 0d, 00, 0e, 05, 00, 13, 00, 14, 20, 4a, 19, 00, 13, 00, 14, 4a, 00, 19, 00, 1a, 20, 46, 15, 00, 19, 00, 1a, 46, 00, 1f, 00, 20, 20, 42, 11, 00, 1f, 00, 20, 42, 00, 24, 00, 27, 20, 09, 0d, 00, 24, 00, 27, 03, 01, 05, 01, 02] +Raw bytes (139): 0x[01, 01, 13, 07, 1a, 0b, 19, 0f, 15, 13, 11, 09, 0d, 01, 05, 01, 05, 05, 19, 05, 19, 4a, 15, 05, 19, 4a, 15, 05, 19, 46, 11, 4a, 15, 05, 19, 46, 11, 4a, 15, 05, 19, 0e, 01, 20, 01, 00, 41, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 05, 00, 0d, 00, 2a, 30, 05, 1a, 01, 02, 00, 00, 0d, 00, 0e, 05, 00, 13, 00, 14, 30, 4a, 19, 02, 03, 00, 00, 13, 00, 14, 4a, 00, 19, 00, 1a, 30, 46, 15, 03, 04, 00, 00, 19, 00, 1a, 46, 00, 1f, 00, 20, 30, 42, 11, 04, 05, 00, 00, 1f, 00, 20, 42, 00, 24, 00, 27, 30, 09, 0d, 05, 00, 00, 00, 24, 00, 27, 03, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 19 @@ -167,31 +171,32 @@ Number of expressions: 19 - expression 16 operands: lhs = Expression(17, Sub), rhs = Counter(4) - expression 17 operands: lhs = Expression(18, Sub), rhs = Counter(5) - expression 18 operands: lhs = Counter(1), rhs = Counter(6) -Number of file 0 mappings: 13 +Number of file 0 mappings: 14 - Code(Counter(0)) at (prev + 32, 1) to (start + 0, 65) - Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) = (((((c2 + c3) + c4) + c5) + c6) + (c0 - c1)) - Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) -- Branch { true: Counter(1), false: Expression(6, Sub) } at (prev + 0, 13) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 0, conditions_num: 5 } at (prev + 0, 13) to (start + 0, 42) +- MCDCBranch { true: Counter(1), false: Expression(6, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14) true = c1 false = (c0 - c1) - Code(Counter(1)) at (prev + 0, 19) to (start + 0, 20) -- Branch { true: Expression(18, Sub), false: Counter(6) } at (prev + 0, 19) to (start + 0, 20) +- MCDCBranch { true: Expression(18, Sub), false: Counter(6), condition_id: 2, true_next_id: 3, false_next_id: 0 } at (prev + 0, 19) to (start + 0, 20) true = (c1 - c6) false = c6 - Code(Expression(18, Sub)) at (prev + 0, 25) to (start + 0, 26) = (c1 - c6) -- Branch { true: Expression(17, Sub), false: Counter(5) } at (prev + 0, 25) to (start + 0, 26) +- MCDCBranch { true: Expression(17, Sub), false: Counter(5), condition_id: 3, true_next_id: 4, false_next_id: 0 } at (prev + 0, 25) to (start + 0, 26) true = ((c1 - c6) - c5) false = c5 - Code(Expression(17, Sub)) at (prev + 0, 31) to (start + 0, 32) = ((c1 - c6) - c5) -- Branch { true: Expression(16, Sub), false: Counter(4) } at (prev + 0, 31) to (start + 0, 32) +- MCDCBranch { true: Expression(16, Sub), false: Counter(4), condition_id: 4, true_next_id: 5, false_next_id: 0 } at (prev + 0, 31) to (start + 0, 32) true = (((c1 - c6) - c5) - c4) false = c4 - Code(Expression(16, Sub)) at (prev + 0, 36) to (start + 0, 39) = (((c1 - c6) - c5) - c4) -- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 36) to (start + 0, 39) +- MCDCBranch { true: Counter(2), false: Counter(3), condition_id: 5, true_next_id: 0, false_next_id: 0 } at (prev + 0, 36) to (start + 0, 39) true = c2 false = c3 - Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) diff --git a/tests/coverage/mcdc_non_control_flow.coverage b/tests/coverage/mcdc_non_control_flow.coverage index 4e53a7a0d8e8..cd733885a98b 100644 --- a/tests/coverage/mcdc_non_control_flow.coverage +++ b/tests/coverage/mcdc_non_control_flow.coverage @@ -12,24 +12,6 @@ LL| 3|fn assign_and(a: bool, b: bool) { LL| 3| let x = a && b; ^2 - LL| 3| black_box(x); - LL| 3|} - LL| | - LL| 3|fn assign_or(a: bool, b: bool) { - LL| 3| let x = a || b; - ^1 - LL| 3| black_box(x); - LL| 3|} - LL| | - LL| 4|fn assign_3(a: bool, b: bool, c: bool) { - LL| 4| let x = a || b && c; - ^2 ^1 - LL| 4| black_box(x); - LL| 4|} - LL| | - LL| 4|fn assign_3_bis(a: bool, b: bool, c: bool) { - LL| 4| let x = a && b || c; - ^2 ^3 ------------------ |---> MC/DC Decision Region (LL:13) to (LL:19) | @@ -48,6 +30,84 @@ | C2-Pair: covered: (2,3) | MC/DC Coverage for Decision: 100.00% | + ------------------ + LL| 3| black_box(x); + LL| 3|} + LL| | + LL| 3|fn assign_or(a: bool, b: bool) { + LL| 3| let x = a || b; + ^1 + ------------------ + |---> MC/DC Decision Region (LL:13) to (LL:19) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:13) + | Condition C2 --> (LL:18) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, F = F } + | 2 { T, - = T } + | + | C1-Pair: covered: (1,2) + | C2-Pair: not covered + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 3| black_box(x); + LL| 3|} + LL| | + LL| 4|fn assign_3(a: bool, b: bool, c: bool) { + LL| 4| let x = a || b && c; + ^2 ^1 + ------------------ + |---> MC/DC Decision Region (LL:13) to (LL:24) + | + | Number of Conditions: 3 + | Condition C1 --> (LL:13) + | Condition C2 --> (LL:18) + | Condition C3 --> (LL:23) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3 Result + | 1 { F, F, - = F } + | 2 { T, -, - = T } + | 3 { F, T, T = T } + | + | C1-Pair: covered: (1,2) + | C2-Pair: covered: (1,3) + | C3-Pair: not covered + | MC/DC Coverage for Decision: 66.67% + | + ------------------ + LL| 4| black_box(x); + LL| 4|} + LL| | + LL| 4|fn assign_3_bis(a: bool, b: bool, c: bool) { + LL| 4| let x = a && b || c; + ^2 ^3 + ------------------ + |---> MC/DC Decision Region (LL:13) to (LL:24) + | + | Number of Conditions: 3 + | Condition C1 --> (LL:13) + | Condition C2 --> (LL:18) + | Condition C3 --> (LL:23) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3 Result + | 1 { T, F, F = F } + | 2 { F, -, T = T } + | 3 { T, T, - = T } + | + | C1-Pair: not covered + | C2-Pair: covered: (1,3) + | C3-Pair: not covered + | MC/DC Coverage for Decision: 33.33% + | ------------------ LL| 4| black_box(x); LL| 4|} @@ -55,6 +115,31 @@ LL| 3|fn right_comb_tree(a: bool, b: bool, c: bool, d: bool, e: bool) { LL| 3| let x = a && (b && (c && (d && (e)))); ^2 ^1 ^1 ^1 + ------------------ + |---> MC/DC Decision Region (LL:13) to (LL:42) + | + | Number of Conditions: 5 + | Condition C1 --> (LL:13) + | Condition C2 --> (LL:19) + | Condition C3 --> (LL:25) + | Condition C4 --> (LL:31) + | Condition C5 --> (LL:36) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3, C4, C5 Result + | 1 { F, -, -, -, - = F } + | 2 { T, F, -, -, - = F } + | 3 { T, T, T, T, T = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (2,3) + | C3-Pair: not covered + | C4-Pair: not covered + | C5-Pair: not covered + | MC/DC Coverage for Decision: 40.00% + | + ------------------ LL| 3| black_box(x); LL| 3|} LL| | @@ -65,6 +150,25 @@ LL| 3|fn func_call(a: bool, b: bool) { LL| 3| foo(a && b); ^2 + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:15) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:9) + | Condition C2 --> (LL:14) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, F = F } + | 3 { T, T = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (2,3) + | MC/DC Coverage for Decision: 100.00% + | + ------------------ LL| 3|} LL| | LL| |#[coverage(off)] From ba4510ece85018408627b2d3ceef1a3b7889cb71 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 May 2024 11:49:05 +0000 Subject: [PATCH 072/147] Allow constraining opaque types during subtyping in the trait system --- compiler/rustc_infer/src/infer/mod.rs | 4 +- .../impl-trait/lazy_subtyping_of_opaques.rs | 65 +++++-------------- .../lazy_subtyping_of_opaques.stderr | 21 ------ .../lazy_subtyping_of_opaques.rs | 3 +- .../lazy_subtyping_of_opaques.stderr | 31 +++++---- 5 files changed, 37 insertions(+), 87 deletions(-) delete mode 100644 tests/ui/impl-trait/lazy_subtyping_of_opaques.stderr diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 510e9a06dfb0..4d6ddd7ba66a 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -878,9 +878,9 @@ impl<'tcx> InferCtxt<'tcx> { self.enter_forall(predicate, |ty::SubtypePredicate { a_is_expected, a, b }| { if a_is_expected { - Ok(self.at(cause, param_env).sub(DefineOpaqueTypes::No, a, b)) + Ok(self.at(cause, param_env).sub(DefineOpaqueTypes::Yes, a, b)) } else { - Ok(self.at(cause, param_env).sup(DefineOpaqueTypes::No, b, a)) + Ok(self.at(cause, param_env).sup(DefineOpaqueTypes::Yes, b, a)) } }) } diff --git a/tests/ui/impl-trait/lazy_subtyping_of_opaques.rs b/tests/ui/impl-trait/lazy_subtyping_of_opaques.rs index 65331894725a..8fd1f35645a8 100644 --- a/tests/ui/impl-trait/lazy_subtyping_of_opaques.rs +++ b/tests/ui/impl-trait/lazy_subtyping_of_opaques.rs @@ -2,58 +2,23 @@ //! No hidden types are being constrained in the subtyping predicate, but type and //! lifetime variables get subtyped in the generic parameter list of the opaque. -use std::iter; +//@ check-pass -mod either { - pub enum Either { - Left(L), - Right(R), - } - - impl> Iterator for Either { - type Item = L::Item; - fn next(&mut self) -> Option { - todo!() - } - } - pub use self::Either::{Left, Right}; -} - -pub enum BabeConsensusLogRef<'a> { - NextEpochData(BabeNextEpochRef<'a>), - NextConfigData, -} - -impl<'a> BabeConsensusLogRef<'a> { - pub fn scale_encoding( - &self, - ) -> impl Iterator + Clone + 'a> + Clone + 'a { - //~^ ERROR is not satisfied - //~| ERROR is not satisfied - //~| ERROR is not satisfied - match self { - BabeConsensusLogRef::NextEpochData(digest) => either::Left(either::Left( - digest.scale_encoding().map(either::Left).map(either::Left), - )), - BabeConsensusLogRef::NextConfigData => either::Right( - // The Opaque type from ``scale_encoding` gets used opaquely here, while the `R` - // generic parameter of `Either` contains type variables that get subtyped and the - // opaque type contains lifetime variables that get subtyped. - iter::once(either::Right(either::Left([1]))) - .chain(std::iter::once([1]).map(either::Right).map(either::Right)), - ), - } - } -} - -pub struct BabeNextEpochRef<'a>(&'a ()); - -impl<'a> BabeNextEpochRef<'a> { - pub fn scale_encoding( - &self, - ) -> impl Iterator + Clone + 'a> + Clone + 'a { - std::iter::once([1]) +fn foo() -> impl Default + Copy { + if false { + let x = Default::default(); + // add `Subtype(?x, ?y)` obligation + let y = x; + + // Make a tuple `(?x, ?y)` and equate it with `(impl Default, u32)`. + // For us to try and prove a `Subtype(impl Default, u32)` obligation, + // we have to instantiate both `?x` and `?y` without any + // `select_where_possible` calls inbetween. + let mut tup = &mut (x, y); + let assign_tup = &mut (foo(), 1u32); + tup = assign_tup; } + 1u32 } fn main() {} diff --git a/tests/ui/impl-trait/lazy_subtyping_of_opaques.stderr b/tests/ui/impl-trait/lazy_subtyping_of_opaques.stderr deleted file mode 100644 index 2f8c957c2c7d..000000000000 --- a/tests/ui/impl-trait/lazy_subtyping_of_opaques.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0277]: the trait bound `Either + Clone + '_> + Clone + '_, fn(impl AsRef<[u8]> + Clone + '_) -> Either + Clone + '_, _> {Either:: + Clone + '_, _>::Left}>, fn(Either + Clone + '_, _>) -> Either + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>> {Either:: + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>::Left}>, _>, std::iter::Chain + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>>, Map, fn([{integer}; 1]) -> Either<[{integer}; 1], [{integer}; 1]> {Either::<[{integer}; 1], [{integer}; 1]>::Right}>, fn(Either<[{integer}; 1], [{integer}; 1]>) -> Either + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>> {Either:: + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>::Right}>>>: Clone` is not satisfied - --> $DIR/lazy_subtyping_of_opaques.rs:30:10 - | -LL | ) -> impl Iterator + Clone + 'a> + Clone + 'a { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `Either + Clone + '_> + Clone + '_, fn(impl AsRef<[u8]> + Clone + '_) -> Either + Clone + '_, _> {Either:: + Clone + '_, _>::Left}>, fn(Either + Clone + '_, _>) -> Either + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>> {Either:: + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>::Left}>, _>, std::iter::Chain + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>>, Map, fn([{integer}; 1]) -> Either<[{integer}; 1], [{integer}; 1]> {Either::<[{integer}; 1], [{integer}; 1]>::Right}>, fn(Either<[{integer}; 1], [{integer}; 1]>) -> Either + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>> {Either:: + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>::Right}>>>` - -error[E0277]: the trait bound `Either + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>: AsRef<[u8]>` is not satisfied - --> $DIR/lazy_subtyping_of_opaques.rs:30:31 - | -LL | ) -> impl Iterator + Clone + 'a> + Clone + 'a { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsRef<[u8]>` is not implemented for `Either + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>` - -error[E0277]: the trait bound `Either + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>: Clone` is not satisfied - --> $DIR/lazy_subtyping_of_opaques.rs:30:31 - | -LL | ) -> impl Iterator + Clone + 'a> + Clone + 'a { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `Either + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>` - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs index 72a90287e374..1bc352041a57 100644 --- a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs +++ b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs @@ -7,9 +7,10 @@ type Tait = impl FnOnce() -> (); fn reify_as_tait() -> Thunk { + //~^ ERROR: expected a `FnOnce()` closure, found `()` Thunk::new(|cont| cont) //~^ ERROR: mismatched types - //~| ERROR: mismatched types + //~| ERROR: expected a `FnOnce()` closure, found `()` } struct Thunk(F); diff --git a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr index 5a35dc27446c..7bc2fa1b09ea 100644 --- a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr +++ b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr @@ -1,26 +1,31 @@ -error[E0308]: mismatched types - --> $DIR/lazy_subtyping_of_opaques.rs:10:23 +error[E0277]: expected a `FnOnce()` closure, found `()` + --> $DIR/lazy_subtyping_of_opaques.rs:11:23 | -LL | type Tait = impl FnOnce() -> (); - | ------------------- the found opaque type -... LL | Thunk::new(|cont| cont) - | ^^^^ expected `()`, found opaque type + | ^^^^ expected an `FnOnce()` closure, found `()` | - = note: expected unit type `()` - found opaque type `Tait` + = help: the trait `FnOnce()` is not implemented for `()` + = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` -error[E0308]: mismatched types - --> $DIR/lazy_subtyping_of_opaques.rs:10:5 +error[E0277]: expected a `FnOnce()` closure, found `()` + --> $DIR/lazy_subtyping_of_opaques.rs:9:23 | LL | fn reify_as_tait() -> Thunk { - | ----------- expected `Thunk<_>` because of return type + | ^^^^^^^^^^^ expected an `FnOnce()` closure, found `()` + | + = help: the trait `FnOnce()` is not implemented for `()` + = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` + +error[E0308]: mismatched types + --> $DIR/lazy_subtyping_of_opaques.rs:11:5 + | LL | Thunk::new(|cont| cont) | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Thunk<_>`, found `()` | = note: expected struct `Thunk<_>` found unit type `()` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. From 393dea8bc3796a4513477813de1fef99c4fa44a9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 14 Jun 2024 11:07:08 +0000 Subject: [PATCH 073/147] Allow tracing through item_bounds query invocations on opaques Previously these caused cycles when printing the result --- compiler/rustc_hir_analysis/src/collect/item_bounds.rs | 2 +- compiler/rustc_query_impl/src/plumbing.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 50c93e9f1d7e..94d6e13d751f 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -59,7 +59,7 @@ fn associated_type_bounds<'tcx>( /// impl trait it isn't possible to write a suitable predicate on the /// containing function and for type-alias impl trait we don't have a backwards /// compatibility issue. -#[instrument(level = "trace", skip(tcx), ret)] +#[instrument(level = "trace", skip(tcx, item_ty))] fn opaque_type_bounds<'tcx>( tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId, diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 86531bd95900..62e393772147 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -620,7 +620,9 @@ macro_rules! define_queries { tcx, { let ret = call_provider!([$($modifiers)*][tcx, $name, key]); - tracing::trace!(?ret); + rustc_middle::ty::print::with_reduced_queries!({ + tracing::trace!(?ret); + }); ret } ) From 56cd30104a0723cb2d74e9f48ad260414f1201fc Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 13 Jun 2024 10:54:15 +0000 Subject: [PATCH 074/147] Change a `DefineOpaqueTypes::No` to `Yes` in diagnostics code --- compiler/rustc_hir_typeck/src/method/probe.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 3986374a343f..47ea221d1a11 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1357,6 +1357,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { traits::SelectionContext::new(self).select(&obligation) } + /// Used for ambiguous method call error reporting. Uses probing that throws away the result internally, + /// so do not use to make a decision that may lead to a successful compilation. fn candidate_source(&self, candidate: &Candidate<'tcx>, self_ty: Ty<'tcx>) -> CandidateSource { match candidate.kind { InherentImplCandidate(_) => { @@ -1370,8 +1372,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.instantiate_binder_with_fresh_vars(self.span, infer::FnCall, trait_ref); let (xform_self_ty, _) = self.xform_self_ty(candidate.item, trait_ref.self_ty(), trait_ref.args); + // Guide the trait selection to show impls that have methods whose type matches + // up with the `self` parameter of the method. let _ = self.at(&ObligationCause::dummy(), self.param_env).sup( - DefineOpaqueTypes::No, + DefineOpaqueTypes::Yes, xform_self_ty, self_ty, ); From 219389360c0b20cd6b980072e08723b91fea2c9b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 18 Jun 2024 21:32:50 +1000 Subject: [PATCH 075/147] Add a comment. Something that was non-obvious to me. --- compiler/rustc_ast/src/ast.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 9cb193b4a678..ca7f0363fa7c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -972,7 +972,9 @@ impl UnOp { } } -/// A statement +/// A statement. No `attrs` or `tokens` fields because each `StmtKind` variant +/// contains an AST node with those fields. (Except for `StmtKind::Empty`, +/// which never has attrs or tokens) #[derive(Clone, Encodable, Decodable, Debug)] pub struct Stmt { pub id: NodeId, From 1fbb3eca67032721e6f286e2a6f20da1b5659cc5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 19 Jun 2024 10:12:37 +1000 Subject: [PATCH 076/147] Expand another comment. --- compiler/rustc_parse/src/parser/attr_wrapper.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 62c8f9f5dacb..042ee21e3f7c 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -15,7 +15,7 @@ use std::ops::Range; /// for the attribute target. This allows us to perform cfg-expansion on /// a token stream before we invoke a derive proc-macro. /// -/// This wrapper prevents direct access to the underlying `ast::AttrVec>`. +/// This wrapper prevents direct access to the underlying `ast::AttrVec`. /// Parsing code can only get access to the underlying attributes /// by passing an `AttrWrapper` to `collect_tokens_trailing_tokens`. /// This makes it difficult to accidentally construct an AST node @@ -177,6 +177,10 @@ impl<'a> Parser<'a> { /// into a `LazyAttrTokenStream`, and returned along with the result /// of the callback. /// + /// The `attrs` passed in are in `AttrWrapper` form, which is opaque. The + /// `AttrVec` within is passed to `f`. See the comment on `AttrWrapper` for + /// details. + /// /// Note: If your callback consumes an opening delimiter /// (including the case where you call `collect_tokens` /// when the current token is an opening delimiter), From 1c28229ada005ed899f8ce71315baaa5420e1ed6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 18 Jun 2024 20:00:45 +1000 Subject: [PATCH 077/147] Simplify `Parser::parse_expr_dot_or_call`. The call in `parse_expr_prefix` for the `++` case passes an empty `attrs`, but it doesn' need to. This commit changes it to pass the parsed `attrs`, which doesn't change any behaviour. As a result, `parse_expr_dot_or_call` no longer needs an `Option` argument, and no longer needs to call `parse_or_use_outer_attributes`. --- compiler/rustc_parse/src/parser/expr.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e15d6ab2123a..cdeb29118994 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -629,7 +629,7 @@ impl<'a> Parser<'a> { this.bump(); this.bump(); - let operand_expr = this.parse_expr_dot_or_call(Default::default())?; + let operand_expr = this.parse_expr_dot_or_call(attrs)?; this.recover_from_prefix_increment(operand_expr, pre_span, starts_stmt) } token::Ident(..) if this.token.is_keyword(kw::Box) => { @@ -638,7 +638,7 @@ impl<'a> Parser<'a> { token::Ident(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => { make_it!(this, attrs, |this, _| this.recover_not_expr(lo)) } - _ => return this.parse_expr_dot_or_call(Some(attrs)), + _ => return this.parse_expr_dot_or_call(attrs), } } @@ -927,8 +927,7 @@ impl<'a> Parser<'a> { } /// Parses `a.b` or `a(13)` or `a[4]` or just `a`. - fn parse_expr_dot_or_call(&mut self, attrs: Option) -> PResult<'a, P> { - let attrs = self.parse_or_use_outer_attributes(attrs)?; + fn parse_expr_dot_or_call(&mut self, attrs: AttrWrapper) -> PResult<'a, P> { self.collect_tokens_for_expr(attrs, |this, attrs| { let base = this.parse_expr_bottom()?; let span = this.interpolated_or_expr_span(&base); From 42e47dfe8251c8356c409485d66164b6fceb03c6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 18 Jun 2024 20:21:05 +1000 Subject: [PATCH 078/147] Remove `From` impls for `LhsExpr`. The `Option` one maps to the first two variants, and the `P` one maps to the third. Weird. The code is shorter and clearer without them. --- compiler/rustc_parse/src/parser/expr.rs | 28 +++++++------------------ compiler/rustc_parse/src/parser/pat.rs | 7 +++---- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index cdeb29118994..a0f57ffecfa6 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -75,25 +75,6 @@ pub(super) enum LhsExpr { AlreadyParsed { expr: P, starts_statement: bool }, } -impl From> for LhsExpr { - /// Converts `Some(attrs)` into `LhsExpr::AttributesParsed(attrs)` - /// and `None` into `LhsExpr::NotYetParsed`. - /// - /// This conversion does not allocate. - fn from(o: Option) -> Self { - if let Some(attrs) = o { LhsExpr::AttributesParsed(attrs) } else { LhsExpr::NotYetParsed } - } -} - -impl From> for LhsExpr { - /// Converts the `expr: P` into `LhsExpr::AlreadyParsed { expr, starts_statement: false }`. - /// - /// This conversion does not allocate. - fn from(expr: P) -> Self { - LhsExpr::AlreadyParsed { expr, starts_statement: false } - } -} - #[derive(Debug)] enum DestructuredFloat { /// 1e2 @@ -166,7 +147,11 @@ impl<'a> Parser<'a> { &mut self, already_parsed_attrs: Option, ) -> PResult<'a, P> { - self.parse_expr_assoc_with(0, already_parsed_attrs.into()) + let lhs = match already_parsed_attrs { + Some(attrs) => LhsExpr::AttributesParsed(attrs), + None => LhsExpr::NotYetParsed, + }; + self.parse_expr_assoc_with(0, lhs) } /// Parses an associative expression with operators of at least `min_prec` precedence. @@ -2660,7 +2645,8 @@ impl<'a> Parser<'a> { } else { self.expect(&token::Eq)?; } - let expr = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), None.into())?; + let expr = + self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), LhsExpr::NotYetParsed)?; let span = lo.to(expr.span); Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered))) } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 8af415f7c9dd..a3efecffcc65 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -10,7 +10,7 @@ use crate::errors::{ UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, }; -use crate::parser::expr::could_be_unclosed_char_literal; +use crate::parser::expr::{could_be_unclosed_char_literal, LhsExpr}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; use rustc_ast::ptr::P; @@ -398,9 +398,8 @@ impl<'a> Parser<'a> { // Parse an associative expression such as `+ expr`, `% expr`, ... // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`]. - if let Ok(expr) = - snapshot.parse_expr_assoc_with(0, expr.into()).map_err(|err| err.cancel()) - { + let lhs = LhsExpr::AlreadyParsed { expr, starts_statement: false }; + if let Ok(expr) = snapshot.parse_expr_assoc_with(0, lhs).map_err(|err| err.cancel()) { // We got a valid expression. self.restore_snapshot(snapshot); self.restrictions.remove(Restrictions::IS_PAT); From 25523ba382037494e8f8f17779a5f1c72f6bddb2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 18 Jun 2024 20:40:27 +1000 Subject: [PATCH 079/147] Refactor `LhsExpr`. Combine `NotYetParsed` and `AttributesParsed` into a single variant, because (a) that reflects the structure of the code that consumes `LhsExpr`, and (b) because that variant will have the `Option` removed in a later commit. --- compiler/rustc_parse/src/parser/expr.rs | 67 +++++++++++++------------ compiler/rustc_parse/src/parser/pat.rs | 2 +- compiler/rustc_parse/src/parser/stmt.rs | 11 ++-- 3 files changed, 38 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index a0f57ffecfa6..eb3421cfee4a 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -70,9 +70,10 @@ macro_rules! maybe_whole_expr { #[derive(Debug)] pub(super) enum LhsExpr { - NotYetParsed, - AttributesParsed(AttrWrapper), - AlreadyParsed { expr: P, starts_statement: bool }, + // Already parsed either (a) nothing or (b) just the outer attributes. + Unparsed { attrs: Option }, + // Already parsed the expression. + Parsed { expr: P, starts_statement: bool }, } #[derive(Debug)] @@ -133,9 +134,9 @@ impl<'a> Parser<'a> { pub(super) fn parse_expr_res( &mut self, r: Restrictions, - already_parsed_attrs: Option, + attrs: Option, ) -> PResult<'a, P> { - self.with_res(r, |this| this.parse_expr_assoc(already_parsed_attrs)) + self.with_res(r, |this| this.parse_expr_assoc(attrs)) } /// Parses an associative expression. @@ -143,15 +144,8 @@ impl<'a> Parser<'a> { /// This parses an expression accounting for associativity and precedence of the operators in /// the expression. #[inline] - fn parse_expr_assoc( - &mut self, - already_parsed_attrs: Option, - ) -> PResult<'a, P> { - let lhs = match already_parsed_attrs { - Some(attrs) => LhsExpr::AttributesParsed(attrs), - None => LhsExpr::NotYetParsed, - }; - self.parse_expr_assoc_with(0, lhs) + fn parse_expr_assoc(&mut self, attrs: Option) -> PResult<'a, P> { + self.parse_expr_assoc_with(0, LhsExpr::Unparsed { attrs }) } /// Parses an associative expression with operators of at least `min_prec` precedence. @@ -161,18 +155,17 @@ impl<'a> Parser<'a> { lhs: LhsExpr, ) -> PResult<'a, P> { let mut starts_stmt = false; - let mut lhs = if let LhsExpr::AlreadyParsed { expr, starts_statement } = lhs { - starts_stmt = starts_statement; - expr - } else { - let attrs = match lhs { - LhsExpr::AttributesParsed(attrs) => Some(attrs), - _ => None, - }; - if self.token.is_range_separator() { - return self.parse_expr_prefix_range(attrs); - } else { - self.parse_expr_prefix(attrs)? + let mut lhs = match lhs { + LhsExpr::Parsed { expr, starts_statement } => { + starts_stmt = starts_statement; + expr + } + LhsExpr::Unparsed { attrs } => { + if self.token.is_range_separator() { + return self.parse_expr_prefix_range(attrs); + } else { + self.parse_expr_prefix(attrs)? + } } }; @@ -310,7 +303,10 @@ impl<'a> Parser<'a> { Fixity::None => 1, }; let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { - this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::NotYetParsed) + this.parse_expr_assoc_with( + prec + prec_adjustment, + LhsExpr::Unparsed { attrs: None }, + ) })?; let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); @@ -484,7 +480,7 @@ impl<'a> Parser<'a> { let rhs = if self.is_at_start_of_range_notation_rhs() { let maybe_lt = self.token.clone(); Some( - self.parse_expr_assoc_with(prec + 1, LhsExpr::NotYetParsed) + self.parse_expr_assoc_with(prec + 1, LhsExpr::Unparsed { attrs: None }) .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?, ) } else { @@ -539,9 +535,12 @@ impl<'a> Parser<'a> { this.bump(); let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() { // RHS must be parsed with more associativity than the dots. - this.parse_expr_assoc_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed) - .map(|x| (lo.to(x.span), Some(x))) - .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))? + this.parse_expr_assoc_with( + op.unwrap().precedence() + 1, + LhsExpr::Unparsed { attrs: None }, + ) + .map(|x| (lo.to(x.span), Some(x))) + .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))? } else { (lo, None) }; @@ -2645,8 +2644,10 @@ impl<'a> Parser<'a> { } else { self.expect(&token::Eq)?; } - let expr = - self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), LhsExpr::NotYetParsed)?; + let expr = self.parse_expr_assoc_with( + 1 + prec_let_scrutinee_needs_par(), + LhsExpr::Unparsed { attrs: None }, + )?; let span = lo.to(expr.span); Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered))) } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index a3efecffcc65..921e71a1f964 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -398,7 +398,7 @@ impl<'a> Parser<'a> { // Parse an associative expression such as `+ expr`, `% expr`, ... // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`]. - let lhs = LhsExpr::AlreadyParsed { expr, starts_statement: false }; + let lhs = LhsExpr::Parsed { expr, starts_statement: false }; if let Ok(expr) = snapshot.parse_expr_assoc_with(0, lhs).map_err(|err| err.cancel()) { // We got a valid expression. self.restore_snapshot(snapshot); diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 104aae9b257e..8684f2ed829d 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -174,10 +174,7 @@ impl<'a> Parser<'a> { // Perform this outside of the `collect_tokens_trailing_token` closure, // since our outer attributes do not apply to this part of the expression let expr = self.with_res(Restrictions::STMT_EXPR, |this| { - this.parse_expr_assoc_with( - 0, - LhsExpr::AlreadyParsed { expr, starts_statement: true }, - ) + this.parse_expr_assoc_with(0, LhsExpr::Parsed { expr, starts_statement: true }) })?; Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) } else { @@ -210,10 +207,8 @@ impl<'a> Parser<'a> { let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac)); let e = self.maybe_recover_from_bad_qpath(e)?; let e = self.parse_expr_dot_or_call_with(e, lo, attrs)?; - let e = self.parse_expr_assoc_with( - 0, - LhsExpr::AlreadyParsed { expr: e, starts_statement: false }, - )?; + let e = self + .parse_expr_assoc_with(0, LhsExpr::Parsed { expr: e, starts_statement: false })?; StmtKind::Expr(e) }; Ok(self.mk_stmt(lo.to(hi), kind)) From ead0a45202af9cefb9607a621d27da4927700229 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 19 Jun 2024 10:34:05 +1000 Subject: [PATCH 080/147] Inline and remove `parse_expr_assoc`. It has a single call site. --- compiler/rustc_parse/src/parser/expr.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index eb3421cfee4a..272067dca872 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -136,16 +136,7 @@ impl<'a> Parser<'a> { r: Restrictions, attrs: Option, ) -> PResult<'a, P> { - self.with_res(r, |this| this.parse_expr_assoc(attrs)) - } - - /// Parses an associative expression. - /// - /// This parses an expression accounting for associativity and precedence of the operators in - /// the expression. - #[inline] - fn parse_expr_assoc(&mut self, attrs: Option) -> PResult<'a, P> { - self.parse_expr_assoc_with(0, LhsExpr::Unparsed { attrs }) + self.with_res(r, |this| this.parse_expr_assoc_with(0, LhsExpr::Unparsed { attrs })) } /// Parses an associative expression with operators of at least `min_prec` precedence. From 802779f77ddaac18865e5e52e01c5e4d122e9090 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 19 Jun 2024 14:41:01 +1000 Subject: [PATCH 081/147] Move `parse_or_use_outer_attributes` out of `parse_expr_prefix`. This eliminates one `Option` argument. --- compiler/rustc_parse/src/parser/expr.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 272067dca872..e96b97da9fdf 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -155,6 +155,7 @@ impl<'a> Parser<'a> { if self.token.is_range_separator() { return self.parse_expr_prefix_range(attrs); } else { + let attrs = self.parse_or_use_outer_attributes(attrs)?; self.parse_expr_prefix(attrs)? } } @@ -541,8 +542,7 @@ impl<'a> Parser<'a> { } /// Parses a prefix-unary-operator expr. - fn parse_expr_prefix(&mut self, attrs: Option) -> PResult<'a, P> { - let attrs = self.parse_or_use_outer_attributes(attrs)?; + fn parse_expr_prefix(&mut self, attrs: AttrWrapper) -> PResult<'a, P> { let lo = self.token.span; macro_rules! make_it { @@ -591,7 +591,8 @@ impl<'a> Parser<'a> { this.dcx().emit_err(err); this.bump(); - this.parse_expr_prefix(None) + let attrs = this.parse_outer_attributes()?; + this.parse_expr_prefix(attrs) } // Recover from `++x`: token::BinOp(token::Plus) @@ -619,7 +620,8 @@ impl<'a> Parser<'a> { fn parse_expr_prefix_common(&mut self, lo: Span) -> PResult<'a, (Span, P)> { self.bump(); - let expr = self.parse_expr_prefix(None)?; + let attrs = self.parse_outer_attributes()?; + let expr = self.parse_expr_prefix(attrs)?; let span = self.interpolated_or_expr_span(&expr); Ok((lo.to(span), expr)) } @@ -872,7 +874,8 @@ impl<'a> Parser<'a> { let expr = if self.token.is_range_separator() { self.parse_expr_prefix_range(None) } else { - self.parse_expr_prefix(None) + let attrs = self.parse_outer_attributes()?; + self.parse_expr_prefix(attrs) }?; let hi = self.interpolated_or_expr_span(&expr); let span = lo.to(hi); From aaa220e8757d1d2bb3a7b4742db9e289c8454dc2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 19 Jun 2024 15:37:31 +1000 Subject: [PATCH 082/147] Move `parse_or_use_outer_attributes` out of `parse_expr_prefix_range`. This eliminates another `Option` argument and changes one obscure error message. --- compiler/rustc_parse/messages.ftl | 2 ++ compiler/rustc_parse/src/errors.rs | 7 +++++++ compiler/rustc_parse/src/parser/expr.rs | 19 ++++++++++--------- .../attribute/attr-stmt-expr-attr-bad.rs | 4 ++-- .../attribute/attr-stmt-expr-attr-bad.stderr | 8 ++++---- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index f678d11213c7..efb9526eabcf 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -133,6 +133,8 @@ parse_dot_dot_dot_for_remaining_fields = expected field pattern, found `{$token_ parse_dot_dot_dot_range_to_pattern_not_allowed = range-to patterns with `...` are not allowed .suggestion = use `..=` instead +parse_dot_dot_range_attribute = attributes are not allowed on range expressions starting with `..` + parse_dotdotdot = unexpected token: `...` .suggest_exclusive_range = use `..` for an exclusive range .suggest_inclusive_range = or `..=` for an inclusive range diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 6c1fcbe06fc5..f0ef1112f9e3 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2989,3 +2989,10 @@ pub(crate) struct ExprRArrowCall { #[suggestion(style = "short", applicability = "machine-applicable", code = ".")] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_dot_dot_range_attribute)] +pub(crate) struct DotDotRangeAttribute { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e96b97da9fdf..ab3f8a5dbeb8 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -152,10 +152,10 @@ impl<'a> Parser<'a> { expr } LhsExpr::Unparsed { attrs } => { + let attrs = self.parse_or_use_outer_attributes(attrs)?; if self.token.is_range_separator() { return self.parse_expr_prefix_range(attrs); } else { - let attrs = self.parse_or_use_outer_attributes(attrs)?; self.parse_expr_prefix(attrs)? } } @@ -499,7 +499,12 @@ impl<'a> Parser<'a> { } /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`. - fn parse_expr_prefix_range(&mut self, attrs: Option) -> PResult<'a, P> { + fn parse_expr_prefix_range(&mut self, attrs: AttrWrapper) -> PResult<'a, P> { + if !attrs.is_empty() { + let err = errors::DotDotRangeAttribute { span: self.token.span }; + self.dcx().emit_err(err); + } + // Check for deprecated `...` syntax. if self.token == token::DotDotDot { self.err_dotdotdot_syntax(self.token.span); @@ -516,11 +521,7 @@ impl<'a> Parser<'a> { _ => RangeLimits::Closed, }; let op = AssocOp::from_token(&self.token); - // FIXME: `parse_prefix_range_expr` is called when the current - // token is `DotDot`, `DotDotDot`, or `DotDotEq`. If we haven't already - // parsed attributes, then trying to parse them here will always fail. - // We should figure out how we want attributes on range expressions to work. - let attrs = self.parse_or_use_outer_attributes(attrs)?; + let attrs = self.parse_outer_attributes()?; self.collect_tokens_for_expr(attrs, |this, attrs| { let lo = this.token.span; let maybe_lt = this.look_ahead(1, |t| t.clone()); @@ -871,10 +872,10 @@ impl<'a> Parser<'a> { let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon); let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below. let (borrow_kind, mutbl) = self.parse_borrow_modifiers(lo); + let attrs = self.parse_outer_attributes()?; let expr = if self.token.is_range_separator() { - self.parse_expr_prefix_range(None) + self.parse_expr_prefix_range(attrs) } else { - let attrs = self.parse_outer_attributes()?; self.parse_expr_prefix(attrs) }?; let hi = self.interpolated_or_expr_span(&expr); diff --git a/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.rs b/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.rs index d1950087c4c2..26761a1d2544 100644 --- a/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.rs +++ b/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.rs @@ -28,9 +28,9 @@ fn main() {} #[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; } -//~^ ERROR expected expression, found `..` +//~^ ERROR attributes are not allowed on range expressions starting with `..` #[cfg(FALSE)] fn e() { let _ = #[attr] ..; } -//~^ ERROR expected expression, found `..` +//~^ ERROR attributes are not allowed on range expressions starting with `..` #[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } diff --git a/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.stderr b/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.stderr index e46c591080d4..3593c5182ce9 100644 --- a/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.stderr +++ b/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.stderr @@ -119,17 +119,17 @@ LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; } = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files = note: outer attributes, like `#[test]`, annotate the item following them -error: expected expression, found `..` +error: attributes are not allowed on range expressions starting with `..` --> $DIR/attr-stmt-expr-attr-bad.rs:30:40 | LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; } - | ^^ expected expression + | ^^ -error: expected expression, found `..` +error: attributes are not allowed on range expressions starting with `..` --> $DIR/attr-stmt-expr-attr-bad.rs:32:40 | LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..; } - | ^^ expected expression + | ^^ error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:34:41 From 43eae4cef4f4e519641e0f0a3915b8023db92f00 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 19 Jun 2024 16:06:16 +1000 Subject: [PATCH 083/147] Simplify `LhsExpr::Unparsed`. By making the `AttrWrapper` non-optional. --- compiler/rustc_parse/src/parser/expr.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index ab3f8a5dbeb8..aea04ceca914 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -70,8 +70,8 @@ macro_rules! maybe_whole_expr { #[derive(Debug)] pub(super) enum LhsExpr { - // Already parsed either (a) nothing or (b) just the outer attributes. - Unparsed { attrs: Option }, + // Already parsed just the outer attributes. + Unparsed { attrs: AttrWrapper }, // Already parsed the expression. Parsed { expr: P, starts_statement: bool }, } @@ -136,6 +136,7 @@ impl<'a> Parser<'a> { r: Restrictions, attrs: Option, ) -> PResult<'a, P> { + let attrs = self.parse_or_use_outer_attributes(attrs)?; self.with_res(r, |this| this.parse_expr_assoc_with(0, LhsExpr::Unparsed { attrs })) } @@ -152,7 +153,6 @@ impl<'a> Parser<'a> { expr } LhsExpr::Unparsed { attrs } => { - let attrs = self.parse_or_use_outer_attributes(attrs)?; if self.token.is_range_separator() { return self.parse_expr_prefix_range(attrs); } else { @@ -295,10 +295,8 @@ impl<'a> Parser<'a> { Fixity::None => 1, }; let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { - this.parse_expr_assoc_with( - prec + prec_adjustment, - LhsExpr::Unparsed { attrs: None }, - ) + let attrs = this.parse_outer_attributes()?; + this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::Unparsed { attrs }) })?; let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); @@ -471,8 +469,9 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P> { let rhs = if self.is_at_start_of_range_notation_rhs() { let maybe_lt = self.token.clone(); + let attrs = self.parse_outer_attributes()?; Some( - self.parse_expr_assoc_with(prec + 1, LhsExpr::Unparsed { attrs: None }) + self.parse_expr_assoc_with(prec + 1, LhsExpr::Unparsed { attrs }) .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?, ) } else { @@ -528,9 +527,10 @@ impl<'a> Parser<'a> { this.bump(); let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() { // RHS must be parsed with more associativity than the dots. + let attrs = this.parse_outer_attributes()?; this.parse_expr_assoc_with( op.unwrap().precedence() + 1, - LhsExpr::Unparsed { attrs: None }, + LhsExpr::Unparsed { attrs }, ) .map(|x| (lo.to(x.span), Some(x))) .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))? @@ -2639,9 +2639,10 @@ impl<'a> Parser<'a> { } else { self.expect(&token::Eq)?; } + let attrs = self.parse_outer_attributes()?; let expr = self.parse_expr_assoc_with( 1 + prec_let_scrutinee_needs_par(), - LhsExpr::Unparsed { attrs: None }, + LhsExpr::Unparsed { attrs }, )?; let span = lo.to(expr.span); Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered))) From 8170acb197d7658505949cefc12e01dfc4c8feab Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 19 Jun 2024 16:24:21 +1000 Subject: [PATCH 084/147] Refactor `parse_expr_res`. This removes the final `Option` argument. --- .../rustc_parse/src/parser/diagnostics.rs | 13 ++++++-- compiler/rustc_parse/src/parser/expr.rs | 30 ++++++++++++------- compiler/rustc_parse/src/parser/mod.rs | 11 ------- compiler/rustc_parse/src/parser/path.rs | 3 +- compiler/rustc_parse/src/parser/stmt.rs | 4 +-- 5 files changed, 33 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 2bb6fb53869b..17e62a8e0468 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2502,7 +2502,8 @@ impl<'a> Parser<'a> { /// wrapped in braces. pub(super) fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P> { let start = self.token.span; - let expr = self.parse_expr_res(Restrictions::CONST_EXPR, None).map_err(|mut err| { + let attrs = self.parse_outer_attributes()?; + let expr = self.parse_expr_res(Restrictions::CONST_EXPR, attrs).map_err(|mut err| { err.span_label( start.shrink_to_lo(), "while parsing a const generic argument starting here", @@ -2624,7 +2625,10 @@ impl<'a> Parser<'a> { if is_op_or_dot { self.bump(); } - match self.parse_expr_res(Restrictions::CONST_EXPR, None) { + match (|| { + let attrs = self.parse_outer_attributes()?; + self.parse_expr_res(Restrictions::CONST_EXPR, attrs) + })() { Ok(expr) => { // Find a mistake like `MyTrait`. if token::EqEq == snapshot.token.kind { @@ -2678,7 +2682,10 @@ impl<'a> Parser<'a> { &mut self, mut snapshot: SnapshotParser<'a>, ) -> Option> { - match snapshot.parse_expr_res(Restrictions::CONST_EXPR, None) { + match (|| { + let attrs = self.parse_outer_attributes()?; + snapshot.parse_expr_res(Restrictions::CONST_EXPR, attrs) + })() { // Since we don't know the exact reason why we failed to parse the type or the // expression, employ a simple heuristic to weed out some pathological cases. Ok(expr) if let token::Comma | token::Gt = snapshot.token.kind => { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index aea04ceca914..d27c612bbc1e 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -94,7 +94,8 @@ impl<'a> Parser<'a> { pub fn parse_expr(&mut self) -> PResult<'a, P> { self.current_closure.take(); - self.parse_expr_res(Restrictions::empty(), None) + let attrs = self.parse_outer_attributes()?; + self.parse_expr_res(Restrictions::empty(), attrs) } /// Parses an expression, forcing tokens to be collected @@ -107,7 +108,8 @@ impl<'a> Parser<'a> { } fn parse_expr_catch_underscore(&mut self, restrictions: Restrictions) -> PResult<'a, P> { - match self.parse_expr_res(restrictions, None) { + let attrs = self.parse_outer_attributes()?; + match self.parse_expr_res(restrictions, attrs) { Ok(expr) => Ok(expr), Err(err) => match self.token.ident() { Some((Ident { name: kw::Underscore, .. }, IdentIsRaw::No)) @@ -134,9 +136,8 @@ impl<'a> Parser<'a> { pub(super) fn parse_expr_res( &mut self, r: Restrictions, - attrs: Option, + attrs: AttrWrapper, ) -> PResult<'a, P> { - let attrs = self.parse_or_use_outer_attributes(attrs)?; self.with_res(r, |this| this.parse_expr_assoc_with(0, LhsExpr::Unparsed { attrs })) } @@ -2343,7 +2344,8 @@ impl<'a> Parser<'a> { self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET; let prev = self.prev_token.clone(); let token = self.token.clone(); - match self.parse_expr_res(restrictions, None) { + let attrs = self.parse_outer_attributes()?; + match self.parse_expr_res(restrictions, attrs) { Ok(expr) => expr, Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?, } @@ -2591,8 +2593,9 @@ impl<'a> Parser<'a> { /// Parses the condition of a `if` or `while` expression. fn parse_expr_cond(&mut self) -> PResult<'a, P> { + let attrs = self.parse_outer_attributes()?; let mut cond = - self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)?; + self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?; CondChecker::new(self).visit_expr(&mut cond); @@ -2776,7 +2779,8 @@ impl<'a> Parser<'a> { (Err(err), Some((start_span, left))) if self.eat_keyword(kw::In) => { // We know for sure we have seen `for ($SOMETHING in`. In the happy path this would // happen right before the return of this method. - let expr = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None) { + let attrs = self.parse_outer_attributes()?; + let expr = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs) { Ok(expr) => expr, Err(expr_err) => { // We don't know what followed the `in`, so cancel and bubble up the @@ -2810,7 +2814,8 @@ impl<'a> Parser<'a> { self.error_missing_in_for_loop(); } self.check_for_for_in_in_typo(self.prev_token.span); - let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + let attrs = self.parse_outer_attributes()?; + let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?; Ok((pat, expr)) } @@ -2922,7 +2927,8 @@ impl<'a> Parser<'a> { /// Parses a `match ... { ... }` expression (`match` token already eaten). fn parse_expr_match(&mut self) -> PResult<'a, P> { let match_span = self.prev_token.span; - let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + let attrs = self.parse_outer_attributes()?; + let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?; self.parse_match_block(match_span, match_span, scrutinee, MatchKind::Prefix) } @@ -3126,8 +3132,9 @@ impl<'a> Parser<'a> { let arrow_span = this.prev_token.span; let arm_start_span = this.token.span; + let attrs = this.parse_outer_attributes()?; let expr = - this.parse_expr_res(Restrictions::STMT_EXPR, None).map_err(|mut err| { + this.parse_expr_res(Restrictions::STMT_EXPR, attrs).map_err(|mut err| { err.span_label(arrow_span, "while parsing the `match` arm starting here"); err })?; @@ -3332,7 +3339,8 @@ impl<'a> Parser<'a> { } fn parse_match_guard_condition(&mut self) -> PResult<'a, P> { - self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, None).map_err( + let attrs = self.parse_outer_attributes()?; + self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, attrs).map_err( |mut err| { if self.prev_token == token::OpenDelim(Delimiter::Brace) { let sugg_sp = self.prev_token.span.shrink_to_lo(); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index adf04fcf2241..f4cab480669b 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1337,17 +1337,6 @@ impl<'a> Parser<'a> { }) } - fn parse_or_use_outer_attributes( - &mut self, - already_parsed_attrs: Option, - ) -> PResult<'a, AttrWrapper> { - if let Some(attrs) = already_parsed_attrs { - Ok(attrs) - } else { - self.parse_outer_attributes() - } - } - /// Parses a single token tree from the input. pub fn parse_token_tree(&mut self) -> TokenTree { match self.token.kind { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 9beecd9849fb..da8d1194325b 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -920,7 +920,8 @@ impl<'a> Parser<'a> { // Fall back by trying to parse a const-expr expression. If we successfully do so, // then we should report an error that it needs to be wrapped in braces. let snapshot = self.create_snapshot_for_diagnostic(); - match self.parse_expr_res(Restrictions::CONST_EXPR, None) { + let attrs = self.parse_outer_attributes()?; + match self.parse_expr_res(Restrictions::CONST_EXPR, attrs) { Ok(expr) => { return Ok(Some(self.dummy_const_arg_needs_braces( self.dcx().struct_span_err(expr.span, "invalid const generic expression"), diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 8684f2ed829d..d65f6ff68eee 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -126,9 +126,9 @@ impl<'a> Parser<'a> { // Remainder are line-expr stmts. let e = match force_collect { ForceCollect::Yes => self.collect_tokens_no_attrs(|this| { - this.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs)) + this.parse_expr_res(Restrictions::STMT_EXPR, attrs) })?, - ForceCollect::No => self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs))?, + ForceCollect::No => self.parse_expr_res(Restrictions::STMT_EXPR, attrs)?, }; if matches!(e.kind, ExprKind::Assign(..)) && self.eat_keyword(kw::Else) { let bl = self.parse_block()?; From 64c2e9ed3bfb6d6e8f9463f90d4eefa736242b8e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 19 Jun 2024 17:33:46 +1000 Subject: [PATCH 085/147] Change how `parse_expr_force_collect` works. It now parses outer attributes before collecting tokens. This avoids the problem where the outer attribute tokens were being stored twice -- for the attribute tokesn, and also for the expression tokens. Fixes #86055. --- compiler/rustc_parse/src/parser/expr.rs | 7 +++-- .../expr-stmt-nonterminal-tokens.stdout | 27 +------------------ 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index d27c612bbc1e..4094fb53659b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -98,9 +98,12 @@ impl<'a> Parser<'a> { self.parse_expr_res(Restrictions::empty(), attrs) } - /// Parses an expression, forcing tokens to be collected + /// Parses an expression, forcing tokens to be collected. pub fn parse_expr_force_collect(&mut self) -> PResult<'a, P> { - self.collect_tokens_no_attrs(|this| this.parse_expr()) + self.current_closure.take(); + + let attrs = self.parse_outer_attributes()?; + self.collect_tokens_no_attrs(|this| this.parse_expr_res(Restrictions::empty(), attrs)) } pub fn parse_expr_anon_const(&mut self) -> PResult<'a, AnonConst> { diff --git a/tests/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout b/tests/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout index 6523f2485cd1..0168689b605f 100644 --- a/tests/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout +++ b/tests/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout @@ -1,4 +1,4 @@ -PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = #[allow(warnings)] #[allow(warnings)] 0; 0 }, } +PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = #[allow(warnings)] 0; 0 }, } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "enum", @@ -39,31 +39,6 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Group { delimiter: None, stream: TokenStream [ - Punct { - ch: '#', - spacing: Alone, - span: #0 bytes(543..544), - }, - Group { - delimiter: Bracket, - stream: TokenStream [ - Ident { - ident: "allow", - span: #0 bytes(545..550), - }, - Group { - delimiter: Parenthesis, - stream: TokenStream [ - Ident { - ident: "warnings", - span: #0 bytes(551..559), - }, - ], - span: #0 bytes(550..560), - }, - ], - span: #0 bytes(544..561), - }, Punct { ch: '#', spacing: Alone, From fd4fe7d129cac2b7a0668847117775ee23031771 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 1 Jun 2024 21:35:43 +1000 Subject: [PATCH 086/147] Remove `src/tools/rust-demangler` --- Cargo.lock | 8 -- Cargo.toml | 1 - config.example.toml | 4 +- src/bootstrap/src/core/build_steps/clippy.rs | 1 - src/bootstrap/src/core/build_steps/dist.rs | 93 +----------------- src/bootstrap/src/core/build_steps/install.rs | 16 --- src/bootstrap/src/core/build_steps/test.rs | 59 ----------- src/bootstrap/src/core/build_steps/tool.rs | 1 - src/bootstrap/src/core/builder.rs | 5 - src/bootstrap/src/utils/tarball.rs | 5 - src/doc/rustc/src/instrument-coverage.md | 8 +- src/tools/rust-demangler/Cargo.toml | 16 --- src/tools/rust-demangler/README.md | 36 ------- src/tools/rust-demangler/src/lib.rs | 21 ---- src/tools/rust-demangler/src/main.rs | 97 ------------------- src/tools/rust-demangler/tests/lib.rs | 84 ---------------- 16 files changed, 5 insertions(+), 450 deletions(-) delete mode 100644 src/tools/rust-demangler/Cargo.toml delete mode 100644 src/tools/rust-demangler/README.md delete mode 100644 src/tools/rust-demangler/src/lib.rs delete mode 100644 src/tools/rust-demangler/src/main.rs delete mode 100644 src/tools/rust-demangler/tests/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 14ee031ad047..da99ac9b8afc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3479,14 +3479,6 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "rust-demangler" -version = "0.0.1" -dependencies = [ - "regex", - "rustc-demangle", -] - [[package]] name = "rustbook" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index a601ebf4369e..c17ea99d0376 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ members = [ "src/tools/remote-test-client", "src/tools/remote-test-server", "src/tools/rust-installer", - "src/tools/rust-demangler", "src/tools/rustdoc", "src/tools/rls", "src/tools/rustfmt", diff --git a/config.example.toml b/config.example.toml index 76541ee4e9e7..679abcdc7771 100644 --- a/config.example.toml +++ b/config.example.toml @@ -321,8 +321,7 @@ # # If `extended = false`, the only one of these built by default is rustdoc. # -# If `extended = true`, they're all included, with the exception of -# rust-demangler which additionally requires `profiler = true` to be set. +# If `extended = true`, they are all included. # # If any enabled tool fails to build, the installation fails. #tools = [ @@ -334,7 +333,6 @@ # "rust-analyzer-proc-macro-srv", # "analysis", # "src", -# "rust-demangler", # if profiler = true #] # Verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose, 3 == print environment variables on each rustc invocation diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 01b5e99116ff..40a2112b1925 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -322,7 +322,6 @@ lint_any!( RemoteTestServer, "src/tools/remote-test-server", "remote-test-server"; Rls, "src/tools/rls", "rls"; RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer"; - RustDemangler, "src/tools/rust-demangler", "rust-demangler"; Rustdoc, "src/tools/rustdoc", "clippy"; Rustfmt, "src/tools/rustfmt", "rustfmt"; RustInstaller, "src/tools/rust-installer", "rust-installer"; diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index f651f751441f..54136d2aebda 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1459,62 +1459,6 @@ impl Step for Rustfmt { } } -#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] -pub struct RustDemangler { - pub compiler: Compiler, - pub target: TargetSelection, -} - -impl Step for RustDemangler { - type Output = Option; - const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - // While other tools use `should_build_extended_tool` to decide whether to be run by - // default or not, `rust-demangler` must be build when *either* it's enabled as a tool like - // the other ones or if `profiler = true`. Because we don't know the target at this stage - // we run the step by default when only `extended = true`, and decide whether to actually - // run it or not later. - let default = run.builder.config.extended; - run.alias("rust-demangler").default_condition(default) - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(RustDemangler { - compiler: run.builder.compiler_for( - run.builder.top_stage, - run.builder.config.build, - run.target, - ), - target: run.target, - }); - } - - fn run(self, builder: &Builder<'_>) -> Option { - let compiler = self.compiler; - let target = self.target; - - // Only build this extended tool if explicitly included in `tools`, or if `profiler = true` - let condition = should_build_extended_tool(builder, "rust-demangler") - || builder.config.profiler_enabled(target); - if builder.config.extended && !condition { - return None; - } - - let rust_demangler = - builder.ensure(tool::RustDemangler { compiler, target, extra_features: Vec::new() }); - - // Prepare the image directory - let mut tarball = Tarball::new(builder, "rust-demangler", &target.triple); - tarball.set_overlay(OverlayKind::RustDemangler); - tarball.is_preview(true); - tarball.add_file(rust_demangler, "bin", 0o755); - tarball.add_legal_and_readme_to("share/doc/rust-demangler"); - Some(tarball.generate()) - } -} - #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] pub struct Extended { stage: u32, @@ -1572,7 +1516,6 @@ impl Step for Extended { add_component!("rust-docs" => Docs { host: target }); add_component!("rust-json-docs" => JsonDocs { host: target }); - add_component!("rust-demangler"=> RustDemangler { compiler, target }); add_component!("cargo" => Cargo { compiler, target }); add_component!("rustfmt" => Rustfmt { compiler, target }); add_component!("rls" => Rls { compiler, target }); @@ -1636,7 +1579,7 @@ impl Step for Extended { let xform = |p: &Path| { let mut contents = t!(fs::read_to_string(p)); - for tool in &["rust-demangler", "miri", "rust-docs"] { + for tool in &["miri", "rust-docs"] { if !built_tools.contains(tool) { contents = filter(&contents, tool); } @@ -1677,7 +1620,7 @@ impl Step for Extended { prepare("rust-analysis"); prepare("clippy"); prepare("rust-analyzer"); - for tool in &["rust-docs", "rust-demangler", "miri", "rustc-codegen-cranelift"] { + for tool in &["rust-docs", "miri", "rustc-codegen-cranelift"] { if built_tools.contains(tool) { prepare(tool); } @@ -1717,8 +1660,6 @@ impl Step for Extended { "rust-analyzer-preview".to_string() } else if name == "clippy" { "clippy-preview".to_string() - } else if name == "rust-demangler" { - "rust-demangler-preview".to_string() } else if name == "miri" { "miri-preview".to_string() } else if name == "rustc-codegen-cranelift" { @@ -1738,7 +1679,7 @@ impl Step for Extended { prepare("cargo"); prepare("rust-analysis"); prepare("rust-std"); - for tool in &["clippy", "rust-analyzer", "rust-docs", "rust-demangler", "miri"] { + for tool in &["clippy", "rust-analyzer", "rust-docs", "miri"] { if built_tools.contains(tool) { prepare(tool); } @@ -1862,25 +1803,6 @@ impl Step for Extended { .arg(etc.join("msi/remove-duplicates.xsl")), ); } - if built_tools.contains("rust-demangler") { - builder.run( - Command::new(&heat) - .current_dir(&exe) - .arg("dir") - .arg("rust-demangler") - .args(heat_flags) - .arg("-cg") - .arg("RustDemanglerGroup") - .arg("-dr") - .arg("RustDemangler") - .arg("-var") - .arg("var.RustDemanglerDir") - .arg("-out") - .arg(exe.join("RustDemanglerGroup.wxs")) - .arg("-t") - .arg(etc.join("msi/remove-duplicates.xsl")), - ); - } if built_tools.contains("miri") { builder.run( Command::new(&heat) @@ -1958,9 +1880,6 @@ impl Step for Extended { if built_tools.contains("rust-docs") { cmd.arg("-dDocsDir=rust-docs"); } - if built_tools.contains("rust-demangler") { - cmd.arg("-dRustDemanglerDir=rust-demangler"); - } if built_tools.contains("rust-analyzer") { cmd.arg("-dRustAnalyzerDir=rust-analyzer"); } @@ -1987,9 +1906,6 @@ impl Step for Extended { if built_tools.contains("miri") { candle("MiriGroup.wxs".as_ref()); } - if built_tools.contains("rust-demangler") { - candle("RustDemanglerGroup.wxs".as_ref()); - } if built_tools.contains("rust-analyzer") { candle("RustAnalyzerGroup.wxs".as_ref()); } @@ -2031,9 +1947,6 @@ impl Step for Extended { if built_tools.contains("rust-analyzer") { cmd.arg("RustAnalyzerGroup.wixobj"); } - if built_tools.contains("rust-demangler") { - cmd.arg("RustDemanglerGroup.wixobj"); - } if built_tools.contains("rust-docs") { cmd.arg("DocsGroup.wixobj"); } diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 6a75f35c93a6..c47233ca42ab 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -265,22 +265,6 @@ install!((self, builder, _config), ); } }; - RustDemangler, alias = "rust-demangler", Self::should_build(_config), only_hosts: true, { - // NOTE: Even though `should_build` may return true for `extended` default tools, - // dist::RustDemangler may still return None, unless the target-dependent `profiler` config - // is also true, or the `tools` array explicitly includes "rust-demangler". - if let Some(tarball) = builder.ensure(dist::RustDemangler { - compiler: self.compiler, - target: self.target - }) { - install_sh(builder, "rust-demangler", self.compiler.stage, Some(self.target), &tarball); - } else { - builder.info( - &format!("skipping Install RustDemangler stage{} ({})", - self.compiler.stage, self.target), - ); - } - }; Rustc, path = "compiler/rustc", true, only_hosts: true, { let tarball = builder.ensure(dist::Rustc { compiler: builder.compiler(builder.top_stage, self.target), diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 7556a19c90ca..445096e9786d 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -432,65 +432,6 @@ impl Step for Rustfmt { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct RustDemangler { - stage: u32, - host: TargetSelection, -} - -impl Step for RustDemangler { - type Output = (); - const ONLY_HOSTS: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/tools/rust-demangler") - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(RustDemangler { stage: run.builder.top_stage, host: run.target }); - } - - /// Runs `cargo test` for rust-demangler. - fn run(self, builder: &Builder<'_>) { - let stage = self.stage; - let host = self.host; - let compiler = builder.compiler(stage, host); - - let rust_demangler = builder.ensure(tool::RustDemangler { - compiler, - target: self.host, - extra_features: Vec::new(), - }); - let mut cargo = tool::prepare_tool_cargo( - builder, - compiler, - Mode::ToolRustc, - host, - "test", - "src/tools/rust-demangler", - SourceType::InTree, - &[], - ); - - let dir = testdir(builder, compiler.host); - t!(fs::create_dir_all(dir)); - - cargo.env("RUST_DEMANGLER_DRIVER_PATH", rust_demangler); - cargo.add_rustc_lib_path(builder); - - run_cargo_test( - cargo, - &[], - &[], - "rust-demangler", - "rust-demangler", - compiler, - host, - builder, - ); - } -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { target: TargetSelection, diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index e0a9674ae5a9..613484788b60 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -964,7 +964,6 @@ tool_extended!((self, builder), // But `builder.cargo` doesn't know how to handle ToolBootstrap in stages other than 0, // and this is close enough for now. Rls, "src/tools/rls", "rls", stable=true, tool_std=true; - RustDemangler, "src/tools/rust-demangler", "rust-demangler", stable=false, tool_std=true; Rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, add_bins_to_sysroot = ["rustfmt", "cargo-fmt"]; ); diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index d9e4cbae17d7..dd47ae96329b 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -736,7 +736,6 @@ impl<'a> Builder<'a> { tool::Rls, tool::RustAnalyzer, tool::RustAnalyzerProcMacroSrv, - tool::RustDemangler, tool::Rustdoc, tool::Clippy, tool::CargoClippy, @@ -774,7 +773,6 @@ impl<'a> Builder<'a> { clippy::RemoteTestServer, clippy::Rls, clippy::RustAnalyzer, - clippy::RustDemangler, clippy::Rustdoc, clippy::Rustfmt, clippy::RustInstaller, @@ -842,7 +840,6 @@ impl<'a> Builder<'a> { test::Miri, test::CargoMiri, test::Clippy, - test::RustDemangler, test::CompiletestTest, test::CrateRunMakeSupport, test::RustdocJSStd, @@ -903,7 +900,6 @@ impl<'a> Builder<'a> { dist::Rls, dist::RustAnalyzer, dist::Rustfmt, - dist::RustDemangler, dist::Clippy, dist::Miri, dist::LlvmTools, @@ -930,7 +926,6 @@ impl<'a> Builder<'a> { install::Cargo, install::RustAnalyzer, install::Rustfmt, - install::RustDemangler, install::Clippy, install::Miri, install::LlvmTools, diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index 57cdf7473a19..fd934f18de23 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -23,7 +23,6 @@ pub(crate) enum OverlayKind { Clippy, Miri, Rustfmt, - RustDemangler, Rls, RustAnalyzer, RustcCodegenCranelift, @@ -58,9 +57,6 @@ impl OverlayKind { "src/tools/rustfmt/LICENSE-APACHE", "src/tools/rustfmt/LICENSE-MIT", ], - OverlayKind::RustDemangler => { - &["src/tools/rust-demangler/README.md", "LICENSE-APACHE", "LICENSE-MIT"] - } OverlayKind::Rls => &["src/tools/rls/README.md", "LICENSE-APACHE", "LICENSE-MIT"], OverlayKind::RustAnalyzer => &[ "src/tools/rust-analyzer/README.md", @@ -85,7 +81,6 @@ impl OverlayKind { match self { OverlayKind::Rust => builder.rust_version(), OverlayKind::Llvm => builder.rust_version(), - OverlayKind::RustDemangler => builder.release_num("rust-demangler"), OverlayKind::Cargo => { builder.cargo_info.version(builder, &builder.release_num("cargo")) } diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index 32dc992c42fa..ed091d8fc571 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -49,12 +49,6 @@ One option for a Rust demangler is [`rustfilt`], which can be installed with: cargo install rustfilt ``` -Another option, if you are building from the Rust compiler source distribution, is to use the `rust-demangler` tool included in the Rust source distribution, which can be built with: - -```shell -$ ./x.py build rust-demangler -``` - [`rustfilt`]: https://crates.io/crates/rustfilt ## Compiling with coverage enabled @@ -164,7 +158,7 @@ $ llvm-cov show -Xdemangler=rustfilt target/debug/examples/formatjson5 \ Some of the more notable options in this example include: -- `--Xdemangler=rustfilt` - the command name or path used to demangle Rust symbols (`rustfilt` in the example, but this could also be a path to the `rust-demangler` tool) +- `--Xdemangler=rustfilt` - the command name or path used to demangle Rust symbols (`rustfilt` in the example) - `target/debug/examples/formatjson5` - the instrumented binary (from which to extract the coverage map) - `--instr-profile=.profdata` - the location of the `.profdata` file created by `llvm-profdata merge` (from the `.profraw` file generated by the instrumented binary) - `--name=` - to show coverage for a specific function (or, consider using another filter option, such as `--name-regex=`) diff --git a/src/tools/rust-demangler/Cargo.toml b/src/tools/rust-demangler/Cargo.toml deleted file mode 100644 index 2bb73b3262d8..000000000000 --- a/src/tools/rust-demangler/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "rust-demangler" -version = "0.0.1" -edition = "2021" - -[dependencies] -regex = "1.0" -rustc-demangle = "0.1.17" - -[lib] -name = "rust_demangler" -doctest = false - -[[bin]] -name = "rust-demangler" -test = false diff --git a/src/tools/rust-demangler/README.md b/src/tools/rust-demangler/README.md deleted file mode 100644 index 4e8a689a13a4..000000000000 --- a/src/tools/rust-demangler/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# rust-demangler - -_Demangles rustc mangled names._ - -`rust-demangler` supports the requirements of the [`llvm-cov show -Xdemangler` -option](https://llvm.org/docs/CommandGuide/llvm-cov.html#cmdoption-llvm-cov-show-xdemangler), -to perform Rust-specific symbol demangling: - -> _The demangler is expected to read a newline-separated list of symbols from -> stdin and write a newline-separated list of the same length to stdout._ - -To use `rust-demangler` with `llvm-cov` for example: - -```shell -$ TARGET="${PWD}/build/x86_64-unknown-linux-gnu" -$ "${TARGET}"/llvm/bin/llvm-cov show \ - --Xdemangler=path/to/rust-demangler \ - --instr-profile=main.profdata ./main --show-line-counts-or-regions -``` - -`rust-demangler` is a Rust "extended tool", used in Rust compiler tests, and -optionally included in Rust distributions that enable coverage profiling. Symbol -demangling is implemented using the -[rustc-demangle](https://crates.io/crates/rustc-demangle) crate. - -_(Note, for Rust developers, the third-party tool -[`rustfilt`](https://crates.io/crates/rustfilt) also supports `llvm-cov` symbol -demangling. `rustfilt` is a more generalized tool that searches any body of -text, using pattern matching, to find and demangle Rust symbols.)_ - -## License - -Rust-demangler is distributed under the terms of both the MIT license and the -Apache License (Version 2.0). - -See [LICENSE-APACHE](/LICENSE-APACHE) and [LICENSE-MIT](/LICENSE-MIT) for details. diff --git a/src/tools/rust-demangler/src/lib.rs b/src/tools/rust-demangler/src/lib.rs deleted file mode 100644 index 1d972229d953..000000000000 --- a/src/tools/rust-demangler/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -use regex::Regex; -use rustc_demangle::demangle; -use std::str::Lines; - -const REPLACE_COLONS: &str = "::"; - -pub fn create_disambiguator_re() -> Regex { - Regex::new(r"\[[a-f0-9]{5,16}\]::").unwrap() -} - -pub fn demangle_lines(lines: Lines<'_>, strip_crate_disambiguators: Option) -> Vec { - let mut demangled_lines = Vec::new(); - for mangled in lines { - let mut demangled = demangle(mangled).to_string(); - if let Some(re) = &strip_crate_disambiguators { - demangled = re.replace_all(&demangled, REPLACE_COLONS).to_string(); - } - demangled_lines.push(demangled); - } - demangled_lines -} diff --git a/src/tools/rust-demangler/src/main.rs b/src/tools/rust-demangler/src/main.rs deleted file mode 100644 index 1b5ef5d2442b..000000000000 --- a/src/tools/rust-demangler/src/main.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! Demangles rustc mangled names. -//! -//! Note regarding crate disambiguators: -//! -//! Some demangled symbol paths can include "crate disambiguator" suffixes, represented as a large -//! hexadecimal value enclosed in square braces, and appended to the name of the crate. a suffix to the -//! original crate name. For example, the `core` crate, here, includes a disambiguator: -//! -//! ```rust -//! as core[a7a74cee373f048]::ops::drop::Drop>::drop -//! ``` -//! -//! These disambiguators are known to vary depending on environmental circumstances. As a result, -//! tests that compare results including demangled names can fail across development environments, -//! particularly with cross-platform testing. Also, the resulting crate paths are not syntactically -//! valid, and don't match the original source symbol paths, which can impact development tools. -//! -//! For these reasons, by default, `rust-demangler` uses a heuristic to remove crate disambiguators -//! from their original demangled representation before printing them to standard output. If crate -//! disambiguators are required, add the `-d` (or `--disambiguators`) flag, and the disambiguators -//! will not be removed. -//! -//! Also note that the disambiguators are stripped by a Regex pattern that is tolerant to some -//! variation in the number of hexadecimal digits. The disambiguators come from a hash value, which -//! typically generates a 16-digit hex representation on a 64-bit architecture; however, leading -//! zeros are not included, which can shorten the hex digit length, and a different hash algorithm -//! that might also be dependent on the architecture, might shorten the length even further. A -//! minimum length of 5 digits is assumed, which should be more than sufficient to support hex -//! representations that generate only 8-digits of precision with an extremely rare (but not -//! impossible) result with up to 3 leading zeros. -//! -//! Using a minimum number of digits less than 5 risks the possibility of stripping demangled name -//! components with a similar pattern. For example, some closures instantiated multiple times -//! include their own disambiguators, demangled as non-hashed zero-based indexes in square brackets. -//! These disambiguators seem to have more analytical value (for instance, in coverage analysis), so -//! they are not removed. - -use rust_demangler::*; -use std::io::{self, Read, Write}; - -fn main() -> io::Result<()> { - // FIXME(richkadel): In Issue #77615 discussed updating the `rustc-demangle` library, to provide - // an option to generate demangled names without including crate disambiguators. If that - // happens, update this tool to use that option (if the `-d` flag is not set) instead stripping - // them via the Regex heuristic. The update the doc comments and help. - - // Strip hashed hexadecimal crate disambiguators. Leading zeros are not enforced, and can be - // different across different platform/architecture types, so while 16 hex digits are common, - // they can also be shorter. - // - // Also note that a demangled symbol path may include the `[]` pattern, with zero-based - // indexes (such as for closures, and possibly for types defined in anonymous scopes). Preferably - // these should not be stripped. - // - // The minimum length of 5 digits supports the possibility that some target architecture (maybe - // a 32-bit or smaller architecture) could generate a hash value with a maximum of 8 digits, - // and more than three leading zeros should be extremely unlikely. Conversely, it should be - // sufficient to assume the zero-based indexes for closures and anonymous scopes will never - // exceed the value 9999. - let mut strip_crate_disambiguators = Some(create_disambiguator_re()); - - let mut args = std::env::args(); - let progname = args.next().unwrap(); - for arg in args { - if arg == "--disambiguators" || arg == "-d" { - strip_crate_disambiguators = None; - } else { - eprintln!(); - eprintln!("Usage: {} [-d|--disambiguators]", progname); - eprintln!(); - eprintln!( - "This tool converts a list of Rust mangled symbols (one per line) into a\n\ - corresponding list of demangled symbols." - ); - eprintln!(); - eprintln!( - "With -d (--disambiguators), Rust symbols mangled with the v0 symbol mangler may\n\ - include crate disambiguators (a hexadecimal hash value, typically up to 16 digits\n\ - long, enclosed in square brackets)." - ); - eprintln!(); - eprintln!( - "By default, crate disambiguators are removed, using a heuristics-based regular\n\ - expression. (See the `rust-demangler` doc comments for more information.)" - ); - eprintln!(); - std::process::exit(1) - } - } - - let mut buffer = String::new(); - io::stdin().read_to_string(&mut buffer)?; - let mut demangled_lines = demangle_lines(buffer.lines(), strip_crate_disambiguators); - demangled_lines.push("".to_string()); // ensure a trailing newline - io::stdout().write_all(demangled_lines.join("\n").as_bytes())?; - Ok(()) -} diff --git a/src/tools/rust-demangler/tests/lib.rs b/src/tools/rust-demangler/tests/lib.rs deleted file mode 100644 index 85019df7867d..000000000000 --- a/src/tools/rust-demangler/tests/lib.rs +++ /dev/null @@ -1,84 +0,0 @@ -use rust_demangler::*; - -const MANGLED_INPUT: &str = r" -_RNvC6_123foo3bar -_RNqCs4fqI2P2rA04_11utf8_identsu30____7hkackfecea1cbdathfdh9hlq6y -_RNCNCNgCs6DXkGYLi8lr_2cc5spawn00B5_ -_RNCINkXs25_NgCsbmNqQUJIY6D_4core5sliceINyB9_4IterhENuNgNoBb_4iter8iterator8Iterator9rpositionNCNgNpB9_6memchr7memrchrs_0E0Bb_ -_RINbNbCskIICzLVDPPb_5alloc5alloc8box_freeDINbNiB4_5boxed5FnBoxuEp6OutputuEL_ECs1iopQbuBiw2_3std -INtC8arrayvec8ArrayVechKj7b_E -_RMCs4fqI2P2rA04_13const_genericINtB0_8UnsignedKhb_E -_RMCs4fqI2P2rA04_13const_genericINtB0_6SignedKs98_E -_RMCs4fqI2P2rA04_13const_genericINtB0_6SignedKanb_E -_RMCs4fqI2P2rA04_13const_genericINtB0_4BoolKb0_E -_RMCs4fqI2P2rA04_13const_genericINtB0_4BoolKb1_E -_RMCs4fqI2P2rA04_13const_genericINtB0_4CharKc76_E -_RMCs4fqI2P2rA04_13const_genericINtB0_4CharKca_E -_RMCs4fqI2P2rA04_13const_genericINtB0_4CharKc2202_E -_RNvNvMCs4fqI2P2rA04_13const_genericINtB4_3FooKpE3foo3FOO -_RC3foo.llvm.9D1C9369 -_RC3foo.llvm.9D1C9369@@16 -_RNvC9backtrace3foo.llvm.A5310EB9 -_RNvNtNtNtNtCs92dm3009vxr_4rand4rngs7adapter9reseeding4fork23FORK_HANDLER_REGISTERED.0.0 -"; - -const DEMANGLED_OUTPUT: &str = r" -123foo[0]::bar -utf8_idents[317d481089b8c8fe]::საჭმელად_გემრიელი_სადილი -cc[4d6468d6c9fd4bb3]::spawn::{closure#0}::{closure#0} - as core[846817f741e54dfd]::iter::iterator::Iterator>::rposition::::{closure#0} -alloc[f15a878b47eb696b]::alloc::box_free::> -INtC8arrayvec8ArrayVechKj7b_E -> -> -> -> -> -> -> -> ->::foo::FOO -foo[0] -foo[0] -backtrace[0]::foo -rand[693ea8e72247470f]::rngs::adapter::reseeding::fork::FORK_HANDLER_REGISTERED.0.0 -"; - -const DEMANGLED_OUTPUT_NO_CRATE_DISAMBIGUATORS: &str = r" -123foo[0]::bar -utf8_idents::საჭმელად_გემრიელი_სადილი -cc::spawn::{closure#0}::{closure#0} - as core::iter::iterator::Iterator>::rposition::::{closure#0} -alloc::alloc::box_free::> -INtC8arrayvec8ArrayVechKj7b_E -> -> -> -> -> -> -> -> ->::foo::FOO -foo[0] -foo[0] -backtrace[0]::foo -rand::rngs::adapter::reseeding::fork::FORK_HANDLER_REGISTERED.0.0 -"; - -#[test] -fn test_demangle_lines() { - let demangled_lines = demangle_lines(MANGLED_INPUT.lines(), None); - for (expected, actual) in DEMANGLED_OUTPUT.lines().zip(demangled_lines) { - assert_eq!(expected, actual); - } -} - -#[test] -fn test_demangle_lines_no_crate_disambiguators() { - let demangled_lines = demangle_lines(MANGLED_INPUT.lines(), Some(create_disambiguator_re())); - for (expected, actual) in DEMANGLED_OUTPUT_NO_CRATE_DISAMBIGUATORS.lines().zip(demangled_lines) - { - assert_eq!(expected, actual); - } -} From d70d76b9e7cd80b01797b433dd2baf7a6720e21e Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 19 Jun 2024 13:08:26 +0200 Subject: [PATCH 087/147] Rework doc-test attribute documentation example --- src/doc/rustdoc/src/write-documentation/the-doc-attribute.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md index 669dc9358ebf..ff033aa14b82 100644 --- a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md +++ b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md @@ -144,10 +144,10 @@ it will not. ### `test(attr(...))` This form of the `doc` attribute allows you to add arbitrary attributes to all your doctests. For -example, if you want your doctests to fail if they produce any warnings, you could add this: +example, if you want your doctests to fail if they have dead code, you could add this: ```rust,no_run -#![doc(test(attr(deny(warnings))))] +#![doc(test(attr(deny(dead_code))))] ``` ## At the item level From db5ed4bd799cda1217ec6431ffa56cecd09ef6e9 Mon Sep 17 00:00:00 2001 From: Michael Baikov Date: Thu, 13 Jun 2024 08:27:21 -0400 Subject: [PATCH 088/147] Allow for try_force_from_dep_node to fail The way it is implemented currently try_force_from_dep_node returns true as long as there's a function to force the query. It wasn't this way from the beginning, earlier version was producing forcing result and it was changed in https://github.com/rust-lang/rust/pull/89978, I couldn't find any comments addressing this change. One way it can fail is by failing to recover the query in DepNodeParams::recover - when we are trying to query something that no longer exists in the current environment --- .../rustc_query_system/src/dep_graph/mod.rs | 18 +++++---- tests/incremental/unrecoverable_query.rs | 40 +++++++++++++++++++ 2 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 tests/incremental/unrecoverable_query.rs diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index cbd802958870..cfb25ec905fb 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -51,20 +51,24 @@ pub trait DepContext: Copy { } /// Try to force a dep node to execute and see if it's green. + /// + /// Returns true if the query has actually been forced. It is valid that a query + /// fails to be forced, e.g. when the query key cannot be reconstructed from the + /// dep-node or when the query kind outright does not support it. #[inline] #[instrument(skip(self, frame), level = "debug")] fn try_force_from_dep_node(self, dep_node: DepNode, frame: Option<&MarkFrame<'_>>) -> bool { let cb = self.dep_kind_info(dep_node.kind); if let Some(f) = cb.force_from_dep_node { - if let Err(value) = panic::catch_unwind(panic::AssertUnwindSafe(|| { - f(self, dep_node); - })) { - if !value.is::() { - print_markframe_trace(self.dep_graph(), frame); + match panic::catch_unwind(panic::AssertUnwindSafe(|| f(self, dep_node))) { + Err(value) => { + if !value.is::() { + print_markframe_trace(self.dep_graph(), frame); + } + panic::resume_unwind(value) } - panic::resume_unwind(value) + Ok(query_has_been_forced) => query_has_been_forced, } - true } else { false } diff --git a/tests/incremental/unrecoverable_query.rs b/tests/incremental/unrecoverable_query.rs new file mode 100644 index 000000000000..e17236bebd25 --- /dev/null +++ b/tests/incremental/unrecoverable_query.rs @@ -0,0 +1,40 @@ +// If it is impossible to find query arguments just from the hash +// compiler should treat the node as red + +// In this test prior to fixing compiler was having problems figuring out +// drop impl for T inside of m + +//@ revisions:cfail1 cfail2 +//@ compile-flags: --crate-type=lib +//@ build-pass + +pub trait P { + type A; +} + +struct S; + +impl P for S { + type A = C; +} + +struct T(D::A, Z); + +struct Z(D::A, String); + +impl T { + pub fn i() -> Self { + loop {} + } +} + +enum C { + #[cfg(cfail1)] + Up(()), + #[cfg(cfail2)] + Lorry(()), +} + +pub fn m() { + T::::i(); +} From 12f8d12b418c9bbe4d2142f2c8e00a28a5b6dfe5 Mon Sep 17 00:00:00 2001 From: Michael Baikov Date: Thu, 13 Jun 2024 08:46:30 -0400 Subject: [PATCH 089/147] local_def_path_hash_to_def_id can fail local_def_path_hash_to_def_id is used by Debug impl for DepNode and it looks for DefPathHash inside the current compilation. During incremental compilation we are going through nodes that belong to a previous compilation and might not be present and a simple attempt to print such node with tracing::debug (try_mark_parent_green does it for example) results in a otherwise avoidable panic Panic was added in https://github.com/rust-lang/rust/pull/82183, specifically in 2b60338ee9, with a comment "We only use this mapping for cases where we know that it must succeed.", but I'm not sure if this property holds when we traverse nodes from the old compilation in order to figure out if they are valid or not --- compiler/rustc_hir/src/definitions.rs | 17 ++++++----------- compiler/rustc_middle/src/dep_graph/dep_node.rs | 12 ++---------- .../rustc_middle/src/query/on_disk_cache.rs | 8 ++++---- compiler/rustc_middle/src/ty/context.rs | 14 +++----------- 4 files changed, 15 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index 35833e258d55..5c86135ec8de 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -377,22 +377,17 @@ impl Definitions { } #[inline(always)] - pub fn local_def_path_hash_to_def_id( - &self, - hash: DefPathHash, - err_msg: &dyn std::fmt::Debug, - ) -> LocalDefId { + /// Returns `None` if the `DefPathHash` does not correspond to a `LocalDefId` + /// in the current compilation session. This can legitimately happen if the + /// `DefPathHash` is from a `DefId` in an upstream crate or, during incr. comp., + /// if the `DefPathHash` is from a previous compilation session and + /// the def-path does not exist anymore. + pub fn local_def_path_hash_to_def_id(&self, hash: DefPathHash) -> Option { debug_assert!(hash.stable_crate_id() == self.table.stable_crate_id); - #[cold] - #[inline(never)] - fn err(err_msg: &dyn std::fmt::Debug) -> ! { - panic!("{err_msg:?}") - } self.table .def_path_hash_to_index .get(&hash.local_hash()) .map(|local_def_index| LocalDefId { local_def_index }) - .unwrap_or_else(|| err(err_msg)) } pub fn def_path_hash_to_def_index_map(&self) -> &DefPathHashMap { diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 3c5bf6eb8246..84b47a6ed447 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -194,10 +194,7 @@ impl DepNodeExt for DepNode { /// has been removed. fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option { if tcx.fingerprint_style(self.kind) == FingerprintStyle::DefPathHash { - Some(tcx.def_path_hash_to_def_id( - DefPathHash(self.hash.into()), - &("Failed to extract DefId", self.kind, self.hash), - )) + tcx.def_path_hash_to_def_id(DefPathHash(self.hash.into())) } else { None } @@ -390,12 +387,7 @@ impl<'tcx> DepNodeParams> for HirId { if tcx.fingerprint_style(dep_node.kind) == FingerprintStyle::HirId { let (local_hash, local_id) = Fingerprint::from(dep_node.hash).split(); let def_path_hash = DefPathHash::new(tcx.stable_crate_id(LOCAL_CRATE), local_hash); - let def_id = tcx - .def_path_hash_to_def_id( - def_path_hash, - &("Failed to extract HirId", dep_node.kind, dep_node.hash), - ) - .expect_local(); + let def_id = tcx.def_path_hash_to_def_id(def_path_hash)?.expect_local(); let local_id = local_id .as_u64() .try_into() diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index ccd0c7cb10c4..924249bf37d6 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -733,10 +733,10 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { // If we get to this point, then all of the query inputs were green, // which means that the definition with this hash is guaranteed to // still exist in the current compilation session. - self.tcx.def_path_hash_to_def_id( - def_path_hash, - &("Failed to convert DefPathHash", def_path_hash), - ) + match self.tcx.def_path_hash_to_def_id(def_path_hash) { + Some(r) => r, + None => panic!("Failed to convert DefPathHash {def_path_hash:?}"), + } } fn decode_attr_id(&mut self) -> rustc_span::AttrId { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 6d64c1d50aea..7848aa21eac9 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1677,11 +1677,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation /// session, if it still exists. This is used during incremental compilation to /// turn a deserialized `DefPathHash` into its current `DefId`. - pub fn def_path_hash_to_def_id( - self, - hash: DefPathHash, - err_msg: &dyn std::fmt::Debug, - ) -> DefId { + pub fn def_path_hash_to_def_id(self, hash: DefPathHash) -> Option { debug!("def_path_hash_to_def_id({:?})", hash); let stable_crate_id = hash.stable_crate_id(); @@ -1689,13 +1685,9 @@ impl<'tcx> TyCtxt<'tcx> { // If this is a DefPathHash from the local crate, we can look up the // DefId in the tcx's `Definitions`. if stable_crate_id == self.stable_crate_id(LOCAL_CRATE) { - self.untracked - .definitions - .read() - .local_def_path_hash_to_def_id(hash, err_msg) - .to_def_id() + Some(self.untracked.definitions.read().local_def_path_hash_to_def_id(hash)?.to_def_id()) } else { - self.def_path_hash_to_def_id_extern(hash, stable_crate_id) + Some(self.def_path_hash_to_def_id_extern(hash, stable_crate_id)) } } From 0c2bfd913ed12872e98d02201eb706ce3ffcb013 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 19 Jun 2024 13:57:01 +0200 Subject: [PATCH 090/147] Migrate `run-make/glibc-staticlib-args` to `rmake.rs` --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/glibc-staticlib-args/Makefile | 13 ------------- tests/run-make/glibc-staticlib-args/rmake.rs | 18 ++++++++++++++++++ 3 files changed, 18 insertions(+), 14 deletions(-) delete mode 100644 tests/run-make/glibc-staticlib-args/Makefile create mode 100644 tests/run-make/glibc-staticlib-args/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 24e51b8fee51..9e2d922f23d2 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -59,7 +59,6 @@ run-make/forced-unwind-terminate-pof/Makefile run-make/foreign-double-unwind/Makefile run-make/foreign-exceptions/Makefile run-make/foreign-rust-exceptions/Makefile -run-make/glibc-staticlib-args/Makefile run-make/include_bytes_deps/Makefile run-make/incr-add-rust-src-component/Makefile run-make/incr-foreign-head-span/Makefile diff --git a/tests/run-make/glibc-staticlib-args/Makefile b/tests/run-make/glibc-staticlib-args/Makefile deleted file mode 100644 index cad6c049e459..000000000000 --- a/tests/run-make/glibc-staticlib-args/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# ignore-cross-compile -# only-gnu -# only-linux - -include ../tools.mk - -# This ensures that std::env::args works in a library called from C on glibc Linux. - -all: - $(RUSTC) --crate-type=staticlib library.rs - $(CC) program.c $(call STATICLIB,library) $(call OUT_EXE,program) \ - $(EXTRACFLAGS) $(EXTRACXXFLAGS) - $(call RUN,program) diff --git a/tests/run-make/glibc-staticlib-args/rmake.rs b/tests/run-make/glibc-staticlib-args/rmake.rs new file mode 100644 index 000000000000..8ab10419ab9d --- /dev/null +++ b/tests/run-make/glibc-staticlib-args/rmake.rs @@ -0,0 +1,18 @@ +// This ensures that std::env::args works in a library called from C on glibc Linux. + +//@ only-gnu +//@ only-linux +//@ ignore-cross-compile + +use run_make_support::{bin_name, cc, extra_c_flags, extra_cxx_flags, run, rustc, static_lib_name}; + +fn main() { + rustc().input("library.rs").crate_type("staticlib").run(); + cc().input("program.c") + .arg(static_lib_name("library")) + .out_exe("program") + .args(&extra_c_flags()) + .args(&extra_cxx_flags()) + .run(); + run(&bin_name("program")); +} From ebdfcd93a3a71bde87be117bdb9e01e0ade34981 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 25 Aug 2023 13:45:35 +0100 Subject: [PATCH 091/147] Stabilise c_unwind --- compiler/rustc_feature/src/accepted.rs | 2 ++ compiler/rustc_feature/src/unstable.rs | 2 -- compiler/rustc_middle/src/ty/layout.rs | 36 +------------------ .../src/ffi_unwind_calls.rs | 34 +----------------- library/alloc/src/lib.rs | 2 +- library/core/src/lib.rs | 2 +- library/panic_abort/src/lib.rs | 2 +- library/panic_unwind/src/lib.rs | 2 +- library/std/src/lib.rs | 2 +- library/unwind/src/lib.rs | 2 +- .../src/language-features/c-unwind.md | 26 -------------- 11 files changed, 10 insertions(+), 102 deletions(-) delete mode 100644 src/doc/unstable-book/src/language-features/c-unwind.md diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index ef141c7c25e5..9beaa6b8d95f 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -80,6 +80,8 @@ declare_features! ( (accepted, braced_empty_structs, "1.8.0", Some(29720)), /// Allows `c"foo"` literals. (accepted, c_str_literals, "1.77.0", Some(105723)), + /// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries and treat `extern "C" fn` as nounwind. + (accepted, c_unwind, "CURRENT_RUSTC_VERSION", Some(74990)), /// Allows `#[cfg_attr(predicate, multiple, attributes, here)]`. (accepted, cfg_attr_multi, "1.33.0", Some(54881)), /// Allows the use of `#[cfg(doctest)]`, set when rustdoc is collecting doctests. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 5cfbcdcbbbe0..45527bec2f2e 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -363,8 +363,6 @@ declare_features! ( (unstable, async_for_loop, "1.77.0", Some(118898)), /// Allows builtin # foo() syntax (unstable, builtin_syntax, "1.71.0", Some(110680)), - /// Treat `extern "C"` function as nounwind. - (unstable, c_unwind, "1.52.0", Some(74990)), /// Allows using C-variadics. (unstable, c_variadic, "1.34.0", Some(44930)), /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 3d397b6b37e1..f608b02f42c9 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1182,37 +1182,6 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: SpecAbi) -> // ABIs have such an option. Otherwise the only other thing here is Rust // itself, and those ABIs are determined by the panic strategy configured // for this compilation. - // - // Unfortunately at this time there's also another caveat. Rust [RFC - // 2945][rfc] has been accepted and is in the process of being implemented - // and stabilized. In this interim state we need to deal with historical - // rustc behavior as well as plan for future rustc behavior. - // - // Historically functions declared with `extern "C"` were marked at the - // codegen layer as `nounwind`. This happened regardless of `panic=unwind` - // or not. This is UB for functions in `panic=unwind` mode that then - // actually panic and unwind. Note that this behavior is true for both - // externally declared functions as well as Rust-defined function. - // - // To fix this UB rustc would like to change in the future to catch unwinds - // from function calls that may unwind within a Rust-defined `extern "C"` - // function and forcibly abort the process, thereby respecting the - // `nounwind` attribute emitted for `extern "C"`. This behavior change isn't - // ready to roll out, so determining whether or not the `C` family of ABIs - // unwinds is conditional not only on their definition but also whether the - // `#![feature(c_unwind)]` feature gate is active. - // - // Note that this means that unlike historical compilers rustc now, by - // default, unconditionally thinks that the `C` ABI may unwind. This will - // prevent some optimization opportunities, however, so we try to scope this - // change and only assume that `C` unwinds with `panic=unwind` (as opposed - // to `panic=abort`). - // - // Eventually the check against `c_unwind` here will ideally get removed and - // this'll be a little cleaner as it'll be a straightforward check of the - // ABI. - // - // [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md use SpecAbi::*; match abi { C { unwind } @@ -1224,10 +1193,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: SpecAbi) -> | Thiscall { unwind } | Aapcs { unwind } | Win64 { unwind } - | SysV64 { unwind } => { - unwind - || (!tcx.features().c_unwind && tcx.sess.panic_strategy() == PanicStrategy::Unwind) - } + | SysV64 { unwind } => unwind, PtxKernel | Msp430Interrupt | X86Interrupt diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index 5e3cd8536750..0cb304da80a1 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -11,38 +11,6 @@ use rustc_target::spec::PanicStrategy; use crate::errors; -/// Some of the functions declared as "may unwind" by `fn_can_unwind` can't actually unwind. In -/// particular, `extern "C"` is still considered as can-unwind on stable, but we need to consider -/// it cannot-unwind here. So below we check `fn_can_unwind() && abi_can_unwind()` before concluding -/// that a function call can unwind. -fn abi_can_unwind(abi: Abi) -> bool { - use Abi::*; - match abi { - C { unwind } - | System { unwind } - | Cdecl { unwind } - | Stdcall { unwind } - | Fastcall { unwind } - | Vectorcall { unwind } - | Thiscall { unwind } - | Aapcs { unwind } - | Win64 { unwind } - | SysV64 { unwind } => unwind, - PtxKernel - | Msp430Interrupt - | X86Interrupt - | EfiApi - | AvrInterrupt - | AvrNonBlockingInterrupt - | RiscvInterruptM - | RiscvInterruptS - | CCmseNonSecureCall - | Wasm - | Unadjusted => false, - RustIntrinsic | Rust | RustCall | RustCold => unreachable!(), // these ABIs are already skipped earlier - } -} - // Check if the body of this def_id can possibly leak a foreign unwind into Rust code. fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { debug!("has_ffi_unwind_calls({local_def_id:?})"); @@ -103,7 +71,7 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { _ => bug!("invalid callee of type {:?}", ty), }; - if layout::fn_can_unwind(tcx, fn_def_id, sig.abi()) && abi_can_unwind(sig.abi()) { + if layout::fn_can_unwind(tcx, fn_def_id, sig.abi()) { // We have detected a call that can possibly leak foreign unwind. // // Because the function body itself can unwind, we are not aborting this function call diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 895d1b8d59f2..022a14b931a3 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -165,12 +165,12 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(c_unwind))] #![cfg_attr(not(test), feature(coroutine_trait))] #![cfg_attr(test, feature(panic_update_hook))] #![cfg_attr(test, feature(test))] #![feature(allocator_internals)] #![feature(allow_internal_unstable)] -#![feature(c_unwind)] #![feature(cfg_sanitize)] #![feature(const_mut_refs)] #![feature(const_precise_live_drops)] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index ef28bc99c4fc..a533a7938f13 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -199,13 +199,13 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(c_unwind))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(asm_const)] #![feature(auto_traits)] -#![feature(c_unwind)] #![feature(cfg_sanitize)] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_has_atomic_equal_alignment)] diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index 353de8c5c574..14ba4af2bb57 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -14,7 +14,7 @@ #![feature(std_internals)] #![feature(staged_api)] #![feature(rustc_attrs)] -#![feature(c_unwind)] +#![cfg_attr(bootstrap, feature(c_unwind))] #![allow(internal_features)] #[cfg(target_os = "android")] diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index b0245de501e7..4dc361206084 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -24,7 +24,7 @@ #![feature(rustc_attrs)] #![panic_runtime] #![feature(panic_runtime)] -#![feature(c_unwind)] +#![cfg_attr(bootstrap, feature(c_unwind))] // `real_imp` is unused with Miri, so silence warnings. #![cfg_attr(miri, allow(dead_code))] #![allow(internal_features)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 1c226f9f08f1..80f67838ac00 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -273,12 +273,12 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(c_unwind))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(asm_experimental_arch)] -#![feature(c_unwind)] #![feature(cfg_sanitizer_cfi)] #![feature(cfg_target_thread_local)] #![feature(cfi_encoding)] diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 544d9fbf1ae0..45a1c334a44d 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -2,7 +2,7 @@ #![unstable(feature = "panic_unwind", issue = "32837")] #![feature(link_cfg)] #![feature(staged_api)] -#![feature(c_unwind)] +#![cfg_attr(bootstrap, feature(c_unwind))] #![feature(strict_provenance)] #![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] #![cfg_attr(not(target_env = "msvc"), feature(libc))] diff --git a/src/doc/unstable-book/src/language-features/c-unwind.md b/src/doc/unstable-book/src/language-features/c-unwind.md deleted file mode 100644 index fb32918d5e43..000000000000 --- a/src/doc/unstable-book/src/language-features/c-unwind.md +++ /dev/null @@ -1,26 +0,0 @@ -# `c_unwind` - -The tracking issue for this feature is: [#74990] - -[#74990]: https://github.com/rust-lang/rust/issues/74990 - ------------------------- - -Introduces new ABI strings: -- "C-unwind" -- "cdecl-unwind" -- "stdcall-unwind" -- "fastcall-unwind" -- "vectorcall-unwind" -- "thiscall-unwind" -- "aapcs-unwind" -- "win64-unwind" -- "sysv64-unwind" -- "system-unwind" - -These enable unwinding from other languages (such as C++) into Rust frames and -from Rust into other languages. - -See [RFC 2945] for more information. - -[RFC 2945]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md From 5812b1fd124768991b690621654aea3eea7400f4 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 25 Aug 2023 13:52:51 +0100 Subject: [PATCH 092/147] Remove c_unwind from tests and fix tests --- .../exported_symbol_bad_unwind1.rs | 2 -- .../exported_symbol_bad_unwind2.rs | 2 +- src/tools/miri/tests/fail/panic/bad_unwind.rs | 2 -- .../miri/tests/fail/terminate-terminator.rs | 2 -- .../miri/tests/fail/unwind-action-terminate.rs | 2 -- .../exported_symbol_good_unwind.rs | 2 +- tests/assembly/asm/aarch64-modifiers.rs | 2 +- tests/assembly/asm/arm-modifiers.rs | 2 +- tests/assembly/asm/x86-modifiers.rs | 2 +- tests/assembly/simd-bitmask.rs | 2 +- tests/assembly/simd-intrinsic-gather.rs | 2 +- tests/assembly/simd-intrinsic-mask-load.rs | 2 +- tests/assembly/simd-intrinsic-mask-reduce.rs | 2 +- tests/assembly/simd-intrinsic-mask-store.rs | 2 +- tests/assembly/simd-intrinsic-scatter.rs | 2 +- tests/assembly/simd-intrinsic-select.rs | 2 +- tests/codegen/align-byval-alignment-mismatch.rs | 4 ++-- tests/codegen/avr/avr-func-addrspace.rs | 2 +- tests/codegen/catch-unwind.rs | 1 - tests/codegen/cffi/c-variadic.rs | 1 - .../debuginfo-inline-callsite-location.rs | 2 +- tests/codegen/unwind-abis/aapcs-unwind-abi.rs | 2 +- .../unwind-abis/c-unwind-abi-panic-abort.rs | 1 - tests/codegen/unwind-abis/c-unwind-abi.rs | 1 - tests/codegen/unwind-abis/cdecl-unwind-abi.rs | 1 - .../codegen/unwind-abis/fastcall-unwind-abi.rs | 2 +- .../nounwind-on-stable-panic-unwind.rs | 17 ----------------- tests/codegen/unwind-abis/nounwind.rs | 1 - tests/codegen/unwind-abis/stdcall-unwind-abi.rs | 2 +- tests/codegen/unwind-abis/system-unwind-abi.rs | 1 - tests/codegen/unwind-abis/sysv64-unwind-abi.rs | 2 +- .../codegen/unwind-abis/thiscall-unwind-abi.rs | 2 +- .../unwind-abis/vectorcall-unwind-abi.rs | 2 +- tests/codegen/unwind-abis/win64-unwind-abi.rs | 2 +- tests/codegen/unwind-and-panic-abort.rs | 1 - tests/codegen/unwind-extern-exports.rs | 1 - tests/codegen/unwind-extern-imports.rs | 1 - tests/coverage/abort.cov-map | 8 ++++---- tests/coverage/abort.coverage | 1 - tests/coverage/abort.rs | 1 - .../run-make/forced-unwind-terminate-pof/foo.rs | 1 - tests/ui/asm/x86_64/may_unwind.rs | 2 +- tests/ui/consts/const-eval/unwind-abort.rs | 2 +- tests/ui/consts/unwind-abort.rs | 2 +- tests/ui/panics/abort-on-panic.rs | 1 - tests/ui/panics/panic-in-ffi.rs | 1 - tests/ui/panics/panic-in-ffi.run.stderr | 2 +- tests/ui/unwind-abis/feature-gate-c_unwind.rs | 4 ---- 48 files changed, 31 insertions(+), 75 deletions(-) delete mode 100644 tests/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs delete mode 100644 tests/ui/unwind-abis/feature-gate-c_unwind.rs diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind1.rs b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind1.rs index 6d68b9a46d9e..dc192d031963 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind1.rs +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind1.rs @@ -1,5 +1,3 @@ -#![feature(c_unwind)] - #[no_mangle] extern "C-unwind" fn unwind() { panic!(); diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs index e6aff19b02d4..1382e9571f31 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs @@ -4,7 +4,7 @@ //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" //@[definition,both]error-in-other-file: aborted execution -#![feature(rustc_attrs, c_unwind)] +#![feature(rustc_attrs)] #[cfg_attr(any(definition, both), rustc_nounwind)] #[no_mangle] diff --git a/src/tools/miri/tests/fail/panic/bad_unwind.rs b/src/tools/miri/tests/fail/panic/bad_unwind.rs index 8c8a9f18cdc6..5370485b2a62 100644 --- a/src/tools/miri/tests/fail/panic/bad_unwind.rs +++ b/src/tools/miri/tests/fail/panic/bad_unwind.rs @@ -1,5 +1,3 @@ -#![feature(c_unwind)] - //! Unwinding when the caller ABI is "C" (without "-unwind") is UB. // The opposite version (callee does not allow unwinding) is impossible to // even write: MIR validation catches functions that have `UnwindContinue` but diff --git a/src/tools/miri/tests/fail/terminate-terminator.rs b/src/tools/miri/tests/fail/terminate-terminator.rs index 7c6728280305..465625c75725 100644 --- a/src/tools/miri/tests/fail/terminate-terminator.rs +++ b/src/tools/miri/tests/fail/terminate-terminator.rs @@ -7,8 +7,6 @@ // Enable MIR inlining to ensure that `TerminatorKind::UnwindTerminate` is generated // instead of just `UnwindAction::Terminate`. -#![feature(c_unwind)] - struct Foo; impl Drop for Foo { diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.rs b/src/tools/miri/tests/fail/unwind-action-terminate.rs index 86406872c599..465e07c8db4b 100644 --- a/src/tools/miri/tests/fail/unwind-action-terminate.rs +++ b/src/tools/miri/tests/fail/unwind-action-terminate.rs @@ -3,8 +3,6 @@ //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" -#![feature(c_unwind)] - extern "C" fn panic_abort() { panic!() } diff --git a/src/tools/miri/tests/panic/function_calls/exported_symbol_good_unwind.rs b/src/tools/miri/tests/panic/function_calls/exported_symbol_good_unwind.rs index 71b799a1f12b..0e8d45af27fd 100644 --- a/src/tools/miri/tests/panic/function_calls/exported_symbol_good_unwind.rs +++ b/src/tools/miri/tests/panic/function_calls/exported_symbol_good_unwind.rs @@ -2,7 +2,7 @@ // found in this form" errors works without `-C prefer-dynamic` (`panic!` calls foreign function // `__rust_start_panic`). // no-prefer-dynamic -#![feature(c_unwind, unboxed_closures)] +#![feature(unboxed_closures)] use std::panic; diff --git a/tests/assembly/asm/aarch64-modifiers.rs b/tests/assembly/asm/aarch64-modifiers.rs index ffba06ae47bb..b7ef1d77ea02 100644 --- a/tests/assembly/asm/aarch64-modifiers.rs +++ b/tests/assembly/asm/aarch64-modifiers.rs @@ -1,5 +1,5 @@ //@ assembly-output: emit-asm -//@ compile-flags: -O +//@ compile-flags: -O -C panic=abort //@ compile-flags: --target aarch64-unknown-linux-gnu //@ needs-llvm-components: aarch64 diff --git a/tests/assembly/asm/arm-modifiers.rs b/tests/assembly/asm/arm-modifiers.rs index fa2e75eb35b0..0674e169d72e 100644 --- a/tests/assembly/asm/arm-modifiers.rs +++ b/tests/assembly/asm/arm-modifiers.rs @@ -1,5 +1,5 @@ //@ assembly-output: emit-asm -//@ compile-flags: -O +//@ compile-flags: -O -C panic=abort //@ compile-flags: --target armv7-unknown-linux-gnueabihf //@ compile-flags: -C target-feature=+neon //@ needs-llvm-components: arm diff --git a/tests/assembly/asm/x86-modifiers.rs b/tests/assembly/asm/x86-modifiers.rs index 1a92585298d9..c5e393b10564 100644 --- a/tests/assembly/asm/x86-modifiers.rs +++ b/tests/assembly/asm/x86-modifiers.rs @@ -1,6 +1,6 @@ //@ revisions: x86_64 i686 //@ assembly-output: emit-asm -//@ compile-flags: -O +//@ compile-flags: -O -C panic=abort //@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu //@[x86_64] needs-llvm-components: x86 //@[i686] compile-flags: --target i686-unknown-linux-gnu diff --git a/tests/assembly/simd-bitmask.rs b/tests/assembly/simd-bitmask.rs index 8264a7068520..cd22ca067061 100644 --- a/tests/assembly/simd-bitmask.rs +++ b/tests/assembly/simd-bitmask.rs @@ -11,7 +11,7 @@ //@ [aarch64] needs-llvm-components: aarch64 //@ [aarch64] min-llvm-version: 18.0 //@ assembly-output: emit-asm -//@ compile-flags: --crate-type=lib -O +//@ compile-flags: --crate-type=lib -O -C panic=abort #![feature(no_core, lang_items, repr_simd, intrinsics)] #![no_core] diff --git a/tests/assembly/simd-intrinsic-gather.rs b/tests/assembly/simd-intrinsic-gather.rs index ef6b597c25f1..83015f05ab3e 100644 --- a/tests/assembly/simd-intrinsic-gather.rs +++ b/tests/assembly/simd-intrinsic-gather.rs @@ -4,7 +4,7 @@ //@ [x86-avx512] needs-llvm-components: x86 //@ [x86-avx512] min-llvm-version: 18.0 //@ assembly-output: emit-asm -//@ compile-flags: --crate-type=lib -O +//@ compile-flags: --crate-type=lib -O -C panic=abort #![feature(no_core, lang_items, repr_simd, intrinsics)] #![no_core] diff --git a/tests/assembly/simd-intrinsic-mask-load.rs b/tests/assembly/simd-intrinsic-mask-load.rs index 49d231c45f85..d537c143d365 100644 --- a/tests/assembly/simd-intrinsic-mask-load.rs +++ b/tests/assembly/simd-intrinsic-mask-load.rs @@ -6,7 +6,7 @@ //@ [x86-avx512] compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bw,+avx512dq //@ [x86-avx512] needs-llvm-components: x86 //@ assembly-output: emit-asm -//@ compile-flags: --crate-type=lib -O +//@ compile-flags: --crate-type=lib -O -C panic=abort #![feature(no_core, lang_items, repr_simd, intrinsics)] #![no_core] diff --git a/tests/assembly/simd-intrinsic-mask-reduce.rs b/tests/assembly/simd-intrinsic-mask-reduce.rs index 72c3f6376e80..dd4dbaeda76c 100644 --- a/tests/assembly/simd-intrinsic-mask-reduce.rs +++ b/tests/assembly/simd-intrinsic-mask-reduce.rs @@ -8,7 +8,7 @@ //@ [aarch64] needs-llvm-components: aarch64 //@ [aarch64] min-llvm-version: 18.0 //@ assembly-output: emit-asm -//@ compile-flags: --crate-type=lib -O +//@ compile-flags: --crate-type=lib -O -C panic=abort #![feature(no_core, lang_items, repr_simd, intrinsics)] #![no_core] diff --git a/tests/assembly/simd-intrinsic-mask-store.rs b/tests/assembly/simd-intrinsic-mask-store.rs index a6611e1c23d5..5d4c00c38233 100644 --- a/tests/assembly/simd-intrinsic-mask-store.rs +++ b/tests/assembly/simd-intrinsic-mask-store.rs @@ -6,7 +6,7 @@ //@ [x86-avx512] compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bw,+avx512dq //@ [x86-avx512] needs-llvm-components: x86 //@ assembly-output: emit-asm -//@ compile-flags: --crate-type=lib -O +//@ compile-flags: --crate-type=lib -O -C panic=abort #![feature(no_core, lang_items, repr_simd, intrinsics)] #![no_core] diff --git a/tests/assembly/simd-intrinsic-scatter.rs b/tests/assembly/simd-intrinsic-scatter.rs index 6ffefb0801ae..55095e4cb681 100644 --- a/tests/assembly/simd-intrinsic-scatter.rs +++ b/tests/assembly/simd-intrinsic-scatter.rs @@ -4,7 +4,7 @@ //@ [x86-avx512] needs-llvm-components: x86 //@ [x86-avx512] min-llvm-version: 18.0 //@ assembly-output: emit-asm -//@ compile-flags: --crate-type=lib -O +//@ compile-flags: --crate-type=lib -O -C panic=abort #![feature(no_core, lang_items, repr_simd, intrinsics)] #![no_core] diff --git a/tests/assembly/simd-intrinsic-select.rs b/tests/assembly/simd-intrinsic-select.rs index 3f36402e3d0d..4dfc2f9ed1fb 100644 --- a/tests/assembly/simd-intrinsic-select.rs +++ b/tests/assembly/simd-intrinsic-select.rs @@ -9,7 +9,7 @@ //@ [aarch64] needs-llvm-components: aarch64 //@ [aarch64] min-llvm-version: 18.0 //@ assembly-output: emit-asm -//@ compile-flags: --crate-type=lib -O +//@ compile-flags: --crate-type=lib -O -C panic=abort #![feature(no_core, lang_items, repr_simd, intrinsics)] #![no_core] diff --git a/tests/codegen/align-byval-alignment-mismatch.rs b/tests/codegen/align-byval-alignment-mismatch.rs index 71f2dd42ec22..835cc7393e51 100644 --- a/tests/codegen/align-byval-alignment-mismatch.rs +++ b/tests/codegen/align-byval-alignment-mismatch.rs @@ -1,9 +1,9 @@ // ignore-tidy-linelength //@ revisions:i686-linux x86_64-linux -//@[i686-linux] compile-flags: --target i686-unknown-linux-gnu +//@[i686-linux] compile-flags: --target i686-unknown-linux-gnu -C panic=abort //@[i686-linux] needs-llvm-components: x86 -//@[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu +//@[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu -C panic=abort //@[x86_64-linux] needs-llvm-components: x86 // Tests that we correctly copy arguments into allocas when the alignment of the byval argument diff --git a/tests/codegen/avr/avr-func-addrspace.rs b/tests/codegen/avr/avr-func-addrspace.rs index 2d9efb52c7cf..708347075640 100644 --- a/tests/codegen/avr/avr-func-addrspace.rs +++ b/tests/codegen/avr/avr-func-addrspace.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -O --target=avr-unknown-gnu-atmega328 --crate-type=rlib +//@ compile-flags: -O --target=avr-unknown-gnu-atmega328 --crate-type=rlib -C panic=abort //@ needs-llvm-components: avr // This test validates that function pointers can be stored in global variables diff --git a/tests/codegen/catch-unwind.rs b/tests/codegen/catch-unwind.rs index 4e1f9a88e957..48ad486fa03a 100644 --- a/tests/codegen/catch-unwind.rs +++ b/tests/codegen/catch-unwind.rs @@ -14,7 +14,6 @@ //@ ignore-loongarch64 FIXME #![crate_type = "lib"] -#![feature(c_unwind)] extern "C" { fn bar(); diff --git a/tests/codegen/cffi/c-variadic.rs b/tests/codegen/cffi/c-variadic.rs index 914d1623ed23..140d2f37f469 100644 --- a/tests/codegen/cffi/c-variadic.rs +++ b/tests/codegen/cffi/c-variadic.rs @@ -4,7 +4,6 @@ #![crate_type = "lib"] #![feature(c_variadic)] -#![feature(c_unwind)] #![no_std] use core::ffi::VaList; diff --git a/tests/codegen/debuginfo-inline-callsite-location.rs b/tests/codegen/debuginfo-inline-callsite-location.rs index f690c9c0937f..aee07b4eb8c6 100644 --- a/tests/codegen/debuginfo-inline-callsite-location.rs +++ b/tests/codegen/debuginfo-inline-callsite-location.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -g -O +//@ compile-flags: -g -O -C panic=abort // Check that each inline call site for the same function uses the same "sub-program" so that LLVM // can correctly merge the debug info if it merges the inlined code (e.g., for merging of tail diff --git a/tests/codegen/unwind-abis/aapcs-unwind-abi.rs b/tests/codegen/unwind-abis/aapcs-unwind-abi.rs index 48c7ecf78242..0d9c7757883f 100644 --- a/tests/codegen/unwind-abis/aapcs-unwind-abi.rs +++ b/tests/codegen/unwind-abis/aapcs-unwind-abi.rs @@ -1,7 +1,7 @@ //@ needs-llvm-components: arm //@ compile-flags: --target=armv7-unknown-linux-gnueabihf --crate-type=rlib -Cno-prepopulate-passes #![no_core] -#![feature(no_core, lang_items, c_unwind)] +#![feature(no_core, lang_items)] #[lang = "sized"] trait Sized {} diff --git a/tests/codegen/unwind-abis/c-unwind-abi-panic-abort.rs b/tests/codegen/unwind-abis/c-unwind-abi-panic-abort.rs index 8bb2122946fc..8d2745ba2f7a 100644 --- a/tests/codegen/unwind-abis/c-unwind-abi-panic-abort.rs +++ b/tests/codegen/unwind-abis/c-unwind-abi-panic-abort.rs @@ -4,7 +4,6 @@ // when the code is compiled with `panic=abort`. #![crate_type = "lib"] -#![feature(c_unwind)] // CHECK: @rust_item_that_can_unwind() unnamed_addr [[ATTR0:#[0-9]+]] #[no_mangle] diff --git a/tests/codegen/unwind-abis/c-unwind-abi.rs b/tests/codegen/unwind-abis/c-unwind-abi.rs index 99763943a6ac..46c08b5fc4ff 100644 --- a/tests/codegen/unwind-abis/c-unwind-abi.rs +++ b/tests/codegen/unwind-abis/c-unwind-abi.rs @@ -6,7 +6,6 @@ // to prevent LLVM from inferring the attribute. #![crate_type = "lib"] -#![feature(c_unwind)] // CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] diff --git a/tests/codegen/unwind-abis/cdecl-unwind-abi.rs b/tests/codegen/unwind-abis/cdecl-unwind-abi.rs index 78dbb55b35a2..8e643d6ce494 100644 --- a/tests/codegen/unwind-abis/cdecl-unwind-abi.rs +++ b/tests/codegen/unwind-abis/cdecl-unwind-abi.rs @@ -6,7 +6,6 @@ // disable optimizations above to prevent LLVM from inferring the attribute. #![crate_type = "lib"] -#![feature(c_unwind)] // CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] diff --git a/tests/codegen/unwind-abis/fastcall-unwind-abi.rs b/tests/codegen/unwind-abis/fastcall-unwind-abi.rs index 3cbeaf51d960..4c7b2856e2e4 100644 --- a/tests/codegen/unwind-abis/fastcall-unwind-abi.rs +++ b/tests/codegen/unwind-abis/fastcall-unwind-abi.rs @@ -1,7 +1,7 @@ //@ needs-llvm-components: x86 //@ compile-flags: --target=i686-pc-windows-msvc --crate-type=rlib -Cno-prepopulate-passes #![no_core] -#![feature(no_core, lang_items, c_unwind)] +#![feature(no_core, lang_items)] #[lang = "sized"] trait Sized {} diff --git a/tests/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs b/tests/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs deleted file mode 100644 index a7f7f2fb77f7..000000000000 --- a/tests/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ compile-flags: -C opt-level=0 -//@ needs-unwind - -#![crate_type = "lib"] - -// We disable optimizations to prevent LLVM from inferring the attribute. - -extern "C" { - fn bar(); -} - -// CHECK-NOT: Function Attrs:{{.*}}nounwind -pub unsafe extern "C" fn foo() { - bar(); -} - -// Note that this test will get removed when `C-unwind` is fully stabilized diff --git a/tests/codegen/unwind-abis/nounwind.rs b/tests/codegen/unwind-abis/nounwind.rs index 80bf8d670913..e40ed48ca737 100644 --- a/tests/codegen/unwind-abis/nounwind.rs +++ b/tests/codegen/unwind-abis/nounwind.rs @@ -2,7 +2,6 @@ //@ needs-unwind #![crate_type = "lib"] -#![feature(c_unwind)] // We disable optimizations to prevent LLVM from inferring the attribute. diff --git a/tests/codegen/unwind-abis/stdcall-unwind-abi.rs b/tests/codegen/unwind-abis/stdcall-unwind-abi.rs index ffb235cd1168..ffc11d1faef8 100644 --- a/tests/codegen/unwind-abis/stdcall-unwind-abi.rs +++ b/tests/codegen/unwind-abis/stdcall-unwind-abi.rs @@ -1,7 +1,7 @@ //@ needs-llvm-components: x86 //@ compile-flags: --target=i686-pc-windows-msvc --crate-type=rlib -Cno-prepopulate-passes #![no_core] -#![feature(no_core, lang_items, c_unwind)] +#![feature(no_core, lang_items)] #[lang = "sized"] trait Sized {} diff --git a/tests/codegen/unwind-abis/system-unwind-abi.rs b/tests/codegen/unwind-abis/system-unwind-abi.rs index 1dd0e9bbbec7..5f9102483464 100644 --- a/tests/codegen/unwind-abis/system-unwind-abi.rs +++ b/tests/codegen/unwind-abis/system-unwind-abi.rs @@ -6,7 +6,6 @@ // optimizations above to prevent LLVM from inferring the attribute. #![crate_type = "lib"] -#![feature(c_unwind)] // CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] diff --git a/tests/codegen/unwind-abis/sysv64-unwind-abi.rs b/tests/codegen/unwind-abis/sysv64-unwind-abi.rs index 9824009dc559..c869ca7e2b82 100644 --- a/tests/codegen/unwind-abis/sysv64-unwind-abi.rs +++ b/tests/codegen/unwind-abis/sysv64-unwind-abi.rs @@ -1,7 +1,7 @@ //@ needs-llvm-components: x86 //@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib -Cno-prepopulate-passes #![no_core] -#![feature(no_core, lang_items, c_unwind)] +#![feature(no_core, lang_items)] #[lang = "sized"] trait Sized {} diff --git a/tests/codegen/unwind-abis/thiscall-unwind-abi.rs b/tests/codegen/unwind-abis/thiscall-unwind-abi.rs index a96f4d5a350c..19b23ee47ba8 100644 --- a/tests/codegen/unwind-abis/thiscall-unwind-abi.rs +++ b/tests/codegen/unwind-abis/thiscall-unwind-abi.rs @@ -1,7 +1,7 @@ //@ needs-llvm-components: x86 //@ compile-flags: --target=i686-pc-windows-msvc --crate-type=rlib -Cno-prepopulate-passes #![no_core] -#![feature(no_core, lang_items, c_unwind)] +#![feature(no_core, lang_items)] #[lang = "sized"] trait Sized {} diff --git a/tests/codegen/unwind-abis/vectorcall-unwind-abi.rs b/tests/codegen/unwind-abis/vectorcall-unwind-abi.rs index 9929e3e3703d..b420f67ca9b7 100644 --- a/tests/codegen/unwind-abis/vectorcall-unwind-abi.rs +++ b/tests/codegen/unwind-abis/vectorcall-unwind-abi.rs @@ -1,7 +1,7 @@ //@ needs-llvm-components: x86 //@ compile-flags: --target=i686-pc-windows-msvc --crate-type=rlib -Cno-prepopulate-passes #![no_core] -#![feature(no_core, lang_items, c_unwind, abi_vectorcall)] +#![feature(no_core, lang_items, abi_vectorcall)] #[lang = "sized"] trait Sized {} diff --git a/tests/codegen/unwind-abis/win64-unwind-abi.rs b/tests/codegen/unwind-abis/win64-unwind-abi.rs index a45a94f628d1..2697d3cbcd68 100644 --- a/tests/codegen/unwind-abis/win64-unwind-abi.rs +++ b/tests/codegen/unwind-abis/win64-unwind-abi.rs @@ -1,7 +1,7 @@ //@ needs-llvm-components: x86 //@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib -Cno-prepopulate-passes #![no_core] -#![feature(no_core, lang_items, c_unwind)] +#![feature(no_core, lang_items)] #[lang = "sized"] trait Sized {} diff --git a/tests/codegen/unwind-and-panic-abort.rs b/tests/codegen/unwind-and-panic-abort.rs index 430502308cf0..8efa140058ad 100644 --- a/tests/codegen/unwind-and-panic-abort.rs +++ b/tests/codegen/unwind-and-panic-abort.rs @@ -1,7 +1,6 @@ //@ compile-flags: -C panic=abort #![crate_type = "lib"] -#![feature(c_unwind)] extern "C-unwind" { fn bar(); diff --git a/tests/codegen/unwind-extern-exports.rs b/tests/codegen/unwind-extern-exports.rs index ea59748b3bcd..e692fd1a5472 100644 --- a/tests/codegen/unwind-extern-exports.rs +++ b/tests/codegen/unwind-extern-exports.rs @@ -2,7 +2,6 @@ //@ needs-unwind #![crate_type = "lib"] -#![feature(c_unwind)] // Make sure these all do *not* get the attribute. // We disable optimizations to prevent LLVM from inferring the attribute. diff --git a/tests/codegen/unwind-extern-imports.rs b/tests/codegen/unwind-extern-imports.rs index 790e4def8b36..dfae8aae64a5 100644 --- a/tests/codegen/unwind-extern-imports.rs +++ b/tests/codegen/unwind-extern-imports.rs @@ -2,7 +2,6 @@ //@ needs-unwind #![crate_type = "lib"] -#![feature(c_unwind)] extern "C" { // CHECK: Function Attrs:{{.*}}nounwind diff --git a/tests/coverage/abort.cov-map b/tests/coverage/abort.cov-map index 5673fa98ca60..15156ec9811b 100644 --- a/tests/coverage/abort.cov-map +++ b/tests/coverage/abort.cov-map @@ -1,5 +1,5 @@ Function name: abort::main -Raw bytes (89): 0x[01, 01, 0a, 01, 27, 05, 09, 03, 0d, 22, 11, 03, 0d, 03, 0d, 22, 15, 03, 0d, 03, 0d, 05, 09, 0d, 01, 0e, 01, 01, 1b, 03, 02, 0b, 00, 18, 22, 01, 0c, 00, 19, 11, 00, 1a, 02, 0a, 0e, 02, 0a, 00, 0b, 22, 02, 0c, 00, 19, 15, 00, 1a, 00, 31, 1a, 00, 31, 00, 32, 22, 04, 0c, 00, 19, 05, 00, 1a, 00, 31, 09, 00, 31, 00, 32, 27, 01, 09, 00, 17, 0d, 02, 05, 01, 02] +Raw bytes (89): 0x[01, 01, 0a, 01, 27, 05, 09, 03, 0d, 22, 11, 03, 0d, 03, 0d, 22, 15, 03, 0d, 03, 0d, 05, 09, 0d, 01, 0d, 01, 01, 1b, 03, 02, 0b, 00, 18, 22, 01, 0c, 00, 19, 11, 00, 1a, 02, 0a, 0e, 02, 0a, 00, 0b, 22, 02, 0c, 00, 19, 15, 00, 1a, 00, 31, 1a, 00, 31, 00, 32, 22, 04, 0c, 00, 19, 05, 00, 1a, 00, 31, 09, 00, 31, 00, 32, 27, 01, 09, 00, 17, 0d, 02, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 10 @@ -14,7 +14,7 @@ Number of expressions: 10 - expression 8 operands: lhs = Expression(0, Add), rhs = Counter(3) - expression 9 operands: lhs = Counter(1), rhs = Counter(2) Number of file 0 mappings: 13 -- Code(Counter(0)) at (prev + 14, 1) to (start + 1, 27) +- Code(Counter(0)) at (prev + 13, 1) to (start + 1, 27) - Code(Expression(0, Add)) at (prev + 2, 11) to (start + 0, 24) = (c0 + (c1 + c2)) - Code(Expression(8, Sub)) at (prev + 1, 12) to (start + 0, 25) @@ -36,13 +36,13 @@ Number of file 0 mappings: 13 - Code(Counter(3)) at (prev + 2, 5) to (start + 1, 2) Function name: abort::might_abort -Raw bytes (21): 0x[01, 01, 01, 01, 05, 03, 01, 04, 01, 01, 14, 05, 02, 09, 01, 24, 02, 02, 0c, 03, 02] +Raw bytes (21): 0x[01, 01, 01, 01, 05, 03, 01, 03, 01, 01, 14, 05, 02, 09, 01, 24, 02, 02, 0c, 03, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) Number of file 0 mappings: 3 -- Code(Counter(0)) at (prev + 4, 1) to (start + 1, 20) +- Code(Counter(0)) at (prev + 3, 1) to (start + 1, 20) - Code(Counter(1)) at (prev + 2, 9) to (start + 1, 36) - Code(Expression(0, Sub)) at (prev + 2, 12) to (start + 3, 2) = (c0 - c1) diff --git a/tests/coverage/abort.coverage b/tests/coverage/abort.coverage index b658a6562591..29e3ad5eda7a 100644 --- a/tests/coverage/abort.coverage +++ b/tests/coverage/abort.coverage @@ -1,4 +1,3 @@ - LL| |#![feature(c_unwind)] LL| |#![allow(unused_assignments)] LL| | LL| 12|extern "C" fn might_abort(should_abort: bool) { diff --git a/tests/coverage/abort.rs b/tests/coverage/abort.rs index 649e8e52a490..6c06a3f9d875 100644 --- a/tests/coverage/abort.rs +++ b/tests/coverage/abort.rs @@ -1,4 +1,3 @@ -#![feature(c_unwind)] #![allow(unused_assignments)] extern "C" fn might_abort(should_abort: bool) { diff --git a/tests/run-make/forced-unwind-terminate-pof/foo.rs b/tests/run-make/forced-unwind-terminate-pof/foo.rs index 0a51287313f6..3a8478462170 100644 --- a/tests/run-make/forced-unwind-terminate-pof/foo.rs +++ b/tests/run-make/forced-unwind-terminate-pof/foo.rs @@ -1,6 +1,5 @@ // Tests that forced unwind through POF Rust frames wouldn't trigger our terminating guards. -#![feature(c_unwind)] #![no_main] extern "C-unwind" { diff --git a/tests/ui/asm/x86_64/may_unwind.rs b/tests/ui/asm/x86_64/may_unwind.rs index 3b2c1edcd478..d3a2916df9db 100644 --- a/tests/ui/asm/x86_64/may_unwind.rs +++ b/tests/ui/asm/x86_64/may_unwind.rs @@ -16,7 +16,7 @@ impl Drop for Foo<'_> { } } -extern "C" fn panicky() { +extern "C-unwind" fn panicky() { resume_unwind(Box::new(())); } diff --git a/tests/ui/consts/const-eval/unwind-abort.rs b/tests/ui/consts/const-eval/unwind-abort.rs index 57959e7db6ad..26113e238883 100644 --- a/tests/ui/consts/const-eval/unwind-abort.rs +++ b/tests/ui/consts/const-eval/unwind-abort.rs @@ -1,4 +1,4 @@ -#![feature(c_unwind, const_extern_fn)] +#![feature(const_extern_fn)] const extern "C" fn foo() { panic!() //~ ERROR evaluation of constant value failed diff --git a/tests/ui/consts/unwind-abort.rs b/tests/ui/consts/unwind-abort.rs index 35db9152bd55..1dd33f327fb6 100644 --- a/tests/ui/consts/unwind-abort.rs +++ b/tests/ui/consts/unwind-abort.rs @@ -1,6 +1,6 @@ //@ check-pass -#![feature(c_unwind, const_extern_fn)] +#![feature(const_extern_fn)] // We don't unwind in const-eval anyways. const extern "C" fn foo() { diff --git a/tests/ui/panics/abort-on-panic.rs b/tests/ui/panics/abort-on-panic.rs index 5736ecf86856..feccefb25376 100644 --- a/tests/ui/panics/abort-on-panic.rs +++ b/tests/ui/panics/abort-on-panic.rs @@ -4,7 +4,6 @@ //@[next] compile-flags: -Znext-solver #![allow(unused_must_use)] -#![feature(c_unwind)] #![feature(panic_always_abort)] // Since we mark some ABIs as "nounwind" to LLVM, we must make sure that // we never unwind through them. diff --git a/tests/ui/panics/panic-in-ffi.rs b/tests/ui/panics/panic-in-ffi.rs index 6f54acb3e045..88f45f9a871d 100644 --- a/tests/ui/panics/panic-in-ffi.rs +++ b/tests/ui/panics/panic-in-ffi.rs @@ -7,7 +7,6 @@ //@ normalize-stderr-test: "(core/src/panicking\.rs):[0-9]+:[0-9]+" -> "$1:$$LINE:$$COL" //@ needs-unwind //@ ignore-emscripten "RuntimeError" junk in output -#![feature(c_unwind)] extern "C" fn panic_in_ffi() { panic!("Test"); diff --git a/tests/ui/panics/panic-in-ffi.run.stderr b/tests/ui/panics/panic-in-ffi.run.stderr index a92a66c57fd7..596355399c80 100644 --- a/tests/ui/panics/panic-in-ffi.run.stderr +++ b/tests/ui/panics/panic-in-ffi.run.stderr @@ -1,4 +1,4 @@ -thread 'main' panicked at $DIR/panic-in-ffi.rs:13:5: +thread 'main' panicked at $DIR/panic-in-ffi.rs:12:5: Test note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'main' panicked at library/core/src/panicking.rs:$LINE:$COL: diff --git a/tests/ui/unwind-abis/feature-gate-c_unwind.rs b/tests/ui/unwind-abis/feature-gate-c_unwind.rs deleted file mode 100644 index 28ef7d57788e..000000000000 --- a/tests/ui/unwind-abis/feature-gate-c_unwind.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ ignore-test - -// After partial stabilisation, `c_unwind` only contains codegen behaviour changes -// and are tested in `src/test/codegen/unwind-abis` From da8ac73d910a446e796f511c0dda97e49d14f044 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 6 Dec 2023 20:59:23 +0000 Subject: [PATCH 093/147] Add a hack to prevent proc_macro misopt in CI --- library/proc_macro/src/bridge/buffer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/proc_macro/src/bridge/buffer.rs b/library/proc_macro/src/bridge/buffer.rs index 48030f8d82dc..149767bf7052 100644 --- a/library/proc_macro/src/bridge/buffer.rs +++ b/library/proc_macro/src/bridge/buffer.rs @@ -119,7 +119,9 @@ impl Write for Buffer { } impl Drop for Buffer { - #[inline] + // HACK(nbdd0121): Hack to prevent LLVM < 17.0.4 from misoptimising, + // change to `#[inline]` if fixed. + #[inline(never)] fn drop(&mut self) { let b = self.take(); (b.drop)(b); From 9d2b932d71b2fbeb31a83f9aa17b21f4d8f4b7a6 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sat, 8 Jun 2024 13:13:30 +0100 Subject: [PATCH 094/147] Fix broken aarch64 unwind test --- tests/ui/asm/aarch64/may_unwind.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/asm/aarch64/may_unwind.rs b/tests/ui/asm/aarch64/may_unwind.rs index a483008c3649..01d52f9022da 100644 --- a/tests/ui/asm/aarch64/may_unwind.rs +++ b/tests/ui/asm/aarch64/may_unwind.rs @@ -16,7 +16,7 @@ impl Drop for Foo<'_> { } } -extern "C" fn panicky() { +extern "C-unwind" fn panicky() { resume_unwind(Box::new(())); } From 4c4d62d74fdb7da78e8b2bab5b23c3ba6d4ec505 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sat, 8 Jun 2024 22:49:14 +0100 Subject: [PATCH 095/147] Fix non-x86 tests --- tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs b/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs index 977749ad578a..ed0af90aaafd 100644 --- a/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs +++ b/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --target riscv64gc-unknown-linux-gnu -O -C no-prepopulate-passes +//@ compile-flags: --target riscv64gc-unknown-linux-gnu -O -C no-prepopulate-passes -C panic=abort //@ needs-llvm-components: riscv #![crate_type = "lib"] From e4c9a8cf9b2e2a17c589dadcaffae0b9e6b62cbc Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 19 Jun 2024 08:25:58 +0000 Subject: [PATCH 096/147] Const generic parameters aren't bounds, even if we end up erroring because of the bound that binds the parameter's type --- .../src/traits/error_reporting/suggestions.rs | 188 ++++++++++-------- .../defaults/doesnt_infer.stderr | 8 +- .../defaults/rp_impl_trait_fail.stderr | 4 +- .../generic_arg_infer/issue-91614.stderr | 4 +- .../issue-62504.full.stderr | 8 +- .../issue-62504.min.stderr | 8 +- .../object-safety-ok-infer-err.stderr | 4 +- .../infer/cannot-infer-const-args.stderr | 4 +- .../const-generics/infer/issue-77092.stderr | 4 +- .../const-generics/infer/method-chain.stderr | 4 +- .../infer/one-param-uninferred.stderr | 4 +- .../infer/uninferred-consts.stderr | 8 +- ...ent_generics_of_encoding_impl_trait.stderr | 4 +- tests/ui/const-generics/type_mismatch.stderr | 4 +- .../unify_with_nested_expr.stderr | 4 +- tests/ui/inference/issue-83606.stderr | 4 +- tests/ui/issues/issue-98299.stderr | 4 +- ...oj-ty-as-type-of-const-issue-125757.stderr | 4 +- .../ui/transmutability/issue-101739-1.stderr | 2 +- 19 files changed, 146 insertions(+), 128 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index f632f1ad4f26..8f8ef4b7acbd 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2776,97 +2776,115 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let mut this = "this bound"; let mut note = None; let mut help = None; - if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() - && let ty::ClauseKind::Trait(trait_pred) = clause - { - let def_id = trait_pred.def_id(); - let visible_item = if let Some(local) = def_id.as_local() { - // Check for local traits being reachable. - let vis = &tcx.resolutions(()).effective_visibilities; - // Account for non-`pub` traits in the root of the local crate. - let is_locally_reachable = tcx.parent(def_id).is_crate_root(); - vis.is_reachable(local) || is_locally_reachable - } else { - // Check for foreign traits being reachable. - tcx.visible_parent_map(()).get(&def_id).is_some() - }; - if tcx.is_lang_item(def_id, LangItem::Sized) { - // Check if this is an implicit bound, even in foreign crates. - if tcx - .generics_of(item_def_id) - .own_params - .iter() - .any(|param| tcx.def_span(param.def_id) == span) - { - a = "an implicit `Sized`"; - this = "the implicit `Sized` requirement on this type parameter"; - } - if let Some(hir::Node::TraitItem(hir::TraitItem { - generics, - kind: hir::TraitItemKind::Type(bounds, None), - .. - })) = tcx.hir().get_if_local(item_def_id) - // Do not suggest relaxing if there is an explicit `Sized` obligation. - && !bounds.iter() - .filter_map(|bound| bound.trait_ref()) - .any(|tr| tr.trait_def_id() == tcx.lang_items().sized_trait()) - { - let (span, separator) = if let [.., last] = bounds { - (last.span().shrink_to_hi(), " +") + if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() { + match clause { + ty::ClauseKind::Trait(trait_pred) => { + let def_id = trait_pred.def_id(); + let visible_item = if let Some(local) = def_id.as_local() { + // Check for local traits being reachable. + let vis = &tcx.resolutions(()).effective_visibilities; + // Account for non-`pub` traits in the root of the local crate. + let is_locally_reachable = tcx.parent(def_id).is_crate_root(); + vis.is_reachable(local) || is_locally_reachable } else { - (generics.span.shrink_to_hi(), ":") + // Check for foreign traits being reachable. + tcx.visible_parent_map(()).get(&def_id).is_some() }; - err.span_suggestion_verbose( - span, - "consider relaxing the implicit `Sized` restriction", - format!("{separator} ?Sized"), - Applicability::MachineApplicable, - ); + if tcx.is_lang_item(def_id, LangItem::Sized) { + // Check if this is an implicit bound, even in foreign crates. + if tcx + .generics_of(item_def_id) + .own_params + .iter() + .any(|param| tcx.def_span(param.def_id) == span) + { + a = "an implicit `Sized`"; + this = + "the implicit `Sized` requirement on this type parameter"; + } + if let Some(hir::Node::TraitItem(hir::TraitItem { + generics, + kind: hir::TraitItemKind::Type(bounds, None), + .. + })) = tcx.hir().get_if_local(item_def_id) + // Do not suggest relaxing if there is an explicit `Sized` obligation. + && !bounds.iter() + .filter_map(|bound| bound.trait_ref()) + .any(|tr| tr.trait_def_id() == tcx.lang_items().sized_trait()) + { + let (span, separator) = if let [.., last] = bounds { + (last.span().shrink_to_hi(), " +") + } else { + (generics.span.shrink_to_hi(), ":") + }; + err.span_suggestion_verbose( + span, + "consider relaxing the implicit `Sized` restriction", + format!("{separator} ?Sized"), + Applicability::MachineApplicable, + ); + } + } + if let DefKind::Trait = tcx.def_kind(item_def_id) + && !visible_item + { + note = Some(format!( + "`{short_item_name}` is a \"sealed trait\", because to implement it \ + you also need to implement `{}`, which is not accessible; this is \ + usually done to force you to use one of the provided types that \ + already implement it", + with_no_trimmed_paths!(tcx.def_path_str(def_id)), + )); + let impls_of = tcx.trait_impls_of(def_id); + let impls = impls_of + .non_blanket_impls() + .values() + .flatten() + .chain(impls_of.blanket_impls().iter()) + .collect::>(); + if !impls.is_empty() { + let len = impls.len(); + let mut types = impls + .iter() + .map(|t| { + with_no_trimmed_paths!(format!( + " {}", + tcx.type_of(*t).instantiate_identity(), + )) + }) + .collect::>(); + let post = if types.len() > 9 { + types.truncate(8); + format!("\nand {} others", len - 8) + } else { + String::new() + }; + help = Some(format!( + "the following type{} implement{} the trait:\n{}{post}", + pluralize!(len), + if len == 1 { "s" } else { "" }, + types.join("\n"), + )); + } + } } - } - if let DefKind::Trait = tcx.def_kind(item_def_id) - && !visible_item - { - note = Some(format!( - "`{short_item_name}` is a \"sealed trait\", because to implement it \ - you also need to implement `{}`, which is not accessible; this is \ - usually done to force you to use one of the provided types that \ - already implement it", - with_no_trimmed_paths!(tcx.def_path_str(def_id)), - )); - let impls_of = tcx.trait_impls_of(def_id); - let impls = impls_of - .non_blanket_impls() - .values() - .flatten() - .chain(impls_of.blanket_impls().iter()) - .collect::>(); - if !impls.is_empty() { - let len = impls.len(); - let mut types = impls - .iter() - .map(|t| { - with_no_trimmed_paths!(format!( - " {}", - tcx.type_of(*t).instantiate_identity(), - )) - }) - .collect::>(); - let post = if types.len() > 9 { - types.truncate(8); - format!("\nand {} others", len - 8) + ty::ClauseKind::ConstArgHasType(..) => { + let descr = + format!("required by a const generic parameter in `{item_name}`"); + if span.is_visible(sm) { + let msg = format!( + "required by this const generic parameter in `{short_item_name}`" + ); + multispan.push_span_label(span, msg); + err.span_note(multispan, descr); } else { - String::new() - }; - help = Some(format!( - "the following type{} implement{} the trait:\n{}{post}", - pluralize!(len), - if len == 1 { "s" } else { "" }, - types.join("\n"), - )); + err.span_note(tcx.def_span(item_def_id), descr); + } + return; } + _ => (), } - }; + } let descr = format!("required by {a} bound in `{item_name}`"); if span.is_visible(sm) { let msg = format!("required by {this} in `{short_item_name}`"); diff --git a/tests/ui/const-generics/defaults/doesnt_infer.stderr b/tests/ui/const-generics/defaults/doesnt_infer.stderr index e099289827e9..c17f57f36bc5 100644 --- a/tests/ui/const-generics/defaults/doesnt_infer.stderr +++ b/tests/ui/const-generics/defaults/doesnt_infer.stderr @@ -4,11 +4,11 @@ error[E0284]: type annotations needed for `Foo<_>` LL | let foo = Foo::foo(); | ^^^ ---------- type must be known at this point | -note: required by a bound in `Foo::::foo` +note: required by a const generic parameter in `Foo::::foo` --> $DIR/doesnt_infer.rs:5:6 | LL | impl Foo { - | ^^^^^^^^^^^^ required by this bound in `Foo::::foo` + | ^^^^^^^^^^^^ required by this const generic parameter in `Foo::::foo` LL | fn foo() -> Self { | --- required by a bound in this associated function help: consider giving `foo` an explicit type, where the value of const parameter `N` is specified @@ -22,11 +22,11 @@ error[E0284]: type annotations needed for `Foo<_>` LL | let foo = Foo::foo(); | ^^^ --- type must be known at this point | -note: required by a bound in `Foo` +note: required by a const generic parameter in `Foo` --> $DIR/doesnt_infer.rs:3:12 | LL | struct Foo; - | ^^^^^^^^^^^^^^^^ required by this bound in `Foo` + | ^^^^^^^^^^^^^^^^ required by this const generic parameter in `Foo` help: consider giving `foo` an explicit type, where the value of const parameter `N` is specified | LL | let foo: Foo = Foo::foo(); diff --git a/tests/ui/const-generics/defaults/rp_impl_trait_fail.stderr b/tests/ui/const-generics/defaults/rp_impl_trait_fail.stderr index d91e717b3ebe..45be3126e3b3 100644 --- a/tests/ui/const-generics/defaults/rp_impl_trait_fail.stderr +++ b/tests/ui/const-generics/defaults/rp_impl_trait_fail.stderr @@ -37,11 +37,11 @@ error[E0284]: type annotations needed LL | uwu(); | ^^^ cannot infer the value of the const parameter `N` declared on the function `uwu` | -note: required by a bound in `uwu` +note: required by a const generic parameter in `uwu` --> $DIR/rp_impl_trait_fail.rs:16:8 | LL | fn uwu() -> impl Traitor { - | ^^^^^^^^^^^ required by this bound in `uwu` + | ^^^^^^^^^^^ required by this const generic parameter in `uwu` help: consider specifying the generic argument | LL | uwu::(); diff --git a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr index b6982e05b889..217f609459ee 100644 --- a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr +++ b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr @@ -4,7 +4,7 @@ error[E0284]: type annotations needed for `Mask<_, _>` LL | let y = Mask::<_, _>::splat(false); | ^ -------------------------- type must be known at this point | -note: required by a bound in `Mask::::splat` +note: required by a const generic parameter in `Mask::::splat` --> $SRC_DIR/core/src/../../portable-simd/crates/core_simd/src/masks.rs:LL:COL help: consider giving `y` an explicit type, where the value of const parameter `N` is specified | @@ -17,7 +17,7 @@ error[E0284]: type annotations needed for `Mask<_, _>` LL | let y = Mask::<_, _>::splat(false); | ^ ------------ type must be known at this point | -note: required by a bound in `Mask` +note: required by a const generic parameter in `Mask` --> $SRC_DIR/core/src/../../portable-simd/crates/core_simd/src/masks.rs:LL:COL help: consider giving `y` an explicit type, where the value of const parameter `N` is specified | diff --git a/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr b/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr index 27411646cb72..3739637c2795 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr @@ -24,11 +24,11 @@ error[E0284]: type annotations needed for `ArrayHolder<_>` LL | let mut array = ArrayHolder::new(); | ^^^^^^^^^ ------------------ type must be known at this point | -note: required by a bound in `ArrayHolder::::new` +note: required by a const generic parameter in `ArrayHolder::::new` --> $DIR/issue-62504.rs:16:6 | LL | impl ArrayHolder { - | ^^^^^^^^^^^^^^ required by this bound in `ArrayHolder::::new` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `ArrayHolder::::new` LL | pub const fn new() -> Self { | --- required by a bound in this associated function help: consider giving `array` an explicit type, where the value of const parameter `X` is specified @@ -42,11 +42,11 @@ error[E0284]: type annotations needed for `ArrayHolder<_>` LL | let mut array = ArrayHolder::new(); | ^^^^^^^^^ ----------- type must be known at this point | -note: required by a bound in `ArrayHolder` +note: required by a const generic parameter in `ArrayHolder` --> $DIR/issue-62504.rs:14:20 | LL | struct ArrayHolder([u32; X]); - | ^^^^^^^^^^^^^^ required by this bound in `ArrayHolder` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `ArrayHolder` help: consider giving `array` an explicit type, where the value of const parameter `X` is specified | LL | let mut array: ArrayHolder = ArrayHolder::new(); diff --git a/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr b/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr index 036b9001c03d..14c67e2528a7 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr @@ -28,11 +28,11 @@ error[E0284]: type annotations needed for `ArrayHolder<_>` LL | let mut array = ArrayHolder::new(); | ^^^^^^^^^ ------------------ type must be known at this point | -note: required by a bound in `ArrayHolder::::new` +note: required by a const generic parameter in `ArrayHolder::::new` --> $DIR/issue-62504.rs:16:6 | LL | impl ArrayHolder { - | ^^^^^^^^^^^^^^ required by this bound in `ArrayHolder::::new` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `ArrayHolder::::new` LL | pub const fn new() -> Self { | --- required by a bound in this associated function help: consider giving `array` an explicit type, where the value of const parameter `X` is specified @@ -46,11 +46,11 @@ error[E0284]: type annotations needed for `ArrayHolder<_>` LL | let mut array = ArrayHolder::new(); | ^^^^^^^^^ ----------- type must be known at this point | -note: required by a bound in `ArrayHolder` +note: required by a const generic parameter in `ArrayHolder` --> $DIR/issue-62504.rs:14:20 | LL | struct ArrayHolder([u32; X]); - | ^^^^^^^^^^^^^^ required by this bound in `ArrayHolder` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `ArrayHolder` help: consider giving `array` an explicit type, where the value of const parameter `X` is specified | LL | let mut array: ArrayHolder = ArrayHolder::new(); diff --git a/tests/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.stderr b/tests/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.stderr index 0c290448fc5a..d1e1c976da66 100644 --- a/tests/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.stderr +++ b/tests/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.stderr @@ -4,11 +4,11 @@ error[E0284]: type annotations needed LL | use_dyn(&()); | ^^^^^^^ cannot infer the value of the const parameter `N` declared on the function `use_dyn` | -note: required by a bound in `use_dyn` +note: required by a const generic parameter in `use_dyn` --> $DIR/object-safety-ok-infer-err.rs:14:12 | LL | fn use_dyn(v: &dyn Foo) where [u8; N + 1]: Sized { - | ^^^^^^^^^^^^^^ required by this bound in `use_dyn` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `use_dyn` help: consider specifying the generic argument | LL | use_dyn::(&()); diff --git a/tests/ui/const-generics/infer/cannot-infer-const-args.stderr b/tests/ui/const-generics/infer/cannot-infer-const-args.stderr index 2180ba2f8095..c349a50a83ff 100644 --- a/tests/ui/const-generics/infer/cannot-infer-const-args.stderr +++ b/tests/ui/const-generics/infer/cannot-infer-const-args.stderr @@ -4,11 +4,11 @@ error[E0284]: type annotations needed LL | foo(); | ^^^ cannot infer the value of the const parameter `X` declared on the function `foo` | -note: required by a bound in `foo` +note: required by a const generic parameter in `foo` --> $DIR/cannot-infer-const-args.rs:1:8 | LL | fn foo() -> usize { - | ^^^^^^^^^^^^^^ required by this bound in `foo` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `foo` help: consider specifying the generic argument | LL | foo::(); diff --git a/tests/ui/const-generics/infer/issue-77092.stderr b/tests/ui/const-generics/infer/issue-77092.stderr index 1579d217c2d4..9a6374a2adcb 100644 --- a/tests/ui/const-generics/infer/issue-77092.stderr +++ b/tests/ui/const-generics/infer/issue-77092.stderr @@ -4,11 +4,11 @@ error[E0284]: type annotations needed LL | println!("{:?}", take_array_from_mut(&mut arr, i)); | ^^^^^^^^^^^^^^^^^^^ cannot infer the value of the const parameter `N` declared on the function `take_array_from_mut` | -note: required by a bound in `take_array_from_mut` +note: required by a const generic parameter in `take_array_from_mut` --> $DIR/issue-77092.rs:3:27 | LL | fn take_array_from_mut(data: &mut [T], start: usize) -> &mut [T; N] { - | ^^^^^^^^^^^^^^ required by this bound in `take_array_from_mut` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `take_array_from_mut` help: consider specifying the generic arguments | LL | println!("{:?}", take_array_from_mut::(&mut arr, i)); diff --git a/tests/ui/const-generics/infer/method-chain.stderr b/tests/ui/const-generics/infer/method-chain.stderr index 08c664e82f7e..95044bb5203b 100644 --- a/tests/ui/const-generics/infer/method-chain.stderr +++ b/tests/ui/const-generics/infer/method-chain.stderr @@ -4,11 +4,11 @@ error[E0284]: type annotations needed LL | Foo.bar().bar().bar().bar().baz(); | ^^^ cannot infer the value of the const parameter `N` declared on the method `baz` | -note: required by a bound in `Foo::baz` +note: required by a const generic parameter in `Foo::baz` --> $DIR/method-chain.rs:8:12 | LL | fn baz(self) -> Foo { - | ^^^^^^^^^^^^^^ required by this bound in `Foo::baz` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `Foo::baz` help: consider specifying the generic argument | LL | Foo.bar().bar().bar().bar().baz::(); diff --git a/tests/ui/const-generics/infer/one-param-uninferred.stderr b/tests/ui/const-generics/infer/one-param-uninferred.stderr index 68bec93e8262..f3aa7973e67d 100644 --- a/tests/ui/const-generics/infer/one-param-uninferred.stderr +++ b/tests/ui/const-generics/infer/one-param-uninferred.stderr @@ -4,11 +4,11 @@ error[E0284]: type annotations needed LL | let _: [u8; 17] = foo(); | ^^^ cannot infer the value of the const parameter `M` declared on the function `foo` | -note: required by a bound in `foo` +note: required by a const generic parameter in `foo` --> $DIR/one-param-uninferred.rs:2:24 | LL | fn foo() -> [u8; N] { - | ^^^^^^^^^^^^^^ required by this bound in `foo` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `foo` help: consider specifying the generic arguments | LL | let _: [u8; 17] = foo::<17, M>(); diff --git a/tests/ui/const-generics/infer/uninferred-consts.stderr b/tests/ui/const-generics/infer/uninferred-consts.stderr index e8ad336af70c..839fb25c4e1e 100644 --- a/tests/ui/const-generics/infer/uninferred-consts.stderr +++ b/tests/ui/const-generics/infer/uninferred-consts.stderr @@ -4,11 +4,11 @@ error[E0284]: type annotations needed LL | Foo.foo(); | ^^^ cannot infer the value of the const parameter `A` declared on the method `foo` | -note: required by a bound in `Foo::foo` +note: required by a const generic parameter in `Foo::foo` --> $DIR/uninferred-consts.rs:6:12 | LL | fn foo(self) {} - | ^^^^^^^^^^^^^^ required by this bound in `Foo::foo` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `Foo::foo` help: consider specifying the generic arguments | LL | Foo.foo::(); @@ -20,11 +20,11 @@ error[E0284]: type annotations needed LL | Foo.foo(); | ^^^ cannot infer the value of the const parameter `B` declared on the method `foo` | -note: required by a bound in `Foo::foo` +note: required by a const generic parameter in `Foo::foo` --> $DIR/uninferred-consts.rs:6:28 | LL | fn foo(self) {} - | ^^^^^^^^^^^^^^ required by this bound in `Foo::foo` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `Foo::foo` help: consider specifying the generic arguments | LL | Foo.foo::(); diff --git a/tests/ui/const-generics/parent_generics_of_encoding_impl_trait.stderr b/tests/ui/const-generics/parent_generics_of_encoding_impl_trait.stderr index cb8f56c6bb08..4809f7d37dd3 100644 --- a/tests/ui/const-generics/parent_generics_of_encoding_impl_trait.stderr +++ b/tests/ui/const-generics/parent_generics_of_encoding_impl_trait.stderr @@ -4,11 +4,11 @@ error[E0284]: type annotations needed LL | generics_of_parent_impl_trait::foo([()]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer the value of const parameter `N` declared on the function `foo` | -note: required by a bound in `foo` +note: required by a const generic parameter in `foo` --> $DIR/auxiliary/generics_of_parent_impl_trait.rs:5:12 | LL | pub fn foo(foo: impl Into<[(); N + 1]>) { - | ^^^^^^^^^^^^^^ required by this bound in `foo` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `foo` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/type_mismatch.stderr b/tests/ui/const-generics/type_mismatch.stderr index 07476ae76c65..026999827c0e 100644 --- a/tests/ui/const-generics/type_mismatch.stderr +++ b/tests/ui/const-generics/type_mismatch.stderr @@ -4,11 +4,11 @@ error: the constant `N` is not of type `u8` LL | bar::() | ^ expected `u8`, found `usize` | -note: required by a bound in `bar` +note: required by a const generic parameter in `bar` --> $DIR/type_mismatch.rs:6:8 | LL | fn bar() -> [u8; N] {} - | ^^^^^^^^^^^ required by this bound in `bar` + | ^^^^^^^^^^^ required by this const generic parameter in `bar` error[E0308]: mismatched types --> $DIR/type_mismatch.rs:6:26 diff --git a/tests/ui/const-generics/unify_with_nested_expr.stderr b/tests/ui/const-generics/unify_with_nested_expr.stderr index 87610db67ae4..b1aecdb3cb5a 100644 --- a/tests/ui/const-generics/unify_with_nested_expr.stderr +++ b/tests/ui/const-generics/unify_with_nested_expr.stderr @@ -4,11 +4,11 @@ error[E0284]: type annotations needed LL | bar(); | ^^^ cannot infer the value of the const parameter `N` declared on the function `bar` | -note: required by a bound in `bar` +note: required by a const generic parameter in `bar` --> $DIR/unify_with_nested_expr.rs:12:8 | LL | fn bar() - | ^^^^^^^^^^^^^^ required by this bound in `bar` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `bar` help: consider specifying the generic argument | LL | bar::(); diff --git a/tests/ui/inference/issue-83606.stderr b/tests/ui/inference/issue-83606.stderr index 2d74024bb287..69d1d71ef3cc 100644 --- a/tests/ui/inference/issue-83606.stderr +++ b/tests/ui/inference/issue-83606.stderr @@ -4,11 +4,11 @@ error[E0284]: type annotations needed for `[usize; _]` LL | let _ = foo("foo"); | ^ ---------- type must be known at this point | -note: required by a bound in `foo` +note: required by a const generic parameter in `foo` --> $DIR/issue-83606.rs:3:8 | LL | fn foo(_: impl std::fmt::Display) -> [usize; N] { - | ^^^^^^^^^^^^^^ required by this bound in `foo` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `foo` help: consider giving this pattern a type, where the value of const parameter `N` is specified | LL | let _: [usize; N] = foo("foo"); diff --git a/tests/ui/issues/issue-98299.stderr b/tests/ui/issues/issue-98299.stderr index f4b40cbbd195..b645267e3b91 100644 --- a/tests/ui/issues/issue-98299.stderr +++ b/tests/ui/issues/issue-98299.stderr @@ -6,11 +6,11 @@ LL | SmallCString::try_from(p).map(|cstr| cstr); | | | type must be known at this point | -note: required by a bound in `SmallCString` +note: required by a const generic parameter in `SmallCString` --> $DIR/issue-98299.rs:10:25 | LL | pub struct SmallCString {} - | ^^^^^^^^^^^^^^ required by this bound in `SmallCString` + | ^^^^^^^^^^^^^^ required by this const generic parameter in `SmallCString` help: consider giving this closure parameter an explicit type, where the value of const parameter `N` is specified | LL | SmallCString::try_from(p).map(|cstr: SmallCString| cstr); diff --git a/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.stderr b/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.stderr index b4c14c2294e5..71d4277275fe 100644 --- a/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.stderr +++ b/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.stderr @@ -14,11 +14,11 @@ LL | impl Wrapper {} | = help: consider constraining the associated type `::Type` to `usize` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html -note: required by a bound in `Wrapper` +note: required by a const generic parameter in `Wrapper` --> $DIR/default-proj-ty-as-type-of-const-issue-125757.rs:12:16 | LL | struct Wrapper::Type> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Wrapper` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this const generic parameter in `Wrapper` error[E0308]: mismatched types --> $DIR/default-proj-ty-as-type-of-const-issue-125757.rs:15:30 diff --git a/tests/ui/transmutability/issue-101739-1.stderr b/tests/ui/transmutability/issue-101739-1.stderr index 1df7c3e2f974..6f79bf7b4246 100644 --- a/tests/ui/transmutability/issue-101739-1.stderr +++ b/tests/ui/transmutability/issue-101739-1.stderr @@ -10,7 +10,7 @@ error: the constant `ASSUME_ALIGNMENT` is not of type `Assume` LL | Dst: BikeshedIntrinsicFrom, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Assume`, found `bool` | -note: required by a bound in `BikeshedIntrinsicFrom` +note: required by a const generic parameter in `BikeshedIntrinsicFrom` --> $SRC_DIR/core/src/mem/transmutability.rs:LL:COL error[E0308]: mismatched types From 3d4f8b1f450e883b18c17bc56a1de575dd06f148 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 19 Dec 2023 19:30:52 +0100 Subject: [PATCH 097/147] core: implement `array::repeat` --- library/core/src/array/mod.rs | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 2569ce237077..fdb707bf51d3 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -10,6 +10,7 @@ use crate::convert::Infallible; use crate::error::Error; use crate::fmt; use crate::hash::{self, Hash}; +use crate::intrinsics::transmute_unchecked; use crate::iter::UncheckedIterator; use crate::mem::{self, MaybeUninit}; use crate::ops::{ @@ -27,6 +28,41 @@ pub(crate) use drain::drain_array_with; #[stable(feature = "array_value_iter", since = "1.51.0")] pub use iter::IntoIter; +/// Creates an array of type `[T; N]` by repeatedly cloning a value. +/// +/// The value will be used as the last element of the resulting array, so it +/// will be cloned N - 1 times. If N is zero, the value will be dropped. +/// +/// # Example +/// +/// Creating muliple copies of a string: +/// ```rust +/// #![feature(array_repeat)] +/// +/// use std::array; +/// +/// let string = "Hello there!".to_string(); +/// let strings = array::repeat(string); +/// assert_eq!(strings, ["Hello there!", "Hello there!"]); +/// ``` +#[inline] +#[unstable(feature = "array_repeat", issue = "none")] +pub fn repeat(val: T) -> [T; N] { + match N { + // SAFETY: we know N to be 0 at this point. + 0 => unsafe { transmute_unchecked::<[T; 0], [T; N]>([]) }, + // SAFETY: we know N to be 1 at this point. + 1 => unsafe { transmute_unchecked::<[T; 1], [T; N]>([val]) }, + _ => { + let mut array = MaybeUninit::uninit_array::(); + try_from_fn_erased(&mut array[..N - 1], NeverShortCircuit::wrap_mut_1(|_| val.clone())); + array[N - 1].write(val); + // SAFETY: all elements were initialized. + unsafe { MaybeUninit::array_assume_init(array) } + } + } +} + /// Creates an array of type [T; N], where each element `T` is the returned value from `cb` /// using that element's index. /// From 8aa24572f08e84ac649f69d02eb74439b6b448ba Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 20 Dec 2023 00:13:22 +0100 Subject: [PATCH 098/147] core: implement `UncheckedIterator` for `RepeatN` --- library/core/src/iter/sources/repeat_n.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/core/src/iter/sources/repeat_n.rs b/library/core/src/iter/sources/repeat_n.rs index 8224e4b12a0e..8390dab8e543 100644 --- a/library/core/src/iter/sources/repeat_n.rs +++ b/library/core/src/iter/sources/repeat_n.rs @@ -1,4 +1,4 @@ -use crate::iter::{FusedIterator, TrustedLen}; +use crate::iter::{FusedIterator, TrustedLen, UncheckedIterator}; use crate::mem::ManuallyDrop; use crate::num::NonZero; @@ -193,3 +193,5 @@ impl FusedIterator for RepeatN {} #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for RepeatN {} +#[unstable(feature = "trusted_len_next_unchecked", issue = "37572")] +impl UncheckedIterator for RepeatN {} From 39a918002e67e21330ac9103d471dc1701382c31 Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 20 Dec 2023 00:14:25 +0100 Subject: [PATCH 099/147] core: simplify implementation of `array::repeat`, address other nits --- library/core/src/array/mod.rs | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index fdb707bf51d3..63b79d5256da 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -10,8 +10,7 @@ use crate::convert::Infallible; use crate::error::Error; use crate::fmt; use crate::hash::{self, Hash}; -use crate::intrinsics::transmute_unchecked; -use crate::iter::UncheckedIterator; +use crate::iter::{repeat_n, UncheckedIterator}; use crate::mem::{self, MaybeUninit}; use crate::ops::{ ChangeOutputType, ControlFlow, FromResidual, Index, IndexMut, NeverShortCircuit, Residual, Try, @@ -30,12 +29,16 @@ pub use iter::IntoIter; /// Creates an array of type `[T; N]` by repeatedly cloning a value. /// -/// The value will be used as the last element of the resulting array, so it -/// will be cloned N - 1 times. If N is zero, the value will be dropped. +/// This is the same as `[val; N]`, but it also works for types that do not +/// implement [`Copy`]. +/// +/// The provided value will be used as an element of the resulting array and +/// will be cloned N - 1 times to fill up the rest. If N is zero, the value +/// will be dropped. /// /// # Example /// -/// Creating muliple copies of a string: +/// Creating muliple copies of a `String`: /// ```rust /// #![feature(array_repeat)] /// @@ -48,19 +51,7 @@ pub use iter::IntoIter; #[inline] #[unstable(feature = "array_repeat", issue = "none")] pub fn repeat(val: T) -> [T; N] { - match N { - // SAFETY: we know N to be 0 at this point. - 0 => unsafe { transmute_unchecked::<[T; 0], [T; N]>([]) }, - // SAFETY: we know N to be 1 at this point. - 1 => unsafe { transmute_unchecked::<[T; 1], [T; N]>([val]) }, - _ => { - let mut array = MaybeUninit::uninit_array::(); - try_from_fn_erased(&mut array[..N - 1], NeverShortCircuit::wrap_mut_1(|_| val.clone())); - array[N - 1].write(val); - // SAFETY: all elements were initialized. - unsafe { MaybeUninit::array_assume_init(array) } - } - } + from_trusted_iterator(repeat_n(val, N)) } /// Creates an array of type [T; N], where each element `T` is the returned value from `cb` From 1a8b0d7c53b62825cbe1319d6ff06e12f4dba187 Mon Sep 17 00:00:00 2001 From: joboet Date: Fri, 5 Jan 2024 12:43:01 +0100 Subject: [PATCH 100/147] add codegen test for `array::repeat` --- tests/codegen/array-repeat.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/codegen/array-repeat.rs diff --git a/tests/codegen/array-repeat.rs b/tests/codegen/array-repeat.rs new file mode 100644 index 000000000000..d4cdb054ad81 --- /dev/null +++ b/tests/codegen/array-repeat.rs @@ -0,0 +1,15 @@ +// compile-flags: -O + +#![crate_type = "lib"] +#![feature(array_repeat)] + +use std::array::repeat; + +// CHECK-LABEL: @byte_repeat +#[no_mangle] +fn byte_repeat(b: u8) -> [u8; 1024] { + // CHECK-NOT: alloca + // CHECK-NOT: store + // CHECK: memset + repeat(b) +} From e1aacea74dcdd53095af1618e6466ae8475458ac Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 19 Jun 2024 17:48:00 +0200 Subject: [PATCH 101/147] core: add tracking issue for `array::repeat` --- library/core/src/array/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 63b79d5256da..3e4eadbb7c97 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -49,7 +49,7 @@ pub use iter::IntoIter; /// assert_eq!(strings, ["Hello there!", "Hello there!"]); /// ``` #[inline] -#[unstable(feature = "array_repeat", issue = "none")] +#[unstable(feature = "array_repeat", issue = "126695")] pub fn repeat(val: T) -> [T; N] { from_trusted_iterator(repeat_n(val, N)) } From 0aa3310a9ac22509f138566398c2e65a777f5967 Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 19 Jun 2024 17:48:05 +0200 Subject: [PATCH 102/147] update codegen test for `array::repeat` --- tests/codegen/array-repeat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen/array-repeat.rs b/tests/codegen/array-repeat.rs index d4cdb054ad81..b6f3b2e83d3e 100644 --- a/tests/codegen/array-repeat.rs +++ b/tests/codegen/array-repeat.rs @@ -1,4 +1,4 @@ -// compile-flags: -O +//@ compile-flags: -O #![crate_type = "lib"] #![feature(array_repeat)] From b3c51323b507192521cfa264a6ab129fcdb85b34 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Sun, 9 Jun 2024 16:43:24 -0400 Subject: [PATCH 103/147] make assert_stderr_contains print its contents on panic --- src/doc/book | 2 +- src/tools/cargo | 2 +- src/tools/run-make-support/src/rustc.rs | 12 ------------ tests/run-make/link-args-order/rmake.rs | 19 +++++++++++-------- tests/run-make/lto-readonly-lib/rmake.rs | 16 +++++----------- 5 files changed, 18 insertions(+), 33 deletions(-) diff --git a/src/doc/book b/src/doc/book index 5228bfac8267..45c1a6d69edf 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 5228bfac8267ad24659a81b92ec5417976b5edbc +Subproject commit 45c1a6d69edfd1fc91fb7504cb73958dbd09441e diff --git a/src/tools/cargo b/src/tools/cargo index 4dcbca118ab7..a1f47ec3f7cd 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 4dcbca118ab7f9ffac4728004c983754bc6a04ff +Subproject commit a1f47ec3f7cd076986f1bfcd7061f2e8cb1a726e diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index 7e5a637e20e1..289d847a5728 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -236,18 +236,6 @@ impl Rustc { self } - /// Add an extra argument to prepend the linker invocation, via `-Zpre-link-arg`. - pub fn pre_link_arg(&mut self, link_arg: &str) -> &mut Self { - self.cmd.arg(format!("-Zpre-link-arg={link_arg}")); - self - } - - /// Add multiple extra arguments to the linker invocation, via `-Zpre-link-args`. - pub fn pre_link_args(&mut self, link_args: &str) -> &mut Self { - self.cmd.arg(format!("-Zpre-link-args={link_args}")); - self - } - /// Specify a stdin input pub fn stdin>(&mut self, input: I) -> &mut Self { self.cmd.set_stdin(input.as_ref().to_vec().into_boxed_slice()); diff --git a/tests/run-make/link-args-order/rmake.rs b/tests/run-make/link-args-order/rmake.rs index 4530251c7a58..d238ad23f27c 100644 --- a/tests/run-make/link-args-order/rmake.rs +++ b/tests/run-make/link-args-order/rmake.rs @@ -3,6 +3,9 @@ // checks that linker arguments remain intact and in the order they were originally passed in. // See https://github.com/rust-lang/rust/pull/70665 +//@ ignore-msvc +// Reason: the ld linker does not exist on Windows. + use run_make_support::rustc; fn main() { @@ -10,18 +13,18 @@ fn main() { .input("empty.rs") .linker_flavor("ld") .link_arg("a") - .link_args("\"b c\"") - .link_args("\"d e\"") + .link_args("b c") + .link_args("d e") .link_arg("f") .run_fail() - .assert_stderr_contains("\"a\" \"b\" \"c\" \"d\" \"e\" \"f\""); + .assert_stderr_contains(r#""a" "b" "c" "d" "e" "f""#); rustc() .input("empty.rs") .linker_flavor("ld") - .pre_link_arg("a") - .pre_link_args("\"b c\"") - .pre_link_args("\"d e\"") - .pre_link_arg("f") + .arg("-Zpre-link-arg=a") + .arg("-Zpre-link-args=b c") + .arg("-Zpre-link-args=d e") + .arg("-Zpre-link-arg=f") .run_fail() - .assert_stderr_contains("\"a\" \"b\" \"c\" \"d\" \"e\" \"f\""); + .assert_stderr_contains(r#""a" "b" "c" "d" "e" "f""#); } diff --git a/tests/run-make/lto-readonly-lib/rmake.rs b/tests/run-make/lto-readonly-lib/rmake.rs index bf1dafee697d..9eb135addd9e 100644 --- a/tests/run-make/lto-readonly-lib/rmake.rs +++ b/tests/run-make/lto-readonly-lib/rmake.rs @@ -8,18 +8,12 @@ //@ ignore-cross-compile use run_make_support::fs_wrapper; -use run_make_support::{cwd, run, rustc}; +use run_make_support::{run, rust_lib_name, rustc, test_while_readonly}; fn main() { rustc().input("lib.rs").run(); - let entries = fs_wrapper::read_dir(cwd()); - for entry in entries { - if entry.path().extension().and_then(|s| s.to_str()) == Some("rlib") { - let mut perms = fs_wrapper::metadata(entry.path()).permissions(); - perms.set_readonly(true); - fs_wrapper::set_permissions(entry.path(), perms); - } - } - rustc().input("main.rs").arg("-Clto").run(); - run("main"); + test_while_readonly(rust_lib_name("lib"), || { + rustc().input("main.rs").arg("-Clto").run(); + run("main"); + }); } From 50d1efa3e2aa3c1e91f9f7869225897800ce1cce Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Jun 2024 12:33:25 -0400 Subject: [PATCH 104/147] Add a test demonstrating the problem --- tests/ui/macros/auxiliary/expr_2021_implicit.rs | 9 +++++++++ tests/ui/macros/expr_2021_implicit_in_2024.rs | 10 ++++++++++ tests/ui/macros/expr_2021_implicit_in_2024.stderr | 10 ++++++++++ 3 files changed, 29 insertions(+) create mode 100644 tests/ui/macros/auxiliary/expr_2021_implicit.rs create mode 100644 tests/ui/macros/expr_2021_implicit_in_2024.rs create mode 100644 tests/ui/macros/expr_2021_implicit_in_2024.stderr diff --git a/tests/ui/macros/auxiliary/expr_2021_implicit.rs b/tests/ui/macros/auxiliary/expr_2021_implicit.rs new file mode 100644 index 000000000000..61762e41dee0 --- /dev/null +++ b/tests/ui/macros/auxiliary/expr_2021_implicit.rs @@ -0,0 +1,9 @@ +//@ edition:2021 + +#[macro_export] +macro_rules! m { + ($expr:expr) => { + compile_error!("did not expect an expression to be parsed"); + }; + (const { }) => {}; +} diff --git a/tests/ui/macros/expr_2021_implicit_in_2024.rs b/tests/ui/macros/expr_2021_implicit_in_2024.rs new file mode 100644 index 000000000000..d612c03650df --- /dev/null +++ b/tests/ui/macros/expr_2021_implicit_in_2024.rs @@ -0,0 +1,10 @@ +//@ compile-flags: --edition=2024 -Zunstable-options +//@ aux-build:expr_2021_implicit.rs + +extern crate expr_2021_implicit; + +// Makes sure that a `:expr` fragment matcher defined in a edition 2021 crate +// still parses like an `expr_2021` fragment matcher in a 2024 user crate. +expr_2021_implicit::m!(const {}); + +fn main() {} diff --git a/tests/ui/macros/expr_2021_implicit_in_2024.stderr b/tests/ui/macros/expr_2021_implicit_in_2024.stderr new file mode 100644 index 000000000000..962d572ca07e --- /dev/null +++ b/tests/ui/macros/expr_2021_implicit_in_2024.stderr @@ -0,0 +1,10 @@ +error: did not expect an expression to be parsed + --> $DIR/expr_2021_implicit_in_2024.rs:8:1 + | +LL | expr_2021_implicit::m!(const {}); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `expr_2021_implicit::m` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + From 3e8898a4e1afd44f09a5a80d466cd5b1a87e0fa8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Jun 2024 12:21:09 -0400 Subject: [PATCH 105/147] Allow naming expr_2021 in all editions --- compiler/rustc_ast/src/token.rs | 19 ++++++++++---- compiler/rustc_expand/src/mbe/macro_rules.rs | 4 ++- compiler/rustc_expand/src/mbe/quoted.rs | 3 ++- .../rustc_parse/src/parser/nonterminal.rs | 5 ++-- tests/ui/macros/expr_2021_implicit_in_2024.rs | 2 ++ .../macros/expr_2021_implicit_in_2024.stderr | 10 ------- tests/ui/macros/expr_2021_old_edition.rs | 13 ---------- tests/ui/macros/expr_2021_old_edition.stderr | 26 ------------------- 8 files changed, 23 insertions(+), 59 deletions(-) delete mode 100644 tests/ui/macros/expr_2021_implicit_in_2024.stderr delete mode 100644 tests/ui/macros/expr_2021_old_edition.rs delete mode 100644 tests/ui/macros/expr_2021_old_edition.stderr diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 109c401bb6a2..9c8b2451b798 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -884,7 +884,11 @@ pub enum NonterminalKind { PatWithOr, Expr, /// Matches an expression using the rules from edition 2021 and earlier. - Expr2021, + Expr2021 { + /// Keep track of whether the user used `:expr` or `:expr_2021` and we inferred it from the + /// edition of the span. This is used for diagnostics AND feature gating. + inferred: bool, + }, Ty, Ident, Lifetime, @@ -913,8 +917,13 @@ impl NonterminalKind { Edition::Edition2021 | Edition::Edition2024 => NonterminalKind::PatWithOr, }, sym::pat_param => NonterminalKind::PatParam { inferred: false }, - sym::expr => NonterminalKind::Expr, - sym::expr_2021 if edition().at_least_rust_2021() => NonterminalKind::Expr2021, + sym::expr => match edition() { + Edition::Edition2015 | Edition::Edition2018 | Edition::Edition2021 => { + NonterminalKind::Expr2021 { inferred: true } + } + Edition::Edition2024 => NonterminalKind::Expr, + }, + sym::expr_2021 => NonterminalKind::Expr2021 { inferred: false }, sym::ty => NonterminalKind::Ty, sym::ident => NonterminalKind::Ident, sym::lifetime => NonterminalKind::Lifetime, @@ -933,8 +942,8 @@ impl NonterminalKind { NonterminalKind::Stmt => sym::stmt, NonterminalKind::PatParam { inferred: false } => sym::pat_param, NonterminalKind::PatParam { inferred: true } | NonterminalKind::PatWithOr => sym::pat, - NonterminalKind::Expr => sym::expr, - NonterminalKind::Expr2021 => sym::expr_2021, + NonterminalKind::Expr | NonterminalKind::Expr2021 { inferred: true } => sym::expr, + NonterminalKind::Expr2021 { inferred: false } => sym::expr_2021, NonterminalKind::Ty => sym::ty, NonterminalKind::Ident => sym::ident, NonterminalKind::Lifetime => sym::lifetime, diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 49b1f5ce0e3e..0050ff10539a 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -1292,7 +1292,9 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { // maintain IsInFollow::Yes } - NonterminalKind::Stmt | NonterminalKind::Expr | NonterminalKind::Expr2021 => { + NonterminalKind::Stmt + | NonterminalKind::Expr + | NonterminalKind::Expr2021 { inferred: _ } => { const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"]; match tok { TokenTree::Token(token) => match token.kind { diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 74f78c0ef785..fdf187438d3d 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -113,7 +113,8 @@ pub(super) fn parse( ); token::NonterminalKind::Ident }); - if kind == token::NonterminalKind::Expr2021 + if kind + == (token::NonterminalKind::Expr2021 { inferred: false }) && !features.expr_fragment_specifier_2024 { rustc_session::parse::feature_err( diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index a0b704aeea5f..59f6eff07b32 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -36,7 +36,7 @@ impl<'a> Parser<'a> { } match kind { - NonterminalKind::Expr2021 => { + NonterminalKind::Expr2021 { inferred: _ } => { token.can_begin_expr() // This exception is here for backwards compatibility. && !token.is_keyword(kw::Let) @@ -47,7 +47,6 @@ impl<'a> Parser<'a> { token.can_begin_expr() // This exception is here for backwards compatibility. && !token.is_keyword(kw::Let) - && (!token.is_keyword(kw::Const) || token.span.edition().at_least_rust_2024()) } NonterminalKind::Ty => token.can_begin_type(), NonterminalKind::Ident => get_macro_ident(token).is_some(), @@ -149,7 +148,7 @@ impl<'a> Parser<'a> { })?) } - NonterminalKind::Expr | NonterminalKind::Expr2021 => { + NonterminalKind::Expr | NonterminalKind::Expr2021 { inferred: _ } => { NtExpr(self.parse_expr_force_collect()?) } NonterminalKind::Literal => { diff --git a/tests/ui/macros/expr_2021_implicit_in_2024.rs b/tests/ui/macros/expr_2021_implicit_in_2024.rs index d612c03650df..b3f7a31a802d 100644 --- a/tests/ui/macros/expr_2021_implicit_in_2024.rs +++ b/tests/ui/macros/expr_2021_implicit_in_2024.rs @@ -1,6 +1,8 @@ //@ compile-flags: --edition=2024 -Zunstable-options //@ aux-build:expr_2021_implicit.rs +//@ check-pass + extern crate expr_2021_implicit; // Makes sure that a `:expr` fragment matcher defined in a edition 2021 crate diff --git a/tests/ui/macros/expr_2021_implicit_in_2024.stderr b/tests/ui/macros/expr_2021_implicit_in_2024.stderr deleted file mode 100644 index 962d572ca07e..000000000000 --- a/tests/ui/macros/expr_2021_implicit_in_2024.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: did not expect an expression to be parsed - --> $DIR/expr_2021_implicit_in_2024.rs:8:1 - | -LL | expr_2021_implicit::m!(const {}); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `expr_2021_implicit::m` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 1 previous error - diff --git a/tests/ui/macros/expr_2021_old_edition.rs b/tests/ui/macros/expr_2021_old_edition.rs deleted file mode 100644 index a77112661068..000000000000 --- a/tests/ui/macros/expr_2021_old_edition.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ compile-flags: --edition=2018 - -// This test ensures that expr_2021 is not allowed on pre-2021 editions - -macro_rules! m { - ($e:expr_2021) => { //~ ERROR: invalid fragment specifier `expr_2021` - $e - }; -} - -fn main() { - m!(()); //~ ERROR: no rules expected the token `(` -} diff --git a/tests/ui/macros/expr_2021_old_edition.stderr b/tests/ui/macros/expr_2021_old_edition.stderr deleted file mode 100644 index bffa8a1ca175..000000000000 --- a/tests/ui/macros/expr_2021_old_edition.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error: invalid fragment specifier `expr_2021` - --> $DIR/expr_2021_old_edition.rs:6:6 - | -LL | ($e:expr_2021) => { - | ^^^^^^^^^^^^ - | - = help: fragment specifier `expr_2021` requires Rust 2021 or later - valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis` - -error: no rules expected the token `(` - --> $DIR/expr_2021_old_edition.rs:12:8 - | -LL | macro_rules! m { - | -------------- when calling this macro -... -LL | m!(()); - | ^ no rules expected this token in macro call - | -note: while trying to match meta-variable `$e:ident` - --> $DIR/expr_2021_old_edition.rs:6:6 - | -LL | ($e:expr_2021) => { - | ^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - From ff9efea6460391684fc15536b978507b456ee626 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 18 Jun 2024 11:46:26 -0500 Subject: [PATCH 106/147] Resolve Clippy `f16` and `f128` `unimplemented!`/`FIXME`s This removes the ICE codepaths for `f16` and `f128` in Clippy. `rustc_apfloat` is used as a dependency for the parsing of these types, since their `FromStr` implementation will not be available in the standard library for a while. --- Cargo.lock | 1 + .../clippy_lints/src/casts/cast_nan_to_int.rs | 1 + .../clippy/clippy_lints/src/float_literal.rs | 7 ++- src/tools/clippy/clippy_lints/src/lib.rs | 2 + .../clippy_lints/src/manual_float_methods.rs | 2 + .../clippy_lints/src/operators/float_cmp.rs | 1 + .../src/operators/modulo_arithmetic.rs | 1 + .../clippy/clippy_lints/src/zero_div_zero.rs | 1 + src/tools/clippy/clippy_utils/Cargo.toml | 2 + src/tools/clippy/clippy_utils/src/consts.rs | 43 ++++++++++++++++--- src/tools/clippy/clippy_utils/src/lib.rs | 2 + 11 files changed, 53 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da99ac9b8afc..0015c6d87b23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -709,6 +709,7 @@ dependencies = [ "clippy_config", "itertools 0.12.1", "rustc-semver", + "rustc_apfloat", ] [[package]] diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs b/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs index 1743ce71adde..5bc8692c289f 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs @@ -21,6 +21,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { match constant(cx, cx.typeck_results(), e) { + // FIXME(f16_f128): add these types when nan checks are available on all platforms Some(Constant::F64(n)) => n.is_nan(), Some(Constant::F32(n)) => n.is_nan(), _ => false, diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index 4d301daabe4c..2261fcdbdabc 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -141,18 +141,17 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { #[must_use] fn max_digits(fty: FloatTy) -> u32 { match fty { - // FIXME(f16_f128): replace the magic numbers once `{f16,f128}::DIGITS` are available - FloatTy::F16 => 3, + FloatTy::F16 => f16::DIGITS, FloatTy::F32 => f32::DIGITS, FloatTy::F64 => f64::DIGITS, - FloatTy::F128 => 33, + FloatTy::F128 => f128::DIGITS, } } /// Counts the digits excluding leading zeros #[must_use] fn count_digits(s: &str) -> usize { - // Note that s does not contain the f32/64 suffix, and underscores have been stripped + // Note that s does not contain the `f{16,32,64,128}` suffix, and underscores have been stripped s.chars() .filter(|c| *c != '-' && *c != '.') .take_while(|c| *c != 'e' && *c != 'E') diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index c65581d5203e..ef322786dbcd 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -1,6 +1,8 @@ #![feature(array_windows)] #![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] +#![feature(f128)] +#![feature(f16)] #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(let_chains)] diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs index 72cf1d7a3546..89eea0b4456d 100644 --- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs +++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs @@ -156,6 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { fn is_infinity(constant: &Constant<'_>) -> bool { match constant { + // FIXME(f16_f128): add f16 and f128 when constants are available Constant::F32(float) => *float == f32::INFINITY, Constant::F64(float) => *float == f64::INFINITY, _ => false, @@ -164,6 +165,7 @@ fn is_infinity(constant: &Constant<'_>) -> bool { fn is_neg_infinity(constant: &Constant<'_>) -> bool { match constant { + // FIXME(f16_f128): add f16 and f128 when constants are available Constant::F32(float) => *float == f32::NEG_INFINITY, Constant::F64(float) => *float == f64::NEG_INFINITY, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs index 0561739d160f..faab79de9d3c 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs @@ -86,6 +86,7 @@ fn get_lint_and_message(is_local: bool, is_comparing_arrays: bool) -> (&'static fn is_allowed(val: &Constant<'_>) -> bool { match val { + // FIXME(f16_f128): add when equality check is available on all platforms &Constant::F32(f) => f == 0.0 || f.is_infinite(), &Constant::F64(f) => f == 0.0 || f.is_infinite(), Constant::Vec(vec) => vec.iter().all(|f| match f { diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs index c56518ac72a4..d65fffac5a82 100644 --- a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs @@ -79,6 +79,7 @@ fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> }, _ => {}, }, + // FIXME(f16_f128): add when casting is available on all platforms Some(Constant::F32(f)) => { return Some(floating_point_operand_info(&f)); }, diff --git a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs index 662242f6196b..60d8a13d3599 100644 --- a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs +++ b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs @@ -38,6 +38,7 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too. && let Some(lhs_value) = constant_simple(cx, cx.typeck_results(), left) && let Some(rhs_value) = constant_simple(cx, cx.typeck_results(), right) + // FIXME(f16_f128): add these types when eq is available on all platforms && (Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value) && (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value) { diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 3a3aeb882164..6e53ff3ee6e9 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -9,6 +9,8 @@ clippy_config = { path = "../clippy_config" } arrayvec = { version = "0.7", default-features = false } itertools = "0.12" rustc-semver = "1.1" +# FIXME(f16_f128): remove when no longer needed for parsing +rustc_apfloat = "0.2.0" [features] deny-warnings = ["clippy_config/deny-warnings"] diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index cfd142fe1ff6..681c86f76d07 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -3,6 +3,8 @@ use crate::source::{get_source_text, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; +use rustc_apfloat::ieee::{Half, Quad}; +use rustc_apfloat::Float; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; @@ -33,10 +35,14 @@ pub enum Constant<'tcx> { Char(char), /// An integer's bit representation. Int(u128), + /// An `f16`. + F16(f16), /// An `f32`. F32(f32), /// An `f64`. F64(f64), + /// An `f128`. + F128(f128), /// `true` or `false`. Bool(bool), /// An array of constants. @@ -161,12 +167,19 @@ impl<'tcx> Hash for Constant<'tcx> { Self::Int(i) => { i.hash(state); }, + Self::F16(f) => { + // FIXME(f16_f128): once conversions to/from `f128` are available on all platforms, + f.to_bits().hash(state); + }, Self::F32(f) => { f64::from(f).to_bits().hash(state); }, Self::F64(f) => { f.to_bits().hash(state); }, + Self::F128(f) => { + f.to_bits().hash(state); + }, Self::Bool(b) => { b.hash(state); }, @@ -268,6 +281,16 @@ impl<'tcx> Constant<'tcx> { } self } + + fn parse_f16(s: &str) -> Self { + let f: Half = s.parse().unwrap(); + Self::F16(f16::from_bits(f.to_bits().try_into().unwrap())) + } + + fn parse_f128(s: &str) -> Self { + let f: Quad = s.parse().unwrap(); + Self::F128(f128::from_bits(f.to_bits())) + } } /// Parses a `LitKind` to a `Constant`. @@ -279,16 +302,17 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constan LitKind::Char(c) => Constant::Char(c), LitKind::Int(n, _) => Constant::Int(n.get()), LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { - ast::FloatTy::F16 => unimplemented!("f16_f128"), + // FIXME(f16_f128): just use `parse()` directly when available for `f16`/`f128` + ast::FloatTy::F16 => Constant::parse_f16(is.as_str()), ast::FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()), ast::FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()), - ast::FloatTy::F128 => unimplemented!("f16_f128"), + ast::FloatTy::F128 => Constant::parse_f128(is.as_str()), }, LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() { - ty::Float(FloatTy::F16) => unimplemented!("f16_f128"), + ty::Float(FloatTy::F16) => Constant::parse_f16(is.as_str()), ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()), ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()), - ty::Float(FloatTy::F128) => unimplemented!("f16_f128"), + ty::Float(FloatTy::F128) => Constant::parse_f128(is.as_str()), _ => bug!(), }, LitKind::Bool(b) => Constant::Bool(b), @@ -625,15 +649,19 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { match (lhs, index) { (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) { + Some(Constant::F16(x)) => Some(Constant::F16(*x)), Some(Constant::F32(x)) => Some(Constant::F32(*x)), Some(Constant::F64(x)) => Some(Constant::F64(*x)), + Some(Constant::F128(x)) => Some(Constant::F128(*x)), _ => None, }, (Some(Constant::Vec(vec)), _) => { if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) { match vec.first() { + Some(Constant::F16(x)) => Some(Constant::F16(*x)), Some(Constant::F32(x)) => Some(Constant::F32(*x)), Some(Constant::F64(x)) => Some(Constant::F64(*x)), + Some(Constant::F128(x)) => Some(Constant::F128(*x)), _ => None, } } else { @@ -760,6 +788,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { }, _ => None, }, + // FIXME(f16_f128): add these types when binary operations are available on all platforms (Constant::F32(l), Some(Constant::F32(r))) => match op.node { BinOpKind::Add => Some(Constant::F32(l + r)), BinOpKind::Sub => Some(Constant::F32(l - r)), @@ -813,8 +842,10 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))), + ty::Float(FloatTy::F16) => Some(Constant::F16(f16::from_bits(int.into()))), ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(int.into()))), ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(int.into()))), + ty::Float(FloatTy::F128) => Some(Constant::F128(f128::from_bits(int.into()))), ty::RawPtr(_, _) => Some(Constant::RawPtr(int.to_bits(int.size()))), _ => None, }, @@ -835,10 +866,10 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> let range = alloc_range(offset + size * idx, size); let val = alloc.read_scalar(&lcx.tcx, range, /* read_provenance */ false).ok()?; res.push(match flt { - FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().ok()?)), FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)), FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().ok()?)), - FloatTy::F128 => unimplemented!("f16_f128"), + FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().ok()?)), }); } Some(Constant::Vec(res)) diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 7dc341ec8d71..6848e8e5c304 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1,6 +1,8 @@ #![feature(array_chunks)] #![feature(box_patterns)] #![feature(control_flow_enum)] +#![feature(f128)] +#![feature(f16)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(lint_reasons)] From 477e9e80516985abf7319f46b612b275b2958333 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 18 Jun 2024 12:34:49 -0500 Subject: [PATCH 107/147] Update float tests to include `f16` and `f128` --- .../conf_disallowed_methods.rs | 1 + .../conf_disallowed_methods.stderr | 20 +- .../tests/ui/arithmetic_side_effects.rs | 4 + .../tests/ui/arithmetic_side_effects.stderr | 306 +++++++++--------- src/tools/clippy/tests/ui/cast.rs | 2 + src/tools/clippy/tests/ui/cast.stderr | 184 +++++------ .../clippy/tests/ui/cast_lossless_float.fixed | 2 + .../clippy/tests/ui/cast_lossless_float.rs | 2 + .../tests/ui/cast_lossless_float.stderr | 26 +- src/tools/clippy/tests/ui/cast_nan_to_int.rs | 2 + .../clippy/tests/ui/cast_nan_to_int.stderr | 12 +- .../clippy/tests/ui/cast_size.64bit.stderr | 58 ++-- src/tools/clippy/tests/ui/cast_size.rs | 13 +- src/tools/clippy/tests/ui/endian_bytes.rs | 2 + src/tools/clippy/tests/ui/endian_bytes.stderr | 172 +++++----- src/tools/clippy/tests/ui/float_cmp.rs | 2 + src/tools/clippy/tests/ui/float_cmp.stderr | 12 +- .../tests/ui/float_equality_without_abs.rs | 3 + .../ui/float_equality_without_abs.stderr | 22 +- .../ui/floating_point_arithmetic_nostd.rs | 4 +- .../clippy/tests/ui/floating_point_exp.fixed | 2 + .../clippy/tests/ui/floating_point_exp.rs | 2 + .../clippy/tests/ui/floating_point_exp.stderr | 10 +- .../clippy/tests/ui/floating_point_log.fixed | 2 + .../clippy/tests/ui/floating_point_log.rs | 2 + .../clippy/tests/ui/floating_point_log.stderr | 58 ++-- .../clippy/tests/ui/floating_point_powf.fixed | 2 + .../clippy/tests/ui/floating_point_powf.rs | 2 + .../tests/ui/floating_point_powf.stderr | 62 ++-- .../clippy/tests/ui/lossy_float_literal.fixed | 24 ++ .../clippy/tests/ui/lossy_float_literal.rs | 24 ++ .../tests/ui/lossy_float_literal.stderr | 22 +- .../clippy/tests/ui/manual_float_methods.rs | 2 + .../tests/ui/manual_float_methods.stderr | 12 +- .../tests/ui/modulo_arithmetic_float.rs | 27 ++ .../tests/ui/modulo_arithmetic_float.stderr | 70 +++- src/tools/clippy/tests/ui/transmute.rs | 30 +- src/tools/clippy/tests/ui/transmute.stderr | 186 ++++++----- .../tests/ui/transmute_float_to_int.fixed | 12 + .../clippy/tests/ui/transmute_float_to_int.rs | 12 + .../tests/ui/transmute_float_to_int.stderr | 12 +- .../clippy/tests/ui/unused_rounding.fixed | 2 + src/tools/clippy/tests/ui/unused_rounding.rs | 2 + .../clippy/tests/ui/unused_rounding.stderr | 10 +- 44 files changed, 854 insertions(+), 584 deletions(-) diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index 63fdea710cb6..17fceae01780 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -40,6 +40,7 @@ fn main() { a.sort_unstable(); + // FIXME(f16_f128): add a clamp test once the function is available let _ = 2.0f32.clamp(3.0f32, 4.0f32); let _ = 2.0f64.clamp(3.0f64, 4.0f64); diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index 55e867d5f393..4afbbf5f8079 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -28,61 +28,61 @@ LL | a.sort_unstable(); | ^^^^^^^^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:43:13 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:44:13 | LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:46:61 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:47:61 | LL | let indirect: fn(&str) -> Result = Regex::new; | ^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:49:28 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:50:28 | LL | let in_call = Box::new(f32::clamp); | ^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:50:53 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:51:53 | LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new); | ^^^^^^^^^^ error: use of a disallowed method `futures::stream::select_all` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:53:31 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:54:31 | LL | let same_name_as_module = select_all(vec![empty::<()>()]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::local_fn` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:55:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:56:5 | LL | local_fn(); | ^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::local_mod::f` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:56:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:57:5 | LL | local_mod::f(); | ^^^^^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Struct::method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:58:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:59:5 | LL | s.method(); | ^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:59:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:60:5 | LL | s.provided_method(); | ^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:60:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:61:5 | LL | s.implemented_method(); | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs index 33a91e8bbbe5..9d06e14e88c5 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs @@ -11,6 +11,8 @@ unconditional_panic )] #![feature(const_mut_refs)] +#![feature(f128)] +#![feature(f16)] #![warn(clippy::arithmetic_side_effects)] extern crate proc_macro_derive; @@ -162,8 +164,10 @@ pub fn association_with_structures_should_not_trigger_the_lint() { } pub fn hard_coded_allowed() { + let _ = 1f16 + 1f16; let _ = 1f32 + 1f32; let _ = 1f64 + 1f64; + let _ = 1f128 + 1f128; let _ = Saturating(0u32) + Saturating(0u32); let _ = String::new() + ""; diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr index 8039c0bfa248..78914667bf30 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr @@ -1,731 +1,743 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:304:5 + --> tests/ui/arithmetic_side_effects.rs:167:13 | -LL | _n += 1; - | ^^^^^^^ +LL | let _ = 1f16 + 1f16; + | ^^^^^^^^^^^ | = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::arithmetic_side_effects)]` error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:305:5 + --> tests/ui/arithmetic_side_effects.rs:170:13 | -LL | _n += &1; - | ^^^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:306:5 - | -LL | _n -= 1; - | ^^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:307:5 - | -LL | _n -= &1; - | ^^^^^^^^ +LL | let _ = 1f128 + 1f128; + | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:308:5 | -LL | _n /= 0; +LL | _n += 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:309:5 | -LL | _n /= &0; +LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:310:5 | -LL | _n %= 0; +LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:311:5 | -LL | _n %= &0; +LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:312:5 | -LL | _n *= 2; +LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:313:5 | -LL | _n *= &2; +LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:314:5 | -LL | _n += -1; - | ^^^^^^^^ +LL | _n %= 0; + | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:315:5 | -LL | _n += &-1; - | ^^^^^^^^^ +LL | _n %= &0; + | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:316:5 | -LL | _n -= -1; - | ^^^^^^^^ +LL | _n *= 2; + | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:317:5 | -LL | _n -= &-1; - | ^^^^^^^^^ +LL | _n *= &2; + | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:318:5 | -LL | _n /= -0; +LL | _n += -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:319:5 | -LL | _n /= &-0; +LL | _n += &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:320:5 | -LL | _n %= -0; +LL | _n -= -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:321:5 | -LL | _n %= &-0; +LL | _n -= &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:322:5 | -LL | _n *= -2; +LL | _n /= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:323:5 | -LL | _n *= &-2; +LL | _n /= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:324:5 | -LL | _custom += Custom; - | ^^^^^^^^^^^^^^^^^ +LL | _n %= -0; + | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:325:5 | -LL | _custom += &Custom; - | ^^^^^^^^^^^^^^^^^^ +LL | _n %= &-0; + | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:326:5 | -LL | _custom -= Custom; - | ^^^^^^^^^^^^^^^^^ +LL | _n *= -2; + | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:327:5 | -LL | _custom -= &Custom; - | ^^^^^^^^^^^^^^^^^^ +LL | _n *= &-2; + | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:328:5 | -LL | _custom /= Custom; +LL | _custom += Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:329:5 | -LL | _custom /= &Custom; +LL | _custom += &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:330:5 | -LL | _custom %= Custom; +LL | _custom -= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:331:5 | -LL | _custom %= &Custom; +LL | _custom -= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:332:5 | -LL | _custom *= Custom; +LL | _custom /= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:333:5 | -LL | _custom *= &Custom; +LL | _custom /= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:334:5 | -LL | _custom >>= Custom; - | ^^^^^^^^^^^^^^^^^^ +LL | _custom %= Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:335:5 | -LL | _custom >>= &Custom; - | ^^^^^^^^^^^^^^^^^^^ +LL | _custom %= &Custom; + | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:336:5 | -LL | _custom <<= Custom; - | ^^^^^^^^^^^^^^^^^^ +LL | _custom *= Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:337:5 | -LL | _custom <<= &Custom; - | ^^^^^^^^^^^^^^^^^^^ +LL | _custom *= &Custom; + | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:338:5 | -LL | _custom += -Custom; +LL | _custom >>= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:339:5 | -LL | _custom += &-Custom; +LL | _custom >>= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:340:5 | -LL | _custom -= -Custom; +LL | _custom <<= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:341:5 | -LL | _custom -= &-Custom; +LL | _custom <<= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:342:5 | -LL | _custom /= -Custom; +LL | _custom += -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:343:5 | -LL | _custom /= &-Custom; +LL | _custom += &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:344:5 | -LL | _custom %= -Custom; +LL | _custom -= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:345:5 | -LL | _custom %= &-Custom; +LL | _custom -= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:346:5 | -LL | _custom *= -Custom; +LL | _custom /= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:347:5 | -LL | _custom *= &-Custom; +LL | _custom /= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:348:5 | +LL | _custom %= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:349:5 + | +LL | _custom %= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:350:5 + | +LL | _custom *= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:351:5 + | +LL | _custom *= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:352:5 + | LL | _custom >>= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:349:5 + --> tests/ui/arithmetic_side_effects.rs:353:5 | LL | _custom >>= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:350:5 + --> tests/ui/arithmetic_side_effects.rs:354:5 | LL | _custom <<= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:351:5 + --> tests/ui/arithmetic_side_effects.rs:355:5 | LL | _custom <<= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:354:10 + --> tests/ui/arithmetic_side_effects.rs:358:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:355:10 + --> tests/ui/arithmetic_side_effects.rs:359:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:356:10 + --> tests/ui/arithmetic_side_effects.rs:360:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:357:10 + --> tests/ui/arithmetic_side_effects.rs:361:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:358:10 + --> tests/ui/arithmetic_side_effects.rs:362:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:359:10 + --> tests/ui/arithmetic_side_effects.rs:363:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:360:10 + --> tests/ui/arithmetic_side_effects.rs:364:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:361:10 + --> tests/ui/arithmetic_side_effects.rs:365:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:362:10 + --> tests/ui/arithmetic_side_effects.rs:366:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:363:10 + --> tests/ui/arithmetic_side_effects.rs:367:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:364:10 + --> tests/ui/arithmetic_side_effects.rs:368:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:365:10 + --> tests/ui/arithmetic_side_effects.rs:369:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:366:10 + --> tests/ui/arithmetic_side_effects.rs:370:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:367:10 + --> tests/ui/arithmetic_side_effects.rs:371:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:368:10 + --> tests/ui/arithmetic_side_effects.rs:372:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:369:10 + --> tests/ui/arithmetic_side_effects.rs:373:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:370:10 + --> tests/ui/arithmetic_side_effects.rs:374:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:371:10 + --> tests/ui/arithmetic_side_effects.rs:375:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:372:10 + --> tests/ui/arithmetic_side_effects.rs:376:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:373:15 + --> tests/ui/arithmetic_side_effects.rs:377:15 | LL | _custom = _custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:374:15 + --> tests/ui/arithmetic_side_effects.rs:378:15 | LL | _custom = _custom + &_custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:375:15 + --> tests/ui/arithmetic_side_effects.rs:379:15 | LL | _custom = Custom + _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:376:15 + --> tests/ui/arithmetic_side_effects.rs:380:15 | LL | _custom = &Custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:377:15 + --> tests/ui/arithmetic_side_effects.rs:381:15 | LL | _custom = _custom - Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:378:15 + --> tests/ui/arithmetic_side_effects.rs:382:15 | LL | _custom = _custom - &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:379:15 + --> tests/ui/arithmetic_side_effects.rs:383:15 | LL | _custom = Custom - _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:380:15 + --> tests/ui/arithmetic_side_effects.rs:384:15 | LL | _custom = &Custom - _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:381:15 + --> tests/ui/arithmetic_side_effects.rs:385:15 | LL | _custom = _custom / Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:382:15 + --> tests/ui/arithmetic_side_effects.rs:386:15 | LL | _custom = _custom / &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:383:15 + --> tests/ui/arithmetic_side_effects.rs:387:15 | LL | _custom = _custom % Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:384:15 + --> tests/ui/arithmetic_side_effects.rs:388:15 | LL | _custom = _custom % &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:385:15 + --> tests/ui/arithmetic_side_effects.rs:389:15 | LL | _custom = _custom * Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:386:15 + --> tests/ui/arithmetic_side_effects.rs:390:15 | LL | _custom = _custom * &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:387:15 + --> tests/ui/arithmetic_side_effects.rs:391:15 | LL | _custom = Custom * _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:388:15 + --> tests/ui/arithmetic_side_effects.rs:392:15 | LL | _custom = &Custom * _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:389:15 + --> tests/ui/arithmetic_side_effects.rs:393:15 | LL | _custom = Custom + &Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:390:15 + --> tests/ui/arithmetic_side_effects.rs:394:15 | LL | _custom = &Custom + Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:391:15 + --> tests/ui/arithmetic_side_effects.rs:395:15 | LL | _custom = &Custom + &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:392:15 + --> tests/ui/arithmetic_side_effects.rs:396:15 | LL | _custom = _custom >> _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:393:15 + --> tests/ui/arithmetic_side_effects.rs:397:15 | LL | _custom = _custom >> &_custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:394:15 + --> tests/ui/arithmetic_side_effects.rs:398:15 | LL | _custom = Custom << _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:395:15 + --> tests/ui/arithmetic_side_effects.rs:399:15 | LL | _custom = &Custom << _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:398:23 + --> tests/ui/arithmetic_side_effects.rs:402:23 | LL | _n.saturating_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:399:21 + --> tests/ui/arithmetic_side_effects.rs:403:21 | LL | _n.wrapping_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:400:21 + --> tests/ui/arithmetic_side_effects.rs:404:21 | LL | _n.wrapping_rem(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:401:28 + --> tests/ui/arithmetic_side_effects.rs:405:28 | LL | _n.wrapping_rem_euclid(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:403:23 + --> tests/ui/arithmetic_side_effects.rs:407:23 | LL | _n.saturating_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:404:21 + --> tests/ui/arithmetic_side_effects.rs:408:21 | LL | _n.wrapping_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:405:21 + --> tests/ui/arithmetic_side_effects.rs:409:21 | LL | _n.wrapping_rem(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:406:28 + --> tests/ui/arithmetic_side_effects.rs:410:28 | LL | _n.wrapping_rem_euclid(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:409:10 + --> tests/ui/arithmetic_side_effects.rs:413:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:410:10 + --> tests/ui/arithmetic_side_effects.rs:414:10 | LL | _n = -&_n; | ^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:411:15 + --> tests/ui/arithmetic_side_effects.rs:415:15 | LL | _custom = -_custom; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:412:15 + --> tests/ui/arithmetic_side_effects.rs:416:15 | LL | _custom = -&_custom; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:421:5 + --> tests/ui/arithmetic_side_effects.rs:425:5 | LL | 1 + i; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:422:5 + --> tests/ui/arithmetic_side_effects.rs:426:5 | LL | i * 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:423:5 + --> tests/ui/arithmetic_side_effects.rs:427:5 | LL | 1 % i / 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:424:5 + --> tests/ui/arithmetic_side_effects.rs:428:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:425:5 + --> tests/ui/arithmetic_side_effects.rs:429:5 | LL | -i; | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:436:5 + --> tests/ui/arithmetic_side_effects.rs:440:5 | LL | i += 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:437:5 + --> tests/ui/arithmetic_side_effects.rs:441:5 | LL | i -= 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:438:5 + --> tests/ui/arithmetic_side_effects.rs:442:5 | LL | i *= 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:440:5 + --> tests/ui/arithmetic_side_effects.rs:444:5 | LL | i /= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:442:5 + --> tests/ui/arithmetic_side_effects.rs:446:5 | LL | i /= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:443:5 + --> tests/ui/arithmetic_side_effects.rs:447:5 | LL | i /= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:445:5 + --> tests/ui/arithmetic_side_effects.rs:449:5 | LL | i %= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:447:5 + --> tests/ui/arithmetic_side_effects.rs:451:5 | LL | i %= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:448:5 + --> tests/ui/arithmetic_side_effects.rs:452:5 | LL | i %= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:458:5 + --> tests/ui/arithmetic_side_effects.rs:462:5 | LL | 10 / a | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:512:9 + --> tests/ui/arithmetic_side_effects.rs:516:9 | LL | x / maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:516:9 + --> tests/ui/arithmetic_side_effects.rs:520:9 | LL | x % maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:527:5 + --> tests/ui/arithmetic_side_effects.rs:531:5 | LL | one.add_assign(1); | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:531:5 + --> tests/ui/arithmetic_side_effects.rs:535:5 | LL | one.sub_assign(1); | ^^^^^^^^^^^^^^^^^ -error: aborting due to 121 previous errors +error: aborting due to 123 previous errors diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs index 453d62ce6075..c39f65a43e39 100644 --- a/src/tools/clippy/tests/ui/cast.rs +++ b/src/tools/clippy/tests/ui/cast.rs @@ -17,6 +17,8 @@ clippy::identity_op )] +// FIXME(f16_f128): add tests once const casting is available for these types + fn main() { // Test clippy::cast_precision_loss let x0 = 1i32; diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr index 43c0d8f4ed73..452482fc88e2 100644 --- a/src/tools/clippy/tests/ui/cast.stderr +++ b/src/tools/clippy/tests/ui/cast.stderr @@ -1,5 +1,5 @@ error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:23:5 + --> tests/ui/cast.rs:25:5 | LL | x0 as f32; | ^^^^^^^^^ @@ -8,37 +8,37 @@ LL | x0 as f32; = help: to override `-D warnings` add `#[allow(clippy::cast_precision_loss)]` error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:27:5 + --> tests/ui/cast.rs:29:5 | LL | x1 as f32; | ^^^^^^^^^ error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> tests/ui/cast.rs:29:5 + --> tests/ui/cast.rs:31:5 | LL | x1 as f64; | ^^^^^^^^^ error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:32:5 + --> tests/ui/cast.rs:34:5 | LL | x2 as f32; | ^^^^^^^^^ error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:35:5 + --> tests/ui/cast.rs:37:5 | LL | x3 as f32; | ^^^^^^^^^ error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> tests/ui/cast.rs:37:5 + --> tests/ui/cast.rs:39:5 | LL | x3 as f64; | ^^^^^^^^^ error: casting `f32` to `i32` may truncate the value - --> tests/ui/cast.rs:40:5 + --> tests/ui/cast.rs:42:5 | LL | 1f32 as i32; | ^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | 1f32 as i32; = help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]` error: casting `f32` to `u32` may truncate the value - --> tests/ui/cast.rs:42:5 + --> tests/ui/cast.rs:44:5 | LL | 1f32 as u32; | ^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | 1f32 as u32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:42:5 + --> tests/ui/cast.rs:44:5 | LL | 1f32 as u32; | ^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | 1f32 as u32; = help: to override `-D warnings` add `#[allow(clippy::cast_sign_loss)]` error: casting `f64` to `f32` may truncate the value - --> tests/ui/cast.rs:46:5 + --> tests/ui/cast.rs:48:5 | LL | 1f64 as f32; | ^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | 1f64 as f32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `i32` to `i8` may truncate the value - --> tests/ui/cast.rs:48:5 + --> tests/ui/cast.rs:50:5 | LL | 1i32 as i8; | ^^^^^^^^^^ @@ -85,7 +85,7 @@ LL | i8::try_from(1i32); | ~~~~~~~~~~~~~~~~~~ error: casting `i32` to `u8` may truncate the value - --> tests/ui/cast.rs:50:5 + --> tests/ui/cast.rs:52:5 | LL | 1i32 as u8; | ^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | u8::try_from(1i32); | ~~~~~~~~~~~~~~~~~~ error: casting `f64` to `isize` may truncate the value - --> tests/ui/cast.rs:52:5 + --> tests/ui/cast.rs:54:5 | LL | 1f64 as isize; | ^^^^^^^^^^^^^ @@ -105,7 +105,7 @@ LL | 1f64 as isize; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f64` to `usize` may truncate the value - --> tests/ui/cast.rs:54:5 + --> tests/ui/cast.rs:56:5 | LL | 1f64 as usize; | ^^^^^^^^^^^^^ @@ -113,13 +113,13 @@ LL | 1f64 as usize; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f64` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:54:5 + --> tests/ui/cast.rs:56:5 | LL | 1f64 as usize; | ^^^^^^^^^^^^^ error: casting `u32` to `u16` may truncate the value - --> tests/ui/cast.rs:57:5 + --> tests/ui/cast.rs:59:5 | LL | 1f32 as u32 as u16; | ^^^^^^^^^^^^^^^^^^ @@ -131,7 +131,7 @@ LL | u16::try_from(1f32 as u32); | ~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `f32` to `u32` may truncate the value - --> tests/ui/cast.rs:57:5 + --> tests/ui/cast.rs:59:5 | LL | 1f32 as u32 as u16; | ^^^^^^^^^^^ @@ -139,13 +139,13 @@ LL | 1f32 as u32 as u16; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:57:5 + --> tests/ui/cast.rs:59:5 | LL | 1f32 as u32 as u16; | ^^^^^^^^^^^ error: casting `i32` to `i8` may truncate the value - --> tests/ui/cast.rs:62:22 + --> tests/ui/cast.rs:64:22 | LL | let _x: i8 = 1i32 as _; | ^^^^^^^^^ @@ -157,7 +157,7 @@ LL | let _x: i8 = 1i32.try_into(); | ~~~~~~~~~~~~~~~ error: casting `f32` to `i32` may truncate the value - --> tests/ui/cast.rs:64:9 + --> tests/ui/cast.rs:66:9 | LL | 1f32 as i32; | ^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | 1f32 as i32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f64` to `i32` may truncate the value - --> tests/ui/cast.rs:66:9 + --> tests/ui/cast.rs:68:9 | LL | 1f64 as i32; | ^^^^^^^^^^^ @@ -173,7 +173,7 @@ LL | 1f64 as i32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u8` may truncate the value - --> tests/ui/cast.rs:68:9 + --> tests/ui/cast.rs:70:9 | LL | 1f32 as u8; | ^^^^^^^^^^ @@ -181,13 +181,13 @@ LL | 1f32 as u8; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u8` may lose the sign of the value - --> tests/ui/cast.rs:68:9 + --> tests/ui/cast.rs:70:9 | LL | 1f32 as u8; | ^^^^^^^^^^ error: casting `u8` to `i8` may wrap around the value - --> tests/ui/cast.rs:73:5 + --> tests/ui/cast.rs:75:5 | LL | 1u8 as i8; | ^^^^^^^^^ @@ -196,31 +196,31 @@ LL | 1u8 as i8; = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` error: casting `u16` to `i16` may wrap around the value - --> tests/ui/cast.rs:76:5 + --> tests/ui/cast.rs:78:5 | LL | 1u16 as i16; | ^^^^^^^^^^^ error: casting `u32` to `i32` may wrap around the value - --> tests/ui/cast.rs:78:5 + --> tests/ui/cast.rs:80:5 | LL | 1u32 as i32; | ^^^^^^^^^^^ error: casting `u64` to `i64` may wrap around the value - --> tests/ui/cast.rs:80:5 + --> tests/ui/cast.rs:82:5 | LL | 1u64 as i64; | ^^^^^^^^^^^ error: casting `usize` to `isize` may wrap around the value - --> tests/ui/cast.rs:82:5 + --> tests/ui/cast.rs:84:5 | LL | 1usize as isize; | ^^^^^^^^^^^^^^^ error: casting `usize` to `i8` may truncate the value - --> tests/ui/cast.rs:85:5 + --> tests/ui/cast.rs:87:5 | LL | 1usize as i8; | ^^^^^^^^^^^^ @@ -232,7 +232,7 @@ LL | i8::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i16` may truncate the value - --> tests/ui/cast.rs:88:5 + --> tests/ui/cast.rs:90:5 | LL | 1usize as i16; | ^^^^^^^^^^^^^ @@ -244,7 +244,7 @@ LL | i16::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i16` may wrap around the value on targets with 16-bit wide pointers - --> tests/ui/cast.rs:88:5 + --> tests/ui/cast.rs:90:5 | LL | 1usize as i16; | ^^^^^^^^^^^^^ @@ -253,7 +253,7 @@ LL | 1usize as i16; = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> tests/ui/cast.rs:93:5 + --> tests/ui/cast.rs:95:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -265,19 +265,19 @@ LL | i32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:93:5 + --> tests/ui/cast.rs:95:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ error: casting `usize` to `i64` may wrap around the value on targets with 64-bit wide pointers - --> tests/ui/cast.rs:97:5 + --> tests/ui/cast.rs:99:5 | LL | 1usize as i64; | ^^^^^^^^^^^^^ error: casting `u16` to `isize` may wrap around the value on targets with 16-bit wide pointers - --> tests/ui/cast.rs:102:5 + --> tests/ui/cast.rs:104:5 | LL | 1u16 as isize; | ^^^^^^^^^^^^^ @@ -286,13 +286,13 @@ LL | 1u16 as isize; = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:106:5 + --> tests/ui/cast.rs:108:5 | LL | 1u32 as isize; | ^^^^^^^^^^^^^ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:109:5 + --> tests/ui/cast.rs:111:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ @@ -304,55 +304,55 @@ LL | isize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers - --> tests/ui/cast.rs:109:5 + --> tests/ui/cast.rs:111:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:114:5 + --> tests/ui/cast.rs:116:5 | LL | -1i32 as u32; | ^^^^^^^^^^^^ error: casting `isize` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:117:5 + --> tests/ui/cast.rs:119:5 | LL | -1isize as usize; | ^^^^^^^^^^^^^^^^ error: casting `i8` to `u8` may lose the sign of the value - --> tests/ui/cast.rs:128:5 + --> tests/ui/cast.rs:130:5 | LL | (i8::MIN).abs() as u8; | ^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `u64` may lose the sign of the value - --> tests/ui/cast.rs:132:5 + --> tests/ui/cast.rs:134:5 | LL | (-1i64).abs() as u64; | ^^^^^^^^^^^^^^^^^^^^ error: casting `isize` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:133:5 + --> tests/ui/cast.rs:135:5 | LL | (-1isize).abs() as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `u64` may lose the sign of the value - --> tests/ui/cast.rs:140:5 + --> tests/ui/cast.rs:142:5 | LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `u64` may lose the sign of the value - --> tests/ui/cast.rs:155:5 + --> tests/ui/cast.rs:157:5 | LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `i8` may truncate the value - --> tests/ui/cast.rs:206:5 + --> tests/ui/cast.rs:208:5 | LL | (-99999999999i64).min(1) as i8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -364,7 +364,7 @@ LL | i8::try_from((-99999999999i64).min(1)); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `u8` may truncate the value - --> tests/ui/cast.rs:220:5 + --> tests/ui/cast.rs:222:5 | LL | 999999u64.clamp(0, 256) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -376,7 +376,7 @@ LL | u8::try_from(999999u64.clamp(0, 256)); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `main::E2` to `u8` may truncate the value - --> tests/ui/cast.rs:243:21 + --> tests/ui/cast.rs:245:21 | LL | let _ = self as u8; | ^^^^^^^^^^ @@ -388,7 +388,7 @@ LL | let _ = u8::try_from(self); | ~~~~~~~~~~~~~~~~~~ error: casting `main::E2::B` to `u8` will truncate the value - --> tests/ui/cast.rs:245:21 + --> tests/ui/cast.rs:247:21 | LL | let _ = Self::B as u8; | ^^^^^^^^^^^^^ @@ -397,7 +397,7 @@ LL | let _ = Self::B as u8; = help: to override `-D warnings` add `#[allow(clippy::cast_enum_truncation)]` error: casting `main::E5` to `i8` may truncate the value - --> tests/ui/cast.rs:287:21 + --> tests/ui/cast.rs:289:21 | LL | let _ = self as i8; | ^^^^^^^^^^ @@ -409,13 +409,13 @@ LL | let _ = i8::try_from(self); | ~~~~~~~~~~~~~~~~~~ error: casting `main::E5::A` to `i8` will truncate the value - --> tests/ui/cast.rs:289:21 + --> tests/ui/cast.rs:291:21 | LL | let _ = Self::A as i8; | ^^^^^^^^^^^^^ error: casting `main::E6` to `i16` may truncate the value - --> tests/ui/cast.rs:306:21 + --> tests/ui/cast.rs:308:21 | LL | let _ = self as i16; | ^^^^^^^^^^^ @@ -427,7 +427,7 @@ LL | let _ = i16::try_from(self); | ~~~~~~~~~~~~~~~~~~~ error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:325:21 + --> tests/ui/cast.rs:327:21 | LL | let _ = self as usize; | ^^^^^^^^^^^^^ @@ -439,7 +439,7 @@ LL | let _ = usize::try_from(self); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `main::E10` to `u16` may truncate the value - --> tests/ui/cast.rs:372:21 + --> tests/ui/cast.rs:374:21 | LL | let _ = self as u16; | ^^^^^^^^^^^ @@ -451,7 +451,7 @@ LL | let _ = u16::try_from(self); | ~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `u8` may truncate the value - --> tests/ui/cast.rs:383:13 + --> tests/ui/cast.rs:385:13 | LL | let c = (q >> 16) as u8; | ^^^^^^^^^^^^^^^ @@ -463,7 +463,7 @@ LL | let c = u8::try_from(q >> 16); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `u8` may truncate the value - --> tests/ui/cast.rs:387:13 + --> tests/ui/cast.rs:389:13 | LL | let c = (q / 1000) as u8; | ^^^^^^^^^^^^^^^^ @@ -475,85 +475,85 @@ LL | let c = u8::try_from(q / 1000); | ~~~~~~~~~~~~~~~~~~~~~~ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:399:9 + --> tests/ui/cast.rs:401:9 | LL | (x * x) as u32; | ^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:404:32 + --> tests/ui/cast.rs:406:32 | LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:406:5 + --> tests/ui/cast.rs:408:5 | LL | (2_i32).checked_pow(3).unwrap() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:407:5 + --> tests/ui/cast.rs:409:5 | LL | (-2_i32).pow(3) as u32; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:412:5 + --> tests/ui/cast.rs:414:5 | LL | (-5_i32 % 2) as u32; | ^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:414:5 + --> tests/ui/cast.rs:416:5 | LL | (-5_i32 % -2) as u32; | ^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:417:5 + --> tests/ui/cast.rs:419:5 | LL | (-2_i32 >> 1) as u32; | ^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:421:5 + --> tests/ui/cast.rs:423:5 | LL | (x * x) as u32; | ^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:422:5 + --> tests/ui/cast.rs:424:5 | LL | (x * x * x) as u32; | ^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:426:5 + --> tests/ui/cast.rs:428:5 | LL | (y * y * y * y * -2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:428:5 + --> tests/ui/cast.rs:430:5 | LL | (y * y * y / y * 2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:429:5 + --> tests/ui/cast.rs:431:5 | LL | (y * y / y * 2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:431:5 + --> tests/ui/cast.rs:433:5 | LL | (y / y * y * -2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `/` - --> tests/ui/cast.rs:431:6 + --> tests/ui/cast.rs:433:6 | LL | (y / y * y * -2) as u16; | ^^^^^ @@ -561,97 +561,97 @@ LL | (y / y * y * -2) as u16; = note: `#[deny(clippy::eq_op)]` on by default error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:434:5 + --> tests/ui/cast.rs:436:5 | LL | (y + y + y + -2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:436:5 + --> tests/ui/cast.rs:438:5 | LL | (y + y + y + 2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:440:5 + --> tests/ui/cast.rs:442:5 | LL | (z + -2) as u16; | ^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:442:5 + --> tests/ui/cast.rs:444:5 | LL | (z + z + 2) as u16; | ^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:445:9 + --> tests/ui/cast.rs:447:9 | LL | (a * a * b * b * c * c) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:446:9 + --> tests/ui/cast.rs:448:9 | LL | (a * b * c) as u32; | ^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:448:9 + --> tests/ui/cast.rs:450:9 | LL | (a * -b * c) as u32; | ^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:450:9 + --> tests/ui/cast.rs:452:9 | LL | (a * b * c * c) as u32; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:451:9 + --> tests/ui/cast.rs:453:9 | LL | (a * -2) as u32; | ^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:453:9 + --> tests/ui/cast.rs:455:9 | LL | (a * b * c * -2) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:455:9 + --> tests/ui/cast.rs:457:9 | LL | (a / b) as u32; | ^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:456:9 + --> tests/ui/cast.rs:458:9 | LL | (a / b * c) as u32; | ^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:458:9 + --> tests/ui/cast.rs:460:9 | LL | (a / b + b * c) as u32; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:460:9 + --> tests/ui/cast.rs:462:9 | LL | a.saturating_pow(3) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:462:9 + --> tests/ui/cast.rs:464:9 | LL | (a.abs() * b.pow(2) / c.abs()) as u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:470:21 + --> tests/ui/cast.rs:472:21 | LL | let _ = i32::MIN as u32; // cast_sign_loss | ^^^^^^^^^^^^^^^ @@ -662,7 +662,7 @@ LL | m!(); = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) error: casting `u32` to `u8` may truncate the value - --> tests/ui/cast.rs:471:21 + --> tests/ui/cast.rs:473:21 | LL | let _ = u32::MAX as u8; // cast_possible_truncation | ^^^^^^^^^^^^^^ @@ -678,7 +678,7 @@ LL | let _ = u8::try_from(u32::MAX); // cast_possible_truncation | ~~~~~~~~~~~~~~~~~~~~~~ error: casting `f64` to `f32` may truncate the value - --> tests/ui/cast.rs:472:21 + --> tests/ui/cast.rs:474:21 | LL | let _ = std::f64::consts::PI as f32; // cast_possible_truncation | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -690,7 +690,7 @@ LL | m!(); = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:481:5 + --> tests/ui/cast.rs:483:5 | LL | bar.unwrap().unwrap() as usize | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -702,13 +702,13 @@ LL | usize::try_from(bar.unwrap().unwrap()) | error: casting `i64` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:481:5 + --> tests/ui/cast.rs:483:5 | LL | bar.unwrap().unwrap() as usize | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `u64` to `u8` may truncate the value - --> tests/ui/cast.rs:496:5 + --> tests/ui/cast.rs:498:5 | LL | (256 & 999999u64) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -720,7 +720,7 @@ LL | u8::try_from(256 & 999999u64); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `u8` may truncate the value - --> tests/ui/cast.rs:498:5 + --> tests/ui/cast.rs:500:5 | LL | (255 % 999999u64) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.fixed b/src/tools/clippy/tests/ui/cast_lossless_float.fixed index 96a67b1945ca..163432631e13 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_float.fixed +++ b/src/tools/clippy/tests/ui/cast_lossless_float.fixed @@ -1,6 +1,8 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] #![warn(clippy::cast_lossless)] +// FIXME(f16_f128): add tests for these types once const casting is available + type F32 = f32; type F64 = f64; diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.rs b/src/tools/clippy/tests/ui/cast_lossless_float.rs index d37b2c1d920e..afb2a3d890ee 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_float.rs +++ b/src/tools/clippy/tests/ui/cast_lossless_float.rs @@ -1,6 +1,8 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] #![warn(clippy::cast_lossless)] +// FIXME(f16_f128): add tests for these types once const casting is available + type F32 = f32; type F64 = f64; diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.stderr b/src/tools/clippy/tests/ui/cast_lossless_float.stderr index ad7de760adfb..f2ba4e3b9903 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_float.stderr +++ b/src/tools/clippy/tests/ui/cast_lossless_float.stderr @@ -1,5 +1,5 @@ error: casting `i8` to `f32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:10:13 + --> tests/ui/cast_lossless_float.rs:12:13 | LL | let _ = x0 as f32; | ^^^^^^^^^ help: try: `f32::from(x0)` @@ -8,73 +8,73 @@ LL | let _ = x0 as f32; = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]` error: casting `i8` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:11:13 + --> tests/ui/cast_lossless_float.rs:13:13 | LL | let _ = x0 as f64; | ^^^^^^^^^ help: try: `f64::from(x0)` error: casting `i8` to `F32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:12:13 + --> tests/ui/cast_lossless_float.rs:14:13 | LL | let _ = x0 as F32; | ^^^^^^^^^ help: try: `F32::from(x0)` error: casting `i8` to `F64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:13:13 + --> tests/ui/cast_lossless_float.rs:15:13 | LL | let _ = x0 as F64; | ^^^^^^^^^ help: try: `F64::from(x0)` error: casting `u8` to `f32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:15:13 + --> tests/ui/cast_lossless_float.rs:17:13 | LL | let _ = x1 as f32; | ^^^^^^^^^ help: try: `f32::from(x1)` error: casting `u8` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:16:13 + --> tests/ui/cast_lossless_float.rs:18:13 | LL | let _ = x1 as f64; | ^^^^^^^^^ help: try: `f64::from(x1)` error: casting `i16` to `f32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:18:13 + --> tests/ui/cast_lossless_float.rs:20:13 | LL | let _ = x2 as f32; | ^^^^^^^^^ help: try: `f32::from(x2)` error: casting `i16` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:19:13 + --> tests/ui/cast_lossless_float.rs:21:13 | LL | let _ = x2 as f64; | ^^^^^^^^^ help: try: `f64::from(x2)` error: casting `u16` to `f32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:21:13 + --> tests/ui/cast_lossless_float.rs:23:13 | LL | let _ = x3 as f32; | ^^^^^^^^^ help: try: `f32::from(x3)` error: casting `u16` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:22:13 + --> tests/ui/cast_lossless_float.rs:24:13 | LL | let _ = x3 as f64; | ^^^^^^^^^ help: try: `f64::from(x3)` error: casting `i32` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:24:13 + --> tests/ui/cast_lossless_float.rs:26:13 | LL | let _ = x4 as f64; | ^^^^^^^^^ help: try: `f64::from(x4)` error: casting `u32` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:26:13 + --> tests/ui/cast_lossless_float.rs:28:13 | LL | let _ = x5 as f64; | ^^^^^^^^^ help: try: `f64::from(x5)` error: casting `f32` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:29:13 + --> tests/ui/cast_lossless_float.rs:31:13 | LL | let _ = 1.0f32 as f64; | ^^^^^^^^^^^^^ help: try: `f64::from(1.0f32)` diff --git a/src/tools/clippy/tests/ui/cast_nan_to_int.rs b/src/tools/clippy/tests/ui/cast_nan_to_int.rs index 2d7467ff0400..aee38da9a159 100644 --- a/src/tools/clippy/tests/ui/cast_nan_to_int.rs +++ b/src/tools/clippy/tests/ui/cast_nan_to_int.rs @@ -1,3 +1,5 @@ +// FIXME(f16_f128): add tests when constants are available + #![warn(clippy::cast_nan_to_int)] #![allow(clippy::eq_op)] diff --git a/src/tools/clippy/tests/ui/cast_nan_to_int.stderr b/src/tools/clippy/tests/ui/cast_nan_to_int.stderr index 3cb46d1e79bb..3aeb2d545251 100644 --- a/src/tools/clippy/tests/ui/cast_nan_to_int.stderr +++ b/src/tools/clippy/tests/ui/cast_nan_to_int.stderr @@ -1,5 +1,5 @@ error: casting a known NaN to usize - --> tests/ui/cast_nan_to_int.rs:5:13 + --> tests/ui/cast_nan_to_int.rs:7:13 | LL | let _ = (0.0_f32 / -0.0) as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = (0.0_f32 / -0.0) as usize; = help: to override `-D warnings` add `#[allow(clippy::cast_nan_to_int)]` error: casting a known NaN to usize - --> tests/ui/cast_nan_to_int.rs:8:13 + --> tests/ui/cast_nan_to_int.rs:10:13 | LL | let _ = (f64::INFINITY * -0.0) as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let _ = (f64::INFINITY * -0.0) as usize; = note: this always evaluates to 0 error: casting a known NaN to usize - --> tests/ui/cast_nan_to_int.rs:11:13 + --> tests/ui/cast_nan_to_int.rs:13:13 | LL | let _ = (0.0 * f32::INFINITY) as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = (0.0 * f32::INFINITY) as usize; = note: this always evaluates to 0 error: casting a known NaN to usize - --> tests/ui/cast_nan_to_int.rs:15:13 + --> tests/ui/cast_nan_to_int.rs:17:13 | LL | let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize; = note: this always evaluates to 0 error: casting a known NaN to usize - --> tests/ui/cast_nan_to_int.rs:18:13 + --> tests/ui/cast_nan_to_int.rs:20:13 | LL | let _ = (f32::INFINITY - f32::INFINITY) as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | let _ = (f32::INFINITY - f32::INFINITY) as usize; = note: this always evaluates to 0 error: casting a known NaN to usize - --> tests/ui/cast_nan_to_int.rs:21:13 + --> tests/ui/cast_nan_to_int.rs:23:13 | LL | let _ = (f32::INFINITY / f32::NEG_INFINITY) as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/cast_size.64bit.stderr b/src/tools/clippy/tests/ui/cast_size.64bit.stderr index 0dc4ca91529c..bc37107d80e3 100644 --- a/src/tools/clippy/tests/ui/cast_size.64bit.stderr +++ b/src/tools/clippy/tests/ui/cast_size.64bit.stderr @@ -12,35 +12,35 @@ help: ... or use `try_from` and handle the error accordingly LL | i8::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~ -error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> tests/ui/cast_size.rs:18:5 +error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> tests/ui/cast_size.rs:21:5 | -LL | x0 as f64; +LL | x0 as f32; | ^^^^^^^^^ | = note: `-D clippy::cast-precision-loss` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_precision_loss)]` -error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> tests/ui/cast_size.rs:19:5 - | -LL | x1 as f64; - | ^^^^^^^^^ - -error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast_size.rs:20:5 - | -LL | x0 as f32; - | ^^^^^^^^^ - error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast_size.rs:21:5 + --> tests/ui/cast_size.rs:22:5 | LL | x1 as f32; | ^^^^^^^^^ +error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> tests/ui/cast_size.rs:23:5 + | +LL | x0 as f64; + | ^^^^^^^^^ + +error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> tests/ui/cast_size.rs:24:5 + | +LL | x1 as f64; + | ^^^^^^^^^ + error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> tests/ui/cast_size.rs:22:5 + --> tests/ui/cast_size.rs:28:5 | LL | 1isize as i32; | ^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | i32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> tests/ui/cast_size.rs:23:5 + --> tests/ui/cast_size.rs:29:5 | LL | 1isize as u32; | ^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | u32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> tests/ui/cast_size.rs:24:5 + --> tests/ui/cast_size.rs:30:5 | LL | 1usize as u32; | ^^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | u32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> tests/ui/cast_size.rs:25:5 + --> tests/ui/cast_size.rs:31:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | i32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers - --> tests/ui/cast_size.rs:25:5 + --> tests/ui/cast_size.rs:31:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | 1usize as i32; = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast_size.rs:26:5 + --> tests/ui/cast_size.rs:32:5 | LL | 1i64 as isize; | ^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | isize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast_size.rs:27:5 + --> tests/ui/cast_size.rs:33:5 | LL | 1i64 as usize; | ^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL | usize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast_size.rs:28:5 + --> tests/ui/cast_size.rs:34:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ @@ -133,13 +133,13 @@ LL | isize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers - --> tests/ui/cast_size.rs:28:5 + --> tests/ui/cast_size.rs:34:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast_size.rs:29:5 + --> tests/ui/cast_size.rs:35:5 | LL | 1u64 as usize; | ^^^^^^^^^^^^^ @@ -151,19 +151,19 @@ LL | usize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers - --> tests/ui/cast_size.rs:30:5 + --> tests/ui/cast_size.rs:36:5 | LL | 1u32 as isize; | ^^^^^^^^^^^^^ error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast_size.rs:35:5 + --> tests/ui/cast_size.rs:43:5 | LL | 999_999_999 as f32; | ^^^^^^^^^^^^^^^^^^ error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> tests/ui/cast_size.rs:36:5 + --> tests/ui/cast_size.rs:44:5 | LL | 9_999_999_999_999_999usize as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/cast_size.rs b/src/tools/clippy/tests/ui/cast_size.rs index d063a70ccdfc..d0ed8e57d7ae 100644 --- a/src/tools/clippy/tests/ui/cast_size.rs +++ b/src/tools/clippy/tests/ui/cast_size.rs @@ -15,10 +15,16 @@ fn main() { 1isize as i8; let x0 = 1isize; let x1 = 1usize; - x0 as f64; - x1 as f64; + // FIXME(f16_f128): enable f16 and f128 conversions once const eval supports them + // x0 as f16; + // x1 as f16; x0 as f32; x1 as f32; + x0 as f64; + x1 as f64; + // x0 as f128; + // x1 as f128; + 1isize as i32; 1isize as u32; 1usize as u32; @@ -31,7 +37,10 @@ fn main() { 1u32 as usize; // Should not trigger any lint 1i32 as isize; // Neither should this 1i32 as usize; + // Big integer literal to float + // 999_999 as f16; 999_999_999 as f32; 9_999_999_999_999_999usize as f64; + // 999_999_999_999_999_999_999_999_999_999u128 as f128; } diff --git a/src/tools/clippy/tests/ui/endian_bytes.rs b/src/tools/clippy/tests/ui/endian_bytes.rs index 6bf014fc8095..580fc2fc24d7 100644 --- a/src/tools/clippy/tests/ui/endian_bytes.rs +++ b/src/tools/clippy/tests/ui/endian_bytes.rs @@ -2,6 +2,8 @@ #![allow(clippy::diverging_sub_expression)] #![no_main] +// FIXME(f16_f128): add these types when `{to_from}_*_bytes` are available + macro_rules! fn_body { () => { 2u8.to_ne_bytes(); diff --git a/src/tools/clippy/tests/ui/endian_bytes.stderr b/src/tools/clippy/tests/ui/endian_bytes.stderr index 3fc26dcab886..fd19ec45872b 100644 --- a/src/tools/clippy/tests/ui/endian_bytes.stderr +++ b/src/tools/clippy/tests/ui/endian_bytes.stderr @@ -1,5 +1,5 @@ error: usage of the `u8::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:7:9 + --> tests/ui/endian_bytes.rs:9:9 | LL | 2u8.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `i8::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:8:9 + --> tests/ui/endian_bytes.rs:10:9 | LL | 2i8.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u16::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:9:9 + --> tests/ui/endian_bytes.rs:11:9 | LL | 2u16.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `i16::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:10:9 + --> tests/ui/endian_bytes.rs:12:9 | LL | 2i16.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u32::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:11:9 + --> tests/ui/endian_bytes.rs:13:9 | LL | 2u32.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `i32::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:12:9 + --> tests/ui/endian_bytes.rs:14:9 | LL | 2i32.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u64::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:13:9 + --> tests/ui/endian_bytes.rs:15:9 | LL | 2u64.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `i64::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:14:9 + --> tests/ui/endian_bytes.rs:16:9 | LL | 2i64.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u128::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:15:9 + --> tests/ui/endian_bytes.rs:17:9 | LL | 2u128.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `i128::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:16:9 + --> tests/ui/endian_bytes.rs:18:9 | LL | 2i128.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `f32::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:17:9 + --> tests/ui/endian_bytes.rs:19:9 | LL | 2.0f32.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `f64::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:18:9 + --> tests/ui/endian_bytes.rs:20:9 | LL | 2.0f64.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `usize::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:19:9 + --> tests/ui/endian_bytes.rs:21:9 | LL | 2usize.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `isize::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:20:9 + --> tests/ui/endian_bytes.rs:22:9 | LL | 2isize.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_ne_bytes` - --> tests/ui/endian_bytes.rs:21:9 + --> tests/ui/endian_bytes.rs:23:9 | LL | u8::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `i8::from_ne_bytes` - --> tests/ui/endian_bytes.rs:22:9 + --> tests/ui/endian_bytes.rs:24:9 | LL | i8::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u16::from_ne_bytes` - --> tests/ui/endian_bytes.rs:23:9 + --> tests/ui/endian_bytes.rs:25:9 | LL | u16::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -205,7 +205,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `i16::from_ne_bytes` - --> tests/ui/endian_bytes.rs:24:9 + --> tests/ui/endian_bytes.rs:26:9 | LL | i16::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u32::from_ne_bytes` - --> tests/ui/endian_bytes.rs:25:9 + --> tests/ui/endian_bytes.rs:27:9 | LL | u32::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `i32::from_ne_bytes` - --> tests/ui/endian_bytes.rs:26:9 + --> tests/ui/endian_bytes.rs:28:9 | LL | i32::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u64::from_ne_bytes` - --> tests/ui/endian_bytes.rs:27:9 + --> tests/ui/endian_bytes.rs:29:9 | LL | u64::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -253,7 +253,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `i64::from_ne_bytes` - --> tests/ui/endian_bytes.rs:28:9 + --> tests/ui/endian_bytes.rs:30:9 | LL | i64::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -265,7 +265,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u128::from_ne_bytes` - --> tests/ui/endian_bytes.rs:29:9 + --> tests/ui/endian_bytes.rs:31:9 | LL | u128::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -277,7 +277,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `i128::from_ne_bytes` - --> tests/ui/endian_bytes.rs:30:9 + --> tests/ui/endian_bytes.rs:32:9 | LL | i128::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -289,7 +289,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `usize::from_ne_bytes` - --> tests/ui/endian_bytes.rs:31:9 + --> tests/ui/endian_bytes.rs:33:9 | LL | usize::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -301,7 +301,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `isize::from_ne_bytes` - --> tests/ui/endian_bytes.rs:32:9 + --> tests/ui/endian_bytes.rs:34:9 | LL | isize::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -313,7 +313,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `f32::from_ne_bytes` - --> tests/ui/endian_bytes.rs:33:9 + --> tests/ui/endian_bytes.rs:35:9 | LL | f32::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -325,7 +325,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `f64::from_ne_bytes` - --> tests/ui/endian_bytes.rs:34:9 + --> tests/ui/endian_bytes.rs:36:9 | LL | f64::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -337,7 +337,7 @@ LL | fn host() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_le_bytes` method - --> tests/ui/endian_bytes.rs:36:9 + --> tests/ui/endian_bytes.rs:38:9 | LL | 2u8.to_le_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -351,7 +351,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `i8::to_le_bytes` method - --> tests/ui/endian_bytes.rs:37:9 + --> tests/ui/endian_bytes.rs:39:9 | LL | 2i8.to_le_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -363,7 +363,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u16::to_le_bytes` method - --> tests/ui/endian_bytes.rs:38:9 + --> tests/ui/endian_bytes.rs:40:9 | LL | 2u16.to_le_bytes(); | ^^^^^^^^^^^^^^^^^^ @@ -375,7 +375,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `i16::to_le_bytes` method - --> tests/ui/endian_bytes.rs:39:9 + --> tests/ui/endian_bytes.rs:41:9 | LL | 2i16.to_le_bytes(); | ^^^^^^^^^^^^^^^^^^ @@ -387,7 +387,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u32::to_le_bytes` method - --> tests/ui/endian_bytes.rs:40:9 + --> tests/ui/endian_bytes.rs:42:9 | LL | 2u32.to_le_bytes(); | ^^^^^^^^^^^^^^^^^^ @@ -399,7 +399,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `i32::to_le_bytes` method - --> tests/ui/endian_bytes.rs:41:9 + --> tests/ui/endian_bytes.rs:43:9 | LL | 2i32.to_le_bytes(); | ^^^^^^^^^^^^^^^^^^ @@ -411,7 +411,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u64::to_le_bytes` method - --> tests/ui/endian_bytes.rs:42:9 + --> tests/ui/endian_bytes.rs:44:9 | LL | 2u64.to_le_bytes(); | ^^^^^^^^^^^^^^^^^^ @@ -423,7 +423,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `i64::to_le_bytes` method - --> tests/ui/endian_bytes.rs:43:9 + --> tests/ui/endian_bytes.rs:45:9 | LL | 2i64.to_le_bytes(); | ^^^^^^^^^^^^^^^^^^ @@ -435,7 +435,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u128::to_le_bytes` method - --> tests/ui/endian_bytes.rs:44:9 + --> tests/ui/endian_bytes.rs:46:9 | LL | 2u128.to_le_bytes(); | ^^^^^^^^^^^^^^^^^^^ @@ -447,7 +447,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `i128::to_le_bytes` method - --> tests/ui/endian_bytes.rs:45:9 + --> tests/ui/endian_bytes.rs:47:9 | LL | 2i128.to_le_bytes(); | ^^^^^^^^^^^^^^^^^^^ @@ -459,7 +459,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `f32::to_le_bytes` method - --> tests/ui/endian_bytes.rs:46:9 + --> tests/ui/endian_bytes.rs:48:9 | LL | 2.0f32.to_le_bytes(); | ^^^^^^^^^^^^^^^^^^^^ @@ -471,7 +471,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `f64::to_le_bytes` method - --> tests/ui/endian_bytes.rs:47:9 + --> tests/ui/endian_bytes.rs:49:9 | LL | 2.0f64.to_le_bytes(); | ^^^^^^^^^^^^^^^^^^^^ @@ -483,7 +483,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `usize::to_le_bytes` method - --> tests/ui/endian_bytes.rs:48:9 + --> tests/ui/endian_bytes.rs:50:9 | LL | 2usize.to_le_bytes(); | ^^^^^^^^^^^^^^^^^^^^ @@ -495,7 +495,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `isize::to_le_bytes` method - --> tests/ui/endian_bytes.rs:49:9 + --> tests/ui/endian_bytes.rs:51:9 | LL | 2isize.to_le_bytes(); | ^^^^^^^^^^^^^^^^^^^^ @@ -507,7 +507,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_le_bytes` - --> tests/ui/endian_bytes.rs:50:9 + --> tests/ui/endian_bytes.rs:52:9 | LL | u8::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -519,7 +519,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `i8::from_le_bytes` - --> tests/ui/endian_bytes.rs:51:9 + --> tests/ui/endian_bytes.rs:53:9 | LL | i8::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -531,7 +531,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u16::from_le_bytes` - --> tests/ui/endian_bytes.rs:52:9 + --> tests/ui/endian_bytes.rs:54:9 | LL | u16::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -543,7 +543,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `i16::from_le_bytes` - --> tests/ui/endian_bytes.rs:53:9 + --> tests/ui/endian_bytes.rs:55:9 | LL | i16::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -555,7 +555,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u32::from_le_bytes` - --> tests/ui/endian_bytes.rs:54:9 + --> tests/ui/endian_bytes.rs:56:9 | LL | u32::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -567,7 +567,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `i32::from_le_bytes` - --> tests/ui/endian_bytes.rs:55:9 + --> tests/ui/endian_bytes.rs:57:9 | LL | i32::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -579,7 +579,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u64::from_le_bytes` - --> tests/ui/endian_bytes.rs:56:9 + --> tests/ui/endian_bytes.rs:58:9 | LL | u64::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -591,7 +591,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `i64::from_le_bytes` - --> tests/ui/endian_bytes.rs:57:9 + --> tests/ui/endian_bytes.rs:59:9 | LL | i64::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -603,7 +603,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u128::from_le_bytes` - --> tests/ui/endian_bytes.rs:58:9 + --> tests/ui/endian_bytes.rs:60:9 | LL | u128::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -615,7 +615,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `i128::from_le_bytes` - --> tests/ui/endian_bytes.rs:59:9 + --> tests/ui/endian_bytes.rs:61:9 | LL | i128::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -627,7 +627,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `usize::from_le_bytes` - --> tests/ui/endian_bytes.rs:60:9 + --> tests/ui/endian_bytes.rs:62:9 | LL | usize::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -639,7 +639,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `isize::from_le_bytes` - --> tests/ui/endian_bytes.rs:61:9 + --> tests/ui/endian_bytes.rs:63:9 | LL | isize::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -651,7 +651,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `f32::from_le_bytes` - --> tests/ui/endian_bytes.rs:62:9 + --> tests/ui/endian_bytes.rs:64:9 | LL | f32::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -663,7 +663,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `f64::from_le_bytes` - --> tests/ui/endian_bytes.rs:63:9 + --> tests/ui/endian_bytes.rs:65:9 | LL | f64::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -675,7 +675,7 @@ LL | fn little() { fn_body!(); } = note: this error originates in the macro `fn_body` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:70:9 + --> tests/ui/endian_bytes.rs:72:9 | LL | 2u8.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -687,7 +687,7 @@ LL | fn host_encourage_little() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_ne_bytes` - --> tests/ui/endian_bytes.rs:71:9 + --> tests/ui/endian_bytes.rs:73:9 | LL | u8::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -699,7 +699,7 @@ LL | fn host_encourage_little() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_be_bytes` method - --> tests/ui/endian_bytes.rs:76:9 + --> tests/ui/endian_bytes.rs:78:9 | LL | 2u8.to_be_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -713,7 +713,7 @@ LL | fn host_encourage_little() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_be_bytes` - --> tests/ui/endian_bytes.rs:77:9 + --> tests/ui/endian_bytes.rs:79:9 | LL | u8::from_be_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -725,7 +725,7 @@ LL | fn host_encourage_little() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:70:9 + --> tests/ui/endian_bytes.rs:72:9 | LL | 2u8.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -737,7 +737,7 @@ LL | fn host_encourage_big() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_ne_bytes` - --> tests/ui/endian_bytes.rs:71:9 + --> tests/ui/endian_bytes.rs:73:9 | LL | u8::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -749,7 +749,7 @@ LL | fn host_encourage_big() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_le_bytes` method - --> tests/ui/endian_bytes.rs:73:9 + --> tests/ui/endian_bytes.rs:75:9 | LL | 2u8.to_le_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -761,7 +761,7 @@ LL | fn host_encourage_big() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_le_bytes` - --> tests/ui/endian_bytes.rs:74:9 + --> tests/ui/endian_bytes.rs:76:9 | LL | u8::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -773,7 +773,7 @@ LL | fn host_encourage_big() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:70:9 + --> tests/ui/endian_bytes.rs:72:9 | LL | 2u8.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -784,7 +784,7 @@ LL | fn no_help() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_ne_bytes` - --> tests/ui/endian_bytes.rs:71:9 + --> tests/ui/endian_bytes.rs:73:9 | LL | u8::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -795,7 +795,7 @@ LL | fn no_help() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_le_bytes` method - --> tests/ui/endian_bytes.rs:73:9 + --> tests/ui/endian_bytes.rs:75:9 | LL | 2u8.to_le_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -806,7 +806,7 @@ LL | fn no_help() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_le_bytes` - --> tests/ui/endian_bytes.rs:74:9 + --> tests/ui/endian_bytes.rs:76:9 | LL | u8::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -817,7 +817,7 @@ LL | fn no_help() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_be_bytes` method - --> tests/ui/endian_bytes.rs:76:9 + --> tests/ui/endian_bytes.rs:78:9 | LL | 2u8.to_be_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -828,7 +828,7 @@ LL | fn no_help() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_be_bytes` - --> tests/ui/endian_bytes.rs:77:9 + --> tests/ui/endian_bytes.rs:79:9 | LL | u8::from_be_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -839,7 +839,7 @@ LL | fn no_help() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_le_bytes` method - --> tests/ui/endian_bytes.rs:73:9 + --> tests/ui/endian_bytes.rs:75:9 | LL | 2u8.to_le_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -851,7 +851,7 @@ LL | fn little_encourage_host() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_le_bytes` - --> tests/ui/endian_bytes.rs:74:9 + --> tests/ui/endian_bytes.rs:76:9 | LL | u8::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -863,7 +863,7 @@ LL | fn little_encourage_host() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_be_bytes` method - --> tests/ui/endian_bytes.rs:76:9 + --> tests/ui/endian_bytes.rs:78:9 | LL | 2u8.to_be_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -875,7 +875,7 @@ LL | fn little_encourage_host() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_be_bytes` - --> tests/ui/endian_bytes.rs:77:9 + --> tests/ui/endian_bytes.rs:79:9 | LL | u8::from_be_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -887,7 +887,7 @@ LL | fn little_encourage_host() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:70:9 + --> tests/ui/endian_bytes.rs:72:9 | LL | 2u8.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -899,7 +899,7 @@ LL | fn little_encourage_big() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_ne_bytes` - --> tests/ui/endian_bytes.rs:71:9 + --> tests/ui/endian_bytes.rs:73:9 | LL | u8::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -911,7 +911,7 @@ LL | fn little_encourage_big() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_le_bytes` method - --> tests/ui/endian_bytes.rs:73:9 + --> tests/ui/endian_bytes.rs:75:9 | LL | 2u8.to_le_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -923,7 +923,7 @@ LL | fn little_encourage_big() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_le_bytes` - --> tests/ui/endian_bytes.rs:74:9 + --> tests/ui/endian_bytes.rs:76:9 | LL | u8::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -935,7 +935,7 @@ LL | fn little_encourage_big() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_le_bytes` method - --> tests/ui/endian_bytes.rs:73:9 + --> tests/ui/endian_bytes.rs:75:9 | LL | 2u8.to_le_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -947,7 +947,7 @@ LL | fn big_encourage_host() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_le_bytes` - --> tests/ui/endian_bytes.rs:74:9 + --> tests/ui/endian_bytes.rs:76:9 | LL | u8::from_le_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -959,7 +959,7 @@ LL | fn big_encourage_host() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_be_bytes` method - --> tests/ui/endian_bytes.rs:76:9 + --> tests/ui/endian_bytes.rs:78:9 | LL | 2u8.to_be_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -971,7 +971,7 @@ LL | fn big_encourage_host() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_be_bytes` - --> tests/ui/endian_bytes.rs:77:9 + --> tests/ui/endian_bytes.rs:79:9 | LL | u8::from_be_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -983,7 +983,7 @@ LL | fn big_encourage_host() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_ne_bytes` method - --> tests/ui/endian_bytes.rs:70:9 + --> tests/ui/endian_bytes.rs:72:9 | LL | 2u8.to_ne_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -995,7 +995,7 @@ LL | fn big_encourage_little() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_ne_bytes` - --> tests/ui/endian_bytes.rs:71:9 + --> tests/ui/endian_bytes.rs:73:9 | LL | u8::from_ne_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1007,7 +1007,7 @@ LL | fn big_encourage_little() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `u8::to_be_bytes` method - --> tests/ui/endian_bytes.rs:76:9 + --> tests/ui/endian_bytes.rs:78:9 | LL | 2u8.to_be_bytes(); | ^^^^^^^^^^^^^^^^^ @@ -1019,7 +1019,7 @@ LL | fn big_encourage_little() { fn_body_smol!(); } = note: this error originates in the macro `fn_body_smol` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the function `u8::from_be_bytes` - --> tests/ui/endian_bytes.rs:77:9 + --> tests/ui/endian_bytes.rs:79:9 | LL | u8::from_be_bytes(todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/float_cmp.rs b/src/tools/clippy/tests/ui/float_cmp.rs index 5057c6437329..1923ad7c677e 100644 --- a/src/tools/clippy/tests/ui/float_cmp.rs +++ b/src/tools/clippy/tests/ui/float_cmp.rs @@ -1,3 +1,5 @@ +// FIXME(f16_f128): const casting is not yet supported for these types. Add when available. + #![warn(clippy::float_cmp)] #![allow( unused, diff --git a/src/tools/clippy/tests/ui/float_cmp.stderr b/src/tools/clippy/tests/ui/float_cmp.stderr index 49b65184f731..c8a0bde6e63a 100644 --- a/src/tools/clippy/tests/ui/float_cmp.stderr +++ b/src/tools/clippy/tests/ui/float_cmp.stderr @@ -1,5 +1,5 @@ error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:70:5 + --> tests/ui/float_cmp.rs:72:5 | LL | ONE as f64 != 2.0; | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` @@ -9,7 +9,7 @@ LL | ONE as f64 != 2.0; = help: to override `-D warnings` add `#[allow(clippy::float_cmp)]` error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:77:5 + --> tests/ui/float_cmp.rs:79:5 | LL | x == 1.0; | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` @@ -17,7 +17,7 @@ LL | x == 1.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:82:5 + --> tests/ui/float_cmp.rs:84:5 | LL | twice(x) != twice(ONE as f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` @@ -25,7 +25,7 @@ LL | twice(x) != twice(ONE as f64); = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:104:5 + --> tests/ui/float_cmp.rs:106:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` @@ -33,7 +33,7 @@ LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays - --> tests/ui/float_cmp.rs:111:5 + --> tests/ui/float_cmp.rs:113:5 | LL | a1 == a2; | ^^^^^^^^ @@ -41,7 +41,7 @@ LL | a1 == a2; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:114:5 + --> tests/ui/float_cmp.rs:116:5 | LL | a1[0] == a2[0]; | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` diff --git a/src/tools/clippy/tests/ui/float_equality_without_abs.rs b/src/tools/clippy/tests/ui/float_equality_without_abs.rs index 6363472d8990..2b67c8bec103 100644 --- a/src/tools/clippy/tests/ui/float_equality_without_abs.rs +++ b/src/tools/clippy/tests/ui/float_equality_without_abs.rs @@ -1,5 +1,8 @@ #![warn(clippy::float_equality_without_abs)] //@no-rustfix + +// FIXME(f16_f128): add tests for these types when abs is available + pub fn is_roughly_equal(a: f32, b: f32) -> bool { (a - b) < f32::EPSILON //~^ ERROR: float equality check without `.abs()` diff --git a/src/tools/clippy/tests/ui/float_equality_without_abs.stderr b/src/tools/clippy/tests/ui/float_equality_without_abs.stderr index 0124dd983a39..cdaaf0cdbcf7 100644 --- a/src/tools/clippy/tests/ui/float_equality_without_abs.stderr +++ b/src/tools/clippy/tests/ui/float_equality_without_abs.stderr @@ -1,5 +1,5 @@ error: float equality check without `.abs()` - --> tests/ui/float_equality_without_abs.rs:4:5 + --> tests/ui/float_equality_without_abs.rs:7:5 | LL | (a - b) < f32::EPSILON | -------^^^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | (a - b) < f32::EPSILON = help: to override `-D warnings` add `#[allow(clippy::float_equality_without_abs)]` error: float equality check without `.abs()` - --> tests/ui/float_equality_without_abs.rs:15:13 + --> tests/ui/float_equality_without_abs.rs:18:13 | LL | let _ = (a - b) < f32::EPSILON; | -------^^^^^^^^^^^^^^^ @@ -18,7 +18,7 @@ LL | let _ = (a - b) < f32::EPSILON; | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` - --> tests/ui/float_equality_without_abs.rs:17:13 + --> tests/ui/float_equality_without_abs.rs:20:13 | LL | let _ = a - b < f32::EPSILON; | -----^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | let _ = a - b < f32::EPSILON; | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` - --> tests/ui/float_equality_without_abs.rs:19:13 + --> tests/ui/float_equality_without_abs.rs:22:13 | LL | let _ = a - b.abs() < f32::EPSILON; | -----------^^^^^^^^^^^^^^^ @@ -34,7 +34,7 @@ LL | let _ = a - b.abs() < f32::EPSILON; | help: add `.abs()`: `(a - b.abs()).abs()` error: float equality check without `.abs()` - --> tests/ui/float_equality_without_abs.rs:21:13 + --> tests/ui/float_equality_without_abs.rs:24:13 | LL | let _ = (a as f64 - b as f64) < f64::EPSILON; | ---------------------^^^^^^^^^^^^^^^ @@ -42,7 +42,7 @@ LL | let _ = (a as f64 - b as f64) < f64::EPSILON; | help: add `.abs()`: `(a as f64 - b as f64).abs()` error: float equality check without `.abs()` - --> tests/ui/float_equality_without_abs.rs:23:13 + --> tests/ui/float_equality_without_abs.rs:26:13 | LL | let _ = 1.0 - 2.0 < f32::EPSILON; | ---------^^^^^^^^^^^^^^^ @@ -50,7 +50,7 @@ LL | let _ = 1.0 - 2.0 < f32::EPSILON; | help: add `.abs()`: `(1.0 - 2.0).abs()` error: float equality check without `.abs()` - --> tests/ui/float_equality_without_abs.rs:26:13 + --> tests/ui/float_equality_without_abs.rs:29:13 | LL | let _ = f32::EPSILON > (a - b); | ^^^^^^^^^^^^^^^------- @@ -58,7 +58,7 @@ LL | let _ = f32::EPSILON > (a - b); | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` - --> tests/ui/float_equality_without_abs.rs:28:13 + --> tests/ui/float_equality_without_abs.rs:31:13 | LL | let _ = f32::EPSILON > a - b; | ^^^^^^^^^^^^^^^----- @@ -66,7 +66,7 @@ LL | let _ = f32::EPSILON > a - b; | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` - --> tests/ui/float_equality_without_abs.rs:30:13 + --> tests/ui/float_equality_without_abs.rs:33:13 | LL | let _ = f32::EPSILON > a - b.abs(); | ^^^^^^^^^^^^^^^----------- @@ -74,7 +74,7 @@ LL | let _ = f32::EPSILON > a - b.abs(); | help: add `.abs()`: `(a - b.abs()).abs()` error: float equality check without `.abs()` - --> tests/ui/float_equality_without_abs.rs:32:13 + --> tests/ui/float_equality_without_abs.rs:35:13 | LL | let _ = f64::EPSILON > (a as f64 - b as f64); | ^^^^^^^^^^^^^^^--------------------- @@ -82,7 +82,7 @@ LL | let _ = f64::EPSILON > (a as f64 - b as f64); | help: add `.abs()`: `(a as f64 - b as f64).abs()` error: float equality check without `.abs()` - --> tests/ui/float_equality_without_abs.rs:34:13 + --> tests/ui/float_equality_without_abs.rs:37:13 | LL | let _ = f32::EPSILON > 1.0 - 2.0; | ^^^^^^^^^^^^^^^--------- diff --git a/src/tools/clippy/tests/ui/floating_point_arithmetic_nostd.rs b/src/tools/clippy/tests/ui/floating_point_arithmetic_nostd.rs index a42c6383ccea..47c113d61c04 100644 --- a/src/tools/clippy/tests/ui/floating_point_arithmetic_nostd.rs +++ b/src/tools/clippy/tests/ui/floating_point_arithmetic_nostd.rs @@ -3,8 +3,8 @@ #![warn(clippy::suboptimal_flops)] #![no_std] -// The following should not lint, as the suggested methods {f32,f64}.mul_add() -// and {f32,f64}::abs() are not available in no_std +// The following should not lint, as the suggested methods `{f16,f32,f64,f128}.mul_add()` +// and ``{f16,f32,f64,f128}::abs()` are not available in no_std pub fn mul_add() { let a: f64 = 1234.567; diff --git a/src/tools/clippy/tests/ui/floating_point_exp.fixed b/src/tools/clippy/tests/ui/floating_point_exp.fixed index 15072bb1ee9a..fbd91cbc9d70 100644 --- a/src/tools/clippy/tests/ui/floating_point_exp.fixed +++ b/src/tools/clippy/tests/ui/floating_point_exp.fixed @@ -1,3 +1,5 @@ +// FIXME(f16_f128): add tests when exp is available + #![warn(clippy::imprecise_flops)] #![allow(clippy::unnecessary_cast)] diff --git a/src/tools/clippy/tests/ui/floating_point_exp.rs b/src/tools/clippy/tests/ui/floating_point_exp.rs index 7d8b17946d0a..340bacaf56b5 100644 --- a/src/tools/clippy/tests/ui/floating_point_exp.rs +++ b/src/tools/clippy/tests/ui/floating_point_exp.rs @@ -1,3 +1,5 @@ +// FIXME(f16_f128): add tests when exp is available + #![warn(clippy::imprecise_flops)] #![allow(clippy::unnecessary_cast)] diff --git a/src/tools/clippy/tests/ui/floating_point_exp.stderr b/src/tools/clippy/tests/ui/floating_point_exp.stderr index a19edf87e244..6ce67254abc9 100644 --- a/src/tools/clippy/tests/ui/floating_point_exp.stderr +++ b/src/tools/clippy/tests/ui/floating_point_exp.stderr @@ -1,5 +1,5 @@ error: (e.pow(x) - 1) can be computed more accurately - --> tests/ui/floating_point_exp.rs:6:13 + --> tests/ui/floating_point_exp.rs:8:13 | LL | let _ = x.exp() - 1.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` @@ -8,25 +8,25 @@ LL | let _ = x.exp() - 1.0; = help: to override `-D warnings` add `#[allow(clippy::imprecise_flops)]` error: (e.pow(x) - 1) can be computed more accurately - --> tests/ui/floating_point_exp.rs:7:13 + --> tests/ui/floating_point_exp.rs:9:13 | LL | let _ = x.exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> tests/ui/floating_point_exp.rs:8:13 + --> tests/ui/floating_point_exp.rs:10:13 | LL | let _ = (x as f32).exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> tests/ui/floating_point_exp.rs:14:13 + --> tests/ui/floating_point_exp.rs:16:13 | LL | let _ = x.exp() - 1.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> tests/ui/floating_point_exp.rs:15:13 + --> tests/ui/floating_point_exp.rs:17:13 | LL | let _ = x.exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` diff --git a/src/tools/clippy/tests/ui/floating_point_log.fixed b/src/tools/clippy/tests/ui/floating_point_log.fixed index 15cc47eef0dd..75e9c40a5218 100644 --- a/src/tools/clippy/tests/ui/floating_point_log.fixed +++ b/src/tools/clippy/tests/ui/floating_point_log.fixed @@ -1,6 +1,8 @@ #![allow(dead_code, clippy::double_parens, clippy::unnecessary_cast)] #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +// FIXME(f16_f128): add tests for these types once math functions are available + const TWO: f32 = 2.0; const E: f32 = std::f32::consts::E; diff --git a/src/tools/clippy/tests/ui/floating_point_log.rs b/src/tools/clippy/tests/ui/floating_point_log.rs index 1241af828593..d68369a38618 100644 --- a/src/tools/clippy/tests/ui/floating_point_log.rs +++ b/src/tools/clippy/tests/ui/floating_point_log.rs @@ -1,6 +1,8 @@ #![allow(dead_code, clippy::double_parens, clippy::unnecessary_cast)] #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +// FIXME(f16_f128): add tests for these types once math functions are available + const TWO: f32 = 2.0; const E: f32 = std::f32::consts::E; diff --git a/src/tools/clippy/tests/ui/floating_point_log.stderr b/src/tools/clippy/tests/ui/floating_point_log.stderr index 3a449a98eadd..19c28de8e39a 100644 --- a/src/tools/clippy/tests/ui/floating_point_log.stderr +++ b/src/tools/clippy/tests/ui/floating_point_log.stderr @@ -1,5 +1,5 @@ error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:9:13 + --> tests/ui/floating_point_log.rs:11:13 | LL | let _ = x.log(2f32); | ^^^^^^^^^^^ help: consider using: `x.log2()` @@ -8,55 +8,55 @@ LL | let _ = x.log(2f32); = help: to override `-D warnings` add `#[allow(clippy::suboptimal_flops)]` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:10:13 + --> tests/ui/floating_point_log.rs:12:13 | LL | let _ = x.log(10f32); | ^^^^^^^^^^^^ help: consider using: `x.log10()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:11:13 + --> tests/ui/floating_point_log.rs:13:13 | LL | let _ = x.log(std::f32::consts::E); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:12:13 + --> tests/ui/floating_point_log.rs:14:13 | LL | let _ = x.log(TWO); | ^^^^^^^^^^ help: consider using: `x.log2()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:13:13 + --> tests/ui/floating_point_log.rs:15:13 | LL | let _ = x.log(E); | ^^^^^^^^ help: consider using: `x.ln()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:14:13 + --> tests/ui/floating_point_log.rs:16:13 | LL | let _ = (x as f32).log(2f32); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log2()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:17:13 + --> tests/ui/floating_point_log.rs:19:13 | LL | let _ = x.log(2f64); | ^^^^^^^^^^^ help: consider using: `x.log2()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:18:13 + --> tests/ui/floating_point_log.rs:20:13 | LL | let _ = x.log(10f64); | ^^^^^^^^^^^^ help: consider using: `x.log10()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:19:13 + --> tests/ui/floating_point_log.rs:21:13 | LL | let _ = x.log(std::f64::consts::E); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:24:13 + --> tests/ui/floating_point_log.rs:26:13 | LL | let _ = (1f32 + 2.).ln(); | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` @@ -65,115 +65,115 @@ LL | let _ = (1f32 + 2.).ln(); = help: to override `-D warnings` add `#[allow(clippy::imprecise_flops)]` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:25:13 + --> tests/ui/floating_point_log.rs:27:13 | LL | let _ = (1f32 + 2.0).ln(); | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:26:13 + --> tests/ui/floating_point_log.rs:28:13 | LL | let _ = (1.0 + x).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:27:13 + --> tests/ui/floating_point_log.rs:29:13 | LL | let _ = (1.0 + x / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:28:13 + --> tests/ui/floating_point_log.rs:30:13 | LL | let _ = (1.0 + x.powi(3)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:29:13 + --> tests/ui/floating_point_log.rs:31:13 | LL | let _ = (1.0 + x.powi(3) / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:30:13 + --> tests/ui/floating_point_log.rs:32:13 | LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(std::f32::consts::E - 1.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:31:13 + --> tests/ui/floating_point_log.rs:33:13 | LL | let _ = (x + 1.0).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:32:13 + --> tests/ui/floating_point_log.rs:34:13 | LL | let _ = (x.powi(3) + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:33:13 + --> tests/ui/floating_point_log.rs:35:13 | LL | let _ = (x + 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:34:13 + --> tests/ui/floating_point_log.rs:36:13 | LL | let _ = (x / 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:42:13 + --> tests/ui/floating_point_log.rs:44:13 | LL | let _ = (1f64 + 2.).ln(); | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:43:13 + --> tests/ui/floating_point_log.rs:45:13 | LL | let _ = (1f64 + 2.0).ln(); | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:44:13 + --> tests/ui/floating_point_log.rs:46:13 | LL | let _ = (1.0 + x).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:45:13 + --> tests/ui/floating_point_log.rs:47:13 | LL | let _ = (1.0 + x / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:46:13 + --> tests/ui/floating_point_log.rs:48:13 | LL | let _ = (1.0 + x.powi(3)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:47:13 + --> tests/ui/floating_point_log.rs:49:13 | LL | let _ = (x + 1.0).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:48:13 + --> tests/ui/floating_point_log.rs:50:13 | LL | let _ = (x.powi(3) + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:49:13 + --> tests/ui/floating_point_log.rs:51:13 | LL | let _ = (x + 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:50:13 + --> tests/ui/floating_point_log.rs:52:13 | LL | let _ = (x / 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` diff --git a/src/tools/clippy/tests/ui/floating_point_powf.fixed b/src/tools/clippy/tests/ui/floating_point_powf.fixed index c2884ca318c6..a44938fdf692 100644 --- a/src/tools/clippy/tests/ui/floating_point_powf.fixed +++ b/src/tools/clippy/tests/ui/floating_point_powf.fixed @@ -1,6 +1,8 @@ #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] #![allow(clippy::unnecessary_cast)] +// FIXME(f16_f128): add tests for these types when `powf` is available + fn main() { let x = 3f32; let _ = x.exp2(); diff --git a/src/tools/clippy/tests/ui/floating_point_powf.rs b/src/tools/clippy/tests/ui/floating_point_powf.rs index 37d58af05513..80f6c1791d7c 100644 --- a/src/tools/clippy/tests/ui/floating_point_powf.rs +++ b/src/tools/clippy/tests/ui/floating_point_powf.rs @@ -1,6 +1,8 @@ #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] #![allow(clippy::unnecessary_cast)] +// FIXME(f16_f128): add tests for these types when `powf` is available + fn main() { let x = 3f32; let _ = 2f32.powf(x); diff --git a/src/tools/clippy/tests/ui/floating_point_powf.stderr b/src/tools/clippy/tests/ui/floating_point_powf.stderr index bd3fa771fe50..671383401b55 100644 --- a/src/tools/clippy/tests/ui/floating_point_powf.stderr +++ b/src/tools/clippy/tests/ui/floating_point_powf.stderr @@ -1,5 +1,5 @@ error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:6:13 + --> tests/ui/floating_point_powf.rs:8:13 | LL | let _ = 2f32.powf(x); | ^^^^^^^^^^^^ help: consider using: `x.exp2()` @@ -8,43 +8,43 @@ LL | let _ = 2f32.powf(x); = help: to override `-D warnings` add `#[allow(clippy::suboptimal_flops)]` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:7:13 + --> tests/ui/floating_point_powf.rs:9:13 | LL | let _ = 2f32.powf(3.1); | ^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:8:13 + --> tests/ui/floating_point_powf.rs:10:13 | LL | let _ = 2f32.powf(-3.1); | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp2()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:9:13 + --> tests/ui/floating_point_powf.rs:11:13 | LL | let _ = std::f32::consts::E.powf(x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:10:13 + --> tests/ui/floating_point_powf.rs:12:13 | LL | let _ = std::f32::consts::E.powf(3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:11:13 + --> tests/ui/floating_point_powf.rs:13:13 | LL | let _ = std::f32::consts::E.powf(-3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp()` error: square-root of a number can be computed more efficiently and accurately - --> tests/ui/floating_point_powf.rs:12:13 + --> tests/ui/floating_point_powf.rs:14:13 | LL | let _ = x.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` error: cube-root of a number can be computed more accurately - --> tests/ui/floating_point_powf.rs:13:13 + --> tests/ui/floating_point_powf.rs:15:13 | LL | let _ = x.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` @@ -53,139 +53,139 @@ LL | let _ = x.powf(1.0 / 3.0); = help: to override `-D warnings` add `#[allow(clippy::imprecise_flops)]` error: cube-root of a number can be computed more accurately - --> tests/ui/floating_point_powf.rs:14:13 + --> tests/ui/floating_point_powf.rs:16:13 | LL | let _ = (x as f32).powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).cbrt()` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:15:13 + --> tests/ui/floating_point_powf.rs:17:13 | LL | let _ = x.powf(3.0); | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:16:13 + --> tests/ui/floating_point_powf.rs:18:13 | LL | let _ = x.powf(-2.0); | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:17:13 + --> tests/ui/floating_point_powf.rs:19:13 | LL | let _ = x.powf(16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:18:13 + --> tests/ui/floating_point_powf.rs:20:13 | LL | let _ = x.powf(-16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:19:13 + --> tests/ui/floating_point_powf.rs:21:13 | LL | let _ = (x as f32).powf(-16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(-16_777_215)` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:20:13 + --> tests/ui/floating_point_powf.rs:22:13 | LL | let _ = (x as f32).powf(3.0); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(3)` error: cube-root of a number can be computed more accurately - --> tests/ui/floating_point_powf.rs:21:13 + --> tests/ui/floating_point_powf.rs:23:13 | LL | let _ = (1.5_f32 + 1.0).powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(1.5_f32 + 1.0).cbrt()` error: cube-root of a number can be computed more accurately - --> tests/ui/floating_point_powf.rs:22:13 + --> tests/ui/floating_point_powf.rs:24:13 | LL | let _ = 1.5_f64.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.cbrt()` error: square-root of a number can be computed more efficiently and accurately - --> tests/ui/floating_point_powf.rs:23:13 + --> tests/ui/floating_point_powf.rs:25:13 | LL | let _ = 1.5_f64.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.sqrt()` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:24:13 + --> tests/ui/floating_point_powf.rs:26:13 | LL | let _ = 1.5_f64.powf(3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.powi(3)` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:33:13 + --> tests/ui/floating_point_powf.rs:35:13 | LL | let _ = 2f64.powf(x); | ^^^^^^^^^^^^ help: consider using: `x.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:34:13 + --> tests/ui/floating_point_powf.rs:36:13 | LL | let _ = 2f64.powf(3.1); | ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:35:13 + --> tests/ui/floating_point_powf.rs:37:13 | LL | let _ = 2f64.powf(-3.1); | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:36:13 + --> tests/ui/floating_point_powf.rs:38:13 | LL | let _ = std::f64::consts::E.powf(x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:37:13 + --> tests/ui/floating_point_powf.rs:39:13 | LL | let _ = std::f64::consts::E.powf(3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()` error: exponent for bases 2 and e can be computed more accurately - --> tests/ui/floating_point_powf.rs:38:13 + --> tests/ui/floating_point_powf.rs:40:13 | LL | let _ = std::f64::consts::E.powf(-3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()` error: square-root of a number can be computed more efficiently and accurately - --> tests/ui/floating_point_powf.rs:39:13 + --> tests/ui/floating_point_powf.rs:41:13 | LL | let _ = x.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` error: cube-root of a number can be computed more accurately - --> tests/ui/floating_point_powf.rs:40:13 + --> tests/ui/floating_point_powf.rs:42:13 | LL | let _ = x.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:41:13 + --> tests/ui/floating_point_powf.rs:43:13 | LL | let _ = x.powf(3.0); | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:42:13 + --> tests/ui/floating_point_powf.rs:44:13 | LL | let _ = x.powf(-2.0); | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:43:13 + --> tests/ui/floating_point_powf.rs:45:13 | LL | let _ = x.powf(-2_147_483_648.0); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)` error: exponentiation with integer powers can be computed more efficiently - --> tests/ui/floating_point_powf.rs:44:13 + --> tests/ui/floating_point_powf.rs:46:13 | LL | let _ = x.powf(2_147_483_647.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)` diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.fixed b/src/tools/clippy/tests/ui/lossy_float_literal.fixed index 92a0084a6aeb..cc8c0b4a0d17 100644 --- a/src/tools/clippy/tests/ui/lossy_float_literal.fixed +++ b/src/tools/clippy/tests/ui/lossy_float_literal.fixed @@ -1,31 +1,55 @@ #![warn(clippy::lossy_float_literal)] #![allow(overflowing_literals, unused)] +#![feature(f128)] +#![feature(f16)] fn main() { // Lossy whole-number float literals + let _: f16 = 4_097.0; + let _: f16 = 4_097.; + let _: f16 = 4_097.000; + let _ = 4_097f16; + let _: f16 = -4_097.0; + let _: f32 = 16_777_216.0; let _: f32 = 16_777_220.0; let _: f32 = 16_777_220.0; let _: f32 = 16_777_220.0; let _ = 16_777_220_f32; let _: f32 = -16_777_220.0; + let _: f64 = 9_007_199_254_740_992.0; let _: f64 = 9_007_199_254_740_992.0; let _: f64 = 9_007_199_254_740_992.0; let _ = 9_007_199_254_740_992_f64; let _: f64 = -9_007_199_254_740_992.0; + let _: f128 = 10_384_593_717_069_655_257_060_992_658_440_193.0; + let _: f128 = 10_384_593_717_069_655_257_060_992_658_440_193.; + let _: f128 = 10_384_593_717_069_655_257_060_992_658_440_193.00; + let _ = 10_384_593_717_069_655_257_060_992_658_440_193f128; + let _: f128 = -10_384_593_717_069_655_257_060_992_658_440_193.0; + // Lossless whole number float literals + let _: f16 = 4_096.0; + let _: f16 = -4_096.0; + let _: f32 = 16_777_216.0; let _: f32 = 16_777_218.0; let _: f32 = 16_777_220.0; let _: f32 = -16_777_216.0; let _: f32 = -16_777_220.0; + let _: f64 = 16_777_217.0; let _: f64 = -16_777_217.0; let _: f64 = 9_007_199_254_740_992.0; let _: f64 = -9_007_199_254_740_992.0; + let _: f128 = 9_007_199_254_740_993.0; + let _: f128 = -9_007_199_254_740_993.0; + let _: f128 = 10_384_593_717_069_655_257_060_992_658_440_192.0; + let _: f128 = -10_384_593_717_069_655_257_060_992_658_440_192.0; + // Ignored whole number float literals let _: f32 = 1e25; let _: f32 = 1E25; diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.rs b/src/tools/clippy/tests/ui/lossy_float_literal.rs index 5abef3c442ec..c84eef396d58 100644 --- a/src/tools/clippy/tests/ui/lossy_float_literal.rs +++ b/src/tools/clippy/tests/ui/lossy_float_literal.rs @@ -1,31 +1,55 @@ #![warn(clippy::lossy_float_literal)] #![allow(overflowing_literals, unused)] +#![feature(f128)] +#![feature(f16)] fn main() { // Lossy whole-number float literals + let _: f16 = 4_097.0; + let _: f16 = 4_097.; + let _: f16 = 4_097.000; + let _ = 4_097f16; + let _: f16 = -4_097.0; + let _: f32 = 16_777_217.0; let _: f32 = 16_777_219.0; let _: f32 = 16_777_219.; let _: f32 = 16_777_219.000; let _ = 16_777_219f32; let _: f32 = -16_777_219.0; + let _: f64 = 9_007_199_254_740_993.0; let _: f64 = 9_007_199_254_740_993.; let _: f64 = 9_007_199_254_740_993.00; let _ = 9_007_199_254_740_993f64; let _: f64 = -9_007_199_254_740_993.0; + let _: f128 = 10_384_593_717_069_655_257_060_992_658_440_193.0; + let _: f128 = 10_384_593_717_069_655_257_060_992_658_440_193.; + let _: f128 = 10_384_593_717_069_655_257_060_992_658_440_193.00; + let _ = 10_384_593_717_069_655_257_060_992_658_440_193f128; + let _: f128 = -10_384_593_717_069_655_257_060_992_658_440_193.0; + // Lossless whole number float literals + let _: f16 = 4_096.0; + let _: f16 = -4_096.0; + let _: f32 = 16_777_216.0; let _: f32 = 16_777_218.0; let _: f32 = 16_777_220.0; let _: f32 = -16_777_216.0; let _: f32 = -16_777_220.0; + let _: f64 = 16_777_217.0; let _: f64 = -16_777_217.0; let _: f64 = 9_007_199_254_740_992.0; let _: f64 = -9_007_199_254_740_992.0; + let _: f128 = 9_007_199_254_740_993.0; + let _: f128 = -9_007_199_254_740_993.0; + let _: f128 = 10_384_593_717_069_655_257_060_992_658_440_192.0; + let _: f128 = -10_384_593_717_069_655_257_060_992_658_440_192.0; + // Ignored whole number float literals let _: f32 = 1e25; let _: f32 = 1E25; diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.stderr b/src/tools/clippy/tests/ui/lossy_float_literal.stderr index 7904719141c1..b5a07418734c 100644 --- a/src/tools/clippy/tests/ui/lossy_float_literal.stderr +++ b/src/tools/clippy/tests/ui/lossy_float_literal.stderr @@ -1,5 +1,5 @@ error: literal cannot be represented as the underlying type without loss of precision - --> tests/ui/lossy_float_literal.rs:6:18 + --> tests/ui/lossy_float_literal.rs:14:18 | LL | let _: f32 = 16_777_217.0; | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_216.0` @@ -8,61 +8,61 @@ LL | let _: f32 = 16_777_217.0; = help: to override `-D warnings` add `#[allow(clippy::lossy_float_literal)]` error: literal cannot be represented as the underlying type without loss of precision - --> tests/ui/lossy_float_literal.rs:7:18 + --> tests/ui/lossy_float_literal.rs:15:18 | LL | let _: f32 = 16_777_219.0; | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` error: literal cannot be represented as the underlying type without loss of precision - --> tests/ui/lossy_float_literal.rs:8:18 + --> tests/ui/lossy_float_literal.rs:16:18 | LL | let _: f32 = 16_777_219.; | ^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` error: literal cannot be represented as the underlying type without loss of precision - --> tests/ui/lossy_float_literal.rs:9:18 + --> tests/ui/lossy_float_literal.rs:17:18 | LL | let _: f32 = 16_777_219.000; | ^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` error: literal cannot be represented as the underlying type without loss of precision - --> tests/ui/lossy_float_literal.rs:10:13 + --> tests/ui/lossy_float_literal.rs:18:13 | LL | let _ = 16_777_219f32; | ^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220_f32` error: literal cannot be represented as the underlying type without loss of precision - --> tests/ui/lossy_float_literal.rs:11:19 + --> tests/ui/lossy_float_literal.rs:19:19 | LL | let _: f32 = -16_777_219.0; | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` error: literal cannot be represented as the underlying type without loss of precision - --> tests/ui/lossy_float_literal.rs:12:18 + --> tests/ui/lossy_float_literal.rs:21:18 | LL | let _: f64 = 9_007_199_254_740_993.0; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` error: literal cannot be represented as the underlying type without loss of precision - --> tests/ui/lossy_float_literal.rs:13:18 + --> tests/ui/lossy_float_literal.rs:22:18 | LL | let _: f64 = 9_007_199_254_740_993.; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` error: literal cannot be represented as the underlying type without loss of precision - --> tests/ui/lossy_float_literal.rs:14:18 + --> tests/ui/lossy_float_literal.rs:23:18 | LL | let _: f64 = 9_007_199_254_740_993.00; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` error: literal cannot be represented as the underlying type without loss of precision - --> tests/ui/lossy_float_literal.rs:15:13 + --> tests/ui/lossy_float_literal.rs:24:13 | LL | let _ = 9_007_199_254_740_993f64; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992_f64` error: literal cannot be represented as the underlying type without loss of precision - --> tests/ui/lossy_float_literal.rs:16:19 + --> tests/ui/lossy_float_literal.rs:25:19 | LL | let _: f64 = -9_007_199_254_740_993.0; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` diff --git a/src/tools/clippy/tests/ui/manual_float_methods.rs b/src/tools/clippy/tests/ui/manual_float_methods.rs index 80781ecda721..ee3daa12834b 100644 --- a/src/tools/clippy/tests/ui/manual_float_methods.rs +++ b/src/tools/clippy/tests/ui/manual_float_methods.rs @@ -3,6 +3,8 @@ #![allow(clippy::needless_if, unused)] #![warn(clippy::manual_is_infinite, clippy::manual_is_finite)] +// FIXME(f16_f128): add tests for these types once constants are available + #[macro_use] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/manual_float_methods.stderr b/src/tools/clippy/tests/ui/manual_float_methods.stderr index 930df0b97cb3..70057620a4a8 100644 --- a/src/tools/clippy/tests/ui/manual_float_methods.stderr +++ b/src/tools/clippy/tests/ui/manual_float_methods.stderr @@ -1,5 +1,5 @@ error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:22:8 + --> tests/ui/manual_float_methods.rs:24:8 | LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` @@ -8,7 +8,7 @@ LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {} = help: to override `-D warnings` add `#[allow(clippy::manual_is_infinite)]` error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:23:8 + --> tests/ui/manual_float_methods.rs:25:8 | LL | if x != f32::INFINITY && x != f32::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,13 +29,13 @@ LL | if !x.is_infinite() {} | ~~~~~~~~~~~~~~~~ error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:24:8 + --> tests/ui/manual_float_methods.rs:26:8 | LL | if x == INFINITE || x == NEG_INFINITE {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:25:8 + --> tests/ui/manual_float_methods.rs:27:8 | LL | if x != INFINITE && x != NEG_INFINITE {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -54,13 +54,13 @@ LL | if !x.is_infinite() {} | ~~~~~~~~~~~~~~~~ error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:27:8 + --> tests/ui/manual_float_methods.rs:29:8 | LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:28:8 + --> tests/ui/manual_float_methods.rs:30:8 | LL | if x != f64::INFINITY && x != f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs index 37895ea09e72..a5b63bed531b 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs @@ -1,3 +1,5 @@ +#![feature(f128)] +#![feature(f16)] #![warn(clippy::modulo_arithmetic)] #![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] @@ -16,6 +18,19 @@ fn main() { //~^ ERROR: you are using modulo operator on constants with different signs: `3.400 % //~| NOTE: double check for expected result especially when interoperating with differ + // Lint on floating point numbers + let a_f16: f16 = -1.6; + let mut b_f16: f16 = 2.1; + a_f16 % b_f16; + //~^ ERROR: you are using modulo operator on types that might have different signs + //~| NOTE: double check for expected result especially when interoperating with differ + b_f16 % a_f16; + //~^ ERROR: you are using modulo operator on types that might have different signs + //~| NOTE: double check for expected result especially when interoperating with differ + b_f16 %= a_f16; + //~^ ERROR: you are using modulo operator on types that might have different signs + //~| NOTE: double check for expected result especially when interoperating with differ + // Lint on floating point numbers let a_f32: f32 = -1.6; let mut b_f32: f32 = 2.1; @@ -41,6 +56,18 @@ fn main() { //~^ ERROR: you are using modulo operator on types that might have different signs //~| NOTE: double check for expected result especially when interoperating with differ + let a_f128: f128 = -1.6; + let mut b_f128: f128 = 2.1; + a_f128 % b_f128; + //~^ ERROR: you are using modulo operator on types that might have different signs + //~| NOTE: double check for expected result especially when interoperating with differ + b_f128 % a_f128; + //~^ ERROR: you are using modulo operator on types that might have different signs + //~| NOTE: double check for expected result especially when interoperating with differ + b_f128 %= a_f128; + //~^ ERROR: you are using modulo operator on types that might have different signs + //~| NOTE: double check for expected result especially when interoperating with differ + // No lint when both sides are const and of the same sign 1.6 % 2.1; -1.6 % -2.1; diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr index fa3a64cfb714..2b4937552bda 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr @@ -1,5 +1,5 @@ error: you are using modulo operator on constants with different signs: `-1.600 % 2.100` - --> tests/ui/modulo_arithmetic_float.rs:6:5 + --> tests/ui/modulo_arithmetic_float.rs:8:5 | LL | -1.6 % 2.1; | ^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | -1.6 % 2.1; = help: to override `-D warnings` add `#[allow(clippy::modulo_arithmetic)]` error: you are using modulo operator on constants with different signs: `1.600 % -2.100` - --> tests/ui/modulo_arithmetic_float.rs:9:5 + --> tests/ui/modulo_arithmetic_float.rs:11:5 | LL | 1.6 % -2.1; | ^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | 1.6 % -2.1; = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on constants with different signs: `-1.200 % 3.400` - --> tests/ui/modulo_arithmetic_float.rs:12:5 + --> tests/ui/modulo_arithmetic_float.rs:14:5 | LL | (1.1 - 2.3) % (1.1 + 2.3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | (1.1 - 2.3) % (1.1 + 2.3); = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on constants with different signs: `3.400 % -1.200` - --> tests/ui/modulo_arithmetic_float.rs:15:5 + --> tests/ui/modulo_arithmetic_float.rs:17:5 | LL | (1.1 + 2.3) % (1.1 - 2.3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,31 @@ LL | (1.1 + 2.3) % (1.1 - 2.3); = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on types that might have different signs - --> tests/ui/modulo_arithmetic_float.rs:22:5 + --> tests/ui/modulo_arithmetic_float.rs:24:5 + | +LL | a_f16 % b_f16; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> tests/ui/modulo_arithmetic_float.rs:27:5 + | +LL | b_f16 % a_f16; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> tests/ui/modulo_arithmetic_float.rs:30:5 + | +LL | b_f16 %= a_f16; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> tests/ui/modulo_arithmetic_float.rs:37:5 | LL | a_f32 % b_f32; | ^^^^^^^^^^^^^ @@ -41,7 +65,7 @@ LL | a_f32 % b_f32; = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on types that might have different signs - --> tests/ui/modulo_arithmetic_float.rs:25:5 + --> tests/ui/modulo_arithmetic_float.rs:40:5 | LL | b_f32 % a_f32; | ^^^^^^^^^^^^^ @@ -49,7 +73,7 @@ LL | b_f32 % a_f32; = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on types that might have different signs - --> tests/ui/modulo_arithmetic_float.rs:28:5 + --> tests/ui/modulo_arithmetic_float.rs:43:5 | LL | b_f32 %= a_f32; | ^^^^^^^^^^^^^^ @@ -57,7 +81,7 @@ LL | b_f32 %= a_f32; = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on types that might have different signs - --> tests/ui/modulo_arithmetic_float.rs:34:5 + --> tests/ui/modulo_arithmetic_float.rs:49:5 | LL | a_f64 % b_f64; | ^^^^^^^^^^^^^ @@ -65,7 +89,7 @@ LL | a_f64 % b_f64; = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on types that might have different signs - --> tests/ui/modulo_arithmetic_float.rs:37:5 + --> tests/ui/modulo_arithmetic_float.rs:52:5 | LL | b_f64 % a_f64; | ^^^^^^^^^^^^^ @@ -73,12 +97,36 @@ LL | b_f64 % a_f64; = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on types that might have different signs - --> tests/ui/modulo_arithmetic_float.rs:40:5 + --> tests/ui/modulo_arithmetic_float.rs:55:5 | LL | b_f64 %= a_f64; | ^^^^^^^^^^^^^^ | = note: double check for expected result especially when interoperating with different languages -error: aborting due to 10 previous errors +error: you are using modulo operator on types that might have different signs + --> tests/ui/modulo_arithmetic_float.rs:61:5 + | +LL | a_f128 % b_f128; + | ^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> tests/ui/modulo_arithmetic_float.rs:64:5 + | +LL | b_f128 % a_f128; + | ^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> tests/ui/modulo_arithmetic_float.rs:67:5 + | +LL | b_f128 %= a_f128; + | ^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index be6e071767d9..46629526367e 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -1,3 +1,5 @@ +#![feature(f128)] +#![feature(f16)] #![allow( dead_code, clippy::borrow_as_ptr, @@ -117,20 +119,34 @@ fn int_to_bool() { #[warn(clippy::transmute_int_to_float)] mod int_to_float { fn test() { + let _: f16 = unsafe { std::mem::transmute(0_u16) }; + //~^ ERROR: transmute from a `u16` to a `f16` + //~| NOTE: `-D clippy::transmute-int-to-float` implied by `-D warnings` + let _: f16 = unsafe { std::mem::transmute(0_i16) }; + //~^ ERROR: transmute from a `i16` to a `f16` let _: f32 = unsafe { std::mem::transmute(0_u32) }; //~^ ERROR: transmute from a `u32` to a `f32` - //~| NOTE: `-D clippy::transmute-int-to-float` implied by `-D warnings` let _: f32 = unsafe { std::mem::transmute(0_i32) }; //~^ ERROR: transmute from a `i32` to a `f32` let _: f64 = unsafe { std::mem::transmute(0_u64) }; //~^ ERROR: transmute from a `u64` to a `f64` let _: f64 = unsafe { std::mem::transmute(0_i64) }; //~^ ERROR: transmute from a `i64` to a `f64` + let _: f128 = unsafe { std::mem::transmute(0_u128) }; + //~^ ERROR: transmute from a `u128` to a `f128` + let _: f128 = unsafe { std::mem::transmute(0_i128) }; + //~^ ERROR: transmute from a `i128` to a `f128` } mod issue_5747 { + const VALUE16: f16 = unsafe { std::mem::transmute(0_u16) }; const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; + const VALUE128: f128 = unsafe { std::mem::transmute(0_i128) }; + + const fn from_bits_16(v: i16) -> f16 { + unsafe { std::mem::transmute(v) } + } const fn from_bits_32(v: i32) -> f32 { unsafe { std::mem::transmute(v) } @@ -139,6 +155,10 @@ mod int_to_float { const fn from_bits_64(v: u64) -> f64 { unsafe { std::mem::transmute(v) } } + + const fn from_bits_128(v: u128) -> f128 { + unsafe { std::mem::transmute(v) } + } } } @@ -158,10 +178,15 @@ mod num_to_bytes { //~^ ERROR: transmute from a `i32` to a `[u8; 4]` let _: [u8; 16] = std::mem::transmute(0i128); //~^ ERROR: transmute from a `i128` to a `[u8; 16]` + + let _: [u8; 2] = std::mem::transmute(0.0f16); + //~^ ERROR: transmute from a `f16` to a `[u8; 2]` let _: [u8; 4] = std::mem::transmute(0.0f32); //~^ ERROR: transmute from a `f32` to a `[u8; 4]` let _: [u8; 8] = std::mem::transmute(0.0f64); //~^ ERROR: transmute from a `f64` to a `[u8; 8]` + let _: [u8; 16] = std::mem::transmute(0.0f128); + //~^ ERROR: transmute from a `f128` to a `[u8; 16]` } } const fn test_const() { @@ -178,8 +203,11 @@ mod num_to_bytes { //~^ ERROR: transmute from a `i32` to a `[u8; 4]` let _: [u8; 16] = std::mem::transmute(0i128); //~^ ERROR: transmute from a `i128` to a `[u8; 16]` + + let _: [u8; 2] = std::mem::transmute(0.0f16); let _: [u8; 4] = std::mem::transmute(0.0f32); let _: [u8; 8] = std::mem::transmute(0.0f64); + let _: [u8; 16] = std::mem::transmute(0.0f128); } } } diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr index 375e8f19dd6b..0072f62962a7 100644 --- a/src/tools/clippy/tests/ui/transmute.stderr +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -1,5 +1,5 @@ error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:29:23 + --> tests/ui/transmute.rs:31:23 | LL | let _: *const T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` @@ -8,61 +8,61 @@ LL | let _: *const T = core::intrinsics::transmute(t); = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:33:21 + --> tests/ui/transmute.rs:35:21 | LL | let _: *mut T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:36:23 + --> tests/ui/transmute.rs:38:23 | LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:43:27 + --> tests/ui/transmute.rs:45:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:46:27 + --> tests/ui/transmute.rs:48:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:49:27 + --> tests/ui/transmute.rs:51:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:52:27 + --> tests/ui/transmute.rs:54:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:55:27 + --> tests/ui/transmute.rs:57:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> tests/ui/transmute.rs:58:31 + --> tests/ui/transmute.rs:60:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> tests/ui/transmute.rs:63:31 + --> tests/ui/transmute.rs:65:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> tests/ui/transmute.rs:95:24 + --> tests/ui/transmute.rs:97:24 | LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,25 +71,25 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); = help: to override `-D warnings` add `#[allow(clippy::crosspointer_transmute)]` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> tests/ui/transmute.rs:99:24 + --> tests/ui/transmute.rs:101:24 | LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> tests/ui/transmute.rs:102:31 + --> tests/ui/transmute.rs:104:31 | LL | let _: *const Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> tests/ui/transmute.rs:105:29 + --> tests/ui/transmute.rs:107:29 | LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u8` to a `bool` - --> tests/ui/transmute.rs:112:28 + --> tests/ui/transmute.rs:114:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -97,35 +97,59 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_bool)]` -error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:120:31 +error: transmute from a `u16` to a `f16` + --> tests/ui/transmute.rs:122:31 | -LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` +LL | let _: f16 = unsafe { std::mem::transmute(0_u16) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` | = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_float)]` +error: transmute from a `i16` to a `f16` + --> tests/ui/transmute.rs:125:31 + | +LL | let _: f16 = unsafe { std::mem::transmute(0_i16) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_i16 as u16)` + +error: transmute from a `u32` to a `f32` + --> tests/ui/transmute.rs:127:31 + | +LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` + error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:123:31 + --> tests/ui/transmute.rs:129:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `u64` to a `f64` - --> tests/ui/transmute.rs:125:31 + --> tests/ui/transmute.rs:131:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` error: transmute from a `i64` to a `f64` - --> tests/ui/transmute.rs:127:31 + --> tests/ui/transmute.rs:133:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` +error: transmute from a `u128` to a `f128` + --> tests/ui/transmute.rs:135:32 + | +LL | let _: f128 = unsafe { std::mem::transmute(0_u128) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_u128)` + +error: transmute from a `i128` to a `f128` + --> tests/ui/transmute.rs:137:32 + | +LL | let _: f128 = unsafe { std::mem::transmute(0_i128) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` + error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:148:30 + --> tests/ui/transmute.rs:168:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -133,54 +157,6 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8); = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::transmute_num_to_bytes)]` -error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:151:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` - -error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:153:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0u128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` - -error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:155:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0i8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` - -error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:157:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` - -error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:159:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0i128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` - -error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:161:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0.0f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` - -error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:163:30 - | -LL | let _: [u8; 8] = std::mem::transmute(0.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` - -error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:169:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0u8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` - error: transmute from a `u32` to a `[u8; 4]` --> tests/ui/transmute.rs:171:30 | @@ -211,8 +187,68 @@ error: transmute from a `i128` to a `[u8; 16]` LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` +error: transmute from a `f16` to a `[u8; 2]` + --> tests/ui/transmute.rs:182:30 + | +LL | let _: [u8; 2] = std::mem::transmute(0.0f16); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` + +error: transmute from a `f32` to a `[u8; 4]` + --> tests/ui/transmute.rs:184:30 + | +LL | let _: [u8; 4] = std::mem::transmute(0.0f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` + +error: transmute from a `f64` to a `[u8; 8]` + --> tests/ui/transmute.rs:186:30 + | +LL | let _: [u8; 8] = std::mem::transmute(0.0f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` + +error: transmute from a `f128` to a `[u8; 16]` + --> tests/ui/transmute.rs:188:31 + | +LL | let _: [u8; 16] = std::mem::transmute(0.0f128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` + +error: transmute from a `u8` to a `[u8; 1]` + --> tests/ui/transmute.rs:194:30 + | +LL | let _: [u8; 1] = std::mem::transmute(0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` + +error: transmute from a `u32` to a `[u8; 4]` + --> tests/ui/transmute.rs:196:30 + | +LL | let _: [u8; 4] = std::mem::transmute(0u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` + +error: transmute from a `u128` to a `[u8; 16]` + --> tests/ui/transmute.rs:198:31 + | +LL | let _: [u8; 16] = std::mem::transmute(0u128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` + +error: transmute from a `i8` to a `[u8; 1]` + --> tests/ui/transmute.rs:200:30 + | +LL | let _: [u8; 1] = std::mem::transmute(0i8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` + +error: transmute from a `i32` to a `[u8; 4]` + --> tests/ui/transmute.rs:202:30 + | +LL | let _: [u8; 4] = std::mem::transmute(0i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` + +error: transmute from a `i128` to a `[u8; 16]` + --> tests/ui/transmute.rs:204:31 + | +LL | let _: [u8; 16] = std::mem::transmute(0i128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` + error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:190:28 + --> tests/ui/transmute.rs:218:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -221,16 +257,16 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_bytes_to_str)]` error: transmute from a `&mut [u8]` to a `&mut str` - --> tests/ui/transmute.rs:193:32 + --> tests/ui/transmute.rs:221:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:195:30 + --> tests/ui/transmute.rs:223:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` -error: aborting due to 36 previous errors +error: aborting due to 42 previous errors diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed index 82d5f7fdca10..4361a7407d11 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed @@ -1,5 +1,7 @@ #![warn(clippy::transmute_float_to_int)] #![allow(clippy::missing_transmute_annotations)] +#![feature(f128)] +#![feature(f16)] fn float_to_int() { let _: u32 = unsafe { 1f32.to_bits() }; @@ -18,8 +20,14 @@ fn float_to_int() { } mod issue_5747 { + const VALUE16: i16 = unsafe { std::mem::transmute(1f16) }; const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; + const VALUE128: u128 = unsafe { std::mem::transmute(1f128) }; + + const fn to_bits_16(v: f16) -> u16 { + unsafe { std::mem::transmute(v) } + } const fn to_bits_32(v: f32) -> u32 { unsafe { std::mem::transmute(v) } @@ -28,6 +36,10 @@ mod issue_5747 { const fn to_bits_64(v: f64) -> i64 { unsafe { std::mem::transmute(v) } } + + const fn to_bits_128(v: f128) -> i128 { + unsafe { std::mem::transmute(v) } + } } fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.rs b/src/tools/clippy/tests/ui/transmute_float_to_int.rs index 9f056330adf9..363ce0bcb16d 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.rs +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.rs @@ -1,5 +1,7 @@ #![warn(clippy::transmute_float_to_int)] #![allow(clippy::missing_transmute_annotations)] +#![feature(f128)] +#![feature(f16)] fn float_to_int() { let _: u32 = unsafe { std::mem::transmute(1f32) }; @@ -18,8 +20,14 @@ fn float_to_int() { } mod issue_5747 { + const VALUE16: i16 = unsafe { std::mem::transmute(1f16) }; const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; + const VALUE128: u128 = unsafe { std::mem::transmute(1f128) }; + + const fn to_bits_16(v: f16) -> u16 { + unsafe { std::mem::transmute(v) } + } const fn to_bits_32(v: f32) -> u32 { unsafe { std::mem::transmute(v) } @@ -28,6 +36,10 @@ mod issue_5747 { const fn to_bits_64(v: f64) -> i64 { unsafe { std::mem::transmute(v) } } + + const fn to_bits_128(v: f128) -> i128 { + unsafe { std::mem::transmute(v) } + } } fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.stderr b/src/tools/clippy/tests/ui/transmute_float_to_int.stderr index ac3aae5f8b78..9cac75f72cdd 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.stderr +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.stderr @@ -1,5 +1,5 @@ error: transmute from a `f32` to a `u32` - --> tests/ui/transmute_float_to_int.rs:5:27 + --> tests/ui/transmute_float_to_int.rs:7:27 | LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()` @@ -8,31 +8,31 @@ LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_float_to_int)]` error: transmute from a `f32` to a `i32` - --> tests/ui/transmute_float_to_int.rs:8:27 + --> tests/ui/transmute_float_to_int.rs:10:27 | LL | let _: i32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:10:27 + --> tests/ui/transmute_float_to_int.rs:12:27 | LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` error: transmute from a `f64` to a `i64` - --> tests/ui/transmute_float_to_int.rs:12:27 + --> tests/ui/transmute_float_to_int.rs:14:27 | LL | let _: i64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64` error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:14:27 + --> tests/ui/transmute_float_to_int.rs:16:27 | LL | let _: u64 = unsafe { std::mem::transmute(1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()` error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:16:27 + --> tests/ui/transmute_float_to_int.rs:18:27 | LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` diff --git a/src/tools/clippy/tests/ui/unused_rounding.fixed b/src/tools/clippy/tests/ui/unused_rounding.fixed index 02f970f42a42..7af2c8650a3d 100644 --- a/src/tools/clippy/tests/ui/unused_rounding.fixed +++ b/src/tools/clippy/tests/ui/unused_rounding.fixed @@ -1,3 +1,5 @@ +// FIXME(f16_f128): add tests when math functions are available + #![warn(clippy::unused_rounding)] fn main() { diff --git a/src/tools/clippy/tests/ui/unused_rounding.rs b/src/tools/clippy/tests/ui/unused_rounding.rs index fd14c466f120..1b0b22a9b685 100644 --- a/src/tools/clippy/tests/ui/unused_rounding.rs +++ b/src/tools/clippy/tests/ui/unused_rounding.rs @@ -1,3 +1,5 @@ +// FIXME(f16_f128): add tests when math functions are available + #![warn(clippy::unused_rounding)] fn main() { diff --git a/src/tools/clippy/tests/ui/unused_rounding.stderr b/src/tools/clippy/tests/ui/unused_rounding.stderr index 75ad895012b3..c5ae2da75f84 100644 --- a/src/tools/clippy/tests/ui/unused_rounding.stderr +++ b/src/tools/clippy/tests/ui/unused_rounding.stderr @@ -1,5 +1,5 @@ error: used the `ceil` method with a whole number float - --> tests/ui/unused_rounding.rs:4:13 + --> tests/ui/unused_rounding.rs:6:13 | LL | let _ = 1f32.ceil(); | ^^^^^^^^^^^ help: remove the `ceil` method call: `1f32` @@ -8,25 +8,25 @@ LL | let _ = 1f32.ceil(); = help: to override `-D warnings` add `#[allow(clippy::unused_rounding)]` error: used the `floor` method with a whole number float - --> tests/ui/unused_rounding.rs:5:13 + --> tests/ui/unused_rounding.rs:7:13 | LL | let _ = 1.0f64.floor(); | ^^^^^^^^^^^^^^ help: remove the `floor` method call: `1.0f64` error: used the `round` method with a whole number float - --> tests/ui/unused_rounding.rs:6:13 + --> tests/ui/unused_rounding.rs:8:13 | LL | let _ = 1.00f32.round(); | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `1.00f32` error: used the `round` method with a whole number float - --> tests/ui/unused_rounding.rs:12:13 + --> tests/ui/unused_rounding.rs:14:13 | LL | let _ = 3.0_f32.round(); | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `3.0_f32` error: used the `round` method with a whole number float - --> tests/ui/unused_rounding.rs:14:13 + --> tests/ui/unused_rounding.rs:16:13 | LL | let _ = 3_3.0_0_f32.round(); | ^^^^^^^^^^^^^^^^^^^ help: remove the `round` method call: `3_3.0_0_f32` From 4630d1b23b996793e9188490cc99b1576176233f Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Wed, 12 Jun 2024 01:22:31 -0700 Subject: [PATCH 108/147] Ban `ArrayToPointer` and `MutToConstPointer` from runtime MIR Apparently MIR borrowck cares about at least one of these for checking variance. In runtime MIR, though, there's no need for them as `PtrToPtr` does the same thing. (Banning them simplifies passes like GVN that no longer need to handle multiple cast possibilities.) --- compiler/rustc_middle/src/mir/syntax.rs | 13 +++++++++++-- .../src/cleanup_post_borrowck.rs | 19 ++++++++++++++++++- compiler/rustc_mir_transform/src/gvn.rs | 10 +++------- compiler/rustc_mir_transform/src/lib.rs | 6 +++--- compiler/rustc_mir_transform/src/validate.rs | 8 +++++++- ...oxed_slice.main.GVN.32bit.panic-abort.diff | 2 +- ...xed_slice.main.GVN.32bit.panic-unwind.diff | 2 +- ...oxed_slice.main.GVN.64bit.panic-abort.diff | 2 +- ...xed_slice.main.GVN.64bit.panic-unwind.diff | 2 +- .../casts.roundtrip.InstSimplify.diff | 2 +- tests/mir-opt/instsimplify/casts.rs | 2 +- ...mut_range.PreCodegen.after.panic-abort.mir | 2 +- ...ut_range.PreCodegen.after.panic-unwind.mir | 2 +- ...ated_loop.PreCodegen.after.panic-abort.mir | 2 +- ...ted_loop.PreCodegen.after.panic-unwind.mir | 2 +- ...ward_loop.PreCodegen.after.panic-abort.mir | 2 +- ...ard_loop.PreCodegen.after.panic-unwind.mir | 2 +- ...erse_loop.PreCodegen.after.panic-abort.mir | 2 +- ...rse_loop.PreCodegen.after.panic-unwind.mir | 2 +- ...fg-pre-optimizations.after.panic-abort.mir | 4 ++-- ...g-pre-optimizations.after.panic-unwind.mir | 4 ++-- 21 files changed, 60 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 736cef3cdb81..5957a25f0f2b 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -127,6 +127,9 @@ pub enum AnalysisPhase { /// * [`StatementKind::AscribeUserType`] /// * [`StatementKind::Coverage`] with [`CoverageKind::BlockMarker`] or [`CoverageKind::SpanMarker`] /// * [`Rvalue::Ref`] with `BorrowKind::Fake` + /// * [`CastKind::PointerCoercion`] with any of the following: + /// * [`PointerCoercion::ArrayToPointer`] + /// * [`PointerCoercion::MutToConstPointer`] /// /// Furthermore, `Deref` projections must be the first projection within any place (if they /// appear at all) @@ -1284,8 +1287,7 @@ pub enum Rvalue<'tcx> { /// /// This allows for casts from/to a variety of types. /// - /// **FIXME**: Document exactly which `CastKind`s allow which types of casts. Figure out why - /// `ArrayToPointer` and `MutToConstPointer` are special. + /// **FIXME**: Document exactly which `CastKind`s allow which types of casts. Cast(CastKind, Operand<'tcx>, Ty<'tcx>), /// * `Offset` has the same semantics as [`offset`](pointer::offset), except that the second @@ -1365,6 +1367,13 @@ pub enum CastKind { PointerWithExposedProvenance, /// Pointer related casts that are done by coercions. Note that reference-to-raw-ptr casts are /// translated into `&raw mut/const *r`, i.e., they are not actually casts. + /// + /// The following are allowed in [`AnalysisPhase::Initial`] as they're needed for borrowck, + /// but after that are forbidden (including in all phases of runtime MIR): + /// * [`PointerCoercion::ArrayToPointer`] + /// * [`PointerCoercion::MutToConstPointer`] + /// + /// Both are runtime nops, so should be [`CastKind::PtrToPtr`] instead in runtime MIR. PointerCoercion(PointerCoercion), /// Cast into a dyn* object. DynStar, diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index 48a6a83e1460..264d8a139960 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -18,7 +18,8 @@ use crate::MirPass; use rustc_middle::mir::coverage::CoverageKind; -use rustc_middle::mir::{Body, BorrowKind, Rvalue, StatementKind, TerminatorKind}; +use rustc_middle::mir::{Body, BorrowKind, CastKind, Rvalue, StatementKind, TerminatorKind}; +use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::TyCtxt; pub struct CleanupPostBorrowck; @@ -36,6 +37,22 @@ impl<'tcx> MirPass<'tcx> for CleanupPostBorrowck { CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. }, ) | StatementKind::FakeRead(..) => statement.make_nop(), + StatementKind::Assign(box ( + _, + Rvalue::Cast( + ref mut cast_kind @ CastKind::PointerCoercion( + PointerCoercion::ArrayToPointer + | PointerCoercion::MutToConstPointer, + ), + .., + ), + )) => { + // BorrowCk needed to track whether these cases were coercions or casts, + // to know whether to check lifetimes in their pointees, + // but from now on that distinction doesn't matter, + // so just make them ordinary pointer casts instead. + *cast_kind = CastKind::PtrToPtr; + } _ => (), } } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 0f8f28e3462a..b2670040b14b 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -571,11 +571,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let ret = self.ecx.ptr_to_ptr(&src, to).ok()?; ret.into() } - CastKind::PointerCoercion( - ty::adjustment::PointerCoercion::MutToConstPointer - | ty::adjustment::PointerCoercion::ArrayToPointer - | ty::adjustment::PointerCoercion::UnsafeFnPointer, - ) => { + CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer) => { let src = self.evaluated[value].as_ref()?; let src = self.ecx.read_immediate(src).ok()?; let to = self.ecx.layout_of(to).ok()?; @@ -1164,10 +1160,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } - if let PtrToPtr | PointerCoercion(MutToConstPointer) = kind + if let PtrToPtr = kind && let Value::Cast { kind: inner_kind, value: inner_value, from: inner_from, to: _ } = *self.get(value) - && let PtrToPtr | PointerCoercion(MutToConstPointer) = inner_kind + && let PtrToPtr = inner_kind { from = inner_from; value = inner_value; diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index afba6781a703..87dd9cc11ebb 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -51,11 +51,11 @@ mod abort_unwinding_calls; mod add_call_guards; mod add_moves_for_packed_drops; mod add_retag; +mod add_subtyping_projections; +mod check_alignment; mod check_const_item_mutation; mod check_packed_ref; -mod remove_place_mention; // This pass is public to allow external drivers to perform MIR cleanup -mod add_subtyping_projections; pub mod cleanup_post_borrowck; mod copy_prop; mod coroutine; @@ -94,6 +94,7 @@ mod prettify; mod promote_consts; mod ref_prop; mod remove_noop_landing_pads; +mod remove_place_mention; mod remove_storage_markers; mod remove_uninit_drops; mod remove_unneeded_drops; @@ -103,7 +104,6 @@ mod reveal_all; mod shim; mod ssa; // This pass is public to allow external drivers to perform MIR cleanup -mod check_alignment; pub mod simplify; mod simplify_branches; mod simplify_comparison_integral; diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 2cca1a6f507b..f5d10521fdd0 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1188,6 +1188,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { "CastKind::{kind:?} output must be a raw const pointer, not {:?}", ty::RawPtr(_, Mutability::Not) ); + if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) { + self.fail(location, format!("After borrowck, MIR disallows {kind:?}")); + } } CastKind::PointerCoercion(PointerCoercion::ArrayToPointer) => { // FIXME: Check pointee types @@ -1201,6 +1204,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { "CastKind::{kind:?} output must be a raw pointer, not {:?}", ty::RawPtr(..) ); + if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) { + self.fail(location, format!("After borrowck, MIR disallows {kind:?}")); + } } CastKind::PointerCoercion(PointerCoercion::Unsize) => { // This is used for all `CoerceUnsized` types, @@ -1212,7 +1218,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { if !input_valid || !target_valid { self.fail( location, - format!("Wrong cast kind {kind:?} for the type {op_ty}",), + format!("Wrong cast kind {kind:?} for the type {op_ty}"), ); } } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff index ca445046279b..06011f9d7596 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff @@ -80,7 +80,7 @@ bb4: { StorageDead(_8); -- _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _11 = _6 as *const [bool; 0] (PtrToPtr); - _5 = NonNull::<[bool; 0]> { pointer: _11 }; + _11 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff index 1f498c65fa78..eb4a3ffd91d2 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff @@ -84,7 +84,7 @@ bb5: { StorageDead(_8); -- _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _11 = _6 as *const [bool; 0] (PtrToPtr); - _5 = NonNull::<[bool; 0]> { pointer: _11 }; + _11 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff index da72e581496e..a7cc243e548e 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff @@ -80,7 +80,7 @@ bb4: { StorageDead(_8); -- _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _11 = _6 as *const [bool; 0] (PtrToPtr); - _5 = NonNull::<[bool; 0]> { pointer: _11 }; + _11 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff index 920e66452e3b..c905a48862ca 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff @@ -84,7 +84,7 @@ bb5: { StorageDead(_8); -- _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _11 = _6 as *const [bool; 0] (PtrToPtr); - _5 = NonNull::<[bool; 0]> { pointer: _11 }; + _11 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; diff --git a/tests/mir-opt/instsimplify/casts.roundtrip.InstSimplify.diff b/tests/mir-opt/instsimplify/casts.roundtrip.InstSimplify.diff index a6d68cd4e4b8..e87ac762dfeb 100644 --- a/tests/mir-opt/instsimplify/casts.roundtrip.InstSimplify.diff +++ b/tests/mir-opt/instsimplify/casts.roundtrip.InstSimplify.diff @@ -14,7 +14,7 @@ StorageLive(_4); _4 = _1; _3 = move _4 as *mut u8 (PtrToPtr); - _2 = move _3 as *const u8 (PointerCoercion(MutToConstPointer)); + _2 = move _3 as *const u8 (PtrToPtr); StorageDead(_4); StorageDead(_3); - _0 = move _2 as *const u8 (PtrToPtr); diff --git a/tests/mir-opt/instsimplify/casts.rs b/tests/mir-opt/instsimplify/casts.rs index a7786fa570f1..15ceea767136 100644 --- a/tests/mir-opt/instsimplify/casts.rs +++ b/tests/mir-opt/instsimplify/casts.rs @@ -21,7 +21,7 @@ pub fn roundtrip(x: *const u8) -> *const u8 { // CHECK-LABEL: fn roundtrip( // CHECK: _4 = _1; // CHECK: _3 = move _4 as *mut u8 (PtrToPtr); - // CHECK: _2 = move _3 as *const u8 (PointerCoercion(MutToConstPointer)); + // CHECK: _2 = move _3 as *const u8 (PtrToPtr); x as *mut u8 as *const u8 } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir index 5e0398d11147..41cca811922e 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir @@ -41,7 +41,7 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> StorageLive(_9); StorageLive(_7); StorageLive(_6); - _6 = _5 as *const [u32] (PointerCoercion(MutToConstPointer)); + _6 = _5 as *const [u32] (PtrToPtr); _7 = PtrMetadata(_6); StorageDead(_6); _8 = as SliceIndex<[T]>>::get_unchecked_mut::precondition_check(_3, _4, move _7) -> [return: bb1, unwind unreachable]; diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir index 5e0398d11147..41cca811922e 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir @@ -41,7 +41,7 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> StorageLive(_9); StorageLive(_7); StorageLive(_6); - _6 = _5 as *const [u32] (PointerCoercion(MutToConstPointer)); + _6 = _5 as *const [u32] (PtrToPtr); _7 = PtrMetadata(_6); StorageDead(_6); _8 = as SliceIndex<[T]>>::get_unchecked_mut::precondition_check(_3, _4, move _7) -> [return: bb1, unwind unreachable]; diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir index 8f41fb70925f..8eb102e68f36 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir @@ -79,7 +79,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { _7 = _4 as *mut T (PtrToPtr); _8 = Offset(_7, _3); StorageDead(_7); - _9 = move _8 as *const T (PointerCoercion(MutToConstPointer)); + _9 = move _8 as *const T (PtrToPtr); StorageDead(_8); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir index 17cf305468e8..f805967d64ff 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir @@ -79,7 +79,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { _7 = _4 as *mut T (PtrToPtr); _8 = Offset(_7, _3); StorageDead(_7); - _9 = move _8 as *const T (PointerCoercion(MutToConstPointer)); + _9 = move _8 as *const T (PtrToPtr); StorageDead(_8); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir index 3c1bbdc87424..6ae64200f4e7 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -71,7 +71,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { _7 = _4 as *mut T (PtrToPtr); _8 = Offset(_7, _3); StorageDead(_7); - _9 = move _8 as *const T (PointerCoercion(MutToConstPointer)); + _9 = move _8 as *const T (PtrToPtr); StorageDead(_8); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir index b2ec1ea7b9f2..ac72329fcd64 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -71,7 +71,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { _7 = _4 as *mut T (PtrToPtr); _8 = Offset(_7, _3); StorageDead(_7); - _9 = move _8 as *const T (PointerCoercion(MutToConstPointer)); + _9 = move _8 as *const T (PtrToPtr); StorageDead(_8); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir index bf982f076de7..8008336e268c 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir @@ -79,7 +79,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _7 = _4 as *mut T (PtrToPtr); _8 = Offset(_7, _3); StorageDead(_7); - _9 = move _8 as *const T (PointerCoercion(MutToConstPointer)); + _9 = move _8 as *const T (PtrToPtr); StorageDead(_8); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir index 532b81625212..47253bf7a0d3 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir @@ -79,7 +79,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _7 = _4 as *mut T (PtrToPtr); _8 = Offset(_7, _3); StorageDead(_7); - _9 = move _8 as *const T (PointerCoercion(MutToConstPointer)); + _9 = move _8 as *const T (PtrToPtr); StorageDead(_8); goto -> bb3; } diff --git a/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-abort.mir b/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-abort.mir index f9d58ea60a39..6dba667dd155 100644 --- a/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-abort.mir +++ b/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-abort.mir @@ -64,7 +64,7 @@ fn array_casts() -> () { StorageLive(_4); _4 = &mut _1; _3 = &raw mut (*_4); - _2 = move _3 as *mut usize (PointerCoercion(ArrayToPointer)); + _2 = move _3 as *mut usize (PtrToPtr); StorageDead(_3); StorageDead(_4); StorageLive(_5); @@ -87,7 +87,7 @@ fn array_casts() -> () { StorageLive(_11); _11 = &_8; _10 = &raw const (*_11); - _9 = move _10 as *const usize (PointerCoercion(ArrayToPointer)); + _9 = move _10 as *const usize (PtrToPtr); StorageDead(_10); StorageDead(_11); StorageLive(_12); diff --git a/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-unwind.mir b/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-unwind.mir index b0b70cd5d910..fa812002e26c 100644 --- a/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-unwind.mir +++ b/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-unwind.mir @@ -64,7 +64,7 @@ fn array_casts() -> () { StorageLive(_4); _4 = &mut _1; _3 = &raw mut (*_4); - _2 = move _3 as *mut usize (PointerCoercion(ArrayToPointer)); + _2 = move _3 as *mut usize (PtrToPtr); StorageDead(_3); StorageDead(_4); StorageLive(_5); @@ -87,7 +87,7 @@ fn array_casts() -> () { StorageLive(_11); _11 = &_8; _10 = &raw const (*_11); - _9 = move _10 as *const usize (PointerCoercion(ArrayToPointer)); + _9 = move _10 as *const usize (PtrToPtr); StorageDead(_10); StorageDead(_11); StorageLive(_12); From e04e35133f28426df9fa61f9703da957d99b48d8 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 14 Jun 2024 23:01:22 -0700 Subject: [PATCH 109/147] `bug!` more uses of these in runtime stuff --- compiler/rustc_codegen_cranelift/src/base.rs | 21 ++++++++++--------- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 11 +++++----- .../rustc_const_eval/src/interpret/cast.rs | 4 +--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 6d26ca0b899b..b117dc496c2b 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -677,21 +677,22 @@ fn codegen_stmt<'tcx>( CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer), ref operand, to_ty, - ) - | Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::MutToConstPointer), - ref operand, - to_ty, - ) - | Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::ArrayToPointer), - ref operand, - to_ty, ) => { let to_layout = fx.layout_of(fx.monomorphize(to_ty)); let operand = codegen_operand(fx, operand); lval.write_cvalue(fx, operand.cast_pointer_to(to_layout)); } + Rvalue::Cast( + CastKind::PointerCoercion( + PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, + ), + .., + ) => { + bug!( + "{:?} is for borrowck, and should never appear in codegen", + to_place_and_rval.1 + ); + } Rvalue::Cast( CastKind::IntToInt | CastKind::FloatToFloat diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 3dc7dc355115..05861e337674 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -456,8 +456,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { base::unsize_ptr(bx, lldata, operand.layout.ty, cast.ty, llextra); OperandValue::Pair(lldata, llextra) } - mir::CastKind::PointerCoercion(PointerCoercion::MutToConstPointer) - | mir::CastKind::PtrToPtr + mir::CastKind::PointerCoercion( + PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, + ) => { + bug!("{kind:?} is for borrowck, and should never appear in codegen"); + } + mir::CastKind::PtrToPtr if bx.cx().is_backend_scalar_pair(operand.layout) => { if let OperandValue::Pair(data_ptr, meta) = operand.val { @@ -477,9 +481,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { base::cast_to_dyn_star(bx, lldata, operand.layout, cast.ty, llextra); OperandValue::Pair(lldata, llextra) } - mir::CastKind::PointerCoercion( - PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, - ) | mir::CastKind::IntToInt | mir::CastKind::FloatToInt | mir::CastKind::FloatToFloat diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 6961e13c2399..a13630ce084d 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -70,9 +70,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { CastKind::PointerCoercion( PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, ) => { - // These are NOPs, but can be wide pointers. - let v = self.read_immediate(src)?; - self.write_immediate(*v, dest)?; + bug!("{cast_kind:?} casts are for borrowck only, not runtime MIR"); } CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => { From 95e214d6e5cbacd4bcc12f449674dbcc3f409a02 Mon Sep 17 00:00:00 2001 From: The 8472 Date: Wed, 19 Jun 2024 20:01:32 +0200 Subject: [PATCH 110/147] reword the hint::blackbox non-guarantees People were tripped up by the "precludes", interpreting it that this function must not ever be used in cryptographic contexts rather than the std lib merely making zero promises about it being fit-for-purpose. What remains unchanged is that if someone does try to use it *despite the warnings* then it is on them to pin their compiler versions and verify the assembly of every single binary build they do. --- library/core/src/hint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 6e2d88c6b833..c3b16c34293c 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -263,7 +263,7 @@ pub fn spin_loop() { /// extent to which it can block optimisations may vary depending upon the platform and code-gen /// backend used. Programs cannot rely on `black_box` for *correctness*, beyond it behaving as the /// identity function. As such, it **must not be relied upon to control critical program behavior.** -/// This _immediately_ precludes any direct use of this function for cryptographic or security +/// This also means that this function does not offer any guarantees for cryptographic or security /// purposes. /// /// [`std::convert::identity`]: crate::convert::identity From e7ea063622d4a150fb22030b78d21b18f4855481 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Tue, 18 Jun 2024 16:43:52 -0400 Subject: [PATCH 111/147] rewrite forced-unwind-terminate-pof to rmake --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - .../forced-unwind-terminate-pof/Makefile | 9 --------- .../forced-unwind-terminate-pof/rmake.rs | 16 ++++++++++++++++ .../metadata-flag-frobs-symbols/rmake.rs | 1 - 4 files changed, 16 insertions(+), 11 deletions(-) delete mode 100644 tests/run-make/forced-unwind-terminate-pof/Makefile create mode 100644 tests/run-make/forced-unwind-terminate-pof/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 8eee16e87a88..a5ccb1b78424 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -51,7 +51,6 @@ run-make/extern-multiple-copies/Makefile run-make/extern-multiple-copies2/Makefile run-make/extra-filename-with-temp-outputs/Makefile run-make/fmt-write-bloat/Makefile -run-make/forced-unwind-terminate-pof/Makefile run-make/foreign-double-unwind/Makefile run-make/foreign-exceptions/Makefile run-make/foreign-rust-exceptions/Makefile diff --git a/tests/run-make/forced-unwind-terminate-pof/Makefile b/tests/run-make/forced-unwind-terminate-pof/Makefile deleted file mode 100644 index 871621520b91..000000000000 --- a/tests/run-make/forced-unwind-terminate-pof/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# ignore-cross-compile -# only-linux -include ../tools.mk - -all: foo - $(call RUN,foo) | $(CGREP) -v "cannot unwind" - -foo: foo.rs - $(RUSTC) $< diff --git a/tests/run-make/forced-unwind-terminate-pof/rmake.rs b/tests/run-make/forced-unwind-terminate-pof/rmake.rs new file mode 100644 index 000000000000..320ddb172b6e --- /dev/null +++ b/tests/run-make/forced-unwind-terminate-pof/rmake.rs @@ -0,0 +1,16 @@ +// During a forced unwind, crossing the non-Plain Old Frame +// would define the forced unwind as undefined behaviour, and +// immediately abort the unwinding process. This test checks +// that the forced unwinding takes precedence. +// See https://github.com/rust-lang/rust/issues/101469 + +//@ ignore-cross-compile +//@ ignore-windows +//Reason: pthread (POSIX threads) is not available on Windows + +use run_make_support::{run, rustc}; + +fn main() { + rustc().input("foo.rs").run(); + run("foo").assert_stdout_not_contains("cannot unwind"); +} diff --git a/tests/run-make/metadata-flag-frobs-symbols/rmake.rs b/tests/run-make/metadata-flag-frobs-symbols/rmake.rs index 12c07f9e3aee..938886957fb9 100644 --- a/tests/run-make/metadata-flag-frobs-symbols/rmake.rs +++ b/tests/run-make/metadata-flag-frobs-symbols/rmake.rs @@ -15,7 +15,6 @@ fn main() { .input("bar.rs") .extern_("foo1", rust_lib_name("foo-a")) .extern_("foo2", rust_lib_name("foo-b")) - .print("link-args") .run(); run("bar"); } From 7d9a92ba31122950f2c7f6a71ad6dee49b3e95e4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 13 Jun 2024 13:40:14 +1000 Subject: [PATCH 112/147] Inline `can_begin_literal_maybe_minus` call into two places. It's clearer this way, because the `Interpolated` cases in `can_begin_const_arg` and `is_pat_range_end_start` are more permissive than the `Interpolated` cases in `can_begin_literal_maybe_minus`. --- compiler/rustc_ast/src/token.rs | 5 +++-- compiler/rustc_parse/src/parser/pat.rs | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 109c401bb6a2..4d513797f5fe 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -558,9 +558,10 @@ impl Token { /// Returns `true` if the token can appear at the start of a const param. pub fn can_begin_const_arg(&self) -> bool { match self.kind { - OpenDelim(Delimiter::Brace) => true, + OpenDelim(Delimiter::Brace) | Literal(..) | BinOp(Minus) => true, + Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true, Interpolated(ref nt) => matches!(&**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)), - _ => self.can_begin_literal_maybe_minus(), + _ => false, } } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 03aea0888d95..6f2b71771594 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -939,7 +939,8 @@ impl<'a> Parser<'a> { || self.look_ahead(dist, |t| { t.is_path_start() // e.g. `MY_CONST`; || t.kind == token::Dot // e.g. `.5` for recovery; - || t.can_begin_literal_maybe_minus() // e.g. `42`. + || matches!(t.kind, token::Literal(..) | token::BinOp(token::Minus)) + || t.is_bool_lit() || t.is_whole_expr() || t.is_lifetime() // recover `'a` instead of `'a'` || (self.may_recover() // recover leading `(` From c6f78270b66b2ffc78742006445df76105b7d558 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 13 Jun 2024 13:52:20 +1000 Subject: [PATCH 113/147] Introduce `can_begin_string_literal`. We currently use `can_begin_literal_maybe_minus` in a couple of places where only string literals are allowed. This commit introduces a more specific function, which makes things clearer. It doesn't change behaviour because the two functions affected (`is_unsafe_foreign_mod` and `check_keyword_case`) are always followed by a call to `parse_abi`, which checks again for a string literal. --- compiler/rustc_ast/src/token.rs | 15 +++++++++++++++ compiler/rustc_parse/src/parser/item.rs | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 4d513797f5fe..4dc41a02cb85 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -621,6 +621,21 @@ impl Token { } } + pub fn can_begin_string_literal(&self) -> bool { + match self.uninterpolate().kind { + Literal(..) => true, + Interpolated(ref nt) => match &**nt { + NtLiteral(_) => true, + NtExpr(e) => match &e.kind { + ast::ExprKind::Lit(_) => true, + _ => false, + }, + _ => false, + }, + _ => false, + } + } + /// A convenience function for matching on identifiers during parsing. /// Turns interpolated identifier (`$i: ident`) or lifetime (`$l: lifetime`) token /// into the regular identifier or lifetime token it refers to, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 2db777a9f70c..3e1ea7b129de 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1259,7 +1259,7 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Extern]) && self.look_ahead( - 2 + self.look_ahead(2, |t| t.can_begin_literal_maybe_minus() as usize), + 2 + self.look_ahead(2, |t| t.can_begin_string_literal() as usize), |t| t.kind == token::OpenDelim(Delimiter::Brace), ) } @@ -2448,7 +2448,7 @@ impl<'a> Parser<'a> { }) // `extern ABI fn` || self.check_keyword_case(kw::Extern, case) - && self.look_ahead(1, |t| t.can_begin_literal_maybe_minus()) + && self.look_ahead(1, |t| t.can_begin_string_literal()) && (self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) || // this branch is only for better diagnostic in later, `pub` is not allowed here (self.may_recover() From f44494cb3ad0a1c599620e9c020cf7e6a54bb774 Mon Sep 17 00:00:00 2001 From: Jerry Wang Date: Sat, 15 Jun 2024 21:47:27 -0400 Subject: [PATCH 114/147] Migrate `run-make/comment-section` to `rmake.rs` --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/comment-section/Makefile | 18 ------- tests/run-make/comment-section/rmake.rs | 47 +++++++++++++++++++ 3 files changed, 47 insertions(+), 19 deletions(-) delete mode 100644 tests/run-make/comment-section/Makefile create mode 100644 tests/run-make/comment-section/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 2b273b92ec2d..c4c1ad033413 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -10,7 +10,6 @@ run-make/c-unwind-abi-catch-panic/Makefile run-make/cat-and-grep-sanity-check/Makefile run-make/cdylib-dylib-linkage/Makefile run-make/cdylib-fewer-symbols/Makefile -run-make/comment-section/Makefile run-make/compiler-lookup-paths-2/Makefile run-make/compiler-lookup-paths/Makefile run-make/compiler-rt-works-on-mingw/Makefile diff --git a/tests/run-make/comment-section/Makefile b/tests/run-make/comment-section/Makefile deleted file mode 100644 index d0b98176ffed..000000000000 --- a/tests/run-make/comment-section/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# Both GCC and Clang write by default a `.comment` section with compiler information. Rustc received a similar .comment section, so this tests checks that this section properly appears. -# See https://github.com/rust-lang/rust/commit/74b8d324eb77a8f337b35dc68ac91b0c2c06debc - -include ../tools.mk - -# only-linux - -all: - echo 'fn main(){}' | $(RUSTC) - --emit=link,obj -Csave-temps --target=$(TARGET) - - # Check linked output has a `.comment` section with the expected content. - readelf -p '.comment' $(TMPDIR)/rust_out | $(CGREP) -F 'rustc version 1.' - - # Check all object files (including temporary outputs) have a `.comment` - # section with the expected content. - set -e; for f in $(TMPDIR)/*.o; do \ - readelf -p '.comment' $$f | $(CGREP) -F 'rustc version 1.'; \ - done diff --git a/tests/run-make/comment-section/rmake.rs b/tests/run-make/comment-section/rmake.rs new file mode 100644 index 000000000000..41df04da7a57 --- /dev/null +++ b/tests/run-make/comment-section/rmake.rs @@ -0,0 +1,47 @@ +// Both GCC and Clang write by default a `.comment` section with compiler information. +// Rustc received a similar .comment section, so this tests checks that this section +// properly appears. +// See https://github.com/rust-lang/rust/commit/74b8d324eb77a8f337b35dc68ac91b0c2c06debc + +//@ only-linux +// FIXME(jieyouxu): check cross-compile setup +//@ ignore-cross-compile + +use std::path::PathBuf; + +use run_make_support::llvm_readobj; +use run_make_support::rustc; +use run_make_support::{cwd, env_var, read_dir, run_in_tmpdir}; + +fn main() { + let target = env_var("TARGET"); + + rustc() + .arg("-") + .stdin("fn main() {}") + .emit("link,obj") + .arg("-Csave-temps") + .target(&target) + .run(); + + // Check linked output has a `.comment` section with the expected content. + llvm_readobj() + .section(".comment") + .input("rust_out") + .run() + .assert_stdout_contains("rustc version 1."); + + // Check all object files (including temporary outputs) have a `.comment` + // section with the expected content. + read_dir(cwd(), |f| { + if !f.extension().is_some_and(|ext| ext == "o") { + return; + } + + llvm_readobj() + .section(".comment") + .input(&f) + .run() + .assert_stdout_contains("rustc version 1."); + }); +} From bb2716effde9713efad15c269b35779a79c90271 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 19 Jun 2024 20:06:56 +0100 Subject: [PATCH 115/147] Fix wasm_exceptions test --- tests/assembly/wasm_exceptions.rs | 8 ++++---- tests/codegen/wasm_exceptions.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/assembly/wasm_exceptions.rs b/tests/assembly/wasm_exceptions.rs index 7bdf7f1287c2..f05ccfadc585 100644 --- a/tests/assembly/wasm_exceptions.rs +++ b/tests/assembly/wasm_exceptions.rs @@ -6,12 +6,12 @@ #![crate_type = "lib"] #![feature(core_intrinsics)] -#![feature(rustc_attrs)] + +extern "C-unwind" { + fn may_panic(); +} extern "C" { - fn may_panic(); - - #[rustc_nounwind] fn log_number(number: usize); } diff --git a/tests/codegen/wasm_exceptions.rs b/tests/codegen/wasm_exceptions.rs index 3910850e03a0..719499dd8ead 100644 --- a/tests/codegen/wasm_exceptions.rs +++ b/tests/codegen/wasm_exceptions.rs @@ -3,12 +3,12 @@ #![crate_type = "lib"] #![feature(core_intrinsics)] -#![feature(rustc_attrs)] + +extern "C-unwind" { + fn may_panic(); +} extern "C" { - fn may_panic(); - - #[rustc_nounwind] fn log_number(number: usize); } From 35c65a8c0cf018d472afec5adf9da8242adc8c23 Mon Sep 17 00:00:00 2001 From: George Bateman Date: Wed, 19 Jun 2024 21:44:47 +0100 Subject: [PATCH 116/147] Make Option::as_[mut_]slice const --- library/core/src/option.rs | 10 ++++++---- library/core/tests/option.rs | 9 +++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 4d6ba2fa3b38..8ec7716012f5 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -797,7 +797,8 @@ impl Option { #[inline] #[must_use] #[stable(feature = "option_as_slice", since = "1.75.0")] - pub fn as_slice(&self) -> &[T] { + #[rustc_const_unstable(feature = "const_option_ext", issue = "91930")] + pub const fn as_slice(&self) -> &[T] { // SAFETY: When the `Option` is `Some`, we're using the actual pointer // to the payload, with a length of 1, so this is equivalent to // `slice::from_ref`, and thus is safe. @@ -811,7 +812,7 @@ impl Option { unsafe { slice::from_raw_parts( (self as *const Self).byte_add(core::mem::offset_of!(Self, Some.0)).cast(), - usize::from(self.is_some()), + self.is_some() as usize, ) } } @@ -851,7 +852,8 @@ impl Option { #[inline] #[must_use] #[stable(feature = "option_as_slice", since = "1.75.0")] - pub fn as_mut_slice(&mut self) -> &mut [T] { + #[rustc_const_unstable(feature = "const_option_ext", issue = "91930")] + pub const fn as_mut_slice(&mut self) -> &mut [T] { // SAFETY: When the `Option` is `Some`, we're using the actual pointer // to the payload, with a length of 1, so this is equivalent to // `slice::from_mut`, and thus is safe. @@ -867,7 +869,7 @@ impl Option { unsafe { slice::from_raw_parts_mut( (self as *mut Self).byte_add(core::mem::offset_of!(Self, Some.0)).cast(), - usize::from(self.is_some()), + self.is_some() as usize, ) } } diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs index b1b9492f182e..336a79a02cee 100644 --- a/library/core/tests/option.rs +++ b/library/core/tests/option.rs @@ -574,4 +574,13 @@ fn as_slice() { assert_eq!(Some(43).as_mut_slice(), &[43]); assert_eq!(None::.as_slice(), &[]); assert_eq!(None::.as_mut_slice(), &[]); + + const A: &[u32] = Some(44).as_slice(); + const B: &[u32] = None.as_slice(); + const _: () = { + let [45] = Some(45).as_mut_slice() else { panic!() }; + let []: &[u32] = None.as_mut_slice() else { panic!() }; + }; + assert_eq!(A, &[44]); + assert_eq!(B, &[]); } From 09006d6a88019b80f7158f1a5f754a358751d7f1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 20 Jun 2024 05:02:12 +1000 Subject: [PATCH 117/147] Convert some module-level `//` and `///` comments to `//!`. This makes their intent and expected location clearer. We see some examples where these comments were not clearly separate from `use` declarations, which made it hard to understand what the comment is describing. --- compiler/rustc_builtin_macros/src/test.rs | 5 +++-- compiler/rustc_data_structures/src/base_n.rs | 5 +++-- compiler/rustc_middle/src/mir/statement.rs | 3 ++- compiler/rustc_middle/src/mir/terminator.rs | 3 ++- .../spec/targets/mips64_openwrt_linux_musl.rs | 4 ++-- library/alloc/src/boxed/thin.rs | 7 ++++--- .../crates/core_simd/examples/dot_product.rs | 5 ++--- .../std/src/io/buffered/bufreader/buffer.rs | 21 ++++++++++--------- library/std/src/sys/os_str/wtf8.rs | 5 +++-- 9 files changed, 32 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 8f96070d1495..c0310a2f4b00 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -1,6 +1,7 @@ +//! The expansion from a test function to the appropriate test struct for libtest +//! Ideally, this code would be in libtest but for efficiency and error messages it lives here. + use crate::errors; -/// The expansion from a test function to the appropriate test struct for libtest -/// Ideally, this code would be in libtest but for efficiency and error messages it lives here. use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute}; use rustc_ast::ptr::P; use rustc_ast::{self as ast, attr, GenericParamKind}; diff --git a/compiler/rustc_data_structures/src/base_n.rs b/compiler/rustc_data_structures/src/base_n.rs index aed89fadc4c8..80810df14d01 100644 --- a/compiler/rustc_data_structures/src/base_n.rs +++ b/compiler/rustc_data_structures/src/base_n.rs @@ -1,5 +1,6 @@ -/// Converts unsigned integers into a string representation with some base. -/// Bases up to and including 36 can be used for case-insensitive things. +//! Converts unsigned integers into a string representation with some base. +//! Bases up to and including 36 can be used for case-insensitive things. + use std::ascii; use std::fmt; diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 4d9a931d6979..ac3feb71a2b4 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -1,4 +1,5 @@ -/// Functionality for statements, operands, places, and things that appear in them. +//! Functionality for statements, operands, places, and things that appear in them. + use super::{interpret::GlobalAlloc, *}; /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index c8c12e205e11..ed592612358b 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -1,4 +1,5 @@ -/// Functionality for terminators and helper types that appear in terminators. +//! Functionality for terminators and helper types that appear in terminators. + use rustc_hir::LangItem; use smallvec::{smallvec, SmallVec}; diff --git a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs index b8bdfc943de1..9df7b2b670f5 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs @@ -1,5 +1,5 @@ -/// A target tuple for OpenWrt MIPS64 targets -/// +//! A target tuple for OpenWrt MIPS64 targets. + use crate::abi::Endian; use crate::spec::{base, Target, TargetOptions}; diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 8b145b67bf18..e9bfecba160a 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -1,6 +1,7 @@ -// Based on -// https://github.com/matthieu-m/rfc2580/blob/b58d1d3cba0d4b5e859d3617ea2d0943aaa31329/examples/thin.rs -// by matthieu-m +//! Based on +//! +//! by matthieu-m + use crate::alloc::{self, Layout, LayoutError}; use core::error::Error; use core::fmt::{self, Debug, Display, Formatter}; diff --git a/library/portable-simd/crates/core_simd/examples/dot_product.rs b/library/portable-simd/crates/core_simd/examples/dot_product.rs index f047010a65c1..75d152ae7f0e 100644 --- a/library/portable-simd/crates/core_simd/examples/dot_product.rs +++ b/library/portable-simd/crates/core_simd/examples/dot_product.rs @@ -1,6 +1,5 @@ -// Code taken from the `packed_simd` crate -// Run this code with `cargo test --example dot_product` -//use std::iter::zip; +//! Code taken from the `packed_simd` crate. +//! Run this code with `cargo test --example dot_product`. #![feature(array_chunks)] #![feature(slice_as_chunks)] diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index e9e29d60ca28..796137c0123e 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -1,13 +1,14 @@ -///! An encapsulation of `BufReader`'s buffer management logic. -/// -/// This module factors out the basic functionality of `BufReader` in order to protect two core -/// invariants: -/// * `filled` bytes of `buf` are always initialized -/// * `pos` is always <= `filled` -/// Since this module encapsulates the buffer management logic, we can ensure that the range -/// `pos..filled` is always a valid index into the initialized region of the buffer. This means -/// that user code which wants to do reads from a `BufReader` via `buffer` + `consume` can do so -/// without encountering any runtime bounds checks. +//! An encapsulation of `BufReader`'s buffer management logic. +//! +//! This module factors out the basic functionality of `BufReader` in order to protect two core +//! invariants: +//! * `filled` bytes of `buf` are always initialized +//! * `pos` is always <= `filled` +//! Since this module encapsulates the buffer management logic, we can ensure that the range +//! `pos..filled` is always a valid index into the initialized region of the buffer. This means +//! that user code which wants to do reads from a `BufReader` via `buffer` + `consume` can do so +//! without encountering any runtime bounds checks. + use crate::cmp; use crate::io::{self, BorrowedBuf, Read}; use crate::mem::MaybeUninit; diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index dfff4dd4fb02..96690f8c44e9 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -1,5 +1,6 @@ -/// The underlying OsString/OsStr implementation on Windows is a -/// wrapper around the "WTF-8" encoding; see the `wtf8` module for more. +//! The underlying OsString/OsStr implementation on Windows is a +//! wrapper around the "WTF-8" encoding; see the `wtf8` module for more. + use crate::borrow::Cow; use crate::collections::TryReserveError; use crate::fmt; From 665821cb606076bcf159c47e3e8e72a14fa512e3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 20 Jun 2024 05:04:30 +1000 Subject: [PATCH 118/147] Add blank lines after module-level `//!` comments. Most modules have such a blank line, but some don't. Inserting the blank line makes it clearer that the `//!` comments are describing the entire module, rather than the `use` declaration(s) that immediately follows. --- compiler/rustc_codegen_gcc/tests/lang_tests_common.rs | 1 + compiler/rustc_codegen_ssa/src/mir/locals.rs | 1 + compiler/rustc_errors/src/markdown/mod.rs | 1 + compiler/rustc_expand/src/mbe/macro_check.rs | 1 + compiler/rustc_hir_typeck/src/autoderef.rs | 3 ++- compiler/rustc_hir_typeck/src/errors.rs | 1 + compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs | 1 + .../error_reporting/nice_region_error/named_anon_conflict.rs | 1 + compiler/rustc_infer/src/infer/freshen.rs | 1 + compiler/rustc_infer/src/infer/outlives/mod.rs | 1 + compiler/rustc_middle/src/middle/privacy.rs | 1 + compiler/rustc_middle/src/ty/abstract_const.rs | 1 + compiler/rustc_mir_transform/src/ctfe_limit.rs | 1 + compiler/rustc_mir_transform/src/inline.rs | 3 ++- compiler/rustc_mir_transform/src/lint.rs | 1 + compiler/rustc_pattern_analysis/src/pat.rs | 1 + compiler/rustc_pattern_analysis/tests/complexity.rs | 1 + compiler/rustc_pattern_analysis/tests/exhaustiveness.rs | 1 + compiler/rustc_pattern_analysis/tests/intersection.rs | 1 + .../rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs | 1 + .../rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs | 1 + .../src/cfi/typeid/itanium_cxx_abi/transform.rs | 1 + compiler/rustc_sanitizers/src/cfi/typeid/mod.rs | 1 + compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs | 1 + compiler/rustc_session/src/output.rs | 1 + compiler/rustc_smir/src/rustc_smir/builder.rs | 1 + compiler/rustc_smir/src/rustc_smir/convert/error.rs | 1 + compiler/rustc_trait_selection/src/traits/normalize.rs | 1 + .../rustc_trait_selection/src/traits/select/confirmation.rs | 1 + compiler/stable_mir/src/mir/alloc.rs | 1 + library/alloc/src/vec/in_place_collect.rs | 1 + library/core/src/str/count.rs | 1 + library/core/tests/iter/adapters/map_windows.rs | 1 + library/core/tests/num/ieee754.rs | 1 + library/panic_unwind/src/miri.rs | 1 + library/portable-simd/crates/core_simd/src/ops/assign.rs | 1 + library/portable-simd/crates/core_simd/src/ops/deref.rs | 1 + library/std/src/hash/random.rs | 1 + library/std/src/sys/pal/itron/thread.rs | 1 + library/std/src/sys/pal/solid/abi/fs.rs | 1 + .../sys/pal/unix/process/process_unsupported/wait_status.rs | 1 + library/std/src/sys/pal/unix/thread.rs | 1 + library/std/src/sys/sync/condvar/itron.rs | 1 + library/std/src/sys/sync/mutex/itron.rs | 1 + library/std/src/sys/sync/rwlock/solid.rs | 1 + library/std/tests/create_dir_all_bare.rs | 1 + library/test/src/bench.rs | 1 + library/test/src/helpers/concurrency.rs | 1 + library/test/src/helpers/metrics.rs | 1 + src/librustdoc/passes/calculate_doc_coverage.rs | 1 + src/librustdoc/passes/collect_trait_impls.rs | 1 + src/librustdoc/passes/lint/check_code_block_syntax.rs | 1 + src/librustdoc/passes/lint/html_tags.rs | 1 + src/librustdoc/passes/propagate_doc_cfg.rs | 1 + src/librustdoc/passes/strip_priv_imports.rs | 1 + src/librustdoc/passes/strip_private.rs | 1 + src/librustdoc/passes/stripper.rs | 1 + src/tools/tidy/src/ui_tests.rs | 1 + 58 files changed, 60 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs index d321ffc8ff54..09307836fd42 100644 --- a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs +++ b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs @@ -1,4 +1,5 @@ //! The common code for `tests/lang_tests_*.rs` + use std::{ env::{self, current_dir}, path::{Path, PathBuf}, diff --git a/compiler/rustc_codegen_ssa/src/mir/locals.rs b/compiler/rustc_codegen_ssa/src/mir/locals.rs index c7f63eab8298..a6c873e195eb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/locals.rs +++ b/compiler/rustc_codegen_ssa/src/mir/locals.rs @@ -1,6 +1,7 @@ //! Locals are in a private module as updating `LocalRef::Operand` has to //! be careful wrt to subtyping. To deal with this we only allow updates by using //! `FunctionCx::overwrite_local` which handles it automatically. + use crate::mir::{FunctionCx, LocalRef}; use crate::traits::BuilderMethods; use rustc_index::IndexVec; diff --git a/compiler/rustc_errors/src/markdown/mod.rs b/compiler/rustc_errors/src/markdown/mod.rs index 53b766dfccec..64576cdc8cac 100644 --- a/compiler/rustc_errors/src/markdown/mod.rs +++ b/compiler/rustc_errors/src/markdown/mod.rs @@ -1,6 +1,7 @@ //! A simple markdown parser that can write formatted text to the terminal //! //! Entrypoint is `MdStream::parse_str(...)` + use std::io; use termcolor::{Buffer, BufferWriter, ColorChoice}; diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index d9a945a32150..56ef609612ac 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -104,6 +104,7 @@ //! Kleene operators under which a meta-variable is repeating is the concatenation of the stacks //! stored when entering a macro definition starting from the state in which the meta-variable is //! bound. + use crate::errors; use crate::mbe::{KleeneToken, TokenTree}; diff --git a/compiler/rustc_hir_typeck/src/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index 2bb7caea3c43..5db71591e66e 100644 --- a/compiler/rustc_hir_typeck/src/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs @@ -1,4 +1,5 @@ -//! Some helper functions for `AutoDeref` +//! Some helper functions for `AutoDeref`. + use super::method::MethodCallee; use super::{FnCtxt, PlaceOp}; diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 24f039b8e90b..98add86252c5 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1,4 +1,5 @@ //! Errors emitted by `rustc_hir_typeck`. + use std::borrow::Cow; use crate::fluent_generated as fluent; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index 23f4d3c36a37..fab7eb7495c5 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -1,4 +1,5 @@ //! A utility module to inspect currently ambiguous obligations in the current context. + use crate::FnCtxt; use rustc_infer::traits::{self, ObligationCause}; use rustc_middle::traits::solve::Goal; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs index 29da12e7d151..85624c9ca73d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -1,5 +1,6 @@ //! Error Reporting for Anonymous Region Lifetime Errors //! where one region is named and the other is anonymous. + use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::{ errors::ExplicitLifetimeRequired, diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index 4bb59bd9037c..50526c964edc 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -30,6 +30,7 @@ //! solving a set of constraints. In contrast, the type inferencer assigns a value to each type //! variable only once, and it does so as soon as it can, so it is reasonable to ask what the type //! inferencer knows "so far". + use super::InferCtxt; use rustc_data_structures::fx::FxHashMap; use rustc_middle::bug; diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index a4f9316b5020..48d006e7fbcf 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -1,4 +1,5 @@ //! Various code related to computing outlives relations. + use self::env::OutlivesEnvironment; use super::region_constraints::RegionConstraintData; use super::{InferCtxt, RegionResolutionError, SubregionOrigin}; diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 46520d69e183..4b47b019fd4b 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -1,6 +1,7 @@ //! A pass that checks to make sure private fields and methods aren't used //! outside their scopes. This pass will also generate a set of exported items //! which are available for use externally when compiled as a library. + use crate::ty::{TyCtxt, Visibility}; use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 254e1b544819..fa44fbaa0599 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -1,4 +1,5 @@ //! A subset of a mir body used for const evaluability checking. + use crate::ty::{ self, Const, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index dcc960e1e020..a0dddec185cc 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -1,5 +1,6 @@ //! A pass that inserts the `ConstEvalCounter` instruction into any blocks that have a back edge //! (thus indicating there is a loop in the CFG), or whose terminator is a function call. + use crate::MirPass; use rustc_data_structures::graph::dominators::Dominators; diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index d04bb8d302e2..32b5d8120252 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -1,4 +1,5 @@ -//! Inlining pass for MIR functions +//! Inlining pass for MIR functions. + use crate::deref_separator::deref_finder; use rustc_attr::InlineAttr; use rustc_hir::def::DefKind; diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index 5b269185e87a..3d1e1e481750 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -1,6 +1,7 @@ //! This pass statically detects code which has undefined behaviour or is likely to be erroneous. //! It can be used to locate problems in MIR building or optimizations. It assumes that all code //! can be executed, so it has false positives. + use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{PlaceContext, Visitor}; diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 5f388ee9f897..5e75976621e0 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -1,5 +1,6 @@ //! As explained in [`crate::usefulness`], values and patterns are made from constructors applied to //! fields. This file defines types that represent patterns in this way. + use std::fmt; use smallvec::{smallvec, SmallVec}; diff --git a/compiler/rustc_pattern_analysis/tests/complexity.rs b/compiler/rustc_pattern_analysis/tests/complexity.rs index 93f455c6257d..19242d44e352 100644 --- a/compiler/rustc_pattern_analysis/tests/complexity.rs +++ b/compiler/rustc_pattern_analysis/tests/complexity.rs @@ -1,4 +1,5 @@ //! Test the pattern complexity limit. + use common::*; use rustc_pattern_analysis::{pat::DeconstructedPat, usefulness::PlaceValidity, MatchArm}; diff --git a/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs b/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs index 4c6c72fa8ecb..4f8d68d5514b 100644 --- a/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs +++ b/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs @@ -1,4 +1,5 @@ //! Test exhaustiveness checking. + use common::*; use rustc_pattern_analysis::{ pat::{DeconstructedPat, WitnessPat}, diff --git a/compiler/rustc_pattern_analysis/tests/intersection.rs b/compiler/rustc_pattern_analysis/tests/intersection.rs index 4d8a21506d76..4a96b7248dae 100644 --- a/compiler/rustc_pattern_analysis/tests/intersection.rs +++ b/compiler/rustc_pattern_analysis/tests/intersection.rs @@ -1,4 +1,5 @@ //! Test the computation of arm intersections. + use common::*; use rustc_pattern_analysis::{pat::DeconstructedPat, usefulness::PlaceValidity, MatchArm}; diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 0be8b5d57187..f998fd599b7c 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -4,6 +4,7 @@ //! //! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, //! see design document in the tracking issue #89653. + use rustc_data_structures::base_n::ToBaseN; use rustc_data_structures::base_n::ALPHANUMERIC_ONLY; use rustc_data_structures::base_n::CASE_INSENSITIVE; diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs index b6182dc4e635..ac664c53f445 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs @@ -3,6 +3,7 @@ //! //! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, //! see design document in the tracking issue #89653. + use rustc_data_structures::fx::FxHashMap; use rustc_middle::bug; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 742ec4c377cc..e74c1208a395 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -3,6 +3,7 @@ //! //! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, //! see design document in the tracking issue #89653. + use rustc_hir as hir; use rustc_hir::LangItem; use rustc_middle::bug; diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs b/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs index ad8b18044399..f37ffcbc4db2 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs @@ -3,6 +3,7 @@ //! //! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, //! see design document in the tracking issue #89653. + use bitflags::bitflags; use rustc_middle::ty::{Instance, Ty, TyCtxt}; use rustc_target::abi::call::FnAbi; diff --git a/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs index 651ba6124696..bfe907e3cf6a 100644 --- a/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs +++ b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs @@ -3,6 +3,7 @@ //! //! For more information about LLVM KCFI and cross-language LLVM KCFI support for the Rust compiler, //! see the tracking issue #123479. + use rustc_middle::ty::{Instance, InstanceKind, ReifyReason, Ty, TyCtxt}; use rustc_target::abi::call::FnAbi; use std::hash::Hasher; diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index 35cd3cbab66c..9a5314312e56 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -1,4 +1,5 @@ //! Related to out filenames of compilation (e.g. binaries). + use crate::config::{self, CrateType, Input, OutFileName, OutputFilenames, OutputType}; use crate::errors::{ self, CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable, diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index 42fa6989ddcf..09866515a2ab 100644 --- a/compiler/rustc_smir/src/rustc_smir/builder.rs +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -3,6 +3,7 @@ //! We first retrieve and monomorphize the rustc body representation, i.e., we generate a //! monomorphic body using internal representation. //! After that, we convert the internal representation into a stable one. + use crate::rustc_smir::{Stable, Tables}; use rustc_hir::def::DefKind; use rustc_middle::mir; diff --git a/compiler/rustc_smir/src/rustc_smir/convert/error.rs b/compiler/rustc_smir/src/rustc_smir/convert/error.rs index 8298450f741f..3d6897089155 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/error.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/error.rs @@ -1,6 +1,7 @@ //! Handle the conversion of different internal errors into a stable version. //! //! Currently we encode everything as [stable_mir::Error], which is represented as a string. + use crate::rustc_smir::{Stable, Tables}; use rustc_middle::mir::interpret::AllocError; use rustc_middle::ty::layout::LayoutError; diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index e7ab0b7791c4..d7408eab84a4 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -1,4 +1,5 @@ //! Deeply normalize types using the old trait solver. + use super::error_reporting::OverflowCause; use super::error_reporting::TypeErrCtxtExt; use super::SelectionContext; diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index af599108c49d..b50f6260ab30 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -6,6 +6,7 @@ //! //! [rustc dev guide]: //! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation + use rustc_ast::Mutability; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; diff --git a/compiler/stable_mir/src/mir/alloc.rs b/compiler/stable_mir/src/mir/alloc.rs index 408e0bafa589..ef1568151f2e 100644 --- a/compiler/stable_mir/src/mir/alloc.rs +++ b/compiler/stable_mir/src/mir/alloc.rs @@ -1,4 +1,5 @@ //! This module provides methods to retrieve allocation information, such as static variables. + use crate::mir::mono::{Instance, StaticDef}; use crate::target::{Endian, MachineInfo}; use crate::ty::{Allocation, Binder, ExistentialTraitRef, IndexedVal, Ty}; diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index 22541a2b9d82..0dc193d82c53 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -154,6 +154,7 @@ //! } //! vec.truncate(write_idx); //! ``` + use crate::alloc::{handle_alloc_error, Global}; use core::alloc::Allocator; use core::alloc::Layout; diff --git a/library/core/src/str/count.rs b/library/core/src/str/count.rs index d8667864fe55..b5d7aaf05d4b 100644 --- a/library/core/src/str/count.rs +++ b/library/core/src/str/count.rs @@ -17,6 +17,7 @@ //! Note: Because the term "leading byte" can sometimes be ambiguous (for //! example, it could also refer to the first byte of a slice), we'll often use //! the term "non-continuation byte" to refer to these bytes in the code. + use core::intrinsics::unlikely; const USIZE_SIZE: usize = core::mem::size_of::(); diff --git a/library/core/tests/iter/adapters/map_windows.rs b/library/core/tests/iter/adapters/map_windows.rs index 7fb2408f8acb..6744eff3fa26 100644 --- a/library/core/tests/iter/adapters/map_windows.rs +++ b/library/core/tests/iter/adapters/map_windows.rs @@ -3,6 +3,7 @@ use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; #[cfg(not(panic = "abort"))] mod drop_checks { //! These tests mainly make sure the elements are correctly dropped. + use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}; #[derive(Debug)] diff --git a/library/core/tests/num/ieee754.rs b/library/core/tests/num/ieee754.rs index 48ab75b6f17a..b0f6a7545aa9 100644 --- a/library/core/tests/num/ieee754.rs +++ b/library/core/tests/num/ieee754.rs @@ -27,6 +27,7 @@ //! standard. That is why they accept wildly diverse inputs or may seem to duplicate other tests. //! Please consider this carefully when adding, removing, or reorganizing these tests. They are //! here so that it is clear what tests are required by the standard and what can be changed. + use ::core::str::FromStr; // IEEE 754 for many tests is applied to specific bit patterns. diff --git a/library/panic_unwind/src/miri.rs b/library/panic_unwind/src/miri.rs index 4d21e846010e..695adadd59b5 100644 --- a/library/panic_unwind/src/miri.rs +++ b/library/panic_unwind/src/miri.rs @@ -1,4 +1,5 @@ //! Unwinding panics for Miri. + use alloc::boxed::Box; use core::any::Any; diff --git a/library/portable-simd/crates/core_simd/src/ops/assign.rs b/library/portable-simd/crates/core_simd/src/ops/assign.rs index 0e87785025a3..d21d867de26d 100644 --- a/library/portable-simd/crates/core_simd/src/ops/assign.rs +++ b/library/portable-simd/crates/core_simd/src/ops/assign.rs @@ -1,4 +1,5 @@ //! Assignment operators + use super::*; use core::ops::{AddAssign, MulAssign}; // commutative binary op-assignment use core::ops::{BitAndAssign, BitOrAssign, BitXorAssign}; // commutative bit binary op-assignment diff --git a/library/portable-simd/crates/core_simd/src/ops/deref.rs b/library/portable-simd/crates/core_simd/src/ops/deref.rs index 89a60ba11414..0ff76cfba39b 100644 --- a/library/portable-simd/crates/core_simd/src/ops/deref.rs +++ b/library/portable-simd/crates/core_simd/src/ops/deref.rs @@ -2,6 +2,7 @@ //! Ideally, Rust would take care of this itself, //! and method calls usually handle the LHS implicitly. //! But this is not the case with arithmetic ops. + use super::*; macro_rules! deref_lhs { diff --git a/library/std/src/hash/random.rs b/library/std/src/hash/random.rs index a1ccbb25369b..0adf91e14ac6 100644 --- a/library/std/src/hash/random.rs +++ b/library/std/src/hash/random.rs @@ -6,6 +6,7 @@ //! outside this crate. //! //! [`collections`]: crate::collections + #[allow(deprecated)] use super::{BuildHasher, Hasher, SipHasher13}; use crate::cell::Cell; diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index 205226ce1da8..a0c8217128f3 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -1,5 +1,6 @@ //! Thread implementation backed by μITRON tasks. Assumes `acre_tsk` and //! `exd_tsk` are available. + use super::{ abi, error::{expect_success, expect_success_aborting, ItronError}, diff --git a/library/std/src/sys/pal/solid/abi/fs.rs b/library/std/src/sys/pal/solid/abi/fs.rs index 49526f4c9cd4..75efaaac2a94 100644 --- a/library/std/src/sys/pal/solid/abi/fs.rs +++ b/library/std/src/sys/pal/solid/abi/fs.rs @@ -1,4 +1,5 @@ //! `solid_fs.h` + use crate::os::raw::{c_char, c_int, c_uchar}; pub use libc::{ ino_t, off_t, stat, time_t, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, diff --git a/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs b/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs index e6dfadcf4a4c..973188b1f2b2 100644 --- a/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs +++ b/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs @@ -1,6 +1,7 @@ //! Emulated wait status for non-Unix #[cfg(unix) platforms //! //! Separate module to facilitate testing against a real Unix implementation. + use crate::ffi::c_int; use crate::fmt; use crate::num::NonZero; diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 1ab54ec57c3b..3ebf1cfd33f8 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -475,6 +475,7 @@ mod cgroups { //! * cgroup v2 in non-standard mountpoints //! * paths containing control characters or spaces, since those would be escaped in procfs //! output and we don't unescape + use crate::borrow::Cow; use crate::ffi::OsString; use crate::fs::{try_exists, File}; diff --git a/library/std/src/sys/sync/condvar/itron.rs b/library/std/src/sys/sync/condvar/itron.rs index 9b64d241efd1..3a3039889e98 100644 --- a/library/std/src/sys/sync/condvar/itron.rs +++ b/library/std/src/sys/sync/condvar/itron.rs @@ -1,4 +1,5 @@ //! POSIX conditional variable implementation based on user-space wait queues. + use crate::sys::pal::itron::{ abi, error::expect_success_aborting, spin::SpinMutex, task, time::with_tmos_strong, }; diff --git a/library/std/src/sys/sync/mutex/itron.rs b/library/std/src/sys/sync/mutex/itron.rs index a134eb2d1bec..4ba32a8fbcd6 100644 --- a/library/std/src/sys/sync/mutex/itron.rs +++ b/library/std/src/sys/sync/mutex/itron.rs @@ -1,5 +1,6 @@ //! Mutex implementation backed by μITRON mutexes. Assumes `acre_mtx` and //! `TA_INHERIT` are available. + use crate::sys::pal::itron::{ abi, error::{expect_success, expect_success_aborting, fail, ItronError}, diff --git a/library/std/src/sys/sync/rwlock/solid.rs b/library/std/src/sys/sync/rwlock/solid.rs index 9bf6f5dbb731..7558eee8edd3 100644 --- a/library/std/src/sys/sync/rwlock/solid.rs +++ b/library/std/src/sys/sync/rwlock/solid.rs @@ -1,4 +1,5 @@ //! A readers-writer lock implementation backed by the SOLID kernel extension. + use crate::sys::pal::{ abi, itron::{ diff --git a/library/std/tests/create_dir_all_bare.rs b/library/std/tests/create_dir_all_bare.rs index fd2a7f906f83..79c3c8f528ef 100644 --- a/library/std/tests/create_dir_all_bare.rs +++ b/library/std/tests/create_dir_all_bare.rs @@ -2,6 +2,7 @@ //! Note that this test changes the current directory so //! should not be in the same process as other tests. + use std::env; use std::fs; use std::path::{Path, PathBuf}; diff --git a/library/test/src/bench.rs b/library/test/src/bench.rs index 64ca13c0d4ed..9f34f54c3d60 100644 --- a/library/test/src/bench.rs +++ b/library/test/src/bench.rs @@ -1,4 +1,5 @@ //! Benchmarking module. + use super::{ event::CompletedTest, options::BenchMode, diff --git a/library/test/src/helpers/concurrency.rs b/library/test/src/helpers/concurrency.rs index b395adcf885c..1854c6a76524 100644 --- a/library/test/src/helpers/concurrency.rs +++ b/library/test/src/helpers/concurrency.rs @@ -1,5 +1,6 @@ //! Helper module which helps to determine amount of threads to be used //! during tests execution. + use std::{env, num::NonZero, thread}; pub fn get_concurrency() -> usize { diff --git a/library/test/src/helpers/metrics.rs b/library/test/src/helpers/metrics.rs index f77a23e6875b..bc38969cefb8 100644 --- a/library/test/src/helpers/metrics.rs +++ b/library/test/src/helpers/metrics.rs @@ -1,4 +1,5 @@ //! Benchmark metrics. + use std::collections::BTreeMap; #[derive(Clone, PartialEq, Debug, Copy)] diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 592dd0a145cf..9e576002dc3e 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -1,4 +1,5 @@ //! Calculates information used for the --show-coverage flag. + use crate::clean; use crate::core::DocContext; use crate::html::markdown::{find_testable_code, ErrorCodes}; diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index c92cf9d3e80d..9ac9941e5219 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -1,6 +1,7 @@ //! Collects trait impls for each item in the crate. For example, if a crate //! defines a struct that implements a trait, this pass will note that the //! struct implements that trait. + use super::Pass; use crate::clean::*; use crate::core::DocContext; diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs index c185442fd55d..afeb7111e15e 100644 --- a/src/librustdoc/passes/lint/check_code_block_syntax.rs +++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs @@ -1,4 +1,5 @@ //! Validates syntax inside Rust code blocks (\`\`\`rust). + use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::{ emitter::Emitter, diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs index 25b0c61b826f..a0064a901125 100644 --- a/src/librustdoc/passes/lint/html_tags.rs +++ b/src/librustdoc/passes/lint/html_tags.rs @@ -1,4 +1,5 @@ //! Detects invalid HTML (like an unclosed ``) in doc comments. + use crate::clean::*; use crate::core::DocContext; use crate::html::markdown::main_body_opts; diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 95273a225a91..8de7f2379b4b 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -1,4 +1,5 @@ //! Propagates [`#[doc(cfg(...))]`](https://github.com/rust-lang/rust/issues/43781) to child items. + use std::sync::Arc; use crate::clean::cfg::Cfg; diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs index 468712ba3d0b..7b05756ae215 100644 --- a/src/librustdoc/passes/strip_priv_imports.rs +++ b/src/librustdoc/passes/strip_priv_imports.rs @@ -1,5 +1,6 @@ //! Strips all private import statements (use, extern crate) from a //! crate. + use crate::clean; use crate::core::DocContext; use crate::fold::DocFolder; diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index 3b6f484fde65..1bafa450be90 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -1,5 +1,6 @@ //! Strip all private items from the output. Additionally implies strip_priv_imports. //! Basically, the goal is to remove items that are not relevant for public documentation. + use crate::clean::{self, ItemIdSet}; use crate::core::DocContext; use crate::fold::DocFolder; diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 1bea93c78421..1cf80a0d80c5 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -1,4 +1,5 @@ //! A collection of utility functions for the `strip_*` passes. + use rustc_hir::def_id::DefId; use rustc_middle::ty::{TyCtxt, Visibility}; use std::mem; diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index f2eeda339d80..95857502108d 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -1,6 +1,7 @@ //! Tidy check to ensure below in UI test directories: //! - the number of entries in each directory must be less than `ENTRY_LIMIT` //! - there are no stray `.stderr` files + use ignore::Walk; use std::collections::{BTreeSet, HashMap}; use std::ffi::OsStr; From b104fbec85ac819d11bf8bf0d9cc11d0b8fb737e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 20 Jun 2024 06:06:53 +1000 Subject: [PATCH 119/147] Add blank lines after module-level `//` comments. Similar to the previous commit. --- compiler/rustc_hir_analysis/src/check/dropck.rs | 1 + compiler/rustc_parse/src/parser/expr.rs | 1 + library/core/tests/net/parser.rs | 1 + library/core/tests/pin_macro.rs | 1 + tests/run-make/symlinked-libraries/rmake.rs | 1 + 5 files changed, 5 insertions(+) diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index 8ec6dd12a78d..193714480255 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -1,6 +1,7 @@ // FIXME(@lcnr): Move this module out of `rustc_hir_analysis`. // // We don't do any drop checking during hir typeck. + use rustc_data_structures::fx::FxHashSet; use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 09f706143fa7..9fad954addad 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,4 +1,5 @@ // ignore-tidy-filelength + use super::diagnostics::SnapshotParser; use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; diff --git a/library/core/tests/net/parser.rs b/library/core/tests/net/parser.rs index 36b87d7c1f5e..e03959ac77c3 100644 --- a/library/core/tests/net/parser.rs +++ b/library/core/tests/net/parser.rs @@ -1,4 +1,5 @@ // FIXME: These tests are all excellent candidates for AFL fuzz testing + use core::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use core::str::FromStr; diff --git a/library/core/tests/pin_macro.rs b/library/core/tests/pin_macro.rs index 79c8c166c58d..57485ef3974c 100644 --- a/library/core/tests/pin_macro.rs +++ b/library/core/tests/pin_macro.rs @@ -1,4 +1,5 @@ // edition:2021 + use core::{ marker::PhantomPinned, mem::{drop as stuff, transmute}, diff --git a/tests/run-make/symlinked-libraries/rmake.rs b/tests/run-make/symlinked-libraries/rmake.rs index eaf0c44206a5..3f02f19ccd5b 100644 --- a/tests/run-make/symlinked-libraries/rmake.rs +++ b/tests/run-make/symlinked-libraries/rmake.rs @@ -6,6 +6,7 @@ // See https://github.com/rust-lang/rust/issues/12459 //@ ignore-cross-compile + use run_make_support::{create_symlink, dynamic_lib_name, fs_wrapper, rustc}; fn main() { From 9981d61cdbaf4cdcfca8cb5ca953144f2294cc84 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 20 Jun 2024 05:39:45 +1000 Subject: [PATCH 120/147] Remove useless `tidy-alphabetical` markers. rustfmt already sorts `use` declarations within the same group. --- compiler/rustc_fs_util/src/lib.rs | 2 -- compiler/rustc_lint_defs/src/lib.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/compiler/rustc_fs_util/src/lib.rs b/compiler/rustc_fs_util/src/lib.rs index 5a9b15204d58..d376c24cb589 100644 --- a/compiler/rustc_fs_util/src/lib.rs +++ b/compiler/rustc_fs_util/src/lib.rs @@ -1,9 +1,7 @@ -// tidy-alphabetical-start use std::ffi::CString; use std::fs; use std::io; use std::path::{absolute, Path, PathBuf}; -// tidy-alphabetical-end // Unfortunately, on windows, it looks like msvcrt.dll is silently translating // verbatim paths under the hood to non-verbatim paths! This manifests itself as diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 70330c445776..b3838f915f6b 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -1,4 +1,3 @@ -// tidy-alphabetical-start pub use self::Level::*; use rustc_ast::node_id::NodeId; use rustc_ast::{AttrId, Attribute}; @@ -15,7 +14,6 @@ use rustc_span::edition::Edition; use rustc_span::symbol::MacroRulesNormalizedIdent; use rustc_span::{sym, symbol::Ident, Span, Symbol}; use rustc_target::spec::abi::Abi; -// tidy-alphabetical-end use serde::{Deserialize, Serialize}; From b5a5647ee0f8af226fcc44a3e6b054fc758a785e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 20 Jun 2024 06:05:27 +1000 Subject: [PATCH 121/147] Move an `EMIT_MIR` comment. This belongs on a function, not a `use` declaration. --- tests/mir-opt/issue_76432.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mir-opt/issue_76432.rs b/tests/mir-opt/issue_76432.rs index be5ccbd49e24..0daeb85434f5 100644 --- a/tests/mir-opt/issue_76432.rs +++ b/tests/mir-opt/issue_76432.rs @@ -3,9 +3,9 @@ //@ compile-flags: -Zmir-enable-passes=-NormalizeArrayLen // Check that we do not insert StorageDead at each target if StorageDead was never seen -// EMIT_MIR issue_76432.test.SimplifyComparisonIntegral.diff use std::fmt::Debug; +// EMIT_MIR issue_76432.test.SimplifyComparisonIntegral.diff fn test(x: T) { let v: &[T] = &[x, x, x]; match v { From 19b7192c7266ddd6d3b6fb3b436458fb84682f9c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 20 Jun 2024 10:08:06 +1000 Subject: [PATCH 122/147] Fix assertion failure for some `Expect` diagnostics. In #120699 I moved some code dealing with `has_future_breakage` earlier in `emit_diagnostic`. Issue #126521 identified a case where that reordering was invalid (leading to an assertion failure) for some `Expect` diagnostics. This commit partially undoes the change, by moving the handling of unstable `Expect` diagnostics earlier again. This makes `emit_diagnostic` a bit uglier, but is necessary to fix the problem. Fixes #126521. --- compiler/rustc_errors/src/lib.rs | 28 +++++++++++++------ ...ect-future_breakage-crash-issue-126521.rs} | 2 +- ...-future_breakage-crash-issue-126521.stderr | 13 +++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) rename tests/{crashes/126521.rs => ui/lint/expect-future_breakage-crash-issue-126521.rs} (69%) create mode 100644 tests/ui/lint/expect-future_breakage-crash-issue-126521.stderr diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 83d5bbff0b0a..620f56c01e89 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1437,6 +1437,24 @@ impl DiagCtxtInner { // Return value is only `Some` if the level is `Error` or `DelayedBug`. fn emit_diagnostic(&mut self, mut diagnostic: DiagInner) -> Option { + match diagnostic.level { + Expect(expect_id) | ForceWarning(Some(expect_id)) => { + // The `LintExpectationId` can be stable or unstable depending on when it was + // created. Diagnostics created before the definition of `HirId`s are unstable and + // can not yet be stored. Instead, they are buffered until the `LintExpectationId` + // is replaced by a stable one by the `LintLevelsBuilder`. + if let LintExpectationId::Unstable { .. } = expect_id { + // We don't call TRACK_DIAGNOSTIC because we wait for the + // unstable ID to be updated, whereupon the diagnostic will be + // passed into this method again. + self.unstable_expect_diagnostics.push(diagnostic); + return None; + } + // Continue through to the `Expect`/`ForceWarning` case below. + } + _ => {} + } + if diagnostic.has_future_breakage() { // Future breakages aren't emitted if they're `Level::Allow`, // but they still need to be constructed and stashed below, @@ -1512,16 +1530,8 @@ impl DiagCtxtInner { return None; } Expect(expect_id) | ForceWarning(Some(expect_id)) => { - // Diagnostics created before the definition of `HirId`s are - // unstable and can not yet be stored. Instead, they are - // buffered until the `LintExpectationId` is replaced by a - // stable one by the `LintLevelsBuilder`. if let LintExpectationId::Unstable { .. } = expect_id { - // We don't call TRACK_DIAGNOSTIC because we wait for the - // unstable ID to be updated, whereupon the diagnostic will - // be passed into this method again. - self.unstable_expect_diagnostics.push(diagnostic); - return None; + unreachable!(); // this case was handled at the top of this function } self.fulfilled_expectations.insert(expect_id.normalize()); if let Expect(_) = diagnostic.level { diff --git a/tests/crashes/126521.rs b/tests/ui/lint/expect-future_breakage-crash-issue-126521.rs similarity index 69% rename from tests/crashes/126521.rs rename to tests/ui/lint/expect-future_breakage-crash-issue-126521.rs index 0a025aabcf2e..a3c8544613bf 100644 --- a/tests/crashes/126521.rs +++ b/tests/ui/lint/expect-future_breakage-crash-issue-126521.rs @@ -1,4 +1,3 @@ -//@ known-bug: rust-lang/rust#126521 macro_rules! foo { ($val:ident) => { true; @@ -7,5 +6,6 @@ macro_rules! foo { fn main() { #[expect(semicolon_in_expressions_from_macros)] + //~^ ERROR the `#[expect]` attribute is an experimental feature let _ = foo!(x); } diff --git a/tests/ui/lint/expect-future_breakage-crash-issue-126521.stderr b/tests/ui/lint/expect-future_breakage-crash-issue-126521.stderr new file mode 100644 index 000000000000..b24831b1ae43 --- /dev/null +++ b/tests/ui/lint/expect-future_breakage-crash-issue-126521.stderr @@ -0,0 +1,13 @@ +error[E0658]: the `#[expect]` attribute is an experimental feature + --> $DIR/expect-future_breakage-crash-issue-126521.rs:8:5 + | +LL | #[expect(semicolon_in_expressions_from_macros)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #54503 for more information + = help: add `#![feature(lint_reasons)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. From e8a9af9ad972be1b2df26b8e0b4905e6808a912f Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 18 Jun 2024 17:04:40 -0700 Subject: [PATCH 123/147] Clarify that anonymous consts still do introduce a new scope --- compiler/rustc_lint/messages.ftl | 2 +- tests/ui/lint/non-local-defs/cargo-update.stderr | 2 +- tests/ui/lint/non-local-defs/consts.stderr | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 007709e32d87..468673f05c11 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -550,7 +550,7 @@ lint_non_local_definitions_impl = non-local `impl` definition, `impl` blocks sho .with_trait = an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` .bounds = `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type .doctest = make this doc-test a standalone test with its own `fn main() {"{"} ... {"}"}` - .exception = items in an anonymous const item (`const _: () = {"{"} ... {"}"}`) are treated as in the same scope as the anonymous const's declaration + .exception = items in an anonymous const item (`const _: () = {"{"} ... {"}"}`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint .const_anon = use a const-anon item to suppress this lint .macro_to_change = the {$macro_kind} `{$macro_to_change}` defines the non-local `impl`, and may need to be changed diff --git a/tests/ui/lint/non-local-defs/cargo-update.stderr b/tests/ui/lint/non-local-defs/cargo-update.stderr index afd37d03a231..bccf8622bac6 100644 --- a/tests/ui/lint/non-local-defs/cargo-update.stderr +++ b/tests/ui/lint/non-local-defs/cargo-update.stderr @@ -12,7 +12,7 @@ LL | non_local_macro::non_local_impl!(LocalStruct); = note: the macro `non_local_macro::non_local_impl` may come from an old version of the `non_local_macro` crate, try updating your dependency with `cargo update -p non_local_macro` = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration + = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default = note: this warning originates in the macro `non_local_macro::non_local_impl` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/lint/non-local-defs/consts.stderr b/tests/ui/lint/non-local-defs/consts.stderr index 2756ea401387..9f70119e0f8c 100644 --- a/tests/ui/lint/non-local-defs/consts.stderr +++ b/tests/ui/lint/non-local-defs/consts.stderr @@ -15,7 +15,7 @@ LL | impl Uto for &Test {} | = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration + = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default @@ -32,7 +32,7 @@ LL | impl Uto2 for Test {} | = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration + = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item @@ -48,7 +48,7 @@ LL | impl Uto3 for Test {} | = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration + = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item @@ -94,7 +94,7 @@ LL | impl Test { | `Test` is not local | = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` - = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration + = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item From f3349510303531edaf55dc18ae286bece8c85bdb Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 16 Jun 2024 23:44:29 -0700 Subject: [PATCH 124/147] Give `CostChecker` both penalties and bonuses --- .../rustc_mir_transform/src/cost_checker.rs | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index 2c692c950030..bca4ef5b3d12 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -12,7 +12,8 @@ const RESUME_PENALTY: usize = 45; pub(crate) struct CostChecker<'b, 'tcx> { tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, - cost: usize, + penalty: usize, + bonus: usize, callee_body: &'b Body<'tcx>, instance: Option>, } @@ -24,11 +25,11 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> { instance: Option>, callee_body: &'b Body<'tcx>, ) -> CostChecker<'b, 'tcx> { - CostChecker { tcx, param_env, callee_body, instance, cost: 0 } + CostChecker { tcx, param_env, callee_body, instance, penalty: 0, bonus: 0 } } pub fn cost(&self) -> usize { - self.cost + usize::saturating_sub(self.penalty, self.bonus) } fn instantiate_ty(&self, v: Ty<'tcx>) -> Ty<'tcx> { @@ -48,7 +49,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { | StatementKind::StorageDead(_) | StatementKind::Deinit(_) | StatementKind::Nop => {} - _ => self.cost += INSTR_COST, + _ => self.penalty += INSTR_COST, } } @@ -59,17 +60,17 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { // If the place doesn't actually need dropping, treat it like a regular goto. let ty = self.instantiate_ty(place.ty(self.callee_body, tcx).ty); if ty.needs_drop(tcx, self.param_env) { - self.cost += CALL_PENALTY; + self.penalty += CALL_PENALTY; if let UnwindAction::Cleanup(_) = unwind { - self.cost += LANDINGPAD_PENALTY; + self.penalty += LANDINGPAD_PENALTY; } } else { - self.cost += INSTR_COST; + self.penalty += INSTR_COST; } } TerminatorKind::Call { func: Operand::Constant(ref f), unwind, .. } => { let fn_ty = self.instantiate_ty(f.const_.ty()); - self.cost += if let ty::FnDef(def_id, _) = *fn_ty.kind() + self.penalty += if let ty::FnDef(def_id, _) = *fn_ty.kind() && tcx.intrinsic(def_id).is_some() { // Don't give intrinsics the extra penalty for calls @@ -78,23 +79,23 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { CALL_PENALTY }; if let UnwindAction::Cleanup(_) = unwind { - self.cost += LANDINGPAD_PENALTY; + self.penalty += LANDINGPAD_PENALTY; } } TerminatorKind::Assert { unwind, .. } => { - self.cost += CALL_PENALTY; + self.penalty += CALL_PENALTY; if let UnwindAction::Cleanup(_) = unwind { - self.cost += LANDINGPAD_PENALTY; + self.penalty += LANDINGPAD_PENALTY; } } - TerminatorKind::UnwindResume => self.cost += RESUME_PENALTY, + TerminatorKind::UnwindResume => self.penalty += RESUME_PENALTY, TerminatorKind::InlineAsm { unwind, .. } => { - self.cost += INSTR_COST; + self.penalty += INSTR_COST; if let UnwindAction::Cleanup(_) = unwind { - self.cost += LANDINGPAD_PENALTY; + self.penalty += LANDINGPAD_PENALTY; } } - _ => self.cost += INSTR_COST, + _ => self.penalty += INSTR_COST, } } } From 4236da52af3c6eeb4b1c926d932a31e1e53c8e77 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Mon, 17 Jun 2024 00:36:21 -0700 Subject: [PATCH 125/147] Give inlining bonuses to things that optimize out --- .../rustc_mir_transform/src/cost_checker.rs | 83 +++++++++++++---- .../issue-112509-slice-get-andthen-get.rs | 6 +- tests/crashes/123893.rs | 4 + ...p_forward.PreCodegen.after.panic-abort.mir | 77 ++++++++++++++- ..._forward.PreCodegen.after.panic-unwind.mir | 77 ++++++++++++++- .../loops.mapped.PreCodegen.after.mir | 79 ++++++++++++---- ...ated_loop.PreCodegen.after.panic-abort.mir | 93 ++++++++++++++----- 7 files changed, 355 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index bca4ef5b3d12..32c0d27f635c 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -1,3 +1,4 @@ +use rustc_middle::bug; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; @@ -6,6 +7,8 @@ const INSTR_COST: usize = 5; const CALL_PENALTY: usize = 25; const LANDINGPAD_PENALTY: usize = 50; const RESUME_PENALTY: usize = 45; +const LARGE_SWITCH_PENALTY: usize = 20; +const CONST_SWITCH_BONUS: usize = 10; /// Verify that the callee body is compatible with the caller. #[derive(Clone)] @@ -42,36 +45,49 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> { } impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { - fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) { - // Don't count StorageLive/StorageDead in the inlining cost. + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + // Most costs are in rvalues and terminators, not in statements. match statement.kind { - StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::Deinit(_) - | StatementKind::Nop => {} + StatementKind::Intrinsic(ref ndi) => { + self.penalty += match **ndi { + NonDivergingIntrinsic::Assume(..) => INSTR_COST, + NonDivergingIntrinsic::CopyNonOverlapping(..) => CALL_PENALTY, + }; + } + _ => self.super_statement(statement, location), + } + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, _location: Location) { + match rvalue { + Rvalue::NullaryOp(NullOp::UbChecks, ..) if !self.tcx.sess.ub_checks() => { + // If this is in optimized MIR it's because it's used later, + // so if we don't need UB checks this session, give a bonus + // here to offset the cost of the call later. + self.bonus += CALL_PENALTY; + } + // These are essentially constants that didn't end up in an Operand, + // so treat them as also being free. + Rvalue::NullaryOp(..) => {} _ => self.penalty += INSTR_COST, } } fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _: Location) { - let tcx = self.tcx; - match terminator.kind { - TerminatorKind::Drop { ref place, unwind, .. } => { + match &terminator.kind { + TerminatorKind::Drop { place, unwind, .. } => { // If the place doesn't actually need dropping, treat it like a regular goto. - let ty = self.instantiate_ty(place.ty(self.callee_body, tcx).ty); - if ty.needs_drop(tcx, self.param_env) { + let ty = self.instantiate_ty(place.ty(self.callee_body, self.tcx).ty); + if ty.needs_drop(self.tcx, self.param_env) { self.penalty += CALL_PENALTY; if let UnwindAction::Cleanup(_) = unwind { self.penalty += LANDINGPAD_PENALTY; } - } else { - self.penalty += INSTR_COST; } } - TerminatorKind::Call { func: Operand::Constant(ref f), unwind, .. } => { - let fn_ty = self.instantiate_ty(f.const_.ty()); - self.penalty += if let ty::FnDef(def_id, _) = *fn_ty.kind() - && tcx.intrinsic(def_id).is_some() + TerminatorKind::Call { func, unwind, .. } => { + self.penalty += if let Some((def_id, ..)) = func.const_fn_def() + && self.tcx.intrinsic(def_id).is_some() { // Don't give intrinsics the extra penalty for calls INSTR_COST @@ -82,8 +98,25 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { self.penalty += LANDINGPAD_PENALTY; } } - TerminatorKind::Assert { unwind, .. } => { - self.penalty += CALL_PENALTY; + TerminatorKind::SwitchInt { discr, targets } => { + if discr.constant().is_some() { + // Not only will this become a `Goto`, but likely other + // things will be removable as unreachable. + self.bonus += CONST_SWITCH_BONUS; + } else if targets.all_targets().len() > 3 { + // More than false/true/unreachable gets extra cost. + self.penalty += LARGE_SWITCH_PENALTY; + } else { + self.penalty += INSTR_COST; + } + } + TerminatorKind::Assert { unwind, msg, .. } => { + self.penalty += + if msg.is_optional_overflow_check() && !self.tcx.sess.overflow_checks() { + INSTR_COST + } else { + CALL_PENALTY + }; if let UnwindAction::Cleanup(_) = unwind { self.penalty += LANDINGPAD_PENALTY; } @@ -95,7 +128,17 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { self.penalty += LANDINGPAD_PENALTY; } } - _ => self.penalty += INSTR_COST, + TerminatorKind::Unreachable => { + self.bonus += INSTR_COST; + } + TerminatorKind::Goto { .. } | TerminatorKind::Return => {} + TerminatorKind::UnwindTerminate(..) => {} + kind @ (TerminatorKind::FalseUnwind { .. } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::Yield { .. } + | TerminatorKind::CoroutineDrop) => { + bug!("{kind:?} should not be in runtime MIR"); + } } } } diff --git a/tests/codegen/issues/issue-112509-slice-get-andthen-get.rs b/tests/codegen/issues/issue-112509-slice-get-andthen-get.rs index ae02c3fb79e7..aee2edd8dfad 100644 --- a/tests/codegen/issues/issue-112509-slice-get-andthen-get.rs +++ b/tests/codegen/issues/issue-112509-slice-get-andthen-get.rs @@ -3,8 +3,12 @@ // CHECK-LABEL: @write_u8_variant_a // CHECK-NEXT: {{.*}}: -// CHECK-NEXT: getelementptr // CHECK-NEXT: icmp ugt +// CHECK-NEXT: getelementptr +// CHECK-NEXT: select i1 {{.+}} null +// CHECK-NEXT: insertvalue +// CHECK-NEXT: insertvalue +// CHECK-NEXT: ret #[no_mangle] pub fn write_u8_variant_a(bytes: &mut [u8], buf: u8, offset: usize) -> Option<&mut [u8]> { let buf = buf.to_le_bytes(); diff --git a/tests/crashes/123893.rs b/tests/crashes/123893.rs index 137ae7835115..05237d002b85 100644 --- a/tests/crashes/123893.rs +++ b/tests/crashes/123893.rs @@ -11,6 +11,7 @@ fn generic_impl() { impl MagicTrait for T { const IS_BIG: bool = true; } + more_cost(); if T::IS_BIG { big_impl::(); } @@ -18,3 +19,6 @@ fn generic_impl() { #[inline(never)] fn big_impl() {} + +#[inline(never)] +fn more_cost() {} diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir index cfb9134a1f13..e31a8cb69370 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir @@ -4,12 +4,87 @@ fn step_forward(_1: u16, _2: usize) -> u16 { debug x => _1; debug n => _2; let mut _0: u16; + scope 1 (inlined ::forward) { + let mut _8: u16; + scope 2 { + } + scope 3 (inlined ::forward_checked) { + scope 4 { + scope 6 (inlined core::num::::checked_add) { + let mut _7: bool; + scope 7 { + } + scope 8 (inlined core::num::::overflowing_add) { + let mut _5: (u16, bool); + let _6: bool; + scope 9 { + } + } + } + } + scope 5 (inlined convert::num::ptr_try_from_impls:: for u16>::try_from) { + let mut _3: bool; + let mut _4: u16; + } + } + scope 10 (inlined Option::::is_none) { + scope 11 (inlined Option::::is_some) { + } + } + scope 12 (inlined core::num::::wrapping_add) { + } + } bb0: { - _0 = ::forward(move _1, move _2) -> [return: bb1, unwind unreachable]; + StorageLive(_4); + StorageLive(_3); + _3 = Gt(_2, const 65535_usize); + switchInt(move _3) -> [0: bb1, otherwise: bb5]; } bb1: { + _4 = _2 as u16 (IntToInt); + StorageDead(_3); + StorageLive(_6); + StorageLive(_5); + _5 = AddWithOverflow(_1, _4); + _6 = (_5.1: bool); + StorageDead(_5); + StorageLive(_7); + _7 = unlikely(move _6) -> [return: bb2, unwind unreachable]; + } + + bb2: { + switchInt(move _7) -> [0: bb3, otherwise: bb4]; + } + + bb3: { + StorageDead(_7); + StorageDead(_6); + goto -> bb7; + } + + bb4: { + StorageDead(_7); + StorageDead(_6); + goto -> bb6; + } + + bb5: { + StorageDead(_3); + goto -> bb6; + } + + bb6: { + assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind unreachable]; + } + + bb7: { + StorageLive(_8); + _8 = _2 as u16 (IntToInt); + _0 = Add(_1, _8); + StorageDead(_8); + StorageDead(_4); return; } } diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir index cacc1224aba4..8cc9be27e21d 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir @@ -4,12 +4,87 @@ fn step_forward(_1: u16, _2: usize) -> u16 { debug x => _1; debug n => _2; let mut _0: u16; + scope 1 (inlined ::forward) { + let mut _8: u16; + scope 2 { + } + scope 3 (inlined ::forward_checked) { + scope 4 { + scope 6 (inlined core::num::::checked_add) { + let mut _7: bool; + scope 7 { + } + scope 8 (inlined core::num::::overflowing_add) { + let mut _5: (u16, bool); + let _6: bool; + scope 9 { + } + } + } + } + scope 5 (inlined convert::num::ptr_try_from_impls:: for u16>::try_from) { + let mut _3: bool; + let mut _4: u16; + } + } + scope 10 (inlined Option::::is_none) { + scope 11 (inlined Option::::is_some) { + } + } + scope 12 (inlined core::num::::wrapping_add) { + } + } bb0: { - _0 = ::forward(move _1, move _2) -> [return: bb1, unwind continue]; + StorageLive(_4); + StorageLive(_3); + _3 = Gt(_2, const 65535_usize); + switchInt(move _3) -> [0: bb1, otherwise: bb5]; } bb1: { + _4 = _2 as u16 (IntToInt); + StorageDead(_3); + StorageLive(_6); + StorageLive(_5); + _5 = AddWithOverflow(_1, _4); + _6 = (_5.1: bool); + StorageDead(_5); + StorageLive(_7); + _7 = unlikely(move _6) -> [return: bb2, unwind unreachable]; + } + + bb2: { + switchInt(move _7) -> [0: bb3, otherwise: bb4]; + } + + bb3: { + StorageDead(_7); + StorageDead(_6); + goto -> bb7; + } + + bb4: { + StorageDead(_7); + StorageDead(_6); + goto -> bb6; + } + + bb5: { + StorageDead(_3); + goto -> bb6; + } + + bb6: { + assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind continue]; + } + + bb7: { + StorageLive(_8); + _8 = _2 as u16 (IntToInt); + _0 = Add(_1, _8); + StorageDead(_8); + StorageDead(_4); return; } } diff --git a/tests/mir-opt/pre-codegen/loops.mapped.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.mapped.PreCodegen.after.mir index b800a1be22ba..51d41e9ff051 100644 --- a/tests/mir-opt/pre-codegen/loops.mapped.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.mapped.PreCodegen.after.mir @@ -7,14 +7,30 @@ fn mapped(_1: impl Iterator, _2: impl Fn(T) -> U) -> () { let mut _3: std::iter::Map, impl Fn(T) -> U>; let mut _4: std::iter::Map, impl Fn(T) -> U>; let mut _5: &mut std::iter::Map, impl Fn(T) -> U>; - let mut _6: std::option::Option; - let mut _7: isize; - let _9: (); + let mut _13: std::option::Option; + let _15: (); scope 1 { debug iter => _4; - let _8: U; + let _14: U; scope 2 { - debug x => _8; + debug x => _14; + } + scope 4 (inlined , impl Fn(T) -> U> as Iterator>::next) { + debug self => _5; + let mut _6: &mut impl Iterator; + let mut _7: std::option::Option; + let mut _8: &mut impl Fn(T) -> U; + scope 5 (inlined Option::::map:: U>) { + debug self => _7; + debug f => _8; + let mut _9: isize; + let _10: T; + let mut _11: (T,); + let mut _12: U; + scope 6 { + debug x => _10; + } + } } } scope 3 (inlined , impl Fn(T) -> U> as IntoIterator>::into_iter) { @@ -32,20 +48,30 @@ fn mapped(_1: impl Iterator, _2: impl Fn(T) -> U) -> () { } bb2: { - StorageLive(_6); - StorageLive(_5); + StorageLive(_13); _5 = &mut _4; - _6 = , impl Fn(T) -> U> as Iterator>::next(move _5) -> [return: bb3, unwind: bb9]; + StorageLive(_8); + StorageLive(_7); + StorageLive(_6); + _6 = &mut (_4.0: impl Iterator); + _7 = as Iterator>::next(move _6) -> [return: bb3, unwind: bb10]; } bb3: { - StorageDead(_5); - _7 = discriminant(_6); - switchInt(move _7) -> [0: bb4, 1: bb6, otherwise: bb8]; + StorageDead(_6); + _8 = &mut (_4.1: impl Fn(T) -> U); + StorageLive(_9); + StorageLive(_10); + _9 = discriminant(_7); + switchInt(move _9) -> [0: bb4, 1: bb6, otherwise: bb9]; } bb4: { - StorageDead(_6); + StorageDead(_10); + StorageDead(_9); + StorageDead(_7); + StorageDead(_8); + StorageDead(_13); drop(_4) -> [return: bb5, unwind continue]; } @@ -55,24 +81,39 @@ fn mapped(_1: impl Iterator, _2: impl Fn(T) -> U) -> () { } bb6: { - _8 = move ((_6 as Some).0: U); - _9 = opaque::(move _8) -> [return: bb7, unwind: bb9]; + _10 = move ((_7 as Some).0: T); + StorageLive(_12); + StorageLive(_11); + _11 = (_10,); + _12 = <&mut impl Fn(T) -> U as FnOnce<(T,)>>::call_once(move _8, move _11) -> [return: bb7, unwind: bb10]; } bb7: { - StorageDead(_6); - goto -> bb2; + StorageDead(_11); + _13 = Option::::Some(move _12); + StorageDead(_12); + StorageDead(_10); + StorageDead(_9); + StorageDead(_7); + StorageDead(_8); + _14 = move ((_13 as Some).0: U); + _15 = opaque::(move _14) -> [return: bb8, unwind: bb10]; } bb8: { + StorageDead(_13); + goto -> bb2; + } + + bb9: { unreachable; } - bb9 (cleanup): { - drop(_4) -> [return: bb10, unwind terminate(cleanup)]; + bb10 (cleanup): { + drop(_4) -> [return: bb11, unwind terminate(cleanup)]; } - bb10 (cleanup): { + bb11 (cleanup): { resume; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir index 8eb102e68f36..35a1c783bf23 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir @@ -7,19 +7,44 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let mut _11: std::slice::Iter<'_, T>; let mut _12: std::iter::Enumerate>; let mut _13: std::iter::Enumerate>; - let mut _14: &mut std::iter::Enumerate>; - let mut _15: std::option::Option<(usize, &T)>; - let mut _16: isize; - let mut _19: &impl Fn(usize, &T); - let mut _20: (usize, &T); - let _21: (); + let mut _21: std::option::Option<(usize, &T)>; + let mut _24: &impl Fn(usize, &T); + let mut _25: (usize, &T); + let _26: (); scope 1 { debug iter => _13; - let _17: usize; - let _18: &T; + let _22: usize; + let _23: &T; scope 2 { - debug i => _17; - debug x => _18; + debug i => _22; + debug x => _23; + } + scope 17 (inlined > as Iterator>::next) { + let mut _14: &mut std::slice::Iter<'_, T>; + let mut _15: std::option::Option<&T>; + let mut _19: (usize, bool); + let mut _20: (usize, &T); + scope 18 { + let _18: usize; + scope 23 { + } + } + scope 19 { + scope 20 { + scope 26 (inlined as FromResidual>::from_residual) { + } + } + } + scope 21 { + scope 22 { + } + } + scope 24 (inlined as Try>::branch) { + let mut _16: isize; + let _17: &T; + scope 25 { + } + } } } scope 3 (inlined core::slice::::iter) { @@ -107,20 +132,28 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb4: { + StorageLive(_21); + StorageLive(_18); + StorageLive(_19); StorageLive(_15); StorageLive(_14); - _14 = &mut _13; - _15 = > as Iterator>::next(move _14) -> [return: bb5, unwind unreachable]; + _14 = &mut (_13.0: std::slice::Iter<'_, T>); + _15 = as Iterator>::next(move _14) -> [return: bb5, unwind unreachable]; } bb5: { StorageDead(_14); + StorageLive(_16); _16 = discriminant(_15); - switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; + switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb11]; } bb6: { + StorageDead(_16); StorageDead(_15); + StorageDead(_19); + StorageDead(_18); + StorageDead(_21); StorageDead(_13); drop(_2) -> [return: bb7, unwind unreachable]; } @@ -130,23 +163,39 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb8: { - _17 = (((_15 as Some).0: (usize, &T)).0: usize); - _18 = (((_15 as Some).0: (usize, &T)).1: &T); - StorageLive(_19); - _19 = &_2; - StorageLive(_20); - _20 = (_17, _18); - _21 = >::call(move _19, move _20) -> [return: bb9, unwind unreachable]; + _17 = move ((_15 as Some).0: &T); + StorageDead(_16); + StorageDead(_15); + _18 = (_13.1: usize); + _19 = AddWithOverflow((_13.1: usize), const 1_usize); + assert(!move (_19.1: bool), "attempt to compute `{} + {}`, which would overflow", (_13.1: usize), const 1_usize) -> [success: bb9, unwind unreachable]; } bb9: { + (_13.1: usize) = move (_19.0: usize); + StorageLive(_20); + _20 = (_18, _17); + _21 = Option::<(usize, &T)>::Some(move _20); StorageDead(_20); StorageDead(_19); - StorageDead(_15); - goto -> bb4; + StorageDead(_18); + _22 = (((_21 as Some).0: (usize, &T)).0: usize); + _23 = (((_21 as Some).0: (usize, &T)).1: &T); + StorageLive(_24); + _24 = &_2; + StorageLive(_25); + _25 = (_22, _23); + _26 = >::call(move _24, move _25) -> [return: bb10, unwind unreachable]; } bb10: { + StorageDead(_25); + StorageDead(_24); + StorageDead(_21); + goto -> bb4; + } + + bb11: { unreachable; } } From eac6b2910a5513a3d395814aa20739cabd388723 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Mon, 17 Jun 2024 01:49:09 -0700 Subject: [PATCH 126/147] Shrink some slice iterator MIR --- library/core/src/slice/iter/macros.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index 0b8ff5cc0124..c2a381946441 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -103,7 +103,8 @@ macro_rules! iterator { // so this new pointer is inside `self` and thus guaranteed to be non-null. unsafe { if_zst!(mut self, - len => *len = len.unchecked_sub(offset), + // Using the intrinsic directly avoids emitting a UbCheck + len => *len = crate::intrinsics::unchecked_sub(*len, offset), _end => self.ptr = self.ptr.add(offset), ); } @@ -119,7 +120,8 @@ macro_rules! iterator { // SAFETY: By our precondition, `offset` can be at most the // current length, so the subtraction can never overflow. len => unsafe { - *len = len.unchecked_sub(offset); + // Using the intrinsic directly avoids emitting a UbCheck + *len = crate::intrinsics::unchecked_sub(*len, offset); self.ptr }, // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`, From 388aea471f0c311634622ec788f14cfca3459221 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 19 Jun 2024 12:09:56 +1000 Subject: [PATCH 127/147] More status-quo tests for the `#[coverage(..)]` attribute These tests reveal some inconsistencies that are tracked by . --- tests/ui/coverage-attr/bad-syntax.rs | 8 +- tests/ui/coverage-attr/name-value.rs | 64 +++++++ tests/ui/coverage-attr/name-value.stderr | 219 +++++++++++++++++++++++ tests/ui/coverage-attr/word-only.rs | 54 ++++++ tests/ui/coverage-attr/word-only.stderr | 57 ++++++ 5 files changed, 398 insertions(+), 4 deletions(-) create mode 100644 tests/ui/coverage-attr/name-value.rs create mode 100644 tests/ui/coverage-attr/name-value.stderr create mode 100644 tests/ui/coverage-attr/word-only.rs create mode 100644 tests/ui/coverage-attr/word-only.stderr diff --git a/tests/ui/coverage-attr/bad-syntax.rs b/tests/ui/coverage-attr/bad-syntax.rs index 8783714992b7..127179877e55 100644 --- a/tests/ui/coverage-attr/bad-syntax.rs +++ b/tests/ui/coverage-attr/bad-syntax.rs @@ -3,13 +3,13 @@ // Tests the error messages produced (or not produced) by various unusual // uses of the `#[coverage(..)]` attribute. -// FIXME(#84605): Multiple coverage attributes with the same value are useless, +// FIXME(#126658): Multiple coverage attributes with the same value are useless, // and should probably produce a diagnostic. #[coverage(off)] #[coverage(off)] fn multiple_consistent() {} -// FIXME(#84605): When there are multiple inconsistent coverage attributes, +// FIXME(#126658): When there are multiple inconsistent coverage attributes, // it's unclear which one will prevail. #[coverage(off)] #[coverage(on)] @@ -18,7 +18,7 @@ fn multiple_inconsistent() {} #[coverage] //~ ERROR expected `coverage(off)` or `coverage(on)` fn bare_word() {} -// FIXME(#84605): This shows as multiple different errors, one of which suggests +// FIXME(#126658): This shows as multiple different errors, one of which suggests // writing bare `#[coverage]`, which is not allowed. #[coverage = true] //~^ ERROR expected `coverage(off)` or `coverage(on)` @@ -48,7 +48,7 @@ fn bogus_word_after() {} #[coverage(off,)] fn comma_after() {} -// FIXME(#84605): This shows as multiple different errors. +// FIXME(#126658): This shows as multiple different errors. #[coverage(,off)] //~^ ERROR expected identifier, found `,` //~| HELP remove this comma diff --git a/tests/ui/coverage-attr/name-value.rs b/tests/ui/coverage-attr/name-value.rs new file mode 100644 index 000000000000..24c329780c59 --- /dev/null +++ b/tests/ui/coverage-attr/name-value.rs @@ -0,0 +1,64 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 + +// Demonstrates the diagnostics produced when using the syntax +// `#[coverage = "off"]`, which should not be allowed. +// +// The syntax is tested both in places that can have a coverage attribute, +// and in places that cannot have a coverage attribute, to demonstrate the +// interaction between multiple errors. + +// FIXME(#126658): The error messages for using this syntax are inconsistent +// with the error message in other cases. They also sometimes appear together +// with other errors, and they suggest using the incorrect `#[coverage]` syntax. + +#[coverage = "off"] //~ ERROR malformed `coverage` attribute input +mod my_mod {} + +mod my_mod_inner { + #![coverage = "off"] //~ ERROR malformed `coverage` attribute input +} + +#[coverage = "off"] +//~^ ERROR `#[coverage]` must be applied to coverable code +//~| ERROR malformed `coverage` attribute input +struct MyStruct; + +#[coverage = "off"] //~ ERROR malformed `coverage` attribute input +impl MyStruct { + #[coverage = "off"] + //~^ ERROR `#[coverage]` must be applied to coverable code + //~| ERROR malformed `coverage` attribute input + const X: u32 = 7; +} + +#[coverage = "off"] //~ ERROR malformed `coverage` attribute input +trait MyTrait { + #[coverage = "off"] + //~^ ERROR `#[coverage]` must be applied to coverable code + //~| ERROR malformed `coverage` attribute input + const X: u32; + + #[coverage = "off"] + //~^ ERROR `#[coverage]` must be applied to coverable code + //~| ERROR malformed `coverage` attribute input + type T; +} + +#[coverage = "off"] //~ ERROR malformed `coverage` attribute input +impl MyTrait for MyStruct { + #[coverage = "off"] + //~^ ERROR `#[coverage]` must be applied to coverable code + //~| ERROR malformed `coverage` attribute input + const X: u32 = 8; + + #[coverage = "off"] + //~^ ERROR `#[coverage]` must be applied to coverable code + //~| ERROR malformed `coverage` attribute input + type T = (); +} + +#[coverage = "off"] +//~^ ERROR expected `coverage(off)` or `coverage(on)` +//~| ERROR malformed `coverage` attribute input +fn main() {} diff --git a/tests/ui/coverage-attr/name-value.stderr b/tests/ui/coverage-attr/name-value.stderr new file mode 100644 index 000000000000..90bc3a3b53b3 --- /dev/null +++ b/tests/ui/coverage-attr/name-value.stderr @@ -0,0 +1,219 @@ +error: malformed `coverage` attribute input + --> $DIR/name-value.rs:15:1 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(on|off)] + | ~~~~~~~~~~~~~~~~~~~ +LL | #[coverage] + | ~~~~~~~~~~~ + +error: malformed `coverage` attribute input + --> $DIR/name-value.rs:19:5 + | +LL | #![coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #![coverage(on|off)] + | ~~~~~~~~~~~~~~~~~~~~ +LL | #![coverage] + | ~~~~~~~~~~~~ + +error: malformed `coverage` attribute input + --> $DIR/name-value.rs:22:1 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(on|off)] + | +LL | #[coverage] + | + +error: malformed `coverage` attribute input + --> $DIR/name-value.rs:29:5 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(on|off)] + | +LL | #[coverage] + | + +error: malformed `coverage` attribute input + --> $DIR/name-value.rs:27:1 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(on|off)] + | ~~~~~~~~~~~~~~~~~~~ +LL | #[coverage] + | ~~~~~~~~~~~ + +error: malformed `coverage` attribute input + --> $DIR/name-value.rs:37:5 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(on|off)] + | +LL | #[coverage] + | + +error: malformed `coverage` attribute input + --> $DIR/name-value.rs:42:5 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(on|off)] + | +LL | #[coverage] + | + +error: malformed `coverage` attribute input + --> $DIR/name-value.rs:35:1 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(on|off)] + | ~~~~~~~~~~~~~~~~~~~ +LL | #[coverage] + | ~~~~~~~~~~~ + +error: malformed `coverage` attribute input + --> $DIR/name-value.rs:50:5 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(on|off)] + | +LL | #[coverage] + | + +error: malformed `coverage` attribute input + --> $DIR/name-value.rs:55:5 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(on|off)] + | +LL | #[coverage] + | + +error: malformed `coverage` attribute input + --> $DIR/name-value.rs:48:1 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(on|off)] + | ~~~~~~~~~~~~~~~~~~~ +LL | #[coverage] + | ~~~~~~~~~~~ + +error: malformed `coverage` attribute input + --> $DIR/name-value.rs:61:1 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(on|off)] + | +LL | #[coverage] + | + +error[E0788]: `#[coverage]` must be applied to coverable code + --> $DIR/name-value.rs:22:1 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ +... +LL | struct MyStruct; + | ---------------- not coverable code + +error[E0788]: `#[coverage]` must be applied to coverable code + --> $DIR/name-value.rs:37:5 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ +... +LL | const X: u32; + | ------------- not coverable code + +error[E0788]: `#[coverage]` must be applied to coverable code + --> $DIR/name-value.rs:42:5 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ +... +LL | type T; + | ------- not coverable code + +error[E0788]: `#[coverage]` must be applied to coverable code + --> $DIR/name-value.rs:29:5 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ +... +LL | const X: u32 = 7; + | ----------------- not coverable code + +error[E0788]: `#[coverage]` must be applied to coverable code + --> $DIR/name-value.rs:50:5 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ +... +LL | const X: u32 = 8; + | ----------------- not coverable code + +error[E0788]: `#[coverage]` must be applied to coverable code + --> $DIR/name-value.rs:55:5 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ +... +LL | type T = (); + | ------------ not coverable code + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/name-value.rs:61:1 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 19 previous errors + +For more information about this error, try `rustc --explain E0788`. diff --git a/tests/ui/coverage-attr/word-only.rs b/tests/ui/coverage-attr/word-only.rs new file mode 100644 index 000000000000..5c723b1b6b61 --- /dev/null +++ b/tests/ui/coverage-attr/word-only.rs @@ -0,0 +1,54 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 + +// Demonstrates the diagnostics produced when using the syntax `#[coverage]`, +// which should not be allowed. +// +// The syntax is tested both in places that can have a coverage attribute, +// and in places that cannot have a coverage attribute, to demonstrate the +// interaction between multiple errors. + +// FIXME(#126658): The error messages for using this syntax give the impression +// that it is legal, even though it should never be legal. + +// FIXME(#126658): This is silently allowed, but should not be. +#[coverage] +mod my_mod {} + +// FIXME(#126658): This is silently allowed, but should not be. +mod my_mod_inner { + #![coverage] +} + +#[coverage] //~ ERROR `#[coverage]` must be applied to coverable code +struct MyStruct; + +// FIXME(#126658): This is silently allowed, but should not be. +#[coverage] +impl MyStruct { + #[coverage] //~ ERROR `#[coverage]` must be applied to coverable code + const X: u32 = 7; +} + +// FIXME(#126658): This is silently allowed, but should not be. +#[coverage] +trait MyTrait { + #[coverage] //~ ERROR `#[coverage]` must be applied to coverable code + const X: u32; + + #[coverage] //~ ERROR `#[coverage]` must be applied to coverable code + type T; +} + +// FIXME(#126658): This is silently allowed, but should not be. +#[coverage] +impl MyTrait for MyStruct { + #[coverage] //~ ERROR `#[coverage]` must be applied to coverable code + const X: u32 = 8; + + #[coverage] //~ ERROR `#[coverage]` must be applied to coverable code + type T = (); +} + +#[coverage] //~ ERROR expected `coverage(off)` or `coverage(on)` +fn main() {} diff --git a/tests/ui/coverage-attr/word-only.stderr b/tests/ui/coverage-attr/word-only.stderr new file mode 100644 index 000000000000..bcafc23bc8d7 --- /dev/null +++ b/tests/ui/coverage-attr/word-only.stderr @@ -0,0 +1,57 @@ +error[E0788]: `#[coverage]` must be applied to coverable code + --> $DIR/word-only.rs:23:1 + | +LL | #[coverage] + | ^^^^^^^^^^^ +LL | struct MyStruct; + | ---------------- not coverable code + +error[E0788]: `#[coverage]` must be applied to coverable code + --> $DIR/word-only.rs:36:5 + | +LL | #[coverage] + | ^^^^^^^^^^^ +LL | const X: u32; + | ------------- not coverable code + +error[E0788]: `#[coverage]` must be applied to coverable code + --> $DIR/word-only.rs:39:5 + | +LL | #[coverage] + | ^^^^^^^^^^^ +LL | type T; + | ------- not coverable code + +error[E0788]: `#[coverage]` must be applied to coverable code + --> $DIR/word-only.rs:29:5 + | +LL | #[coverage] + | ^^^^^^^^^^^ +LL | const X: u32 = 7; + | ----------------- not coverable code + +error[E0788]: `#[coverage]` must be applied to coverable code + --> $DIR/word-only.rs:46:5 + | +LL | #[coverage] + | ^^^^^^^^^^^ +LL | const X: u32 = 8; + | ----------------- not coverable code + +error[E0788]: `#[coverage]` must be applied to coverable code + --> $DIR/word-only.rs:49:5 + | +LL | #[coverage] + | ^^^^^^^^^^^ +LL | type T = (); + | ------------ not coverable code + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/word-only.rs:53:1 + | +LL | #[coverage] + | ^^^^^^^^^^^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0788`. From ebb3aa0d46ff7c216a886c5be0e91ff2619b083d Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 19 Jun 2024 17:58:55 +1000 Subject: [PATCH 128/147] Also test that yes/no must be bare words --- tests/ui/coverage-attr/subword.rs | 19 +++++++++++++++++++ tests/ui/coverage-attr/subword.stderr | 26 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tests/ui/coverage-attr/subword.rs create mode 100644 tests/ui/coverage-attr/subword.stderr diff --git a/tests/ui/coverage-attr/subword.rs b/tests/ui/coverage-attr/subword.rs new file mode 100644 index 000000000000..98b8c25113cb --- /dev/null +++ b/tests/ui/coverage-attr/subword.rs @@ -0,0 +1,19 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 + +// Check that yes/no in `#[coverage(yes)]` and `#[coverage(no)]` must be bare +// words, not part of a more complicated substructure. + +#[coverage(yes(milord))] //~ ERROR expected `coverage(off)` or `coverage(on)` +fn yes_list() {} + +#[coverage(no(milord))] //~ ERROR expected `coverage(off)` or `coverage(on)` +fn no_list() {} + +#[coverage(yes = "milord")] //~ ERROR expected `coverage(off)` or `coverage(on)` +fn yes_key() {} + +#[coverage(no = "milord")] //~ ERROR expected `coverage(off)` or `coverage(on)` +fn no_key() {} + +fn main() {} diff --git a/tests/ui/coverage-attr/subword.stderr b/tests/ui/coverage-attr/subword.stderr new file mode 100644 index 000000000000..561573b8ada6 --- /dev/null +++ b/tests/ui/coverage-attr/subword.stderr @@ -0,0 +1,26 @@ +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/subword.rs:7:1 + | +LL | #[coverage(yes(milord))] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/subword.rs:10:1 + | +LL | #[coverage(no(milord))] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/subword.rs:13:1 + | +LL | #[coverage(yes = "milord")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/subword.rs:16:1 + | +LL | #[coverage(no = "milord")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + From 53f10b936bc7052b7616f5a9e7f7f81c78f79f88 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 20 Jun 2024 09:20:45 +0000 Subject: [PATCH 129/147] Add opaque type test --- ...-method-resolution-opaque-type.next.stderr | 9 +++++ ...e-method-resolution-opaque-type.old.stderr | 36 +++++++++++++++++++ ...rm-before-method-resolution-opaque-type.rs | 29 +++++++++++++++ .../norm-before-method-resolution.rs | 12 ++++--- 4 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.next.stderr create mode 100644 tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.old.stderr create mode 100644 tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.rs diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.next.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.next.stderr new file mode 100644 index 000000000000..72646b7bc768 --- /dev/null +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.next.stderr @@ -0,0 +1,9 @@ +error[E0284]: type annotations needed: cannot satisfy `Foo == _` + --> $DIR/norm-before-method-resolution-opaque-type.rs:16:19 + | +LL | fn weird_bound(x: &>::Out) -> X + | ^ cannot satisfy `Foo == _` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.old.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.old.stderr new file mode 100644 index 000000000000..dbd0d5dc7332 --- /dev/null +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.old.stderr @@ -0,0 +1,36 @@ +error: item does not constrain `Foo::{opaque#0}`, but has it in its signature + --> $DIR/norm-before-method-resolution-opaque-type.rs:16:4 + | +LL | fn weird_bound(x: &>::Out) -> X + | ^^^^^^^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/norm-before-method-resolution-opaque-type.rs:13:12 + | +LL | type Foo = impl Sized; + | ^^^^^^^^^^ + +error: unconstrained opaque type + --> $DIR/norm-before-method-resolution-opaque-type.rs:13:12 + | +LL | type Foo = impl Sized; + | ^^^^^^^^^^ + | + = note: `Foo` must be used in combination with a concrete type within the same module + +error[E0507]: cannot move out of `*x` which is behind a shared reference + --> $DIR/norm-before-method-resolution-opaque-type.rs:23:13 + | +LL | let x = *x; + | ^^ move occurs because `*x` has type `>::Out`, which does not implement the `Copy` trait + | +help: consider removing the dereference here + | +LL - let x = *x; +LL + let x = x; + | + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.rs new file mode 100644 index 000000000000..cf752f814c90 --- /dev/null +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.rs @@ -0,0 +1,29 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver + +#![feature(type_alias_impl_trait)] +trait Trait<'a> { + type Out; +} + +impl<'a, T> Trait<'a> for T { + type Out = T; +} + +type Foo = impl Sized; +//[old]~^ ERROR: unconstrained opaque type + +fn weird_bound(x: &>::Out) -> X +//[old]~^ ERROR: item does not constrain +//[next]~^^ ERROR: cannot satisfy `Foo == _` +where + for<'a> X: Trait<'a>, + for<'a> >::Out<()>: Copy, +{ + let x = *x; //[old]~ ERROR: cannot move out of `*x` + todo!(); +} + +fn main() { + let _: () = weird_bound(&()); +} diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.rs index e1aa1babdbb1..f0e13a74b2c8 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.rs +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.rs @@ -1,6 +1,6 @@ //@ check-pass -// Should pass, but we normalize and check bounds before we resolve the generics +// We normalize and check bounds before we resolve the generics // of the function (which we know because of the return type). trait Trait<'a> { @@ -12,10 +12,12 @@ impl<'a, T> Trait<'a> for T { } fn weird_bound() -> X - where - for<'a> X: Trait<'a>, - for<'a> >::Out: Copy -{ todo!() } +where + for<'a> X: Trait<'a>, + for<'a> >::Out: Copy, +{ + todo!() +} fn main() { let _: () = weird_bound(); From 1e42bb606d9abcfa22ee248e3a8598f813632418 Mon Sep 17 00:00:00 2001 From: bohan Date: Thu, 20 Jun 2024 19:44:36 +0800 Subject: [PATCH 130/147] collect attrs in const block expr --- compiler/rustc_resolve/src/def_collector.rs | 3 +++ tests/ui/resolve/path-attr-in-const-block.rs | 9 +++++++++ tests/ui/resolve/path-attr-in-const-block.stderr | 8 ++++++++ 3 files changed, 20 insertions(+) create mode 100644 tests/ui/resolve/path-attr-in-const-block.rs create mode 100644 tests/ui/resolve/path-attr-in-const-block.stderr diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index fb6e55f2b7bd..60789c083133 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -343,6 +343,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span) } ExprKind::ConstBlock(ref constant) => { + for attr in &expr.attrs { + visit::walk_attribute(self, attr); + } let def = self.create_def( constant.id, kw::Empty, diff --git a/tests/ui/resolve/path-attr-in-const-block.rs b/tests/ui/resolve/path-attr-in-const-block.rs new file mode 100644 index 000000000000..076511d26d6d --- /dev/null +++ b/tests/ui/resolve/path-attr-in-const-block.rs @@ -0,0 +1,9 @@ +// issue#126516 +// issue#126647 + +fn main() { + const { + #![path = foo!()] + //~^ ERROR: cannot find macro `foo` in this scope + } +} diff --git a/tests/ui/resolve/path-attr-in-const-block.stderr b/tests/ui/resolve/path-attr-in-const-block.stderr new file mode 100644 index 000000000000..8f9e58157c80 --- /dev/null +++ b/tests/ui/resolve/path-attr-in-const-block.stderr @@ -0,0 +1,8 @@ +error: cannot find macro `foo` in this scope + --> $DIR/path-attr-in-const-block.rs:6:19 + | +LL | #![path = foo!()] + | ^^^ + +error: aborting due to 1 previous error + From b30ef41833bef87fe6cca7ec3717380c300150ea Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 20 Jun 2024 14:06:37 +0200 Subject: [PATCH 131/147] Ignore arm targets as well for `run-make/rustdoc-io-error` tests --- tests/run-make/rustdoc-io-error/rmake.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run-make/rustdoc-io-error/rmake.rs b/tests/run-make/rustdoc-io-error/rmake.rs index d47d1cfe7274..d60e4438e6f2 100644 --- a/tests/run-make/rustdoc-io-error/rmake.rs +++ b/tests/run-make/rustdoc-io-error/rmake.rs @@ -6,8 +6,8 @@ // permissions so that it is not writable. We have to take special care to set // the permissions back to normal so that it's able to be deleted later. -// On windows, the `set_readonly` functions doesn't work on folders. -//@ ignore-windows +//@ ignore-windows - the `set_readonly` functions doesn't work on folders. +//@ ignore-arm - weird file perms on armhf-gnu use run_make_support::{path, rustdoc}; use std::fs; From a656bb6eb26d9ba63eac5f619b885230b97e8a4a Mon Sep 17 00:00:00 2001 From: Ana Hobden Date: Wed, 19 Jun 2024 11:57:03 -0700 Subject: [PATCH 132/147] Specify target for inaccessible-temp-dir rmake test --- tests/run-make/inaccessible-temp-dir/rmake.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/run-make/inaccessible-temp-dir/rmake.rs b/tests/run-make/inaccessible-temp-dir/rmake.rs index be24e47b6dec..c6bfae4cc01d 100644 --- a/tests/run-make/inaccessible-temp-dir/rmake.rs +++ b/tests/run-make/inaccessible-temp-dir/rmake.rs @@ -19,7 +19,7 @@ // Reason: `set_readonly` has no effect on directories // and does not prevent modification. -use run_make_support::{fs_wrapper, rustc, test_while_readonly}; +use run_make_support::{fs_wrapper, rustc, target, test_while_readonly}; fn main() { // Create an inaccessible directory. @@ -28,6 +28,7 @@ fn main() { // Run rustc with `-Z temps-dir` set to a directory *inside* the inaccessible one, // so that it can't create `tmp`. rustc() + .target(&target()) .input("program.rs") .arg("-Ztemps-dir=inaccessible/tmp") .run_fail() From 4d3b617911f5903c5b4e6189fa3dea99734e1bc5 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 16 Jun 2024 00:53:00 +0300 Subject: [PATCH 133/147] rustc_span: Optimize span parent get/set methods --- compiler/rustc_span/src/lib.rs | 11 +-- compiler/rustc_span/src/span_encoding.rs | 94 +++++++++++++++++------- 2 files changed, 70 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 99ac973497e1..b3f3899542d4 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -525,8 +525,9 @@ impl SpanData { fn with_ctxt(&self, ctxt: SyntaxContext) -> Span { Span::new(self.lo, self.hi, ctxt, self.parent) } + /// Avoid if possible, `Span::with_parent` should be preferred. #[inline] - pub fn with_parent(&self, parent: Option) -> Span { + fn with_parent(&self, parent: Option) -> Span { Span::new(self.lo, self.hi, self.ctxt, parent) } /// Returns `true` if this is a dummy span with any hygienic context. @@ -580,14 +581,6 @@ impl Span { pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span { self.map_ctxt(|_| ctxt) } - #[inline] - pub fn parent(self) -> Option { - self.data().parent - } - #[inline] - pub fn with_parent(self, ctxt: Option) -> Span { - self.data().with_parent(ctxt) - } #[inline] pub fn is_visible(self, sm: &SourceMap) -> bool { diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index 116056a69c1f..53d7b7511a62 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -257,17 +257,17 @@ impl Span { std::mem::swap(&mut lo, &mut hi); } - // Small len may enable one of fully inline formats (or may not). + // Small len and ctxt may enable one of fully inline formats (or may not). let (len, ctxt32) = (hi.0 - lo.0, ctxt.as_u32()); - if len <= MAX_LEN { - if ctxt32 <= MAX_CTXT && parent.is_none() { - return InlineCtxt::span(lo.0, len as u16, ctxt32 as u16); - } else if ctxt32 == 0 - && let Some(parent) = parent - && let parent32 = parent.local_def_index.as_u32() - && parent32 <= MAX_CTXT - { - return InlineParent::span(lo.0, len as u16, parent32 as u16); + if len <= MAX_LEN && ctxt32 <= MAX_CTXT { + match parent { + None => return InlineCtxt::span(lo.0, len as u16, ctxt32 as u16), + Some(parent) => { + let parent32 = parent.local_def_index.as_u32(); + if ctxt32 == 0 && parent32 <= MAX_CTXT { + return InlineParent::span(lo.0, len as u16, parent32 as u16); + } + } } } @@ -322,29 +322,28 @@ impl Span { } } - // For optimization we are interested in cases in which the context is inline and the context - // update doesn't change format. All non-inline or format changing scenarios require accessing - // interner and can fall back to `Span::new`. #[inline] - pub fn map_ctxt(self, update: impl FnOnce(SyntaxContext) -> SyntaxContext) -> Span { - match_span_kind! { + pub fn map_ctxt(self, map: impl FnOnce(SyntaxContext) -> SyntaxContext) -> Span { + let data = match_span_kind! { self, InlineCtxt(span) => { - let updated_ctxt32 = update(SyntaxContext::from_u16(span.ctxt)).as_u32(); - // Any small new context including zero will preserve the format. - return if updated_ctxt32 <= MAX_CTXT { - InlineCtxt::span(span.lo, span.len, updated_ctxt32 as u16) + // This format occurs 1-2 orders of magnitude more often than others (#125017), + // so it makes sense to micro-optimize it to avoid `span.data()` and `Span::new()`. + let new_ctxt = map(SyntaxContext::from_u16(span.ctxt)); + let new_ctxt32 = new_ctxt.as_u32(); + return if new_ctxt32 <= MAX_CTXT { + // Any small new context including zero will preserve the format. + InlineCtxt::span(span.lo, span.len, new_ctxt32 as u16) } else { - span.data().with_ctxt(SyntaxContext::from_u32(updated_ctxt32)) + span.data().with_ctxt(new_ctxt) }; }, - InlineParent(_span) => {}, - PartiallyInterned(_span) => {}, - Interned(_span) => {}, - } + InlineParent(span) => span.data(), + PartiallyInterned(span) => span.data(), + Interned(span) => span.data(), + }; - let data = self.data_untracked(); - data.with_ctxt(update(data.ctxt)) + data.with_ctxt(map(data.ctxt)) } // Returns either syntactic context, if it can be retrieved without taking the interner lock, @@ -381,6 +380,49 @@ impl Span { }), } } + + #[inline] + pub fn with_parent(self, parent: Option) -> Span { + let data = match_span_kind! { + self, + InlineCtxt(span) => { + // This format occurs 1-2 orders of magnitude more often than others (#126544), + // so it makes sense to micro-optimize it to avoid `span.data()` and `Span::new()`. + // Copypaste from `Span::new`, the small len & ctxt conditions are known to hold. + match parent { + None => return self, + Some(parent) => { + let parent32 = parent.local_def_index.as_u32(); + if span.ctxt == 0 && parent32 <= MAX_CTXT { + return InlineParent::span(span.lo, span.len, parent32 as u16); + } + } + } + span.data() + }, + InlineParent(span) => span.data(), + PartiallyInterned(span) => span.data(), + Interned(span) => span.data(), + }; + + if let Some(old_parent) = data.parent { + (*SPAN_TRACK)(old_parent); + } + data.with_parent(parent) + } + + #[inline] + pub fn parent(self) -> Option { + let interned_parent = + |index: u32| with_span_interner(|interner| interner.spans[index as usize].parent); + match_span_kind! { + self, + InlineCtxt(_span) => None, + InlineParent(span) => Some(LocalDefId { local_def_index: DefIndex::from_u16(span.parent) }), + PartiallyInterned(span) => interned_parent(span.index), + Interned(span) => interned_parent(span.index), + } + } } #[derive(Default)] From d3091df79b92eff2648e2b1537f22c6f834aecf0 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 20 Jun 2024 15:15:48 +0000 Subject: [PATCH 134/147] Remove `feature(const_closures)` from libcore --- library/core/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index a533a7938f13..94ad8fbd5df0 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -209,7 +209,6 @@ #![feature(cfg_sanitize)] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_has_atomic_equal_alignment)] -#![feature(const_closures)] #![feature(const_fn_floating_point_arithmetic)] #![feature(const_for)] #![feature(const_mut_refs)] From f42fa4f6e006d0d9eb90c7b10a9698dffb104174 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 20 Jun 2024 17:42:40 +0200 Subject: [PATCH 135/147] add `needs-unwind` to UI test the `tail-expr-lock-poisoning` UI test uses the `panic::catch_unwind` API so it relies on unwinding being implemented. this test ought not to run on targets that do not support unwinding. add the `needs-unwind` attribute to signal this --- tests/ui/lifetimes/tail-expr-lock-poisoning.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui/lifetimes/tail-expr-lock-poisoning.rs b/tests/ui/lifetimes/tail-expr-lock-poisoning.rs index 69b8f286d774..cdfd35304b44 100644 --- a/tests/ui/lifetimes/tail-expr-lock-poisoning.rs +++ b/tests/ui/lifetimes/tail-expr-lock-poisoning.rs @@ -3,6 +3,7 @@ //@ [edition2024] compile-flags: -Zunstable-options //@ [edition2024] edition: 2024 //@ run-pass +//@ needs-unwind #![cfg_attr(edition2024, feature(shorter_tail_lifetimes))] use std::sync::Mutex; From 5caa7f9a4a2455e82da9bf1171937f024e9e026c Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Thu, 20 Jun 2024 18:03:42 +0200 Subject: [PATCH 136/147] Improve html-checker error message The previous message omits which of the dozens of tools called tidy is meant. And it's written in a way that one can easily miss the *not*, thinking it reads "Note that `tidy` is the in-tree `src/tools/tidy` but needs to be installed". The error message should hopefully help future contributors. --- src/bootstrap/src/core/build_steps/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 360bd3840d45..8b65085e14e7 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -201,7 +201,7 @@ impl Step for HtmlCheck { if !check_if_tidy_is_installed() { eprintln!("not running HTML-check tool because `tidy` is missing"); eprintln!( - "Note that `tidy` is not the in-tree `src/tools/tidy` but needs to be installed" + "You need the HTML tidy tool https://www.html-tidy.org/, this tool is *not* part of the rust project and needs to be installed separately, for example via your package manager." ); panic!("Cannot run html-check tests"); } From a895ef77f062325f6b9cf7bf7e422a3008c3cd17 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Thu, 20 Jun 2024 18:07:04 +0200 Subject: [PATCH 137/147] Fix wrong big O star bracing in the doc comments --- library/alloc/src/slice.rs | 6 +++--- library/core/src/slice/mod.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 846f8ba6c6e4..c7960b3fb49c 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -193,7 +193,7 @@ impl [T] { /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which /// combines the fast average case of quicksort with the fast worst case and partial run /// detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs - /// with k distinct elements, the expected time to sort the data is *O(*n* \* log(*k*))*. + /// with k distinct elements, the expected time to sort the data is *O*(*n* \* log(*k*)). /// /// The auxiliary memory allocation behavior depends on the input length. Short slices are /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it @@ -252,7 +252,7 @@ impl [T] { /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which /// combines the fast average case of quicksort with the fast worst case and partial run /// detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs - /// with k distinct elements, the expected time to sort the data is *O(*n* \* log(*k*))*. + /// with k distinct elements, the expected time to sort the data is *O*(*n* \* log(*k*)). /// /// The auxiliary memory allocation behavior depends on the input length. Short slices are /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it @@ -298,7 +298,7 @@ impl [T] { /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which /// combines the fast average case of quicksort with the fast worst case and partial run /// detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs - /// with k distinct elements, the expected time to sort the data is *O(*n* \* log(*k*))*. + /// with k distinct elements, the expected time to sort the data is *O*(*n* \* log(*k*)). /// /// The auxiliary memory allocation behavior depends on the input length. Short slices are /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 3c818eacca31..d68dde823541 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2893,7 +2893,7 @@ impl [T] { /// The current implementation is based on [ipnsort] by Lukas Bergdoll and Orson Peters, which /// combines the fast average case of quicksort with the fast worst case of heapsort, achieving /// linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the - /// expected time to sort the data is *O(*n* \* log(*k*))*. + /// expected time to sort the data is *O*(*n* \* log(*k*)). /// /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. @@ -2950,7 +2950,7 @@ impl [T] { /// The current implementation is based on [ipnsort] by Lukas Bergdoll and Orson Peters, which /// combines the fast average case of quicksort with the fast worst case of heapsort, achieving /// linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the - /// expected time to sort the data is *O(*n* \* log(*k*))*. + /// expected time to sort the data is *O*(*n* \* log(*k*)). /// /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. @@ -2994,7 +2994,7 @@ impl [T] { /// The current implementation is based on [ipnsort] by Lukas Bergdoll and Orson Peters, which /// combines the fast average case of quicksort with the fast worst case of heapsort, achieving /// linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the - /// expected time to sort the data is *O(*n* \* log(*k*))*. + /// expected time to sort the data is *O*(*n* \* log(*k*)). /// /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. From 108b3f214a7e160ecb7ac2ec05bbb41aedd05a95 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 20 Jun 2024 14:14:36 -0400 Subject: [PATCH 138/147] Properly gate `safe` keyword in pre-expansion --- compiler/rustc_ast_passes/src/feature_gate.rs | 4 ++++ compiler/rustc_parse/src/parser/mod.rs | 3 +++ .../feature-gate-unsafe-extern-blocks.rs | 8 ++++++++ .../feature-gate-unsafe-extern-blocks.stderr | 13 ++++++++++++- 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 764d942836c2..e1c1a027a30a 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -562,6 +562,10 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(precise_capturing, "precise captures on `impl Trait` are experimental"); gate_all!(global_registration, "global registration is experimental"); gate_all!(unsafe_attributes, "`#[unsafe()]` markers for attributes are experimental"); + gate_all!( + unsafe_extern_blocks, + "`unsafe extern {}` blocks and `safe` keyword are experimental" + ); if !visitor.features.never_patterns { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 2f12459da573..cfd0a72c056d 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1214,6 +1214,9 @@ impl<'a> Parser<'a> { if self.eat_keyword_case(kw::Unsafe, case) { Safety::Unsafe(self.prev_token.uninterpolated_span()) } else if self.eat_keyword_case(kw::Safe, case) { + self.psess + .gated_spans + .gate(sym::unsafe_extern_blocks, self.prev_token.uninterpolated_span()); Safety::Safe(self.prev_token.uninterpolated_span()) } else { Safety::Default diff --git a/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.rs b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.rs index eab134a4a4de..3ea62e875b8e 100644 --- a/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.rs +++ b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.rs @@ -2,4 +2,12 @@ unsafe extern "C" { //~^ ERROR extern block cannot be declared unsafe } +// We can't gate `unsafe extern` blocks themselves since they were previously +// allowed, but we should gate the `safe` soft keyword. +#[cfg(any())] +unsafe extern "C" { + safe fn foo(); + //~^ ERROR `unsafe extern {}` blocks and `safe` keyword are experimental +} + fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.stderr b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.stderr index 7e9b199a2db5..84f00827c601 100644 --- a/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.stderr +++ b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.stderr @@ -4,5 +4,16 @@ error: extern block cannot be declared unsafe LL | unsafe extern "C" { | ^^^^^^ -error: aborting due to 1 previous error +error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental + --> $DIR/feature-gate-unsafe-extern-blocks.rs:9:5 + | +LL | safe fn foo(); + | ^^^^ + | + = note: see issue #123743 for more information + = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. From 3e59f0c3c5b4e23057dcfbe9da784164e2410a78 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 20 Jun 2024 19:50:57 -0400 Subject: [PATCH 139/147] StaticForeignItem and StaticItem are the same --- compiler/rustc_ast/src/ast.rs | 34 +------------------ compiler/rustc_ast/src/mut_visit.rs | 7 +--- compiler/rustc_ast/src/visit.rs | 7 +--- compiler/rustc_ast_lowering/src/item.rs | 7 +--- .../rustc_ast_passes/src/ast_validation.rs | 2 +- .../rustc_ast_pretty/src/pprust/state/item.rs | 7 +--- compiler/rustc_parse/src/parser/item.rs | 2 +- compiler/rustc_resolve/src/def_collector.rs | 7 +--- .../clippy/clippy_utils/src/ast_utils.rs | 4 +-- 9 files changed, 10 insertions(+), 67 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 71932f02017c..30c54ef2d3c4 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3184,38 +3184,6 @@ pub struct StaticItem { pub expr: Option>, } -/// A static item in `extern` block. -// This struct is identical to StaticItem for now but it's going to have a safety attribute. -#[derive(Clone, Encodable, Decodable, Debug)] -pub struct StaticForeignItem { - pub ty: P, - pub safety: Safety, - pub mutability: Mutability, - pub expr: Option>, -} - -impl From for StaticForeignItem { - fn from(static_item: StaticItem) -> StaticForeignItem { - StaticForeignItem { - ty: static_item.ty, - safety: static_item.safety, - mutability: static_item.mutability, - expr: static_item.expr, - } - } -} - -impl From for StaticItem { - fn from(static_item: StaticForeignItem) -> StaticItem { - StaticItem { - ty: static_item.ty, - safety: static_item.safety, - mutability: static_item.mutability, - expr: static_item.expr, - } - } -} - #[derive(Clone, Encodable, Decodable, Debug)] pub struct ConstItem { pub defaultness: Defaultness, @@ -3430,7 +3398,7 @@ impl TryFrom for AssocItemKind { #[derive(Clone, Encodable, Decodable, Debug)] pub enum ForeignItemKind { /// A foreign static item (`static FOO: u8`). - Static(Box), + Static(Box), /// An foreign function. Fn(Box), /// An foreign type. diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 35aa53e978c1..c9d2f5c779bb 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1310,12 +1310,7 @@ pub fn noop_flat_map_item( impl NoopVisitItemKind for ForeignItemKind { fn noop_visit(&mut self, visitor: &mut impl MutVisitor) { match self { - ForeignItemKind::Static(box StaticForeignItem { - ty, - mutability: _, - expr, - safety: _, - }) => { + ForeignItemKind::Static(box StaticItem { ty, mutability: _, expr, safety: _ }) => { visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index ed34a44db677..ce38a67ea69a 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -672,12 +672,7 @@ impl WalkItemKind for ForeignItemKind { ) -> V::Result { let &Item { id, span, ident, ref vis, .. } = item; match self { - ForeignItemKind::Static(box StaticForeignItem { - ty, - mutability: _, - expr, - safety: _, - }) => { + ForeignItemKind::Static(box StaticItem { ty, mutability: _, expr, safety: _ }) => { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index c6c0d9a2e608..4c7e8c24d32a 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -664,12 +664,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ForeignItemKind::Fn(fn_dec, fn_args, generics, safety) } - ForeignItemKind::Static(box StaticForeignItem { - ty, - mutability, - expr: _, - safety, - }) => { + ForeignItemKind::Static(box StaticItem { ty, mutability, expr: _, safety }) => { let ty = self .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); let safety = self.lower_safety(*safety, hir::Safety::Unsafe); diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index b274a9b9114a..cad1fc79d7f4 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1232,7 +1232,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_foreign_ty_genericless(generics, where_clauses); self.check_foreign_item_ascii_only(fi.ident); } - ForeignItemKind::Static(box StaticForeignItem { expr, safety, .. }) => { + ForeignItemKind::Static(box StaticItem { expr, safety, .. }) => { self.check_foreign_item_safety(fi.span, *safety); self.check_foreign_kind_bodyless(fi.ident, "static", expr.as_ref().map(|b| b.span)); self.check_foreign_item_ascii_only(fi.ident); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 49ac5ece337f..d8382057d3f6 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -37,12 +37,7 @@ impl<'a> State<'a> { ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); } - ast::ForeignItemKind::Static(box ast::StaticForeignItem { - ty, - mutability, - expr, - safety, - }) => { + ast::ForeignItemKind::Static(box ast::StaticItem { ty, mutability, expr, safety }) => { self.print_safety(*safety); self.print_item_const( ident, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 3e1ea7b129de..abb6b51cebd6 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1228,7 +1228,7 @@ impl<'a> Parser<'a> { ident_span: ident.span, const_span, }); - ForeignItemKind::Static(Box::new(StaticForeignItem { + ForeignItemKind::Static(Box::new(StaticItem { ty, mutability: Mutability::Not, expr, diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index fb6e55f2b7bd..ad1ec4438cc8 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -217,12 +217,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { let def_kind = match fi.kind { - ForeignItemKind::Static(box StaticForeignItem { - ty: _, - mutability, - expr: _, - safety, - }) => { + ForeignItemKind::Static(box StaticItem { ty: _, mutability, expr: _, safety }) => { let safety = match safety { ast::Safety::Unsafe(_) | ast::Safety::Default => hir::Safety::Unsafe, ast::Safety::Safe(_) => hir::Safety::Safe, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index fb43f7d80aff..785d5ed5dbeb 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -449,13 +449,13 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { use ForeignItemKind::*; match (l, r) { ( - Static(box StaticForeignItem { + Static(box StaticItem { ty: lt, mutability: lm, expr: le, safety: ls, }), - Static(box StaticForeignItem { + Static(box StaticItem { ty: rt, mutability: rm, expr: re, From d6efcbb760ae0a17ed05e843bdced7afee74695a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 21 Jun 2024 13:58:06 +1000 Subject: [PATCH 140/147] Fix another assertion failure for some Expect diagnostics. Very similar to #126719. So much so that I added a new case to the test from that PR rather than creating a new one. --- compiler/rustc_errors/src/lib.rs | 8 ++++---- .../expect-future_breakage-crash-issue-126521.rs | 12 ++++++++++++ ...xpect-future_breakage-crash-issue-126521.stderr | 14 ++++++++++++-- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 620f56c01e89..91112a572770 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1456,10 +1456,10 @@ impl DiagCtxtInner { } if diagnostic.has_future_breakage() { - // Future breakages aren't emitted if they're `Level::Allow`, - // but they still need to be constructed and stashed below, - // so they'll trigger the must_produce_diag check. - assert!(matches!(diagnostic.level, Error | Warning | Allow)); + // Future breakages aren't emitted if they're `Level::Allow` or + // `Level::Expect`, but they still need to be constructed and + // stashed below, so they'll trigger the must_produce_diag check. + assert!(matches!(diagnostic.level, Error | Warning | Allow | Expect(_))); self.future_breakage_diagnostics.push(diagnostic.clone()); } diff --git a/tests/ui/lint/expect-future_breakage-crash-issue-126521.rs b/tests/ui/lint/expect-future_breakage-crash-issue-126521.rs index a3c8544613bf..0e622ff3aaf7 100644 --- a/tests/ui/lint/expect-future_breakage-crash-issue-126521.rs +++ b/tests/ui/lint/expect-future_breakage-crash-issue-126521.rs @@ -1,11 +1,23 @@ +// This test covers similar crashes from both #126521 and #126751. + macro_rules! foo { ($val:ident) => { true; }; } +macro_rules! bar { + ($val:ident) => { + (5_i32.overflowing_sub(3)); + }; +} + fn main() { #[expect(semicolon_in_expressions_from_macros)] //~^ ERROR the `#[expect]` attribute is an experimental feature let _ = foo!(x); + + #[expect(semicolon_in_expressions_from_macros)] + //~^ ERROR the `#[expect]` attribute is an experimental feature + let _ = bar!(x); } diff --git a/tests/ui/lint/expect-future_breakage-crash-issue-126521.stderr b/tests/ui/lint/expect-future_breakage-crash-issue-126521.stderr index b24831b1ae43..994630ec23b2 100644 --- a/tests/ui/lint/expect-future_breakage-crash-issue-126521.stderr +++ b/tests/ui/lint/expect-future_breakage-crash-issue-126521.stderr @@ -1,5 +1,5 @@ error[E0658]: the `#[expect]` attribute is an experimental feature - --> $DIR/expect-future_breakage-crash-issue-126521.rs:8:5 + --> $DIR/expect-future_breakage-crash-issue-126521.rs:16:5 | LL | #[expect(semicolon_in_expressions_from_macros)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,6 +8,16 @@ LL | #[expect(semicolon_in_expressions_from_macros)] = help: add `#![feature(lint_reasons)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error[E0658]: the `#[expect]` attribute is an experimental feature + --> $DIR/expect-future_breakage-crash-issue-126521.rs:20:5 + | +LL | #[expect(semicolon_in_expressions_from_macros)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #54503 for more information + = help: add `#![feature(lint_reasons)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. From 31d8696ac943056d6902869fbb9f4297a1fce814 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 16 Jun 2024 18:12:17 -0700 Subject: [PATCH 141/147] Add a try_as_constant+try_as_local helper No behaviour changes. --- compiler/rustc_mir_transform/src/gvn.rs | 38 ++++++++++++------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index b2670040b14b..75bf5b4c07b7 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -949,13 +949,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { was_updated = true; } - if was_updated { - if let Some(const_) = self.try_as_constant(fields[0]) { - field_ops[FieldIdx::ZERO] = Operand::Constant(Box::new(const_)); - } else if let Some(local) = self.try_as_local(fields[0], location) { - field_ops[FieldIdx::ZERO] = Operand::Copy(Place::from(local)); - self.reused_locals.insert(local); - } + if was_updated && let Some(op) = self.try_as_operand(fields[0], location) { + field_ops[FieldIdx::ZERO] = op; } } @@ -965,11 +960,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let first = fields[0]; if fields.iter().all(|&v| v == first) { let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap()); - if let Some(const_) = self.try_as_constant(first) { - *rvalue = Rvalue::Repeat(Operand::Constant(Box::new(const_)), len); - } else if let Some(local) = self.try_as_local(first, location) { - *rvalue = Rvalue::Repeat(Operand::Copy(local.into()), len); - self.reused_locals.insert(local); + if let Some(op) = self.try_as_operand(first, location) { + *rvalue = Rvalue::Repeat(op, len); } return Some(self.insert(Value::Repeat(first, len))); } @@ -1174,13 +1166,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } - if was_updated { - if let Some(const_) = self.try_as_constant(value) { - *operand = Operand::Constant(Box::new(const_)); - } else if let Some(local) = self.try_as_local(value, location) { - *operand = Operand::Copy(local.into()); - self.reused_locals.insert(local); - } + if was_updated && let Some(op) = self.try_as_operand(value, location) { + *operand = op; } Some(self.insert(Value::Cast { kind: *kind, value, from, to })) @@ -1296,6 +1283,19 @@ fn op_to_prop_const<'tcx>( } impl<'tcx> VnState<'_, 'tcx> { + /// If either [`Self::try_as_constant`] as [`Self::try_as_local`] succeeds, + /// returns that result as an [`Operand`]. + fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option> { + if let Some(const_) = self.try_as_constant(index) { + Some(Operand::Constant(Box::new(const_))) + } else if let Some(local) = self.try_as_local(index, location) { + self.reused_locals.insert(local); + Some(Operand::Copy(local.into())) + } else { + None + } + } + /// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR. fn try_as_constant(&mut self, index: VnIndex) -> Option> { // This was already constant in MIR, do not change it. From 121b06bd0593369477a70dfee156f10055ca7638 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 21 Jun 2024 05:07:19 +0000 Subject: [PATCH 142/147] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index a2da736656d1..1502fa120be6 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -a1ca449981e3b8442e358026437b7bedb9a1458e +7a08f84627ff3035de4d66ff3209e5fc93165dcb From 4a7b6c0e6ce760964284d50705eefdc8dea7d0c3 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Wed, 19 Jun 2024 22:14:31 -0700 Subject: [PATCH 143/147] More GVN for PtrMetadata `PtrMetadata` doesn't care about `*const`/`*mut`/`&`/`&mut`, so GVN away those casts in its argument. This includes updating MIR to allow calling PtrMetadata on references too, not just raw pointers. That means that `[T]::len` can be just `_0 = PtrMetadata(_1)`, for example. # Conflicts: # tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir # tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 4 +- .../src/interpret/operator.rs | 2 +- compiler/rustc_middle/src/mir/syntax.rs | 6 +- compiler/rustc_mir_transform/src/gvn.rs | 96 +++++++++++++++++-- compiler/rustc_mir_transform/src/validate.rs | 9 +- .../gvn.array_len.GVN.panic-abort.diff | 31 ++++++ .../gvn.array_len.GVN.panic-unwind.diff | 31 ++++++ .../gvn.fn_pointers.GVN.panic-abort.diff | 18 ++-- .../gvn.fn_pointers.GVN.panic-unwind.diff | 18 ++-- ....manual_slice_mut_len.GVN.panic-abort.diff | 41 ++++++++ ...manual_slice_mut_len.GVN.panic-unwind.diff | 41 ++++++++ tests/mir-opt/gvn.rs | 20 ++++ ...mut_range.PreCodegen.after.panic-abort.mir | 40 ++++---- ...ut_range.PreCodegen.after.panic-unwind.mir | 40 ++++---- 14 files changed, 319 insertions(+), 78 deletions(-) create mode 100644 tests/mir-opt/gvn.array_len.GVN.panic-abort.diff create mode 100644 tests/mir-opt/gvn.array_len.GVN.panic-unwind.diff create mode 100644 tests/mir-opt/gvn.manual_slice_mut_len.GVN.panic-abort.diff create mode 100644 tests/mir-opt/gvn.manual_slice_mut_len.GVN.panic-unwind.diff diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 05861e337674..f9e928a84a78 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -639,7 +639,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (OperandValue::Immediate(llval), operand.layout) } mir::UnOp::PtrMetadata => { - debug_assert!(operand.layout.ty.is_unsafe_ptr()); + debug_assert!( + operand.layout.ty.is_unsafe_ptr() || operand.layout.ty.is_ref(), + ); let (_, meta) = operand.val.pointer_parts(); assert_eq!(operand.layout.fields.count() > 1, meta.is_some()); if let Some(meta) = meta { diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 73bdf96627ae..2723507397ee 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -460,7 +460,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let res = ScalarInt::truncate_from_uint(res, layout.size).0; Ok(ImmTy::from_scalar(res.into(), layout)) } - ty::RawPtr(..) => { + ty::RawPtr(..) | ty::Ref(..) => { assert_eq!(un_op, PtrMetadata); let (_, meta) = val.to_scalar_and_meta(); Ok(match meta { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 5957a25f0f2b..0fc84ea5c621 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1446,10 +1446,12 @@ pub enum UnOp { Not, /// The `-` operator for negation Neg, - /// Get the metadata `M` from a `*const/mut impl Pointee`. + /// Gets the metadata `M` from a `*const`/`*mut`/`&`/`&mut` to + /// `impl Pointee`. /// /// For example, this will give a `()` from `*const i32`, a `usize` from - /// `*mut [u8]`, or a pointer to a vtable from a `*const dyn Foo`. + /// `&mut [u8]`, or a `ptr::DynMetadata` (internally a pointer) + /// from a `*mut dyn Foo`. /// /// Allowed only in [`MirPhase::Runtime`]; earlier it's an intrinsic. PtrMetadata, diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 75bf5b4c07b7..bfdefd5a7d62 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -836,12 +836,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } Value::BinaryOp(op, lhs, rhs) } - Rvalue::UnaryOp(op, ref mut arg) => { - let arg = self.simplify_operand(arg, location)?; - if let Some(value) = self.simplify_unary(op, arg) { - return Some(value); - } - Value::UnaryOp(op, arg) + Rvalue::UnaryOp(op, ref mut arg_op) => { + return self.simplify_unary(op, arg_op, location); } Rvalue::Discriminant(ref mut place) => { let place = self.simplify_place_value(place, location)?; @@ -971,8 +967,71 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } #[instrument(level = "trace", skip(self), ret)] - fn simplify_unary(&mut self, op: UnOp, value: VnIndex) -> Option { - let value = match (op, self.get(value)) { + fn simplify_unary( + &mut self, + op: UnOp, + arg_op: &mut Operand<'tcx>, + location: Location, + ) -> Option { + let mut arg_index = self.simplify_operand(arg_op, location)?; + + // PtrMetadata doesn't care about *const vs *mut vs & vs &mut, + // so start by removing those distinctions so we can update the `Operand` + if op == UnOp::PtrMetadata { + let mut was_updated = false; + loop { + match self.get(arg_index) { + // Pointer casts that preserve metadata, such as + // `*const [i32]` <-> `*mut [i32]` <-> `*mut [f32]`. + // It's critical that this not eliminate cases like + // `*const [T]` -> `*const T` which remove metadata. + // We run on potentially-generic MIR, though, so unlike codegen + // we can't always know exactly what the metadata are. + // Thankfully, equality on `ptr_metadata_ty_or_tail` gives us + // what we need: `Ok(meta_ty)` if the metadata is known, or + // `Err(tail_ty)` if not. Matching metadata is ok, but if + // that's not known, then matching tail types is also ok, + // allowing things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`. + // FIXME: Would it be worth trying to normalize, rather than + // passing the identity closure? Or are the types in the + // Cast realistically about as normalized as we can get anyway? + Value::Cast { kind: CastKind::PtrToPtr, value: inner, from, to } + if from + .builtin_deref(true) + .unwrap() + .ptr_metadata_ty_or_tail(self.tcx, |t| t) + == to + .builtin_deref(true) + .unwrap() + .ptr_metadata_ty_or_tail(self.tcx, |t| t) => + { + arg_index = *inner; + was_updated = true; + continue; + } + + // `&mut *p`, `&raw *p`, etc don't change metadata. + Value::Address { place, kind: _, provenance: _ } + if let PlaceRef { local, projection: [PlaceElem::Deref] } = + place.as_ref() + && let Some(local_index) = self.locals[local] => + { + arg_index = local_index; + was_updated = true; + continue; + } + + _ => { + if was_updated && let Some(op) = self.try_as_operand(arg_index, location) { + *arg_op = op; + } + break; + } + } + } + } + + let value = match (op, self.get(arg_index)) { (UnOp::Not, Value::UnaryOp(UnOp::Not, inner)) => return Some(*inner), (UnOp::Neg, Value::UnaryOp(UnOp::Neg, inner)) => return Some(*inner), (UnOp::Not, Value::BinaryOp(BinOp::Eq, lhs, rhs)) => { @@ -984,9 +1043,26 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { (UnOp::PtrMetadata, Value::Aggregate(AggregateTy::RawPtr { .. }, _, fields)) => { return Some(fields[1]); } - _ => return None, + // We have an unsizing cast, which assigns the length to fat pointer metadata. + ( + UnOp::PtrMetadata, + Value::Cast { + kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize), + from, + to, + .. + }, + ) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind() + && let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() => + { + return self.insert_constant(Const::from_ty_const( + *len, + self.tcx.types.usize, + self.tcx, + )); + } + _ => Value::UnaryOp(op, arg_index), }; - Some(self.insert(value)) } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index f5d10521fdd0..2018a8fe667d 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1116,12 +1116,17 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { UnOp::PtrMetadata => { if !matches!(self.mir_phase, MirPhase::Runtime(_)) { // It would probably be fine to support this in earlier phases, - // but at the time of writing it's only ever introduced from intrinsic lowering, + // but at the time of writing it's only ever introduced from intrinsic lowering + // or other runtime-phase optimization passes, // so earlier things can just `bug!` on it. self.fail(location, "PtrMetadata should be in runtime MIR only"); } - check_kinds!(a, "Cannot PtrMetadata non-pointer type {:?}", ty::RawPtr(..)); + check_kinds!( + a, + "Cannot PtrMetadata non-pointer non-reference type {:?}", + ty::RawPtr(..) | ty::Ref(..) + ); } } } diff --git a/tests/mir-opt/gvn.array_len.GVN.panic-abort.diff b/tests/mir-opt/gvn.array_len.GVN.panic-abort.diff new file mode 100644 index 000000000000..90654e056628 --- /dev/null +++ b/tests/mir-opt/gvn.array_len.GVN.panic-abort.diff @@ -0,0 +1,31 @@ +- // MIR for `array_len` before GVN ++ // MIR for `array_len` after GVN + + fn array_len(_1: &mut [i32; 42]) -> usize { + debug x => _1; + let mut _0: usize; + let _2: &[i32]; + let mut _3: &[i32; 42]; + let mut _4: *const [i32]; + scope 1 { + debug x => _2; + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = &(*_1); + _2 = move _3 as &[i32] (PointerCoercion(Unsize)); + StorageDead(_3); + StorageLive(_4); + _4 = &raw const (*_2); +- _0 = PtrMetadata(move _4); ++ _0 = const 42_usize; + StorageDead(_4); +- StorageDead(_2); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.array_len.GVN.panic-unwind.diff b/tests/mir-opt/gvn.array_len.GVN.panic-unwind.diff new file mode 100644 index 000000000000..90654e056628 --- /dev/null +++ b/tests/mir-opt/gvn.array_len.GVN.panic-unwind.diff @@ -0,0 +1,31 @@ +- // MIR for `array_len` before GVN ++ // MIR for `array_len` after GVN + + fn array_len(_1: &mut [i32; 42]) -> usize { + debug x => _1; + let mut _0: usize; + let _2: &[i32]; + let mut _3: &[i32; 42]; + let mut _4: *const [i32]; + scope 1 { + debug x => _2; + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = &(*_1); + _2 = move _3 as &[i32] (PointerCoercion(Unsize)); + StorageDead(_3); + StorageLive(_4); + _4 = &raw const (*_2); +- _0 = PtrMetadata(move _4); ++ _0 = const 42_usize; + StorageDead(_4); +- StorageDead(_2); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff index 25d0e5ccf496..0c49e706c9ec 100644 --- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff @@ -8,10 +8,10 @@ let mut _3: fn(u8) -> u8; let _5: (); let mut _6: fn(u8) -> u8; - let mut _9: {closure@$DIR/gvn.rs:612:19: 612:21}; + let mut _9: {closure@$DIR/gvn.rs:614:19: 614:21}; let _10: (); let mut _11: fn(); - let mut _13: {closure@$DIR/gvn.rs:612:19: 612:21}; + let mut _13: {closure@$DIR/gvn.rs:614:19: 614:21}; let _14: (); let mut _15: fn(); scope 1 { @@ -19,7 +19,7 @@ let _4: fn(u8) -> u8; scope 2 { debug g => _4; - let _7: {closure@$DIR/gvn.rs:612:19: 612:21}; + let _7: {closure@$DIR/gvn.rs:614:19: 614:21}; scope 3 { debug closure => _7; let _8: fn(); @@ -62,16 +62,16 @@ StorageDead(_6); StorageDead(_5); - StorageLive(_7); -- _7 = {closure@$DIR/gvn.rs:612:19: 612:21}; +- _7 = {closure@$DIR/gvn.rs:614:19: 614:21}; - StorageLive(_8); + nop; -+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21}; ++ _7 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21}; + nop; StorageLive(_9); - _9 = _7; - _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe))); -+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21}; -+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21} as fn() (PointerCoercion(ClosureFnPointer(Safe))); ++ _9 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21}; ++ _8 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21} as fn() (PointerCoercion(ClosureFnPointer(Safe))); StorageDead(_9); StorageLive(_10); StorageLive(_11); @@ -88,8 +88,8 @@ StorageLive(_13); - _13 = _7; - _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe))); -+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21}; -+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21} as fn() (PointerCoercion(ClosureFnPointer(Safe))); ++ _13 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21}; ++ _12 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21} as fn() (PointerCoercion(ClosureFnPointer(Safe))); StorageDead(_13); StorageLive(_14); StorageLive(_15); diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff index 3c5fd944d6b9..e5f865b74b9f 100644 --- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff @@ -8,10 +8,10 @@ let mut _3: fn(u8) -> u8; let _5: (); let mut _6: fn(u8) -> u8; - let mut _9: {closure@$DIR/gvn.rs:612:19: 612:21}; + let mut _9: {closure@$DIR/gvn.rs:614:19: 614:21}; let _10: (); let mut _11: fn(); - let mut _13: {closure@$DIR/gvn.rs:612:19: 612:21}; + let mut _13: {closure@$DIR/gvn.rs:614:19: 614:21}; let _14: (); let mut _15: fn(); scope 1 { @@ -19,7 +19,7 @@ let _4: fn(u8) -> u8; scope 2 { debug g => _4; - let _7: {closure@$DIR/gvn.rs:612:19: 612:21}; + let _7: {closure@$DIR/gvn.rs:614:19: 614:21}; scope 3 { debug closure => _7; let _8: fn(); @@ -62,16 +62,16 @@ StorageDead(_6); StorageDead(_5); - StorageLive(_7); -- _7 = {closure@$DIR/gvn.rs:612:19: 612:21}; +- _7 = {closure@$DIR/gvn.rs:614:19: 614:21}; - StorageLive(_8); + nop; -+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21}; ++ _7 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21}; + nop; StorageLive(_9); - _9 = _7; - _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe))); -+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21}; -+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21} as fn() (PointerCoercion(ClosureFnPointer(Safe))); ++ _9 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21}; ++ _8 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21} as fn() (PointerCoercion(ClosureFnPointer(Safe))); StorageDead(_9); StorageLive(_10); StorageLive(_11); @@ -88,8 +88,8 @@ StorageLive(_13); - _13 = _7; - _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe))); -+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21}; -+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21} as fn() (PointerCoercion(ClosureFnPointer(Safe))); ++ _13 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21}; ++ _12 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21} as fn() (PointerCoercion(ClosureFnPointer(Safe))); StorageDead(_13); StorageLive(_14); StorageLive(_15); diff --git a/tests/mir-opt/gvn.manual_slice_mut_len.GVN.panic-abort.diff b/tests/mir-opt/gvn.manual_slice_mut_len.GVN.panic-abort.diff new file mode 100644 index 000000000000..c877d8a3c0e5 --- /dev/null +++ b/tests/mir-opt/gvn.manual_slice_mut_len.GVN.panic-abort.diff @@ -0,0 +1,41 @@ +- // MIR for `manual_slice_mut_len` before GVN ++ // MIR for `manual_slice_mut_len` after GVN + + fn manual_slice_mut_len(_1: &mut [i32]) -> usize { + debug x => _1; + let mut _0: usize; + let _2: *mut [i32]; + let mut _4: *mut [i32]; + let mut _5: *const [i32]; + scope 1 { + debug x => _2; + let _3: *const [i32]; + scope 2 { + debug x => _3; + } + } + + bb0: { +- StorageLive(_2); ++ nop; + _2 = &raw mut (*_1); +- StorageLive(_3); ++ nop; + StorageLive(_4); + _4 = _2; +- _3 = move _4 as *const [i32] (PtrToPtr); ++ _3 = _2 as *const [i32] (PtrToPtr); + StorageDead(_4); + StorageLive(_5); + _5 = _3; +- _0 = PtrMetadata(move _5); ++ _0 = PtrMetadata(_1); + StorageDead(_5); +- StorageDead(_3); +- StorageDead(_2); ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.manual_slice_mut_len.GVN.panic-unwind.diff b/tests/mir-opt/gvn.manual_slice_mut_len.GVN.panic-unwind.diff new file mode 100644 index 000000000000..c877d8a3c0e5 --- /dev/null +++ b/tests/mir-opt/gvn.manual_slice_mut_len.GVN.panic-unwind.diff @@ -0,0 +1,41 @@ +- // MIR for `manual_slice_mut_len` before GVN ++ // MIR for `manual_slice_mut_len` after GVN + + fn manual_slice_mut_len(_1: &mut [i32]) -> usize { + debug x => _1; + let mut _0: usize; + let _2: *mut [i32]; + let mut _4: *mut [i32]; + let mut _5: *const [i32]; + scope 1 { + debug x => _2; + let _3: *const [i32]; + scope 2 { + debug x => _3; + } + } + + bb0: { +- StorageLive(_2); ++ nop; + _2 = &raw mut (*_1); +- StorageLive(_3); ++ nop; + StorageLive(_4); + _4 = _2; +- _3 = move _4 as *const [i32] (PtrToPtr); ++ _3 = _2 as *const [i32] (PtrToPtr); + StorageDead(_4); + StorageLive(_5); + _5 = _3; +- _0 = PtrMetadata(move _5); ++ _0 = PtrMetadata(_1); + StorageDead(_5); +- StorageDead(_3); +- StorageDead(_2); ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 720018f112e5..74f1849c42a9 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -7,7 +7,9 @@ #![feature(custom_mir)] #![feature(core_intrinsics)] #![feature(freeze)] +#![allow(ambiguous_wide_pointer_comparisons)] #![allow(unconditional_panic)] +#![allow(unused)] use std::intrinsics::mir::*; use std::marker::Freeze; @@ -816,6 +818,22 @@ fn casts_before_aggregate_raw_ptr(x: *const u32) -> *const [u8] { std::intrinsics::aggregate_raw_ptr(x, 4) } +fn manual_slice_mut_len(x: &mut [i32]) -> usize { + // CHECK-LABEL: fn manual_slice_mut_len + // CHECK: _0 = PtrMetadata(_1); + let x: *mut [i32] = x; + let x: *const [i32] = x; + std::intrinsics::ptr_metadata(x) +} + +// `.len()` on arrays ends up being something like this +fn array_len(x: &mut [i32; 42]) -> usize { + // CHECK-LABEL: fn array_len + // CHECK: _0 = const 42_usize; + let x: &[i32] = x; + std::intrinsics::ptr_metadata(x) +} + fn main() { subexpression_elimination(2, 4, 5); wrap_unwrap(5); @@ -880,3 +898,5 @@ fn identity(x: T) -> T { // EMIT_MIR gvn.meta_of_ref_to_slice.GVN.diff // EMIT_MIR gvn.slice_from_raw_parts_as_ptr.GVN.diff // EMIT_MIR gvn.casts_before_aggregate_raw_ptr.GVN.diff +// EMIT_MIR gvn.manual_slice_mut_len.GVN.diff +// EMIT_MIR gvn.array_len.GVN.diff diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir index 41cca811922e..2f13cfa4dab6 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir @@ -8,25 +8,24 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> let mut _4: usize; scope 1 (inlined core::slice::::get_unchecked_mut::>) { let mut _5: *mut [u32]; - let mut _12: *mut [u32]; + let mut _11: *mut [u32]; scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked_mut) { - let mut _7: usize; - let _8: (); - let _9: usize; + let mut _6: usize; + let _7: (); + let _8: usize; scope 3 { scope 6 (inlined core::slice::index::get_offset_len_mut_noubcheck::) { - let _11: *mut u32; + let _10: *mut u32; scope 7 { } scope 8 (inlined core::slice::index::get_mut_noubcheck::) { - let _10: *mut u32; + let _9: *mut u32; scope 9 { } } } } scope 4 (inlined std::ptr::mut_ptr::::len) { - let mut _6: *const [u32]; scope 5 (inlined std::ptr::metadata::<[u32]>) { } } @@ -38,28 +37,25 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> _4 = move (_2.1: usize); StorageLive(_5); _5 = &raw mut (*_1); - StorageLive(_9); - StorageLive(_7); + StorageLive(_8); StorageLive(_6); - _6 = _5 as *const [u32] (PtrToPtr); - _7 = PtrMetadata(_6); - StorageDead(_6); - _8 = as SliceIndex<[T]>>::get_unchecked_mut::precondition_check(_3, _4, move _7) -> [return: bb1, unwind unreachable]; + _6 = PtrMetadata(_1); + _7 = as SliceIndex<[T]>>::get_unchecked_mut::precondition_check(_3, _4, move _6) -> [return: bb1, unwind unreachable]; } bb1: { - StorageDead(_7); - _9 = SubUnchecked(_4, _3); - StorageLive(_11); + StorageDead(_6); + _8 = SubUnchecked(_4, _3); StorageLive(_10); - _10 = _5 as *mut u32 (PtrToPtr); - _11 = Offset(_10, _3); - StorageDead(_10); - _12 = *mut [u32] from (_11, _9); - StorageDead(_11); + StorageLive(_9); + _9 = _5 as *mut u32 (PtrToPtr); + _10 = Offset(_9, _3); StorageDead(_9); + _11 = *mut [u32] from (_10, _8); + StorageDead(_10); + StorageDead(_8); StorageDead(_5); - _0 = &mut (*_12); + _0 = &mut (*_11); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir index 41cca811922e..2f13cfa4dab6 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir @@ -8,25 +8,24 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> let mut _4: usize; scope 1 (inlined core::slice::::get_unchecked_mut::>) { let mut _5: *mut [u32]; - let mut _12: *mut [u32]; + let mut _11: *mut [u32]; scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked_mut) { - let mut _7: usize; - let _8: (); - let _9: usize; + let mut _6: usize; + let _7: (); + let _8: usize; scope 3 { scope 6 (inlined core::slice::index::get_offset_len_mut_noubcheck::) { - let _11: *mut u32; + let _10: *mut u32; scope 7 { } scope 8 (inlined core::slice::index::get_mut_noubcheck::) { - let _10: *mut u32; + let _9: *mut u32; scope 9 { } } } } scope 4 (inlined std::ptr::mut_ptr::::len) { - let mut _6: *const [u32]; scope 5 (inlined std::ptr::metadata::<[u32]>) { } } @@ -38,28 +37,25 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> _4 = move (_2.1: usize); StorageLive(_5); _5 = &raw mut (*_1); - StorageLive(_9); - StorageLive(_7); + StorageLive(_8); StorageLive(_6); - _6 = _5 as *const [u32] (PtrToPtr); - _7 = PtrMetadata(_6); - StorageDead(_6); - _8 = as SliceIndex<[T]>>::get_unchecked_mut::precondition_check(_3, _4, move _7) -> [return: bb1, unwind unreachable]; + _6 = PtrMetadata(_1); + _7 = as SliceIndex<[T]>>::get_unchecked_mut::precondition_check(_3, _4, move _6) -> [return: bb1, unwind unreachable]; } bb1: { - StorageDead(_7); - _9 = SubUnchecked(_4, _3); - StorageLive(_11); + StorageDead(_6); + _8 = SubUnchecked(_4, _3); StorageLive(_10); - _10 = _5 as *mut u32 (PtrToPtr); - _11 = Offset(_10, _3); - StorageDead(_10); - _12 = *mut [u32] from (_11, _9); - StorageDead(_11); + StorageLive(_9); + _9 = _5 as *mut u32 (PtrToPtr); + _10 = Offset(_9, _3); StorageDead(_9); + _11 = *mut [u32] from (_10, _8); + StorageDead(_10); + StorageDead(_8); StorageDead(_5); - _0 = &mut (*_12); + _0 = &mut (*_11); return; } } From b611b6bbb81c67579429c41dc99f112a807b0a38 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 16 Jun 2024 01:22:58 -0700 Subject: [PATCH 144/147] Replace `NormalizeArrayLen` with `GVN` GVN is actually on in release, and covers all the same things (or more), with `LowerSliceLen` changed to produce `PtrMetadata`. --- compiler/rustc_mir_transform/src/lib.rs | 4 - .../src/lower_slice_len.rs | 24 ++-- .../src/normalize_array_len.rs | 103 ------------------ tests/mir-opt/issue_76432.rs | 1 - ...rray_len.array_bound.GVN.panic-abort.diff} | 21 ++-- ...ray_len.array_bound.GVN.panic-unwind.diff} | 21 ++-- ..._len.array_bound_mut.GVN.panic-abort.diff} | 33 ++++-- ...len.array_bound_mut.GVN.panic-unwind.diff} | 33 ++++-- ..._array_len.array_len.GVN.panic-abort.diff} | 6 +- ...array_len.array_len.GVN.panic-unwind.diff} | 6 +- ...n.array_len_by_value.GVN.panic-abort.diff} | 6 +- ....array_len_by_value.GVN.panic-unwind.diff} | 6 +- ...ay_len.array_len_raw.GVN.panic-abort.diff} | 12 +- ...y_len.array_len_raw.GVN.panic-unwind.diff} | 12 +- ...n.array_len_reborrow.GVN.panic-abort.diff} | 12 +- ....array_len_reborrow.GVN.panic-unwind.diff} | 12 +- tests/mir-opt/lower_array_len.rs | 18 +-- ....bound.LowerSliceLenCalls.panic-abort.diff | 2 +- ...bound.LowerSliceLenCalls.panic-unwind.diff | 2 +- tests/mir-opt/pre-codegen/slice_index.rs | 2 +- ...mut_usize.PreCodegen.after.panic-abort.mir | 2 +- ...ut_usize.PreCodegen.after.panic-unwind.mir | 2 +- ...ated_loop.PreCodegen.after.panic-abort.mir | 2 +- ...ted_loop.PreCodegen.after.panic-unwind.mir | 2 +- ...ward_loop.PreCodegen.after.panic-abort.mir | 2 +- ...ard_loop.PreCodegen.after.panic-unwind.mir | 2 +- ...ange_loop.PreCodegen.after.panic-abort.mir | 2 +- ...nge_loop.PreCodegen.after.panic-unwind.mir | 2 +- ...erse_loop.PreCodegen.after.panic-abort.mir | 2 +- ...rse_loop.PreCodegen.after.panic-unwind.mir | 2 +- 30 files changed, 138 insertions(+), 218 deletions(-) delete mode 100644 compiler/rustc_mir_transform/src/normalize_array_len.rs rename tests/mir-opt/{lower_array_len.array_bound.NormalizeArrayLen.panic-abort.diff => lower_array_len.array_bound.GVN.panic-abort.diff} (69%) rename tests/mir-opt/{lower_array_len.array_bound.NormalizeArrayLen.panic-unwind.diff => lower_array_len.array_bound.GVN.panic-unwind.diff} (69%) rename tests/mir-opt/{lower_array_len.array_bound_mut.NormalizeArrayLen.panic-abort.diff => lower_array_len.array_bound_mut.GVN.panic-abort.diff} (60%) rename tests/mir-opt/{lower_array_len.array_bound_mut.NormalizeArrayLen.panic-unwind.diff => lower_array_len.array_bound_mut.GVN.panic-unwind.diff} (60%) rename tests/mir-opt/{lower_array_len.array_len.NormalizeArrayLen.panic-abort.diff => lower_array_len.array_len.GVN.panic-abort.diff} (78%) rename tests/mir-opt/{lower_array_len.array_len.NormalizeArrayLen.panic-unwind.diff => lower_array_len.array_len.GVN.panic-unwind.diff} (78%) rename tests/mir-opt/{lower_array_len.array_len_by_value.NormalizeArrayLen.panic-abort.diff => lower_array_len.array_len_by_value.GVN.panic-abort.diff} (76%) rename tests/mir-opt/{lower_array_len.array_len_by_value.NormalizeArrayLen.panic-unwind.diff => lower_array_len.array_len_by_value.GVN.panic-unwind.diff} (76%) rename tests/mir-opt/{lower_array_len.array_len_raw.NormalizeArrayLen.panic-abort.diff => lower_array_len.array_len_raw.GVN.panic-abort.diff} (82%) rename tests/mir-opt/{lower_array_len.array_len_raw.NormalizeArrayLen.panic-unwind.diff => lower_array_len.array_len_raw.GVN.panic-unwind.diff} (82%) rename tests/mir-opt/{lower_array_len.array_len_reborrow.NormalizeArrayLen.panic-abort.diff => lower_array_len.array_len_reborrow.GVN.panic-abort.diff} (80%) rename tests/mir-opt/{lower_array_len.array_len_reborrow.NormalizeArrayLen.panic-unwind.diff => lower_array_len.array_len_reborrow.GVN.panic-unwind.diff} (80%) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 87dd9cc11ebb..fe195f0112f8 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -88,7 +88,6 @@ mod lower_slice_len; mod match_branches; mod mentioned_items; mod multiple_return_terminators; -mod normalize_array_len; mod nrvo; mod prettify; mod promote_consts; @@ -581,9 +580,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &o1(simplify::SimplifyCfg::AfterUnreachableEnumBranching), // Inlining may have introduced a lot of redundant code and a large move pattern. // Now, we need to shrink the generated MIR. - - // Has to run after `slice::len` lowering - &normalize_array_len::NormalizeArrayLen, &ref_prop::ReferencePropagation, &sroa::ScalarReplacementOfAggregates, &match_branches::MatchBranchSimplification, diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 2267a621a834..77a7f4f47dd4 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -1,10 +1,9 @@ -//! This pass lowers calls to core::slice::len to just Len op. +//! This pass lowers calls to core::slice::len to just PtrMetadata op. //! It should run before inlining! use rustc_hir::def_id::DefId; -use rustc_index::IndexSlice; use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::TyCtxt; pub struct LowerSliceLenCalls; @@ -29,16 +28,11 @@ pub fn lower_slice_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let basic_blocks = body.basic_blocks.as_mut_preserves_cfg(); for block in basic_blocks { // lower `<[_]>::len` calls - lower_slice_len_call(tcx, block, &body.local_decls, slice_len_fn_item_def_id); + lower_slice_len_call(block, slice_len_fn_item_def_id); } } -fn lower_slice_len_call<'tcx>( - tcx: TyCtxt<'tcx>, - block: &mut BasicBlockData<'tcx>, - local_decls: &IndexSlice>, - slice_len_fn_item_def_id: DefId, -) { +fn lower_slice_len_call<'tcx>(block: &mut BasicBlockData<'tcx>, slice_len_fn_item_def_id: DefId) { let terminator = block.terminator(); if let TerminatorKind::Call { func, @@ -50,19 +44,17 @@ fn lower_slice_len_call<'tcx>( } = &terminator.kind // some heuristics for fast rejection && let [arg] = &args[..] - && let Some(arg) = arg.node.place() - && let ty::FnDef(fn_def_id, _) = func.ty(local_decls, tcx).kind() - && *fn_def_id == slice_len_fn_item_def_id + && let Some((fn_def_id, _)) = func.const_fn_def() + && fn_def_id == slice_len_fn_item_def_id { // perform modifications from something like: // _5 = core::slice::::len(move _6) -> bb1 // into: - // _5 = Len(*_6) + // _5 = PtrMetadata(move _6) // goto bb1 // make new RValue for Len - let deref_arg = tcx.mk_place_deref(arg); - let r_value = Rvalue::Len(deref_arg); + let r_value = Rvalue::UnaryOp(UnOp::PtrMetadata, arg.node.clone()); let len_statement_kind = StatementKind::Assign(Box::new((*destination, r_value))); let add_statement = Statement { kind: len_statement_kind, source_info: terminator.source_info }; diff --git a/compiler/rustc_mir_transform/src/normalize_array_len.rs b/compiler/rustc_mir_transform/src/normalize_array_len.rs deleted file mode 100644 index d5e727066614..000000000000 --- a/compiler/rustc_mir_transform/src/normalize_array_len.rs +++ /dev/null @@ -1,103 +0,0 @@ -//! This pass eliminates casting of arrays into slices when their length -//! is taken using `.len()` method. Handy to preserve information in MIR for const prop - -use crate::ssa::SsaLocals; -use rustc_index::IndexVec; -use rustc_middle::mir::visit::*; -use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyCtxt}; - -pub struct NormalizeArrayLen; - -impl<'tcx> MirPass<'tcx> for NormalizeArrayLen { - fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.mir_opt_level() >= 3 - } - - #[instrument(level = "trace", skip(self, tcx, body))] - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - debug!(def_id = ?body.source.def_id()); - normalize_array_len_calls(tcx, body) - } -} - -fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); - let ssa = SsaLocals::new(tcx, body, param_env); - - let slice_lengths = compute_slice_length(tcx, &ssa, body); - debug!(?slice_lengths); - - Replacer { tcx, slice_lengths }.visit_body_preserves_cfg(body); -} - -fn compute_slice_length<'tcx>( - tcx: TyCtxt<'tcx>, - ssa: &SsaLocals, - body: &Body<'tcx>, -) -> IndexVec>> { - let mut slice_lengths = IndexVec::from_elem(None, &body.local_decls); - - for (local, rvalue, _) in ssa.assignments(body) { - match rvalue { - Rvalue::Cast( - CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize), - operand, - cast_ty, - ) => { - let operand_ty = operand.ty(body, tcx); - debug!(?operand_ty); - if let Some(operand_ty) = operand_ty.builtin_deref(true) - && let ty::Array(_, len) = operand_ty.kind() - && let Some(cast_ty) = cast_ty.builtin_deref(true) - && let ty::Slice(..) = cast_ty.kind() - { - slice_lengths[local] = Some(*len); - } - } - // The length information is stored in the fat pointer, so we treat `operand` as a value. - Rvalue::Use(operand) => { - if let Some(rhs) = operand.place() - && let Some(rhs) = rhs.as_local() - { - slice_lengths[local] = slice_lengths[rhs]; - } - } - // The length information is stored in the fat pointer. - // Reborrowing copies length information from one pointer to the other. - Rvalue::Ref(_, _, rhs) | Rvalue::AddressOf(_, rhs) => { - if let [PlaceElem::Deref] = rhs.projection[..] { - slice_lengths[local] = slice_lengths[rhs.local]; - } - } - _ => {} - } - } - - slice_lengths -} - -struct Replacer<'tcx> { - tcx: TyCtxt<'tcx>, - slice_lengths: IndexVec>>, -} - -impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, loc: Location) { - if let Rvalue::Len(place) = rvalue - && let [PlaceElem::Deref] = &place.projection[..] - && let Some(len) = self.slice_lengths[place.local] - { - *rvalue = Rvalue::Use(Operand::Constant(Box::new(ConstOperand { - span: rustc_span::DUMMY_SP, - user_ty: None, - const_: Const::from_ty_const(len, self.tcx.types.usize, self.tcx), - }))); - } - self.super_rvalue(rvalue, loc); - } -} diff --git a/tests/mir-opt/issue_76432.rs b/tests/mir-opt/issue_76432.rs index 0daeb85434f5..6d884063caaf 100644 --- a/tests/mir-opt/issue_76432.rs +++ b/tests/mir-opt/issue_76432.rs @@ -1,6 +1,5 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ compile-flags: -Zmir-enable-passes=-NormalizeArrayLen // Check that we do not insert StorageDead at each target if StorageDead was never seen use std::fmt::Debug; diff --git a/tests/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.panic-abort.diff b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff similarity index 69% rename from tests/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.panic-abort.diff rename to tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff index 5242c5f6afd8..6c0c7a1d4389 100644 --- a/tests/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff @@ -1,5 +1,5 @@ -- // MIR for `array_bound` before NormalizeArrayLen -+ // MIR for `array_bound` after NormalizeArrayLen +- // MIR for `array_bound` before GVN ++ // MIR for `array_bound` after GVN fn array_bound(_1: usize, _2: &[u8; N]) -> u8 { debug index => _1; @@ -24,14 +24,15 @@ _7 = &(*_2); _6 = move _7 as &[u8] (PointerCoercion(Unsize)); StorageDead(_7); -- _5 = Len((*_6)); +- _5 = PtrMetadata(move _6); + _5 = const N; goto -> bb1; } bb1: { StorageDead(_6); - _3 = Lt(move _4, move _5); +- _3 = Lt(move _4, move _5); ++ _3 = Lt(_1, move _5); switchInt(move _3) -> [0: bb4, otherwise: bb2]; } @@ -40,13 +41,17 @@ StorageDead(_4); StorageLive(_8); _8 = _1; - _9 = Len((*_2)); - _10 = Lt(_8, _9); - assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind unreachable]; +- _9 = Len((*_2)); +- _10 = Lt(_8, _9); +- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind unreachable]; ++ _9 = const N; ++ _10 = Lt(_1, const N); ++ assert(move _10, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind unreachable]; } bb3: { - _0 = (*_2)[_8]; +- _0 = (*_2)[_8]; ++ _0 = (*_2)[_1]; StorageDead(_8); goto -> bb5; } diff --git a/tests/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff similarity index 69% rename from tests/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.panic-unwind.diff rename to tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff index a9e99933b123..ed41703c8734 100644 --- a/tests/mir-opt/lower_array_len.array_bound.NormalizeArrayLen.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff @@ -1,5 +1,5 @@ -- // MIR for `array_bound` before NormalizeArrayLen -+ // MIR for `array_bound` after NormalizeArrayLen +- // MIR for `array_bound` before GVN ++ // MIR for `array_bound` after GVN fn array_bound(_1: usize, _2: &[u8; N]) -> u8 { debug index => _1; @@ -24,14 +24,15 @@ _7 = &(*_2); _6 = move _7 as &[u8] (PointerCoercion(Unsize)); StorageDead(_7); -- _5 = Len((*_6)); +- _5 = PtrMetadata(move _6); + _5 = const N; goto -> bb1; } bb1: { StorageDead(_6); - _3 = Lt(move _4, move _5); +- _3 = Lt(move _4, move _5); ++ _3 = Lt(_1, move _5); switchInt(move _3) -> [0: bb4, otherwise: bb2]; } @@ -40,13 +41,17 @@ StorageDead(_4); StorageLive(_8); _8 = _1; - _9 = Len((*_2)); - _10 = Lt(_8, _9); - assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind continue]; +- _9 = Len((*_2)); +- _10 = Lt(_8, _9); +- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind continue]; ++ _9 = const N; ++ _10 = Lt(_1, const N); ++ assert(move _10, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind continue]; } bb3: { - _0 = (*_2)[_8]; +- _0 = (*_2)[_8]; ++ _0 = (*_2)[_1]; StorageDead(_8); goto -> bb5; } diff --git a/tests/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.panic-abort.diff b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff similarity index 60% rename from tests/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.panic-abort.diff rename to tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff index 7749ba6beca7..80e8ea37f418 100644 --- a/tests/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff @@ -1,5 +1,5 @@ -- // MIR for `array_bound_mut` before NormalizeArrayLen -+ // MIR for `array_bound_mut` after NormalizeArrayLen +- // MIR for `array_bound_mut` before GVN ++ // MIR for `array_bound_mut` after GVN fn array_bound_mut(_1: usize, _2: &mut [u8; N]) -> u8 { debug index => _1; @@ -27,14 +27,15 @@ _7 = &(*_2); _6 = move _7 as &[u8] (PointerCoercion(Unsize)); StorageDead(_7); -- _5 = Len((*_6)); +- _5 = PtrMetadata(move _6); + _5 = const N; goto -> bb1; } bb1: { StorageDead(_6); - _3 = Lt(move _4, move _5); +- _3 = Lt(move _4, move _5); ++ _3 = Lt(_1, move _5); switchInt(move _3) -> [0: bb4, otherwise: bb2]; } @@ -43,13 +44,17 @@ StorageDead(_4); StorageLive(_8); _8 = _1; - _9 = Len((*_2)); - _10 = Lt(_8, _9); - assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind unreachable]; +- _9 = Len((*_2)); +- _10 = Lt(_8, _9); +- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind unreachable]; ++ _9 = const N; ++ _10 = Lt(_1, const N); ++ assert(move _10, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind unreachable]; } bb3: { - _0 = (*_2)[_8]; +- _0 = (*_2)[_8]; ++ _0 = (*_2)[_1]; StorageDead(_8); goto -> bb6; } @@ -59,13 +64,17 @@ StorageDead(_4); StorageLive(_11); _11 = const 0_usize; - _12 = Len((*_2)); - _13 = Lt(_11, _12); - assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb5, unwind unreachable]; +- _12 = Len((*_2)); +- _13 = Lt(_11, _12); +- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb5, unwind unreachable]; ++ _12 = const N; ++ _13 = Lt(const 0_usize, const N); ++ assert(move _13, "index out of bounds: the length is {} but the index is {}", const N, const 0_usize) -> [success: bb5, unwind unreachable]; } bb5: { - (*_2)[_11] = const 42_u8; +- (*_2)[_11] = const 42_u8; ++ (*_2)[0 of 1] = const 42_u8; StorageDead(_11); _0 = const 42_u8; goto -> bb6; diff --git a/tests/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff similarity index 60% rename from tests/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.panic-unwind.diff rename to tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff index fcc2c1653dc1..6e67a6c17efb 100644 --- a/tests/mir-opt/lower_array_len.array_bound_mut.NormalizeArrayLen.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff @@ -1,5 +1,5 @@ -- // MIR for `array_bound_mut` before NormalizeArrayLen -+ // MIR for `array_bound_mut` after NormalizeArrayLen +- // MIR for `array_bound_mut` before GVN ++ // MIR for `array_bound_mut` after GVN fn array_bound_mut(_1: usize, _2: &mut [u8; N]) -> u8 { debug index => _1; @@ -27,14 +27,15 @@ _7 = &(*_2); _6 = move _7 as &[u8] (PointerCoercion(Unsize)); StorageDead(_7); -- _5 = Len((*_6)); +- _5 = PtrMetadata(move _6); + _5 = const N; goto -> bb1; } bb1: { StorageDead(_6); - _3 = Lt(move _4, move _5); +- _3 = Lt(move _4, move _5); ++ _3 = Lt(_1, move _5); switchInt(move _3) -> [0: bb4, otherwise: bb2]; } @@ -43,13 +44,17 @@ StorageDead(_4); StorageLive(_8); _8 = _1; - _9 = Len((*_2)); - _10 = Lt(_8, _9); - assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind continue]; +- _9 = Len((*_2)); +- _10 = Lt(_8, _9); +- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind continue]; ++ _9 = const N; ++ _10 = Lt(_1, const N); ++ assert(move _10, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind continue]; } bb3: { - _0 = (*_2)[_8]; +- _0 = (*_2)[_8]; ++ _0 = (*_2)[_1]; StorageDead(_8); goto -> bb6; } @@ -59,13 +64,17 @@ StorageDead(_4); StorageLive(_11); _11 = const 0_usize; - _12 = Len((*_2)); - _13 = Lt(_11, _12); - assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb5, unwind continue]; +- _12 = Len((*_2)); +- _13 = Lt(_11, _12); +- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb5, unwind continue]; ++ _12 = const N; ++ _13 = Lt(const 0_usize, const N); ++ assert(move _13, "index out of bounds: the length is {} but the index is {}", const N, const 0_usize) -> [success: bb5, unwind continue]; } bb5: { - (*_2)[_11] = const 42_u8; +- (*_2)[_11] = const 42_u8; ++ (*_2)[0 of 1] = const 42_u8; StorageDead(_11); _0 = const 42_u8; goto -> bb6; diff --git a/tests/mir-opt/lower_array_len.array_len.NormalizeArrayLen.panic-abort.diff b/tests/mir-opt/lower_array_len.array_len.GVN.panic-abort.diff similarity index 78% rename from tests/mir-opt/lower_array_len.array_len.NormalizeArrayLen.panic-abort.diff rename to tests/mir-opt/lower_array_len.array_len.GVN.panic-abort.diff index 1bdc62183c37..9c1b9a708c59 100644 --- a/tests/mir-opt/lower_array_len.array_len.NormalizeArrayLen.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_len.GVN.panic-abort.diff @@ -1,5 +1,5 @@ -- // MIR for `array_len` before NormalizeArrayLen -+ // MIR for `array_len` after NormalizeArrayLen +- // MIR for `array_len` before GVN ++ // MIR for `array_len` after GVN fn array_len(_1: &[u8; N]) -> usize { debug arr => _1; @@ -13,7 +13,7 @@ _3 = &(*_1); _2 = move _3 as &[u8] (PointerCoercion(Unsize)); StorageDead(_3); -- _0 = Len((*_2)); +- _0 = PtrMetadata(move _2); + _0 = const N; goto -> bb1; } diff --git a/tests/mir-opt/lower_array_len.array_len.NormalizeArrayLen.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_len.GVN.panic-unwind.diff similarity index 78% rename from tests/mir-opt/lower_array_len.array_len.NormalizeArrayLen.panic-unwind.diff rename to tests/mir-opt/lower_array_len.array_len.GVN.panic-unwind.diff index 1bdc62183c37..9c1b9a708c59 100644 --- a/tests/mir-opt/lower_array_len.array_len.NormalizeArrayLen.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_len.GVN.panic-unwind.diff @@ -1,5 +1,5 @@ -- // MIR for `array_len` before NormalizeArrayLen -+ // MIR for `array_len` after NormalizeArrayLen +- // MIR for `array_len` before GVN ++ // MIR for `array_len` after GVN fn array_len(_1: &[u8; N]) -> usize { debug arr => _1; @@ -13,7 +13,7 @@ _3 = &(*_1); _2 = move _3 as &[u8] (PointerCoercion(Unsize)); StorageDead(_3); -- _0 = Len((*_2)); +- _0 = PtrMetadata(move _2); + _0 = const N; goto -> bb1; } diff --git a/tests/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.panic-abort.diff b/tests/mir-opt/lower_array_len.array_len_by_value.GVN.panic-abort.diff similarity index 76% rename from tests/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.panic-abort.diff rename to tests/mir-opt/lower_array_len.array_len_by_value.GVN.panic-abort.diff index 9862b2fad8ee..97fa503ac2e4 100644 --- a/tests/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_len_by_value.GVN.panic-abort.diff @@ -1,5 +1,5 @@ -- // MIR for `array_len_by_value` before NormalizeArrayLen -+ // MIR for `array_len_by_value` after NormalizeArrayLen +- // MIR for `array_len_by_value` before GVN ++ // MIR for `array_len_by_value` after GVN fn array_len_by_value(_1: [u8; N]) -> usize { debug arr => _1; @@ -13,7 +13,7 @@ _3 = &_1; _2 = move _3 as &[u8] (PointerCoercion(Unsize)); StorageDead(_3); -- _0 = Len((*_2)); +- _0 = PtrMetadata(move _2); + _0 = const N; goto -> bb1; } diff --git a/tests/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_len_by_value.GVN.panic-unwind.diff similarity index 76% rename from tests/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.panic-unwind.diff rename to tests/mir-opt/lower_array_len.array_len_by_value.GVN.panic-unwind.diff index 9862b2fad8ee..97fa503ac2e4 100644 --- a/tests/mir-opt/lower_array_len.array_len_by_value.NormalizeArrayLen.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_len_by_value.GVN.panic-unwind.diff @@ -1,5 +1,5 @@ -- // MIR for `array_len_by_value` before NormalizeArrayLen -+ // MIR for `array_len_by_value` after NormalizeArrayLen +- // MIR for `array_len_by_value` before GVN ++ // MIR for `array_len_by_value` after GVN fn array_len_by_value(_1: [u8; N]) -> usize { debug arr => _1; @@ -13,7 +13,7 @@ _3 = &_1; _2 = move _3 as &[u8] (PointerCoercion(Unsize)); StorageDead(_3); -- _0 = Len((*_2)); +- _0 = PtrMetadata(move _2); + _0 = const N; goto -> bb1; } diff --git a/tests/mir-opt/lower_array_len.array_len_raw.NormalizeArrayLen.panic-abort.diff b/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-abort.diff similarity index 82% rename from tests/mir-opt/lower_array_len.array_len_raw.NormalizeArrayLen.panic-abort.diff rename to tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-abort.diff index 633a344a2edb..b5e8b66813ae 100644 --- a/tests/mir-opt/lower_array_len.array_len_raw.NormalizeArrayLen.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-abort.diff @@ -1,5 +1,5 @@ -- // MIR for `array_len_raw` before NormalizeArrayLen -+ // MIR for `array_len_raw` after NormalizeArrayLen +- // MIR for `array_len_raw` before GVN ++ // MIR for `array_len_raw` after GVN fn array_len_raw(_1: [u8; N]) -> usize { debug arr => _1; @@ -18,7 +18,8 @@ } bb0: { - StorageLive(_2); +- StorageLive(_2); ++ nop; StorageLive(_3); StorageLive(_4); _4 = &_1; @@ -32,7 +33,7 @@ StorageLive(_7); _7 = &(*_5); _6 = &(*_7); -- _0 = Len((*_6)); +- _0 = PtrMetadata(move _6); + _0 = const N; goto -> bb1; } @@ -40,7 +41,8 @@ bb1: { StorageDead(_6); StorageDead(_5); - StorageDead(_2); +- StorageDead(_2); ++ nop; StorageDead(_7); return; } diff --git a/tests/mir-opt/lower_array_len.array_len_raw.NormalizeArrayLen.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-unwind.diff similarity index 82% rename from tests/mir-opt/lower_array_len.array_len_raw.NormalizeArrayLen.panic-unwind.diff rename to tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-unwind.diff index 633a344a2edb..b5e8b66813ae 100644 --- a/tests/mir-opt/lower_array_len.array_len_raw.NormalizeArrayLen.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-unwind.diff @@ -1,5 +1,5 @@ -- // MIR for `array_len_raw` before NormalizeArrayLen -+ // MIR for `array_len_raw` after NormalizeArrayLen +- // MIR for `array_len_raw` before GVN ++ // MIR for `array_len_raw` after GVN fn array_len_raw(_1: [u8; N]) -> usize { debug arr => _1; @@ -18,7 +18,8 @@ } bb0: { - StorageLive(_2); +- StorageLive(_2); ++ nop; StorageLive(_3); StorageLive(_4); _4 = &_1; @@ -32,7 +33,7 @@ StorageLive(_7); _7 = &(*_5); _6 = &(*_7); -- _0 = Len((*_6)); +- _0 = PtrMetadata(move _6); + _0 = const N; goto -> bb1; } @@ -40,7 +41,8 @@ bb1: { StorageDead(_6); StorageDead(_5); - StorageDead(_2); +- StorageDead(_2); ++ nop; StorageDead(_7); return; } diff --git a/tests/mir-opt/lower_array_len.array_len_reborrow.NormalizeArrayLen.panic-abort.diff b/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-abort.diff similarity index 80% rename from tests/mir-opt/lower_array_len.array_len_reborrow.NormalizeArrayLen.panic-abort.diff rename to tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-abort.diff index ecba06a2e4de..0299c6acd804 100644 --- a/tests/mir-opt/lower_array_len.array_len_reborrow.NormalizeArrayLen.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-abort.diff @@ -1,5 +1,5 @@ -- // MIR for `array_len_reborrow` before NormalizeArrayLen -+ // MIR for `array_len_reborrow` after NormalizeArrayLen +- // MIR for `array_len_reborrow` before GVN ++ // MIR for `array_len_reborrow` after GVN fn array_len_reborrow(_1: [u8; N]) -> usize { debug arr => _1; @@ -17,7 +17,8 @@ } bb0: { - StorageLive(_2); +- StorageLive(_2); ++ nop; StorageLive(_3); StorageLive(_4); _4 = &mut _1; @@ -29,7 +30,7 @@ _5 = &(*_2); StorageLive(_6); _6 = &(*_5); -- _0 = Len((*_6)); +- _0 = PtrMetadata(move _6); + _0 = const N; goto -> bb1; } @@ -37,7 +38,8 @@ bb1: { StorageDead(_6); StorageDead(_5); - StorageDead(_2); +- StorageDead(_2); ++ nop; return; } } diff --git a/tests/mir-opt/lower_array_len.array_len_reborrow.NormalizeArrayLen.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-unwind.diff similarity index 80% rename from tests/mir-opt/lower_array_len.array_len_reborrow.NormalizeArrayLen.panic-unwind.diff rename to tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-unwind.diff index ecba06a2e4de..0299c6acd804 100644 --- a/tests/mir-opt/lower_array_len.array_len_reborrow.NormalizeArrayLen.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-unwind.diff @@ -1,5 +1,5 @@ -- // MIR for `array_len_reborrow` before NormalizeArrayLen -+ // MIR for `array_len_reborrow` after NormalizeArrayLen +- // MIR for `array_len_reborrow` before GVN ++ // MIR for `array_len_reborrow` after GVN fn array_len_reborrow(_1: [u8; N]) -> usize { debug arr => _1; @@ -17,7 +17,8 @@ } bb0: { - StorageLive(_2); +- StorageLive(_2); ++ nop; StorageLive(_3); StorageLive(_4); _4 = &mut _1; @@ -29,7 +30,7 @@ _5 = &(*_2); StorageLive(_6); _6 = &(*_5); -- _0 = Len((*_6)); +- _0 = PtrMetadata(move _6); + _0 = const N; goto -> bb1; } @@ -37,7 +38,8 @@ bb1: { StorageDead(_6); StorageDead(_5); - StorageDead(_2); +- StorageDead(_2); ++ nop; return; } } diff --git a/tests/mir-opt/lower_array_len.rs b/tests/mir-opt/lower_array_len.rs index 62fc9ef67d66..caa598d067a3 100644 --- a/tests/mir-opt/lower_array_len.rs +++ b/tests/mir-opt/lower_array_len.rs @@ -1,20 +1,20 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ test-mir-pass: NormalizeArrayLen +//@ test-mir-pass: GVN //@ compile-flags: -Zmir-enable-passes=+LowerSliceLenCalls -// EMIT_MIR lower_array_len.array_bound.NormalizeArrayLen.diff +// EMIT_MIR lower_array_len.array_bound.GVN.diff pub fn array_bound(index: usize, slice: &[u8; N]) -> u8 { // CHECK-LABEL: fn array_bound( // CHECK: [[len:_.*]] = const N; - // CHECK: Lt(move {{_.*}}, move [[len]]); + // CHECK: Lt(_1, move [[len]]); if index < slice.len() { slice[index] } else { 42 } } -// EMIT_MIR lower_array_len.array_bound_mut.NormalizeArrayLen.diff +// EMIT_MIR lower_array_len.array_bound_mut.GVN.diff pub fn array_bound_mut(index: usize, slice: &mut [u8; N]) -> u8 { // CHECK-LABEL: fn array_bound_mut( // CHECK: [[len:_.*]] = const N; - // CHECK: Lt(move {{_.*}}, move [[len]]); + // CHECK: Lt(_1, move [[len]]); if index < slice.len() { slice[index] } else { @@ -24,21 +24,21 @@ pub fn array_bound_mut(index: usize, slice: &mut [u8; N]) -> u8 } } -// EMIT_MIR lower_array_len.array_len.NormalizeArrayLen.diff +// EMIT_MIR lower_array_len.array_len.GVN.diff pub fn array_len(arr: &[u8; N]) -> usize { // CHECK-LABEL: fn array_len( // CHECK: _0 = const N; arr.len() } -// EMIT_MIR lower_array_len.array_len_by_value.NormalizeArrayLen.diff +// EMIT_MIR lower_array_len.array_len_by_value.GVN.diff pub fn array_len_by_value(arr: [u8; N]) -> usize { // CHECK-LABEL: fn array_len_by_value( // CHECK: _0 = const N; arr.len() } -// EMIT_MIR lower_array_len.array_len_reborrow.NormalizeArrayLen.diff +// EMIT_MIR lower_array_len.array_len_reborrow.GVN.diff pub fn array_len_reborrow(mut arr: [u8; N]) -> usize { // CHECK-LABEL: fn array_len_reborrow( // CHECK: _0 = const N; @@ -47,7 +47,7 @@ pub fn array_len_reborrow(mut arr: [u8; N]) -> usize { arr.len() } -// EMIT_MIR lower_array_len.array_len_raw.NormalizeArrayLen.diff +// EMIT_MIR lower_array_len.array_len_raw.GVN.diff pub fn array_len_raw(arr: [u8; N]) -> usize { // CHECK-LABEL: fn array_len_raw( // CHECK: _0 = const N; diff --git a/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-abort.diff b/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-abort.diff index 7f752ca0f5a2..a212ee678811 100644 --- a/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-abort.diff +++ b/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-abort.diff @@ -21,7 +21,7 @@ StorageLive(_6); _6 = &(*_2); - _5 = core::slice::::len(move _6) -> [return: bb1, unwind unreachable]; -+ _5 = Len((*_6)); ++ _5 = PtrMetadata(move _6); + goto -> bb1; } diff --git a/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-unwind.diff b/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-unwind.diff index d73b563a0e5d..38ec8a5b0c72 100644 --- a/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-unwind.diff +++ b/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-unwind.diff @@ -21,7 +21,7 @@ StorageLive(_6); _6 = &(*_2); - _5 = core::slice::::len(move _6) -> [return: bb1, unwind continue]; -+ _5 = Len((*_6)); ++ _5 = PtrMetadata(move _6); + goto -> bb1; } diff --git a/tests/mir-opt/pre-codegen/slice_index.rs b/tests/mir-opt/pre-codegen/slice_index.rs index 886e57a3380c..6ddc4ad02207 100644 --- a/tests/mir-opt/pre-codegen/slice_index.rs +++ b/tests/mir-opt/pre-codegen/slice_index.rs @@ -19,7 +19,7 @@ pub fn slice_index_usize(slice: &[u32], index: usize) -> u32 { // EMIT_MIR slice_index.slice_get_mut_usize.PreCodegen.after.mir pub fn slice_get_mut_usize(slice: &mut [u32], index: usize) -> Option<&mut u32> { // CHECK-LABEL: slice_get_mut_usize - // CHECK: [[LEN:_[0-9]+]] = Len((*_1)) + // CHECK: [[LEN:_[0-9]+]] = PtrMetadata(_1) // CHECK: Lt(_2, move [[LEN]]) // CHECK-NOT: precondition_check slice.get_mut(index) diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir index f2ef2b0cc3c6..d7f09fafeebe 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir @@ -23,7 +23,7 @@ fn slice_get_mut_usize(_1: &mut [u32], _2: usize) -> Option<&mut u32> { StorageLive(_7); StorageLive(_4); StorageLive(_3); - _3 = Len((*_1)); + _3 = PtrMetadata(_1); _4 = Lt(_2, move _3); switchInt(move _4) -> [0: bb1, otherwise: bb2]; } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir index f2ef2b0cc3c6..d7f09fafeebe 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir @@ -23,7 +23,7 @@ fn slice_get_mut_usize(_1: &mut [u32], _2: usize) -> Option<&mut u32> { StorageLive(_7); StorageLive(_4); StorageLive(_3); - _3 = Len((*_1)); + _3 = PtrMetadata(_1); _4 = Lt(_2, move _3); switchInt(move _4) -> [0: bb1, otherwise: bb2]; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir index 35a1c783bf23..953e7550479d 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir @@ -90,7 +90,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { StorageLive(_6); StorageLive(_4); StorageLive(_5); - _3 = Len((*_1)); + _3 = PtrMetadata(_1); _4 = &raw const (*_1); _5 = _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: _5 }; diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir index f805967d64ff..d5fdb2e08cc1 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir @@ -65,7 +65,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { StorageLive(_6); StorageLive(_4); StorageLive(_5); - _3 = Len((*_1)); + _3 = PtrMetadata(_1); _4 = &raw const (*_1); _5 = _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: _5 }; diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir index 6ae64200f4e7..cc2beac06f23 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -57,7 +57,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageLive(_6); StorageLive(_4); StorageLive(_5); - _3 = Len((*_1)); + _3 = PtrMetadata(_1); _4 = &raw const (*_1); _5 = _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: _5 }; diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir index ac72329fcd64..d66edb28570f 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -57,7 +57,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageLive(_6); StorageLive(_4); StorageLive(_5); - _3 = Len((*_1)); + _3 = PtrMetadata(_1); _4 = &raw const (*_1); _5 = _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: _5 }; diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir index dbe6f39548c8..cea2fcbcdc0b 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir @@ -40,7 +40,7 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb0: { - _3 = Len((*_1)); + _3 = PtrMetadata(_1); StorageLive(_4); _4 = const 0_usize; goto -> bb1; diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir index 5b6441cfb3b2..bd658a770ea9 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir @@ -40,7 +40,7 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb0: { - _3 = Len((*_1)); + _3 = PtrMetadata(_1); StorageLive(_4); _4 = const 0_usize; goto -> bb1; diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir index 8008336e268c..fbb887fe76a5 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir @@ -65,7 +65,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageLive(_6); StorageLive(_4); StorageLive(_5); - _3 = Len((*_1)); + _3 = PtrMetadata(_1); _4 = &raw const (*_1); _5 = _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: _5 }; diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir index 47253bf7a0d3..db9409f72ab1 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir @@ -65,7 +65,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageLive(_6); StorageLive(_4); StorageLive(_5); - _3 = Len((*_1)); + _3 = PtrMetadata(_1); _4 = &raw const (*_1); _5 = _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: _5 }; From 4341cb709d4d1c1d26d9aea55b00ff16945fe644 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 16 Jun 2024 02:31:24 -0700 Subject: [PATCH 145/147] I'd never even heard of a coverage map --- tests/coverage/closure_macro.cov-map | 8 ++++---- tests/coverage/closure_macro_async.cov-map | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/coverage/closure_macro.cov-map b/tests/coverage/closure_macro.cov-map index 156947f4e21c..21fad22f58f5 100644 --- a/tests/coverage/closure_macro.cov-map +++ b/tests/coverage/closure_macro.cov-map @@ -22,19 +22,19 @@ Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) Function name: closure_macro::main::{closure#0} -Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 00, 05, 01, 10, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 00, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] +Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 05, 01, 10, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 0d, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] Number of files: 1 - file 0 => global file 1 Number of expressions: 3 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) - expression 1 operands: lhs = Counter(1), rhs = Expression(2, Add) -- expression 2 operands: lhs = Counter(2), rhs = Zero +- expression 2 operands: lhs = Counter(2), rhs = Counter(3) Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 16, 28) to (start + 3, 33) - Code(Counter(1)) at (prev + 4, 17) to (start + 1, 39) - Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 22) = (c0 - c1) -- Code(Zero) at (prev + 0, 23) to (start + 0, 30) +- Code(Counter(3)) at (prev + 0, 23) to (start + 0, 30) - Code(Expression(1, Add)) at (prev + 2, 9) to (start + 0, 10) - = (c1 + (c2 + Zero)) + = (c1 + (c2 + c3)) diff --git a/tests/coverage/closure_macro_async.cov-map b/tests/coverage/closure_macro_async.cov-map index 0f2b4e017483..f2efd5505919 100644 --- a/tests/coverage/closure_macro_async.cov-map +++ b/tests/coverage/closure_macro_async.cov-map @@ -30,19 +30,19 @@ Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) Function name: closure_macro_async::test::{closure#0}::{closure#0} -Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 00, 05, 01, 12, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 00, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] +Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 05, 01, 12, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 0d, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] Number of files: 1 - file 0 => global file 1 Number of expressions: 3 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) - expression 1 operands: lhs = Counter(1), rhs = Expression(2, Add) -- expression 2 operands: lhs = Counter(2), rhs = Zero +- expression 2 operands: lhs = Counter(2), rhs = Counter(3) Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 18, 28) to (start + 3, 33) - Code(Counter(1)) at (prev + 4, 17) to (start + 1, 39) - Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 22) = (c0 - c1) -- Code(Zero) at (prev + 0, 23) to (start + 0, 30) +- Code(Counter(3)) at (prev + 0, 23) to (start + 0, 30) - Code(Expression(1, Add)) at (prev + 2, 9) to (start + 0, 10) - = (c1 + (c2 + Zero)) + = (c1 + (c2 + c3)) From 55d13379ac57227a08ba537316adcf2d3c388f57 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 16 Jun 2024 22:25:57 -0700 Subject: [PATCH 146/147] [GVN] Add tests for generic pointees with PtrMetadata --- .../src/build/custom/parse/instruction.rs | 4 ++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mir.rs | 7 +++ ...generic_cast_metadata.GVN.panic-abort.diff | 38 ++++++++++++++ ...eneric_cast_metadata.GVN.panic-unwind.diff | 38 ++++++++++++++ tests/mir-opt/gvn.rs | 50 +++++++++++++++++++ 6 files changed, 138 insertions(+) create mode 100644 tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-abort.diff create mode 100644 tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-unwind.diff diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index b1a305efa4c6..7549481c1b30 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -193,6 +193,10 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { let source = self.parse_operand(args[0])?; Ok(Rvalue::Cast(CastKind::Transmute, source, expr.ty)) }, + @call(mir_cast_ptr_to_ptr, args) => { + let source = self.parse_operand(args[0])?; + Ok(Rvalue::Cast(CastKind::PtrToPtr, source, expr.ty)) + }, @call(mir_checked, args) => { parse_by_kind!(self, args[0], _, "binary op", ExprKind::Binary { op, lhs, rhs } => { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 55eba257ca21..a508c2794390 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1175,6 +1175,7 @@ symbols! { mir_assume, mir_basic_block, mir_call, + mir_cast_ptr_to_ptr, mir_cast_transmute, mir_checked, mir_copy_for_deref, diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index ec8488009b96..1daf1d723fb9 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -444,6 +444,13 @@ define!( /// generated via the normal `mem::transmute`. fn CastTransmute(operand: T) -> U ); +define!( + "mir_cast_ptr_to_ptr", + /// Emits a `CastKind::PtrToPtr` cast. + /// + /// This allows bypassing normal validation to generate strange casts. + fn CastPtrToPtr(operand: T) -> U +); define!( "mir_make_place", #[doc(hidden)] diff --git a/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-abort.diff b/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-abort.diff new file mode 100644 index 000000000000..1d462a8a23c2 --- /dev/null +++ b/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-abort.diff @@ -0,0 +1,38 @@ +- // MIR for `generic_cast_metadata` before GVN ++ // MIR for `generic_cast_metadata` after GVN + + fn generic_cast_metadata(_1: *const [T], _2: *const A, _3: *const B) -> () { + let mut _0: (); + let mut _4: *const T; + let mut _5: (); + let mut _6: *const (&A, [T]); + let mut _7: usize; + let mut _8: *const (T, B); + let mut _9: ::Metadata; + let mut _10: *const (T, A); + let mut _11: ::Metadata; + let mut _12: *mut A; + let mut _13: ::Metadata; + let mut _14: *mut B; + let mut _15: ::Metadata; + + bb0: { + _4 = _1 as *const T (PtrToPtr); + _5 = PtrMetadata(_4); + _6 = _1 as *const (&A, [T]) (PtrToPtr); +- _7 = PtrMetadata(_6); ++ _7 = PtrMetadata(_1); + _8 = _2 as *const (T, B) (PtrToPtr); + _9 = PtrMetadata(_8); + _10 = _2 as *const (T, A) (PtrToPtr); +- _11 = PtrMetadata(_10); ++ _11 = PtrMetadata(_2); + _12 = _3 as *mut A (PtrToPtr); + _13 = PtrMetadata(_12); + _14 = _3 as *mut B (PtrToPtr); +- _15 = PtrMetadata(_14); ++ _15 = PtrMetadata(_3); + return; + } + } + diff --git a/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-unwind.diff b/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-unwind.diff new file mode 100644 index 000000000000..1d462a8a23c2 --- /dev/null +++ b/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-unwind.diff @@ -0,0 +1,38 @@ +- // MIR for `generic_cast_metadata` before GVN ++ // MIR for `generic_cast_metadata` after GVN + + fn generic_cast_metadata(_1: *const [T], _2: *const A, _3: *const B) -> () { + let mut _0: (); + let mut _4: *const T; + let mut _5: (); + let mut _6: *const (&A, [T]); + let mut _7: usize; + let mut _8: *const (T, B); + let mut _9: ::Metadata; + let mut _10: *const (T, A); + let mut _11: ::Metadata; + let mut _12: *mut A; + let mut _13: ::Metadata; + let mut _14: *mut B; + let mut _15: ::Metadata; + + bb0: { + _4 = _1 as *const T (PtrToPtr); + _5 = PtrMetadata(_4); + _6 = _1 as *const (&A, [T]) (PtrToPtr); +- _7 = PtrMetadata(_6); ++ _7 = PtrMetadata(_1); + _8 = _2 as *const (T, B) (PtrToPtr); + _9 = PtrMetadata(_8); + _10 = _2 as *const (T, A) (PtrToPtr); +- _11 = PtrMetadata(_10); ++ _11 = PtrMetadata(_2); + _12 = _3 as *mut A (PtrToPtr); + _13 = PtrMetadata(_12); + _14 = _3 as *mut B (PtrToPtr); +- _15 = PtrMetadata(_14); ++ _15 = PtrMetadata(_3); + return; + } + } + diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 74f1849c42a9..8bc2550b8f55 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -834,6 +834,55 @@ fn array_len(x: &mut [i32; 42]) -> usize { std::intrinsics::ptr_metadata(x) } +#[custom_mir(dialect = "runtime")] +fn generic_cast_metadata(ps: *const [T], pa: *const A, pb: *const B) { + // CHECK-LABEL: fn generic_cast_metadata + mir! { + { + // These tests check that we correctly do or don't elide casts + // when the pointee metadata do or don't match, respectively. + + // Metadata usize -> (), do not optimize. + // CHECK: [[T:_.+]] = _1 as + // CHECK-NEXT: PtrMetadata([[T]]) + let t1 = CastPtrToPtr::<_, *const T>(ps); + let m1 = PtrMetadata(t1); + + // `(&A, [T])` has `usize` metadata, same as `[T]`, yes optimize. + // CHECK: [[T:_.+]] = _1 as + // CHECK-NEXT: PtrMetadata(_1) + let t2 = CastPtrToPtr::<_, *const (&A, [T])>(ps); + let m2 = PtrMetadata(t2); + + // Tail `A` and tail `B`, do not optimize. + // CHECK: [[T:_.+]] = _2 as + // CHECK-NEXT: PtrMetadata([[T]]) + let t3 = CastPtrToPtr::<_, *const (T, B)>(pa); + let m3 = PtrMetadata(t3); + + // Both have tail `A`, yes optimize. + // CHECK: [[T:_.+]] = _2 as + // CHECK-NEXT: PtrMetadata(_2) + let t4 = CastPtrToPtr::<_, *const (T, A)>(pa); + let m4 = PtrMetadata(t4); + + // Tail `B` and tail `A`, do not optimize. + // CHECK: [[T:_.+]] = _3 as + // CHECK-NEXT: PtrMetadata([[T]]) + let t5 = CastPtrToPtr::<_, *mut A>(pb); + let m5 = PtrMetadata(t5); + + // Both have tail `B`, yes optimize. + // CHECK: [[T:_.+]] = _3 as + // CHECK-NEXT: PtrMetadata(_3) + let t6 = CastPtrToPtr::<_, *mut B>(pb); + let m6 = PtrMetadata(t6); + + Return() + } + } +} + fn main() { subexpression_elimination(2, 4, 5); wrap_unwrap(5); @@ -900,3 +949,4 @@ fn identity(x: T) -> T { // EMIT_MIR gvn.casts_before_aggregate_raw_ptr.GVN.diff // EMIT_MIR gvn.manual_slice_mut_len.GVN.diff // EMIT_MIR gvn.array_len.GVN.diff +// EMIT_MIR gvn.generic_cast_metadata.GVN.diff From 553a69030e5a086eb3841d020db8c9c463948c72 Mon Sep 17 00:00:00 2001 From: Ana Hobden Date: Mon, 24 Jun 2024 12:57:14 -0700 Subject: [PATCH 147/147] Specify target specific linker for riscv64gc-gnu job --- src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile b/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile index a9ffa5918b5b..a52c3839196f 100644 --- a/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile @@ -91,7 +91,9 @@ RUN sh /scripts/sccache.sh # Avoid "fatal: detected dubious ownership in repository at '/checkout'" error RUN git config --global --add safe.directory /checkout -ENV RUST_CONFIGURE_ARGS --qemu-riscv64-rootfs=/tmp/rootfs +ENV RUST_CONFIGURE_ARGS \ + --qemu-riscv64-rootfs=/tmp/rootfs \ + --set target.riscv64gc-unknown-linux-gnu.linker=riscv64-linux-gnu-gcc ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target riscv64gc-unknown-linux-gnu ENV NO_CHANGE_USER=1