Skip to content

Commit 49fe72a

Browse files
committed
refactor: Support associated constants in reorganize_definitions
1 parent 6c10307 commit 49fe72a

4 files changed

Lines changed: 298 additions & 24 deletions

File tree

c2rust-refactor/src/context.rs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use std::ops::Deref;
44

55
use rustc_ast::ptr::P;
66
use rustc_ast::{
7-
Expr, ExprKind, FnDecl, FnRetTy, ForeignItem, ForeignItemKind, Item, ItemKind, NodeId, Path,
8-
QSelf, UseTreeKind, DUMMY_NODE_ID,
7+
AssocItem, Expr, ExprKind, FnDecl, FnRetTy, ForeignItem, ForeignItemKind, Item, ItemKind,
8+
NodeId, Path, QSelf, UseTreeKind, DUMMY_NODE_ID,
99
};
1010
use rustc_data_structures::fx::FxHashMap;
1111
use rustc_errors::{DiagnosticBuilder, Level};
@@ -1072,6 +1072,16 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> {
10721072
pub fn compatible_types(&self, item1: &Item, item2: &Item, match_vis: bool) -> bool {
10731073
use rustc_ast::ItemKind::*;
10741074
match (&item1.kind, &item2.kind) {
1075+
(Impl(box ref impl1), Impl(box ref impl2)) => {
1076+
if impl1.items.len() != impl2.items.len() {
1077+
return false;
1078+
}
1079+
1080+
(impl1.items.iter())
1081+
.zip(impl2.items.iter())
1082+
.all(|(item1, item2)| self.compatible_assoc_items(item1, item2, match_vis))
1083+
}
1084+
10751085
// * Assure that these two items are in fact of the same type, just to be safe.
10761086
(TyAlias(box ref ta1), TyAlias(box ref ta2)) => {
10771087
match (
@@ -1226,6 +1236,40 @@ impl<'a, 'tcx, 'b> TypeCompare<'a, 'tcx, 'b> {
12261236
}
12271237
}
12281238

1239+
pub fn compatible_assoc_items(
1240+
&self,
1241+
item1: &AssocItem,
1242+
item2: &AssocItem,
1243+
match_vis: bool,
1244+
) -> bool {
1245+
use rustc_ast::AssocItemKind::*;
1246+
1247+
// Unlike for regular items, associated items must also match by name.
1248+
if item1.ident.as_str() != item2.ident.as_str() {
1249+
return false;
1250+
}
1251+
1252+
match (&item1.kind, &item2.kind) {
1253+
(Const(def1, ty1, expr1), Const(def2, ty2, expr2)) => match (
1254+
self.cx.opt_node_type(item1.id),
1255+
self.cx.opt_node_type(item2.id),
1256+
) {
1257+
(Some(ty1), Some(ty2)) => {
1258+
self.structural_eq_tys(ty1, ty2)
1259+
&& expr1.unnamed_equiv(expr2)
1260+
&& def1.unnamed_equiv(def2)
1261+
}
1262+
_ => {
1263+
self.structural_eq_ast_tys(ty1, ty2, match_vis)
1264+
&& expr1.unnamed_equiv(expr2)
1265+
&& def1.unnamed_equiv(def2)
1266+
}
1267+
},
1268+
1269+
_ => false,
1270+
}
1271+
}
1272+
12291273
/// Compare two function declarations for equivalent argument and return types,
12301274
/// ignoring argument names.
12311275
pub fn compatible_fn_prototypes(&self, decl1: &FnDecl, decl2: &FnDecl) -> bool {

c2rust-refactor/src/transform/reorganize_definitions.rs

Lines changed: 163 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,67 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> {
287287
keep_items
288288
}
289289

290+
// First, remove and store `impl` items, indexing them by the type they belong to.
291+
let mut impls: HashMap<DefId, MovedDeclImpl> = HashMap::new();
292+
FlatMapNodes::visit(krate, |mut item: P<Item>| {
293+
if let Some((path, _)) = parse_source_header(&item.attrs) {
294+
let header_item = item.clone();
295+
if let ItemKind::Mod(_, ModKind::Loaded(ref mut mod_items, _, _)) = &mut item.kind {
296+
mod_items.retain(|item| {
297+
if let ItemKind::Impl(impl_) = &item.kind {
298+
// Only keep `impl` items with simple path types, and only if they
299+
// contain nothing but `const` items.
300+
fn impl_type_def_id(cx: &RefactorCtxt, impl_: &Impl) -> Option<DefId> {
301+
// Only inherent `impl`s that contain no items other than `const`.
302+
if impl_.of_trait.is_some()
303+
|| !impl_
304+
.items
305+
.iter()
306+
.all(|item| matches!(item.kind, AssocItemKind::Const(..)))
307+
{
308+
return None;
309+
};
310+
let ty = match cx.hir_map().find(impl_.self_ty.id)? {
311+
hir::Node::Ty(ty) => ty,
312+
_ => return None,
313+
};
314+
let ty_path = match &ty.kind {
315+
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => path,
316+
_ => return None,
317+
};
318+
match ty_path.res {
319+
Res::Def(_, def_id) => Some(def_id),
320+
_ => None,
321+
}
322+
}
323+
324+
if let Some(impl_type_def_id) = impl_type_def_id(&self.cx, &impl_) {
325+
impls.entry(impl_type_def_id).or_insert_with(|| {
326+
let parent_header =
327+
HeaderInfo::new(header_item.ident, path.clone());
328+
MovedDeclImpl {
329+
item: item.clone(),
330+
parent_header,
331+
}
332+
});
333+
}
334+
335+
false
336+
} else {
337+
true
338+
}
339+
});
340+
341+
smallvec![item]
342+
} else {
343+
panic!("Unexpected Item kind with header_src attribute");
344+
}
345+
} else {
346+
smallvec![item]
347+
}
348+
});
349+
350+
// Process all the remaining items.
290351
let mut declarations = HeaderDeclarations::new(self.cx);
291352
FlatMapNodes::visit(krate, |mut item: P<Item>| {
292353
if let Some((path, _)) = parse_source_header(&item.attrs) {
@@ -321,8 +382,13 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> {
321382
}
322383
}
323384

385+
let new_def_id = self.cx.node_def_id(item.id);
324386
let header_info = HeaderInfo::new(header_item.ident, path.clone());
325-
let inserted = declarations.insert_item(item.clone(), header_info);
387+
let inserted = declarations.insert_item(
388+
item.clone(),
389+
header_info,
390+
impls.remove(&new_def_id),
391+
);
326392
// Keep the item if we are not collapsing it
327393
!inserted
328394
});
@@ -676,7 +742,7 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> {
676742
} else {
677743
let namespace = self.cx.item_namespace(&item);
678744
if let Some(namespace) = namespace {
679-
match declarations.find_item(item, namespace) {
745+
match declarations.find_item(item, namespace, None) {
680746
ContainsDecl::NotContained => false,
681747
ContainsDecl::Equivalent(_) => true,
682748
ContainsDecl::Definition(_) => true,
@@ -750,6 +816,13 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> {
750816
// Remove src_loc attributes
751817
FlatMapNodes::visit(krate, |mut item: P<Item>| {
752818
item.attrs.retain(|attr| !is_c2rust_attr(attr, "src_loc"));
819+
820+
if let ItemKind::Impl(impl_) = &mut item.kind {
821+
for item in &mut impl_.items {
822+
item.attrs.retain(|attr| !is_c2rust_attr(attr, "src_loc"));
823+
}
824+
}
825+
753826
smallvec![item]
754827
});
755828
FlatMapNodes::visit(krate, |mut item: P<ForeignItem>| {
@@ -1085,10 +1158,23 @@ struct MovedDecl {
10851158
namespace: Namespace,
10861159
loc: Option<SrcLoc>,
10871160
parent_header: HeaderInfo,
1161+
impl_: Option<MovedDeclImpl>,
1162+
}
1163+
1164+
#[derive(Debug, Clone)]
1165+
struct MovedDeclImpl {
1166+
item: P<Item>,
1167+
parent_header: HeaderInfo,
10881168
}
10891169

10901170
impl MovedDecl {
1091-
fn new<T>(decl: T, def_id: DefId, namespace: Namespace, parent_header: HeaderInfo) -> Self
1171+
fn new<T>(
1172+
decl: T,
1173+
def_id: DefId,
1174+
namespace: Namespace,
1175+
parent_header: HeaderInfo,
1176+
impl_: Option<MovedDeclImpl>,
1177+
) -> Self
10921178
where
10931179
T: Into<DeclKind>,
10941180
{
@@ -1105,6 +1191,7 @@ impl MovedDecl {
11051191
namespace,
11061192
loc,
11071193
parent_header,
1194+
impl_,
11081195
}
11091196
}
11101197

@@ -1291,14 +1378,12 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> {
12911378

12921379
/// Add an item into the module. If it has a name conflict with an existing
12931380
/// item, choose the definition item over any declarations.
1294-
pub fn insert_item(&mut self, mut item: P<Item>, parent_header: HeaderInfo) -> bool {
1295-
let namespace = self.cx.item_namespace(&item);
1296-
let new_def_id = self.cx.node_def_id(item.id);
1297-
let ident = if let ItemKind::Use(tree) = &item.kind {
1298-
tree.ident()
1299-
} else {
1300-
item.ident
1301-
};
1381+
pub fn insert_item(
1382+
&mut self,
1383+
mut item: P<Item>,
1384+
parent_header: HeaderInfo,
1385+
impl_: Option<MovedDeclImpl>,
1386+
) -> bool {
13021387
match &item.kind {
13031388
// We have to disambiguate anonymous items by contents,
13041389
// since we don't have a proper Ident.
@@ -1307,7 +1392,7 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> {
13071392
// ident_map.
13081393
ItemKind::Use(tree) if is_nested(tree) => {
13091394
for u in split_uses(item).into_iter() {
1310-
self.insert_item(u, parent_header.clone());
1395+
self.insert_item(u, parent_header.clone(), impl_.clone());
13111396
}
13121397
true
13131398
}
@@ -1335,11 +1420,24 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> {
13351420
// we don't have any items with the same name but different
13361421
// contents.
13371422
_ => {
1423+
let namespace = self.cx.item_namespace(&item);
1424+
let new_def_id = self.cx.node_def_id(item.id);
1425+
let ident = if let ItemKind::Use(tree) = &item.kind {
1426+
tree.ident()
1427+
} else {
1428+
item.ident
1429+
};
13381430
let unnamed = ident.as_str().contains("C2Rust_Unnamed");
1339-
let def_id_mapping = match self.find_item(&item, namespace.unwrap()) {
1431+
let impl_item = impl_.as_ref().map(|impl_| &*impl_.item);
1432+
let def_id_mapping = match self.find_item(&item, namespace.unwrap(), impl_item) {
13401433
ContainsDecl::NotContained => {
1341-
let new_item =
1342-
MovedDecl::new(item, new_def_id, namespace.unwrap(), parent_header);
1434+
let new_item = MovedDecl::new(
1435+
item,
1436+
new_def_id,
1437+
namespace.unwrap(),
1438+
parent_header,
1439+
impl_,
1440+
);
13431441
if unnamed {
13441442
self.unnamed_items[namespace.unwrap()].push(new_item);
13451443
} else {
@@ -1359,17 +1457,27 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> {
13591457
ContainsDecl::Use(existing) => {
13601458
let existing_def_id = existing.def_id;
13611459
existing.join_visibility(&item.vis.kind);
1362-
*existing =
1363-
MovedDecl::new(item, new_def_id, namespace.unwrap(), parent_header);
1460+
*existing = MovedDecl::new(
1461+
item,
1462+
new_def_id,
1463+
namespace.unwrap(),
1464+
parent_header,
1465+
impl_,
1466+
);
13641467
Some((existing_def_id, new_def_id))
13651468
}
13661469

13671470
ContainsDecl::Equivalent(existing) if existing.is_foreign() => {
13681471
let existing_def_id = existing.def_id;
13691472
item.vis.kind =
13701473
join_visibility(&existing.visibility().kind, &item.vis.kind);
1371-
*existing =
1372-
MovedDecl::new(item, new_def_id, namespace.unwrap(), parent_header);
1474+
*existing = MovedDecl::new(
1475+
item,
1476+
new_def_id,
1477+
namespace.unwrap(),
1478+
parent_header,
1479+
impl_,
1480+
);
13731481
Some((existing_def_id, new_def_id))
13741482
}
13751483

@@ -1395,6 +1503,7 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> {
13951503
new_def_id,
13961504
namespace,
13971505
parent_header.clone(),
1506+
None,
13981507
);
13991508
if unnamed {
14001509
self.unnamed_items[namespace].push(new_item);
@@ -1414,6 +1523,7 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> {
14141523
new_def_id,
14151524
namespace,
14161525
parent_header.clone(),
1526+
None,
14171527
);
14181528
Some((existing_def_id, new_def_id))
14191529
}
@@ -1507,6 +1617,17 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> {
15071617
foreign_items.entry(abi).or_default().push(fi);
15081618
}
15091619
}
1620+
1621+
// If there is an impl item, add it now.
1622+
if let Some(impl_) = item.impl_ {
1623+
let cur_mod_name = impl_.parent_header.ident;
1624+
let i = impl_.item;
1625+
if last_item_mod != Some(cur_mod_name) {
1626+
st.add_comment(i.id, make_header_comment(last_item_mod, cur_mod_name));
1627+
last_item_mod = Some(cur_mod_name);
1628+
}
1629+
items.push(i);
1630+
}
15101631
}
15111632

15121633
let foreign_mods = foreign_items
@@ -1516,14 +1637,30 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> {
15161637
foreign_mods.chain(items.into_iter()).collect()
15171638
}
15181639

1519-
fn find_item<'b>(&'b mut self, item: &Item, namespace: Namespace) -> ContainsDecl<'b> {
1640+
fn find_item<'b>(
1641+
&'b mut self,
1642+
item: &Item,
1643+
namespace: Namespace,
1644+
impl_item: Option<&Item>,
1645+
) -> ContainsDecl<'b> {
15201646
let ident = if let ItemKind::Use(tree) = &item.kind {
15211647
tree.ident()
15221648
} else {
15231649
item.ident
15241650
};
15251651
assert!(ident.name != kw::Empty);
15261652

1653+
let impl_is_compatible = |existing_decl: &MovedDecl| -> bool {
1654+
match (impl_item, &existing_decl.impl_) {
1655+
(None, None) => true,
1656+
(Some(impl_item), Some(existing_impl)) => {
1657+
self.cx
1658+
.compatible_types(impl_item, &existing_impl.item, false)
1659+
}
1660+
_ => false,
1661+
}
1662+
};
1663+
15271664
if ident.as_str().contains("C2Rust_Unnamed") {
15281665
for existing_decl in self.unnamed_items[namespace].iter_mut() {
15291666
match &existing_decl.kind {
@@ -1534,7 +1671,9 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> {
15341671
| ItemKind::Enum(..) => {
15351672
// Does the new item match the existing item, except
15361673
// for unnamed names?
1537-
if item.kind.unnamed_equiv(&existing_item.kind) {
1674+
if item.kind.unnamed_equiv(&existing_item.kind)
1675+
&& impl_is_compatible(existing_decl)
1676+
{
15381677
return ContainsDecl::Equivalent(existing_decl);
15391678
}
15401679
}
@@ -1579,7 +1718,9 @@ impl<'a, 'tcx> HeaderDeclarations<'a, 'tcx> {
15791718
// Otherwise make sure these items are structurally
15801719
// equivalent.
15811720
_ => {
1582-
if self.cx.compatible_types(&item, &existing_item, true) {
1721+
if self.cx.compatible_types(&item, &existing_item, true)
1722+
&& impl_is_compatible(existing_decl)
1723+
{
15831724
return ContainsDecl::Equivalent(existing_decl);
15841725
}
15851726
}

0 commit comments

Comments
 (0)