@@ -2113,14 +2113,15 @@ pub unsafe trait FromZeros: TryFromBytes {
2113
2113
/// Note that `Box<Self>` can be converted to `Arc<Self>` and other
2114
2114
/// container types without reallocation.
2115
2115
///
2116
- /// # Panics
2116
+ /// # Errors
2117
2117
///
2118
- /// Panics if allocation of `size_of::<Self>()` bytes fails.
2118
+ /// Returns an error on allocation failure. Allocation failure is guaranteed
2119
+ /// never to cause a panic or an abort.
2119
2120
#[ must_use = "has no side effects (other than allocation)" ]
2120
2121
#[ cfg( any( feature = "alloc" , test) ) ]
2121
2122
#[ cfg_attr( doc_cfg, doc( cfg( feature = "alloc" ) ) ) ]
2122
2123
#[ inline]
2123
- fn new_box_zeroed ( ) -> Box < Self >
2124
+ fn new_box_zeroed ( ) -> Result < Box < Self > , AllocError >
2124
2125
where
2125
2126
Self : Sized ,
2126
2127
{
@@ -2146,22 +2147,18 @@ pub unsafe trait FromZeros: TryFromBytes {
2146
2147
// [2] Per https://doc.rust-lang.org/std/ptr/struct.NonNull.html#method.dangling:
2147
2148
//
2148
2149
// Creates a new `NonNull` that is dangling, but well-aligned.
2149
- unsafe {
2150
- return Box :: from_raw ( NonNull :: dangling ( ) . as_ptr ( ) ) ;
2151
- }
2150
+ return Ok ( unsafe { Box :: from_raw ( NonNull :: dangling ( ) . as_ptr ( ) ) } ) ;
2152
2151
}
2153
2152
2154
2153
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
2155
2154
#[ allow( clippy:: undocumented_unsafe_blocks) ]
2156
2155
let ptr = unsafe { alloc:: alloc:: alloc_zeroed ( layout) . cast :: < Self > ( ) } ;
2157
2156
if ptr. is_null ( ) {
2158
- alloc :: alloc :: handle_alloc_error ( layout ) ;
2157
+ return Err ( AllocError ) ;
2159
2158
}
2160
2159
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
2161
2160
#[ allow( clippy:: undocumented_unsafe_blocks) ]
2162
- unsafe {
2163
- Box :: from_raw ( ptr)
2164
- }
2161
+ Ok ( unsafe { Box :: from_raw ( ptr) } )
2165
2162
}
2166
2163
2167
2164
/// Creates a `Box<[Self]>` (a boxed slice) from zeroed bytes.
@@ -2181,22 +2178,24 @@ pub unsafe trait FromZeros: TryFromBytes {
2181
2178
/// actual information, but its `len()` property will report the correct
2182
2179
/// value.
2183
2180
///
2184
- /// # Panics
2181
+ /// # Errors
2185
2182
///
2186
- /// * Panics if `size_of::<Self>() * len` overflows.
2187
- /// * Panics if allocation of `size_of::<Self>() * len` bytes fails .
2183
+ /// Returns an error on allocation failure. Allocation failure is
2184
+ /// guaranteed never to cause a panic or an abort .
2188
2185
#[ must_use = "has no side effects (other than allocation)" ]
2189
2186
#[ cfg( feature = "alloc" ) ]
2190
2187
#[ cfg_attr( doc_cfg, doc( cfg( feature = "alloc" ) ) ) ]
2191
2188
#[ inline]
2192
- fn new_box_slice_zeroed ( len : usize ) -> Box < [ Self ] >
2189
+ fn new_box_zeroed_with_elems ( count : usize ) -> Result < Box < Self > , AllocError >
2193
2190
where
2194
- Self : Sized ,
2191
+ Self : KnownLayout < PointerMetadata = usize > ,
2195
2192
{
2196
- let size = mem:: size_of :: < Self > ( )
2197
- . checked_mul ( len)
2198
- . expect ( "mem::size_of::<Self>() * len overflows `usize`" ) ;
2199
- let align = mem:: align_of :: < Self > ( ) ;
2193
+ let size = match count. size_for_metadata ( Self :: LAYOUT ) {
2194
+ Some ( size) => size,
2195
+ None => return Err ( AllocError ) ,
2196
+ } ;
2197
+
2198
+ let align = Self :: LAYOUT . align . get ( ) ;
2200
2199
// On stable Rust versions <= 1.64.0, `Layout::from_size_align` has a
2201
2200
// bug in which sufficiently-large allocations (those which, when
2202
2201
// rounded up to the alignment, overflow `isize`) are not rejected,
@@ -2205,32 +2204,73 @@ pub unsafe trait FromZeros: TryFromBytes {
2205
2204
// TODO(#67): Once our MSRV is > 1.64.0, remove this assertion.
2206
2205
#[ allow( clippy:: as_conversions) ]
2207
2206
let max_alloc = ( isize:: MAX as usize ) . saturating_sub ( align) ;
2208
- assert ! ( size <= max_alloc) ;
2207
+ if size > max_alloc {
2208
+ return Err ( AllocError ) ;
2209
+ }
2210
+
2209
2211
// TODO(https://github.com/rust-lang/rust/issues/55724): Use
2210
2212
// `Layout::repeat` once it's stabilized.
2211
- let layout =
2212
- Layout :: from_size_align ( size, align) . expect ( "total allocation size overflows `isize`" ) ;
2213
+ let layout = Layout :: from_size_align ( size, align) . or ( Err ( AllocError ) ) ?;
2213
2214
2214
2215
let ptr = if layout. size ( ) != 0 {
2215
2216
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
2216
2217
#[ allow( clippy:: undocumented_unsafe_blocks) ]
2217
- let ptr = unsafe { alloc:: alloc:: alloc_zeroed ( layout) . cast :: < Self > ( ) } ;
2218
- if ptr. is_null ( ) {
2219
- alloc:: alloc:: handle_alloc_error ( layout) ;
2218
+ let ptr = unsafe { alloc:: alloc:: alloc_zeroed ( layout) } ;
2219
+ match NonNull :: new ( ptr) {
2220
+ Some ( ptr) => ptr,
2221
+ None => return Err ( AllocError ) ,
2220
2222
}
2221
- ptr
2222
2223
} else {
2224
+ let align = Self :: LAYOUT . align . get ( ) ;
2225
+ // We use `transmute` instead of an `as` cast since Miri (with
2226
+ // strict provenance enabled) notices and complains that an `as`
2227
+ // cast creates a pointer with no provenance. Miri isn't smart
2228
+ // enough to realize that we're only executing this branch when
2229
+ // we're constructing a zero-sized `Box`, which doesn't require
2230
+ // provenance.
2231
+ //
2232
+ // SAFETY: any initialized bit sequence is a bit-valid `*mut u8`.
2233
+ // All bits of a `usize` are initialized.
2234
+ let dangling = unsafe { mem:: transmute :: < usize , * mut u8 > ( align) } ;
2235
+ // SAFETY: `dangling` is constructed from `Self::LAYOUT.align`,
2236
+ // which is a `NonZeroUsize`, which is guaranteed to be non-zero.
2237
+ //
2223
2238
// `Box<[T]>` does not allocate when `T` is zero-sized or when `len`
2224
2239
// is zero, but it does require a non-null dangling pointer for its
2225
2240
// allocation.
2226
- NonNull :: < Self > :: dangling ( ) . as_ptr ( )
2241
+ //
2242
+ // TODO(https://github.com/rust-lang/rust/issues/95228): Use
2243
+ // `std::ptr::without_provenance` once it's stable. That may
2244
+ // optimize better. As written, Rust may assume that this consumes
2245
+ // "exposed" provenance, and thus Rust may have to assume that this
2246
+ // may consume provenance from any pointer whose provenance has been
2247
+ // exposed.
2248
+ #[ allow( fuzzy_provenance_casts) ]
2249
+ unsafe {
2250
+ NonNull :: new_unchecked ( dangling)
2251
+ }
2227
2252
} ;
2228
2253
2229
- // TODO(#429): Add a "SAFETY" comment and remove this `allow`.
2254
+ let ptr = Self :: raw_from_ptr_len ( ptr, count) ;
2255
+
2256
+ // TODO(#429): Add a "SAFETY" comment and remove this `allow`. Make sure
2257
+ // to include a justification that `ptr.as_ptr()` is validly-aligned in
2258
+ // the ZST case (in which we manually construct a dangling pointer).
2230
2259
#[ allow( clippy:: undocumented_unsafe_blocks) ]
2231
- unsafe {
2232
- Box :: from_raw ( slice:: from_raw_parts_mut ( ptr, len) )
2233
- }
2260
+ Ok ( unsafe { Box :: from_raw ( ptr. as_ptr ( ) ) } )
2261
+ }
2262
+
2263
+ #[ deprecated( since = "0.8.0" , note = "renamed to `FromZeros::new_box_zeroed_with_elems`" ) ]
2264
+ #[ doc( hidden) ]
2265
+ #[ cfg( feature = "alloc" ) ]
2266
+ #[ cfg_attr( doc_cfg, doc( cfg( feature = "alloc" ) ) ) ]
2267
+ #[ must_use = "has no side effects (other than allocation)" ]
2268
+ #[ inline( always) ]
2269
+ fn new_box_slice_zeroed ( len : usize ) -> Result < Box < [ Self ] > , AllocError >
2270
+ where
2271
+ Self : Sized ,
2272
+ {
2273
+ <[ Self ] >:: new_box_zeroed_with_elems ( len)
2234
2274
}
2235
2275
2236
2276
/// Creates a `Vec<Self>` from zeroed bytes.
@@ -2249,19 +2289,19 @@ pub unsafe trait FromZeros: TryFromBytes {
2249
2289
/// actual information, but its `len()` property will report the correct
2250
2290
/// value.
2251
2291
///
2252
- /// # Panics
2292
+ /// # Errors
2253
2293
///
2254
- /// * Panics if `size_of::<Self>() * len` overflows.
2255
- /// * Panics if allocation of `size_of::<Self>() * len` bytes fails .
2294
+ /// Returns an error on allocation failure. Allocation failure is
2295
+ /// guaranteed never to cause a panic or an abort .
2256
2296
#[ must_use = "has no side effects (other than allocation)" ]
2257
2297
#[ cfg( feature = "alloc" ) ]
2258
2298
#[ cfg_attr( doc_cfg, doc( cfg( feature = "alloc" ) ) ) ]
2259
2299
#[ inline( always) ]
2260
- fn new_vec_zeroed ( len : usize ) -> Vec < Self >
2300
+ fn new_vec_zeroed ( len : usize ) -> Result < Vec < Self > , AllocError >
2261
2301
where
2262
2302
Self : Sized ,
2263
2303
{
2264
- Self :: new_box_slice_zeroed ( len) . into ( )
2304
+ < [ Self ] > :: new_box_zeroed_with_elems ( len) . map ( Into :: into )
2265
2305
}
2266
2306
}
2267
2307
@@ -4972,7 +5012,7 @@ mod alloc_support {
4972
5012
4973
5013
#[ test]
4974
5014
fn test_new_box_zeroed ( ) {
4975
- assert_eq ! ( * u64 :: new_box_zeroed( ) , 0 ) ;
5015
+ assert_eq ! ( u64 :: new_box_zeroed( ) , Ok ( Box :: new ( 0 ) ) ) ;
4976
5016
}
4977
5017
4978
5018
#[ test]
@@ -4986,28 +5026,28 @@ mod alloc_support {
4986
5026
// when running under Miri.
4987
5027
#[ allow( clippy:: unit_cmp) ]
4988
5028
{
4989
- assert_eq ! ( * <( ) >:: new_box_zeroed( ) , ( ) ) ;
5029
+ assert_eq ! ( <( ) >:: new_box_zeroed( ) , Ok ( Box :: new ( ( ) ) ) ) ;
4990
5030
}
4991
5031
}
4992
5032
4993
5033
#[ test]
4994
- fn test_new_box_slice_zeroed ( ) {
4995
- let mut s: Box < [ u64 ] > = u64:: new_box_slice_zeroed ( 3 ) ;
5034
+ fn test_new_box_zeroed_with_elems ( ) {
5035
+ let mut s: Box < [ u64 ] > = < [ u64 ] > :: new_box_zeroed_with_elems ( 3 ) . unwrap ( ) ;
4996
5036
assert_eq ! ( s. len( ) , 3 ) ;
4997
5037
assert_eq ! ( & * s, & [ 0 , 0 , 0 ] ) ;
4998
5038
s[ 1 ] = 3 ;
4999
5039
assert_eq ! ( & * s, & [ 0 , 3 , 0 ] ) ;
5000
5040
}
5001
5041
5002
5042
#[ test]
5003
- fn test_new_box_slice_zeroed_empty ( ) {
5004
- let s: Box < [ u64 ] > = u64:: new_box_slice_zeroed ( 0 ) ;
5043
+ fn test_new_box_zeroed_with_elems_empty ( ) {
5044
+ let s: Box < [ u64 ] > = < [ u64 ] > :: new_box_zeroed_with_elems ( 0 ) . unwrap ( ) ;
5005
5045
assert_eq ! ( s. len( ) , 0 ) ;
5006
5046
}
5007
5047
5008
5048
#[ test]
5009
- fn test_new_box_slice_zeroed_zst ( ) {
5010
- let mut s: Box < [ ( ) ] > = <( ) >:: new_box_slice_zeroed ( 3 ) ;
5049
+ fn test_new_box_zeroed_with_elems_zst ( ) {
5050
+ let mut s: Box < [ ( ) ] > = <[ ( ) ] >:: new_box_zeroed_with_elems ( 3 ) . unwrap ( ) ;
5011
5051
assert_eq ! ( s. len( ) , 3 ) ;
5012
5052
assert ! ( s. get( 10 ) . is_none( ) ) ;
5013
5053
// This test exists in order to exercise unsafe code, especially
@@ -5020,22 +5060,20 @@ mod alloc_support {
5020
5060
}
5021
5061
5022
5062
#[ test]
5023
- fn test_new_box_slice_zeroed_zst_empty ( ) {
5024
- let s: Box < [ ( ) ] > = <( ) >:: new_box_slice_zeroed ( 0 ) ;
5063
+ fn test_new_box_zeroed_with_elems_zst_empty ( ) {
5064
+ let s: Box < [ ( ) ] > = <[ ( ) ] >:: new_box_zeroed_with_elems ( 0 ) . unwrap ( ) ;
5025
5065
assert_eq ! ( s. len( ) , 0 ) ;
5026
5066
}
5027
5067
5028
5068
#[ test]
5029
- #[ should_panic( expected = "mem::size_of::<Self>() * len overflows `usize`" ) ]
5030
- fn test_new_box_slice_zeroed_panics_mul_overflow ( ) {
5031
- let _ = u16:: new_box_slice_zeroed ( usize:: MAX ) ;
5032
- }
5069
+ fn new_box_zeroed_with_elems_errors ( ) {
5070
+ assert_eq ! ( <[ u16 ] >:: new_box_zeroed_with_elems( usize :: MAX ) , Err ( AllocError ) ) ;
5033
5071
5034
- #[ test]
5035
- #[ should_panic( expected = "assertion failed: size <= max_alloc" ) ]
5036
- fn test_new_box_slice_zeroed_panics_isize_overflow ( ) {
5037
5072
let max = usize:: try_from ( isize:: MAX ) . unwrap ( ) ;
5038
- let _ = u16:: new_box_slice_zeroed ( ( max / mem:: size_of :: < u16 > ( ) ) + 1 ) ;
5073
+ assert_eq ! (
5074
+ <[ u16 ] >:: new_box_zeroed_with_elems( ( max / mem:: size_of:: <u16 >( ) ) + 1 ) ,
5075
+ Err ( AllocError )
5076
+ ) ;
5039
5077
}
5040
5078
}
5041
5079
}
@@ -5525,14 +5563,20 @@ mod tests {
5525
5563
5526
5564
#[ cfg( feature = "alloc" ) ]
5527
5565
{
5528
- assert_eq ! ( bool :: new_box_zeroed( ) , Box :: new( false ) ) ;
5529
- assert_eq ! ( char :: new_box_zeroed( ) , Box :: new( '\0' ) ) ;
5566
+ assert_eq ! ( bool :: new_box_zeroed( ) , Ok ( Box :: new( false ) ) ) ;
5567
+ assert_eq ! ( char :: new_box_zeroed( ) , Ok ( Box :: new( '\0' ) ) ) ;
5530
5568
5531
- assert_eq ! ( bool :: new_box_slice_zeroed( 3 ) . as_ref( ) , [ false , false , false ] ) ;
5532
- assert_eq ! ( char :: new_box_slice_zeroed( 3 ) . as_ref( ) , [ '\0' , '\0' , '\0' ] ) ;
5569
+ assert_eq ! (
5570
+ <[ bool ] >:: new_box_zeroed_with_elems( 3 ) . unwrap( ) . as_ref( ) ,
5571
+ [ false , false , false ]
5572
+ ) ;
5573
+ assert_eq ! (
5574
+ <[ char ] >:: new_box_zeroed_with_elems( 3 ) . unwrap( ) . as_ref( ) ,
5575
+ [ '\0' , '\0' , '\0' ]
5576
+ ) ;
5533
5577
5534
- assert_eq ! ( bool :: new_vec_zeroed( 3 ) . as_ref( ) , [ false , false , false ] ) ;
5535
- assert_eq ! ( char :: new_vec_zeroed( 3 ) . as_ref( ) , [ '\0' , '\0' , '\0' ] ) ;
5578
+ assert_eq ! ( bool :: new_vec_zeroed( 3 ) . unwrap ( ) . as_ref( ) , [ false , false , false ] ) ;
5579
+ assert_eq ! ( char :: new_vec_zeroed( 3 ) . unwrap ( ) . as_ref( ) , [ '\0' , '\0' , '\0' ] ) ;
5536
5580
}
5537
5581
5538
5582
let mut string = "hello" . to_string ( ) ;
0 commit comments