diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4559246ce4276..81fb39cdc5692 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: # If you want to modify CI jobs, take a look at src/ci/github-actions/jobs.yml. calculate_matrix: name: Calculate job matrix - runs-on: ubuntu-24.04 + runs-on: ubuntu-24.04-arm outputs: jobs: ${{ steps.jobs.outputs.jobs }} run_type: ${{ steps.jobs.outputs.run_type }} diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 8c0c309689902..5e511f1a418b6 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -284,12 +284,14 @@ fn can_cast( let v = match src_layout.ty.kind() { ty::Uint(_) => from_scalar.to_uint(src_layout.size), ty::Int(_) => from_scalar.to_int(src_layout.size) as u128, - _ => unreachable!("invalid int"), + // We can also transform the values of other integer representations (such as char), + // although this may not be practical in real-world scenarios. + _ => return false, }; let size = match *cast_ty.kind() { ty::Int(t) => Integer::from_int_ty(&tcx, t).size(), ty::Uint(t) => Integer::from_uint_ty(&tcx, t).size(), - _ => unreachable!("invalid int"), + _ => return false, }; let v = size.truncate(v); let cast_scalar = ScalarInt::try_from_uint(v, size).unwrap(); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 2a7910a6af4dd..1a44f4af8a629 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3311,26 +3311,44 @@ impl<'a> Parser<'a> { let sm = this.psess.source_map(); if let Ok(expr_lines) = sm.span_to_lines(expr_span) && let Ok(arm_start_lines) = sm.span_to_lines(arm_start_span) - && arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col && expr_lines.lines.len() == 2 { - // We check whether there's any trailing code in the parse span, - // if there isn't, we very likely have the following: - // - // X | &Y => "y" - // | -- - missing comma - // | | - // | arrow_span - // X | &X => "x" - // | - ^^ self.token.span - // | | - // | parsed until here as `"y" & X` - err.span_suggestion_short( - arm_start_span.shrink_to_hi(), - "missing a comma here to end this `match` arm", - ",", - Applicability::MachineApplicable, - ); + if arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col { + // We check whether there's any trailing code in the parse span, + // if there isn't, we very likely have the following: + // + // X | &Y => "y" + // | -- - missing comma + // | | + // | arrow_span + // X | &X => "x" + // | - ^^ self.token.span + // | | + // | parsed until here as `"y" & X` + err.span_suggestion_short( + arm_start_span.shrink_to_hi(), + "missing a comma here to end this `match` arm", + ",", + Applicability::MachineApplicable, + ); + } else if arm_start_lines.lines[0].end_col + rustc_span::CharPos(1) + == expr_lines.lines[0].end_col + { + // similar to the above, but we may typo a `.` or `/` at the end of the line + let comma_span = arm_start_span + .shrink_to_hi() + .with_hi(arm_start_span.hi() + rustc_span::BytePos(1)); + if let Ok(res) = sm.span_to_snippet(comma_span) + && (res == "." || res == "/") + { + err.span_suggestion_short( + comma_span, + "you might have meant to write a `,` to end this `match` arm", + ",", + Applicability::MachineApplicable, + ); + } + } } } else { err.span_label( diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4cfa079e49b44..fb1534d0b2798 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -934,8 +934,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r ) } TyKind::UnsafeBinder(unsafe_binder) => { - // FIXME(unsafe_binder): Better span - let span = ty.span; + let span = ty.span.shrink_to_lo().to(unsafe_binder.inner_ty.span.shrink_to_lo()); self.with_generic_param_rib( &unsafe_binder.generic_params, RibKind::Normal, diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index ea81645aa6491..52b98291ff93f 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1151,7 +1151,7 @@ impl BTreeMap { K: Ord, F: FnMut(&K, &mut V) -> bool, { - self.extract_if(|k, v| !f(k, v)).for_each(drop); + self.extract_if(.., |k, v| !f(k, v)).for_each(drop); } /// Moves all elements from `other` into `self`, leaving `other` empty. @@ -1397,7 +1397,7 @@ impl BTreeMap { } } - /// Creates an iterator that visits all elements (key-value pairs) in + /// Creates an iterator that visits elements (key-value pairs) in the specified range in /// ascending key order and uses a closure to determine if an element /// should be removed. /// @@ -1423,33 +1423,42 @@ impl BTreeMap { /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap = (0..8).map(|x| (x, x)).collect(); - /// let evens: BTreeMap<_, _> = map.extract_if(|k, _v| k % 2 == 0).collect(); + /// let evens: BTreeMap<_, _> = map.extract_if(.., |k, _v| k % 2 == 0).collect(); /// let odds = map; /// assert_eq!(evens.keys().copied().collect::>(), [0, 2, 4, 6]); /// assert_eq!(odds.keys().copied().collect::>(), [1, 3, 5, 7]); + /// + /// let mut map: BTreeMap = (0..8).map(|x| (x, x)).collect(); + /// let low: BTreeMap<_, _> = map.extract_if(0..4, |_k, _v| true).collect(); + /// let high = map; + /// assert_eq!(low.keys().copied().collect::>(), [0, 1, 2, 3]); + /// assert_eq!(high.keys().copied().collect::>(), [4, 5, 6, 7]); /// ``` #[unstable(feature = "btree_extract_if", issue = "70530")] - pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, K, V, F, A> + pub fn extract_if(&mut self, range: R, pred: F) -> ExtractIf<'_, K, V, R, F, A> where K: Ord, + R: RangeBounds, F: FnMut(&K, &mut V) -> bool, { - let (inner, alloc) = self.extract_if_inner(); + let (inner, alloc) = self.extract_if_inner(range); ExtractIf { pred, inner, alloc } } - pub(super) fn extract_if_inner(&mut self) -> (ExtractIfInner<'_, K, V>, A) + pub(super) fn extract_if_inner(&mut self, range: R) -> (ExtractIfInner<'_, K, V, R>, A) where K: Ord, + R: RangeBounds, { if let Some(root) = self.root.as_mut() { let (root, dormant_root) = DormantMutRef::new(root); - let front = root.borrow_mut().first_leaf_edge(); + let first = root.borrow_mut().lower_bound(SearchBound::from_range(range.start_bound())); ( ExtractIfInner { length: &mut self.length, dormant_root: Some(dormant_root), - cur_leaf_edge: Some(front), + cur_leaf_edge: Some(first), + range, }, (*self.alloc).clone(), ) @@ -1459,6 +1468,7 @@ impl BTreeMap { length: &mut self.length, dormant_root: None, cur_leaf_edge: None, + range, }, (*self.alloc).clone(), ) @@ -1917,18 +1927,19 @@ pub struct ExtractIf< 'a, K, V, + R, F, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, > { pred: F, - inner: ExtractIfInner<'a, K, V>, + inner: ExtractIfInner<'a, K, V, R>, /// The BTreeMap will outlive this IntoIter so we don't care about drop order for `alloc`. alloc: A, } /// Most of the implementation of ExtractIf are generic over the type /// of the predicate, thus also serving for BTreeSet::ExtractIf. -pub(super) struct ExtractIfInner<'a, K, V> { +pub(super) struct ExtractIfInner<'a, K, V, R> { /// Reference to the length field in the borrowed map, updated live. length: &'a mut usize, /// Buried reference to the root field in the borrowed map. @@ -1938,10 +1949,13 @@ pub(super) struct ExtractIfInner<'a, K, V> { /// Empty if the map has no root, if iteration went beyond the last leaf edge, /// or if a panic occurred in the predicate. cur_leaf_edge: Option, K, V, marker::Leaf>, marker::Edge>>, + /// Range over which iteration was requested. We don't need the left side, but we + /// can't extract the right side without requiring K: Clone. + range: R, } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl fmt::Debug for ExtractIf<'_, K, V, F, A> +impl fmt::Debug for ExtractIf<'_, K, V, R, F, A> where K: fmt::Debug, V: fmt::Debug, @@ -1953,8 +1967,10 @@ where } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl Iterator for ExtractIf<'_, K, V, F, A> +impl Iterator for ExtractIf<'_, K, V, R, F, A> where + K: PartialOrd, + R: RangeBounds, F: FnMut(&K, &mut V) -> bool, { type Item = (K, V); @@ -1968,7 +1984,7 @@ where } } -impl<'a, K, V> ExtractIfInner<'a, K, V> { +impl<'a, K, V, R> ExtractIfInner<'a, K, V, R> { /// Allow Debug implementations to predict the next element. pub(super) fn peek(&self) -> Option<(&K, &V)> { let edge = self.cur_leaf_edge.as_ref()?; @@ -1978,10 +1994,22 @@ impl<'a, K, V> ExtractIfInner<'a, K, V> { /// Implementation of a typical `ExtractIf::next` method, given the predicate. pub(super) fn next(&mut self, pred: &mut F, alloc: A) -> Option<(K, V)> where + K: PartialOrd, + R: RangeBounds, F: FnMut(&K, &mut V) -> bool, { while let Ok(mut kv) = self.cur_leaf_edge.take()?.next_kv() { let (k, v) = kv.kv_mut(); + + // On creation, we navigated directly to the left bound, so we need only check the + // right bound here to decide whether to stop. + match self.range.end_bound() { + Bound::Included(ref end) if (*k).le(end) => (), + Bound::Excluded(ref end) if (*k).lt(end) => (), + Bound::Unbounded => (), + _ => return None, + } + if pred(k, v) { *self.length -= 1; let (kv, pos) = kv.remove_kv_tracking( @@ -2013,7 +2041,13 @@ impl<'a, K, V> ExtractIfInner<'a, K, V> { } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl FusedIterator for ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} +impl FusedIterator for ExtractIf<'_, K, V, R, F> +where + K: PartialOrd, + R: RangeBounds, + F: FnMut(&K, &mut V) -> bool, +{ +} #[stable(feature = "btree_range", since = "1.17.0")] impl<'a, K, V> Iterator for Range<'a, K, V> { diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 5975134382e7d..79879d31d3dfd 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -944,7 +944,7 @@ mod test_extract_if { #[test] fn empty() { let mut map: BTreeMap = BTreeMap::new(); - map.extract_if(|_, _| unreachable!("there's nothing to decide on")).for_each(drop); + map.extract_if(.., |_, _| unreachable!("there's nothing to decide on")).for_each(drop); assert_eq!(map.height(), None); map.check(); } @@ -954,7 +954,7 @@ mod test_extract_if { fn consumed_keeping_all() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - assert!(map.extract_if(|_, _| false).eq(iter::empty())); + assert!(map.extract_if(.., |_, _| false).eq(iter::empty())); map.check(); } @@ -963,18 +963,42 @@ mod test_extract_if { fn consumed_removing_all() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs.clone()); - assert!(map.extract_if(|_, _| true).eq(pairs)); + assert!(map.extract_if(.., |_, _| true).eq(pairs)); assert!(map.is_empty()); map.check(); } + #[test] + fn consumed_removing_some() { + let pairs = (0..3).map(|i| (i, i)); + let map = BTreeMap::from_iter(pairs); + for x in 0..3 { + for y in 0..3 { + let mut map = map.clone(); + assert!(map.extract_if(x..y, |_, _| true).eq((x..y).map(|i| (i, i)))); + for i in 0..3 { + assert_ne!(map.contains_key(&i), (x..y).contains(&i)); + } + } + } + for x in 0..3 { + for y in 0..2 { + let mut map = map.clone(); + assert!(map.extract_if(x..=y, |_, _| true).eq((x..=y).map(|i| (i, i)))); + for i in 0..3 { + assert_ne!(map.contains_key(&i), (x..=y).contains(&i)); + } + } + } + } + // Explicitly consumes the iterator and modifies values through it. #[test] fn mutating_and_keeping() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); assert!( - map.extract_if(|_, v| { + map.extract_if(.., |_, v| { *v += 6; false }) @@ -991,7 +1015,7 @@ mod test_extract_if { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); assert!( - map.extract_if(|_, v| { + map.extract_if(.., |_, v| { *v += 6; true }) @@ -1005,7 +1029,7 @@ mod test_extract_if { fn underfull_keeping_all() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.extract_if(|_, _| false).for_each(drop); + map.extract_if(.., |_, _| false).for_each(drop); assert!(map.keys().copied().eq(0..3)); map.check(); } @@ -1015,7 +1039,7 @@ mod test_extract_if { let pairs = (0..3).map(|i| (i, i)); for doomed in 0..3 { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i == doomed).for_each(drop); + map.extract_if(.., |i, _| *i == doomed).for_each(drop); assert_eq!(map.len(), 2); map.check(); } @@ -1026,7 +1050,7 @@ mod test_extract_if { let pairs = (0..3).map(|i| (i, i)); for sacred in 0..3 { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i != sacred).for_each(drop); + map.extract_if(.., |i, _| *i != sacred).for_each(drop); assert!(map.keys().copied().eq(sacred..=sacred)); map.check(); } @@ -1036,7 +1060,7 @@ mod test_extract_if { fn underfull_removing_all() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.extract_if(|_, _| true).for_each(drop); + map.extract_if(.., |_, _| true).for_each(drop); assert!(map.is_empty()); map.check(); } @@ -1045,7 +1069,7 @@ mod test_extract_if { fn height_0_keeping_all() { let pairs = (0..node::CAPACITY).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.extract_if(|_, _| false).for_each(drop); + map.extract_if(.., |_, _| false).for_each(drop); assert!(map.keys().copied().eq(0..node::CAPACITY)); map.check(); } @@ -1055,7 +1079,7 @@ mod test_extract_if { let pairs = (0..node::CAPACITY).map(|i| (i, i)); for doomed in 0..node::CAPACITY { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i == doomed).for_each(drop); + map.extract_if(.., |i, _| *i == doomed).for_each(drop); assert_eq!(map.len(), node::CAPACITY - 1); map.check(); } @@ -1066,7 +1090,7 @@ mod test_extract_if { let pairs = (0..node::CAPACITY).map(|i| (i, i)); for sacred in 0..node::CAPACITY { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i != sacred).for_each(drop); + map.extract_if(.., |i, _| *i != sacred).for_each(drop); assert!(map.keys().copied().eq(sacred..=sacred)); map.check(); } @@ -1076,7 +1100,7 @@ mod test_extract_if { fn height_0_removing_all() { let pairs = (0..node::CAPACITY).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.extract_if(|_, _| true).for_each(drop); + map.extract_if(.., |_, _| true).for_each(drop); assert!(map.is_empty()); map.check(); } @@ -1084,7 +1108,7 @@ mod test_extract_if { #[test] fn height_0_keeping_half() { let mut map = BTreeMap::from_iter((0..16).map(|i| (i, i))); - assert_eq!(map.extract_if(|i, _| *i % 2 == 0).count(), 8); + assert_eq!(map.extract_if(.., |i, _| *i % 2 == 0).count(), 8); assert_eq!(map.len(), 8); map.check(); } @@ -1093,7 +1117,7 @@ mod test_extract_if { fn height_1_removing_all() { let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.extract_if(|_, _| true).for_each(drop); + map.extract_if(.., |_, _| true).for_each(drop); assert!(map.is_empty()); map.check(); } @@ -1103,7 +1127,7 @@ mod test_extract_if { let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); for doomed in 0..MIN_INSERTS_HEIGHT_1 { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i == doomed).for_each(drop); + map.extract_if(.., |i, _| *i == doomed).for_each(drop); assert_eq!(map.len(), MIN_INSERTS_HEIGHT_1 - 1); map.check(); } @@ -1114,7 +1138,7 @@ mod test_extract_if { let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); for sacred in 0..MIN_INSERTS_HEIGHT_1 { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i != sacred).for_each(drop); + map.extract_if(.., |i, _| *i != sacred).for_each(drop); assert!(map.keys().copied().eq(sacred..=sacred)); map.check(); } @@ -1125,7 +1149,7 @@ mod test_extract_if { let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); for doomed in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i == doomed).for_each(drop); + map.extract_if(.., |i, _| *i == doomed).for_each(drop); assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2 - 1); map.check(); } @@ -1136,7 +1160,7 @@ mod test_extract_if { let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); for sacred in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i != sacred).for_each(drop); + map.extract_if(.., |i, _| *i != sacred).for_each(drop); assert!(map.keys().copied().eq(sacred..=sacred)); map.check(); } @@ -1146,7 +1170,7 @@ mod test_extract_if { fn height_2_removing_all() { let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.extract_if(|_, _| true).for_each(drop); + map.extract_if(.., |_, _| true).for_each(drop); assert!(map.is_empty()); map.check(); } @@ -1162,7 +1186,7 @@ mod test_extract_if { map.insert(b.spawn(Panic::InDrop), ()); map.insert(c.spawn(Panic::Never), ()); - catch_unwind(move || map.extract_if(|dummy, _| dummy.query(true)).for_each(drop)) + catch_unwind(move || map.extract_if(.., |dummy, _| dummy.query(true)).for_each(drop)) .unwrap_err(); assert_eq!(a.queried(), 1); @@ -1185,7 +1209,7 @@ mod test_extract_if { map.insert(c.spawn(Panic::InQuery), ()); catch_unwind(AssertUnwindSafe(|| { - map.extract_if(|dummy, _| dummy.query(true)).for_each(drop) + map.extract_if(.., |dummy, _| dummy.query(true)).for_each(drop) })) .unwrap_err(); @@ -1214,7 +1238,7 @@ mod test_extract_if { map.insert(c.spawn(Panic::InQuery), ()); { - let mut it = map.extract_if(|dummy, _| dummy.query(true)); + let mut it = map.extract_if(.., |dummy, _| dummy.query(true)); catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err(); // Iterator behavior after a panic is explicitly unspecified, // so this is just the current implementation: @@ -1658,7 +1682,7 @@ fn assert_sync() { } fn extract_if(v: &mut BTreeMap) -> impl Sync + '_ { - v.extract_if(|_, _| false) + v.extract_if(.., |_, _| false) } fn iter(v: &BTreeMap) -> impl Sync + '_ { @@ -1727,7 +1751,7 @@ fn assert_send() { } fn extract_if(v: &mut BTreeMap) -> impl Send + '_ { - v.extract_if(|_, _| false) + v.extract_if(.., |_, _| false) } fn iter(v: &BTreeMap) -> impl Send + '_ { diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 343934680b87a..780bd8b0dd143 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -1109,7 +1109,7 @@ impl BTreeSet { T: Ord, F: FnMut(&T) -> bool, { - self.extract_if(|v| !f(v)).for_each(drop); + self.extract_if(.., |v| !f(v)).for_each(drop); } /// Moves all elements from `other` into `self`, leaving `other` empty. @@ -1187,7 +1187,7 @@ impl BTreeSet { BTreeSet { map: self.map.split_off(value) } } - /// Creates an iterator that visits all elements in ascending order and + /// Creates an iterator that visits elements in the specified range in ascending order and /// uses a closure to determine if an element should be removed. /// /// If the closure returns `true`, the element is removed from the set and @@ -1208,18 +1208,25 @@ impl BTreeSet { /// use std::collections::BTreeSet; /// /// let mut set: BTreeSet = (0..8).collect(); - /// let evens: BTreeSet<_> = set.extract_if(|v| v % 2 == 0).collect(); + /// let evens: BTreeSet<_> = set.extract_if(.., |v| v % 2 == 0).collect(); /// let odds = set; /// assert_eq!(evens.into_iter().collect::>(), vec![0, 2, 4, 6]); /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 7]); + /// + /// let mut map: BTreeSet = (0..8).collect(); + /// let low: BTreeSet<_> = map.extract_if(0..4, |_v| true).collect(); + /// let high = map; + /// assert_eq!(low.into_iter().collect::>(), [0, 1, 2, 3]); + /// assert_eq!(high.into_iter().collect::>(), [4, 5, 6, 7]); /// ``` #[unstable(feature = "btree_extract_if", issue = "70530")] - pub fn extract_if<'a, F>(&'a mut self, pred: F) -> ExtractIf<'a, T, F, A> + pub fn extract_if<'a, F, R>(&'a mut self, range: R, pred: F) -> ExtractIf<'a, T, R, F, A> where T: Ord, + R: RangeBounds, F: 'a + FnMut(&T) -> bool, { - let (inner, alloc) = self.map.extract_if_inner(); + let (inner, alloc) = self.map.extract_if_inner(range); ExtractIf { pred, inner, alloc } } @@ -1554,17 +1561,18 @@ impl<'a, T, A: Allocator + Clone> IntoIterator for &'a BTreeSet { pub struct ExtractIf< 'a, T, + R, F, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, > { pred: F, - inner: super::map::ExtractIfInner<'a, T, SetValZST>, + inner: super::map::ExtractIfInner<'a, T, SetValZST, R>, /// The BTreeMap will outlive this IntoIter so we don't care about drop order for `alloc`. alloc: A, } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl fmt::Debug for ExtractIf<'_, T, F, A> +impl fmt::Debug for ExtractIf<'_, T, R, F, A> where T: fmt::Debug, A: Allocator + Clone, @@ -1577,8 +1585,10 @@ where } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl<'a, T, F, A: Allocator + Clone> Iterator for ExtractIf<'_, T, F, A> +impl<'a, T, R, F, A: Allocator + Clone> Iterator for ExtractIf<'_, T, R, F, A> where + T: PartialOrd, + R: RangeBounds, F: 'a + FnMut(&T) -> bool, { type Item = T; @@ -1595,7 +1605,13 @@ where } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl FusedIterator for ExtractIf<'_, T, F, A> where F: FnMut(&T) -> bool {} +impl FusedIterator for ExtractIf<'_, T, R, F, A> +where + T: PartialOrd, + R: RangeBounds, + F: FnMut(&T) -> bool, +{ +} #[stable(feature = "rust1", since = "1.0.0")] impl Extend for BTreeSet { diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index d538ef707eb93..85c9a98c461d9 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -368,8 +368,8 @@ fn test_extract_if() { let mut x = BTreeSet::from([1]); let mut y = BTreeSet::from([1]); - x.extract_if(|_| true).for_each(drop); - y.extract_if(|_| false).for_each(drop); + x.extract_if(.., |_| true).for_each(drop); + y.extract_if(.., |_| false).for_each(drop); assert_eq!(x.len(), 0); assert_eq!(y.len(), 1); } @@ -385,7 +385,7 @@ fn test_extract_if_drop_panic_leak() { set.insert(b.spawn(Panic::InDrop)); set.insert(c.spawn(Panic::Never)); - catch_unwind(move || set.extract_if(|dummy| dummy.query(true)).for_each(drop)).ok(); + catch_unwind(move || set.extract_if(.., |dummy| dummy.query(true)).for_each(drop)).ok(); assert_eq!(a.queried(), 1); assert_eq!(b.queried(), 1); @@ -406,7 +406,7 @@ fn test_extract_if_pred_panic_leak() { set.insert(b.spawn(Panic::InQuery)); set.insert(c.spawn(Panic::InQuery)); - catch_unwind(AssertUnwindSafe(|| set.extract_if(|dummy| dummy.query(true)).for_each(drop))) + catch_unwind(AssertUnwindSafe(|| set.extract_if(.., |dummy| dummy.query(true)).for_each(drop))) .ok(); assert_eq!(a.queried(), 1); @@ -605,7 +605,7 @@ fn assert_sync() { } fn extract_if(v: &mut BTreeSet) -> impl Sync + '_ { - v.extract_if(|_| false) + v.extract_if(.., |_| false) } fn difference(v: &BTreeSet) -> impl Sync + '_ { @@ -644,7 +644,7 @@ fn assert_send() { } fn extract_if(v: &mut BTreeSet) -> impl Send + '_ { - v.extract_if(|_| false) + v.extract_if(.., |_| false) } fn difference(v: &BTreeSet) -> impl Send + '_ { diff --git a/library/alloctests/benches/btree/map.rs b/library/alloctests/benches/btree/map.rs index 20f02dc3a968a..778065fd96575 100644 --- a/library/alloctests/benches/btree/map.rs +++ b/library/alloctests/benches/btree/map.rs @@ -386,7 +386,7 @@ pub fn clone_slim_100_and_clear(b: &mut Bencher) { #[bench] pub fn clone_slim_100_and_drain_all(b: &mut Bencher) { let src = slim_map(100); - b.iter(|| src.clone().extract_if(|_, _| true).count()) + b.iter(|| src.clone().extract_if(.., |_, _| true).count()) } #[bench] @@ -394,7 +394,7 @@ pub fn clone_slim_100_and_drain_half(b: &mut Bencher) { let src = slim_map(100); b.iter(|| { let mut map = src.clone(); - assert_eq!(map.extract_if(|i, _| i % 2 == 0).count(), 100 / 2); + assert_eq!(map.extract_if(.., |i, _| i % 2 == 0).count(), 100 / 2); assert_eq!(map.len(), 100 / 2); }) } @@ -457,7 +457,7 @@ pub fn clone_slim_10k_and_clear(b: &mut Bencher) { #[bench] pub fn clone_slim_10k_and_drain_all(b: &mut Bencher) { let src = slim_map(10_000); - b.iter(|| src.clone().extract_if(|_, _| true).count()) + b.iter(|| src.clone().extract_if(.., |_, _| true).count()) } #[bench] @@ -465,7 +465,7 @@ pub fn clone_slim_10k_and_drain_half(b: &mut Bencher) { let src = slim_map(10_000); b.iter(|| { let mut map = src.clone(); - assert_eq!(map.extract_if(|i, _| i % 2 == 0).count(), 10_000 / 2); + assert_eq!(map.extract_if(.., |i, _| i % 2 == 0).count(), 10_000 / 2); assert_eq!(map.len(), 10_000 / 2); }) } @@ -528,7 +528,7 @@ pub fn clone_fat_val_100_and_clear(b: &mut Bencher) { #[bench] pub fn clone_fat_val_100_and_drain_all(b: &mut Bencher) { let src = fat_val_map(100); - b.iter(|| src.clone().extract_if(|_, _| true).count()) + b.iter(|| src.clone().extract_if(.., |_, _| true).count()) } #[bench] @@ -536,7 +536,7 @@ pub fn clone_fat_val_100_and_drain_half(b: &mut Bencher) { let src = fat_val_map(100); b.iter(|| { let mut map = src.clone(); - assert_eq!(map.extract_if(|i, _| i % 2 == 0).count(), 100 / 2); + assert_eq!(map.extract_if(.., |i, _| i % 2 == 0).count(), 100 / 2); assert_eq!(map.len(), 100 / 2); }) } diff --git a/library/alloctests/benches/btree/set.rs b/library/alloctests/benches/btree/set.rs index 5aa395b4d52ac..027c86a89a51a 100644 --- a/library/alloctests/benches/btree/set.rs +++ b/library/alloctests/benches/btree/set.rs @@ -69,7 +69,7 @@ pub fn clone_100_and_clear(b: &mut Bencher) { #[bench] pub fn clone_100_and_drain_all(b: &mut Bencher) { let src = slim_set(100); - b.iter(|| src.clone().extract_if(|_| true).count()) + b.iter(|| src.clone().extract_if(.., |_| true).count()) } #[bench] @@ -77,7 +77,7 @@ pub fn clone_100_and_drain_half(b: &mut Bencher) { let src = slim_set(100); b.iter(|| { let mut set = src.clone(); - assert_eq!(set.extract_if(|i| i % 2 == 0).count(), 100 / 2); + assert_eq!(set.extract_if(.., |i| i % 2 == 0).count(), 100 / 2); assert_eq!(set.len(), 100 / 2); }) } @@ -140,7 +140,7 @@ pub fn clone_10k_and_clear(b: &mut Bencher) { #[bench] pub fn clone_10k_and_drain_all(b: &mut Bencher) { let src = slim_set(10_000); - b.iter(|| src.clone().extract_if(|_| true).count()) + b.iter(|| src.clone().extract_if(.., |_| true).count()) } #[bench] @@ -148,7 +148,7 @@ pub fn clone_10k_and_drain_half(b: &mut Bencher) { let src = slim_set(10_000); b.iter(|| { let mut set = src.clone(); - assert_eq!(set.extract_if(|i| i % 2 == 0).count(), 10_000 / 2); + assert_eq!(set.extract_if(.., |i| i % 2 == 0).count(), 10_000 / 2); assert_eq!(set.len(), 10_000 / 2); }) } diff --git a/library/alloctests/tests/autotraits.rs b/library/alloctests/tests/autotraits.rs index 6b82deeac8acb..ad0a103859696 100644 --- a/library/alloctests/tests/autotraits.rs +++ b/library/alloctests/tests/autotraits.rs @@ -1,3 +1,5 @@ +use std::ops::Range; + fn require_sync(_: T) {} fn require_send_sync(_: T) {} @@ -55,7 +57,13 @@ fn test_btree_map() { require_send_sync(async { let _v = None::< - alloc::collections::btree_map::ExtractIf<'_, &u32, &u32, fn(&&u32, &mut &u32) -> bool>, + alloc::collections::btree_map::ExtractIf< + '_, + &u32, + &u32, + Range, + fn(&&u32, &mut &u32) -> bool, + >, >; async {}.await; }); @@ -144,7 +152,9 @@ fn test_btree_set() { }); require_send_sync(async { - let _v = None:: bool>>; + let _v = None::< + alloc::collections::btree_set::ExtractIf<'_, &u32, Range, fn(&&u32) -> bool>, + >; async {}.await; }); diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index f320a194271eb..65560f63c1859 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -4,7 +4,7 @@ macro_rules! int_impl { ActualT = $ActualT:ident, UnsignedT = $UnsignedT:ty, - // There are all for use *only* in doc comments. + // These are all for use *only* in doc comments. // As such, they're all passed as literals -- passing them as a string // literal is fine if they need to be multiple code tokens. // In non-comments, use the associated constants rather than these. @@ -1018,6 +1018,110 @@ macro_rules! int_impl { if b { overflow_panic::div() } else { a } } + /// Checked integer division without remainder. Computes `self / rhs`, + /// returning `None` if `rhs == 0`, the division results in overflow, + /// or `self % rhs != 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(exact_div)] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).checked_exact_div(-1), Some(", stringify!($Max), "));")] + #[doc = concat!("assert_eq!((-5", stringify!($SelfT), ").checked_exact_div(2), None);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_exact_div(-1), None);")] + #[doc = concat!("assert_eq!((1", stringify!($SelfT), ").checked_exact_div(0), None);")] + /// ``` + #[unstable( + feature = "exact_div", + issue = "139911", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_exact_div(self, rhs: Self) -> Option { + if intrinsics::unlikely(rhs == 0 || ((self == Self::MIN) && (rhs == -1))) { + None + } else { + // SAFETY: division by zero and overflow are checked above + unsafe { + if intrinsics::unlikely(intrinsics::unchecked_rem(self, rhs) != 0) { + None + } else { + Some(intrinsics::exact_div(self, rhs)) + } + } + } + } + + /// Checked integer division without remainder. Computes `self / rhs`. + /// + /// # Panics + /// + /// This function will panic if `rhs == 0`, the division results in overflow, + /// or `self % rhs != 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(exact_div)] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).exact_div(-1), ", stringify!($Max), ");")] + /// ``` + /// + /// ```should_panic + /// #![feature(exact_div)] + #[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")] + /// ``` + /// ```should_panic + /// #![feature(exact_div)] + #[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.exact_div(-1);")] + /// ``` + #[unstable( + feature = "exact_div", + issue = "139911", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn exact_div(self, rhs: Self) -> Self { + match self.checked_exact_div(rhs) { + Some(x) => x, + None => panic!("Failed to divide without remainder"), + } + } + + /// Unchecked integer division without remainder. Computes `self / rhs`. + /// + /// # Safety + /// + /// This results in undefined behavior when `rhs == 0`, `self % rhs != 0`, or + #[doc = concat!("`self == ", stringify!($SelfT), "::MIN && rhs == -1`,")] + /// i.e. when [`checked_exact_div`](Self::checked_exact_div) would return `None`. + #[unstable( + feature = "exact_div", + issue = "139911", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const unsafe fn unchecked_exact_div(self, rhs: Self) -> Self { + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_exact_div cannot overflow, divide by zero, or leave a remainder"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => rhs > 0 && lhs % rhs == 0 && (lhs != <$SelfT>::MIN || rhs != -1), + ); + // SAFETY: Same precondition + unsafe { intrinsics::exact_div(self, rhs) } + } + /// Checked integer remainder. Computes `self % rhs`, returning `None` if /// `rhs == 0` or the division results in overflow. /// diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 10597854ff877..5f82e6af86b08 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1110,6 +1110,108 @@ macro_rules! uint_impl { self / rhs } + /// Checked integer division without remainder. Computes `self / rhs`. + /// + /// # Panics + /// + /// This function will panic if `rhs == 0` or `self % rhs != 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(exact_div)] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")] + /// ``` + /// + /// ```should_panic + /// #![feature(exact_div)] + #[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")] + /// ``` + #[unstable( + feature = "exact_div", + issue = "139911", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_exact_div(self, rhs: Self) -> Option { + if intrinsics::unlikely(rhs == 0) { + None + } else { + // SAFETY: division by zero is checked above + unsafe { + if intrinsics::unlikely(intrinsics::unchecked_rem(self, rhs) != 0) { + None + } else { + Some(intrinsics::exact_div(self, rhs)) + } + } + } + } + + /// Checked integer division without remainder. Computes `self / rhs`. + /// + /// # Panics + /// + /// This function will panic if `rhs == 0` or `self % rhs != 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(exact_div)] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")] + /// ``` + /// + /// ```should_panic + /// #![feature(exact_div)] + #[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")] + /// ``` + #[unstable( + feature = "exact_div", + issue = "139911", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn exact_div(self, rhs: Self) -> Self { + match self.checked_exact_div(rhs) { + Some(x) => x, + None => panic!("Failed to divide without remainder"), + } + } + + /// Unchecked integer division without remainder. Computes `self / rhs`. + /// + /// # Safety + /// + /// This results in undefined behavior when `rhs == 0` or `self % rhs != 0`, + /// i.e. when [`checked_exact_div`](Self::checked_exact_div) would return `None`. + #[unstable( + feature = "exact_div", + issue = "139911", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const unsafe fn unchecked_exact_div(self, rhs: Self) -> Self { + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_exact_div divide by zero or leave a remainder"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => rhs > 0 && lhs % rhs == 0, + ); + // SAFETY: Same precondition + unsafe { intrinsics::exact_div(self, rhs) } + } + /// Checked integer remainder. Computes `self % rhs`, returning `None` /// if `rhs == 0`. /// diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 31267bfb1f9c1..f94737138dcaa 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -397,35 +397,7 @@ impl *const T { if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit) }) } } - /// Adds a signed offset to a pointer. - /// - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// If any of the following conditions are violated, the result is Undefined Behavior: - /// - /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without - /// "wrapping around"), must fit in an `isize`. - /// - /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some - /// [allocated object], and the entire memory range between `self` and the result must be in - /// bounds of that allocated object. In particular, this range must not "wrap around" the edge - /// of the address space. Note that "range" here refers to a half-open range as usual in Rust, - /// i.e., `self..result` for non-negative offsets and `result..self` for negative offsets. - /// - /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset - /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. - /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always - /// safe. - /// - /// Consider using [`wrapping_offset`] instead if these constraints are - /// difficult to satisfy. The only advantage of this method is that it - /// enables more aggressive compiler optimizations. - /// - /// [`wrapping_offset`]: #method.wrapping_offset - /// [allocated object]: crate::ptr#allocated-object + #[doc = include_str!("./docs/offset.md")] /// /// # Examples /// @@ -905,38 +877,7 @@ impl *const T { } } - /// Adds an unsigned offset to a pointer. - /// - /// This can only move the pointer forward (or not move it). If you need to move forward or - /// backward depending on the value, then you might want [`offset`](#method.offset) instead - /// which takes a signed offset. - /// - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// If any of the following conditions are violated, the result is Undefined Behavior: - /// - /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without - /// "wrapping around"), must fit in an `isize`. - /// - /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some - /// [allocated object], and the entire memory range between `self` and the result must be in - /// bounds of that allocated object. In particular, this range must not "wrap around" the edge - /// of the address space. - /// - /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset - /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. - /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always - /// safe. - /// - /// Consider using [`wrapping_add`] instead if these constraints are - /// difficult to satisfy. The only advantage of this method is that it - /// enables more aggressive compiler optimizations. - /// - /// [`wrapping_add`]: #method.wrapping_add - /// [allocated object]: crate::ptr#allocated-object + #[doc = include_str!("./docs/add.md")] /// /// # Examples /// diff --git a/library/core/src/ptr/docs/add.md b/library/core/src/ptr/docs/add.md new file mode 100644 index 0000000000000..555dc11c1bb40 --- /dev/null +++ b/library/core/src/ptr/docs/add.md @@ -0,0 +1,32 @@ +Adds an unsigned offset to a pointer. + +This can only move the pointer forward (or not move it). If you need to move forward or +backward depending on the value, then you might want [`offset`](#method.offset) instead +which takes a signed offset. + +`count` is in units of T; e.g., a `count` of 3 represents a pointer +offset of `3 * size_of::()` bytes. + +# Safety + +If any of the following conditions are violated, the result is Undefined Behavior: + +* The offset in bytes, `count * size_of::()`, computed on mathematical integers (without +"wrapping around"), must fit in an `isize`. + +* If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some +[allocated object], and the entire memory range between `self` and the result must be in +bounds of that allocated object. In particular, this range must not "wrap around" the edge +of the address space. + +Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset +stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. +This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always +safe. + +Consider using [`wrapping_add`] instead if these constraints are +difficult to satisfy. The only advantage of this method is that it +enables more aggressive compiler optimizations. + +[`wrapping_add`]: #method.wrapping_add +[allocated object]: crate::ptr#allocated-object diff --git a/library/core/src/ptr/docs/offset.md b/library/core/src/ptr/docs/offset.md new file mode 100644 index 0000000000000..6e431e054b052 --- /dev/null +++ b/library/core/src/ptr/docs/offset.md @@ -0,0 +1,29 @@ +Adds a signed offset to a pointer. + +`count` is in units of T; e.g., a `count` of 3 represents a pointer +offset of `3 * size_of::()` bytes. + +# Safety + +If any of the following conditions are violated, the result is Undefined Behavior: + +* The offset in bytes, `count * size_of::()`, computed on mathematical integers (without +"wrapping around"), must fit in an `isize`. + +* If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some +[allocated object], and the entire memory range between `self` and the result must be in +bounds of that allocated object. In particular, this range must not "wrap around" the edge +of the address space. Note that "range" here refers to a half-open range as usual in Rust, +i.e., `self..result` for non-negative offsets and `result..self` for negative offsets. + +Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset +stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. +This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always +safe. + +Consider using [`wrapping_offset`] instead if these constraints are +difficult to satisfy. The only advantage of this method is that it +enables more aggressive compiler optimizations. + +[`wrapping_offset`]: #method.wrapping_offset +[allocated object]: crate::ptr#allocated-object diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index b1b3379d741d7..040d91e912429 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -394,34 +394,7 @@ impl *mut T { if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit) }) } } - /// Adds a signed offset to a pointer. - /// - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// If any of the following conditions are violated, the result is Undefined Behavior: - /// - /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without - /// "wrapping around"), must fit in an `isize`. - /// - /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some - /// [allocated object], and the entire memory range between `self` and the result must be in - /// bounds of that allocated object. In particular, this range must not "wrap around" the edge - /// of the address space. - /// - /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset - /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. - /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always - /// safe. - /// - /// Consider using [`wrapping_offset`] instead if these constraints are - /// difficult to satisfy. The only advantage of this method is that it - /// enables more aggressive compiler optimizations. - /// - /// [`wrapping_offset`]: #method.wrapping_offset - /// [allocated object]: crate::ptr#allocated-object + #[doc = include_str!("./docs/offset.md")] /// /// # Examples /// @@ -996,44 +969,13 @@ impl *mut T { unsafe { (self as *const T).byte_offset_from_unsigned(origin) } } - /// Adds an unsigned offset to a pointer. - /// - /// This can only move the pointer forward (or not move it). If you need to move forward or - /// backward depending on the value, then you might want [`offset`](#method.offset) instead - /// which takes a signed offset. - /// - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// If any of the following conditions are violated, the result is Undefined Behavior: - /// - /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without - /// "wrapping around"), must fit in an `isize`. - /// - /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some - /// [allocated object], and the entire memory range between `self` and the result must be in - /// bounds of that allocated object. In particular, this range must not "wrap around" the edge - /// of the address space. - /// - /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset - /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. - /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always - /// safe. - /// - /// Consider using [`wrapping_add`] instead if these constraints are - /// difficult to satisfy. The only advantage of this method is that it - /// enables more aggressive compiler optimizations. - /// - /// [`wrapping_add`]: #method.wrapping_add - /// [allocated object]: crate::ptr#allocated-object + #[doc = include_str!("./docs/add.md")] /// /// # Examples /// /// ``` - /// let s: &str = "123"; - /// let ptr: *const u8 = s.as_ptr(); + /// let mut s: String = "123".to_string(); + /// let ptr: *mut u8 = s.as_mut_ptr(); /// /// unsafe { /// assert_eq!('2', *ptr.add(1) as char); diff --git a/rust-bors.toml b/rust-bors.toml index f27eb2393675b..fbfaa980f0567 100644 --- a/rust-bors.toml +++ b/rust-bors.toml @@ -1 +1,2 @@ -timeout = 14400 +# 6 hours timeout for CI builds +timeout = 21600 diff --git a/src/tools/miri/tests/pass/btreemap.rs b/src/tools/miri/tests/pass/btreemap.rs index 1213f81a6f128..1d65e69bf726f 100644 --- a/src/tools/miri/tests/pass/btreemap.rs +++ b/src/tools/miri/tests/pass/btreemap.rs @@ -50,7 +50,7 @@ pub fn main() { test_all_refs(&mut 13, b.values_mut()); // Test forgetting the extractor. - let mut d = b.extract_if(|_, i| *i < 30); + let mut d = b.extract_if(.., |_, i| *i < 30); d.next().unwrap(); mem::forget(d); } diff --git a/tests/mir-opt/matches_reduce_branches.match_non_int_failed.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_non_int_failed.MatchBranchSimplification.diff new file mode 100644 index 0000000000000..81e900a34c087 --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_non_int_failed.MatchBranchSimplification.diff @@ -0,0 +1,29 @@ +- // MIR for `match_non_int_failed` before MatchBranchSimplification ++ // MIR for `match_non_int_failed` after MatchBranchSimplification + + fn match_non_int_failed(_1: char) -> u8 { + let mut _0: u8; + + bb0: { + switchInt(copy _1) -> [97: bb1, 98: bb2, otherwise: bb3]; + } + + bb1: { + _0 = const 97_u8; + goto -> bb4; + } + + bb2: { + _0 = const 98_u8; + goto -> bb4; + } + + bb3: { + unreachable; + } + + bb4: { + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.rs b/tests/mir-opt/matches_reduce_branches.rs index 00131b0116db0..89ef3bfb30857 100644 --- a/tests/mir-opt/matches_reduce_branches.rs +++ b/tests/mir-opt/matches_reduce_branches.rs @@ -627,6 +627,37 @@ fn match_i128_u128(i: EnumAi128) -> u128 { } } +// EMIT_MIR matches_reduce_branches.match_non_int_failed.MatchBranchSimplification.diff +#[custom_mir(dialect = "runtime")] +fn match_non_int_failed(i: char) -> u8 { + // CHECK-LABEL: fn match_non_int_failed( + // CHECK: switchInt + // CHECK: return + mir! { + { + match i { + 'a' => bb1, + 'b' => bb2, + _ => unreachable_bb, + } + } + bb1 = { + RET = 97; + Goto(ret) + } + bb2 = { + RET = 98; + Goto(ret) + } + unreachable_bb = { + Unreachable() + } + ret = { + Return() + } + } +} + fn main() { let _ = foo(None); let _ = foo(Some(())); @@ -664,4 +695,5 @@ fn main() { let _ = match_i128_u128(EnumAi128::A); let _ = my_is_some(None); + let _ = match_non_int_failed('a'); } diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs b/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs index 7a4d7d9a81ec4..afb16cf58e871 100644 --- a/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs +++ b/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs @@ -14,14 +14,14 @@ fn main() { map.insert("c", ()); { - let mut it = map.extract_if(|_, _| true); + let mut it = map.extract_if(.., |_, _| true); catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err(); let result = catch_unwind(AssertUnwindSafe(|| it.next())); assert!(matches!(result, Ok(None))); } { - let mut it = map.extract_if(|_, _| true); + let mut it = map.extract_if(.., |_, _| true); catch_unwind(AssertUnwindSafe(|| while let Some(_) = it.next() {})).unwrap_err(); let result = catch_unwind(AssertUnwindSafe(|| it.next())); assert!(matches!(result, Ok(None))); diff --git a/tests/ui/parser/match-arm-comma-typo-issue-140991.fixed b/tests/ui/parser/match-arm-comma-typo-issue-140991.fixed new file mode 100644 index 0000000000000..4d99e4bdc1c3f --- /dev/null +++ b/tests/ui/parser/match-arm-comma-typo-issue-140991.fixed @@ -0,0 +1,24 @@ +//@ run-rustfix + +pub enum Foo { + X, Y +} + +pub fn typo1(foo: Foo) -> Foo { + use Foo::*; + match foo { + X => Y, + Y => X, //~ ERROR expected one of + } +} + +pub fn typo2(foo: Foo) -> Foo { + use Foo::*; + match foo { + X => Y, + Y => X, //~ ERROR expected one of + } +} + + +fn main() { } diff --git a/tests/ui/parser/match-arm-comma-typo-issue-140991.rs b/tests/ui/parser/match-arm-comma-typo-issue-140991.rs new file mode 100644 index 0000000000000..3baf1ff3fa1df --- /dev/null +++ b/tests/ui/parser/match-arm-comma-typo-issue-140991.rs @@ -0,0 +1,24 @@ +//@ run-rustfix + +pub enum Foo { + X, Y +} + +pub fn typo1(foo: Foo) -> Foo { + use Foo::*; + match foo { + X => Y. + Y => X, //~ ERROR expected one of + } +} + +pub fn typo2(foo: Foo) -> Foo { + use Foo::*; + match foo { + X => Y/ + Y => X, //~ ERROR expected one of + } +} + + +fn main() { } diff --git a/tests/ui/parser/match-arm-comma-typo-issue-140991.stderr b/tests/ui/parser/match-arm-comma-typo-issue-140991.stderr new file mode 100644 index 0000000000000..19532d14245d1 --- /dev/null +++ b/tests/ui/parser/match-arm-comma-typo-issue-140991.stderr @@ -0,0 +1,26 @@ +error: expected one of `(`, `,`, `.`, `::`, `?`, `}`, or an operator, found `=>` + --> $DIR/match-arm-comma-typo-issue-140991.rs:11:11 + | +LL | Y => X, + | ^^ expected one of 7 possible tokens + | +help: you might have meant to write a `,` to end this `match` arm + | +LL - X => Y. +LL + X => Y, + | + +error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, `}`, or an operator, found `=>` + --> $DIR/match-arm-comma-typo-issue-140991.rs:19:11 + | +LL | Y => X, + | ^^ expected one of 8 possible tokens + | +help: you might have meant to write a `,` to end this `match` arm + | +LL - X => Y/ +LL + X => Y, + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/unsafe-binders/unused-lifetimes-2.fixed b/tests/ui/unsafe-binders/unused-lifetimes-2.fixed new file mode 100644 index 0000000000000..714a5fdaf0311 --- /dev/null +++ b/tests/ui/unsafe-binders/unused-lifetimes-2.fixed @@ -0,0 +1,20 @@ +// regression test for #141758 +//@ run-rustfix +//@ check-pass + +#![warn(unused_lifetimes)] +#![allow(incomplete_features, unused_imports, dead_code)] +#![feature(unsafe_binders)] + +use std::unsafe_binder::unwrap_binder; + +#[derive(Copy, Clone)] +pub struct S([usize; 8]); + +// Regression test for . +pub fn by_value(_x: unsafe<'a> &'a S) -> usize { + //~^ WARN lifetime parameter `'b` never used + 0 +} + +fn main() {} diff --git a/tests/ui/unsafe-binders/unused-lifetimes-2.rs b/tests/ui/unsafe-binders/unused-lifetimes-2.rs new file mode 100644 index 0000000000000..5b34cf911637a --- /dev/null +++ b/tests/ui/unsafe-binders/unused-lifetimes-2.rs @@ -0,0 +1,20 @@ +// regression test for #141758 +//@ run-rustfix +//@ check-pass + +#![warn(unused_lifetimes)] +#![allow(incomplete_features, unused_imports, dead_code)] +#![feature(unsafe_binders)] + +use std::unsafe_binder::unwrap_binder; + +#[derive(Copy, Clone)] +pub struct S([usize; 8]); + +// Regression test for . +pub fn by_value(_x: unsafe<'a, 'b> &'a S) -> usize { + //~^ WARN lifetime parameter `'b` never used + 0 +} + +fn main() {} diff --git a/tests/ui/unsafe-binders/unused-lifetimes-2.stderr b/tests/ui/unsafe-binders/unused-lifetimes-2.stderr new file mode 100644 index 0000000000000..bca8a15d56bd6 --- /dev/null +++ b/tests/ui/unsafe-binders/unused-lifetimes-2.stderr @@ -0,0 +1,16 @@ +warning: lifetime parameter `'b` never used + --> $DIR/unused-lifetimes-2.rs:15:32 + | +LL | pub fn by_value(_x: unsafe<'a, 'b> &'a S) -> usize { + | --^^ + | | + | help: elide the unused lifetime + | +note: the lint level is defined here + --> $DIR/unused-lifetimes-2.rs:5:9 + | +LL | #![warn(unused_lifetimes)] + | ^^^^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/tests/ui/unsafe-binders/unused-lifetimes.fixed b/tests/ui/unsafe-binders/unused-lifetimes.fixed new file mode 100644 index 0000000000000..4295b6a848c37 --- /dev/null +++ b/tests/ui/unsafe-binders/unused-lifetimes.fixed @@ -0,0 +1,20 @@ +// regression test for #141758 +//@ run-rustfix +//@ check-pass + +#![warn(unused_lifetimes)] +#![allow(incomplete_features, unused_imports, dead_code)] +#![feature(unsafe_binders)] + +use std::unsafe_binder::unwrap_binder; + +#[derive(Copy, Clone)] +pub struct S([usize; 8]); + +// Regression test for . +pub fn by_value(_x: S) -> usize { + //~^ WARN lifetime parameter `'a` never used + 0 +} + +fn main() {} diff --git a/tests/ui/unsafe-binders/unused-lifetimes.rs b/tests/ui/unsafe-binders/unused-lifetimes.rs new file mode 100644 index 0000000000000..b13823283183d --- /dev/null +++ b/tests/ui/unsafe-binders/unused-lifetimes.rs @@ -0,0 +1,20 @@ +// regression test for #141758 +//@ run-rustfix +//@ check-pass + +#![warn(unused_lifetimes)] +#![allow(incomplete_features, unused_imports, dead_code)] +#![feature(unsafe_binders)] + +use std::unsafe_binder::unwrap_binder; + +#[derive(Copy, Clone)] +pub struct S([usize; 8]); + +// Regression test for . +pub fn by_value(_x: unsafe<'a> S) -> usize { + //~^ WARN lifetime parameter `'a` never used + 0 +} + +fn main() {} diff --git a/tests/ui/unsafe-binders/unused-lifetimes.stderr b/tests/ui/unsafe-binders/unused-lifetimes.stderr new file mode 100644 index 0000000000000..d9a5216301f50 --- /dev/null +++ b/tests/ui/unsafe-binders/unused-lifetimes.stderr @@ -0,0 +1,14 @@ +warning: lifetime parameter `'a` never used + --> $DIR/unused-lifetimes.rs:15:28 + | +LL | pub fn by_value(_x: unsafe<'a> S) -> usize { + | -------^^-- help: elide the unused lifetime + | +note: the lint level is defined here + --> $DIR/unused-lifetimes.rs:5:9 + | +LL | #![warn(unused_lifetimes)] + | ^^^^^^^^^^^^^^^^ + +warning: 1 warning emitted +