//===--- Subsystems.h - Swift Compiler Subsystem Entrypoints ----*- 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 declares the main entrypoints to the various subsystems.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_SUBSYSTEMS_H
#define SWIFT_SUBSYSTEMS_H

#include "swift/Basic/LLVM.h"
#include "swift/Basic/OptionSet.h"
#include "swift/Basic/PrimarySpecificPaths.h"
#include "swift/Basic/Version.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Mutex.h"

#include <memory>

namespace llvm {
  class raw_pwrite_stream;
  class GlobalVariable;
  class MemoryBuffer;
  class Module;
  class TargetOptions;
  class TargetMachine;
}

namespace swift {
  class GenericSignatureBuilder;
  class ASTContext;
  class CodeCompletionCallbacksFactory;
  class Decl;
  class DeclContext;
  class DiagnosticConsumer;
  class DiagnosticEngine;
  class Evaluator;
  class FileUnit;
  class GeneratedModule;
  class GenericEnvironment;
  class GenericParamList;
  class IRGenOptions;
  class LangOptions;
  class SILOptions;
  class ModuleDecl;
  /// A opaque syntax node created by a \c SyntaxParseAction, whose contents
  /// must be interpreted by the \c SyntaxParseAction which created it.
  /// Requires the two low bits to be 0, so that it can be stored in an
  /// \c llvm::PointerIntPair. This is in particular guaranteed for pointers
  /// to C/C++ objects and for pointers that were generated by Swift and passed
  /// to the compiler via a C API (in particular \c CLibParseActions ).
  typedef const void *OpaqueSyntaxNode;
  class Parser;
  class SerializationOptions;
  class SILOptions;
  class SILModule;
  class SourceFile;
  enum class SourceFileKind;
  class SourceManager;
  class SyntaxParseActions;
  class SyntaxParsingCache;
  struct TBDGenOptions;
  class Token;
  class TopLevelContext;
  class Type;
  class TypeCheckerOptions;
  class TypeRepr;
  class UnifiedStatsReporter;

  namespace Lowering {
    class TypeConverter;
  }

  namespace fine_grained_dependencies {
    class SourceFileDepGraph;
  }

  namespace symbolgraphgen {
    struct SymbolGraphOptions;
  }

  /// @{

  /// \returns true if the declaration should be verified.  This can return
  /// false to decrease the number of declarations we verify in a single
  /// compilation.
  bool shouldVerify(const Decl *D, const ASTContext &Context);

  /// Check that the source file is well-formed, aborting and spewing
  /// errors if not.
  ///
  /// "Well-formed" here means following the invariants of the AST, not that the
  /// code written by the user makes sense.
  void verify(SourceFile &SF);
  void verify(Decl *D);

  /// @}

  /// Finish the code completion.
  void performCodeCompletionSecondPass(SourceFile &SF,
                                       CodeCompletionCallbacksFactory &Factory);

  /// Lex and return a vector of tokens for the given buffer.
  std::vector<Token> tokenize(const LangOptions &LangOpts,
                              const SourceManager &SM, unsigned BufferID,
                              unsigned Offset = 0, unsigned EndOffset = 0,
                              DiagnosticEngine *Diags = nullptr,
                              bool KeepComments = true,
                              bool TokenizeInterpolatedString = true,
                              ArrayRef<Token> SplitTokens = ArrayRef<Token>());

  /// This walks the AST to resolve imports.
  void performImportResolution(SourceFile &SF);

  /// Once type-checking is complete, this instruments code with calls to an
  /// intrinsic that record the expected values of local variables so they can
  /// be compared against the results from the debugger.
  void performDebuggerTestingTransform(SourceFile &SF);

  /// Once type checking is complete, this optionally transforms the ASTs to add
  /// calls to external logging functions.
  ///
  /// \param HighPerformance True if the playground transform should omit
  /// instrumentation that has a high runtime performance impact.
  void performPlaygroundTransform(SourceFile &SF, bool HighPerformance);
  
  /// Once type checking is complete this optionally walks the ASTs to add calls
  /// to externally provided functions that simulate "program counter"-like
  /// debugging events. See the comment at the top of lib/Sema/PCMacro.cpp for a
  /// description of the calls inserted.
  void performPCMacro(SourceFile &SF);

  /// Bind all 'extension' visible from \p SF to the extended nominal.
  void bindExtensions(ModuleDecl &mod);

  /// Once import resolution is complete, this walks the AST to resolve types
  /// and diagnose problems therein.
  void performTypeChecking(SourceFile &SF);

  /// Now that we have type-checked an entire module, perform any type
  /// checking that requires the full module, e.g., Objective-C method
  /// override checking.
  ///
  /// Note that clients still perform this checking file-by-file to
  /// provide a somewhat defined order in which diagnostics should be
  /// emitted.
  void performWholeModuleTypeChecking(SourceFile &SF);

  /// Resolve the given \c TypeRepr to an interface type.
  ///
  /// This is used when dealing with partial source files (e.g. SIL parsing,
  /// code completion).
  ///
  /// \returns A well-formed type on success, or an \c ErrorType.
  Type performTypeResolution(TypeRepr *TyR, ASTContext &Ctx, bool isSILMode,
                             bool isSILType,
                             GenericEnvironment *GenericEnv,
                             GenericParamList *GenericParams,
                             DeclContext *DC, bool ProduceDiagnostics = true);

  /// Expose TypeChecker's handling of GenericParamList to SIL parsing.
  GenericEnvironment *handleSILGenericParams(GenericParamList *genericParams,
                                             DeclContext *DC);

  /// Turn the given module into SIL IR.
  ///
  /// The module must contain source files. The optimizer will assume that the
  /// SIL of all files in the module is present in the SILModule.
  std::unique_ptr<SILModule>
  performASTLowering(ModuleDecl *M, Lowering::TypeConverter &TC,
                     const SILOptions &options);

  /// Turn a source file into SIL IR.
  std::unique_ptr<SILModule>
  performASTLowering(FileUnit &SF, Lowering::TypeConverter &TC,
                     const SILOptions &options);

  using ModuleOrSourceFile = PointerUnion<ModuleDecl *, SourceFile *>;

  /// Serializes a module or single source file to the given output file.
  void
  serialize(ModuleOrSourceFile DC, const SerializationOptions &options,
            const symbolgraphgen::SymbolGraphOptions &symbolGraphOptions,
            const SILModule *M = nullptr,
            const fine_grained_dependencies::SourceFileDepGraph *DG = nullptr);

  /// Serializes a module or single source file to the given output file and
  /// returns back the file's contents as a memory buffer.
  ///
  /// Use this if you intend to immediately load the serialized module, as that
  /// will both avoid extra filesystem traffic and will ensure you read back
  /// exactly what was written.
  void serializeToBuffers(ModuleOrSourceFile DC,
                          const SerializationOptions &opts,
                          std::unique_ptr<llvm::MemoryBuffer> *moduleBuffer,
                          std::unique_ptr<llvm::MemoryBuffer> *moduleDocBuffer,
                          std::unique_ptr<llvm::MemoryBuffer> *moduleSourceInfoBuffer,
                          const SILModule *M = nullptr);

  /// Get the CPU, subtarget feature options, and triple to use when emitting code.
  std::tuple<llvm::TargetOptions, std::string, std::vector<std::string>,
             std::string>
  getIRTargetOptions(const IRGenOptions &Opts, ASTContext &Ctx);

  /// Turn the given Swift module into LLVM IR and return the generated module.
  /// To compile and output the generated code, call \c performLLVM.
  GeneratedModule
  performIRGeneration(ModuleDecl *M, const IRGenOptions &Opts,
                      const TBDGenOptions &TBDOpts,
                      std::unique_ptr<SILModule> SILMod,
                      StringRef ModuleName, const PrimarySpecificPaths &PSPs,
                      ArrayRef<std::string> parallelOutputFilenames,
                      llvm::GlobalVariable **outModuleHash = nullptr);

  /// Turn the given Swift file into LLVM IR and return the generated module.
  /// To compile and output the generated code, call \c performLLVM.
  GeneratedModule
  performIRGeneration(FileUnit *file, const IRGenOptions &Opts, 
                      const TBDGenOptions &TBDOpts,
                      std::unique_ptr<SILModule> SILMod,
                      StringRef ModuleName, const PrimarySpecificPaths &PSPs,
                      StringRef PrivateDiscriminator,
                      llvm::GlobalVariable **outModuleHash = nullptr);

  /// Given an already created LLVM module, construct a pass pipeline and run
  /// the Swift LLVM Pipeline upon it. This does not cause the module to be
  /// printed, only to be optimized.
  void performLLVMOptimizations(const IRGenOptions &Opts, llvm::Module *Module,
                                llvm::TargetMachine *TargetMachine);

  /// Compiles and writes the given LLVM module into an output stream in the
  /// format specified in the \c IRGenOptions.
  bool compileAndWriteLLVM(llvm::Module *module,
                           llvm::TargetMachine *targetMachine,
                           const IRGenOptions &opts,
                           UnifiedStatsReporter *stats, DiagnosticEngine &diags,
                           llvm::raw_pwrite_stream &out,
                           llvm::sys::Mutex *diagMutex = nullptr);

  /// Wrap a serialized module inside a swift AST section in an object file.
  void createSwiftModuleObjectFile(SILModule &SILMod, StringRef Buffer,
                                   StringRef OutputPath);

  /// Turn the given LLVM module into native code and return true on error.
  bool performLLVM(const IRGenOptions &Opts,
                   ASTContext &Ctx,
                   llvm::Module *Module,
                   StringRef OutputFilename);

  /// Run the LLVM passes. In multi-threaded compilation this will be done for
  /// multiple LLVM modules in parallel.
  /// \param Diags The Diagnostic Engine.
  /// \param DiagMutex in contexts that require parallel codegen, a mutex that the
  ///                  diagnostic engine uses to synchronize emission.
  /// \param HashGlobal used with incremental LLVMCodeGen to know if a module
  ///                   was already compiled, may be null if not desired.
  /// \param Module LLVM module to code gen, required.
  /// \param TargetMachine target of code gen, required.
  /// \param OutputFilename Filename for output.
  bool performLLVM(const IRGenOptions &Opts,
                   DiagnosticEngine &Diags,
                   llvm::sys::Mutex *DiagMutex,
                   llvm::GlobalVariable *HashGlobal,
                   llvm::Module *Module,
                   llvm::TargetMachine *TargetMachine,
                   StringRef OutputFilename,
                   UnifiedStatsReporter *Stats);

  /// Dump YAML describing all fixed-size types imported from the given module.
  bool performDumpTypeInfo(const IRGenOptions &Opts, SILModule &SILMod);

  /// Creates a TargetMachine from the IRGen opts and AST Context.
  std::unique_ptr<llvm::TargetMachine>
  createTargetMachine(const IRGenOptions &Opts, ASTContext &Ctx);

  /// A convenience wrapper for Parser functionality.
  class ParserUnit {
  public:
    ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID,
               const LangOptions &LangOpts, const TypeCheckerOptions &TyOpts,
               const SILOptions &SILOpts, StringRef ModuleName,
               std::shared_ptr<SyntaxParseActions> spActions = nullptr,
               SyntaxParsingCache *SyntaxCache = nullptr);
    ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID);
    ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID,
               unsigned Offset, unsigned EndOffset);

    ~ParserUnit();

    OpaqueSyntaxNode parse();

    Parser &getParser();
    SourceFile &getSourceFile();
    DiagnosticEngine &getDiagnosticEngine();
    const LangOptions &getLangOptions() const;

  private:
    struct Implementation;
    Implementation &Impl;
  };

  /// Register AST-level request functions with the evaluator.
  ///
  /// The ASTContext will automatically call these upon construction.
  void registerAccessRequestFunctions(Evaluator &evaluator);

  /// Register AST-level request functions with the evaluator.
  ///
  /// The ASTContext will automatically call these upon construction.
  void registerNameLookupRequestFunctions(Evaluator &evaluator);

  /// Register Parse-level request functions with the evaluator.
  ///
  /// Clients that form an ASTContext and will perform any parsing queries
  /// using Parse-level logic should call these functions after forming the
  /// ASTContext.
  void registerParseRequestFunctions(Evaluator &evaluator);

  /// Register Sema-level request functions with the evaluator.
  ///
  /// Clients that form an ASTContext and will perform any semantic queries
  /// using Sema-level logic should call these functions after forming the
  /// ASTContext.
  void registerTypeCheckerRequestFunctions(Evaluator &evaluator);

  /// Register SILGen-level request functions with the evaluator.
  ///
  /// Clients that form an ASTContext and will perform any SIL generation
  /// should call this functions after forming the ASTContext.
  void registerSILGenRequestFunctions(Evaluator &evaluator);

  /// Register SILOptimizer-level request functions with the evaluator.
  ///
  /// Clients that form an ASTContext and will perform any SIL optimization
  /// should call this functions after forming the ASTContext.
  void registerSILOptimizerRequestFunctions(Evaluator &evaluator);

  /// Register TBDGen-level request functions with the evaluator.
  ///
  /// Clients that form an ASTContext and will perform any TBD generation
  /// should call this functions after forming the ASTContext.
  void registerTBDGenRequestFunctions(Evaluator &evaluator);

  /// Register IRGen-level request functions with the evaluator.
  ///
  /// Clients that form an ASTContext and will perform any IR generation
  /// should call this functions after forming the ASTContext.
  void registerIRGenRequestFunctions(Evaluator &evaluator);

  /// Register IDE-level request functions with the evaluator.
  ///
  /// The ASTContext will automatically call these upon construction.
  void registerIDERequestFunctions(Evaluator &evaluator);

  /// Register type check request functions for IDE's usage with the evaluator.
  ///
  /// The ASTContext will automatically call these upon construction.
  /// Calling registerIDERequestFunctions will invoke this function as well.
  void registerIDETypeCheckRequestFunctions(Evaluator &evaluator);

  /// Register clang importer request functions with the evaluator.
  ///
  /// Clients that form an ASTContext and import any Clang APIs should call this function
  /// after forming the ASTContext.
  void registerClangImporterRequestFunctions(Evaluator &evaluator);

  /// Register SILOptimizer passes necessary for IRGen.
  void registerIRGenSILTransforms(ASTContext &ctx);

} // end namespace swift

#endif // SWIFT_SUBSYSTEMS_H
