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