Skip to content

Commit afef0e8

Browse files
authored
refactor: Update reorganize_definitions test and fix some issues (#1484)
* Fix reorganize_definitions test: the test (and probably all the refactoring tests) has bitrotted; get it working again * Add repro of statvfs issue from python2: reproduce the issue encountered by the python2 test on Ubuntu 22.04 where the `statvfs` structure has a private field that we cannot use * Check visibility when comparing structural equivalence for function signatures * Deep match visibility when comparing function signatures: updates to the algorithm we use to match equivalent types and function signatures. * Enable more transforms on the python2 integration test. When matching between two types to determine whether they can be unified, we now account for differences in visibility because importing a structure with private types from an external crate (e.g. `libc`) makes them unusable to us when trying to construct new values of those types. The motivating example is `statvfs`: ```rust pub struct statvfs { pub f_bsize: c_ulong, pub f_frsize: c_ulong, pub f_blocks: crate::fsblkcnt_t, pub f_bfree: crate::fsblkcnt_t, pub f_bavail: crate::fsblkcnt_t, pub f_files: crate::fsfilcnt_t, pub f_ffree: crate::fsfilcnt_t, pub f_favail: crate::fsfilcnt_t, pub f_fsid: c_ulong, pub f_flag: c_ulong, pub f_namemax: c_ulong, __f_spare: [c_int; 6], } ```
2 parents 31dcd11 + af68648 commit afef0e8

File tree

4 files changed

+274
-40
lines changed

4 files changed

+274
-40
lines changed

c2rust-refactor/src/context.rs

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -788,9 +788,12 @@ impl<'a, 'tcx> RefactorCtxt<'a, 'tcx> {
788788
Some(path)
789789
}
790790

791-
/// Compare two items for type compatibility under the C definition
792-
pub fn compatible_types(&self, item1: &Item, item2: &Item) -> bool {
793-
TypeCompare::new(self).compatible_types(item1, item2)
791+
/// Compare two items for type compatibility under the C definition.
792+
///
793+
/// If `match_vis` is `true`, the visibility of all `struct`/`enum`/`union`
794+
/// fields (i.e. if they are `pub`) must match between the two items.
795+
pub fn compatible_types(&self, item1: &Item, item2: &Item, match_vis: bool) -> bool {
796+
TypeCompare::new(self).compatible_types(item1, item2, match_vis)
794797
}
795798

796799
/// Compare two function declarations for equivalent argument and return types,
@@ -1062,8 +1065,11 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> {
10621065
}
10631066
}
10641067

1065-
/// Compare two items for type compatibility under the C definition
1066-
pub fn compatible_types(&self, item1: &Item, item2: &Item) -> bool {
1068+
/// Compare two items for type compatibility under the C definition.
1069+
///
1070+
/// If `match_vis` is `true`, the visibility of all `struct`/`enum`/`union`
1071+
/// fields (i.e. if they are `pub`) must match between the two items.
1072+
pub fn compatible_types(&self, item1: &Item, item2: &Item, match_vis: bool) -> bool {
10671073
use rustc_ast::ItemKind::*;
10681074
match (&item1.kind, &item2.kind) {
10691075
// * 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> {
10721078
self.cx.opt_node_type(item1.id),
10731079
self.cx.opt_node_type(item2.id),
10741080
) {
1075-
(Some(ty1), Some(ty2)) => self.structural_eq_tys(ty1, ty2),
1081+
(Some(ty1), Some(ty2)) => {
1082+
if match_vis {
1083+
self.structural_eq_tys_with_vis(ty1, ty2)
1084+
} else {
1085+
self.structural_eq_tys(ty1, ty2)
1086+
}
1087+
}
10761088
_ => {
10771089
// TODO: handle type aliases in traits; for now we don't
10781090
// care about them because C2Rust does not emit traits
@@ -1081,7 +1093,7 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> {
10811093

10821094
if ta1.generics.params.is_empty() && ta2.generics.params.is_empty() {
10831095
// TODO: compare the other fields
1084-
self.structural_eq_ast_tys(ty1, ty2)
1096+
self.structural_eq_ast_tys(ty1, ty2, match_vis)
10851097
&& ta1.defaultness.unnamed_equiv(&ta2.defaultness)
10861098
} else {
10871099
// FIXME: handle generics (we don't need to for now)
@@ -1102,7 +1114,7 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> {
11021114
&& def1.unnamed_equiv(def2)
11031115
}
11041116
_ => {
1105-
self.structural_eq_ast_tys(ty1, ty2)
1117+
self.structural_eq_ast_tys(ty1, ty2, match_vis)
11061118
&& expr1.unnamed_equiv(expr2)
11071119
&& def1.unnamed_equiv(def2)
11081120
}
@@ -1139,11 +1151,27 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> {
11391151
self.cx.opt_node_type(item1.id),
11401152
self.cx.opt_node_type(item2.id),
11411153
) {
1142-
(Some(ty1), Some(ty2)) => self.structural_eq_tys(ty1, ty2),
1143-
_ => {
1154+
(Some(ty1), Some(ty2)) => {
1155+
if match_vis {
1156+
self.structural_eq_tys_with_vis(ty1, ty2)
1157+
} else {
1158+
self.structural_eq_tys(ty1, ty2)
1159+
}
1160+
}
1161+
_ => 'match_fields: {
1162+
if variant1.fields().len() != variant2.fields().len() {
1163+
break 'match_fields false;
1164+
}
1165+
11441166
let mut fields = variant1.fields().iter().zip(variant2.fields().iter());
11451167
fields.all(|(field1, field2)| {
1146-
self.structural_eq_ast_tys(&field1.ty, &field2.ty)
1168+
// TODO: either Visibility or VisibilityKind should implement
1169+
// PartialEq; until then, the closest we have is `is_pub`
1170+
if match_vis && field1.vis.kind.is_pub() != field2.vis.kind.is_pub() {
1171+
return false;
1172+
}
1173+
1174+
self.structural_eq_ast_tys(&field1.ty, &field2.ty, match_vis)
11471175
})
11481176
}
11491177
}
@@ -1159,6 +1187,10 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> {
11591187
.zip(variant2.data.fields().iter())
11601188
});
11611189
fields.all(|(field1, field2)| {
1190+
if match_vis && field1.vis.kind.is_pub() != field2.vis.kind.is_pub() {
1191+
return false;
1192+
}
1193+
11621194
match (
11631195
self.cx.opt_node_type(field1.id),
11641196
self.cx.opt_node_type(field2.id),
@@ -1177,7 +1209,13 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> {
11771209
self.cx.opt_node_type(item1.id),
11781210
self.cx.opt_node_type(item2.id),
11791211
) {
1180-
(Some(ty1), Some(ty2)) => return self.structural_eq_tys(ty1, ty2),
1212+
(Some(ty1), Some(ty2)) => {
1213+
if match_vis {
1214+
return self.structural_eq_tys_with_vis(ty1, ty2);
1215+
} else {
1216+
return self.structural_eq_tys(ty1, ty2);
1217+
}
1218+
}
11811219
_ => {}
11821220
}
11831221
}
@@ -1192,7 +1230,7 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> {
11921230
/// ignoring argument names.
11931231
pub fn compatible_fn_prototypes(&self, decl1: &FnDecl, decl2: &FnDecl) -> bool {
11941232
let mut args = decl1.inputs.iter().zip(decl2.inputs.iter());
1195-
if !args.all(|(arg1, arg2)| self.structural_eq_ast_tys(&arg1.ty, &arg2.ty)) {
1233+
if !args.all(|(arg1, arg2)| self.structural_eq_ast_tys(&arg1.ty, &arg2.ty, true)) {
11961234
return false;
11971235
}
11981236

@@ -1208,7 +1246,7 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> {
12081246
FnRetTy::Ty(ty) => &ty,
12091247
};
12101248

1211-
self.structural_eq_ast_tys(ty1, ty2)
1249+
self.structural_eq_ast_tys(ty1, ty2, true)
12121250
}
12131251

12141252
/// Compare two ty function signatures for equivalent argument and return
@@ -1223,24 +1261,35 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> {
12231261
}
12241262

12251263
for (&arg_ty1, &arg_ty2) in sig1.inputs().iter().zip(sig2.inputs().iter()) {
1226-
if !self.structural_eq_tys(arg_ty1, arg_ty2) {
1264+
if !self.structural_eq_tys_with_vis(arg_ty1, arg_ty2) {
12271265
return false;
12281266
}
12291267
}
12301268

12311269
let out_ty1 = sig1.output();
12321270
let out_ty2 = sig2.output();
1233-
self.structural_eq_tys(out_ty1, out_ty2)
1271+
self.structural_eq_tys_with_vis(out_ty1, out_ty2)
12341272
}
12351273

12361274
/// Compare two AST types for structural equivalence, ignoring names.
1237-
fn structural_eq_ast_tys(&self, ty1: &rustc_ast::Ty, ty2: &rustc_ast::Ty) -> bool {
1275+
///
1276+
/// If `match_vis` is `true`, the visibility of all `struct`/`enum`/`union`
1277+
/// fields (i.e. if they are `pub`) must match between the two types.
1278+
fn structural_eq_ast_tys(
1279+
&self,
1280+
ty1: &rustc_ast::Ty,
1281+
ty2: &rustc_ast::Ty,
1282+
match_vis: bool,
1283+
) -> bool {
12381284
match (self.cx.opt_node_type(ty1.id), self.cx.opt_node_type(ty2.id)) {
1285+
(Some(ty1), Some(ty2)) if match_vis => {
1286+
return self.structural_eq_tys_with_vis(ty1, ty2)
1287+
}
12391288
(Some(ty1), Some(ty2)) => return self.structural_eq_tys(ty1, ty2),
12401289
_ => {}
12411290
}
12421291
match (self.cx.try_resolve_ty(ty1), self.cx.try_resolve_ty(ty2)) {
1243-
(Some(did1), Some(did2)) => self.structural_eq_defs(did1, did2, false),
1292+
(Some(did1), Some(did2)) => self.structural_eq_defs(did1, did2, match_vis),
12441293
_ => ty1.unnamed_equiv(ty2),
12451294
}
12461295
}

c2rust-refactor/src/transform/reorganize_definitions.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> {
361361

362362
let decl_ids = declarations.remove_matching_defs(ns, item.ident, |decl| {
363363
match decl {
364-
DeclKind::Item(decl) => self.cx.compatible_types(&decl, item),
364+
DeclKind::Item(decl) => self.cx.compatible_types(&decl, item, true),
365365
DeclKind::ForeignItem(foreign, _) => foreign_equiv(&foreign, item),
366366
}
367367
});
@@ -1547,7 +1547,7 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> {
15471547
// Otherwise make sure these items are structurally
15481548
// equivalent.
15491549
_ => {
1550-
if self.cx.compatible_types(&item, &existing_item) {
1550+
if self.cx.compatible_types(&item, &existing_item, true) {
15511551
return ContainsDecl::Equivalent(existing_decl);
15521552
}
15531553
}

c2rust-refactor/tests/reorganize_definitions/new.rs

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![feature(extern_types)]
22
#![feature(rustc_private)]
3+
#![feature(register_tool)]
34
#![register_tool(c2rust)]
45
#![allow(non_upper_case_globals)]
56
#![allow(non_camel_case_types)]
@@ -8,27 +9,68 @@
89
#![allow(mutable_transmutes)]
910
#![allow(unused_mut)]
1011

11-
pub mod compat_h_0 {
12-
pub struct conflicting {
13-
pub y: libc::c_char,
14-
}
15-
}
1612
pub mod compat_h {
1713
pub struct conflicting {
1814
pub x: libc::c_char,
1915
}
16+
17+
pub struct conflicting_1 {
18+
pub y: libc::c_char,
19+
}
2020
}
21+
extern crate libc;
2122

2223
type outside = i32;
2324

2425
pub mod bar {
2526

27+
extern "C" {
28+
pub fn statvfs(path: *const libc::c_char, buf: *mut crate::bar::statvfs) -> libc::c_int;
29+
}
2630
// =============== BEGIN bar_h ================
2731

2832
// Test relative paths
2933
use crate::outside;
3034
//test2
3135
use libc;
36+
// Import both the statfs64 type and function declaration from
37+
// libc exactly as they are defined in that crate. However, since
38+
// both definitions have a private field, the transform shouldn't
39+
// unify them across crates.
40+
#[repr(C)]
41+
42+
pub struct statvfs {
43+
pub f_bsize: libc::c_ulong,
44+
pub f_frsize: libc::c_ulong,
45+
pub f_blocks: libc::fsblkcnt_t,
46+
pub f_bfree: libc::fsblkcnt_t,
47+
pub f_bavail: libc::fsblkcnt_t,
48+
pub f_files: libc::fsfilcnt_t,
49+
pub f_ffree: libc::fsfilcnt_t,
50+
pub f_favail: libc::fsfilcnt_t,
51+
pub f_fsid: libc::c_ulong,
52+
pub f_flag: libc::c_ulong,
53+
pub f_namemax: libc::c_ulong,
54+
__f_spare: [libc::c_int; 6],
55+
}
56+
// Slightly different version of the structure: all fields are public.
57+
// This shouldn't get unified either.
58+
#[repr(C)]
59+
60+
pub struct statvfs_1 {
61+
pub f_bsize: libc::c_ulong,
62+
pub f_frsize: libc::c_ulong,
63+
pub f_blocks: libc::fsblkcnt_t,
64+
pub f_bfree: libc::fsblkcnt_t,
65+
pub f_bavail: libc::fsblkcnt_t,
66+
pub f_files: libc::fsfilcnt_t,
67+
pub f_ffree: libc::fsfilcnt_t,
68+
pub f_favail: libc::fsfilcnt_t,
69+
pub f_fsid: libc::c_ulong,
70+
pub f_flag: libc::c_ulong,
71+
pub f_namemax: libc::c_ulong,
72+
pub __f_spare: [libc::c_int; 6],
73+
}
3274
//test1
3375
type OtherInt = i32;
3476
// Comment on bar_t
@@ -45,12 +87,10 @@ pub mod bar {
4587
type FooInt = i32;
4688

4789
#[no_mangle]
48-
static mut Bar: crate::bar::bar_t = unsafe {
49-
crate::bar::bar_t {
50-
alloc: 0 as *mut libc::c_char,
51-
data: 0 as *mut libc::c_char,
52-
i: 0,
53-
}
90+
static mut Bar: crate::bar::bar_t = crate::bar::bar_t {
91+
alloc: 0 as *mut libc::c_char,
92+
data: 0 as *mut libc::c_char,
93+
i: 0,
5494
};
5595
}
5696

@@ -59,7 +99,7 @@ pub mod foo {
5999

60100
use crate::bar::bar_t;
61101
use crate::bar::Bar;
62-
use crate::compat_h_0::conflicting;
102+
use crate::compat_h::conflicting_1;
63103

64104
// Comment on foo_t
65105

@@ -71,7 +111,30 @@ pub mod foo {
71111
}
72112

73113
unsafe fn foo() -> *const crate::bar::bar_t {
74-
let c = crate::compat_h_0::conflicting { y: 10 };
114+
// Use the local definitions.
115+
let mut buf = unsafe { std::mem::zeroed::<crate::bar::statvfs>() };
116+
crate::bar::statvfs(core::ptr::null(), &mut buf);
117+
118+
// Use the definitions that have all public fields.
119+
// The transform should not reuse any of the libc declarations.
120+
let mut buf = unsafe { std::mem::zeroed::<crate::bar::statvfs_1>() };
121+
crate::bar::statvfs(core::ptr::null(), &mut buf);
122+
123+
// Use the definitions that are identical to libc.
124+
let mut buf = unsafe { std::mem::zeroed::<::libc::statfs64>() };
125+
::libc::statfs64(
126+
core::ptr::null(),
127+
&mut buf as *mut _ as *mut ::libc::statfs64,
128+
);
129+
130+
// Use the definitions that are identical to libc.
131+
let mut buf = unsafe { std::mem::zeroed::<::libc::statfs64>() };
132+
::libc::statfs64(
133+
core::ptr::null(),
134+
&mut buf as *mut _ as *mut ::libc::statfs64,
135+
);
136+
137+
let _c = crate::compat_h::conflicting_1 { y: 10 };
75138
&crate::bar::Bar as *const crate::bar::bar_t
76139
}
77140
}

0 commit comments

Comments
 (0)