Skip to content

Commit 11aa613

Browse files
committed
impove integer check
1 parent 6aedccd commit 11aa613

4 files changed

Lines changed: 190 additions & 96 deletions

File tree

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod simd;
66

77
use std::assert_matches;
88

9+
use either::{Either, Left, Right};
910
use rustc_abi::{FieldIdx, HasDataLayout, Size, VariantIdx};
1011
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
1112
use rustc_ast::{IntTy, UintTy};
@@ -60,8 +61,7 @@ pub(crate) enum MinMax {
6061
enum 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
}

library/core/src/ffi/va_list.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ impl<'f> VaList<'f> {
406406
/// - `T` is a pointer to [`i8]` or [`u8`] and `U` is a pointer to [`c_void`].
407407
///
408408
/// When `T` and `U` are both integer types, the value that the caller passes must be
409-
/// representable by `U`.
409+
/// representable by `T` and `U`.
410410
///
411411
/// [`c_void`]: core::ffi::c_void
412412
#[inline] // Avoid codegen when not used to help backends that don't support VaList.

tests/ui/consts/const-eval/c-variadic-fail.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,17 @@ unsafe fn read_cast_numeric() {
5252
const { read_as::<i32>(1u32) };
5353

5454
const { read_as::<u32>(-1i32) };
55-
//~^ ERROR va_arg value mismatch: value `-1` cannot be represented by type u32
55+
//~^ ERROR va_arg value mismatch: value `-1_i32` cannot be represented by type u32
56+
const { read_as::<u32>(i32::MIN) };
57+
//~^ ERROR va_arg value mismatch: value `-2147483648_i32` cannot be represented by type u32
5658
const { read_as::<i32>(u32::MAX) };
57-
//~^ ERROR va_arg value mismatch: value `4294967295` cannot be represented by type i32
59+
//~^ ERROR va_arg value mismatch: value `4294967295_u32` cannot be represented by type i32
60+
const { read_as::<i32>(i32::MAX as u32 + 1) };
61+
//~^ ERROR va_arg value mismatch: value `2147483648_u32` cannot be represented by type i32
62+
const { read_as::<i64>(u64::MAX) };
63+
//~^ ERROR va_arg value mismatch: value `18446744073709551615_u64` cannot be represented by type i64
64+
const { read_as::<i64>(i64::MAX as u64 + 1) };
65+
//~^ ERROR va_arg value mismatch: value `9223372036854775808_u64` cannot be represented by type i64
5866

5967
const { read_as::<i32>(1u64) };
6068
//~^ ERROR va_arg type mismatch: requested `i32` is incompatible with next argument of type `u64`

0 commit comments

Comments
 (0)