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:
parent
d6eb0f5a09
commit
3aa14e3b2e
@ -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);
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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...
|
||||
|
@ -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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
156
tests/ui/transmutability/enums/niche_optimization.rs
Normal file
156
tests/ui/transmutability/enums/niche_optimization.rs
Normal 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>();
|
||||
}
|
80
tests/ui/transmutability/enums/repr/padding_differences.rs
Normal file
80
tests/ui/transmutability/enums/repr/padding_differences.rs
Normal 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>();
|
||||
}
|
@ -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>();
|
@ -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`.
|
@ -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
|
||||
|
43
tests/ui/transmutability/maybeuninit.rs
Normal file
43
tests/ui/transmutability/maybeuninit.rs
Normal 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>();
|
||||
}
|
18
tests/ui/transmutability/maybeuninit.stderr
Normal file
18
tests/ui/transmutability/maybeuninit.stderr
Normal 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`.
|
47
tests/ui/transmutability/references/unsafecell.rs
Normal file
47
tests/ui/transmutability/references/unsafecell.rs
Normal 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>>();
|
||||
}
|
33
tests/ui/transmutability/references/unsafecell.stderr
Normal file
33
tests/ui/transmutability/references/unsafecell.stderr
Normal 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`.
|
@ -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;
|
@ -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`.
|
@ -22,4 +22,5 @@ fn should_pad_explicitly_packed_field() {
|
||||
//~^ ERROR: recursive type
|
||||
|
||||
assert::is_maybe_transmutable::<ExplicitlyPadded, ()>();
|
||||
//~^ ERROR: cannot be safely transmuted
|
||||
}
|
||||
|
@ -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`.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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`.
|
71
tests/ui/transmutability/uninhabited.rs
Normal file
71
tests/ui/transmutability/uninhabited.rs
Normal 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
|
||||
}
|
86
tests/ui/transmutability/uninhabited.stderr
Normal file
86
tests/ui/transmutability/uninhabited.stderr
Normal 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`.
|
@ -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,
|
||||
}
|
||||
|
@ -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>();
|
||||
}
|
@ -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,
|
||||
|
@ -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`.
|
Loading…
Reference in New Issue
Block a user