Skip to content

Commit 48b16e1

Browse files
author
Gabor Marton
committed
[ASTImporter] Reorder fields after structure import is finished
We reorder declarations in RecordDecls because they may have another order in the "to" context than they have in the "from" context. This may happen e.g when we import a class like this: struct declToImport { int a = c + b; int b = 1; int c = 2; }; During the import of `a` we import first the dependencies in sequence, thus the order would be `c`, `b`, `a`. We will get the normal order by first removing the already imported members and then adding them in the order as they apper in the "from" context. Keeping field order is vital because it determines structure layout. Reviewers: a_sidorin, shafik Tags: #clang Differential Revision: https://reviews.llvm.org/D44100 llvm-svn: 366997
1 parent 18fa729 commit 48b16e1

File tree

2 files changed

+64
-2
lines changed

2 files changed

+64
-2
lines changed

clang/lib/AST/ASTImporter.cpp

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1645,7 +1645,6 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
16451645
bool AccumulateChildErrors = isa<TagDecl>(FromDC);
16461646

16471647
Error ChildErrors = Error::success();
1648-
llvm::SmallVector<Decl *, 8> ImportedDecls;
16491648
for (auto *From : FromDC->decls()) {
16501649
ExpectedDecl ImportedOrErr = import(From);
16511650
if (!ImportedOrErr) {
@@ -1657,6 +1656,59 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
16571656
}
16581657
}
16591658

1659+
// We reorder declarations in RecordDecls because they may have another order
1660+
// in the "to" context than they have in the "from" context. This may happen
1661+
// e.g when we import a class like this:
1662+
// struct declToImport {
1663+
// int a = c + b;
1664+
// int b = 1;
1665+
// int c = 2;
1666+
// };
1667+
// During the import of `a` we import first the dependencies in sequence,
1668+
// thus the order would be `c`, `b`, `a`. We will get the normal order by
1669+
// first removing the already imported members and then adding them in the
1670+
// order as they apper in the "from" context.
1671+
//
1672+
// Keeping field order is vital because it determines structure layout.
1673+
//
1674+
// Here and below, we cannot call field_begin() method and its callers on
1675+
// ToDC if it has an external storage. Calling field_begin() will
1676+
// automatically load all the fields by calling
1677+
// LoadFieldsFromExternalStorage(). LoadFieldsFromExternalStorage() would
1678+
// call ASTImporter::Import(). This is because the ExternalASTSource
1679+
// interface in LLDB is implemented by the means of the ASTImporter. However,
1680+
// calling an import at this point would result in an uncontrolled import, we
1681+
// must avoid that.
1682+
const auto *FromRD = dyn_cast<RecordDecl>(FromDC);
1683+
if (!FromRD)
1684+
return ChildErrors;
1685+
1686+
auto ToDCOrErr = Importer.ImportContext(FromDC);
1687+
if (!ToDCOrErr) {
1688+
consumeError(std::move(ChildErrors));
1689+
return ToDCOrErr.takeError();
1690+
}
1691+
1692+
DeclContext *ToDC = *ToDCOrErr;
1693+
// Remove all declarations, which may be in wrong order in the
1694+
// lexical DeclContext and then add them in the proper order.
1695+
for (auto *D : FromRD->decls()) {
1696+
if (isa<FieldDecl>(D) || isa<FriendDecl>(D)) {
1697+
assert(D && "DC contains a null decl");
1698+
Decl *ToD = Importer.GetAlreadyImportedOrNull(D);
1699+
// Remove only the decls which we successfully imported.
1700+
if (ToD) {
1701+
assert(ToDC == ToD->getLexicalDeclContext() && ToDC->containsDecl(ToD));
1702+
// Remove the decl from its wrong place in the linked list.
1703+
ToDC->removeDecl(ToD);
1704+
// Add the decl to the end of the linked list.
1705+
// This time it will be at the proper place because the enclosing for
1706+
// loop iterates in the original (good) order of the decls.
1707+
ToDC->addDeclInternal(ToD);
1708+
}
1709+
}
1710+
}
1711+
16601712
return ChildErrors;
16611713
}
16621714

clang/unittests/AST/ASTImporterTest.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1472,7 +1472,7 @@ TEST_P(ASTImporterOptionSpecificTestBase,
14721472
}
14731473

14741474
TEST_P(ASTImporterOptionSpecificTestBase,
1475-
DISABLED_CXXRecordDeclFieldOrderShouldNotDependOnImportOrder) {
1475+
CXXRecordDeclFieldOrderShouldNotDependOnImportOrder) {
14761476
Decl *From, *To;
14771477
std::tie(From, To) = getImportedDecl(
14781478
// The original recursive algorithm of ASTImporter first imports 'c' then
@@ -2795,6 +2795,16 @@ TEST_P(ImportDecl, ImportEnumSequential) {
27952795
"main.c", enumDecl(), VerificationMatcher);
27962796
}
27972797

2798+
TEST_P(ImportDecl, ImportFieldOrder) {
2799+
MatchVerifier<Decl> Verifier;
2800+
testImport("struct declToImport {"
2801+
" int b = a + 2;"
2802+
" int a = 5;"
2803+
"};",
2804+
Lang_CXX11, "", Lang_CXX11, Verifier,
2805+
recordDecl(hasFieldOrder({"b", "a"})));
2806+
}
2807+
27982808
const internal::VariadicDynCastAllOfMatcher<Expr, DependentScopeDeclRefExpr>
27992809
dependentScopeDeclRefExpr;
28002810

0 commit comments

Comments
 (0)