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.