Skip to content

Commit 1654634

Browse files
committed
[clang][ASTImporter] Fix partially import of repeated friend templates
Pointer comparison is not reliable in this case. Use ASTStructuralEquivalence to compute FriendCountAndPosition. Reviewed By: balazske Differential Revision: https://reviews.llvm.org/D157684
1 parent 935ba83 commit 1654634

File tree

2 files changed

+68
-69
lines changed

2 files changed

+68
-69
lines changed

clang/lib/AST/ASTImporter.cpp

Lines changed: 25 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4079,22 +4079,34 @@ struct FriendCountAndPosition {
40794079
unsigned int IndexOfDecl;
40804080
};
40814081

4082-
template <class T>
4083-
static FriendCountAndPosition getFriendCountAndPosition(
4084-
const FriendDecl *FD,
4085-
llvm::function_ref<T(const FriendDecl *)> GetCanTypeOrDecl) {
4082+
static bool IsEquivalentFriend(ASTImporter &Importer, FriendDecl *FD1,
4083+
FriendDecl *FD2) {
4084+
if ((!FD1->getFriendType()) != (!FD2->getFriendType()))
4085+
return false;
4086+
4087+
if (const TypeSourceInfo *TSI = FD1->getFriendType())
4088+
return Importer.IsStructurallyEquivalent(
4089+
TSI->getType(), FD2->getFriendType()->getType(), /*Complain=*/false);
4090+
4091+
ASTImporter::NonEquivalentDeclSet NonEquivalentDecls;
4092+
StructuralEquivalenceContext Ctx(
4093+
FD1->getASTContext(), FD2->getASTContext(), NonEquivalentDecls,
4094+
StructuralEquivalenceKind::Default,
4095+
/* StrictTypeSpelling = */ false, /* Complain = */ false);
4096+
return Ctx.IsEquivalent(FD1, FD2);
4097+
}
4098+
4099+
static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer,
4100+
FriendDecl *FD) {
40864101
unsigned int FriendCount = 0;
40874102
std::optional<unsigned int> FriendPosition;
40884103
const auto *RD = cast<CXXRecordDecl>(FD->getLexicalDeclContext());
40894104

4090-
T TypeOrDecl = GetCanTypeOrDecl(FD);
4091-
4092-
for (const FriendDecl *FoundFriend : RD->friends()) {
4105+
for (FriendDecl *FoundFriend : RD->friends()) {
40934106
if (FoundFriend == FD) {
40944107
FriendPosition = FriendCount;
40954108
++FriendCount;
4096-
} else if (!FoundFriend->getFriendDecl() == !FD->getFriendDecl() &&
4097-
GetCanTypeOrDecl(FoundFriend) == TypeOrDecl) {
4109+
} else if (IsEquivalentFriend(Importer, FD, FoundFriend)) {
40984110
++FriendCount;
40994111
}
41004112
}
@@ -4104,21 +4116,6 @@ static FriendCountAndPosition getFriendCountAndPosition(
41044116
return {FriendCount, *FriendPosition};
41054117
}
41064118

4107-
static FriendCountAndPosition getFriendCountAndPosition(const FriendDecl *FD) {
4108-
if (FD->getFriendType())
4109-
return getFriendCountAndPosition<QualType>(FD, [](const FriendDecl *F) {
4110-
if (TypeSourceInfo *TSI = F->getFriendType())
4111-
return TSI->getType().getCanonicalType();
4112-
llvm_unreachable("Wrong friend object type.");
4113-
});
4114-
else
4115-
return getFriendCountAndPosition<Decl *>(FD, [](const FriendDecl *F) {
4116-
if (Decl *D = F->getFriendDecl())
4117-
return D->getCanonicalDecl();
4118-
llvm_unreachable("Wrong friend object type.");
4119-
});
4120-
}
4121-
41224119
ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
41234120
// Import the major distinguishing characteristics of a declaration.
41244121
DeclContext *DC, *LexicalDC;
@@ -4129,26 +4126,13 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
41294126
// FriendDecl is not a NamedDecl so we cannot use lookup.
41304127
// We try to maintain order and count of redundant friend declarations.
41314128
const auto *RD = cast<CXXRecordDecl>(DC);
4132-
FriendDecl *ImportedFriend = RD->getFirstFriend();
41334129
SmallVector<FriendDecl *, 2> ImportedEquivalentFriends;
4134-
4135-
while (ImportedFriend) {
4136-
bool Match = false;
4137-
if (D->getFriendDecl() && ImportedFriend->getFriendDecl()) {
4138-
Match =
4139-
IsStructuralMatch(D->getFriendDecl(), ImportedFriend->getFriendDecl(),
4140-
/*Complain=*/false);
4141-
} else if (D->getFriendType() && ImportedFriend->getFriendType()) {
4142-
Match = Importer.IsStructurallyEquivalent(
4143-
D->getFriendType()->getType(),
4144-
ImportedFriend->getFriendType()->getType(), /*Complain=*/false);
4145-
}
4146-
if (Match)
4130+
for (FriendDecl *ImportedFriend : RD->friends())
4131+
if (IsEquivalentFriend(Importer, D, ImportedFriend))
41474132
ImportedEquivalentFriends.push_back(ImportedFriend);
41484133

4149-
ImportedFriend = ImportedFriend->getNextFriend();
4150-
}
4151-
FriendCountAndPosition CountAndPosition = getFriendCountAndPosition(D);
4134+
FriendCountAndPosition CountAndPosition =
4135+
getFriendCountAndPosition(Importer, D);
41524136

41534137
assert(ImportedEquivalentFriends.size() <= CountAndPosition.TotalCount &&
41544138
"Class with non-matching friends is imported, ODR check wrong?");

clang/unittests/AST/ASTImporterTest.cpp

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4054,6 +4054,25 @@ struct ImportFriendClasses : ASTImporterOptionSpecificTestBase {
40544054
->lookup(ToRecordOfFriend->getDeclName())
40554055
.empty());
40564056
}
4057+
4058+
void testRepeatedFriendImport(const char *Code) {
4059+
Decl *ToTu = getToTuDecl(Code, Lang_CXX03);
4060+
Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc");
4061+
4062+
auto *ToFriend1 = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
4063+
auto *ToFriend2 = LastDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
4064+
auto *FromFriend1 =
4065+
FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
4066+
auto *FromFriend2 =
4067+
LastDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
4068+
4069+
FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03);
4070+
FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03);
4071+
4072+
EXPECT_NE(ToImportedFriend1, ToImportedFriend2);
4073+
EXPECT_EQ(ToFriend1, ToImportedFriend1);
4074+
EXPECT_EQ(ToFriend2, ToImportedFriend2);
4075+
}
40574076
};
40584077

40594078
TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) {
@@ -4395,21 +4414,7 @@ TEST_P(ImportFriendClasses, ImportOfRepeatedFriendType) {
43954414
friend class X;
43964415
};
43974416
)";
4398-
Decl *ToTu = getToTuDecl(Code, Lang_CXX03);
4399-
Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc");
4400-
4401-
auto *ToFriend1 = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
4402-
auto *ToFriend2 = LastDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
4403-
auto *FromFriend1 =
4404-
FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
4405-
auto *FromFriend2 = LastDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
4406-
4407-
FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03);
4408-
FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03);
4409-
4410-
EXPECT_NE(ToImportedFriend1, ToImportedFriend2);
4411-
EXPECT_EQ(ToFriend1, ToImportedFriend1);
4412-
EXPECT_EQ(ToFriend2, ToImportedFriend2);
4417+
testRepeatedFriendImport(Code);
44134418
}
44144419

44154420
TEST_P(ImportFriendClasses, ImportOfRepeatedFriendDecl) {
@@ -4420,21 +4425,31 @@ TEST_P(ImportFriendClasses, ImportOfRepeatedFriendDecl) {
44204425
friend void f();
44214426
};
44224427
)";
4423-
Decl *ToTu = getToTuDecl(Code, Lang_CXX03);
4424-
Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc");
4425-
4426-
auto *ToFriend1 = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
4427-
auto *ToFriend2 = LastDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
4428-
auto *FromFriend1 =
4429-
FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
4430-
auto *FromFriend2 = LastDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
4428+
testRepeatedFriendImport(Code);
4429+
}
44314430

4432-
FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03);
4433-
FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03);
4431+
TEST_P(ImportFriendClasses, ImportOfRepeatedFriendFunctionTemplateDecl) {
4432+
const char *Code =
4433+
R"(
4434+
template <class T>
4435+
class Container {
4436+
template <class U> friend void m();
4437+
template <class U> friend void m();
4438+
};
4439+
)";
4440+
testRepeatedFriendImport(Code);
4441+
}
44344442

4435-
EXPECT_NE(ToImportedFriend1, ToImportedFriend2);
4436-
EXPECT_EQ(ToFriend1, ToImportedFriend1);
4437-
EXPECT_EQ(ToFriend2, ToImportedFriend2);
4443+
TEST_P(ImportFriendClasses, ImportOfRepeatedFriendClassTemplateDecl) {
4444+
const char *Code =
4445+
R"(
4446+
template <class T>
4447+
class Container {
4448+
template <class U> friend class X;
4449+
template <class U> friend class X;
4450+
};
4451+
)";
4452+
testRepeatedFriendImport(Code);
44384453
}
44394454

44404455
TEST_P(ASTImporterOptionSpecificTestBase, FriendFunInClassTemplate) {

0 commit comments

Comments
 (0)