From 74d28bd084a29eac89aa16ed452582df91cfa1fd Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 29 Jun 2010 22:47:00 +0000 Subject: [PATCH] Support C++ friend declarations for PCH. This commit 'introduces' a slightly different way to restore the state of the AST object. It makes PCHDeclReader/PCHDeclWriter friends and gives them access to the private members of the object. The rationale is to avoid using/modifying the AST interfaces for PCH read/write so that to: -Avoid complications with objects that have side-effects during creation or when using some setters. -Not 'pollute' the AST interface with methods only used by the PCH reader/writer -Allow AST objects to be read-only. llvm-svn: 107219 --- clang/include/clang/AST/DeclBase.h | 8 ++++++++ clang/include/clang/AST/DeclCXX.h | 3 +++ clang/include/clang/AST/DeclFriend.h | 6 ++++++ clang/lib/AST/DeclFriend.cpp | 4 ++++ clang/lib/Frontend/PCHReaderDecl.cpp | 17 +++++++++++++++-- clang/lib/Frontend/PCHWriter.cpp | 15 ++++++++++----- clang/lib/Frontend/PCHWriterDecl.cpp | 17 +++++++++++++++-- clang/test/PCH/cxx-friends.cpp | 13 +++++++++++++ clang/test/PCH/cxx-friends.h | 6 ++++++ 9 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 clang/test/PCH/cxx-friends.cpp create mode 100644 clang/test/PCH/cxx-friends.h diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index fe091ad17bc4..f4d76f52419b 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -248,6 +248,14 @@ protected: if (Decl::CollectingStats()) add(DK); } + Decl(Kind DK, EmptyShell Empty) + : NextDeclInContext(0), DeclKind(DK), InvalidDecl(0), + HasAttrs(false), Implicit(false), Used(false), + Access(AS_none), PCHLevel(0), + IdentifierNamespace(getIdentifierNamespaceForKind(DK)) { + if (Decl::CollectingStats()) add(DK); + } + virtual ~Decl(); public: diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index b1b8d058f775..33f34fa524da 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -973,6 +973,9 @@ public: static bool classof(const ClassTemplateSpecializationDecl *D) { return true; } + + friend class PCHDeclReader; + friend class PCHDeclWriter; }; /// CXXMethodDecl - Represents a static or instance method of a diff --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h index a20625da56b7..85085c3080b5 100644 --- a/clang/include/clang/AST/DeclFriend.h +++ b/clang/include/clang/AST/DeclFriend.h @@ -59,10 +59,13 @@ private: FriendLoc(FriendL) { } + FriendDecl(EmptyShell Empty) : Decl(Decl::Friend, Empty), NextFriend(0) { } + public: static FriendDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L, FriendUnion Friend_, SourceLocation FriendL); + static FriendDecl *Create(ASTContext &C, EmptyShell Empty); /// If this friend declaration names an (untemplated but /// possibly dependent) type, return the type; otherwise @@ -87,6 +90,9 @@ public: static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classof(const FriendDecl *D) { return true; } static bool classofKind(Kind K) { return K == Decl::Friend; } + + friend class PCHDeclReader; + friend class PCHDeclWriter; }; /// An iterator over the friend declarations of a class. diff --git a/clang/lib/AST/DeclFriend.cpp b/clang/lib/AST/DeclFriend.cpp index ab3552db28e0..99bfe40c31f4 100644 --- a/clang/lib/AST/DeclFriend.cpp +++ b/clang/lib/AST/DeclFriend.cpp @@ -39,3 +39,7 @@ FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC, cast(DC)->pushFriendDecl(FD); return FD; } + +FriendDecl *FriendDecl::Create(ASTContext &C, EmptyShell Empty) { + return new (C) FriendDecl(Empty); +} diff --git a/clang/lib/Frontend/PCHReaderDecl.cpp b/clang/lib/Frontend/PCHReaderDecl.cpp index 05cefa9ffeb6..9f057b0c1dc3 100644 --- a/clang/lib/Frontend/PCHReaderDecl.cpp +++ b/clang/lib/Frontend/PCHReaderDecl.cpp @@ -27,7 +27,7 @@ using namespace clang; // Declaration deserialization //===----------------------------------------------------------------------===// -namespace { +namespace clang { class PCHDeclReader : public DeclVisitor { PCHReader &Reader; const PCHReader::RecordData &Record; @@ -81,6 +81,7 @@ namespace { void VisitLinkageSpecDecl(LinkageSpecDecl *D); void VisitFileScopeAsmDecl(FileScopeAsmDecl *AD); void VisitAccessSpecDecl(AccessSpecDecl *D); + void VisitFriendDecl(FriendDecl *D); void VisitFriendTemplateDecl(FriendTemplateDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); void VisitBlockDecl(BlockDecl *BD); @@ -673,6 +674,9 @@ void PCHDeclReader::VisitCXXRecordDecl(CXXRecordDecl *D) { Bases.push_back(ReadCXXBaseSpecifier()); D->setBases(Bases.begin(), NumBases); + D->data().FirstFriend + = cast_or_null(Reader.GetDecl(Record[Idx++])); + // FIXME: there's a lot of stuff we do here that's kindof sketchy // if we're leaving the context incomplete. D->completeDefinition(); @@ -704,6 +708,15 @@ void PCHDeclReader::VisitAccessSpecDecl(AccessSpecDecl *D) { D->setColonLoc(Reader.ReadSourceLocation(Record, Idx)); } +void PCHDeclReader::VisitFriendDecl(FriendDecl *D) { + if (Record[Idx++]) + D->Friend = Reader.GetTypeSourceInfo(Record, Idx); + else + D->Friend = cast(Reader.GetDecl(Record[Idx++])); + D->NextFriend = cast_or_null(Reader.GetDecl(Record[Idx++])); + D->FriendLoc = Reader.ReadSourceLocation(Record, Idx); +} + void PCHDeclReader::VisitFriendTemplateDecl(FriendTemplateDecl *D) { assert(false && "cannot read FriendTemplateDecl"); } @@ -1204,7 +1217,7 @@ Decl *PCHReader::ReadDeclRecord(uint64_t Offset, unsigned Index) { SourceLocation()); break; case pch::DECL_FRIEND: - assert(false && "cannot read FriendDecl"); + D = FriendDecl::Create(*Context, Decl::EmptyShell()); break; case pch::DECL_FRIEND_TEMPLATE: assert(false && "cannot read FriendTemplateDecl"); diff --git a/clang/lib/Frontend/PCHWriter.cpp b/clang/lib/Frontend/PCHWriter.cpp index 8b714386c112..d947f7250d2a 100644 --- a/clang/lib/Frontend/PCHWriter.cpp +++ b/clang/lib/Frontend/PCHWriter.cpp @@ -1444,11 +1444,16 @@ uint64_t PCHWriter::WriteDeclContextVisibleBlock(ASTContext &Context, if (DC->getPrimaryContext() != DC) return 0; - // Since there is no name lookup into functions or methods, and we - // perform name lookup for the translation unit via the - // IdentifierInfo chains, don't bother to build a - // visible-declarations table for these entities. - if (DC->isFunctionOrMethod() || DC->isTranslationUnit()) + // Since there is no name lookup into functions or methods, don't bother to + // build a visible-declarations table for these entities. + if (DC->isFunctionOrMethod()) + return 0; + + // If not in C++, we perform name lookup for the translation unit via the + // IdentifierInfo chains, don't bother to build a visible-declarations table. + // FIXME: In C++ we need the visible declarations in order to "see" the + // friend declarations, is there a way to do this without writing the table ? + if (DC->isTranslationUnit() && !Context.getLangOptions().CPlusPlus) return 0; // Force the DeclContext to build a its name-lookup table. diff --git a/clang/lib/Frontend/PCHWriterDecl.cpp b/clang/lib/Frontend/PCHWriterDecl.cpp index 42d506953cb5..309cc3536d9b 100644 --- a/clang/lib/Frontend/PCHWriterDecl.cpp +++ b/clang/lib/Frontend/PCHWriterDecl.cpp @@ -25,7 +25,7 @@ using namespace clang; // Declaration serialization //===----------------------------------------------------------------------===// -namespace { +namespace clang { class PCHDeclWriter : public DeclVisitor { PCHWriter &Writer; @@ -82,6 +82,7 @@ namespace { void VisitLinkageSpecDecl(LinkageSpecDecl *D); void VisitFileScopeAsmDecl(FileScopeAsmDecl *D); void VisitAccessSpecDecl(AccessSpecDecl *D); + void VisitFriendDecl(FriendDecl *D); void VisitFriendTemplateDecl(FriendTemplateDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); void VisitBlockDecl(BlockDecl *D); @@ -636,7 +637,6 @@ void PCHDeclWriter::WriteCXXBaseSpecifier(const CXXBaseSpecifier *Base) { } void PCHDeclWriter::VisitCXXRecordDecl(CXXRecordDecl *D) { - // assert(false && "cannot write CXXRecordDecl"); VisitRecordDecl(D); enum { @@ -664,6 +664,8 @@ void PCHDeclWriter::VisitCXXRecordDecl(CXXRecordDecl *D) { for (CXXRecordDecl::base_class_iterator I = D->bases_begin(), E = D->bases_end(); I != E; ++I) WriteCXXBaseSpecifier(&*I); + + Writer.AddDeclRef(D->data().FirstFriend, Record); } Code = pch::DECL_CXX_RECORD; } @@ -698,6 +700,17 @@ void PCHDeclWriter::VisitAccessSpecDecl(AccessSpecDecl *D) { Code = pch::DECL_ACCESS_SPEC; } +void PCHDeclWriter::VisitFriendDecl(FriendDecl *D) { + Record.push_back(D->Friend.is()); + if (D->Friend.is()) + Writer.AddTypeSourceInfo(D->Friend.get(), Record); + else + Writer.AddDeclRef(D->Friend.get(), Record); + Writer.AddDeclRef(D->NextFriend, Record); + Writer.AddSourceLocation(D->FriendLoc, Record); + Code = pch::DECL_FRIEND; +} + void PCHDeclWriter::VisitFriendTemplateDecl(FriendTemplateDecl *D) { assert(false && "cannot write FriendTemplateDecl"); } diff --git a/clang/test/PCH/cxx-friends.cpp b/clang/test/PCH/cxx-friends.cpp new file mode 100644 index 000000000000..a8d75586e431 --- /dev/null +++ b/clang/test/PCH/cxx-friends.cpp @@ -0,0 +1,13 @@ +// Test this without pch. +// RUN: %clang_cc1 -include %S/cxx-friends.h -fsyntax-only -verify %s + +// Test with pch. +// RUN: %clang_cc1 -x c++-header -emit-pch -o %t %S/cxx-friends.h +// RUN: %clang_cc1 -include-pch %t -fsyntax-only -verify %s + +class F { + void m() { + A* a; + a->x = 0; + } +}; diff --git a/clang/test/PCH/cxx-friends.h b/clang/test/PCH/cxx-friends.h new file mode 100644 index 000000000000..2a33f15a5329 --- /dev/null +++ b/clang/test/PCH/cxx-friends.h @@ -0,0 +1,6 @@ +// Header for PCH test cxx-friends.cpp + +class A { + int x; + friend class F; +};