Skip to content

Commit baef20b

Browse files
authored
Rollup merge of rust-lang#69011 - foeb:document-unsafe-core-fmt, r=Mark-Simulacrum
Document unsafe blocks in core::fmt r? @RalfJung CC: @rust-lang/wg-unsafe-code-guidelines rust-lang#66219 Sorry for the hiatus, but here's a few more files with the unsafe blocks documented! I think working on it smaller chunks like this will be easier for everyone.
2 parents 8296e2c + 3d146a3 commit baef20b

File tree

3 files changed

+45
-6
lines changed

3 files changed

+45
-6
lines changed

src/libcore/fmt/float.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ use crate::fmt::{Debug, Display, Formatter, LowerExp, Result, UpperExp};
22
use crate::mem::MaybeUninit;
33
use crate::num::flt2dec;
44

5-
// ignore-tidy-undocumented-unsafe
6-
75
// Don't inline this so callers don't use the stack space this function
86
// requires unless they have to.
97
#[inline(never)]
@@ -16,6 +14,7 @@ fn float_to_decimal_common_exact<T>(
1614
where
1715
T: flt2dec::DecodableFloat,
1816
{
17+
// SAFETY: Possible undefined behavior, see FIXME(#53491)
1918
unsafe {
2019
let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64
2120
let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 4]>::uninit();
@@ -48,6 +47,7 @@ fn float_to_decimal_common_shortest<T>(
4847
where
4948
T: flt2dec::DecodableFloat,
5049
{
50+
// SAFETY: Possible undefined behavior, see FIXME(#53491)
5151
unsafe {
5252
// enough for f32 and f64
5353
let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit();
@@ -103,6 +103,7 @@ fn float_to_exponential_common_exact<T>(
103103
where
104104
T: flt2dec::DecodableFloat,
105105
{
106+
// SAFETY: Possible undefined behavior, see FIXME(#53491)
106107
unsafe {
107108
let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64
108109
let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 6]>::uninit();
@@ -132,6 +133,7 @@ fn float_to_exponential_common_shortest<T>(
132133
where
133134
T: flt2dec::DecodableFloat,
134135
{
136+
// SAFETY: Possible undefined behavior, see FIXME(#53491)
135137
unsafe {
136138
// enough for f32 and f64
137139
let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit();

src/libcore/fmt/mod.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
//! Utilities for formatting and printing strings.
22
3-
// ignore-tidy-undocumented-unsafe
4-
53
#![stable(feature = "rust1", since = "1.0.0")]
64

75
use crate::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
@@ -281,6 +279,14 @@ impl<'a> ArgumentV1<'a> {
281279
#[doc(hidden)]
282280
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
283281
pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> {
282+
// SAFETY: `mem::transmute(x)` is safe because
283+
// 1. `&'b T` keeps the lifetime it originated with `'b`
284+
// (so as to not have an unbounded lifetime)
285+
// 2. `&'b T` and `&'b Void` have the same memory layout
286+
// (when `T` is `Sized`, as it is here)
287+
// `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result`
288+
// and `fn(&Void, &mut Formatter<'_>) -> Result` have the same ABI
289+
// (as long as `T` is `Sized`)
284290
unsafe { ArgumentV1 { formatter: mem::transmute(f), value: mem::transmute(x) } }
285291
}
286292

@@ -1399,6 +1405,14 @@ impl<'a> Formatter<'a> {
13991405

14001406
fn write_formatted_parts(&mut self, formatted: &flt2dec::Formatted<'_>) -> Result {
14011407
fn write_bytes(buf: &mut dyn Write, s: &[u8]) -> Result {
1408+
// SAFETY: This is used for `flt2dec::Part::Num` and `flt2dec::Part::Copy`.
1409+
// It's safe to use for `flt2dec::Part::Num` since every char `c` is between
1410+
// `b'0'` and `b'9'`, which means `s` is valid UTF-8.
1411+
// It's also probably safe in practice to use for `flt2dec::Part::Copy(buf)`
1412+
// since `buf` should be plain ASCII, but it's possible for someone to pass
1413+
// in a bad value for `buf` into `flt2dec::to_shortest_str` since it is a
1414+
// public function.
1415+
// FIXME: Determine whether this could result in UB.
14021416
buf.write_str(unsafe { str::from_utf8_unchecked(s) })
14031417
}
14041418

src/libcore/fmt/num.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
//! Integer and floating-point number formatting
22
3-
// ignore-tidy-undocumented-unsafe
4-
53
use crate::fmt;
64
use crate::mem::MaybeUninit;
75
use crate::num::flt2dec;
@@ -84,6 +82,8 @@ trait GenericRadix {
8482
}
8583
}
8684
let buf = &buf[curr..];
85+
// SAFETY: The only chars in `buf` are created by `Self::digit` which are assumed to be
86+
// valid UTF-8
8787
let buf = unsafe {
8888
str::from_utf8_unchecked(slice::from_raw_parts(MaybeUninit::first_ptr(buf), buf.len()))
8989
};
@@ -189,11 +189,19 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\
189189
macro_rules! impl_Display {
190190
($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => {
191191
fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192+
// 2^128 is about 3*10^38, so 39 gives an extra byte of space
192193
let mut buf = [MaybeUninit::<u8>::uninit(); 39];
193194
let mut curr = buf.len() as isize;
194195
let buf_ptr = MaybeUninit::first_ptr_mut(&mut buf);
195196
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
196197

198+
// SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we
199+
// can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show
200+
// that it's OK to copy into `buf_ptr`, notice that at the beginning
201+
// `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
202+
// each step this is kept the same as `n` is divided. Since `n` is always
203+
// non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
204+
// is safe to access.
197205
unsafe {
198206
// need at least 16 bits for the 4-characters-at-a-time to work.
199207
assert!(crate::mem::size_of::<$u>() >= 2);
@@ -206,6 +214,10 @@ macro_rules! impl_Display {
206214
let d1 = (rem / 100) << 1;
207215
let d2 = (rem % 100) << 1;
208216
curr -= 4;
217+
218+
// We are allowed to copy to `buf_ptr[curr..curr + 3]` here since
219+
// otherwise `curr < 0`. But then `n` was originally at least `10000^10`
220+
// which is `10^40 > 2^128 > n`.
209221
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
210222
ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
211223
}
@@ -232,6 +244,8 @@ macro_rules! impl_Display {
232244
}
233245
}
234246

247+
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid
248+
// UTF-8 since `DEC_DIGITS_LUT` is
235249
let buf_slice = unsafe {
236250
str::from_utf8_unchecked(
237251
slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize))
@@ -304,6 +318,8 @@ macro_rules! impl_Exp {
304318
};
305319

306320
// 39 digits (worst case u128) + . = 40
321+
// Since `curr` always decreases by the number of digits copied, this means
322+
// that `curr >= 0`.
307323
let mut buf = [MaybeUninit::<u8>::uninit(); 40];
308324
let mut curr = buf.len() as isize; //index for buf
309325
let buf_ptr = MaybeUninit::first_ptr_mut(&mut buf);
@@ -313,6 +329,8 @@ macro_rules! impl_Exp {
313329
while n >= 100 {
314330
let d1 = ((n % 100) as isize) << 1;
315331
curr -= 2;
332+
// SAFETY: `d1 <= 198`, so we can copy from `lut_ptr[d1..d1 + 2]` since
333+
// `DEC_DIGITS_LUT` has a length of 200.
316334
unsafe {
317335
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
318336
}
@@ -324,6 +342,7 @@ macro_rules! impl_Exp {
324342
// decode second-to-last character
325343
if n >= 10 {
326344
curr -= 1;
345+
// SAFETY: Safe since `40 > curr >= 0` (see comment)
327346
unsafe {
328347
*buf_ptr.offset(curr) = (n as u8 % 10_u8) + b'0';
329348
}
@@ -333,11 +352,13 @@ macro_rules! impl_Exp {
333352
// add decimal point iff >1 mantissa digit will be printed
334353
if exponent != trailing_zeros || added_precision != 0 {
335354
curr -= 1;
355+
// SAFETY: Safe since `40 > curr >= 0`
336356
unsafe {
337357
*buf_ptr.offset(curr) = b'.';
338358
}
339359
}
340360

361+
// SAFETY: Safe since `40 > curr >= 0`
341362
let buf_slice = unsafe {
342363
// decode last character
343364
curr -= 1;
@@ -350,6 +371,8 @@ macro_rules! impl_Exp {
350371
// stores 'e' (or 'E') and the up to 2-digit exponent
351372
let mut exp_buf = [MaybeUninit::<u8>::uninit(); 3];
352373
let exp_ptr = MaybeUninit::first_ptr_mut(&mut exp_buf);
374+
// SAFETY: In either case, `exp_buf` is written within bounds and `exp_ptr[..len]`
375+
// is contained within `exp_buf` since `len <= 3`.
353376
let exp_slice = unsafe {
354377
*exp_ptr.offset(0) = if upper {b'E'} else {b'e'};
355378
let len = if exponent < 10 {

0 commit comments

Comments
 (0)