Skip to content

Commit ffa4f4c

Browse files
committed
transpile: fix fn literal_kind_matches_ty to check if literals are in their type's range
This fixes #1435. If a literal is within its type's range, then no explicit cast is needed, but otherwise, a cast is still needed. For fixed-width types, this is simple, but for platform-dependent types like `int`, this is trickier, as we don't know what platform the transpiled Rust will be compiled on. Thus, we use the narrowest ranges that are guaranteed to be valid for each type. On some platforms, this will result in an extra cast, but the same code will work on all platforms.
1 parent 70dac75 commit ffa4f4c

File tree

13 files changed

+226
-54
lines changed

13 files changed

+226
-54
lines changed

c2rust-transpile/src/c_ast/mod.rs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2183,6 +2183,177 @@ impl CTypeKind {
21832183
_ => unimplemented!("Printer::print_type({:?})", self),
21842184
}
21852185
}
2186+
2187+
/// Whether `value` is guaranteed to be in this integer type's range.
2188+
/// Thus, minimum ranges are used (e.x. [`i16::MIN`] for [`Self::Int`]).
2189+
pub fn guaranteed_integer_in_range(&self, value: u64) -> bool {
2190+
fn in_range<T: TryFrom<u64>>(value: u64) -> bool {
2191+
T::try_from(value).is_ok()
2192+
}
2193+
2194+
use CTypeKind::*;
2195+
match *self {
2196+
Void => false,
2197+
2198+
Bool => (0..=1).contains(&value),
2199+
2200+
// Can be signed or unsigned, so choose the minimum range of each.
2201+
Char => (u8::MIN as u64..=i8::MAX as u64).contains(&value),
2202+
WChar => in_range::<i32>(value),
2203+
2204+
// `int` is at least `i16` and `long` is at least `i32`.
2205+
SChar => in_range::<i8>(value),
2206+
Short => in_range::<i16>(value),
2207+
Int => in_range::<i16>(value),
2208+
Long => in_range::<i32>(value),
2209+
LongLong => in_range::<i64>(value),
2210+
2211+
// `unsigned int` is at least `u16` and `unsigned long` is at least `u32`.
2212+
UChar => in_range::<u8>(value),
2213+
UShort => in_range::<u16>(value),
2214+
UInt => in_range::<u16>(value),
2215+
ULong => in_range::<u32>(value),
2216+
ULongLong => in_range::<u64>(value),
2217+
2218+
Int8 => in_range::<i8>(value),
2219+
Int16 => in_range::<i16>(value),
2220+
Int32 => in_range::<i32>(value),
2221+
Int64 => in_range::<i64>(value),
2222+
Int128 => in_range::<i128>(value),
2223+
2224+
UInt8 => in_range::<u8>(value),
2225+
UInt16 => in_range::<u16>(value),
2226+
UInt32 => in_range::<u32>(value),
2227+
UInt64 => in_range::<u64>(value),
2228+
UInt128 => in_range::<u128>(value),
2229+
2230+
// There's no guarantee on pointer size, but `NULL` should work.
2231+
IntPtr => value == 0,
2232+
UIntPtr => value == 0,
2233+
2234+
IntMax => in_range::<i64>(value),
2235+
UIntMax => in_range::<u64>(value),
2236+
2237+
// `size_t` is at least a `u16`, and similar for `ssize_t` and `ptrdiff_t`.
2238+
Size => in_range::<u16>(value),
2239+
SSize => in_range::<i16>(value),
2240+
PtrDiff => in_range::<i16>(value),
2241+
2242+
// Floats, see `Self::guaranteed_float_in_range`.
2243+
Float => false,
2244+
Double => false,
2245+
LongDouble => false,
2246+
Half => false,
2247+
BFloat16 => false,
2248+
Float128 => false,
2249+
2250+
// Non-scalars.
2251+
Complex(_) => false,
2252+
Pointer(_) => false,
2253+
Reference(_) => false,
2254+
ConstantArray(_, _) => false,
2255+
IncompleteArray(_) => false,
2256+
VariableArray(_, _) => false,
2257+
TypeOf(_) => false,
2258+
TypeOfExpr(_) => false,
2259+
Function(_, _, _, _, _) => false,
2260+
Typedef(_) => false,
2261+
Decayed(_) => false,
2262+
Elaborated(_) => false,
2263+
Paren(_) => false,
2264+
Struct(_) => false,
2265+
Union(_) => false,
2266+
Enum(_) => false,
2267+
BuiltinFn => false,
2268+
Attributed(_, _) => false,
2269+
BlockPointer(_) => false,
2270+
Vector(_, _) => false,
2271+
UnhandledSveType => false,
2272+
Atomic(_) => false,
2273+
}
2274+
}
2275+
2276+
pub fn guaranteed_float_in_range(&self, value: f64) -> bool {
2277+
fn in_range<T: TryFrom<f64>>(value: f64) -> bool {
2278+
T::try_from(value).is_ok()
2279+
}
2280+
2281+
use CTypeKind::*;
2282+
match *self {
2283+
// `f32: TryFrom<f64>` is not implemented.
2284+
// C `float`s are not guaranteed to be `f32`,
2285+
// but Rust (namely `libc`) doesn't support any platform where this isn't the case.
2286+
Float => value >= f32::MIN as f64 && value <= f32::MAX as f64,
2287+
2288+
// Similarly to `float`, `double` is not guaranteed to be `f64`,
2289+
// but `libc` doesn't support any platform where this isn't the case.
2290+
Double => in_range::<f64>(value),
2291+
2292+
// `long double` (not `f128`) is only guaranteed to be at least as precise as a `double`.
2293+
LongDouble => in_range::<f64>(value),
2294+
2295+
// All `f64`s are valid `f128`s.
2296+
Float128 => in_range::<f64>(value),
2297+
2298+
// TODO Would like to depend on `half`.
2299+
Half => todo!("f16 range"),
2300+
BFloat16 => todo!("bf16 range"),
2301+
2302+
Void => false,
2303+
Bool => false,
2304+
Char => false,
2305+
SChar => false,
2306+
Short => false,
2307+
Int => false,
2308+
Long => false,
2309+
LongLong => false,
2310+
UChar => false,
2311+
UShort => false,
2312+
UInt => false,
2313+
ULong => false,
2314+
ULongLong => false,
2315+
Int128 => false,
2316+
UInt128 => false,
2317+
Complex(_) => false,
2318+
Pointer(_) => false,
2319+
Reference(_) => false,
2320+
ConstantArray(_, _) => false,
2321+
IncompleteArray(_) => false,
2322+
VariableArray(_, _) => false,
2323+
TypeOf(_) => false,
2324+
TypeOfExpr(_) => false,
2325+
Function(_, _, _, _, _) => false,
2326+
Typedef(_) => false,
2327+
Decayed(_) => false,
2328+
Elaborated(_) => false,
2329+
Paren(_) => false,
2330+
Struct(_) => false,
2331+
Union(_) => false,
2332+
Enum(_) => false,
2333+
BuiltinFn => false,
2334+
Attributed(_, _) => false,
2335+
BlockPointer(_) => false,
2336+
Vector(_, _) => false,
2337+
UnhandledSveType => false,
2338+
Atomic(_) => false,
2339+
Int8 => false,
2340+
Int16 => false,
2341+
Int32 => false,
2342+
Int64 => false,
2343+
IntPtr => false,
2344+
UInt8 => false,
2345+
UInt16 => false,
2346+
UInt32 => false,
2347+
UInt64 => false,
2348+
UIntPtr => false,
2349+
IntMax => false,
2350+
UIntMax => false,
2351+
Size => false,
2352+
SSize => false,
2353+
PtrDiff => false,
2354+
WChar => false,
2355+
}
2356+
}
21862357
}
21872358

21882359
impl Display for CTypeKind {

c2rust-transpile/src/translator/literals.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,18 @@ impl<'c> Translation<'c> {
7070
mk().cast_expr(value, target_ty)
7171
}
7272

73-
/// Return whether the literal can be directly translated as this type. This does not check
74-
/// that the literal fits into the type's range of values (as doing so is in general dependent
75-
/// on the target platform), just that it is the appropriate kind for the type.
73+
/// Return whether the literal can be directly translated as this type.
7674
pub fn literal_kind_matches_ty(&self, lit: &CLiteral, ty: CQualTypeId) -> bool {
7775
let ty_kind = &self.ast_context.resolve_type(ty.ctype).kind;
7876
match *lit {
79-
CLiteral::Integer(..) if ty_kind.is_integral_type() && !ty_kind.is_bool() => true,
77+
CLiteral::Integer(value, _) if ty_kind.is_integral_type() && !ty_kind.is_bool() => {
78+
ty_kind.guaranteed_integer_in_range(value)
79+
}
8080
// `convert_literal` always casts these to i32.
81-
CLiteral::Character(..) => matches!(ty_kind, CTypeKind::Int32),
82-
CLiteral::Floating(..) if ty_kind.is_floating_type() => true,
81+
CLiteral::Character(_value) => matches!(ty_kind, CTypeKind::Int32),
82+
CLiteral::Floating(value, _) if ty_kind.is_floating_type() => {
83+
ty_kind.guaranteed_float_in_range(value)
84+
}
8385
_ => false,
8486
}
8587
}

c2rust-transpile/src/translator/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3696,7 +3696,6 @@ impl<'c> Translation<'c> {
36963696
// But for literals, if we don't absolutely have to cast, we would rather the
36973697
// literal is translated according to the type we're expecting, and then we can
36983698
// skip the cast entirely.
3699-
#[cfg(any())] // Breaks #1435. Disabled until fixed.
37003699
if let (Some(ty), CExprKind::Literal(_ty, lit)) =
37013700
(override_ty, &self.ast_context[expr].kind)
37023701
{

c2rust-transpile/tests/snapshots/[email protected]

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ pub unsafe extern "C" fn memcpy_str_literal(mut out: *mut ::core::ffi::c_char) {
3939
POS.as_ptr() as *const ::core::ffi::c_void,
4040
(::core::mem::size_of::<[::core::ffi::c_char; 3]>() as size_t)
4141
.wrapping_div(::core::mem::size_of::<::core::ffi::c_char>() as size_t)
42-
.wrapping_sub(1 as ::core::ffi::c_int as size_t)
43-
.wrapping_add(1 as ::core::ffi::c_int as size_t)
42+
.wrapping_sub(1 as size_t)
43+
.wrapping_add(1 as size_t)
4444
.wrapping_mul(::core::mem::size_of::<::core::ffi::c_char>() as size_t),
4545
);
4646
}

c2rust-transpile/tests/snapshots/[email protected]

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ pub type __uint32_t = u32;
1919
pub type int32_t = __int32_t;
2020
pub type uint32_t = __uint32_t;
2121
#[no_mangle]
22-
pub static mut cur_rand_seed: uint32_t = 0 as ::core::ffi::c_int as uint32_t;
22+
pub static mut cur_rand_seed: uint32_t = 0 as uint32_t;
2323
#[no_mangle]
2424
pub unsafe extern "C" fn set_rand_seed(mut s: uint32_t) {
2525
cur_rand_seed = s;
2626
}
2727
#[no_mangle]
2828
pub unsafe extern "C" fn get_rand_seed() -> uint32_t {
29-
let INCREMENT: uint32_t = 1 as ::core::ffi::c_int as uint32_t;
30-
let MULTIPLIER: uint32_t = 0x15a4e35 as ::core::ffi::c_int as uint32_t;
29+
let INCREMENT: uint32_t = 1 as uint32_t;
30+
let MULTIPLIER: uint32_t = 0x15a4e35 as uint32_t;
3131
cur_rand_seed = MULTIPLIER
3232
.wrapping_mul(cur_rand_seed)
3333
.wrapping_add(INCREMENT);

c2rust-transpile/tests/snapshots/[email protected]

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,32 +37,33 @@ pub type ptrdiff_t = isize;
3737
#[no_mangle]
3838
pub static mut intvar: ::core::ffi::c_int = 0 as ::core::ffi::c_int;
3939
#[no_mangle]
40-
pub static mut sizevar: size_t = 0 as ::core::ffi::c_int as size_t;
40+
pub static mut sizevar: size_t = 0 as size_t;
4141
#[no_mangle]
42-
pub static mut ssizevar: ssize_t = 0 as ::core::ffi::c_int as ssize_t;
42+
pub static mut ssizevar: ssize_t = 0 as ssize_t;
4343
#[no_mangle]
44-
pub static mut intptrvar: intptr_t = 0 as ::core::ffi::c_int as intptr_t;
44+
pub static mut intptrvar: intptr_t = 0 as intptr_t;
4545
#[no_mangle]
46-
pub static mut uintptrvar: uintptr_t = 0 as ::core::ffi::c_int as uintptr_t;
46+
pub static mut uintptrvar: uintptr_t = 0 as uintptr_t;
4747
#[no_mangle]
48-
pub static mut ptrdiffvar: ptrdiff_t = 0 as ::core::ffi::c_int as ptrdiff_t;
48+
pub static mut ptrdiffvar: ptrdiff_t = 0 as ptrdiff_t;
4949
#[no_mangle]
50-
pub static mut uint8var: uint8_t = 0 as ::core::ffi::c_int as uint8_t;
50+
pub static mut uint8var: uint8_t = 0 as uint8_t;
5151
#[no_mangle]
52-
pub static mut uint16var: uint16_t = 0 as ::core::ffi::c_int as uint16_t;
52+
pub static mut uint16var: uint16_t = 0 as uint16_t;
5353
#[no_mangle]
54-
pub static mut uint32var: uint32_t = 0 as ::core::ffi::c_int as uint32_t;
54+
pub static mut uint32var: uint32_t = 0 as uint32_t;
5555
#[no_mangle]
56-
pub static mut uint64var: uint64_t = 0 as ::core::ffi::c_int as uint64_t;
56+
pub static mut uint64var: uint64_t = 0 as uint64_t;
5757
#[no_mangle]
58-
pub static mut int8var: int8_t = 0 as ::core::ffi::c_int as int8_t;
58+
pub static mut int8var: int8_t = 0 as int8_t;
5959
#[no_mangle]
60-
pub static mut int16var: int16_t = 0 as ::core::ffi::c_int as int16_t;
60+
pub static mut int16var: int16_t = 0 as int16_t;
6161
#[no_mangle]
6262
pub static mut int32var: int32_t = 0 as int32_t;
6363
#[no_mangle]
64-
pub static mut int64var: int64_t = 0 as ::core::ffi::c_int as int64_t;
64+
pub static mut int64var: int64_t = 0 as int64_t;
6565
#[no_mangle]
66-
pub static mut maxvar: intmax_t = 0 as ::core::ffi::c_int as intmax_t;
66+
pub static mut maxvar: intmax_t = 0 as intmax_t;
6767
#[no_mangle]
68-
pub static mut umaxvar: uintmax_t = 0 as ::core::ffi::c_int as uintmax_t;
68+
pub static mut umaxvar: uintmax_t = 0 as uintmax_t;
69+

c2rust-transpile/tests/snapshots/[email protected]

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ pub unsafe extern "C" fn memcpy_str_literal(mut out: *mut ::core::ffi::c_char) {
4040
POS.as_ptr() as *const ::core::ffi::c_void,
4141
(::core::mem::size_of::<[::core::ffi::c_char; 3]>() as size_t)
4242
.wrapping_div(::core::mem::size_of::<::core::ffi::c_char>() as size_t)
43-
.wrapping_sub(1 as ::core::ffi::c_int as size_t)
44-
.wrapping_add(1 as ::core::ffi::c_int as size_t)
43+
.wrapping_sub(1 as size_t)
44+
.wrapping_add(1 as size_t)
4545
.wrapping_mul(::core::mem::size_of::<::core::ffi::c_char>() as size_t),
4646
);
4747
}

c2rust-transpile/tests/snapshots/[email protected]

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ extern "C" {
1717
pub type int32_t = i32;
1818
pub type uint32_t = u32;
1919
#[no_mangle]
20-
pub static mut cur_rand_seed: uint32_t = 0 as ::core::ffi::c_int as uint32_t;
20+
pub static mut cur_rand_seed: uint32_t = 0 as uint32_t;
2121
#[no_mangle]
2222
pub unsafe extern "C" fn set_rand_seed(mut s: uint32_t) {
2323
cur_rand_seed = s;
2424
}
2525
#[no_mangle]
2626
pub unsafe extern "C" fn get_rand_seed() -> uint32_t {
27-
let INCREMENT: uint32_t = 1 as ::core::ffi::c_int as uint32_t;
28-
let MULTIPLIER: uint32_t = 0x15a4e35 as ::core::ffi::c_int as uint32_t;
27+
let INCREMENT: uint32_t = 1 as uint32_t;
28+
let MULTIPLIER: uint32_t = 0x15a4e35 as uint32_t;
2929
cur_rand_seed = MULTIPLIER
3030
.wrapping_mul(cur_rand_seed)
3131
.wrapping_add(INCREMENT);

c2rust-transpile/tests/snapshots/[email protected]

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,33 @@ pub type ssize_t = __darwin_ssize_t;
3232
#[no_mangle]
3333
pub static mut intvar: ::core::ffi::c_int = 0 as ::core::ffi::c_int;
3434
#[no_mangle]
35-
pub static mut sizevar: size_t = 0 as ::core::ffi::c_int as size_t;
35+
pub static mut sizevar: size_t = 0 as size_t;
3636
#[no_mangle]
37-
pub static mut ssizevar: ssize_t = 0 as ::core::ffi::c_int as ssize_t;
37+
pub static mut ssizevar: ssize_t = 0 as ssize_t;
3838
#[no_mangle]
39-
pub static mut intptrvar: intptr_t = 0 as ::core::ffi::c_int as intptr_t;
39+
pub static mut intptrvar: intptr_t = 0 as intptr_t;
4040
#[no_mangle]
41-
pub static mut uintptrvar: uintptr_t = 0 as ::core::ffi::c_int as uintptr_t;
41+
pub static mut uintptrvar: uintptr_t = 0 as uintptr_t;
4242
#[no_mangle]
43-
pub static mut ptrdiffvar: ptrdiff_t = 0 as ::core::ffi::c_int as ptrdiff_t;
43+
pub static mut ptrdiffvar: ptrdiff_t = 0 as ptrdiff_t;
4444
#[no_mangle]
45-
pub static mut uint8var: uint8_t = 0 as ::core::ffi::c_int as uint8_t;
45+
pub static mut uint8var: uint8_t = 0 as uint8_t;
4646
#[no_mangle]
47-
pub static mut uint16var: uint16_t = 0 as ::core::ffi::c_int as uint16_t;
47+
pub static mut uint16var: uint16_t = 0 as uint16_t;
4848
#[no_mangle]
49-
pub static mut uint32var: uint32_t = 0 as ::core::ffi::c_int as uint32_t;
49+
pub static mut uint32var: uint32_t = 0 as uint32_t;
5050
#[no_mangle]
51-
pub static mut uint64var: uint64_t = 0 as ::core::ffi::c_int as uint64_t;
51+
pub static mut uint64var: uint64_t = 0 as uint64_t;
5252
#[no_mangle]
53-
pub static mut int8var: int8_t = 0 as ::core::ffi::c_int as int8_t;
53+
pub static mut int8var: int8_t = 0 as int8_t;
5454
#[no_mangle]
55-
pub static mut int16var: int16_t = 0 as ::core::ffi::c_int as int16_t;
55+
pub static mut int16var: int16_t = 0 as int16_t;
5656
#[no_mangle]
5757
pub static mut int32var: int32_t = 0 as int32_t;
5858
#[no_mangle]
59-
pub static mut int64var: int64_t = 0 as ::core::ffi::c_int as int64_t;
59+
pub static mut int64var: int64_t = 0 as int64_t;
6060
#[no_mangle]
61-
pub static mut maxvar: intmax_t = 0 as ::core::ffi::c_int as intmax_t;
61+
pub static mut maxvar: intmax_t = 0 as intmax_t;
6262
#[no_mangle]
63-
pub static mut umaxvar: uintmax_t = 0 as ::core::ffi::c_int as uintmax_t;
63+
pub static mut umaxvar: uintmax_t = 0 as uintmax_t;
64+

c2rust-transpile/tests/snapshots/[email protected]

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,15 @@ pub unsafe extern "C" fn short_initializer() {
108108
let mut excess_elements_2: [::core::ffi::c_int; 0] = [0; 0];
109109
let mut single_struct: [C2RustUnnamed_2; 1] = [{
110110
let mut init = C2RustUnnamed_2 {
111-
x: 1 as ::core::ffi::c_int as ::core::ffi::c_short,
111+
x: 1 as ::core::ffi::c_short,
112112
y: 2 as ::core::ffi::c_int,
113113
};
114114
init
115115
}];
116116
let mut many_struct: [C2RustUnnamed_1; 3] = [
117117
{
118118
let mut init = C2RustUnnamed_1 {
119-
x: 1 as ::core::ffi::c_int as ::core::ffi::c_short,
119+
x: 1 as ::core::ffi::c_short,
120120
y: 2 as ::core::ffi::c_int,
121121
};
122122
init

0 commit comments

Comments
 (0)