Safe Transmute: Enable handling references, including recursive types
This patch enables support for references in Safe Transmute, by generating nested obligations during trait selection. Specifically, when we call `confirm_transmutability_candidate(...)`, we now recursively traverse the `rustc_transmute::Answer` tree and create obligations for all the `Answer` variants, some of which include multiple nested `Answer`s. Also, to handle recursive types, enable support for coinduction for the Safe Transmute trait (`BikeshedIntrinsicFrom`) by adding the `#[rustc_coinduction]` annotation. Also fix some small logic issues when reducing the `or` and `and` combinations in `rustc_transmute`, so that we don't end up with additional redundant `Answer`s in the tree. Co-authored-by: Jack Wrenn <jack@wrenn.fyi>
This commit is contained in:
parent
97d328012b
commit
8f1cec8d84
26 changed files with 460 additions and 103 deletions
|
@ -2783,6 +2783,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||||
rustc_transmute::Reason::DstIsTooBig => {
|
rustc_transmute::Reason::DstIsTooBig => {
|
||||||
format!("The size of `{src}` is smaller than the size of `{dst}`")
|
format!("The size of `{src}` is smaller than the size of `{dst}`")
|
||||||
}
|
}
|
||||||
|
rustc_transmute::Reason::DstHasStricterAlignment => {
|
||||||
|
format!(
|
||||||
|
"The alignment of `{src}` should be stricter than that of `{dst}`, but it is not"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
rustc_transmute::Reason::DstIsMoreUnique => {
|
||||||
|
format!("`{src}` is a shared reference, but `{dst}` is a unique reference")
|
||||||
|
}
|
||||||
};
|
};
|
||||||
(custom_err_msg, Some(reason_msg))
|
(custom_err_msg, Some(reason_msg))
|
||||||
}
|
}
|
||||||
|
@ -2791,7 +2799,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||||
span,
|
span,
|
||||||
"Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
|
"Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
|
||||||
),
|
),
|
||||||
_ => span_bug!(span, "Unsupported rustc_transmute::Reason variant"),
|
other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
|
||||||
use rustc_middle::traits::SelectionOutputTypeParameterMismatch;
|
use rustc_middle::traits::SelectionOutputTypeParameterMismatch;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, Binder, GenericParamDefKind, InternalSubsts, SubstsRef, ToPolyTraitRef, ToPredicate,
|
self, Binder, GenericParamDefKind, InternalSubsts, SubstsRef, ToPolyTraitRef, ToPredicate,
|
||||||
TraitRef, Ty, TyCtxt, TypeVisitableExt,
|
TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt,
|
||||||
};
|
};
|
||||||
use rustc_session::config::TraitSolver;
|
use rustc_session::config::TraitSolver;
|
||||||
use rustc_span::def_id::DefId;
|
use rustc_span::def_id::DefId;
|
||||||
|
@ -279,10 +279,61 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
ImplSourceBuiltinData { nested: obligations }
|
ImplSourceBuiltinData { nested: obligations }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self))]
|
||||||
fn confirm_transmutability_candidate(
|
fn confirm_transmutability_candidate(
|
||||||
&mut self,
|
&mut self,
|
||||||
obligation: &TraitObligation<'tcx>,
|
obligation: &TraitObligation<'tcx>,
|
||||||
) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
|
) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
|
||||||
|
fn flatten_answer_tree<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
obligation: &TraitObligation<'tcx>,
|
||||||
|
predicate: TraitPredicate<'tcx>,
|
||||||
|
answer: rustc_transmute::Answer<rustc_transmute::layout::rustc::Ref<'tcx>>,
|
||||||
|
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
|
||||||
|
match answer {
|
||||||
|
rustc_transmute::Answer::Yes => Ok(vec![]),
|
||||||
|
rustc_transmute::Answer::No(_) => Err(Unimplemented),
|
||||||
|
// FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
|
||||||
|
rustc_transmute::Answer::IfAll(answers)
|
||||||
|
| rustc_transmute::Answer::IfAny(answers) => {
|
||||||
|
let mut nested = vec![];
|
||||||
|
for flattened in answers
|
||||||
|
.into_iter()
|
||||||
|
.map(|answer| flatten_answer_tree(tcx, obligation, predicate, answer))
|
||||||
|
{
|
||||||
|
nested.extend(flattened?);
|
||||||
|
}
|
||||||
|
Ok(nested)
|
||||||
|
}
|
||||||
|
rustc_transmute::Answer::IfTransmutable { src, dst } => {
|
||||||
|
let trait_def_id = obligation.predicate.def_id();
|
||||||
|
let scope = predicate.trait_ref.substs.type_at(2);
|
||||||
|
let assume_const = predicate.trait_ref.substs.const_at(3);
|
||||||
|
let make_obl = |from_ty, to_ty| {
|
||||||
|
let trait_ref1 = tcx.mk_trait_ref(
|
||||||
|
trait_def_id,
|
||||||
|
[
|
||||||
|
ty::GenericArg::from(to_ty),
|
||||||
|
ty::GenericArg::from(from_ty),
|
||||||
|
ty::GenericArg::from(scope),
|
||||||
|
ty::GenericArg::from(assume_const),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
Obligation::with_depth(
|
||||||
|
tcx,
|
||||||
|
obligation.cause.clone(),
|
||||||
|
obligation.recursion_depth + 1,
|
||||||
|
obligation.param_env,
|
||||||
|
trait_ref1,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// // FIXME(bryangarza): Check src.mutability or dst.mutability to know whether dst -> src obligation is needed
|
||||||
|
Ok(vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debug!(?obligation, "confirm_transmutability_candidate");
|
debug!(?obligation, "confirm_transmutability_candidate");
|
||||||
|
|
||||||
// We erase regions here because transmutability calls layout queries,
|
// We erase regions here because transmutability calls layout queries,
|
||||||
|
@ -312,10 +363,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
assume,
|
assume,
|
||||||
);
|
);
|
||||||
|
|
||||||
match maybe_transmutable {
|
info!(?maybe_transmutable);
|
||||||
rustc_transmute::Answer::Yes => Ok(ImplSourceBuiltinData { nested: vec![] }),
|
let nested = flatten_answer_tree(self.tcx(), obligation, predicate, maybe_transmutable)?;
|
||||||
_ => Err(Unimplemented),
|
info!(?nested);
|
||||||
}
|
Ok(ImplSourceBuiltinData { nested })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This handles the case where an `auto trait Foo` impl is being used.
|
/// This handles the case where an `auto trait Foo` impl is being used.
|
||||||
|
|
|
@ -30,33 +30,46 @@ impl fmt::Debug for Byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {}
|
pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {}
|
||||||
pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {}
|
pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {
|
||||||
|
fn min_align(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_mutable(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Def for ! {}
|
impl Def for ! {}
|
||||||
impl Ref for ! {}
|
impl Ref for ! {}
|
||||||
|
|
||||||
#[cfg(feature = "rustc")]
|
#[cfg(feature = "rustc")]
|
||||||
pub(crate) mod rustc {
|
pub mod rustc {
|
||||||
use rustc_middle::mir::Mutability;
|
use rustc_middle::mir::Mutability;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_middle::ty::Region;
|
|
||||||
use rustc_middle::ty::Ty;
|
|
||||||
|
|
||||||
/// A reference in the layout.
|
/// A reference in the layout.
|
||||||
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
|
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
|
||||||
pub struct Ref<'tcx> {
|
pub struct Ref<'tcx> {
|
||||||
lifetime: Region<'tcx>,
|
pub lifetime: ty::Region<'tcx>,
|
||||||
ty: Ty<'tcx>,
|
pub ty: Ty<'tcx>,
|
||||||
mutability: Mutability,
|
pub mutability: Mutability,
|
||||||
|
pub align: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> super::Ref for Ref<'tcx> {}
|
impl<'tcx> super::Ref for Ref<'tcx> {
|
||||||
|
fn min_align(&self) -> usize {
|
||||||
|
self.align
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> Ref<'tcx> {
|
fn is_mutable(&self) -> bool {
|
||||||
pub fn min_align(&self) -> usize {
|
match self.mutability {
|
||||||
todo!()
|
Mutability::Mut => true,
|
||||||
|
Mutability::Not => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<'tcx> Ref<'tcx> {}
|
||||||
|
|
||||||
/// A visibility node in the layout.
|
/// A visibility node in the layout.
|
||||||
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
|
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
|
||||||
|
|
|
@ -365,6 +365,17 @@ pub(crate) mod rustc {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ty::Ref(lifetime, ty, mutability) => {
|
||||||
|
let align = layout_of(tcx, *ty)?.align();
|
||||||
|
Ok(Tree::Ref(Ref {
|
||||||
|
lifetime: *lifetime,
|
||||||
|
ty: *ty,
|
||||||
|
mutability: *mutability,
|
||||||
|
align,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
_ => Err(Err::Unspecified),
|
_ => Err(Err::Unspecified),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ extern crate tracing;
|
||||||
|
|
||||||
pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set};
|
pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set};
|
||||||
|
|
||||||
pub(crate) mod layout;
|
pub mod layout;
|
||||||
pub(crate) mod maybe_transmutable;
|
pub(crate) mod maybe_transmutable;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -21,10 +21,7 @@ pub struct Assume {
|
||||||
|
|
||||||
/// The type encodes answers to the question: "Are these types transmutable?"
|
/// The type encodes answers to the question: "Are these types transmutable?"
|
||||||
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
|
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
|
||||||
pub enum Answer<R>
|
pub enum Answer<R> {
|
||||||
where
|
|
||||||
R: layout::Ref,
|
|
||||||
{
|
|
||||||
/// `Src` is transmutable into `Dst`.
|
/// `Src` is transmutable into `Dst`.
|
||||||
Yes,
|
Yes,
|
||||||
|
|
||||||
|
@ -54,6 +51,10 @@ pub enum Reason {
|
||||||
DstIsPrivate,
|
DstIsPrivate,
|
||||||
/// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
|
/// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
|
||||||
DstIsTooBig,
|
DstIsTooBig,
|
||||||
|
/// Src should have a stricter alignment than Dst, but it does not.
|
||||||
|
DstHasStricterAlignment,
|
||||||
|
/// Can't go from shared pointer to unique pointer
|
||||||
|
DstIsMoreUnique,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rustc")]
|
#[cfg(feature = "rustc")]
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::Map;
|
pub(crate) mod query_context;
|
||||||
use crate::{Answer, Reason};
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
mod query_context;
|
use crate::{
|
||||||
use query_context::QueryContext;
|
layout::{self, dfa, Byte, Dfa, Nfa, Ref, Tree, Uninhabited},
|
||||||
|
maybe_transmutable::query_context::QueryContext,
|
||||||
|
Answer, Map, Reason,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::layout::{self, dfa, Byte, Dfa, Nfa, Tree, Uninhabited};
|
|
||||||
pub(crate) struct MaybeTransmutableQuery<L, C>
|
pub(crate) struct MaybeTransmutableQuery<L, C>
|
||||||
where
|
where
|
||||||
C: QueryContext,
|
C: QueryContext,
|
||||||
|
@ -53,6 +53,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Nix this cfg, so we can write unit tests independently of rustc
|
||||||
#[cfg(feature = "rustc")]
|
#[cfg(feature = "rustc")]
|
||||||
mod rustc {
|
mod rustc {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -77,12 +78,11 @@ mod rustc {
|
||||||
match (src, dst) {
|
match (src, dst) {
|
||||||
// Answer `Yes` here, because 'unknown layout' and type errors will already
|
// Answer `Yes` here, because 'unknown layout' and type errors will already
|
||||||
// be reported by rustc. No need to spam the user with more errors.
|
// be reported by rustc. No need to spam the user with more errors.
|
||||||
(Err(Err::TypeError(_)), _) => Err(Answer::Yes),
|
(Err(Err::TypeError(_)), _) | (_, Err(Err::TypeError(_))) => Err(Answer::Yes),
|
||||||
(_, Err(Err::TypeError(_))) => Err(Answer::Yes),
|
(Err(Err::Unknown), _) | (_, Err(Err::Unknown)) => Err(Answer::Yes),
|
||||||
(Err(Err::Unknown), _) => Err(Answer::Yes),
|
(Err(Err::Unspecified), _) | (_, Err(Err::Unspecified)) => {
|
||||||
(_, Err(Err::Unknown)) => Err(Answer::Yes),
|
Err(Answer::No(Reason::SrcIsUnspecified))
|
||||||
(Err(Err::Unspecified), _) => Err(Answer::No(Reason::SrcIsUnspecified)),
|
}
|
||||||
(_, Err(Err::Unspecified)) => Err(Answer::No(Reason::DstIsUnspecified)),
|
|
||||||
(Ok(src), Ok(dst)) => Ok((src, dst)),
|
(Ok(src), Ok(dst)) => Ok((src, dst)),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -214,34 +214,99 @@ where
|
||||||
Answer::No(Reason::DstIsTooBig)
|
Answer::No(Reason::DstIsTooBig)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let src_quantification = if self.assume.validity {
|
let src_quantifier = if self.assume.validity {
|
||||||
// if the compiler may assume that the programmer is doing additional validity checks,
|
// if the compiler may assume that the programmer is doing additional validity checks,
|
||||||
// (e.g.: that `src != 3u8` when the destination type is `bool`)
|
// (e.g.: that `src != 3u8` when the destination type is `bool`)
|
||||||
// then there must exist at least one transition out of `src_state` such that the transmute is viable...
|
// then there must exist at least one transition out of `src_state` such that the transmute is viable...
|
||||||
there_exists
|
Quantifier::ThereExists
|
||||||
} else {
|
} else {
|
||||||
// if the compiler cannot assume that the programmer is doing additional validity checks,
|
// if the compiler cannot assume that the programmer is doing additional validity checks,
|
||||||
// then for all transitions out of `src_state`, such that the transmute is viable...
|
// then for all transitions out of `src_state`, such that the transmute is viable...
|
||||||
// then there must exist at least one transition out of `src_state` such that the transmute is viable...
|
// then there must exist at least one transition out of `dst_state` such that the transmute is viable...
|
||||||
for_all
|
Quantifier::ForAll
|
||||||
};
|
};
|
||||||
|
|
||||||
src_quantification(
|
let bytes_answer = src_quantifier.apply(
|
||||||
self.src.bytes_from(src_state).unwrap_or(&Map::default()),
|
// for each of the byte transitions out of the `src_state`...
|
||||||
|(&src_validity, &src_state_prime)| {
|
self.src.bytes_from(src_state).unwrap_or(&Map::default()).into_iter().map(
|
||||||
if let Some(dst_state_prime) = self.dst.byte_from(dst_state, src_validity) {
|
|(&src_validity, &src_state_prime)| {
|
||||||
self.answer_memo(cache, src_state_prime, dst_state_prime)
|
// ...try to find a matching transition out of `dst_state`.
|
||||||
} else if let Some(dst_state_prime) =
|
if let Some(dst_state_prime) =
|
||||||
self.dst.byte_from(dst_state, Byte::Uninit)
|
self.dst.byte_from(dst_state, src_validity)
|
||||||
{
|
{
|
||||||
self.answer_memo(cache, src_state_prime, dst_state_prime)
|
self.answer_memo(cache, src_state_prime, dst_state_prime)
|
||||||
} else {
|
} else if let Some(dst_state_prime) =
|
||||||
Answer::No(Reason::DstIsBitIncompatible)
|
// otherwise, see if `dst_state` has any outgoing `Uninit` transitions
|
||||||
}
|
// (any init byte is a valid uninit byte)
|
||||||
},
|
self.dst.byte_from(dst_state, Byte::Uninit)
|
||||||
)
|
{
|
||||||
|
self.answer_memo(cache, src_state_prime, dst_state_prime)
|
||||||
|
} else {
|
||||||
|
// otherwise, we've exhausted our options.
|
||||||
|
// the DFAs, from this point onwards, are bit-incompatible.
|
||||||
|
Answer::No(Reason::DstIsBitIncompatible)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// The below early returns reflect how this code would behave:
|
||||||
|
// if self.assume.validity {
|
||||||
|
// bytes_answer.or(refs_answer)
|
||||||
|
// } else {
|
||||||
|
// bytes_answer.and(refs_answer)
|
||||||
|
// }
|
||||||
|
// ...if `refs_answer` was computed lazily. The below early
|
||||||
|
// returns can be deleted without impacting the correctness of
|
||||||
|
// the algoritm; only its performance.
|
||||||
|
match bytes_answer {
|
||||||
|
Answer::No(..) if !self.assume.validity => return bytes_answer,
|
||||||
|
Answer::Yes if self.assume.validity => return bytes_answer,
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
let refs_answer = src_quantifier.apply(
|
||||||
|
// for each reference transition out of `src_state`...
|
||||||
|
self.src.refs_from(src_state).unwrap_or(&Map::default()).into_iter().map(
|
||||||
|
|(&src_ref, &src_state_prime)| {
|
||||||
|
// ...there exists a reference transition out of `dst_state`...
|
||||||
|
Quantifier::ThereExists.apply(
|
||||||
|
self.dst
|
||||||
|
.refs_from(dst_state)
|
||||||
|
.unwrap_or(&Map::default())
|
||||||
|
.into_iter()
|
||||||
|
.map(|(&dst_ref, &dst_state_prime)| {
|
||||||
|
if !src_ref.is_mutable() && dst_ref.is_mutable() {
|
||||||
|
Answer::No(Reason::DstIsMoreUnique)
|
||||||
|
} else if !self.assume.alignment
|
||||||
|
&& src_ref.min_align() < dst_ref.min_align()
|
||||||
|
{
|
||||||
|
Answer::No(Reason::DstHasStricterAlignment)
|
||||||
|
} else {
|
||||||
|
// ...such that `src` is transmutable into `dst`, if
|
||||||
|
// `src_ref` is transmutability into `dst_ref`.
|
||||||
|
Answer::IfTransmutable { src: src_ref, dst: dst_ref }
|
||||||
|
.and(self.answer_memo(
|
||||||
|
cache,
|
||||||
|
src_state_prime,
|
||||||
|
dst_state_prime,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if self.assume.validity {
|
||||||
|
bytes_answer.or(refs_answer)
|
||||||
|
} else {
|
||||||
|
bytes_answer.and(refs_answer)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
cache.insert((src_state, dst_state), answer.clone());
|
if let Some(..) = cache.insert((src_state, dst_state), answer.clone()) {
|
||||||
|
panic!("failed to correctly cache transmutability")
|
||||||
|
}
|
||||||
answer
|
answer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,17 +318,21 @@ where
|
||||||
{
|
{
|
||||||
pub(crate) fn and(self, rhs: Self) -> Self {
|
pub(crate) fn and(self, rhs: Self) -> Self {
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(Self::No(reason), _) | (_, Self::No(reason)) => Self::No(reason),
|
(_, Self::No(reason)) | (Self::No(reason), _) => Self::No(reason),
|
||||||
(Self::Yes, Self::Yes) => Self::Yes,
|
|
||||||
|
(Self::Yes, other) | (other, Self::Yes) => other,
|
||||||
|
|
||||||
(Self::IfAll(mut lhs), Self::IfAll(ref mut rhs)) => {
|
(Self::IfAll(mut lhs), Self::IfAll(ref mut rhs)) => {
|
||||||
lhs.append(rhs);
|
lhs.append(rhs);
|
||||||
Self::IfAll(lhs)
|
Self::IfAll(lhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
(constraint, Self::IfAll(mut constraints))
|
(constraint, Self::IfAll(mut constraints))
|
||||||
| (Self::IfAll(mut constraints), constraint) => {
|
| (Self::IfAll(mut constraints), constraint) => {
|
||||||
constraints.push(constraint);
|
constraints.push(constraint);
|
||||||
Self::IfAll(constraints)
|
Self::IfAll(constraints)
|
||||||
}
|
}
|
||||||
|
|
||||||
(lhs, rhs) => Self::IfAll(vec![lhs, rhs]),
|
(lhs, rhs) => Self::IfAll(vec![lhs, rhs]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,7 +340,7 @@ where
|
||||||
pub(crate) fn or(self, rhs: Self) -> Self {
|
pub(crate) fn or(self, rhs: Self) -> Self {
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(Self::Yes, _) | (_, Self::Yes) => Self::Yes,
|
(Self::Yes, _) | (_, Self::Yes) => Self::Yes,
|
||||||
(Self::No(lhr), Self::No(rhr)) => Self::No(lhr),
|
(other, Self::No(reason)) | (Self::No(reason), other) => other,
|
||||||
(Self::IfAny(mut lhs), Self::IfAny(ref mut rhs)) => {
|
(Self::IfAny(mut lhs), Self::IfAny(ref mut rhs)) => {
|
||||||
lhs.append(rhs);
|
lhs.append(rhs);
|
||||||
Self::IfAny(lhs)
|
Self::IfAny(lhs)
|
||||||
|
@ -319,3 +388,36 @@ where
|
||||||
);
|
);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum Quantifier {
|
||||||
|
ThereExists,
|
||||||
|
ForAll,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Quantifier {
|
||||||
|
pub fn apply<R, I>(&self, iter: I) -> Answer<R>
|
||||||
|
where
|
||||||
|
R: layout::Ref,
|
||||||
|
I: IntoIterator<Item = Answer<R>>,
|
||||||
|
{
|
||||||
|
use std::ops::ControlFlow::{Break, Continue};
|
||||||
|
|
||||||
|
let (init, try_fold_f): (_, fn(_, _) -> _) = match self {
|
||||||
|
Self::ThereExists => {
|
||||||
|
(Answer::No(Reason::DstIsBitIncompatible), |accum: Answer<R>, next| {
|
||||||
|
match accum.or(next) {
|
||||||
|
Answer::Yes => Break(Answer::Yes),
|
||||||
|
maybe => Continue(maybe),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Self::ForAll => (Answer::Yes, |accum: Answer<R>, next| match accum.and(next) {
|
||||||
|
Answer::No(reason) => Break(Answer::No(reason)),
|
||||||
|
maybe => Continue(maybe),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (Continue(result) | Break(result)) = iter.into_iter().try_fold(init, try_fold_f);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
/// notwithstanding whatever safety checks you have asked the compiler to [`Assume`] are satisfied.
|
/// notwithstanding whatever safety checks you have asked the compiler to [`Assume`] are satisfied.
|
||||||
#[unstable(feature = "transmutability", issue = "99571")]
|
#[unstable(feature = "transmutability", issue = "99571")]
|
||||||
#[lang = "transmute_trait"]
|
#[lang = "transmute_trait"]
|
||||||
|
#[rustc_coinductive]
|
||||||
pub unsafe trait BikeshedIntrinsicFrom<Src, Context, const ASSUME: Assume = { Assume::NOTHING }>
|
pub unsafe trait BikeshedIntrinsicFrom<Src, Context, const ASSUME: Assume = { Assume::NOTHING }>
|
||||||
where
|
where
|
||||||
Src: ?Sized,
|
Src: ?Sized,
|
||||||
|
|
|
@ -23,7 +23,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 0]` in the defin
|
||||||
--> $DIR/should_require_well_defined_layout.rs:27:47
|
--> $DIR/should_require_well_defined_layout.rs:27:47
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
||||||
| ^^^^^^^^^ `[String; 0]` does not have a well-specified layout
|
| ^^^^^^^^^ `u128` does not have a well-specified layout
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/should_require_well_defined_layout.rs:13:14
|
--> $DIR/should_require_well_defined_layout.rs:13:14
|
||||||
|
@ -65,7 +65,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 1]` in the defin
|
||||||
--> $DIR/should_require_well_defined_layout.rs:33:47
|
--> $DIR/should_require_well_defined_layout.rs:33:47
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
||||||
| ^^^^^^^^^ `[String; 1]` does not have a well-specified layout
|
| ^^^^^^^^^ `u128` does not have a well-specified layout
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/should_require_well_defined_layout.rs:13:14
|
--> $DIR/should_require_well_defined_layout.rs:13:14
|
||||||
|
@ -107,7 +107,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 2]` in the defin
|
||||||
--> $DIR/should_require_well_defined_layout.rs:39:47
|
--> $DIR/should_require_well_defined_layout.rs:39:47
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
||||||
| ^^^^^^^^^ `[String; 2]` does not have a well-specified layout
|
| ^^^^^^^^^ `u128` does not have a well-specified layout
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/should_require_well_defined_layout.rs:13:14
|
--> $DIR/should_require_well_defined_layout.rs:13:14
|
||||||
|
|
|
@ -24,7 +24,7 @@ error[E0277]: `V0i8` cannot be safely transmuted into `u16` in the defining scop
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:50:44
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:50:44
|
||||||
|
|
|
|
||||||
LL | assert::is_transmutable::<Current, Larger, Context>();
|
LL | assert::is_transmutable::<Current, Larger, Context>();
|
||||||
| ^^^^^^ The size of `V0i8` is smaller than the size of `u16`
|
| ^^^^^^ At least one value of `V0i8` isn't a bit-valid value of `u16`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_transmutable`
|
note: required by a bound in `is_transmutable`
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
||||||
|
@ -68,7 +68,7 @@ error[E0277]: `V0u8` cannot be safely transmuted into `u16` in the defining scop
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:58:44
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:58:44
|
||||||
|
|
|
|
||||||
LL | assert::is_transmutable::<Current, Larger, Context>();
|
LL | assert::is_transmutable::<Current, Larger, Context>();
|
||||||
| ^^^^^^ The size of `V0u8` is smaller than the size of `u16`
|
| ^^^^^^ At least one value of `V0u8` isn't a bit-valid value of `u16`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_transmutable`
|
note: required by a bound in `is_transmutable`
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
||||||
|
@ -112,7 +112,7 @@ error[E0277]: `V0i16` cannot be safely transmuted into `u32` in the defining sco
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:74:44
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:74:44
|
||||||
|
|
|
|
||||||
LL | assert::is_transmutable::<Current, Larger, Context>();
|
LL | assert::is_transmutable::<Current, Larger, Context>();
|
||||||
| ^^^^^^ The size of `V0i16` is smaller than the size of `u32`
|
| ^^^^^^ At least one value of `V0i16` isn't a bit-valid value of `u32`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_transmutable`
|
note: required by a bound in `is_transmutable`
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
||||||
|
@ -156,7 +156,7 @@ error[E0277]: `V0u16` cannot be safely transmuted into `u32` in the defining sco
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:82:44
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:82:44
|
||||||
|
|
|
|
||||||
LL | assert::is_transmutable::<Current, Larger, Context>();
|
LL | assert::is_transmutable::<Current, Larger, Context>();
|
||||||
| ^^^^^^ The size of `V0u16` is smaller than the size of `u32`
|
| ^^^^^^ At least one value of `V0u16` isn't a bit-valid value of `u32`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_transmutable`
|
note: required by a bound in `is_transmutable`
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
||||||
|
@ -200,7 +200,7 @@ error[E0277]: `V0i32` cannot be safely transmuted into `u64` in the defining sco
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:98:44
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:98:44
|
||||||
|
|
|
|
||||||
LL | assert::is_transmutable::<Current, Larger, Context>();
|
LL | assert::is_transmutable::<Current, Larger, Context>();
|
||||||
| ^^^^^^ The size of `V0i32` is smaller than the size of `u64`
|
| ^^^^^^ At least one value of `V0i32` isn't a bit-valid value of `u64`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_transmutable`
|
note: required by a bound in `is_transmutable`
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
||||||
|
@ -244,7 +244,7 @@ error[E0277]: `V0u32` cannot be safely transmuted into `u64` in the defining sco
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:106:44
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:106:44
|
||||||
|
|
|
|
||||||
LL | assert::is_transmutable::<Current, Larger, Context>();
|
LL | assert::is_transmutable::<Current, Larger, Context>();
|
||||||
| ^^^^^^ The size of `V0u32` is smaller than the size of `u64`
|
| ^^^^^^ At least one value of `V0u32` isn't a bit-valid value of `u64`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_transmutable`
|
note: required by a bound in `is_transmutable`
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
||||||
|
@ -288,7 +288,7 @@ error[E0277]: `V0i64` cannot be safely transmuted into `u128` in the defining sc
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:122:44
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:122:44
|
||||||
|
|
|
|
||||||
LL | assert::is_transmutable::<Current, Larger, Context>();
|
LL | assert::is_transmutable::<Current, Larger, Context>();
|
||||||
| ^^^^^^ The size of `V0i64` is smaller than the size of `u128`
|
| ^^^^^^ At least one value of `V0i64` isn't a bit-valid value of `u128`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_transmutable`
|
note: required by a bound in `is_transmutable`
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
||||||
|
@ -332,7 +332,7 @@ error[E0277]: `V0u64` cannot be safely transmuted into `u128` in the defining sc
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:130:44
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:130:44
|
||||||
|
|
|
|
||||||
LL | assert::is_transmutable::<Current, Larger, Context>();
|
LL | assert::is_transmutable::<Current, Larger, Context>();
|
||||||
| ^^^^^^ The size of `V0u64` is smaller than the size of `u128`
|
| ^^^^^^ At least one value of `V0u64` isn't a bit-valid value of `u128`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_transmutable`
|
note: required by a bound in `is_transmutable`
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
||||||
|
@ -376,7 +376,7 @@ error[E0277]: `V0isize` cannot be safely transmuted into `[usize; 2]` in the def
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:146:44
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:146:44
|
||||||
|
|
|
|
||||||
LL | assert::is_transmutable::<Current, Larger, Context>();
|
LL | assert::is_transmutable::<Current, Larger, Context>();
|
||||||
| ^^^^^^ The size of `V0isize` is smaller than the size of `[usize; 2]`
|
| ^^^^^^ At least one value of `V0isize` isn't a bit-valid value of `[usize; 2]`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_transmutable`
|
note: required by a bound in `is_transmutable`
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
||||||
|
@ -420,7 +420,7 @@ error[E0277]: `V0usize` cannot be safely transmuted into `[usize; 2]` in the def
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:154:44
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:154:44
|
||||||
|
|
|
|
||||||
LL | assert::is_transmutable::<Current, Larger, Context>();
|
LL | assert::is_transmutable::<Current, Larger, Context>();
|
||||||
| ^^^^^^ The size of `V0usize` is smaller than the size of `[usize; 2]`
|
| ^^^^^^ At least one value of `V0usize` isn't a bit-valid value of `[usize; 2]`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_transmutable`
|
note: required by a bound in `is_transmutable`
|
||||||
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
--> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
|
||||||
|
|
|
@ -24,7 +24,7 @@ error[E0277]: `u128` cannot be safely transmuted into `void::repr_rust` in the d
|
||||||
--> $DIR/should_require_well_defined_layout.rs:29:47
|
--> $DIR/should_require_well_defined_layout.rs:29:47
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
||||||
| ^^^^^^^^^ `void::repr_rust` does not have a well-specified layout
|
| ^^^^^^^^^ `u128` does not have a well-specified layout
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/should_require_well_defined_layout.rs:14:14
|
--> $DIR/should_require_well_defined_layout.rs:14:14
|
||||||
|
@ -68,7 +68,7 @@ error[E0277]: `u128` cannot be safely transmuted into `singleton::repr_rust` in
|
||||||
--> $DIR/should_require_well_defined_layout.rs:35:47
|
--> $DIR/should_require_well_defined_layout.rs:35:47
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
||||||
| ^^^^^^^^^ `singleton::repr_rust` does not have a well-specified layout
|
| ^^^^^^^^^ `u128` does not have a well-specified layout
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/should_require_well_defined_layout.rs:14:14
|
--> $DIR/should_require_well_defined_layout.rs:14:14
|
||||||
|
@ -112,7 +112,7 @@ error[E0277]: `u128` cannot be safely transmuted into `duplex::repr_rust` in the
|
||||||
--> $DIR/should_require_well_defined_layout.rs:41:47
|
--> $DIR/should_require_well_defined_layout.rs:41:47
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
||||||
| ^^^^^^^^^ `duplex::repr_rust` does not have a well-specified layout
|
| ^^^^^^^^^ `u128` does not have a well-specified layout
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/should_require_well_defined_layout.rs:14:14
|
--> $DIR/should_require_well_defined_layout.rs:14:14
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0277]: `Src` cannot be safely transmuted into `Dst` in the defining scope
|
||||||
--> $DIR/should_pad_variants.rs:44:36
|
--> $DIR/should_pad_variants.rs:44:36
|
||||||
|
|
|
|
||||||
LL | assert::is_transmutable::<Src, Dst, Context>();
|
LL | assert::is_transmutable::<Src, Dst, Context>();
|
||||||
| ^^^ The size of `Src` is smaller than the size of `Dst`
|
| ^^^ At least one value of `Src` isn't a bit-valid value of `Dst`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_transmutable`
|
note: required by a bound in `is_transmutable`
|
||||||
--> $DIR/should_pad_variants.rs:13:14
|
--> $DIR/should_pad_variants.rs:13:14
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
// check-fail
|
||||||
|
#![feature(transmutability)]
|
||||||
|
|
||||||
|
mod assert {
|
||||||
|
use std::mem::{Assume, BikeshedIntrinsicFrom};
|
||||||
|
pub struct Context;
|
||||||
|
|
||||||
|
pub fn is_maybe_transmutable<Src, Dst>()
|
||||||
|
where
|
||||||
|
Dst: BikeshedIntrinsicFrom<Src, Context, {
|
||||||
|
Assume {
|
||||||
|
alignment: true,
|
||||||
|
lifetimes: false,
|
||||||
|
safety: true,
|
||||||
|
validity: false,
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[repr(C)] struct A(bool, &'static A);
|
||||||
|
#[repr(C)] struct B(u8, &'static B);
|
||||||
|
// FIXME(bryangarza): Make 2 variants of this test, depending on mutability.
|
||||||
|
// Right now, we are being strict by default and checking A->B and B->A both.
|
||||||
|
assert::is_maybe_transmutable::<&'static A, &'static B>(); //~ ERROR `B` cannot be safely transmuted into `A`
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
error[E0277]: `B` cannot be safely transmuted into `A` in the defining scope of `assert::Context`
|
||||||
|
--> $DIR/recursive-wrapper-types-bit-compatible.rs:26:49
|
||||||
|
|
|
||||||
|
LL | assert::is_maybe_transmutable::<&'static A, &'static B>();
|
||||||
|
| ^^^^^^^^^^ At least one value of `B` isn't a bit-valid value of `A`
|
||||||
|
|
|
||||||
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
|
--> $DIR/recursive-wrapper-types-bit-compatible.rs:10:14
|
||||||
|
|
|
||||||
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
|
||||||
|
| --------------------- required by a bound in this function
|
||||||
|
LL | where
|
||||||
|
LL | Dst: BikeshedIntrinsicFrom<Src, Context, {
|
||||||
|
| ______________^
|
||||||
|
LL | | Assume {
|
||||||
|
LL | | alignment: true,
|
||||||
|
LL | | lifetimes: false,
|
||||||
|
... |
|
||||||
|
LL | | }
|
||||||
|
LL | | }>
|
||||||
|
| |__________^ required by this bound in `is_maybe_transmutable`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
|
@ -0,0 +1,25 @@
|
||||||
|
// check-fail
|
||||||
|
#![feature(transmutability)]
|
||||||
|
|
||||||
|
mod assert {
|
||||||
|
use std::mem::{Assume, BikeshedIntrinsicFrom};
|
||||||
|
pub struct Context;
|
||||||
|
|
||||||
|
pub fn is_maybe_transmutable<Src, Dst>()
|
||||||
|
where
|
||||||
|
Dst: BikeshedIntrinsicFrom<Src, Context, {
|
||||||
|
Assume {
|
||||||
|
alignment: true,
|
||||||
|
lifetimes: false,
|
||||||
|
safety: true,
|
||||||
|
validity: false,
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[repr(C)] struct A(bool, &'static A);
|
||||||
|
#[repr(C)] struct B(u8, &'static B);
|
||||||
|
assert::is_maybe_transmutable::<&'static B, &'static A>(); //~ ERROR `B` cannot be safely transmuted into `A`
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
error[E0277]: `B` cannot be safely transmuted into `A` in the defining scope of `assert::Context`
|
||||||
|
--> $DIR/recursive-wrapper-types-bit-incompatible.rs:24:49
|
||||||
|
|
|
||||||
|
LL | assert::is_maybe_transmutable::<&'static B, &'static A>();
|
||||||
|
| ^^^^^^^^^^ At least one value of `B` isn't a bit-valid value of `A`
|
||||||
|
|
|
||||||
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
|
--> $DIR/recursive-wrapper-types-bit-incompatible.rs:10:14
|
||||||
|
|
|
||||||
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
|
||||||
|
| --------------------- required by a bound in this function
|
||||||
|
LL | where
|
||||||
|
LL | Dst: BikeshedIntrinsicFrom<Src, Context, {
|
||||||
|
| ______________^
|
||||||
|
LL | | Assume {
|
||||||
|
LL | | alignment: true,
|
||||||
|
LL | | lifetimes: false,
|
||||||
|
... |
|
||||||
|
LL | | }
|
||||||
|
LL | | }>
|
||||||
|
| |__________^ required by this bound in `is_maybe_transmutable`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
|
@ -0,0 +1,26 @@
|
||||||
|
// check-pass
|
||||||
|
#![feature(transmutability)]
|
||||||
|
|
||||||
|
mod assert {
|
||||||
|
use std::mem::{Assume, BikeshedIntrinsicFrom};
|
||||||
|
pub struct Context;
|
||||||
|
|
||||||
|
pub fn is_maybe_transmutable<Src, Dst>()
|
||||||
|
where
|
||||||
|
Dst: BikeshedIntrinsicFrom<Src, Context, {
|
||||||
|
Assume {
|
||||||
|
alignment: true,
|
||||||
|
lifetimes: false,
|
||||||
|
safety: true,
|
||||||
|
validity: false,
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[repr(C)] struct A(&'static B);
|
||||||
|
#[repr(C)] struct B(&'static A);
|
||||||
|
assert::is_maybe_transmutable::<&'static A, &'static B>();
|
||||||
|
assert::is_maybe_transmutable::<&'static B, &'static A>();
|
||||||
|
}
|
|
@ -1,11 +1,5 @@
|
||||||
// revisions: current next
|
// check-fail
|
||||||
//[next] compile-flags: -Ztrait-solver=next
|
|
||||||
|
|
||||||
//! Transmutations involving references are not yet supported.
|
|
||||||
|
|
||||||
#![crate_type = "lib"]
|
|
||||||
#![feature(transmutability)]
|
#![feature(transmutability)]
|
||||||
#![allow(dead_code, incomplete_features, non_camel_case_types)]
|
|
||||||
|
|
||||||
mod assert {
|
mod assert {
|
||||||
use std::mem::{Assume, BikeshedIntrinsicFrom};
|
use std::mem::{Assume, BikeshedIntrinsicFrom};
|
||||||
|
@ -24,7 +18,7 @@ mod assert {
|
||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn not_yet_implemented() {
|
fn main() {
|
||||||
#[repr(C)] struct Unit;
|
#[repr(C)] struct Unit;
|
||||||
assert::is_maybe_transmutable::<&'static Unit, &'static Unit>(); //~ ERROR cannot be safely transmuted
|
assert::is_maybe_transmutable::<&'static u8, &'static Unit>(); //~ ERROR `Unit` cannot be safely transmuted into `u8`
|
||||||
}
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
error[E0277]: `&Unit` cannot be safely transmuted into `&Unit` in the defining scope of `assert::Context`
|
error[E0277]: `Unit` cannot be safely transmuted into `u8` in the defining scope of `assert::Context`
|
||||||
--> $DIR/references.rs:29:52
|
--> $DIR/u8-to-unit.rs:23:50
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<&'static Unit, &'static Unit>();
|
LL | assert::is_maybe_transmutable::<&'static u8, &'static Unit>();
|
||||||
| ^^^^^^^^^^^^^ `&Unit` does not have a well-specified layout
|
| ^^^^^^^^^^^^^ The size of `Unit` is smaller than the size of `u8`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/references.rs:16:14
|
--> $DIR/u8-to-unit.rs:10:14
|
||||||
|
|
|
|
||||||
LL | pub fn is_maybe_transmutable<Src, Dst>()
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
|
||||||
| --------------------- required by a bound in this function
|
| --------------------- required by a bound in this function
|
24
tests/ui/transmutability/references/unit-to-itself.rs
Normal file
24
tests/ui/transmutability/references/unit-to-itself.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// check-pass
|
||||||
|
#![feature(transmutability)]
|
||||||
|
|
||||||
|
mod assert {
|
||||||
|
use std::mem::{Assume, BikeshedIntrinsicFrom};
|
||||||
|
pub struct Context;
|
||||||
|
|
||||||
|
pub fn is_maybe_transmutable<Src, Dst>()
|
||||||
|
where
|
||||||
|
Dst: BikeshedIntrinsicFrom<Src, Context, {
|
||||||
|
Assume {
|
||||||
|
alignment: true,
|
||||||
|
lifetimes: false,
|
||||||
|
safety: true,
|
||||||
|
validity: false,
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[repr(C)] struct Unit;
|
||||||
|
assert::is_maybe_transmutable::<&'static Unit, &'static Unit>();
|
||||||
|
}
|
24
tests/ui/transmutability/references/unit-to-u8.rs
Normal file
24
tests/ui/transmutability/references/unit-to-u8.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// check-fail
|
||||||
|
#![feature(transmutability)]
|
||||||
|
|
||||||
|
mod assert {
|
||||||
|
use std::mem::{Assume, BikeshedIntrinsicFrom};
|
||||||
|
pub struct Context;
|
||||||
|
|
||||||
|
pub fn is_maybe_transmutable<Src, Dst>()
|
||||||
|
where
|
||||||
|
Dst: BikeshedIntrinsicFrom<Src, Context, {
|
||||||
|
Assume {
|
||||||
|
alignment: true,
|
||||||
|
lifetimes: true,
|
||||||
|
safety: true,
|
||||||
|
validity: true,
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[repr(C)] struct Unit;
|
||||||
|
assert::is_maybe_transmutable::<&'static Unit, &'static u8>(); //~ ERROR `Unit` cannot be safely transmuted into `u8`
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
error[E0277]: `&Unit` cannot be safely transmuted into `&Unit` in the defining scope of `assert::Context`
|
error[E0277]: `Unit` cannot be safely transmuted into `u8` in the defining scope of `assert::Context`
|
||||||
--> $DIR/references.rs:29:52
|
--> $DIR/unit-to-u8.rs:23:52
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<&'static Unit, &'static Unit>();
|
LL | assert::is_maybe_transmutable::<&'static Unit, &'static u8>();
|
||||||
| ^^^^^^^^^^^^^ `&Unit` does not have a well-specified layout
|
| ^^^^^^^^^^^ The size of `Unit` is smaller than the size of `u8`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/references.rs:16:14
|
--> $DIR/unit-to-u8.rs:10:14
|
||||||
|
|
|
|
||||||
LL | pub fn is_maybe_transmutable<Src, Dst>()
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
|
||||||
| --------------------- required by a bound in this function
|
| --------------------- required by a bound in this function
|
|
@ -2,7 +2,7 @@ error[E0277]: `()` cannot be safely transmuted into `W<'_>` in the defining scop
|
||||||
--> $DIR/region-infer.rs:20:5
|
--> $DIR/region-infer.rs:20:5
|
||||||
|
|
|
|
||||||
LL | test();
|
LL | test();
|
||||||
| ^^^^ `W<'_>` does not have a well-specified layout
|
| ^^^^ The size of `()` is smaller than the size of `W<'_>`
|
||||||
|
|
|
|
||||||
note: required by a bound in `test`
|
note: required by a bound in `test`
|
||||||
--> $DIR/region-infer.rs:11:12
|
--> $DIR/region-infer.rs:11:12
|
||||||
|
|
|
@ -24,7 +24,7 @@ error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::
|
||||||
--> $DIR/should_require_well_defined_layout.rs:29:47
|
--> $DIR/should_require_well_defined_layout.rs:29:47
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
||||||
| ^^^^^^^^^ `should_reject_repr_rust::unit::repr_rust` does not have a well-specified layout
|
| ^^^^^^^^^ `u128` does not have a well-specified layout
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/should_require_well_defined_layout.rs:13:14
|
--> $DIR/should_require_well_defined_layout.rs:13:14
|
||||||
|
@ -68,7 +68,7 @@ error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::
|
||||||
--> $DIR/should_require_well_defined_layout.rs:35:47
|
--> $DIR/should_require_well_defined_layout.rs:35:47
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
||||||
| ^^^^^^^^^ `should_reject_repr_rust::tuple::repr_rust` does not have a well-specified layout
|
| ^^^^^^^^^ `u128` does not have a well-specified layout
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/should_require_well_defined_layout.rs:13:14
|
--> $DIR/should_require_well_defined_layout.rs:13:14
|
||||||
|
@ -112,7 +112,7 @@ error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::
|
||||||
--> $DIR/should_require_well_defined_layout.rs:41:47
|
--> $DIR/should_require_well_defined_layout.rs:41:47
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
||||||
| ^^^^^^^^^ `should_reject_repr_rust::braces::repr_rust` does not have a well-specified layout
|
| ^^^^^^^^^ `u128` does not have a well-specified layout
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/should_require_well_defined_layout.rs:13:14
|
--> $DIR/should_require_well_defined_layout.rs:13:14
|
||||||
|
@ -156,7 +156,7 @@ error[E0277]: `u128` cannot be safely transmuted into `aligned::repr_rust` in th
|
||||||
--> $DIR/should_require_well_defined_layout.rs:47:47
|
--> $DIR/should_require_well_defined_layout.rs:47:47
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
||||||
| ^^^^^^^^^ `aligned::repr_rust` does not have a well-specified layout
|
| ^^^^^^^^^ `u128` does not have a well-specified layout
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/should_require_well_defined_layout.rs:13:14
|
--> $DIR/should_require_well_defined_layout.rs:13:14
|
||||||
|
@ -200,7 +200,7 @@ error[E0277]: `u128` cannot be safely transmuted into `packed::repr_rust` in the
|
||||||
--> $DIR/should_require_well_defined_layout.rs:53:47
|
--> $DIR/should_require_well_defined_layout.rs:53:47
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
||||||
| ^^^^^^^^^ `packed::repr_rust` does not have a well-specified layout
|
| ^^^^^^^^^ `u128` does not have a well-specified layout
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/should_require_well_defined_layout.rs:13:14
|
--> $DIR/should_require_well_defined_layout.rs:13:14
|
||||||
|
@ -244,7 +244,7 @@ error[E0277]: `u128` cannot be safely transmuted into `nested::repr_c` in the de
|
||||||
--> $DIR/should_require_well_defined_layout.rs:60:47
|
--> $DIR/should_require_well_defined_layout.rs:60:47
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<u128, repr_c>();
|
LL | assert::is_maybe_transmutable::<u128, repr_c>();
|
||||||
| ^^^^^^ `nested::repr_c` does not have a well-specified layout
|
| ^^^^^^ `u128` does not have a well-specified layout
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/should_require_well_defined_layout.rs:13:14
|
--> $DIR/should_require_well_defined_layout.rs:13:14
|
||||||
|
|
|
@ -24,7 +24,7 @@ error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::
|
||||||
--> $DIR/should_require_well_defined_layout.rs:31:43
|
--> $DIR/should_require_well_defined_layout.rs:31:43
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
|
||||||
| ^^^^^^^^^ `should_reject_repr_rust::repr_rust` does not have a well-specified layout
|
| ^^^^^^^^^ `u128` does not have a well-specified layout
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/should_require_well_defined_layout.rs:13:14
|
--> $DIR/should_require_well_defined_layout.rs:13:14
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0277]: `Src` cannot be safely transmuted into `Dst` in the defining scope
|
||||||
--> $DIR/should_pad_variants.rs:44:36
|
--> $DIR/should_pad_variants.rs:44:36
|
||||||
|
|
|
|
||||||
LL | assert::is_transmutable::<Src, Dst, Context>();
|
LL | assert::is_transmutable::<Src, Dst, Context>();
|
||||||
| ^^^ The size of `Src` is smaller than the size of `Dst`
|
| ^^^ At least one value of `Src` isn't a bit-valid value of `Dst`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_transmutable`
|
note: required by a bound in `is_transmutable`
|
||||||
--> $DIR/should_pad_variants.rs:13:14
|
--> $DIR/should_pad_variants.rs:13:14
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0277]: `B` cannot be safely transmuted into `A` in the defining scope of
|
||||||
--> $DIR/transmute-padding-ice.rs:27:40
|
--> $DIR/transmute-padding-ice.rs:27:40
|
||||||
|
|
|
|
||||||
LL | assert::is_maybe_transmutable::<B, A>();
|
LL | assert::is_maybe_transmutable::<B, A>();
|
||||||
| ^ The size of `B` is smaller than the size of `A`
|
| ^ At least one value of `B` isn't a bit-valid value of `A`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_maybe_transmutable`
|
note: required by a bound in `is_maybe_transmutable`
|
||||||
--> $DIR/transmute-padding-ice.rs:11:14
|
--> $DIR/transmute-padding-ice.rs:11:14
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue