Compute transmutability from rustc_target::abi::Layout

In its first step of computing transmutability, `rustc_transmutability`
constructs a byte-level representation of type layout (`Tree`). Previously, this
representation was computed for ADTs by inspecting the ADT definition and
performing our own layout computations. This process was error-prone, verbose,
and limited our ability to analyze many types (particularly default-repr types).

In this PR, we instead construct `Tree`s from `rustc_target::abi::Layout`s. This
helps ensure that layout optimizations are reflected our analyses, and increases
the kinds of types we can now analyze, including:
- default repr ADTs
- transparent unions
- `UnsafeCell`-containing types

Overall, this PR expands the expressvity of `rustc_transmutability` to be much
closer to the transmutability analysis performed by miri. Future PRs will work
to close the remaining gaps (e.g., support for `Box`, raw pointers, `NonZero*`,
coroutines, etc.).
This commit is contained in:
Jack Wrenn 2024-03-19 14:49:13 +00:00
parent d6eb0f5a09
commit 3aa14e3b2e
32 changed files with 905 additions and 789 deletions

View File

@ -40,7 +40,7 @@ pub struct ImplCandidate<'tcx> {
enum GetSafeTransmuteErrorAndReason {
Silent,
Error { err_msg: String, safe_transmute_explanation: String },
Error { err_msg: String, safe_transmute_explanation: Option<String> },
}
struct UnsatisfiedConst(pub bool);

View File

@ -557,7 +557,7 @@ fn report_selection_error(
GetSafeTransmuteErrorAndReason::Error {
err_msg,
safe_transmute_explanation,
} => (err_msg, Some(safe_transmute_explanation)),
} => (err_msg, safe_transmute_explanation),
}
} else {
(err_msg, None)
@ -3061,28 +3061,33 @@ fn get_safe_transmute_error_and_reason(
return GetSafeTransmuteErrorAndReason::Silent;
};
let dst = trait_ref.args.type_at(0);
let src = trait_ref.args.type_at(1);
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
obligation.cause,
src_and_dst,
assume,
) {
Answer::No(reason) => {
let dst = trait_ref.args.type_at(0);
let src = trait_ref.args.type_at(1);
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
let safe_transmute_explanation = match reason {
rustc_transmute::Reason::SrcIsNotYetSupported => {
format!("analyzing the transmutability of `{src}` is not yet supported.")
format!("analyzing the transmutability of `{src}` is not yet supported")
}
rustc_transmute::Reason::DstIsNotYetSupported => {
format!("analyzing the transmutability of `{dst}` is not yet supported.")
format!("analyzing the transmutability of `{dst}` is not yet supported")
}
rustc_transmute::Reason::DstIsBitIncompatible => {
format!("at least one value of `{src}` isn't a bit-valid value of `{dst}`")
}
rustc_transmute::Reason::DstUninhabited => {
format!("`{dst}` is uninhabited")
}
rustc_transmute::Reason::DstMayHaveSafetyInvariants => {
format!("`{dst}` may carry safety invariants")
}
@ -3128,14 +3133,23 @@ fn get_safe_transmute_error_and_reason(
format!("`{dst}` has an unknown layout")
}
};
GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation }
GetSafeTransmuteErrorAndReason::Error {
err_msg,
safe_transmute_explanation: Some(safe_transmute_explanation),
}
}
// Should never get a Yes at this point! We already ran it before, and did not get a Yes.
Answer::Yes => span_bug!(
span,
"Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
),
other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"),
// Reached when a different obligation (namely `Freeze`) causes the
// transmutability analysis to fail. In this case, silence the
// transmutability error message in favor of that more specific
// error.
Answer::If(_) => {
GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation: None }
}
}
}

View File

@ -314,12 +314,12 @@ fn flatten_answer_tree<'tcx>(
.flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond))
.collect(),
Condition::IfTransmutable { src, dst } => {
let trait_def_id = obligation.predicate.def_id();
let transmute_trait = obligation.predicate.def_id();
let assume_const = predicate.trait_ref.args.const_at(2);
let make_obl = |from_ty, to_ty| {
let trait_ref1 = ty::TraitRef::new(
let make_transmute_obl = |from_ty, to_ty| {
let trait_ref = ty::TraitRef::new(
tcx,
trait_def_id,
transmute_trait,
[
ty::GenericArg::from(to_ty),
ty::GenericArg::from(from_ty),
@ -331,17 +331,45 @@ fn flatten_answer_tree<'tcx>(
obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
trait_ref1,
trait_ref,
)
};
let make_freeze_obl = |ty| {
let trait_ref = ty::TraitRef::new(
tcx,
tcx.lang_items().freeze_trait().unwrap(),
[ty::GenericArg::from(ty)],
);
Obligation::with_depth(
tcx,
obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
trait_ref,
)
};
let mut obls = vec![];
// If the source is a shared reference, it must be `Freeze`;
// otherwise, transmuting could lead to data races.
if src.mutability == Mutability::Not {
obls.extend([make_freeze_obl(src.ty), make_freeze_obl(dst.ty)])
}
// If Dst is mutable, check bidirectionally.
// For example, transmuting bool -> u8 is OK as long as you can't update that u8
// to be > 1, because you could later transmute the u8 back to a bool and get UB.
match dst.mutability {
Mutability::Not => vec![make_obl(src.ty, dst.ty)],
Mutability::Mut => vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)],
Mutability::Not => obls.push(make_transmute_obl(src.ty, dst.ty)),
Mutability::Mut => obls.extend([
make_transmute_obl(src.ty, dst.ty),
make_transmute_obl(dst.ty, src.ty),
]),
}
obls
}
}
}

View File

@ -35,6 +35,7 @@ impl<R> Transitions<R>
where
R: Ref,
{
#[allow(dead_code)]
fn insert(&mut self, transition: Transition<R>, state: State) {
match transition {
Transition::Byte(b) => {
@ -82,6 +83,7 @@ impl<R> Dfa<R>
where
R: Ref,
{
#[allow(dead_code)]
pub(crate) fn unit() -> Self {
let transitions: Map<State, Transitions<R>> = Map::default();
let start = State::new();

View File

@ -160,6 +160,7 @@ pub(crate) fn union(self, other: Self) -> Self {
Self { transitions, start, accepting }
}
#[allow(dead_code)]
pub(crate) fn edges_from(&self, start: State) -> Option<&Map<Transition<R>, Set<State>>> {
self.transitions.get(&start)
}

View File

@ -173,16 +173,20 @@ pub(crate) mod rustc {
use super::Tree;
use crate::layout::rustc::{Def, Ref};
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::layout::LayoutCx;
use rustc_middle::ty::layout::LayoutError;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::AdtDef;
use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::ParamEnv;
use rustc_middle::ty::AdtKind;
use rustc_middle::ty::List;
use rustc_middle::ty::ScalarInt;
use rustc_middle::ty::VariantDef;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::ErrorGuaranteed;
use rustc_target::abi::Align;
use std::alloc;
use rustc_target::abi::FieldsShape;
use rustc_target::abi::Size;
use rustc_target::abi::TyAndLayout;
use rustc_target::abi::Variants;
#[derive(Debug, Copy, Clone)]
pub(crate) enum Err {
@ -206,176 +210,64 @@ fn from(err: &LayoutError<'tcx>) -> Self {
}
}
trait LayoutExt {
fn clamp_align(&self, min_align: Align, max_align: Align) -> Self;
}
impl LayoutExt for alloc::Layout {
fn clamp_align(&self, min_align: Align, max_align: Align) -> Self {
let min_align = min_align.bytes().try_into().unwrap();
let max_align = max_align.bytes().try_into().unwrap();
Self::from_size_align(self.size(), self.align().clamp(min_align, max_align)).unwrap()
}
}
struct LayoutSummary {
total_align: Align,
total_size: usize,
discriminant_size: usize,
discriminant_align: Align,
}
impl LayoutSummary {
fn from_ty<'tcx>(ty: Ty<'tcx>, ctx: TyCtxt<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
use rustc_middle::ty::ParamEnvAnd;
use rustc_target::abi::{TyAndLayout, Variants};
let param_env = ParamEnv::reveal_all();
let param_env_and_type = ParamEnvAnd { param_env, value: ty };
let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?;
let total_size: usize = layout.size().bytes_usize();
let total_align: Align = layout.align().abi;
let discriminant_align: Align;
let discriminant_size: usize;
if let Variants::Multiple { tag, .. } = layout.variants() {
discriminant_align = tag.align(&ctx).abi;
discriminant_size = tag.size(&ctx).bytes_usize();
} else {
discriminant_align = Align::ONE;
discriminant_size = 0;
};
Ok(Self { total_align, total_size, discriminant_align, discriminant_size })
}
fn into(&self) -> alloc::Layout {
alloc::Layout::from_size_align(
self.total_size,
self.total_align.bytes().try_into().unwrap(),
)
.unwrap()
}
}
impl<'tcx> Tree<Def<'tcx>, Ref<'tcx>> {
pub fn from_ty(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Result<Self, Err> {
use rustc_middle::ty::FloatTy::*;
use rustc_middle::ty::IntTy::*;
use rustc_middle::ty::UintTy::*;
pub fn from_ty(
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
) -> Result<Self, Err> {
use rustc_target::abi::HasDataLayout;
if let Err(e) = ty.error_reported() {
if let Err(e) = ty_and_layout.ty.error_reported() {
return Err(Err::TypeError(e));
}
let target = tcx.data_layout();
let target = cx.tcx.data_layout();
let pointer_size = target.pointer_size;
match ty.kind() {
match ty_and_layout.ty.kind() {
ty::Bool => Ok(Self::bool()),
ty::Int(I8) | ty::Uint(U8) => Ok(Self::u8()),
ty::Int(I16) | ty::Uint(U16) => Ok(Self::number(2)),
ty::Int(I32) | ty::Uint(U32) | ty::Float(F32) => Ok(Self::number(4)),
ty::Int(I64) | ty::Uint(U64) | ty::Float(F64) => Ok(Self::number(8)),
ty::Int(I128) | ty::Uint(U128) => Ok(Self::number(16)),
ty::Int(Isize) | ty::Uint(Usize) => {
Ok(Self::number(target.pointer_size.bytes_usize()))
ty::Float(nty) => {
let width = nty.bit_width() / 8;
Ok(Self::number(width as _))
}
ty::Tuple(members) => {
if members.len() == 0 {
Ok(Tree::unit())
} else {
Err(Err::NotYetSupported)
}
ty::Int(nty) => {
let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8;
Ok(Self::number(width as _))
}
ty::Array(ty, len) => {
let len = len
.try_eval_target_usize(tcx, ParamEnv::reveal_all())
.ok_or(Err::NotYetSupported)?;
let elt = Tree::from_ty(*ty, tcx)?;
ty::Uint(nty) => {
let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8;
Ok(Self::number(width as _))
}
ty::Tuple(members) => Self::from_tuple(ty_and_layout, members, cx),
ty::Array(inner_ty, len) => {
let FieldsShape::Array { stride, count } = &ty_and_layout.fields else {
return Err(Err::NotYetSupported);
};
let inner_ty_and_layout = cx.layout_of(*inner_ty)?;
assert_eq!(*stride, inner_ty_and_layout.size);
let elt = Tree::from_ty(inner_ty_and_layout, cx)?;
Ok(std::iter::repeat(elt)
.take(len as usize)
.take(*count as usize)
.fold(Tree::unit(), |tree, elt| tree.then(elt)))
}
ty::Adt(adt_def, args_ref) => {
use rustc_middle::ty::AdtKind;
// If the layout is ill-specified, halt.
if !(adt_def.repr().c() || adt_def.repr().int.is_some()) {
return Err(Err::NotYetSupported);
ty::Adt(adt_def, _args_ref) if !ty_and_layout.ty.is_box() => {
match adt_def.adt_kind() {
AdtKind::Struct => Self::from_struct(ty_and_layout, *adt_def, cx),
AdtKind::Enum => Self::from_enum(ty_and_layout, *adt_def, cx),
AdtKind::Union => Self::from_union(ty_and_layout, *adt_def, cx),
}
// Compute a summary of the type's layout.
let layout_summary = LayoutSummary::from_ty(ty, tcx)?;
// The layout begins with this adt's visibility.
let vis = Self::def(Def::Adt(*adt_def));
// And is followed the layout(s) of its variants
Ok(vis.then(match adt_def.adt_kind() {
AdtKind::Struct => Self::from_repr_c_variant(
ty,
*adt_def,
args_ref,
&layout_summary,
None,
adt_def.non_enum_variant(),
tcx,
)?,
AdtKind::Enum => {
trace!(?adt_def, "treeifying enum");
let mut tree = Tree::uninhabited();
for (idx, variant) in adt_def.variants().iter_enumerated() {
let tag = tcx.tag_for_variant((ty, idx));
tree = tree.or(Self::from_repr_c_variant(
ty,
*adt_def,
args_ref,
&layout_summary,
tag,
variant,
tcx,
)?);
}
tree
}
AdtKind::Union => {
// is the layout well-defined?
if !adt_def.repr().c() {
return Err(Err::NotYetSupported);
}
let ty_layout = layout_of(tcx, ty)?;
let mut tree = Tree::uninhabited();
for field in adt_def.all_fields() {
let variant_ty = field.ty(tcx, args_ref);
let variant_layout = layout_of(tcx, variant_ty)?;
let padding_needed = ty_layout.size() - variant_layout.size();
let variant = Self::def(Def::Field(field))
.then(Self::from_ty(variant_ty, tcx)?)
.then(Self::padding(padding_needed));
tree = tree.or(variant);
}
tree
}
}))
}
ty::Ref(lifetime, ty, mutability) => {
let layout = layout_of(tcx, *ty)?;
let align = layout.align();
let size = layout.size();
let ty_and_layout = cx.layout_of(*ty)?;
let align = ty_and_layout.align.abi.bytes() as usize;
let size = ty_and_layout.size.bytes_usize();
Ok(Tree::Ref(Ref {
lifetime: *lifetime,
ty: *ty,
@ -389,80 +281,143 @@ pub fn from_ty(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Result<Self, Err> {
}
}
fn from_repr_c_variant(
ty: Ty<'tcx>,
adt_def: AdtDef<'tcx>,
args_ref: GenericArgsRef<'tcx>,
layout_summary: &LayoutSummary,
tag: Option<ScalarInt>,
variant_def: &'tcx VariantDef,
tcx: TyCtxt<'tcx>,
/// Constructs a `Tree` from a tuple.
fn from_tuple(
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
members: &'tcx List<Ty<'tcx>>,
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
) -> Result<Self, Err> {
let mut tree = Tree::unit();
let repr = adt_def.repr();
let min_align = repr.align.unwrap_or(Align::ONE);
let max_align = repr.pack.unwrap_or(Align::MAX);
let variant_span = trace_span!(
"treeifying variant",
min_align = ?min_align,
max_align = ?max_align,
)
.entered();
let mut variant_layout = alloc::Layout::from_size_align(
0,
layout_summary.total_align.bytes().try_into().unwrap(),
)
.unwrap();
// The layout of the variant is prefixed by the tag, if any.
if let Some(tag) = tag {
let tag_layout =
alloc::Layout::from_size_align(tag.size().bytes_usize(), 1).unwrap();
tree = tree.then(Self::from_tag(tag, tcx));
variant_layout = variant_layout.extend(tag_layout).unwrap().0;
}
// Next come fields.
let fields_span = trace_span!("treeifying fields").entered();
for field_def in variant_def.fields.iter() {
let field_ty = field_def.ty(tcx, args_ref);
let _span = trace_span!("treeifying field", field = ?field_ty).entered();
// begin with the field's visibility
tree = tree.then(Self::def(Def::Field(field_def)));
// compute the field's layout characteristics
let field_layout = layout_of(tcx, field_ty)?.clamp_align(min_align, max_align);
// next comes the field's padding
let padding_needed = variant_layout.padding_needed_for(field_layout.align());
if padding_needed > 0 {
tree = tree.then(Self::padding(padding_needed));
match &ty_and_layout.fields {
FieldsShape::Primitive => {
assert_eq!(members.len(), 1);
let inner_ty = members[0];
let inner_ty_and_layout = cx.layout_of(inner_ty)?;
assert_eq!(ty_and_layout.layout, inner_ty_and_layout.layout);
Self::from_ty(inner_ty_and_layout, cx)
}
// finally, the field's layout
tree = tree.then(Self::from_ty(field_ty, tcx)?);
// extend the variant layout with the field layout
variant_layout = variant_layout.extend(field_layout).unwrap().0;
FieldsShape::Arbitrary { offsets, .. } => {
assert_eq!(offsets.len(), members.len());
Self::from_variant(Def::Primitive, None, ty_and_layout, ty_and_layout.size, cx)
}
FieldsShape::Array { .. } | FieldsShape::Union(_) => Err(Err::NotYetSupported),
}
drop(fields_span);
// finally: padding
let padding_span = trace_span!("adding trailing padding").entered();
if layout_summary.total_size > variant_layout.size() {
let padding_needed = layout_summary.total_size - variant_layout.size();
tree = tree.then(Self::padding(padding_needed));
};
drop(padding_span);
drop(variant_span);
Ok(tree)
}
pub fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self {
/// Constructs a `Tree` from a struct.
///
/// # Panics
///
/// Panics if `def` is not a struct definition.
fn from_struct(
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
def: AdtDef<'tcx>,
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
) -> Result<Self, Err> {
assert!(def.is_struct());
let def = Def::Adt(def);
Self::from_variant(def, None, ty_and_layout, ty_and_layout.size, cx)
}
/// Constructs a `Tree` from an enum.
///
/// # Panics
///
/// Panics if `def` is not an enum definition.
fn from_enum(
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
def: AdtDef<'tcx>,
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
) -> Result<Self, Err> {
assert!(def.is_enum());
let layout = ty_and_layout.layout;
if let Variants::Multiple { tag_field, .. } = layout.variants() {
// For enums (but not coroutines), the tag field is
// currently always the first field of the layout.
assert_eq!(*tag_field, 0);
}
let variants = def.discriminants(cx.tcx()).try_fold(
Self::uninhabited(),
|variants, (idx, ref discriminant)| {
let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, idx));
let variant_def = Def::Variant(def.variant(idx));
let variant_ty_and_layout = ty_and_layout.for_variant(&cx, idx);
let variant = Self::from_variant(
variant_def,
tag,
variant_ty_and_layout,
layout.size,
cx,
)?;
Result::<Self, Err>::Ok(variants.or(variant))
},
)?;
return Ok(Self::def(Def::Adt(def)).then(variants));
}
/// Constructs a `Tree` from a 'variant-like' layout.
///
/// A 'variant-like' layout includes those of structs and, of course,
/// enum variants. Pragmatically speaking, this method supports anything
/// with `FieldsShape::Arbitrary`.
///
/// Note: This routine assumes that the optional `tag` is the first
/// field, and enum callers should check that `tag_field` is, in fact,
/// `0`.
fn from_variant(
def: Def<'tcx>,
tag: Option<ScalarInt>,
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
total_size: Size,
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
) -> Result<Self, Err> {
// This constructor does not support non-`FieldsShape::Arbitrary`
// layouts.
let FieldsShape::Arbitrary { offsets, memory_index } = ty_and_layout.layout.fields()
else {
return Err(Err::NotYetSupported);
};
// When this function is invoked with enum variants,
// `ty_and_layout.size` does not encompass the entire size of the
// enum. We rely on `total_size` for this.
assert!(ty_and_layout.size <= total_size);
let mut size = Size::ZERO;
let mut struct_tree = Self::def(def);
// If a `tag` is provided, place it at the start of the layout.
if let Some(tag) = tag {
size += tag.size();
struct_tree = struct_tree.then(Self::from_tag(tag, cx.tcx));
}
// Append the fields, in memory order, to the layout.
let inverse_memory_index = memory_index.invert_bijective_mapping();
for (memory_idx, field_idx) in inverse_memory_index.iter_enumerated() {
// Add interfield padding.
let padding_needed = offsets[*field_idx] - size;
let padding = Self::padding(padding_needed.bytes_usize());
let field_ty_and_layout = ty_and_layout.field(&cx, field_idx.as_usize());
let field_tree = Self::from_ty(field_ty_and_layout, cx)?;
struct_tree = struct_tree.then(padding).then(field_tree);
size += padding_needed + field_ty_and_layout.size;
}
// Add trailing padding.
let padding_needed = total_size - size;
let trailing_padding = Self::padding(padding_needed.bytes_usize());
Ok(struct_tree.then(trailing_padding))
}
/// Constructs a `Tree` representing the value of a enum tag.
fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self {
use rustc_target::abi::Endian;
let size = tag.size();
let bits = tag.to_bits(size).unwrap();
@ -479,24 +434,42 @@ pub fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self {
};
Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect())
}
}
fn layout_of<'tcx>(
ctx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
) -> Result<alloc::Layout, &'tcx LayoutError<'tcx>> {
use rustc_middle::ty::ParamEnvAnd;
use rustc_target::abi::TyAndLayout;
/// Constructs a `Tree` from a union.
///
/// # Panics
///
/// Panics if `def` is not a union definition.
fn from_union(
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
def: AdtDef<'tcx>,
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
) -> Result<Self, Err> {
assert!(def.is_union());
let param_env = ParamEnv::reveal_all();
let param_env_and_type = ParamEnvAnd { param_env, value: ty };
let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?;
let layout = alloc::Layout::from_size_align(
layout.size().bytes_usize(),
layout.align().abi.bytes().try_into().unwrap(),
)
.unwrap();
trace!(?ty, ?layout, "computed layout for type");
Ok(layout)
let union_layout = ty_and_layout.layout;
// This constructor does not support non-`FieldsShape::Union`
// layouts. Fields of this shape are all placed at offset 0.
let FieldsShape::Union(fields) = union_layout.fields() else {
return Err(Err::NotYetSupported);
};
let fields = &def.non_enum_variant().fields;
let fields = fields.iter_enumerated().try_fold(
Self::uninhabited(),
|fields, (idx, ref field_def)| {
let field_def = Def::Field(field_def);
let field_ty_and_layout = ty_and_layout.field(&cx, idx.as_usize());
let field = Self::from_ty(field_ty_and_layout, cx)?;
let trailing_padding_needed = union_layout.size - field_ty_and_layout.size;
let trailing_padding = Self::padding(trailing_padding_needed.bytes_usize());
let field_and_padding = field.then(trailing_padding);
Result::<Self, Err>::Ok(fields.or(field_and_padding))
},
)?;
Ok(Self::def(Def::Adt(def)).then(fields))
}
}
}

View File

@ -1,6 +1,6 @@
#![feature(alloc_layout_extra)]
#![feature(never_type)]
#![allow(dead_code, unused_variables)]
#![allow(unused_variables)]
#[macro_use]
extern crate tracing;
@ -49,6 +49,8 @@ pub enum Reason<T> {
DstIsNotYetSupported,
/// The layout of the destination type is bit-incompatible with the source type.
DstIsBitIncompatible,
/// The destination type is uninhabited.
DstUninhabited,
/// The destination type may carry safety invariants.
DstMayHaveSafetyInvariants,
/// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.

View File

@ -33,6 +33,9 @@ mod rustc {
use super::*;
use crate::layout::tree::rustc::Err;
use rustc_middle::ty::layout::LayoutCx;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::ParamEnv;
use rustc_middle::ty::Ty;
use rustc_middle::ty::TyCtxt;
@ -43,12 +46,20 @@ impl<'tcx> MaybeTransmutableQuery<Ty<'tcx>, TyCtxt<'tcx>> {
pub fn answer(self) -> Answer<<TyCtxt<'tcx> as QueryContext>::Ref> {
let Self { src, dst, assume, context } = self;
let layout_cx = LayoutCx { tcx: context, param_env: ParamEnv::reveal_all() };
let layout_of = |ty| {
layout_cx
.layout_of(ty)
.map_err(|_| Err::NotYetSupported)
.and_then(|tl| Tree::from_ty(tl, layout_cx))
};
// Convert `src` and `dst` from their rustc representations, to `Tree`-based
// representations. If these conversions fail, conclude that the transmutation is
// unacceptable; the layouts of both the source and destination types must be
// well-defined.
let src = Tree::from_ty(src, context);
let dst = Tree::from_ty(dst, context);
let src = layout_of(src);
let dst = layout_of(dst);
match (src, dst) {
(Err(Err::TypeError(_)), _) | (_, Err(Err::TypeError(_))) => {
@ -86,6 +97,10 @@ pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
// references.
let src = src.prune(&|def| false);
if src.is_inhabited() && !dst.is_inhabited() {
return Answer::No(Reason::DstUninhabited);
}
trace!(?src, "pruned src");
// Remove all `Def` nodes from `dst`, additionally...

View File

@ -5,8 +5,6 @@ pub(crate) trait QueryContext {
type Def: layout::Def;
type Ref: layout::Ref;
type Scope: Copy;
fn min_align(&self, reference: Self::Ref) -> usize;
}
#[cfg(test)]
@ -31,10 +29,6 @@ impl QueryContext for UltraMinimal {
type Def = Def;
type Ref = !;
type Scope = ();
fn min_align(&self, reference: !) -> usize {
unimplemented!()
}
}
}
@ -48,9 +42,5 @@ impl<'tcx> super::QueryContext for TyCtxt<'tcx> {
type Ref = layout::rustc::Ref<'tcx>;
type Scope = Ty<'tcx>;
fn min_align(&self, reference: Self::Ref) -> usize {
unimplemented!()
}
}
}

View File

@ -2,7 +2,7 @@ error[E0277]: `()` cannot be safely transmuted into `ExplicitlyPadded`
--> $DIR/huge-len.rs:21:41
|
LL | assert::is_maybe_transmutable::<(), ExplicitlyPadded>();
| ^^^^^^^^^^^^^^^^ values of the type `ExplicitlyPadded` are too big for the current architecture
| ^^^^^^^^^^^^^^^^ analyzing the transmutability of `ExplicitlyPadded` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/huge-len.rs:8:14
@ -17,7 +17,7 @@ error[E0277]: `ExplicitlyPadded` cannot be safely transmuted into `()`
--> $DIR/huge-len.rs:24:55
|
LL | assert::is_maybe_transmutable::<ExplicitlyPadded, ()>();
| ^^ values of the type `ExplicitlyPadded` are too big for the current architecture
| ^^ analyzing the transmutability of `ExplicitlyPadded` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/huge-len.rs:8:14

View File

@ -2,7 +2,7 @@ error[E0277]: `[String; 0]` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:25:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `[String; 0]` is not yet supported.
| ^^ analyzing the transmutability of `[String; 0]` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
@ -23,7 +23,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 0]`
--> $DIR/should_require_well_defined_layout.rs:26:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `[String; 0]` is not yet supported.
| ^^^^^^^^^ analyzing the transmutability of `[String; 0]` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
@ -44,7 +44,7 @@ error[E0277]: `[String; 1]` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:31:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `[String; 1]` is not yet supported.
| ^^ analyzing the transmutability of `[String; 1]` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
@ -65,7 +65,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 1]`
--> $DIR/should_require_well_defined_layout.rs:32:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `[String; 1]` is not yet supported.
| ^^^^^^^^^ analyzing the transmutability of `[String; 1]` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
@ -86,7 +86,7 @@ error[E0277]: `[String; 2]` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:37:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `[String; 2]` is not yet supported.
| ^^ analyzing the transmutability of `[String; 2]` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
@ -107,7 +107,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 2]`
--> $DIR/should_require_well_defined_layout.rs:38:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `[String; 2]` is not yet supported.
| ^^^^^^^^^ analyzing the transmutability of `[String; 2]` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14

View File

@ -0,0 +1,156 @@
//@ check-pass
//! Checks that niche optimizations are encoded correctly.
#![crate_type = "lib"]
#![feature(transmutability)]
#![allow(dead_code, incomplete_features, non_camel_case_types)]
mod assert {
use std::mem::{Assume, BikeshedIntrinsicFrom};
pub fn is_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, {
Assume {
alignment: false,
lifetimes: false,
safety: true,
validity: false,
}
}>
{}
pub fn is_maybe_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, {
Assume {
alignment: false,
lifetimes: false,
safety: true,
validity: true,
}
}>
{}
}
#[repr(u8)] enum V0 { V = 0 }
#[repr(u8)] enum V1 { V = 1 }
#[repr(u8)] enum V2 { V = 2 }
#[repr(u8)] enum V253 { V = 253 }
#[repr(u8)] enum V254 { V = 254 }
#[repr(u8)] enum V255 { V = 255 }
fn bool() {
enum OptionLike {
A(bool),
B,
}
const _: () = {
assert!(std::mem::size_of::<OptionLike>() == 1);
};
assert::is_transmutable::<OptionLike, u8>();
assert::is_transmutable::<bool, OptionLike>();
assert::is_transmutable::<V0, OptionLike>();
assert::is_transmutable::<V1, OptionLike>();
assert::is_transmutable::<V2, OptionLike>();
}
fn one_niche() {
#[repr(u8)]
enum N1 {
S = 0,
E = 255 - 1,
}
enum OptionLike {
A(N1),
B,
}
const _: () = {
assert!(std::mem::size_of::<OptionLike>() == 1);
};
assert::is_transmutable::<OptionLike, u8>();
assert::is_transmutable::<V0, OptionLike>();
assert::is_transmutable::<V254, OptionLike>();
assert::is_transmutable::<V255, OptionLike>();
}
fn one_niche_alt() {
#[repr(u8)]
enum N1 {
S = 1,
E = 255 - 1,
}
enum OptionLike {
A(N1),
B,
C,
}
const _: () = {
assert!(std::mem::size_of::<OptionLike>() == 1);
};
assert::is_transmutable::<OptionLike, u8>();
assert::is_transmutable::<V0, OptionLike>();
assert::is_transmutable::<V254, OptionLike>();
assert::is_transmutable::<V255, OptionLike>();
}
fn two_niche() {
#[repr(u8)]
enum Niche {
S = 0,
E = 255 - 2,
}
enum OptionLike {
A(Niche),
B,
C,
}
const _: () = {
assert!(std::mem::size_of::<OptionLike>() == 1);
};
assert::is_transmutable::<OptionLike, u8>();
assert::is_transmutable::<V0, OptionLike>();
assert::is_transmutable::<V253, OptionLike>();
assert::is_transmutable::<V254, OptionLike>();
assert::is_transmutable::<V255, OptionLike>();
}
fn no_niche() {
use std::mem::MaybeUninit;
#[repr(u8)]
enum Niche {
S = 0,
E = 255 - 1,
}
enum OptionLike {
A(Niche),
B,
C,
}
const _: () = {
assert!(std::mem::size_of::<OptionLike>() == 2);
};
#[repr(C)]
struct Pair<T, U>(T, U);
assert::is_transmutable::<V0, Niche>();
assert::is_transmutable::<V254, Niche>();
assert::is_transmutable::<Pair<V0, Niche>, OptionLike>();
assert::is_transmutable::<Pair<V1, MaybeUninit<u8>>, OptionLike>();
assert::is_transmutable::<Pair<V2, MaybeUninit<u8>>, OptionLike>();
}

View File

@ -0,0 +1,80 @@
//@ check-pass
//! Adapted from https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html#explicit-repr-annotation-without-c-compatibility
#![crate_type = "lib"]
#![feature(transmutability)]
#![allow(dead_code, incomplete_features, non_camel_case_types)]
use std::mem::MaybeUninit;
mod assert {
use std::mem::{Assume, BikeshedIntrinsicFrom};
pub fn is_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, {
Assume {
alignment: false,
lifetimes: false,
safety: true,
validity: false,
}
}>
{}
pub fn is_maybe_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, {
Assume {
alignment: false,
lifetimes: false,
safety: true,
validity: true,
}
}>
{}
}
#[repr(u8)] enum V0 { V = 0 }
#[repr(u8)] enum V1 { V = 1 }
fn repr_u8() {
#[repr(u8)]
enum TwoCases {
A(u8, u16), // 0x00 INIT INIT INIT
B(u16), // 0x01 PADD INIT INIT
}
const _: () = {
assert!(std::mem::size_of::<TwoCases>() == 4);
};
#[repr(C)] struct TwoCasesA(V0, u8, u8, u8);
#[repr(C)] struct TwoCasesB(V1, MaybeUninit<u8>, u8, u8);
assert::is_transmutable::<TwoCasesA, TwoCases>();
assert::is_transmutable::<TwoCasesB, TwoCases>();
assert::is_maybe_transmutable::<TwoCases, TwoCasesA>();
assert::is_maybe_transmutable::<TwoCases, TwoCasesB>();
}
fn repr_c_u8() {
#[repr(C, u8)]
enum TwoCases {
A(u8, u16), // 0x00 PADD INIT PADD INIT INIT
B(u16), // 0x01 PADD INIT INIT PADD PADD
}
const _: () = {
assert!(std::mem::size_of::<TwoCases>() == 6);
};
#[repr(C)] struct TwoCasesA(V0, MaybeUninit<u8>, u8, MaybeUninit<u8>, u8, u8);
#[repr(C)] struct TwoCasesB(V1, MaybeUninit<u8>, u8, u8, MaybeUninit<u8>, MaybeUninit<u8>);
assert::is_transmutable::<TwoCasesA, TwoCases>();
assert::is_transmutable::<TwoCasesB, TwoCases>();
assert::is_maybe_transmutable::<TwoCases, TwoCasesA>();
assert::is_maybe_transmutable::<TwoCases, TwoCasesB>();
}

View File

@ -1,5 +1,4 @@
//! An enum must have a well-defined layout to participate in a transmutation.
//@ check-pass
#![crate_type = "lib"]
#![feature(repr128)]
#![feature(transmutability)]
@ -21,23 +20,17 @@ pub fn is_maybe_transmutable<Src, Dst>()
{}
}
fn should_reject_repr_rust() {
fn void() {
enum repr_rust {}
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
}
fn should_accept_repr_rust() {
fn singleton() {
enum repr_rust { V }
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn duplex() {
enum repr_rust { A, B }
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
}
@ -116,7 +109,7 @@ fn should_accept_repr_usize() {
}
}
fn should_accept_repr_C() {
fn should_accept_repr_c() {
#[repr(C)] enum repr_c { V }
assert::is_maybe_transmutable::<repr_c, ()>();
assert::is_maybe_transmutable::<i128, repr_c>();

View File

@ -1,135 +0,0 @@
error[E0277]: `void::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:27:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `void::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:13:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `void::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:28:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `void::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:13:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `singleton::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:33:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `singleton::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:13:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `singleton::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:34:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `singleton::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:13:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `duplex::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:39:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `duplex::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:13:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `duplex::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:40:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `duplex::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:13:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -8,7 +8,7 @@ error[E0277]: `Src` cannot be safely transmuted into `Dst`
--> $DIR/unknown_src_field.rs:19:36
|
LL | assert::is_transmutable::<Src, Dst>();
| ^^^ `Dst` has an unknown layout
| ^^^ analyzing the transmutability of `Dst` is not yet supported
|
note: required by a bound in `is_transmutable`
--> $DIR/unknown_src_field.rs:12:14

View File

@ -0,0 +1,43 @@
#![crate_type = "lib"]
#![feature(transmutability)]
#![allow(dead_code, incomplete_features, non_camel_case_types)]
use std::mem::MaybeUninit;
mod assert {
use std::mem::{Assume, BikeshedIntrinsicFrom};
pub fn is_maybe_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
{}
}
fn validity() {
// An initialized byte is a valid uninitialized byte.
assert::is_maybe_transmutable::<u8, MaybeUninit<u8>>();
// An uninitialized byte is never a valid initialized byte.
assert::is_maybe_transmutable::<MaybeUninit<u8>, u8>(); //~ ERROR: cannot be safely transmuted
}
fn padding() {
#[repr(align(8))]
struct Align8;
#[repr(u8)]
enum ImplicitlyPadded {
A(Align8),
}
#[repr(u8)]
enum V0 {
V0 = 0,
}
#[repr(C)]
struct ExplicitlyPadded(V0, MaybeUninit<[u8; 7]>);
assert::is_maybe_transmutable::<ExplicitlyPadded, ImplicitlyPadded>();
assert::is_maybe_transmutable::<ImplicitlyPadded, ExplicitlyPadded>();
}

View File

@ -0,0 +1,18 @@
error[E0277]: `MaybeUninit<u8>` cannot be safely transmuted into `u8`
--> $DIR/maybeuninit.rs:21:54
|
LL | assert::is_maybe_transmutable::<MaybeUninit<u8>, u8>();
| ^^ at least one value of `MaybeUninit<u8>` isn't a bit-valid value of `u8`
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/maybeuninit.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,47 @@
#![crate_type = "lib"]
#![feature(transmutability)]
#![allow(dead_code, incomplete_features, non_camel_case_types)]
use std::cell::UnsafeCell;
mod assert {
use std::mem::{Assume, BikeshedIntrinsicFrom};
pub fn is_maybe_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
{}
}
fn value_to_value() {
// We accept value-to-value transmutations of `UnsafeCell`-containing types,
// because owning a value implies exclusive access.
assert::is_maybe_transmutable::<UnsafeCell<u8>, u8>();
assert::is_maybe_transmutable::<u8, UnsafeCell<u8>>();
assert::is_maybe_transmutable::<UnsafeCell<u8>, UnsafeCell<u8>>();
}
fn ref_to_ref() {
// We forbid `UnsafeCell`-containing ref-to-ref transmutations, because the
// two types may use different, incompatible synchronization strategies.
assert::is_maybe_transmutable::<&'static u8, &'static UnsafeCell<u8>>(); //~ ERROR: cannot be safely transmuted
assert::is_maybe_transmutable::<&'static UnsafeCell<u8>, &'static UnsafeCell<u8>>(); //~ ERROR: cannot be safely transmuted
}
fn mut_to_mut() {
// `UnsafeCell` does't matter for `&mut T` to `&mut U`, since exclusive
// borrows can't be used for shared access.
assert::is_maybe_transmutable::<&'static mut u8, &'static mut UnsafeCell<u8>>();
assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static mut u8>();
assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static mut UnsafeCell<u8>>();
}
fn mut_to_ref() {
// We don't care about `UnsafeCell` for transmutations in the form `&mut T
// -> &U`, because downgrading a `&mut T` to a `&U` deactivates `&mut T` for
// the lifetime of `&U`.
assert::is_maybe_transmutable::<&'static mut u8, &'static UnsafeCell<u8>>();
assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static u8>();
assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static UnsafeCell<u8>>();
}

View File

@ -0,0 +1,33 @@
error[E0277]: `&u8` cannot be safely transmuted into `&UnsafeCell<u8>`
--> $DIR/unsafecell.rs:27:50
|
LL | assert::is_maybe_transmutable::<&'static u8, &'static UnsafeCell<u8>>();
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `&'static UnsafeCell<u8>`
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/unsafecell.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
error[E0277]: `&UnsafeCell<u8>` cannot be safely transmuted into `&UnsafeCell<u8>`
--> $DIR/unsafecell.rs:29:62
|
LL | assert::is_maybe_transmutable::<&'static UnsafeCell<u8>, &'static UnsafeCell<u8>>();
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `&'static UnsafeCell<u8>`
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/unsafecell.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -1,3 +1,4 @@
//@ check-pass
//! A struct must have a well-defined layout to participate in a transmutation.
#![crate_type = "lib"]
@ -20,47 +21,47 @@ pub fn is_maybe_transmutable<Src, Dst>()
{}
}
fn should_reject_repr_rust()
fn should_accept_repr_rust()
{
fn unit() {
struct repr_rust;
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn tuple() {
struct repr_rust();
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn braces() {
struct repr_rust{}
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn aligned() {
#[repr(align(1))] struct repr_rust{}
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn packed() {
#[repr(packed)] struct repr_rust{}
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn nested() {
struct repr_rust;
#[repr(C)] struct repr_c(repr_rust);
assert::is_maybe_transmutable::<repr_c, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_c>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_c, ()>();
assert::is_maybe_transmutable::<u128, repr_c>();
}
}
fn should_accept_repr_C()
fn should_accept_repr_c()
{
fn unit() {
#[repr(C)] struct repr_c;

View File

@ -1,267 +0,0 @@
error[E0277]: `should_reject_repr_rust::unit::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:27:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `should_reject_repr_rust::unit::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::unit::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:28:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::unit::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `should_reject_repr_rust::tuple::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:33:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `should_reject_repr_rust::tuple::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::tuple::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:34:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::tuple::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `should_reject_repr_rust::braces::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:39:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `should_reject_repr_rust::braces::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::braces::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:40:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::braces::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `aligned::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:45:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `aligned::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `aligned::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:46:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `aligned::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `packed::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:51:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `packed::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `packed::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:52:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `packed::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `nested::repr_c` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:58:49
|
LL | assert::is_maybe_transmutable::<repr_c, ()>();
| ^^ analyzing the transmutability of `nested::repr_c` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `nested::repr_c`
--> $DIR/should_require_well_defined_layout.rs:59:47
|
LL | assert::is_maybe_transmutable::<u128, repr_c>();
| ^^^^^^ analyzing the transmutability of `nested::repr_c` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error: aborting due to 12 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -22,4 +22,5 @@ fn should_pad_explicitly_packed_field() {
//~^ ERROR: recursive type
assert::is_maybe_transmutable::<ExplicitlyPadded, ()>();
//~^ ERROR: cannot be safely transmuted
}

View File

@ -15,7 +15,22 @@ error[E0391]: cycle detected when computing layout of `should_pad_explicitly_pac
= note: cycle used when evaluating trait selection obligation `(): core::mem::transmutability::BikeshedIntrinsicFrom<should_pad_explicitly_packed_field::ExplicitlyPadded, core::mem::transmutability::Assume { alignment: false, lifetimes: false, safety: false, validity: false }>`
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error: aborting due to 2 previous errors
error[E0277]: `ExplicitlyPadded` cannot be safely transmuted into `()`
--> $DIR/transmute_infinitely_recursive_type.rs:24:55
|
LL | assert::is_maybe_transmutable::<ExplicitlyPadded, ()>();
| ^^ analyzing the transmutability of `ExplicitlyPadded` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/transmute_infinitely_recursive_type.rs:14:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
Some errors have detailed explanations: E0072, E0391.
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0072, E0277, E0391.
For more information about an error, try `rustc --explain E0072`.

View File

@ -1,7 +1,13 @@
//@ check-pass
//! This UI test was introduced as check-fail by a buggy bug-fix for an ICE. In
//! fact, this transmutation should be valid.
#![crate_type = "lib"]
#![feature(transmutability)]
#![allow(dead_code)]
use std::mem::size_of;
mod assert {
use std::mem::{Assume, BikeshedIntrinsicFrom};
@ -22,6 +28,7 @@ fn test() {
#[repr(C)]
struct B(u8, u8);
assert_eq!(size_of::<A>(), size_of::<B>());
assert::is_maybe_transmutable::<B, A>();
//~^ ERROR cannot be safely transmuted
}

View File

@ -1,22 +0,0 @@
error[E0277]: `B` cannot be safely transmuted into `A`
--> $DIR/transmute-padding-ice.rs:25:40
|
LL | assert::is_maybe_transmutable::<B, A>();
| ^ the size of `B` is smaller than the size of `A`
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/transmute-padding-ice.rs:10:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<
| ______________^
LL | | Src,
LL | | { Assume { alignment: true, lifetimes: true, safety: true, validity: true } },
LL | | >,
| |_________^ required by this bound in `is_maybe_transmutable`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,71 @@
#![crate_type = "lib"]
#![feature(transmutability)]
#![allow(dead_code, incomplete_features, non_camel_case_types)]
mod assert {
use std::mem::{Assume, BikeshedIntrinsicFrom};
pub fn is_maybe_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, {
Assume {
alignment: true,
lifetimes: true,
safety: true,
validity: true,
}
}>
{}
}
fn void() {
enum Void {}
// This transmutation is vacuously acceptable; since one cannot construct a
// `Void`, unsoundness cannot directly arise from transmuting a void into
// anything else.
assert::is_maybe_transmutable::<Void, u128>();
assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted
}
// Non-ZST uninhabited types are, nonetheless, uninhabited.
fn yawning_void() {
enum Void {}
struct YawningVoid(Void, u128);
const _: () = {
assert!(std::mem::size_of::<YawningVoid>() == std::mem::size_of::<u128>());
// Just to be sure the above constant actually evaluated:
assert!(false); //~ ERROR: evaluation of constant value failed
};
// This transmutation is vacuously acceptable; since one cannot construct a
// `Void`, unsoundness cannot directly arise from transmuting a void into
// anything else.
assert::is_maybe_transmutable::<YawningVoid, u128>();
assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted
}
// References to uninhabited types are, logically, uninhabited, but for layout
// purposes are not ZSTs, and aren't treated as uninhabited when they appear in
// enum variants.
fn distant_void() {
enum Void {}
enum DistantVoid {
A(&'static Void)
}
const _: () = {
assert!(std::mem::size_of::<DistantVoid>() == std::mem::size_of::<usize>());
// Just to be sure the above constant actually evaluated:
assert!(false); //~ ERROR: evaluation of constant value failed
};
assert::is_maybe_transmutable::<DistantVoid, ()>();
assert::is_maybe_transmutable::<DistantVoid, &'static Void>();
assert::is_maybe_transmutable::<u128, DistantVoid>(); //~ ERROR: cannot be safely transmuted
}

View File

@ -0,0 +1,86 @@
error[E0080]: evaluation of constant value failed
--> $DIR/uninhabited.rs:41:9
|
LL | assert!(false);
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:41:9
|
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0080]: evaluation of constant value failed
--> $DIR/uninhabited.rs:65:9
|
LL | assert!(false);
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:65:9
|
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: `()` cannot be safely transmuted into `void::Void`
--> $DIR/uninhabited.rs:29:41
|
LL | assert::is_maybe_transmutable::<(), Void>();
| ^^^^ `void::Void` is uninhabited
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/uninhabited.rs:10:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `()` cannot be safely transmuted into `yawning_void::Void`
--> $DIR/uninhabited.rs:49:41
|
LL | assert::is_maybe_transmutable::<(), Void>();
| ^^^^ `yawning_void::Void` is uninhabited
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/uninhabited.rs:10:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `DistantVoid`
--> $DIR/uninhabited.rs:70:43
|
LL | assert::is_maybe_transmutable::<u128, DistantVoid>();
| ^^^^^^^^^^^ at least one value of `u128` isn't a bit-valid value of `DistantVoid`
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/uninhabited.rs:10:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error: aborting due to 5 previous errors
Some errors have detailed explanations: E0080, E0277.
For more information about an error, try `rustc --explain E0080`.

View File

@ -25,13 +25,13 @@ fn should_pad_explicitly_aligned_field() {
#[derive(Clone, Copy)] #[repr(u8)] enum V0u8 { V = 0 }
#[derive(Clone, Copy)] #[repr(u8)] enum V1u8 { V = 1 }
#[repr(C)]
#[repr(align(1))]
pub union Uninit {
a: (),
b: V1u8,
}
#[repr(C, align(2))]
#[repr(align(2))]
pub union align_2 {
a: V0u8,
}

View File

@ -1,7 +1,7 @@
//! A struct must have a well-defined layout to participate in a transmutation.
//@ check-pass
#![crate_type = "lib"]
#![feature(transmutability)]
#![feature(transmutability, transparent_unions)]
#![allow(dead_code, incomplete_features, non_camel_case_types)]
mod assert {
@ -20,17 +20,17 @@ pub fn is_maybe_transmutable<Src, Dst>()
{}
}
fn should_reject_repr_rust()
fn should_accept_repr_rust()
{
union repr_rust {
a: u8
}
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn should_accept_repr_C()
fn should_accept_repr_c()
{
#[repr(C)]
union repr_c {
@ -41,3 +41,15 @@ union repr_c {
assert::is_maybe_transmutable::<repr_c, ()>();
assert::is_maybe_transmutable::<u128, repr_c>();
}
fn should_accept_transparent()
{
#[repr(transparent)]
union repr_transparent {
a: u8
}
assert::is_maybe_transmutable::<repr_transparent, ()>();
assert::is_maybe_transmutable::<u128, repr_transparent>();
}

View File

@ -27,7 +27,6 @@ fn should_pad_explicitly_packed_field() {
#[derive(Clone, Copy)] #[repr(u8)] enum V2u8 { V = 2 }
#[derive(Clone, Copy)] #[repr(u32)] enum V3u32 { V = 3 }
#[repr(C)]
pub union Uninit {
a: (),
b: V1u8,

View File

@ -1,47 +0,0 @@
error[E0277]: `should_reject_repr_rust::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:29:48
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `should_reject_repr_rust::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:30:43
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.