Module files emitted by this Fortran compiler are valid Fortran source files. Symbols that are USE-associated into modules are represented in their module files with USE statements and special comments with hash codes in them to ensure that those USE statements resolve to the same modules that were used to build the module when its module file was generated. This scheme prevents unchecked module file growth in large applications by not emitting USE-associated symbols redundantly. This problem can be especially bad when derived type definitions must be repeated in the module files of their clients, and the clients of those modules, and so on. However, this scheme has the disadvantage that clients of modules must be compiled with dependent modules in the module search path. This new -fhermetic-module-files option causes module file output to be free of dependences on any non-intrinsic module files; dependent modules are instead emitted as part of the module file, rather than being USE-associated. It is intended for top level library module files that are shipped with binary libraries when it is not convenient to collect and ship their dependent module files as well. Fixes https://github.com/llvm/llvm-project/issues/97398.
249 lines
8.4 KiB
C++
249 lines
8.4 KiB
C++
//===--- 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();
|
|
}
|
|
|
|
if (!invoc.getFortranOpts().features.IsEnabled(
|
|
Fortran::common::LanguageFeature::CUDA)) {
|
|
// Enable CUDA Fortran if source file is *.cuf/*.CUF and not already
|
|
// enabled.
|
|
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(bool emitMessages) {
|
|
CompilerInstance &ci = this->getInstance();
|
|
|
|
// Parse. In case of failure, report and return.
|
|
ci.getParsing().Parse(llvm::outs());
|
|
|
|
if (reportFatalParsingErrors()) {
|
|
return false;
|
|
}
|
|
|
|
if (emitMessages) {
|
|
// Report any non-fatal diagnostics from getParsing now rather than
|
|
// combining them with messages from semantics.
|
|
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!");
|
|
|
|
// Transfer any pending non-fatal messages from parsing to semantics
|
|
// so that they are merged and all printed in order.
|
|
auto &semanticsCtx{ci.getSemanticsContext()};
|
|
semanticsCtx.messages().Annex(std::move(ci.getParsing().messages()));
|
|
semanticsCtx.set_debugModuleWriter(ci.getInvocation().getDebugModuleDir());
|
|
|
|
// Prepare semantics
|
|
ci.setSemantics(std::make_unique<Fortran::semantics::Semantics>(semanticsCtx,
|
|
*parseTree));
|
|
auto &semantics = ci.getSemantics();
|
|
semantics.set_hermeticModuleFileOutput(
|
|
ci.getInvocation().getHermeticModuleFileOutput());
|
|
|
|
// Run semantic checks
|
|
semantics.Perform();
|
|
|
|
if (reportFatalSemanticErrors()) {
|
|
return false;
|
|
}
|
|
|
|
// Report the diagnostics from parsing and the semantic checks
|
|
semantics.EmitMessages(ci.getSemaOutputStream());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FrontendAction::generateRtTypeTables() {
|
|
getInstance().setRtTyTables(
|
|
std::make_unique<Fortran::semantics::RuntimeDerivedTypeTables>(
|
|
BuildRuntimeDerivedTypeTables(getInstance().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;
|
|
}
|