60
60
//!
61
61
//! - ([`try_`][try_transmute])[`transmute`] (conditionally) converts a value of
62
62
//! one type to a value of another type of the same size
63
- //! - [`transmute_mut`] converts a mutable reference of one type to a mutable
64
- //! reference of another type of the same size
65
- //! - [`transmute_ref`] converts a mutable or immutable reference
66
- //! of one type to an immutable reference of another type of the same size
63
+ //! - ([`try_`][try_transmute_mut])[`transmute_mut`] (conditionally) converts a
64
+ //! mutable reference of one type to a mutable reference of another type of
65
+ //! the same size
66
+ //! - ([`try_`][try_transmute_ref])[`transmute_ref`] (conditionally) converts a
67
+ //! mutable or immutable reference of one type to an immutable reference of
68
+ //! another type of the same size
67
69
//!
68
70
//! These macros perform *compile-time* alignment and size checks, but cannot be
69
71
//! used in generic contexts. For generic conversions, use the methods defined
@@ -4750,7 +4752,7 @@ macro_rules! transmute_mut {
4750
4752
/// This macro behaves like an invocation of this function:
4751
4753
///
4752
4754
/// ```ignore
4753
- /// const fn try_transmute<Src, Dst>(src: Src) -> Result<Dst, ValidityError<Src, Dst>>
4755
+ /// fn try_transmute<Src, Dst>(src: Src) -> Result<Dst, ValidityError<Src, Dst>>
4754
4756
/// where
4755
4757
/// Src: IntoBytes,
4756
4758
/// Dst: TryFromBytes,
@@ -4784,10 +4786,9 @@ macro_rules! transmute_mut {
4784
4786
///
4785
4787
/// // 2u8 → bool = error
4786
4788
/// assert!(matches!(
4787
- /// try_transmute!(3u8 ),
4789
+ /// try_transmute!(2u8 ),
4788
4790
/// Result::<bool, _>::Err(ValidityError { .. })
4789
4791
/// ));
4790
- ///
4791
4792
/// ```
4792
4793
#[ macro_export]
4793
4794
macro_rules! try_transmute {
@@ -4814,6 +4815,231 @@ macro_rules! try_transmute {
4814
4815
} }
4815
4816
}
4816
4817
4818
+ /// Conditionally transmutes a mutable or immutable reference of one type to an
4819
+ /// immutable reference of another type of the same size.
4820
+ ///
4821
+ /// This macro behaves like an invocation of this function:
4822
+ ///
4823
+ /// ```ignore
4824
+ /// fn try_transmute_ref<Src, Dst>(src: &Src) -> Result<&Dst, ValidityError<&Src, &Dst>>
4825
+ /// where
4826
+ /// Src: IntoBytes + Immutable,
4827
+ /// Dst: TryFromBytes + Immutable,
4828
+ /// size_of::<Src>() == size_of::<Dst>(),
4829
+ /// align_of::<Src>() >= align_of::<Dst>(),
4830
+ /// {
4831
+ /// # /*
4832
+ /// ...
4833
+ /// # */
4834
+ /// }
4835
+ /// ```
4836
+ ///
4837
+ /// However, unlike a function, this macro can only be invoked when the types of
4838
+ /// `Src` and `Dst` are completely concrete. The types `Src` and `Dst` are
4839
+ /// inferred from the calling context; they cannot be explicitly specified in
4840
+ /// the macro invocation.
4841
+ ///
4842
+ /// Note that the `Src` produced by the expression `$e` will *not* be dropped.
4843
+ /// Semantically, its bits will be copied into a new value of type `Dst`, the
4844
+ /// original `Src` will be forgotten, and the value of type `Dst` will be
4845
+ /// returned.
4846
+ ///
4847
+ /// # Examples
4848
+ ///
4849
+ /// ```
4850
+ /// # use zerocopy::*;
4851
+ /// // 0u8 → bool = false
4852
+ /// assert_eq!(try_transmute_ref!(&0u8), Ok(&false));
4853
+ ///
4854
+ /// // 1u8 → bool = true
4855
+ /// assert_eq!(try_transmute_ref!(&1u8), Ok(&true));
4856
+ ///
4857
+ /// // 2u8 → bool = error
4858
+ /// assert!(matches!(
4859
+ /// try_transmute_ref!(&2u8),
4860
+ /// Result::<&bool, _>::Err(ValidityError { .. })
4861
+ /// ));
4862
+ /// ```
4863
+ ///
4864
+ /// # Alignment increase error message
4865
+ ///
4866
+ /// Because of limitations on macros, the error message generated when
4867
+ /// `try_transmute_ref!` is used to transmute from a type of lower alignment to
4868
+ /// a type of higher alignment is somewhat confusing. For example, the following
4869
+ /// code:
4870
+ ///
4871
+ /// ```compile_fail
4872
+ /// let increase_alignment: Result<&u16, _> = zerocopy::try_transmute_ref!(&[0u8; 2]);
4873
+ /// ```
4874
+ ///
4875
+ /// ...generates the following error:
4876
+ ///
4877
+ /// ```text
4878
+ /// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
4879
+ /// --> example.rs:1:47
4880
+ /// |
4881
+ /// 1 | let increase_alignment: Result<&u16, _> = zerocopy::try_transmute_ref!(&[0u8; 2]);
4882
+ /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4883
+ /// |
4884
+ /// = note: source type: `AlignOf<[u8; 2]>` (8 bits)
4885
+ /// = note: target type: `MaxAlignsOf<[u8; 2], u16>` (16 bits)
4886
+ /// = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `zerocopy::try_transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info)/// ```
4887
+ /// ```
4888
+ ///
4889
+ /// This is saying that `max(align_of::<T>(), align_of::<U>()) !=
4890
+ /// align_of::<T>()`, which is equivalent to `align_of::<T>() <
4891
+ /// align_of::<U>()`.
4892
+ #[ macro_export]
4893
+ macro_rules! try_transmute_ref {
4894
+ ( $e: expr) => { {
4895
+ // NOTE: This must be a macro (rather than a function with trait bounds)
4896
+ // because there's no way, in a generic context, to enforce that two
4897
+ // types have the same size. `core::mem::transmute` uses compiler magic
4898
+ // to enforce this so long as the types are concrete.
4899
+
4900
+ // Ensure that the source type is a reference or a mutable reference
4901
+ // (note that mutable references are implicitly reborrowed here).
4902
+ let e: & _ = $e;
4903
+
4904
+ #[ allow( unreachable_code, unused, clippy:: diverging_sub_expression) ]
4905
+ if false {
4906
+ // This branch, though never taken, ensures that `size_of::<T>() ==
4907
+ // size_of::<U>()` and that that `align_of::<T>() >=
4908
+ // align_of::<U>()`.
4909
+
4910
+ // `t` is inferred to have type `T` because it's assigned to `e` (of
4911
+ // type `&T`) as `&t`.
4912
+ let mut t = loop { } ;
4913
+ e = & t;
4914
+
4915
+ // `u` is inferred to have type `U` because it's used as `&u` as the
4916
+ // value returned from this branch.
4917
+ let u;
4918
+
4919
+ $crate:: assert_size_eq!( t, u) ;
4920
+ $crate:: assert_align_gt_eq!( t, u) ;
4921
+
4922
+ Ok ( & u)
4923
+ } else {
4924
+ $crate:: macro_util:: try_transmute_ref:: <_, _>( e)
4925
+ }
4926
+ } }
4927
+ }
4928
+
4929
+ /// Conditionally transmutes a mutable reference of one type to a mutable
4930
+ /// reference of another type of the same size.
4931
+ ///
4932
+ /// This macro behaves like an invocation of this function:
4933
+ ///
4934
+ /// ```ignore
4935
+ /// fn try_transmute_mut<Src, Dst>(src: &mut Src) -> Result<&mut Dst, ValidityError<&mut Src, &mut Dst>>
4936
+ /// where
4937
+ /// Src: IntoBytes,
4938
+ /// Dst: TryFromBytes,
4939
+ /// size_of::<Src>() == size_of::<Dst>(),
4940
+ /// align_of::<Src>() >= align_of::<Dst>(),
4941
+ /// {
4942
+ /// # /*
4943
+ /// ...
4944
+ /// # */
4945
+ /// }
4946
+ /// ```
4947
+ ///
4948
+ /// However, unlike a function, this macro can only be invoked when the types of
4949
+ /// `Src` and `Dst` are completely concrete. The types `Src` and `Dst` are
4950
+ /// inferred from the calling context; they cannot be explicitly specified in
4951
+ /// the macro invocation.
4952
+ ///
4953
+ /// Note that the `Src` produced by the expression `$e` will *not* be dropped.
4954
+ /// Semantically, its bits will be copied into a new value of type `Dst`, the
4955
+ /// original `Src` will be forgotten, and the value of type `Dst` will be
4956
+ /// returned.
4957
+ ///
4958
+ /// # Examples
4959
+ ///
4960
+ /// ```
4961
+ /// # use zerocopy::*;
4962
+ /// // 0u8 → bool = false
4963
+ /// let src = &mut 0u8;
4964
+ /// assert_eq!(try_transmute_mut!(src), Ok(&mut false));
4965
+ ///
4966
+ /// // 1u8 → bool = true
4967
+ /// let src = &mut 1u8;
4968
+ /// assert_eq!(try_transmute_mut!(src), Ok(&mut true));
4969
+ ///
4970
+ /// // 2u8 → bool = error
4971
+ /// let src = &mut 2u8;
4972
+ /// assert!(matches!(
4973
+ /// try_transmute_mut!(src),
4974
+ /// Result::<&mut bool, _>::Err(ValidityError { .. })
4975
+ /// ));
4976
+ /// ```
4977
+ ///
4978
+ /// # Alignment increase error message
4979
+ ///
4980
+ /// Because of limitations on macros, the error message generated when
4981
+ /// `try_transmute_ref!` is used to transmute from a type of lower alignment to
4982
+ /// a type of higher alignment is somewhat confusing. For example, the following
4983
+ /// code:
4984
+ ///
4985
+ /// ```compile_fail
4986
+ /// let src = &mut [0u8; 2];
4987
+ /// let increase_alignment: Result<&mut u16, _> = zerocopy::try_transmute_mut!(src);
4988
+ /// ```
4989
+ ///
4990
+ /// ...generates the following error:
4991
+ ///
4992
+ /// ```text
4993
+ /// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
4994
+ /// --> example.rs:2:51
4995
+ /// |
4996
+ /// 2 | let increase_alignment: Result<&mut u16, _> = zerocopy::try_transmute_mut!(src);
4997
+ /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4998
+ /// |
4999
+ /// = note: source type: `AlignOf<[u8; 2]>` (8 bits)
5000
+ /// = note: target type: `MaxAlignsOf<[u8; 2], u16>` (16 bits)
5001
+ /// = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `zerocopy::try_transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info)
5002
+ /// ```
5003
+ ///
5004
+ /// This is saying that `max(align_of::<T>(), align_of::<U>()) !=
5005
+ /// align_of::<T>()`, which is equivalent to `align_of::<T>() <
5006
+ /// align_of::<U>()`.
5007
+ #[ macro_export]
5008
+ macro_rules! try_transmute_mut {
5009
+ ( $e: expr) => { {
5010
+ // NOTE: This must be a macro (rather than a function with trait bounds)
5011
+ // because there's no way, in a generic context, to enforce that two
5012
+ // types have the same size. `core::mem::transmute` uses compiler magic
5013
+ // to enforce this so long as the types are concrete.
5014
+
5015
+ // Ensure that the source type is a mutable reference.
5016
+ let e: & mut _ = $e;
5017
+
5018
+ #[ allow( unreachable_code, unused, clippy:: diverging_sub_expression) ]
5019
+ if false {
5020
+ // This branch, though never taken, ensures that `size_of::<T>() ==
5021
+ // size_of::<U>()` and that that `align_of::<T>() >=
5022
+ // align_of::<U>()`.
5023
+
5024
+ // `t` is inferred to have type `T` because it's assigned to `e` (of
5025
+ // type `&mut T`) as `&mut t`.
5026
+ let mut t = loop { } ;
5027
+ e = & mut t;
5028
+
5029
+ // `u` is inferred to have type `U` because it's used as `&mut u` as
5030
+ // the value returned from this branch.
5031
+ let u;
5032
+
5033
+ $crate:: assert_size_eq!( t, u) ;
5034
+ $crate:: assert_align_gt_eq!( t, u) ;
5035
+
5036
+ Ok ( & mut u)
5037
+ } else {
5038
+ $crate:: macro_util:: try_transmute_mut:: <_, _>( e)
5039
+ }
5040
+ } }
5041
+ }
5042
+
4817
5043
/// Includes a file and safely transmutes it to a value of an arbitrary type.
4818
5044
///
4819
5045
/// The file will be included as a byte array, `[u8; N]`, which will be
@@ -5775,6 +6001,71 @@ mod tests {
5775
6001
mem:: forget ( y) ;
5776
6002
}
5777
6003
6004
+ #[ test]
6005
+ fn test_try_transmute_ref ( ) {
6006
+ // Test that memory is transmuted with `try_transmute_ref` as expected.
6007
+ let array_of_bools = & [ false , true , false , true , false , true , false , true ] ;
6008
+ let array_of_arrays = & [ [ 0 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] ] ;
6009
+ let x: Result < & [ [ u8 ; 2 ] ; 4 ] , _ > = try_transmute_ref ! ( array_of_bools) ;
6010
+ assert_eq ! ( x, Ok ( array_of_arrays) ) ;
6011
+ let x: Result < & [ bool ; 8 ] , _ > = try_transmute_ref ! ( array_of_arrays) ;
6012
+ assert_eq ! ( x, Ok ( array_of_bools) ) ;
6013
+
6014
+ // Test that it's legal to transmute a reference while shrinking the
6015
+ // lifetime.
6016
+ {
6017
+ let x: Result < & [ [ u8 ; 2 ] ; 4 ] , _ > = try_transmute_ref ! ( array_of_bools) ;
6018
+ assert_eq ! ( x, Ok ( array_of_arrays) ) ;
6019
+ }
6020
+
6021
+ // Test that `try_transmute_ref!` supports decreasing alignment.
6022
+ let u = AU64 ( 0 ) ;
6023
+ let array = [ 0u8 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ;
6024
+ let x: Result < & [ u8 ; 8 ] , _ > = try_transmute_ref ! ( & u) ;
6025
+ assert_eq ! ( x, Ok ( & array) ) ;
6026
+
6027
+ // Test that a mutable reference can be turned into an immutable one.
6028
+ let mut x = 0u8 ;
6029
+ #[ allow( clippy:: useless_transmute) ]
6030
+ let y: Result < & u8 , _ > = try_transmute_ref ! ( & mut x) ;
6031
+ assert_eq ! ( y, Ok ( & 0 ) ) ;
6032
+ }
6033
+
6034
+ #[ test]
6035
+ fn test_try_transmute_mut ( ) {
6036
+ // Test that memory is transmuted with `try_transmute_mut` as expected.
6037
+ let array_of_bools = & mut [ false , true , false , true , false , true , false , true ] ;
6038
+ let array_of_arrays = & mut [ [ 0u8 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] ] ;
6039
+ let x: Result < & mut [ [ u8 ; 2 ] ; 4 ] , _ > = try_transmute_mut ! ( array_of_bools) ;
6040
+ assert_eq ! ( x, Ok ( array_of_arrays) ) ;
6041
+
6042
+ let array_of_bools = & mut [ false , true , false , true , false , true , false , true ] ;
6043
+ let array_of_arrays = & mut [ [ 0u8 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] ] ;
6044
+ let x: Result < & mut [ bool ; 8 ] , _ > = try_transmute_mut ! ( array_of_arrays) ;
6045
+ assert_eq ! ( x, Ok ( array_of_bools) ) ;
6046
+
6047
+ // Test that it's legal to transmute a reference while shrinking the
6048
+ // lifetime.
6049
+ let array_of_bools = & mut [ false , true , false , true , false , true , false , true ] ;
6050
+ let array_of_arrays = & mut [ [ 0u8 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] ] ;
6051
+ {
6052
+ let x: Result < & mut [ [ u8 ; 2 ] ; 4 ] , _ > = try_transmute_mut ! ( array_of_bools) ;
6053
+ assert_eq ! ( x, Ok ( array_of_arrays) ) ;
6054
+ }
6055
+
6056
+ // Test that `try_transmute_mut!` supports decreasing alignment.
6057
+ let u = & mut AU64 ( 0 ) ;
6058
+ let array = & mut [ 0u8 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ;
6059
+ let x: Result < & mut [ u8 ; 8 ] , _ > = try_transmute_mut ! ( u) ;
6060
+ assert_eq ! ( x, Ok ( array) ) ;
6061
+
6062
+ // Test that a mutable reference can be turned into an immutable one.
6063
+ let mut x = 0u8 ;
6064
+ #[ allow( clippy:: useless_transmute) ]
6065
+ let y: Result < & mut u8 , _ > = try_transmute_mut ! ( & mut x) ;
6066
+ assert_eq ! ( y, Ok ( & mut 0 ) ) ;
6067
+ }
6068
+
5778
6069
#[ test]
5779
6070
fn test_transmute_mut ( ) {
5780
6071
// Test that memory is transmuted as expected.
0 commit comments