Skip to content

Commit 2a82bf1

Browse files
committed
transmute: fix check for whether newtypes have equal size
1 parent a51f3a8 commit 2a82bf1

6 files changed

Lines changed: 88 additions & 20 deletions

File tree

compiler/rustc_middle/src/ty/layout.rs

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

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

434454
// Get a zero-sized variant or a pointer newtype.
435-
let zero_or_ptr_variant = |i| {
455+
// Returns `Ok(None)` for 1-ZST types, `Ok(Some)` if (ignoring all 1-ZST fields)
456+
// there's just a single pointer, and `Err` otherwise.
457+
let zero_or_ptr_variant = |i| -> Result<Option<SizeSkeleton<'tcx>>, _> {
436458
let i = VariantIdx::from_usize(i);
437459
let fields =
438460
def.variant(i).fields.iter().map(|field| {

tests/ui/transmute/raw-ptr-non-null.rs

Lines changed: 0 additions & 11 deletions
This file was deleted.

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: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![allow(dead_code)]
66

77
use std::mem::transmute;
8+
use std::ptr::NonNull;
89

910
fn a<T, U: ?Sized>(x: &[T]) -> &U {
1011
unsafe { transmute(x) } //~ ERROR cannot transmute between types of different sizes
@@ -15,11 +16,11 @@ fn b<T: ?Sized, U: ?Sized>(x: &T) -> &U {
1516
}
1617

1718
fn c<T, U>(x: &T) -> &U {
18-
unsafe { transmute(x) }
19+
unsafe { transmute(x) } // Ok!
1920
}
2021

2122
fn d<T, U>(x: &[T]) -> &[U] {
22-
unsafe { transmute(x) }
23+
unsafe { transmute(x) } // Ok!
2324
}
2425

2526
fn e<T: ?Sized, U>(x: &T) -> &U {
@@ -30,4 +31,23 @@ fn f<T, U: ?Sized>(x: &T) -> &U {
3031
unsafe { transmute(x) } //~ ERROR cannot transmute between types of different sizes
3132
}
3233

34+
// We can transmute even between pointers of unknown size as long as the metadata of
35+
// input and output type are the same. This even accounts for null pointer optimizations.
36+
fn g1<T: ?Sized>(x: &T) -> Option<&T> {
37+
unsafe { transmute(x) } // Ok!
38+
}
39+
40+
fn g2<T: ?Sized>(x: Option<NonNull<T>>) -> NonNull<T> {
41+
unsafe { transmute(x) } // Ok!
42+
}
43+
44+
fn g3<T: ?Sized>(x: *const T) -> Option<NonNull<T>> {
45+
unsafe { transmute(x) } // Ok!
46+
}
47+
48+
// Make sure we can see through all the layers of `Box`.
49+
fn h<T: ?Sized>(x: Box<T>) -> &'static T {
50+
unsafe { transmute(x) } // Ok!
51+
}
52+
3353
fn main() { }

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
2-
--> $DIR/transmute-fat-pointers.rs:10:14
2+
--> $DIR/transmute-fat-pointers.rs:11:14
33
|
44
LL | unsafe { transmute(x) }
55
| ^^^^^^^^^
@@ -8,7 +8,7 @@ LL | unsafe { transmute(x) }
88
= note: target type: `&U` (pointer to `U`)
99

1010
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
11-
--> $DIR/transmute-fat-pointers.rs:14:14
11+
--> $DIR/transmute-fat-pointers.rs:15:14
1212
|
1313
LL | unsafe { transmute(x) }
1414
| ^^^^^^^^^
@@ -17,7 +17,7 @@ LL | unsafe { transmute(x) }
1717
= note: target type: `&U` (pointer to `U`)
1818

1919
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
20-
--> $DIR/transmute-fat-pointers.rs:26:14
20+
--> $DIR/transmute-fat-pointers.rs:27:14
2121
|
2222
LL | unsafe { transmute(x) }
2323
| ^^^^^^^^^
@@ -26,7 +26,7 @@ LL | unsafe { transmute(x) }
2626
= note: target type: `&U` (N bits)
2727

2828
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
29-
--> $DIR/transmute-fat-pointers.rs:30:14
29+
--> $DIR/transmute-fat-pointers.rs:31:14
3030
|
3131
LL | unsafe { transmute(x) }
3232
| ^^^^^^^^^

0 commit comments

Comments
 (0)