1 //===--- FrontendAction.cpp -----------------------------------------------===// 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 #include "flang/Frontend/FrontendAction.h" 14 #include "flang/Frontend/CompilerInstance.h" 15 #include "flang/Frontend/FrontendActions.h" 16 #include "flang/Frontend/FrontendOptions.h" 17 #include "flang/Frontend/FrontendPluginRegistry.h" 18 #include "clang/Basic/DiagnosticFrontend.h" 19 #include "llvm/Support/Errc.h" 20 #include "llvm/Support/VirtualFileSystem.h" 21 22 using namespace Fortran::frontend; 23 24 LLVM_INSTANTIATE_REGISTRY(FrontendPluginRegistry) 25 26 void FrontendAction::setCurrentInput(const FrontendInputFile &input) { 27 this->currentInput = input; 28 } 29 30 // Call this method if BeginSourceFile fails. 31 // Deallocate compiler instance, input and output descriptors 32 static void beginSourceFileCleanUp(FrontendAction &fa, CompilerInstance &ci) { 33 ci.clearOutputFiles(/*EraseFiles=*/true); 34 fa.setCurrentInput(FrontendInputFile()); 35 fa.setInstance(nullptr); 36 } 37 38 bool FrontendAction::beginSourceFile(CompilerInstance &ci, 39 const FrontendInputFile &realInput) { 40 41 FrontendInputFile input(realInput); 42 43 // Return immediately if the input file does not exist or is not a file. Note 44 // that we cannot check this for input from stdin. 45 if (input.getFile() != "-") { 46 if (!llvm::sys::fs::is_regular_file(input.getFile())) { 47 // Create an diagnostic ID to report 48 unsigned diagID; 49 if (llvm::vfs::getRealFileSystem()->exists(input.getFile())) { 50 ci.getDiagnostics().Report(clang::diag::err_fe_error_reading) 51 << input.getFile() << "not a regular file"; 52 diagID = ci.getDiagnostics().getCustomDiagID( 53 clang::DiagnosticsEngine::Error, "%0 is not a regular file"); 54 } else { 55 diagID = ci.getDiagnostics().getCustomDiagID( 56 clang::DiagnosticsEngine::Error, "%0 does not exist"); 57 } 58 59 // Report the diagnostic and return 60 ci.getDiagnostics().Report(diagID) << input.getFile(); 61 beginSourceFileCleanUp(*this, ci); 62 return false; 63 } 64 } 65 66 assert(!instance && "Already processing a source file!"); 67 assert(!realInput.isEmpty() && "Unexpected empty filename!"); 68 setCurrentInput(realInput); 69 setInstance(&ci); 70 71 if (!ci.hasAllSources()) { 72 beginSourceFileCleanUp(*this, ci); 73 return false; 74 } 75 76 auto &invoc = ci.getInvocation(); 77 78 // Include command-line and predefined preprocessor macros. Use either: 79 // * `-cpp/-nocpp`, or 80 // * the file extension (if the user didn't express any preference) 81 // to decide whether to include them or not. 82 if ((invoc.getPreprocessorOpts().macrosFlag == PPMacrosFlag::Include) || 83 (invoc.getPreprocessorOpts().macrosFlag == PPMacrosFlag::Unknown && 84 getCurrentInput().getMustBePreprocessed())) { 85 invoc.setDefaultPredefinitions(); 86 invoc.collectMacroDefinitions(); 87 } 88 89 if (!invoc.getFortranOpts().features.IsEnabled( 90 Fortran::common::LanguageFeature::CUDA)) { 91 // Enable CUDA Fortran if source file is *.cuf/*.CUF and not already 92 // enabled. 93 invoc.getFortranOpts().features.Enable( 94 Fortran::common::LanguageFeature::CUDA, 95 getCurrentInput().getIsCUDAFortran()); 96 } 97 98 // -fpreprocess-include-lines 99 invoc.getFortranOpts().expandIncludeLinesInPreprocessedOutput = 100 invoc.getPreprocessorOpts().preprocessIncludeLines; 101 102 // Decide between fixed and free form (if the user didn't express any 103 // preference, use the file extension to decide) 104 if (invoc.getFrontendOpts().fortranForm == FortranForm::Unknown) { 105 invoc.getFortranOpts().isFixedForm = getCurrentInput().getIsFixedForm(); 106 } 107 108 if (!beginSourceFileAction()) { 109 beginSourceFileCleanUp(*this, ci); 110 return false; 111 } 112 113 return true; 114 } 115 116 bool FrontendAction::shouldEraseOutputFiles() { 117 return getInstance().getDiagnostics().hasErrorOccurred(); 118 } 119 120 llvm::Error FrontendAction::execute() { 121 executeAction(); 122 123 return llvm::Error::success(); 124 } 125 126 void FrontendAction::endSourceFile() { 127 CompilerInstance &ci = getInstance(); 128 129 // Cleanup the output streams, and erase the output files if instructed by the 130 // FrontendAction. 131 ci.clearOutputFiles(/*EraseFiles=*/shouldEraseOutputFiles()); 132 133 setInstance(nullptr); 134 setCurrentInput(FrontendInputFile()); 135 } 136 137 bool FrontendAction::runPrescan() { 138 CompilerInstance &ci = this->getInstance(); 139 std::string currentInputPath{getCurrentFileOrBufferName()}; 140 Fortran::parser::Options parserOptions = ci.getInvocation().getFortranOpts(); 141 142 if (ci.getInvocation().getFrontendOpts().fortranForm == 143 FortranForm::Unknown) { 144 // Switch between fixed and free form format based on the input file 145 // extension. 146 // 147 // Ideally we should have all Fortran options set before entering this 148 // method (i.e. before processing any specific input files). However, we 149 // can't decide between fixed and free form based on the file extension 150 // earlier than this. 151 parserOptions.isFixedForm = getCurrentInput().getIsFixedForm(); 152 } 153 154 // Prescan. In case of failure, report and return. 155 ci.getParsing().Prescan(currentInputPath, parserOptions); 156 157 return !reportFatalScanningErrors(); 158 } 159 160 bool FrontendAction::runParse(bool emitMessages) { 161 CompilerInstance &ci = this->getInstance(); 162 163 // Parse. In case of failure, report and return. 164 ci.getParsing().Parse(llvm::outs()); 165 166 if (reportFatalParsingErrors()) { 167 return false; 168 } 169 170 if (emitMessages) { 171 // Report any non-fatal diagnostics from getParsing now rather than 172 // combining them with messages from semantics. 173 ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources()); 174 } 175 return true; 176 } 177 178 bool FrontendAction::runSemanticChecks() { 179 CompilerInstance &ci = this->getInstance(); 180 std::optional<parser::Program> &parseTree{ci.getParsing().parseTree()}; 181 assert(parseTree && "Cannot run semantic checks without a parse tree!"); 182 183 // Transfer any pending non-fatal messages from parsing to semantics 184 // so that they are merged and all printed in order. 185 auto &semanticsCtx{ci.getSemanticsContext()}; 186 semanticsCtx.messages().Annex(std::move(ci.getParsing().messages())); 187 semanticsCtx.set_debugModuleWriter(ci.getInvocation().getDebugModuleDir()); 188 189 // Prepare semantics 190 ci.setSemantics(std::make_unique<Fortran::semantics::Semantics>(semanticsCtx, 191 *parseTree)); 192 auto &semantics = ci.getSemantics(); 193 semantics.set_hermeticModuleFileOutput( 194 ci.getInvocation().getHermeticModuleFileOutput()); 195 196 // Run semantic checks 197 semantics.Perform(); 198 199 if (reportFatalSemanticErrors()) { 200 return false; 201 } 202 203 // Report the diagnostics from parsing and the semantic checks 204 semantics.EmitMessages(ci.getSemaOutputStream()); 205 206 return true; 207 } 208 209 bool FrontendAction::generateRtTypeTables() { 210 getInstance().setRtTyTables( 211 std::make_unique<Fortran::semantics::RuntimeDerivedTypeTables>( 212 BuildRuntimeDerivedTypeTables(getInstance().getSemanticsContext()))); 213 214 // The runtime derived type information table builder may find additional 215 // semantic errors. Report them. 216 if (reportFatalSemanticErrors()) { 217 return false; 218 } 219 220 return true; 221 } 222 223 template <unsigned N> 224 bool FrontendAction::reportFatalErrors(const char (&message)[N]) { 225 if (!instance->getParsing().messages().empty() && 226 (instance->getInvocation().getWarnAsErr() || 227 instance->getParsing().messages().AnyFatalError())) { 228 const unsigned diagID = instance->getDiagnostics().getCustomDiagID( 229 clang::DiagnosticsEngine::Error, message); 230 instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName(); 231 instance->getParsing().messages().Emit(llvm::errs(), 232 instance->getAllCookedSources()); 233 return true; 234 } 235 if (instance->getParsing().parseTree().has_value() && 236 !instance->getParsing().consumedWholeFile()) { 237 // Parsing failed without error. 238 const unsigned diagID = instance->getDiagnostics().getCustomDiagID( 239 clang::DiagnosticsEngine::Error, message); 240 instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName(); 241 instance->getParsing().messages().Emit(llvm::errs(), 242 instance->getAllCookedSources()); 243 instance->getParsing().EmitMessage( 244 llvm::errs(), instance->getParsing().finalRestingPlace(), 245 "parser FAIL (final position)", "error: ", llvm::raw_ostream::RED); 246 return true; 247 } 248 return false; 249 } 250 251 bool FrontendAction::reportFatalSemanticErrors() { 252 auto &diags = instance->getDiagnostics(); 253 auto &sema = instance->getSemantics(); 254 255 if (instance->getSemantics().AnyFatalError()) { 256 unsigned diagID = diags.getCustomDiagID(clang::DiagnosticsEngine::Error, 257 "Semantic errors in %0"); 258 diags.Report(diagID) << getCurrentFileOrBufferName(); 259 sema.EmitMessages(instance->getSemaOutputStream()); 260 261 return true; 262 } 263 264 return false; 265 } 266