@@ -6,6 +6,7 @@ mod simd;
66
77use std:: assert_matches;
88
9+ use either:: { Either , Left , Right } ;
910use rustc_abi:: { FieldIdx , HasDataLayout , Size , VariantIdx } ;
1011use rustc_apfloat:: ieee:: { Double , Half , Quad , Single } ;
1112use rustc_ast:: { IntTy , UintTy } ;
@@ -60,8 +61,7 @@ pub(crate) enum MinMax {
6061enum VarArgCompatible {
6162 Identical ,
6263 Incompatible ,
63- CastSignedToUnsigned ( IntTy ) ,
64- CastUnsignedToSigned ( UintTy ) ,
64+ CastIntTo ( Either < IntTy , UintTy > ) ,
6565 ValidPtrCast ,
6666}
6767
@@ -781,8 +781,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
781781 arg_mplace : & MPlaceTy < ' tcx , M :: Provenance > ,
782782 callee_type : TyAndLayout < ' tcx > ,
783783 ) -> InterpResult < ' tcx > {
784+ let callee_ty = callee_type. ty ;
785+ let caller_ty = arg_mplace. layout . ty ;
786+
784787 // Identical types are clearly compatible.
785- if arg_mplace . layout . ty == callee_type . ty {
788+ if caller_ty == callee_ty {
786789 return interp_ok ( ( ) ) ;
787790 }
788791
@@ -795,20 +798,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
795798 )
796799 }
797800
798- fn validate_cast < ' tcx , T , U > ( x : T , target_ty : Ty < ' tcx > ) -> InterpResult < ' tcx >
799- where
800- T : std:: fmt:: Display + Copy ,
801- U : std:: fmt:: Display + TryFrom < T > ,
802- {
803- if U :: try_from ( x) . is_ok ( ) {
804- return interp_ok ( ( ) ) ;
805- }
806-
807- throw_ub_format ! (
808- "va_arg value mismatch: value `{x}` cannot be represented by type {target_ty}" ,
809- )
810- }
811-
812801 match self . validate_c_variadic_compatible_ty ( arg_mplace. layout . ty , callee_type. ty ) {
813802 VarArgCompatible :: Identical => interp_ok ( ( ) ) ,
814803 VarArgCompatible :: ValidPtrCast => interp_ok ( ( ) ) ,
@@ -817,43 +806,24 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
817806 callee_type. ty,
818807 arg_mplace. layout. ty,
819808 ) ,
820- VarArgCompatible :: CastSignedToUnsigned ( int_ty ) => {
809+ VarArgCompatible :: CastIntTo ( target_ty ) => {
821810 // Check that the value can be represented in the target type.
822- let callee_ty = callee_type. ty ;
823- let scalar = self . read_scalar ( arg_mplace) ?;
824- match int_ty {
825- IntTy :: Isize => match self . data_layout ( ) . pointer_size ( ) . bits ( ) {
826- 16 => validate_cast :: < i16 , u16 > ( scalar. to_i16 ( ) ?, callee_ty) ?,
827- 32 => validate_cast :: < i32 , u32 > ( scalar. to_i32 ( ) ?, callee_ty) ?,
828- 64 => validate_cast :: < i64 , u64 > ( scalar. to_i64 ( ) ?, callee_ty) ?,
829- _ => unreachable ! ( "unexpected pointer size" ) ,
830- } ,
831- IntTy :: I8 => validate_cast :: < i8 , u8 > ( scalar. to_i8 ( ) ?, callee_ty) ?,
832- IntTy :: I16 => validate_cast :: < i16 , u16 > ( scalar. to_i16 ( ) ?, callee_ty) ?,
833- IntTy :: I32 => validate_cast :: < i32 , u32 > ( scalar. to_i32 ( ) ?, callee_ty) ?,
834- IntTy :: I64 => validate_cast :: < i64 , u64 > ( scalar. to_i64 ( ) ?, callee_ty) ?,
835- IntTy :: I128 => validate_cast :: < i128 , u128 > ( scalar. to_i128 ( ) ?, callee_ty) ?,
811+ let ptr_size = self . data_layout ( ) . pointer_size ( ) ;
812+ let size = match target_ty {
813+ Left ( int_ty) => Size :: from_bits ( int_ty. bit_width ( ) ) . unwrap_or ( ptr_size) ,
814+ Right ( uint_ty) => Size :: from_bits ( uint_ty. bit_width ( ) ) . unwrap_or ( ptr_size) ,
836815 } ;
837816
838- interp_ok ( ( ) )
839- }
840- VarArgCompatible :: CastUnsignedToSigned ( uint_ty) => {
841- // Check that the value can be represented in the target type.
842- let callee_ty = callee_type. ty ;
843817 let scalar = self . read_scalar ( arg_mplace) ?;
844- match uint_ty {
845- UintTy :: Usize => match self . data_layout ( ) . pointer_size ( ) . bits ( ) {
846- 16 => validate_cast :: < u16 , i16 > ( scalar. to_u16 ( ) ?, callee_ty) ?,
847- 32 => validate_cast :: < u32 , i32 > ( scalar. to_u32 ( ) ?, callee_ty) ?,
848- 64 => validate_cast :: < u64 , i64 > ( scalar. to_u64 ( ) ?, callee_ty) ?,
849- _ => unreachable ! ( "unexpected pointer size" ) ,
850- } ,
851- UintTy :: U8 => validate_cast :: < u8 , i8 > ( scalar. to_u8 ( ) ?, callee_ty) ?,
852- UintTy :: U16 => validate_cast :: < u16 , i16 > ( scalar. to_u16 ( ) ?, callee_ty) ?,
853- UintTy :: U32 => validate_cast :: < u32 , i32 > ( scalar. to_u32 ( ) ?, callee_ty) ?,
854- UintTy :: U64 => validate_cast :: < u64 , i64 > ( scalar. to_u64 ( ) ?, callee_ty) ?,
855- UintTy :: U128 => validate_cast :: < u128 , i128 > ( scalar. to_u128 ( ) ?, callee_ty) ?,
856- } ;
818+ if scalar. to_int ( size) ? < 0 {
819+ throw_ub_format ! (
820+ "va_arg value mismatch: value `{value}_{caller_ty}` cannot be represented by type {callee_ty}" ,
821+ value = match target_ty {
822+ Left ( _int_ty) => scalar. to_int( size) ?. to_string( ) ,
823+ Right ( _uint_ty) => scalar. to_uint( size) ?. to_string( ) ,
824+ }
825+ )
826+ }
857827
858828 interp_ok ( ( ) )
859829 }
@@ -869,7 +839,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
869839 /// - `T` is a signed integer and `U` the corresponding unsigned integer,
870840 /// - `T` is an unsigned integer and `U` the corresponding signed integer,
871841 /// - `T` and `U` are both pointers, and their target types are compatible.
872- /// - `T` is a pointer to `c_void` and `U` is a pointer to `i8` or `u8`.
842+ /// - `T` is a pointer to `c_void` and `U` is a pointer to `i8` or `u8` (i.e., any "character type") .
873843 /// - `T` is a pointer to `i8` or `u8` and `U` is a pointer to `c_void`.
874844 fn validate_c_variadic_compatible_ty (
875845 & mut self ,
@@ -880,13 +850,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
880850 return VarArgCompatible :: Identical ;
881851 }
882852
883- // The signedness of c_char is irrelevant here.
853+ // Any character type (`char`, `unsigned char` and `signed char`) is compatible with
854+ // `void*`, so the signedness of `c_char` is irrelevant here.
884855 let is_c_char = |ty : Ty < ' _ > | matches ! ( ty. kind( ) , ty:: Uint ( UintTy :: U8 ) | ty:: Int ( IntTy :: I8 ) ) ;
885856
886857 match ( caller_type. kind ( ) , callee_type. kind ( ) ) {
887858 ( ty:: RawPtr ( caller_target_ty, _) , ty:: RawPtr ( callee_target_ty, _) ) => {
888859 // Accept the cast if one type is pointer to qualified or unqualified void,
889860 // and the other is a pointer to a qualified or unqualified character type.
861+ //
862+ // In C types can be qualified by a combination of `const`, `volatile` and
863+ // `restrict`. These properties are not relevant for the ABI.
890864 if caller_target_ty. is_c_void ( self . tcx . tcx ) && is_c_char ( * callee_target_ty) {
891865 return VarArgCompatible :: ValidPtrCast ;
892866 }
0 commit comments