diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 0cbfdca7bd..c8be6c1af0 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -3084,6 +3084,10 @@ impl CTypeKind { matches!(*self, Self::Pointer { .. }) } + pub(crate) fn is_numeric(&self) -> bool { + self.is_integral_type() || self.is_floating_type() + } + pub fn is_scalar(&self) -> bool { self.is_bool() || self.is_integral_type() diff --git a/c2rust-transpile/src/translator/enums.rs b/c2rust-transpile/src/translator/enums.rs index 06529c41d2..19f80b3fef 100644 --- a/c2rust-transpile/src/translator/enums.rs +++ b/c2rust-transpile/src/translator/enums.rs @@ -1,8 +1,10 @@ use c2rust_ast_builder::mk; +use failure::format_err; use proc_macro2::Span; use syn::{Expr, Pat}; use crate::c_ast::iterators::SomeId; +use crate::translator::IdOrExpr; use crate::{ diagnostics::TranslationResult, translator::{signed_int_expr, ConvertedDecl, ExprContext, Translation, TranslationError}, @@ -106,24 +108,26 @@ impl<'c> Translation<'c> { expr_type_id: CQualTypeId, enum_constant_id: CEnumConstantId, ) -> TranslationResult>> { - let val = self.enum_constant_expr(enum_constant_id); + let mut val = self.enum_constant_expr(enum_constant_id); if self.enum_constant_matches_type(expr_type_id.ctype, enum_constant_id) { return Ok(WithStmts::new_val(val)); } + val = self.make_enum_to_underlying_cast(val); + // Add a cast to the expected integral type. let enum_id = self.ast_context.parents[&enum_constant_id]; - self.convert_cast_from_enum(ctx, enum_id, expr_type_id, val) + let underlying_type_id = self.enum_underlying_type(enum_id); + self.make_cast(ctx, underlying_type_id, expr_type_id, val.into()) } - /// Translate a cast where the source type, but not the target type, is an `enum` type. - pub fn convert_cast_from_enum( + /// Translate a cast where the source type is an `enum` type. + pub(crate) fn convert_cast_from_enum( &self, ctx: ExprContext, - enum_id: CEnumId, - target_cty: CQualTypeId, - mut val: Box, + target_type_id: CQualTypeId, + expr: impl Into, ) -> TranslationResult>> { if ctx.is_pattern { return Err(TranslationError::generic( @@ -131,12 +135,19 @@ impl<'c> Translation<'c> { )); } + let expr = expr.into(); + let source_type_id = expr + .type_id(&self.ast_context) + .ok_or_else(|| format_err!("Invalid expression type"))?; + // First extract the enum's underlying type... - val = self.make_enum_to_underlying_cast(val); + let mut expr_rs = self.convert_expr(ctx, expr, None)?; + expr_rs = expr_rs.map(|expr_rs| self.make_enum_to_underlying_cast(expr_rs)); // Cast from the underlying type to the expected type. - let source_cty = self.enum_underlying_type(enum_id); - self.make_cast(ctx, source_cty, target_cty, WithStmts::new_val(val)) + let enum_id = self.enum_id_from_type(source_type_id.ctype); + let underlying_type_id = self.enum_underlying_type(enum_id); + self.make_cast(ctx, underlying_type_id, target_type_id, expr_rs) } /// Gets the underlying value of an enum value. @@ -145,39 +156,24 @@ impl<'c> Translation<'c> { } /// Translates a cast where the target type is an `enum` type. - pub fn convert_cast_to_enum( + pub(crate) fn convert_cast_to_enum( &self, ctx: ExprContext, - mut source_cty: CQualTypeId, enum_id: CEnumId, - mut val: Box, + expr: impl Into, ) -> TranslationResult>> { - // We could be casting from enum to enum... - if let CTypeKind::Enum(source_enum_id) = - self.ast_context.resolve_type(source_cty.ctype).kind - { - // Casting to ourselves, the audacity! - if source_enum_id == enum_id { - return Ok(WithStmts::new_val(val)); - } - - if ctx.is_pattern { - return Err(TranslationError::generic( - "cast from enum is not supported in patterns", - )); - } - - // Enum-to-enum casts need to be translated via the underlying value as an intermediate. - val = self.make_enum_to_underlying_cast(val); - source_cty = self.enum_underlying_type(source_enum_id); - } + let expr = expr.into(); + let source_type_id = expr + .type_id(&self.ast_context) + .ok_or_else(|| format_err!("Invalid expression type"))?; + // First cast the value to the enum's underlying type. + let mut expr_rs = self.convert_expr(ctx, expr, None)?; let underlying_type_id = self.enum_underlying_type(enum_id); - let mut val = WithStmts::new_val(val); - val = self.make_cast(ctx, source_cty, underlying_type_id, val)?; - val = val.map(|val| self.enum_constructor_expr(enum_id, val, false)); + expr_rs = self.make_cast(ctx, source_type_id, underlying_type_id, expr_rs)?; - Ok(val) + // Then construct an enum from the underlying value. + Ok(expr_rs.map(|expr_rs| self.enum_constructor_expr(enum_id, expr_rs, false))) } /// Given an integer value this attempts to either generate the corresponding enum @@ -264,6 +260,13 @@ impl<'c> Translation<'c> { mk().tuple_struct_pat(enum_name.as_str(), None, vec![value]) } + fn enum_id_from_type(&self, type_id: CTypeId) -> CEnumId { + match self.ast_context.resolve_type(type_id).kind { + CTypeKind::Enum(enum_id) => enum_id, + _ => panic!("{type_id:?} is not an enum type"), + } + } + pub(crate) fn enum_constant_matches_type( &self, type_id: CTypeId, diff --git a/c2rust-transpile/src/translator/literals.rs b/c2rust-transpile/src/translator/literals.rs index 7483a053dd..01dbb0059a 100644 --- a/c2rust-transpile/src/translator/literals.rs +++ b/c2rust-transpile/src/translator/literals.rs @@ -359,12 +359,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..e853d50569 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -4187,6 +4187,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 +4251,6 @@ impl<'c> Translation<'c> { val, Some(expr), Some(kind), - opt_field_id, is_explicit, ) } @@ -4341,16 +4352,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 +4363,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; @@ -4394,85 +4395,76 @@ impl<'c> Translation<'c> { } match kind { - CastKind::BitCast | CastKind::NoOp => { - if !is_explicit - && self - .ast_context - .is_mut_to_const_pointer_cast(source_cty.ctype, target_cty.ctype) - { - Ok(val) - } else { - self.convert_pointer_to_pointer_cast(source_cty, target_cty, val) - } - } - - CastKind::IntegralToPointer => { - self.convert_integral_to_pointer_cast(ctx, source_cty, target_cty, val) - } - - CastKind::PointerToIntegral => { - self.convert_pointer_to_integral_cast(ctx, source_cty, target_cty, val) - } - CastKind::IntegralCast | CastKind::FloatingCast | CastKind::FloatingToIntegral | CastKind::IntegralToFloating - | CastKind::BooleanToSignedIntegral => { - let target_ty = self.convert_type(target_cty.ctype)?; - - if ctx.is_pattern && !target_ty_kind.is_enum() { + | CastKind::BooleanToSignedIntegral + | CastKind::BitCast + | CastKind::NoOp + | CastKind::IntegralToPointer + | CastKind::NullToPointer + | CastKind::PointerToIntegral => { + if ctx.is_pattern + && !(source_ty_kind.is_integral_type() && target_ty_kind.is_enum()) + { return Err(TranslationError::generic( - "integral casts to non-enums are not supported in patterns", + "only casts from integers to enums are supported in patterns", )); } - if let CTypeKind::LongDouble | CTypeKind::Float128 = target_ty_kind { - if let CTypeKind::LongDouble | CTypeKind::Float128 = - self.ast_context[source_cty.ctype].kind - { - // These are both converted to `f128`, so a cast between the two should - // just be a no-op. - Ok(val) - } else { - if ctx.is_const { - return Err(format_translation_err!( - None, - "f128 cannot be used in constants because \ - `f128::f128::new` is not `const`", - )); + match (source_ty_kind, target_ty_kind) { + (CTypeKind::Pointer(..), CTypeKind::Pointer(..)) => { + if !is_explicit + && self + .ast_context + .is_mut_to_const_pointer_cast(source_cty.ctype, target_cty.ctype) + { + Ok(val) + } else { + val.and_then_try(|val| { + self.convert_pointer_to_pointer_cast( + ctx, + target_cty, + (val, source_cty), + ) + }) } + } + + (CTypeKind::Enum(..), _) => val.and_then_try(|val| { + self.convert_cast_from_enum(ctx, target_cty, (val, source_cty)) + }), + + (CTypeKind::Pointer(..), _) => val.and_then_try(|val| { + self.convert_pointer_to_integral_cast(ctx, target_cty, (val, source_cty)) + }), + + (_, &CTypeKind::Enum(enum_id)) => val.and_then_try(|val| { + self.convert_cast_to_enum(ctx, enum_id, (val, source_cty)) + }), - self.use_crate(ExternCrate::F128); + (_, CTypeKind::Pointer(..)) => val.and_then_try(|val| { + self.convert_integral_to_pointer_cast(ctx, target_cty, (val, source_cty)) + }), - let fn_path = mk().abs_path_expr(vec!["f128", "f128", "new"]); - Ok(val.map(|val| mk().call_expr(fn_path, vec![val]))) + (CTypeKind::Bool, _) if target_ty_kind.is_numeric() => { + val.and_then_try(|val| { + self.convert_numeric_to_numeric_cast(ctx, target_cty, (val, source_cty)) + }) } - } else if let CTypeKind::LongDouble | CTypeKind::Float128 = - self.ast_context[source_cty.ctype].kind - { - // The `f128` crate only implements `Into for f128` and not - // `From for X` for some reason. So we have to use `Into::::into()` - // here, which is a bit ugly and unidiomatic. - let target_type_rs = self.convert_type(target_cty.ctype)?; - let type_args_rs = mk().angle_bracketed_args(vec![target_type_rs]); - let callee_rs = mk().path_expr(vec![ - mk().path_segment_with_args("Into", type_args_rs), - mk().path_segment("into"), - ]); - Ok(val.map(|val| mk().call_expr(callee_rs, vec![val]))) - } else if let &CTypeKind::Enum(enum_id) = target_ty_kind { - val.and_then_try(|val| self.convert_cast_to_enum(ctx, source_cty, enum_id, val)) - } else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() { - Ok(val.map(|val| { - mk().cast_expr(mk().cast_expr(val, mk().path_ty(vec!["u8"])), target_ty) - })) - } else if let &CTypeKind::Enum(enum_id) = source_ty_kind { - val.and_then_try(|val| { - self.convert_cast_from_enum(ctx, enum_id, target_cty, val) - }) - } else { - Ok(val.map(|val| mk().cast_expr(val, target_ty))) + + _ if source_ty_kind.is_numeric() && target_ty_kind.is_numeric() => val + .and_then_try(|val| { + self.convert_numeric_to_numeric_cast(ctx, target_cty, (val, source_cty)) + }), + + _ => Err(format_err!( + "cast between unsupported type kinds:\n\ + source_type_kind = {source_ty_kind:?}\n\ + target_type_kind = {target_ty_kind:?}" + ) + .into()), } } @@ -4490,13 +4482,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 @@ -4522,6 +4511,81 @@ impl<'c> Translation<'c> { } } + /// Converts a cast between two numeric types, or from `bool` to a numeric type. + fn convert_numeric_to_numeric_cast( + &self, + ctx: ExprContext, + target_type_id: CQualTypeId, + expr: impl Into, + ) -> TranslationResult>> { + let expr = expr.into(); + let source_type_id = expr + .type_id(&self.ast_context) + .ok_or_else(|| format_err!("Invalid expression type"))?; + + let source_type_kind = &self.ast_context.resolve_type(source_type_id.ctype).kind; + let target_type_kind = &self.ast_context.resolve_type(target_type_id.ctype).kind; + + match (source_type_kind, target_type_kind) { + (CTypeKind::Bool, _) if target_type_kind.is_floating_type() => { + // `bool` can't be cast directly to a floating type. Cast it to `u8` first. + let mut expr_rs = self.convert_expr(ctx, expr, None)?; + + let u8_type_id = + CQualTypeId::new(self.ast_context.type_for_kind(&CTypeKind::UInt8)); + let u8_type_rs = self.convert_type(u8_type_id.ctype)?; + expr_rs = expr_rs.map(|expr_rs| mk().cast_expr(expr_rs, u8_type_rs)); + + expr_rs.and_then_try(|expr_rs| { + self.convert_numeric_to_numeric_cast(ctx, target_type_id, (expr_rs, u8_type_id)) + }) + } + + ( + CTypeKind::LongDouble | CTypeKind::Float128, + CTypeKind::LongDouble | CTypeKind::Float128, + ) => { + // These are both converted to `f128`, so a cast between the two should + // just be a no-op. + self.convert_expr(ctx, expr, None) + } + + (CTypeKind::LongDouble | CTypeKind::Float128, _) => { + let expr_rs = self.convert_expr(ctx, expr, None)?; + + let target_type_rs = self.convert_type(target_type_id.ctype)?; + let type_args_rs = mk().angle_bracketed_args(vec![target_type_rs]); + let callee_rs = mk().path_expr(vec![ + mk().path_segment_with_args("Into", type_args_rs), + mk().path_segment("into"), + ]); + Ok(expr_rs.map(|expr_rs| mk().call_expr(callee_rs, vec![expr_rs]))) + } + + (_, 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`", + )); + } + + let expr_rs = self.convert_expr(ctx, expr, None)?; + + self.use_crate(ExternCrate::F128); + let fn_path = mk().abs_path_expr(vec!["f128", "f128", "new"]); + Ok(expr_rs.map(|expr_rs| mk().call_expr(fn_path, vec![expr_rs]))) + } + + _ => { + let expr_rs = self.convert_expr(ctx, expr, None)?; + + let target_type_rs = self.convert_type(target_type_id.ctype)?; + Ok(expr_rs.map(|expr_rs| mk().cast_expr(expr_rs, target_type_rs))) + } + } + } + pub fn implicit_default_expr( &self, ctx: ExprContext, diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 08c5e17ae0..591b07b950 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -13,7 +13,7 @@ use crate::{ cast_int, neg_expr, transmute_expr, unwrap_function_pointer, ExprContext, Translation, }, with_stmts::WithStmts, - CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId, CTypeKind, CastKind, ExternCrate, + CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId, CTypeKind, CastKind, }; impl<'c> Translation<'c> { @@ -479,53 +479,47 @@ impl<'c> Translation<'c> { .convert_pointee(&self.ast_context, type_id) } - pub fn convert_pointer_to_pointer_cast( + pub(crate) fn convert_pointer_to_pointer_cast( &self, - source_cty: CQualTypeId, - target_cty: CQualTypeId, - val: WithStmts>, + ctx: ExprContext, + target_type_id: CQualTypeId, + expr: impl Into, ) -> TranslationResult>> { - if self.ast_context.is_function_pointer(target_cty.ctype) - || self.ast_context.is_function_pointer(source_cty.ctype) - { - let source_ty = self - .type_converter - .borrow_mut() - .convert(&self.ast_context, source_cty.ctype)?; - let target_ty = self - .type_converter - .borrow_mut() - .convert(&self.ast_context, target_cty.ctype)?; - - if source_ty == target_ty { - return Ok(val); - } + let expr = expr.into(); + let source_type_id = expr + .type_id(&self.ast_context) + .ok_or_else(|| format_err!("Invalid expression type"))?; - self.import_type(source_cty.ctype); - self.import_type(target_cty.ctype); + let target_type_rs = self.convert_type(target_type_id.ctype)?; - Ok(val.and_then(|val| { - WithStmts::new_val(transmute_expr(source_ty, target_ty, val)).set_unsafe() - })) + if self.ast_context.is_function_pointer(target_type_id.ctype) + || self.ast_context.is_function_pointer(source_type_id.ctype) + { + let expr_rs = self.convert_expr(ctx, expr, None)?; + + let source_type_rs = self.convert_type(source_type_id.ctype)?; + Ok(expr_rs + .map(|expr_rs| transmute_expr(source_type_rs, target_type_rs, expr_rs)) + .set_unsafe()) } else { - // Normal case - let target_ty = self.convert_type(target_cty.ctype)?; - Ok(val.map(|val| mk().cast_expr(val, target_ty))) + let expr_rs = self.convert_expr(ctx, expr, None)?; + + Ok(expr_rs.map(|expr_rs| mk().cast_expr(expr_rs, target_type_rs))) } } - /// Makes a cast from an integer, enum or boolean type to a pointer. - pub fn convert_integral_to_pointer_cast( + pub(crate) fn convert_integral_to_pointer_cast( &self, ctx: ExprContext, - source_cty: CQualTypeId, - target_cty: CQualTypeId, - val: WithStmts>, + target_type_id: CQualTypeId, + expr: impl Into, ) -> TranslationResult>> { - let source_ty_kind = &self.ast_context.resolve_type(source_cty.ctype).kind; - let target_ty = self.convert_type(target_cty.ctype)?; + // First cast the value to `usize`. + let usize_type_id = CQualTypeId::new(self.ast_context.type_for_kind(&CTypeKind::UIntPtr)); + let mut expr_rs = self.convert_expr(ctx, expr, Some(usize_type_id))?; - if self.ast_context.is_function_pointer(target_cty.ctype) { + // Then convert the `usize` into a pointer. + if self.ast_context.is_function_pointer(target_type_id.ctype) { if ctx.is_const { return Err(format_translation_err!( None, @@ -533,48 +527,21 @@ impl<'c> Translation<'c> { )); } - self.use_crate(ExternCrate::Libc); - Ok(val.and_then(|mut val| { - // First cast the integer to pointer size - let intptr_t = mk().abs_path_ty(vec!["libc", "intptr_t"]); - val = mk().cast_expr(val, intptr_t.clone()); - - WithStmts::new_val(transmute_expr(intptr_t, target_ty, val)).set_unsafe() - })) + let usize_type_rs = self.convert_type(usize_type_id.ctype)?; + let target_type_rs = self.convert_type(target_type_id.ctype)?; + Ok(expr_rs + .map(|expr_rs| transmute_expr(usize_type_rs, target_type_rs, expr_rs)) + .set_unsafe()) } // Rust 1.90: `const_strict_provenance` feature added // Rust 1.91: stabilized else if ctx.is_const && self.tcfg.edition < RustEdition::Edition2024 { - if source_ty_kind.is_bool() { - self.use_crate(ExternCrate::Libc); - Ok(val.map(|mut val| { - // First cast the boolean to pointer size - val = mk().cast_expr(val, mk().abs_path_ty(vec!["libc", "size_t"])); - mk().cast_expr(val, target_ty) - })) - } else if let &CTypeKind::Enum(enum_id) = source_ty_kind { - val.and_then_try(|val| self.convert_cast_from_enum(ctx, enum_id, target_cty, val)) - } else { - Ok(val.map(|val| mk().cast_expr(val, target_ty))) - } + let target_type_rs = self.convert_type(target_type_id.ctype)?; + Ok(expr_rs.map(|expr_rs| mk().cast_expr(expr_rs, target_type_rs))) } else { - // First cast the value to `usize`. - let source_type_kind = &self.ast_context.resolve_type(source_cty.ctype).kind; - let size_type_id = self.ast_context.type_for_kind(&CTypeKind::Size); - - let val = if let &CTypeKind::Enum(enum_id) = source_type_kind { - val.and_then_try(|val| { - self.convert_cast_from_enum(ctx, enum_id, CQualTypeId::new(size_type_id), val) - })? - } else { - let size_type_rs = self.convert_type(size_type_id)?; - val.map(|val| mk().cast_expr(val, size_type_rs)) - }; - - // Then convert the `usize` into a pointer. let pointee_type_id = self .ast_context - .get_pointee_qual_type(target_cty.ctype) + .get_pointee_qual_type(target_type_id.ctype) .expect("target type must be a pointer"); let mutability = pointee_type_id.mutability(); @@ -612,25 +579,22 @@ impl<'c> Translation<'c> { mk().path_segment("ptr"), mk().path_segment_with_args(fn_name, type_args), ]); - let val = val.map(|val| { - let ptr = mk().call_expr(fn_expr, vec![val]); - if is_opaque { - mk().cast_expr(ptr, target_ty) - } else { - ptr - } - }); + expr_rs = expr_rs.map(|expr_rs| mk().call_expr(fn_expr, vec![expr_rs])); - Ok(val) + if is_opaque { + let target_type_rs = self.convert_type(target_type_id.ctype)?; + expr_rs = expr_rs.map(|expr_rs| mk().cast_expr(expr_rs, target_type_rs)); + } + + Ok(expr_rs) } } - pub fn convert_pointer_to_integral_cast( + pub(crate) fn convert_pointer_to_integral_cast( &self, ctx: ExprContext, - source_cty: CQualTypeId, - target_cty: CQualTypeId, - val: WithStmts>, + target_type_id: CQualTypeId, + expr: impl Into, ) -> TranslationResult>> { if ctx.is_const { return Err(format_translation_err!( @@ -639,16 +603,25 @@ impl<'c> Translation<'c> { )); } - let target_type_rs = self.convert_type(target_cty.ctype)?; + let expr = expr.into(); + let source_type_id = expr + .type_id(&self.ast_context) + .ok_or_else(|| format_err!("Invalid expression type"))?; + + // First convert the pointer to `usize`. + let usize_type_id = CQualTypeId::new(self.ast_context.type_for_kind(&CTypeKind::UIntPtr)); - if self.ast_context.is_function_pointer(source_cty.ctype) { - let source_ty = self.convert_type(source_cty.ctype)?; + let expr_rs = if self.ast_context.is_function_pointer(source_type_id.ctype) { + let expr_rs = self.convert_expr(ctx, expr, None)?; - Ok(val.and_then(|val| { - WithStmts::new_val(transmute_expr(source_ty, target_type_rs, val)).set_unsafe() - })) + let source_type_rs = self.convert_type(source_type_id.ctype)?; + let usize_type_rs = self.convert_type(usize_type_id.ctype)?; + expr_rs + .map(|expr_rs| transmute_expr(source_type_rs, usize_type_rs, expr_rs)) + .set_unsafe() } else { - // First convert the pointer to `usize`. + let expr_rs = self.convert_expr(ctx, expr, None)?; + let method_name = match self.tcfg.edition { RustEdition::Edition2021 => { // Rust 1.76: feature name changed to `exposed_provenance` @@ -660,26 +633,11 @@ impl<'c> Translation<'c> { } RustEdition::Edition2024 => "expose_provenance", }; + expr_rs.map(|expr_rs| mk().method_call_expr(expr_rs, method_name, vec![])) + }; - let val = val.map(|val| mk().method_call_expr(val, method_name, vec![])); - - // Then cast the `usize` to the target type. - let size_type_id = self.ast_context.type_for_kind(&CTypeKind::Size); - let target_ty_kind = &self.ast_context.resolve_type(target_cty.ctype).kind; - - if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { - val.and_then_try(|val| { - self.convert_cast_to_enum( - ctx, - CQualTypeId::new(size_type_id), - enum_decl_id, - val, - ) - }) - } else { - Ok(val.map(|val| mk().cast_expr(val, target_type_rs))) - } - } + // Then cast the `usize` to the target type. + self.make_cast(ctx, usize_type_id, target_type_id, expr_rs) } pub(crate) fn convert_pointer_is_null( 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)] diff --git a/c2rust-transpile/tests/snapshots.rs b/c2rust-transpile/tests/snapshots.rs index d9d5736a4d..f8266dbb20 100644 --- a/c2rust-transpile/tests/snapshots.rs +++ b/c2rust-transpile/tests/snapshots.rs @@ -603,10 +603,7 @@ fn test_rotate_os_specific() { #[test] fn test_sigign() { - transpile("sigign.c") - .os_specific(true) - .expect_unresolved_import("libc") - .run(); + transpile("sigign.c").os_specific(true).run(); } #[test] diff --git a/c2rust-transpile/tests/snapshots/os-specific/f128.c b/c2rust-transpile/tests/snapshots/os-specific/f128.c index 5afc2dad3e..477153fbb5 100644 --- a/c2rust-transpile/tests/snapshots/os-specific/f128.c +++ b/c2rust-transpile/tests/snapshots/os-specific/f128.c @@ -1,6 +1,8 @@ #include #include +enum E { A }; + void long_double_test(void) { long double one = 1.0l; long double zero; @@ -9,8 +11,12 @@ void long_double_test(void) { int i = one; float f = one; + _Bool b = one; + enum E e = one; long double cast_from_int = i; long double cast_from_float = f; + long double cast_from_bool = b; + long double cast_from_enum = e; bool is_inf = isinf(huge); if (one) { @@ -28,8 +34,12 @@ void float128_test(void) { int i = one; float f = one; + _Bool b = one; + enum E e = one; __float128 cast_from_int = i; __float128 cast_from_float = f; + __float128 cast_from_bool = b; + __float128 cast_from_enum = e; long double ld_from_float128 = one; bool is_inf = isinf(huge); diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2021.linux.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2021.linux.clang15.snap index 4d97bde7c8..dfaaa88ab1 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2021.linux.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2021.linux.clang15.snap @@ -12,6 +12,12 @@ expression: cat tests/snapshots/os-specific/f128.2021.linux.clang15.rs unused_mut )] use ::num_traits::Float; +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct E(pub ::core::ffi::c_uint); +impl E { + pub const A: Self = Self(0); +} #[no_mangle] pub unsafe extern "C" fn long_double_test() { let mut one: ::f128::f128 = ::f128::f128::new(1.0); @@ -20,8 +26,12 @@ pub unsafe extern "C" fn long_double_test() { let mut huge: ::f128::f128 = ::f128::f128::INFINITY; let mut i: ::core::ffi::c_int = Into::<::core::ffi::c_int>::into(one); let mut f: ::core::ffi::c_float = Into::<::core::ffi::c_float>::into(one); + let mut b: bool = one != ::f128::f128::ZERO; + let mut e: E = E(Into::<::core::ffi::c_uint>::into(one)); let mut cast_from_int: ::f128::f128 = ::f128::f128::new(i); let mut cast_from_float: ::f128::f128 = ::f128::f128::new(f); + let mut cast_from_bool: ::f128::f128 = ::f128::f128::new(b as u8); + let mut cast_from_enum: ::f128::f128 = ::f128::f128::new(e.0); let mut is_inf: bool = if huge.is_infinite() { if huge.is_sign_positive() { 1 @@ -43,8 +53,12 @@ pub unsafe extern "C" fn float128_test() { let mut huge: ::f128::f128 = ::f128::f128::INFINITY; let mut i: ::core::ffi::c_int = Into::<::core::ffi::c_int>::into(one); let mut f: ::core::ffi::c_float = Into::<::core::ffi::c_float>::into(one); + let mut b: bool = one != ::f128::f128::ZERO; + let mut e: E = E(Into::<::core::ffi::c_uint>::into(one)); let mut cast_from_int: ::f128::f128 = ::f128::f128::new(i); let mut cast_from_float: ::f128::f128 = ::f128::f128::new(f); + let mut cast_from_bool: ::f128::f128 = ::f128::f128::new(b as u8); + let mut cast_from_enum: ::f128::f128 = ::f128::f128::new(e.0); let mut ld_from_float128: ::f128::f128 = one; let mut is_inf: bool = if huge.is_infinite() { if huge.is_sign_positive() { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2021.macos.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2021.macos.clang15.snap index c0af0a4df3..45829672e9 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2021.macos.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2021.macos.clang15.snap @@ -12,6 +12,12 @@ expression: cat tests/snapshots/os-specific/f128.c.2021.macos.rs unused_mut )] use ::num_traits::Float; +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct E(pub ::core::ffi::c_uint); +impl E { + pub const A: Self = Self(0); +} #[inline(always)] unsafe extern "C" fn __inline_isinff(mut __x: ::core::ffi::c_float) -> ::core::ffi::c_int { return (__x.abs() == ::core::f32::INFINITY) as ::core::ffi::c_int; @@ -32,8 +38,12 @@ pub unsafe extern "C" fn long_double_test() { let mut huge: ::f128::f128 = ::f128::f128::INFINITY; let mut i: ::core::ffi::c_int = Into::<::core::ffi::c_int>::into(one); let mut f: ::core::ffi::c_float = Into::<::core::ffi::c_float>::into(one); + let mut b: bool = one != ::f128::f128::ZERO; + let mut e: E = E(Into::<::core::ffi::c_uint>::into(one)); let mut cast_from_int: ::f128::f128 = ::f128::f128::new(i); let mut cast_from_float: ::f128::f128 = ::f128::f128::new(f); + let mut cast_from_bool: ::f128::f128 = ::f128::f128::new(b as u8); + let mut cast_from_enum: ::f128::f128 = ::f128::f128::new(e.0); let mut is_inf: bool = if ::core::mem::size_of::<::f128::f128>() == ::core::mem::size_of::<::core::ffi::c_float>() { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2024.linux.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2024.linux.clang15.snap index 2c73cee49b..a3de4ea9f7 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2024.linux.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2024.linux.clang15.snap @@ -13,6 +13,12 @@ expression: cat tests/snapshots/os-specific/f128.2024.linux.clang15.rs unused_mut )] use ::num_traits::Float; +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct E(pub ::core::ffi::c_uint); +impl E { + pub const A: Self = Self(0); +} #[unsafe(no_mangle)] pub unsafe extern "C" fn long_double_test() { let mut one: ::f128::f128 = ::f128::f128::new(1.0); @@ -21,8 +27,12 @@ pub unsafe extern "C" fn long_double_test() { let mut huge: ::f128::f128 = ::f128::f128::INFINITY; let mut i: ::core::ffi::c_int = Into::<::core::ffi::c_int>::into(one); let mut f: ::core::ffi::c_float = Into::<::core::ffi::c_float>::into(one); + let mut b: bool = one != ::f128::f128::ZERO; + let mut e: E = E(Into::<::core::ffi::c_uint>::into(one)); let mut cast_from_int: ::f128::f128 = ::f128::f128::new(i); let mut cast_from_float: ::f128::f128 = ::f128::f128::new(f); + let mut cast_from_bool: ::f128::f128 = ::f128::f128::new(b as u8); + let mut cast_from_enum: ::f128::f128 = ::f128::f128::new(e.0); let mut is_inf: bool = if huge.is_infinite() { if huge.is_sign_positive() { 1 } else { -1 } } else { @@ -40,8 +50,12 @@ pub unsafe extern "C" fn float128_test() { let mut huge: ::f128::f128 = ::f128::f128::INFINITY; let mut i: ::core::ffi::c_int = Into::<::core::ffi::c_int>::into(one); let mut f: ::core::ffi::c_float = Into::<::core::ffi::c_float>::into(one); + let mut b: bool = one != ::f128::f128::ZERO; + let mut e: E = E(Into::<::core::ffi::c_uint>::into(one)); let mut cast_from_int: ::f128::f128 = ::f128::f128::new(i); let mut cast_from_float: ::f128::f128 = ::f128::f128::new(f); + let mut cast_from_bool: ::f128::f128 = ::f128::f128::new(b as u8); + let mut cast_from_enum: ::f128::f128 = ::f128::f128::new(e.0); let mut ld_from_float128: ::f128::f128 = one; let mut is_inf: bool = if huge.is_infinite() { if huge.is_sign_positive() { 1 } else { -1 } diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2024.macos.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2024.macos.clang15.snap index 3554b7cb91..cce92980a3 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2024.macos.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2024.macos.clang15.snap @@ -13,6 +13,12 @@ expression: cat tests/snapshots/os-specific/f128.c.2024.macos.rs unused_mut )] use ::num_traits::Float; +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct E(pub ::core::ffi::c_uint); +impl E { + pub const A: Self = Self(0); +} #[inline(always)] unsafe extern "C" fn __inline_isinff(mut __x: ::core::ffi::c_float) -> ::core::ffi::c_int { return (__x.abs() == ::core::f32::INFINITY) as ::core::ffi::c_int; @@ -33,8 +39,12 @@ pub unsafe extern "C" fn long_double_test() { let mut huge: ::f128::f128 = ::f128::f128::INFINITY; let mut i: ::core::ffi::c_int = Into::<::core::ffi::c_int>::into(one); let mut f: ::core::ffi::c_float = Into::<::core::ffi::c_float>::into(one); + let mut b: bool = one != ::f128::f128::ZERO; + let mut e: E = E(Into::<::core::ffi::c_uint>::into(one)); let mut cast_from_int: ::f128::f128 = ::f128::f128::new(i); let mut cast_from_float: ::f128::f128 = ::f128::f128::new(f); + let mut cast_from_bool: ::f128::f128 = ::f128::f128::new(b as u8); + let mut cast_from_enum: ::f128::f128 = ::f128::f128::new(e.0); let mut is_inf: bool = if ::core::mem::size_of::<::f128::f128>() == ::core::mem::size_of::<::core::ffi::c_float>() { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@opaque_pointer_casts.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@opaque_pointer_casts.c.2021.clang15.snap index 7c3b5597b0..750e3931f8 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@opaque_pointer_casts.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@opaque_pointer_casts.c.2021.clang15.snap @@ -38,7 +38,7 @@ pub unsafe extern "C" fn from_address(mut address: ::core::ffi::c_ulong) -> *mut return ::core::ptr::from_exposed_addr_mut::<::core::ffi::c_void>(address as usize) as *mut Opaque; } -static mut static_sentinel: *const Opaque = 8 as ::core::ffi::c_int as *const Opaque; +static mut static_sentinel: *const Opaque = 8 as ::core::ffi::c_int as usize as *const Opaque; #[no_mangle] pub unsafe extern "C" fn get_static_sentinel() -> *const Opaque { return static_sentinel; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2021.linux.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2021.linux.clang15.snap index ef7e399f74..b985e0b7ec 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2021.linux.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2021.linux.clang15.snap @@ -15,8 +15,7 @@ pub type __sighandler_t = Option ()> #[no_mangle] pub unsafe extern "C" fn foo() { let mut handler: Option ()> = - ::core::mem::transmute::< - ::libc::intptr_t, - Option ()>, - >(1 as ::core::ffi::c_int as ::libc::intptr_t); + ::core::mem::transmute:: ()>>( + 1 as ::core::ffi::c_int as usize, + ); } diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2021.macos.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2021.macos.clang15.snap index 14f4a26380..d2d00411e8 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2021.macos.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2021.macos.clang15.snap @@ -14,8 +14,7 @@ expression: cat tests/snapshots/os-specific/sigign.2021.macos.rs #[no_mangle] pub unsafe extern "C" fn foo() { let mut handler: Option ()> = - ::core::mem::transmute::< - ::libc::intptr_t, - Option ()>, - >(1 as ::core::ffi::c_int as ::libc::intptr_t); + ::core::mem::transmute:: ()>>( + 1 as ::core::ffi::c_int as usize, + ); } diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2024.linux.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2024.linux.clang15.snap index 9a69b94999..e8a0a7a253 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2024.linux.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2024.linux.clang15.snap @@ -16,8 +16,7 @@ pub type __sighandler_t = Option ()> #[unsafe(no_mangle)] pub unsafe extern "C" fn foo() { let mut handler: Option ()> = - ::core::mem::transmute::< - ::libc::intptr_t, - Option ()>, - >(1 as ::core::ffi::c_int as ::libc::intptr_t); + ::core::mem::transmute:: ()>>( + 1 as ::core::ffi::c_int as usize, + ); } diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2024.macos.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2024.macos.clang15.snap index 6395a0d797..ac6cde48d3 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2024.macos.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@sigign.c.2024.macos.clang15.snap @@ -15,8 +15,7 @@ expression: cat tests/snapshots/os-specific/sigign.2024.macos.rs #[unsafe(no_mangle)] pub unsafe extern "C" fn foo() { let mut handler: Option ()> = - ::core::mem::transmute::< - ::libc::intptr_t, - Option ()>, - >(1 as ::core::ffi::c_int as ::libc::intptr_t); + ::core::mem::transmute:: ()>>( + 1 as ::core::ffi::c_int as usize, + ); }