Skip to content

Commit 95815c9

Browse files
committed
Auto merge of rust-lang#76241 - RalfJung:flt2dec, r=Mark-Simulacrum
flt2dec: properly handle uninitialized memory The float-to-str code currently uses uninitialized memory incorrectly (see rust-lang#76092). This PR fixes that. Specifically, that code used `&mut [T]` as "out references", but it would be incorrect for the caller to actually pass uninitialized memory. So the PR changes this to `&mut [MaybeUninit<T>]`, and then functions return a `&[T]` to the part of the buffer that they initialized (some functions already did that, indirectly via `&Formatted`, others were adjusted to return that buffer instead of just the initialized length). What I particularly like about this is that it moves `unsafe` to the right place: previously, the outermost caller had to use `unsafe` to assert that things are initialized; now it is the functions that do the actual initializing which have the corresponding `unsafe` block when they call `MaybeUninit::slice_get_ref` (renamed in rust-lang#76217 to `slice_assume_init_ref`). Reviewers please be aware that I have no idea how any of this code actually works. My changes were purely mechanical and type-driven. The test suite passes so I guess I didn't screw up badly... Cc @sfackler this is somewhat related to your RFC, and possibly some of this code could benefit from (a generalized version of) the API you describe there. But for now I think what I did is "good enough". Fixes rust-lang#76092.
2 parents da897df + 56129d3 commit 95815c9

File tree

8 files changed

+387
-293
lines changed

8 files changed

+387
-293
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,76 @@
11
use super::super::*;
22
use core::num::flt2dec::strategy::dragon::*;
3+
use std::mem::MaybeUninit;
34
use test::Bencher;
45

56
#[bench]
67
fn bench_small_shortest(b: &mut Bencher) {
78
let decoded = decode_finite(3.141592f64);
8-
let mut buf = [0; MAX_SIG_DIGITS];
9-
b.iter(|| format_shortest(&decoded, &mut buf));
9+
let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS];
10+
b.iter(|| {
11+
format_shortest(&decoded, &mut buf);
12+
});
1013
}
1114

1215
#[bench]
1316
fn bench_big_shortest(b: &mut Bencher) {
1417
let decoded = decode_finite(f64::MAX);
15-
let mut buf = [0; MAX_SIG_DIGITS];
16-
b.iter(|| format_shortest(&decoded, &mut buf));
18+
let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS];
19+
b.iter(|| {
20+
format_shortest(&decoded, &mut buf);
21+
});
1722
}
1823

1924
#[bench]
2025
fn bench_small_exact_3(b: &mut Bencher) {
2126
let decoded = decode_finite(3.141592f64);
22-
let mut buf = [0; 3];
23-
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
27+
let mut buf = [MaybeUninit::new(0); 3];
28+
b.iter(|| {
29+
format_exact(&decoded, &mut buf, i16::MIN);
30+
});
2431
}
2532

2633
#[bench]
2734
fn bench_big_exact_3(b: &mut Bencher) {
2835
let decoded = decode_finite(f64::MAX);
29-
let mut buf = [0; 3];
30-
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
36+
let mut buf = [MaybeUninit::new(0); 3];
37+
b.iter(|| {
38+
format_exact(&decoded, &mut buf, i16::MIN);
39+
});
3140
}
3241

3342
#[bench]
3443
fn bench_small_exact_12(b: &mut Bencher) {
3544
let decoded = decode_finite(3.141592f64);
36-
let mut buf = [0; 12];
37-
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
45+
let mut buf = [MaybeUninit::new(0); 12];
46+
b.iter(|| {
47+
format_exact(&decoded, &mut buf, i16::MIN);
48+
});
3849
}
3950

4051
#[bench]
4152
fn bench_big_exact_12(b: &mut Bencher) {
4253
let decoded = decode_finite(f64::MAX);
43-
let mut buf = [0; 12];
44-
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
54+
let mut buf = [MaybeUninit::new(0); 12];
55+
b.iter(|| {
56+
format_exact(&decoded, &mut buf, i16::MIN);
57+
});
4558
}
4659

4760
#[bench]
4861
fn bench_small_exact_inf(b: &mut Bencher) {
4962
let decoded = decode_finite(3.141592f64);
50-
let mut buf = [0; 1024];
51-
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
63+
let mut buf = [MaybeUninit::new(0); 1024];
64+
b.iter(|| {
65+
format_exact(&decoded, &mut buf, i16::MIN);
66+
});
5267
}
5368

5469
#[bench]
5570
fn bench_big_exact_inf(b: &mut Bencher) {
5671
let decoded = decode_finite(f64::MAX);
57-
let mut buf = [0; 1024];
58-
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
72+
let mut buf = [MaybeUninit::new(0); 1024];
73+
b.iter(|| {
74+
format_exact(&decoded, &mut buf, i16::MIN);
75+
});
5976
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::super::*;
22
use core::num::flt2dec::strategy::grisu::*;
3+
use std::mem::MaybeUninit;
34
use test::Bencher;
45

56
pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
@@ -12,55 +13,71 @@ pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
1213
#[bench]
1314
fn bench_small_shortest(b: &mut Bencher) {
1415
let decoded = decode_finite(3.141592f64);
15-
let mut buf = [0; MAX_SIG_DIGITS];
16-
b.iter(|| format_shortest(&decoded, &mut buf));
16+
let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS];
17+
b.iter(|| {
18+
format_shortest(&decoded, &mut buf);
19+
});
1720
}
1821

1922
#[bench]
2023
fn bench_big_shortest(b: &mut Bencher) {
2124
let decoded = decode_finite(f64::MAX);
22-
let mut buf = [0; MAX_SIG_DIGITS];
23-
b.iter(|| format_shortest(&decoded, &mut buf));
25+
let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS];
26+
b.iter(|| {
27+
format_shortest(&decoded, &mut buf);
28+
});
2429
}
2530

2631
#[bench]
2732
fn bench_small_exact_3(b: &mut Bencher) {
2833
let decoded = decode_finite(3.141592f64);
29-
let mut buf = [0; 3];
30-
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
34+
let mut buf = [MaybeUninit::new(0); 3];
35+
b.iter(|| {
36+
format_exact(&decoded, &mut buf, i16::MIN);
37+
});
3138
}
3239

3340
#[bench]
3441
fn bench_big_exact_3(b: &mut Bencher) {
3542
let decoded = decode_finite(f64::MAX);
36-
let mut buf = [0; 3];
37-
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
43+
let mut buf = [MaybeUninit::new(0); 3];
44+
b.iter(|| {
45+
format_exact(&decoded, &mut buf, i16::MIN);
46+
});
3847
}
3948

4049
#[bench]
4150
fn bench_small_exact_12(b: &mut Bencher) {
4251
let decoded = decode_finite(3.141592f64);
43-
let mut buf = [0; 12];
44-
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
52+
let mut buf = [MaybeUninit::new(0); 12];
53+
b.iter(|| {
54+
format_exact(&decoded, &mut buf, i16::MIN);
55+
});
4556
}
4657

4758
#[bench]
4859
fn bench_big_exact_12(b: &mut Bencher) {
4960
let decoded = decode_finite(f64::MAX);
50-
let mut buf = [0; 12];
51-
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
61+
let mut buf = [MaybeUninit::new(0); 12];
62+
b.iter(|| {
63+
format_exact(&decoded, &mut buf, i16::MIN);
64+
});
5265
}
5366

5467
#[bench]
5568
fn bench_small_exact_inf(b: &mut Bencher) {
5669
let decoded = decode_finite(3.141592f64);
57-
let mut buf = [0; 1024];
58-
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
70+
let mut buf = [MaybeUninit::new(0); 1024];
71+
b.iter(|| {
72+
format_exact(&decoded, &mut buf, i16::MIN);
73+
});
5974
}
6075

6176
#[bench]
6277
fn bench_big_exact_inf(b: &mut Bencher) {
6378
let decoded = decode_finite(f64::MAX);
64-
let mut buf = [0; 1024];
65-
b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
79+
let mut buf = [MaybeUninit::new(0); 1024];
80+
b.iter(|| {
81+
format_exact(&decoded, &mut buf, i16::MIN);
82+
});
6683
}

library/core/src/fmt/float.rs

+48-68
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,17 @@ fn float_to_decimal_common_exact<T>(
1414
where
1515
T: flt2dec::DecodableFloat,
1616
{
17-
// SAFETY: Possible undefined behavior, see FIXME(#76092)
18-
unsafe {
19-
let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64
20-
let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 4]>::uninit();
21-
// FIXME(#76092): This is calling `assume_init_mut` on an uninitialized
22-
// `MaybeUninit` (here and elsewhere in this file). Revisit this once
23-
// we decided whether that is valid or not.
24-
// We can do this only because we are libstd and coupled to the compiler.
25-
// (FWIW, using `freeze` would not be enough; `flt2dec::Part` is an enum!)
26-
let formatted = flt2dec::to_exact_fixed_str(
27-
flt2dec::strategy::grisu::format_exact,
28-
*num,
29-
sign,
30-
precision,
31-
buf.assume_init_mut(),
32-
parts.assume_init_mut(),
33-
);
34-
fmt.pad_formatted_parts(&formatted)
35-
}
17+
let mut buf: [MaybeUninit<u8>; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64
18+
let mut parts: [MaybeUninit<flt2dec::Part<'_>>; 4] = MaybeUninit::uninit_array();
19+
let formatted = flt2dec::to_exact_fixed_str(
20+
flt2dec::strategy::grisu::format_exact,
21+
*num,
22+
sign,
23+
precision,
24+
&mut buf,
25+
&mut parts,
26+
);
27+
fmt.pad_formatted_parts(&formatted)
3628
}
3729

3830
// Don't inline this so callers that call both this and the above won't wind
@@ -47,22 +39,18 @@ fn float_to_decimal_common_shortest<T>(
4739
where
4840
T: flt2dec::DecodableFloat,
4941
{
50-
// SAFETY: Possible undefined behavior, see FIXME(#76092)
51-
unsafe {
52-
// enough for f32 and f64
53-
let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit();
54-
let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 4]>::uninit();
55-
// FIXME(#76092)
56-
let formatted = flt2dec::to_shortest_str(
57-
flt2dec::strategy::grisu::format_shortest,
58-
*num,
59-
sign,
60-
precision,
61-
buf.assume_init_mut(),
62-
parts.assume_init_mut(),
63-
);
64-
fmt.pad_formatted_parts(&formatted)
65-
}
42+
// enough for f32 and f64
43+
let mut buf: [MaybeUninit<u8>; flt2dec::MAX_SIG_DIGITS] = MaybeUninit::uninit_array();
44+
let mut parts: [MaybeUninit<flt2dec::Part<'_>>; 4] = MaybeUninit::uninit_array();
45+
let formatted = flt2dec::to_shortest_str(
46+
flt2dec::strategy::grisu::format_shortest,
47+
*num,
48+
sign,
49+
precision,
50+
&mut buf,
51+
&mut parts,
52+
);
53+
fmt.pad_formatted_parts(&formatted)
6654
}
6755

6856
// Common code of floating point Debug and Display.
@@ -103,22 +91,18 @@ fn float_to_exponential_common_exact<T>(
10391
where
10492
T: flt2dec::DecodableFloat,
10593
{
106-
// SAFETY: Possible undefined behavior, see FIXME(#76092)
107-
unsafe {
108-
let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64
109-
let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 6]>::uninit();
110-
// FIXME(#76092)
111-
let formatted = flt2dec::to_exact_exp_str(
112-
flt2dec::strategy::grisu::format_exact,
113-
*num,
114-
sign,
115-
precision,
116-
upper,
117-
buf.assume_init_mut(),
118-
parts.assume_init_mut(),
119-
);
120-
fmt.pad_formatted_parts(&formatted)
121-
}
94+
let mut buf: [MaybeUninit<u8>; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64
95+
let mut parts: [MaybeUninit<flt2dec::Part<'_>>; 6] = MaybeUninit::uninit_array();
96+
let formatted = flt2dec::to_exact_exp_str(
97+
flt2dec::strategy::grisu::format_exact,
98+
*num,
99+
sign,
100+
precision,
101+
upper,
102+
&mut buf,
103+
&mut parts,
104+
);
105+
fmt.pad_formatted_parts(&formatted)
122106
}
123107

124108
// Don't inline this so callers that call both this and the above won't wind
@@ -133,23 +117,19 @@ fn float_to_exponential_common_shortest<T>(
133117
where
134118
T: flt2dec::DecodableFloat,
135119
{
136-
// SAFETY: Possible undefined behavior, see FIXME(#76092)
137-
unsafe {
138-
// enough for f32 and f64
139-
let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit();
140-
let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 6]>::uninit();
141-
// FIXME(#76092)
142-
let formatted = flt2dec::to_shortest_exp_str(
143-
flt2dec::strategy::grisu::format_shortest,
144-
*num,
145-
sign,
146-
(0, 0),
147-
upper,
148-
buf.assume_init_mut(),
149-
parts.assume_init_mut(),
150-
);
151-
fmt.pad_formatted_parts(&formatted)
152-
}
120+
// enough for f32 and f64
121+
let mut buf: [MaybeUninit<u8>; flt2dec::MAX_SIG_DIGITS] = MaybeUninit::uninit_array();
122+
let mut parts: [MaybeUninit<flt2dec::Part<'_>>; 6] = MaybeUninit::uninit_array();
123+
let formatted = flt2dec::to_shortest_exp_str(
124+
flt2dec::strategy::grisu::format_shortest,
125+
*num,
126+
sign,
127+
(0, 0),
128+
upper,
129+
&mut buf,
130+
&mut parts,
131+
);
132+
fmt.pad_formatted_parts(&formatted)
153133
}
154134

155135
// Common code of floating point LowerExp and UpperExp.

0 commit comments

Comments
 (0)