Index: flang/docs/Extensions.md =================================================================== --- flang/docs/Extensions.md +++ flang/docs/Extensions.md @@ -540,6 +540,13 @@ 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 Index: flang/lib/Semantics/resolve-names.cpp =================================================================== --- flang/lib/Semantics/resolve-names.cpp +++ flang/lib/Semantics/resolve-names.cpp @@ -1484,6 +1484,7 @@ 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 &); @@ -7711,6 +7712,102 @@ 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 { Index: flang/test/Semantics/modfile53.f90 =================================================================== --- /dev/null +++ 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 Index: flang/test/Semantics/modfile54.f90 =================================================================== --- /dev/null +++ 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