diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 56c800dcf7c7e..bb0bc99b030d6 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -179,7 +179,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let _: Result<_, ErrorGuaranteed> = self.fully_perform_op( locations, category, - param_env.and(type_op::prove_predicate::ProvePredicate { predicate }), + param_env.and(type_op::prove_predicate::ProvePredicate { + predicate, + body_id: self.infcx.root_def_id, + }), ); } diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index d6e6c56c00312..29afc4ce12002 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -16,6 +16,7 @@ use crate::ty::{self, GenericArg, Ty, TyCtxt}; pub mod type_op { use rustc_macros::{StableHash, TypeFoldable, TypeVisitable}; + use rustc_span::def_id::LocalDefId; use crate::ty::{Predicate, Ty, UserType}; @@ -40,6 +41,7 @@ pub mod type_op { #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, StableHash, TypeFoldable, TypeVisitable)] pub struct ProvePredicate<'tcx> { pub predicate: Predicate<'tcx>, + pub body_id: LocalDefId, } /// Normalizes, but not in the new solver. diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 8fd7d6d0471c7..d1a1f500749c4 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -88,5 +88,6 @@ pub trait SolverDelegate: Deref + Sized { src: ::Ty, dst: ::Ty, assume: ::Const, + body_id: Option<::LocalDefId>, ) -> Result; } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 23fcfc2788656..eb8e84ab65531 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -137,6 +137,7 @@ where nested_goals: Vec<(GoalSource, Goal, Option>)>, pub(super) origin_span: I::Span, + root_body_id: Option, // Has this `EvalCtxt` errored out with `NoSolution` in `try_evaluate_added_goals`? // @@ -168,6 +169,7 @@ pub trait SolverDelegateEvalExt: SolverDelegate { &self, goal: Goal::Predicate>, span: ::Span, + body_id: Option<::LocalDefId>, stalled_on: Option>, ) -> Result, NoSolution>; @@ -215,11 +217,13 @@ where &self, goal: Goal, span: I::Span, + body_id: Option, stalled_on: Option>, ) -> Result, NoSolution> { - let result = EvalCtxt::enter_root(self, self.cx().recursion_limit(), span, |ecx| { - ecx.evaluate_goal(GoalSource::Misc, goal, stalled_on) - }); + let result = + EvalCtxt::enter_root(self, self.cx().recursion_limit(), span, body_id, |ecx| { + ecx.evaluate_goal(GoalSource::Misc, goal, stalled_on) + }); match result { Ok(i) => Ok(i), @@ -236,7 +240,7 @@ where goal: Goal::Predicate>, ) -> bool { self.probe(|| { - EvalCtxt::enter_root(self, self.cx().recursion_limit(), I::Span::dummy(), |ecx| { + EvalCtxt::enter_root(self, self.cx().recursion_limit(), I::Span::dummy(), None, |ecx| { ecx.evaluate_goal(GoalSource::Misc, goal, None) }) .is_ok_and(|r| match r.certainty { @@ -259,7 +263,7 @@ where goal: Goal::Predicate>, ) -> bool { self.probe(|| { - EvalCtxt::enter_root(self, root_depth, I::Span::dummy(), |ecx| { + EvalCtxt::enter_root(self, root_depth, I::Span::dummy(), None, |ecx| { ecx.evaluate_goal(GoalSource::Misc, goal, None) }) }) @@ -339,6 +343,7 @@ where delegate: &D, root_depth: usize, origin_span: I::Span, + root_body_id: Option, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> R, ) -> R { let mut search_graph = SearchGraph::new(root_depth); @@ -357,6 +362,7 @@ where var_values: CanonicalVarValues::dummy(), current_goal_kind: CurrentGoalKind::Misc, origin_span, + root_body_id, tainted: Ok(()), opaque_accesses: AccessedOpaques::default(), }; @@ -382,6 +388,7 @@ where search_graph: &'a mut SearchGraph, canonical_input: CanonicalInput, proof_tree_builder: &mut inspect::ProofTreeBuilder, + root_body_id: Option, f: impl FnOnce( &mut EvalCtxt<'_, D>, Goal, @@ -421,6 +428,7 @@ where search_graph, nested_goals: Default::default(), origin_span: I::Span::dummy(), + root_body_id, tainted: Ok(()), inspect: proof_tree_builder.new_evaluation_step(var_values), opaque_accesses: AccessedOpaques::default(), @@ -572,11 +580,12 @@ where TypingMode::ErasedNotCoherence(MayBeErased), ); + let mut inspect = inspect::ProofTreeBuilder::new_noop(self.root_body_id); let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal( self.cx(), canonical_goal, step_kind, - &mut inspect::ProofTreeBuilder::new_noop(), + &mut inspect, ); let should_rerun = self.should_rerun_after_erased_canonicalization( @@ -599,12 +608,9 @@ where let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, &opaque_types, typing_mode); - let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal( - self.cx(), - canonical_goal, - step_kind, - &mut inspect::ProofTreeBuilder::new_noop(), - ); + let mut inspect = inspect::ProofTreeBuilder::new_noop(self.root_body_id); + let (canonical_result, accessed_opaques) = + self.search_graph.evaluate_goal(self.cx(), canonical_goal, step_kind, &mut inspect); assert!( !accessed_opaques.might_rerun(), "we run without TypingMode::ErasedNotCoherence, so opaques are available, and we don't retry if the outer typing mode is ErasedNotCoherence: {accessed_opaques:?} after {goal:?}" @@ -1516,7 +1522,7 @@ where dst: I::Ty, assume: I::Const, ) -> Result { - self.delegate.is_transmutable(dst, src, assume) + self.delegate.is_transmutable(dst, src, assume, self.root_body_id) } pub(super) fn replace_bound_vars>( diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs index 1c5d6e0b14c65..e4fc7ac0146e5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs @@ -86,6 +86,7 @@ where search_graph: outer.search_graph, nested_goals: propagated_nested_goals, origin_span: outer.origin_span, + root_body_id: outer.root_body_id, tainted: outer.tainted, inspect: outer.inspect.take_and_enter_probe(), opaque_accesses: AccessedOpaques::default(), diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index 4369148baf91d..706378058f69c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -28,22 +28,27 @@ where I: Interner, { state: Option>>>, + root_body_id: Option, _infcx: PhantomData, } impl, I: Interner> ProofTreeBuilder { pub(crate) fn new() -> ProofTreeBuilder { - ProofTreeBuilder { state: Some(Box::new(None)), _infcx: PhantomData } + ProofTreeBuilder { state: Some(Box::new(None)), root_body_id: None, _infcx: PhantomData } } - pub(crate) fn new_noop() -> ProofTreeBuilder { - ProofTreeBuilder { state: None, _infcx: PhantomData } + pub(crate) fn new_noop(root_body_id: Option) -> ProofTreeBuilder { + ProofTreeBuilder { state: None, root_body_id, _infcx: PhantomData } } pub(crate) fn is_noop(&self) -> bool { self.state.is_none() } + pub(crate) fn root_body_id(&self) -> Option { + self.root_body_id + } + pub(crate) fn new_evaluation_step( &mut self, var_values: ty::CanonicalVarValues, diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 38b21bfea432a..1e8faf49bbf74 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -140,22 +140,30 @@ where inspect: &mut Self::ProofTreeBuilder, ) -> (QueryResult, AccessedOpaques) { ensure_sufficient_stack(|| { - EvalCtxt::enter_canonical(cx, search_graph, input, inspect, |ecx, goal| { - let result = ecx.compute_goal(goal); - - // if we're in `RerunNonErased`, don't even bother with inspect, - // and immediately return - let result = match result { - Ok(i) => Ok(i), - Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Err(NoSolution), - Err(NoSolutionOrRerunNonErased::RerunNonErased(e)) => { - return Err(e.into()); - } - }; - - ecx.inspect.query_result(result); - result.map_err(Into::into) - }) + let root_body_id = inspect.root_body_id(); + EvalCtxt::enter_canonical( + cx, + search_graph, + input, + inspect, + root_body_id, + |ecx, goal| { + let result = ecx.compute_goal(goal); + + // if we're in `RerunNonErased`, don't even bother with inspect, + // and immediately return + let result = match result { + Ok(i) => Ok(i), + Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Err(NoSolution), + Err(NoSolutionOrRerunNonErased::RerunNonErased(e)) => { + return Err(e.into()); + } + }; + + ecx.inspect.query_result(result); + result.map_err(Into::into) + }, + ) }) } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index f78bc3e8b4884..21dc93356c9b7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2927,8 +2927,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return (obligation.clone(), trait_predicate); }; + let caller_module = self.tcx.parent_module_from_def_id(obligation.cause.body_id); let is_normalized_yes = matches!( - rustc_transmute::TransmuteTypeEnv::new(self.tcx).is_transmutable( + rustc_transmute::TransmuteTypeEnv::new(self.tcx, caller_module).is_transmutable( trait_ref.args.type_at(1), trait_ref.args.type_at(0), assume, @@ -2985,8 +2986,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let dst = trait_pred.trait_ref.args.type_at(0); let src = trait_pred.trait_ref.args.type_at(1); let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`"); + let caller_module = self.tcx.parent_module_from_def_id(obligation.cause.body_id); - match rustc_transmute::TransmuteTypeEnv::new(self.infcx.tcx) + match rustc_transmute::TransmuteTypeEnv::new(self.infcx.tcx, caller_module) .is_transmutable(src, dst, assume) { Answer::No(reason) => { diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index bd22ba6b6bf6d..5d039776c8fd9 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use rustc_data_structures::fx::FxHashMap; use rustc_hir::LangItem; -use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; +use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId, LocalModDefId}; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::{ Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues, @@ -365,6 +365,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< src: Ty<'tcx>, dst: Ty<'tcx>, assume: ty::Const<'tcx>, + body_id: Option, ) -> Result { // Erase regions because we compute layouts in `rustc_transmute`, // which will ICE for region vars. @@ -374,8 +375,14 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< return Err(NoSolution); }; + let caller_module = body_id + .map(|body_id| self.tcx.parent_module_from_def_id(body_id)) + .unwrap_or(LocalModDefId::CRATE_DEF_ID); + // FIXME(transmutability): This really should be returning nested goals for `Answer::If*` - match rustc_transmute::TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume) { + match rustc_transmute::TransmuteTypeEnv::new(self.0.tcx, caller_module) + .is_transmutable(src, dst, assume) + { rustc_transmute::Answer::Yes => Ok(Certainty::Yes), rustc_transmute::Answer::No(_) | rustc_transmute::Answer::If(_) => Err(NoSolution), } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 78220d4d426b7..06aa9600c8bfc 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -103,6 +103,7 @@ impl<'tcx> ObligationStorage<'tcx> { let result = <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal( goal, o.cause.span, + Some(o.cause.body_id), stalled_on.take(), ); matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. })) @@ -205,7 +206,12 @@ where continue; } - let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on); + let result = delegate.evaluate_root_goal( + goal, + obligation.cause.span, + Some(obligation.cause.body_id), + stalled_on, + ); self.inspect_evaluated_obligation(infcx, &obligation, &result); let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result { Ok(result) => result, diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index 1ba0bb5f776ba..8c46d392d179d 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -95,6 +95,7 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( match <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal( root_obligation.as_goal(), root_obligation.cause.span, + Some(root_obligation.cause.body_id), None, ) { Ok(GoalEvaluation { diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 97b460fe347d8..d04e7df0c2548 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -53,7 +53,8 @@ where ty::AliasRelationDirection::Equate, ); let goal = Goal::new(infcx.tcx, at.param_env, predicate); - let result = delegate.evaluate_root_goal(goal, at.cause.span, None)?; + let result = + delegate.evaluate_root_goal(goal, at.cause.span, Some(at.cause.body_id), None)?; let normalized = infcx.resolve_vars_if_possible(infer_term); let stalled_goal = match result.certainty { Certainty::Yes => None, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index f24214145bafc..f6d55e8af0840 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -43,7 +43,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { ) -> Result { ocx.register_obligation(Obligation::new( ocx.infcx.tcx, - ObligationCause::dummy_with_span(span), + ObligationCause::misc(span, key.value.body_id), key.param_env, key.value.predicate, )); diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 11ce6235eb7ff..736b359da7048 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -362,7 +362,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let src = predicate.trait_ref.args.type_at(1); debug!(?src, ?dst); - let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx.tcx); + let caller_module = self.tcx().parent_module_from_def_id(obligation.cause.body_id); + let mut transmute_env = + rustc_transmute::TransmuteTypeEnv::new(self.infcx.tcx, caller_module); let maybe_transmutable = transmute_env.is_transmutable(src, dst, assume); let fully_flattened = match maybe_transmutable { diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index f77e1994cf472..433c826ba412b 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -94,8 +94,9 @@ fn type_op_prove_predicate<'tcx>( pub fn type_op_prove_predicate_with_cause<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, ProvePredicate<'tcx>>, - cause: ObligationCause<'tcx>, + mut cause: ObligationCause<'tcx>, ) { - let ParamEnvAnd { param_env, value: ProvePredicate { predicate } } = key; + let ParamEnvAnd { param_env, value: ProvePredicate { predicate, body_id } } = key; + cause.body_id = body_id; ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate)); } diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs index 4e548ff8fe20c..afe6df4598cb6 100644 --- a/compiler/rustc_transmute/src/layout/mod.rs +++ b/compiler/rustc_transmute/src/layout/mod.rs @@ -139,18 +139,20 @@ pub mod rustc { /// A visibility node in the layout. #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] pub enum Def<'tcx> { - Adt(ty::AdtDef<'tcx>), - Variant(&'tcx ty::VariantDef), - Field(&'tcx ty::FieldDef), + Adt(ty::AdtDef<'tcx>, bool), + Variant(&'tcx ty::VariantDef, bool), + Field(&'tcx ty::FieldDef, bool), Primitive, } impl<'tcx> super::Def for Def<'tcx> { fn has_safety_invariants(&self) -> bool { - // Rust presently has no notion of 'unsafe fields', so for now we - // make the conservative assumption that everything besides - // primitive types carry safety invariants. - self != &Self::Primitive + match self { + Self::Adt(_, has_safety_invariants) + | Self::Variant(_, has_safety_invariants) + | Self::Field(_, has_safety_invariants) => *has_safety_invariants, + Self::Primitive => false, + } } } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index d7aeb6b06b82f..49d544e5101a5 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -105,6 +105,21 @@ where Self::alt([x, y, z]) } + /// A `Tree` whose layout matches that of `std::num::NonZero`. + pub(crate) fn nonzero_char(order: Endian) -> Self { + // This is `char`'s bit validity with `\0` removed: + // - [0x000001, 0x00D7FF] + // - [0x00E000, 0x10FFFF] + const _0: RangeInclusive = 0..=0; + const BYTE: RangeInclusive = 0x00..=0xFF; + + let a = Self::from_big_endian(order, [_0, _0, _0, 0x01..=0xFF]); + let b = Self::from_big_endian(order, [_0, _0, 0x01..=0xD7, BYTE]); + let c = Self::from_big_endian(order, [_0, _0, 0xE0..=0xFF, BYTE]); + let d = Self::from_big_endian(order, [_0, 0x01..=0x10, BYTE, BYTE]); + Self::alt([a, b, c, d]) + } + /// A `Tree` whose layout matches `std::num::NonZeroXxx`. #[allow(dead_code)] pub(crate) fn nonzero(width_in_bytes: u64) -> Self { @@ -250,8 +265,11 @@ where #[cfg(feature = "rustc")] pub(crate) mod rustc { use rustc_abi::{ - FieldIdx, FieldsShape, Layout, Size, TagEncoding, TyAndLayout, VariantIdx, Variants, + BackendRepr, FieldIdx, FieldsShape, Layout, Scalar, Size, TagEncoding, TyAndLayout, + VariantIdx, Variants, }; + use rustc_hir::def_id::DefId; + use rustc_hir::find_attr; use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError}; use rustc_middle::ty::{ self, AdtDef, AdtKind, List, Region, ScalarInt, Ty, TyCtxt, TypeVisitableExt, @@ -287,7 +305,11 @@ pub(crate) mod rustc { } impl<'tcx> Tree, Region<'tcx>, Ty<'tcx>> { - pub(crate) fn from_ty(ty: Ty<'tcx>, cx: LayoutCx<'tcx>) -> Result { + pub(crate) fn from_ty( + ty: Ty<'tcx>, + cx: LayoutCx<'tcx>, + caller_module: DefId, + ) -> Result { use rustc_abi::HasDataLayout; let layout = layout_of(cx, ty)?; @@ -316,7 +338,7 @@ pub(crate) mod rustc { Ok(Self::number(width.try_into().unwrap())) } - ty::Tuple(members) => Self::from_tuple((ty, layout), members, cx), + ty::Tuple(members) => Self::from_tuple((ty, layout), members, cx, caller_module), ty::Array(inner_ty, _len) => { let FieldsShape::Array { stride, count } = &layout.fields else { @@ -324,16 +346,41 @@ pub(crate) mod rustc { }; let inner_layout = layout_of(cx, *inner_ty)?; assert_eq!(*stride, inner_layout.size); - let elt = Tree::from_ty(*inner_ty, cx)?; + let elt = Tree::from_ty(*inner_ty, cx, caller_module)?; Ok(std::iter::repeat_n(elt, *count as usize) .fold(Tree::unit(), |tree, elt| tree.then(elt))) } - ty::Adt(adt_def, _args_ref) if !ty.is_box() => match adt_def.adt_kind() { - AdtKind::Struct => Self::from_struct((ty, layout), *adt_def, cx), - AdtKind::Enum => Self::from_enum((ty, layout), *adt_def, cx), - AdtKind::Union => Self::from_union((ty, layout), *adt_def, cx), - }, + ty::Adt(adt_def, args_ref) if !ty.is_box() => { + let scalar = match layout.backend_repr { + BackendRepr::Scalar(scalar) => Some(scalar), + _ => None, + }; + + match adt_def.adt_kind() { + AdtKind::Struct => { + if adt_def.repr().transparent() + && let Some(scalar) = scalar + && !scalar.is_always_valid(&cx) + { + if let Some(tree) = Self::from_transparent_nonzero_struct( + *adt_def, + args_ref, + scalar, + cx, + caller_module, + )? { + return Ok(tree); + } + } + Self::from_struct((ty, layout), *adt_def, cx, caller_module) + } + AdtKind::Enum => Self::from_enum((ty, layout), *adt_def, cx, caller_module), + AdtKind::Union => { + Self::from_union((ty, layout), *adt_def, cx, caller_module) + } + } + } ty::Ref(region, ty, mutability) => { let layout = layout_of(cx, *ty)?; @@ -360,21 +407,69 @@ pub(crate) mod rustc { (ty, layout): (Ty<'tcx>, Layout<'tcx>), members: &'tcx List>, cx: LayoutCx<'tcx>, + caller_module: DefId, ) -> Result { match &layout.fields { FieldsShape::Primitive => { assert_eq!(members.len(), 1); let inner_ty = members[0]; - Self::from_ty(inner_ty, cx) + Self::from_ty(inner_ty, cx, caller_module) } FieldsShape::Arbitrary { offsets, .. } => { assert_eq!(offsets.len(), members.len()); - Self::from_variant(Def::Primitive, None, (ty, layout), layout.size, cx) + Self::from_variant( + Def::Primitive, + None, + (ty, layout), + layout.size, + cx, + caller_module, + ) } FieldsShape::Array { .. } | FieldsShape::Union(_) => Err(Err::NotYetSupported), } } + fn from_transparent_nonzero_struct( + def: AdtDef<'tcx>, + args_ref: ty::GenericArgsRef<'tcx>, + scalar: Scalar, + cx: LayoutCx<'tcx>, + caller_module: DefId, + ) -> Result, Err> { + let variant = def.non_enum_variant(); + // For now, only support `repr(transparent)` types with a single + // field. Technically `repr(transparent)` also works with types with + // any number of zero-sized fields in addition to their single + // non-zero-sized field, but no standard library NonZero type needs + // that extra generality. + let [field] = &variant.fields.as_slice().raw else { + return Err(Err::NotYetSupported); + }; + + let field_ty = + cx.tcx().normalize_erasing_regions(cx.typing_env, field.ty(cx.tcx(), args_ref)); + + let Some(field_tree) = Self::nonzero_scalar_tree(field_ty, scalar, cx)? else { + if find_attr!(cx.tcx(), def.did(), RustcNonnullOptimizationGuaranteed) { + return Err(Err::NotYetSupported); + } + return Ok(None); + }; + + Ok(Some(Self::seq([ + Self::def(Def::Adt( + def, + struct_has_safety_invariants(def.non_enum_variant(), caller_module, cx.tcx()), + )), + Self::def(Def::Field( + field, + field_has_safety_invariants(field, caller_module, cx.tcx()), + )), + field_tree, + ]))) + } + /// Constructs a `Tree` from a struct. /// /// # Panics @@ -384,10 +479,14 @@ pub(crate) mod rustc { (ty, layout): (Ty<'tcx>, Layout<'tcx>), def: AdtDef<'tcx>, cx: LayoutCx<'tcx>, + caller_module: DefId, ) -> Result { assert!(def.is_struct()); - let def = Def::Adt(def); - Self::from_variant(def, None, (ty, layout), layout.size, cx) + let def = Def::Adt( + def, + struct_has_safety_invariants(def.non_enum_variant(), caller_module, cx.tcx()), + ); + Self::from_variant(def, None, (ty, layout), layout.size, cx, caller_module) } /// Constructs a `Tree` from an enum. @@ -399,6 +498,7 @@ pub(crate) mod rustc { (ty, layout): (Ty<'tcx>, Layout<'tcx>), def: AdtDef<'tcx>, cx: LayoutCx<'tcx>, + caller_module: DefId, ) -> Result { assert!(def.is_enum()); @@ -411,13 +511,17 @@ pub(crate) mod rustc { let tag = cx.tcx().tag_for_variant( cx.typing_env.as_query_input((cx.tcx().erase_and_anonymize_regions(ty), index)), ); - let variant_def = Def::Variant(def.variant(index)); + let variant_def = Def::Variant( + def.variant(index), + variant_has_safety_invariants(def.variant(index), caller_module, cx.tcx()), + ); Self::from_variant( variant_def, tag.map(|tag| (tag, index, encoding.unwrap())), (ty, variant_layout), layout.size, cx, + caller_module, ) }; @@ -445,7 +549,7 @@ pub(crate) mod rustc { }, )?; - Ok(Self::def(Def::Adt(def)).then(variants)) + Ok(Self::def(Def::Adt(def, false)).then(variants)) } } } @@ -453,8 +557,9 @@ pub(crate) mod rustc { /// Constructs a `Tree` from a 'variant-like' layout. /// /// A 'variant-like' layout includes those of structs and, of course, - /// enum variants. Pragmatically speaking, this method supports anything - /// with `FieldsShape::Arbitrary`. + /// enum variants. Pragmatically speaking, this method supports ADT + /// layouts with either `FieldsShape::Primitive` or + /// `FieldsShape::Arbitrary`. /// /// Note: This routine assumes that the optional `tag` is the first /// field, and enum callers should check that `tag_field` is, in fact, @@ -465,18 +570,29 @@ pub(crate) mod rustc { (ty, layout): (Ty<'tcx>, Layout<'tcx>), total_size: Size, cx: LayoutCx<'tcx>, + caller_module: DefId, ) -> Result { - // This constructor does not support non-`FieldsShape::Arbitrary` - // layouts. - let FieldsShape::Arbitrary { offsets, in_memory_order } = layout.fields() else { - return Err(Err::NotYetSupported); - }; - // When this function is invoked with enum variants, // `ty_and_layout.size` does not encompass the entire size of the // enum. We rely on `total_size` for this. assert!(layout.size <= total_size); + if matches!(layout.fields(), FieldsShape::Primitive) { + return Self::from_primitive_variant( + def, + tag, + (ty, layout), + total_size, + cx, + caller_module, + ); + } + + // The remaining variant layouts must have explicit field offsets. + let FieldsShape::Arbitrary { offsets, in_memory_order } = layout.fields() else { + return Err(Err::NotYetSupported); + }; + let mut size = Size::ZERO; let mut struct_tree = Self::def(def); @@ -503,9 +619,18 @@ pub(crate) mod rustc { let field_ty = ty_field(cx, (ty, layout), field_idx); let field_layout = layout_of(cx, field_ty)?; - let field_tree = Self::from_ty(field_ty, cx)?; + let field_tree = Self::from_ty(field_ty, cx, caller_module)?; + + struct_tree = struct_tree.then(padding); - struct_tree = struct_tree.then(padding).then(field_tree); + if let Some(field) = field_def((ty, layout), field_idx) { + struct_tree = struct_tree.then(Self::def(Def::Field( + field, + field_has_safety_invariants(field, caller_module, cx.tcx()), + ))); + } + + struct_tree = struct_tree.then(field_tree); size += padding_needed + field_layout.size; } @@ -517,6 +642,46 @@ pub(crate) mod rustc { Ok(struct_tree.then(trailing_padding)) } + fn from_primitive_variant( + def: Def<'tcx>, + tag: Option<(ScalarInt, VariantIdx, TagEncoding)>, + (ty, layout): (Ty<'tcx>, Layout<'tcx>), + total_size: Size, + cx: LayoutCx<'tcx>, + caller_module: DefId, + ) -> Result { + if tag.is_some() { + return Err(Err::NotYetSupported); + } + + let ty::Adt(adt_def, _) = ty.kind() else { + return Err(Err::NotYetSupported); + }; + let Variants::Single { index } = layout.variants() else { + return Err(Err::NotYetSupported); + }; + + let mut size = Size::ZERO; + let mut variant_tree = Self::def(def); + + for (field_idx, field) in adt_def.variant(*index).fields.iter_enumerated() { + let field_ty = ty_field(cx, (ty, layout), field_idx); + let field_layout = layout_of(cx, field_ty)?; + let field_tree = Self::from_ty(field_ty, cx, caller_module)?; + + variant_tree = variant_tree.then(Self::def(Def::Field( + field, + field_has_safety_invariants(field, caller_module, cx.tcx()), + ))); + variant_tree = variant_tree.then(field_tree); + size += field_layout.size; + } + + let padding_needed = total_size - size; + let trailing_padding = Self::padding(padding_needed.bytes_usize()); + Ok(variant_tree.then(trailing_padding)) + } + /// Constructs a `Tree` representing the value of a enum tag. fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self { use rustc_abi::Endian; @@ -545,6 +710,7 @@ pub(crate) mod rustc { (ty, layout): (Ty<'tcx>, Layout<'tcx>), def: AdtDef<'tcx>, cx: LayoutCx<'tcx>, + caller_module: DefId, ) -> Result { assert!(def.is_union()); @@ -560,7 +726,7 @@ pub(crate) mod rustc { |fields, (idx, _field_def)| { let field_ty = ty_field(cx, (ty, layout), idx); let field_layout = layout_of(cx, field_ty)?; - let field = Self::from_ty(field_ty, cx)?; + let field = Self::from_ty(field_ty, cx, caller_module)?; let trailing_padding_needed = layout.size - field_layout.size; let trailing_padding = Self::padding(trailing_padding_needed.bytes_usize()); let field_and_padding = field.then(trailing_padding); @@ -568,10 +734,85 @@ pub(crate) mod rustc { }, )?; - Ok(Self::def(Def::Adt(def)).then(fields)) + Ok(Self::def(Def::Adt(def, false)).then(fields)) + } + + fn nonzero_scalar_tree( + field_ty: Ty<'tcx>, + scalar: Scalar, + cx: LayoutCx<'tcx>, + ) -> Result, Err> { + let scalar_size = scalar.size(&cx); + let valid_range = scalar.valid_range(&cx); + let base_ty = transparent_scalar_base_ty(field_ty, cx)?; + + match *base_ty.kind() { + ty::Int(_) | ty::Uint(_) + if valid_range.start == 1 + && valid_range.end == scalar_size.unsigned_int_max() => + { + Ok(Some(Self::nonzero(scalar_size.bytes()))) + } + ty::Char if valid_range.start == 1 && valid_range.end == 0x10FFFF => { + Ok(Some(Self::nonzero_char(cx.tcx().data_layout.endian.into()))) + } + _ => Ok(None), + } + } + } + + fn transparent_scalar_base_ty<'tcx>(ty: Ty<'tcx>, cx: LayoutCx<'tcx>) -> Result, Err> { + let layout = layout_of(cx, ty)?; + match ty.kind() { + ty::Pat(base, _) => Ok(*base), + ty::Adt(def, args) if def.repr().transparent() => { + let variant = def.non_enum_variant(); + let [field] = &variant.fields.as_slice().raw else { return Ok(ty) }; + let field_ty = + cx.tcx().normalize_erasing_regions(cx.typing_env, field.ty(cx.tcx(), args)); + let field_layout = layout_of(cx, field_ty)?; + if field_layout.size == layout.size + && matches!(layout.backend_repr, BackendRepr::Scalar(_)) + && matches!(field_layout.backend_repr, BackendRepr::Scalar(_)) + { + transparent_scalar_base_ty(field_ty, cx) + } else { + Ok(ty) + } + } + _ => Ok(ty), } } + fn struct_has_safety_invariants<'tcx>( + variant: &'tcx ty::VariantDef, + caller_module: DefId, + tcx: TyCtxt<'tcx>, + ) -> bool { + variant.fields.is_empty() + || variant.field_list_has_applicable_non_exhaustive() + || variant.ctor_def_id().is_some_and(|ctor_def_id| { + !tcx.visibility(ctor_def_id).is_accessible_from(caller_module, tcx) + }) + } + + fn variant_has_safety_invariants<'tcx>( + variant: &'tcx ty::VariantDef, + caller_module: DefId, + tcx: TyCtxt<'tcx>, + ) -> bool { + variant.field_list_has_applicable_non_exhaustive() + || !tcx.visibility(variant.def_id).is_accessible_from(caller_module, tcx) + } + + fn field_has_safety_invariants<'tcx>( + field: &'tcx ty::FieldDef, + caller_module: DefId, + tcx: TyCtxt<'tcx>, + ) -> bool { + field.safety.is_unsafe() || !field.vis.is_accessible_from(caller_module, tcx) + } + fn ty_field<'tcx>( cx: LayoutCx<'tcx>, (ty, layout): (Ty<'tcx>, Layout<'tcx>), @@ -604,6 +845,21 @@ pub(crate) mod rustc { } } + fn field_def<'tcx>( + (ty, layout): (Ty<'tcx>, Layout<'tcx>), + i: FieldIdx, + ) -> Option<&'tcx ty::FieldDef> { + match ty.kind() { + ty::Adt(def, _args) => match layout.variants { + Variants::Single { index } => Some(&def.variant(index).fields[i]), + Variants::Empty => panic!("there is no field in Variants::Empty types"), + Variants::Multiple { .. } => None, + }, + ty::Tuple(_) => None, + _ => None, + } + } + fn ty_variant<'tcx>( cx: LayoutCx<'tcx>, (ty, layout): (Ty<'tcx>, Layout<'tcx>), diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index 732881f12d2cb..9845fe3d80451 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -88,18 +88,20 @@ pub enum Reason { #[cfg(feature = "rustc")] mod rustc { + use rustc_hir::def_id::LocalModDefId; use rustc_hir::lang_items::LangItem; use rustc_middle::ty::{Const, Region, Ty, TyCtxt}; use super::*; + use crate::maybe_transmutable::query_context::rustc::RustcQueryContext; pub struct TransmuteTypeEnv<'tcx> { - tcx: TyCtxt<'tcx>, + context: RustcQueryContext<'tcx>, } impl<'tcx> TransmuteTypeEnv<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>) -> Self { - Self { tcx } + pub fn new(tcx: TyCtxt<'tcx>, caller_module: LocalModDefId) -> Self { + Self { context: RustcQueryContext { tcx, caller_module: caller_module.to_def_id() } } } pub fn is_transmutable( @@ -108,7 +110,7 @@ mod rustc { dst: Ty<'tcx>, assume: crate::Assume, ) -> crate::Answer, Ty<'tcx>> { - crate::maybe_transmutable::MaybeTransmutableQuery::new(src, dst, assume, self.tcx) + crate::maybe_transmutable::MaybeTransmutableQuery::new(src, dst, assume, self.context) .answer() } } diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index b748611be383f..a7b88bba4c3f8 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -32,27 +32,30 @@ where #[cfg(feature = "rustc")] mod rustc { use rustc_middle::ty::layout::LayoutCx; - use rustc_middle::ty::{Ty, TyCtxt, TypingEnv}; + use rustc_middle::ty::{Ty, TypingEnv}; use super::*; use crate::layout::tree::rustc::Err; + use crate::maybe_transmutable::query_context::rustc::RustcQueryContext; - impl<'tcx> MaybeTransmutableQuery, TyCtxt<'tcx>> { + impl<'tcx> MaybeTransmutableQuery, RustcQueryContext<'tcx>> { /// This method begins by converting `src` and `dst` from `Ty`s to `Tree`s, /// then computes an answer using those trees. #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))] pub(crate) fn answer( self, - ) -> Answer< as QueryContext>::Region, as QueryContext>::Type> - { + ) -> Answer< + as QueryContext>::Region, + as QueryContext>::Type, + > { let Self { src, dst, assume, context } = self; - let layout_cx = LayoutCx::new(context, TypingEnv::fully_monomorphized()); + let layout_cx = LayoutCx::new(context.tcx, TypingEnv::fully_monomorphized()); // Convert `src` and `dst` from their rustc representations, to `Tree`-based // representations. - let src = Tree::from_ty(src, layout_cx); - let dst = Tree::from_ty(dst, layout_cx); + let src = Tree::from_ty(src, layout_cx, context.caller_module); + let dst = Tree::from_ty(dst, layout_cx, context.caller_module); match (src, dst) { (Err(Err::TypeError(_)), _) | (_, Err(Err::TypeError(_))) => { diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs index 6be0cb1190436..e5fc0df488d22 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs @@ -45,14 +45,27 @@ pub(crate) mod test { } #[cfg(feature = "rustc")] -mod rustc { +pub(crate) mod rustc { + use rustc_hir::def_id::DefId; use rustc_middle::ty::{Region, Ty, TyCtxt}; use super::*; + #[derive(Clone, Copy)] + pub(crate) struct RustcQueryContext<'tcx> { + pub(crate) tcx: TyCtxt<'tcx>, + pub(crate) caller_module: DefId, + } + impl<'tcx> super::QueryContext for TyCtxt<'tcx> { type Def = layout::rustc::Def<'tcx>; type Region = Region<'tcx>; type Type = Ty<'tcx>; } + + impl<'tcx> super::QueryContext for RustcQueryContext<'tcx> { + type Def = layout::rustc::Def<'tcx>; + type Region = Region<'tcx>; + type Type = Ty<'tcx>; + } } diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs index 8440ace260883..dc6185a4ff710 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -361,6 +361,56 @@ mod char { } } } + + #[test] + fn should_permit_valid_nonzero_char_transmutation() { + for order in [Endian::Big, Endian::Little] { + use Answer::*; + let char_layout = layout::Tree::::char(order); + let nonzero_char_layout = layout::Tree::::nonzero_char(order); + let nonzero_u32_layout = layout::Tree::::nonzero(4); + + assert_eq!( + is_transmutable(&nonzero_char_layout, &char_layout, Assume::default()), + Yes, + "endian:{order:?}", + ); + assert_eq!( + is_transmutable(&char_layout, &nonzero_char_layout, Assume::default()), + No(Reason::DstIsBitIncompatible), + "endian:{order:?}", + ); + assert_eq!( + is_transmutable(&nonzero_char_layout, &nonzero_u32_layout, Assume::default()), + Yes, + "endian:{order:?}", + ); + assert_eq!( + is_transmutable(&nonzero_u32_layout, &nonzero_char_layout, Assume::default()), + No(Reason::DstIsBitIncompatible), + "endian:{order:?}", + ); + + let no = No(Reason::DstIsBitIncompatible); + for (src, answer) in [ + (0u32, no.clone()), + (1, Yes), + (0xD7FF, Yes), + (0xD800, no.clone()), + (0xDFFF, no.clone()), + (0xE000, Yes), + (0x10FFFF, Yes), + (0x110000, no.clone()), + (0xFFFFFFFF, no), + ] { + let src_layout = + layout::tree::Tree::::from_big_endian(order, src.to_be_bytes()); + + let a = is_transmutable(&src_layout, &nonzero_char_layout, Assume::default()); + assert_eq!(a, answer, "endian:{order:?},\nsrc:{src:x}"); + } + } + } } mod nonzero { diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index e26c1b8fa1e19..7166a18755ff3 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -208,13 +208,15 @@ pub struct Assume { /// /// let src: u8 = 3; /// - /// struct EvenU8 { - /// // SAFETY: `val` must be an even number. - /// val: u8, + /// mod owner { + /// pub struct EvenU8 { + /// // SAFETY: `val` must be an even number. + /// val: u8, + /// } /// } /// /// // SAFETY: No safety obligations. - /// let dst: EvenU8 = unsafe { + /// let dst: owner::EvenU8 = unsafe { /// <_ as TransmuteFrom<_>>::transmute(src) /// }; /// ``` @@ -229,12 +231,20 @@ pub struct Assume { /// /// let src: u8 = 42; /// - /// struct EvenU8 { - /// // SAFETY: `val` must be an even number. - /// val: u8, + /// mod owner { + /// pub struct EvenU8 { + /// // SAFETY: `val` must be an even number. + /// val: u8, + /// } + /// + /// impl EvenU8 { + /// pub fn get(&self) -> u8 { + /// self.val + /// } + /// } /// } /// - /// let maybe_dst: Option = if src % 2 == 0 { + /// let maybe_dst: Option = if src % 2 == 0 { /// // SAFETY: We have checked above that the value of `src` is even. /// Some(unsafe { /// <_ as TransmuteFrom<_, { Assume::SAFETY }>>::transmute(src) @@ -243,7 +253,7 @@ pub struct Assume { /// None /// }; /// - /// assert!(matches!(maybe_dst, Some(EvenU8 { val: 42 }))); + /// assert_eq!(maybe_dst.map(|dst| dst.get()), Some(42)); /// ``` pub safety: bool, diff --git a/tests/ui/transmutability/nonzero-pass.rs b/tests/ui/transmutability/nonzero-pass.rs new file mode 100644 index 0000000000000..0bbfdc3b79215 --- /dev/null +++ b/tests/ui/transmutability/nonzero-pass.rs @@ -0,0 +1,137 @@ +//@ check-pass + +#![feature(transmutability)] +#![feature(unsafe_fields)] +#![allow(dead_code, incomplete_features)] + +mod assert { + use std::mem::{Assume, TransmuteFrom}; + + pub fn nothing() + where + Dst: TransmuteFrom, + { + } + + pub fn safety() + where + Dst: TransmuteFrom, + { + } + + pub fn safety_and_validity() + where + Dst: TransmuteFrom, + { + } +} + +macro_rules! assert_integer_nonzero { + ($int:ty, $nonzero:ty) => { + assert::safety::<$nonzero, $int>(); + assert::safety::<$nonzero, $nonzero>(); + assert::safety_and_validity::<$int, $nonzero>(); + + assert::safety::, $int>(); + assert::safety::<$int, Option<$nonzero>>(); + assert::safety::, Option<$nonzero>>(); + + assert::safety::<[$nonzero; 3], [$int; 3]>(); + assert::safety_and_validity::<[$int; 3], [$nonzero; 3]>(); + assert::safety::<($nonzero, $nonzero), ($int, $int)>(); + assert::safety_and_validity::<($int, $int), ($nonzero, $nonzero)>(); + }; +} + +macro_rules! assert_same_width_integer_nonzeros { + ($uint:ty, $int:ty, $nonzero_uint:ty, $nonzero_int:ty) => { + assert::safety::<$nonzero_uint, $int>(); + assert::safety::<$nonzero_int, $uint>(); + assert::safety::<$nonzero_uint, $nonzero_int>(); + assert::safety::<$nonzero_int, $nonzero_uint>(); + + assert::safety::, $int>(); + assert::safety::, $uint>(); + assert::safety::<$uint, Option<$nonzero_int>>(); + assert::safety::<$int, Option<$nonzero_uint>>(); + }; +} + +#[repr(C)] +struct PublicField { + pub field: u8, +} + +#[repr(C)] +struct PublicUnsafeField { + pub unsafe field: u8, +} + +mod owner { + #[repr(C)] + pub struct VisibleFromChild { + field: u8, + } + + pub mod child { + use super::VisibleFromChild; + use crate::assert; + + pub fn check() { + assert::nothing::(); + assert::nothing::(); + } + } +} + +fn main() { + use std::num::{ + NonZero, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, + NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, + }; + + type NonZeroChar = NonZero; + + assert_integer_nonzero!(u8, NonZeroU8); + assert_integer_nonzero!(u16, NonZeroU16); + assert_integer_nonzero!(u32, NonZeroU32); + assert_integer_nonzero!(u64, NonZeroU64); + assert_integer_nonzero!(u128, NonZeroU128); + assert_integer_nonzero!(usize, NonZeroUsize); + + assert_integer_nonzero!(i8, NonZeroI8); + assert_integer_nonzero!(i16, NonZeroI16); + assert_integer_nonzero!(i32, NonZeroI32); + assert_integer_nonzero!(i64, NonZeroI64); + assert_integer_nonzero!(i128, NonZeroI128); + assert_integer_nonzero!(isize, NonZeroIsize); + + assert_same_width_integer_nonzeros!(u8, i8, NonZeroU8, NonZeroI8); + assert_same_width_integer_nonzeros!(u16, i16, NonZeroU16, NonZeroI16); + assert_same_width_integer_nonzeros!(u32, i32, NonZeroU32, NonZeroI32); + assert_same_width_integer_nonzeros!(u64, i64, NonZeroU64, NonZeroI64); + assert_same_width_integer_nonzeros!(u128, i128, NonZeroU128, NonZeroI128); + assert_same_width_integer_nonzeros!(usize, isize, NonZeroUsize, NonZeroIsize); + + assert::safety::(); + assert::safety::(); + assert::safety::(); + assert::safety::, char>(); + assert::safety::>(); + assert::safety::, u32>(); + assert::safety::, Option>(); + assert::safety_and_validity::(); + assert::safety_and_validity::(); + + assert::safety::<[NonZeroChar; 3], [char; 3]>(); + assert::safety::<[NonZeroChar; 3], [u32; 3]>(); + assert::safety_and_validity::<[char; 3], [NonZeroChar; 3]>(); + assert::safety::<(NonZeroChar, NonZeroChar), (char, char)>(); + + assert::nothing::(); + assert::nothing::(); + owner::child::check(); + + assert::safety::(); + assert::safety::(); +} diff --git a/tests/ui/transmutability/nonzero.rs b/tests/ui/transmutability/nonzero.rs new file mode 100644 index 0000000000000..bba986f1a2205 --- /dev/null +++ b/tests/ui/transmutability/nonzero.rs @@ -0,0 +1,73 @@ +#![feature(transmutability)] +#![allow(dead_code)] + +mod assert { + use std::mem::{Assume, TransmuteFrom}; + + pub fn nothing() + where + Dst: TransmuteFrom, + { + } + + pub fn safety() + where + Dst: TransmuteFrom, + { + } +} + +fn main() { + use std::num::{ + NonZero, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, + NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, + }; + + type NonZeroChar = NonZero; + + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::safety::(); //~ ERROR: cannot be safely transmuted + + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::safety::(); //~ ERROR: cannot be safely transmuted + + assert::nothing::(); //~ ERROR: cannot be safely transmuted + assert::nothing::(); //~ ERROR: cannot be safely transmuted + assert::nothing::(); //~ ERROR: cannot be safely transmuted + assert::nothing::(); //~ ERROR: cannot be safely transmuted + assert::nothing::(); //~ ERROR: cannot be safely transmuted + assert::nothing::(); //~ ERROR: cannot be safely transmuted + + assert::nothing::(); //~ ERROR: cannot be safely transmuted + assert::nothing::(); //~ ERROR: cannot be safely transmuted + assert::nothing::(); //~ ERROR: cannot be safely transmuted + assert::nothing::(); //~ ERROR: cannot be safely transmuted + assert::nothing::(); //~ ERROR: cannot be safely transmuted + assert::nothing::(); //~ ERROR: cannot be safely transmuted + + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::nothing::(); //~ ERROR: cannot be safely transmuted + + assert::safety::<[u8; 3], [NonZeroU8; 3]>(); //~ ERROR: cannot be safely transmuted + assert::safety::<[i16; 3], [NonZeroI16; 3]>(); //~ ERROR: cannot be safely transmuted + assert::safety::<[char; 3], [NonZeroChar; 3]>(); //~ ERROR: cannot be safely transmuted + + assert::safety::<(u8, u16), (NonZeroU8, NonZeroU16)>(); //~ ERROR: cannot be safely transmuted + assert::safety::<(char, u32), (NonZeroChar, NonZeroU32)>(); //~ ERROR: cannot be safely transmuted + + assert::safety::>(); //~ ERROR: cannot be safely transmuted + assert::safety::>(); //~ ERROR: cannot be safely transmuted + + assert::safety::(); //~ ERROR: cannot be safely transmuted + assert::safety::(); //~ ERROR: cannot be safely transmuted +} diff --git a/tests/ui/transmutability/nonzero.stderr b/tests/ui/transmutability/nonzero.stderr new file mode 100644 index 0000000000000..73214530aa3c8 --- /dev/null +++ b/tests/ui/transmutability/nonzero.stderr @@ -0,0 +1,558 @@ +error[E0277]: `u8` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:28:26 + | +LL | assert::safety::(); + | ^^^^^^^^^ at least one value of `u8` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `u16` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:29:27 + | +LL | assert::safety::(); + | ^^^^^^^^^^ at least one value of `u16` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `u32` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:30:27 + | +LL | assert::safety::(); + | ^^^^^^^^^^ at least one value of `u32` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `u64` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:31:27 + | +LL | assert::safety::(); + | ^^^^^^^^^^ at least one value of `u64` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `u128` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:32:28 + | +LL | assert::safety::(); + | ^^^^^^^^^^^ at least one value of `u128` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `usize` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:33:29 + | +LL | assert::safety::(); + | ^^^^^^^^^^^^ at least one value of `usize` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `i8` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:35:26 + | +LL | assert::safety::(); + | ^^^^^^^^^ at least one value of `i8` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `i16` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:36:27 + | +LL | assert::safety::(); + | ^^^^^^^^^^ at least one value of `i16` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `i32` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:37:27 + | +LL | assert::safety::(); + | ^^^^^^^^^^ at least one value of `i32` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `i64` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:38:27 + | +LL | assert::safety::(); + | ^^^^^^^^^^ at least one value of `i64` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `i128` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:39:28 + | +LL | assert::safety::(); + | ^^^^^^^^^^^ at least one value of `i128` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `isize` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:40:29 + | +LL | assert::safety::(); + | ^^^^^^^^^^^^ at least one value of `isize` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:42:34 + | +LL | assert::nothing::(); + | ^^^^^^^^^ `NonZero` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/nonzero.rs:9:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:43:35 + | +LL | assert::nothing::(); + | ^^^^^^^^^^ `NonZero` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/nonzero.rs:9:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:44:35 + | +LL | assert::nothing::(); + | ^^^^^^^^^^ `NonZero` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/nonzero.rs:9:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:45:35 + | +LL | assert::nothing::(); + | ^^^^^^^^^^ `NonZero` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/nonzero.rs:9:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:46:36 + | +LL | assert::nothing::(); + | ^^^^^^^^^^^ `NonZero` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/nonzero.rs:9:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:47:37 + | +LL | assert::nothing::(); + | ^^^^^^^^^^^^ `NonZero` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/nonzero.rs:9:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:49:34 + | +LL | assert::nothing::(); + | ^^^^^^^^^ `NonZero` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/nonzero.rs:9:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:50:35 + | +LL | assert::nothing::(); + | ^^^^^^^^^^ `NonZero` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/nonzero.rs:9:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:51:35 + | +LL | assert::nothing::(); + | ^^^^^^^^^^ `NonZero` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/nonzero.rs:9:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:52:35 + | +LL | assert::nothing::(); + | ^^^^^^^^^^ `NonZero` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/nonzero.rs:9:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:53:36 + | +LL | assert::nothing::(); + | ^^^^^^^^^^^ `NonZero` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/nonzero.rs:9:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:54:37 + | +LL | assert::nothing::(); + | ^^^^^^^^^^^^ `NonZero` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/nonzero.rs:9:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `char` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:56:28 + | +LL | assert::safety::(); + | ^^^^^^^^^^^ at least one value of `char` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `u32` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:57:27 + | +LL | assert::safety::(); + | ^^^^^^^^^^^ at least one value of `u32` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:58:34 + | +LL | assert::safety::(); + | ^^^^^^^^^^^ at least one value of `NonZero` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `NonZero` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:59:36 + | +LL | assert::nothing::(); + | ^^^^^^^^^^^ `NonZero` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/nonzero.rs:9:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `[u8; 3]` cannot be safely transmuted into `[NonZero; 3]` + --> $DIR/nonzero.rs:61:31 + | +LL | assert::safety::<[u8; 3], [NonZeroU8; 3]>(); + | ^^^^^^^^^^^^^^ at least one value of `[u8; 3]` isn't a bit-valid value of `[NonZero; 3]` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `[i16; 3]` cannot be safely transmuted into `[NonZero; 3]` + --> $DIR/nonzero.rs:62:32 + | +LL | assert::safety::<[i16; 3], [NonZeroI16; 3]>(); + | ^^^^^^^^^^^^^^^ at least one value of `[i16; 3]` isn't a bit-valid value of `[NonZero; 3]` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `[char; 3]` cannot be safely transmuted into `[NonZero; 3]` + --> $DIR/nonzero.rs:63:33 + | +LL | assert::safety::<[char; 3], [NonZeroChar; 3]>(); + | ^^^^^^^^^^^^^^^^ at least one value of `[char; 3]` isn't a bit-valid value of `[NonZero; 3]` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `(u8, u16)` cannot be safely transmuted into `(NonZero, NonZero)` + --> $DIR/nonzero.rs:65:33 + | +LL | assert::safety::<(u8, u16), (NonZeroU8, NonZeroU16)>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ at least one value of `(u8, u16)` isn't a bit-valid value of `(NonZero, NonZero)` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `(char, u32)` cannot be safely transmuted into `(NonZero, NonZero)` + --> $DIR/nonzero.rs:66:35 + | +LL | assert::safety::<(char, u32), (NonZeroChar, NonZeroU32)>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ at least one value of `(char, u32)` isn't a bit-valid value of `(NonZero, NonZero)` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `u32` cannot be safely transmuted into `Option>` + --> $DIR/nonzero.rs:68:27 + | +LL | assert::safety::>(); + | ^^^^^^^^^^^^^^^^^^^ at least one value of `u32` isn't a bit-valid value of `Option>` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `NonZero` cannot be safely transmuted into `Option>` + --> $DIR/nonzero.rs:69:34 + | +LL | assert::safety::>(); + | ^^^^^^^^^^^^^^^^^^^ at least one value of `NonZero` isn't a bit-valid value of `Option>` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `bool` cannot be safely transmuted into `NonZero` + --> $DIR/nonzero.rs:71:28 + | +LL | assert::safety::(); + | ^^^^^^^^^ at least one value of `bool` isn't a bit-valid value of `NonZero` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error[E0277]: `NonZero` cannot be safely transmuted into `bool` + --> $DIR/nonzero.rs:72:33 + | +LL | assert::safety::(); + | ^^^^ at least one value of `NonZero` isn't a bit-valid value of `bool` + | +note: required by a bound in `safety` + --> $DIR/nonzero.rs:15:14 + | +LL | pub fn safety() + | ------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `safety` + +error: aborting due to 37 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/safety/field-visibility.rs b/tests/ui/transmutability/safety/field-visibility.rs new file mode 100644 index 0000000000000..e70f17cc07b36 --- /dev/null +++ b/tests/ui/transmutability/safety/field-visibility.rs @@ -0,0 +1,49 @@ +#![feature(transmutability)] +#![feature(unsafe_fields)] +#![allow(dead_code, incomplete_features)] + +mod assert { + use std::mem::{Assume, TransmuteFrom}; + + pub fn nothing() + where + Dst: TransmuteFrom, + { + } + + pub fn safety() + where + Dst: TransmuteFrom, + { + } +} + +mod owner { + #[repr(C)] + pub struct PublicField { + pub field: u8, + } + + #[repr(C)] + pub struct PrivateField { + field: u8, + } + + #[repr(C)] + pub struct PublicUnsafeField { + pub unsafe field: u8, + } +} + +fn main() { + assert::nothing::(); + assert::nothing::(); + + assert::nothing::(); //~ ERROR cannot be safely transmuted + assert::safety::(); + assert::nothing::(); + + assert::nothing::(); //~ ERROR cannot be safely transmuted + assert::safety::(); + assert::nothing::(); +} diff --git a/tests/ui/transmutability/safety/field-visibility.stderr b/tests/ui/transmutability/safety/field-visibility.stderr new file mode 100644 index 0000000000000..0b03506b92ee3 --- /dev/null +++ b/tests/ui/transmutability/safety/field-visibility.stderr @@ -0,0 +1,33 @@ +error[E0277]: `u8` cannot be safely transmuted into `PrivateField` + --> $DIR/field-visibility.rs:42:27 + | +LL | assert::nothing::(); + | ^^^^^^^^^^^^^^^^^^^ `PrivateField` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/field-visibility.rs:10:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error[E0277]: `u8` cannot be safely transmuted into `PublicUnsafeField` + --> $DIR/field-visibility.rs:46:27 + | +LL | assert::nothing::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ `PublicUnsafeField` may carry safety invariants + | +note: required by a bound in `nothing` + --> $DIR/field-visibility.rs:10:14 + | +LL | pub fn nothing() + | ------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `nothing` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/safety/should_reject_if_ref_src_has_safety_invariant.rs b/tests/ui/transmutability/safety/should_reject_if_ref_src_has_safety_invariant.rs index 16d163d5420b3..203f77e9058b1 100644 --- a/tests/ui/transmutability/safety/should_reject_if_ref_src_has_safety_invariant.rs +++ b/tests/ui/transmutability/safety/should_reject_if_ref_src_has_safety_invariant.rs @@ -2,7 +2,7 @@ //! be rejected if the source potentially carries safety invariants. #![crate_type = "lib"] -#![feature(transmutability)] +#![feature(transmutability, unsafe_fields)] #![allow(dead_code)] mod assert { @@ -17,7 +17,7 @@ mod assert { fn test() { #[repr(C)] struct Src { - non_zero: u8, + pub unsafe non_zero: u8, } type Dst = u8; assert::is_transmutable::<&mut Src, &mut Dst>(); //~ ERROR cannot be safely transmuted