Skip to content

Commit 472364c

Browse files
committed
const-eval: always do mem-to-mem copies if there might be padding involved
1 parent e1a2ec6 commit 472364c

File tree

4 files changed

+67
-5
lines changed

4 files changed

+67
-5
lines changed

compiler/rustc_const_eval/src/interpret/place.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -885,10 +885,29 @@ where
885885
dest.layout().ty,
886886
);
887887
}
888+
// If the source has padding, we want to always do the mem-to-mem copy to ensure consistent
889+
// padding in the target independent of layout choices.
890+
let src_has_padding = match src.layout().backend_repr {
891+
BackendRepr::Scalar(_) => false,
892+
BackendRepr::ScalarPair(left, right) => {
893+
let left_size = left.size(self);
894+
let right_size = right.size(self);
895+
// We have padding if the sizes don't add up to the total.
896+
left_size + right_size != src.layout().size
897+
}
898+
// Everything else can only exist in memory anyway.
899+
_ => true,
900+
};
888901

889-
// Let us see if the layout is simple so we take a shortcut,
890-
// avoid force_allocation.
891-
let src = match self.read_immediate_raw(src)? {
902+
let src_val = if src_has_padding {
903+
// Do our best to get an mplace. If there's no mplace, then this is stored as an
904+
// "optimized" local, so its padding is definitely uninitialized and we are fine.
905+
src.to_op(self)?.as_mplace_or_imm()
906+
} else {
907+
// Do our best to get an immediate, to avoid having to force_allocate the destination.
908+
self.read_immediate_raw(src)?
909+
};
910+
let src = match src_val {
892911
Right(src_val) => {
893912
assert!(!src.layout().is_unsized());
894913
assert!(!dest.layout().is_unsized());

compiler/rustc_const_eval/src/interpret/projection.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
9797
}
9898

9999
/// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for
100-
/// reading from this thing.
100+
/// reading from this thing. This will never actually do a read from memory!
101101
fn to_op<M: Machine<'tcx, Provenance = Prov>>(
102102
&self,
103103
ecx: &InterpCx<'tcx, M>,

tests/ui/consts/const-eval/ptr_fragments_in_final.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,39 @@ const MIXED_PTR: MaybeUninit<*const u8> = { //~ERROR: partial pointer in final v
3737
}
3838
};
3939

40+
/// This has pointer bytes in the padding of the memory that the final value is read from.
41+
/// To ensure consistent behavior, we want to *always* copy that padding, even if the value
42+
/// could be represented as a more efficient ScalarPair. Hence this must fail to compile.
43+
fn fragment_in_padding() -> impl Copy {
44+
#[cfg(target_pointer_width = "64")]
45+
type TwoUsize = u128;
46+
#[cfg(target_pointer_width = "32")]
47+
type TwoUsize = u64;
48+
49+
#[repr(C, align(16))]
50+
#[derive(Clone, Copy)]
51+
struct Thing {
52+
x: TwoUsize,
53+
y: usize,
54+
// one pointer worth of padding
55+
}
56+
const _: () = assert!(mem::size_of::<Thing>() == 4 * mem::size_of::<usize>());
57+
#[derive(Clone, Copy)]
58+
union PreservePad {
59+
thing: Thing,
60+
bytes: [u8; mem::size_of::<Thing>()],
61+
}
62+
63+
const A: Thing = unsafe { //~ERROR: partial pointer in final value
64+
let mut buffer = [PreservePad { bytes: [0u8; mem::size_of::<Thing>()] }; 2];
65+
// The offset half-way through the padding, so that copying one `Thing` copies exactly
66+
// half the pointer.
67+
let offset = mem::size_of::<Thing>() - mem::size_of::<usize>()/2;
68+
(&raw mut buffer).cast::<&i32>().byte_add(offset).write_unaligned(&1);
69+
buffer[0].thing
70+
};
71+
72+
A
73+
}
74+
4075
fn main() {}

tests/ui/consts/const-eval/ptr_fragments_in_final.stderr

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,13 @@ LL | const MIXED_PTR: MaybeUninit<*const u8> = {
1414
|
1515
= note: while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value
1616

17-
error: aborting due to 2 previous errors
17+
error: encountered partial pointer in final value of constant
18+
--> $DIR/ptr_fragments_in_final.rs:63:5
19+
|
20+
LL | const A: Thing = unsafe {
21+
| ^^^^^^^^^^^^^^
22+
|
23+
= note: while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value
24+
25+
error: aborting due to 3 previous errors
1826

0 commit comments

Comments
 (0)