From 778696db3df3c32acead51cc98475f2dbe95154b Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Sat, 3 Jul 2021 12:18:13 -0400 Subject: [PATCH] Initial (incomplete) implementation of transmutability trait. This initial implementation handles transmutations between primitive types and some structs, enums and unions. See: https://github.com/rust-lang/compiler-team/issues/411 --- Cargo.lock | 14 + compiler/rustc_hir/src/lang_items.rs | 3 + compiler/rustc_middle/src/traits/select.rs | 4 + compiler/rustc_middle/src/ty/mod.rs | 71 +- compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/abi/mod.rs | 3 +- compiler/rustc_trait_selection/Cargo.toml | 1 + .../src/traits/select/candidate_assembly.rs | 23 + .../src/traits/select/confirmation.rs | 51 + .../src/traits/select/mod.rs | 3 + compiler/rustc_transmute/Cargo.toml | 25 + compiler/rustc_transmute/src/layout/dfa.rs | 189 +++ compiler/rustc_transmute/src/layout/mod.rs | 251 ++++ compiler/rustc_transmute/src/layout/nfa.rs | 179 +++ compiler/rustc_transmute/src/layout/tests.rs | 12 + compiler/rustc_transmute/src/layout/tree.rs | 160 +++ compiler/rustc_transmute/src/lib.rs | 92 ++ .../src/maybe_transmutable/mod.rs | 310 +++++ .../src/maybe_transmutable/query_context.rs | 76 ++ .../src/maybe_transmutable/tests.rs | 34 + library/core/src/mem/mod.rs | 4 + library/core/src/mem/transmutability.rs | 39 + .../ui/transmutability/assume_validity.rs | 128 ++ .../ui/transmutability/assume_validity.stderr | 18 + src/test/ui/transmutability/enums.rs | 233 ++++ src/test/ui/transmutability/enums.stderr | 33 + src/test/ui/transmutability/numbers.rs | 217 ++++ src/test/ui/transmutability/numbers.stderr | 1038 +++++++++++++++++ src/test/ui/transmutability/structs.rs | 160 +++ src/test/ui/transmutability/structs.stderr | 78 ++ src/test/ui/transmutability/unions.rs | 204 ++++ .../ui/transmutability/visibility/assume.rs | 151 +++ .../transmutability/visibility/assume.stderr | 12 + src/test/ui/transmutability/visibility/dst.rs | 116 ++ .../ui/transmutability/visibility/dst.stderr | 60 + src/test/ui/transmutability/visibility/src.rs | 140 +++ .../ui/transmutability/visibility/src.stderr | 12 + 37 files changed, 4143 insertions(+), 2 deletions(-) create mode 100644 compiler/rustc_transmute/Cargo.toml create mode 100644 compiler/rustc_transmute/src/layout/dfa.rs create mode 100644 compiler/rustc_transmute/src/layout/mod.rs create mode 100644 compiler/rustc_transmute/src/layout/nfa.rs create mode 100644 compiler/rustc_transmute/src/layout/tests.rs create mode 100644 compiler/rustc_transmute/src/layout/tree.rs create mode 100644 compiler/rustc_transmute/src/lib.rs create mode 100644 compiler/rustc_transmute/src/maybe_transmutable/mod.rs create mode 100644 compiler/rustc_transmute/src/maybe_transmutable/query_context.rs create mode 100644 compiler/rustc_transmute/src/maybe_transmutable/tests.rs create mode 100644 library/core/src/mem/transmutability.rs create mode 100644 src/test/ui/transmutability/assume_validity.rs create mode 100644 src/test/ui/transmutability/assume_validity.stderr create mode 100644 src/test/ui/transmutability/enums.rs create mode 100644 src/test/ui/transmutability/enums.stderr create mode 100644 src/test/ui/transmutability/numbers.rs create mode 100644 src/test/ui/transmutability/numbers.stderr create mode 100644 src/test/ui/transmutability/structs.rs create mode 100644 src/test/ui/transmutability/structs.stderr create mode 100644 src/test/ui/transmutability/unions.rs create mode 100644 src/test/ui/transmutability/visibility/assume.rs create mode 100644 src/test/ui/transmutability/visibility/assume.stderr create mode 100644 src/test/ui/transmutability/visibility/dst.rs create mode 100644 src/test/ui/transmutability/visibility/dst.stderr create mode 100644 src/test/ui/transmutability/visibility/src.rs create mode 100644 src/test/ui/transmutability/visibility/src.stderr diff --git a/Cargo.lock b/Cargo.lock index df6f46f26cf0d..a17e224931cca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4533,6 +4533,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", + "rustc_transmute", "smallvec", "tracing", ] @@ -4557,6 +4558,19 @@ dependencies = [ "tracing", ] +[[package]] +name = "rustc_transmute" +version = "0.1.0" +dependencies = [ + "rustc_data_structures", + "rustc_infer", + "rustc_macros", + "rustc_middle", + "rustc_span", + "rustc_target", + "tracing", +] + [[package]] name = "rustc_ty_utils" version = "0.0.0" diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 9e5d781892487..406d7d57d4300 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -191,6 +191,9 @@ language_item_table! { CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1); DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1); + // language items relating to transmutability + TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(6); + Add(Op), sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1); Sub(Op), sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1); Mul(Op), sym::mul, mul_trait, Target::Trait, GenericRequirement::Exact(1); diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index ffa70cddbd59c..2733ab226ad1c 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -109,6 +109,10 @@ pub enum SelectionCandidate<'tcx> { /// `false` if there are no *further* obligations. has_nested: bool, }, + + /// Implementation of transmutability trait. + TransmutabilityCandidate, + ParamCandidate(ty::PolyTraitPredicate<'tcx>), ImplCandidate(DefId), AutoImplCandidate(DefId), diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index b386ed68dd246..e7522272da7d1 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -46,8 +46,9 @@ use rustc_target::abi::{Align, VariantIdx}; pub use subst::*; pub use vtable::*; +use std::cmp::Ordering; use std::fmt::Debug; -use std::hash::Hash; +use std::hash::{Hash, Hasher}; use std::ops::ControlFlow; use std::{fmt, str}; @@ -1750,6 +1751,40 @@ impl VariantDef { } } +impl PartialOrd for VariantDef { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(&other)) + } +} + +/// There should be only one VariantDef for each `def_id`, therefore +/// it is fine to implement `Ord` only based on `def_id`. +impl Ord for VariantDef { + fn cmp(&self, other: &Self) -> Ordering { + self.def_id.cmp(&other.def_id) + } +} + +/// There should be only one VariantDef for each `def_id`, therefore +/// it is fine to implement `PartialEq` only based on `def_id`. +impl PartialEq for VariantDef { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.def_id == other.def_id + } +} + +impl Eq for VariantDef {} + +/// There should be only one VariantDef for each `def_id`, therefore +/// it is fine to implement `Hash` only based on `def_id`. +impl Hash for VariantDef { + #[inline] + fn hash(&self, s: &mut H) { + self.def_id.hash(s) + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] pub enum VariantDiscr { /// Explicit value for this variant, i.e., `X = 123`. @@ -1770,6 +1805,40 @@ pub struct FieldDef { pub vis: Visibility, } +impl PartialOrd for FieldDef { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(&other)) + } +} + +/// There should be only one FieldDef for each `did`, therefore +/// it is fine to implement `Ord` only based on `did`. +impl Ord for FieldDef { + fn cmp(&self, other: &Self) -> Ordering { + self.did.cmp(&other.did) + } +} + +/// There should be only one FieldDef for each `did`, therefore +/// it is fine to implement `PartialEq` only based on `did`. +impl PartialEq for FieldDef { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.did == other.did + } +} + +impl Eq for FieldDef {} + +/// There should be only one FieldDef for each `did`, therefore +/// it is fine to implement `Hash` only based on `did`. +impl Hash for FieldDef { + #[inline] + fn hash(&self, s: &mut H) { + self.did.hash(s) + } +} + bitflags! { #[derive(TyEncodable, TyDecodable, Default, HashStable)] pub struct ReprFlags: u8 { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 6daf811e26f14..03d6941fd4ffa 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1434,6 +1434,7 @@ symbols! { trait_alias, trait_upcasting, transmute, + transmute_trait, transparent, transparent_enums, transparent_unions, diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index d1eafd6ac5fb8..4afe71c2feeae 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -508,6 +508,7 @@ impl fmt::Debug for Align { impl Align { pub const ONE: Align = Align { pow2: 0 }; + pub const MAX: Align = Align { pow2: 29 }; #[inline] pub fn from_bits(bits: u64) -> Result { @@ -540,7 +541,7 @@ impl Align { if bytes != 1 { return Err(not_power_of_2(align)); } - if pow2 > 29 { + if pow2 > Self::MAX.pow2 { return Err(too_large(align)); } diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index d59bdae0332cb..ca49861b38fa0 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -23,4 +23,5 @@ rustc_query_system = { path = "../rustc_query_system" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } +rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] } smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index c8b4303e1e0fe..34bd13ecb1c38 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -305,6 +305,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_candidates_for_unsizing(obligation, &mut candidates); } else if lang_items.destruct_trait() == Some(def_id) { self.assemble_const_destruct_candidates(obligation, &mut candidates); + } else if lang_items.transmute_trait() == Some(def_id) { + // User-defined transmutability impls are permitted. + self.assemble_candidates_from_impls(obligation, &mut candidates); + self.assemble_candidates_for_transmutability(obligation, &mut candidates); } else { if lang_items.clone_trait() == Some(def_id) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support @@ -873,6 +877,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; } + #[tracing::instrument(level = "debug", skip(self, obligation, candidates))] + fn assemble_candidates_for_transmutability( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + if obligation.has_param_types_or_consts() { + candidates.ambiguous = false; + return; + } + + if obligation.has_infer_types_or_consts() { + candidates.ambiguous = true; + return; + } + + candidates.vec.push(TransmutabilityCandidate); + } + #[tracing::instrument(level = "debug", skip(self, obligation, candidates))] fn assemble_candidates_for_trait_alias( &mut self, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 5942bb79d69e8..d6c29362c2488 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -77,6 +77,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(ImplSource::Builtin(data)) } + TransmutabilityCandidate => { + self.confirm_transmutability_candidate(obligation).map(ImplSource::Builtin) + } + ParamCandidate(param) => { let obligations = self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref)); @@ -286,6 +290,53 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSourceBuiltinData { nested: obligations } } + fn confirm_transmutability_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> Result>, SelectionError<'tcx>> { + debug!(?obligation, "confirm_transmutability_candidate"); + + let predicate = obligation.predicate; + + let type_at = |i| predicate.map_bound(|p| p.trait_ref.substs.type_at(i)); + let bool_at = |i| { + predicate + .skip_binder() + .trait_ref + .substs + .const_at(i) + .try_eval_bool(self.tcx(), obligation.param_env) + .unwrap() + }; + + let src_and_dst = predicate.map_bound(|p| rustc_transmute::Types { + src: p.trait_ref.substs.type_at(1), + dst: p.trait_ref.substs.type_at(0), + }); + + let scope = type_at(2).skip_binder(); + + let assume = rustc_transmute::Assume { + alignment: bool_at(3), + lifetimes: bool_at(4), + validity: bool_at(5), + visibility: bool_at(6), + }; + + let cause = obligation.cause.clone(); + + let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx); + + let maybe_transmutable = transmute_env.is_transmutable(cause, src_and_dst, scope, assume); + + use rustc_transmute::Answer; + + match maybe_transmutable { + Answer::Yes => Ok(ImplSourceBuiltinData { nested: vec![] }), + _ => Err(Unimplemented), + } + } + /// This handles the case where an `auto trait Foo` impl is being used. /// The idea is that the impl applies to `X : Foo` if the following conditions are met: /// diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index ee2c8da5a005d..0dd02cd2c18b6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1551,6 +1551,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); } + // FIXME(@jswrenn): this should probably be more sophisticated + (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => false, + // (*) ( BuiltinCandidate { has_nested: false } diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml new file mode 100644 index 0000000000000..f18d64fc10999 --- /dev/null +++ b/compiler/rustc_transmute/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "rustc_transmute" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tracing = "0.1" +rustc_data_structures = { path = "../rustc_data_structures", optional = true} +rustc_infer = { path = "../rustc_infer", optional = true} +rustc_macros = { path = "../rustc_macros", optional = true} +rustc_middle = { path = "../rustc_middle", optional = true} +rustc_span = { path = "../rustc_span", optional = true} +rustc_target = { path = "../rustc_target", optional = true} + +[features] +rustc = [ + "rustc_middle", + "rustc_data_structures", + "rustc_infer", + "rustc_macros", + "rustc_span", + "rustc_target", +] diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs new file mode 100644 index 0000000000000..8fe1b16d904ea --- /dev/null +++ b/compiler/rustc_transmute/src/layout/dfa.rs @@ -0,0 +1,189 @@ +use super::{nfa, Byte, Nfa, Ref}; +use crate::{Map, Set}; +use std::fmt; +use std::sync::atomic::{AtomicU64, Ordering}; + +#[derive(PartialEq, Clone, Debug)] +pub(crate) struct Dfa +where + R: Ref, +{ + pub(crate) transitions: Map>, + pub(crate) start: State, + pub(crate) accepting: State, +} + +#[derive(PartialEq, Clone, Debug)] +pub(crate) struct Transitions +where + R: Ref, +{ + byte_transitions: Map, + ref_transitions: Map, +} + +impl Default for Transitions +where + R: Ref, +{ + fn default() -> Self { + Self { byte_transitions: Map::default(), ref_transitions: Map::default() } + } +} + +impl Transitions +where + R: Ref, +{ + fn insert(&mut self, transition: Transition, state: State) { + match transition { + Transition::Byte(b) => { + self.byte_transitions.insert(b, state); + } + Transition::Ref(r) => { + self.ref_transitions.insert(r, state); + } + } + } +} + +/// The states in a `Nfa` represent byte offsets. +#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Copy, Clone)] +pub(crate) struct State(u64); + +#[derive(Hash, Eq, PartialEq, Clone, Copy)] +pub(crate) enum Transition +where + R: Ref, +{ + Byte(Byte), + Ref(R), +} + +impl fmt::Debug for State { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "S_{}", self.0) + } +} + +impl fmt::Debug for Transition +where + R: Ref, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self { + Self::Byte(b) => b.fmt(f), + Self::Ref(r) => r.fmt(f), + } + } +} + +impl Dfa +where + R: Ref, +{ + pub(crate) fn unit() -> Self { + let transitions: Map> = Map::default(); + let start = State::new(); + let accepting = start; + + Self { transitions, start, accepting } + } + + #[cfg(test)] + pub(crate) fn bool() -> Self { + let mut transitions: Map> = Map::default(); + let start = State::new(); + let accepting = State::new(); + + transitions.entry(start).or_default().insert(Transition::Byte(Byte::Init(0x00)), accepting); + + transitions.entry(start).or_default().insert(Transition::Byte(Byte::Init(0x01)), accepting); + + Self { transitions, start, accepting } + } + + #[tracing::instrument] + pub(crate) fn from_nfa(nfa: Nfa) -> Self { + #[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))] + fn nfa_set_transitions( + starts: &Vec, + transitions: &Map, Set>>, + ) -> Map, Vec> + where + R: Ref, + { + let mut map: Map, Vec> = Map::default(); + + for (transition, states) in starts + .into_iter() + .map(|start| transitions.get(start).into_iter().flatten()) + .flatten() + { + map.entry(transition.clone()).or_default().extend(states); + } + map + } + + let Nfa { transitions: nfa_transitions, start: nfa_start, accepting: nfa_accepting } = nfa; + let mut dfa_transitions: Map> = Map::default(); + + let mut nfa_to_dfa: Map, State> = Map::default(); + + let nfa_start_set = vec![nfa_start]; + nfa_to_dfa.insert(nfa_start_set.clone(), State::new()); + + let mut queue = vec![nfa_start_set.clone()]; + + while let Some(nfa_states) = queue.pop() { + let dfa_state = *nfa_to_dfa.get(&nfa_states).unwrap(); + #[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))] + for (transition, nfa_states_prime) in nfa_set_transitions(&nfa_states, &nfa_transitions) + { + let dfa_state_prime = State::new(); + nfa_to_dfa.insert(nfa_states_prime.clone(), dfa_state_prime); + dfa_transitions + .entry(dfa_state) + .or_default() + .insert(transition.into(), dfa_state_prime); + queue.push(nfa_states_prime); + } + } + + let dfa_start = nfa_to_dfa[&nfa_start_set]; + let dfa_accepting = nfa_to_dfa[&vec![nfa_accepting]]; + + Self { transitions: dfa_transitions, start: dfa_start, accepting: dfa_accepting } + } + + pub(crate) fn bytes_from(&self, start: State) -> Option<&Map> { + Some(&self.transitions.get(&start)?.byte_transitions) + } + + pub(crate) fn byte_from(&self, start: State, byte: Byte) -> Option { + self.transitions.get(&start)?.byte_transitions.get(&byte).copied() + } + + pub(crate) fn refs_from(&self, start: State) -> Option<&Map> { + Some(&self.transitions.get(&start)?.ref_transitions) + } +} + +impl State { + pub(crate) fn new() -> Self { + static COUNTER: AtomicU64 = AtomicU64::new(0); + Self(COUNTER.fetch_add(1, Ordering::SeqCst)) + } +} + +impl From> for Transition +where + R: Ref, +{ + fn from(nfa_transition: nfa::Transition) -> Self { + match nfa_transition { + nfa::Transition::Byte(byte) => Transition::Byte(byte), + nfa::Transition::Ref(r) => Transition::Ref(r), + } + } +} diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs new file mode 100644 index 0000000000000..77ad0c1e0f629 --- /dev/null +++ b/compiler/rustc_transmute/src/layout/mod.rs @@ -0,0 +1,251 @@ +use std::fmt::{self, Debug}; +use std::hash::Hash; + +pub(crate) mod tree; +pub(crate) use tree::Tree; + +pub(crate) mod nfa; +pub(crate) use nfa::Nfa; + +pub(crate) mod dfa; +pub(crate) use dfa::Dfa; + +#[cfg(test)] +mod tests; + +#[derive(Debug)] +pub(crate) struct Uninhabited; + +/// An instance of a byte is either initialized to a particular value, or uninitialized. +#[derive(Hash, Eq, PartialEq, Clone, Copy)] +pub(crate) enum Byte { + Uninit, + Init(u8), +} + +impl fmt::Debug for Byte { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self { + Self::Uninit => f.write_str("??u8"), + Self::Init(b) => write!(f, "{:#04x}u8", b), + } + } +} + +pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {} +pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {} + +impl Def for ! {} +impl Ref for ! {} + +#[cfg(feature = "rustc")] +pub(crate) mod rustc { + use super::Tree; + + use rustc_middle::mir::Mutability; + use rustc_middle::ty; + use rustc_middle::ty::util::Discr; + use rustc_middle::ty::AdtDef; + use rustc_middle::ty::Region; + use rustc_middle::ty::SubstsRef; + use rustc_middle::ty::Ty; + use rustc_middle::ty::TyCtxt; + use rustc_middle::ty::VariantDef; + use std::alloc::Layout; + + /// A reference in the layout [`Nfa`]. + #[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)] + pub struct Ref<'tcx> { + lifetime: Region<'tcx>, + ty: Ty<'tcx>, + mutability: Mutability, + } + + impl<'tcx> super::Ref for Ref<'tcx> {} + + impl<'tcx> Ref<'tcx> { + pub fn min_align(&self) -> usize { + todo!() + } + } + + /// A visibility node in the layout [`Nfa`]. + #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] + pub enum Def<'tcx> { + Adt(ty::AdtDef<'tcx>), + Variant(&'tcx ty::VariantDef), + Field(&'tcx ty::FieldDef), + Primitive, + } + + impl<'tcx> super::Def for Def<'tcx> {} + + impl<'tcx> Tree, Ref<'tcx>> { + pub fn from_ty(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Result { + use rustc_middle::ty::FloatTy::*; + use rustc_middle::ty::IntTy::*; + use rustc_middle::ty::UintTy::*; + use rustc_target::abi::HasDataLayout; + + let target = tcx.data_layout(); + + match 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::Adt(adt_def, substs_ref) => { + use rustc_middle::ty::AdtKind; + + // The layout begins with this adt's visibility. + let vis = Self::vis(Def::Adt(*adt_def)); + + // And is followed the layout(s) of its variants + Ok(vis.then(match adt_def.adt_kind() { + AdtKind::Struct => { + // is the layout well-defined? + if !adt_def.repr().c() { + return Err(()); + } + + Self::from_repr_c_variant( + ty, + *adt_def, + substs_ref, + None, + adt_def.non_enum_variant(), + tcx, + )? + } + AdtKind::Enum => { + // is the layout well-defined? + if !(adt_def.repr().c() || adt_def.repr().int.is_some()) { + return Err(()); + } + + let mut variants = vec![]; + + for (idx, discr) in adt_def.discriminants(tcx) { + variants.push(Self::from_repr_c_variant( + ty, + *adt_def, + substs_ref, + Some(discr), + adt_def.variant(idx), + tcx, + )?); + } + + Self::Alt(variants) + } + AdtKind::Union => { + // is the layout well-defined? + if !adt_def.repr().c() { + return Err(()); + } + + let ty_layout = layout_of(tcx, ty); + + let mut variants = vec![]; + + for field in adt_def.all_fields() { + let variant_ty = field.ty(tcx, substs_ref); + let variant_layout = layout_of(tcx, variant_ty); + + let padding_needed = ty_layout.size() - variant_layout.size(); + let def = Def::Field(field); + let variant = Self::from_ty(variant_ty, tcx)? + .then(Self::padding(padding_needed)); + + variants.push(variant); + } + + Self::Alt(variants) + } + })) + } + _ => Err(()), + } + } + + pub fn from_repr_c_variant( + ty: Ty<'tcx>, + adt_def: AdtDef<'tcx>, + substs_ref: SubstsRef<'tcx>, + discr: Option>, + variant_def: &'tcx VariantDef, + tcx: TyCtxt<'tcx>, + ) -> Result { + use rustc_target::abi::Align; + + let mut seq = vec![]; + + let repr = adt_def.repr(); + let max_align = repr.align.unwrap_or(Align::MAX); + + let ty_layout = layout_of(tcx, ty); + let mut variant_layout = Layout::from_size_align(0, ty_layout.align()).unwrap(); + + // The layout of the variant is prefixed by the discriminant, if any. + if let Some(discr) = discr { + let discr_layout = layout_of(tcx, discr.ty); + variant_layout = variant_layout.extend(discr_layout).unwrap().0; + let disr = Self::from_disr(discr, tcx); + seq.push(disr); + } + + // Next come fields. + for field_def in variant_def.fields.iter() { + //let field_vis = Self::vis(Def::Field(field_def)); + let field_ty = field_def.ty(tcx, substs_ref); + let field_layout = layout_of(tcx, field_ty); + + let padding_needed = variant_layout + .padding_needed_for(field_layout.align()) + .min(max_align.bytes().try_into().unwrap()); + + let padding = Self::padding(padding_needed); + + variant_layout = variant_layout.extend(field_layout).unwrap().0; + + seq.push(padding); + seq.push(Self::from_ty(field_ty, tcx)?); + } + + // Finally: padding. + let padding_needed = ty_layout.size() - variant_layout.size(); + let nfa = seq.push(Self::padding(padding_needed)); + + Ok(Self::Seq(seq)) + } + + pub fn from_disr(discr: Discr<'tcx>, tcx: TyCtxt<'tcx>) -> Self { + // FIXME(@jswrenn): I'm certain this is missing needed endian nuance. + let width = layout_of(tcx, discr.ty).size(); + let bytes = discr.val.to_ne_bytes(); + let bytes = &bytes[..width]; + Self::Seq(bytes.into_iter().copied().map(|b| Self::from_bits(b)).collect()) + } + } + + fn layout_of<'tcx>(ctx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Layout { + use rustc_middle::ty::{ParamEnv, ParamEnvAnd}; + use rustc_target::abi::TyAndLayout; + + 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).unwrap(); + Layout::from_size_align( + layout.size().bytes_usize(), + layout.align().abi.bytes().try_into().unwrap(), + ) + .unwrap() + } +} diff --git a/compiler/rustc_transmute/src/layout/nfa.rs b/compiler/rustc_transmute/src/layout/nfa.rs new file mode 100644 index 0000000000000..817e426ba274e --- /dev/null +++ b/compiler/rustc_transmute/src/layout/nfa.rs @@ -0,0 +1,179 @@ +use super::{Byte, Ref, Tree, Uninhabited}; +use crate::{Map, Set}; +use std::fmt; +use std::sync::atomic::{AtomicU64, Ordering}; + +/// A non-deterministic finite automaton (NFA) that represents the layout of a type. +/// The transmutability of two given types is computed by comparing their `Nfa`s. +#[derive(PartialEq, Debug)] +pub(crate) struct Nfa +where + R: Ref, +{ + pub(crate) transitions: Map, Set>>, + pub(crate) start: State, + pub(crate) accepting: State, +} + +/// The states in a `Nfa` represent byte offsets. +#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Copy, Clone)] +pub(crate) struct State(u64); + +/// The transitions between states in a `Nfa` reflect bit validity. +#[derive(Hash, Eq, PartialEq, Clone, Copy)] +pub(crate) enum Transition +where + R: Ref, +{ + Byte(Byte), + Ref(R), +} + +impl fmt::Debug for State { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "S_{}", self.0) + } +} + +impl fmt::Debug for Transition +where + R: Ref, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self { + Self::Byte(b) => b.fmt(f), + Self::Ref(r) => r.fmt(f), + } + } +} + +impl Nfa +where + R: Ref, +{ + pub(crate) fn unit() -> Self { + let transitions: Map, Set>> = Map::default(); + let start = State::new(); + let accepting = start; + + Nfa { transitions, start, accepting } + } + + pub(crate) fn from_byte(byte: Byte) -> Self { + let mut transitions: Map, Set>> = Map::default(); + let start = State::new(); + let accepting = State::new(); + + let source = transitions.entry(start).or_default(); + let edge = source.entry(Transition::Byte(byte)).or_default(); + edge.insert(accepting); + + Nfa { transitions, start, accepting } + } + + pub(crate) fn from_ref(r: R) -> Self { + let mut transitions: Map, Set>> = Map::default(); + let start = State::new(); + let accepting = State::new(); + + let source = transitions.entry(start).or_default(); + let edge = source.entry(Transition::Ref(r)).or_default(); + edge.insert(accepting); + + Nfa { transitions, start, accepting } + } + + pub(crate) fn from_tree(tree: Tree) -> Result { + Ok(match tree { + Tree::Byte(b) => Self::from_byte(b), + Tree::Def(..) => unreachable!(), + Tree::Ref(r) => Self::from_ref(r), + Tree::Alt(alts) => { + let mut alts = alts.into_iter().map(Self::from_tree); + let mut nfa = alts.next().ok_or(Uninhabited)??; + for alt in alts { + nfa = nfa.union(&alt?); + } + nfa + } + Tree::Seq(elts) => { + let mut nfa = Self::unit(); + for elt in elts.into_iter().map(Self::from_tree) { + nfa = nfa.concat(elt?); + } + nfa + } + }) + } + + /// Concatenate two `Nfa`s. + pub(crate) fn concat(self, other: Self) -> Self { + if self.start == self.accepting { + return other; + } else if other.start == other.accepting { + return self; + } + + let start = self.start; + let accepting = other.accepting; + + let mut transitions: Map, Set>> = self.transitions; + + // the iteration order doesn't matter + #[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))] + for (source, transition) in other.transitions { + let fix_state = |state| if state == other.start { self.accepting } else { state }; + let entry = transitions.entry(fix_state(source)).or_default(); + for (edge, destinations) in transition { + let entry = entry.entry(edge.clone()).or_default(); + for destination in destinations { + entry.insert(fix_state(destination)); + } + } + } + + Self { transitions, start, accepting } + } + + /// Compute the union of two `Nfa`s. + pub(crate) fn union(&self, other: &Self) -> Self { + let start = self.start; + let accepting = self.accepting; + + let mut transitions: Map, Set>> = self.transitions.clone(); + + // the iteration order doesn't matter + #[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))] + for (&(mut source), transition) in other.transitions.iter() { + // if source is starting state of `other`, replace with starting state of `self` + if source == other.start { + source = self.start; + } + let entry = transitions.entry(source).or_default(); + for (edge, destinations) in transition { + let entry = entry.entry(edge.clone()).or_default(); + // the iteration order doesn't matter + #[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))] + for &(mut destination) in destinations { + // if dest is accepting state of `other`, replace with accepting state of `self` + if destination == other.accepting { + destination = self.accepting; + } + entry.insert(destination); + } + } + } + Self { transitions, start, accepting } + } + + pub(crate) fn edges_from(&self, start: State) -> Option<&Map, Set>> { + self.transitions.get(&start) + } +} + +impl State { + pub(crate) fn new() -> Self { + static COUNTER: AtomicU64 = AtomicU64::new(0); + Self(COUNTER.fetch_add(1, Ordering::SeqCst)) + } +} diff --git a/compiler/rustc_transmute/src/layout/tests.rs b/compiler/rustc_transmute/src/layout/tests.rs new file mode 100644 index 0000000000000..af6c3edd84cc4 --- /dev/null +++ b/compiler/rustc_transmute/src/layout/tests.rs @@ -0,0 +1,12 @@ +#[test] +fn should_translate_correctly() { + let b = crate::layout::Tree::::bool(); + println!("tree: {:?}\n", b); + let b = b.de_def(); + println!("tree: {:?}\n", b); + let b = crate::layout::Nfa::from_tree(b).unwrap(); + println!("nfa: {:#?}\n", b); + let b = crate::layout::Dfa::from_nfa(b); + println!("dfa: {:#?}\n", b); + assert!(true); +} diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs new file mode 100644 index 0000000000000..8fb96751fb661 --- /dev/null +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -0,0 +1,160 @@ +use super::{Byte, Def, Ref}; + +#[derive(Clone, Debug)] +pub(crate) enum Tree +where + D: Def, + R: Ref, +{ + Seq(Vec), + Alt(Vec), + Def(D), + Ref(R), + Byte(Byte), +} + +impl Tree +where + D: Def, + R: Ref, +{ + pub fn vis(def: D) -> Self { + Self::Def(def) + } + + /// A `Tree` representing an uninhabited type. + pub(crate) fn uninhabited() -> Self { + Self::Alt(vec![]) + } + + /// A `Tree` representing a zero-sized type. + pub(crate) fn unit() -> Self { + Self::Seq(Vec::new()) + } + + /// A `Tree` containing a single, uninitialized byte. + pub(crate) fn uninit() -> Self { + Self::Byte(Byte::Uninit) + } + + /// A `Tree` representing the layout of `bool`. + pub(crate) fn bool() -> Self { + Self::from_bits(0x00).or(Self::from_bits(0x01)) + } + + pub(crate) fn u8() -> Self { + Self::Alt((0u8..=255).map(Self::from_bits).collect()) + } + + pub(crate) fn from_bits(bits: u8) -> Self { + Self::Byte(Byte::Init(bits)) + } + + pub(crate) fn number(width_in_bytes: usize) -> Self { + Self::Seq(vec![Self::u8(); width_in_bytes]) + } + + pub(crate) fn padding(width_in_bytes: usize) -> Self { + Self::Seq(vec![Self::uninit(); width_in_bytes]) + } + + pub(crate) fn de_def(self) -> Tree { + match self { + Self::Seq(elts) => Tree::Seq( + elts.into_iter() + .filter_map( + |elt| if let Self::Def(..) = elt { None } else { Some(elt.de_def()) }, + ) + .collect(), + ), + Self::Alt(alts) => Tree::Alt( + alts.into_iter() + .filter_map( + |alt| if let Self::Def(..) = alt { None } else { Some(alt.de_def()) }, + ) + .collect(), + ), + Self::Byte(b) => Tree::Byte(b), + Self::Ref(r) => Tree::Ref(r), + Self::Def(d) => Tree::uninhabited(), + } + } + + /// Remove all `Def` nodes, and all branches of the layout for which `f` produces false. + pub(crate) fn prune(self, f: &F) -> Tree + where + F: Fn(&D) -> bool, + { + match self { + Self::Seq(elts) => { + let mut pruned = vec![]; + for elt in elts { + if let Self::Def(d) = elt { + if !f(&d) { + return Tree::uninhabited(); + } + } else { + pruned.push(elt.prune(f)); + } + } + Tree::Seq(pruned) + } + Self::Alt(alts) => Tree::Alt( + alts.into_iter() + .filter_map(|alt| { + if let Self::Def(d) = alt { None } else { Some(alt.prune(f.clone())) } + }) + .collect(), + ), + Self::Byte(b) => Tree::Byte(b), + Self::Ref(r) => Tree::Ref(r), + Self::Def(d) => Tree::uninhabited(), + } + } + + pub(crate) fn is_inhabited(&self) -> bool { + match self { + Self::Seq(elts) => elts.into_iter().all(|elt| elt.is_inhabited()), + Self::Alt(alts) => alts.into_iter().any(|alt| alt.is_inhabited()), + Self::Byte(..) | Self::Ref(..) | Self::Def(..) => true, + } + } +} + +impl Tree +where + D: Def, + R: Ref, +{ + pub(crate) fn then(self, other: Self) -> Self { + match (self, other) { + (Self::Seq(mut lhs), Self::Seq(mut rhs)) => { + lhs.append(&mut rhs); + Self::Seq(lhs) + } + (Self::Seq(mut lhs), rhs) => { + lhs.push(rhs); + Self::Seq(lhs) + } + (lhs, Self::Seq(mut rhs)) => { + rhs.insert(0, lhs); + Self::Seq(rhs) + } + (lhs, rhs) => Self::Seq(vec![lhs, rhs]), + } + } + + pub(crate) fn or(self, other: Self) -> Self { + match (self, other) { + (Self::Alt(mut lhs), Self::Alt(rhs)) => { + lhs.extend(rhs); + Self::Alt(lhs) + } + (Self::Alt(mut alts), alt) | (alt, Self::Alt(mut alts)) => { + alts.push(alt); + Self::Alt(alts) + } + (lhs, rhs) => Self::Alt(vec![lhs, rhs]), + } + } +} diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs new file mode 100644 index 0000000000000..677c1dc50cc37 --- /dev/null +++ b/compiler/rustc_transmute/src/lib.rs @@ -0,0 +1,92 @@ +#![feature(alloc_layout_extra, control_flow_enum, decl_macro, iterator_try_reduce, never_type)] +#![allow(dead_code, unused_variables)] + +#[cfg(feature = "rustc")] +pub(crate) use rustc_data_structures::fx::{FxHashMap as Map, FxHashSet as Set}; + +#[cfg(not(feature = "rustc"))] +pub(crate) use std::collections::{HashMap as Map, HashSet as Set}; + +pub(crate) mod layout; +pub(crate) mod maybe_transmutable; + +#[derive(Default)] +pub struct Assume { + pub alignment: bool, + pub lifetimes: bool, + pub validity: bool, + pub visibility: bool, +} + +/// The type encodes answers to the question: "Are these types transmutable?" +#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)] +pub enum Answer +where + R: layout::Ref, +{ + /// `Src` is transmutable into `Dst`. + Yes, + + /// `Src` is NOT transmutable into `Dst`. + No, + + /// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`. + IfTransmutable { src: R, dst: R }, + + /// `Src` is transmutable into `Dst`, if all of the enclosed requirements are met. + IfAll(Vec>), + + /// `Src` is transmutable into `Dst` if any of the enclosed requirements are met. + IfAny(Vec>), +} + +#[cfg(feature = "rustc")] +mod rustc { + use rustc_infer::infer::InferCtxt; + use rustc_macros::TypeFoldable; + use rustc_middle::traits::ObligationCause; + use rustc_middle::ty::Binder; + use rustc_middle::ty::Ty; + + /// The source and destination types of a transmutation. + #[derive(TypeFoldable, Debug, Clone, Copy)] + pub struct Types<'tcx> { + /// The source type. + pub src: Ty<'tcx>, + /// The destination type. + pub dst: Ty<'tcx>, + } + + pub struct TransmuteTypeEnv<'cx, 'tcx> { + infcx: &'cx InferCtxt<'cx, 'tcx>, + } + + impl<'cx, 'tcx> TransmuteTypeEnv<'cx, 'tcx> { + pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> Self { + Self { infcx } + } + + #[allow(unused)] + pub fn is_transmutable( + &mut self, + cause: ObligationCause<'tcx>, + src_and_dst: Binder<'tcx, Types<'tcx>>, + scope: Ty<'tcx>, + assume: crate::Assume, + ) -> crate::Answer> { + let src = src_and_dst.map_bound(|types| types.src).skip_binder(); + let dst = src_and_dst.map_bound(|types| types.dst).skip_binder(); + crate::maybe_transmutable::MaybeTransmutableQuery::new( + src, + dst, + scope, + assume, + self.infcx.tcx, + ) + .answer() + } + } +} + +#[cfg(feature = "rustc")] +pub use rustc::*; diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs new file mode 100644 index 0000000000000..d41f3e29a6f50 --- /dev/null +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -0,0 +1,310 @@ +use crate::Answer; +use crate::Map; + +#[cfg(test)] +mod tests; + +mod query_context; +use query_context::QueryContext; + +use crate::layout::{self, dfa, Byte, Dfa, Nfa, Tree}; + +pub(crate) struct MaybeTransmutableQuery +where + C: QueryContext, +{ + src: L, + dst: L, + scope: ::Scope, + assume: crate::Assume, + context: C, +} + +impl MaybeTransmutableQuery +where + C: QueryContext, +{ + pub(crate) fn new( + src: L, + dst: L, + scope: ::Scope, + assume: crate::Assume, + context: C, + ) -> Self { + Self { src, dst, scope, assume, context } + } + + pub(crate) fn map_layouts( + self, + f: F, + ) -> Result, Answer<::Ref>> + where + F: FnOnce( + L, + L, + ::Scope, + &C, + ) -> Result<(M, M), Answer<::Ref>>, + { + let Self { src, dst, scope, assume, context } = self; + + let (src, dst) = f(src, dst, scope, &context)?; + + Ok(MaybeTransmutableQuery { src, dst, scope, assume, context }) + } +} + +#[cfg(feature = "rustc")] +mod rustc { + use super::*; + use rustc_middle::ty::Ty; + use rustc_middle::ty::TyCtxt; + + impl<'tcx> MaybeTransmutableQuery, TyCtxt<'tcx>> { + /// This method begins by converting `src` and `dst` from `Ty`s to `Tree`s, + /// then computes an answer using those trees. + #[tracing::instrument(skip(self))] + pub fn answer(self) -> Answer< as QueryContext>::Ref> { + tracing::trace!("bing"); + let query_or_answer = self.map_layouts(|src, dst, scope, &context| { + let src = if let Ok(tree) = Tree::from_ty(src, context) { + tree + } else { + // The layout of `src` is unspecified. + return Err(Answer::No); + }; + let dst = if let Ok(tree) = Tree::from_ty(dst, context) { + tree + } else { + // The layout of `dstsrc` is unspecified. + return Err(Answer::No); + }; + + Ok((src, dst)) + }); + + match query_or_answer { + Ok(query) => query.answer(), + Err(answer) => answer, + } + } + } +} + +impl MaybeTransmutableQuery::Def, ::Ref>, C> +where + C: QueryContext, +{ + /// Answers whether a `Tree` is transmutable into another `Tree`. + /// + /// This method begins by de-def'ing `src` and `dst`, and prunes private paths from `dst`, + /// then converts `src` and `dst` to `Nfa`s, and computes an answer using those NFAs. + #[inline(always)] + #[tracing::instrument(skip(self))] + pub(crate) fn answer(self) -> Answer<::Ref> { + tracing::trace!("bang"); + let query_or_answer = self.map_layouts(|src, dst, scope, context| { + let src = src.de_def(); + let dst = dst.prune(&|def| context.is_accessible_from(*def, scope)); + + let src = if let Ok(nfa) = Nfa::from_tree(src) { + nfa + } else { + return Err(Answer::Yes); + }; + + let dst = if let Ok(nfa) = Nfa::from_tree(dst) { + nfa + } else { + return Err(Answer::No); + }; + + Ok((src, dst)) + }); + + match query_or_answer { + Ok(query) => query.answer(), + Err(answer) => answer, + } + } +} + +impl MaybeTransmutableQuery::Ref>, C> +where + C: QueryContext, +{ + /// Answers whether a `Nfa` is transmutable into another `Nfa`. + /// + /// This method converts `src` and `dst` to DFAs, then computes an answer using those DFAs. + #[inline(always)] + #[tracing::instrument(skip(self))] + pub(crate) fn answer(self) -> Answer<::Ref> { + tracing::trace!("boom"); + let query_or_answer = self + .map_layouts(|src, dst, scope, context| Ok((Dfa::from_nfa(src), Dfa::from_nfa(dst)))); + + match query_or_answer { + Ok(query) => query.answer(), + Err(answer) => answer, + } + } +} + +impl MaybeTransmutableQuery::Ref>, C> +where + C: QueryContext, +{ + /// Answers whether a `Nfa` is transmutable into another `Nfa`. + /// + /// This method converts `src` and `dst` to DFAs, then computes an answer using those DFAs. + pub(crate) fn answer(self) -> Answer<::Ref> { + MaybeTransmutableQuery { + src: &self.src, + dst: &self.dst, + scope: self.scope, + assume: self.assume, + context: self.context, + } + .answer() + } +} + +impl<'l, C> MaybeTransmutableQuery<&'l Dfa<::Ref>, C> +where + C: QueryContext, +{ + pub(crate) fn answer(&mut self) -> Answer<::Ref> { + tracing::trace!("bam"); + self.answer_memo(&mut Map::default(), self.src.start, self.dst.start) + } + + #[inline(always)] + #[tracing::instrument(skip(self))] + fn answer_memo( + &self, + cache: &mut Map<(dfa::State, dfa::State), Answer<::Ref>>, + src_state: dfa::State, + dst_state: dfa::State, + ) -> Answer<::Ref> { + if let Some(answer) = cache.get(&(src_state, dst_state)) { + answer.clone() + } else { + let answer = if dst_state == self.dst.accepting { + // truncation: `size_of(Src) >= size_of(Dst)` + tracing::debug!("done! dst is accepting"); + Answer::Yes + } else if src_state == self.src.accepting { + unimplemented!() + } else { + let src_quantification = if self.assume.validity { + // if the compiler may assume that the programmer is doing additional validity checks, + // (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... + there_exists + } else { + // 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 there must exist at least one transition out of `src_state` such that the transmute is viable... + for_all + }; + + src_quantification( + self.src.bytes_from(src_state).unwrap_or(&Map::default()), + |(&src_validity, &src_state_prime)| { + if let Some(dst_state_prime) = self.dst.byte_from(dst_state, src_validity) { + self.answer_memo(cache, src_state_prime, dst_state_prime) + } else if let Some(dst_state_prime) = + self.dst.byte_from(dst_state, Byte::Uninit) + { + self.answer_memo(cache, src_state_prime, dst_state_prime) + } else { + Answer::No + } + }, + ) + }; + cache.insert((src_state, dst_state), answer.clone()); + answer + } + } +} + +impl Answer +where + R: layout::Ref, +{ + pub(crate) fn and_with(self, rhs: F) -> Self + where + F: FnOnce() -> Self, + { + if let Self::No = self { Self::No } else { self.and(rhs()) } + } + + pub(crate) fn and(self, rhs: Self) -> Self { + match (self, rhs) { + (Self::No, _) | (_, Self::No) => Self::No, + (Self::Yes, Self::Yes) => Self::Yes, + (Self::IfAll(mut lhs), Self::IfAll(ref mut rhs)) => { + lhs.append(rhs); + Self::IfAll(lhs) + } + (constraint, Self::IfAll(mut constraints)) + | (Self::IfAll(mut constraints), constraint) => { + constraints.push(constraint); + Self::IfAll(constraints) + } + (lhs, rhs) => Self::IfAll(vec![lhs, rhs]), + } + } + + pub(crate) fn or(self, rhs: Self) -> Self { + match (self, rhs) { + (Self::Yes, _) | (_, Self::Yes) => Self::Yes, + (Self::No, Self::No) => Self::No, + (Self::IfAny(mut lhs), Self::IfAny(ref mut rhs)) => { + lhs.append(rhs); + Self::IfAny(lhs) + } + (constraint, Self::IfAny(mut constraints)) + | (Self::IfAny(mut constraints), constraint) => { + constraints.push(constraint); + Self::IfAny(constraints) + } + (lhs, rhs) => Self::IfAny(vec![lhs, rhs]), + } + } +} + +pub fn for_all(iter: I, f: F) -> Answer +where + R: layout::Ref, + I: IntoIterator, + F: FnMut(::Item) -> Answer, +{ + use std::ops::ControlFlow::{Break, Continue}; + let (Continue(result) | Break(result)) = + iter.into_iter().map(f).try_fold(Answer::Yes, |constraints, constraint| { + match constraint.and(constraints) { + Answer::No => Break(Answer::No), + maybe => Continue(maybe), + } + }); + result +} + +pub fn there_exists(iter: I, f: F) -> Answer +where + R: layout::Ref, + I: IntoIterator, + F: FnMut(::Item) -> Answer, +{ + use std::ops::ControlFlow::{Break, Continue}; + let (Continue(result) | Break(result)) = + iter.into_iter().map(f).try_fold(Answer::No, |constraints, constraint| { + match constraint.or(constraints) { + Answer::Yes => Break(Answer::Yes), + maybe => Continue(maybe), + } + }); + result +} diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs new file mode 100644 index 0000000000000..ef3bfca066932 --- /dev/null +++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs @@ -0,0 +1,76 @@ +use crate::layout; + +/// Context necessary to answer the question "Are these types transmutable?". +pub(crate) trait QueryContext { + type Def: layout::Def; + type Ref: layout::Ref; + type Scope: Copy; + + /// Is `def` accessible from the defining module of `scope`? + fn is_accessible_from(&self, def: Self::Def, scope: Self::Scope) -> bool; + + fn min_align(&self, reference: Self::Ref) -> usize; +} + +#[cfg(test)] +pub(crate) mod test { + use super::QueryContext; + + pub(crate) struct UltraMinimal; + + impl QueryContext for UltraMinimal { + type Def = !; + type Ref = !; + type Scope = (); + + fn is_accessible_from(&self, def: !, scope: ()) -> bool { + unimplemented!() + } + + fn min_align(&self, reference: !) -> usize { + unimplemented!() + } + } +} + +#[cfg(feature = "rustc")] +mod rustc { + use super::*; + use rustc_middle::ty::{Ty, TyCtxt}; + + impl<'tcx> super::QueryContext for TyCtxt<'tcx> { + type Def = layout::rustc::Def<'tcx>; + type Ref = layout::rustc::Ref<'tcx>; + + type Scope = Ty<'tcx>; + + fn is_accessible_from(&self, def: Self::Def, scope: Self::Scope) -> bool { + use layout::rustc::Def; + use rustc_middle::ty; + + let parent = if let ty::Adt(adt_def, ..) = scope.kind() { + use rustc_middle::ty::DefIdTree; + self.parent(adt_def.did()) + } else { + // is this reachable? + return false; + }; + + let def_id = match def { + Def::Adt(adt_def) => adt_def.did(), + Def::Variant(variant_def) => variant_def.def_id, + Def::Field(field_def) => field_def.did, + Def::Primitive => { + // primitives do not have a def_id, but they're always accessible + return true; + } + }; + + if self.visibility(def_id).is_accessible_from(parent, *self) { true } else { false } + } + + fn min_align(&self, reference: Self::Ref) -> usize { + unimplemented!() + } + } +} diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs new file mode 100644 index 0000000000000..f46f03c644a48 --- /dev/null +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -0,0 +1,34 @@ +use super::query_context::test::UltraMinimal; +use crate::{layout, Answer}; + +mod bool { + use super::*; + + #[test] + fn should_permit_identity_transmutation_tree() { + println!("{:?}", layout::Tree::::bool()); + let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( + layout::Tree::::bool(), + layout::Tree::::bool(), + (), + crate::Assume { alignment: false, lifetimes: false, validity: true, visibility: false }, + UltraMinimal, + ) + .answer(); + assert_eq!(answer, Answer::Yes); + } + + #[test] + #[ignore] + fn should_permit_identity_transmutation_dfa() { + let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( + layout::Dfa::::bool(), + layout::Dfa::::bool(), + (), + crate::Assume { alignment: false, lifetimes: false, validity: true, visibility: false }, + UltraMinimal, + ) + .answer(); + assert_eq!(answer, Answer::Yes); + } +} diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 4ce7c8c2b607b..e9d9782e9a949 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -27,6 +27,10 @@ mod valid_align; // alignment as a parameter, such as `Layout::padding_needed_for`. pub(crate) use valid_align::ValidAlign; +mod transmutability; +#[unstable(feature = "transmutability", issue = "none")] +pub use transmutability::{Assume, BikeshedIntrinsicFrom}; + #[stable(feature = "rust1", since = "1.0.0")] #[doc(inline)] pub use crate::intrinsics::transmute; diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs new file mode 100644 index 0000000000000..52342f8a0ecd6 --- /dev/null +++ b/library/core/src/mem/transmutability.rs @@ -0,0 +1,39 @@ +/// Are values of a type transmutable into values of another type? +/// +/// This trait is implemented on-the-fly by the compiler for types `Src` and `Self` when the bits of +/// any value of type `Self` are safely transmutable into a value of type `Dst`, in a given `Context`, +/// notwithstanding whatever safety checks you have asked the compiler to [`Assume`] are satisfied. +#[unstable(feature = "transmutability", issue = "none")] +#[cfg_attr(not(bootstrap), lang = "transmute_trait")] +pub unsafe trait BikeshedIntrinsicFrom< + Src, + Context, + const ASSUME_ALIGNMENT: bool, + const ASSUME_LIFETIMES: bool, + const ASSUME_VALIDITY: bool, + const ASSUME_VISIBILITY: bool, +> where + Src: ?Sized, +{ +} + +/// What transmutation safety conditions shall the compiler assume that *you* are checking? +#[unstable(feature = "transmutability", issue = "none")] +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct Assume { + /// When `true`, the compiler assumes that *you* are ensuring (either dynamically or statically) that + /// destination referents do not have stricter alignment requirements than source referents. + pub alignment: bool, + + /// When `true`, the compiler assume that *you* are ensuring that lifetimes are not extended in a manner + /// that violates Rust's memory model. + pub lifetimes: bool, + + /// When `true`, the compiler assumes that *you* are ensuring that the source type is actually a valid + /// instance of the destination type. + pub validity: bool, + + /// When `true`, the compiler assumes that *you* have ensured that it is safe for you to violate the + /// type and field privacy of the destination type (and sometimes of the source type, too). + pub visibility: bool, +} diff --git a/src/test/ui/transmutability/assume_validity.rs b/src/test/ui/transmutability/assume_validity.rs new file mode 100644 index 0000000000000..cffbff07f1342 --- /dev/null +++ b/src/test/ui/transmutability/assume_validity.rs @@ -0,0 +1,128 @@ +#![feature(transmutability)] +#![feature(marker_trait_attr)] +#![allow(dead_code)] +#![allow(incomplete_features)] + +fn test_assume_validity() { + // if the compiler can assume that the programmer is performing additional validity + // checks, a transmute from bool to u8 can be sound + assert_is_transmutable_assume_validity::(); + + // ...but even with validity assumed, the compiler will still reject transmutations + // that couldn't *possibly* be valid. e.g.: an uninit byte cannot become an initialized byte. + #[repr(C, align(2))] pub struct BoolThenPadding(pub bool); + assert_is_transmutable_any::(); //~ ERROR not satisfied +} + +use std::mem::BikeshedIntrinsicFrom; + +struct Context; + +/// Assert that `Src` is transmutable to `Dst` under all combinations of options +/// where validity is assumed. +fn assert_is_transmutable_assume_validity() +where + Dst: + // Uncomment once visibility checking is implemented: + /* + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + */ BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom +{} + +/// Assert that `Src` is transmutable to `Dst` for at least one combination of options, +/// except those where validity is assumed. +fn assert_is_transmutable_any() +where + Dst: BikeshedIntrinsicFromAny +{} + +#[marker] +trait BikeshedIntrinsicFromAny {} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +fn main() {} diff --git a/src/test/ui/transmutability/assume_validity.stderr b/src/test/ui/transmutability/assume_validity.stderr new file mode 100644 index 0000000000000..8433fcba423f9 --- /dev/null +++ b/src/test/ui/transmutability/assume_validity.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `u16: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/assume_validity.rs:14:51 + | +LL | assert_is_transmutable_any::(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u16` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/assume_validity.rs:42:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/transmutability/enums.rs b/src/test/ui/transmutability/enums.rs new file mode 100644 index 0000000000000..3c771e230cbb9 --- /dev/null +++ b/src/test/ui/transmutability/enums.rs @@ -0,0 +1,233 @@ +#![feature(transmutability)] +#![feature(marker_trait_attr)] +#![allow(dead_code)] +#![allow(incomplete_features)] + +fn between_differently_signed_reprs() { + #[repr(i8)] + enum I8 { + V = -2, + } + + #[repr(u8)] + enum U8 { + V = 254, + } + + assert_is_transmutable_all::(); + assert_is_transmutable_all::(); +} + +fn truncation() { + #[repr(i16)] + enum I16 { + V = i16::from_ne_bytes([42, 0]), + } + + #[repr(u8)] + enum U8 { + V = u8::from_ne_bytes([42]), + } + + assert_is_transmutable_all::(); + assert_is_transmutable_any::(); //~ ERROR not satisfied +} + +fn set_expansion() { + #[repr(u8)] + enum Src { + A = 2, + B = 8, + C = 32 + } + + #[repr(u8)] + enum Dst { + A = 2, + B = 4, + C = 8, + D = 16, + E = 32, + } + + assert_is_transmutable_all::(); + assert_is_transmutable_any_except_validity::(); //~ ERROR not satisfied +} + +use std::mem::BikeshedIntrinsicFrom; + +struct Context; + +/// Assert that `Src` is transmutable to `Dst` under all combinations of options. +fn assert_is_transmutable_all() +where + Dst: + // Uncomment once visibility checking is implemented: + /* BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + */ BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom +{} + +/////////////////////////////////////////////////////////////////////////////// + +/// Assert that `Src` is transmutable to `Dst` for at least one combination of options. +fn assert_is_transmutable_any() +where + Dst: BikeshedIntrinsicFromAny +{} + +#[marker] +trait BikeshedIntrinsicFromAny {} + +// Uncomment once visibility checking is implemented: +/* +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} +*/ + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +/////////////////////////////////////////////////////////////////////////////// + +/// Assert that `Src` is transmutable to `Dst` for at least one combination of options. +/// Validity is not assumed. +fn assert_is_transmutable_any_except_validity() +where + Dst: BikeshedIntrinsicFromAnyExceptValidity +{} + +#[marker] +trait BikeshedIntrinsicFromAnyExceptValidity {} + +// Uncomment once visibility checking is implemented: +/* +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} +*/ + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +fn main() {} diff --git a/src/test/ui/transmutability/enums.stderr b/src/test/ui/transmutability/enums.stderr new file mode 100644 index 0000000000000..8b3ae23ef859a --- /dev/null +++ b/src/test/ui/transmutability/enums.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `I16: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/enums.rs:33:38 + | +LL | assert_is_transmutable_any::(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `I16` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/enums.rs:89:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `Src: BikeshedIntrinsicFromAnyExceptValidity` is not satisfied + --> $DIR/enums.rs:54:55 + | +LL | assert_is_transmutable_any_except_validity::(); + | ^^^ the trait `BikeshedIntrinsicFromAnyExceptValidity` is not implemented for `Src` + | +note: required by a bound in `assert_is_transmutable_any_except_validity` + --> $DIR/enums.rs:184:11 + | +LL | fn assert_is_transmutable_any_except_validity() + | ------------------------------------------ required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAnyExceptValidity + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any_except_validity` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/transmutability/numbers.rs b/src/test/ui/transmutability/numbers.rs new file mode 100644 index 0000000000000..c24e694ee1eb9 --- /dev/null +++ b/src/test/ui/transmutability/numbers.rs @@ -0,0 +1,217 @@ +#![feature(transmutability)] +#![feature(marker_trait_attr)] +#![allow(dead_code)] +#![allow(incomplete_features)] + +fn test_identity() { + /*assert_is_transmutable_all::< bool, bool>(); + assert_is_transmutable_all::< i8, i8>(); + assert_is_transmutable_all::< u8, u8>(); + assert_is_transmutable_all::< i16, i16>(); + assert_is_transmutable_all::< u16, u16>();*/ + assert_is_transmutable_all::< i32, i32>(); + /*assert_is_transmutable_all::< f32, f32>(); + assert_is_transmutable_all::< u32, u32>(); + assert_is_transmutable_all::< i64, i64>(); + assert_is_transmutable_all::< f64, f64>(); + assert_is_transmutable_all::< u64, u64>(); + assert_is_transmutable_all::< i128, i128>(); + assert_is_transmutable_all::< u128, u128>(); + assert_is_transmutable_all::(); + assert_is_transmutable_all::();*/ +} +/* +fn test_same_size() { + assert_is_transmutable_all::< bool, i8>(); + assert_is_transmutable_all::< bool, u8>(); + assert_is_transmutable_any::< i8, bool>(); //~ ERROR not satisfied + assert_is_transmutable_all::< i8, u8>(); + assert_is_transmutable_any::< u8, bool>(); //~ ERROR not satisfied + assert_is_transmutable_all::< u8, i8>(); + + assert_is_transmutable_all::< i16, u16>(); + assert_is_transmutable_all::< u16, i16>(); + + assert_is_transmutable_all::< i32, f32>(); + assert_is_transmutable_all::< i32, u32>(); + assert_is_transmutable_all::< f32, i32>(); + assert_is_transmutable_all::< f32, u32>(); + assert_is_transmutable_all::< u32, i32>(); + assert_is_transmutable_all::< u32, f32>(); + + assert_is_transmutable_all::< u64, i64>(); + assert_is_transmutable_all::< u64, f64>(); + assert_is_transmutable_all::< i64, u64>(); + assert_is_transmutable_all::< i64, f64>(); + assert_is_transmutable_all::< f64, u64>(); + assert_is_transmutable_all::< f64, i64>(); + + assert_is_transmutable_all::< u128, i128>(); + assert_is_transmutable_all::< i128, u128>(); + + assert_is_transmutable_all::(); + assert_is_transmutable_all::(); +} + +fn test_extension() { + assert_is_transmutable_any::< bool, i16>(); //~ ERROR not satisfied + assert_is_transmutable_any::< bool, u16>(); //~ ERROR not satisfied + assert_is_transmutable_any::< bool, i32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< bool, f32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< bool, u32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< bool, u64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< bool, i64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< bool, f64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< bool, u128>(); //~ ERROR not satisfied + assert_is_transmutable_any::< bool, i128>(); //~ ERROR not satisfied + + assert_is_transmutable_any::< i8, i16>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i8, u16>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i8, i32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i8, f32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i8, u32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i8, u64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i8, i64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i8, f64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i8, u128>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i8, i128>(); //~ ERROR not satisfied + + assert_is_transmutable_any::< u8, i16>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u8, u16>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u8, i32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u8, f32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u8, u32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u8, u64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u8, i64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u8, f64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u8, u128>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u8, i128>(); //~ ERROR not satisfied + + assert_is_transmutable_any::< i16, i32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i16, f32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i16, u32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i16, u64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i16, i64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i16, f64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i16, u128>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i16, i128>(); //~ ERROR not satisfied + + assert_is_transmutable_any::< u16, i32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u16, f32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u16, u32>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u16, u64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u16, i64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u16, f64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u16, u128>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u16, i128>(); //~ ERROR not satisfied + + assert_is_transmutable_any::< i32, u64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i32, i64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i32, f64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i32, u128>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i32, i128>(); //~ ERROR not satisfied + + assert_is_transmutable_any::< f32, u64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< f32, i64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< f32, f64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< f32, u128>(); //~ ERROR not satisfied + assert_is_transmutable_any::< f32, i128>(); //~ ERROR not satisfied + + assert_is_transmutable_any::< u32, u64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u32, i64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u32, f64>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u32, u128>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u32, i128>(); //~ ERROR not satisfied + + assert_is_transmutable_any::< u64, u128>(); //~ ERROR not satisfied + assert_is_transmutable_any::< u64, i128>(); //~ ERROR not satisfied + + assert_is_transmutable_any::< i64, u128>(); //~ ERROR not satisfied + assert_is_transmutable_any::< i64, i128>(); //~ ERROR not satisfied + + assert_is_transmutable_any::< f64, u128>(); //~ ERROR not satisfied + assert_is_transmutable_any::< f64, i128>(); //~ ERROR not satisfied +} +*/ + +use std::mem::BikeshedIntrinsicFrom; + +struct Context; + +/// Assert that `Src` is transmutable to `Dst` under all combinations of options. +fn assert_is_transmutable_all() +where + Dst: + // Uncomment once visibility checking is implemented: + /* BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + */ BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom +{} + +/// Assert that `Src` is transmutable to `Dst` for at least one combination of options, +/// except those where validity is assumed. +fn assert_is_transmutable_any() +where + Dst: BikeshedIntrinsicFromAny +{} + +#[marker] +trait BikeshedIntrinsicFromAny {} + +// Uncomment once visibility checking is implemented: +/* +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} +*/ + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +fn main() {} diff --git a/src/test/ui/transmutability/numbers.stderr b/src/test/ui/transmutability/numbers.stderr new file mode 100644 index 0000000000000..3092c23dd5665 --- /dev/null +++ b/src/test/ui/transmutability/numbers.stderr @@ -0,0 +1,1038 @@ +error[E0277]: the trait bound `bool: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:27:42 + | +LL | assert_is_transmutable_any::< i8, bool>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `bool` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `bool: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:29:42 + | +LL | assert_is_transmutable_any::< u8, bool>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `bool` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i16: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:57:43 + | +LL | assert_is_transmutable_any::< bool, i16>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i16` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u16: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:58:43 + | +LL | assert_is_transmutable_any::< bool, u16>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u16` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:59:43 + | +LL | assert_is_transmutable_any::< bool, i32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `f32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:60:43 + | +LL | assert_is_transmutable_any::< bool, f32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `f32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:61:43 + | +LL | assert_is_transmutable_any::< bool, u32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:62:43 + | +LL | assert_is_transmutable_any::< bool, u64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:63:43 + | +LL | assert_is_transmutable_any::< bool, i64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `f64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:64:43 + | +LL | assert_is_transmutable_any::< bool, f64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `f64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:65:42 + | +LL | assert_is_transmutable_any::< bool, u128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:66:42 + | +LL | assert_is_transmutable_any::< bool, i128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i16: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:68:43 + | +LL | assert_is_transmutable_any::< i8, i16>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i16` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u16: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:69:43 + | +LL | assert_is_transmutable_any::< i8, u16>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u16` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:70:43 + | +LL | assert_is_transmutable_any::< i8, i32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `f32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:71:43 + | +LL | assert_is_transmutable_any::< i8, f32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `f32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:72:43 + | +LL | assert_is_transmutable_any::< i8, u32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:73:43 + | +LL | assert_is_transmutable_any::< i8, u64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:74:43 + | +LL | assert_is_transmutable_any::< i8, i64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `f64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:75:43 + | +LL | assert_is_transmutable_any::< i8, f64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `f64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:76:42 + | +LL | assert_is_transmutable_any::< i8, u128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:77:42 + | +LL | assert_is_transmutable_any::< i8, i128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i16: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:79:43 + | +LL | assert_is_transmutable_any::< u8, i16>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i16` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u16: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:80:43 + | +LL | assert_is_transmutable_any::< u8, u16>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u16` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:81:43 + | +LL | assert_is_transmutable_any::< u8, i32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `f32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:82:43 + | +LL | assert_is_transmutable_any::< u8, f32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `f32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:83:43 + | +LL | assert_is_transmutable_any::< u8, u32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:84:43 + | +LL | assert_is_transmutable_any::< u8, u64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:85:43 + | +LL | assert_is_transmutable_any::< u8, i64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `f64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:86:43 + | +LL | assert_is_transmutable_any::< u8, f64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `f64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:87:42 + | +LL | assert_is_transmutable_any::< u8, u128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:88:42 + | +LL | assert_is_transmutable_any::< u8, i128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:90:43 + | +LL | assert_is_transmutable_any::< i16, i32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `f32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:91:43 + | +LL | assert_is_transmutable_any::< i16, f32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `f32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:92:43 + | +LL | assert_is_transmutable_any::< i16, u32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:93:43 + | +LL | assert_is_transmutable_any::< i16, u64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:94:43 + | +LL | assert_is_transmutable_any::< i16, i64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `f64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:95:43 + | +LL | assert_is_transmutable_any::< i16, f64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `f64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:96:42 + | +LL | assert_is_transmutable_any::< i16, u128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:97:42 + | +LL | assert_is_transmutable_any::< i16, i128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:99:43 + | +LL | assert_is_transmutable_any::< u16, i32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `f32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:100:43 + | +LL | assert_is_transmutable_any::< u16, f32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `f32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u32: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:101:43 + | +LL | assert_is_transmutable_any::< u16, u32>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u32` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:102:43 + | +LL | assert_is_transmutable_any::< u16, u64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:103:43 + | +LL | assert_is_transmutable_any::< u16, i64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `f64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:104:43 + | +LL | assert_is_transmutable_any::< u16, f64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `f64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:105:42 + | +LL | assert_is_transmutable_any::< u16, u128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:106:42 + | +LL | assert_is_transmutable_any::< u16, i128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:108:43 + | +LL | assert_is_transmutable_any::< i32, u64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:109:43 + | +LL | assert_is_transmutable_any::< i32, i64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `f64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:110:43 + | +LL | assert_is_transmutable_any::< i32, f64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `f64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:111:42 + | +LL | assert_is_transmutable_any::< i32, u128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:112:42 + | +LL | assert_is_transmutable_any::< i32, i128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:114:43 + | +LL | assert_is_transmutable_any::< f32, u64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:115:43 + | +LL | assert_is_transmutable_any::< f32, i64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `f64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:116:43 + | +LL | assert_is_transmutable_any::< f32, f64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `f64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:117:42 + | +LL | assert_is_transmutable_any::< f32, u128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:118:42 + | +LL | assert_is_transmutable_any::< f32, i128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:120:43 + | +LL | assert_is_transmutable_any::< u32, u64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:121:43 + | +LL | assert_is_transmutable_any::< u32, i64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `f64: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:122:43 + | +LL | assert_is_transmutable_any::< u32, f64>(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `f64` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:123:42 + | +LL | assert_is_transmutable_any::< u32, u128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:124:42 + | +LL | assert_is_transmutable_any::< u32, i128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:126:42 + | +LL | assert_is_transmutable_any::< u64, u128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:127:42 + | +LL | assert_is_transmutable_any::< u64, i128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:129:42 + | +LL | assert_is_transmutable_any::< i64, u128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:130:42 + | +LL | assert_is_transmutable_any::< i64, i128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:132:42 + | +LL | assert_is_transmutable_any::< f64, u128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `i128: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/numbers.rs:133:42 + | +LL | assert_is_transmutable_any::< f64, i128>(); + | ^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `i128` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/numbers.rs:168:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error: aborting due to 69 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/transmutability/structs.rs b/src/test/ui/transmutability/structs.rs new file mode 100644 index 0000000000000..2b7e360f10ef3 --- /dev/null +++ b/src/test/ui/transmutability/structs.rs @@ -0,0 +1,160 @@ +#![feature(transmutability)] +#![feature(marker_trait_attr)] +#![allow(dead_code)] +#![allow(incomplete_features)] + +fn test_well_defined() { + #[repr(C)] struct WellDefined; + pub struct Unspecified; + + assert_is_transmutable_all::(); + assert_is_transmutable_any::(); //~ ERROR not satisfied + assert_is_transmutable_any::(); //~ ERROR not satisfied + assert_is_transmutable_any::(); //~ ERROR not satisfied +} + +fn truncation() { + #[repr(C)] pub struct Zst; + + assert_is_transmutable_all::(); +} + +fn extension() { + #[repr(C, align(8))] pub struct U8ThenPadding(pub u8); + + // a `u8` is extensible to `U8ThenPadding` + assert_is_transmutable_all::(); + // a `U8ThenPadding` is truncatable to a `u8` + assert_is_transmutable_all::(); + // a `U8ThenPadding` is NOT extensible to a `u16`. + assert_is_transmutable_any::(); //~ ERROR not satisfied +} + +fn same_size() { + #[repr(C)] pub struct Padded(pub u16, pub u8); + #[repr(C)] pub struct Unpadded(pub u16, pub u16); + + assert_is_transmutable_all::(); + assert_is_transmutable_any::(); //~ ERROR not satisfied +} + +use std::mem::BikeshedIntrinsicFrom; + +struct Context; + +/// Assert that `Src` is transmutable to `Dst` under all combinations of options. +fn assert_is_transmutable_all() +where + Dst: + // Uncomment once visibility checking is implemented: + /* BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + */ BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom +{} + +/// Assert that `Src` is transmutable to `Dst` for at least one combination of options. +fn assert_is_transmutable_any() +where + Dst: BikeshedIntrinsicFromAny +{} + +#[marker] +trait BikeshedIntrinsicFromAny {} + +// Uncomment once visibility checking is implemented: +/* +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} +*/ + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +fn main() {} diff --git a/src/test/ui/transmutability/structs.stderr b/src/test/ui/transmutability/structs.stderr new file mode 100644 index 0000000000000..1fbf3d2c03d35 --- /dev/null +++ b/src/test/ui/transmutability/structs.stderr @@ -0,0 +1,78 @@ +error[E0277]: the trait bound `Unspecified: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/structs.rs:11:47 + | +LL | assert_is_transmutable_any::(); + | ^^^^^^^^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `Unspecified` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/structs.rs:71:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `WellDefined: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/structs.rs:12:47 + | +LL | assert_is_transmutable_any::(); + | ^^^^^^^^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `WellDefined` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/structs.rs:71:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `Unspecified: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/structs.rs:13:47 + | +LL | assert_is_transmutable_any::(); + | ^^^^^^^^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `Unspecified` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/structs.rs:71:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `u16: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/structs.rs:30:49 + | +LL | assert_is_transmutable_any::(); + | ^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `u16` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/structs.rs:71:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error[E0277]: the trait bound `Unpadded: BikeshedIntrinsicFromAny` is not satisfied + --> $DIR/structs.rs:38:42 + | +LL | assert_is_transmutable_any::(); + | ^^^^^^^^ the trait `BikeshedIntrinsicFromAny` is not implemented for `Unpadded` + | +note: required by a bound in `assert_is_transmutable_any` + --> $DIR/structs.rs:71:11 + | +LL | fn assert_is_transmutable_any() + | -------------------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFromAny + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_transmutable_any` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/transmutability/unions.rs b/src/test/ui/transmutability/unions.rs new file mode 100644 index 0000000000000..e87d29711252f --- /dev/null +++ b/src/test/ui/transmutability/unions.rs @@ -0,0 +1,204 @@ +#![feature(transmutability)] +#![feature(marker_trait_attr)] +#![allow(dead_code)] +#![allow(incomplete_features)] + +fn validity_expansion() { + #[derive(Copy, Clone)] #[repr(u8)] pub enum V0 { V = 0 } + #[derive(Copy, Clone)] #[repr(u8)] pub enum V1 { V = 1 } + #[derive(Copy, Clone)] #[repr(u8)] pub enum V2 { V = 2 } + + #[repr(C)] + pub union Src { + pub v0: V0, + pub v1: V1, + //pub v2: V2, + } + + #[repr(C)] + pub union Dst { + pub v0: V0, + pub v1: V1, + //pub v2: V2, + } + + assert_is_transmutable_all::(); +} + +use std::mem::BikeshedIntrinsicFrom; + +struct Context; + +/// Assert that `Src` is transmutable to `Dst` under all combinations of options. +fn assert_is_transmutable_all() +where + Dst: + // Uncomment once visibility checking is implemented: + /* BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + + BikeshedIntrinsicFrom + BikeshedIntrinsicFrom*/ + BikeshedIntrinsicFrom + // BikeshedIntrinsicFrom + // BikeshedIntrinsicFrom + // BikeshedIntrinsicFrom + // BikeshedIntrinsicFrom + //BikeshedIntrinsicFrom + // BikeshedIntrinsicFrom +{} + +/////////////////////////////////////////////////////////////////////////////// + +/// Assert that `Src` is transmutable to `Dst` for at least one combination of options. +fn assert_is_transmutable_any() +where + Dst: BikeshedIntrinsicFromAny +{} + +#[marker] +trait BikeshedIntrinsicFromAny {} + +// Uncomment once visibility checking is implemented: +/* +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} +*/ + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAny for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +/////////////////////////////////////////////////////////////////////////////// + +/// Assert that `Src` is transmutable to `Dst` for at least one combination of options. +/// Validity is not assumed. +fn assert_is_transmutable_any_except_validity() +where + Dst: BikeshedIntrinsicFromAnyExceptValidity +{} + +#[marker] +trait BikeshedIntrinsicFromAnyExceptValidity {} + +// Uncomment once visibility checking is implemented: +/* +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} +*/ + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +impl BikeshedIntrinsicFromAnyExceptValidity for Dst +where + Dst: BikeshedIntrinsicFrom +{} + +fn main() {} diff --git a/src/test/ui/transmutability/visibility/assume.rs b/src/test/ui/transmutability/visibility/assume.rs new file mode 100644 index 0000000000000..8afbcffa291eb --- /dev/null +++ b/src/test/ui/transmutability/visibility/assume.rs @@ -0,0 +1,151 @@ +//! The destination type, its fields, and its fields' types (and so on) must be visible from +//! `Context`. However, this check is relaxed if visibility is assumed. + +#![feature(transmutability)] +#![allow(dead_code)] + +// all involved types and fields are public +mod all_visible { + mod assert { + use std::mem::BikeshedIntrinsicFrom; + + pub fn is_transmutable() + where + Dst: BikeshedIntrinsicFrom + {} + } + + mod src { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Src { + pub(in super) field: Zst, + } + } + + mod dst { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Dst { + pub(in super) field: Zst, + } + } + + const _: () = {|| { + struct Context; + assert::is_transmutable::(); + };}; +} + +// the field of `Dst` is private +mod dst_field_private { + mod assert { + use std::mem::BikeshedIntrinsicFrom; + + pub fn is_transmutable() + where + Dst: BikeshedIntrinsicFrom + {} + } + + mod src { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Src { + pub(in super) field: Zst, + } + } + + mod dst { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Dst { + pub(self) field: Zst, // <- private field + } + } + + const _: () = {|| { + struct Context; + assert::is_transmutable::(); + };}; +} + +// the type of `Dst` is private +mod dst_type_private { + mod src { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Src { + pub(in super) field: Zst, + } + } + + mod dst { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(self) struct Dst { // <- private type + pub(in super) field: Zst, + } + + use std::mem::BikeshedIntrinsicFrom; + + pub trait IsTransmutable {} + + impl IsTransmutable for () + where + Dst: BikeshedIntrinsicFrom + {} + + pub fn is_transmutable() + where + (): IsTransmutable + {} + } + + const _: () = {|| { + pub(self) struct Context; + dst::is_transmutable::(); + };}; +} + +// the type of `Dst`'s field is private +// as far as transmutation is concerned, `field` being private is fine so long as validity is +// assumed. however, the priv-in-pub lint currently rejects this code. +mod dst_field_type_private { + mod src { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Src { + pub(in super) field: Zst, + } + } + + mod dst { + #[repr(C)] pub(self) struct Zst; // <- private type + + #[repr(C)] pub(in super) struct Dst { + pub(in super) field: Zst, //~ ERROR private type `dst_field_type_private::dst::Zst` in public interface + } + + use std::mem::BikeshedIntrinsicFrom; + + pub trait IsTransmutable {} + + impl IsTransmutable for () + where + Dst: BikeshedIntrinsicFrom + {} + + pub fn is_transmutable() + where + (): IsTransmutable + {} + } + + const _: () = {|| { + pub(self) struct Context; + dst::is_transmutable::(); + };}; +} + +fn main() {} diff --git a/src/test/ui/transmutability/visibility/assume.stderr b/src/test/ui/transmutability/visibility/assume.stderr new file mode 100644 index 0000000000000..2995e3d7353a5 --- /dev/null +++ b/src/test/ui/transmutability/visibility/assume.stderr @@ -0,0 +1,12 @@ +error[E0446]: private type `dst_field_type_private::dst::Zst` in public interface + --> $DIR/assume.rs:127:13 + | +LL | #[repr(C)] pub(self) struct Zst; // <- private type + | --------------------- `dst_field_type_private::dst::Zst` declared as private +... +LL | pub(in super) field: Zst, + | ^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0446`. diff --git a/src/test/ui/transmutability/visibility/dst.rs b/src/test/ui/transmutability/visibility/dst.rs new file mode 100644 index 0000000000000..8444b7077010a --- /dev/null +++ b/src/test/ui/transmutability/visibility/dst.rs @@ -0,0 +1,116 @@ +//! The destination type, its fields, and its fields' types (and so on) must be visible from +//! `Context`. + +#![feature(transmutability)] +#![allow(dead_code)] + +// the field of `Dst` is private +mod dst_field_private { + mod assert { + use std::mem::BikeshedIntrinsicFrom; + + pub fn is_transmutable() + where + Dst: BikeshedIntrinsicFrom + {} + } + + mod src { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Src { + pub(in super) field: Zst, + } + } + + mod dst { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Dst { + pub(self) field: Zst, // <- private field + } + } + + const _: () = {|| { + struct Context; + assert::is_transmutable::(); //~ ERROR not satisfied + };}; +} + +// the type of `Dst` is private +mod dst_type_private { + mod src { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Src { + pub(in super) field: Zst, + } + } + + mod dst { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(self) struct Dst { // <- private type + pub(in super) field: Zst, + } + + use std::mem::BikeshedIntrinsicFrom; + + pub trait IsTransmutable {} + + impl IsTransmutable for Dst + where + Dst: BikeshedIntrinsicFrom + {} + + pub fn is_transmutable() + where + Dst: IsTransmutable + {} + } + + const _: () = {|| { + pub(self) struct Context; + dst::is_transmutable::(); //~ ERROR not satisfied + };}; +} + +// the type of `Dst`'s field is private +mod dst_field_type_private { + mod src { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Src { + pub(in super) field: Zst, + } + } + + mod dst { + #[repr(C)] pub(self) struct Zst; // <- private type + + #[repr(C)] pub(in super) struct Dst { + pub(in super) field: Zst, + } + + use std::mem::BikeshedIntrinsicFrom; + + pub trait IsTransmutable {} + + impl IsTransmutable for Dst + where + Dst: BikeshedIntrinsicFrom + {} + + pub fn is_transmutable() + where + Dst: IsTransmutable + {} + } + + const _: () = {|| { + pub(self) struct Context; + dst::is_transmutable::(); //~ ERROR not satisfied + };}; +} + +fn main() {} diff --git a/src/test/ui/transmutability/visibility/dst.stderr b/src/test/ui/transmutability/visibility/dst.stderr new file mode 100644 index 0000000000000..86d6459b1bf0a --- /dev/null +++ b/src/test/ui/transmutability/visibility/dst.stderr @@ -0,0 +1,60 @@ +error[E0277]: the trait bound `dst_field_private::dst::Dst: BikeshedIntrinsicFrom` is not satisfied + --> $DIR/dst.rs:36:45 + | +LL | assert::is_transmutable::(); + | ^^^^^^^^ the trait `BikeshedIntrinsicFrom` is not implemented for `dst_field_private::dst::Dst` + | +note: required by a bound in `assert::is_transmutable` + --> $DIR/dst.rs:14:18 + | +LL | pub fn is_transmutable() + | --------------- required by a bound in this +LL | where +LL | Dst: BikeshedIntrinsicFrom + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert::is_transmutable` + +error[E0277]: the trait bound `dst_type_private::dst::Dst: BikeshedIntrinsicFrom` is not satisfied + --> $DIR/dst.rs:74:9 + | +LL | dst::is_transmutable::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `BikeshedIntrinsicFrom` is not implemented for `dst_type_private::dst::Dst` + | + = help: the trait `dst_type_private::dst::IsTransmutable` is implemented for `dst_type_private::dst::Dst` +note: required because of the requirements on the impl of `dst_type_private::dst::IsTransmutable` for `dst_type_private::dst::Dst` + --> $DIR/dst.rs:61:28 + | +LL | impl IsTransmutable for Dst + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ +note: required by a bound in `dst_type_private::dst::is_transmutable` + --> $DIR/dst.rs:68:18 + | +LL | pub fn is_transmutable() + | --------------- required by a bound in this +LL | where +LL | Dst: IsTransmutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `dst_type_private::dst::is_transmutable` + +error[E0277]: the trait bound `dst_field_type_private::dst::Dst: BikeshedIntrinsicFrom` is not satisfied + --> $DIR/dst.rs:112:9 + | +LL | dst::is_transmutable::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `BikeshedIntrinsicFrom` is not implemented for `dst_field_type_private::dst::Dst` + | + = help: the trait `dst_field_type_private::dst::IsTransmutable` is implemented for `dst_field_type_private::dst::Dst` +note: required because of the requirements on the impl of `dst_field_type_private::dst::IsTransmutable` for `dst_field_type_private::dst::Dst` + --> $DIR/dst.rs:99:28 + | +LL | impl IsTransmutable for Dst + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ +note: required by a bound in `dst_field_type_private::dst::is_transmutable` + --> $DIR/dst.rs:106:18 + | +LL | pub fn is_transmutable() + | --------------- required by a bound in this +LL | where +LL | Dst: IsTransmutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `dst_field_type_private::dst::is_transmutable` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/transmutability/visibility/src.rs b/src/test/ui/transmutability/visibility/src.rs new file mode 100644 index 0000000000000..bf3c8f00e0e8b --- /dev/null +++ b/src/test/ui/transmutability/visibility/src.rs @@ -0,0 +1,140 @@ +//! The visibilities of the `Src` type, its fields, and its fields' types (and so on) generally do +//! not matter. + +#![feature(transmutability)] +#![allow(dead_code)] + +mod assert { + use std::mem::BikeshedIntrinsicFrom; + + pub fn is_transmutable() + where + Dst: BikeshedIntrinsicFrom + {} +} + +// all involved types and fields are public +mod all_visible { + mod src { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Src { + pub(in super) field: Zst, + } + } + + mod dst { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Dst { + pub(in super) field: Zst, + } + } + + const _: () = {|| { + struct Context; + crate::assert::is_transmutable::(); + };}; +} + +// the field of `Src` is private +mod src_field_private { + mod src { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Src { + pub(self) field: Zst, // <- private field + } + } + + mod dst { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Dst { + pub(in super) field: Zst, + } + } + + const _: () = {|| { + struct Context; + crate::assert::is_transmutable::(); + };}; +} + +// the type of `Src` is private +mod src_type_private { + mod src { + use std::mem::BikeshedIntrinsicFrom; + + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(self) struct Src { // <- private type + pub(in super) field: Zst, + } + + pub trait IsTransmutable {} + + impl IsTransmutable for Dst + where + Dst: BikeshedIntrinsicFrom + {} + + pub fn is_transmutable() + where + Dst: IsTransmutable + {} + } + + mod dst { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Dst { + pub(in super) field: Zst, + } + } + + const _: () = {|| { + pub(self) struct Context; + src::is_transmutable::(); + };}; +} + +// the type of `Src`'s field is private +mod src_field_type_private { + mod src { + #[repr(C)] pub(self) struct Zst; // <- private type + + #[repr(C)] pub(in super) struct Src { + pub(in super) field: Zst, //~ ERROR private type `src_field_type_private::src::Zst` in public interface + } + + use std::mem::BikeshedIntrinsicFrom; + + pub trait IsTransmutable {} + + impl IsTransmutable for Dst + where + Dst: BikeshedIntrinsicFrom + {} + + pub fn is_transmutable() + where + Dst: IsTransmutable + {} + } + + mod dst { + #[repr(C)] pub(in super) struct Zst; + + #[repr(C)] pub(in super) struct Dst { + pub(in super) field: Zst, + } + } + + const _: () = {|| { + pub(self) struct Context; + src::is_transmutable::(); + };}; +} + +fn main() {} diff --git a/src/test/ui/transmutability/visibility/src.stderr b/src/test/ui/transmutability/visibility/src.stderr new file mode 100644 index 0000000000000..d0eb2c4e161e7 --- /dev/null +++ b/src/test/ui/transmutability/visibility/src.stderr @@ -0,0 +1,12 @@ +error[E0446]: private type `src_field_type_private::src::Zst` in public interface + --> $DIR/src.rs:108:13 + | +LL | #[repr(C)] pub(self) struct Zst; // <- private type + | --------------------- `src_field_type_private::src::Zst` declared as private +... +LL | pub(in super) field: Zst, + | ^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0446`.