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,9 +14,10 @@ #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/ExecutionEngine/JITSymbol.h" #include "llvm/Support/Error.h" @@ -49,6 +50,7 @@ std::unique_ptr TSCtx; std::unique_ptr IncrParser; std::unique_ptr IncrExecutor; + Expr *RuntimeInterface = nullptr; Interpreter(std::unique_ptr CI, llvm::Error &Err); @@ -60,14 +62,7 @@ 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); /// Undo N previous incremental inputs. llvm::Error Undo(unsigned N = 1); @@ -85,6 +80,16 @@ /// file. llvm::Expected getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const; + +private: + llvm::Expected CaptureExpr(TranslationUnitDecl &TU); + + bool FindRuntimeInterface(); + + std::pair, FunctionDecl *> + SynthesizePTU(clang::Expr *E); + + llvm::Error GenerateValue(TranslationUnitDecl &TU, Value *V); }; } // 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,115 @@ +//===--- 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 Interpreter; +class QualType; + +#define BUILTIN_TYPES \ + X(bool, Bool) \ + X(char, Char) \ + 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) + +class LLVM_EXTERNAL_VISIBILITY Value { + union Storage { +#define X(type, name) type m_##name; + BUILTIN_TYPES +#undef X + void *m_Ptr; + }; + +public: + enum Kind { +#define X(type, name) K_##name, + BUILTIN_TYPES +#undef X + K_Void, + K_PtrOrObj, + K_Unspecified + }; + + Value() = default; + + bool isValid() const { return ValueKind != K_Unspecified; } + bool isVoid() const { return ValueKind == K_Void; } + Kind getKind() const { return ValueKind; } + void setKind(Kind K) { ValueKind = K; } + void setOpaqueType(void *Ty) { OpaqueType = Ty; } + + clang::QualType getType() const; + void print(llvm::raw_ostream &Out) const; + void dump() const; + + void *getPtr() const { + assert(ValueKind == K_PtrOrObj); + return Data.m_Ptr; + } + + void setPtr(void *Ptr) { Data.m_Ptr = Ptr; } + +#define X(type, name) \ + void set##name(type Val) { \ + Data.m_##name = Val; \ + ValueKind = K_##name; \ + } \ + type get##name() const { \ + assert(ValueKind == K_##name); \ + return Data.m_##name; \ + } + 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; \ + } + BUILTIN_TYPES +#undef X + +private: + // QualType, stored as void* to reduce dependencies. + void *OpaqueType; + Storage Data; + Kind ValueKind = K_Unspecified; +}; + +} // 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; @@ -1062,7 +1077,8 @@ /// If the next token is not a semicolon, this emits the specified diagnostic, /// or, if there's just some closing-delimiter noise (e.g., ')' or ']') prior /// to the semicolon, consumes that extra token. - bool ExpectAndConsumeSemi(unsigned DiagID , StringRef TokenUsed = ""); + bool ExpectAndConsumeSemi(unsigned DiagID, StringRef TokenUsed = "", + bool IsTopExpr = false); /// The kind of extra semi diagnostic to emit. enum ExtraSemiKind { 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 + ValueSynthesize.cpp + Value.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,11 @@ std::list &getPTUs() { return PTUs; } -private: - llvm::Expected ParseOrWrapTopLevelDecl(); + 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); @@ -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(); @@ -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 @@ -15,8 +15,18 @@ #include "IncrementalExecutor.h" #include "IncrementalParser.h" - #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Mangle.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/OperationKinds.h" +#include "clang/AST/Type.h" +#include "clang/AST/TypeVisitor.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" @@ -27,11 +37,21 @@ #include "clang/Driver/Tool.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Interpreter/PartialTranslationUnit.h" #include "clang/Lex/PreprocessorOptions.h" - +#include "clang/Sema/DeclSpec.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Ownership.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/TargetLowering.h" +#include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/IR/Module.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Host.h" +#include using namespace clang; @@ -235,6 +255,59 @@ return llvm::Error::success(); } +llvm::Error Interpreter::GenerateValue(TranslationUnitDecl &TU, Value *V) { + // Capture the expression we want to print. + auto ExprOrErr = CaptureExpr(TU); + if (!ExprOrErr) + return ExprOrErr.takeError(); + // Generate a PartialTranslationUnit. + std::pair, FunctionDecl *> + SynthesizedPTUOrErr = SynthesizePTU(*ExprOrErr); + if (!SynthesizedPTUOrErr.first) + return SynthesizedPTUOrErr.first.takeError(); + + if (SynthesizedPTUOrErr.first->TheModule) + if (llvm::Error Err = Execute(*SynthesizedPTUOrErr.first)) + return Err; + + ASTNameGenerator ASTNameGen(getCompilerInstance()->getASTContext()); + llvm::Expected AddrOrErr = + getSymbolAddressFromLinkerName( + ASTNameGen.getName(SynthesizedPTUOrErr.second)); + if (!AddrOrErr) + return AddrOrErr.takeError(); + + if (auto *Main = + llvm::jitTargetAddressToPointer(AddrOrErr.get())) + (*Main)((void *)V); + 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; + + // 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) @@ -283,3 +356,187 @@ } 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"); + if (const auto *S = + llvm::dyn_cast(*TU.decls_begin())) + 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()); +} + +static constexpr llvm::StringRef MagicRuntimeInterface = + "__InterpreterCreateValue"; + +bool Interpreter::FindRuntimeInterface() { + if (RuntimeInterface) + return true; + + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + LookupResult R(S, &Ctx.Idents.get(MagicRuntimeInterface), SourceLocation(), + Sema::LookupOrdinaryName, Sema::ForVisibleRedeclaration); + S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl()); + if (R.empty()) + return false; + + CXXScopeSpec CSS; + RuntimeInterface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get(); + return true; +} + +static IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uintptr_t Ptr) { + const llvm::APInt Addr(8 * sizeof(void *), Ptr); + return IntegerLiteral::Create(C, Addr, C.getUIntPtrType(), SourceLocation()); +} + +static 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; +} + +static Expr *CStyleCastPtrExpr(Sema *S, QualType Ty, uintptr_t Ptr) { + ASTContext &Ctx = S->getASTContext(); + return CStyleCastPtrExpr(S, Ty, IntegerLiteralExpr(Ctx, Ptr)); +} + +static std::string GetUniqueWrapperName() { + static int I = 0; + std::string WrapperName("__InterpreterValueGetter"); + WrapperName += std::to_string(I); + I += 1; + return WrapperName; +} + +namespace { + +class ArgsCreator : public TypeVisitor { + ASTContext &Ctx; + Sema &S; + Expr *E; + llvm::SmallVector Args; + +public: + ArgsCreator(ASTContext &C, Sema &SemaRef, Expr *VE, Expr *OVR) + : Ctx(C), S(SemaRef), E(VE) { + // The out parameter `OutVal`. + Args.push_back(OVR); + } + llvm::SmallVector getArgs() { + QualType Ty = E->getType(); + Expr *TypeArg = CStyleCastPtrExpr(&S, S.getASTContext().VoidPtrTy, + (uintptr_t)Ty.getAsOpaquePtr()); + // The QualType parameter `OpaqueType`, represented as `void*`. + Args.push_back(TypeArg); + + QualType DesugaredTy = Ty.getDesugaredType(Ctx); + if (DesugaredTy->isRecordType() && E->isLValue()) { + DesugaredTy = Ctx.getLValueReferenceType(DesugaredTy); + Ty = Ctx.getLValueReferenceType(Ty); + } + Visit(&*DesugaredTy); + + return Args; + } + void VisitRecordType(const RecordType *Ty) { + llvm_unreachable("Not implemented yet"); + } + void VisitMemberPointerType(const MemberPointerType *Ty) { + llvm_unreachable("Not implemented yet"); + } + void VisitConstantArrayType(const ConstantArrayType *Ty) { + llvm_unreachable("Not implemented yet"); + } + + void VisitPointerType(const PointerType *Ty) { + TypeSourceInfo *TSI = + Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy, SourceLocation()); + ExprResult CastedExpr = + S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); + assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression"); + Args.push_back(CastedExpr.get()); + } + void 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()); + } + void 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()) { + // If we want to reduce the number of the overloads, we should cast it to + // uint64. + Args.push_back(E); + } + } +}; +} // namespace + +// 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: +// clang-repl> void ValueGetter(void* OpaqueValue) { +// __InterpreterSetValue(OpaqueValue, xQualType, x); +// } +std::pair, FunctionDecl *> +Interpreter::SynthesizePTU(clang::Expr *E) { + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + QualType RetTy = Ctx.VoidTy; + QualType ArgTy = Ctx.VoidPtrTy; + QualType FunctionTy = Ctx.getFunctionType(RetTy, {ArgTy}, {}); + IdentifierInfo *II = &Ctx.Idents.get(GetUniqueWrapperName()); + auto *FD = FunctionDecl::Create( + S.getASTContext(), Ctx.getTranslationUnitDecl(), SourceLocation(), + SourceLocation(), II, FunctionTy, /*TInfo=*/nullptr, SC_None); + auto *Param = ParmVarDecl::Create( + Ctx, FD, SourceLocation(), SourceLocation(), /*Id=*/nullptr, ArgTy, + Ctx.getTrivialTypeSourceInfo(ArgTy, SourceLocation()), SC_None, + /*DefArg=*/nullptr); + FD->setParams({Param}); + + if (!FindRuntimeInterface()) + llvm_unreachable("We can't find the runtime iterface for pretty print!"); + + auto *OutValueRef = + DeclRefExpr::Create(Ctx, NestedNameSpecifierLoc(), SourceLocation(), + Param, /*RefersToEnclosingVariableOrCapture=*/false, + SourceLocation(), ArgTy, VK_PRValue); + + ArgsCreator Creator(Ctx, S, E, OutValueRef); + llvm::SmallVector Args = Creator.getArgs(); + + ExprResult Result = S.ActOnCallExpr(/*Scope=*/nullptr, RuntimeInterface, + E->getBeginLoc(), Args, E->getEndLoc()); + assert(!Result.isInvalid() && "Failed to generate the CallExpr!"); + + FD->setBody(Result.get()); + + SmallVector DeclsInGroup; + DeclsInGroup.push_back(FD); + Sema::DeclGroupPtrTy DeclGroupPtr = S.BuildDeclaratorGroup(DeclsInGroup); + return {IncrParser->Parse(DeclGroupPtr), FD}; +} 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,32 @@ +#include "clang/Interpreter/Value.h" +#include "clang/AST/Type.h" +#include "llvm/Support/raw_os_ostream.h" +#include + +using namespace clang; + +QualType Value::getType() const { + return QualType::getFromOpaquePtr(OpaqueType); +} + +void Value::print(llvm::raw_ostream &Out) const { + Out << "(" << getType().getAsString() << ") "; + switch (getKind()) { + case K_Unspecified: + assert(false); + case K_Void: + Out << " \n"; + break; + case K_PtrOrObj: + Out << getPtr() << "\n"; + break; +#define X(type, name) \ + case K_##name: \ + Out << get##name() << "\n"; \ + break; + BUILTIN_TYPES +#undef X + } +} + +void Value::dump() const { print(llvm::outs()); } diff --git a/clang/lib/Interpreter/ValueSynthesize.cpp b/clang/lib/Interpreter/ValueSynthesize.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Interpreter/ValueSynthesize.cpp @@ -0,0 +1,22 @@ +#include "clang/Interpreter/Value.h" +#include "llvm/Support/Compiler.h" + +using namespace clang; + +void __InterpreterCreateValue(void *OutVal, void *OpaqueType, void *Val) { + Value *V = reinterpret_cast(OutVal); + V->setOpaqueType(OpaqueType); + V->setKind(Value::K_PtrOrObj); + V->setPtr(Val); +} + +#define X(type, name) \ + LLVM_EXTERNAL_VISIBILITY void __InterpreterCreateValue( \ + void *OutVal, void *OpaqueType, type Val) { \ + Value *V = reinterpret_cast(OutVal); \ + V->setOpaqueType(OpaqueType); \ + V->setKind(Value::K_##name); \ + V->set##name(Val); \ + } +BUILTIN_TYPES +#undef X 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 @@ -542,9 +542,9 @@ // Recover parsing as a case statement. return ParseCaseStatement(StmtCtx, /*MissingCase=*/true, Expr); } - // Otherwise, eat the semicolon. - ExpectAndConsumeSemi(diag::err_expected_semi_after_expr); + ExpectAndConsumeSemi(diag::err_expected_semi_after_expr, /*TokenUsed=*/"", + StmtCtx == ParsedStmtContext::SubStmt); 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 @@ -154,10 +154,20 @@ return true; } -bool Parser::ExpectAndConsumeSemi(unsigned DiagID, StringRef TokenUsed) { +bool Parser::ExpectAndConsumeSemi(unsigned DiagID, StringRef TokenUsed, + bool IsTopExpr) { if (TryConsumeToken(tok::semi)) return false; + // If this is in the incremental C++ mode, then it means we need to pretty + // print this expression. Thus, let's pretend we have this semi and continue + // parsing. + if (PP.isIncrementalProcessingEnabled() && IsTopExpr && + DiagID == diag::err_expected_semi_after_expr) { + setPrettyPrintMode(); + return false; + } + if (Tok.is(tok::code_completion)) { handleUnexpectedCodeCompletionToken(); return false; diff --git a/clang/test/Interpreter/pretty-print.cpp b/clang/test/Interpreter/pretty-print.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Interpreter/pretty-print.cpp @@ -0,0 +1,12 @@ +// RUN: clang-repl "int i = 10;" 'extern "C" int printf(const char*,...);' \ +// RUN: 'auto r1 = printf("i = %d\n", i);' | FileCheck --check-prefix=CHECK-DRIVER %s +// UNSUPPORTED: system-aix +// CHECK-DRIVER: i = 10 +// RUN: cat %s | clang-repl | FileCheck %s +int x = 42; +x +// CHECK: (int) 42 +x - 2 +// CHECK-NEXT: (int) 40 +%quit + 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/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/Decl.h" #include "clang/Basic/Diagnostic.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" @@ -18,6 +19,8 @@ #include "llvm/ExecutionEngine/Orc/LLJIT.h" #include "llvm/LineEditor/LineEditor.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ManagedStatic.h" // llvm_shutdown #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" // llvm::Initialize* @@ -66,6 +69,29 @@ return (Errs || HasError) ? EXIT_FAILURE : EXIT_SUCCESS; } +static void DeclareMagicFunctions(clang::Interpreter &Interp) { + llvm::ArrayRef MagicFunctions = { + "void __InterpreterCreateValue(void*, void*, bool);", + "void __InterpreterCreateValue(void*, void*, char);", + "void __InterpreterCreateValue(void*, void*, signed char);", + "void __InterpreterCreateValue(void*, void*, short);", + "void __InterpreterCreateValue(void*, void*, int);", + "void __InterpreterCreateValue(void*, void*, long);", + "void __InterpreterCreateValue(void*, void*, long long);", + "void __InterpreterCreateValue(void*, void*, unsigned char);", + "void __InterpreterCreateValue(void*, void*, unsigned short);", + "void __InterpreterCreateValue(void*, void*, unsigned int);", + "void __InterpreterCreateValue(void*, void*, unsigned long);", + "void __InterpreterCreateValue(void*, void*, unsigned long long);", + "void __InterpreterCreateValue(void*, void*, float);", + "void __InterpreterCreateValue(void*, void*, double);", + "void __InterpreterCreateValue(void*, void*, void*);"}; + + for (llvm::StringRef Function : MagicFunctions) { + llvm::cantFail(Interp.ParseAndExecute(Function)); + } +} + llvm::ExitOnError ExitOnErr; int main(int argc, const char **argv) { ExitOnErr.setBanner("clang-repl: "); @@ -110,6 +136,7 @@ bool HasError = false; + DeclareMagicFunctions(*Interp); if (OptInputs.empty()) { llvm::LineEditor LE("clang-repl"); // FIXME: Add LE.setListCompleter