[cfe-commits] r110125 - in /cfe/trunk: include/clang/AST/Redeclarable.h include/clang/Frontend/PCHBitCodes.h include/clang/Frontend/PCHReader.h include/clang/Frontend/PCHWriter.h lib/Frontend/PCHReader.cpp lib/Frontend/PCHReaderDecl.cpp lib/Frontend/PCHWriter.cpp lib/Frontend/PCHWriterDecl.cpp test/PCH/cxx-templates.cpp test/PCH/cxx-templates.h
Argyrios Kyrtzidis
kyrtzidis at apple.com
Tue Aug 3 10:34:21 PDT 2010
Hi Sebastian, please review.
-Argiris
On Aug 3, 2010, at 6:30 PM, Argyrios Kyrtzidis wrote:
> Author: akirtzidis
> Date: Tue Aug 3 12:30:10 2010
> New Revision: 110125
>
> URL: http://llvm.org/viewvc/llvm-project?rev=110125&view=rev
> Log:
> Apart from storing/retrieving the previous redeclaration from PCH, also store/retrieve the most recent
> redeclaration. That way we are sure that the full redeclarations chain is loaded.
>
> When using chained PCHs, first declarations point to the most recent redeclarations in the same PCH.
> To address this use a REDECLS_UPDATE_LATEST record block to keep track of which first declarations need
> to point to a most recent redeclaration in another PCH.
>
> Modified:
> cfe/trunk/include/clang/AST/Redeclarable.h
> cfe/trunk/include/clang/Frontend/PCHBitCodes.h
> cfe/trunk/include/clang/Frontend/PCHReader.h
> cfe/trunk/include/clang/Frontend/PCHWriter.h
> cfe/trunk/lib/Frontend/PCHReader.cpp
> cfe/trunk/lib/Frontend/PCHReaderDecl.cpp
> cfe/trunk/lib/Frontend/PCHWriter.cpp
> cfe/trunk/lib/Frontend/PCHWriterDecl.cpp
> cfe/trunk/test/PCH/cxx-templates.cpp
> cfe/trunk/test/PCH/cxx-templates.h
>
> Modified: cfe/trunk/include/clang/AST/Redeclarable.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Redeclarable.h?rev=110125&r1=110124&r2=110125&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/AST/Redeclarable.h (original)
> +++ cfe/trunk/include/clang/AST/Redeclarable.h Tue Aug 3 12:30:10 2010
> @@ -177,6 +177,9 @@
> static_cast<const decl_type*>(this)));
> }
> redecl_iterator redecls_end() const { return redecl_iterator(); }
> +
> + friend class PCHDeclReader;
> + friend class PCHDeclWriter;
> };
>
> }
>
> Modified: cfe/trunk/include/clang/Frontend/PCHBitCodes.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/PCHBitCodes.h?rev=110125&r1=110124&r2=110125&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Frontend/PCHBitCodes.h (original)
> +++ cfe/trunk/include/clang/Frontend/PCHBitCodes.h Tue Aug 3 12:30:10 2010
> @@ -245,8 +245,12 @@
> /// declarations.
> TU_UPDATE_LEXICAL = 28,
>
> + /// \brief Record code for an update to first decls pointing to the
> + /// latest redeclarations.
> + REDECLS_UPDATE_LATEST = 29,
> +
> /// \brief Record code for declarations that Sema keeps references of.
> - SEMA_DECL_REFS = 29
> + SEMA_DECL_REFS = 30
> };
>
> /// \brief Record types used within a source manager block.
>
> Modified: cfe/trunk/include/clang/Frontend/PCHReader.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/PCHReader.h?rev=110125&r1=110124&r2=110125&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Frontend/PCHReader.h (original)
> +++ cfe/trunk/include/clang/Frontend/PCHReader.h Tue Aug 3 12:30:10 2010
> @@ -319,6 +319,11 @@
> /// DeclContext.
> DeclContextOffsetsMap DeclContextOffsets;
>
> + typedef llvm::DenseMap<pch::DeclID, pch::DeclID> FirstLatestDeclIDMap;
> + /// \brief Map of first declarations from a chained PCH that point to the
> + /// most recent declarations in another PCH.
> + FirstLatestDeclIDMap FirstLatestDeclIDs;
> +
> /// \brief Read the records that describe the contents of declcontexts.
> bool ReadDeclContextStorage(llvm::BitstreamCursor &Cursor,
> const std::pair<uint64_t, uint64_t> &Offsets,
> @@ -561,7 +566,7 @@
> QualType ReadTypeRecord(unsigned Index);
> RecordLocation TypeCursorForIndex(unsigned Index);
> void LoadedDecl(unsigned Index, Decl *D);
> - Decl *ReadDeclRecord(unsigned Index);
> + Decl *ReadDeclRecord(unsigned Index, pch::DeclID ID);
> RecordLocation DeclCursorForIndex(unsigned Index);
>
> void PassInterestingDeclsToConsumer();
>
> Modified: cfe/trunk/include/clang/Frontend/PCHWriter.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/PCHWriter.h?rev=110125&r1=110124&r2=110125&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Frontend/PCHWriter.h (original)
> +++ cfe/trunk/include/clang/Frontend/PCHWriter.h Tue Aug 3 12:30:10 2010
> @@ -79,6 +79,7 @@
> public:
> typedef llvm::SmallVector<uint64_t, 64> RecordData;
>
> + friend class PCHDeclWriter;
> private:
> /// \brief The bitstream writer used to emit this precompiled header.
> llvm::BitstreamWriter &Stream;
> @@ -195,6 +196,11 @@
> /// \brief Mapping from the macro definition indices in \c MacroDefinitions
> /// to the corresponding offsets within the preprocessor block.
> std::vector<uint32_t> MacroDefinitionOffsets;
> +
> + typedef llvm::DenseMap<Decl *, Decl *> FirstLatestDeclMap;
> + /// \brief Map of first declarations from a chained PCH that point to the
> + /// most recent declarations in another PCH.
> + FirstLatestDeclMap FirstLatestDecls;
>
> /// \brief Declarations encountered that might be external
> /// definitions.
>
> Modified: cfe/trunk/lib/Frontend/PCHReader.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHReader.cpp?rev=110125&r1=110124&r2=110125&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Frontend/PCHReader.cpp (original)
> +++ cfe/trunk/lib/Frontend/PCHReader.cpp Tue Aug 3 12:30:10 2010
> @@ -1557,6 +1557,18 @@
> break;
> }
>
> + case pch::REDECLS_UPDATE_LATEST: {
> + assert(Record.size() % 2 == 0 && "Expected pairs of DeclIDs");
> + for (unsigned i = 0, e = Record.size(); i < e; i += 2) {
> + pch::DeclID First = Record[i], Latest = Record[i+1];
> + assert((FirstLatestDeclIDs.find(First) == FirstLatestDeclIDs.end() ||
> + Latest > FirstLatestDeclIDs[First]) &&
> + "The new latest is supposed to come after the previous latest");
> + FirstLatestDeclIDs[First] = Latest;
> + }
> + break;
> + }
> +
> case pch::LANGUAGE_OPTIONS:
> if (ParseLanguageOptions(Record) && !DisableValidation)
> return IgnorePCH;
> @@ -2868,7 +2880,7 @@
>
> TranslationUnitDecl *PCHReader::GetTranslationUnitDecl() {
> if (!DeclsLoaded[0]) {
> - ReadDeclRecord(0);
> + ReadDeclRecord(0, 0);
> if (DeserializationListener)
> DeserializationListener->DeclRead(1, DeclsLoaded[0]);
> }
> @@ -2887,7 +2899,7 @@
>
> unsigned Index = ID - 1;
> if (!DeclsLoaded[Index]) {
> - ReadDeclRecord(Index);
> + ReadDeclRecord(Index, ID);
> if (DeserializationListener)
> DeserializationListener->DeclRead(ID, DeclsLoaded[Index]);
> }
>
> Modified: cfe/trunk/lib/Frontend/PCHReaderDecl.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHReaderDecl.cpp?rev=110125&r1=110124&r2=110125&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Frontend/PCHReaderDecl.cpp (original)
> +++ cfe/trunk/lib/Frontend/PCHReaderDecl.cpp Tue Aug 3 12:30:10 2010
> @@ -31,6 +31,7 @@
> class PCHDeclReader : public DeclVisitor<PCHDeclReader, void> {
> PCHReader &Reader;
> llvm::BitstreamCursor &Cursor;
> + const pch::DeclID ThisDeclID;
> const PCHReader::RecordData &Record;
> unsigned &Idx;
> pch::TypeID TypeIDForTypeDecl;
> @@ -39,9 +40,10 @@
>
> public:
> PCHDeclReader(PCHReader &Reader, llvm::BitstreamCursor &Cursor,
> - const PCHReader::RecordData &Record, unsigned &Idx)
> - : Reader(Reader), Cursor(Cursor), Record(Record), Idx(Idx),
> - TypeIDForTypeDecl(0) { }
> + pch::DeclID thisDeclID, const PCHReader::RecordData &Record,
> + unsigned &Idx)
> + : Reader(Reader), Cursor(Cursor), ThisDeclID(thisDeclID), Record(Record),
> + Idx(Idx), TypeIDForTypeDecl(0) { }
>
> void Visit(Decl *D);
>
> @@ -93,6 +95,7 @@
> void VisitBlockDecl(BlockDecl *BD);
>
> std::pair<uint64_t, uint64_t> VisitDeclContext(DeclContext *DC);
> + template <typename T> void VisitRedeclarable(Redeclarable<T> *D);
>
> // FIXME: Reorder according to DeclNodes.td?
> void VisitObjCMethodDecl(ObjCMethodDecl *D);
> @@ -178,8 +181,7 @@
> void PCHDeclReader::VisitTagDecl(TagDecl *TD) {
> VisitTypeDecl(TD);
> TD->IdentifierNamespace = Record[Idx++];
> - TD->setPreviousDeclaration(
> - cast_or_null<TagDecl>(Reader.GetDecl(Record[Idx++])));
> + VisitRedeclarable(TD);
> TD->setTagKind((TagDecl::TagKind)Record[Idx++]);
> TD->setDefinition(Record[Idx++]);
> TD->setEmbeddedInDeclarator(Record[Idx++]);
> @@ -305,10 +307,7 @@
> // FunctionDecl's body is handled last at PCHReaderDecl::Visit,
> // after everything else is read.
>
> - // Avoid side effects and invariant checking of FunctionDecl's
> - // setPreviousDeclaration.
> - FD->redeclarable_base::setPreviousDeclaration(
> - cast_or_null<FunctionDecl>(Reader.GetDecl(Record[Idx++])));
> + VisitRedeclarable(FD);
> FD->setStorageClass((FunctionDecl::StorageClass)Record[Idx++]);
> FD->setStorageClassAsWritten((FunctionDecl::StorageClass)Record[Idx++]);
> FD->setInlineSpecified(Record[Idx++]);
> @@ -550,8 +549,7 @@
> VD->setDeclaredInCondition(Record[Idx++]);
> VD->setExceptionVariable(Record[Idx++]);
> VD->setNRVOVariable(Record[Idx++]);
> - VD->setPreviousDeclaration(
> - cast_or_null<VarDecl>(Reader.GetDecl(Record[Idx++])));
> + VisitRedeclarable(VD);
> if (Record[Idx++])
> VD->setInit(Reader.ReadExpr(Cursor));
>
> @@ -918,6 +916,25 @@
>
> RedeclarableTemplateDecl *LatestDecl =
> cast_or_null<RedeclarableTemplateDecl>(Reader.GetDecl(Record[Idx++]));
> +
> + // This decl is a first one and the latest declaration that it points to is
> + // in the same PCH. However, if this actually needs to point to a
> + // redeclaration in another chained PCH, we need to update it by checking
> + // the FirstLatestDeclIDs map which tracks this kind of decls.
> + assert(Reader.GetDecl(ThisDeclID) == D && "Invalid ThisDeclID ?");
> + PCHReader::FirstLatestDeclIDMap::iterator I
> + = Reader.FirstLatestDeclIDs.find(ThisDeclID);
> + if (I != Reader.FirstLatestDeclIDs.end()) {
> + Decl *NewLatest = Reader.GetDecl(I->second);
> + assert((LatestDecl->getLocation().isInvalid() ||
> + NewLatest->getLocation().isInvalid() ||
> + Reader.SourceMgr.isBeforeInTranslationUnit(
> + LatestDecl->getLocation(),
> + NewLatest->getLocation())) &&
> + "The new latest is supposed to come after the previous latest");
> + LatestDecl = cast<RedeclarableTemplateDecl>(NewLatest);
> + }
> +
> assert(LatestDecl->getKind() == D->getKind() && "Latest kind mismatch");
> D->getCommonPtr()->Latest = LatestDecl;
> }
> @@ -1072,6 +1089,54 @@
> return std::make_pair(LexicalOffset, VisibleOffset);
> }
>
> +template <typename T>
> +void PCHDeclReader::VisitRedeclarable(Redeclarable<T> *D) {
> + enum RedeclKind { NoRedeclaration = 0, PointsToPrevious, PointsToLatest };
> + RedeclKind Kind = (RedeclKind)Record[Idx++];
> + switch (Kind) {
> + default:
> + assert(0 && "Out of sync with PCHDeclWriter::VisitRedeclarable or messed up"
> + " reading");
> + case NoRedeclaration:
> + break;
> + case PointsToPrevious:
> + D->RedeclLink = typename Redeclarable<T>::PreviousDeclLink(
> + cast_or_null<T>(Reader.GetDecl(Record[Idx++])));
> + break;
> + case PointsToLatest:
> + D->RedeclLink = typename Redeclarable<T>::LatestDeclLink(
> + cast_or_null<T>(Reader.GetDecl(Record[Idx++])));
> + break;
> + }
> +
> + assert(!(Kind == PointsToPrevious &&
> + Reader.FirstLatestDeclIDs.find(ThisDeclID) !=
> + Reader.FirstLatestDeclIDs.end()) &&
> + "This decl is not first, it should not be in the map");
> + if (Kind == PointsToPrevious)
> + return;
> +
> + // This decl is a first one and the latest declaration that it points to is in
> + // the same PCH. However, if this actually needs to point to a redeclaration
> + // in another chained PCH, we need to update it by checking the
> + // FirstLatestDeclIDs map which tracks this kind of decls.
> + assert(Reader.GetDecl(ThisDeclID) == static_cast<T*>(D) &&
> + "Invalid ThisDeclID ?");
> + PCHReader::FirstLatestDeclIDMap::iterator I
> + = Reader.FirstLatestDeclIDs.find(ThisDeclID);
> + if (I != Reader.FirstLatestDeclIDs.end()) {
> + Decl *NewLatest = Reader.GetDecl(I->second);
> + assert((D->getMostRecentDeclaration()->getLocation().isInvalid() ||
> + NewLatest->getLocation().isInvalid() ||
> + Reader.SourceMgr.isBeforeInTranslationUnit(
> + D->getMostRecentDeclaration()->getLocation(),
> + NewLatest->getLocation())) &&
> + "The new latest is supposed to come after the previous latest");
> + D->RedeclLink
> + = typename Redeclarable<T>::LatestDeclLink(cast_or_null<T>(NewLatest));
> + }
> +}
> +
> //===----------------------------------------------------------------------===//
> // Attribute Reading
> //===----------------------------------------------------------------------===//
> @@ -1304,7 +1369,7 @@
> }
>
> /// \brief Read the declaration at the given offset from the PCH file.
> -Decl *PCHReader::ReadDeclRecord(unsigned Index) {
> +Decl *PCHReader::ReadDeclRecord(unsigned Index, pch::DeclID ID) {
> RecordLocation Loc = DeclCursorForIndex(Index);
> llvm::BitstreamCursor &DeclsCursor = *Loc.first;
> // Keep track of where we are in the stream, then jump back there
> @@ -1320,7 +1385,7 @@
> RecordData Record;
> unsigned Code = DeclsCursor.ReadCode();
> unsigned Idx = 0;
> - PCHDeclReader Reader(*this, DeclsCursor, Record, Idx);
> + PCHDeclReader Reader(*this, DeclsCursor, ID, Record, Idx);
>
> Decl *D = 0;
> switch ((pch::DeclCode)DeclsCursor.ReadRecord(Code, Record)) {
>
> Modified: cfe/trunk/lib/Frontend/PCHWriter.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHWriter.cpp?rev=110125&r1=110124&r2=110125&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Frontend/PCHWriter.cpp (original)
> +++ cfe/trunk/lib/Frontend/PCHWriter.cpp Tue Aug 3 12:30:10 2010
> @@ -2410,6 +2410,19 @@
> WriteIdentifierTable(PP);
> WriteTypeDeclOffsets();
>
> + /// Build a record containing first declarations from a chained PCH and the
> + /// most recent declarations in this PCH that they point to.
> + RecordData FirstLatestDeclIDs;
> + for (FirstLatestDeclMap::iterator
> + I = FirstLatestDecls.begin(), E = FirstLatestDecls.end(); I != E; ++I) {
> + assert(I->first->getPCHLevel() > I->second->getPCHLevel() &&
> + "Expected first & second to be in different PCHs");
> + AddDeclRef(I->first, FirstLatestDeclIDs);
> + AddDeclRef(I->second, FirstLatestDeclIDs);
> + }
> + if (!FirstLatestDeclIDs.empty())
> + Stream.EmitRecord(pch::REDECLS_UPDATE_LATEST, FirstLatestDeclIDs);
> +
> // Write the record containing external, unnamed definitions.
> if (!ExternalDefinitions.empty())
> Stream.EmitRecord(pch::EXTERNAL_DEFINITIONS, ExternalDefinitions);
>
> Modified: cfe/trunk/lib/Frontend/PCHWriterDecl.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHWriterDecl.cpp?rev=110125&r1=110124&r2=110125&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Frontend/PCHWriterDecl.cpp (original)
> +++ cfe/trunk/lib/Frontend/PCHWriterDecl.cpp Tue Aug 3 12:30:10 2010
> @@ -92,6 +92,7 @@
>
> void VisitDeclContext(DeclContext *DC, uint64_t LexicalOffset,
> uint64_t VisibleOffset);
> + template <typename T> void VisitRedeclarable(Redeclarable<T> *D);
>
>
> // FIXME: Put in the same order is DeclNodes.td?
> @@ -163,7 +164,7 @@
> void PCHDeclWriter::VisitTagDecl(TagDecl *D) {
> VisitTypeDecl(D);
> Record.push_back(D->getIdentifierNamespace());
> - Writer.AddDeclRef(D->getPreviousDeclaration(), Record);
> + VisitRedeclarable(D);
> Record.push_back((unsigned)D->getTagKind()); // FIXME: stable encoding
> Record.push_back(D->isDefinition());
> Record.push_back(D->isEmbeddedInDeclarator());
> @@ -279,7 +280,7 @@
> // FunctionDecl's body is handled last at PCHWriterDecl::Visit,
> // after everything else is written.
>
> - Writer.AddDeclRef(D->getPreviousDeclaration(), Record);
> + VisitRedeclarable(D);
> Record.push_back(D->getStorageClass()); // FIXME: stable encoding
> Record.push_back(D->getStorageClassAsWritten());
> Record.push_back(D->isInlineSpecified());
> @@ -500,7 +501,7 @@
> Record.push_back(D->isDeclaredInCondition());
> Record.push_back(D->isExceptionVariable());
> Record.push_back(D->isNRVOVariable());
> - Writer.AddDeclRef(D->getPreviousDeclaration(), Record);
> + VisitRedeclarable(D);
> Record.push_back(D->getInit() ? 1 : 0);
> if (D->getInit())
> Writer.AddStmt(D->getInit());
> @@ -854,6 +855,18 @@
> Record.push_back(D->isMemberSpecialization());
>
> Writer.AddDeclRef(D->getCommonPtr()->Latest, Record);
> + } else {
> + RedeclarableTemplateDecl *First = D->getFirstDeclaration();
> + assert(First != D);
> + // If this is a most recent redeclaration that is pointed to by a first decl
> + // in a chained PCH, keep track of the association with the map so we can
> + // update the first decl during PCH reading.
> + if (First->getMostRecentDeclaration() == D &&
> + First->getPCHLevel() > D->getPCHLevel()) {
> + assert(Writer.FirstLatestDecls.find(First)==Writer.FirstLatestDecls.end()
> + && "The latest is already set");
> + Writer.FirstLatestDecls[First] = D;
> + }
> }
> }
>
> @@ -1016,6 +1029,29 @@
> Record.push_back(VisibleOffset);
> }
>
> +template <typename T>
> +void PCHDeclWriter::VisitRedeclarable(Redeclarable<T> *D) {
> + enum { NoRedeclaration = 0, PointsToPrevious, PointsToLatest };
> + if (D->RedeclLink.getNext() == D) {
> + Record.push_back(NoRedeclaration);
> + } else {
> + Record.push_back(D->RedeclLink.NextIsPrevious() ? PointsToPrevious
> + : PointsToLatest);
> + Writer.AddDeclRef(D->RedeclLink.getPointer(), Record);
> + }
> +
> + T *First = D->getFirstDeclaration();
> + T *ThisDecl = static_cast<T*>(D);
> + // If this is a most recent redeclaration that is pointed to by a first decl
> + // in a chained PCH, keep track of the association with the map so we can
> + // update the first decl during PCH reading.
> + if (ThisDecl != First && First->getMostRecentDeclaration() == ThisDecl &&
> + First->getPCHLevel() > ThisDecl->getPCHLevel()) {
> + assert(Writer.FirstLatestDecls.find(First) == Writer.FirstLatestDecls.end()
> + && "The latest is already set");
> + Writer.FirstLatestDecls[First] = ThisDecl;
> + }
> +}
>
> //===----------------------------------------------------------------------===//
> // PCHWriter Implementation
>
> Modified: cfe/trunk/test/PCH/cxx-templates.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/cxx-templates.cpp?rev=110125&r1=110124&r2=110125&view=diff
> ==============================================================================
> --- cfe/trunk/test/PCH/cxx-templates.cpp (original)
> +++ cfe/trunk/test/PCH/cxx-templates.cpp Tue Aug 3 12:30:10 2010
> @@ -1,9 +1,13 @@
> // Test this without pch.
> -// RUN: %clang_cc1 -include %S/cxx-templates.h -verify %s -ast-dump
> +// RUN: %clang_cc1 -include %S/cxx-templates.h -verify %s -ast-dump 1>/dev/null
> +// RUN: %clang_cc1 -include %S/cxx-templates.h %s -emit-llvm -o - | FileCheck %s
>
> // Test with pch.
> // RUN: %clang_cc1 -x c++-header -emit-pch -o %t %S/cxx-templates.h
> -// RUN: %clang_cc1 -include-pch %t -verify %s -ast-dump
> +// RUN: %clang_cc1 -include-pch %t -verify %s -ast-dump 1>/dev/null
> +// RUN: %clang_cc1 -include-pch %t %s -emit-llvm -o - | FileCheck %s
> +
> +// CHECK: define linkonce_odr void @_ZN2S3IiE1mEv
>
> struct A {
> typedef int type;
> @@ -22,4 +26,7 @@
> Dep<A>::Ty ty;
> Dep<A> a;
> a.f();
> +
> + S3<int> s3;
> + s3.m();
> }
>
> Modified: cfe/trunk/test/PCH/cxx-templates.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/cxx-templates.h?rev=110125&r1=110124&r2=110125&view=diff
> ==============================================================================
> --- cfe/trunk/test/PCH/cxx-templates.h (original)
> +++ cfe/trunk/test/PCH/cxx-templates.h Tue Aug 3 12:30:10 2010
> @@ -116,3 +116,11 @@
> };
>
> extern template class S2<true>;
> +
> +template <typename T>
> +struct S3 {
> + void m();
> +};
> +
> +template <typename T>
> +inline void S3<T>::m() { }
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
More information about the cfe-commits
mailing list