343 lines
13 KiB
C++
343 lines
13 KiB
C++
//------------------------------------------------------------------------------
|
|
// CLING - the C++ LLVM-based InterpreterG :)
|
|
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
|
|
//
|
|
// This file is dual-licensed: you can choose to license it under the University
|
|
// of Illinois Open Source License or the GNU Lesser General Public License. See
|
|
// LICENSE.TXT for details.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "DeclCollector.h"
|
|
|
|
#include "IncrementalParser.h"
|
|
#include "cling/Interpreter/Transaction.h"
|
|
#include "cling/Utils/AST.h"
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/DeclGroup.h"
|
|
#include "clang/Lex/MacroInfo.h"
|
|
#include "clang/Lex/Preprocessor.h"
|
|
#include "clang/Lex/Token.h"
|
|
#include "llvm/Support/Signals.h"
|
|
|
|
using namespace clang;
|
|
|
|
namespace {
|
|
///\brief Return true if this decl (which comes from an AST file) should
|
|
/// not be sent to CodeGen. The module is assumed to describe the contents
|
|
/// of a library; symbols inside the library must thus not be reemitted /
|
|
/// duplicated by CodeGen.
|
|
///
|
|
static bool shouldIgnore(const Decl* D) {
|
|
// This function is called for all "deserialized" decls, where the
|
|
// "deserialized" decl either really comes from an AST file or from
|
|
// a header that's loaded to import the AST for a library with a dictionary
|
|
// (the non-PCM case).
|
|
//
|
|
// Functions that are inlined must be sent to CodeGen - they will not have a
|
|
// symbol in the library.
|
|
if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(D)) {
|
|
if (D->isFromASTFile()) {
|
|
return !FD->hasBody();
|
|
} else {
|
|
// If the decl must be emitted then it will be in the library.
|
|
// If not, we must expose it to CodeGen now because it might
|
|
// not be in the library. Does this correspond to a weak symbol
|
|
// by definition?
|
|
return !(FD->isInlined() || FD->isTemplateInstantiation());
|
|
}
|
|
}
|
|
// Don't codegen statics coming in from a module; they are already part of
|
|
// the library.
|
|
// We do need to expose static variables from template instantiations.
|
|
if (const VarDecl* VD = dyn_cast<VarDecl>(D))
|
|
if (VD->hasGlobalStorage() && !VD->getType().isConstQualified()
|
|
&& VD->getTemplateSpecializationKind() == TSK_Undeclared)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// \brief Asserts that the given transaction is not null, otherwise prints a
|
|
/// stack trace to stderr and aborts execution.
|
|
static void assertHasTransaction(const cling::Transaction* T) {
|
|
if (!T) {
|
|
llvm::sys::PrintStackTrace(llvm::errs());
|
|
llvm_unreachable("Missing transaction during deserialization!");
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace cling {
|
|
///\brief Serves as DeclCollector's connector to the PPCallbacks interface.
|
|
///
|
|
class DeclCollector::PPAdapter : public clang::PPCallbacks {
|
|
cling::DeclCollector* m_Parent;
|
|
|
|
void MacroDirective(const clang::Token& MacroNameTok,
|
|
const clang::MacroDirective* MD) {
|
|
assertHasTransaction(m_Parent->m_CurTransaction);
|
|
Transaction::MacroDirectiveInfo MDE(MacroNameTok.getIdentifierInfo(), MD);
|
|
m_Parent->m_CurTransaction->append(MDE);
|
|
}
|
|
|
|
public:
|
|
PPAdapter(cling::DeclCollector* P) : m_Parent(P) {}
|
|
|
|
/// \name PPCallbacks overrides
|
|
/// Macro support
|
|
void MacroDefined(const clang::Token& MacroNameTok,
|
|
const clang::MacroDirective* MD) final {
|
|
MacroDirective(MacroNameTok, MD);
|
|
}
|
|
|
|
/// \name PPCallbacks overrides
|
|
/// Macro support
|
|
void MacroUndefined(const clang::Token& MacroNameTok,
|
|
const clang::MacroDefinition& /*MD*/,
|
|
const clang::MacroDirective* Undef) final {
|
|
if (Undef)
|
|
MacroDirective(MacroNameTok, Undef);
|
|
}
|
|
};
|
|
|
|
void DeclCollector::Setup(IncrementalParser* IncrParser,
|
|
std::unique_ptr<ASTConsumer> Consumer,
|
|
clang::Preprocessor& PP) {
|
|
m_IncrParser = IncrParser;
|
|
m_Consumer = std::move(Consumer);
|
|
PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(new PPAdapter(this)));
|
|
}
|
|
|
|
bool DeclCollector::comesFromASTReader(DeclGroupRef DGR) const {
|
|
assert(!DGR.isNull() && "DeclGroupRef is Null!");
|
|
assertHasTransaction(m_CurTransaction);
|
|
if (m_CurTransaction->getCompilationOpts().CodeGenerationForModule)
|
|
return true;
|
|
|
|
// Take the first/only decl in the group.
|
|
Decl* D = *DGR.begin();
|
|
return D->isFromASTFile();
|
|
}
|
|
|
|
bool DeclCollector::comesFromASTReader(const Decl* D) const {
|
|
// The operation is const but clang::DeclGroupRef doesn't allow us to
|
|
// express it.
|
|
return comesFromASTReader(DeclGroupRef(const_cast<Decl*>(D)));
|
|
}
|
|
|
|
// pin the vtable here.
|
|
DeclCollector::~DeclCollector() { }
|
|
|
|
ASTTransformer::Result DeclCollector::TransformDecl(Decl* D) const {
|
|
// We are sure it's safe to pipe it through the transformers
|
|
// Consume late transformers init
|
|
for (size_t i = 0; D && i < m_TransactionTransformers.size(); ++i) {
|
|
ASTTransformer::Result NewDecl
|
|
= m_TransactionTransformers[i]->Transform(D, m_CurTransaction);
|
|
if (!NewDecl.getInt()) {
|
|
m_CurTransaction->setIssuedDiags(Transaction::kErrors);
|
|
return NewDecl;
|
|
}
|
|
D = NewDecl.getPointer();
|
|
}
|
|
if (FunctionDecl* FD = dyn_cast_or_null<FunctionDecl>(D)) {
|
|
if (utils::Analyze::IsWrapper(FD)) {
|
|
for (size_t i = 0; D && i < m_WrapperTransformers.size(); ++i) {
|
|
ASTTransformer::Result NewDecl
|
|
= m_WrapperTransformers[i]->Transform(D, m_CurTransaction);
|
|
if (!NewDecl.getInt()) {
|
|
m_CurTransaction->setIssuedDiags(Transaction::kErrors);
|
|
return NewDecl;
|
|
}
|
|
D = NewDecl.getPointer();
|
|
}
|
|
}
|
|
}
|
|
return ASTTransformer::Result(D, true);
|
|
}
|
|
|
|
bool DeclCollector::Transform(DeclGroupRef& DGR) {
|
|
// Do not tranform recursively, e.g. when emitting a DeclExtracted decl.
|
|
if (m_Transforming)
|
|
return true;
|
|
|
|
struct TransformingRAII {
|
|
bool& m_Transforming;
|
|
TransformingRAII(bool& Transforming): m_Transforming(Transforming) {
|
|
m_Transforming = true;
|
|
}
|
|
~TransformingRAII() { m_Transforming = false; }
|
|
} transformingUpdater(m_Transforming);
|
|
|
|
llvm::SmallVector<Decl*, 4> ReplacedDecls;
|
|
bool HaveReplacement = false;
|
|
for (Decl* D: DGR) {
|
|
ASTTransformer::Result NewDecl = TransformDecl(D);
|
|
if (!NewDecl.getInt())
|
|
return false;
|
|
HaveReplacement |= (NewDecl.getPointer() != D);
|
|
if (NewDecl.getPointer())
|
|
ReplacedDecls.push_back(NewDecl.getPointer());
|
|
}
|
|
if (HaveReplacement)
|
|
DGR = DeclGroupRef::Create((*DGR.begin())->getASTContext(),
|
|
ReplacedDecls.data(), ReplacedDecls.size());
|
|
return true;
|
|
}
|
|
|
|
bool DeclCollector::HandleTopLevelDecl(DeclGroupRef DGR) {
|
|
if (!Transform(DGR))
|
|
return false;
|
|
|
|
if (DGR.isNull())
|
|
return true;
|
|
|
|
if (!m_Consumer)
|
|
return true;
|
|
|
|
assertHasTransaction(m_CurTransaction);
|
|
|
|
Transaction::DelayCallInfo DCI(DGR, Transaction::kCCIHandleTopLevelDecl);
|
|
m_CurTransaction->append(DCI);
|
|
|
|
if (getTransaction()->getIssuedDiags() == Transaction::kErrors)
|
|
return true;
|
|
|
|
if (comesFromASTReader(DGR)) {
|
|
for (DeclGroupRef::iterator DI = DGR.begin(), DE = DGR.end();
|
|
DI != DE; ++DI) {
|
|
DeclGroupRef SplitDGR(*DI);
|
|
// FIXME: The special namespace treatment (not sending itself to
|
|
// CodeGen, but only its content - if the contained decl should be
|
|
// emitted) works around issue with the static initialization when
|
|
// having a PCH and loading a library. We don't want to generate
|
|
// code for the static that will come through the library.
|
|
//
|
|
// This will be fixed with the clang::Modules. Make sure we remember.
|
|
// assert(!getCI()->getLangOpts().Modules && "Please revisit!");
|
|
if (NamespaceDecl* ND = dyn_cast<NamespaceDecl>(*DI)) {
|
|
for (NamespaceDecl::decl_iterator NDI = ND->decls_begin(),
|
|
EN = ND->decls_end(); NDI != EN; ++NDI) {
|
|
// Recurse over decls inside the namespace, like
|
|
// CodeGenModule::EmitNamespace() does.
|
|
if (!shouldIgnore(*NDI))
|
|
m_Consumer->HandleTopLevelDecl(DeclGroupRef(*NDI));
|
|
}
|
|
} else if (!shouldIgnore(*DI)) {
|
|
m_Consumer->HandleTopLevelDecl(DeclGroupRef(*DI));
|
|
}
|
|
continue;
|
|
}
|
|
} else {
|
|
|
|
// FIXME: This is a temporary fix for the ROOT module preloading mechanism.
|
|
// When we preload modules we would like to enable a module as if we called
|
|
// clang::Sema::ActOnModuleImport (which does not call HandleTopLevelDecl).
|
|
// However, we need a valid source locations as modules are very sensitive
|
|
// to them. In order to have a valid source location,
|
|
// Interpreter::loadModule calls '#pragma clang module import "A"', which
|
|
// calls HandleTopLevelDecl which causes CodeGen to run the module
|
|
// initializers eagerly.
|
|
if (DGR.isSingleDecl() && isa<ImportDecl>(DGR.getSingleDecl()))
|
|
return true;
|
|
|
|
m_Consumer->HandleTopLevelDecl(DGR);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void DeclCollector::HandleInterestingDecl(DeclGroupRef DGR) {
|
|
assertHasTransaction(m_CurTransaction);
|
|
Transaction::DelayCallInfo DCI(DGR, Transaction::kCCIHandleInterestingDecl);
|
|
m_CurTransaction->append(DCI);
|
|
if (m_Consumer
|
|
&& (!comesFromASTReader(DGR) || !shouldIgnore(*DGR.begin())))
|
|
m_Consumer->HandleTopLevelDecl(DGR);
|
|
}
|
|
|
|
void DeclCollector::HandleTagDeclDefinition(TagDecl* TD) {
|
|
assertHasTransaction(m_CurTransaction);
|
|
Transaction::DelayCallInfo DCI(DeclGroupRef(TD),
|
|
Transaction::kCCIHandleTagDeclDefinition);
|
|
m_CurTransaction->append(DCI);
|
|
if (m_Consumer
|
|
&& (!comesFromASTReader(DeclGroupRef(TD))
|
|
|| !shouldIgnore(TD)))
|
|
m_Consumer->HandleTagDeclDefinition(TD);
|
|
}
|
|
|
|
void DeclCollector::HandleInvalidTagDeclDefinition(clang::TagDecl *TD){
|
|
assertHasTransaction(m_CurTransaction);
|
|
Transaction::DelayCallInfo DCI(DeclGroupRef(TD),
|
|
Transaction::kCCIHandleTagDeclDefinition);
|
|
m_CurTransaction->append(DCI);
|
|
m_CurTransaction->setIssuedDiags(Transaction::kErrors);
|
|
if (m_Consumer
|
|
&& (!comesFromASTReader(DeclGroupRef(TD))
|
|
|| !shouldIgnore(TD)))
|
|
m_Consumer->HandleInvalidTagDeclDefinition(TD);
|
|
}
|
|
|
|
void DeclCollector::HandleVTable(CXXRecordDecl* RD) {
|
|
assertHasTransaction(m_CurTransaction);
|
|
Transaction::DelayCallInfo DCI(DeclGroupRef(RD),
|
|
Transaction::kCCIHandleVTable);
|
|
m_CurTransaction->append(DCI);
|
|
|
|
if (m_Consumer
|
|
&& (!comesFromASTReader(DeclGroupRef(RD))
|
|
|| !shouldIgnore(RD)))
|
|
m_Consumer->HandleVTable(RD);
|
|
// Intentional no-op. It comes through Sema::DefineUsedVTables, which
|
|
// comes either Sema::ActOnEndOfTranslationUnit or while instantiating a
|
|
// template. In our case we will do it on transaction commit, without
|
|
// keeping track of used vtables, because we have cases where we bypass the
|
|
// clang/AST and directly ask the module so that we have to generate
|
|
// everything without extra smartness.
|
|
}
|
|
|
|
void DeclCollector::CompleteTentativeDefinition(VarDecl* VD) {
|
|
assertHasTransaction(m_CurTransaction);
|
|
// C has tentative definitions which we might need to deal with when running
|
|
// in C mode.
|
|
Transaction::DelayCallInfo DCI(DeclGroupRef(VD),
|
|
Transaction::kCCICompleteTentativeDefinition);
|
|
m_CurTransaction->append(DCI);
|
|
if (m_Consumer
|
|
&& (!comesFromASTReader(DeclGroupRef(VD))
|
|
|| !shouldIgnore(VD)))
|
|
m_Consumer->CompleteTentativeDefinition(VD);
|
|
}
|
|
|
|
void DeclCollector::HandleTranslationUnit(ASTContext& /*Ctx*/) {
|
|
//if (m_Consumer)
|
|
// m_Consumer->HandleTranslationUnit(Ctx);
|
|
}
|
|
|
|
void DeclCollector::HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) {
|
|
assertHasTransaction(m_CurTransaction);
|
|
Transaction::DelayCallInfo DCI(DeclGroupRef(D),
|
|
Transaction::kCCIHandleCXXImplicitFunctionInstantiation);
|
|
m_CurTransaction->append(DCI);
|
|
if (m_Consumer
|
|
&& (!comesFromASTReader(DeclGroupRef(D))
|
|
|| !shouldIgnore(D)))
|
|
m_Consumer->HandleCXXImplicitFunctionInstantiation(D);
|
|
}
|
|
|
|
void DeclCollector::HandleCXXStaticMemberVarInstantiation(VarDecl *D) {
|
|
assertHasTransaction(m_CurTransaction);
|
|
Transaction::DelayCallInfo DCI(DeclGroupRef(D),
|
|
Transaction::kCCIHandleCXXStaticMemberVarInstantiation);
|
|
m_CurTransaction->append(DCI);
|
|
if (m_Consumer
|
|
&& (!comesFromASTReader(DeclGroupRef(D))
|
|
|| !shouldIgnore(D)))
|
|
m_Consumer->HandleCXXStaticMemberVarInstantiation(D);
|
|
}
|
|
|
|
} // namespace cling
|