Rollup merge of #122438 - jswrenn:check-referent-size, r=compiler-errors
Safe Transmute: Require that source referent is smaller than destination `BikeshedIntrinsicFrom` currently models transmute-via-union; i.e., it attempts to provide a `where` bound for this function: ```rust pub unsafe fn transmute_via_union<Src, Dst>(src: Src) -> Dst { use core::mem::*; #[repr(C)] union Transmute<T, U> { src: ManuallyDrop<T>, dst: ManuallyDrop<U>, } let transmute = Transmute { src: ManuallyDrop::new(src) }; // SAFETY: The caller must guarantee that the transmutation is safe. let dst = transmute.dst; ManuallyDrop::into_inner(dst) } ``` A quirk of this model is that it admits padding extensions in value-to-value transmutation: The destination type can be bigger than the source type, so long as the excess consists of uninitialized bytes. However, this isn't permissible for reference-to-reference transmutations (introduced in #110662) — extra referent bytes cannot come from thin air. This PR patches our analysis for reference-to-reference transmutations to require that the destination referent is no larger than the source referent. r? `@compiler-errors`
This commit is contained in:
commit
89c3fa92d4
@ -3091,6 +3091,13 @@ fn get_safe_transmute_error_and_reason(
|
||||
rustc_transmute::Reason::DstIsTooBig => {
|
||||
format!("The size of `{src}` is smaller than the size of `{dst}`")
|
||||
}
|
||||
rustc_transmute::Reason::DstRefIsTooBig { src, dst } => {
|
||||
let src_size = src.size;
|
||||
let dst_size = dst.size;
|
||||
format!(
|
||||
"The referent size of `{src}` ({src_size} bytes) is smaller than that of `{dst}` ({dst_size} bytes)"
|
||||
)
|
||||
}
|
||||
rustc_transmute::Reason::SrcSizeOverflow => {
|
||||
format!(
|
||||
"values of the type `{src}` are too big for the current architecture"
|
||||
|
@ -35,6 +35,8 @@ pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {
|
||||
pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {
|
||||
fn min_align(&self) -> usize;
|
||||
|
||||
fn size(&self) -> usize;
|
||||
|
||||
fn is_mutable(&self) -> bool;
|
||||
}
|
||||
|
||||
@ -48,6 +50,9 @@ impl Ref for ! {
|
||||
fn min_align(&self) -> usize {
|
||||
unreachable!()
|
||||
}
|
||||
fn size(&self) -> usize {
|
||||
unreachable!()
|
||||
}
|
||||
fn is_mutable(&self) -> bool {
|
||||
unreachable!()
|
||||
}
|
||||
@ -57,6 +62,7 @@ fn is_mutable(&self) -> bool {
|
||||
pub mod rustc {
|
||||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
/// A reference in the layout.
|
||||
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
|
||||
@ -65,6 +71,7 @@ pub struct Ref<'tcx> {
|
||||
pub ty: Ty<'tcx>,
|
||||
pub mutability: Mutability,
|
||||
pub align: usize,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
impl<'tcx> super::Ref for Ref<'tcx> {
|
||||
@ -72,6 +79,10 @@ fn min_align(&self) -> usize {
|
||||
self.align
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
|
||||
fn is_mutable(&self) -> bool {
|
||||
match self.mutability {
|
||||
Mutability::Mut => true,
|
||||
@ -81,6 +92,16 @@ fn is_mutable(&self) -> bool {
|
||||
}
|
||||
impl<'tcx> Ref<'tcx> {}
|
||||
|
||||
impl<'tcx> fmt::Display for Ref<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_char('&')?;
|
||||
if self.mutability == Mutability::Mut {
|
||||
f.write_str("mut ")?;
|
||||
}
|
||||
self.ty.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A visibility node in the layout.
|
||||
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum Def<'tcx> {
|
||||
|
@ -372,12 +372,15 @@ pub fn from_ty(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Result<Self, Err> {
|
||||
}
|
||||
|
||||
ty::Ref(lifetime, ty, mutability) => {
|
||||
let align = layout_of(tcx, *ty)?.align();
|
||||
let layout = layout_of(tcx, *ty)?;
|
||||
let align = layout.align();
|
||||
let size = layout.size();
|
||||
Ok(Tree::Ref(Ref {
|
||||
lifetime: *lifetime,
|
||||
ty: *ty,
|
||||
mutability: *mutability,
|
||||
align,
|
||||
size,
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ pub struct Assume {
|
||||
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
|
||||
pub enum Answer<R> {
|
||||
Yes,
|
||||
No(Reason),
|
||||
No(Reason<R>),
|
||||
If(Condition<R>),
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ pub enum Condition<R> {
|
||||
|
||||
/// Answers "why wasn't the source type transmutable into the destination type?"
|
||||
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
|
||||
pub enum Reason {
|
||||
pub enum Reason<T> {
|
||||
/// The layout of the source type is unspecified.
|
||||
SrcIsUnspecified,
|
||||
/// The layout of the destination type is unspecified.
|
||||
@ -53,6 +53,13 @@ pub enum Reason {
|
||||
DstMayHaveSafetyInvariants,
|
||||
/// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
|
||||
DstIsTooBig,
|
||||
/// A referent of `Dst` is larger than a referent in `Src`.
|
||||
DstRefIsTooBig {
|
||||
/// The referent of the source type.
|
||||
src: T,
|
||||
/// The too-large referent of the destination type.
|
||||
dst: T,
|
||||
},
|
||||
/// Src should have a stricter alignment than Dst, but it does not.
|
||||
DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize },
|
||||
/// Can't go from shared pointer to unique pointer
|
||||
|
@ -266,6 +266,11 @@ fn answer_memo(
|
||||
src_min_align: src_ref.min_align(),
|
||||
dst_min_align: dst_ref.min_align(),
|
||||
})
|
||||
} else if dst_ref.size() > src_ref.size() {
|
||||
Answer::No(Reason::DstRefIsTooBig {
|
||||
src: src_ref,
|
||||
dst: dst_ref,
|
||||
})
|
||||
} else {
|
||||
// ...such that `src` is transmutable into `dst`, if
|
||||
// `src_ref` is transmutability into `dst_ref`.
|
||||
|
49
tests/ui/transmutability/references/reject_extension.rs
Normal file
49
tests/ui/transmutability/references/reject_extension.rs
Normal file
@ -0,0 +1,49 @@
|
||||
//@ check-fail
|
||||
|
||||
//! Reject extensions behind references.
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(transmutability)]
|
||||
|
||||
mod assert {
|
||||
use std::mem::{Assume, BikeshedIntrinsicFrom};
|
||||
|
||||
pub fn is_transmutable<Src, Dst>()
|
||||
where
|
||||
Dst: BikeshedIntrinsicFrom<
|
||||
Src,
|
||||
{
|
||||
Assume {
|
||||
alignment: true,
|
||||
lifetimes: true,
|
||||
safety: true,
|
||||
validity: true,
|
||||
}
|
||||
},
|
||||
>,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
struct Packed<T>(T);
|
||||
|
||||
fn reject_extension() {
|
||||
#[repr(C, align(2))]
|
||||
struct Two(u8);
|
||||
|
||||
#[repr(C, align(4))]
|
||||
struct Four(u8);
|
||||
|
||||
// These two types differ in the number of trailing padding bytes they have.
|
||||
type Src = Packed<Two>;
|
||||
type Dst = Packed<Four>;
|
||||
|
||||
const _: () = {
|
||||
use std::mem::size_of;
|
||||
assert!(size_of::<Src>() == 2);
|
||||
assert!(size_of::<Dst>() == 4);
|
||||
};
|
||||
|
||||
assert::is_transmutable::<&Src, &Dst>(); //~ ERROR cannot be safely transmuted
|
||||
}
|
25
tests/ui/transmutability/references/reject_extension.stderr
Normal file
25
tests/ui/transmutability/references/reject_extension.stderr
Normal file
@ -0,0 +1,25 @@
|
||||
error[E0277]: `&Packed<Two>` cannot be safely transmuted into `&Packed<Four>`
|
||||
--> $DIR/reject_extension.rs:48:37
|
||||
|
|
||||
LL | assert::is_transmutable::<&Src, &Dst>();
|
||||
| ^^^^ The referent size of `&Packed<Two>` (2 bytes) is smaller than that of `&Packed<Four>` (4 bytes)
|
||||
|
|
||||
note: required by a bound in `is_transmutable`
|
||||
--> $DIR/reject_extension.rs:13:14
|
||||
|
|
||||
LL | pub fn is_transmutable<Src, Dst>()
|
||||
| --------------- required by a bound in this function
|
||||
LL | where
|
||||
LL | Dst: BikeshedIntrinsicFrom<
|
||||
| ______________^
|
||||
LL | | Src,
|
||||
LL | | {
|
||||
LL | | Assume {
|
||||
... |
|
||||
LL | | },
|
||||
LL | | >,
|
||||
| |_________^ required by this bound in `is_transmutable`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
@ -1,8 +1,8 @@
|
||||
error[E0277]: `Unit` cannot be safely transmuted into `u8`
|
||||
error[E0277]: `&Unit` cannot be safely transmuted into `&u8`
|
||||
--> $DIR/unit-to-u8.rs:22:52
|
||||
|
|
||||
LL | assert::is_maybe_transmutable::<&'static Unit, &'static u8>();
|
||||
| ^^^^^^^^^^^ The size of `Unit` is smaller than the size of `u8`
|
||||
| ^^^^^^^^^^^ The referent size of `&Unit` (0 bytes) is smaller than that of `&u8` (1 bytes)
|
||||
|
|
||||
note: required by a bound in `is_maybe_transmutable`
|
||||
--> $DIR/unit-to-u8.rs:9:14
|
||||
|
Loading…
Reference in New Issue
Block a user