Skip to content

Commit 8f2c0c3

Browse files
authored
Merge pull request swiftlang#7945 from Michael137/bugfix/astimporter-field-order-fix-to-20230725
[cherry-pick][stable/20230725] [clang][ASTImporter] Only reorder fields of RecordDecls
2 parents 1a1fb4b + 5da7568 commit 8f2c0c3

File tree

2 files changed

+94
-48
lines changed

2 files changed

+94
-48
lines changed

clang/lib/AST/ASTImporter.cpp

Lines changed: 73 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ namespace clang {
433433
Decl *From, DeclContext *&ToDC, DeclContext *&ToLexicalDC);
434434
Error ImportImplicitMethods(const CXXRecordDecl *From, CXXRecordDecl *To);
435435

436+
Error ImportFieldDeclDefinition(const FieldDecl *From, const FieldDecl *To);
436437
Expected<CXXCastPath> ImportCastPath(CastExpr *E);
437438
Expected<APValue> ImportAPValue(const APValue &FromValue);
438439

@@ -1850,52 +1851,33 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
18501851
// different values in two distinct translation units.
18511852
ChildErrorHandlingStrategy HandleChildErrors(FromDC);
18521853

1854+
auto MightNeedReordering = [](const Decl *D) {
1855+
return isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D) || isa<FriendDecl>(D);
1856+
};
1857+
1858+
// Import everything that might need reordering first.
18531859
Error ChildErrors = Error::success();
18541860
for (auto *From : FromDC->decls()) {
1861+
if (!MightNeedReordering(From))
1862+
continue;
1863+
18551864
ExpectedDecl ImportedOrErr = import(From);
18561865

18571866
// If we are in the process of ImportDefinition(...) for a RecordDecl we
18581867
// want to make sure that we are also completing each FieldDecl. There
18591868
// are currently cases where this does not happen and this is correctness
18601869
// fix since operations such as code generation will expect this to be so.
1861-
if (ImportedOrErr) {
1862-
FieldDecl *FieldFrom = dyn_cast_or_null<FieldDecl>(From);
1863-
Decl *ImportedDecl = *ImportedOrErr;
1864-
FieldDecl *FieldTo = dyn_cast_or_null<FieldDecl>(ImportedDecl);
1865-
if (FieldFrom && FieldTo) {
1866-
RecordDecl *FromRecordDecl = nullptr;
1867-
RecordDecl *ToRecordDecl = nullptr;
1868-
// If we have a field that is an ArrayType we need to check if the array
1869-
// element is a RecordDecl and if so we need to import the definition.
1870-
if (FieldFrom->getType()->isArrayType()) {
1871-
// getBaseElementTypeUnsafe(...) handles multi-dimensonal arrays for us.
1872-
FromRecordDecl = FieldFrom->getType()->getBaseElementTypeUnsafe()->getAsRecordDecl();
1873-
ToRecordDecl = FieldTo->getType()->getBaseElementTypeUnsafe()->getAsRecordDecl();
1874-
}
1875-
1876-
if (!FromRecordDecl || !ToRecordDecl) {
1877-
const RecordType *RecordFrom =
1878-
FieldFrom->getType()->getAs<RecordType>();
1879-
const RecordType *RecordTo = FieldTo->getType()->getAs<RecordType>();
1880-
1881-
if (RecordFrom && RecordTo) {
1882-
FromRecordDecl = RecordFrom->getDecl();
1883-
ToRecordDecl = RecordTo->getDecl();
1884-
}
1885-
}
1886-
1887-
if (FromRecordDecl && ToRecordDecl) {
1888-
if (FromRecordDecl->isCompleteDefinition() &&
1889-
!ToRecordDecl->isCompleteDefinition()) {
1890-
Error Err = ImportDefinition(FromRecordDecl, ToRecordDecl);
1891-
HandleChildErrors.handleChildImportResult(ChildErrors,
1892-
std::move(Err));
1893-
}
1894-
}
1895-
}
1896-
} else {
1870+
if (!ImportedOrErr) {
18971871
HandleChildErrors.handleChildImportResult(ChildErrors,
18981872
ImportedOrErr.takeError());
1873+
continue;
1874+
}
1875+
FieldDecl *FieldFrom = dyn_cast_or_null<FieldDecl>(From);
1876+
Decl *ImportedDecl = *ImportedOrErr;
1877+
FieldDecl *FieldTo = dyn_cast_or_null<FieldDecl>(ImportedDecl);
1878+
if (FieldFrom && FieldTo) {
1879+
Error Err = ImportFieldDeclDefinition(FieldFrom, FieldTo);
1880+
HandleChildErrors.handleChildImportResult(ChildErrors, std::move(Err));
18991881
}
19001882
}
19011883

@@ -1910,7 +1892,7 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
19101892
// During the import of `a` we import first the dependencies in sequence,
19111893
// thus the order would be `c`, `b`, `a`. We will get the normal order by
19121894
// first removing the already imported members and then adding them in the
1913-
// order as they apper in the "from" context.
1895+
// order as they appear in the "from" context.
19141896
//
19151897
// Keeping field order is vital because it determines structure layout.
19161898
//
@@ -1922,25 +1904,24 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
19221904
// interface in LLDB is implemented by the means of the ASTImporter. However,
19231905
// calling an import at this point would result in an uncontrolled import, we
19241906
// must avoid that.
1925-
const auto *FromRD = dyn_cast<RecordDecl>(FromDC);
1926-
if (!FromRD)
1927-
return ChildErrors;
19281907

19291908
auto ToDCOrErr = Importer.ImportContext(FromDC);
19301909
if (!ToDCOrErr) {
19311910
consumeError(std::move(ChildErrors));
19321911
return ToDCOrErr.takeError();
19331912
}
19341913

1935-
DeclContext *ToDC = *ToDCOrErr;
1936-
// Remove all declarations, which may be in wrong order in the
1937-
// lexical DeclContext and then add them in the proper order.
1938-
for (auto *D : FromRD->decls()) {
1939-
if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D) || isa<FriendDecl>(D)) {
1914+
if (const auto *FromRD = dyn_cast<RecordDecl>(FromDC)) {
1915+
DeclContext *ToDC = *ToDCOrErr;
1916+
// Remove all declarations, which may be in wrong order in the
1917+
// lexical DeclContext and then add them in the proper order.
1918+
for (auto *D : FromRD->decls()) {
1919+
if (!MightNeedReordering(D))
1920+
continue;
1921+
19401922
assert(D && "DC contains a null decl");
1941-
Decl *ToD = Importer.GetAlreadyImportedOrNull(D);
1942-
// Remove only the decls which we successfully imported.
1943-
if (ToD) {
1923+
if (Decl *ToD = Importer.GetAlreadyImportedOrNull(D)) {
1924+
// Remove only the decls which we successfully imported.
19441925
assert(ToDC == ToD->getLexicalDeclContext() && ToDC->containsDecl(ToD));
19451926
// Remove the decl from its wrong place in the linked list.
19461927
ToDC->removeDecl(ToD);
@@ -1952,9 +1933,53 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
19521933
}
19531934
}
19541935

1936+
// Import everything else.
1937+
for (auto *From : FromDC->decls()) {
1938+
if (MightNeedReordering(From))
1939+
continue;
1940+
1941+
ExpectedDecl ImportedOrErr = import(From);
1942+
if (!ImportedOrErr)
1943+
HandleChildErrors.handleChildImportResult(ChildErrors,
1944+
ImportedOrErr.takeError());
1945+
}
1946+
19551947
return ChildErrors;
19561948
}
19571949

1950+
Error ASTNodeImporter::ImportFieldDeclDefinition(const FieldDecl *From,
1951+
const FieldDecl *To) {
1952+
RecordDecl *FromRecordDecl = nullptr;
1953+
RecordDecl *ToRecordDecl = nullptr;
1954+
// If we have a field that is an ArrayType we need to check if the array
1955+
// element is a RecordDecl and if so we need to import the definition.
1956+
QualType FromType = From->getType();
1957+
QualType ToType = To->getType();
1958+
if (FromType->isArrayType()) {
1959+
// getBaseElementTypeUnsafe(...) handles multi-dimensonal arrays for us.
1960+
FromRecordDecl = FromType->getBaseElementTypeUnsafe()->getAsRecordDecl();
1961+
ToRecordDecl = ToType->getBaseElementTypeUnsafe()->getAsRecordDecl();
1962+
}
1963+
1964+
if (!FromRecordDecl || !ToRecordDecl) {
1965+
const RecordType *RecordFrom = FromType->getAs<RecordType>();
1966+
const RecordType *RecordTo = ToType->getAs<RecordType>();
1967+
1968+
if (RecordFrom && RecordTo) {
1969+
FromRecordDecl = RecordFrom->getDecl();
1970+
ToRecordDecl = RecordTo->getDecl();
1971+
}
1972+
}
1973+
1974+
if (FromRecordDecl && ToRecordDecl) {
1975+
if (FromRecordDecl->isCompleteDefinition() &&
1976+
!ToRecordDecl->isCompleteDefinition())
1977+
return ImportDefinition(FromRecordDecl, ToRecordDecl);
1978+
}
1979+
1980+
return Error::success();
1981+
}
1982+
19581983
Error ASTNodeImporter::ImportDeclContext(
19591984
Decl *FromD, DeclContext *&ToDC, DeclContext *&ToLexicalDC) {
19601985
auto ToDCOrErr = Importer.ImportContext(FromD->getDeclContext());

clang/unittests/AST/ASTImporterTest.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8048,6 +8048,27 @@ TEST_P(ASTImporterOptionSpecificTestBase, ImportDeductionGuideDifferentOrder) {
80488048
ToDGOther);
80498049
}
80508050

8051+
TEST_P(ASTImporterOptionSpecificTestBase,
8052+
ImportFieldsFirstForCorrectRecordLayoutTest) {
8053+
// UnaryOperator(&) triggers RecordLayout computation, which relies on
8054+
// correctly imported fields.
8055+
auto Code =
8056+
R"(
8057+
class A {
8058+
int m() {
8059+
return &((A *)0)->f1 - &((A *)0)->f2;
8060+
}
8061+
int f1;
8062+
int f2;
8063+
};
8064+
)";
8065+
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
8066+
8067+
auto *FromF = FirstDeclMatcher<CXXMethodDecl>().match(
8068+
FromTU, cxxMethodDecl(hasName("A::m")));
8069+
Import(FromF, Lang_CXX11);
8070+
}
8071+
80518072
TEST_P(ASTImporterOptionSpecificTestBase,
80528073
ImportRecordWithLayoutRequestingExpr) {
80538074
TranslationUnitDecl *FromTU = getTuDecl(

0 commit comments

Comments
 (0)