1 //===-- CompilerInstance.h - Flang Compiler Instance ------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef FORTRAN_FRONTEND_COMPILERINSTANCE_H
14 #define FORTRAN_FRONTEND_COMPILERINSTANCE_H
15 
16 #include "flang/Frontend/CompilerInvocation.h"
17 #include "flang/Frontend/FrontendAction.h"
18 #include "flang/Frontend/PreprocessorOptions.h"
19 #include "flang/Parser/parsing.h"
20 #include "flang/Parser/provenance.h"
21 #include "flang/Semantics/runtime-type-info.h"
22 #include "flang/Semantics/semantics.h"
23 #include "llvm/Support/raw_ostream.h"
24 
25 namespace Fortran::frontend {
26 
27 /// Helper class for managing a single instance of the Flang compiler.
28 ///
29 /// This class serves two purposes:
30 ///  (1) It manages the various objects which are necessary to run the compiler
31 ///  (2) It provides utility routines for constructing and manipulating the
32 ///      common Flang objects.
33 ///
34 /// The compiler instance generally owns the instance of all the objects that it
35 /// manages. However, clients can still share objects by manually setting the
36 /// object and retaking ownership prior to destroying the CompilerInstance.
37 ///
38 /// The compiler instance is intended to simplify clients, but not to lock them
39 /// in to the compiler instance for everything. When possible, utility functions
40 /// come in two forms; a short form that reuses the CompilerInstance objects,
41 /// and a long form that takes explicit instances of any required objects.
42 class CompilerInstance {
43 
44   /// The options used in this compiler instance.
45   std::shared_ptr<CompilerInvocation> invocation;
46 
47   /// Flang file  manager.
48   std::shared_ptr<Fortran::parser::AllSources> allSources;
49 
50   std::shared_ptr<Fortran::parser::AllCookedSources> allCookedSources;
51 
52   std::shared_ptr<Fortran::parser::Parsing> parsing;
53 
54   std::unique_ptr<Fortran::semantics::Semantics> semantics;
55 
56   std::unique_ptr<Fortran::semantics::RuntimeDerivedTypeTables> rtTyTables;
57 
58   /// The stream for diagnostics from Semantics
59   llvm::raw_ostream *semaOutputStream = &llvm::errs();
60 
61   /// The stream for diagnostics from Semantics if owned, otherwise nullptr.
62   std::unique_ptr<llvm::raw_ostream> ownedSemaOutputStream;
63 
64   /// The diagnostics engine instance.
65   llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics;
66 
67   /// Holds information about the output file.
68   struct OutputFile {
69     std::string filename;
OutputFileOutputFile70     OutputFile(std::string inputFilename)
71         : filename(std::move(inputFilename)) {}
72   };
73 
74   /// The list of active output files.
75   std::list<OutputFile> outputFiles;
76 
77   /// Holds the output stream provided by the user. Normally, users of
78   /// CompilerInstance will call CreateOutputFile to obtain/create an output
79   /// stream. If they want to provide their own output stream, this field will
80   /// facilitate this. It is optional and will normally be just a nullptr.
81   std::unique_ptr<llvm::raw_pwrite_stream> outputStream;
82 
83 public:
84   explicit CompilerInstance();
85 
86   ~CompilerInstance();
87 
88   /// @name Compiler Invocation
89   /// {
90 
getInvocation()91   CompilerInvocation &getInvocation() {
92     assert(invocation && "Compiler instance has no invocation!");
93     return *invocation;
94   };
95 
96   /// Replace the current invocation.
97   void setInvocation(std::shared_ptr<CompilerInvocation> value);
98 
99   /// }
100   /// @name File manager
101   /// {
102 
103   /// Return the current allSources.
getAllSources()104   Fortran::parser::AllSources &getAllSources() const { return *allSources; }
105 
hasAllSources()106   bool hasAllSources() const { return allSources != nullptr; }
107 
getAllCookedSources()108   parser::AllCookedSources &getAllCookedSources() {
109     assert(allCookedSources && "Compiler instance has no AllCookedSources!");
110     return *allCookedSources;
111   };
112 
113   /// }
114   /// @name Parser Operations
115   /// {
116 
117   /// Return parsing to be used by Actions.
getParsing()118   Fortran::parser::Parsing &getParsing() const { return *parsing; }
119 
120   /// }
121   /// @name Semantic analysis
122   /// {
123 
124   /// Replace the current stream for verbose output.
125   void setSemaOutputStream(llvm::raw_ostream &value);
126 
127   /// Replace the current stream for verbose output.
128   void setSemaOutputStream(std::unique_ptr<llvm::raw_ostream> value);
129 
130   /// Get the current stream for verbose output.
getSemaOutputStream()131   llvm::raw_ostream &getSemaOutputStream() { return *semaOutputStream; }
132 
getSemantics()133   Fortran::semantics::Semantics &getSemantics() { return *semantics; }
getSemantics()134   const Fortran::semantics::Semantics &getSemantics() const {
135     return *semantics;
136   }
137 
setSemantics(std::unique_ptr<Fortran::semantics::Semantics> sema)138   void setSemantics(std::unique_ptr<Fortran::semantics::Semantics> sema) {
139     semantics = std::move(sema);
140   }
141 
setRtTyTables(std::unique_ptr<Fortran::semantics::RuntimeDerivedTypeTables> tables)142   void setRtTyTables(
143       std::unique_ptr<Fortran::semantics::RuntimeDerivedTypeTables> tables) {
144     rtTyTables = std::move(tables);
145   }
146 
getRtTyTables()147   Fortran::semantics::RuntimeDerivedTypeTables &getRtTyTables() {
148     assert(rtTyTables && "Missing runtime derived type tables!");
149     return *rtTyTables;
150   }
151 
152   /// }
153   /// @name High-Level Operations
154   /// {
155 
156   /// Execute the provided action against the compiler's
157   /// CompilerInvocation object.
158   /// \param act - The action to execute.
159   /// \return - True on success.
160   bool executeAction(FrontendAction &act);
161 
162   /// }
163   /// @name Forwarding Methods
164   /// {
165 
getDiagnosticOpts()166   clang::DiagnosticOptions &getDiagnosticOpts() {
167     return invocation->getDiagnosticOpts();
168   }
getDiagnosticOpts()169   const clang::DiagnosticOptions &getDiagnosticOpts() const {
170     return invocation->getDiagnosticOpts();
171   }
172 
getFrontendOpts()173   FrontendOptions &getFrontendOpts() { return invocation->getFrontendOpts(); }
getFrontendOpts()174   const FrontendOptions &getFrontendOpts() const {
175     return invocation->getFrontendOpts();
176   }
177 
preprocessorOpts()178   PreprocessorOptions &preprocessorOpts() {
179     return invocation->getPreprocessorOpts();
180   }
preprocessorOpts()181   const PreprocessorOptions &preprocessorOpts() const {
182     return invocation->getPreprocessorOpts();
183   }
184 
185   /// }
186   /// @name Diagnostics Engine
187   /// {
188 
hasDiagnostics()189   bool hasDiagnostics() const { return diagnostics != nullptr; }
190 
191   /// Get the current diagnostics engine.
getDiagnostics()192   clang::DiagnosticsEngine &getDiagnostics() const {
193     assert(diagnostics && "Compiler instance has no diagnostics!");
194     return *diagnostics;
195   }
196 
getDiagnosticClient()197   clang::DiagnosticConsumer &getDiagnosticClient() const {
198     assert(diagnostics && diagnostics->getClient() &&
199            "Compiler instance has no diagnostic client!");
200     return *diagnostics->getClient();
201   }
202 
203   /// {
204   /// @name Output Files
205   /// {
206 
207   /// Clear the output file list.
208   void clearOutputFiles(bool eraseFiles);
209 
210   /// Create the default output file (based on the invocation's options) and
211   /// add it to the list of tracked output files. If the name of the output
212   /// file is not provided, it will be derived from the input file.
213   ///
214   /// \param binary     The mode to open the file in.
215   /// \param baseInput  If the invocation contains no output file name (i.e.
216   ///                   outputFile in FrontendOptions is empty), the input path
217   ///                   name to use for deriving the output path.
218   /// \param extension  The extension to use for output names derived from
219   ///                   \p baseInput.
220   /// \return           Null on error, ostream for the output file otherwise
221   std::unique_ptr<llvm::raw_pwrite_stream>
222   createDefaultOutputFile(bool binary = true, llvm::StringRef baseInput = "",
223                           llvm::StringRef extension = "");
224 
225 private:
226   /// Create a new output file
227   ///
228   /// \param outputPath   The path to the output file.
229   /// \param binary       The mode to open the file in.
230   /// \return             Null on error, ostream for the output file otherwise
231   llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>>
232   createOutputFileImpl(llvm::StringRef outputPath, bool binary);
233 
234 public:
235   /// }
236   /// @name Construction Utility Methods
237   /// {
238 
239   /// Create a DiagnosticsEngine object
240   ///
241   /// If no diagnostic client is provided, this method creates a
242   /// DiagnosticConsumer that is owned by the returned diagnostic object. If
243   /// using directly the caller is responsible for releasing the returned
244   /// DiagnosticsEngine's client eventually.
245   ///
246   /// \param opts - The diagnostic options; note that the created text
247   /// diagnostic object contains a reference to these options.
248   ///
249   /// \param client - If non-NULL, a diagnostic client that will be attached to
250   /// (and optionally, depending on /p shouldOwnClient, owned by) the returned
251   /// DiagnosticsEngine object.
252   ///
253   /// \return The new object on success, or null on failure.
254   static clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine>
255   createDiagnostics(clang::DiagnosticOptions *opts,
256                     clang::DiagnosticConsumer *client = nullptr,
257                     bool shouldOwnClient = true);
258   void createDiagnostics(clang::DiagnosticConsumer *client = nullptr,
259                          bool shouldOwnClient = true);
260 
261   /// }
262   /// @name Output Stream Methods
263   /// {
setOutputStream(std::unique_ptr<llvm::raw_pwrite_stream> outStream)264   void setOutputStream(std::unique_ptr<llvm::raw_pwrite_stream> outStream) {
265     outputStream = std::move(outStream);
266   }
267 
isOutputStreamNull()268   bool isOutputStreamNull() { return (outputStream == nullptr); }
269 
270   // Allow the frontend compiler to write in the output stream.
writeOutputStream(const std::string & message)271   void writeOutputStream(const std::string &message) {
272     *outputStream << message;
273   }
274 
275   /// Get the user specified output stream.
getOutputStream()276   llvm::raw_pwrite_stream &getOutputStream() {
277     assert(outputStream &&
278            "Compiler instance has no user-specified output stream!");
279     return *outputStream;
280   }
281 };
282 
283 } // end namespace Fortran::frontend
284 #endif // FORTRAN_FRONTEND_COMPILERINSTANCE_H
285