DefinitionShadower: reuse __cling_N5xxx namespace for decls coming from the same transaction

This commit is contained in:
Javier Lopez-Gomez 2019-09-17 18:05:57 +02:00 committed by SFT
parent 93ae61f704
commit 15e67afef2
4 changed files with 51 additions and 28 deletions

View File

@ -27,6 +27,7 @@ namespace clang {
class FunctionDecl;
class IdentifierInfo;
class NamedDecl;
class NamespaceDecl;
class MacroDirective;
class Preprocessor;
struct PrintingPolicy;
@ -142,6 +143,11 @@ namespace cling {
///
CompilationOptions m_Opts;
///\brief If DefinitionShadower is enabled, the `__cling_N5xxx' namespace
/// in which to nest global definitions (if any).
///
clang::NamespaceDecl* m_DefinitionShadowNS = nullptr;
///\brief The llvm Module containing the information that we will revert
///
std::shared_ptr<llvm::Module> m_Module;
@ -316,6 +322,11 @@ namespace cling {
m_Opts = CO;
}
clang::NamespaceDecl* getDefinitionShadowNS() const
{ return m_DefinitionShadowNS; }
void setDefinitionShadowNS(clang::NamespaceDecl* NS);
///\brief Returns the first declaration of the transaction.
///
clang::DeclGroupRef getFirstDecl() const {

View File

@ -35,17 +35,20 @@ namespace cling {
/// \brief Returns whether the given {Function,Tag,Var}Decl/TemplateDecl is a definition.
static bool isDefinition(const Decl *D) {
if (auto FD = dyn_cast<FunctionDecl>(D)) return FD->isThisDeclarationADefinition();
if (auto TD = dyn_cast<TagDecl>(D)) return TD->isThisDeclarationADefinition();
if (auto VD = dyn_cast<VarDecl>(D)) return VD->isThisDeclarationADefinition();
if (auto TD = dyn_cast<TemplateDecl>(D)) return isDefinition(TD->getTemplatedDecl());
if (auto FD = dyn_cast<FunctionDecl>(D))
return FD->isThisDeclarationADefinition();
if (auto TD = dyn_cast<TagDecl>(D))
return TD->isThisDeclarationADefinition();
if (auto VD = dyn_cast<VarDecl>(D))
return VD->isThisDeclarationADefinition();
if (auto TD = dyn_cast<TemplateDecl>(D))
return isDefinition(TD->getTemplatedDecl());
return true;
}
DefinitionShadower::DefinitionShadower(Sema& S, Interpreter& I)
: ASTTransformer(&S), m_Interp(I), m_Context(S.getASTContext()),
m_TU(S.getASTContext().getTranslationUnitDecl()),
m_UniqueNameCounter(0)
: ASTTransformer(&S), m_Interp(I), m_Context(S.getASTContext()),
m_TU(S.getASTContext().getTranslationUnitDecl())
{}
bool DefinitionShadower::isClingShadowNamespace(const DeclContext *DC) {
@ -121,7 +124,7 @@ namespace cling {
return;
// DeclExtractor shall move local declarations to the TU. Invalidate all
// previous definitions (that may clash) before it runs.
// previous definitions (that may clash) before it runs.
auto CS = dyn_cast<CompoundStmt>(D->getBody());
for (auto &I : CS->body()) {
auto DS = dyn_cast<DeclStmt>(I);
@ -136,21 +139,20 @@ namespace cling {
}
void DefinitionShadower::invalidatePreviousDefinitions(Decl *D) const {
if (auto TD = dyn_cast<TagDecl>(D))
invalidatePreviousDefinitions(TD);
else if (auto FD = dyn_cast<FunctionDecl>(D))
if (auto FD = dyn_cast<FunctionDecl>(D))
invalidatePreviousDefinitions(FD);
else if (auto TD = dyn_cast<TemplateDecl>(D))
invalidatePreviousDefinitions(TD);
else if (auto ND = dyn_cast<NamedDecl>(D))
invalidatePreviousDefinitions(ND);
}
ASTTransformer::Result DefinitionShadower::Transform(Decl* D) {
const CompilationOptions &CO = getTransaction()->getCompilationOpts();
Transaction *T = getTransaction();
const CompilationOptions &CO = T->getCompilationOpts();
// Global declarations whose origin is the Cling prompt are subject to be
// nested in a `__cling_N5' namespace.
if (!CO.EnableShadowing
|| D->getLexicalDeclContext() != m_TU || D->isInvalidDecl()
|| isa<UsingDirectiveDecl>(D)
|| isa<UsingDirectiveDecl>(D) || isa<UsingDecl>(D)
// FIXME: NamespaceDecl requires additional processing (TBD)
|| isa<NamespaceDecl>(D)
|| (isa<FunctionDecl>(D) && cast<FunctionDecl>(D)->isTemplateInstantiation())
@ -158,13 +160,21 @@ namespace cling {
m_Context.getSourceManager()}))
return Result(D, true);
NamespaceDecl *NS = NamespaceDecl::Create(m_Context, m_TU, /*inline=*/true,
SourceLocation(), SourceLocation(),
&m_Context.Idents.get("__cling_N5"
+ std::to_string(m_UniqueNameCounter++)),
nullptr);
m_TU->removeDecl(D);
// Each transaction gets at most a `__cling_N5xxx' namespace. If `T' already
// has one, reuse it.
auto NS = T->getDefinitionShadowNS();
if (!NS) {
NS = NamespaceDecl::Create(m_Context, m_TU, /*inline=*/true,
SourceLocation(), SourceLocation(),
&m_Context.Idents.get("__cling_N5"
+ std::to_string(m_UniqueNameCounter++)),
nullptr);
//NS->setImplicit();
m_TU->addDecl(NS);
T->setDefinitionShadowNS(NS);
}
m_TU->removeDecl(D);
if (isa<CXXRecordDecl>(D->getDeclContext()))
D->setLexicalDeclContext(NS);
else
@ -173,17 +183,11 @@ namespace cling {
// templated decl. This is used for name mangling; fix it to avoid clashing.
if (auto FTD = dyn_cast<FunctionTemplateDecl>(D))
FTD->getTemplatedDecl()->setDeclContext(NS);
NS->addDecl(D);
m_TU->addDecl(NS);
//NS->setImplicit();
// Invalidate previous definitions so that LookupResult::resolveKind() does not
// mark resolution as ambiguous.
invalidatePreviousDefinitions(D);
// Ensure `NS` is unloaded from the AST on transaction rollback, e.g. '.undo X'
getTransaction()->append(NS);
return Result(D, true);
}
} // end namespace cling

View File

@ -54,7 +54,7 @@ namespace cling {
Interpreter &m_Interp;
clang::TranslationUnitDecl *m_TU;
unsigned long long m_UniqueNameCounter;
unsigned long long m_UniqueNameCounter = 0;
/// \brief Hide a global declaration from SemaLookup; internally used in
/// `invalidatePreviousDefinitions()'. This directly manipulates lookup

View File

@ -41,6 +41,7 @@ namespace cling {
m_State = kCollecting;
m_IssuedDiags = kNone;
m_Opts = CompilationOptions();
m_DefinitionShadowNS = 0;
m_Module = 0;
m_WrapperFD = 0;
m_Next = 0;
@ -61,6 +62,13 @@ namespace cling {
}
}
void Transaction::setDefinitionShadowNS(clang::NamespaceDecl* NS) {
assert(!m_DefinitionShadowNS && "Transaction has a __cling_N5xxx NS?");
m_DefinitionShadowNS = NS;
// Ensure `NS` is unloaded from the AST on transaction rollback, e.g. '.undo X'
append(static_cast<clang::Decl*>(NS));
}
NamedDecl* Transaction::containsNamedDecl(llvm::StringRef name) const {
for (auto I = decls_begin(), E = decls_end(); I != E; ++I) {
for (auto DI : I->m_DGR) {