Skip to content
105 changes: 67 additions & 38 deletions c2rust-transpile/src/translator/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use super::*;
use failure::format_err;
use std::iter;
use std::{borrow::Cow, iter};
use syn::{Path, TypePath};

impl<'c> Translation<'c> {
Expand Down Expand Up @@ -51,9 +51,10 @@ impl<'c> Translation<'c> {
CLiteral::Integer(value, _) | CLiteral::Character(value) => ty_kind
.integer_kind()
.is_some_and(|integer_kind| integer_kind.is_guaranteed_in_range(value, is_negated)),
CLiteral::Floating(value, _) => ty_kind
.floating_kind()
.is_some_and(|floating_kind| floating_kind.is_guaranteed_in_range(value)),

// `convert_floating_literal` will handle incompatible types itself.
CLiteral::Floating(_, _) => true,

_ => false,
}
}
Expand All @@ -62,9 +63,12 @@ impl<'c> Translation<'c> {
pub fn convert_literal(
&self,
ctx: ExprContext,
ty: CQualTypeId,
expected_type_id: Option<CQualTypeId>,
literal_type_id: CQualTypeId,
lit: &CLiteral,
) -> TranslationResult<WithStmts<Box<Expr>>> {
let ty = expected_type_id.unwrap_or(literal_type_id);

match *lit {
CLiteral::Integer(val, base) => Ok(WithStmts::new_val(
self.mk_int_lit(ctx, ty, val, base, false)?,
Expand Down Expand Up @@ -99,34 +103,8 @@ impl<'c> Translation<'c> {
Ok(WithStmts::new_val(expr))
}

CLiteral::Floating(val, ref c_str) => {
let str = if c_str.is_empty() {
let mut buffer = dtoa::Buffer::new();
buffer.format(val).to_string()
} else {
c_str.to_owned()
};
let val = match self.ast_context.resolve_type(ty.ctype).kind {
CTypeKind::LongDouble | CTypeKind::Float128 => {
if ctx.is_const {
return Err(format_translation_err!(
None,
"f128 cannot be used in constants because `f128::f128::new` is not `const`",
));
}

self.use_crate(ExternCrate::F128);

let fn_path = mk().abs_path_expr(vec!["f128", "f128", "new"]);
let args = vec![mk().lit_expr(mk().float_unsuffixed_lit(&str))];

mk().call_expr(fn_path, args)
}
CTypeKind::Double => mk().lit_expr(mk().float_lit(&str, "f64")),
CTypeKind::Float => mk().lit_expr(mk().float_lit(&str, "f32")),
ref k => panic!("Unsupported floating point literal type {:?}", k),
};
Ok(WithStmts::new_val(val))
CLiteral::Floating(value, ref string) => {
self.convert_floating_literal(ctx, expected_type_id, literal_type_id, value, string)
}

CLiteral::String(ref bytes, element_size) => {
Expand Down Expand Up @@ -176,6 +154,59 @@ impl<'c> Translation<'c> {
}
}

pub(crate) fn convert_floating_literal(
&self,
ctx: ExprContext,
expected_type_id: Option<CQualTypeId>,
mut literal_type_id: CQualTypeId,
value: f64,
string: &str,
) -> TranslationResult<WithStmts<Box<Expr>>> {
let string: Cow<_> = if string.is_empty() {
let mut buffer = dtoa::Buffer::new();
Cow::Owned(buffer.format(value).to_string())
} else {
Cow::Borrowed(string)
};

let target_type_id = expected_type_id.unwrap_or(literal_type_id);

let literal_type_kind = &self.ast_context.resolve_type(literal_type_id.ctype).kind;
let Some(mut literal_floating_kind) = literal_type_kind.floating_kind() else {
panic!("type of floating literal is not a floating type: {literal_type_kind:?}");
};

if let Some(expected_type_id) = expected_type_id {
let expected_type_kind = &self.ast_context.resolve_type(expected_type_id.ctype).kind;

if let Some(expected_floating_kind) = expected_type_kind.floating_kind() {
if literal_floating_kind == expected_floating_kind
|| expected_floating_kind.is_guaranteed_in_range(value)
{
literal_type_id = expected_type_id;
literal_floating_kind = expected_floating_kind;
}
}
}

// Rust only has `f32` and `f64` literals, so any other kinds must be translated as one of
// those two, and then cast.
let val = match literal_floating_kind {
CFloatingKind::Float => mk().lit_expr(mk().float_lit(&string, "f32")),

CFloatingKind::Double => mk().lit_expr(mk().float_lit(&string, "f64")),

CFloatingKind::LongDouble | CFloatingKind::Float128 => {
literal_type_id.ctype = self.ast_context.type_for_kind(&CTypeKind::Double);
mk().lit_expr(mk().float_unsuffixed_lit(&string))
}

ref k => panic!("Unsupported floating point literal type {:?}", k),
};

self.make_cast(ctx, literal_type_id, target_type_id, val.into())
}

/// Returns the bytes of a string literal, including any additional zero bytes to pad the
/// literal to the expected size.
pub fn string_literal_bytes(&self, ctype: CTypeId, bytes: &[u8], element_size: u8) -> Vec<u8> {
Expand Down Expand Up @@ -359,12 +390,10 @@ impl<'c> Translation<'c> {
CTypeKind::Struct(struct_id) => {
self.convert_struct_literal(ctx, struct_id, ids.as_ref())
}
CTypeKind::Union(union_id) => self.convert_union_literal(
CTypeKind::Union(..) => self.convert_union_literal(
ctx,
union_id,
ids.as_ref(),
result_type_id,
opt_union_field_id,
opt_union_field_id.expect("union field ID"),
ids.first().copied(),
),
CTypeKind::Vector(CQualTypeId { ctype, .. }, len) => {
self.vector_list_initializer(ctx, ids, ctype, len)
Expand Down
38 changes: 19 additions & 19 deletions c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3633,7 +3633,9 @@ impl<'c> Translation<'c> {
}
},

Literal(ty, ref kind) => self.convert_literal(ctx, override_ty.unwrap_or(ty), kind),
Literal(result_type_id, ref kind) => {
self.convert_literal(ctx, override_ty, result_type_id, kind)
}

ImplicitCast(ty, expr, kind, opt_field_id, _)
| ExplicitCast(ty, expr, kind, opt_field_id, _) => self.convert_cast(
Expand Down Expand Up @@ -4187,6 +4189,18 @@ impl<'c> Translation<'c> {
return self.make_cast(ctx, source_ty.not_volatile(), target_ty, val);
}

CastKind::NullToPointer => {
return Ok(WithStmts::new_val(self.null_ptr(target_ty.ctype)?));
}

CastKind::ToUnion => {
return self.convert_union_literal(
ctx,
opt_field_id.expect("Missing field ID in union cast"),
Some(expr),
);
}

CastKind::IntegralToBoolean
| CastKind::FloatingToBoolean
| CastKind::PointerToBoolean => {
Expand Down Expand Up @@ -4239,7 +4253,6 @@ impl<'c> Translation<'c> {
val,
Some(expr),
Some(kind),
opt_field_id,
is_explicit,
)
}
Expand Down Expand Up @@ -4341,16 +4354,7 @@ impl<'c> Translation<'c> {
target_type_id: CQualTypeId,
val: WithStmts<Box<Expr>>,
) -> TranslationResult<WithStmts<Box<Expr>>> {
self.make_cast_full(
ctx,
source_type_id,
target_type_id,
val,
None,
None,
None,
false,
)
self.make_cast_full(ctx, source_type_id, target_type_id, val, None, None, false)
}

pub fn make_cast_full(
Expand All @@ -4361,7 +4365,6 @@ impl<'c> Translation<'c> {
val: WithStmts<Box<Expr>>,
expr: Option<CExprId>,
kind: Option<CastKind>,
opt_field_id: Option<CFieldId>,
is_explicit: bool,
) -> TranslationResult<WithStmts<Box<Expr>>> {
let source_ty_kind = &self.ast_context.resolve_type(source_cty.ctype).kind;
Expand Down Expand Up @@ -4406,7 +4409,7 @@ impl<'c> Translation<'c> {
}
}

CastKind::IntegralToPointer => {
CastKind::IntegralToPointer | CastKind::NullToPointer => {
self.convert_integral_to_pointer_cast(ctx, source_cty, target_cty, val)
}

Expand Down Expand Up @@ -4490,13 +4493,10 @@ impl<'c> Translation<'c> {
self.convert_array_to_pointer_decay(ctx, source_cty, target_cty, val, expr)
}

CastKind::NullToPointer => {
assert!(val.stmts().is_empty());
Ok(WithStmts::new_val(self.null_ptr(target_cty.ctype)?))
CastKind::ToUnion => {
panic!("ToUnion casts must be handled in convert_cast")
}

CastKind::ToUnion => self.convert_cast_to_union(val, opt_field_id),

CastKind::IntegralToBoolean
| CastKind::FloatingToBoolean
| CastKind::PointerToBoolean => val
Expand Down
52 changes: 22 additions & 30 deletions c2rust-transpile/src/translator/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,35 +603,25 @@ impl<'c> Translation<'c> {
.get_qual_type()
.ok_or_else(|| format_err!("bad arg type"))?;

let one = match self.ast_context.resolve_type(arg_type.ctype).kind {
// TODO: If rust gets f16 support:
// CTypeKind::Half |
CTypeKind::Float | CTypeKind::Double => mk().lit_expr(mk().float_unsuffixed_lit("1.")),
CTypeKind::LongDouble | CTypeKind::Float128 => {
self.use_crate(ExternCrate::F128);

let fn_path = mk().abs_path_expr(vec!["f128", "f128", "new"]);
let args = vec![mk().lit_expr(mk().float_unsuffixed_lit("1."))];

mk().call_expr(fn_path, args)
}
_ => mk().lit_expr(mk().int_unsuffixed_lit(1)),
};

let mut one_type_id = arg_type;
let mut compute_lhs_type_id = arg_type;
let mut compute_res_type_id = result_type_id;

match self.ast_context.resolve_type(arg_type.ctype).kind {
let one = match self.ast_context.resolve_type(arg_type.ctype).kind {
CTypeKind::Pointer(..) => {
one_type_id = CQualTypeId::new(self.ast_context.type_for_kind(&CTypeKind::Int));
mk().lit_expr(mk().int_unsuffixed_lit(1)).into()
}
CTypeKind::Enum(enum_id) => {
one_type_id = self.enum_underlying_type(enum_id);
compute_lhs_type_id = one_type_id;
compute_res_type_id = one_type_id;
mk().lit_expr(mk().int_unsuffixed_lit(1)).into()
}
_ => {}
ref type_kind if type_kind.is_floating_type() => {
self.convert_floating_literal(ctx, None, one_type_id, 1.0, "1.0")?
}
_ => mk().lit_expr(mk().int_unsuffixed_lit(1)).into(),
};

// If we aren't going to be using the result, may as well do a simple pre-increment
Expand All @@ -646,7 +636,7 @@ impl<'c> Translation<'c> {
op,
arg,
one_type_id,
WithStmts::new_val(one),
one,
Some(compute_lhs_type_id),
Some(compute_res_type_id),
)
Expand All @@ -663,18 +653,20 @@ impl<'c> Translation<'c> {
WithStmts::new(vec![save_old_val], (lhs, old_val_expr))
})
.and_then_try(|(lhs, old_val_expr)| {
let val = self.make_assignment_operator(
ctx.unused(),
expected_type_id,
result_type_id,
op,
lhs,
arg_type,
one,
one_type_id,
compute_lhs_type_id,
compute_res_type_id,
)?;
let val = one.and_then_try(|one| {
self.make_assignment_operator(
ctx.unused(),
expected_type_id,
result_type_id,
op,
lhs,
arg_type,
one,
one_type_id,
compute_lhs_type_id,
compute_res_type_id,
)
})?;

// Replace the assignment result with the old value
Ok(val.map(|_| old_val_expr))
Expand Down
Loading
Loading