Skip to content
68 changes: 39 additions & 29 deletions c2rust-transpile/src/c_ast/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2224,35 +2224,39 @@ impl ConversionContext {
let target_dependent_macro: Option<String> = from_value(node.extras[2].clone())
.expect("Expected to find optional target-dependent macro name");

typ = target_dependent_macro
.as_deref()
.and_then(|macro_name| {
let kind = match macro_name {
// Match names in the order Clang defines them.
"__INTMAX_TYPE__" => CTypeKind::IntMax,
"__UINTMAX_TYPE__" => CTypeKind::UIntMax,
"__PTRDIFF_TYPE__" => CTypeKind::PtrDiff,
"__INTPTR_TYPE__" => CTypeKind::IntPtr,
"__SIZE_TYPE__" => CTypeKind::Size,
"__WCHAR_TYPE__" => CTypeKind::WChar,
// __WINT_TYPE__ is defined by Clang but has no obvious translation
// __CHARn_TYPE__ for n ∈ {8, 16, 32} also lack obvious translation
"__UINTPTR_TYPE__" => CTypeKind::UIntPtr,
_ => {
log::debug!("Unknown target-dependent macro {macro_name}!");
return None;
}
};
log::trace!("Selected kind {kind} for typedef {name}");
Some(CQualTypeId::new(self.typed_context.type_for_kind(&kind)))
let mut new_type_kind = None;

fn type_kind_for_macro_name(macro_name: &str) -> Option<CTypeKind> {
Some(match macro_name {
// Match names in the order Clang defines them.
"__INTMAX_TYPE__" => CTypeKind::IntMax,
"__UINTMAX_TYPE__" => CTypeKind::UIntMax,
"__PTRDIFF_TYPE__" => CTypeKind::PtrDiff,
"__INTPTR_TYPE__" => CTypeKind::IntPtr,
"__SIZE_TYPE__" => CTypeKind::Size,
"__WCHAR_TYPE__" => CTypeKind::WChar,
// __WINT_TYPE__ is defined by Clang but has no obvious translation
// __CHARn_TYPE__ for n ∈ {8, 16, 32} also lack obvious translation
"__UINTPTR_TYPE__" => CTypeKind::UIntPtr,
_ => {
log::debug!("Unknown target-dependent macro {macro_name}!");
return None;
}
})
.unwrap_or(typ);
}

if let Some(kind) = target_dependent_macro
.as_deref()
.and_then(type_kind_for_macro_name)
{
new_type_kind = Some(kind);
}

// Other fixed-size types are defined without special compiler involvement, by
// standard headers and their transitive includes. In these contexts, we
// recognize these typedefs by the name of the typedef type.
let id_for_name = |name| -> Option<_> {
let kind = match name {
fn type_kind_for_typedef_name(name: &str) -> Option<CTypeKind> {
Some(match name {
"intmax_t" => CTypeKind::IntMax,
"uintmax_t" => CTypeKind::UIntMax,
"intptr_t" => CTypeKind::IntPtr,
Expand Down Expand Up @@ -2285,10 +2289,9 @@ impl ConversionContext {
log::debug!("Unknown fixed-size type typedef {name}!");
return None;
}
};
log::trace!("Selected kind {kind} for typedef {name}");
Some(CQualTypeId::new(self.typed_context.type_for_kind(&kind)))
};
})
}

let file = self
.typed_context
.files
Expand All @@ -2313,11 +2316,18 @@ impl ConversionContext {
.map(|s| s.starts_with("_int") || s.starts_with("_uint"))
.unwrap_or(false)
{
typ = id_for_name(&*name).unwrap_or(typ);
if let Some(kind) = type_kind_for_typedef_name(&name) {
new_type_kind = Some(kind);
}
}
}
}

if let Some(kind) = new_type_kind {
log::trace!("Selected kind {kind} for typedef {name}");
typ = CQualTypeId::new(self.typed_context.type_for_kind(&kind));
}

let typdef_decl = CDeclKind::Typedef {
name,
typ,
Expand Down
97 changes: 64 additions & 33 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
4 changes: 3 additions & 1 deletion 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
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