From b180eebed4239e45822b6d20f97241583bdc054b Mon Sep 17 00:00:00 2001 From: Peter Szecsi Date: Wed, 25 Apr 2018 17:28:03 +0000 Subject: [PATCH] [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 --- clang/include/clang/AST/DeclBase.h | 1 + .../include/clang/Basic/DiagnosticASTKinds.td | 2 + clang/lib/AST/ASTImporter.cpp | 14 ++-- clang/lib/AST/ASTStructuralEquivalence.cpp | 83 +++++++++++++++++++ clang/test/ASTMerge/class/Inputs/class1.cpp | 28 +++++++ clang/test/ASTMerge/class/Inputs/class2.cpp | 26 ++++++ clang/test/ASTMerge/class/test.cpp | 10 +++ 7 files changed, 159 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 908894c133c6..1243a67397d5 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -309,6 +309,7 @@ private: protected: friend class ASTDeclReader; friend class ASTDeclWriter; + friend class ASTImporter; friend class ASTReader; friend class CXXClassMemberWrapper; friend class LinkageComputer; diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index 215580b2e9b6..4fa1db96cb89 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -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< diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index a2e6c01dc292..17ff23c5fe09 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -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(Importer.Import(FriendD)); - else + if (NamedDecl *FriendD = D->getFriendDecl()) { + auto *ToFriendD = cast_or_null(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; } diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index 618d27e9b6db..05f414e9fb10 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -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(D1)) { + if (FunctionDecl *FD2 = dyn_cast(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(D1)) { + if (FriendDecl *FrD2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(*this, FrD1, FrD2)) + Equivalent = false; + } else { + // Kind mismatch. + Equivalent = false; + } } if (!Equivalent) { diff --git a/clang/test/ASTMerge/class/Inputs/class1.cpp b/clang/test/ASTMerge/class/Inputs/class1.cpp index b0a7645cfe63..2bd5503ecf3d 100644 --- a/clang/test/ASTMerge/class/Inputs/class1.cpp +++ b/clang/test/ASTMerge/class/Inputs/class1.cpp @@ -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(); +}; diff --git a/clang/test/ASTMerge/class/Inputs/class2.cpp b/clang/test/ASTMerge/class/Inputs/class2.cpp index 2bed6d775bc4..6fe38b920662 100644 --- a/clang/test/ASTMerge/class/Inputs/class2.cpp +++ b/clang/test/ASTMerge/class/Inputs/class2.cpp @@ -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(); +}; diff --git a/clang/test/ASTMerge/class/test.cpp b/clang/test/ASTMerge/class/test.cpp index a68a2d1d7690..99926b649bc9 100644 --- a/clang/test/ASTMerge/class/test.cpp +++ b/clang/test/ASTMerge/class/test.cpp @@ -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.