9090}
9191
9292/// This should act like `as`, keeping the value the same, removing any extra
93- /// bits
93+ /// bits.
94+ ///
95+ /// If the sign would change by casting, panics.
9496pub ( crate ) const fn as_primitive < T , V > ( value : V ) -> T
9597where
9698 V : AsPrimitive < T > ,
@@ -127,7 +129,7 @@ where
127129 } ;
128130
129131 let mut value = as_repr:: as_repr ( value) ;
130- let is_negative = V :: SIGNED && unsafe { is_negative ( value) } ;
132+ let negative = unsafe { is_negative ( value) } ;
131133
132134 // Convert to little endian
133135 if cfg ! ( target_endian = "big" ) {
@@ -136,7 +138,7 @@ where
136138
137139 let mut primitive: T = unsafe { mem:: zeroed ( ) } ;
138140
139- if is_negative {
141+ if negative {
140142 primitive = unsafe { not ( primitive) } ;
141143 }
142144
@@ -157,6 +159,11 @@ where
157159 primitive = unsafe { endian_swizzle ( primitive) } ;
158160 }
159161
162+ // Check if sign changed
163+ if unsafe { is_negative ( primitive) } ^ negative {
164+ panic ! ( "cannot change sign during cast" )
165+ }
166+
160167 primitive
161168}
162169
@@ -251,17 +258,23 @@ where
251258///
252259/// # Safety
253260///
254- /// - `T` must be a signed primitive value
261+ /// - `T` must be a primitive value (since `RangeablePrimitive` and `Primitive`
262+ /// are safe traits that could theoretically break this invariant, but
263+ /// shouldn't, this function must be marked unsafe)
255264const unsafe fn is_negative < T > ( input : T ) -> bool
256265where
257- T : Copy + Clone ,
266+ T : Primitive ,
258267{
259268 const {
260269 if !matches ! ( size_of:: <T >( ) , 1 | 2 | 4 | 8 | 16 ) {
261270 panic ! ( "invalid size" ) ;
262271 }
263272 }
264273
274+ if const { !T :: SIGNED } {
275+ return false ;
276+ }
277+
265278 match size_of :: < T > ( ) {
266279 1 => {
267280 let input: * const T = & input;
0 commit comments