diff --git a/c2rust-transpile/src/translator/literals.rs b/c2rust-transpile/src/translator/literals.rs index 7483a053dd..55348cd729 100644 --- a/c2rust-transpile/src/translator/literals.rs +++ b/c2rust-transpile/src/translator/literals.rs @@ -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> { @@ -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, } } @@ -62,9 +63,12 @@ impl<'c> Translation<'c> { pub fn convert_literal( &self, ctx: ExprContext, - ty: CQualTypeId, + expected_type_id: Option, + literal_type_id: CQualTypeId, lit: &CLiteral, ) -> TranslationResult>> { + 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)?, @@ -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) => { @@ -176,6 +154,59 @@ impl<'c> Translation<'c> { } } + pub(crate) fn convert_floating_literal( + &self, + ctx: ExprContext, + expected_type_id: Option, + mut literal_type_id: CQualTypeId, + value: f64, + string: &str, + ) -> TranslationResult>> { + 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 { @@ -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) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 8fb6f5b95c..78634b688d 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -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( @@ -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 => { @@ -4239,7 +4253,6 @@ impl<'c> Translation<'c> { val, Some(expr), Some(kind), - opt_field_id, is_explicit, ) } @@ -4341,16 +4354,7 @@ impl<'c> Translation<'c> { target_type_id: CQualTypeId, val: WithStmts>, ) -> TranslationResult>> { - 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( @@ -4361,7 +4365,6 @@ impl<'c> Translation<'c> { val: WithStmts>, expr: Option, kind: Option, - opt_field_id: Option, is_explicit: bool, ) -> TranslationResult>> { let source_ty_kind = &self.ast_context.resolve_type(source_cty.ctype).kind; @@ -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) } @@ -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 diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 3c9dfccbf7..b0777ad702 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -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 @@ -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), ) @@ -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)) diff --git a/c2rust-transpile/src/translator/structs_unions.rs b/c2rust-transpile/src/translator/structs_unions.rs index a92444787a..0c0f8634e5 100644 --- a/c2rust-transpile/src/translator/structs_unions.rs +++ b/c2rust-transpile/src/translator/structs_unions.rs @@ -596,49 +596,46 @@ impl<'a> Translation<'a> { Ok(val) } - pub fn convert_union_literal( + pub(crate) fn convert_union_literal( &self, ctx: ExprContext, - union_id: CRecordId, - ids: &[CExprId], - _ty: CQualTypeId, - opt_union_field_id: Option, + field_id: CFieldId, + expr: Option, ) -> TranslationResult>> { - let union_field_id = opt_union_field_id.expect("union field ID"); + let CDeclKind::Field { typ: field_type_id, .. } = self.ast_context[field_id].kind else { + panic!("Expected field decl"); + }; + let union_id = self.ast_context.parents[&field_id]; - match self.ast_context.index(union_id).kind { - CDeclKind::Union { .. } => { - let union_name = self - .type_converter - .borrow() - .resolve_decl_name(union_id) - .unwrap(); - log::debug!("importing union {union_name}, id {union_id:?}"); - self.add_import(union_id, &union_name); - match self.ast_context.index(union_field_id).kind { - CDeclKind::Field { typ: field_ty, .. } => { - let val = if ids.is_empty() { - self.implicit_default_expr(ctx, field_ty.ctype)? - } else { - self.convert_expr(ctx, ids[0], None)? - }; + if !matches!(self.ast_context[union_id].kind, CDeclKind::Union { .. }) { + panic!("Expected union decl"); + }; - Ok(val.map(|v| { - let name = vec![mk().path_segment(union_name)]; - let field_name = self - .type_converter - .borrow() - .resolve_field_name(Some(union_id), union_field_id) - .unwrap(); - let fields = vec![mk().field(field_name, v)]; - mk().struct_expr(name, fields) - })) - } - _ => panic!("Union field decl mismatch"), - } - } - _ => panic!("Expected union decl"), - } + let union_name = self + .type_converter + .borrow() + .resolve_decl_name(union_id) + .expect("required union name"); + let field_name = self + .type_converter + .borrow() + .resolve_field_name(Some(union_id), field_id) + .expect("field name required"); + + log::debug!("importing union {union_name}, id {union_id:?}"); + self.add_import(union_id, &union_name); + + let val = expr.map_or_else( + || self.implicit_default_expr(ctx, field_type_id.ctype), + |expr| self.convert_expr(ctx, expr, Some(field_type_id)), + )?; + + Ok(val.map(|val| { + mk().struct_expr( + vec![mk().path_segment(union_name)], + vec![mk().field(field_name, val)], + ) + })) } /// This method handles zero-initializing bitfield structs including bitfields @@ -1115,30 +1112,6 @@ impl<'a> Translation<'a> { Ok(val) } - - pub fn convert_cast_to_union( - &self, - val: WithStmts>, - opt_field_id: Option, - ) -> TranslationResult>> { - let field_id = opt_field_id.expect("Missing field ID in union cast"); - let union_id = self.ast_context.parents[&field_id]; - - let union_name = self - .type_converter - .borrow() - .resolve_decl_name(union_id) - .expect("required union name"); - let field_name = self - .type_converter - .borrow() - .resolve_field_name(Some(union_id), field_id) - .expect("field name required"); - - Ok(val.map(|x| { - mk().struct_expr(mk().path(vec![union_name]), vec![mk().field(field_name, x)]) - })) - } } #[allow(clippy::large_enum_variant)]