Skip to content

Commit 5967528

Browse files
committed
[clang][ASTImporter] Fix an import error handling related bug.
This bug can cause that more import errors are generated than necessary and many objects fail to import. Chance of an invalid AST after these imports increases. Reviewed By: martong Differential Revision: https://reviews.llvm.org/D122525
1 parent 93471e6 commit 5967528

File tree

2 files changed

+106
-12
lines changed

2 files changed

+106
-12
lines changed

clang/lib/AST/ASTImporter.cpp

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,46 @@ namespace clang {
138138
To->setIsUsed();
139139
}
140140

141+
/// How to handle import errors that occur when import of a child declaration
142+
/// of a DeclContext fails.
143+
class ChildErrorHandlingStrategy {
144+
/// This context is imported (in the 'from' domain).
145+
/// It is nullptr if a non-DeclContext is imported.
146+
const DeclContext *const FromDC;
147+
/// Ignore import errors of the children.
148+
/// If true, the context can be imported successfully if a child
149+
/// of it failed to import. Otherwise the import errors of the child nodes
150+
/// are accumulated (joined) into the import error object of the parent.
151+
/// (Import of a parent can fail in other ways.)
152+
bool const IgnoreChildErrors;
153+
154+
public:
155+
ChildErrorHandlingStrategy(const DeclContext *FromDC)
156+
: FromDC(FromDC), IgnoreChildErrors(!isa<TagDecl>(FromDC)) {}
157+
ChildErrorHandlingStrategy(const Decl *FromD)
158+
: FromDC(dyn_cast<DeclContext>(FromD)),
159+
IgnoreChildErrors(!isa<TagDecl>(FromD)) {}
160+
161+
/// Process the import result of a child (of the current declaration).
162+
/// \param ResultErr The import error that can be used as result of
163+
/// importing the parent. This may be changed by the function.
164+
/// \param ChildErr Result of importing a child. Can be success or error.
165+
void handleChildImportResult(Error &ResultErr, Error &&ChildErr) {
166+
if (ChildErr && !IgnoreChildErrors)
167+
ResultErr = joinErrors(std::move(ResultErr), std::move(ChildErr));
168+
else
169+
consumeError(std::move(ChildErr));
170+
}
171+
172+
/// Determine if import failure of a child does not cause import failure of
173+
/// its parent.
174+
bool ignoreChildErrorOnParent(Decl *FromChildD) const {
175+
if (!IgnoreChildErrors || !FromDC)
176+
return false;
177+
return FromDC->containsDecl(FromChildD);
178+
}
179+
};
180+
141181
class ASTNodeImporter : public TypeVisitor<ASTNodeImporter, ExpectedType>,
142182
public DeclVisitor<ASTNodeImporter, ExpectedDecl>,
143183
public StmtVisitor<ASTNodeImporter, ExpectedStmt> {
@@ -1809,7 +1849,7 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
18091849
// because there is an ODR error with two typedefs. As another example,
18101850
// the client may allow EnumConstantDecls with same names but with
18111851
// different values in two distinct translation units.
1812-
bool AccumulateChildErrors = isa<TagDecl>(FromDC);
1852+
ChildErrorHandlingStrategy HandleChildErrors(FromDC);
18131853

18141854
Error ChildErrors = Error::success();
18151855
for (auto *From : FromDC->decls()) {
@@ -1849,20 +1889,14 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
18491889
if (FromRecordDecl->isCompleteDefinition() &&
18501890
!ToRecordDecl->isCompleteDefinition()) {
18511891
Error Err = ImportDefinition(FromRecordDecl, ToRecordDecl);
1852-
1853-
if (Err && AccumulateChildErrors)
1854-
ChildErrors = joinErrors(std::move(ChildErrors), std::move(Err));
1855-
else
1856-
consumeError(std::move(Err));
1892+
HandleChildErrors.handleChildImportResult(ChildErrors,
1893+
std::move(Err));
18571894
}
18581895
}
18591896
}
18601897
} else {
1861-
if (AccumulateChildErrors)
1862-
ChildErrors =
1863-
joinErrors(std::move(ChildErrors), ImportedOrErr.takeError());
1864-
else
1865-
consumeError(ImportedOrErr.takeError());
1898+
HandleChildErrors.handleChildImportResult(ChildErrors,
1899+
ImportedOrErr.takeError());
18661900
}
18671901
}
18681902

@@ -8799,8 +8833,20 @@ Expected<Decl *> ASTImporter::Import(Decl *FromD) {
87998833

88008834
// Set the error for all nodes which have been created before we
88018835
// recognized the error.
8802-
for (const auto &Path : SavedImportPaths[FromD])
8836+
for (const auto &Path : SavedImportPaths[FromD]) {
8837+
// The import path contains import-dependency nodes first.
8838+
// Save the node that was imported as dependency of the current node.
8839+
Decl *PrevFromDi = FromD;
88038840
for (Decl *FromDi : Path) {
8841+
// Begin and end of the path equals 'FromD', skip it.
8842+
if (FromDi == FromD)
8843+
continue;
8844+
// We should not set import error on a node and all following nodes in
8845+
// the path if child import errors are ignored.
8846+
if (ChildErrorHandlingStrategy(FromDi).ignoreChildErrorOnParent(
8847+
PrevFromDi))
8848+
break;
8849+
PrevFromDi = FromDi;
88048850
setImportDeclError(FromDi, ErrOut);
88058851
//FIXME Should we remove these Decls from ImportedDecls?
88068852
// Set the error for the mapped to Decl, which is in the "to" context.
@@ -8810,6 +8856,7 @@ Expected<Decl *> ASTImporter::Import(Decl *FromD) {
88108856
// FIXME Should we remove these Decls from the LookupTable,
88118857
// and from ImportedFromDecls?
88128858
}
8859+
}
88138860
SavedImportPaths.erase(FromD);
88148861

88158862
// Do not return ToDOrErr, error was taken out of it.

clang/unittests/AST/ASTImporterTest.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5821,6 +5821,53 @@ TEST_P(ErrorHandlingTest, ODRViolationWithinParmVarDecls) {
58215821
EXPECT_FALSE(ImportedF);
58225822
}
58235823

5824+
TEST_P(ErrorHandlingTest, DoNotInheritErrorFromNonDependentChild) {
5825+
// Declarations should not inherit an import error from a child object
5826+
// if the declaration has no direct dependence to such a child.
5827+
// For example a namespace should not get import error if one of the
5828+
// declarations inside it fails to import.
5829+
// There was a special case in error handling (when "import path circles" are
5830+
// encountered) when this property was not held. This case is provoked by the
5831+
// following code.
5832+
constexpr auto ToTUCode = R"(
5833+
namespace ns {
5834+
struct Err {
5835+
char A;
5836+
};
5837+
}
5838+
)";
5839+
constexpr auto FromTUCode = R"(
5840+
namespace ns {
5841+
struct A {
5842+
using U = struct Err;
5843+
};
5844+
}
5845+
namespace ns {
5846+
struct Err {}; // ODR violation
5847+
void f(A) {}
5848+
}
5849+
)";
5850+
5851+
Decl *ToTU = getToTuDecl(ToTUCode, Lang_CXX11);
5852+
static_cast<void>(ToTU);
5853+
Decl *FromTU = getTuDecl(FromTUCode, Lang_CXX11);
5854+
auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
5855+
FromTU, cxxRecordDecl(hasName("A"), hasDefinition()));
5856+
ASSERT_TRUE(FromA);
5857+
auto *ImportedA = Import(FromA, Lang_CXX11);
5858+
// 'A' can not be imported: ODR error at 'Err'
5859+
EXPECT_FALSE(ImportedA);
5860+
// When import of 'A' failed there was a "saved import path circle" that
5861+
// contained namespace 'ns' (A - U - Err - ns - f - A). This should not mean
5862+
// that every object in this path fails to import.
5863+
5864+
Decl *FromNS = FirstDeclMatcher<NamespaceDecl>().match(
5865+
FromTU, namespaceDecl(hasName("ns")));
5866+
EXPECT_TRUE(FromNS);
5867+
auto *ImportedNS = Import(FromNS, Lang_CXX11);
5868+
EXPECT_TRUE(ImportedNS);
5869+
}
5870+
58245871
TEST_P(ASTImporterOptionSpecificTestBase, LambdaInFunctionBody) {
58255872
Decl *FromTU = getTuDecl(
58265873
R"(

0 commit comments

Comments
 (0)