[ASTImporter] Corrected import of repeated friend declarations.

Summary:
Import declarations in correct order if a class contains
multiple redundant friend (type or decl) declarations.
If the order is incorrect this could cause false structural
equivalences and wrong declaration chains after import.

Reviewers: a.sidorin, shafik, a_sidorin

Reviewed By: shafik

Subscribers: dkrupp, Szelethus, gamesh411, teemperor, martong, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D75740
This commit is contained in:
Balázs Kéri 2020-07-07 14:21:18 +02:00
parent c5348aecd7
commit 85f5d1261c
3 changed files with 140 additions and 9 deletions

View File

@ -3646,6 +3646,54 @@ ExpectedDecl ASTNodeImporter::VisitIndirectFieldDecl(IndirectFieldDecl *D) {
return ToIndirectField; return ToIndirectField;
} }
/// Used as return type of getFriendCountAndPosition.
struct FriendCountAndPosition {
/// Number of similar looking friends.
unsigned int TotalCount;
/// Index of the specific FriendDecl.
unsigned int IndexOfDecl;
};
template <class T>
FriendCountAndPosition getFriendCountAndPosition(
const FriendDecl *FD,
std::function<T(const FriendDecl *)> GetCanTypeOrDecl) {
unsigned int FriendCount = 0;
llvm::Optional<unsigned int> FriendPosition;
const auto *RD = cast<CXXRecordDecl>(FD->getLexicalDeclContext());
T TypeOrDecl = GetCanTypeOrDecl(FD);
for (const FriendDecl *FoundFriend : RD->friends()) {
if (FoundFriend == FD) {
FriendPosition = FriendCount;
++FriendCount;
} else if (!FoundFriend->getFriendDecl() == !FD->getFriendDecl() &&
GetCanTypeOrDecl(FoundFriend) == TypeOrDecl) {
++FriendCount;
}
}
assert(FriendPosition && "Friend decl not found in own parent.");
return {FriendCount, *FriendPosition};
}
FriendCountAndPosition getFriendCountAndPosition(const FriendDecl *FD) {
if (FD->getFriendType())
return getFriendCountAndPosition<QualType>(FD, [](const FriendDecl *F) {
if (TypeSourceInfo *TSI = F->getFriendType())
return TSI->getType().getCanonicalType();
llvm_unreachable("Wrong friend object type.");
});
else
return getFriendCountAndPosition<Decl *>(FD, [](const FriendDecl *F) {
if (Decl *D = F->getFriendDecl())
return D->getCanonicalDecl();
llvm_unreachable("Wrong friend object type.");
});
}
ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) { ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
// Import the major distinguishing characteristics of a declaration. // Import the major distinguishing characteristics of a declaration.
DeclContext *DC, *LexicalDC; DeclContext *DC, *LexicalDC;
@ -3654,25 +3702,37 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
// Determine whether we've already imported this decl. // Determine whether we've already imported this decl.
// FriendDecl is not a NamedDecl so we cannot use lookup. // FriendDecl is not a NamedDecl so we cannot use lookup.
auto *RD = cast<CXXRecordDecl>(DC); // We try to maintain order and count of redundant friend declarations.
const auto *RD = cast<CXXRecordDecl>(DC);
FriendDecl *ImportedFriend = RD->getFirstFriend(); FriendDecl *ImportedFriend = RD->getFirstFriend();
SmallVector<FriendDecl *, 2> ImportedEquivalentFriends;
while (ImportedFriend) { while (ImportedFriend) {
bool Match = false;
if (D->getFriendDecl() && ImportedFriend->getFriendDecl()) { if (D->getFriendDecl() && ImportedFriend->getFriendDecl()) {
if (IsStructuralMatch(D->getFriendDecl(), ImportedFriend->getFriendDecl(), Match =
/*Complain=*/false)) IsStructuralMatch(D->getFriendDecl(), ImportedFriend->getFriendDecl(),
return Importer.MapImported(D, ImportedFriend); /*Complain=*/false);
} else if (D->getFriendType() && ImportedFriend->getFriendType()) { } else if (D->getFriendType() && ImportedFriend->getFriendType()) {
if (Importer.IsStructurallyEquivalent( Match = Importer.IsStructurallyEquivalent(
D->getFriendType()->getType(), D->getFriendType()->getType(),
ImportedFriend->getFriendType()->getType(), true)) ImportedFriend->getFriendType()->getType(), /*Complain=*/false);
return Importer.MapImported(D, ImportedFriend);
} }
if (Match)
ImportedEquivalentFriends.push_back(ImportedFriend);
ImportedFriend = ImportedFriend->getNextFriend(); ImportedFriend = ImportedFriend->getNextFriend();
} }
FriendCountAndPosition CountAndPosition = getFriendCountAndPosition(D);
assert(ImportedEquivalentFriends.size() <= CountAndPosition.TotalCount &&
"Class with non-matching friends is imported, ODR check wrong?");
if (ImportedEquivalentFriends.size() == CountAndPosition.TotalCount)
return Importer.MapImported(
D, ImportedEquivalentFriends[CountAndPosition.IndexOfDecl]);
// Not found. Create it. // Not found. Create it.
// The declarations will be put into order later by ImportDeclContext.
FriendDecl::FriendUnion ToFU; FriendDecl::FriendUnion ToFU;
if (NamedDecl *FriendD = D->getFriendDecl()) { if (NamedDecl *FriendD = D->getFriendDecl()) {
NamedDecl *ToFriendD; NamedDecl *ToFriendD;

View File

@ -3974,6 +3974,56 @@ TEST_P(ImportFriendClasses, ImportOfClassDefinitionAndFwdFriendShouldBeLinked) {
EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl()); EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
} }
TEST_P(ImportFriendClasses, ImportOfRepeatedFriendType) {
const char *Code =
R"(
class Container {
friend class X;
friend class X;
};
)";
Decl *ToTu = getToTuDecl(Code, Lang_CXX03);
Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc");
auto *ToFriend1 = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
auto *ToFriend2 = LastDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
auto *FromFriend1 =
FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
auto *FromFriend2 = LastDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03);
FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03);
EXPECT_NE(ToImportedFriend1, ToImportedFriend2);
EXPECT_EQ(ToFriend1, ToImportedFriend1);
EXPECT_EQ(ToFriend2, ToImportedFriend2);
}
TEST_P(ImportFriendClasses, ImportOfRepeatedFriendDecl) {
const char *Code =
R"(
class Container {
friend void f();
friend void f();
};
)";
Decl *ToTu = getToTuDecl(Code, Lang_CXX03);
Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc");
auto *ToFriend1 = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
auto *ToFriend2 = LastDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
auto *FromFriend1 =
FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
auto *FromFriend2 = LastDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03);
FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03);
EXPECT_NE(ToImportedFriend1, ToImportedFriend2);
EXPECT_EQ(ToFriend1, ToImportedFriend1);
EXPECT_EQ(ToFriend2, ToImportedFriend2);
}
TEST_P(ASTImporterOptionSpecificTestBase, FriendFunInClassTemplate) { TEST_P(ASTImporterOptionSpecificTestBase, FriendFunInClassTemplate) {
auto *Code = R"( auto *Code = R"(
template <class T> template <class T>

View File

@ -759,6 +759,27 @@ TEST_F(StructuralEquivalenceRecordTest, RecordsWithDifferentBody) {
EXPECT_FALSE(testStructuralMatch(t)); EXPECT_FALSE(testStructuralMatch(t));
} }
TEST_F(StructuralEquivalenceRecordTest, SameFriendMultipleTimes) {
auto t = makeNamedDecls("struct foo { friend class X; };",
"struct foo { friend class X; friend class X; };",
Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest, SameFriendsDifferentOrder) {
auto t = makeNamedDecls("struct foo { friend class X; friend class Y; };",
"struct foo { friend class Y; friend class X; };",
Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest, SameFriendsSameOrder) {
auto t = makeNamedDecls("struct foo { friend class X; friend class Y; };",
"struct foo { friend class X; friend class Y; };",
Lang_CXX11);
EXPECT_TRUE(testStructuralMatch(t));
}
struct StructuralEquivalenceLambdaTest : StructuralEquivalenceTest {}; struct StructuralEquivalenceLambdaTest : StructuralEquivalenceTest {};
TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithDifferentMethods) { TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithDifferentMethods) {