diff --git a/flang/include/flang/Parser/provenance.h b/flang/include/flang/Parser/provenance.h --- a/flang/include/flang/Parser/provenance.h +++ b/flang/include/flang/Parser/provenance.h @@ -148,9 +148,9 @@ return *this; } - void PushSearchPathDirectory(std::string); - std::string PopSearchPathDirectory(); - const SourceFile *Open(std::string path, llvm::raw_ostream &error); + void AppendSearchPathDirectory(std::string); // new last directory + const SourceFile *Open(std::string path, llvm::raw_ostream &error, + std::optional &&prependPath = std::nullopt); const SourceFile *ReadStandardInput(llvm::raw_ostream &error); ProvenanceRange AddIncludedFile( @@ -210,7 +210,7 @@ ProvenanceRange range_; std::map compilerInsertionProvenance_; std::vector> ownedSourceFiles_; - std::vector searchPath_; + std::list searchPath_; Encoding encoding_{Encoding::UTF_8}; }; diff --git a/flang/include/flang/Parser/source.h b/flang/include/flang/Parser/source.h --- a/flang/include/flang/Parser/source.h +++ b/flang/include/flang/Parser/source.h @@ -17,6 +17,8 @@ #include "characters.h" #include "llvm/Support/MemoryBuffer.h" #include +#include +#include #include #include #include @@ -28,8 +30,8 @@ namespace Fortran::parser { std::string DirectoryName(std::string path); -std::string LocateSourceFile( - std::string name, const std::vector &searchPath); +std::optional LocateSourceFile( + std::string name, const std::list &searchPath); class SourceFile; diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -28,7 +28,7 @@ CompilerInstance &ci = instance(); Fortran::parser::AllSources &allSources{ci.allSources()}; const Fortran::parser::SourceFile *sf; - sf = allSources.Open(path, error_stream); + sf = allSources.Open(path, error_stream, std::optional{"."s}); llvm::ArrayRef fileContent = sf->content(); // Output file descriptor to receive the content of input file. diff --git a/flang/lib/Parser/parsing.cpp b/flang/lib/Parser/parsing.cpp --- a/flang/lib/Parser/parsing.cpp +++ b/flang/lib/Parser/parsing.cpp @@ -25,7 +25,7 @@ AllSources &allSources{allCooked_.allSources()}; if (options.isModuleFile) { for (const auto &path : options.searchDirectories) { - allSources.PushSearchPathDirectory(path); + allSources.AppendSearchPathDirectory(path); } } @@ -35,7 +35,8 @@ if (path == "-") { sourceFile = allSources.ReadStandardInput(fileError); } else { - sourceFile = allSources.Open(path, fileError); + std::optional currentDirectory{"."}; + sourceFile = allSources.Open(path, fileError, std::move(currentDirectory)); } if (!fileError.str().empty()) { ProvenanceRange range{allSources.AddCompilerInsertion(path)}; @@ -46,12 +47,12 @@ if (!options.isModuleFile) { // For .mod files we always want to look in the search directories. - // For normal source files we don't push them until after the primary + // For normal source files we don't add them until after the primary // source file has been opened. If foo.f is missing from the current // working directory, we don't want to accidentally read another foo.f // from another directory that's on the search path. for (const auto &path : options.searchDirectories) { - allSources.PushSearchPathDirectory(path); + allSources.AppendSearchPathDirectory(path); } } diff --git a/flang/lib/Parser/preprocessor.cpp b/flang/lib/Parser/preprocessor.cpp --- a/flang/lib/Parser/preprocessor.cpp +++ b/flang/lib/Parser/preprocessor.cpp @@ -399,6 +399,7 @@ if (j == tokens) { return; } + CHECK(prescanner); // TODO: change to reference if (dir.TokenAt(j).ToString() != "#") { prescanner->Say(dir.GetTokenProvenanceRange(j), "missing '#'"_err_en_US); return; @@ -578,6 +579,7 @@ return; } std::string include; + std::optional prependPath; if (dir.TokenAt(j).ToString() == "<") { // #include std::size_t k{j + 1}; if (k >= tokens) { @@ -598,6 +600,12 @@ } else if ((include = dir.TokenAt(j).ToString()).substr(0, 1) == "\"" && include.substr(include.size() - 1, 1) == "\"") { // #include "foo" include = include.substr(1, include.size() - 2); + // #include "foo" starts search in directory of file containing + // the directive + auto prov{dir.GetTokenProvenanceRange(dirOffset).start()}; + if (const auto *currentFile{allSources_.GetSourceFile(prov)}) { + prependPath = DirectoryName(currentFile->path()); + } } else { prescanner->Say(dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1), "#include: expected name of file to include"_err_en_US); @@ -615,7 +623,8 @@ } std::string buf; llvm::raw_string_ostream error{buf}; - const SourceFile *included{allSources_.Open(include, error)}; + const SourceFile *included{ + allSources_.Open(include, error, std::move(prependPath))}; if (!included) { prescanner->Say(dir.GetTokenProvenanceRange(dirOffset), "#include: %s"_err_en_US, error.str()); diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp --- a/flang/lib/Parser/prescan.cpp +++ b/flang/lib/Parser/prescan.cpp @@ -760,14 +760,12 @@ std::string buf; llvm::raw_string_ostream error{buf}; Provenance provenance{GetProvenance(nextLine_)}; - const SourceFile *currentFile{allSources_.GetSourceFile(provenance)}; - if (currentFile) { - allSources_.PushSearchPathDirectory(DirectoryName(currentFile->path())); - } - const SourceFile *included{allSources_.Open(path, error)}; - if (currentFile) { - allSources_.PopSearchPathDirectory(); + std::optional prependPath; + if (const SourceFile * currentFile{allSources_.GetSourceFile(provenance)}) { + prependPath = DirectoryName(currentFile->path()); } + const SourceFile *included{ + allSources_.Open(path, error, std::move(prependPath))}; if (!included) { Say(provenance, "INCLUDE: %s"_err_en_US, error.str()); } else if (included->bytes() > 0) { diff --git a/flang/lib/Parser/provenance.cpp b/flang/lib/Parser/provenance.cpp --- a/flang/lib/Parser/provenance.cpp +++ b/flang/lib/Parser/provenance.cpp @@ -156,20 +156,28 @@ return origin[origin.covers.MemberOffset(at)]; } -void AllSources::PushSearchPathDirectory(std::string directory) { +void AllSources::AppendSearchPathDirectory(std::string directory) { // gfortran and ifort append to current path, PGI prepends searchPath_.push_back(directory); } -std::string AllSources::PopSearchPathDirectory() { - std::string directory{searchPath_.back()}; - searchPath_.pop_back(); - return directory; -} - -const SourceFile *AllSources::Open(std::string path, llvm::raw_ostream &error) { +const SourceFile *AllSources::Open(std::string path, llvm::raw_ostream &error, + std::optional &&prependPath) { std::unique_ptr source{std::make_unique(encoding_)}; - if (source->Open(LocateSourceFile(path, searchPath_), error)) { + if (prependPath) { + // Set to "." for the initial source file; set to the directory name + // of the including file for #include "quoted-file" directives & + // INCLUDE statements. + searchPath_.emplace_front(std::move(*prependPath)); + } + std::optional found{LocateSourceFile(path, searchPath_)}; + if (prependPath) { + searchPath_.pop_front(); + } + if (!found) { + error << "Source file '" << path << "' was not found"; + return nullptr; + } else if (source->Open(*found, error)) { return ownedSourceFiles_.emplace_back(std::move(source)).get(); } else { return nullptr; diff --git a/flang/lib/Parser/source.cpp b/flang/lib/Parser/source.cpp --- a/flang/lib/Parser/source.cpp +++ b/flang/lib/Parser/source.cpp @@ -56,9 +56,9 @@ return pathBuf.str().str(); } -std::string LocateSourceFile( - std::string name, const std::vector &searchPath) { - if (name.empty() || name == "-" || llvm::sys::path::is_absolute(name)) { +std::optional LocateSourceFile( + std::string name, const std::list &searchPath) { + if (name == "-" || llvm::sys::path::is_absolute(name)) { return name; } for (const std::string &dir : searchPath) { @@ -70,7 +70,7 @@ return path.str().str(); } } - return name; + return std::nullopt; } std::size_t RemoveCarriageReturns(llvm::MutableArrayRef buf) { @@ -123,7 +123,6 @@ bool SourceFile::ReadStandardInput(llvm::raw_ostream &error) { Close(); path_ = "standard input"; - auto buf_or = llvm::MemoryBuffer::getSTDIN(); if (!buf_or) { auto err = buf_or.getError(); @@ -146,7 +145,6 @@ auto tmp_buf{llvm::WritableMemoryBuffer::getNewUninitMemBuffer( content().size() + 1)}; llvm::copy(content(), tmp_buf->getBufferStart()); - Close(); buf_ = std::move(tmp_buf); } buf_end_++; diff --git a/flang/test/Flang-Driver/Inputs/basictestmoduleone.mod b/flang/test/Flang-Driver/Inputs/basictestmoduleone.mod --- a/flang/test/Flang-Driver/Inputs/basictestmoduleone.mod +++ b/flang/test/Flang-Driver/Inputs/basictestmoduleone.mod @@ -1 +1,5 @@ -NOT A REAL MODULE FILE - USE THIS ONLY FOR TESTING THE DRIVER +!mod$ v1 sum:1f5a35ada852dc66 +module basictestmoduleone +type::t1 +end type +end diff --git a/flang/test/Flang-Driver/Inputs/module-dir/basictestmoduleone.mod b/flang/test/Flang-Driver/Inputs/module-dir/basictestmoduleone.mod new file mode 100644 --- /dev/null +++ b/flang/test/Flang-Driver/Inputs/module-dir/basictestmoduleone.mod @@ -0,0 +1,5 @@ +!mod$ v1 sum:449b70509dd4bce3 +module basictestmoduleone +type::t2 +end type +end diff --git a/flang/test/Flang-Driver/Inputs/module-dir/basictestmoduletwo.mod b/flang/test/Flang-Driver/Inputs/module-dir/basictestmoduletwo.mod --- a/flang/test/Flang-Driver/Inputs/module-dir/basictestmoduletwo.mod +++ b/flang/test/Flang-Driver/Inputs/module-dir/basictestmoduletwo.mod @@ -1 +1,3 @@ -NOT A REAL MODULE FILE - USE THIS ONLY FOR TESTING THE DRIVER +!mod$ v1 sum:563b9a1f049282d2 +module basictestmoduletwo +end diff --git a/flang/test/Flang-Driver/include-header.f90 b/flang/test/Flang-Driver/include-header.f90 --- a/flang/test/Flang-Driver/include-header.f90 +++ b/flang/test/Flang-Driver/include-header.f90 @@ -21,7 +21,7 @@ !-------------------------------------------- ! EXPECTED OUTPUT FOR MISSING INCLUDED FILE !-------------------------------------------- -! UNINCLUDED:No such file or directory +! UNINCLUDED:#include: Source file 'basic-header-one.h' was not found ! UNINCLUDED-NOT:program b ! UNINCLUDED-NOT:program c diff --git a/flang/test/Flang-Driver/include-module.f90 b/flang/test/Flang-Driver/include-module.f90 --- a/flang/test/Flang-Driver/include-module.f90 +++ b/flang/test/Flang-Driver/include-module.f90 @@ -1,5 +1,4 @@ ! Ensure argument -I works as expected with module files. -! The module files for this test are not real module files. ! REQUIRES: new-flang-driver @@ -18,15 +17,21 @@ !----------------------------------------- ! EXPECTED OUTPUT FOR MISSING MODULE FILE !----------------------------------------- -! SINGLEINCLUDE:No such file or directory +! SINGLEINCLUDE:Error reading module file for module 'basictestmoduletwo' ! SINGLEINCLUDE-NOT:Error reading module file for module 'basictestmoduletwo' +! SINGLEINCLUDE-NOT:error: Derived type 't1' not found +! SINGLEINCLUDE:error: Derived type 't2' not found !--------------------------------------- ! EXPECTED OUTPUT FOR ALL MODULES FOUND !--------------------------------------- -! INCLUDED-NOT:No such file or directory +! INCLUDED-NOT:Error reading module file +! INCLUDED-NOT:error: Derived type 't1' not found +! INCLUDED:error: Derived type 't2' not found program test_dash_I_with_mod_files USE basictestmoduleone USE basictestmoduletwo + type(t1) :: x1 ! t1 defined in Inputs/basictestmoduleone.mod + type(t2) :: x2 ! t2 defined in Inputs/module-dir/basictestmoduleone.mod end diff --git a/flang/tools/f18/f18.cpp b/flang/tools/f18/f18.cpp --- a/flang/tools/f18/f18.cpp +++ b/flang/tools/f18/f18.cpp @@ -84,7 +84,7 @@ bool verbose{false}; // -v bool compileOnly{false}; // -c std::string outputPath; // -o path - std::vector searchDirectories{"."s}; // -I dir + std::vector searchDirectories; // -I dir std::string moduleDirectory{"."s}; // -module dir std::string moduleFileSuffix{".mod"}; // -moduleSuffix suff bool forcedForm{false}; // -Mfixed or -Mfree appeared diff --git a/flang/unittests/Frontend/FrontendActionTest.cpp b/flang/unittests/Frontend/FrontendActionTest.cpp --- a/flang/unittests/Frontend/FrontendActionTest.cpp +++ b/flang/unittests/Frontend/FrontendActionTest.cpp @@ -157,7 +157,7 @@ EXPECT_TRUE(!outputDiagBuffer.empty()); EXPECT_TRUE( llvm::StringRef(outputDiagBuffer.data()) - .startswith( + .contains( ":1:14: error: IF statement is not allowed in IF statement\n")); } } // namespace