Skip to content

Commit 21cf7fb

Browse files
committed
Auto merge of #150463 - JonathanBrouwer:rollup-ietend4, r=JonathanBrouwer
Rollup of 5 pull requests Successful merges: - #147499 (Implement round-ties-to-even for Duration Debug for consistency with f64) - #149447 (Rewrite `String::replace_range`) - #149469 (Leverage &mut in OnceLock when possible) - #149921 (Add new source component that includes GPL-licensed source) - #150460 (fix ManuallyDrop::into_inner aliasing (Miri) issues) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 9f54abe + 2ecaa1d commit 21cf7fb

File tree

10 files changed

+250
-179
lines changed

10 files changed

+250
-179
lines changed

library/alloc/src/string.rs

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ use core::iter::from_fn;
5050
use core::ops::Add;
5151
#[cfg(not(no_global_oom_handling))]
5252
use core::ops::AddAssign;
53-
#[cfg(not(no_global_oom_handling))]
54-
use core::ops::Bound::{Excluded, Included, Unbounded};
5553
use core::ops::{self, Range, RangeBounds};
5654
use core::str::pattern::{Pattern, Utf8Pattern};
5755
use core::{fmt, hash, ptr, slice};
@@ -2059,30 +2057,19 @@ impl String {
20592057
where
20602058
R: RangeBounds<usize>,
20612059
{
2062-
// Memory safety
2063-
//
2064-
// Replace_range does not have the memory safety issues of a vector Splice.
2065-
// of the vector version. The data is just plain bytes.
2066-
2067-
// WARNING: Inlining this variable would be unsound (#81138)
2068-
let start = range.start_bound();
2069-
match start {
2070-
Included(&n) => assert!(self.is_char_boundary(n)),
2071-
Excluded(&n) => assert!(self.is_char_boundary(n + 1)),
2072-
Unbounded => {}
2073-
};
2074-
// WARNING: Inlining this variable would be unsound (#81138)
2075-
let end = range.end_bound();
2076-
match end {
2077-
Included(&n) => assert!(self.is_char_boundary(n + 1)),
2078-
Excluded(&n) => assert!(self.is_char_boundary(n)),
2079-
Unbounded => {}
2080-
};
2081-
2082-
// Using `range` again would be unsound (#81138)
2083-
// We assume the bounds reported by `range` remain the same, but
2084-
// an adversarial implementation could change between calls
2085-
unsafe { self.as_mut_vec() }.splice((start, end), replace_with.bytes());
2060+
// We avoid #81138 (nondeterministic RangeBounds impls) because we only use `range` once, here.
2061+
let checked_range = slice::range(range, ..self.len());
2062+
2063+
assert!(
2064+
self.is_char_boundary(checked_range.start),
2065+
"start of range should be a character boundary"
2066+
);
2067+
assert!(
2068+
self.is_char_boundary(checked_range.end),
2069+
"end of range should be a character boundary"
2070+
);
2071+
2072+
unsafe { self.as_mut_vec() }.splice(checked_range, replace_with.bytes());
20862073
}
20872074

20882075
/// Replaces the leftmost occurrence of a pattern with another string, in-place.

library/alloctests/tests/string.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -616,8 +616,15 @@ fn test_replace_range() {
616616
}
617617

618618
#[test]
619-
#[should_panic]
620-
fn test_replace_range_char_boundary() {
619+
#[should_panic = "start of range should be a character boundary"]
620+
fn test_replace_range_start_char_boundary() {
621+
let mut s = "Hello, 世界!".to_owned();
622+
s.replace_range(8.., "");
623+
}
624+
625+
#[test]
626+
#[should_panic = "end of range should be a character boundary"]
627+
fn test_replace_range_end_char_boundary() {
621628
let mut s = "Hello, 世界!".to_owned();
622629
s.replace_range(..8, "");
623630
}
@@ -632,28 +639,32 @@ fn test_replace_range_inclusive_range() {
632639
}
633640

634641
#[test]
635-
#[should_panic]
642+
#[should_panic = "range end index 6 out of range for slice of length 5"]
636643
fn test_replace_range_out_of_bounds() {
637644
let mut s = String::from("12345");
638645
s.replace_range(5..6, "789");
639646
}
640647

641648
#[test]
642-
#[should_panic]
649+
#[should_panic = "range end index 5 out of range for slice of length 5"]
643650
fn test_replace_range_inclusive_out_of_bounds() {
644651
let mut s = String::from("12345");
645652
s.replace_range(5..=5, "789");
646653
}
647654

655+
// The overflowed index value is target-dependent,
656+
// so we don't check for its exact value in the panic message
648657
#[test]
649-
#[should_panic]
658+
#[should_panic = "out of range for slice of length 3"]
650659
fn test_replace_range_start_overflow() {
651660
let mut s = String::from("123");
652661
s.replace_range((Excluded(usize::MAX), Included(0)), "");
653662
}
654663

664+
// The overflowed index value is target-dependent,
665+
// so we don't check for its exact value in the panic message
655666
#[test]
656-
#[should_panic]
667+
#[should_panic = "out of range for slice of length 3"]
657668
fn test_replace_range_end_overflow() {
658669
let mut s = String::from("456");
659670
s.replace_range((Included(0), Included(usize::MAX)), "");

library/core/src/mem/manually_drop.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,9 @@ impl<T> ManuallyDrop<T> {
200200
#[rustc_const_stable(feature = "const_manually_drop", since = "1.32.0")]
201201
#[inline(always)]
202202
pub const fn into_inner(slot: ManuallyDrop<T>) -> T {
203-
slot.value.into_inner()
203+
// Cannot use `MaybeDangling::into_inner` as that does not yet have the desired semantics.
204+
// SAFETY: We know this is a valid `T`. `slot` will not be dropped.
205+
unsafe { (&raw const slot).cast::<T>().read() }
204206
}
205207

206208
/// Takes the value from the `ManuallyDrop<T>` container out.

library/core/src/time.rs

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,39 +1315,54 @@ impl fmt::Debug for Duration {
13151315
// need to perform rounding to match the semantics of printing
13161316
// normal floating point numbers. However, we only need to do work
13171317
// when rounding up. This happens if the first digit of the
1318-
// remaining ones is >= 5.
1318+
// remaining ones is >= 5. When the first digit is exactly 5, rounding
1319+
// follows IEEE-754 round-ties-to-even semantics: we only round up
1320+
// if the last written digit is odd.
13191321
let integer_part = if fractional_part > 0 && fractional_part >= divisor * 5 {
1320-
// Round up the number contained in the buffer. We go through
1321-
// the buffer backwards and keep track of the carry.
1322-
let mut rev_pos = pos;
1323-
let mut carry = true;
1324-
while carry && rev_pos > 0 {
1325-
rev_pos -= 1;
1326-
1327-
// If the digit in the buffer is not '9', we just need to
1328-
// increment it and can stop then (since we don't have a
1329-
// carry anymore). Otherwise, we set it to '0' (overflow)
1330-
// and continue.
1331-
if buf[rev_pos] < b'9' {
1332-
buf[rev_pos] += 1;
1333-
carry = false;
1334-
} else {
1335-
buf[rev_pos] = b'0';
1336-
}
1337-
}
1338-
1339-
// If we still have the carry bit set, that means that we set
1340-
// the whole buffer to '0's and need to increment the integer
1341-
// part.
1342-
if carry {
1343-
// If `integer_part == u64::MAX` and precision < 9, any
1344-
// carry of the overflow during rounding of the
1345-
// `fractional_part` into the `integer_part` will cause the
1346-
// `integer_part` itself to overflow. Avoid this by using an
1347-
// `Option<u64>`, with `None` representing `u64::MAX + 1`.
1348-
integer_part.checked_add(1)
1322+
// For ties (fractional_part == divisor * 5), only round up if last digit is odd
1323+
let is_tie = fractional_part == divisor * 5;
1324+
let last_digit_is_odd = if pos > 0 {
1325+
(buf[pos - 1] - b'0') % 2 == 1
13491326
} else {
1327+
// No fractional digits - check the integer part
1328+
(integer_part % 2) == 1
1329+
};
1330+
1331+
if is_tie && !last_digit_is_odd {
13501332
Some(integer_part)
1333+
} else {
1334+
// Round up the number contained in the buffer. We go through
1335+
// the buffer backwards and keep track of the carry.
1336+
let mut rev_pos = pos;
1337+
let mut carry = true;
1338+
while carry && rev_pos > 0 {
1339+
rev_pos -= 1;
1340+
1341+
// If the digit in the buffer is not '9', we just need to
1342+
// increment it and can stop then (since we don't have a
1343+
// carry anymore). Otherwise, we set it to '0' (overflow)
1344+
// and continue.
1345+
if buf[rev_pos] < b'9' {
1346+
buf[rev_pos] += 1;
1347+
carry = false;
1348+
} else {
1349+
buf[rev_pos] = b'0';
1350+
}
1351+
}
1352+
1353+
// If we still have the carry bit set, that means that we set
1354+
// the whole buffer to '0's and need to increment the integer
1355+
// part.
1356+
if carry {
1357+
// If `integer_part == u64::MAX` and precision < 9, any
1358+
// carry of the overflow during rounding of the
1359+
// `fractional_part` into the `integer_part` will cause the
1360+
// `integer_part` itself to overflow. Avoid this by using an
1361+
// `Option<u64>`, with `None` representing `u64::MAX + 1`.
1362+
integer_part.checked_add(1)
1363+
} else {
1364+
Some(integer_part)
1365+
}
13511366
}
13521367
} else {
13531368
Some(integer_part)

library/coretests/tests/time.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,6 @@ fn debug_formatting_precision_two() {
439439
assert_eq!(format!("{:.2?}", Duration::new(4, 001_000_000)), "4.00s");
440440
assert_eq!(format!("{:.2?}", Duration::new(2, 100_000_000)), "2.10s");
441441
assert_eq!(format!("{:.2?}", Duration::new(2, 104_990_000)), "2.10s");
442-
assert_eq!(format!("{:.2?}", Duration::new(2, 105_000_000)), "2.11s");
443442
assert_eq!(format!("{:.2?}", Duration::new(8, 999_999_999)), "9.00s");
444443
}
445444

@@ -480,6 +479,15 @@ fn debug_formatting_precision_high() {
480479
assert_eq!(format!("{:.20?}", Duration::new(4, 001_000_000)), "4.00100000000000000000s");
481480
}
482481

482+
#[test]
483+
fn debug_formatting_round_to_even() {
484+
assert_eq!(format!("{:.0?}", Duration::new(1, 500_000_000)), "2s");
485+
assert_eq!(format!("{:.0?}", Duration::new(2, 500_000_000)), "2s");
486+
assert_eq!(format!("{:.0?}", Duration::new(0, 1_500_000)), "2ms");
487+
assert_eq!(format!("{:.0?}", Duration::new(0, 2_500_000)), "2ms");
488+
assert_eq!(format!("{:.2?}", Duration::new(2, 105_000_000)), "2.10s");
489+
}
490+
483491
#[test]
484492
fn duration_const() {
485493
// test that the methods of `Duration` are usable in a const context

library/std/src/sync/once_lock.rs

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::once::OnceExclusiveState;
12
use crate::cell::UnsafeCell;
23
use crate::fmt;
34
use crate::marker::PhantomData;
@@ -152,8 +153,8 @@ impl<T> OnceLock<T> {
152153
#[stable(feature = "once_cell", since = "1.70.0")]
153154
#[rustc_should_not_be_called_on_const_items]
154155
pub fn get(&self) -> Option<&T> {
155-
if self.is_initialized() {
156-
// Safe b/c checked is_initialized
156+
if self.initialized() {
157+
// Safe b/c checked initialized
157158
Some(unsafe { self.get_unchecked() })
158159
} else {
159160
None
@@ -170,8 +171,8 @@ impl<T> OnceLock<T> {
170171
#[inline]
171172
#[stable(feature = "once_cell", since = "1.70.0")]
172173
pub fn get_mut(&mut self) -> Option<&mut T> {
173-
if self.is_initialized() {
174-
// Safe b/c checked is_initialized and we have a unique access
174+
if self.initialized_mut() {
175+
// Safe b/c checked initialized and we have a unique access
175176
Some(unsafe { self.get_unchecked_mut() })
176177
} else {
177178
None
@@ -402,14 +403,12 @@ impl<T> OnceLock<T> {
402403
// NOTE: We need to perform an acquire on the state in this method
403404
// in order to correctly synchronize `LazyLock::force`. This is
404405
// currently done by calling `self.get()`, which in turn calls
405-
// `self.is_initialized()`, which in turn performs the acquire.
406+
// `self.initialized()`, which in turn performs the acquire.
406407
if let Some(value) = self.get() {
407408
return Ok(value);
408409
}
409410
self.initialize(f)?;
410411

411-
debug_assert!(self.is_initialized());
412-
413412
// SAFETY: The inner value has been initialized
414413
Ok(unsafe { self.get_unchecked() })
415414
}
@@ -451,10 +450,10 @@ impl<T> OnceLock<T> {
451450
where
452451
F: FnOnce() -> Result<T, E>,
453452
{
454-
if self.get().is_none() {
453+
if self.get_mut().is_none() {
455454
self.initialize(f)?;
456455
}
457-
debug_assert!(self.is_initialized());
456+
458457
// SAFETY: The inner value has been initialized
459458
Ok(unsafe { self.get_unchecked_mut() })
460459
}
@@ -503,22 +502,32 @@ impl<T> OnceLock<T> {
503502
#[inline]
504503
#[stable(feature = "once_cell", since = "1.70.0")]
505504
pub fn take(&mut self) -> Option<T> {
506-
if self.is_initialized() {
505+
if self.initialized_mut() {
507506
self.once = Once::new();
508507
// SAFETY: `self.value` is initialized and contains a valid `T`.
509-
// `self.once` is reset, so `is_initialized()` will be false again
508+
// `self.once` is reset, so `initialized()` will be false again
510509
// which prevents the value from being read twice.
511-
unsafe { Some((&mut *self.value.get()).assume_init_read()) }
510+
unsafe { Some(self.value.get_mut().assume_init_read()) }
512511
} else {
513512
None
514513
}
515514
}
516515

517516
#[inline]
518-
fn is_initialized(&self) -> bool {
517+
fn initialized(&self) -> bool {
519518
self.once.is_completed()
520519
}
521520

521+
#[inline]
522+
fn initialized_mut(&mut self) -> bool {
523+
// `state()` does not perform an atomic load, so prefer it over `is_complete()`.
524+
let state = self.once.state();
525+
match state {
526+
OnceExclusiveState::Complete => true,
527+
_ => false,
528+
}
529+
}
530+
522531
#[cold]
523532
#[optimize(size)]
524533
fn initialize<F, E>(&self, f: F) -> Result<(), E>
@@ -552,7 +561,7 @@ impl<T> OnceLock<T> {
552561
/// The cell must be initialized
553562
#[inline]
554563
unsafe fn get_unchecked(&self) -> &T {
555-
debug_assert!(self.is_initialized());
564+
debug_assert!(self.initialized());
556565
unsafe { (&*self.value.get()).assume_init_ref() }
557566
}
558567

@@ -561,8 +570,8 @@ impl<T> OnceLock<T> {
561570
/// The cell must be initialized
562571
#[inline]
563572
unsafe fn get_unchecked_mut(&mut self) -> &mut T {
564-
debug_assert!(self.is_initialized());
565-
unsafe { (&mut *self.value.get()).assume_init_mut() }
573+
debug_assert!(self.initialized_mut());
574+
unsafe { self.value.get_mut().assume_init_mut() }
566575
}
567576
}
568577

@@ -690,11 +699,11 @@ impl<T: Eq> Eq for OnceLock<T> {}
690699
unsafe impl<#[may_dangle] T> Drop for OnceLock<T> {
691700
#[inline]
692701
fn drop(&mut self) {
693-
if self.is_initialized() {
702+
if self.initialized_mut() {
694703
// SAFETY: The cell is initialized and being dropped, so it can't
695704
// be accessed again. We also don't touch the `T` other than
696705
// dropping it, which validates our usage of #[may_dangle].
697-
unsafe { (&mut *self.value.get()).assume_init_drop() };
706+
unsafe { self.value.get_mut().assume_init_drop() };
698707
}
699708
}
700709
}

0 commit comments

Comments
 (0)