//===--- Frontend.h - frontend utility methods ------------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file contains declarations of utility methods for parsing and // performing semantic on modules. // //===----------------------------------------------------------------------===// #ifndef SWIFT_FRONTEND_H #define SWIFT_FRONTEND_H #include "swift/AST/DiagnosticConsumer.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/LinkLibrary.h" #include "swift/AST/Module.h" #include "swift/AST/SILOptions.h" #include "swift/AST/SearchPathOptions.h" #include "swift/Basic/DiagnosticOptions.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/SourceManager.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangImporterOptions.h" #include "swift/Frontend/FrontendOptions.h" #include "swift/Migrator/MigratorOptions.h" #include "swift/Parse/CodeCompletionCallbacks.h" #include "swift/Parse/Parser.h" #include "swift/SIL/SILModule.h" #include "swift/Sema/SourceLoader.h" #include "swift/Serialization/Validation.h" #include "swift/Subsystems.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/SetVector.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/Host.h" #include "llvm/Support/MemoryBuffer.h" #include namespace swift { class SerializedModuleLoader; /// The abstract configuration of the compiler, including: /// - options for all stages of translation, /// - information about the build environment, /// - information about the job being performed, and /// - lists of inputs. /// /// A CompilerInvocation can be built from a frontend command line /// using parseArgs. It can then be used to build a CompilerInstance, /// which manages the actual compiler execution. class CompilerInvocation { LangOptions LangOpts; FrontendOptions FrontendOpts; ClangImporterOptions ClangImporterOpts; SearchPathOptions SearchPathOpts; DiagnosticOptions DiagnosticOpts; MigratorOptions MigratorOpts; SILOptions SILOpts; IRGenOptions IRGenOpts; llvm::MemoryBuffer *CodeCompletionBuffer = nullptr; /// \brief Code completion offset in bytes from the beginning of the main /// source file. Valid only if \c isCodeCompletion() == true. unsigned CodeCompletionOffset = ~0U; CodeCompletionCallbacksFactory *CodeCompletionFactory = nullptr; public: CompilerInvocation(); /// Initializes the compiler invocation for the list of arguments. /// /// All parsing should be additive, i.e. options should not be reset to their /// default values given the /absence/ of a flag. This is because \c parseArgs /// may be used to modify an already partially configured invocation. /// /// If non-empty, relative search paths are resolved relative to /// \p workingDirectory. /// /// \returns true if there was an error, false on success. bool parseArgs(ArrayRef Args, DiagnosticEngine &Diags, StringRef workingDirectory = {}); /// Sets specific options based on the given serialized Swift binary data. /// /// This is additive, i.e. options are not reset to their default values given /// the /absence/ of a flag. However, flags that only have a single value may /// (and should) be overwritten by this method. /// /// Invoking this on more than one serialized AST is likely to result in /// one or both of them failing to load. Please pick one AST to provide base /// flags for the entire ASTContext and let the others succeed or fail the /// normal way. (Some additive flags, like search paths, will be handled /// properly during normal module loading.) /// /// \returns Status::Valid on success, one of the Status issues on error. serialization::Status loadFromSerializedAST(StringRef data); /// Serialize the command line arguments for emitting them /// to DWARF and inject SDKPath if necessary. static void buildDWARFDebugFlags(std::string &Output, const ArrayRef &Args, StringRef SDKPath, StringRef ResourceDir); void setTargetTriple(StringRef Triple); StringRef getTargetTriple() const { return LangOpts.Target.str(); } void setClangModuleCachePath(StringRef Path) { ClangImporterOpts.ModuleCachePath = Path.str(); } StringRef getClangModuleCachePath() const { return ClangImporterOpts.ModuleCachePath; } void setImportSearchPaths(const std::vector &Paths) { SearchPathOpts.ImportSearchPaths = Paths; } ArrayRef getImportSearchPaths() const { return SearchPathOpts.ImportSearchPaths; } void setFrameworkSearchPaths( const std::vector &Paths) { SearchPathOpts.FrameworkSearchPaths = Paths; } ArrayRef getFrameworkSearchPaths() const { return SearchPathOpts.FrameworkSearchPaths; } void setExtraClangArgs(const std::vector &Args) { ClangImporterOpts.ExtraArgs = Args; } ArrayRef getExtraClangArgs() const { return ClangImporterOpts.ExtraArgs; } void addLinkLibrary(StringRef name, LibraryKind kind) { IRGenOpts.LinkLibraries.push_back({name, kind}); } ArrayRef getLinkLibraries() const { return IRGenOpts.LinkLibraries; } void setMainExecutablePath(StringRef Path); void setRuntimeResourcePath(StringRef Path); void setSDKPath(const std::string &Path) { SearchPathOpts.SDKPath = Path; } StringRef getSDKPath() const { return SearchPathOpts.SDKPath; } void setSerializedDiagnosticsPath(StringRef Path) { FrontendOpts.SerializedDiagnosticsPath = Path; } StringRef getSerializedDiagnosticsPath() const { return FrontendOpts.SerializedDiagnosticsPath; } LangOptions &getLangOptions() { return LangOpts; } const LangOptions &getLangOptions() const { return LangOpts; } FrontendOptions &getFrontendOptions() { return FrontendOpts; } const FrontendOptions &getFrontendOptions() const { return FrontendOpts; } ClangImporterOptions &getClangImporterOptions() { return ClangImporterOpts; } const ClangImporterOptions &getClangImporterOptions() const { return ClangImporterOpts; } SearchPathOptions &getSearchPathOptions() { return SearchPathOpts; } const SearchPathOptions &getSearchPathOptions() const { return SearchPathOpts; } DiagnosticOptions &getDiagnosticOptions() { return DiagnosticOpts; } const DiagnosticOptions &getDiagnosticOptions() const { return DiagnosticOpts; } const MigratorOptions &getMigratorOptions() const { return MigratorOpts; } SILOptions &getSILOptions() { return SILOpts; } const SILOptions &getSILOptions() const { return SILOpts; } IRGenOptions &getIRGenOptions() { return IRGenOpts; } const IRGenOptions &getIRGenOptions() const { return IRGenOpts; } void setParseStdlib() { FrontendOpts.ParseStdlib = true; } bool getParseStdlib() const { return FrontendOpts.ParseStdlib; } void setInputKind(InputFileKind K) { FrontendOpts.InputKind = K; } InputFileKind getInputKind() const { return FrontendOpts.InputKind; } SourceFileKind getSourceFileKind() const; void setModuleName(StringRef Name) { FrontendOpts.ModuleName = Name.str(); IRGenOpts.ModuleName = Name.str(); } StringRef getModuleName() const { return FrontendOpts.ModuleName; } StringRef getOutputFilename() const { return FrontendOpts.getSingleOutputFilename(); } void setCodeCompletionPoint(llvm::MemoryBuffer *Buf, unsigned Offset) { assert(Buf); CodeCompletionBuffer = Buf; CodeCompletionOffset = Offset; // We don't need typo-correction for code-completion. // FIXME: This isn't really true, but is a performance issue. LangOpts.TypoCorrectionLimit = 0; } std::pair getCodeCompletionPoint() const { return std::make_pair(CodeCompletionBuffer, CodeCompletionOffset); } /// \returns true if we are doing code completion. bool isCodeCompletion() const { return CodeCompletionOffset != ~0U; } void setCodeCompletionFactory(CodeCompletionCallbacksFactory *Factory) { CodeCompletionFactory = Factory; } CodeCompletionCallbacksFactory *getCodeCompletionFactory() const { return CodeCompletionFactory; } /// Retrieve a module hash string that is suitable for uniquely /// identifying the conditions under which the module was built, for use /// in generating a cached PCH file for the bridging header. std::string getPCHHash() const; SourceFile::ImplicitModuleImportKind getImplicitModuleImportKind() { if (getInputKind() == InputFileKind::IFK_SIL) { return SourceFile::ImplicitModuleImportKind::None; } if (getParseStdlib()) { return SourceFile::ImplicitModuleImportKind::Builtin; } return SourceFile::ImplicitModuleImportKind::Stdlib; } /// Performs input setup common to these tools: /// sil-opt, sil-func-extractor, sil-llvm-gen, and sil-nm. /// Return value includes the buffer so caller can keep it alive. llvm::ErrorOr> setUpInputForSILTool(StringRef inputFilename, StringRef moduleNameArg, bool alwaysSetModuleToMain, bool bePrimary, serialization::ExtendedValidationInfo &extendedInfo); bool hasSerializedAST() { return FrontendOpts.InputKind == InputFileKind::IFK_Swift_Library; } }; /// A class which manages the state and execution of the compiler. /// This owns the primary compiler singletons, such as the ASTContext, /// as well as various build products such as the SILModule. /// /// Before a CompilerInstance can be used, it must be configured by /// calling \a setup. If successful, this will create an ASTContext /// and set up the basic compiler invariants. Calling \a setup multiple /// times on a single CompilerInstance is not permitted. class CompilerInstance { CompilerInvocation Invocation; SourceManager SourceMgr; DiagnosticEngine Diagnostics{SourceMgr}; std::unique_ptr Context; std::unique_ptr TheSILModule; DependencyTracker *DepTracker = nullptr; ReferencedNameTracker *NameTracker = nullptr; ModuleDecl *MainModule = nullptr; SerializedModuleLoader *SML = nullptr; /// Contains buffer IDs for input source code files. std::vector InputSourceCodeBufferIDs; struct PartialModuleInputs { std::unique_ptr ModuleBuffer; std::unique_ptr ModuleDocBuffer; }; /// Contains \c MemoryBuffers for partial serialized module files and /// corresponding partial serialized module documentation files. std::vector PartialModules; enum : unsigned { NO_SUCH_BUFFER = ~0U }; unsigned MainBufferID = NO_SUCH_BUFFER; /// Identifies the set of input buffers in the SourceManager that are /// considered primaries. llvm::SetVector PrimaryBufferIDs; /// Identifies the set of SourceFiles that are considered primaries. An /// invariant is that any SourceFile in this set with an associated /// buffer will also have its buffer ID in PrimaryBufferIDs. std::vector PrimarySourceFiles; /// Return whether there is an entry in PrimaryInputs for buffer \p BufID. bool isPrimaryInput(unsigned BufID) const { return PrimaryBufferIDs.count(BufID) != 0; } /// Record in PrimaryBufferIDs the fact that \p BufID is a primary. /// If \p BufID is already in the set, do nothing. void recordPrimaryInputBuffer(unsigned BufID); /// Record in PrimarySourceFiles the fact that \p SF is a primary, and /// call recordPrimaryInputBuffer on \p SF's buffer (if it exists). void recordPrimarySourceFile(SourceFile *SF); bool isWholeModuleCompilation() { return PrimaryBufferIDs.empty(); } void createSILModule(); public: SourceManager &getSourceMgr() { return SourceMgr; } DiagnosticEngine &getDiags() { return Diagnostics; } ASTContext &getASTContext() { return *Context; } bool hasASTContext() const { return Context != nullptr; } SILOptions &getSILOptions() { return Invocation.getSILOptions(); } const SILOptions &getSILOptions() const { return Invocation.getSILOptions(); } void addDiagnosticConsumer(DiagnosticConsumer *DC) { Diagnostics.addConsumer(*DC); } void setDependencyTracker(DependencyTracker *DT) { assert(!Context && "must be called before setup()"); DepTracker = DT; } DependencyTracker *getDependencyTracker() { return DepTracker; } void setReferencedNameTracker(ReferencedNameTracker *tracker) { assert(PrimarySourceFiles.empty() && "must be called before performSema()"); NameTracker = tracker; } ReferencedNameTracker *getReferencedNameTracker() { return NameTracker; } /// Set the SIL module for this compilation instance. /// /// The CompilerInstance takes ownership of the given SILModule object. void setSILModule(std::unique_ptr M) { TheSILModule = std::move(M); } SILModule *getSILModule() { return TheSILModule.get(); } std::unique_ptr takeSILModule() { return std::move(TheSILModule); } bool hasSILModule() { return static_cast(TheSILModule); } ModuleDecl *getMainModule(); SerializedModuleLoader *getSerializedModuleLoader() const { return SML; } ArrayRef getInputBufferIDs() const { return InputSourceCodeBufferIDs; } ArrayRef getLinkLibraries() const { return Invocation.getLinkLibraries(); } bool hasSourceImport() const { return Invocation.getFrontendOptions().EnableSourceImport; } /// Gets the set of SourceFiles which are the primary inputs for this /// CompilerInstance. ArrayRef getPrimarySourceFiles() { return PrimarySourceFiles; } /// Gets the Primary Source File if one exists, otherwise the main /// module. If multiple Primary Source Files exist, fails with an /// assertion. ModuleOrSourceFile getPrimarySourceFileOrMainModule() { if (PrimarySourceFiles.empty()) return getMainModule(); else return getPrimarySourceFile(); } /// Gets the SourceFile which is the primary input for this CompilerInstance. /// \returns the primary SourceFile, or nullptr if there is no primary input; /// if there are _multiple_ primary inputs, fails with an assertion. /// /// FIXME: This should be removed eventually, once there are no longer any /// codepaths that rely on a single primary file. SourceFile *getPrimarySourceFile() { if (PrimarySourceFiles.empty()) { return nullptr; } else { assert(PrimarySourceFiles.size() == 1); return *PrimarySourceFiles.begin(); } } /// \brief Returns true if there was an error during setup. bool setup(const CompilerInvocation &Invocation); private: void setUpLLVMArguments(); void setUpDiagnosticOptions(); bool setUpModuleLoaders(); bool isInputSwift() { return Invocation.getInputKind() == InputFileKind::IFK_Swift; } bool isInSILMode() { return Invocation.getInputKind() == InputFileKind::IFK_SIL; } bool setUpInputs(); Optional setUpCodeCompletionBuffer(); /// Set up all state in the CompilerInstance to process the given input file. /// Return true on error. bool setUpForInput(const InputFile &input); /// Find a buffer for a given input file and ensure it is recorded in /// SourceMgr, PartialModules, or InputSourceCodeBufferIDs as appropriate. /// Return the buffer ID if it is not already compiled, or None if so. /// Set failed on failure. Optional getRecordedBufferID(const InputFile &input, bool &failed); /// Given an input file, return a buffer to use for its contents, /// and a buffer for the corresponding module doc file if one exists. /// On failure, return a null pointer for the first element of the returned /// pair. std::pair, std::unique_ptr> getInputBufferAndModuleDocBufferIfPresent(const InputFile &input); /// Try to open the module doc file corresponding to the input parameter. /// Return None for error, nullptr if no such file exists, or the buffer if /// one was found. Optional> openModuleDoc(const InputFile &input); public: /// Parses and type-checks all input files. void performSema(); /// Parses the input file but does no type-checking or module imports. /// Note that this only supports parsing an invocation with a single file. /// /// void performParseOnly(bool EvaluateConditionals = false); private: SourceFile * createSourceFileForMainModule(SourceFileKind FileKind, SourceFile::ImplicitModuleImportKind ImportKind, Optional BufferID); public: /// Frees up the ASTContext and SILModule objects that this instance is /// holding on. void freeContextAndSIL(); private: /// Load stdlib & return true if should continue, i.e. no error bool loadStdlib(); ModuleDecl *importUnderlyingModule(); ModuleDecl *importBridgingHeader(); void getImplicitlyImportedModules(SmallVectorImpl &importModules); public: // for static functions in Frontend.cpp struct ImplicitImports { SourceFile::ImplicitModuleImportKind kind; ModuleDecl *objCModuleUnderlyingMixedFramework; ModuleDecl *headerModule; SmallVector modules; explicit ImplicitImports(CompilerInstance &compiler); }; private: void createREPLFile(const ImplicitImports &implicitImports); std::unique_ptr computeDelayedParsingCallback(bool isPrimary); void addMainFileToModule(const ImplicitImports &implicitImports); void parseAndCheckTypes(const ImplicitImports &implicitImports); void parseLibraryFile(unsigned BufferID, const ImplicitImports &implicitImports, PersistentParserState &PersistentState, DelayedParsingCallbacks *PrimaryDelayedCB, DelayedParsingCallbacks *SecondaryDelayedCB); /// Return true if had load error bool parsePartialModulesAndLibraryFiles(const ImplicitImports &implicitImports, PersistentParserState &PersistentState, DelayedParsingCallbacks *PrimaryDelayedCB, DelayedParsingCallbacks *SecondaryDelayedCB); OptionSet computeTypeCheckingOptions(); void forEachFileToTypeCheck(llvm::function_ref fn); void parseAndTypeCheckMainFile(PersistentParserState &PersistentState, DelayedParsingCallbacks *DelayedParseCB, OptionSet TypeCheckOptions); void finishTypeChecking(OptionSet TypeCheckOptions); }; } // namespace swift #endif