xref: /llvm-project/flang/lib/Frontend/FrontendAction.cpp (revision d1ea605ecd3b98c59e37d8e2261fcb5220151f4d)
14c5906cfSCaroline Concatto //===--- FrontendAction.cpp -----------------------------------------------===//
24c5906cfSCaroline Concatto //
34c5906cfSCaroline Concatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44c5906cfSCaroline Concatto // See https://llvm.org/LICENSE.txt for license information.
54c5906cfSCaroline Concatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64c5906cfSCaroline Concatto //
74c5906cfSCaroline Concatto //===----------------------------------------------------------------------===//
81e462fafSAndrzej Warzynski //
91e462fafSAndrzej Warzynski // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
101e462fafSAndrzej Warzynski //
111e462fafSAndrzej Warzynski //===----------------------------------------------------------------------===//
124c5906cfSCaroline Concatto 
134c5906cfSCaroline Concatto #include "flang/Frontend/FrontendAction.h"
144c5906cfSCaroline Concatto #include "flang/Frontend/CompilerInstance.h"
154c5906cfSCaroline Concatto #include "flang/Frontend/FrontendActions.h"
16cea3abc2SAndrzej Warzynski #include "flang/Frontend/FrontendOptions.h"
17f52fc591SStuart Ellis #include "flang/Frontend/FrontendPluginRegistry.h"
18760e6c4cSAndrzej Warzynski #include "clang/Basic/DiagnosticFrontend.h"
194c5906cfSCaroline Concatto #include "llvm/Support/Errc.h"
20760e6c4cSAndrzej Warzynski #include "llvm/Support/VirtualFileSystem.h"
214c5906cfSCaroline Concatto 
224c5906cfSCaroline Concatto using namespace Fortran::frontend;
234c5906cfSCaroline Concatto 
24f52fc591SStuart Ellis LLVM_INSTANTIATE_REGISTRY(FrontendPluginRegistry)
25f52fc591SStuart Ellis 
261e462fafSAndrzej Warzynski void FrontendAction::setCurrentInput(const FrontendInputFile &input) {
271e462fafSAndrzej Warzynski   this->currentInput = input;
284c5906cfSCaroline Concatto }
294c5906cfSCaroline Concatto 
304c5906cfSCaroline Concatto // Call this method if BeginSourceFile fails.
314c5906cfSCaroline Concatto // Deallocate compiler instance, input and output descriptors
321e462fafSAndrzej Warzynski static void beginSourceFileCleanUp(FrontendAction &fa, CompilerInstance &ci) {
331e462fafSAndrzej Warzynski   ci.clearOutputFiles(/*EraseFiles=*/true);
341e462fafSAndrzej Warzynski   fa.setCurrentInput(FrontendInputFile());
351e462fafSAndrzej Warzynski   fa.setInstance(nullptr);
364c5906cfSCaroline Concatto }
374c5906cfSCaroline Concatto 
381e462fafSAndrzej Warzynski bool FrontendAction::beginSourceFile(CompilerInstance &ci,
391e462fafSAndrzej Warzynski                                      const FrontendInputFile &realInput) {
404c5906cfSCaroline Concatto 
414c5906cfSCaroline Concatto   FrontendInputFile input(realInput);
42760e6c4cSAndrzej Warzynski 
43760e6c4cSAndrzej Warzynski   // Return immediately if the input file does not exist or is not a file. Note
44760e6c4cSAndrzej Warzynski   // that we cannot check this for input from stdin.
451e462fafSAndrzej Warzynski   if (input.getFile() != "-") {
461e462fafSAndrzej Warzynski     if (!llvm::sys::fs::is_regular_file(input.getFile())) {
47760e6c4cSAndrzej Warzynski       // Create an diagnostic ID to report
48760e6c4cSAndrzej Warzynski       unsigned diagID;
491e462fafSAndrzej Warzynski       if (llvm::vfs::getRealFileSystem()->exists(input.getFile())) {
501e462fafSAndrzej Warzynski         ci.getDiagnostics().Report(clang::diag::err_fe_error_reading)
51e495eabdSHans Wennborg             << input.getFile() << "not a regular file";
521e462fafSAndrzej Warzynski         diagID = ci.getDiagnostics().getCustomDiagID(
53760e6c4cSAndrzej Warzynski             clang::DiagnosticsEngine::Error, "%0 is not a regular file");
54760e6c4cSAndrzej Warzynski       } else {
551e462fafSAndrzej Warzynski         diagID = ci.getDiagnostics().getCustomDiagID(
56760e6c4cSAndrzej Warzynski             clang::DiagnosticsEngine::Error, "%0 does not exist");
57760e6c4cSAndrzej Warzynski       }
58760e6c4cSAndrzej Warzynski 
59760e6c4cSAndrzej Warzynski       // Report the diagnostic and return
601e462fafSAndrzej Warzynski       ci.getDiagnostics().Report(diagID) << input.getFile();
611e462fafSAndrzej Warzynski       beginSourceFileCleanUp(*this, ci);
62760e6c4cSAndrzej Warzynski       return false;
63760e6c4cSAndrzej Warzynski     }
64760e6c4cSAndrzej Warzynski   }
65760e6c4cSAndrzej Warzynski 
661e462fafSAndrzej Warzynski   assert(!instance && "Already processing a source file!");
671e462fafSAndrzej Warzynski   assert(!realInput.isEmpty() && "Unexpected empty filename!");
681e462fafSAndrzej Warzynski   setCurrentInput(realInput);
691e462fafSAndrzej Warzynski   setInstance(&ci);
70d06e9403SAndrzej Warzynski 
711e462fafSAndrzej Warzynski   if (!ci.hasAllSources()) {
721e462fafSAndrzej Warzynski     beginSourceFileCleanUp(*this, ci);
734c5906cfSCaroline Concatto     return false;
744c5906cfSCaroline Concatto   }
75d06e9403SAndrzej Warzynski 
761e462fafSAndrzej Warzynski   auto &invoc = ci.getInvocation();
77b83a4450SAndrzej Warzynski 
78b83a4450SAndrzej Warzynski   // Include command-line and predefined preprocessor macros. Use either:
79b83a4450SAndrzej Warzynski   //  * `-cpp/-nocpp`, or
80b83a4450SAndrzej Warzynski   //  * the file extension (if the user didn't express any preference)
81b83a4450SAndrzej Warzynski   // to decide whether to include them or not.
821e462fafSAndrzej Warzynski   if ((invoc.getPreprocessorOpts().macrosFlag == PPMacrosFlag::Include) ||
831e462fafSAndrzej Warzynski       (invoc.getPreprocessorOpts().macrosFlag == PPMacrosFlag::Unknown &&
841e462fafSAndrzej Warzynski        getCurrentInput().getMustBePreprocessed())) {
851e462fafSAndrzej Warzynski     invoc.setDefaultPredefinitions();
861e462fafSAndrzej Warzynski     invoc.collectMacroDefinitions();
87b83a4450SAndrzej Warzynski   }
88b83a4450SAndrzej Warzynski 
898a8ef1caSValentin Clement (バレンタイン クレメン)   if (!invoc.getFortranOpts().features.IsEnabled(
908a8ef1caSValentin Clement (バレンタイン クレメン)           Fortran::common::LanguageFeature::CUDA)) {
918a8ef1caSValentin Clement (バレンタイン クレメン)     // Enable CUDA Fortran if source file is *.cuf/*.CUF and not already
928a8ef1caSValentin Clement (バレンタイン クレメン)     // enabled.
938a8ef1caSValentin Clement (バレンタイン クレメン)     invoc.getFortranOpts().features.Enable(
948a8ef1caSValentin Clement (バレンタイン クレメン)         Fortran::common::LanguageFeature::CUDA,
954ad72793SPeter Klausler         getCurrentInput().getIsCUDAFortran());
968a8ef1caSValentin Clement (バレンタイン クレメン)   }
974ad72793SPeter Klausler 
984dfed691SPeter Klausler   // -fpreprocess-include-lines
994dfed691SPeter Klausler   invoc.getFortranOpts().expandIncludeLinesInPreprocessedOutput =
1004dfed691SPeter Klausler       invoc.getPreprocessorOpts().preprocessIncludeLines;
1014dfed691SPeter Klausler 
102b83a4450SAndrzej Warzynski   // Decide between fixed and free form (if the user didn't express any
103b83a4450SAndrzej Warzynski   // preference, use the file extension to decide)
1041e462fafSAndrzej Warzynski   if (invoc.getFrontendOpts().fortranForm == FortranForm::Unknown) {
1051e462fafSAndrzej Warzynski     invoc.getFortranOpts().isFixedForm = getCurrentInput().getIsFixedForm();
106b83a4450SAndrzej Warzynski   }
107b83a4450SAndrzej Warzynski 
1081e462fafSAndrzej Warzynski   if (!beginSourceFileAction()) {
1091e462fafSAndrzej Warzynski     beginSourceFileCleanUp(*this, ci);
110d06e9403SAndrzej Warzynski     return false;
111d06e9403SAndrzej Warzynski   }
112d06e9403SAndrzej Warzynski 
1134c5906cfSCaroline Concatto   return true;
1144c5906cfSCaroline Concatto }
1154c5906cfSCaroline Concatto 
1161e462fafSAndrzej Warzynski bool FrontendAction::shouldEraseOutputFiles() {
1171e462fafSAndrzej Warzynski   return getInstance().getDiagnostics().hasErrorOccurred();
1184c5906cfSCaroline Concatto }
1194c5906cfSCaroline Concatto 
1201e462fafSAndrzej Warzynski llvm::Error FrontendAction::execute() {
1211e462fafSAndrzej Warzynski   executeAction();
122d28de0d7SCaroline Concatto 
1234c5906cfSCaroline Concatto   return llvm::Error::success();
1244c5906cfSCaroline Concatto }
1254c5906cfSCaroline Concatto 
1261e462fafSAndrzej Warzynski void FrontendAction::endSourceFile() {
1271e462fafSAndrzej Warzynski   CompilerInstance &ci = getInstance();
1284c5906cfSCaroline Concatto 
1294c5906cfSCaroline Concatto   // Cleanup the output streams, and erase the output files if instructed by the
1304c5906cfSCaroline Concatto   // FrontendAction.
1311e462fafSAndrzej Warzynski   ci.clearOutputFiles(/*EraseFiles=*/shouldEraseOutputFiles());
1324c5906cfSCaroline Concatto 
1331e462fafSAndrzej Warzynski   setInstance(nullptr);
1341e462fafSAndrzej Warzynski   setCurrentInput(FrontendInputFile());
1354c5906cfSCaroline Concatto }
1364f21e6aeSAndrzej Warzynski 
1371e462fafSAndrzej Warzynski bool FrontendAction::runPrescan() {
1381e462fafSAndrzej Warzynski   CompilerInstance &ci = this->getInstance();
1391e462fafSAndrzej Warzynski   std::string currentInputPath{getCurrentFileOrBufferName()};
1401e462fafSAndrzej Warzynski   Fortran::parser::Options parserOptions = ci.getInvocation().getFortranOpts();
1414f21e6aeSAndrzej Warzynski 
1421e462fafSAndrzej Warzynski   if (ci.getInvocation().getFrontendOpts().fortranForm ==
1431e462fafSAndrzej Warzynski       FortranForm::Unknown) {
1444f21e6aeSAndrzej Warzynski     // Switch between fixed and free form format based on the input file
1454f21e6aeSAndrzej Warzynski     // extension.
1464f21e6aeSAndrzej Warzynski     //
1474f21e6aeSAndrzej Warzynski     // Ideally we should have all Fortran options set before entering this
1484f21e6aeSAndrzej Warzynski     // method (i.e. before processing any specific input files). However, we
1494f21e6aeSAndrzej Warzynski     // can't decide between fixed and free form based on the file extension
1504f21e6aeSAndrzej Warzynski     // earlier than this.
1511e462fafSAndrzej Warzynski     parserOptions.isFixedForm = getCurrentInput().getIsFixedForm();
1524f21e6aeSAndrzej Warzynski   }
1534f21e6aeSAndrzej Warzynski 
1544f21e6aeSAndrzej Warzynski   // Prescan. In case of failure, report and return.
1551e462fafSAndrzej Warzynski   ci.getParsing().Prescan(currentInputPath, parserOptions);
1564f21e6aeSAndrzej Warzynski 
1574f21e6aeSAndrzej Warzynski   return !reportFatalScanningErrors();
1584f21e6aeSAndrzej Warzynski }
1594f21e6aeSAndrzej Warzynski 
160f2e80893SPeter Klausler bool FrontendAction::runParse(bool emitMessages) {
1611e462fafSAndrzej Warzynski   CompilerInstance &ci = this->getInstance();
1624f21e6aeSAndrzej Warzynski 
1634f21e6aeSAndrzej Warzynski   // Parse. In case of failure, report and return.
1641e462fafSAndrzej Warzynski   ci.getParsing().Parse(llvm::outs());
1654f21e6aeSAndrzej Warzynski 
1664f21e6aeSAndrzej Warzynski   if (reportFatalParsingErrors()) {
1674f21e6aeSAndrzej Warzynski     return false;
1684f21e6aeSAndrzej Warzynski   }
1694f21e6aeSAndrzej Warzynski 
170f2e80893SPeter Klausler   if (emitMessages) {
171f2e80893SPeter Klausler     // Report any non-fatal diagnostics from getParsing now rather than
172f2e80893SPeter Klausler     // combining them with messages from semantics.
1731e462fafSAndrzej Warzynski     ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources());
174f2e80893SPeter Klausler   }
1754f21e6aeSAndrzej Warzynski   return true;
1764f21e6aeSAndrzej Warzynski }
1774f21e6aeSAndrzej Warzynski 
1781e462fafSAndrzej Warzynski bool FrontendAction::runSemanticChecks() {
1791e462fafSAndrzej Warzynski   CompilerInstance &ci = this->getInstance();
1801e462fafSAndrzej Warzynski   std::optional<parser::Program> &parseTree{ci.getParsing().parseTree()};
1814f21e6aeSAndrzej Warzynski   assert(parseTree && "Cannot run semantic checks without a parse tree!");
1824f21e6aeSAndrzej Warzynski 
183f2e80893SPeter Klausler   // Transfer any pending non-fatal messages from parsing to semantics
184f2e80893SPeter Klausler   // so that they are merged and all printed in order.
185f2e80893SPeter Klausler   auto &semanticsCtx{ci.getSemanticsContext()};
186f2e80893SPeter Klausler   semanticsCtx.messages().Annex(std::move(ci.getParsing().messages()));
18765987954SPeter Klausler   semanticsCtx.set_debugModuleWriter(ci.getInvocation().getDebugModuleDir());
188f2e80893SPeter Klausler 
1894f21e6aeSAndrzej Warzynski   // Prepare semantics
19065987954SPeter Klausler   ci.setSemantics(std::make_unique<Fortran::semantics::Semantics>(semanticsCtx,
19165987954SPeter Klausler                                                                   *parseTree));
1921e462fafSAndrzej Warzynski   auto &semantics = ci.getSemantics();
19365987954SPeter Klausler   semantics.set_hermeticModuleFileOutput(
19465987954SPeter Klausler       ci.getInvocation().getHermeticModuleFileOutput());
1954f21e6aeSAndrzej Warzynski 
1964f21e6aeSAndrzej Warzynski   // Run semantic checks
1974f21e6aeSAndrzej Warzynski   semantics.Perform();
1984f21e6aeSAndrzej Warzynski 
1994f21e6aeSAndrzej Warzynski   if (reportFatalSemanticErrors()) {
2004f21e6aeSAndrzej Warzynski     return false;
2014f21e6aeSAndrzej Warzynski   }
2024f21e6aeSAndrzej Warzynski 
203f2e80893SPeter Klausler   // Report the diagnostics from parsing and the semantic checks
2041e462fafSAndrzej Warzynski   semantics.EmitMessages(ci.getSemaOutputStream());
2054f21e6aeSAndrzej Warzynski 
2064f21e6aeSAndrzej Warzynski   return true;
2074f21e6aeSAndrzej Warzynski }
2084f21e6aeSAndrzej Warzynski 
2091e462fafSAndrzej Warzynski bool FrontendAction::generateRtTypeTables() {
2101e462fafSAndrzej Warzynski   getInstance().setRtTyTables(
21116a91a1cSAndrzej Warzynski       std::make_unique<Fortran::semantics::RuntimeDerivedTypeTables>(
212ae4d7ac9SAndrzej Warzyński           BuildRuntimeDerivedTypeTables(getInstance().getSemanticsContext())));
21316a91a1cSAndrzej Warzynski 
21416a91a1cSAndrzej Warzynski   // The runtime derived type information table builder may find additional
21516a91a1cSAndrzej Warzynski   // semantic errors. Report them.
21616a91a1cSAndrzej Warzynski   if (reportFatalSemanticErrors()) {
21716a91a1cSAndrzej Warzynski     return false;
21816a91a1cSAndrzej Warzynski   }
21916a91a1cSAndrzej Warzynski 
22016a91a1cSAndrzej Warzynski   return true;
22116a91a1cSAndrzej Warzynski }
22216a91a1cSAndrzej Warzynski 
2234f21e6aeSAndrzej Warzynski template <unsigned N>
2244f21e6aeSAndrzej Warzynski bool FrontendAction::reportFatalErrors(const char (&message)[N]) {
2251e462fafSAndrzej Warzynski   if (!instance->getParsing().messages().empty() &&
2261e462fafSAndrzej Warzynski       (instance->getInvocation().getWarnAsErr() ||
2271e462fafSAndrzej Warzynski        instance->getParsing().messages().AnyFatalError())) {
2281e462fafSAndrzej Warzynski     const unsigned diagID = instance->getDiagnostics().getCustomDiagID(
2294f21e6aeSAndrzej Warzynski         clang::DiagnosticsEngine::Error, message);
2301e462fafSAndrzej Warzynski     instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();
2311e462fafSAndrzej Warzynski     instance->getParsing().messages().Emit(llvm::errs(),
2321e462fafSAndrzej Warzynski                                            instance->getAllCookedSources());
2334f21e6aeSAndrzej Warzynski     return true;
2344f21e6aeSAndrzej Warzynski   }
235*d1ea605eSPeter Klausler   if (instance->getParsing().parseTree().has_value() &&
236*d1ea605eSPeter Klausler       !instance->getParsing().consumedWholeFile()) {
237*d1ea605eSPeter Klausler     // Parsing failed without error.
238*d1ea605eSPeter Klausler     const unsigned diagID = instance->getDiagnostics().getCustomDiagID(
239*d1ea605eSPeter Klausler         clang::DiagnosticsEngine::Error, message);
240*d1ea605eSPeter Klausler     instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();
241*d1ea605eSPeter Klausler     instance->getParsing().messages().Emit(llvm::errs(),
242*d1ea605eSPeter Klausler                                            instance->getAllCookedSources());
243*d1ea605eSPeter Klausler     instance->getParsing().EmitMessage(
244*d1ea605eSPeter Klausler         llvm::errs(), instance->getParsing().finalRestingPlace(),
245*d1ea605eSPeter Klausler         "parser FAIL (final position)", "error: ", llvm::raw_ostream::RED);
246*d1ea605eSPeter Klausler     return true;
247*d1ea605eSPeter Klausler   }
2484f21e6aeSAndrzej Warzynski   return false;
2494f21e6aeSAndrzej Warzynski }
2504f21e6aeSAndrzej Warzynski 
2514f21e6aeSAndrzej Warzynski bool FrontendAction::reportFatalSemanticErrors() {
2521e462fafSAndrzej Warzynski   auto &diags = instance->getDiagnostics();
2531e462fafSAndrzej Warzynski   auto &sema = instance->getSemantics();
2544f21e6aeSAndrzej Warzynski 
2551e462fafSAndrzej Warzynski   if (instance->getSemantics().AnyFatalError()) {
2561e462fafSAndrzej Warzynski     unsigned diagID = diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
2571e462fafSAndrzej Warzynski                                             "Semantic errors in %0");
2581e462fafSAndrzej Warzynski     diags.Report(diagID) << getCurrentFileOrBufferName();
2591e462fafSAndrzej Warzynski     sema.EmitMessages(instance->getSemaOutputStream());
2604f21e6aeSAndrzej Warzynski 
2614f21e6aeSAndrzej Warzynski     return true;
2624f21e6aeSAndrzej Warzynski   }
2634f21e6aeSAndrzej Warzynski 
2644f21e6aeSAndrzej Warzynski   return false;
2654f21e6aeSAndrzej Warzynski }
266