Skip to content

Commit 9c4c6da

Browse files
authored
Translate anonymous structs (#33)
1 parent 9802d0a commit 9c4c6da

34 files changed

Lines changed: 1449 additions & 27 deletions

cpp2rust/converter/converter_lib.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <array>
1313
#include <cctype>
1414
#include <filesystem>
15+
#include <format>
1516
#include <unordered_set>
1617

1718
#include "converter/lex.h"
@@ -296,6 +297,25 @@ unsigned GetArraySize(clang::QualType array_type) {
296297
return constant_array_ty->getSize().getZExtValue();
297298
}
298299

300+
unsigned GetAnonIndex(const clang::NamedDecl *decl) {
301+
if (auto *parent =
302+
clang::dyn_cast<clang::RecordDecl>(decl->getDeclContext())) {
303+
unsigned counter = 0;
304+
for (auto *d : parent->decls()) {
305+
if (d == decl) {
306+
return counter;
307+
}
308+
auto *named = clang::dyn_cast<clang::NamedDecl>(d);
309+
if (named && named->getKind() == decl->getKind() &&
310+
named->getName().empty()) {
311+
counter++;
312+
}
313+
}
314+
return counter;
315+
}
316+
return 0;
317+
}
318+
299319
std::string GetID(const clang::Decl *decl) {
300320
assert(decl);
301321
const auto file_name = GetFileName(decl);
@@ -317,6 +337,17 @@ std::string GetNamedDeclAsString(const clang::NamedDecl *decl) {
317337
auto name = decl->getDeclName().isIdentifier() ? decl->getName().str()
318338
: decl->getNameAsString();
319339

340+
// Anonymous record field
341+
if (auto *field = clang::dyn_cast<clang::FieldDecl>(decl);
342+
field && name.empty()) {
343+
const clang::NamedDecl *target = field;
344+
if (auto *record = field->getType()->getAsRecordDecl();
345+
record && !record->getIdentifier()) {
346+
target = record;
347+
}
348+
return std::format("anon_{}", GetAnonIndex(target));
349+
}
350+
320351
if (auto *fn = clang::dyn_cast<clang::FunctionDecl>(decl)) {
321352
if (!clang::isa<clang::CXXMethodDecl>(fn)) {
322353
auto mangled =

cpp2rust/converter/converter_lib.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ std::string GetID(const clang::Decl *decl);
8282

8383
std::string GetNamedDeclAsString(const clang::NamedDecl *decl);
8484

85+
unsigned GetAnonIndex(const clang::NamedDecl *decl);
86+
8587
const char *AccessSpecifierAsString(clang::AccessSpecifier spec);
8688

8789
template <class T> llvm::SmallString<16> GetNumAsString(const T &num) {

cpp2rust/converter/mapper.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,18 @@ std::string normalizeTranslationRule(std::string rule) {
562562
return rule;
563563
}
564564

565+
static std::string synthesizeAnonRecordName(const clang::RecordDecl *record) {
566+
std::string parent_name;
567+
if (auto *parent =
568+
clang::dyn_cast<clang::RecordDecl>(record->getDeclContext())) {
569+
parent_name = parent->getIdentifier()
570+
? parent->getIdentifier()->getName().str()
571+
: synthesizeAnonRecordName(parent);
572+
parent_name += '_';
573+
}
574+
return std::format("{}anon_{}", parent_name, GetAnonIndex(record));
575+
}
576+
565577
} // namespace
566578

567579
PushASTContext::PushASTContext(clang::ASTContext &ctx) : prev_(ctx_) {
@@ -724,6 +736,11 @@ std::string ToString(clang::QualType qual_type) {
724736
}
725737

726738
std::string ToString(const clang::NamedDecl *decl) {
739+
if (auto *record = clang::dyn_cast<clang::RecordDecl>(decl);
740+
record && !record->getIdentifier()) {
741+
return synthesizeAnonRecordName(record);
742+
}
743+
727744
std::string out;
728745
llvm::raw_string_ostream os(out);
729746

cpp2rust/converter/models/converter_refcount.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,6 +1519,7 @@ bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
15191519

15201520
bool ConverterRefCount::VisitImplicitValueInitExpr(
15211521
clang::ImplicitValueInitExpr *expr) {
1522+
PushConversionKind push(*this, ConversionKind::Unboxed);
15221523
if (auto arr_ty = clang::dyn_cast<clang::ArrayType>(
15231524
expr->getType()->getCanonicalTypeInternal().getTypePtr())) {
15241525
if (clang::isa<clang::ConstantArrayType>(arr_ty)) {

tests/lit/lit/formats/Cpp2RustTest.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -118,22 +118,23 @@ def fail(str, code = fail_code):
118118
if should_not_translate:
119119
return fail('expected translation-fail but cpp2rust succeeded')
120120

121-
expected_file = self.getExpectedFile(filepath, model, fname)
122-
if not os.path.exists(expected_file) and not replace_expected:
123-
return fail('no expected file')
121+
if not should_not_compile:
122+
expected_file = self.getExpectedFile(filepath, model, fname)
123+
if not os.path.exists(expected_file) and not replace_expected:
124+
return fail('no expected file')
124125

125-
if replace_expected:
126-
self.updateExpected(generated, expected_file)
126+
if replace_expected:
127+
self.updateExpected(generated, expected_file)
127128

128-
with open(expected_file, 'r') as f:
129-
expected = f.read()
129+
with open(expected_file, 'r') as f:
130+
expected = f.read()
130131

131-
if generated != expected:
132-
diff = ''.join(difflib.unified_diff(
133-
expected.splitlines(keepends=True),
134-
generated.splitlines(keepends=True),
135-
fromfile='expected', tofile='generated'))
136-
return fail('different output\n' + diff)
132+
if generated != expected:
133+
diff = ''.join(difflib.unified_diff(
134+
expected.splitlines(keepends=True),
135+
generated.splitlines(keepends=True),
136+
fromfile='expected', tofile='generated'))
137+
return fail('different output\n' + diff)
137138

138139
pkg_name = "test_" + re.sub(r'[^a-zA-Z0-9_]', '_', os.path.basename(tmp_dir))
139140

@@ -163,7 +164,7 @@ def fail(str, code = fail_code):
163164
if should_not_compile:
164165
if returncode != 0:
165166
shutil.rmtree(tmp_dir, True)
166-
return lit.Test.PASS, ''
167+
return lit.Test.XFAIL, ''
167168
return fail('expected no-compile but compiled successfully')
168169
if returncode != 0:
169170
return fail('cargo failed\n' + err)

tests/unit/anonymous-struct.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include <cassert>
2+
3+
struct Outer {
4+
struct Named {
5+
int a;
6+
int b;
7+
} named;
8+
9+
struct {
10+
int c;
11+
int d;
12+
} anonymous_named_0;
13+
14+
struct {
15+
int g;
16+
int h;
17+
} anonymous_named_1;
18+
19+
struct {
20+
int e;
21+
int f;
22+
};
23+
24+
struct {
25+
int i;
26+
struct {
27+
int j;
28+
} inner_named;
29+
struct {
30+
int k;
31+
};
32+
};
33+
};
34+
35+
int main() {
36+
Outer o = {};
37+
38+
o.named.a = 1;
39+
o.named.b = 2;
40+
o.anonymous_named_0.c = 3;
41+
o.anonymous_named_0.d = 4;
42+
o.anonymous_named_1.g = 5;
43+
o.anonymous_named_1.h = 6;
44+
o.e = 7;
45+
o.f = 8;
46+
o.i = 9;
47+
o.inner_named.j = 10;
48+
o.k = 11;
49+
50+
assert(o.named.a == 1);
51+
assert(o.named.b == 2);
52+
assert(o.anonymous_named_0.c == 3);
53+
assert(o.anonymous_named_0.d == 4);
54+
assert(o.anonymous_named_1.g == 5);
55+
assert(o.anonymous_named_1.h == 6);
56+
assert(o.e == 7);
57+
assert(o.f == 8);
58+
assert(o.i == 9);
59+
assert(o.inner_named.j == 10);
60+
assert(o.k == 11);
61+
62+
struct {
63+
int x;
64+
int z;
65+
} s;
66+
67+
s.x = 1;
68+
s.z = 2;
69+
70+
assert(s.x = 1);
71+
assert(s.z = 2);
72+
73+
return 0;
74+
}

tests/unit/anonymous-struct_c.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include <assert.h>
2+
3+
struct Outer {
4+
struct Named {
5+
int a;
6+
int b;
7+
} named;
8+
9+
struct {
10+
int c;
11+
int d;
12+
} anon0;
13+
14+
struct {
15+
int g;
16+
int h;
17+
} anon1;
18+
19+
struct {
20+
int e;
21+
int f;
22+
};
23+
24+
struct {
25+
int i;
26+
struct {
27+
int j;
28+
} inner_named;
29+
struct {
30+
int k;
31+
};
32+
};
33+
};
34+
35+
int main(void) {
36+
struct Outer o = {0};
37+
38+
o.named.a = 1;
39+
o.named.b = 2;
40+
o.anon0.c = 3;
41+
o.anon0.d = 4;
42+
o.anon1.g = 5;
43+
o.anon1.h = 6;
44+
o.e = 7;
45+
o.f = 8;
46+
o.i = 9;
47+
o.inner_named.j = 10;
48+
o.k = 11;
49+
50+
assert(o.named.a == 1);
51+
assert(o.named.b == 2);
52+
assert(o.anon0.c == 3);
53+
assert(o.anon0.d == 4);
54+
assert(o.anon1.g == 5);
55+
assert(o.anon1.h == 6);
56+
assert(o.e == 7);
57+
assert(o.f == 8);
58+
assert(o.i == 9);
59+
assert(o.inner_named.j == 10);
60+
assert(o.k == 11);
61+
62+
struct {
63+
int x;
64+
int z;
65+
} s;
66+
67+
s.x = 1;
68+
s.z = 2;
69+
70+
assert(s.x = 1);
71+
assert(s.z = 2);
72+
73+
return 0;
74+
}

0 commit comments

Comments
 (0)