From: Peter Klausler Date: Sun, 5 Feb 2023 01:55:45 +0000 (-0800) Subject: [flang] Handle forward references to modules X-Git-Tag: upstream/17.0.6~17776 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2e4499e749c0e0da71b70247e66f33f7e76991a2;p=platform%2Fupstream%2Fllvm.git [flang] Handle forward references to modules When a USE of a module precedes its definition in the same source file, ensure that the module is processed by name resolution before the USE statement. This prevents the risk of the USE statement using an obsolete module file that is later overwritten during the same compilation. Differential Revision: https://reviews.llvm.org/D143799 --- diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md index 69b3f10..80e2a0c 100644 --- a/flang/docs/Extensions.md +++ b/flang/docs/Extensions.md @@ -545,6 +545,13 @@ end module left-hand side for a pointer assignment statement, and we emit a portability warning when it is not. +* F18 allows a `USE` statement to reference a module that is defined later + in the same compilation unit, so long as mutual dependencies do not form + a cycle. + This feature forestalls any risk of such a `USE` statement reading an + obsolete module file from a previous compilation and then overwriting + that file later. + ## De Facto Standard Features * `EXTENDS_TYPE_OF()` returns `.TRUE.` if both of its arguments have the diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index 77ce3bcc..76dfb52 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -1485,6 +1485,7 @@ public: template void Post(const T &) {} bool Pre(const parser::SpecificationPart &); + bool Pre(const parser::Program &); void Post(const parser::Program &); bool Pre(const parser::ImplicitStmt &); void Post(const parser::PointerObject &); @@ -7737,6 +7738,102 @@ bool ResolveNamesVisitor::Pre(const parser::ProgramUnit &x) { return false; } +template std::set GetUses(const A &x) { + std::set uses; + if constexpr (!std::is_same_v) { + const auto &spec{std::get(x.t)}; + const auto &unitUses{std::get< + std::list>>>( + spec.t)}; + for (const auto &u : unitUses) { + uses.insert(u.statement.value().moduleName.source); + } + } + return uses; +} + +bool ResolveNamesVisitor::Pre(const parser::Program &x) { + std::map modules; + std::set uses; + bool disordered{false}; + for (const auto &progUnit : x.v) { + if (const auto *indMod{ + std::get_if>(&progUnit.u)}) { + const parser::Module &mod{indMod->value()}; + const auto &moduleStmt{ + std::get>(mod.t)}; + const SourceName &name{moduleStmt.statement.v.source}; + if (auto iter{modules.find(name)}; iter != modules.end()) { + Say(name, + "Module '%s' appears multiple times in a compilation unit"_err_en_US) + .Attach(iter->first, "First definition of module"_en_US); + return true; + } + modules.emplace(name, &progUnit); + if (auto iter{uses.find(name)}; iter != uses.end()) { + Say(name, + "A USE statement referencing module '%s' appears earlier in this compilation unit"_port_en_US) + .Attach(*iter, "First USE of module"_en_US); + disordered = true; + } + } + for (SourceName used : common::visit( + [](const auto &indUnit) { return GetUses(indUnit.value()); }, + progUnit.u)) { + uses.insert(used); + } + } + if (!disordered) { + return true; + } + // Process modules in topological order + std::vector moduleOrder; + while (!modules.empty()) { + bool ok; + for (const auto &pair : modules) { + const SourceName &name{pair.first}; + const parser::ProgramUnit &progUnit{*pair.second}; + const parser::Module &m{ + std::get>(progUnit.u).value()}; + ok = true; + for (const SourceName &use : GetUses(m)) { + if (modules.find(use) != modules.end()) { + ok = false; + break; + } + } + if (ok) { + moduleOrder.push_back(&progUnit); + modules.erase(name); + break; + } + } + if (!ok) { + parser::Message *msg{nullptr}; + for (const auto &pair : modules) { + if (msg) { + msg->Attach(pair.first, "Module in a cycle"_en_US); + } else { + msg = &Say(pair.first, + "Some modules in this compilation unit form one or more cycles of dependence"_err_en_US); + } + } + return false; + } + } + // Modules can be ordered. Process them first, and then all of the other + // program units. + for (const parser::ProgramUnit *progUnit : moduleOrder) { + Walk(*progUnit); + } + for (const auto &progUnit : x.v) { + if (!std::get_if>(&progUnit.u)) { + Walk(progUnit); + } + } + return false; +} + // References to procedures need to record that their symbols are known // to be procedures, so that they don't get converted to objects by default. class ExecutionPartSkimmer { diff --git a/flang/test/Semantics/modfile53.f90 b/flang/test/Semantics/modfile53.f90 new file mode 100644 index 0000000..6dda0f7 --- /dev/null +++ b/flang/test/Semantics/modfile53.f90 @@ -0,0 +1,28 @@ +! RUN: %python %S/test_modfile.py %s %flang_fc1 +! Ensure that a module can be forward-referenced within a compilation unit. +module m1 + use m2 +end + +module m2 + use m3 +end + +module m3 + integer n +end + +!Expect: m1.mod +!module m1 +!use m2,only:n +!end + +!Expect: m2.mod +!module m2 +!use m3,only:n +!end + +!Expect: m3.mod +!module m3 +!integer(4)::n +!end diff --git a/flang/test/Semantics/modfile54.f90 b/flang/test/Semantics/modfile54.f90 new file mode 100644 index 0000000..a8efefe12 --- /dev/null +++ b/flang/test/Semantics/modfile54.f90 @@ -0,0 +1,15 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 +!ERROR: Some modules in this compilation unit form one or more cycles of dependence +module m1 + use m2 +end + +!PORTABILITY: A USE statement referencing module 'm2' appears earlier in this compilation unit +module m2 + use m3 +end + +!PORTABILITY: A USE statement referencing module 'm3' appears earlier in this compilation unit +module m3 + use m1 +end