Prepare dynamic scopes for integration in ROOT;
* Merge the DynamicIDHandler into InterpreterCallbacks. The DynamicIDHandler was used to define the failed lookup names as dependent, so that later on they could be "healed" by the EvaluateTSynthesizer - by generating a call to gCling->EvaluateT... * Add a flag in the InterpreterCallbacks giving a hint when the failed lookup is performed - during compilation time or during evaluation type, maybe its name is misnomer. Maybe I should rename it to IsInRuntimeEvalMode or smth like that... * Simplify the evaluation routine. The idea is to be merged with one of the existing - either echo or evaluate * Implement proper mock object as test dynamic symbol resolver callback. Now it fully relies on InterpreterCallbacks. * Implement dynamic expression node marking. Every node that needs runtime resolution is marked as dependent. However, clang marks other nodes as dependent, too (eg. templates). Until now it was very difficult to distinguish between both. Now every "artificially" (marked by us) node's declaration is annotated so that it could be found back easily by using a simple RecursiveASTVisitor. Generally we have two issues when using dynamic scopes - filtering the failed lookups that need to be evaluated at runtime, and filtering the "artificially" marked as dependent dynamic nodes/decls. Whereas the second issue is solved by using the annotations, the first one is more tricky to solve because clang doesn't give us enough information to conclude what should be done. For now I have narrowed down the cases so that dynamic nodes can appear only in function decls. git-svn-id: http://root.cern.ch/svn/root/trunk@48575 27541ba8-7e3a-0410-8455-c3a389f83636
This commit is contained in:
parent
4c4a43770d
commit
3bcfc8b25e
@ -1,54 +0,0 @@
|
||||
//--------------------------------------------------------------------*- C++ -*-
|
||||
// CLING - the C++ LLVM-based InterpreterG :)
|
||||
// version: $Id$
|
||||
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "cling/Interpreter/InterpreterCallbacks.h"
|
||||
|
||||
namespace cling {
|
||||
|
||||
class Interpreter;
|
||||
|
||||
/// \brief Provides last chance of recovery for clang semantic analysis.
|
||||
/// When the compiler doesn't find the symbol in its symbol table it asks
|
||||
/// its ExternalSemaSource to look for the symbol.
|
||||
///
|
||||
/// In contrast to the compiler point of view, where these symbols must be
|
||||
/// errors, the interpreter's point of view these symbols are to be
|
||||
/// evaluated at runtime. For that reason the interpreter marks all unknown
|
||||
/// by the compiler symbols to be with delayed lookup (evaluation).
|
||||
/// One have to be carefull in the cases, in which the compiler expects that
|
||||
/// the lookup will fail!
|
||||
class DynamicIDHandler : public InterpreterExternalSemaSource {
|
||||
public:
|
||||
DynamicIDHandler(InterpreterCallbacks* C)
|
||||
: InterpreterExternalSemaSource(C) { }
|
||||
~DynamicIDHandler();
|
||||
|
||||
/// \brief Provides last resort lookup for failed unqualified lookups
|
||||
///
|
||||
/// If there is failed lookup, tell sema to create an artificial declaration
|
||||
/// which is of dependent type. So the lookup result is marked as dependent
|
||||
/// and the diagnostics are suppressed. After that is's an interpreter's
|
||||
/// responsibility to fix all these fake declarations and lookups.
|
||||
/// It is done by the DynamicExprTransformer.
|
||||
///
|
||||
/// \param[out] R The recovered symbol.
|
||||
/// \param[in] S The scope in which the lookup failed.
|
||||
///
|
||||
/// \returns true if the name was found and the compilation should continue.
|
||||
///
|
||||
virtual bool LookupUnqualified(clang::LookupResult& R, clang::Scope* S);
|
||||
|
||||
/// \brief Checks whether this name should be marked as dynamic, i.e. for
|
||||
/// runtime resolution.
|
||||
///
|
||||
/// \param[out] R The recovered symbol.
|
||||
/// \param[in] S The scope in which the lookup failed.
|
||||
///
|
||||
/// \returns true if the name should be marked as dynamic.
|
||||
///
|
||||
static bool IsDynamicLookup(clang::LookupResult& R, clang::Scope* S);
|
||||
};
|
||||
} // end namespace cling
|
@ -73,6 +73,10 @@ namespace cling {
|
||||
/// callbacks.
|
||||
///
|
||||
llvm::OwningPtr<InterpreterExternalSemaSource> m_SemaExternalSource;
|
||||
|
||||
///\brief DynamicScopes only! Set to true only when evaluating dynamic expr.
|
||||
///
|
||||
bool m_IsRuntime;
|
||||
public:
|
||||
InterpreterCallbacks(Interpreter* interp,
|
||||
InterpreterExternalSemaSource* IESS = 0);
|
||||
@ -104,6 +108,11 @@ namespace cling {
|
||||
///\param[out] - The transaction that was reverted.
|
||||
///
|
||||
virtual void TransactionUnloaded(const Transaction&) {}
|
||||
|
||||
///\brief DynamicScopes only! Set to true if it is currently evaluating a
|
||||
/// dynamic expr.
|
||||
///
|
||||
void SetIsRuntime(bool val) { m_IsRuntime = val; }
|
||||
};
|
||||
} // end namespace cling
|
||||
|
||||
@ -137,6 +146,7 @@ namespace cling {
|
||||
~SymbolResolverCallback();
|
||||
|
||||
bool LookupObject(clang::LookupResult& R, clang::Scope* S);
|
||||
bool ShouldResolveAtRuntime(clang::LookupResult& R, clang::Scope* S);
|
||||
};
|
||||
} // end test
|
||||
} // end cling
|
||||
|
@ -6,13 +6,13 @@
|
||||
|
||||
#include "DynamicLookup.h"
|
||||
|
||||
#include "cling/Interpreter/DynamicLookupExternalSemaSource.h"
|
||||
#include "cling/Interpreter/Interpreter.h"
|
||||
#include "cling/Interpreter/InterpreterCallbacks.h"
|
||||
#include "cling/Interpreter/Transaction.h"
|
||||
#include "cling/Utils/AST.h"
|
||||
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Sema/Scope.h"
|
||||
#include "clang/Sema/Lookup.h"
|
||||
@ -20,72 +20,6 @@
|
||||
|
||||
using namespace clang;
|
||||
|
||||
namespace cling {
|
||||
|
||||
// pin the vtable to this file
|
||||
DynamicIDHandler::~DynamicIDHandler() { }
|
||||
|
||||
bool DynamicIDHandler::LookupUnqualified(LookupResult& R, Scope* S) {
|
||||
|
||||
// FIXME: Extract out somewhere else.
|
||||
if (!IsDynamicLookup(R, S))
|
||||
return false;
|
||||
|
||||
if (getCallbacks()) {
|
||||
return getCallbacks()->LookupObject(R, S);
|
||||
}
|
||||
|
||||
DeclarationName Name = R.getLookupName();
|
||||
IdentifierInfo* II = Name.getAsIdentifierInfo();
|
||||
SourceLocation Loc = R.getNameLoc();
|
||||
ASTContext& C = R.getSema().getASTContext();
|
||||
VarDecl* Result = VarDecl::Create(C,
|
||||
R.getSema().getFunctionLevelDeclContext(),
|
||||
Loc,
|
||||
Loc,
|
||||
II,
|
||||
C.DependentTy,
|
||||
/*TypeSourceInfo*/0,
|
||||
SC_None,
|
||||
SC_None);
|
||||
if (Result) {
|
||||
R.addDecl(Result);
|
||||
// Say that we can handle the situation. Clang should try to recover
|
||||
return true;
|
||||
}
|
||||
// We cannot handle the situation. Give up
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DynamicIDHandler::IsDynamicLookup(LookupResult& R, Scope* S) {
|
||||
if (R.getLookupKind() != Sema::LookupOrdinaryName) return false;
|
||||
if (R.isForRedeclaration()) return false;
|
||||
// FIXME: Figure out better way to handle:
|
||||
// C++ [basic.lookup.classref]p1:
|
||||
// In a class member access expression (5.2.5), if the . or -> token is
|
||||
// immediately followed by an identifier followed by a <, the
|
||||
// identifier must be looked up to determine whether the < is the
|
||||
// beginning of a template argument list (14.2) or a less-than operator.
|
||||
// The identifier is first looked up in the class of the object
|
||||
// expression. If the identifier is not found, it is then looked up in
|
||||
// the context of the entire postfix-expression and shall name a class
|
||||
// or function template.
|
||||
//
|
||||
// We want to ignore object(.|->)member<template>
|
||||
if (R.getSema().PP.LookAhead(0).getKind() == tok::less)
|
||||
// TODO: check for . or -> in the cached token stream
|
||||
return false;
|
||||
|
||||
for (Scope* DepScope = S; DepScope; DepScope = DepScope->getParent()) {
|
||||
if (DeclContext* Ctx = static_cast<DeclContext*>(DepScope->getEntity())) {
|
||||
return !Ctx->isDependentContext();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // end namespace cling
|
||||
|
||||
namespace {
|
||||
|
||||
class StmtPrinterHelper : public PrinterHelper {
|
||||
@ -254,7 +188,7 @@ namespace cling {
|
||||
NamedDecl* ND = R.getFoundDecl();
|
||||
m_NoRange = ND->getSourceRange();
|
||||
m_NoSLoc = m_NoRange.getBegin();
|
||||
m_NoELoc = m_NoRange.getEnd();
|
||||
m_NoELoc = m_NoRange.getEnd();
|
||||
}
|
||||
|
||||
void EvaluateTSynthesizer::Transform() {
|
||||
@ -820,34 +754,45 @@ namespace cling {
|
||||
|
||||
// Helpers
|
||||
|
||||
bool EvaluateTSynthesizer::ShouldVisit(Decl* D) {
|
||||
while (true) {
|
||||
if (isa<TemplateTemplateParmDecl>(D))
|
||||
return false;
|
||||
if (isa<ClassTemplateDecl>(D))
|
||||
return false;
|
||||
if (isa<FriendTemplateDecl>(D))
|
||||
return false;
|
||||
if (isa<ClassTemplatePartialSpecializationDecl>(D))
|
||||
return false;
|
||||
if (CXXRecordDecl* CXX = dyn_cast<CXXRecordDecl>(D)) {
|
||||
if (CXX->getDescribedClassTemplate())
|
||||
return false;
|
||||
}
|
||||
if (CXXMethodDecl* CXX = dyn_cast<CXXMethodDecl>(D)) {
|
||||
if (CXX->getDescribedFunctionTemplate())
|
||||
return false;
|
||||
}
|
||||
if (isa<TranslationUnitDecl>(D)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (DeclContext* DC = D->getDeclContext())
|
||||
if (!(D = dyn_cast<Decl>(DC)))
|
||||
break;
|
||||
// Class extracting recursively every decl defined somewhere.
|
||||
class DeclVisitor : public RecursiveASTVisitor<DeclVisitor> {
|
||||
private:
|
||||
bool m_ShouldVisitSubTree;
|
||||
public:
|
||||
DeclVisitor() : m_ShouldVisitSubTree(false) {}
|
||||
|
||||
bool getShouldVisitSubTree() const { return m_ShouldVisitSubTree; }
|
||||
|
||||
bool VisitDecl(Decl* D) {
|
||||
// FIXME: Here we should have our custom attribute.
|
||||
if (AnnotateAttr* A = D->getAttr<AnnotateAttr>())
|
||||
if (A->getAnnotation().equals("__ResolveAtRuntime")) {
|
||||
m_ShouldVisitSubTree = true;
|
||||
return false; // returning false will abort the in-depth traversal.
|
||||
}
|
||||
|
||||
return true; // returning false will abort the in-depth traversal.
|
||||
}
|
||||
|
||||
return true;
|
||||
bool VisitDeclStmt(DeclStmt* DS) {
|
||||
DeclGroupRef DGR = DS->getDeclGroup();
|
||||
for (DeclGroupRef::const_iterator I = DGR.begin(),
|
||||
E = DGR.end(); I != E; ++I)
|
||||
TraverseDecl(*I);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisitDeclRefExpr(DeclRefExpr* DRE) {
|
||||
TraverseDecl(DRE->getDecl());
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
bool EvaluateTSynthesizer::ShouldVisit(Decl* D) {
|
||||
DeclVisitor Visitor;
|
||||
Visitor.TraverseDecl(D);
|
||||
return Visitor.getShouldVisitSubTree();
|
||||
}
|
||||
|
||||
bool EvaluateTSynthesizer::IsArtificiallyDependent(Expr* Node) {
|
||||
|
@ -57,7 +57,6 @@ namespace cling {
|
||||
} //end namespace cling
|
||||
|
||||
namespace cling {
|
||||
class DynamicIDHandler;
|
||||
|
||||
typedef llvm::DenseMap<clang::Stmt*, clang::Stmt*> MapTy;
|
||||
|
||||
|
@ -791,21 +791,16 @@ namespace cling {
|
||||
Sema& TheSema = getCI()->getSema();
|
||||
if (!DC)
|
||||
DC = TheSema.getASTContext().getTranslationUnitDecl();
|
||||
|
||||
// Set up the declaration context
|
||||
DeclContext* CurContext;
|
||||
|
||||
CurContext = TheSema.CurContext;
|
||||
TheSema.CurContext = DC;
|
||||
// We can't PushDeclContext, because we don't have scope.
|
||||
Sema::ContextRAII pushedDC(TheSema, DC);
|
||||
|
||||
StoredValueRef Result;
|
||||
if (TheSema.getExternalSource()) {
|
||||
(ValuePrinterReq) ? echo(expr, &Result) : evaluate(expr, Result);
|
||||
}
|
||||
else
|
||||
(ValuePrinterReq) ? echo(expr, &Result) : evaluate(expr, Result);
|
||||
|
||||
TheSema.CurContext = CurContext;
|
||||
getCallbacks()->SetIsRuntime(true);
|
||||
if (ValuePrinterReq)
|
||||
echo(expr, &Result);
|
||||
else
|
||||
evaluate(expr, Result);
|
||||
getCallbacks()->SetIsRuntime(false);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include "cling/Interpreter/InterpreterCallbacks.h"
|
||||
|
||||
#include "cling/Interpreter/DynamicLookupExternalSemaSource.h"
|
||||
#include "cling/Interpreter/Interpreter.h"
|
||||
|
||||
#include "clang/Sema/Sema.h"
|
||||
@ -28,7 +27,7 @@ namespace cling {
|
||||
|
||||
InterpreterCallbacks::InterpreterCallbacks(Interpreter* interp,
|
||||
InterpreterExternalSemaSource* IESS)
|
||||
: m_Interpreter(interp), m_SemaExternalSource(IESS) {
|
||||
: m_Interpreter(interp), m_SemaExternalSource(IESS), m_IsRuntime(false) {
|
||||
if (!IESS)
|
||||
m_SemaExternalSource.reset(new InterpreterExternalSemaSource(this));
|
||||
m_Interpreter->getSema().addExternalSource(m_SemaExternalSource.get());
|
||||
@ -53,8 +52,9 @@ namespace cling {
|
||||
#include "DynamicLookup.h"
|
||||
#include "cling/Utils/AST.h"
|
||||
|
||||
#include "clang/Sema/Lookup.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Sema/Lookup.h"
|
||||
#include "clang/Sema/Scope.h"
|
||||
namespace cling {
|
||||
namespace test {
|
||||
@ -100,29 +100,93 @@ namespace test {
|
||||
}
|
||||
|
||||
SymbolResolverCallback::SymbolResolverCallback(Interpreter* interp)
|
||||
: InterpreterCallbacks(interp, new DynamicIDHandler(this)), m_TesterDecl(0) {
|
||||
: InterpreterCallbacks(interp), m_TesterDecl(0) {
|
||||
m_Interpreter->process("cling::test::Tester = new cling::test::TestProxy();");
|
||||
}
|
||||
|
||||
SymbolResolverCallback::~SymbolResolverCallback() { }
|
||||
|
||||
bool SymbolResolverCallback::LookupObject(LookupResult& R, Scope* S) {
|
||||
if (!DynamicIDHandler::IsDynamicLookup(R, S))
|
||||
if (m_IsRuntime) {
|
||||
// Only for demo resolve all unknown objects to cling::test::Tester
|
||||
if (!m_TesterDecl) {
|
||||
clang::Sema& SemaR = m_Interpreter->getSema();
|
||||
clang::NamespaceDecl* NSD = utils::Lookup::Namespace(&SemaR, "cling");
|
||||
NSD = utils::Lookup::Namespace(&SemaR, "test", NSD);
|
||||
m_TesterDecl = utils::Lookup::Named(&SemaR, "Tester", NSD);
|
||||
}
|
||||
assert (m_TesterDecl && "Tester not found!");
|
||||
R.addDecl(m_TesterDecl);
|
||||
return true; // Tell clang to continue.
|
||||
}
|
||||
|
||||
if (ShouldResolveAtRuntime(R, S)) {
|
||||
ASTContext& C = R.getSema().getASTContext();
|
||||
DeclContext* DC = 0;
|
||||
// For DeclContext-less scopes like if (dyn_expr) {}
|
||||
while (!DC) {
|
||||
DC = static_cast<DeclContext*>(S->getEntity());
|
||||
S = S->getParent();
|
||||
}
|
||||
DeclarationName Name = R.getLookupName();
|
||||
IdentifierInfo* II = Name.getAsIdentifierInfo();
|
||||
SourceLocation Loc = R.getNameLoc();
|
||||
VarDecl* Res = VarDecl::Create(C, DC, Loc, Loc, II, C.DependentTy,
|
||||
/*TypeSourceInfo*/0, SC_None, SC_None);
|
||||
|
||||
// Annotate the decl to give a hint in cling. FIXME: Current implementation
|
||||
// is a gross hack, because TClingCallbacks shouldn't know about
|
||||
// EvaluateTSynthesizer at all!
|
||||
SourceRange invalidRange;
|
||||
Res->addAttr(new (C) AnnotateAttr(invalidRange, C, "__ResolveAtRuntime"));
|
||||
R.addDecl(Res);
|
||||
DC->addDecl(Res);
|
||||
// Say that we can handle the situation. Clang should try to recover
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SymbolResolverCallback::ShouldResolveAtRuntime(LookupResult& R,
|
||||
Scope* S) {
|
||||
|
||||
if (R.getLookupKind() != Sema::LookupOrdinaryName)
|
||||
return false;
|
||||
// We should react only on empty lookup result.
|
||||
|
||||
if (R.isForRedeclaration())
|
||||
return false;
|
||||
|
||||
if (!R.empty())
|
||||
return false;
|
||||
|
||||
// Only for demo resolve all unknown objects to cling::test::Tester
|
||||
if (!m_TesterDecl) {
|
||||
clang::Sema& SemaRef = m_Interpreter->getSema();
|
||||
clang::NamespaceDecl* NSD = utils::Lookup::Namespace(&SemaRef, "cling");
|
||||
NSD = utils::Lookup::Namespace(&SemaRef, "test", NSD);
|
||||
m_TesterDecl = utils::Lookup::Named(&SemaRef, "Tester", NSD);
|
||||
// FIXME: Figure out better way to handle:
|
||||
// C++ [basic.lookup.classref]p1:
|
||||
// In a class member access expression (5.2.5), if the . or -> token is
|
||||
// immediately followed by an identifier followed by a <, the
|
||||
// identifier must be looked up to determine whether the < is the
|
||||
// beginning of a template argument list (14.2) or a less-than operator.
|
||||
// The identifier is first looked up in the class of the object
|
||||
// expression. If the identifier is not found, it is then looked up in
|
||||
// the context of the entire postfix-expression and shall name a class
|
||||
// or function template.
|
||||
//
|
||||
// We want to ignore object(.|->)member<template>
|
||||
if (R.getSema().PP.LookAhead(0).getKind() == tok::less)
|
||||
// TODO: check for . or -> in the cached token stream
|
||||
return false;
|
||||
|
||||
for (Scope* DepScope = S; DepScope; DepScope = DepScope->getParent()) {
|
||||
if (DeclContext* Ctx = static_cast<DeclContext*>(DepScope->getEntity())) {
|
||||
if (!Ctx->isDependentContext())
|
||||
// For now we support only the prompt.
|
||||
if (isa<FunctionDecl>(Ctx))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
assert (m_TesterDecl && "Tester not found!");
|
||||
R.addDecl(m_TesterDecl);
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // end test
|
||||
} // end cling
|
||||
|
Loading…
x
Reference in New Issue
Block a user