Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions cpp2rust/converter/converter_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <array>
#include <cctype>
#include <filesystem>
#include <format>
#include <unordered_set>

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

unsigned GetAnonIndex(const clang::NamedDecl *decl) {
if (auto *parent =
clang::dyn_cast<clang::RecordDecl>(decl->getDeclContext())) {
unsigned counter = 0;
for (auto *d : parent->decls()) {
if (d == decl) {
return counter;
}
auto *named = clang::dyn_cast<clang::NamedDecl>(d);
if (named && named->getKind() == decl->getKind() &&
named->getName().empty()) {
counter++;
}
}
return counter;
}
return 0;
}

std::string GetID(const clang::Decl *decl) {
assert(decl);
const auto file_name = GetFileName(decl);
Expand All @@ -317,6 +337,17 @@ std::string GetNamedDeclAsString(const clang::NamedDecl *decl) {
auto name = decl->getDeclName().isIdentifier() ? decl->getName().str()
: decl->getNameAsString();

// Anonymous record field
if (auto *field = clang::dyn_cast<clang::FieldDecl>(decl);
field && name.empty()) {
const clang::NamedDecl *target = field;
if (auto *record = field->getType()->getAsRecordDecl();
record && !record->getIdentifier()) {
target = record;
}
return std::format("anon_{}", GetAnonIndex(target));
}

if (auto *fn = clang::dyn_cast<clang::FunctionDecl>(decl)) {
if (!clang::isa<clang::CXXMethodDecl>(fn)) {
auto mangled =
Expand Down
2 changes: 2 additions & 0 deletions cpp2rust/converter/converter_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ std::string GetID(const clang::Decl *decl);

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

unsigned GetAnonIndex(const clang::NamedDecl *decl);

const char *AccessSpecifierAsString(clang::AccessSpecifier spec);

template <class T> llvm::SmallString<16> GetNumAsString(const T &num) {
Expand Down
17 changes: 17 additions & 0 deletions cpp2rust/converter/mapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,18 @@ std::string normalizeTranslationRule(std::string rule) {
return rule;
}

static std::string synthesizeAnonRecordName(const clang::RecordDecl *record) {
std::string parent_name;
if (auto *parent =
clang::dyn_cast<clang::RecordDecl>(record->getDeclContext())) {
parent_name = parent->getIdentifier()
? parent->getIdentifier()->getName().str()
: synthesizeAnonRecordName(parent);
parent_name += '_';
}
return std::format("{}anon_{}", parent_name, GetAnonIndex(record));
}

} // namespace

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

std::string ToString(const clang::NamedDecl *decl) {
if (auto *record = clang::dyn_cast<clang::RecordDecl>(decl);
record && !record->getIdentifier()) {
return synthesizeAnonRecordName(record);
}

std::string out;
llvm::raw_string_ostream os(out);

Expand Down
1 change: 1 addition & 0 deletions cpp2rust/converter/models/converter_refcount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,7 @@ bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {

bool ConverterRefCount::VisitImplicitValueInitExpr(
clang::ImplicitValueInitExpr *expr) {
PushConversionKind push(*this, ConversionKind::Unboxed);
if (auto arr_ty = clang::dyn_cast<clang::ArrayType>(
expr->getType()->getCanonicalTypeInternal().getTypePtr())) {
if (clang::isa<clang::ConstantArrayType>(arr_ty)) {
Expand Down
29 changes: 15 additions & 14 deletions tests/lit/lit/formats/Cpp2RustTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,22 +118,23 @@ def fail(str, code = fail_code):
if should_not_translate:
return fail('expected translation-fail but cpp2rust succeeded')

expected_file = self.getExpectedFile(filepath, model, fname)
if not os.path.exists(expected_file) and not replace_expected:
return fail('no expected file')
if not should_not_compile:
expected_file = self.getExpectedFile(filepath, model, fname)
if not os.path.exists(expected_file) and not replace_expected:
return fail('no expected file')

if replace_expected:
self.updateExpected(generated, expected_file)
if replace_expected:
self.updateExpected(generated, expected_file)

with open(expected_file, 'r') as f:
expected = f.read()
with open(expected_file, 'r') as f:
expected = f.read()

if generated != expected:
diff = ''.join(difflib.unified_diff(
expected.splitlines(keepends=True),
generated.splitlines(keepends=True),
fromfile='expected', tofile='generated'))
return fail('different output\n' + diff)
if generated != expected:
diff = ''.join(difflib.unified_diff(
expected.splitlines(keepends=True),
generated.splitlines(keepends=True),
fromfile='expected', tofile='generated'))
return fail('different output\n' + diff)

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

Expand Down Expand Up @@ -163,7 +164,7 @@ def fail(str, code = fail_code):
if should_not_compile:
if returncode != 0:
shutil.rmtree(tmp_dir, True)
return lit.Test.PASS, ''
return lit.Test.XFAIL, ''
return fail('expected no-compile but compiled successfully')
if returncode != 0:
return fail('cargo failed\n' + err)
Expand Down
74 changes: 74 additions & 0 deletions tests/unit/anonymous-struct.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include <cassert>

struct Outer {
struct Named {
int a;
int b;
} named;

struct {
int c;
int d;
} anonymous_named_0;

struct {
int g;
int h;
} anonymous_named_1;

struct {
int e;
int f;
};

struct {
int i;
struct {
int j;
} inner_named;
struct {
int k;
};
};
};

int main() {
Outer o = {};

o.named.a = 1;
o.named.b = 2;
o.anonymous_named_0.c = 3;
o.anonymous_named_0.d = 4;
o.anonymous_named_1.g = 5;
o.anonymous_named_1.h = 6;
o.e = 7;
o.f = 8;
o.i = 9;
o.inner_named.j = 10;
o.k = 11;

assert(o.named.a == 1);
assert(o.named.b == 2);
assert(o.anonymous_named_0.c == 3);
assert(o.anonymous_named_0.d == 4);
assert(o.anonymous_named_1.g == 5);
assert(o.anonymous_named_1.h == 6);
assert(o.e == 7);
assert(o.f == 8);
assert(o.i == 9);
assert(o.inner_named.j == 10);
assert(o.k == 11);

struct {
int x;
int z;
} s;

s.x = 1;
s.z = 2;

assert(s.x = 1);
assert(s.z = 2);

return 0;
}
74 changes: 74 additions & 0 deletions tests/unit/anonymous-struct_c.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include <assert.h>

struct Outer {
struct Named {
int a;
int b;
} named;

struct {
int c;
int d;
} anon0;

struct {
int g;
int h;
} anon1;

struct {
int e;
int f;
};

struct {
int i;
struct {
int j;
} inner_named;
struct {
int k;
};
};
};

int main(void) {
struct Outer o = {0};

o.named.a = 1;
o.named.b = 2;
o.anon0.c = 3;
o.anon0.d = 4;
o.anon1.g = 5;
o.anon1.h = 6;
o.e = 7;
o.f = 8;
o.i = 9;
o.inner_named.j = 10;
o.k = 11;

assert(o.named.a == 1);
assert(o.named.b == 2);
assert(o.anon0.c == 3);
assert(o.anon0.d == 4);
assert(o.anon1.g == 5);
assert(o.anon1.h == 6);
assert(o.e == 7);
assert(o.f == 8);
assert(o.i == 9);
assert(o.inner_named.j == 10);
assert(o.k == 11);

struct {
int x;
int z;
} s;

s.x = 1;
s.z = 2;

assert(s.x = 1);
assert(s.z = 2);

return 0;
}
Loading
Loading