Skip to content

Commit b7cae19

Browse files
committed
generalize intrinsics::write_bytes to take any 1-byte wide type as value
This lets us use it for any T since T may contain uninitialized bytes.
1 parent 5c7ae0c commit b7cae19

File tree

5 files changed

+58
-8
lines changed

5 files changed

+58
-8
lines changed

compiler/rustc_codegen_ssa/src/mir/intrinsic.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,12 +198,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
198198
return Ok(());
199199
}
200200
sym::write_bytes => {
201+
// invalid types may be encountered on dead branches after a size check.
202+
if args[1].layout.size.bytes() != 1 {
203+
bx.unreachable_nonterminator();
204+
return Ok(());
205+
}
206+
let val = bx.from_immediate(args[1].immediate());
201207
memset_intrinsic(
202208
bx,
203209
false,
204210
fn_args.type_at(0),
205211
args[0].immediate(),
206-
args[1].immediate(),
212+
val,
207213
args[2].immediate(),
208214
);
209215
return Ok(());

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
use std::assert_matches::assert_matches;
66

7+
use either::Either;
78
use rustc_abi::{FieldIdx, HasDataLayout, Size};
89
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
910
use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint};
@@ -866,18 +867,43 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
866867
count: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
867868
name: &'static str,
868869
) -> InterpResult<'tcx> {
869-
let layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap())?;
870+
let dst_layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap())?;
871+
let src_layout = self.layout_of(byte.layout.ty)?;
872+
873+
if src_layout.size.bytes_usize() != 1 {
874+
throw_ub_custom!(
875+
fluent::const_eval_scalar_size_mismatch,
876+
target_size = 1,
877+
data_size = src_layout.size.bytes(),
878+
);
879+
}
870880

871881
let dst = self.read_pointer(dst)?;
872-
let byte = self.read_scalar(byte)?.to_u8()?;
882+
873883
let count = self.read_target_usize(count)?;
874884

875885
// `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
876886
// but no actual allocation can be big enough for the difference to be noticeable.
877887
let len = self
878-
.compute_size_in_bytes(layout.size, count)
888+
.compute_size_in_bytes(dst_layout.size, count)
879889
.ok_or_else(|| err_ub_custom!(fluent::const_eval_size_overflow, name = name))?;
880890

891+
let byte = match self.read_immediate_raw(byte)? {
892+
Either::Left(src_place) => {
893+
// val is not an immediate, possibly uninit.
894+
self.mem_copy_repeatedly(
895+
src_place.ptr(),
896+
dst,
897+
Size::from_bytes(1),
898+
len.bytes(),
899+
/* nonoverlapping: */ false,
900+
)?;
901+
return interp_ok(());
902+
}
903+
Either::Right(imm) => imm,
904+
};
905+
906+
let byte = byte.to_scalar().to_u8()?;
881907
let bytes = std::iter::repeat(byte).take(len.bytes_usize());
882908
self.write_bytes_ptr(dst, bytes)
883909
}

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,10 @@ pub(crate) fn check_intrinsic_type(
341341
let byte_ptr = Ty::new_imm_ptr(tcx, tcx.types.u8);
342342
(0, 0, vec![byte_ptr, byte_ptr, tcx.types.usize], tcx.types.i32)
343343
}
344-
sym::write_bytes | sym::volatile_set_memory => (
344+
sym::write_bytes => {
345+
(2, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(1), tcx.types.usize], tcx.types.unit)
346+
}
347+
sym::volatile_set_memory => (
345348
1,
346349
0,
347350
vec![Ty::new_mut_ptr(tcx, param(0)), tcx.types.u8, tcx.types.usize],

library/core/src/intrinsics/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2882,6 +2882,8 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
28822882
pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize);
28832883

28842884
/// This is an accidentally-stable alias to [`ptr::write_bytes`]; use that instead.
2885+
///
2886+
/// `val` must be 1 byte wide, it is allowed to be uninit.
28852887
// Note (intentionally not in the doc comment): `ptr::write_bytes` adds some extra
28862888
// debug assertions; if you are writing compiler tests or code inside the standard library
28872889
// that wants to avoid those debug assertions, directly call this intrinsic instead.
@@ -2890,7 +2892,7 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize);
28902892
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
28912893
#[rustc_nounwind]
28922894
#[rustc_intrinsic]
2893-
pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
2895+
pub const unsafe fn write_bytes<T, B>(dst: *mut T, val: B, count: usize);
28942896

28952897
/// Returns the minimum (IEEE 754-2008 minNum) of two `f16` values.
28962898
///

library/coretests/tests/intrinsics.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use core::any::TypeId;
2+
use core::hint::black_box;
23
use core::intrinsics::assume;
4+
use core::mem::MaybeUninit;
35

46
#[test]
57
fn test_typeid_sized_types() {
@@ -43,7 +45,7 @@ const fn test_write_bytes_in_const_contexts() {
4345
const TEST: [u32; 3] = {
4446
let mut arr = [1u32, 2, 3];
4547
unsafe {
46-
write_bytes(arr.as_mut_ptr(), 0, 2);
48+
write_bytes(arr.as_mut_ptr(), 0u8, 2);
4749
}
4850
arr
4951
};
@@ -55,14 +57,25 @@ const fn test_write_bytes_in_const_contexts() {
5557
const TEST2: [u32; 3] = {
5658
let mut arr = [1u32, 2, 3];
5759
unsafe {
58-
write_bytes(arr.as_mut_ptr(), 1, 2);
60+
write_bytes(arr.as_mut_ptr(), 1u8, 2);
5961
}
6062
arr
6163
};
6264

6365
assert!(TEST2[0] == 16843009);
6466
assert!(TEST2[1] == 16843009);
6567
assert!(TEST2[2] == 3);
68+
69+
const TEST3: [MaybeUninit<u32>; 2] = {
70+
let mut arr: [MaybeUninit<u32>; 2] = [MaybeUninit::uninit(), MaybeUninit::new(1)];
71+
unsafe {
72+
write_bytes(arr.as_mut_ptr(), MaybeUninit::<u8>::uninit(), 2);
73+
}
74+
arr
75+
};
76+
77+
// can't do much with uninitialized values, just make sure it compiles
78+
black_box(TEST3);
6679
}
6780

6881
#[test]

0 commit comments

Comments
 (0)