Skip to content
57 changes: 51 additions & 6 deletions lib/Interpreter/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1128,9 +1128,33 @@ namespace Cpp {

if (auto *CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(D)) {
std::vector<TCppScope_t> datamembers;
for (auto it = CXXRD->field_begin(), end = CXXRD->field_end(); it != end;
it++) {
datamembers.push_back((TCppScope_t)*it);
llvm::SmallVector<RecordDecl::field_iterator, 2> stack_begin;
llvm::SmallVector<RecordDecl::field_iterator, 2> stack_end;
stack_begin.push_back(CXXRD->field_begin());
stack_end.push_back(CXXRD->field_end());
while (stack_begin.size()) {
if (stack_begin.back() == stack_end.back()) {
stack_begin.pop_back();
stack_end.pop_back();
continue;
}
Decl* D = *(stack_begin.back());
if (D->getKind() == clang::Decl::Field &&
dyn_cast<FieldDecl>(D)->isAnonymousStructOrUnion()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (D->getKind() == clang::Decl::Field &&
dyn_cast<FieldDecl>(D)->isAnonymousStructOrUnion()) {
if (isa<FieldDecl>(D) &&
cast<FieldDecl>(D)->isAnonymousStructOrUnion()) {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have a proper dyn_cast and isAnonymousStructOrUnion as a nested if...

if (auto* CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(
dyn_cast<FieldDecl>(D)
->getType()
->getAs<RecordType>()
->getDecl())) {
stack_begin.back()++;
stack_begin.push_back(CXXRD->field_begin());
stack_end.push_back(CXXRD->field_end());
continue;
}
assert(false); // should be unreachable else internal error
}
datamembers.push_back((TCppScope_t)D);
stack_begin.back()++;
}

return datamembers;
Expand Down Expand Up @@ -1175,9 +1199,30 @@ namespace Cpp {
auto *D = (Decl *) var;
auto &C = getASTContext();

if (auto *FD = llvm::dyn_cast<FieldDecl>(D))
return (intptr_t) C.toCharUnitsFromBits(C.getASTRecordLayout(FD->getParent())
.getFieldOffset(FD->getFieldIndex())).getQuantity();
if (auto* FD = llvm::dyn_cast<FieldDecl>(D)) {
const clang::RecordDecl* RD = FD->getParent();
intptr_t offset =
C.toCharUnitsFromBits(
C.getASTRecordLayout(RD).getFieldOffset(FD->getFieldIndex()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
C.getASTRecordLayout(RD).getFieldOffset(FD->getFieldIndex()))
C.getFieldOffset(FD)

.getQuantity();
while (RD->isAnonymousStructOrUnion()) {
const clang::RecordDecl* anon = RD;
RD = llvm::dyn_cast<RecordDecl>(anon->getParent());
for (auto f = RD->field_begin(); f != RD->field_end(); ++f) {
auto* rt = f->getType()->getAs<RecordType>();
if (!rt)
continue;
if (anon == rt->getDecl()) {
FD = *f;
break;
}
}
offset += C.toCharUnitsFromBits(C.getASTRecordLayout(RD).getFieldOffset(
FD->getFieldIndex()))
.getQuantity();
}
return offset;
}

if (auto *VD = llvm::dyn_cast<VarDecl>(D)) {
auto GD = GlobalDecl(VD);
Expand Down
71 changes: 71 additions & 0 deletions unittests/CppInterOp/VariableReflectionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,77 @@ TEST(VariableReflectionTest, GetDatamembers) {
EXPECT_EQ(datamembers1.size(), 0);
}

#define CODE \
struct Klass1 { \
Klass1(int i) : num(1), b(i) {} \
int num; \
union { \
double a; \
int b; \
}; \
} k1(5); \
struct Klass2 { \
Klass2(double d) : num(2), a(d) {} \
int num; \
struct { \
double a; \
int b; \
}; \
} k2(2.5); \
struct Klass3 { \
Klass3(int i) : num(i) {} \
int num; \
struct { \
double a; \
union { \
float b; \
int c; \
}; \
}; \
int num2; \
} k3(5);

CODE

TEST(VariableReflectionTest, DatamembersWithAnonymousStructOrUnion) {
std::vector<Decl*> Decls;
#define Stringify(s) Stringifyx(s)
#define Stringifyx(...) #__VA_ARGS__
GetAllTopLevelDecls(Stringify(CODE), Decls);
#undef Stringifyx
#undef Stringify
#undef CODE

auto datamembers_klass1 = Cpp::GetDatamembers(Decls[0]);
auto datamembers_klass2 = Cpp::GetDatamembers(Decls[2]);
auto datamembers_klass3 = Cpp::GetDatamembers(Decls[4]);

EXPECT_EQ(datamembers_klass1.size(), 3);
EXPECT_EQ(datamembers_klass2.size(), 3);

EXPECT_EQ(Cpp::GetVariableOffset(datamembers_klass1[0]), 0);
EXPECT_EQ(Cpp::GetVariableOffset(datamembers_klass1[1]),
((intptr_t) & (k1.a)) - ((intptr_t) & (k1.num)));
EXPECT_EQ(Cpp::GetVariableOffset(datamembers_klass1[2]),
((intptr_t) & (k1.b)) - ((intptr_t) & (k1.num)));

EXPECT_EQ(Cpp::GetVariableOffset(datamembers_klass2[0]), 0);
EXPECT_EQ(Cpp::GetVariableOffset(datamembers_klass2[1]),
((intptr_t) & (k2.a)) - ((intptr_t) & (k2.num)));
EXPECT_EQ(Cpp::GetVariableOffset(datamembers_klass2[2]),
((intptr_t) & (k2.b)) - ((intptr_t) & (k2.num)));

EXPECT_EQ(Cpp::GetVariableOffset(datamembers_klass3[0]), 0);
EXPECT_EQ(Cpp::GetVariableOffset(datamembers_klass3[1]),
((intptr_t) & (k3.a)) - ((intptr_t) & (k3.num)));
EXPECT_EQ(Cpp::GetVariableOffset(datamembers_klass3[2]),
((intptr_t) & (k3.b)) - ((intptr_t) & (k3.num)));
EXPECT_EQ(Cpp::GetVariableOffset(datamembers_klass3[3]),
((intptr_t) & (k3.c)) - ((intptr_t) & (k3.num)));
EXPECT_EQ(Cpp::GetVariableOffset(datamembers_klass3[4]),
((intptr_t) & (k3.num2)) - ((intptr_t) & (k3.num)));
}

TEST(VariableReflectionTest, LookupDatamember) {
std::vector<Decl*> Decls;
std::string code = R"(
Expand Down
Loading