[ASTImporter] FriendDecl importing improvements

There are only a few cases of importing a frienddecl which is currently supported.
This patch aims to improve the friend import process.
Set FriendObjectKind in case of decls, insert friend into the friend chain
correctly, checks structurally equivalent in a more advanced manner.
Test cases added as well.  

llvm-svn: 330847
This commit is contained in:
Peter Szecsi 2018-04-25 17:28:03 +00:00
parent cd8688a4c2
commit b180eebed4
7 changed files with 159 additions and 5 deletions

View File

@ -309,6 +309,7 @@ private:
protected:
friend class ASTDeclReader;
friend class ASTDeclWriter;
friend class ASTImporter;
friend class ASTReader;
friend class CXXClassMemberWrapper;
friend class LinkageComputer;

View File

@ -273,6 +273,8 @@ def note_odr_objc_synthesize_ivar_here : Note<
"property is synthesized to ivar %0 here">;
// Importing C++ ASTs
def note_odr_friend : Note<"friend declared here">;
def note_odr_missing_friend : Note<"no corresponding friend here">;
def err_odr_different_num_template_parameters : Error<
"template parameter lists have a different number of parameters (%0 vs %1)">;
def note_odr_template_parameter_list : Note<

View File

@ -2709,9 +2709,14 @@ Decl *ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
// Not found. Create it.
FriendDecl::FriendUnion ToFU;
if (NamedDecl *FriendD = D->getFriendDecl())
ToFU = cast_or_null<NamedDecl>(Importer.Import(FriendD));
else
if (NamedDecl *FriendD = D->getFriendDecl()) {
auto *ToFriendD = cast_or_null<NamedDecl>(Importer.Import(FriendD));
if (ToFriendD && FriendD->getFriendObjectKind() != Decl::FOK_None &&
!(FriendD->isInIdentifierNamespace(Decl::IDNS_NonMemberOperator)))
ToFriendD->setObjectOfFriendDecl(false);
ToFU = ToFriendD;
} else // The friend is a type, not a decl.
ToFU = Importer.Import(D->getFriendType());
if (!ToFU)
return nullptr;
@ -2731,7 +2736,6 @@ Decl *ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
ToTPLists);
Importer.Imported(D, FrD);
RD->pushFriendDecl(FrD);
FrD->setAccess(D->getAccess());
FrD->setLexicalDeclContext(LexicalDC);
@ -6596,7 +6600,7 @@ Decl *ASTImporter::Import(Decl *FromD) {
// Record the imported declaration.
ImportedDecls[FromD] = ToD;
ToD->IdentifierNamespace = FromD->IdentifierNamespace;
return ToD;
}

View File

@ -18,6 +18,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/NestedNameSpecifier.h"
@ -942,6 +943,44 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
}
}
// Check the friends for consistency.
CXXRecordDecl::friend_iterator Friend2 = D2CXX->friend_begin(),
Friend2End = D2CXX->friend_end();
for (CXXRecordDecl::friend_iterator Friend1 = D1CXX->friend_begin(),
Friend1End = D1CXX->friend_end();
Friend1 != Friend1End; ++Friend1, ++Friend2) {
if (Friend2 == Friend2End) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
diag::warn_odr_tag_type_inconsistent)
<< Context.ToCtx.getTypeDeclType(D2CXX);
Context.Diag1((*Friend1)->getFriendLoc(), diag::note_odr_friend);
Context.Diag2(D2->getLocation(), diag::note_odr_missing_friend);
}
return false;
}
if (!IsStructurallyEquivalent(Context, *Friend1, *Friend2)) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent)
<< Context.ToCtx.getTypeDeclType(D2CXX);
Context.Diag1((*Friend1)->getFriendLoc(), diag::note_odr_friend);
Context.Diag2((*Friend2)->getFriendLoc(), diag::note_odr_friend);
}
return false;
}
}
if (Friend2 != Friend2End) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent)
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag2((*Friend2)->getFriendLoc(), diag::note_odr_friend);
Context.Diag1(D1->getLocation(), diag::note_odr_missing_friend);
}
return false;
}
} else if (D1CXX->getNumBases() > 0) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent)
@ -1184,6 +1223,31 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
D2->getTemplatedDecl()->getType());
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
FriendDecl *D1, FriendDecl *D2) {
if ((D1->getFriendType() && D2->getFriendDecl()) ||
(D1->getFriendDecl() && D2->getFriendType())) {
return false;
}
if (D1->getFriendType() && D2->getFriendType())
return IsStructurallyEquivalent(Context,
D1->getFriendType()->getType(),
D2->getFriendType()->getType());
if (D1->getFriendDecl() && D2->getFriendDecl())
return IsStructurallyEquivalent(Context, D1->getFriendDecl(),
D2->getFriendDecl());
return false;
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
FunctionDecl *D1, FunctionDecl *D2) {
// FIXME: Consider checking for function attributes as well.
if (!IsStructurallyEquivalent(Context, D1->getType(), D2->getType()))
return false;
return true;
}
/// Determine structural equivalence of two declarations.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
Decl *D1, Decl *D2) {
@ -1381,6 +1445,25 @@ bool StructuralEquivalenceContext::Finish() {
// Kind mismatch.
Equivalent = false;
}
} else if (FunctionDecl *FD1 = dyn_cast<FunctionDecl>(D1)) {
if (FunctionDecl *FD2 = dyn_cast<FunctionDecl>(D2)) {
if (!::IsStructurallyEquivalent(FD1->getIdentifier(),
FD2->getIdentifier()))
Equivalent = false;
if (!::IsStructurallyEquivalent(*this, FD1, FD2))
Equivalent = false;
} else {
// Kind mismatch.
Equivalent = false;
}
} else if (FriendDecl *FrD1 = dyn_cast<FriendDecl>(D1)) {
if (FriendDecl *FrD2 = dyn_cast<FriendDecl>(D2)) {
if (!::IsStructurallyEquivalent(*this, FrD1, FrD2))
Equivalent = false;
} else {
// Kind mismatch.
Equivalent = false;
}
}
if (!Equivalent) {

View File

@ -18,3 +18,31 @@ struct C {
enum E {
b = 1
};
//Friend import tests
void f();
int g(int a);
struct X;
struct Y;
struct F1 {
public:
int x;
friend struct X;
friend int g(int);
friend void f();
};
struct F2 {
public:
int x;
friend struct X;
friend void f();
};
struct F3 {
public:
int x;
friend int g(int);
friend void f();
};

View File

@ -12,3 +12,29 @@ enum E {
a = 0,
b = 1
};
//Friend import tests
void f();
int g(int a);
struct X;
struct Y;
struct F1 {
public:
int x;
friend struct X;
friend int g(int);
friend void f();
};
struct F2 {
public:
int x;
friend struct X;
};
struct F3 {
public:
int x;
friend void f();
};

View File

@ -12,3 +12,13 @@
// CHECK: class1.cpp:18:6: warning: type 'E' has incompatible definitions in different translation units
// CHECK: class1.cpp:19:3: note: enumerator 'b' with value 1 here
// CHECK: class2.cpp:12:3: note: enumerator 'a' with value 0 here
// CHECK: class1.cpp:36:8: warning: type 'F2' has incompatible definitions in different translation units
// CHECK: class1.cpp:39:3: note: friend declared here
// CHECK: class2.cpp:30:8: note: no corresponding friend here
// CHECK: class1.cpp:43:8: warning: type 'F3' has incompatible definitions in different translation units
// CHECK: class1.cpp:46:3: note: friend declared here
// CHECK: class2.cpp:36:8: note: no corresponding friend here
// CHECK: 4 warnings generated.