diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -2012,6 +2012,14 @@ } To->startDefinition(); + // Set the definition to complete even if it is really not complete during + // import. Some AST constructs (expressions) require the record layout + // to be calculated (see 'clang::computeDependence') at the time they are + // constructed. Import of such AST node is possible during import of the + // same record, there is no way to have a completely defined record (all + // fields imported) at that time without multiple AST import passes. + if (!Importer.isMinimalImport()) + To->setCompleteDefinition(true); // Complete the definition even if error is returned. // The RecordDecl may be already part of the AST so it is better to // have it in complete state even if something is wrong with it. @@ -2076,9 +2084,10 @@ ToCXX->setBases(Bases.data(), Bases.size()); } - if (shouldForceImportDeclContext(Kind)) + if (shouldForceImportDeclContext(Kind)) { if (Error Err = ImportDeclContext(From, /*ForceImport=*/true)) return Err; + } return Error::success(); } diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -6809,7 +6809,8 @@ bool MinimalImport, const std::shared_ptr &SharedState) { return new ASTImporter(ToContext, ToFileManager, FromContext, - FromFileManager, MinimalImport, + // Use minimal import for these tests. + FromFileManager, /*MinimalImport=*/true, // We use the regular lookup. /*SharedState=*/nullptr); }; @@ -7475,6 +7476,57 @@ ToDGOther); } +TEST_P(ASTImporterOptionSpecificTestBase, + ImportRecordWithLayoutRequestingExpr) { + TranslationUnitDecl *FromTU = getTuDecl( + R"( + struct A { + int idx; + static void foo(A x) { + (void)&"text"[x.idx]; + } + }; + )", + Lang_CXX11); + + auto *FromA = FirstDeclMatcher().match( + FromTU, cxxRecordDecl(hasName("A"))); + + // Test that during import of 'foo' the record layout can be obtained without + // crash. + auto *ToA = Import(FromA, Lang_CXX11); + EXPECT_TRUE(ToA); + EXPECT_TRUE(ToA->isCompleteDefinition()); +} + +TEST_P(ASTImporterOptionSpecificTestBase, + ImportRecordWithLayoutRequestingExprDifferentRecord) { + TranslationUnitDecl *FromTU = getTuDecl( + R"( + struct B; + struct A { + int idx; + B *b; + }; + struct B { + static void foo(A x) { + (void)&"text"[x.idx]; + } + }; + )", + Lang_CXX11); + + auto *FromA = FirstDeclMatcher().match( + FromTU, cxxRecordDecl(hasName("A"))); + + // Test that during import of 'foo' the record layout (of 'A') can be obtained + // without crash. It is not possible to have all of the fields of 'A' imported + // at that time (without big code changes). + auto *ToA = Import(FromA, Lang_CXX11); + EXPECT_TRUE(ToA); + EXPECT_TRUE(ToA->isCompleteDefinition()); +} + INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ASTImporterLookupTableTest, DefaultTestValuesForRunOptions);