diff --git a/interpreter/cling/include/cling/Interpreter/CompilationOptions.h b/interpreter/cling/include/cling/Interpreter/CompilationOptions.h index 76a111b215cb503d245cead9e96c5703f1061269..ecbcfbdc7ec44dbedd160c43b03627f475beeeee 100644 --- a/interpreter/cling/include/cling/Interpreter/CompilationOptions.h +++ b/interpreter/cling/include/cling/Interpreter/CompilationOptions.h @@ -26,6 +26,10 @@ namespace cling { unsigned ValuePrinting : 2; enum ValuePrinting { VPDisabled, VPEnabled, VPAuto }; + ///\brief Whether or not to return result from an execution. + /// + unsigned ResultEvaluation: 1; + ///\brief Whether or not to extend the static scope with new information /// about the names available only at runtime /// @@ -43,6 +47,7 @@ namespace cling { CompilationOptions() { DeclarationExtraction = 1; ValuePrinting = VPAuto; + ResultEvaluation = 0; DynamicScoping = 0; Debug = 0; CodeGeneration = 1; diff --git a/interpreter/cling/lib/Interpreter/IncrementalParser.cpp b/interpreter/cling/lib/Interpreter/IncrementalParser.cpp index 325dfde50b1bc8ff6e3018686d27abf49b7a8542..7a7a2434a0eaa5b39af72a01d3445ad84aa03cc2 100644 --- a/interpreter/cling/lib/Interpreter/IncrementalParser.cpp +++ b/interpreter/cling/lib/Interpreter/IncrementalParser.cpp @@ -10,6 +10,7 @@ #include "DeclCollector.h" #include "DeclExtractor.h" #include "DynamicLookup.h" +#include "ReturnSynthesizer.h" #include "ValuePrinterSynthesizer.h" #include "cling/Interpreter/CIFactory.h" #include "cling/Interpreter/Interpreter.h" @@ -69,6 +70,7 @@ namespace cling { m_TTransformers.push_back(new DeclExtractor(&getCI()->getSema())); m_TTransformers.push_back(new ValuePrinterSynthesizer(&CI->getSema(), 0)); + m_TTransformers.push_back(new ReturnSynthesizer(&CI->getSema())); m_TTransformers.push_back(new ASTDumper()); m_Parser.reset(new Parser(CI->getPreprocessor(), CI->getSema(), @@ -247,8 +249,9 @@ namespace cling { return Result; } - Transaction* IncrementalParser::Parse(llvm::StringRef input) { - beginTransaction(CompilationOptions()); + Transaction* IncrementalParser::Parse(llvm::StringRef input, + const CompilationOptions& Opts) { + beginTransaction(Opts); ParseInternal(input); endTransaction(); diff --git a/interpreter/cling/lib/Interpreter/IncrementalParser.h b/interpreter/cling/lib/Interpreter/IncrementalParser.h index 90cfa68e7eff621906b3939366039a7132ed3aba..13880fb54ea64e5d36d0b95a6c1eee8d0499f848 100644 --- a/interpreter/cling/lib/Interpreter/IncrementalParser.h +++ b/interpreter/cling/lib/Interpreter/IncrementalParser.h @@ -81,7 +81,7 @@ namespace cling { ///\brief Contains the transaction transformers. /// - llvm::SmallVector<TransactionTransformer*, 4> m_TTransformers; + llvm::SmallVector<TransactionTransformer*, 6> m_TTransformers; public: enum EParseResult { @@ -140,6 +140,10 @@ namespace cling { ///\brief Compiles the given input with the given compilation options. /// + ///\param[in] input - The code to compile. + ///\param[in] Opts - The compilation options to use. + ///\returns whether the operation was successful. + /// EParseResult Compile(llvm::StringRef input, const CompilationOptions& Opts); ///\brief Parses the given input without calling the custom consumers and @@ -149,9 +153,10 @@ namespace cling { /// different executable code. /// ///\param[in] input - The code to parse. - ///\returns The transaction coresponding to the input. + ///\param[in] Opts - The compilation options to use. + ///\returns The transaction corresponding to the input. /// - Transaction* Parse(llvm::StringRef input); + Transaction* Parse(llvm::StringRef input, const CompilationOptions& Opts); void unloadTransaction(Transaction* T); diff --git a/interpreter/cling/lib/Interpreter/Interpreter.cpp b/interpreter/cling/lib/Interpreter/Interpreter.cpp index 73ffe1f12be57a41aefd83ff4436d42dfc1d78d9..4bfd8bbca0a5834f284775bda924e62653171821 100644 --- a/interpreter/cling/lib/Interpreter/Interpreter.cpp +++ b/interpreter/cling/lib/Interpreter/Interpreter.cpp @@ -376,6 +376,7 @@ namespace cling { CompilationOptions CO; CO.DeclarationExtraction = 1; CO.ValuePrinting = CompilationOptions::VPAuto; + CO.ResultEvaluation = (bool)V; CO.DynamicScoping = isDynamicLookupEnabled(); CO.Debug = isPrintingAST(); @@ -400,6 +401,7 @@ namespace cling { CO.CodeGeneration = 0; CO.DeclarationExtraction = 0; CO.ValuePrinting = 0; + CO.ResultEvaluation = 0; CO.DynamicScoping = isDynamicLookupEnabled(); CO.Debug = isPrintingAST(); @@ -411,6 +413,7 @@ namespace cling { CompilationOptions CO; CO.DeclarationExtraction = 0; CO.ValuePrinting = 0; + CO.ResultEvaluation = 0; CO.DynamicScoping = isDynamicLookupEnabled(); CO.Debug = isPrintingAST(); @@ -426,6 +429,7 @@ namespace cling { CompilationOptions CO; CO.DeclarationExtraction = 0; CO.ValuePrinting = 0; + CO.ResultEvaluation = (bool)V; return EvaluateInternal(input, CO, V); } @@ -435,6 +439,7 @@ namespace cling { CompilationOptions CO; CO.DeclarationExtraction = 0; CO.ValuePrinting = CompilationOptions::VPEnabled; + CO.ResultEvaluation = 0; return EvaluateInternal(input, CO, V); } @@ -512,8 +517,6 @@ namespace cling { const CompilationOptions& CO, StoredValueRef* V /* = 0 */) { - Sema& TheSema = getCI()->getSema(); - DiagnosticsEngine& Diag = getCI()->getDiagnostics(); // Disable warnings which doesn't make sense when using the prompt // This gets reset with the clang::Diagnostics().Reset() @@ -531,7 +534,7 @@ namespace cling { QualType RetTy = getCI()->getASTContext().VoidTy; if (V) { - const Transaction* CurT = m_IncrParser->Parse(Wrapper); + const Transaction* CurT = m_IncrParser->Parse(Wrapper, CO); assert(CurT->size() && "No decls created by Parse!"); // Find the wrapper function declaration. @@ -540,67 +543,30 @@ namespace cling { // instantiation happened. Our wrapper function should be the // last decl in the set. // - FunctionDecl* TopLevelFD + FunctionDecl* FD = dyn_cast<FunctionDecl>(CurT->getLastDecl().getSingleDecl()); - assert(TopLevelFD && "No Decls Parsed?"); - DeclContext* CurContext = TheSema.CurContext; - TheSema.CurContext = TopLevelFD; - ASTContext& Context(getCI()->getASTContext()); - // We have to be able to mark the expression for printout. There are three - // scenarios: - // 0: Expression printing disabled - don't do anything just disable the - // consumer - // is our marker, even if there wasn't missing ';'. - // 1: Expression printing enabled - make sure we don't have NullStmt, - // which is used as a marker to suppress the print out. - // 2: Expression printing auto - do nothing - rely on the omitted ';' to - // not produce the suppress marker. - if (CompoundStmt* CS = dyn_cast<CompoundStmt>(TopLevelFD->getBody())) { + assert(FD && "No Decls Parsed?"); + if (CompoundStmt* CS = dyn_cast<CompoundStmt>(FD->getBody())) { // Collect all Stmts, contained in the CompoundStmt llvm::SmallVector<Stmt *, 4> Stmts; for (CompoundStmt::body_iterator iStmt = CS->body_begin(), eStmt = CS->body_end(); iStmt != eStmt; ++iStmt) Stmts.push_back(*iStmt); - - size_t indexOfLastExpr = Stmts.size(); + + int indexOfLastExpr = Stmts.size(); while(indexOfLastExpr--) { // find the trailing expression statement (skip e.g. null statements) - if (Expr* E = dyn_cast_or_null<Expr>(Stmts[indexOfLastExpr])) { - RetTy = E->getType(); - if (!RetTy->isVoidType()) { - // Change the void function's return type - FunctionProtoType::ExtProtoInfo EPI; - QualType FuncTy = Context.getFunctionType(RetTy,/* ArgArray = */0, - /* NumArgs = */0, EPI); - TopLevelFD->setType(FuncTy); - // Strip the parenthesis if any - if (ParenExpr* PE = dyn_cast<ParenExpr>(E)) - E = PE->getSubExpr(); - - // Change it with return stmt - Stmts[indexOfLastExpr] - = TheSema.ActOnReturnStmt(SourceLocation(), E).take(); - } + if (isa<Expr>(Stmts[indexOfLastExpr])) { // even if void: we found an expression break; } } - - // case 1: - if (CO.ValuePrinting == CompilationOptions::VPEnabled) - if (indexOfLastExpr < Stmts.size() - 1 && - isa<NullStmt>(Stmts[indexOfLastExpr + 1])) - Stmts.erase(Stmts.begin() + indexOfLastExpr); - // Stmts.insert(Stmts.begin() + indexOfLastExpr + 1, - // TheSema.ActOnNullStmt(SourceLocation()).take()); - - // Update the CompoundStmt body - CS->setStmts(TheSema.getASTContext(), Stmts.data(), Stmts.size()); - + if (indexOfLastExpr >= 0) { + Expr* lastExpr = dyn_cast<Expr>(Stmts[indexOfLastExpr]); + assert(lastExpr && "Last expr not found."); + RetTy = lastExpr->getType(); + } } - - TheSema.CurContext = CurContext; - m_IncrParser->commitCurrentTransaction(); } else @@ -611,7 +577,8 @@ namespace cling { if (RunFunction(WrapperName, RetTy)) { return Interpreter::kSuccess; } - } else if (RunFunction(WrapperName, RetTy, V)) { + } else if (RunFunction(WrapperName, RetTy, V)) { // Why we have to pass-in + // the type again? return Interpreter::kSuccess; } else { *V = StoredValueRef::invalidValue(); diff --git a/interpreter/cling/lib/Interpreter/ReturnSynthesizer.cpp b/interpreter/cling/lib/Interpreter/ReturnSynthesizer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef369d03c6534b1b395eb672b6def6e8a76fce8d --- /dev/null +++ b/interpreter/cling/lib/Interpreter/ReturnSynthesizer.cpp @@ -0,0 +1,80 @@ +//------------------------------------------------------------------------------ +// CLING - the C++ LLVM-based InterpreterG :) +// version: $Id$ +// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch> +//------------------------------------------------------------------------------ + +#include "ReturnSynthesizer.h" + +#include "cling/Interpreter/Transaction.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclGroup.h" +#include "clang/Sema/Sema.h" + +using namespace clang; + +namespace cling { + ReturnSynthesizer::ReturnSynthesizer(clang::Sema* S) + : TransactionTransformer(S), m_Context(&S->getASTContext()) { + } + + // pin the vtable here. + ReturnSynthesizer::~ReturnSynthesizer() + { } + + void ReturnSynthesizer::Transform() { + if (!getTransaction()->getCompilationOpts().ResultEvaluation) + return; + + for (Transaction::const_iterator iDGR = getTransaction()->decls_begin(), + eDGR = getTransaction()->decls_end(); iDGR != eDGR; ++iDGR) + for (DeclGroupRef::const_iterator I = (*iDGR).begin(), E = (*iDGR).end(); + I != E; ++I) + if (FunctionDecl* FD = dyn_cast<FunctionDecl>(*I)) { + if (FD->getNameAsString().find("__cling_Un1Qu3")) + return; + if (CompoundStmt* CS = dyn_cast<CompoundStmt>(FD->getBody())) { + // Collect all Stmts, contained in the CompoundStmt + llvm::SmallVector<Stmt *, 4> Stmts; + for (CompoundStmt::body_iterator iStmt = CS->body_begin(), + eStmt = CS->body_end(); iStmt != eStmt; ++iStmt) + Stmts.push_back(*iStmt); + + int indexOfLastExpr = Stmts.size(); + while(indexOfLastExpr--) { + // find the trailing expression statement (skip e.g. null statements) + if (isa<Expr>(Stmts[indexOfLastExpr])) { + // even if void: we found an expression + break; + } + } + + // If no expressions found quit early. + if (indexOfLastExpr < 0) + return; + // We can't PushDeclContext, because we don't have scope. + Sema::ContextRAII pushedDC(*m_Sema, FD); + Expr* lastExpr = cast<Expr>(Stmts[indexOfLastExpr]); // It is an expr + if (lastExpr) { + QualType RetTy = lastExpr->getType(); + if (!RetTy->isVoidType()) { + // Change the void function's return type + FunctionProtoType::ExtProtoInfo EPI; + QualType FnTy = m_Context->getFunctionType(RetTy, + /* ArgArray = */0, + /* NumArgs = */0, + EPI); + FD->setType(FnTy); + + // Change it with return stmt + Stmts[indexOfLastExpr] + = m_Sema->ActOnReturnStmt(lastExpr->getExprLoc(), + lastExpr).take(); + } + CS->setStmts(*m_Context, Stmts.data(), Stmts.size()); + } + } + } + } +} // end namespace cling diff --git a/interpreter/cling/lib/Interpreter/ReturnSynthesizer.h b/interpreter/cling/lib/Interpreter/ReturnSynthesizer.h new file mode 100644 index 0000000000000000000000000000000000000000..be13d54f32fea8c81e465bfa55fe1770c493749a --- /dev/null +++ b/interpreter/cling/lib/Interpreter/ReturnSynthesizer.h @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// CLING - the C++ LLVM-based InterpreterG :) +// version: $Id$ +// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch> +//------------------------------------------------------------------------------ + +#ifndef CLING_RETURN_SYNTHESIZER_H +#define CLING_RETURN_SYNTHESIZER_H + +#include "TransactionTransformer.h" + +namespace clang { + class ASTContext; + class CompoundStmt; + class DeclGroupRef; + class Expr; + class Sema; +} + +namespace llvm { + class raw_ostream; +} + +namespace cling { + + class ReturnSynthesizer : public TransactionTransformer { + + private: + ///\brief Needed for the AST transformations, owned by Sema. + /// + clang::ASTContext* m_Context; + +public: + ///\ brief Constructs the return synthesizer. + /// + ///\param[in] S - The semantic analysis object. + /// + ReturnSynthesizer(clang::Sema* S); + + virtual ~ReturnSynthesizer(); + + virtual void Transform(); + }; + +} // namespace cling + +#endif // CLING_RETURN_SYNTHESIZER_H diff --git a/interpreter/cling/lib/Interpreter/ValuePrinterSynthesizer.cpp b/interpreter/cling/lib/Interpreter/ValuePrinterSynthesizer.cpp index 7b5451ae7a24a9fd08fe651897c528aca10ec9d3..029d785124014e88656ca30786477cb50696f612 100644 --- a/interpreter/cling/lib/Interpreter/ValuePrinterSynthesizer.cpp +++ b/interpreter/cling/lib/Interpreter/ValuePrinterSynthesizer.cpp @@ -42,13 +42,14 @@ namespace cling { { } void ValuePrinterSynthesizer::Transform() { - if (!getTransaction()->getCompilationOpts().ValuePrinting) + if (getTransaction()->getCompilationOpts().ValuePrinting + == CompilationOptions::VPDisabled) return; for (Transaction::const_iterator I = getTransaction()->decls_begin(), E = getTransaction()->decls_end(); I != E; ++I) if(!tryAttachVP(*I)) - return setTransaction(0); // On error set the to NULL. + return setTransaction(0); // On error set to NULL. } bool ValuePrinterSynthesizer::tryAttachVP(DeclGroupRef DGR) { @@ -56,36 +57,75 @@ namespace cling { if (FunctionDecl* FD = dyn_cast<FunctionDecl>(*I)) { if (FD->getNameAsString().find("__cling_Un1Qu3")) return true; - + const CompilationOptions& CO(getTransaction()->getCompilationOpts()); + if (CO.ValuePrinting == CompilationOptions::VPDisabled) + return true; // Nothing to do. + + // We have to be able to mark the expression for printout. There are + // three scenarios: + // 0: Expression printing disabled - don't do anything just exit + // even if there wasn't missing ';'. + // 1: Expression printing enabled - print no matter what. + // 2: Expression printing auto - analyze - rely on the omitted ';' to + // not produce the suppress marker. if (CompoundStmt* CS = dyn_cast<CompoundStmt>(FD->getBody())) { - for (CompoundStmt::body_iterator - J = CS->body_begin(), E = CS->body_end(); J != E; ++J) { - if (J+1 == E || !isa<NullStmt>(*(J+1))) { - Expr* To = 0; - ReturnStmt* RS = dyn_cast<ReturnStmt>(*J); - if (RS) - To = RS->getRetValue(); - else - To = dyn_cast<Expr>(*J); - - if (To) { - Expr* Result = 0; - if (m_Sema->getLangOpts().CPlusPlus) - Result = SynthesizeCppVP(To); - else - Result = SynthesizeVP(To); - - if (Result) { - if (RS) - RS->setRetValue(Result); - else - *J = Result; - } - } + // Collect all Stmts, contained in the CompoundStmt + llvm::SmallVector<Stmt *, 4> Stmts; + for (CompoundStmt::body_iterator iStmt = CS->body_begin(), + eStmt = CS->body_end(); iStmt != eStmt; ++iStmt) + Stmts.push_back(*iStmt); + + int indexOfLastExpr = Stmts.size(); + while(indexOfLastExpr--) { + // find the trailing expression statement (skip e.g. null statements) + if (isa<Expr>(Stmts[indexOfLastExpr])) { + // even if void: we found an expression + break; } } + + // If no expressions found quit early. + if (indexOfLastExpr < 0) + return true; + + // Update the CompoundStmt body + Expr* To = cast<Expr>(Stmts[indexOfLastExpr]);// We know it is an expr + switch (CO.ValuePrinting) { + case CompilationOptions::VPDisabled: + assert("Don't wait that long. Exit early!"); + break; + case CompilationOptions::VPEnabled: + break; + case CompilationOptions::VPAuto: + if ((int)Stmts.size() > indexOfLastExpr+1 + && Stmts[indexOfLastExpr+1] + && isa<NullStmt>(Stmts[indexOfLastExpr+1])) + return true; // If prev is NullStmt disable VP is disabled - exit. + break; + } + + // We can't PushDeclContext, because we don't have scope. + Sema::ContextRAII pushedDC(*m_Sema, FD); + + if (To) { + // Strip the parenthesis if any + if (ParenExpr* PE = dyn_cast<ParenExpr>(To)) + To = PE->getSubExpr(); + + Expr* Result = 0; + if (m_Sema->getLangOpts().CPlusPlus) + Result = SynthesizeCppVP(To); + else + Result = SynthesizeVP(To); + + if (Result) + Stmts[indexOfLastExpr] = Result; + + CS->setStmts(*m_Context, Stmts.data(), Stmts.size()); + } // Clear the artificial NullStmt-s if (!ClearNullStmts(CS)) { + // FIXME: Why it is here? Shouldn't it be in DeclExtractor? // if no body remove the wrapper DeclContext* DC = FD->getDeclContext(); Scope* S = m_Sema->getScopeForContext(DC); @@ -95,6 +135,7 @@ namespace cling { } } } + return true; } diff --git a/interpreter/cling/lib/Interpreter/ValuePrinterSynthesizer.h b/interpreter/cling/lib/Interpreter/ValuePrinterSynthesizer.h index a60a4cb0464e30d00def0e3ce52d7d1e92472efe..28acfe1f8cb189ece88d3de59cf38a5df7309c06 100644 --- a/interpreter/cling/lib/Interpreter/ValuePrinterSynthesizer.h +++ b/interpreter/cling/lib/Interpreter/ValuePrinterSynthesizer.h @@ -37,7 +37,7 @@ namespace cling { llvm::OwningPtr<llvm::raw_ostream> m_ValuePrinterStream; public: - ///\ brief Construct the value printer synthesizer. + ///\ brief Constructs the value printer synthesizer. /// ///\param[in] S - The semantic analysis object ///\param[in] Stream - The output stream where the value printer will write @@ -49,6 +49,15 @@ public: virtual void Transform(); private: + ///\brief Tries to attach a value printing mechanism to the given decl group + /// ref. + /// + ///\param[in] DGR - A decl group ref the value printer is being attached to. + /// + ///\returns true if the attachment was considered as success. I.e. even if + /// even if the value printer wasn't attached because of the compilation + /// options disallowint it - it will return still true. Returns false on + /// critical error. bool tryAttachVP(clang::DeclGroupRef DGR); clang::Expr* SynthesizeCppVP(clang::Expr* E); clang::Expr* SynthesizeVP(clang::Expr* E); diff --git a/interpreter/cling/test/Interfaces/evaluate.C b/interpreter/cling/test/Interfaces/evaluate.C index fac17c4a58d60d3e6efe3d538879970db4231e3e..6ddc9ec451a7854c73b8d8b58f168b9d93395e55 100644 --- a/interpreter/cling/test/Interfaces/evaluate.C +++ b/interpreter/cling/test/Interfaces/evaluate.C @@ -1,4 +1,4 @@ -// RUN: cat %s | %cling | FileCheck %s +// RUN: cat %s | %cling -Xclang -verify | FileCheck %s #include "cling/Interpreter/Interpreter.h" #include "cling/Interpreter/StoredValueRef.h" @@ -19,9 +19,10 @@ V // CHECK: (cling::StoredValueRef) boxes [(int *) 0x12] cling::StoredValueRef Result; gCling->evaluate("V", &Result); -Result // CHECK: (cling::StoredValueRef) boxes [(cling::StoredValueRef) ] +Result // CHECK: (cling::StoredValueRef) boxes [(cling::StoredValueRef)] V // CHECK: (cling::StoredValueRef) boxes [(int *) 0x12] // Savannah #96277 gCling->evaluate("double sin(double); double one = sin(3.141/2);", &V); -one // CHECK: (double) 1.000000e+00 +V // CHECK: (cling::StoredValueRef) boxes [(void)] +one // expected-error {{use of undeclared identifier 'one'}} diff --git a/interpreter/cling/test/Prompt/Regression.C b/interpreter/cling/test/Prompt/Regression.C index a0cfe4e0ad7b3bed9983ff33ed9a9df2f2fed2eb..5fff2fc4f38044a193061e17b62cd6a2a66a7751 100644 --- a/interpreter/cling/test/Prompt/Regression.C +++ b/interpreter/cling/test/Prompt/Regression.C @@ -12,7 +12,7 @@ cling::StoredValueRef V; gCling->process("int a = print();",&V); //CHECK: print is run. gCling->process("a", &V); -//CHECK: (int const) 1 +//CHECK: (int) 1 gCling->process("a;", &V); //CHECK-NOT: print is run. // End PR #96277