[flang] Implement reading of module files
When a use-stmt is encountered for a module that isn't in the global scope, search for and read the appropriate `.mod` file. To perform the search, pass the search directories in to ResolveNames. For modules that were read from `.mod` files, we have to keep the cooked source from being deleted so that the names so that references to names stay valid. So we store the cooked source in the Scope of the module as a `unique_ptr`. Add `Symbol::Flag::ModFile` to distinguish module symbols that were read from a `.mod` file rather than from the current compilation. Use it to prevent writing those back out. Fix test_errors.sh to run the compiler in the temp subdirectory -- otherwise tests could be affected by `.mod` files left from previous tests. Original-commit: flang-compiler/f18@207065999c Reviewed-on: https://github.com/flang-compiler/f18/pull/145
This commit is contained in:
parent
9381c34f61
commit
f62f8b655d
|
@ -102,6 +102,11 @@ For PGI, `-I` specifies directories to search for include files and module
|
||||||
files. `-module` specifics a directory to write module files in as well as to
|
files. `-module` specifics a directory to write module files in as well as to
|
||||||
search for them. gfortran is similar except it uses `-J` instead of `-module`.
|
search for them. gfortran is similar except it uses `-J` instead of `-module`.
|
||||||
|
|
||||||
|
The search order for module files is:
|
||||||
|
1. The `-module` directory (Note: for gfortran the `-J` directory is not searched).
|
||||||
|
2. The current directory
|
||||||
|
3. The `-I` directories in the order they appear on the command line
|
||||||
|
|
||||||
### Writing module files
|
### Writing module files
|
||||||
|
|
||||||
When writing a module file, if the existing one matches what would be written,
|
When writing a module file, if the existing one matches what would be written,
|
||||||
|
|
|
@ -15,7 +15,11 @@
|
||||||
#include "mod-file.h"
|
#include "mod-file.h"
|
||||||
#include "scope.h"
|
#include "scope.h"
|
||||||
#include "symbol.h"
|
#include "symbol.h"
|
||||||
|
#include "../parser/grammar.h"
|
||||||
#include "../parser/message.h"
|
#include "../parser/message.h"
|
||||||
|
#include "../parser/openmp-grammar.h"
|
||||||
|
#include "../parser/preprocessor.h"
|
||||||
|
#include "../parser/prescan.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -23,18 +27,28 @@
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Fortran::semantics {
|
namespace Fortran::semantics {
|
||||||
|
|
||||||
using namespace parser::literals;
|
using namespace parser::literals;
|
||||||
|
|
||||||
|
// The extension used for module files.
|
||||||
|
static constexpr auto extension{".mod"};
|
||||||
|
// The initial characters of a file that identify it as a .mod file.
|
||||||
|
static constexpr auto magic{"!mod$"};
|
||||||
|
// Construct the path to a module file.
|
||||||
|
static std::string ModFilePath(const std::string &, const std::string &);
|
||||||
|
// Helpers for creating error messages.
|
||||||
|
static parser::Message Error(
|
||||||
|
const SourceName &, parser::MessageFixedText, const std::string &);
|
||||||
|
static parser::Message Error(const SourceName &, parser::MessageFixedText,
|
||||||
|
const std::string &, const std::string &);
|
||||||
|
|
||||||
class ModFileWriter {
|
class ModFileWriter {
|
||||||
public:
|
public:
|
||||||
// The initial characters of a file that identify it as a .mod file.
|
|
||||||
static constexpr auto magic{"!mod$"};
|
|
||||||
static constexpr auto extension{".mod"};
|
|
||||||
|
|
||||||
// The .mod file format version number.
|
// The .mod file format version number.
|
||||||
void set_version(int version) { version_ = version; }
|
void set_version(int version) { version_ = version; }
|
||||||
// The directory to write .mod files in.
|
// The directory to write .mod files in.
|
||||||
|
@ -103,7 +117,9 @@ bool ModFileWriter::WriteAll() {
|
||||||
for (const auto &scope : Scope::globalScope.children()) {
|
for (const auto &scope : Scope::globalScope.children()) {
|
||||||
if (scope.kind() == Scope::Kind::Module) {
|
if (scope.kind() == Scope::Kind::Module) {
|
||||||
auto &symbol{*scope.symbol()}; // symbol must be present for module
|
auto &symbol{*scope.symbol()}; // symbol must be present for module
|
||||||
WriteOne(symbol);
|
if (!symbol.test(Symbol::Flag::ModFile)) {
|
||||||
|
WriteOne(symbol);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errors_.empty();
|
return errors_.empty();
|
||||||
|
@ -112,7 +128,7 @@ bool ModFileWriter::WriteAll() {
|
||||||
bool ModFileWriter::WriteOne(const Symbol &modSymbol) {
|
bool ModFileWriter::WriteOne(const Symbol &modSymbol) {
|
||||||
CHECK(modSymbol.has<ModuleDetails>());
|
CHECK(modSymbol.has<ModuleDetails>());
|
||||||
auto name{parser::ToLowerCaseLetters(modSymbol.name().ToString())};
|
auto name{parser::ToLowerCaseLetters(modSymbol.name().ToString())};
|
||||||
std::string path{dir_ + '/' + name + extension};
|
std::string path{ModFilePath(dir_, name)};
|
||||||
std::ofstream os{path};
|
std::ofstream os{path};
|
||||||
PutSymbols(*modSymbol.scope());
|
PutSymbols(*modSymbol.scope());
|
||||||
std::string all{GetAsString(name)};
|
std::string all{GetAsString(name)};
|
||||||
|
@ -396,4 +412,103 @@ std::string ModFileWriter::CheckSum(const std::string &str) {
|
||||||
|
|
||||||
void WriteModFiles() { ModFileWriter{}.WriteAll(std::cerr); }
|
void WriteModFiles() { ModFileWriter{}.WriteAll(std::cerr); }
|
||||||
|
|
||||||
|
bool ModFileReader::Read(const SourceName &modName) {
|
||||||
|
auto path{FindModFile(modName)};
|
||||||
|
if (!path.has_value()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Prescan(modName, *path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
parser::ParseState parseState{*cooked_};
|
||||||
|
auto parseTree{parser::program.Parse(parseState)};
|
||||||
|
if (!parseState.messages().empty()) {
|
||||||
|
errors_.emplace_back(modName,
|
||||||
|
parser::MessageFormattedText{
|
||||||
|
"Module file for '%s' is corrupt: %s"_err_en_US,
|
||||||
|
modName.ToString().data(), path->data()});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ResolveNames(*parseTree, *cooked_, directories_);
|
||||||
|
const auto &it{Scope::globalScope.find(modName)};
|
||||||
|
if (it == Scope::globalScope.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto &modSymbol{*it->second};
|
||||||
|
modSymbol.scope()->set_cookedSource(std::move(cooked_));
|
||||||
|
modSymbol.set(Symbol::Flag::ModFile);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for the .mod file for this module in the search directories.
|
||||||
|
// Add to errors_ if not found.
|
||||||
|
std::optional<std::string> ModFileReader::FindModFile(
|
||||||
|
const SourceName &modName) {
|
||||||
|
auto error{Error(modName, "Cannot find module file for '%s'"_err_en_US,
|
||||||
|
modName.ToString())};
|
||||||
|
for (auto &dir : directories_) {
|
||||||
|
std::string path{ModFilePath(dir, modName.ToString())};
|
||||||
|
std::ifstream ifstream{path};
|
||||||
|
if (!ifstream.good()) {
|
||||||
|
error.Attach(Error(
|
||||||
|
modName, "%s: %s"_en_US, path, std::string{std::strerror(errno)}));
|
||||||
|
} else {
|
||||||
|
std::string line;
|
||||||
|
ifstream >> line;
|
||||||
|
if (std::equal(line.begin(), line.end(), std::string{magic}.begin())) {
|
||||||
|
return path; // success
|
||||||
|
}
|
||||||
|
error.Attach(Error(modName, "%s: Not a valid module file"_en_US, path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errors_.push_back(error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModFileReader::Prescan(
|
||||||
|
const SourceName &modName, const std::string &path) {
|
||||||
|
std::stringstream fileError;
|
||||||
|
const auto *sourceFile{allSources_.Open(path, &fileError)};
|
||||||
|
if (sourceFile == nullptr) {
|
||||||
|
errors_.push_back(
|
||||||
|
Error(modName, "Cannot read %s: %s"_err_en_US, path, fileError.str()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
parser::Preprocessor preprocessor{allSources_};
|
||||||
|
parser::Messages messages;
|
||||||
|
parser::Prescanner prescanner{messages, *cooked_, preprocessor, {}};
|
||||||
|
parser::ProvenanceRange range{
|
||||||
|
allSources_.AddIncludedFile(*sourceFile, parser::ProvenanceRange{})};
|
||||||
|
prescanner.Prescan(range);
|
||||||
|
if (!messages.empty()) {
|
||||||
|
errors_.push_back(
|
||||||
|
Error(modName, "Module file for '%s' is corrupt: %s"_err_en_US,
|
||||||
|
modName.ToString(), path));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cooked_->Marshal();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ModFilePath(
|
||||||
|
const std::string &dir, const std::string &modName) {
|
||||||
|
if (dir == "."s) {
|
||||||
|
return modName + extension;
|
||||||
|
} else {
|
||||||
|
return dir + '/' + modName + extension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static parser::Message Error(const SourceName &location,
|
||||||
|
parser::MessageFixedText fixedText, const std::string &arg) {
|
||||||
|
return parser::Message{
|
||||||
|
location, parser::MessageFormattedText{fixedText, arg.data()}};
|
||||||
|
}
|
||||||
|
static parser::Message Error(const SourceName &location,
|
||||||
|
parser::MessageFixedText fixedText, const std::string &arg1,
|
||||||
|
const std::string &arg2) {
|
||||||
|
return parser::Message{location,
|
||||||
|
parser::MessageFormattedText{fixedText, arg1.data(), arg2.data()}};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Fortran::semantics
|
} // namespace Fortran::semantics
|
||||||
|
|
|
@ -15,10 +15,43 @@
|
||||||
#ifndef FORTRAN_SEMANTICS_MOD_FILE_H_
|
#ifndef FORTRAN_SEMANTICS_MOD_FILE_H_
|
||||||
#define FORTRAN_SEMANTICS_MOD_FILE_H_
|
#define FORTRAN_SEMANTICS_MOD_FILE_H_
|
||||||
|
|
||||||
|
#include "resolve-names.h"
|
||||||
|
#include "../parser/char-block.h"
|
||||||
|
#include "../parser/message.h"
|
||||||
|
#include "../parser/parse-tree.h"
|
||||||
|
#include "../parser/parsing.h"
|
||||||
|
#include "../parser/provenance.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace Fortran::semantics {
|
namespace Fortran::semantics {
|
||||||
|
|
||||||
|
using SourceName = parser::CharBlock;
|
||||||
|
|
||||||
void WriteModFiles();
|
void WriteModFiles();
|
||||||
|
|
||||||
|
class ModFileReader {
|
||||||
|
public:
|
||||||
|
// directories specifies where to search for module files
|
||||||
|
ModFileReader(const std::vector<std::string> &directories)
|
||||||
|
: directories_{directories} {}
|
||||||
|
|
||||||
|
// Find and read the module file for modName.
|
||||||
|
// Return true on success; otherwise errors() reports the problems.
|
||||||
|
bool Read(const SourceName &modName);
|
||||||
|
std::list<parser::Message> &errors() { return errors_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::string> directories_;
|
||||||
|
parser::AllSources allSources_;
|
||||||
|
std::unique_ptr<parser::CookedSource> cooked_{
|
||||||
|
std::make_unique<parser::CookedSource>(allSources_)};
|
||||||
|
std::list<parser::Message> errors_;
|
||||||
|
|
||||||
|
std::optional<std::string> FindModFile(const SourceName &);
|
||||||
|
bool Prescan(const SourceName &, const std::string &);
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Fortran::semantics
|
} // namespace Fortran::semantics
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Fortran::semantics {
|
namespace Fortran::semantics {
|
||||||
|
|
||||||
|
@ -387,6 +388,10 @@ public:
|
||||||
bool Pre(const parser::UseStmt &);
|
bool Pre(const parser::UseStmt &);
|
||||||
void Post(const parser::UseStmt &);
|
void Post(const parser::UseStmt &);
|
||||||
|
|
||||||
|
void add_searchDirectory(const std::string &dir) {
|
||||||
|
searchDirectories_.push_back(dir);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The default access spec for this module.
|
// The default access spec for this module.
|
||||||
Attr defaultAccess_{Attr::PUBLIC};
|
Attr defaultAccess_{Attr::PUBLIC};
|
||||||
|
@ -394,9 +399,12 @@ private:
|
||||||
const SourceName *prevAccessStmt_{nullptr};
|
const SourceName *prevAccessStmt_{nullptr};
|
||||||
// The scope of the module during a UseStmt
|
// The scope of the module during a UseStmt
|
||||||
const Scope *useModuleScope_{nullptr};
|
const Scope *useModuleScope_{nullptr};
|
||||||
|
// Directories to search for .mod files
|
||||||
|
std::vector<std::string> searchDirectories_;
|
||||||
|
|
||||||
void SetAccess(const parser::Name &, Attr);
|
void SetAccess(const parser::Name &, Attr);
|
||||||
void ApplyDefaultAccess();
|
void ApplyDefaultAccess();
|
||||||
|
const Scope *FindModule(const SourceName &);
|
||||||
void AddUse(const parser::Rename::Names &);
|
void AddUse(const parser::Rename::Names &);
|
||||||
void AddUse(const parser::Name &);
|
void AddUse(const parser::Name &);
|
||||||
// Record a use from useModuleScope_ of useName as localName. location is
|
// Record a use from useModuleScope_ of useName as localName. location is
|
||||||
|
@ -1223,20 +1231,8 @@ bool ModuleVisitor::Pre(const parser::Rename::Names &x) {
|
||||||
|
|
||||||
// Set useModuleScope_ to the Scope of the module being used.
|
// Set useModuleScope_ to the Scope of the module being used.
|
||||||
bool ModuleVisitor::Pre(const parser::UseStmt &x) {
|
bool ModuleVisitor::Pre(const parser::UseStmt &x) {
|
||||||
// x.nature = UseStmt::ModuleNature::Intrinsic or Non_Intrinsic
|
useModuleScope_ = FindModule(x.moduleName.source);
|
||||||
const auto it{Scope::globalScope.find(x.moduleName.source)};
|
return useModuleScope_ != nullptr;
|
||||||
if (it == Scope::globalScope.end()) {
|
|
||||||
Say(x.moduleName, "Module '%s' not found"_err_en_US);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto *details{it->second->detailsIf<ModuleDetails>()};
|
|
||||||
if (!details) {
|
|
||||||
Say(x.moduleName, "'%s' is not a module"_err_en_US);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
useModuleScope_ = details->scope();
|
|
||||||
CHECK(useModuleScope_);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
void ModuleVisitor::Post(const parser::UseStmt &x) {
|
void ModuleVisitor::Post(const parser::UseStmt &x) {
|
||||||
if (const auto *list{std::get_if<std::list<parser::Rename>>(&x.u)}) {
|
if (const auto *list{std::get_if<std::list<parser::Rename>>(&x.u)}) {
|
||||||
|
@ -1270,6 +1266,30 @@ void ModuleVisitor::Post(const parser::UseStmt &x) {
|
||||||
useModuleScope_ = nullptr;
|
useModuleScope_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find the module with this name and return its scope.
|
||||||
|
// May have to read a .mod file to find it.
|
||||||
|
// Return nullptr on error, after reporting it.
|
||||||
|
const Scope *ModuleVisitor::FindModule(const SourceName &name) {
|
||||||
|
auto it{Scope::globalScope.find(name)};
|
||||||
|
if (it == Scope::globalScope.end()) {
|
||||||
|
ModFileReader reader{searchDirectories_};
|
||||||
|
if (!reader.Read(name)) {
|
||||||
|
for (auto &error : reader.errors()) {
|
||||||
|
Say(std::move(error));
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
it = Scope::globalScope.find(name);
|
||||||
|
CHECK(it != Scope::globalScope.end()); // else would have reported error
|
||||||
|
}
|
||||||
|
const auto *details{it->second->detailsIf<ModuleDetails>()};
|
||||||
|
if (!details) {
|
||||||
|
Say(name, "'%s' is not a module"_err_en_US);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return details->scope();
|
||||||
|
}
|
||||||
|
|
||||||
void ModuleVisitor::AddUse(const parser::Rename::Names &names) {
|
void ModuleVisitor::AddUse(const parser::Rename::Names &names) {
|
||||||
const SourceName &useName{std::get<0>(names.t).source};
|
const SourceName &useName{std::get<0>(names.t).source};
|
||||||
const SourceName &localName{std::get<1>(names.t).source};
|
const SourceName &localName{std::get<1>(names.t).source};
|
||||||
|
@ -1468,7 +1488,7 @@ void InterfaceVisitor::Post(const parser::GenericStmt &x) {
|
||||||
void InterfaceVisitor::AddToGeneric(
|
void InterfaceVisitor::AddToGeneric(
|
||||||
const parser::Name &name, bool expectModuleProc) {
|
const parser::Name &name, bool expectModuleProc) {
|
||||||
genericSymbol_->get<GenericDetails>().add_specificProcName(
|
genericSymbol_->get<GenericDetails>().add_specificProcName(
|
||||||
name.source, expectModuleProc);
|
name.source, expectModuleProc);
|
||||||
}
|
}
|
||||||
void InterfaceVisitor::AddToGeneric(const Symbol &symbol) {
|
void InterfaceVisitor::AddToGeneric(const Symbol &symbol) {
|
||||||
genericSymbol_->get<GenericDetails>().add_specificProc(&symbol);
|
genericSymbol_->get<GenericDetails>().add_specificProc(&symbol);
|
||||||
|
@ -2421,9 +2441,13 @@ void ResolveNamesVisitor::Post(const parser::Program &) {
|
||||||
CHECK(!GetDeclTypeSpec());
|
CHECK(!GetDeclTypeSpec());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResolveNames(
|
void ResolveNames(parser::Program &program,
|
||||||
parser::Program &program, const parser::CookedSource &cookedSource) {
|
const parser::CookedSource &cookedSource,
|
||||||
|
const std::vector<std::string> &searchDirectories) {
|
||||||
ResolveNamesVisitor visitor;
|
ResolveNamesVisitor visitor;
|
||||||
|
for (auto &dir : searchDirectories) {
|
||||||
|
visitor.add_searchDirectory(dir);
|
||||||
|
}
|
||||||
parser::Walk(const_cast<const parser::Program &>(program), visitor);
|
parser::Walk(const_cast<const parser::Program &>(program), visitor);
|
||||||
if (!visitor.messages().empty()) {
|
if (!visitor.messages().empty()) {
|
||||||
visitor.messages().Emit(std::cerr, cookedSource);
|
visitor.messages().Emit(std::cerr, cookedSource);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#define FORTRAN_SEMANTICS_RESOLVE_NAMES_H_
|
#define FORTRAN_SEMANTICS_RESOLVE_NAMES_H_
|
||||||
|
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Fortran::parser {
|
namespace Fortran::parser {
|
||||||
struct Program;
|
struct Program;
|
||||||
|
@ -24,7 +25,8 @@ class CookedSource;
|
||||||
|
|
||||||
namespace Fortran::semantics {
|
namespace Fortran::semantics {
|
||||||
|
|
||||||
void ResolveNames(parser::Program &, const parser::CookedSource &);
|
void ResolveNames(parser::Program &, const parser::CookedSource &,
|
||||||
|
const std::vector<std::string> &);
|
||||||
void DumpSymbols(std::ostream &);
|
void DumpSymbols(std::ostream &);
|
||||||
|
|
||||||
} // namespace Fortran::semantics
|
} // namespace Fortran::semantics
|
||||||
|
|
|
@ -60,8 +60,8 @@ std::ostream &operator<<(std::ostream &os, const Scope &scope) {
|
||||||
}
|
}
|
||||||
os << scope.children_.size() << " children\n";
|
os << scope.children_.size() << " children\n";
|
||||||
for (const auto &pair : scope.symbols_) {
|
for (const auto &pair : scope.symbols_) {
|
||||||
const auto &symbol{pair.second};
|
const auto *symbol{pair.second};
|
||||||
os << " " << symbol << '\n';
|
os << " " << *symbol << '\n';
|
||||||
}
|
}
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,11 @@ public:
|
||||||
|
|
||||||
DerivedTypeSpec &MakeDerivedTypeSpec(const SourceName &);
|
DerivedTypeSpec &MakeDerivedTypeSpec(const SourceName &);
|
||||||
|
|
||||||
|
std::unique_ptr<parser::CookedSource> cooked_;
|
||||||
|
void set_cookedSource(std::unique_ptr<parser::CookedSource> cooked) {
|
||||||
|
cooked_ = std::move(cooked);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Scope &parent_;
|
Scope &parent_;
|
||||||
const Kind kind_;
|
const Kind kind_;
|
||||||
|
|
|
@ -233,7 +233,7 @@ std::string DetailsToString(const Details &);
|
||||||
|
|
||||||
class Symbol {
|
class Symbol {
|
||||||
public:
|
public:
|
||||||
ENUM_CLASS(Flag, Function, Subroutine, Implicit);
|
ENUM_CLASS(Flag, Function, Subroutine, Implicit, ModFile);
|
||||||
using Flags = common::EnumSet<Flag, Flag_enumSize>;
|
using Flags = common::EnumSet<Flag, Flag_enumSize>;
|
||||||
|
|
||||||
const Scope &owner() const { return *owner_; }
|
const Scope &owner() const { return *owner_; }
|
||||||
|
|
|
@ -19,7 +19,7 @@ subroutine sub
|
||||||
end
|
end
|
||||||
|
|
||||||
use m1
|
use m1
|
||||||
!ERROR: Module 'm2' not found
|
!ERROR: Cannot find module file for 'm2'
|
||||||
use m2
|
use m2
|
||||||
!ERROR: 'sub' is not a module
|
!ERROR: 'sub' is not a module
|
||||||
use sub
|
use sub
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
PATH=/usr/bin:/bin
|
PATH=/usr/bin:/bin
|
||||||
srcdir=$(dirname $0)
|
srcdir=$(dirname $0)
|
||||||
CMD="${F18:-../../tools/f18/f18} -fdebug-resolve-names -fparse-only"
|
CMD="${F18:-../../../tools/f18/f18} -fdebug-resolve-names -fparse-only"
|
||||||
|
|
||||||
if [[ $# != 1 ]]; then
|
if [[ $# != 1 ]]; then
|
||||||
echo "Usage: $0 <fortran-source>"
|
echo "Usage: $0 <fortran-source>"
|
||||||
|
@ -38,7 +38,7 @@ expect=$temp/expect
|
||||||
diffs=$temp/diffs
|
diffs=$temp/diffs
|
||||||
|
|
||||||
cmd="$CMD $src"
|
cmd="$CMD $src"
|
||||||
$cmd > $log 2>&1
|
( cd $temp; $cmd ) > $log 2>&1
|
||||||
[[ $? -ge 128 ]] && exit 1
|
[[ $? -ge 128 ]] && exit 1
|
||||||
|
|
||||||
# $actual has errors from the compiler; $expect has them from !ERROR comments in source
|
# $actual has errors from the compiler; $expect has them from !ERROR comments in source
|
||||||
|
|
|
@ -206,7 +206,9 @@ std::string CompileFortran(
|
||||||
}
|
}
|
||||||
if (driver.debugResolveNames || driver.dumpSymbols ||
|
if (driver.debugResolveNames || driver.dumpSymbols ||
|
||||||
driver.dumpUnparseWithSymbols) {
|
driver.dumpUnparseWithSymbols) {
|
||||||
Fortran::semantics::ResolveNames(parseTree, parsing.cooked());
|
std::vector<std::string> directories{options.searchDirectories};
|
||||||
|
directories.insert(directories.begin(), "."s);
|
||||||
|
Fortran::semantics::ResolveNames(parseTree, parsing.cooked(), directories);
|
||||||
Fortran::semantics::WriteModFiles();
|
Fortran::semantics::WriteModFiles();
|
||||||
if (driver.dumpSymbols) {
|
if (driver.dumpSymbols) {
|
||||||
Fortran::semantics::DumpSymbols(std::cout);
|
Fortran::semantics::DumpSymbols(std::cout);
|
||||||
|
@ -452,7 +454,8 @@ int main(int argc, char *const argv[]) {
|
||||||
if (options.isStrictlyStandard) {
|
if (options.isStrictlyStandard) {
|
||||||
options.features.WarnOnAllNonstandard();
|
options.features.WarnOnAllNonstandard();
|
||||||
}
|
}
|
||||||
if (!options.features.IsEnabled(Fortran::parser::LanguageFeature::BackslashEscapes)) {
|
if (!options.features.IsEnabled(
|
||||||
|
Fortran::parser::LanguageFeature::BackslashEscapes)) {
|
||||||
driver.pgf90Args.push_back("-Mbackslash");
|
driver.pgf90Args.push_back("-Mbackslash");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue