[flang][Semantics] Ensure deterministic mod file output (#128655)

This PR adds a test to ensure deterministic ordering in `.mod` files. It
also includes related changes to prevent non-deterministic symbol
ordering caused by pointers outside the cooked source. This issue is
particularly noticeable when using Flang as a library and compiling the
same files multiple times.
This commit is contained in:
Iñaki Amatria Barral 2025-02-26 18:19:02 +01:00 committed by GitHub
parent 7717a549e9
commit 722c7c0b0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 43 additions and 19 deletions

View File

@ -836,18 +836,6 @@ void ModFileWriter::PutUseExtraAttr(
}
}
static inline SourceName NameInModuleFile(const Symbol &symbol) {
if (const auto *use{symbol.detailsIf<UseDetails>()}) {
if (use->symbol().attrs().test(Attr::PRIVATE)) {
// Avoid the use in sorting of names created to access private
// specific procedures as a result of generic resolution;
// they're not in the cooked source.
return use->symbol().name();
}
}
return symbol.name();
}
// Collect the symbols of this scope sorted by their original order, not name.
// Generics and namelists are exceptions: they are sorted after other symbols.
void CollectSymbols(const Scope &scope, SymbolVector &sorted,
@ -882,13 +870,8 @@ void CollectSymbols(const Scope &scope, SymbolVector &sorted,
sorted.push_back(symbol);
}
}
// Sort most symbols by name: use of Symbol::ReplaceName ensures the source
// location of a symbol's name is the first "real" use.
auto sorter{[](SymbolRef x, SymbolRef y) {
return NameInModuleFile(*x).begin() < NameInModuleFile(*y).begin();
}};
std::sort(sorted.begin(), sorted.end(), sorter);
std::sort(generics.begin(), generics.end(), sorter);
std::sort(sorted.begin(), sorted.end(), SymbolSourcePositionCompare{});
std::sort(generics.begin(), generics.end(), SymbolSourcePositionCompare{});
sorted.insert(sorted.end(), generics.begin(), generics.end());
sorted.insert(sorted.end(), namelist.begin(), namelist.end());
for (const auto &pair : scope.commonBlocks()) {

View File

@ -0,0 +1,13 @@
module foo
interface do_foo
procedure do_foo_impl
end interface
interface do_bar
procedure do_bar_impl
end interface
contains
subroutine do_foo_impl()
end
subroutine do_bar_impl()
end
end

View File

@ -0,0 +1,28 @@
! This test verifies that both invocations produce a consistent order in the
! generated `.mod` file. Previous versions of Flang exhibited non-deterministic
! behavior due to pointers outside the cooked source being used to order symbols
! in the `.mod` file.
! RUN: rm -rf %t && mkdir -p %t
! RUN: %flang_fc1 -fsyntax-only -J%t %S/Inputs/modfile72.f90
! RUN: %flang_fc1 -fsyntax-only -J%t %s
! RUN: cat %t/bar.mod | FileCheck %s
! RUN: rm -rf %t && mkdir -p %t
! RUN: %flang_fc1 -fsyntax-only -J%t %S/Inputs/modfile72.f90 %s
! RUN: cat %t/bar.mod | FileCheck %s
module bar
use foo, only : do_foo
use foo, only : do_bar
contains
subroutine do_baz()
call do_foo()
call do_bar()
end
end
! CHECK: use foo,only:do_foo
! CHECK-NEXT: use foo,only:do_bar
! CHECK-NEXT: use foo,only:foo$foo$do_bar_impl=>do_bar_impl
! CHECK-NEXT: use foo,only:foo$foo$do_foo_impl=>do_foo_impl