Skip to content

Commit e953669

Browse files
committed
[ASTImporter] Fields are imported first and reordered for correct layout
Fields are imported first and reordered for correct layout. For partially imported record, layout computation is incorrect. Differential Revision: https://reviews.llvm.org/D154764
1 parent 3be16bd commit e953669

File tree

2 files changed

+97
-53
lines changed

2 files changed

+97
-53
lines changed

clang/lib/AST/ASTImporter.cpp

Lines changed: 76 additions & 53 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,9 +1904,6 @@ 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) {
@@ -1935,26 +1914,70 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
19351914
DeclContext *ToDC = *ToDCOrErr;
19361915
// Remove all declarations, which may be in wrong order in the
19371916
// 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)) {
1940-
assert(D && "DC contains a null decl");
1941-
Decl *ToD = Importer.GetAlreadyImportedOrNull(D);
1917+
for (auto *D : FromDC->decls()) {
1918+
if (!MightNeedReordering(D))
1919+
continue;
1920+
1921+
assert(D && "DC contains a null decl");
1922+
if (Decl *ToD = Importer.GetAlreadyImportedOrNull(D)) {
19421923
// Remove only the decls which we successfully imported.
1943-
if (ToD) {
1944-
assert(ToDC == ToD->getLexicalDeclContext() && ToDC->containsDecl(ToD));
1945-
// Remove the decl from its wrong place in the linked list.
1946-
ToDC->removeDecl(ToD);
1947-
// Add the decl to the end of the linked list.
1948-
// This time it will be at the proper place because the enclosing for
1949-
// loop iterates in the original (good) order of the decls.
1950-
ToDC->addDeclInternal(ToD);
1951-
}
1924+
assert(ToDC == ToD->getLexicalDeclContext() && ToDC->containsDecl(ToD));
1925+
// Remove the decl from its wrong place in the linked list.
1926+
ToDC->removeDecl(ToD);
1927+
// Add the decl to the end of the linked list.
1928+
// This time it will be at the proper place because the enclosing for
1929+
// loop iterates in the original (good) order of the decls.
1930+
ToDC->addDeclInternal(ToD);
19521931
}
19531932
}
19541933

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

1948+
Error ASTNodeImporter::ImportFieldDeclDefinition(const FieldDecl *From,
1949+
const FieldDecl *To) {
1950+
RecordDecl *FromRecordDecl = nullptr;
1951+
RecordDecl *ToRecordDecl = nullptr;
1952+
// If we have a field that is an ArrayType we need to check if the array
1953+
// element is a RecordDecl and if so we need to import the definition.
1954+
QualType FromType = From->getType();
1955+
QualType ToType = To->getType();
1956+
if (FromType->isArrayType()) {
1957+
// getBaseElementTypeUnsafe(...) handles multi-dimensonal arrays for us.
1958+
FromRecordDecl = FromType->getBaseElementTypeUnsafe()->getAsRecordDecl();
1959+
ToRecordDecl = ToType->getBaseElementTypeUnsafe()->getAsRecordDecl();
1960+
}
1961+
1962+
if (!FromRecordDecl || !ToRecordDecl) {
1963+
const RecordType *RecordFrom = FromType->getAs<RecordType>();
1964+
const RecordType *RecordTo = ToType->getAs<RecordType>();
1965+
1966+
if (RecordFrom && RecordTo) {
1967+
FromRecordDecl = RecordFrom->getDecl();
1968+
ToRecordDecl = RecordTo->getDecl();
1969+
}
1970+
}
1971+
1972+
if (FromRecordDecl && ToRecordDecl) {
1973+
if (FromRecordDecl->isCompleteDefinition() &&
1974+
!ToRecordDecl->isCompleteDefinition())
1975+
return ImportDefinition(FromRecordDecl, ToRecordDecl);
1976+
}
1977+
1978+
return Error::success();
1979+
}
1980+
19581981
Error ASTNodeImporter::ImportDeclContext(
19591982
Decl *FromD, DeclContext *&ToDC, DeclContext *&ToLexicalDC) {
19601983
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
@@ -8007,6 +8007,27 @@ TEST_P(ASTImporterOptionSpecificTestBase, ImportDeductionGuideDifferentOrder) {
80078007
ToDGOther);
80088008
}
80098009

8010+
TEST_P(ASTImporterOptionSpecificTestBase,
8011+
ImportFieldsFirstForCorrectRecordLayoutTest) {
8012+
// UnaryOperator(&) triggers RecordLayout computation, which relies on
8013+
// correctly imported fields.
8014+
auto Code =
8015+
R"(
8016+
class A {
8017+
int m() {
8018+
return &((A *)0)->f1 - &((A *)0)->f2;
8019+
}
8020+
int f1;
8021+
int f2;
8022+
};
8023+
)";
8024+
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
8025+
8026+
auto *FromF = FirstDeclMatcher<CXXMethodDecl>().match(
8027+
FromTU, cxxMethodDecl(hasName("A::m")));
8028+
Import(FromF, Lang_CXX11);
8029+
}
8030+
80108031
TEST_P(ASTImporterOptionSpecificTestBase,
80118032
ImportRecordWithLayoutRequestingExpr) {
80128033
TranslationUnitDecl *FromTU = getTuDecl(

0 commit comments

Comments
 (0)