Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 185 additions & 1 deletion c2rust-transpile/src/c_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2183,6 +2183,190 @@ impl CTypeKind {
_ => unimplemented!("Printer::print_type({:?})", self),
}
}

/// Whether `value` is guaranteed to be in this integer type's range.
/// Thus, the narrowest possible range is used.
///
/// For example, for [`Self::Long`], [`i32`]'s range is used,
/// as on Linux and macOS (LP64), it's an [`i64`],
/// but on Windows (LLP64), it's only an [`i32`].
///
/// This should only be called on integer types.
/// Other types will return `false`.
pub fn guaranteed_integer_in_range(&self, value: u64) -> bool {
fn in_range<T: TryFrom<u64>>(value: u64) -> bool {
T::try_from(value).is_ok()
}

use CTypeKind::*;
match *self {
Void => false,

// Kind of an integer type, but would definitely need an explicit cast.
Bool => false,

// Can be signed or unsigned, so choose the minimum range of each.
Char => (u8::MIN as u64..=i8::MAX as u64).contains(&value),
WChar => in_range::<i32>(value),

// `int` is at least `i16` and `long` is at least `i32`.
SChar => in_range::<i8>(value),
Short => in_range::<i16>(value),
Int => in_range::<i16>(value),
Long => in_range::<i32>(value),
LongLong => in_range::<i64>(value),

// `unsigned int` is at least `u16` and `unsigned long` is at least `u32`.
UChar => in_range::<u8>(value),
UShort => in_range::<u16>(value),
UInt => in_range::<u16>(value),
ULong => in_range::<u32>(value),
ULongLong => in_range::<u64>(value),

Int8 => in_range::<i8>(value),
Int16 => in_range::<i16>(value),
Int32 => in_range::<i32>(value),
Int64 => in_range::<i64>(value),
Int128 => in_range::<i128>(value),

UInt8 => in_range::<u8>(value),
UInt16 => in_range::<u16>(value),
UInt32 => in_range::<u32>(value),
UInt64 => in_range::<u64>(value),
UInt128 => in_range::<u128>(value),

// There's no guarantee on pointer size, but `NULL` should work.
IntPtr => value == 0,
UIntPtr => value == 0,

IntMax => in_range::<i64>(value),
UIntMax => in_range::<u64>(value),

// `size_t` is at least a `u16`, and similar for `ssize_t` and `ptrdiff_t`.
Size => in_range::<u16>(value),
SSize => in_range::<i16>(value),
PtrDiff => in_range::<i16>(value),

// Floats, see `Self::guaranteed_float_in_range`.
Float => false,
Double => false,
LongDouble => false,
Half => false,
BFloat16 => false,
Float128 => false,

// Non-scalars.
Complex(_) => false,
Pointer(_) => false,
Reference(_) => false,
ConstantArray(_, _) => false,
IncompleteArray(_) => false,
VariableArray(_, _) => false,
TypeOf(_) => false,
TypeOfExpr(_) => false,
Function(_, _, _, _, _) => false,
Typedef(_) => false,
Decayed(_) => false,
Elaborated(_) => false,
Paren(_) => false,
Struct(_) => false,
Union(_) => false,
Enum(_) => false,
BuiltinFn => false,
Attributed(_, _) => false,
BlockPointer(_) => false,
Vector(_, _) => false,
UnhandledSveType => false,
Atomic(_) => false,
}
}

/// See [`Self::guaranteed_integer_in_range`].
/// This is the same, but for floats.
///
/// This should only be called on float types.
/// Other types will return `false`.
pub fn guaranteed_float_in_range(&self, value: f64) -> bool {
fn in_range<T: TryFrom<f64>>(value: f64) -> bool {
T::try_from(value).is_ok()
}

use CTypeKind::*;
match *self {
// `f32: TryFrom<f64>` is not implemented.
// C `float`s are not guaranteed to be `f32`,
// but Rust (namely `libc`) doesn't support any platform where this isn't the case.
Float => value >= f32::MIN as f64 && value <= f32::MAX as f64,

// Similarly to `float`, `double` is not guaranteed to be `f64`,
// but `libc` doesn't support any platform where this isn't the case.
Double => in_range::<f64>(value),

// `long double` (not `f128`) is only guaranteed to be at least as precise as a `double`.
LongDouble => in_range::<f64>(value),

// All `f64`s are valid `f128`s.
Float128 => in_range::<f64>(value),

// TODO Would like to depend on `half`.
Half => todo!("f16 range"),
BFloat16 => todo!("bf16 range"),

Void => false,
Bool => false,
Char => false,
SChar => false,
Short => false,
Int => false,
Long => false,
LongLong => false,
UChar => false,
UShort => false,
UInt => false,
ULong => false,
ULongLong => false,
Int128 => false,
UInt128 => false,
Complex(_) => false,
Pointer(_) => false,
Reference(_) => false,
ConstantArray(_, _) => false,
IncompleteArray(_) => false,
VariableArray(_, _) => false,
TypeOf(_) => false,
TypeOfExpr(_) => false,
Function(_, _, _, _, _) => false,
Typedef(_) => false,
Decayed(_) => false,
Elaborated(_) => false,
Paren(_) => false,
Struct(_) => false,
Union(_) => false,
Enum(_) => false,
BuiltinFn => false,
Attributed(_, _) => false,
BlockPointer(_) => false,
Vector(_, _) => false,
UnhandledSveType => false,
Atomic(_) => false,
Int8 => false,
Int16 => false,
Int32 => false,
Int64 => false,
IntPtr => false,
UInt8 => false,
UInt16 => false,
UInt32 => false,
UInt64 => false,
UIntPtr => false,
IntMax => false,
UIntMax => false,
Size => false,
SSize => false,
PtrDiff => false,
WChar => false,
}
}
}

impl Display for CTypeKind {
Expand Down Expand Up @@ -2286,7 +2470,7 @@ impl CTypeKind {

pub fn is_floating_type(&self) -> bool {
use CTypeKind::*;
matches!(self, Float | Double | LongDouble)
matches!(self, Float | Double | LongDouble | Half | BFloat16)
}

pub fn as_underlying_decl(&self) -> Option<CDeclId> {
Expand Down
16 changes: 9 additions & 7 deletions c2rust-transpile/src/translator/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,18 @@ impl<'c> Translation<'c> {
mk().cast_expr(value, target_ty)
}

/// Return whether the literal can be directly translated as this type. This does not check
/// that the literal fits into the type's range of values (as doing so is in general dependent
/// on the target platform), just that it is the appropriate kind for the type.
pub fn literal_kind_matches_ty(&self, lit: &CLiteral, ty: CQualTypeId) -> bool {
/// Return whether the literal can be directly translated as this type.
pub fn literal_matches_ty(&self, lit: &CLiteral, ty: CQualTypeId) -> bool {
let ty_kind = &self.ast_context.resolve_type(ty.ctype).kind;
match *lit {
CLiteral::Integer(..) if ty_kind.is_integral_type() && !ty_kind.is_bool() => true,
CLiteral::Integer(value, _) if ty_kind.is_integral_type() && !ty_kind.is_bool() => {
ty_kind.guaranteed_integer_in_range(value)
}
// `convert_literal` always casts these to i32.
CLiteral::Character(..) => matches!(ty_kind, CTypeKind::Int32),
CLiteral::Floating(..) if ty_kind.is_floating_type() => true,
CLiteral::Character(_value) => matches!(ty_kind, CTypeKind::Int32),
CLiteral::Floating(value, _) if ty_kind.is_floating_type() => {
ty_kind.guaranteed_float_in_range(value)
}
_ => false,
}
}
Expand Down
2 changes: 1 addition & 1 deletion c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3699,7 +3699,7 @@ impl<'c> Translation<'c> {
if let (Some(ty), CExprKind::Literal(_ty, lit)) =
(override_ty, &self.ast_context[expr].kind)
{
if self.literal_kind_matches_ty(lit, ty) {
if self.literal_matches_ty(lit, ty) {
return self.convert_expr(ctx, expr, override_ty);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <stdint.h>

void f() {
int32_t a = 0x80000000U;
int32_t b = 0x80000000;
int32_t c = 0x8000000000000000;

int32_t d = (unsigned int)0x80000000U; // Test with explicit cast.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
source: c2rust-transpile/tests/snapshots.rs
expression: cat tests/snapshots/out_of_range_lit.rs
input_file: c2rust-transpile/tests/snapshots/out_of_range_lit.c
---
#![allow(
dead_code,
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unused_assignments,
unused_mut
)]
pub type __int32_t = i32;
pub type int32_t = __int32_t;
#[no_mangle]
pub unsafe extern "C" fn f() {
let mut a: int32_t = 0x80000000 as ::core::ffi::c_uint as int32_t;
let mut b: int32_t = 0x80000000 as ::core::ffi::c_uint as int32_t;
let mut c: int32_t = 0x8000000000000000 as ::core::ffi::c_ulong as int32_t;
let mut d: int32_t = 0x80000000 as ::core::ffi::c_uint as int32_t;
}
5 changes: 2 additions & 3 deletions c2rust-transpile/tests/snapshots/[email protected]
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
---
source: c2rust-transpile/tests/snapshots.rs
assertion_line: 69
expression: cat tests/snapshots/platform-specific/types.rs
input_file: c2rust-transpile/tests/snapshots/platform-specific/types.c
expression: cat tests/snapshots/os-specific/types.linux.rs
input_file: c2rust-transpile/tests/snapshots/os-specific/types.c
---
#![allow(
dead_code,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
source: c2rust-transpile/tests/snapshots.rs
expression: cat tests/snapshots/out_of_range_lit.rs
input_file: c2rust-transpile/tests/snapshots/out_of_range_lit.c
---
#![allow(
dead_code,
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unused_assignments,
unused_mut
)]
pub type int32_t = i32;
#[no_mangle]
pub unsafe extern "C" fn f() {
let mut a: int32_t = 0x80000000 as ::core::ffi::c_uint as int32_t;
let mut b: int32_t = 0x80000000 as ::core::ffi::c_uint as int32_t;
let mut c: int32_t = 0x8000000000000000 as ::core::ffi::c_ulong as int32_t;
let mut d: int32_t = 0x80000000 as ::core::ffi::c_uint as int32_t;
}
5 changes: 2 additions & 3 deletions c2rust-transpile/tests/snapshots/[email protected]
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
---
source: c2rust-transpile/tests/snapshots.rs
assertion_line: 69
expression: cat tests/snapshots/platform-specific/types.rs
input_file: c2rust-transpile/tests/snapshots/platform-specific/types.c
expression: cat tests/snapshots/os-specific/types.macos.rs
input_file: c2rust-transpile/tests/snapshots/os-specific/types.c
---
#![allow(
dead_code,
Expand Down
2 changes: 1 addition & 1 deletion c2rust-transpile/tests/snapshots/[email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ pub unsafe extern "C" fn use_local_value() -> ::core::ffi::c_int {
}
#[no_mangle]
pub unsafe extern "C" fn use_portable_type(mut len: uintptr_t) -> bool {
return len <= (UINTPTR_MAX as uintptr_t).wrapping_div(2 as uintptr_t);
return len <= (UINTPTR_MAX as uintptr_t).wrapping_div(2 as ::core::ffi::c_int as uintptr_t);
}
#[no_mangle]
pub unsafe extern "C" fn ntlm_v2_blob_len(mut ntlm: *mut ntlmdata) -> ::core::ffi::c_uint {
Expand Down