Skip to content

Commit 408547e

Browse files
committed
transmute: fix check for whether newtypes have equal size
1 parent e8e4541 commit 408547e

4 files changed

Lines changed: 68 additions & 5 deletions

File tree

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -431,13 +431,35 @@ impl<'tcx> SizeSkeleton<'tcx> {
431431
}
432432

433433
ty::Adt(def, args) => {
434-
// Only newtypes and enums w/ nullable pointer optimization.
434+
// Only newtypes and enums w/ nullable pointer optimization (NPO).
435435
if def.is_union() || def.variants().is_empty() || def.variants().len() > 2 {
436436
return Err(err);
437437
}
438+
// Only default repr types.
439+
{
440+
// We can ignore the seed and some particular flags that can never affect the
441+
// layout of newtypes / NPO types, but we have to check everything else.
442+
// If you are adding a new field to `ReprOptions`, make sure to extend the check
443+
// below so that we bail out if it is not at its default value!
444+
let ReprOptions { int, align, pack, flags, scalable, field_shuffle_seed: _ } =
445+
def.repr();
446+
let ignored_flags = ReprFlags::IS_TRANSPARENT
447+
| ReprFlags::IS_LINEAR
448+
| ReprFlags::RANDOMIZE_LAYOUT;
449+
if int.is_some()
450+
|| align.is_some()
451+
|| pack.is_some()
452+
|| flags.difference(ignored_flags) != ReprFlags::default()
453+
|| scalable.is_some()
454+
{
455+
return Err(err);
456+
}
457+
}
438458

439459
// Get a zero-sized variant or a pointer newtype.
440-
let zero_or_ptr_variant = |i| {
460+
// Returns `Ok(None)` for 1-ZST types, `Ok(Some)` if (ignoring all 1-ZST fields)
461+
// there's just a single pointer, and `Err` otherwise.
462+
let zero_or_ptr_variant = |i| -> Result<Option<SizeSkeleton<'tcx>>, _> {
441463
let i = VariantIdx::from_usize(i);
442464
let fields =
443465
def.variant(i).fields.iter().map(|field| {

tests/ui/transmute/transmute-different-sizes.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,23 @@ pub unsafe fn shouldnt_work2<T: ?Sized>(from: *mut T) -> PtrAndEmptyArray<T> {
4848
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
4949
}
5050

51+
#[repr(align(16))]
52+
struct OverAlignWrap<T>(T);
53+
54+
fn shouldnt_work3<T: ?Sized>(x: OverAlignWrap<*const T>) -> *const T {
55+
unsafe { transmute(x) }
56+
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
57+
}
58+
59+
#[repr(C)]
60+
enum NotNullPointerOptimized<T> {
61+
Some(T),
62+
None
63+
}
64+
65+
fn shouldnt_work4<T: ?Sized>(x: &T) -> NotNullPointerOptimized<&T> {
66+
unsafe { transmute(x) }
67+
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
68+
}
69+
5170
fn main() {}

tests/ui/transmute/transmute-different-sizes.stderr

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,24 @@ LL | std::mem::transmute(from)
4343
= note: source type: `*mut T` (pointer to `T`)
4444
= note: target type: `PtrAndEmptyArray<T>` (size can vary because of <T as Pointee>::Metadata)
4545

46-
error: aborting due to 5 previous errors
46+
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
47+
--> $DIR/transmute-different-sizes.rs:55:14
48+
|
49+
LL | unsafe { transmute(x) }
50+
| ^^^^^^^^^
51+
|
52+
= note: source type: `OverAlignWrap<*const T>` (size can vary because of <T as Pointee>::Metadata)
53+
= note: target type: `*const T` (pointer to `T`)
54+
55+
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
56+
--> $DIR/transmute-different-sizes.rs:66:14
57+
|
58+
LL | unsafe { transmute(x) }
59+
| ^^^^^^^^^
60+
|
61+
= note: source type: `&T` (pointer to `T`)
62+
= note: target type: `NotNullPointerOptimized<&T>` (size can vary because of <T as Pointee>::Metadata)
63+
64+
error: aborting due to 7 previous errors
4765

4866
For more information about this error, try `rustc --explain E0512`.

tests/ui/transmute/transmute-fat-pointers.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ fn b<T: ?Sized, U: ?Sized>(x: &T) -> &U {
1515
}
1616

1717
fn c<T, U>(x: &T) -> &U {
18-
unsafe { transmute(x) }
18+
unsafe { transmute(x) } // Ok!
1919
}
2020

2121
fn d<T, U>(x: &[T]) -> &[U] {
22-
unsafe { transmute(x) }
22+
unsafe { transmute(x) } // Ok!
2323
}
2424

2525
fn e<T: ?Sized, U>(x: &T) -> &U {
@@ -30,4 +30,8 @@ fn f<T, U: ?Sized>(x: &T) -> &U {
3030
unsafe { transmute(x) } //~ ERROR cannot transmute between types of different sizes
3131
}
3232

33+
fn g<T: ?Sized>(x: Box<T>) -> &'static T {
34+
unsafe { transmute(x) } // Ok!
35+
}
36+
3337
fn main() { }

0 commit comments

Comments
 (0)