From 891193b1327217f3bcf72a7c80ae35468fed9df6 Mon Sep 17 00:00:00 2001 From: Rua Date: Fri, 17 Apr 2026 20:30:59 +0200 Subject: [PATCH 01/10] 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 02/10] 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 03/10] 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 04/10] 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 05/10] 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 06/10] 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 07/10] 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 08/10] 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 }; From fe1a02690fb440af06833b719d6131600c156430 Mon Sep 17 00:00:00 2001 From: Rua Date: Fri, 17 Apr 2026 22:43:36 +0200 Subject: [PATCH 09/10] transpile: Translate `EnumConstant` with associated constants --- c2rust-ast-builder/src/builder.rs | 20 +++ c2rust-transpile/src/translator/enums.rs | 123 ++++++++++------ c2rust-transpile/src/translator/mod.rs | 133 +++++++++--------- .../snapshots__transpile@exprs.c.2021.snap | 16 ++- .../snapshots__transpile@exprs.c.2024.snap | 16 ++- ...snapshots__transpile@macrocase.c.2021.snap | 6 +- ...snapshots__transpile@macrocase.c.2024.snap | 6 +- .../snapshots__transpile@records.c.2021.snap | 16 ++- .../snapshots__transpile@records.c.2024.snap | 16 ++- tests/unit/enums/src/test_enums.rs | 10 +- tests/unit/misc/src/test_uninitialized.rs | 8 +- 11 files changed, 225 insertions(+), 145 deletions(-) diff --git a/c2rust-ast-builder/src/builder.rs b/c2rust-ast-builder/src/builder.rs index 909a15bd6e..3508e57683 100644 --- a/c2rust-ast-builder/src/builder.rs +++ b/c2rust-ast-builder/src/builder.rs @@ -1416,6 +1416,26 @@ impl Builder { })) } + pub fn const_impl_item(self, name: I, ty: Box, init: Box) -> ImplItem + where + I: Make, + { + let name = name.make(&self); + ImplItem::Const(ImplItemConst { + attrs: self.attrs, + vis: self.vis, + defaultness: None, + const_token: Token![const](self.span), + ident: name, + generics: self.generics, + colon_token: Token![:](self.span), + ty: *ty, + eq_token: Token![=](self.span), + expr: *init, + semi_token: Token![;](self.span), + }) + } + pub fn fn_item(self, sig: S, mut block: Block) -> Box where S: Make, diff --git a/c2rust-transpile/src/translator/enums.rs b/c2rust-transpile/src/translator/enums.rs index 6f495c1bb0..a4ce2cdf8a 100644 --- a/c2rust-transpile/src/translator/enums.rs +++ b/c2rust-transpile/src/translator/enums.rs @@ -1,9 +1,10 @@ use c2rust_ast_builder::mk; use proc_macro2::Span; -use syn::Expr; +use syn::{Expr, ImplItem}; use crate::c_ast::CUnOp; use crate::{ + c_ast::iterators::SomeId, diagnostics::TranslationResult, translator::{signed_int_expr, ConvertedDecl, ExprContext, Translation}, with_stmts::WithStmts, @@ -17,6 +18,7 @@ impl<'c> Translation<'c> { enum_id: CEnumId, span: Span, integral_type: CQualTypeId, + variants: &[CEnumConstantId], ) -> TranslationResult { let enum_name = &self .type_converter @@ -34,43 +36,61 @@ impl<'c> Translation<'c> { .pub_() .struct_item(enum_name, vec![field], true); - Ok(ConvertedDecl::Item(item)) - } + if variants.is_empty() { + return Ok(ConvertedDecl::Item(item)); + } - pub fn convert_enum_constant( - &self, - enum_constant_id: CEnumConstantId, - span: Span, - value: ConstIntExpr, - ) -> TranslationResult { - let name = self - .renamer - .borrow_mut() - .get(&enum_constant_id) - .expect("Enum constant not named"); - let enum_id = self.ast_context.parents[&enum_constant_id]; - let enum_name = self - .type_converter - .borrow() - .resolve_decl_name(enum_id) - .expect("Enums should already be renamed"); + let self_ty = mk().ident_ty("Self"); + let constants: Vec = variants + .iter() + .map(|&enum_constant_id| { + let span = self + .get_span(SomeId::Decl(enum_constant_id)) + .unwrap_or_else(Span::call_site); - 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); + let (name, value) = match self.ast_context[enum_constant_id].kind { + CDeclKind::EnumConstant { + ref name, value, .. + } => (name, value), + _ => panic!("{:?} does not point to an enum variant", enum_constant_id), + }; + let name = self.type_converter.borrow_mut().declare_field_name( + enum_id, + enum_constant_id, + name, + ); - Ok(ConvertedDecl::Item( - mk().span(span).pub_().const_item(name, ty, init), - )) + 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, true); + + mk().span(span) + .pub_() + .const_impl_item(name, self_ty.clone(), init) + }) + .collect(); + let impl_block = mk().impl_item(mk().ident_ty(enum_name), constants); + + Ok(ConvertedDecl::Items(vec![item, impl_block])) } pub fn convert_enum_zero_initializer(&self, enum_id: CEnumId) -> WithStmts> { WithStmts::new_val(self.enum_for_i64(enum_id, 0)) } + /// Translates a `DeclRef` for an `EnumConstant`. + pub fn convert_enum_constant_decl_ref( + &self, + enum_constant_id: CEnumConstantId, + target_cty: CTypeId, + ) -> TranslationResult> { + let enum_id = self.ast_context.parents[&enum_constant_id]; + let val = self.enum_constant_expr(enum_id, enum_constant_id); + self.convert_cast_from_enum(target_cty, val) + } + /// Translate a cast where the source type, but not the target type, is an `enum` type. pub fn convert_cast_from_enum( &self, @@ -114,7 +134,7 @@ impl<'c> Translation<'c> { // If this DeclRef expanded to a const macro, we actually need to insert a cast, // because the translation of a const macro skips implicit casts in its context. if !expr_is_macro { - return Ok(self.enum_constant_expr(enum_constant_id)); + return Ok(self.enum_constant_expr(enum_id, enum_constant_id)); } } @@ -149,14 +169,14 @@ impl<'c> Translation<'c> { val = mk().cast_expr(val, ty); } - Ok(self.enum_constructor_expr(enum_id, val)) + Ok(self.enum_constructor_expr(enum_id, val, false)) } /// 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_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); + return self.enum_constant_expr(enum_id, enum_constant_id); } let underlying_type_id = self.enum_integral_type(enum_id); @@ -166,7 +186,7 @@ impl<'c> Translation<'c> { _ => signed_int_expr(value), }; - self.enum_constructor_expr(enum_id, value) + self.enum_constructor_expr(enum_id, value, false) } /// Returns the id of the variant of `enum_id` whose value matches `value`, if any. @@ -187,20 +207,41 @@ impl<'c> Translation<'c> { }) } - fn enum_constructor_expr(&self, enum_id: CEnumId, value: Box) -> Box { + fn enum_constructor_expr( + &self, + enum_id: CEnumId, + value: Box, + use_self_ty: bool, + ) -> Box { + let func = if use_self_ty { + mk().ident_expr("Self") + } else { + let enum_name = self + .type_converter + .borrow() + .resolve_decl_name(enum_id) + .unwrap(); + self.add_import(enum_id, &enum_name); + mk().ident_expr(enum_name) + }; + + mk().call_expr(func, vec![value]) + } + + fn enum_constant_expr(&self, enum_id: CEnumId, enum_constant_id: CEnumConstantId) -> 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]) - } + let name = self + .type_converter + .borrow() + .resolve_field_name(Some(enum_id), enum_constant_id) + .unwrap(); - 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); - mk().ident_expr(name) + self.add_import(enum_id, &enum_name); + mk().path_expr(vec![enum_name, name]) } fn is_variant_of_enum(&self, enum_id: CEnumId, enum_constant_id: CEnumConstantId) -> bool { diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 709a0b5bca..03b9847e18 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -769,7 +769,6 @@ pub fn translate( Union { ref name, .. } => some_type_name(name.as_ref().map(String::as_str)), Typedef { ref name, .. } => Name::Type(name), Function { ref name, .. } => Name::Var(name), - EnumConstant { ref name, .. } => Name::Var(name), Variable { ref ident, .. } if t.ast_context.c_decls_top.contains(&decl_id) => { Name::Var(ident) } @@ -847,7 +846,6 @@ pub fn translate( let needs_export = match decl.kind { Struct { .. } => true, Enum { .. } => true, - EnumConstant { .. } => true, Union { .. } => true, Typedef { .. } => { // Only check the key as opposed to `contains` @@ -1922,11 +1920,13 @@ impl<'c> Translation<'c> { )), Enum { + ref variants, integral_type: Some(integral_type), .. - } => self.convert_enum(decl_id, span, integral_type), + } => self.convert_enum(decl_id, span, integral_type, variants), - EnumConstant { value, .. } => self.convert_enum_constant(decl_id, span, value), + // EnumConstant is translated as part of Enum. + EnumConstant { .. } => Ok(ConvertedDecl::NoItem), // We can allow non top level function declarations (i.e. extern // declarations) without any problem. Clang doesn't support nested @@ -3421,76 +3421,77 @@ impl<'c> Translation<'c> { } let varname = decl.get_name().expect("expected variable name").to_owned(); - let rustname = self - .renamer - .borrow_mut() - .get(&decl_id) - .ok_or_else(|| format_err!("name not declared: '{}'", varname))?; - - // Import the referenced global decl into our submodule - if self.tcfg.reorganize_definitions { - self.add_import(decl_id, &rustname); - // match decl { - // CDeclKind::Variable { is_defn: false, .. } => {} - // _ => self.add_import(decl_id, &rustname), - // } - } + let mut set_unsafe = false; - let mut val = mk().path_expr(vec![rustname]); + let mut val = if let &CDeclKind::EnumConstant { .. } = decl { + self.convert_enum_constant_decl_ref(decl_id, qual_ty.ctype)? + } else { + let rustname = self + .renamer + .borrow_mut() + .get(&decl_id) + .ok_or_else(|| format_err!("name not declared: '{}'", varname))?; + + // Import the referenced global decl into our submodule + if self.tcfg.reorganize_definitions { + self.add_import(decl_id, &rustname); + // match decl { + // CDeclKind::Variable { is_defn: false, .. } => {} + // _ => self.add_import(decl_id, &rustname), + // } + } - // If the variable is actually an `EnumConstant`, we need to add a cast to the - // expected integral type. - if let &CDeclKind::EnumConstant { .. } = decl { - val = self.convert_cast_from_enum(qual_ty.ctype, val)?; - } + let mut val = mk().path_expr(vec![rustname]); - // If we are referring to a function and need its address, we - // need to cast it to fn() to ensure that it has a real address. - let mut set_unsafe = false; - if ctx.needs_address() { - if let CDeclKind::Function { parameters, .. } = decl { - let ty = self.convert_type(qual_ty.ctype)?; - let actual_ty = self - .type_converter - .borrow_mut() - .knr_function_type_with_parameters( - &self.ast_context, - qual_ty.ctype, - parameters, - )?; - if let Some(actual_ty) = actual_ty { - if actual_ty != ty { - // If we're casting a concrete function to - // a K&R function pointer type, use transmute - self.import_type(qual_ty.ctype); - - val = transmute_expr(actual_ty, ty, val); - set_unsafe = true; - } - } else { - let decl_kind = &self.ast_context[decl_id].kind; - let kind_with_declared_args = - self.ast_context.fn_decl_ty_with_declared_args(decl_kind); - - if let Some(ty) = self - .ast_context - .type_for_kind(&kind_with_declared_args) - .map(CQualTypeId::new) - { - let ty = self.convert_type(ty.ctype)?; - val = mk().cast_expr(val, ty); + // If we are referring to a function and need its address, we + // need to cast it to fn() to ensure that it has a real address. + if ctx.needs_address() { + if let CDeclKind::Function { parameters, .. } = decl { + let ty = self.convert_type(qual_ty.ctype)?; + let actual_ty = self + .type_converter + .borrow_mut() + .knr_function_type_with_parameters( + &self.ast_context, + qual_ty.ctype, + parameters, + )?; + if let Some(actual_ty) = actual_ty { + if actual_ty != ty { + // If we're casting a concrete function to + // a K&R function pointer type, use transmute + self.import_type(qual_ty.ctype); + + val = transmute_expr(actual_ty, ty, val); + set_unsafe = true; + } } else { - val = mk().cast_expr(val, ty); + let decl_kind = &self.ast_context[decl_id].kind; + let kind_with_declared_args = + self.ast_context.fn_decl_ty_with_declared_args(decl_kind); + + if let Some(ty) = self + .ast_context + .type_for_kind(&kind_with_declared_args) + .map(CQualTypeId::new) + { + let ty = self.convert_type(ty.ctype)?; + val = mk().cast_expr(val, ty); + } else { + val = mk().cast_expr(val, ty); + } } } } - } - if let CTypeKind::VariableArray(..) = - self.ast_context.resolve_type(qual_ty.ctype).kind - { - val = mk().method_call_expr(val, "as_mut_ptr", vec![]); - } + if let CTypeKind::VariableArray(..) = + self.ast_context.resolve_type(qual_ty.ctype).kind + { + val = mk().method_call_expr(val, "as_mut_ptr", vec![]); + } + + val + }; // if the context wants a different type, add a cast if let Some(expected_ty) = override_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 c14728629e..bc2e207e0e 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.snap @@ -18,13 +18,17 @@ extern "C" { #[derive(Clone, Copy)] #[repr(transparent)] pub struct E(pub ::core::ffi::c_uint); -pub const EA: E = E(0); +impl E { + pub const EA: Self = Self(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); +impl C2Rust_Unnamed { + pub const A: Self = Self(0); + pub const B: Self = Self(1); + pub const C: Self = Self(2); +} 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; @@ -62,13 +66,13 @@ pub unsafe extern "C" fn unary_with_side_effect() { 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; + let mut e: E = E::EA; 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.0 as ::core::ffi::c_int; + let mut i: ::core::ffi::c_int = C2Rust_Unnamed::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 806e684a5e..397af0f7e2 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.snap @@ -18,13 +18,17 @@ unsafe extern "C" { #[derive(Clone, Copy)] #[repr(transparent)] pub struct E(pub ::core::ffi::c_uint); -pub const EA: E = E(0); +impl E { + pub const EA: Self = Self(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); +impl C2Rust_Unnamed { + pub const A: Self = Self(0); + pub const B: Self = Self(1); + pub const C: Self = Self(2); +} 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; @@ -62,13 +66,13 @@ pub unsafe extern "C" fn unary_with_side_effect() { 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; + let mut e: E = E::EA; 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.0 as ::core::ffi::c_int; + let mut i: ::core::ffi::c_int = C2Rust_Unnamed::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 45cc1a2870..fcd8899a6b 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2021.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2021.snap @@ -13,8 +13,10 @@ expression: cat tests/snapshots/macrocase.2021.rs #[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); +impl ZSTD_dParameter { + pub const ZSTD_d_windowLogMax: Self = Self(100); + pub const ZSTD_d_experimentalParam1: Self = Self(1000); +} 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 { 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 f49283d657..f9ec0c9ea4 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2024.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2024.snap @@ -14,8 +14,10 @@ expression: cat tests/snapshots/macrocase.2024.rs #[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); +impl ZSTD_dParameter { + pub const ZSTD_d_windowLogMax: Self = Self(100); + pub const ZSTD_d_experimentalParam1: Self = Self(1000); +} 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 { 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 fcb7321dff..7c17837f1b 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2021.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2021.snap @@ -16,8 +16,10 @@ pub struct AnonEnumInStruct {} #[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); +impl C2Rust_Unnamed { + pub const VALUE1: Self = Self(0); + pub const VALUE2: Self = Self(1); +} #[derive(Copy, Clone)] #[repr(C)] pub struct AnonStructInStruct { @@ -44,8 +46,10 @@ pub union AnonEnumInUnion { #[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); +impl C2Rust_Unnamed_1 { + pub const VALUE3: Self = Self(0); + pub const VALUE4: Self = Self(1); +} #[derive(Copy, Clone)] #[repr(C)] pub union AnonStructInUnion { @@ -69,7 +73,7 @@ pub struct InsideUnion { } #[no_mangle] pub unsafe extern "C" fn struct_declaration() { - let mut value: ::core::ffi::c_int = VALUE2.0 as ::core::ffi::c_int; + let mut value: ::core::ffi::c_int = C2Rust_Unnamed::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 }, @@ -80,7 +84,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.0 as ::core::ffi::c_int; + let mut value: ::core::ffi::c_int = C2Rust_Unnamed_1::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 6827ee4fc4..71747f2b61 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2024.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2024.snap @@ -17,8 +17,10 @@ pub struct AnonEnumInStruct {} #[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); +impl C2Rust_Unnamed { + pub const VALUE1: Self = Self(0); + pub const VALUE2: Self = Self(1); +} #[derive(Copy, Clone)] #[repr(C)] pub struct AnonStructInStruct { @@ -45,8 +47,10 @@ pub union AnonEnumInUnion { #[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); +impl C2Rust_Unnamed_1 { + pub const VALUE3: Self = Self(0); + pub const VALUE4: Self = Self(1); +} #[derive(Copy, Clone)] #[repr(C)] pub union AnonStructInUnion { @@ -70,7 +74,7 @@ pub struct InsideUnion { } #[unsafe(no_mangle)] pub unsafe extern "C" fn struct_declaration() { - let mut value: ::core::ffi::c_int = VALUE2.0 as ::core::ffi::c_int; + let mut value: ::core::ffi::c_int = C2Rust_Unnamed::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 }, @@ -81,7 +85,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.0 as ::core::ffi::c_int; + let mut value: ::core::ffi::c_int = C2Rust_Unnamed_1::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 c100fd91cc..17224cd324 100644 --- a/tests/unit/enums/src/test_enums.rs +++ b/tests/unit/enums/src/test_enums.rs @@ -1,12 +1,10 @@ use crate::big_enum::{rust_entry5, E1, E2, E3}; -use crate::enum_as_int::{rust_entry, A, B, E}; +use crate::enum_as_int::{rust_entry, E}; use crate::enum_compound::rust_entry6; use crate::enum_duplicate::{e, rust_entry3}; use crate::enum_fwd_decl::rust_foo; use crate::enum_ret::{rust_entry2, Color}; -use crate::non_canonical_enum_def::{ - hrtimer_restart, rust_abc, HRTIMER_NORESTART, HRTIMER_RESTART, -}; +use crate::non_canonical_enum_def::{hrtimer_restart, rust_abc}; use crate::top_enum::{rust_entry4, E as otherE}; use std::ffi::{c_int, c_uint}; @@ -35,8 +33,8 @@ const BUFFER_SIZE6: usize = 1; #[test] pub fn test_variants() { - assert_eq!(A.0 as u32, 0); - assert_eq!(B.0 as u32, 1); + assert_eq!(E::A.0 as u32, 0); + assert_eq!(E::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 505aee8937..111b1d0b7f 100644 --- a/tests/unit/misc/src/test_uninitialized.rs +++ b/tests/unit/misc/src/test_uninitialized.rs @@ -1,6 +1,6 @@ //! feature_raw_ref_op -use crate::uninitialized::{bar, baz, e, foo, rust_entry2, s, /*myint, myintp,*/ u}; +use crate::uninitialized::{e, rust_entry2, s, /*myint, myintp,*/ u}; use std::ffi::{c_int, c_uint}; unsafe extern "C" { @@ -26,9 +26,9 @@ pub fn test_buffer() { #[test] pub fn test_types() { - assert_eq!(foo.0 as u32, 1); - assert_eq!(bar.0 as u32, 2); - assert_eq!(baz.0 as u32, 3); + assert_eq!(e::foo.0 as u32, 1); + assert_eq!(e::bar.0 as u32, 2); + assert_eq!(e::baz.0 as u32, 3); // FIXME: union fields are private // let my_union = u { x: 32 }; From 43e8bfeddf7d952cd63f3caea485707361b71f60 Mon Sep 17 00:00:00 2001 From: Rua Date: Mon, 20 Apr 2026 23:26:09 +0200 Subject: [PATCH 10/10] test --- c2rust-refactor/src/context.rs | 48 ++++- .../src/transform/reorganize_definitions.rs | 187 +++++++++++++++--- .../tests/snapshots/reorganize_definitions.rs | 54 +++++ ...s__refactor-reorganize_definitions.rs.snap | 35 ++++ 4 files changed, 299 insertions(+), 25 deletions(-) diff --git a/c2rust-refactor/src/context.rs b/c2rust-refactor/src/context.rs index ff63b923b4..9032f0a2ca 100644 --- a/c2rust-refactor/src/context.rs +++ b/c2rust-refactor/src/context.rs @@ -4,8 +4,8 @@ use std::ops::Deref; use rustc_ast::ptr::P; use rustc_ast::{ - Expr, ExprKind, FnDecl, FnRetTy, ForeignItem, ForeignItemKind, Item, ItemKind, NodeId, Path, - QSelf, UseTreeKind, DUMMY_NODE_ID, + AssocItem, Expr, ExprKind, FnDecl, FnRetTy, ForeignItem, ForeignItemKind, Item, ItemKind, + NodeId, Path, QSelf, UseTreeKind, DUMMY_NODE_ID, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{DiagnosticBuilder, Level}; @@ -1072,6 +1072,16 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { pub fn compatible_types(&self, item1: &Item, item2: &Item, match_vis: bool) -> bool { use rustc_ast::ItemKind::*; match (&item1.kind, &item2.kind) { + (Impl(box ref impl1), Impl(box ref impl2)) => { + if impl1.items.len() != impl2.items.len() { + return false; + } + + (impl1.items.iter()) + .zip(impl2.items.iter()) + .all(|(item1, item2)| self.compatible_assoc_items(item1, item2, match_vis)) + } + // * Assure that these two items are in fact of the same type, just to be safe. (TyAlias(box ref ta1), TyAlias(box ref ta2)) => { match ( @@ -1226,6 +1236,40 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { } } + pub fn compatible_assoc_items( + &self, + item1: &AssocItem, + item2: &AssocItem, + match_vis: bool, + ) -> bool { + use rustc_ast::AssocItemKind::*; + + // Unlike for regular items, associated items must also match by name. + if item1.ident.as_str() != item2.ident.as_str() { + return false; + } + + match (&item1.kind, &item2.kind) { + (Const(def1, ty1, expr1), Const(def2, ty2, expr2)) => match ( + self.cx.opt_node_type(item1.id), + self.cx.opt_node_type(item2.id), + ) { + (Some(ty1), Some(ty2)) => { + self.structural_eq_tys(ty1, ty2) + && expr1.unnamed_equiv(expr2) + && def1.unnamed_equiv(def2) + } + _ => { + self.structural_eq_ast_tys(ty1, ty2, match_vis) + && expr1.unnamed_equiv(expr2) + && def1.unnamed_equiv(def2) + } + }, + + _ => false, + } + } + /// Compare two function declarations for equivalent argument and return types, /// ignoring argument names. pub fn compatible_fn_prototypes(&self, decl1: &FnDecl, decl2: &FnDecl) -> bool { diff --git a/c2rust-refactor/src/transform/reorganize_definitions.rs b/c2rust-refactor/src/transform/reorganize_definitions.rs index 08b35bb2bc..e5c3f7f8e4 100644 --- a/c2rust-refactor/src/transform/reorganize_definitions.rs +++ b/c2rust-refactor/src/transform/reorganize_definitions.rs @@ -288,6 +288,67 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> { keep_items } + // First, remove and store `impl` items, indexing them by the type they belong to. + let mut impls: HashMap = HashMap::new(); + FlatMapNodes::visit(krate, |mut item: P| { + if let Some((path, _)) = parse_source_header(&item.attrs) { + let header_item = item.clone(); + if let ItemKind::Mod(_, ModKind::Loaded(ref mut mod_items, _, _)) = &mut item.kind { + mod_items.retain(|item| { + if let ItemKind::Impl(impl_) = &item.kind { + // Only keep `impl` items with simple path types, and only if they + // contain nothing but `const` items. + fn impl_type_def_id(cx: &RefactorCtxt, impl_: &Impl) -> Option { + // Only inherent `impl`s that contain no items other than `const`. + if impl_.of_trait.is_some() + || !impl_ + .items + .iter() + .all(|item| matches!(item.kind, AssocItemKind::Const(..))) + { + return None; + }; + let ty = match cx.hir_map().find(impl_.self_ty.id)? { + hir::Node::Ty(ty) => ty, + _ => return None, + }; + let ty_path = match &ty.kind { + hir::TyKind::Path(hir::QPath::Resolved(None, path)) => path, + _ => return None, + }; + match ty_path.res { + Res::Def(_, def_id) => Some(def_id), + _ => None, + } + } + + if let Some(impl_type_def_id) = impl_type_def_id(&self.cx, &impl_) { + impls.entry(impl_type_def_id).or_insert_with(|| { + let parent_header = + HeaderInfo::new(header_item.ident, path.clone()); + MovedDeclImpl { + item: item.clone(), + parent_header, + } + }); + } + + false + } else { + true + } + }); + + smallvec![item] + } else { + panic!("Unexpected Item kind with header_src attribute"); + } + } else { + smallvec![item] + } + }); + + // Process all the remaining items. let mut declarations = HeaderDeclarations::new(self.cx); FlatMapNodes::visit(krate, |mut item: P| { if let Some((path, _)) = parse_source_header(&item.attrs) { @@ -322,8 +383,13 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> { } } + let new_def_id = self.cx.node_def_id(item.id); let header_info = HeaderInfo::new(header_item.ident, path.clone()); - let inserted = declarations.insert_item(item.clone(), header_info); + let inserted = declarations.insert_item( + item.clone(), + header_info, + impls.remove(&new_def_id), + ); // Keep the item if we are not collapsing it !inserted }); @@ -677,7 +743,7 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> { } else { let namespace = self.cx.item_namespace(&item); if let Some(namespace) = namespace { - match declarations.find_item(item, namespace) { + match declarations.find_item(item, namespace, None) { ContainsDecl::NotContained => false, ContainsDecl::Equivalent(_) => true, ContainsDecl::Definition(_) => true, @@ -751,6 +817,13 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> { // Remove src_loc attributes FlatMapNodes::visit(krate, |mut item: P| { item.attrs.retain(|attr| !is_c2rust_attr(attr, "src_loc")); + + if let ItemKind::Impl(impl_) = &mut item.kind { + for item in &mut impl_.items { + item.attrs.retain(|attr| !is_c2rust_attr(attr, "src_loc")); + } + } + smallvec![item] }); FlatMapNodes::visit(krate, |mut item: P| { @@ -1122,10 +1195,23 @@ struct MovedDecl { namespace: Namespace, loc: Option, parent_header: HeaderInfo, + impl_: Option, +} + +#[derive(Debug, Clone)] +struct MovedDeclImpl { + item: P, + parent_header: HeaderInfo, } impl MovedDecl { - fn new(decl: T, def_id: DefId, namespace: Namespace, parent_header: HeaderInfo) -> Self + fn new( + decl: T, + def_id: DefId, + namespace: Namespace, + parent_header: HeaderInfo, + impl_: Option, + ) -> Self where T: Into, { @@ -1142,6 +1228,7 @@ impl MovedDecl { namespace, loc, parent_header, + impl_, } } @@ -1328,14 +1415,12 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { /// Add an item into the module. If it has a name conflict with an existing /// item, choose the definition item over any declarations. - pub fn insert_item(&mut self, mut item: P, parent_header: HeaderInfo) -> bool { - let namespace = self.cx.item_namespace(&item); - let new_def_id = self.cx.node_def_id(item.id); - let ident = if let ItemKind::Use(tree) = &item.kind { - tree.ident() - } else { - item.ident - }; + pub fn insert_item( + &mut self, + mut item: P, + parent_header: HeaderInfo, + impl_: Option, + ) -> bool { match &item.kind { // We have to disambiguate anonymous items by contents, // since we don't have a proper Ident. @@ -1344,7 +1429,7 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { // ident_map. ItemKind::Use(tree) if is_nested(tree) => { for u in split_uses(item).into_iter() { - self.insert_item(u, parent_header.clone()); + self.insert_item(u, parent_header.clone(), impl_.clone()); } true } @@ -1352,7 +1437,7 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { // Keep function definitions, if any ItemKind::Fn(..) => false, - // Don't keep impl blocks, these are expanded from macros anyway + // These should have already been removed before. ItemKind::Impl(..) => true, // We collect all ForeignItems and later filter out any idents @@ -1372,11 +1457,24 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { // we don't have any items with the same name but different // contents. _ => { + let namespace = self.cx.item_namespace(&item); + let new_def_id = self.cx.node_def_id(item.id); + let ident = if let ItemKind::Use(tree) = &item.kind { + tree.ident() + } else { + item.ident + }; let unnamed = ident.as_str().contains("C2Rust_Unnamed"); - let def_id_mapping = match self.find_item(&item, namespace.unwrap()) { + let impl_item = impl_.as_ref().map(|impl_| &*impl_.item); + let def_id_mapping = match self.find_item(&item, namespace.unwrap(), impl_item) { ContainsDecl::NotContained => { - let new_item = - MovedDecl::new(item, new_def_id, namespace.unwrap(), parent_header); + let new_item = MovedDecl::new( + item, + new_def_id, + namespace.unwrap(), + parent_header, + impl_, + ); if unnamed { self.unnamed_items[namespace.unwrap()].push(new_item); } else { @@ -1396,8 +1494,13 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { ContainsDecl::Use(existing) => { let existing_def_id = existing.def_id; existing.join_visibility(&item.vis.kind); - *existing = - MovedDecl::new(item, new_def_id, namespace.unwrap(), parent_header); + *existing = MovedDecl::new( + item, + new_def_id, + namespace.unwrap(), + parent_header, + impl_, + ); Some((existing_def_id, new_def_id)) } @@ -1405,8 +1508,13 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { let existing_def_id = existing.def_id; item.vis.kind = join_visibility(&existing.visibility().kind, &item.vis.kind); - *existing = - MovedDecl::new(item, new_def_id, namespace.unwrap(), parent_header); + *existing = MovedDecl::new( + item, + new_def_id, + namespace.unwrap(), + parent_header, + impl_, + ); Some((existing_def_id, new_def_id)) } @@ -1432,6 +1540,7 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { new_def_id, namespace, parent_header.clone(), + None, ); if unnamed { self.unnamed_items[namespace].push(new_item); @@ -1451,6 +1560,7 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { new_def_id, namespace, parent_header.clone(), + None, ); Some((existing_def_id, new_def_id)) } @@ -1544,6 +1654,17 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { foreign_items.entry(abi).or_default().push(fi); } } + + // If there is an impl item, add it now. + if let Some(impl_) = item.impl_ { + let cur_mod_name = impl_.parent_header.ident; + let i = impl_.item; + if last_item_mod != Some(cur_mod_name) { + st.add_comment(i.id, make_header_comment(last_item_mod, cur_mod_name)); + last_item_mod = Some(cur_mod_name); + } + items.push(i); + } } let foreign_mods = foreign_items @@ -1553,7 +1674,12 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { foreign_mods.chain(items.into_iter()).collect() } - fn find_item<'b>(&'b mut self, item: &Item, namespace: Namespace) -> ContainsDecl<'b> { + fn find_item<'b>( + &'b mut self, + item: &Item, + namespace: Namespace, + impl_item: Option<&Item>, + ) -> ContainsDecl<'b> { let ident = if let ItemKind::Use(tree) = &item.kind { tree.ident() } else { @@ -1561,6 +1687,17 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { }; assert!(ident.name != kw::Empty); + let impl_is_compatible = |existing_decl: &MovedDecl| -> bool { + match (impl_item, &existing_decl.impl_) { + (None, None) => true, + (Some(impl_item), Some(existing_impl)) => { + self.cx + .compatible_types(impl_item, &existing_impl.item, false) + } + _ => false, + } + }; + if ident.as_str().contains("C2Rust_Unnamed") { for existing_decl in self.unnamed_items[namespace].iter_mut() { match &existing_decl.kind { @@ -1571,7 +1708,9 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { | ItemKind::Enum(..) => { // Does the new item match the existing item, except // for unnamed names? - if item.kind.unnamed_equiv(&existing_item.kind) { + if item.kind.unnamed_equiv(&existing_item.kind) + && impl_is_compatible(existing_decl) + { return ContainsDecl::Equivalent(existing_decl); } } @@ -1616,7 +1755,9 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { // Otherwise make sure these items are structurally // equivalent. _ => { - if self.cx.compatible_types(&item, &existing_item, true) { + if self.cx.compatible_types(&item, &existing_item, true) + && impl_is_compatible(existing_decl) + { return ContainsDecl::Equivalent(existing_decl); } } diff --git a/c2rust-refactor/tests/snapshots/reorganize_definitions.rs b/c2rust-refactor/tests/snapshots/reorganize_definitions.rs index 9b6f0a3e91..42a07f4cf5 100644 --- a/c2rust-refactor/tests/snapshots/reorganize_definitions.rs +++ b/c2rust-refactor/tests/snapshots/reorganize_definitions.rs @@ -22,6 +22,30 @@ pub mod bar { // Test relative paths use super::super::outside; + #[derive(Clone, Copy)] + #[repr(transparent)] + #[c2rust::src_loc = "15:0"] + pub struct C2Rust_Unnamed_2(pub ::core::ffi::c_uint); + + #[c2rust::src_loc = "15:0"] + impl C2Rust_Unnamed_2 { + #[c2rust::src_loc = "16:0"] + pub const U: Self = Self(42); + } + + #[derive(Clone, Copy)] + #[repr(transparent)] + #[c2rust::src_loc = "12:0"] + pub struct SomeEnum(pub ::core::ffi::c_uint); + + #[c2rust::src_loc = "12:0"] + impl SomeEnum { + #[c2rust::src_loc = "13:0"] + pub const A: Self = Self(0); + #[c2rust::src_loc = "14:0"] + pub const B: Self = Self(1); + } + #[c2rust::src_loc = "11:0"] type FooInt = i32; @@ -116,6 +140,30 @@ pub mod foo { // Test relative paths use super::super::outside; + #[derive(Clone, Copy)] + #[repr(transparent)] + #[c2rust::src_loc = "16:0"] + pub struct C2Rust_Unnamed_3(pub ::core::ffi::c_uint); + + #[c2rust::src_loc = "16:0"] + impl C2Rust_Unnamed_3 { + #[c2rust::src_loc = "17:0"] + pub const V: Self = Self(42); + } + + #[derive(Clone, Copy)] + #[repr(transparent)] + #[c2rust::src_loc = "13:0"] + pub struct SomeEnum(pub ::core::ffi::c_uint); + + #[c2rust::src_loc = "13:0"] + impl SomeEnum { + #[c2rust::src_loc = "14:0"] + pub const A: Self = Self(0); + #[c2rust::src_loc = "15:0"] + pub const B: Self = Self(1); + } + // Comment on bar_t #[derive(Copy, Clone)] #[repr(C)] @@ -213,6 +261,12 @@ pub mod foo { super::foo::bar_h::statfs64(core::ptr::null(), &mut buf); let _c = conflicting { y: 10 }; + + let e1 = super::foo::bar_h::SomeEnum::A; + let e2 = super::bar::bar_h::SomeEnum::B; + let e3 = super::bar::bar_h::C2Rust_Unnamed_2::U; + let e4 = super::foo::bar_h::C2Rust_Unnamed_3::V; + &Bar as *const bar_t } } diff --git a/c2rust-refactor/tests/snapshots/snapshots__refactor-reorganize_definitions.rs.snap b/c2rust-refactor/tests/snapshots/snapshots__refactor-reorganize_definitions.rs.snap index a7bb339d58..0dfa695331 100644 --- a/c2rust-refactor/tests/snapshots/snapshots__refactor-reorganize_definitions.rs.snap +++ b/c2rust-refactor/tests/snapshots/snapshots__refactor-reorganize_definitions.rs.snap @@ -90,6 +90,35 @@ pub mod bar { type FooInt = i32; + #[derive(Clone, Copy)] + #[repr(transparent)] + + pub struct SomeEnum(pub ::core::ffi::c_uint); + + impl crate::bar::SomeEnum { + pub const A: Self = Self(0); + + pub const B: Self = Self(1); + } + + #[derive(Clone, Copy)] + #[repr(transparent)] + + pub struct C2Rust_Unnamed_2(pub ::core::ffi::c_uint); + + impl crate::bar::C2Rust_Unnamed_2 { + pub const U: Self = Self(42); + } + + #[derive(Clone, Copy)] + #[repr(transparent)] + + pub struct C2Rust_Unnamed_3(pub ::core::ffi::c_uint); + + impl crate::bar::C2Rust_Unnamed_3 { + pub const V: Self = Self(42); + } + #[no_mangle] static mut Bar: crate::bar::bar_t = crate::bar::bar_t { alloc: 0 as *mut libc::c_char, @@ -138,6 +167,12 @@ pub mod foo { ); let _c = crate::compat_h::conflicting_1 { y: 10 }; + + let e1 = crate::bar::SomeEnum::A; + let e2 = crate::bar::SomeEnum::B; + let e3 = crate::bar::C2Rust_Unnamed_2::U; + let e4 = crate::bar::C2Rust_Unnamed_3::V; + &crate::bar::Bar as *const crate::bar::bar_t } }