From 891193b1327217f3bcf72a7c80ae35468fed9df6 Mon Sep 17 00:00:00 2001 From: Rua Date: Fri, 17 Apr 2026 20:30:59 +0200 Subject: [PATCH 1/8] refactor: Also include constructor ids in `RenameUnnamed` and `reorganize_definitions` --- c2rust-refactor/src/transform/items.rs | 9 +++++ .../src/transform/reorganize_definitions.rs | 39 ++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/c2rust-refactor/src/transform/items.rs b/c2rust-refactor/src/transform/items.rs index f0d2c424fb..dfa0fa434a 100644 --- a/c2rust-refactor/src/transform/items.rs +++ b/c2rust-refactor/src/transform/items.rs @@ -145,6 +145,15 @@ impl Transform for RenameUnnamed { renamer .new_idents .insert(cx.hir_map().node_to_hir_id(i.id), new_name); + + if let ItemKind::Struct(variant_data, _) | ItemKind::Union(variant_data, _) = &i.kind { + if let Some(ctor_id) = variant_data.ctor_id() { + renamer + .new_idents + .insert(cx.hir_map().node_to_hir_id(ctor_id), new_name); + } + } + renamer.new_to_old.insert(new_name, i.ident); counter += 1; smallvec![i.map(|i| Item { diff --git a/c2rust-refactor/src/transform/reorganize_definitions.rs b/c2rust-refactor/src/transform/reorganize_definitions.rs index d1feaa2fde..08b35bb2bc 100644 --- a/c2rust-refactor/src/transform/reorganize_definitions.rs +++ b/c2rust-refactor/src/transform/reorganize_definitions.rs @@ -12,7 +12,7 @@ use rustc_ast::*; use rustc_ast_pretty::pprust::{self, item_to_string, PrintState}; use rustc_data_structures::map_in_place::MapInPlace; use rustc_hir::def::{DefKind, Namespace, PerNS, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, Node}; use rustc_middle::metadata::ModChild; use rustc_middle::ty::{self, ParamEnv}; @@ -122,6 +122,7 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> { self.update_module_info_items(krate); self.move_items(header_decls, krate); + self.add_ctor_mappings(); self.update_paths(krate) } @@ -765,6 +766,42 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> { }); } + fn add_ctor_mappings(&mut self) { + let get_ctor = |def_id: DefId| -> Option { + self.cx.hir_map().get_if_local(def_id).and_then(|node| { + let item = match node { + hir::Node::Item(item) => item, + _ => return None, + }; + let hir_id = match &item.kind { + hir::ItemKind::Struct(variant_data, _) + | hir::ItemKind::Union(variant_data, _) => variant_data.ctor_hir_id()?, + _ => return None, + }; + self.cx.hir_map().opt_local_def_id(hir_id) + }) + }; + + let ctor_mapping: HashMap = self + .path_mapping + .iter() + .filter_map(|(&def_id, replacement)| { + Some(( + get_ctor(def_id)?.to_def_id(), + Replacement { + def: replacement + .def + .and_then(|def| get_ctor(def)) + .map(LocalDefId::to_def_id), + ..replacement.clone() + }, + )) + }) + .collect(); + + self.path_mapping.extend(ctor_mapping); + } + /// Update paths to moved items and remove redundant imports. fn update_paths(&self, krate: &mut Crate) { let tcx = self.cx.ty_ctxt(); From 5c8c84e9f89b999e425a84312048b000ea120e39 Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 16 Apr 2026 11:26:56 +0200 Subject: [PATCH 2/8] ast-exporter: handle foreign/extern enums properly --- c2rust-ast-exporter/src/AstExporter.cpp | 7 ++++++- tests/unit/enums/src/enum_fwd_decl.c | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/c2rust-ast-exporter/src/AstExporter.cpp b/c2rust-ast-exporter/src/AstExporter.cpp index abc92f7ae7..53fd9c74a7 100644 --- a/c2rust-ast-exporter/src/AstExporter.cpp +++ b/c2rust-ast-exporter/src/AstExporter.cpp @@ -2625,11 +2625,16 @@ class TranslateASTVisitor final void TypeEncoder::VisitEnumType(const EnumType *T) { auto ed = T->getDecl()->getDefinition(); + + if (!ed) { + ed = T->getDecl()->getCanonicalDecl(); + } + encodeType(T, TagEnumType, [T, ed](CborEncoder *local) { cbor_encode_uint(local, uintptr_t(ed)); }); - if (ed != nullptr) astEncoder->TraverseDecl(ed); + astEncoder->TraverseDecl(ed); } void TypeEncoder::VisitRecordType(const RecordType *T) { diff --git a/tests/unit/enums/src/enum_fwd_decl.c b/tests/unit/enums/src/enum_fwd_decl.c index e23e446a2b..4a90052755 100644 --- a/tests/unit/enums/src/enum_fwd_decl.c +++ b/tests/unit/enums/src/enum_fwd_decl.c @@ -10,4 +10,4 @@ struct _Evas_Func }; // dummy item imported by `test_enums.rs` -int foo(int i) { return i;} \ No newline at end of file +struct _Evas_Func foo(struct _Evas_Func i) { return i;} From a01d4319703257e1c00bc0c849da632b97bb2368 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 15 Apr 2026 18:42:02 +0200 Subject: [PATCH 3/8] transpile: Split off `TypedAstContext::set_prenamed_decls` --- c2rust-transpile/src/c_ast/mod.rs | 48 +++++++++++++++++++++ c2rust-transpile/src/translator/mod.rs | 59 ++++---------------------- 2 files changed, 57 insertions(+), 50 deletions(-) diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index b83171902c..d6a997d032 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -996,6 +996,54 @@ impl TypedAstContext { } } + /// Identifies typedefs that name unnamed types. + /// Later, the two declarations can be collapsed into a single name and declaration, + /// eliminating the typedef altogether. + pub fn set_prenamed_decls(&mut self) { + let mut prenamed_decls: IndexMap = IndexMap::new(); + + for (&decl_id, decl) in self.iter_decls() { + if let CDeclKind::Typedef { ref name, typ, .. } = decl.kind { + if let Some(subdecl_id) = self.resolve_type(typ.ctype).kind.as_underlying_decl() { + use CDeclKind::*; + let is_unnamed = match self[subdecl_id].kind { + Struct { name: None, .. } + | Union { name: None, .. } + | Enum { name: None, .. } => true, + + // Detect case where typedef and struct share the same name. + // In this case the purpose of the typedef was simply to eliminate + // the need for the 'struct' tag when referring to the type name. + Struct { + name: Some(ref target_name), + .. + } + | Union { + name: Some(ref target_name), + .. + } + | Enum { + name: Some(ref target_name), + .. + } => name == target_name, + + _ => false, + }; + + if is_unnamed + && !prenamed_decls + .values() + .any(|decl_id| *decl_id == subdecl_id) + { + prenamed_decls.insert(decl_id, subdecl_id); + } + } + } + } + + self.prenamed_decls = prenamed_decls; + } + pub fn prune_unwanted_decls(&mut self, want_unused_functions: bool) { // Starting from a set of root declarations, walk each one to find declarations it // depends on. Then walk each of those, recursively. diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index e986c89aeb..37565715b6 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -739,60 +739,19 @@ pub fn translate( // Identify typedefs that name unnamed types and collapse the two declarations // into a single name and declaration, eliminating the typedef altogether. - let mut prenamed_decls: IndexMap = IndexMap::new(); - for (&decl_id, decl) in t.ast_context.iter_decls() { - if let CDeclKind::Typedef { ref name, typ, .. } = decl.kind { - if let Some(subdecl_id) = t - .ast_context - .resolve_type(typ.ctype) - .kind - .as_underlying_decl() - { - use CDeclKind::*; - let is_unnamed = match t.ast_context[subdecl_id].kind { - Struct { name: None, .. } - | Union { name: None, .. } - | Enum { name: None, .. } => true, - - // Detect case where typedef and struct share the same name. - // In this case the purpose of the typedef was simply to eliminate - // the need for the 'struct' tag when referring to the type name. - Struct { - name: Some(ref target_name), - .. - } - | Union { - name: Some(ref target_name), - .. - } - | Enum { - name: Some(ref target_name), - .. - } => name == target_name, - - _ => false, - }; - - if is_unnamed - && !prenamed_decls - .values() - .any(|decl_id| *decl_id == subdecl_id) - { - prenamed_decls.insert(decl_id, subdecl_id); + t.ast_context.set_prenamed_decls(); - t.type_converter - .borrow_mut() - .declare_decl_name(decl_id, name); - t.type_converter - .borrow_mut() - .alias_decl_name(subdecl_id, decl_id); - } - } + for (&decl_id, &subdecl_id) in &t.ast_context.prenamed_decls { + if let CDeclKind::Typedef { ref name, .. } = t.ast_context[decl_id].kind { + t.type_converter + .borrow_mut() + .declare_decl_name(decl_id, name); + t.type_converter + .borrow_mut() + .alias_decl_name(subdecl_id, decl_id); } } - t.ast_context.prenamed_decls = prenamed_decls; - // Helper function that returns true if there is either a matching typedef or its // corresponding struct/union/enum fn contains(prenamed_decls: &IndexMap, decl_id: &CDeclId) -> bool { From 7255b001a4cdbd7791c16f7784103cd2b4e18bb5 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 15 Apr 2026 19:02:19 +0200 Subject: [PATCH 4/8] transpile: Minor refactor of `prune_unwanted_decls` --- c2rust-transpile/src/c_ast/mod.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index d6a997d032..74e88a8408 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1153,19 +1153,18 @@ impl TypedAstContext { } } - // Unset c_main if we are not retaining its declaration - if let Some(main_id) = self.c_main { - if !wanted.contains(&main_id) { - self.c_main = None; - } - } - // Prune any declaration that isn't considered live self.c_decls .retain(|&decl_id, _decl| wanted.contains(&decl_id)); - // Prune top declarations that are not considered live - self.c_decls_top.retain(|x| wanted.contains(x)); + // Remove references to removed decls that are held elsewhere. + self.c_decls_top.retain(|x| self.c_decls.contains_key(x)); + + if let Some(main_id) = self.c_main { + if !self.c_decls.contains_key(&main_id) { + self.c_main = None; + } + } } /// Bubble types of unary and binary operators up from their args into the expression type. From 2a9088477e43d7219a5a298c870cc5487bf6047c Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 15 Apr 2026 19:52:28 +0200 Subject: [PATCH 5/8] transpile: `prune_unwanted_decls` only after setting `prenamed_decls` --- c2rust-transpile/src/c_ast/mod.rs | 2 ++ c2rust-transpile/src/translator/mod.rs | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 74e88a8408..31d1024970 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1159,6 +1159,8 @@ impl TypedAstContext { // Remove references to removed decls that are held elsewhere. self.c_decls_top.retain(|x| self.c_decls.contains_key(x)); + self.prenamed_decls + .retain(|x, _| self.c_decls.contains_key(x)); if let Some(main_id) = self.c_main { if !self.c_decls.contains_key(&main_id) { diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 37565715b6..6e93fda923 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -708,6 +708,10 @@ pub fn translate( None }; + // Identify typedefs that name unnamed types and collapse the two declarations + // into a single name and declaration, eliminating the typedef altogether. + t.ast_context.set_prenamed_decls(); + // Headers often pull in declarations that are unused; // we simplify the translator output by omitting those. t.ast_context @@ -737,10 +741,6 @@ pub fn translate( prefix_names(&mut t, prefix); } - // Identify typedefs that name unnamed types and collapse the two declarations - // into a single name and declaration, eliminating the typedef altogether. - t.ast_context.set_prenamed_decls(); - for (&decl_id, &subdecl_id) in &t.ast_context.prenamed_decls { if let CDeclKind::Typedef { ref name, .. } = t.ast_context[decl_id].kind { t.type_converter From 8b10f5d1db9869a19f3ab1af6958cf912a56789e Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 15 Apr 2026 15:01:47 +0200 Subject: [PATCH 6/8] transpile: Do not require `expr` for `convert_cast_to_enum` --- c2rust-transpile/src/translator/mod.rs | 10 +--------- c2rust-transpile/src/translator/pointers.rs | 3 +-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 6e93fda923..87ec938e08 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -4187,16 +4187,8 @@ impl<'c> Translation<'c> { self.f128_cast_to(val, target_ty_kind) } else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { // Casts targeting `enum` types... - let expr = - expr.ok_or_else(|| format_err!("Casts to enums require a C ExprId"))?; val.result_map(|val| { - self.convert_cast_to_enum( - ctx, - target_cty.ctype, - enum_decl_id, - Some(expr), - val, - ) + self.convert_cast_to_enum(ctx, target_cty.ctype, enum_decl_id, expr, val) }) } else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() { Ok(val.map(|val| { diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index cd678edafa..eb58f47a52 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -547,9 +547,8 @@ impl<'c> Translation<'c> { ))) }) } else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { - let expr = expr.ok_or_else(|| format_err!("Casts to enums require a C ExprId"))?; val.result_map(|val| { - self.convert_cast_to_enum(ctx, target_cty, enum_decl_id, Some(expr), val) + self.convert_cast_to_enum(ctx, target_cty, enum_decl_id, expr, val) }) } else { Ok(val.map(|val| mk().cast_expr(val, target_ty))) From a42aa576382c319ff71dab02a14b36f6c7f27ab9 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 15 Apr 2026 15:02:03 +0200 Subject: [PATCH 7/8] transpile: Include enums in `CastKind::from_types` --- c2rust-transpile/src/c_ast/mod.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 31d1024970..9e47d1d916 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1852,17 +1852,17 @@ impl CastKind { (CTypeKind::Function(..), CTypeKind::Pointer(..)) => CastKind::FunctionToPointerDecay, - (_, CTypeKind::Pointer(..)) if source_ty_kind.is_integral_type() => { + (_, CTypeKind::Pointer(..)) if source_ty_kind.is_enum_or_integral_type() => { CastKind::IntegralToPointer } (CTypeKind::Pointer(..), CTypeKind::Bool) => CastKind::PointerToBoolean, - (CTypeKind::Pointer(..), _) if target_ty_kind.is_integral_type() => { + (CTypeKind::Pointer(..), _) if target_ty_kind.is_enum_or_integral_type() => { CastKind::PointerToIntegral } - (_, CTypeKind::Bool) if source_ty_kind.is_integral_type() => { + (_, CTypeKind::Bool) if source_ty_kind.is_enum_or_integral_type() => { CastKind::IntegralToBoolean } @@ -1870,11 +1870,17 @@ impl CastKind { CastKind::BooleanToSignedIntegral } - (_, _) if source_ty_kind.is_integral_type() && target_ty_kind.is_integral_type() => { + (_, _) + if source_ty_kind.is_enum_or_integral_type() + && target_ty_kind.is_enum_or_integral_type() => + { CastKind::IntegralCast } - (_, _) if source_ty_kind.is_integral_type() && target_ty_kind.is_floating_type() => { + (_, _) + if source_ty_kind.is_enum_or_integral_type() + && target_ty_kind.is_floating_type() => + { CastKind::IntegralToFloating } @@ -1882,7 +1888,10 @@ impl CastKind { CastKind::FloatingToBoolean } - (_, _) if source_ty_kind.is_floating_type() && target_ty_kind.is_integral_type() => { + (_, _) + if source_ty_kind.is_floating_type() + && target_ty_kind.is_enum_or_integral_type() => + { CastKind::FloatingToIntegral } @@ -2733,6 +2742,10 @@ impl CTypeKind { matches!(*self, Self::Enum { .. }) } + pub fn is_enum_or_integral_type(&self) -> bool { + self.is_integral_type() || self.is_enum() + } + pub fn is_integral_type(&self) -> bool { self.is_unsigned_integral_type() || self.is_signed_integral_type() } From fe459c5506deecbed24bd6e5d3aa62cdb1227e08 Mon Sep 17 00:00:00 2001 From: Rua Date: Fri, 17 Apr 2026 22:27:51 +0200 Subject: [PATCH 8/8] transpile: Translate enums into newtype structs --- c2rust-transpile/src/translator/enums.rs | 94 ++++++++++++------- c2rust-transpile/src/translator/mod.rs | 8 +- c2rust-transpile/src/translator/operators.rs | 29 ++++-- c2rust-transpile/src/translator/pointers.rs | 13 ++- .../snapshots__transpile@exprs.c.2021.snap | 20 ++-- .../snapshots__transpile@exprs.c.2024.snap | 20 ++-- ...snapshots__transpile@macrocase.c.2021.snap | 10 +- ...snapshots__transpile@macrocase.c.2024.snap | 10 +- .../snapshots__transpile@records.c.2021.snap | 20 ++-- .../snapshots__transpile@records.c.2024.snap | 20 ++-- tests/unit/enums/src/test_enums.rs | 4 +- tests/unit/misc/src/test_uninitialized.rs | 6 +- 12 files changed, 161 insertions(+), 93 deletions(-) diff --git a/c2rust-transpile/src/translator/enums.rs b/c2rust-transpile/src/translator/enums.rs index d1a9c0b8c9..6f495c1bb0 100644 --- a/c2rust-transpile/src/translator/enums.rs +++ b/c2rust-transpile/src/translator/enums.rs @@ -23,10 +23,18 @@ impl<'c> Translation<'c> { .borrow() .resolve_decl_name(enum_id) .expect("Enums should already be renamed"); - let ty = self.convert_type(integral_type.ctype)?; - Ok(ConvertedDecl::Item( - mk().span(span).pub_().type_item(enum_name, ty), - )) + + let field = mk() + .pub_() + .enum_field(self.convert_type(integral_type.ctype)?); + let item = mk() + .span(span) + .call_attr("derive", vec!["Clone", "Copy"]) + .call_attr("repr", vec!["transparent"]) + .pub_() + .struct_item(enum_name, vec![field], true); + + Ok(ConvertedDecl::Item(item)) } pub fn convert_enum_constant( @@ -46,47 +54,51 @@ impl<'c> Translation<'c> { .borrow() .resolve_decl_name(enum_id) .expect("Enums should already be renamed"); - self.add_import(enum_id, &enum_name); - let ty = mk().ident_ty(enum_name); + let ty = mk().ident_ty(enum_name.clone()); let val = match value { ConstIntExpr::I(value) => signed_int_expr(value), ConstIntExpr::U(value) => mk().lit_expr(mk().int_unsuffixed_lit(value as u128)), }; + let init = self.enum_constructor_expr(enum_id, val); Ok(ConvertedDecl::Item( - mk().span(span).pub_().const_item(name, ty, val), + mk().span(span).pub_().const_item(name, ty, init), )) } - pub fn convert_enum_zero_initializer(&self, type_id: CTypeId) -> WithStmts> { - WithStmts::new_val(self.enum_for_i64(type_id, 0)) + pub fn convert_enum_zero_initializer(&self, enum_id: CEnumId) -> WithStmts> { + WithStmts::new_val(self.enum_for_i64(enum_id, 0)) } /// Translate a cast where the source type, but not the target type, is an `enum` type. pub fn convert_cast_from_enum( &self, target_cty: CTypeId, - val: Box, + mut val: Box, ) -> TranslationResult> { // Convert it to the expected integral type. + val = self.integer_from_enum(val); + let ty = self.convert_type(target_cty)?; - Ok(mk().cast_expr(val, ty)) + val = mk().cast_expr(val, ty); + + Ok(val) } - /// Translate a cast where the target type is an `enum` type. - /// - /// When translating variable references to `EnumConstant`s, we always insert casts to the - /// expected type. In C, `EnumConstant`s have some integral type, _not_ the enum type. However, - /// if we then immediately have a cast to convert this variable back into an enum type, we would - /// like to produce Rust with _no_ casts. This function handles this simplification. + /// Gets the inner integral value of an enum value. + pub fn integer_from_enum(&self, val: Box) -> Box { + mk().anon_field_expr(val, 0) + } + + /// Translates a cast where the target type is an `enum` type. pub fn convert_cast_to_enum( &self, ctx: ExprContext, - enum_type_id: CTypeId, enum_id: CEnumId, expr: Option, - val: Box, + source_cty: CTypeId, + mut val: Box, ) -> TranslationResult> { if let Some(expr) = expr { match self.ast_context[expr].kind { @@ -107,14 +119,14 @@ impl<'c> Translation<'c> { } CExprKind::Literal(_, CLiteral::Integer(i, _)) => { - return Ok(self.enum_for_i64(enum_type_id, i as i64)); + return Ok(self.enum_for_i64(enum_id, i as i64)); } CExprKind::Unary(_, CUnOp::Negate, subexpr_id, _) => { if let &CExprKind::Literal(_, CLiteral::Integer(i, _)) = &self.ast_context[subexpr_id].kind { - return Ok(self.enum_for_i64(enum_type_id, -(i as i64))); + return Ok(self.enum_for_i64(enum_id, -(i as i64))); } } @@ -122,18 +134,27 @@ impl<'c> Translation<'c> { } } - let target_ty = self.convert_type(enum_type_id)?; - Ok(mk().cast_expr(val, target_ty)) + let integral_type = self.enum_integral_type(enum_id); + let mut needs_cast = true; + + // C allows directly casting from enum to enum, but in Rust we need to use + // the inner integer value as an intermediate. + if let CTypeKind::Enum(source_enum_id) = self.ast_context.resolve_type(source_cty).kind { + val = self.integer_from_enum(val); + needs_cast = integral_type.ctype != self.enum_integral_type(source_enum_id).ctype; + } + + if needs_cast { + let ty = self.convert_type(integral_type.ctype)?; + val = mk().cast_expr(val, ty); + } + + Ok(self.enum_constructor_expr(enum_id, val)) } /// Given an integer value this attempts to either generate the corresponding enum /// variant directly, otherwise it converts a number to the enum type. - fn enum_for_i64(&self, enum_type_id: CTypeId, value: i64) -> Box { - let enum_id = match self.ast_context.resolve_type(enum_type_id).kind { - CTypeKind::Enum(enum_id) => enum_id, - _ => panic!("{:?} does not point to an `enum` type", enum_type_id), - }; - + fn enum_for_i64(&self, enum_id: CEnumId, value: i64) -> Box { if let Some(enum_constant_id) = self.enum_variant_for_i64(enum_id, value) { return self.enum_constant_expr(enum_constant_id); } @@ -145,8 +166,7 @@ impl<'c> Translation<'c> { _ => signed_int_expr(value), }; - let target_ty = self.convert_type(enum_type_id).unwrap(); - mk().cast_expr(value, target_ty) + self.enum_constructor_expr(enum_id, value) } /// Returns the id of the variant of `enum_id` whose value matches `value`, if any. @@ -167,6 +187,16 @@ impl<'c> Translation<'c> { }) } + fn enum_constructor_expr(&self, enum_id: CEnumId, value: Box) -> Box { + let enum_name = self + .type_converter + .borrow() + .resolve_decl_name(enum_id) + .unwrap(); + self.add_import(enum_id, &enum_name); + mk().call_expr(mk().ident_expr(enum_name), vec![value]) + } + fn enum_constant_expr(&self, enum_constant_id: CEnumConstantId) -> Box { let name = self.renamer.borrow().get(&enum_constant_id).unwrap(); self.add_import(enum_constant_id, &name); @@ -182,7 +212,7 @@ impl<'c> Translation<'c> { variants.contains(&enum_constant_id) } - fn enum_integral_type(&self, enum_id: CEnumId) -> CQualTypeId { + pub fn enum_integral_type(&self, enum_id: CEnumId) -> CQualTypeId { match self.ast_context[enum_id].kind { CDeclKind::Enum { integral_type: Some(integral_type), diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 87ec938e08..709a0b5bca 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -4185,10 +4185,10 @@ impl<'c> Translation<'c> { self.ast_context[source_cty.ctype].kind { self.f128_cast_to(val, target_ty_kind) - } else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { + } else if let &CTypeKind::Enum(enum_id) = target_ty_kind { // Casts targeting `enum` types... val.result_map(|val| { - self.convert_cast_to_enum(ctx, target_cty.ctype, enum_decl_id, expr, val) + self.convert_cast_to_enum(ctx, enum_id, expr, source_cty.ctype, val) }) } else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() { Ok(val.map(|val| { @@ -4465,7 +4465,7 @@ impl<'c> Translation<'c> { } // Transmute the number `0` into the enum type - CDeclKind::Enum { .. } => self.convert_enum_zero_initializer(type_id), + CDeclKind::Enum { .. } => self.convert_enum_zero_initializer(decl_id), _ => { return Err(TranslationError::generic( @@ -4569,7 +4569,7 @@ impl<'c> Translation<'c> { } let val = if ty.is_enum() { - mk().cast_expr(val, mk().path_ty(vec!["u64"])) + mk().anon_field_expr(val, 0) } else { val }; diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 98e09400a5..34c1909a80 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -723,26 +723,35 @@ impl<'c> Translation<'c> { _ => mk().lit_expr(mk().int_unsuffixed_lit(1)), }; - let one_type_id = - if let CTypeKind::Pointer(..) = self.ast_context.resolve_type(arg_type.ctype).kind { - CQualTypeId::new( + let mut rhs_type_id = arg_type; + let mut compute_lhs_type_id = arg_type; + let mut compute_res_type_id = ty; + + match self.ast_context.resolve_type(arg_type.ctype).kind { + CTypeKind::Pointer(..) => { + rhs_type_id = CQualTypeId::new( self.ast_context .type_for_kind(&CTypeKind::Int) .ok_or_else(|| format_err!("couldn't find type for CTypeKind::Int"))?, - ) - } else { - arg_type - }; + ); + } + CTypeKind::Enum(enum_id) => { + rhs_type_id = self.enum_integral_type(enum_id); + compute_lhs_type_id = rhs_type_id; + compute_res_type_id = rhs_type_id; + } + _ => {} + }; self.convert_assignment_operator_with_rhs( ctx.used(), op, ty, arg, - one_type_id, + rhs_type_id, WithStmts::new_val(one), - Some(arg_type), - Some(ty), + Some(compute_lhs_type_id), + Some(compute_res_type_id), ) } diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index eb58f47a52..db1a093eec 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -253,6 +253,10 @@ impl<'c> Translation<'c> { let lhs_node_type = lhs_node .get_type() .ok_or_else(|| format_err!("lhs node bad type"))?; + let rhs_node_type = rhs_node + .get_type() + .ok_or_else(|| format_err!("rhs node bad type"))?; + if self .ast_context .resolve_type(lhs_node_type) @@ -266,7 +270,12 @@ impl<'c> Translation<'c> { } let rhs = self.convert_expr(ctx.used(), rhs, None)?; - rhs.and_then(|rhs| { + rhs.and_then(|mut rhs| { + // C allows enums to index arrays directly without inserting a numeric cast. + if let CTypeKind::Enum(..) = self.ast_context.resolve_type(rhs_node_type).kind { + rhs = self.integer_from_enum(rhs); + } + let simple_index_array = if ctx.needs_address() { // We can't necessarily index into an array if we're using // that element to compute an address. @@ -548,7 +557,7 @@ impl<'c> Translation<'c> { }) } else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { val.result_map(|val| { - self.convert_cast_to_enum(ctx, target_cty, enum_decl_id, expr, val) + self.convert_cast_to_enum(ctx, enum_decl_id, expr, source_cty, val) }) } else { Ok(val.map(|val| mk().cast_expr(val, target_ty))) diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.snap index 46697e62fe..c14728629e 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.snap @@ -15,12 +15,16 @@ expression: cat tests/snapshots/exprs.2021.rs extern "C" { fn puts(str: *const ::core::ffi::c_char) -> ::core::ffi::c_int; } -pub type E = ::core::ffi::c_uint; -pub const EA: E = 0; -pub type C2Rust_Unnamed = ::core::ffi::c_uint; -pub const C: C2Rust_Unnamed = 2; -pub const B: C2Rust_Unnamed = 1; -pub const A: C2Rust_Unnamed = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct E(pub ::core::ffi::c_uint); +pub const EA: E = E(0); +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct C2Rust_Unnamed(pub ::core::ffi::c_uint); +pub const C: C2Rust_Unnamed = C2Rust_Unnamed(2); +pub const B: C2Rust_Unnamed = C2Rust_Unnamed(1); +pub const A: C2Rust_Unnamed = C2Rust_Unnamed(0); unsafe extern "C" fn side_effect() -> ::core::ffi::c_int { puts(b"the return of side effect\0".as_ptr() as *const ::core::ffi::c_char); return 10 as ::core::ffi::c_int; @@ -59,12 +63,12 @@ pub unsafe extern "C" fn unsigned_compound_desugaring() { let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; let mut u: ::core::ffi::c_uint = 0 as ::core::ffi::c_uint; let mut e: E = EA; - e = (e as ::core::ffi::c_uint).wrapping_add(u) as E; + e = E((e.0 as ::core::ffi::c_uint).wrapping_add(u) as ::core::ffi::c_uint); i = (i as ::core::ffi::c_uint).wrapping_add(u) as ::core::ffi::c_int; } #[no_mangle] pub unsafe extern "C" fn compound_literal() { - let mut i: ::core::ffi::c_int = B as ::core::ffi::c_int; + let mut i: ::core::ffi::c_int = B.0 as ::core::ffi::c_int; } #[no_mangle] pub unsafe extern "C" fn statement_expr() { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.snap index 61d0b78e9c..806e684a5e 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.snap @@ -15,12 +15,16 @@ expression: cat tests/snapshots/exprs.2024.rs unsafe extern "C" { unsafe fn puts(str: *const ::core::ffi::c_char) -> ::core::ffi::c_int; } -pub type E = ::core::ffi::c_uint; -pub const EA: E = 0; -pub type C2Rust_Unnamed = ::core::ffi::c_uint; -pub const C: C2Rust_Unnamed = 2; -pub const B: C2Rust_Unnamed = 1; -pub const A: C2Rust_Unnamed = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct E(pub ::core::ffi::c_uint); +pub const EA: E = E(0); +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct C2Rust_Unnamed(pub ::core::ffi::c_uint); +pub const C: C2Rust_Unnamed = C2Rust_Unnamed(2); +pub const B: C2Rust_Unnamed = C2Rust_Unnamed(1); +pub const A: C2Rust_Unnamed = C2Rust_Unnamed(0); unsafe extern "C" fn side_effect() -> ::core::ffi::c_int { puts(b"the return of side effect\0".as_ptr() as *const ::core::ffi::c_char); return 10 as ::core::ffi::c_int; @@ -59,12 +63,12 @@ pub unsafe extern "C" fn unsigned_compound_desugaring() { let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; let mut u: ::core::ffi::c_uint = 0 as ::core::ffi::c_uint; let mut e: E = EA; - e = (e as ::core::ffi::c_uint).wrapping_add(u) as E; + e = E((e.0 as ::core::ffi::c_uint).wrapping_add(u) as ::core::ffi::c_uint); i = (i as ::core::ffi::c_uint).wrapping_add(u) as ::core::ffi::c_int; } #[unsafe(no_mangle)] pub unsafe extern "C" fn compound_literal() { - let mut i: ::core::ffi::c_int = B as ::core::ffi::c_int; + let mut i: ::core::ffi::c_int = B.0 as ::core::ffi::c_int; } #[unsafe(no_mangle)] pub unsafe extern "C" fn statement_expr() { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2021.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2021.snap index 3695088a01..45cc1a2870 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2021.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2021.snap @@ -10,14 +10,16 @@ expression: cat tests/snapshots/macrocase.2021.rs unused_assignments, unused_mut )] -pub type ZSTD_dParameter = ::core::ffi::c_uint; -pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = 1000; -pub const ZSTD_d_windowLogMax: ZSTD_dParameter = 100; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct ZSTD_dParameter(pub ::core::ffi::c_uint); +pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = ZSTD_dParameter(1000); +pub const ZSTD_d_windowLogMax: ZSTD_dParameter = ZSTD_dParameter(100); pub const ZSTD_d_format: ::core::ffi::c_uint = 1000 as ::core::ffi::c_uint; #[no_mangle] pub unsafe extern "C" fn ZSTD_dParam_getBounds(mut dParam: ZSTD_dParameter) -> ::core::ffi::c_int { let mut bounds: ::core::ffi::c_int = 0 as ::core::ffi::c_int; - match dParam as ::core::ffi::c_uint { + match dParam.0 as ::core::ffi::c_uint { 100 => { bounds = 1 as ::core::ffi::c_int; return bounds; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2024.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2024.snap index c1c67d21d6..f49283d657 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2024.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2024.snap @@ -11,14 +11,16 @@ expression: cat tests/snapshots/macrocase.2024.rs unused_assignments, unused_mut )] -pub type ZSTD_dParameter = ::core::ffi::c_uint; -pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = 1000; -pub const ZSTD_d_windowLogMax: ZSTD_dParameter = 100; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct ZSTD_dParameter(pub ::core::ffi::c_uint); +pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = ZSTD_dParameter(1000); +pub const ZSTD_d_windowLogMax: ZSTD_dParameter = ZSTD_dParameter(100); pub const ZSTD_d_format: ::core::ffi::c_uint = 1000 as ::core::ffi::c_uint; #[unsafe(no_mangle)] pub unsafe extern "C" fn ZSTD_dParam_getBounds(mut dParam: ZSTD_dParameter) -> ::core::ffi::c_int { let mut bounds: ::core::ffi::c_int = 0 as ::core::ffi::c_int; - match dParam as ::core::ffi::c_uint { + match dParam.0 as ::core::ffi::c_uint { 100 => { bounds = 1 as ::core::ffi::c_int; return bounds; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2021.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2021.snap index 969f6cc77c..fcb7321dff 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2021.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2021.snap @@ -13,9 +13,11 @@ expression: cat tests/snapshots/records.2021.rs #[derive(Copy, Clone)] #[repr(C)] pub struct AnonEnumInStruct {} -pub type C2Rust_Unnamed = ::core::ffi::c_uint; -pub const VALUE2: C2Rust_Unnamed = 1; -pub const VALUE1: C2Rust_Unnamed = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct C2Rust_Unnamed(pub ::core::ffi::c_uint); +pub const VALUE2: C2Rust_Unnamed = C2Rust_Unnamed(1); +pub const VALUE1: C2Rust_Unnamed = C2Rust_Unnamed(0); #[derive(Copy, Clone)] #[repr(C)] pub struct AnonStructInStruct { @@ -39,9 +41,11 @@ pub struct InsideStruct { pub union AnonEnumInUnion { pub a: ::core::ffi::c_int, } -pub type C2Rust_Unnamed_1 = ::core::ffi::c_uint; -pub const VALUE4: C2Rust_Unnamed_1 = 1; -pub const VALUE3: C2Rust_Unnamed_1 = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct C2Rust_Unnamed_1(pub ::core::ffi::c_uint); +pub const VALUE4: C2Rust_Unnamed_1 = C2Rust_Unnamed_1(1); +pub const VALUE3: C2Rust_Unnamed_1 = C2Rust_Unnamed_1(0); #[derive(Copy, Clone)] #[repr(C)] pub union AnonStructInUnion { @@ -65,7 +69,7 @@ pub struct InsideUnion { } #[no_mangle] pub unsafe extern "C" fn struct_declaration() { - let mut value: ::core::ffi::c_int = VALUE2 as ::core::ffi::c_int; + let mut value: ::core::ffi::c_int = VALUE2.0 as ::core::ffi::c_int; let mut a: AnonEnumInStruct = AnonEnumInStruct {}; let mut b: AnonStructInStruct = AnonStructInStruct { c2rust_unnamed: C2Rust_Unnamed_0 { some_number: 0 }, @@ -76,7 +80,7 @@ pub unsafe extern "C" fn struct_declaration() { } #[no_mangle] pub unsafe extern "C" fn union_declaration() { - let mut value: ::core::ffi::c_int = VALUE4 as ::core::ffi::c_int; + let mut value: ::core::ffi::c_int = VALUE4.0 as ::core::ffi::c_int; let mut a: AnonEnumInUnion = AnonEnumInUnion { a: 0 }; let mut b: AnonStructInUnion = AnonStructInUnion { c2rust_unnamed: C2Rust_Unnamed_2 { some_number: 0 }, diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2024.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2024.snap index e6dcf87b42..6827ee4fc4 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2024.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2024.snap @@ -14,9 +14,11 @@ expression: cat tests/snapshots/records.2024.rs #[derive(Copy, Clone)] #[repr(C)] pub struct AnonEnumInStruct {} -pub type C2Rust_Unnamed = ::core::ffi::c_uint; -pub const VALUE2: C2Rust_Unnamed = 1; -pub const VALUE1: C2Rust_Unnamed = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct C2Rust_Unnamed(pub ::core::ffi::c_uint); +pub const VALUE2: C2Rust_Unnamed = C2Rust_Unnamed(1); +pub const VALUE1: C2Rust_Unnamed = C2Rust_Unnamed(0); #[derive(Copy, Clone)] #[repr(C)] pub struct AnonStructInStruct { @@ -40,9 +42,11 @@ pub struct InsideStruct { pub union AnonEnumInUnion { pub a: ::core::ffi::c_int, } -pub type C2Rust_Unnamed_1 = ::core::ffi::c_uint; -pub const VALUE4: C2Rust_Unnamed_1 = 1; -pub const VALUE3: C2Rust_Unnamed_1 = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct C2Rust_Unnamed_1(pub ::core::ffi::c_uint); +pub const VALUE4: C2Rust_Unnamed_1 = C2Rust_Unnamed_1(1); +pub const VALUE3: C2Rust_Unnamed_1 = C2Rust_Unnamed_1(0); #[derive(Copy, Clone)] #[repr(C)] pub union AnonStructInUnion { @@ -66,7 +70,7 @@ pub struct InsideUnion { } #[unsafe(no_mangle)] pub unsafe extern "C" fn struct_declaration() { - let mut value: ::core::ffi::c_int = VALUE2 as ::core::ffi::c_int; + let mut value: ::core::ffi::c_int = VALUE2.0 as ::core::ffi::c_int; let mut a: AnonEnumInStruct = AnonEnumInStruct {}; let mut b: AnonStructInStruct = AnonStructInStruct { c2rust_unnamed: C2Rust_Unnamed_0 { some_number: 0 }, @@ -77,7 +81,7 @@ pub unsafe extern "C" fn struct_declaration() { } #[unsafe(no_mangle)] pub unsafe extern "C" fn union_declaration() { - let mut value: ::core::ffi::c_int = VALUE4 as ::core::ffi::c_int; + let mut value: ::core::ffi::c_int = VALUE4.0 as ::core::ffi::c_int; let mut a: AnonEnumInUnion = AnonEnumInUnion { a: 0 }; let mut b: AnonStructInUnion = AnonStructInUnion { c2rust_unnamed: C2Rust_Unnamed_2 { some_number: 0 }, diff --git a/tests/unit/enums/src/test_enums.rs b/tests/unit/enums/src/test_enums.rs index 1e373fd354..c100fd91cc 100644 --- a/tests/unit/enums/src/test_enums.rs +++ b/tests/unit/enums/src/test_enums.rs @@ -35,8 +35,8 @@ const BUFFER_SIZE6: usize = 1; #[test] pub fn test_variants() { - assert_eq!(A as u32, 0); - assert_eq!(B as u32, 1); + assert_eq!(A.0 as u32, 0); + assert_eq!(B.0 as u32, 1); } #[test] diff --git a/tests/unit/misc/src/test_uninitialized.rs b/tests/unit/misc/src/test_uninitialized.rs index 87425c5b32..505aee8937 100644 --- a/tests/unit/misc/src/test_uninitialized.rs +++ b/tests/unit/misc/src/test_uninitialized.rs @@ -26,9 +26,9 @@ pub fn test_buffer() { #[test] pub fn test_types() { - assert_eq!(foo as u32, 1); - assert_eq!(bar as u32, 2); - assert_eq!(baz as u32, 3); + assert_eq!(foo.0 as u32, 1); + assert_eq!(bar.0 as u32, 2); + assert_eq!(baz.0 as u32, 3); // FIXME: union fields are private // let my_union = u { x: 32 };