diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -941,6 +941,9 @@ // into the name of a header unit. ANNOTATION(header_unit) +// Annotation for end of input in clang-repl. +ANNOTATION(input_end) + #undef PRAGMA_ANNOTATION #undef ANNOTATION #undef TESTING_KEYWORD diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -14,10 +14,12 @@ #ifndef LLVM_CLANG_INTERPRETER_INTERPRETER_H #define LLVM_CLANG_INTERPRETER_INTERPRETER_H -#include "clang/Interpreter/PartialTranslationUnit.h" - +#include "clang/AST/Decl.h" #include "clang/AST/GlobalDecl.h" +#include "clang/Interpreter/PartialTranslationUnit.h" +#include "clang/Interpreter/Value.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/Support/Error.h" @@ -28,11 +30,12 @@ namespace orc { class LLJIT; class ThreadSafeContext; -} +} // namespace orc } // namespace llvm namespace clang { +class Parser; class CompilerInstance; class IncrementalExecutor; class IncrementalParser; @@ -52,22 +55,19 @@ Interpreter(std::unique_ptr CI, llvm::Error &Err); + unsigned InitPTUSize = 0; + public: ~Interpreter(); static llvm::Expected> create(std::unique_ptr CI); + ASTContext &getASTContext() const; const CompilerInstance *getCompilerInstance() const; const llvm::orc::LLJIT *getExecutionEngine() const; llvm::Expected Parse(llvm::StringRef Code); llvm::Error Execute(PartialTranslationUnit &T); - llvm::Error ParseAndExecute(llvm::StringRef Code) { - auto PTU = Parse(Code); - if (!PTU) - return PTU.takeError(); - if (PTU->TheModule) - return Execute(*PTU); - return llvm::Error::success(); - } + llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr); + llvm::Expected CompileDtorCall(const RecordDecl *RD); /// Undo N previous incremental inputs. llvm::Error Undo(unsigned N = 1); @@ -85,6 +85,28 @@ /// file. llvm::Expected getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const; + + /// Compiles the synthesized Decl and returns the JITTargetAddress. + llvm::Expected CompileDecl(Decl *FD); + + std::string CreateUniqName(std::string Base); + + Parser &getParser() const; + + // FIXME: Make it private. + Expr *RuntimeNoAllocInterface = nullptr; + Expr *RuntimeAllocInterface = nullptr; + +private: + llvm::Expected CaptureExpr(TranslationUnitDecl &TU); + + bool FindRuntimeInterface(); + + FunctionDecl *SynthesizeValueGetter(clang::Expr *E); + + llvm::Error GenerateValue(TranslationUnitDecl &TU, Value *V); + + llvm::DenseMap Dtors; }; } // namespace clang diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Interpreter/Value.h @@ -0,0 +1,142 @@ +//===--- Interpreter.h - Incremental Compiation and Execution---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the class that used to represent a value in incremental +// C++. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INTERPRETER_VALUE_H +#define LLVM_CLANG_INTERPRETER_VALUE_H + +#include "llvm/Support/Compiler.h" +#include + +namespace llvm { +class raw_ostream; + +} // namespace llvm + +namespace clang { + +class ASTContext; +class Interpreter; + +#if __has_attribute(visibility) && \ + (!(defined(_WIN32) || defined(__CYGWIN__)) || \ + (defined(__MINGW32__) && defined(__clang__))) +#if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS) +#define REPL_EXTERNAL_VISIBILITY __attribute__((visibility("default"))) +#else +#define REPL_EXTERNAL_VISIBILITY +#endif +#else +#if defined(_WIN32) +#define REPL_EXTERNAL_VISIBILITY __declspec(dllexport) +#endif +#endif + +class Interpreter; +class QualType; + +#define REPL_BUILTIN_TYPES \ + X(bool, Bool) \ + X(char, Char_S) \ + X(signed char, SChar) \ + X(unsigned char, UChar) \ + X(short, Short) \ + X(unsigned short, UShort) \ + X(int, Int) \ + X(unsigned int, UInt) \ + X(long, Long) \ + X(unsigned long, ULong) \ + X(long long, LongLong) \ + X(unsigned long long, ULongLong) \ + X(float, Float) \ + X(double, Double) \ + X(long double, LongDouble) + +class REPL_EXTERNAL_VISIBILITY Value { + union Storage { +#define X(type, name) type m_##name; + REPL_BUILTIN_TYPES +#undef X + void *m_Ptr; + }; + +public: + enum Kind { +#define X(type, name) K_##name, + REPL_BUILTIN_TYPES +#undef X + + K_Void, + K_PtrOrObj, + K_Unspecified + }; + + Value() = default; + Value(void /*Interpreter*/ *In, void /*QualType*/ *Ty); + Value(const Value &RHS); + Value(Value &&RHS) noexcept; + Value &operator=(const Value &RHS); + Value &operator=(Value &&RHS) noexcept; + ~Value(); + + void printType(llvm::raw_ostream &Out) const; + void printData(llvm::raw_ostream &Out) const; + void print(llvm::raw_ostream &Out) const; + void dump() const; + + ASTContext &getASTContext() const; + QualType getType() const; + Interpreter &getInterpreter() const; + + bool isValid() const { return ValueKind != K_Unspecified; } + bool isVoid() const { return ValueKind == K_Void; } + bool isManuallyAlloc() const { return IsManuallyAlloc; } + Kind getKind() const { return ValueKind; } + void setKind(Kind K) { ValueKind = K; } + void setOpaqueType(void *Ty) { OpaqueType = Ty; } + + void setPtr(void *Ptr) { Data.m_Ptr = Ptr; } + + void *getPtr() const { + assert(ValueKind == K_PtrOrObj); + return Data.m_Ptr; + } + +#define X(type, name) \ + void set##name(type Val) { Data.m_##name = Val; } \ + type get##name() const { return Data.m_##name; } + REPL_BUILTIN_TYPES +#undef X + + template T getAs(); + +// Allows us to have overloads for each builtin type, plus one that goes via +// void* +#define X(type, name) \ + template <> type getAs() { \ + assert(ValueKind == K_##name); \ + return (type)Data.m_##name; \ + } + REPL_BUILTIN_TYPES +#undef X + +private: + // Interpreter, QualType are stored as void* to reduce dependencies. + void *Interp = nullptr; + void *OpaqueType = nullptr; + Storage Data; + Kind ValueKind = K_Unspecified; + bool IsManuallyAlloc = false; +}; + +} // namespace clang +#endif diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -439,6 +439,10 @@ /// a statement expression and builds a suitable expression statement. StmtResult handleExprStmt(ExprResult E, ParsedStmtContext StmtCtx); + /// A flag that indicate if we run into something that need to pretty print. + /// Note this should only be used in incremental C++ (clang-repl). + bool PrettyPrintMode = false; + public: Parser(Preprocessor &PP, Sema &Actions, bool SkipFunctionBodies); ~Parser() override; @@ -459,6 +463,17 @@ return Actions.getObjCDeclContext(); } + bool isPrettyPrintMode() const { + assert(PP.isIncrementalProcessingEnabled() && + "This should only be used in incremental C++"); + return PrettyPrintMode; + } + void setPrettyPrintMode(bool Mode = true) { + assert(PP.isIncrementalProcessingEnabled() && + "This should only be used in incremental C++"); + PrettyPrintMode = Mode; + } + // Type forwarding. All of these are statically 'void*', but they may all be // different actual classes based on the actions in place. typedef OpaquePtr DeclGroupPtrTy; @@ -692,7 +707,8 @@ bool isEofOrEom() { tok::TokenKind Kind = Tok.getKind(); return Kind == tok::eof || Kind == tok::annot_module_begin || - Kind == tok::annot_module_end || Kind == tok::annot_module_include; + Kind == tok::annot_module_end || Kind == tok::annot_module_include || + Kind == tok::annot_input_end; } /// Checks if the \p Level is valid for use in a fold expression. diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp --- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -663,7 +663,8 @@ // them. if (Tok.is(tok::eof) || (Tok.isAnnotation() && !Tok.is(tok::annot_header_unit) && - !Tok.is(tok::annot_module_begin) && !Tok.is(tok::annot_module_end))) + !Tok.is(tok::annot_module_begin) && !Tok.is(tok::annot_module_end) && + !Tok.is(tok::annot_input_end))) return; // EmittedDirectiveOnThisLine takes priority over RequireSameLine. @@ -819,6 +820,9 @@ // -traditional-cpp the lexer keeps /all/ whitespace, including comments. PP.Lex(Tok); continue; + } else if (Tok.is(tok::annot_input_end)) { + PP.Lex(Tok); + continue; } else if (Tok.is(tok::eod)) { // Don't print end of directive tokens, since they are typically newlines // that mess up our line tracking. These come from unknown pre-processor diff --git a/clang/lib/Interpreter/ASTHelpers.h b/clang/lib/Interpreter/ASTHelpers.h new file mode 100644 --- /dev/null +++ b/clang/lib/Interpreter/ASTHelpers.h @@ -0,0 +1,43 @@ +#ifndef LLVM_CLANG_INTERPRETER_AST_HELPERS_H +#define LLVM_CLANG_INTERPRETER_AST_HELPERS_H + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Mangle.h" +#include "clang/AST/TypeVisitor.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/CodeGen/ObjectFilePCHContainerOperations.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Job.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/Tool.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Lex/PreprocessorOptions.h" + +#include "clang/Sema/Lookup.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Errc.h" +#include "llvm/TargetParser/Host.h" + +// TODO: create a sub namespace `repl`. +namespace clang { +IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uintptr_t Ptr); + +Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, Expr *E); + +Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, uintptr_t Ptr); + +Sema::DeclGroupPtrTy CreateDGPtrFrom(Sema &S, Decl *D); + +NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name, + const DeclContext *Within = nullptr); + +NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name, + const DeclContext *Within); + +std::string GetFullTypeName(ASTContext &Ctx, QualType QT); +} // namespace clang + +#endif diff --git a/clang/lib/Interpreter/ASTHelpers.cpp b/clang/lib/Interpreter/ASTHelpers.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Interpreter/ASTHelpers.cpp @@ -0,0 +1,99 @@ +#include "ASTHelpers.h" + +namespace clang { + +IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uintptr_t Ptr) { + const llvm::APInt Addr(8 * sizeof(void *), Ptr); + return IntegerLiteral::Create(C, Addr, C.getUIntPtrType(), SourceLocation()); +} + +Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, Expr *E) { + ASTContext &Ctx = S.getASTContext(); + if (!Ty->isPointerType()) + Ty = Ctx.getPointerType(Ty); + + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); + Expr *Result = + S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E).get(); + assert(Result && "Cannot create CStyleCastPtrExpr"); + return Result; +} + +Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, uintptr_t Ptr) { + ASTContext &Ctx = S.getASTContext(); + return CStyleCastPtrExpr(S, Ty, IntegerLiteralExpr(Ctx, Ptr)); +} + +Sema::DeclGroupPtrTy CreateDGPtrFrom(Sema &S, Decl *D) { + SmallVector DeclsInGroup; + DeclsInGroup.push_back(D); + Sema::DeclGroupPtrTy DeclGroupPtr = S.BuildDeclaratorGroup(DeclsInGroup); + return DeclGroupPtr; +} + +NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name, + const DeclContext *Within) { + DeclarationName DName = &S.Context.Idents.get(Name); + LookupResult R(S, DName, SourceLocation(), + Sema::LookupNestedNameSpecifierName); + R.suppressDiagnostics(); + if (!Within) + S.LookupName(R, S.TUScope); + else { + if (const clang::TagDecl *TD = dyn_cast(Within)) + if (!TD->getDefinition()) + // No definition, no lookup result. + return nullptr; + + S.LookupQualifiedName(R, const_cast(Within)); + } + + if (R.empty()) + return nullptr; + + R.resolveKind(); + + return dyn_cast(R.getFoundDecl()); +} + +NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name, + const DeclContext *Within) { + DeclarationName DName = &S.Context.Idents.get(Name); + LookupResult R(S, DName, SourceLocation(), Sema::LookupOrdinaryName, + Sema::ForVisibleRedeclaration); + + R.suppressDiagnostics(); + + if (!Within) + S.LookupName(R, S.TUScope); + else { + const DeclContext *PrimaryWithin = nullptr; + if (const auto *TD = dyn_cast(Within)) + PrimaryWithin = llvm::dyn_cast_or_null(TD->getDefinition()); + else + PrimaryWithin = Within->getPrimaryContext(); + + // No definition, no lookup result. + if (!PrimaryWithin) + return nullptr; + + S.LookupQualifiedName(R, const_cast(PrimaryWithin)); + } + + if (R.empty()) + return nullptr; + R.resolveKind(); + + if (R.isSingleResult()) + return llvm::dyn_cast(R.getFoundDecl()); + + return nullptr; +} + +std::string GetFullTypeName(ASTContext &Ctx, QualType QT) { + PrintingPolicy Policy(Ctx.getPrintingPolicy()); + Policy.SuppressScope = false; + Policy.AnonymousTagLocations = false; + return QT.getAsString(Policy); +} +} // namespace clang diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt --- a/clang/lib/Interpreter/CMakeLists.txt +++ b/clang/lib/Interpreter/CMakeLists.txt @@ -12,6 +12,8 @@ IncrementalExecutor.cpp IncrementalParser.cpp Interpreter.cpp + Value.cpp + ASTHelpers.cpp DEPENDS intrinsics_gen diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h --- a/clang/lib/Interpreter/IncrementalParser.h +++ b/clang/lib/Interpreter/IncrementalParser.h @@ -16,7 +16,7 @@ #include "clang/Interpreter/PartialTranslationUnit.h" #include "clang/AST/GlobalDecl.h" - +#include "clang/Parse/Parser.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" @@ -32,7 +32,7 @@ class CompilerInstance; class IncrementalAction; class Parser; - +class Interpreter; /// Provides support for incremental compilation. Keeps track of the state /// changes between the subsequent incremental input. /// @@ -68,6 +68,8 @@ /// \c TranslationUnitDecl and \c llvm::Module corresponding to the input. llvm::Expected Parse(llvm::StringRef Input); + llvm::Expected Parse(Parser::DeclGroupPtrTy D); + /// Uses the CodeGenModule mangled name cache and avoids recomputing. ///\returns the mangled name of a \c GD. llvm::StringRef GetMangledName(GlobalDecl GD) const; @@ -76,8 +78,13 @@ std::list &getPTUs() { return PTUs; } -private: - llvm::Expected ParseOrWrapTopLevelDecl(); + Parser &getParser() const { return *P; } + + llvm::Expected + ParseOrWrapTopLevelDecl(Parser::DeclGroupPtrTy SynthesizedDecl = nullptr); + + bool isPrettyPrintMode() const { return P->isPrettyPrintMode(); } + void setPrettyPrintMode(bool Mode = true) { P->setPrettyPrintMode(Mode); } }; } // end namespace clang diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -19,9 +19,9 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" #include "clang/FrontendTool/Utils.h" +#include "clang/Interpreter/Interpreter.h" #include "clang/Parse/Parser.h" #include "clang/Sema/Sema.h" - #include "llvm/Option/ArgList.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Error.h" @@ -143,7 +143,8 @@ } llvm::Expected -IncrementalParser::ParseOrWrapTopLevelDecl() { +IncrementalParser::ParseOrWrapTopLevelDecl( + Parser::DeclGroupPtrTy SynthesizedDecl) { // Recover resources if we crash before exiting this method. Sema &S = CI->getSema(); llvm::CrashRecoveryContextCleanupRegistrar CleanupSema(&S); @@ -158,8 +159,8 @@ LastPTU.TUPart = C.getTranslationUnitDecl(); // Skip previous eof due to last incremental input. - if (P->getCurToken().is(tok::eof)) { - P->ConsumeToken(); + if (P->getCurToken().is(tok::annot_input_end)) { + P->ConsumeAnyToken(); // FIXME: Clang does not call ExitScope on finalizing the regular TU, we // might want to do that around HandleEndOfTranslationUnit. P->ExitScope(); @@ -169,14 +170,24 @@ S.ActOnTranslationUnitScope(P->getCurScope()); } - Parser::DeclGroupPtrTy ADecl; - Sema::ModuleImportState ImportState; - for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF; - AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) { - if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) - return llvm::make_error("Parsing failed. " - "The consumer rejected a decl", - std::error_code()); + if (SynthesizedDecl) { + if (!Consumer->HandleTopLevelDecl(SynthesizedDecl.get())) { + return llvm::make_error( + "Parsing failed. " + "The consumer rejected the synthesized decl", + std::error_code()); + } + } else { + Parser::DeclGroupPtrTy ADecl; + Sema::ModuleImportState ImportState; + for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF; + AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) { + if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) + return llvm::make_error( + "Parsing failed. " + "The consumer rejected a decl", + std::error_code()); + } } DiagnosticsEngine &Diags = getCI()->getDiagnostics(); @@ -259,12 +270,12 @@ Token Tok; do { PP.Lex(Tok); - } while (Tok.isNot(tok::eof)); + } while (Tok.isNot(tok::annot_input_end)); } Token AssertTok; PP.Lex(AssertTok); - assert(AssertTok.is(tok::eof) && + assert(AssertTok.is(tok::annot_input_end) && "Lexer must be EOF when starting incremental parse!"); if (CodeGenerator *CG = getCodeGen(Act.get())) { @@ -278,6 +289,24 @@ return PTU; } +llvm::Expected +IncrementalParser::Parse(Parser::DeclGroupPtrTy D) { + llvm::Expected PTUOrErr = + ParseOrWrapTopLevelDecl(D); + if (!PTUOrErr) { + return PTUOrErr.takeError(); + } + if (CodeGenerator *CG = getCodeGen(Act.get())) { + std::unique_ptr M(CG->ReleaseModule()); + CG->StartModule("incr_module_" + std::to_string(PTUs.size()), + M->getContext()); + + PTUOrErr.get().TheModule = std::move(M); + } + + return PTUOrErr; +} + void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) { TranslationUnitDecl *MostRecentTU = PTU.TUPart; TranslationUnitDecl *FirstTU = MostRecentTU->getFirstDecl(); diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -17,6 +17,9 @@ #include "IncrementalParser.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Mangle.h" +#include "clang/AST/TypeVisitor.h" +#include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" @@ -29,10 +32,11 @@ #include "clang/Frontend/TextDiagnosticBuffer.h" #include "clang/Lex/PreprocessorOptions.h" +#include "ASTHelpers.h" +#include "clang/Sema/Lookup.h" #include "llvm/IR/Module.h" #include "llvm/Support/Errc.h" #include "llvm/TargetParser/Host.h" - using namespace clang; // FIXME: Figure out how to unify with namespace init_convenience from @@ -189,6 +193,18 @@ } } +// These better to put in a runtime header but we can't. This is because we +// can't find the percise resource directory in unittests so we have to hard +// code them. +std::vector Runtimes = { + "#include ", + "void *__InterpreterSetValueWithAlloc(void*, void*, void*);", + "void __InterpreterSetValueNoAlloc(void*, void*, void*, void*);", + "void __InterpreterSetValueNoAlloc(void*, void*, void*, float);", + "void __InterpreterSetValueNoAlloc(void*, void*, void*, double);", + "void __InterpreterSetValueNoAlloc(void*, void*, void*, long double);", + "void __InterpreterSetValueNoAlloc(void*,void*,void*,unsigned long long);"}; + llvm::Expected> Interpreter::create(std::unique_ptr CI) { llvm::Error Err = llvm::Error::success(); @@ -196,6 +212,12 @@ std::unique_ptr(new Interpreter(std::move(CI), Err)); if (Err) return std::move(Err); + for (const std::string &R : Runtimes) + llvm::cantFail(Interp->ParseAndExecute(R)); + // FIXME: This is a ugly hack. Undo command checks its availability by looking + // at the size of the PTU list. However we have parsed something in the + // beginning of the REPL so we have to mark them as 'Irrevocable'. + Interp->InitPTUSize = Interp->IncrParser->getPTUs().size(); return std::move(Interp); } @@ -203,22 +225,38 @@ return IncrParser->getCI(); } +ASTContext &Interpreter::getASTContext() const { + return getCompilerInstance()->getASTContext(); +} + +Parser &Interpreter::getParser() const { return IncrParser->getParser(); } + const llvm::orc::LLJIT *Interpreter::getExecutionEngine() const { if (IncrExecutor) return IncrExecutor->getExecutionEngine(); return nullptr; } +std::string Interpreter::CreateUniqName(std::string Base) { + static size_t I = 0; + Base += std::to_string(I); + I += 1; + return Base; +} llvm::Expected Interpreter::Parse(llvm::StringRef Code) { + // Tell the interpreter sliently ignore unused expressions since value + // printing could cause it. + getCompilerInstance()->getDiagnostics().setSeverity( + clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation()); + return IncrParser->Parse(Code); } llvm::Error Interpreter::Execute(PartialTranslationUnit &T) { assert(T.TheModule); if (!IncrExecutor) { - const clang::TargetInfo &TI = - getCompilerInstance()->getASTContext().getTargetInfo(); + const clang::TargetInfo &TI = getASTContext().getTargetInfo(); llvm::Error Err = llvm::Error::success(); IncrExecutor = std::make_unique(*TSCtx, Err, TI); @@ -235,6 +273,33 @@ return llvm::Error::success(); } +llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code, Value *V) { + auto PTU = Parse(Code); + if (!PTU) + return PTU.takeError(); + if (IncrParser->isPrettyPrintMode()) { + IncrParser->setPrettyPrintMode(false); + + // The user ask for a value, return it directly. + if (V) { + if (llvm::Error Err = GenerateValue(*PTU->TUPart, V)) + return Err; + return llvm::Error::success(); + } + + // Presumbly we need to perform pretty print. + Value DefaultValue; + if (llvm::Error Err = GenerateValue(*PTU->TUPart, &DefaultValue)) + return Err; + DefaultValue.dump(); + return llvm::Error::success(); + } + + if (PTU->TheModule) + return Execute(*PTU); + return llvm::Error::success(); +} + llvm::Expected Interpreter::getSymbolAddress(GlobalDecl GD) const { if (!IncrExecutor) @@ -268,7 +333,7 @@ llvm::Error Interpreter::Undo(unsigned N) { std::list &PTUs = IncrParser->getPTUs(); - if (N > PTUs.size()) + if (N + InitPTUSize > PTUs.size()) return llvm::make_error("Operation failed. " "Too many undos", std::error_code()); @@ -283,3 +348,432 @@ } return llvm::Error::success(); } + +llvm::Expected Interpreter::CompileDecl(Decl *D) { + assert(D && "The decl being compiled can't be null"); + Sema &S = getCompilerInstance()->getSema(); + llvm::Expected PTUOrErr = + IncrParser->Parse(CreateDGPtrFrom(S, D)); + if (!PTUOrErr) + return PTUOrErr.takeError(); + + if (PTUOrErr->TheModule) + if (llvm::Error Err = Execute(*PTUOrErr)) + return Err; + + ASTNameGenerator ASTNameGen(getASTContext()); + llvm::Expected AddrOrErr = + getSymbolAddressFromLinkerName(ASTNameGen.getName(D)); + + return AddrOrErr; +} + +llvm::Error Interpreter::GenerateValue(TranslationUnitDecl &TU, Value *V) { + // Capture the expression we want to print. + auto ExprOrErr = CaptureExpr(TU); + if (!ExprOrErr) + return ExprOrErr.takeError(); + + // Synthesize the wrapper function that is used to get the value. + FunctionDecl *ValueGetter = SynthesizeValueGetter(*ExprOrErr); + + // Compile the function. + llvm::Expected AddrOrErr = CompileDecl(ValueGetter); + + if (!AddrOrErr) + return AddrOrErr.takeError(); + + if (auto *Main = llvm::jitTargetAddressToPointer( + AddrOrErr.get())) + (*Main)((void *)this, (void *)V); + return llvm::Error::success(); +} + +// Capture the last expression in the interpreter. We assume the user only +// inputted a top level statement. +llvm::Expected Interpreter::CaptureExpr(TranslationUnitDecl &TU) { + assert(std::distance(TU.decls_begin(), TU.decls_end()) > 0 && + "Cannot capture Expr* in empty TranslationUnitDecl"); + + auto Size = std::distance(TU.decls_begin(), TU.decls_end()); + auto Last = TU.decls_begin(); + while (Size-- != 1) + Last++; + + if (const auto *S = llvm::dyn_cast(*Last)) + if (const auto *E = llvm::dyn_cast(S->getStmt())) + return const_cast(E); + + return llvm::make_error("Can not capture any Expr*!", + std::error_code()); +} + +llvm::Expected Interpreter::CompileDtorCall(const RecordDecl *RD) { + if (auto Dtor = Dtors.find(RD); Dtor != Dtors.end()) + return Dtor->getSecond(); + + // No need to generate the destructor if it has no semantic effects. + if (const CXXRecordDecl *CXXRD = llvm::dyn_cast(RD); + CXXRD->hasIrrelevantDestructor()) + return nullptr; + + // Generate AST. + // + // extern "C" void __InterpreterDtorT(void* obj) { + // ((T*)obj)->~T(); + // } + // + QualType QT(RD->getTypeForDecl(), 0); + + std::string DtorName; + llvm::raw_string_ostream SS(DtorName); + SS << "__InterpreterDtor" << RD; + + std::string TypeName = GetFullTypeName(getASTContext(), QT); + + const char *CodeTemplate = R"( + extern "C" void {0}(void* obj) { + (({1}*)obj)->~{1}(); + } + )"; + std::string Code = llvm::formatv(CodeTemplate, DtorName, TypeName); + llvm::Expected PTUOrErr = Parse(Code); + // FIXME: Can't we just synthesize the AST directly? +#if 0 + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + QualType QT(RD->getTypeForDecl(), 0); + std::string DtorName = "__InterpreterDtor" + QT.getAsString(); + + QualType RetTy = Ctx.VoidTy; + QualType ArgTy = Ctx.VoidPtrTy; + + QualType FunctionTy = Ctx.getFunctionType(RetTy, {ArgTy}, {}); + IdentifierInfo *II = &Ctx.Idents.get(DtorName); + // Extern "C" ? + auto *FD = FunctionDecl::Create( + S.getASTContext(), Ctx.getTranslationUnitDecl(), SourceLocation(), + SourceLocation(), II, FunctionTy, /*TInfo=*/nullptr, SC_Extern); + auto *ObjParm = ParmVarDecl::Create( + Ctx, FD, SourceLocation(), SourceLocation(), /*Id=*/nullptr, ArgTy, + Ctx.getTrivialTypeSourceInfo(ArgTy, SourceLocation()), SC_None, + /*DefArg=*/nullptr); + FD->setParams({ObjParm}); + + // The function Body. + auto *Obj = DeclRefExpr::Create(Ctx, NestedNameSpecifierLoc(), + SourceLocation(), ObjParm, + /*RefersToEnclosingVariableOrCapture=*/false, + SourceLocation(), ArgTy, VK_PRValue); + // Force cast it to the right type. + Expr *CastedExpr = CStyleCastPtrExpr(S, QT, Obj); + + ExprResult DtorCall = nullptr; + + assert(!DtorCall.isInvalid() && "Can't create the destructor call!"); + + FD->setBody(DtorCall.get()); + // Compile it. + llvm::Expected PTUOrErr = + IncrParser->Parse(CreateDGPtrFrom(S, FD)); +#endif + if (!PTUOrErr) + return PTUOrErr.takeError(); + if (PTUOrErr->TheModule) + if (llvm::Error Err = Execute(*PTUOrErr)) + return Err; + + // Look up the function address in the JIT. + if (llvm::Expected AddrOrErr = + getSymbolAddressFromLinkerName(DtorName)) { + void *Dtor = (void *)*AddrOrErr; + Dtors[RD] = Dtor; + return Dtor; + } + return nullptr; +} + +static constexpr llvm::StringRef MagicRuntimeInterface[] = { + "__InterpreterSetValueNoAlloc", "__InterpreterSetValueWithAlloc"}; + +bool Interpreter::FindRuntimeInterface() { + if (RuntimeAllocInterface && RuntimeNoAllocInterface) + return true; + + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + auto LookupInterface = [&](Expr *&Interface, llvm::StringRef Name) { + LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(), + Sema::LookupOrdinaryName, Sema::ForVisibleRedeclaration); + S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl()); + if (R.empty()) + return false; + + CXXScopeSpec CSS; + Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get(); + return true; + }; + + if (!LookupInterface(RuntimeNoAllocInterface, MagicRuntimeInterface[0])) + return false; + if (!LookupInterface(RuntimeAllocInterface, MagicRuntimeInterface[1])) + return false; + return true; +} + +namespace { + +enum class RuntimeInterfaceKind { NoAlloc, WithAlloc, CopyArray }; + +class RuntimeInterfaceBuilder + : public TypeVisitor { + clang::Interpreter &Interp; + ASTContext &Ctx; + Sema &S; + Expr *E; + llvm::SmallVector Args; + +public: + RuntimeInterfaceBuilder(clang::Interpreter &In, ASTContext &C, Sema &SemaRef, + Expr *VE, ArrayRef FixedArgs) + : Interp(In), Ctx(C), S(SemaRef), E(VE) { + // The Interpreter* parameter and the out parameter `OutVal`. + for (Expr *E : FixedArgs) + Args.push_back(E); + + // Get rid of ExprWithCleanups. + if (auto *EWC = llvm::dyn_cast_if_present(E)) + E = EWC->getSubExpr(); + } + + ExprResult getCall() { + QualType Ty = E->getType(); + QualType DesugaredTy = Ty.getDesugaredType(Ctx); + + // For lvalue struct, we treat it as a reference. + if (DesugaredTy->isRecordType() && E->isLValue()) { + DesugaredTy = Ctx.getLValueReferenceType(DesugaredTy); + Ty = Ctx.getLValueReferenceType(Ty); + } + + Expr *TypeArg = + CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)Ty.getAsOpaquePtr()); + // The QualType parameter `OpaqueType`, represented as `void*`. + Args.push_back(TypeArg); + + // We push the last parameter based on the type of the Expr. Note we need + // special care for rvalue struct. + switch (Visit(&*DesugaredTy)) { + // Synthesize a CallExpr for __InterpreterSetValueWithAlloc + case RuntimeInterfaceKind::WithAlloc: { + ExprResult AllocCall = + S.ActOnCallExpr(/*Scope=*/nullptr, Interp.RuntimeAllocInterface, + E->getBeginLoc(), Args, E->getEndLoc()); + assert(!AllocCall.isInvalid() && "Can't create runtime interface call!"); + + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); + + Expr *Args2[] = {AllocCall.get()}; + + ExprResult CXXNewCall = S.BuildCXXNew( + E->getSourceRange(), + /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args2, + /*PlacementRParen=*/SourceLocation(), /*TypeIdParens=*/SourceRange(), + TSI->getType(), TSI, std::nullopt, E->getSourceRange(), E); + + assert(!CXXNewCall.isInvalid() && + "Can't create runtime placement new call!"); + + return S.ActOnFinishFullExpr(CXXNewCall.get(), /*DiscardedValue=*/false); + } + // Synthesize a CallExpr for __InterpreterSetValueNoAlloc + case RuntimeInterfaceKind::NoAlloc: { + return S.ActOnCallExpr(/*Scope=*/nullptr, Interp.RuntimeNoAllocInterface, + E->getBeginLoc(), Args, E->getEndLoc()); + } + case RuntimeInterfaceKind::CopyArray: + llvm_unreachable("Not implemented yet!"); + } + } + + RuntimeInterfaceKind VisitRecordType(const RecordType *Ty) { + return RuntimeInterfaceKind::WithAlloc; + } + + RuntimeInterfaceKind VisitMemberPointerType(const MemberPointerType *Ty) { + llvm_unreachable("Not implemented yet"); + } + RuntimeInterfaceKind VisitConstantArrayType(const ConstantArrayType *Ty) { + llvm_unreachable("Not implemented yet"); + return RuntimeInterfaceKind::CopyArray; + } + + RuntimeInterfaceKind VisitPointerType(const PointerType *Ty) { + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy); + ExprResult CastedExpr = + S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); + assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression"); + Args.push_back(CastedExpr.get()); + return RuntimeInterfaceKind::NoAlloc; + } + + RuntimeInterfaceKind VisitReferenceType(const ReferenceType *Ty) { + ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E); + assert(!AddrOfE.isInvalid() && "Can not create unary expression"); + Args.push_back(AddrOfE.get()); + return RuntimeInterfaceKind::NoAlloc; + } + + RuntimeInterfaceKind VisitBuiltinType(const BuiltinType *Ty) { + if (Ty->isNullPtrType()) + Args.push_back(E); + else if (Ty->isFloatingType()) + Args.push_back(E); + else if (Ty->isVoidType()) { + llvm_unreachable("Not implemented yet"); + } else if (Ty->isIntegralOrEnumerationType()) { + HandleIntegralOrEnumType(Ty); + } + return RuntimeInterfaceKind::NoAlloc; + } + + RuntimeInterfaceKind VisitEnumType(const EnumType *Ty) { + HandleIntegralOrEnumType(Ty); + return RuntimeInterfaceKind::NoAlloc; + } + +private: + // Force cast these types to uint64 to reduce the number of overloads of + // `__InterpreterSetValueNoAlloc`. + void HandleIntegralOrEnumType(const Type *Ty) { + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.UnsignedLongLongTy); + ExprResult CastedExpr = + S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); + assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr"); + Args.push_back(CastedExpr.get()); + } +}; +} // namespace + +static constexpr const char *const ValueGetter = "__InterpreterValueGetter"; + +// This synthesizes a wrapper function that used in passing the Value object to +// the interpreter. Inside the wrapper we synthesize another call to a speciall +// function that is responsible for generating the Value. +// In general, we transform: +// clang-repl> x +// To: +// void __InterpreterValueGetter(void* ThisInterp, void* OpaqueValue) { +// // 1. If x is a built-in type like int, float. +// __InterpreterSetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x); +// // 2. If x is a struct, and a lvalue. +// __InterpreterSetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, &x); +// // 3. If x is a struct, but a rvalue. +// new (__InterpreterSetValueWithAlloc(ThisInterp, OpaqueValue, xQualType)) +// (x); +// } +FunctionDecl *Interpreter::SynthesizeValueGetter(clang::Expr *E) { + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + QualType Void = Ctx.VoidTy; + QualType VoidPtr = Ctx.VoidPtrTy; + + // Synthesize function `__InterpreterValueGetter`. + QualType FunctionTy = Ctx.getFunctionType(Void, {VoidPtr, VoidPtr}, {}); + IdentifierInfo *II = &Ctx.Idents.get(CreateUniqName(ValueGetter)); + auto *FD = FunctionDecl::Create( + S.getASTContext(), Ctx.getTranslationUnitDecl(), SourceLocation(), + SourceLocation(), II, FunctionTy, /*TInfo=*/nullptr, SC_None); + + // The two parameters: + // 1. void* OpaqueInterp + // 2. void* OpaqueValue + auto *OpaqueInterp = ParmVarDecl::Create( + Ctx, FD, SourceLocation(), SourceLocation(), /*Id=*/nullptr, VoidPtr, + Ctx.getTrivialTypeSourceInfo(VoidPtr, SourceLocation()), SC_None, + /*DefArg=*/nullptr); + auto *OpaqueValue = ParmVarDecl::Create( + Ctx, FD, SourceLocation(), SourceLocation(), /*Id=*/nullptr, VoidPtr, + Ctx.getTrivialTypeSourceInfo(VoidPtr, SourceLocation()), SC_None, + /*DefArg=*/nullptr); + FD->setParams({OpaqueInterp, OpaqueValue}); + + if (!FindRuntimeInterface()) + llvm_unreachable("We can't find the runtime iterface for pretty print!"); + + // Create parameter `ThisInterp` for `__InterpreterSetValue*` + auto *ThisInterp = DeclRefExpr::Create( + Ctx, NestedNameSpecifierLoc(), SourceLocation(), OpaqueInterp, + /*RefersToEnclosingVariableOrCapture=*/false, SourceLocation(), + Ctx.VoidPtrTy, VK_PRValue); + + // Create parameter `OutVal` for `__InterpreterSetValue*` + auto *OutValue = DeclRefExpr::Create( + Ctx, NestedNameSpecifierLoc(), SourceLocation(), OpaqueValue, + /*RefersToEnclosingVariableOrCapture=*/false, SourceLocation(), + Ctx.VoidPtrTy, VK_PRValue); + + // Build `__InterpreterSetValue*` call. + RuntimeInterfaceBuilder Builder(*this, Ctx, S, E, {ThisInterp, OutValue}); + + ExprResult Result = Builder.getCall(); + assert(!Result.isInvalid() && "Failed to generate the CallExpr!"); + FD->setBody(Result.get()); + return FD; +} + +// Temporary rvalue struct that need special care. +REPL_EXTERNAL_VISIBILITY void * +__InterpreterSetValueWithAlloc(void *This, void *OutVal, void *OpaqueType) { + Value &VRef = *(Value *)OutVal; + VRef = Value(This, OpaqueType); + return VRef.getPtr(); +} + +// Pointers, lvalue struct that can take as a reference. +REPL_EXTERNAL_VISIBILITY void __InterpreterSetValueNoAlloc(void *This, + void *OutVal, + void *OpaqueType, + void *Val) { + Value &VRef = *(Value *)OutVal; + VRef = Value(This, OpaqueType); + VRef.setPtr(Val); +} + +REPL_EXTERNAL_VISIBILITY void +__InterpreterSetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, + unsigned long long Val) { + Value &VRef = *(Value *)OutVal; + VRef = Value(This, OpaqueType); + VRef.setULongLong(Val); +} + +REPL_EXTERNAL_VISIBILITY void __InterpreterSetValueNoAlloc(void *This, + void *OutVal, + void *OpaqueType, + float Val) { + Value &VRef = *(Value *)OutVal; + VRef = Value(This, OpaqueType); + VRef.setFloat(Val); +} + +REPL_EXTERNAL_VISIBILITY void __InterpreterSetValueNoAlloc(void *This, + void *OutVal, + void *OpaqueType, + double Val) { + Value &VRef = *(Value *)OutVal; + VRef = Value(This, OpaqueType); + VRef.setDouble(Val); +} + +REPL_EXTERNAL_VISIBILITY void __InterpreterSetValueNoAlloc(void *This, + void *OutVal, + void *OpaqueType, + long double Val) { + Value &VRef = *(Value *)OutVal; + VRef = Value(This, OpaqueType); + VRef.setLongDouble(Val); +} diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Interpreter/Value.cpp @@ -0,0 +1,189 @@ +#include "clang/Interpreter/Value.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Type.h" +#include "clang/Interpreter/Interpreter.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_os_ostream.h" +#include +#include +#include + +using namespace clang; + +namespace { + +// This is internal buffer maintained by Value, used to hold temporaries. +class ValueStorage { +public: + using DtorFunc = void (*)(void *); + + static unsigned char *CreatePayload(void *DtorF, size_t AllocSize, + size_t ElementsSize) { + unsigned char *Buf = new unsigned char[1024]; + ValueStorage *VS = new (Buf) ValueStorage(DtorF, AllocSize, ElementsSize); + return VS->getPayload(); + } + + unsigned char *getPayload() { return Storage; } + const unsigned char *getPayload() const { return Storage; } + + static unsigned getPayloadOffset() { + static ValueStorage Dummy(nullptr, 0, 0); + return Dummy.getPayload() - reinterpret_cast(&Dummy); + } + + static ValueStorage *getFromPayload(void *Payload) { + return reinterpret_cast((unsigned char *)Payload - + getPayloadOffset()); + } + + void Retain() { ++RefCnt; } + + void Release() { + assert(RefCnt == 0 && "Can't release if reference count is already zero"); + if (--RefCnt == 0) { + // We hace a non-trivial dtor. + if (Dtor) { + size_t Stride = AllocSize / Elements; + for (size_t Idx = 0; Idx < Elements; ++Idx) + (*Dtor)(getPayload() + Idx * Stride); + } + delete[] reinterpret_cast(this); + } + } + +private: + ValueStorage(void *DtorF, size_t AllocSize, size_t ElementsNum) + : RefCnt(1), AllocSize(AllocSize), Elements(ElementsNum), + Dtor(reinterpret_cast(DtorF)) {} + + mutable unsigned RefCnt; + size_t AllocSize = 0; + size_t Elements = 0; + unsigned char Storage[1]; + DtorFunc Dtor = nullptr; +}; +} // namespace + +static Value::Kind ConvertQualTypeToKind(ASTContext &Ctx, QualType QT) { + if (Ctx.hasSameType(QT, Ctx.VoidTy)) + return Value::K_Void; + + if (const auto *ET = dyn_cast(QT.getTypePtr())) + QT = ET->getDecl()->getIntegerType(); + + if (!QT->isBuiltinType() || QT->castAs()->isNullPtrType()) + return Value::K_PtrOrObj; + + switch (QT->getAs()->getKind()) { + default: + assert(false && "Type not supported"); + return Value::K_Unspecified; +#define X(type, name) \ + case BuiltinType::name: \ + return Value::K_##name; + REPL_BUILTIN_TYPES +#undef X + } +} + +Value::Value(void *In, void *Ty) : Interp(In), OpaqueType(Ty) { + setKind(ConvertQualTypeToKind(getASTContext(), getType())); + if (ValueKind == K_PtrOrObj) { + QualType Canon = getType().getCanonicalType(); + if ((Canon->isPointerType() || Canon->isObjectType() || + Canon->isReferenceType()) && + (Canon->isRecordType() || Canon->isConstantArrayType() || + Canon->isMemberPointerType())) { + // Compile dtor function. + Interpreter &Interp = getInterpreter(); + void *DtorF = nullptr; + // FIXME: Arrays + if (const auto *RT = getType()->getAs()) + // FIXME: How to handle errors in a constructor? + DtorF = llvm::cantFail(Interp.CompileDtorCall(RT->getDecl())); + + size_t AllocSize = + getASTContext().getTypeSizeInChars(getType()).getQuantity(); + size_t ElementsSize = 1; + setPtr(ValueStorage::CreatePayload(DtorF, AllocSize, ElementsSize)); + } + } +} + +Value::Value(const Value &RHS) + : Interp(RHS.Interp), OpaqueType(RHS.OpaqueType), ValueKind(RHS.ValueKind), + IsManuallyAlloc(RHS.IsManuallyAlloc) { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Retain(); +} + +Value::Value(Value &&RHS) noexcept { + Interp = std::exchange(RHS.Interp, nullptr); + OpaqueType = std::exchange(RHS.Interp, nullptr); + ValueKind = std::exchange(RHS.ValueKind, K_Unspecified); + + IsManuallyAlloc = RHS.IsManuallyAlloc; + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); +} + +Value &Value::operator=(const Value &RHS) { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); + + Interp = RHS.Interp; + OpaqueType = RHS.Interp; + ValueKind = RHS.ValueKind; + IsManuallyAlloc = RHS.IsManuallyAlloc; + + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Retain(); + + return *this; +} + +Value &Value::operator=(Value &&RHS) noexcept { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); + + Interp = std::exchange(RHS.Interp, nullptr); + OpaqueType = std::exchange(RHS.OpaqueType, nullptr); + ValueKind = std::exchange(RHS.ValueKind, K_Unspecified); + IsManuallyAlloc = RHS.IsManuallyAlloc; + + return *this; +} + +Value::~Value() { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); +} + +QualType Value::getType() const { + return QualType::getFromOpaquePtr(OpaqueType); +} + +Interpreter &Value::getInterpreter() const { + assert(Interp != nullptr && + "Can't get interpreter from a default constructed value"); + return *reinterpret_cast(Interp); +} + +ASTContext &Value::getASTContext() const { + return getInterpreter().getASTContext(); +} + +void Value::dump() const { print(llvm::outs()); } + +void Value::printType(llvm::raw_ostream &Out) const { + Out << "Not implement yet.\n"; +} +void Value::printData(llvm::raw_ostream &Out) const { + Out << "Not implement yet.\n"; +} +void Value::print(llvm::raw_ostream &Out) const { + assert(OpaqueType != nullptr && "Can't print default Value"); + Out << "Not implement yet.\n"; +} diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp --- a/clang/lib/Lex/PPLexerChange.cpp +++ b/clang/lib/Lex/PPLexerChange.cpp @@ -535,13 +535,19 @@ return LeavingSubmodule; } } - // If this is the end of the main file, form an EOF token. assert(CurLexer && "Got EOF but no current lexer set!"); const char *EndPos = getCurLexerEndPos(); Result.startToken(); CurLexer->BufferPtr = EndPos; - CurLexer->FormTokenWithChars(Result, EndPos, tok::eof); + + if (isIncrementalProcessingEnabled()) { + CurLexer->FormTokenWithChars(Result, EndPos, tok::annot_input_end); + Result.setAnnotationEndLoc(Result.getLocation()); + Result.setAnnotationValue(nullptr); + } else { + CurLexer->FormTokenWithChars(Result, EndPos, tok::eof); + } if (isCodeCompletionEnabled()) { // Inserting the code-completion point increases the source buffer by 1, diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -836,6 +836,7 @@ case tok::annot_module_begin: case tok::annot_module_end: case tok::annot_module_include: + case tok::annot_input_end: // Ran out of tokens. return false; @@ -1242,6 +1243,7 @@ case tok::annot_module_begin: case tok::annot_module_end: case tok::annot_module_include: + case tok::annot_input_end: // Ran out of tokens. return false; diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2077,6 +2077,7 @@ case tok::annot_module_begin: case tok::annot_module_end: case tok::annot_module_include: + case tok::annot_input_end: return; default: diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -543,8 +543,16 @@ return ParseCaseStatement(StmtCtx, /*MissingCase=*/true, Expr); } - // Otherwise, eat the semicolon. - ExpectAndConsumeSemi(diag::err_expected_semi_after_expr); + if (PP.isIncrementalProcessingEnabled() && Tok.is(tok::annot_input_end)) { + // If we're parsing an ExprStmt and the last semicolon is missing and the + // incremental externsion is enabled and we're reaching the end, consider we + // want to do value printing. + ConsumeAnyToken(); + setPrettyPrintMode(); + } else { + // Otherwise, eat the semicolon. + ExpectAndConsumeSemi(diag::err_expected_semi_after_expr); + } return handleExprStmt(Expr, StmtCtx); } diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -320,6 +320,7 @@ case tok::annot_module_begin: case tok::annot_module_end: case tok::annot_module_include: + case tok::annot_input_end: // Stop before we change submodules. They generally indicate a "good" // place to pick up parsing again (except in the special case where // we're trying to skip to EOF). @@ -616,8 +617,8 @@ // Skip over the EOF token, flagging end of previous input for incremental // processing - if (PP.isIncrementalProcessingEnabled() && Tok.is(tok::eof)) - ConsumeToken(); + if (PP.isIncrementalProcessingEnabled() && Tok.is(tok::annot_input_end)) + ConsumeAnyToken(); Result = nullptr; switch (Tok.getKind()) { @@ -697,6 +698,7 @@ return false; case tok::eof: + case tok::annot_input_end: // Check whether -fmax-tokens= was reached. if (PP.getMaxTokens() != 0 && PP.getTokenCount() > PP.getMaxTokens()) { PP.Diag(Tok.getLocation(), diag::warn_max_tokens_total) diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt --- a/clang/tools/clang-repl/CMakeLists.txt +++ b/clang/tools/clang-repl/CMakeLists.txt @@ -12,6 +12,7 @@ ) clang_target_link_libraries(clang-repl PRIVATE + clangAST clangBasic clangFrontend clangInterpreter diff --git a/clang/unittests/Interpreter/CMakeLists.txt b/clang/unittests/Interpreter/CMakeLists.txt --- a/clang/unittests/Interpreter/CMakeLists.txt +++ b/clang/unittests/Interpreter/CMakeLists.txt @@ -22,3 +22,5 @@ if(NOT WIN32) add_subdirectory(ExceptionTests) endif() + +export_executable_symbols(ClangReplInterpreterTests) diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -17,6 +17,7 @@ #include "clang/AST/Mangle.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Interpreter/Value.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Sema.h" @@ -33,6 +34,10 @@ #define CLANG_INTERPRETER_NO_SUPPORT_EXEC #endif +int Global = 42; +int getGlobal() { return Global; } +void setGlobal(int val) { Global = val; } + namespace { using Args = std::vector; static std::unique_ptr @@ -139,6 +144,8 @@ auto Interp = createInterpreter(ExtraArgs, DiagPrinter.get()); + // FIXME: Now we will include some runtime headers when the first we enter + // the REPL, so this doesn't work anymore. // Fail to undo. auto Err1 = Interp->Undo(); EXPECT_EQ("Operation failed. Too many undos", @@ -276,8 +283,7 @@ std::vector Args = {"-fno-delayed-template-parsing"}; std::unique_ptr Interp = createInterpreter(Args); - llvm::cantFail(Interp->Parse("void* operator new(__SIZE_TYPE__, void* __p);" - "extern \"C\" int printf(const char*,...);" + llvm::cantFail(Interp->Parse("extern \"C\" int printf(const char*,...);" "class A {};" "struct B {" " template" @@ -314,4 +320,31 @@ free(NewA); } +TEST(InterpreterTest, Value) { + std::unique_ptr Interp = createInterpreter(); + + Value V1; + llvm::cantFail(Interp->ParseAndExecute("int x = 42;")); + llvm::cantFail(Interp->ParseAndExecute("x", &V1)); + EXPECT_EQ(V1.getInt(), 42); + EXPECT_TRUE(V1.getType()->isIntegerType()); + + llvm::cantFail(Interp->ParseAndExecute("int getGlobal();")); + llvm::cantFail(Interp->ParseAndExecute("void setGlobal(int);")); + Value V2; + llvm::cantFail(Interp->ParseAndExecute("getGlobal()", &V2)); + EXPECT_EQ(V2.getInt(), 42); + EXPECT_TRUE(V2.getType()->isIntegerType()); + + // Change the global from the compiled code. + setGlobal(43); + Value V3; + llvm::cantFail(Interp->ParseAndExecute("getGlobal()", &V3)); + EXPECT_EQ(V3.getInt(), 43); + EXPECT_TRUE(V3.getType()->isIntegerType()); + + // Change the global from the interpreted code. + llvm::cantFail(Interp->ParseAndExecute("setGlobal(44);")); + EXPECT_EQ(getGlobal(), 44); +} } // end anonymous namespace