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:
Vassil Vassilev 2013-02-14 17:29:30 +00:00
parent 4c4a43770d
commit 3bcfc8b25e
6 changed files with 135 additions and 176 deletions

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -57,7 +57,6 @@ namespace cling {
} //end namespace cling
namespace cling {
class DynamicIDHandler;
typedef llvm::DenseMap<clang::Stmt*, clang::Stmt*> MapTy;

View File

@ -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;
}

View File

@ -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