[flang] Apply default module accessibility rules a second time (bug#62598)

Apply the default PUBLIC/PRIVATE accessibility of a module to its symbols
a second time after it is known that all symbols, including implicitly typed
names from NAMELIST groups and specification expressions in module subprograms,
have been created in its scope.

Fixes https://github.com/llvm/llvm-project/issues/62598.

Differential Revision: https://reviews.llvm.org/D150307
This commit is contained in:
Peter Klausler
2023-05-10 13:26:01 -07:00
parent ed1539c6ad
commit 689de4c675
5 changed files with 45 additions and 7 deletions

View File

@@ -453,7 +453,7 @@ end
Other Fortran compilers disagree in their interpretations of this example;
some seem to treat the references to `m` as if they were host associations
to an implicitly typed variable (and print `3`), while others seem to
treat them as references to implicitly typed local variabless, and
treat them as references to implicitly typed local variables, and
load uninitialized values.
In f18, we chose to emit an error message for this case since the standard

View File

@@ -54,9 +54,12 @@ public:
const Scope *ancestor() const; // for submodule; nullptr for module
const Scope *parent() const; // for submodule; nullptr for module
void set_scope(const Scope *);
bool isDefaultPrivate() const { return isDefaultPrivate_; }
void set_isDefaultPrivate(bool yes = true) { isDefaultPrivate_ = yes; }
private:
bool isSubmodule_;
bool isDefaultPrivate_{false};
const Scope *scope_{nullptr};
};

View File

@@ -749,8 +749,6 @@ public:
}
private:
// The default access spec for this module.
Attr defaultAccess_{Attr::PUBLIC};
// The location of the last AccessStmt without access-ids, if any.
std::optional<SourceName> prevAccessStmt_;
// The scope of the module during a UseStmt
@@ -3119,7 +3117,6 @@ void ModuleVisitor::BeginModule(const parser::Name &name, bool isSubmodule) {
auto &details{symbol.get<ModuleDetails>()};
PushScope(Scope::Kind::Module, &symbol);
details.set_scope(&currScope());
defaultAccess_ = Attr::PUBLIC;
prevAccessStmt_ = std::nullopt;
}
@@ -3142,10 +3139,15 @@ Scope *ModuleVisitor::FindModule(const parser::Name &name,
}
void ModuleVisitor::ApplyDefaultAccess() {
const auto *moduleDetails{
DEREF(currScope().symbol()).detailsIf<ModuleDetails>()};
CHECK(moduleDetails);
for (auto &pair : currScope()) {
Symbol &symbol = *pair.second;
Symbol &symbol{*pair.second};
if (!symbol.attrs().HasAny({Attr::PUBLIC, Attr::PRIVATE})) {
SetImplicitAttr(symbol, defaultAccess_);
SetImplicitAttr(symbol,
DEREF(moduleDetails).isDefaultPrivate() ? Attr::PRIVATE
: Attr::PUBLIC);
}
}
}
@@ -7319,7 +7321,8 @@ bool ModuleVisitor::Pre(const parser::AccessStmt &x) {
.Attach(*prevAccessStmt_, "Previous declaration"_en_US);
}
prevAccessStmt_ = currStmtSource();
defaultAccess_ = accessAttr;
auto *moduleDetails{DEREF(currScope().symbol()).detailsIf<ModuleDetails>()};
DEREF(moduleDetails).set_isDefaultPrivate(accessAttr == Attr::PRIVATE);
} else {
for (const auto &accessId : accessIds) {
GenericSpecInfo info{accessId.v.value()};
@@ -8232,6 +8235,12 @@ void ResolveNamesVisitor::ResolveExecutionParts(const ProgramTree &node) {
Walk(*exec);
}
FinishNamelists();
if (node.IsModule()) {
// A second final pass to catch new symbols added from implicitly
// typed names in NAMELIST groups or the specification parts of
// module subprograms.
ApplyDefaultAccess();
}
PopScope(); // converts unclassified entities into objects
for (const auto &child : node.children()) {
ResolveExecutionParts(child);

View File

@@ -494,6 +494,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) {
}
os << ")";
}
if (x.isDefaultPrivate()) {
os << " isDefaultPrivate";
}
},
[&](const SubprogramNameDetails &x) {
os << ' ' << EnumToString(x.kind());

View File

@@ -0,0 +1,23 @@
! RUN: %python %S/test_symbols.py %s %flang_fc1
! Regression test for https://github.com/llvm/llvm-project/issues/62598
! Ensure that implicitly typed names in module NAMELIST groups receive
! the module's default accessibility attribute.
!DEF: /m Module
module m
!DEF: /m/a PUBLIC Namelist
!DEF: /m/j PUBLIC (Implicit, InNamelist) ObjectEntity INTEGER(4)
namelist/a/j
end module m
!DEF: /main MainProgram
program main
!DEF: /main/j (Implicit) ObjectEntity INTEGER(4)
j = 1
contains
!DEF: /main/inner (Subroutine) Subprogram
subroutine inner
!REF: /m
use :: m
!DEF: /main/inner/j (Implicit, InNamelist) Use INTEGER(4)
j = 2
end subroutine
end program