From 8777efbe2e7e135bd8fe2ec00fbd3bd8ac5bc941 Mon Sep 17 00:00:00 2001 From: Andrei Homescu Date: Wed, 26 Nov 2025 18:03:54 -0800 Subject: [PATCH 1/7] c2rust-refactor: tests: Fix reorganize_definitions test --- .../tests/reorganize_definitions/new.rs | 15 ++++++++------- .../tests/reorganize_definitions/old.rs | 3 +++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/c2rust-refactor/tests/reorganize_definitions/new.rs b/c2rust-refactor/tests/reorganize_definitions/new.rs index 1066827ab9..846d1db453 100644 --- a/c2rust-refactor/tests/reorganize_definitions/new.rs +++ b/c2rust-refactor/tests/reorganize_definitions/new.rs @@ -1,5 +1,6 @@ #![feature(extern_types)] #![feature(rustc_private)] +#![feature(register_tool)] #![register_tool(c2rust)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] @@ -8,16 +9,16 @@ #![allow(mutable_transmutes)] #![allow(unused_mut)] -pub mod compat_h_0 { - pub struct conflicting { - pub y: libc::c_char, - } -} pub mod compat_h { pub struct conflicting { pub x: libc::c_char, } + + pub struct conflicting_1 { + pub y: libc::c_char, + } } +extern crate libc; type outside = i32; @@ -59,7 +60,7 @@ pub mod foo { use crate::bar::bar_t; use crate::bar::Bar; - use crate::compat_h_0::conflicting; + use crate::compat_h::conflicting_1; // Comment on foo_t @@ -71,7 +72,7 @@ pub mod foo { } unsafe fn foo() -> *const crate::bar::bar_t { - let c = crate::compat_h_0::conflicting { y: 10 }; + let c = crate::compat_h::conflicting_1 { y: 10 }; &crate::bar::Bar as *const crate::bar::bar_t } } diff --git a/c2rust-refactor/tests/reorganize_definitions/old.rs b/c2rust-refactor/tests/reorganize_definitions/old.rs index 88eaf18c74..2cea864cfd 100644 --- a/c2rust-refactor/tests/reorganize_definitions/old.rs +++ b/c2rust-refactor/tests/reorganize_definitions/old.rs @@ -1,5 +1,6 @@ #![feature(extern_types)] #![feature(rustc_private)] +#![feature(register_tool)] #![register_tool(c2rust)] #![allow(non_upper_case_globals)] @@ -9,6 +10,8 @@ #![allow(mutable_transmutes)] #![allow(unused_mut)] +extern crate libc; + #[c2rust::src_loc = "15:0"] type outside = i32; From 9d98cffe3e43b488f08c945da9c0827a0694f616 Mon Sep 17 00:00:00 2001 From: Andrei Homescu Date: Wed, 26 Nov 2025 18:04:45 -0800 Subject: [PATCH 2/7] c2rust-refactor: tests: Fix warnings in reorganize_definitions --- c2rust-refactor/tests/reorganize_definitions/new.rs | 12 +++++------- c2rust-refactor/tests/reorganize_definitions/old.rs | 12 +++++------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/c2rust-refactor/tests/reorganize_definitions/new.rs b/c2rust-refactor/tests/reorganize_definitions/new.rs index 846d1db453..43f0985836 100644 --- a/c2rust-refactor/tests/reorganize_definitions/new.rs +++ b/c2rust-refactor/tests/reorganize_definitions/new.rs @@ -46,12 +46,10 @@ pub mod bar { type FooInt = i32; #[no_mangle] - static mut Bar: crate::bar::bar_t = unsafe { - crate::bar::bar_t { - alloc: 0 as *mut libc::c_char, - data: 0 as *mut libc::c_char, - i: 0, - } + static mut Bar: crate::bar::bar_t = crate::bar::bar_t { + alloc: 0 as *mut libc::c_char, + data: 0 as *mut libc::c_char, + i: 0, }; } @@ -72,7 +70,7 @@ pub mod foo { } unsafe fn foo() -> *const crate::bar::bar_t { - let c = crate::compat_h::conflicting_1 { y: 10 }; + let _c = crate::compat_h::conflicting_1 { y: 10 }; &crate::bar::Bar as *const crate::bar::bar_t } } diff --git a/c2rust-refactor/tests/reorganize_definitions/old.rs b/c2rust-refactor/tests/reorganize_definitions/old.rs index 2cea864cfd..27732b1f85 100644 --- a/c2rust-refactor/tests/reorganize_definitions/old.rs +++ b/c2rust-refactor/tests/reorganize_definitions/old.rs @@ -53,12 +53,10 @@ pub mod bar { use bar_h::bar_t; #[no_mangle] - static mut Bar: bar_t = unsafe { - bar_t { - alloc: 0 as *mut libc::c_char, - data: 0 as *mut libc::c_char, - i: 0, - } + static mut Bar: bar_t = bar_t { + alloc: 0 as *mut libc::c_char, + data: 0 as *mut libc::c_char, + i: 0, }; } @@ -106,7 +104,7 @@ pub mod foo { } unsafe fn foo() -> *const bar_t { - let c = conflicting { y: 10 }; + let _c = conflicting { y: 10 }; &Bar as *const bar_t } } From ec9b2b8b36449e7902f29b89e2e259a5cd73c636 Mon Sep 17 00:00:00 2001 From: Andrei Homescu Date: Wed, 26 Nov 2025 23:11:35 -0800 Subject: [PATCH 3/7] c2rust-refactor: tests: Add repro of statvfs issue from python2 --- .../tests/reorganize_definitions/new.rs | 51 ++++++++ .../tests/reorganize_definitions/old.rs | 123 +++++++++++++++++- 2 files changed, 173 insertions(+), 1 deletion(-) diff --git a/c2rust-refactor/tests/reorganize_definitions/new.rs b/c2rust-refactor/tests/reorganize_definitions/new.rs index 43f0985836..77cb4f244b 100644 --- a/c2rust-refactor/tests/reorganize_definitions/new.rs +++ b/c2rust-refactor/tests/reorganize_definitions/new.rs @@ -30,6 +30,26 @@ pub mod bar { use crate::outside; //test2 use libc; + // Import both the statfs64 type and function declaration from + // libc exactly as they are defined in that crate. However, since + // both definitions have a private field, the transform shouldn't + // unify them across crates. + #[repr(C)] + + pub struct statvfs { + pub f_bsize: libc::c_ulong, + pub f_frsize: libc::c_ulong, + pub f_blocks: libc::fsblkcnt_t, + pub f_bfree: libc::fsblkcnt_t, + pub f_bavail: libc::fsblkcnt_t, + pub f_files: libc::fsfilcnt_t, + pub f_ffree: libc::fsfilcnt_t, + pub f_favail: libc::fsfilcnt_t, + pub f_fsid: libc::c_ulong, + pub f_flag: libc::c_ulong, + pub f_namemax: libc::c_ulong, + __f_spare: [libc::c_int; 6], + } //test1 type OtherInt = i32; // Comment on bar_t @@ -57,8 +77,10 @@ pub mod foo { use libc; use crate::bar::bar_t; + use crate::bar::statvfs; use crate::bar::Bar; use crate::compat_h::conflicting_1; + use libc::statvfs; // Comment on foo_t @@ -70,6 +92,35 @@ pub mod foo { } unsafe fn foo() -> *const crate::bar::bar_t { + // Use the local definitions. + let mut buf = unsafe { std::mem::zeroed::() }; + ::libc::statvfs( + core::ptr::null(), + &mut buf as *mut _ as *mut ::libc::statvfs, + ); + + // Use the definitions that have all public fields. + // The transform should not reuse any of the libc declarations. + let mut buf = unsafe { std::mem::zeroed::() }; + ::libc::statvfs( + core::ptr::null(), + &mut buf as *mut _ as *mut ::libc::statvfs, + ); + + // Use the definitions that are identical to libc. + let mut buf = unsafe { std::mem::zeroed::<::libc::statfs64>() }; + ::libc::statfs64( + core::ptr::null(), + &mut buf as *mut _ as *mut ::libc::statfs64, + ); + + // Use the definitions that are identical to libc. + let mut buf = unsafe { std::mem::zeroed::<::libc::statfs64>() }; + ::libc::statfs64( + core::ptr::null(), + &mut buf as *mut _ as *mut ::libc::statfs64, + ); + let _c = crate::compat_h::conflicting_1 { y: 10 }; &crate::bar::Bar as *const crate::bar::bar_t } diff --git a/c2rust-refactor/tests/reorganize_definitions/old.rs b/c2rust-refactor/tests/reorganize_definitions/old.rs index 27732b1f85..17e6c7053f 100644 --- a/c2rust-refactor/tests/reorganize_definitions/old.rs +++ b/c2rust-refactor/tests/reorganize_definitions/old.rs @@ -40,6 +40,61 @@ pub mod bar { #[c2rust::src_loc = "8:0"] type OtherInt = i32; + // Import both the statfs64 type and function declaration from + // libc exactly as they are defined in that crate. However, since + // both definitions have a private field, the transform shouldn't + // unify them across crates. + #[repr(C)] + #[c2rust::src_loc = "7:0"] + pub struct statvfs { + pub f_bsize: libc::c_ulong, + pub f_frsize: libc::c_ulong, + pub f_blocks: libc::fsblkcnt_t, + pub f_bfree: libc::fsblkcnt_t, + pub f_bavail: libc::fsblkcnt_t, + pub f_files: libc::fsfilcnt_t, + pub f_ffree: libc::fsfilcnt_t, + pub f_favail: libc::fsfilcnt_t, + pub f_fsid: libc::c_ulong, + pub f_flag: libc::c_ulong, + pub f_namemax: libc::c_ulong, + __f_spare: [libc::c_int; 6], + } + + // Import both the statfs64 type and function declaration from + // libc exactly as they are defined in that crate, so reorganize_definitions + // replaces both of them with the libc definition. + #[repr(C)] + #[c2rust::src_loc = "6:0"] + pub struct statfs64 { + pub f_type: libc::__fsword_t, + pub f_bsize: libc::__fsword_t, + pub f_blocks: u64, + pub f_bfree: u64, + pub f_bavail: u64, + pub f_files: u64, + pub f_ffree: u64, + pub f_fsid: libc::fsid_t, + pub f_namelen: libc::__fsword_t, + pub f_frsize: libc::__fsword_t, + pub f_flags: libc::__fsword_t, + pub f_spare: [libc::__fsword_t; 4], + } + + extern "C" { + #[c2rust::src_loc = "5:0"] + pub fn statvfs( + path: *const libc::c_char, + buf: *mut statvfs, + ) -> libc::c_int; + + #[c2rust::src_loc = "4:0"] + pub fn statfs64( + path: *const libc::c_char, + buf: *mut statfs64, + ) -> libc::c_int; + } + use super::libc; } @@ -80,9 +135,58 @@ pub mod foo { } use super::libc; + // Slightly different version of the structure: all fields are public. + // This shouldn't get unified either. + #[repr(C)] + #[c2rust::src_loc = "7:0"] + pub struct statvfs { + pub f_bsize: libc::c_ulong, + pub f_frsize: libc::c_ulong, + pub f_blocks: libc::fsblkcnt_t, + pub f_bfree: libc::fsblkcnt_t, + pub f_bavail: libc::fsblkcnt_t, + pub f_files: libc::fsfilcnt_t, + pub f_ffree: libc::fsfilcnt_t, + pub f_favail: libc::fsfilcnt_t, + pub f_fsid: libc::c_ulong, + pub f_flag: libc::c_ulong, + pub f_namemax: libc::c_ulong, + pub __f_spare: [libc::c_int; 6], + } + + // This one is identical to the libc one + #[repr(C)] + #[c2rust::src_loc = "6:0"] + pub struct statfs64 { + pub f_type: libc::__fsword_t, + pub f_bsize: libc::__fsword_t, + pub f_blocks: u64, + pub f_bfree: u64, + pub f_bavail: u64, + pub f_files: u64, + pub f_ffree: u64, + pub f_fsid: libc::fsid_t, + pub f_namelen: libc::__fsword_t, + pub f_frsize: libc::__fsword_t, + pub f_flags: libc::__fsword_t, + pub f_spare: [libc::__fsword_t; 4], + } + extern "C" { // Comment on Bar pub static mut Bar: bar_t; + + #[c2rust::src_loc = "5:0"] + pub fn statvfs( + path: *const libc::c_char, + buf: *mut statvfs, + ) -> libc::c_int; + + #[c2rust::src_loc = "4:0"] + pub fn statfs64( + path: *const libc::c_char, + buf: *mut statfs64, + ) -> libc::c_int; } } @@ -92,7 +196,7 @@ pub mod foo { pub y: libc::c_char, } } - use bar_h::{Bar, bar_t}; + use bar_h::{Bar, bar_t, statvfs}; use compat_h::conflicting; // Comment on foo_t @@ -104,6 +208,23 @@ pub mod foo { } unsafe fn foo() -> *const bar_t { + // Use the local definitions. + let mut buf = unsafe { std::mem::zeroed::() }; + super::bar::bar_h::statvfs(core::ptr::null(), &mut buf); + + // Use the definitions that have all public fields. + // The transform should not reuse any of the libc declarations. + let mut buf = unsafe { std::mem::zeroed::() }; + super::foo::bar_h::statvfs(core::ptr::null(), &mut buf); + + // Use the definitions that are identical to libc. + let mut buf = unsafe { std::mem::zeroed::() }; + super::bar::bar_h::statfs64(core::ptr::null(), &mut buf); + + // Use the definitions that are identical to libc. + let mut buf = unsafe { std::mem::zeroed::() }; + super::foo::bar_h::statfs64(core::ptr::null(), &mut buf); + let _c = conflicting { y: 10 }; &Bar as *const bar_t } From 856d772613a6af06ca507c7751ccbb39b4f0ddf1 Mon Sep 17 00:00:00 2001 From: Andrei Homescu Date: Mon, 1 Dec 2025 15:54:16 -0800 Subject: [PATCH 4/7] c2rust-refactor: Check visibility when comparing structural equivalence for function signatures --- c2rust-refactor/src/context.rs | 4 ++-- .../tests/reorganize_definitions/new.rs | 15 +++++---------- .../tests/reorganize_definitions/old.rs | 2 +- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/c2rust-refactor/src/context.rs b/c2rust-refactor/src/context.rs index 9e50ddf735..bbaa3b4cfb 100644 --- a/c2rust-refactor/src/context.rs +++ b/c2rust-refactor/src/context.rs @@ -1223,14 +1223,14 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { } for (&arg_ty1, &arg_ty2) in sig1.inputs().iter().zip(sig2.inputs().iter()) { - if !self.structural_eq_tys(arg_ty1, arg_ty2) { + if !self.structural_eq_tys_with_vis(arg_ty1, arg_ty2) { return false; } } let out_ty1 = sig1.output(); let out_ty2 = sig2.output(); - self.structural_eq_tys(out_ty1, out_ty2) + self.structural_eq_tys_with_vis(out_ty1, out_ty2) } /// Compare two AST types for structural equivalence, ignoring names. diff --git a/c2rust-refactor/tests/reorganize_definitions/new.rs b/c2rust-refactor/tests/reorganize_definitions/new.rs index 77cb4f244b..0f2795b70b 100644 --- a/c2rust-refactor/tests/reorganize_definitions/new.rs +++ b/c2rust-refactor/tests/reorganize_definitions/new.rs @@ -24,6 +24,9 @@ type outside = i32; pub mod bar { + extern "C" { + pub fn statvfs(path: *const libc::c_char, buf: *mut crate::bar::statvfs) -> libc::c_int; + } // =============== BEGIN bar_h ================ // Test relative paths @@ -77,10 +80,8 @@ pub mod foo { use libc; use crate::bar::bar_t; - use crate::bar::statvfs; use crate::bar::Bar; use crate::compat_h::conflicting_1; - use libc::statvfs; // Comment on foo_t @@ -94,18 +95,12 @@ pub mod foo { unsafe fn foo() -> *const crate::bar::bar_t { // Use the local definitions. let mut buf = unsafe { std::mem::zeroed::() }; - ::libc::statvfs( - core::ptr::null(), - &mut buf as *mut _ as *mut ::libc::statvfs, - ); + crate::bar::statvfs(core::ptr::null(), &mut buf); // Use the definitions that have all public fields. // The transform should not reuse any of the libc declarations. let mut buf = unsafe { std::mem::zeroed::() }; - ::libc::statvfs( - core::ptr::null(), - &mut buf as *mut _ as *mut ::libc::statvfs, - ); + crate::bar::statvfs(core::ptr::null(), &mut buf); // Use the definitions that are identical to libc. let mut buf = unsafe { std::mem::zeroed::<::libc::statfs64>() }; diff --git a/c2rust-refactor/tests/reorganize_definitions/old.rs b/c2rust-refactor/tests/reorganize_definitions/old.rs index 17e6c7053f..bf79a22b0f 100644 --- a/c2rust-refactor/tests/reorganize_definitions/old.rs +++ b/c2rust-refactor/tests/reorganize_definitions/old.rs @@ -196,7 +196,7 @@ pub mod foo { pub y: libc::c_char, } } - use bar_h::{Bar, bar_t, statvfs}; + use bar_h::{Bar, bar_t}; use compat_h::conflicting; // Comment on foo_t From a2f854e841f106da86140cf461522da588c3c7c9 Mon Sep 17 00:00:00 2001 From: Andrei Homescu Date: Mon, 1 Dec 2025 16:18:42 -0800 Subject: [PATCH 5/7] c2rust-refactor: Deep match visibility when comparing function signatures --- c2rust-refactor/src/context.rs | 75 +++++++++++++++---- .../src/transform/reorganize_definitions.rs | 4 +- .../tests/reorganize_definitions/new.rs | 20 ++++- 3 files changed, 81 insertions(+), 18 deletions(-) diff --git a/c2rust-refactor/src/context.rs b/c2rust-refactor/src/context.rs index bbaa3b4cfb..e80b98c329 100644 --- a/c2rust-refactor/src/context.rs +++ b/c2rust-refactor/src/context.rs @@ -788,9 +788,12 @@ impl<'a, 'tcx> RefactorCtxt<'a, 'tcx> { Some(path) } - /// Compare two items for type compatibility under the C definition - pub fn compatible_types(&self, item1: &Item, item2: &Item) -> bool { - TypeCompare::new(self).compatible_types(item1, item2) + /// Compare two items for type compatibility under the C definition. + /// + /// If `match_vis` is `true`, the visibility of all structure/enum/union + /// fields (i.e. if they are `pub`) must match between the two items. + pub fn compatible_types(&self, item1: &Item, item2: &Item, match_vis: bool) -> bool { + TypeCompare::new(self).compatible_types(item1, item2, match_vis) } /// Compare two function declarations for equivalent argument and return types, @@ -1062,8 +1065,11 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { } } - /// Compare two items for type compatibility under the C definition - pub fn compatible_types(&self, item1: &Item, item2: &Item) -> bool { + /// Compare two items for type compatibility under the C definition. + /// + /// If `match_vis` is `true`, the visibility of all structure/enum/union + /// fields (i.e. if they are `pub`) must match between the two items. + pub fn compatible_types(&self, item1: &Item, item2: &Item, match_vis: bool) -> bool { use rustc_ast::ItemKind::*; match (&item1.kind, &item2.kind) { // * Assure that these two items are in fact of the same type, just to be safe. @@ -1072,7 +1078,13 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { self.cx.opt_node_type(item1.id), self.cx.opt_node_type(item2.id), ) { - (Some(ty1), Some(ty2)) => self.structural_eq_tys(ty1, ty2), + (Some(ty1), Some(ty2)) => { + if match_vis { + self.structural_eq_tys_with_vis(ty1, ty2) + } else { + self.structural_eq_tys(ty1, ty2) + } + } _ => { // TODO: handle type aliases in traits; for now we don't // care about them because C2Rust does not emit traits @@ -1081,7 +1093,7 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { if ta1.generics.params.is_empty() && ta2.generics.params.is_empty() { // TODO: compare the other fields - self.structural_eq_ast_tys(ty1, ty2) + self.structural_eq_ast_tys(ty1, ty2, match_vis) && ta1.defaultness.unnamed_equiv(&ta2.defaultness) } else { // FIXME: handle generics (we don't need to for now) @@ -1102,7 +1114,7 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { && def1.unnamed_equiv(def2) } _ => { - self.structural_eq_ast_tys(ty1, ty2) + self.structural_eq_ast_tys(ty1, ty2, match_vis) && expr1.unnamed_equiv(expr2) && def1.unnamed_equiv(def2) } @@ -1139,11 +1151,23 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { self.cx.opt_node_type(item1.id), self.cx.opt_node_type(item2.id), ) { - (Some(ty1), Some(ty2)) => self.structural_eq_tys(ty1, ty2), + (Some(ty1), Some(ty2)) => { + if match_vis { + self.structural_eq_tys_with_vis(ty1, ty2) + } else { + self.structural_eq_tys(ty1, ty2) + } + } _ => { let mut fields = variant1.fields().iter().zip(variant2.fields().iter()); fields.all(|(field1, field2)| { - self.structural_eq_ast_tys(&field1.ty, &field2.ty) + // TODO: either Visibility or VisibilityKind should implement + // PartialEq; until then, the closest we have is `is_pub` + if match_vis && field1.vis.kind.is_pub() != field2.vis.kind.is_pub() { + return false; + } + + self.structural_eq_ast_tys(&field1.ty, &field2.ty, match_vis) }) } } @@ -1159,6 +1183,10 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { .zip(variant2.data.fields().iter()) }); fields.all(|(field1, field2)| { + if match_vis && field1.vis.kind.is_pub() != field2.vis.kind.is_pub() { + return false; + } + match ( self.cx.opt_node_type(field1.id), self.cx.opt_node_type(field2.id), @@ -1177,7 +1205,13 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { self.cx.opt_node_type(item1.id), self.cx.opt_node_type(item2.id), ) { - (Some(ty1), Some(ty2)) => return self.structural_eq_tys(ty1, ty2), + (Some(ty1), Some(ty2)) => { + if match_vis { + return self.structural_eq_tys_with_vis(ty1, ty2); + } else { + return self.structural_eq_tys(ty1, ty2); + } + } _ => {} } } @@ -1192,7 +1226,7 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { /// ignoring argument names. pub fn compatible_fn_prototypes(&self, decl1: &FnDecl, decl2: &FnDecl) -> bool { let mut args = decl1.inputs.iter().zip(decl2.inputs.iter()); - if !args.all(|(arg1, arg2)| self.structural_eq_ast_tys(&arg1.ty, &arg2.ty)) { + if !args.all(|(arg1, arg2)| self.structural_eq_ast_tys(&arg1.ty, &arg2.ty, true)) { return false; } @@ -1208,7 +1242,7 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { FnRetTy::Ty(ty) => &ty, }; - self.structural_eq_ast_tys(ty1, ty2) + self.structural_eq_ast_tys(ty1, ty2, true) } /// Compare two ty function signatures for equivalent argument and return @@ -1234,13 +1268,24 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { } /// Compare two AST types for structural equivalence, ignoring names. - fn structural_eq_ast_tys(&self, ty1: &rustc_ast::Ty, ty2: &rustc_ast::Ty) -> bool { + /// + /// If `match_vis` is `true`, the visibility of all structure/enum/union + /// fields (i.e. if they are `pub`) must match between the two types. + fn structural_eq_ast_tys( + &self, + ty1: &rustc_ast::Ty, + ty2: &rustc_ast::Ty, + match_vis: bool, + ) -> bool { match (self.cx.opt_node_type(ty1.id), self.cx.opt_node_type(ty2.id)) { + (Some(ty1), Some(ty2)) if match_vis => { + return self.structural_eq_tys_with_vis(ty1, ty2) + } (Some(ty1), Some(ty2)) => return self.structural_eq_tys(ty1, ty2), _ => {} } match (self.cx.try_resolve_ty(ty1), self.cx.try_resolve_ty(ty2)) { - (Some(did1), Some(did2)) => self.structural_eq_defs(did1, did2, false), + (Some(did1), Some(did2)) => self.structural_eq_defs(did1, did2, match_vis), _ => ty1.unnamed_equiv(ty2), } } diff --git a/c2rust-refactor/src/transform/reorganize_definitions.rs b/c2rust-refactor/src/transform/reorganize_definitions.rs index 6c996634c8..e492ae07e4 100644 --- a/c2rust-refactor/src/transform/reorganize_definitions.rs +++ b/c2rust-refactor/src/transform/reorganize_definitions.rs @@ -361,7 +361,7 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> { let decl_ids = declarations.remove_matching_defs(ns, item.ident, |decl| { match decl { - DeclKind::Item(decl) => self.cx.compatible_types(&decl, item), + DeclKind::Item(decl) => self.cx.compatible_types(&decl, item, true), DeclKind::ForeignItem(foreign, _) => foreign_equiv(&foreign, item), } }); @@ -1547,7 +1547,7 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> { // Otherwise make sure these items are structurally // equivalent. _ => { - if self.cx.compatible_types(&item, &existing_item) { + if self.cx.compatible_types(&item, &existing_item, true) { return ContainsDecl::Equivalent(existing_decl); } } diff --git a/c2rust-refactor/tests/reorganize_definitions/new.rs b/c2rust-refactor/tests/reorganize_definitions/new.rs index 0f2795b70b..9c364b1d0f 100644 --- a/c2rust-refactor/tests/reorganize_definitions/new.rs +++ b/c2rust-refactor/tests/reorganize_definitions/new.rs @@ -53,6 +53,24 @@ pub mod bar { pub f_namemax: libc::c_ulong, __f_spare: [libc::c_int; 6], } + // Slightly different version of the structure: all fields are public. + // This shouldn't get unified either. + #[repr(C)] + + pub struct statvfs_1 { + pub f_bsize: libc::c_ulong, + pub f_frsize: libc::c_ulong, + pub f_blocks: libc::fsblkcnt_t, + pub f_bfree: libc::fsblkcnt_t, + pub f_bavail: libc::fsblkcnt_t, + pub f_files: libc::fsfilcnt_t, + pub f_ffree: libc::fsfilcnt_t, + pub f_favail: libc::fsfilcnt_t, + pub f_fsid: libc::c_ulong, + pub f_flag: libc::c_ulong, + pub f_namemax: libc::c_ulong, + pub __f_spare: [libc::c_int; 6], + } //test1 type OtherInt = i32; // Comment on bar_t @@ -99,7 +117,7 @@ pub mod foo { // Use the definitions that have all public fields. // The transform should not reuse any of the libc declarations. - let mut buf = unsafe { std::mem::zeroed::() }; + let mut buf = unsafe { std::mem::zeroed::() }; crate::bar::statvfs(core::ptr::null(), &mut buf); // Use the definitions that are identical to libc. From e1fbe803c4e579ffa6e4c6ec2f5f6062d72dec06 Mon Sep 17 00:00:00 2001 From: Andrei Homescu Date: Mon, 1 Dec 2025 22:34:53 -0800 Subject: [PATCH 6/7] c2rust-refactor: Handle different number of fields between structures/unions in compatible_types --- c2rust-refactor/src/context.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/c2rust-refactor/src/context.rs b/c2rust-refactor/src/context.rs index e80b98c329..49fb8ab6ed 100644 --- a/c2rust-refactor/src/context.rs +++ b/c2rust-refactor/src/context.rs @@ -1158,7 +1158,11 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { self.structural_eq_tys(ty1, ty2) } } - _ => { + _ => 'match_fields: { + if variant1.fields().len() != variant2.fields().len() { + break 'match_fields false; + } + let mut fields = variant1.fields().iter().zip(variant2.fields().iter()); fields.all(|(field1, field2)| { // TODO: either Visibility or VisibilityKind should implement From af686483b35da5fc792e245002868a6ce3c9c7c5 Mon Sep 17 00:00:00 2001 From: Khyber Sen Date: Mon, 8 Dec 2025 04:02:11 -0800 Subject: [PATCH 7/7] refactor: use "struct" instead of "structure" in comments --- c2rust-refactor/src/context.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/c2rust-refactor/src/context.rs b/c2rust-refactor/src/context.rs index 49fb8ab6ed..3d0ced31bf 100644 --- a/c2rust-refactor/src/context.rs +++ b/c2rust-refactor/src/context.rs @@ -790,7 +790,7 @@ impl<'a, 'tcx> RefactorCtxt<'a, 'tcx> { /// Compare two items for type compatibility under the C definition. /// - /// If `match_vis` is `true`, the visibility of all structure/enum/union + /// If `match_vis` is `true`, the visibility of all `struct`/`enum`/`union` /// fields (i.e. if they are `pub`) must match between the two items. pub fn compatible_types(&self, item1: &Item, item2: &Item, match_vis: bool) -> bool { TypeCompare::new(self).compatible_types(item1, item2, match_vis) @@ -1067,7 +1067,7 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { /// Compare two items for type compatibility under the C definition. /// - /// If `match_vis` is `true`, the visibility of all structure/enum/union + /// If `match_vis` is `true`, the visibility of all `struct`/`enum`/`union` /// fields (i.e. if they are `pub`) must match between the two items. pub fn compatible_types(&self, item1: &Item, item2: &Item, match_vis: bool) -> bool { use rustc_ast::ItemKind::*; @@ -1273,7 +1273,7 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> { /// Compare two AST types for structural equivalence, ignoring names. /// - /// If `match_vis` is `true`, the visibility of all structure/enum/union + /// If `match_vis` is `true`, the visibility of all `struct`/`enum`/`union` /// fields (i.e. if they are `pub`) must match between the two types. fn structural_eq_ast_tys( &self,