From c41a49dabb36b7cf11cf10aed081b407e97fd7d2 Mon Sep 17 00:00:00 2001 From: Raphael Isemann Date: Mon, 6 Nov 2017 14:40:40 +0100 Subject: [PATCH] Multiplex the ExternalASTSource in cling We need to respect the existing ExternalASTSource when setting up the interpreter. Otherwise the ASTReader (which is the existing source) doesn't receive the required callbacks to properly load C++ modules. This patch now creates a multiplexer that contains our new ASTSource and the existing one if it's necessary. We also no longer attach the existing sema source which seemingly was only a workaround that only works for the special case were the external sema source and the external AST source are the same object. --- lib/Interpreter/InterpreterCallbacks.cpp | 147 +++++++++++++++++++++-- 1 file changed, 140 insertions(+), 7 deletions(-) diff --git a/lib/Interpreter/InterpreterCallbacks.cpp b/lib/Interpreter/InterpreterCallbacks.cpp index 2e4e3b94..2106eca2 100644 --- a/lib/Interpreter/InterpreterCallbacks.cpp +++ b/lib/Interpreter/InterpreterCallbacks.cpp @@ -16,6 +16,7 @@ #include "clang/Frontend/MultiplexConsumer.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/CodeCompleteConsumer.h" +#include "clang/Sema/MultiplexExternalSemaSource.h" #include "clang/Sema/Sema.h" #include "clang/Serialization/ASTDeserializationListener.h" #include "clang/Serialization/ASTReader.h" @@ -92,6 +93,120 @@ namespace cling { } }; + /// \brief wraps an ExternalASTSource in an ExternalSemaSource. No functional + /// difference between the original source and this wrapper intended. + class ExternalASTSourceWrapper : public ExternalSemaSource { + ExternalASTSource* m_Source; + + public: + ExternalASTSourceWrapper(ExternalASTSource* Source) : m_Source(Source) { + assert(m_Source && "Can't wrap nullptr ExternalASTSource"); + } + + virtual Decl* GetExternalDecl(uint32_t ID) override { + return m_Source->GetExternalDecl(ID); + } + + virtual Selector GetExternalSelector(uint32_t ID) override { + return m_Source->GetExternalSelector(ID); + } + + virtual uint32_t GetNumExternalSelectors() override { + return m_Source->GetNumExternalSelectors(); + } + + virtual Stmt* GetExternalDeclStmt(uint64_t Offset) override { + return m_Source->GetExternalDeclStmt(Offset); + } + + virtual CXXCtorInitializer** + GetExternalCXXCtorInitializers(uint64_t Offset) override { + return m_Source->GetExternalCXXCtorInitializers(Offset); + } + + virtual CXXBaseSpecifier* + GetExternalCXXBaseSpecifiers(uint64_t Offset) override { + return m_Source->GetExternalCXXBaseSpecifiers(Offset); + } + + virtual void updateOutOfDateIdentifier(IdentifierInfo& II) override { + m_Source->updateOutOfDateIdentifier(II); + } + + virtual bool FindExternalVisibleDeclsByName(const DeclContext* DC, + DeclarationName Name) override { + return m_Source->FindExternalVisibleDeclsByName(DC, Name); + } + + virtual void completeVisibleDeclsMap(const DeclContext* DC) override { + m_Source->completeVisibleDeclsMap(DC); + } + + virtual Module* getModule(unsigned ID) override { + return m_Source->getModule(ID); + } + + virtual llvm::Optional + getSourceDescriptor(unsigned ID) override { + return m_Source->getSourceDescriptor(ID); + } + + virtual ExtKind hasExternalDefinitions(const Decl* D) override { + return m_Source->hasExternalDefinitions(D); + } + + virtual void + FindExternalLexicalDecls(const DeclContext* DC, + llvm::function_ref IsKindWeWant, + SmallVectorImpl& Result) override { + m_Source->FindExternalLexicalDecls(DC, IsKindWeWant, Result); + } + + virtual void FindFileRegionDecls(FileID File, unsigned Offset, + unsigned Length, + SmallVectorImpl& Decls) override { + m_Source->FindFileRegionDecls(File, Offset, Length, Decls); + } + + virtual void CompleteRedeclChain(const Decl* D) override { + m_Source->CompleteRedeclChain(D); + } + + virtual void CompleteType(TagDecl* Tag) override { + m_Source->CompleteType(Tag); + } + + virtual void CompleteType(ObjCInterfaceDecl* Class) override { + m_Source->CompleteType(Class); + } + + virtual void ReadComments() override { m_Source->ReadComments(); } + + virtual void StartedDeserializing() override { + m_Source->StartedDeserializing(); + } + + virtual void FinishedDeserializing() override { + m_Source->FinishedDeserializing(); + } + + virtual void StartTranslationUnit(ASTConsumer* Consumer) override { + m_Source->StartTranslationUnit(Consumer); + } + + virtual void PrintStats() override { m_Source->PrintStats(); } + + virtual bool layoutRecordType( + const RecordDecl* Record, uint64_t& Size, uint64_t& Alignment, + llvm::DenseMap& FieldOffsets, + llvm::DenseMap& BaseOffsets, + llvm::DenseMap& VirtualBaseOffsets) + override { + return m_Source->layoutRecordType(Record, Size, Alignment, FieldOffsets, + BaseOffsets, VirtualBaseOffsets); + } + }; + ///\brief Translates 'interesting' for the interpreter ExternalSemaSource /// events into interpreter callbacks. /// @@ -187,14 +302,32 @@ namespace cling { m_ExternalSemaSource->InitializeSema(SemaRef); m_Interpreter->getSema().addExternalSource(m_ExternalSemaSource); - // FIXME: We should add a multiplexer in the ASTContext, too. - llvm::IntrusiveRefCntPtr - astContextExternalSource(SemaRef.getExternalSource()); + // Overwrite the ExternalASTSource. clang::ASTContext& Ctx = SemaRef.getASTContext(); - // FIXME: This is a gross hack. We must make multiplexer in the - // astcontext or a derived class that extends what we need. - Ctx.ExternalSource.resetWithoutRelease();//FIXME: make sure we delete it. - Ctx.setExternalSource(astContextExternalSource); + auto ExistingSource = Ctx.getExternalSource(); + // If we already have source, we need to create a multiplexer with the + // existing source. + if (ExistingSource) { + // Make sure the context is not deleting the existing source. + // FIXME: We should delete this, but looking at the other TODO's in + // the destructor we can't easily free our callbacks from here... + Ctx.ExternalSource.resetWithoutRelease(); + + // Wrap the existing source in a wrapper so that it becomes an + // external sema source. This way we can use the existing multiplexer + // for this. + auto wrapper = new ExternalASTSourceWrapper(ExistingSource); + + // Wrap both the existing source and our source. We give our own + // source preference to the existing one. + IntrusiveRefCntPtr S; + S = new MultiplexExternalSemaSource(*m_ExternalSemaSource, *wrapper); + + Ctx.setExternalSource(S); + } else { + // We don't have an existing source, so just set our own source. + Ctx.setExternalSource(m_ExternalSemaSource); + } } if (enableDeserializationListenerCallbacks && Reader) {