-
Notifications
You must be signed in to change notification settings - Fork 16k
[flang] Update PUBLIC and PRIVATE accessibility to Fortran 2018 #177596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This patch enforces PUBLIC and PRIVATE accessibility as defined
starting in F2018, and passed initially as paper J3/13-327r3;
the brief description below is from "What's New In Fortran 2018"
by John Reid:
If a module A uses module B, the default accessibility for entities
it accesses from B is that of A. Specifying another accessibility
for each entity is awkward and error prone. It is now possible
for the name of a module to be included in the list of names of
entities made public or private on a public or private statement.
This sets the default for all entities accessed from that module.
In the F2023 standard, this is clause 8.6.1.
|
@llvm/pr-subscribers-flang-semantics Author: tedj (tmjbios) ChangesThis patch enforces PUBLIC and PRIVATE accessibility as defined starting in F2018, and passed initially as paper J3/13-327r3; the brief description below is from "What's New In Fortran 2018" by John Reid: In the F2023 standard, this is clause 8.6.1. Other minor nit: The Standard Support markdown doc had the "Whats new" links for Full diff: https://github.com/llvm/llvm-project/pull/177596.diff 7 Files Affected:
diff --git a/flang/docs/FortranStandardsSupport.md b/flang/docs/FortranStandardsSupport.md
index 97363dbd048a3..1b2a9b63dcf5e 100644
--- a/flang/docs/FortranStandardsSupport.md
+++ b/flang/docs/FortranStandardsSupport.md
@@ -67,6 +67,7 @@ the multi-image execution. The table entries are based on the document [The new
| Feature | Status | Comments |
|------------------------------------------------------------|--------|---------------------------------------------------------|
| Asynchronous communication | P | Syntax is accepted |
+| Default Accessibility | Y | |
| Teams | N | Multi-image/Coarray feature |
| Image failure | P | Multi-image/Coarray feature. stat_failed_image is added |
| Form team statement | N | Multi-image/Coarray feature |
@@ -89,7 +90,7 @@ the multi-image execution. The table entries are based on the document [The new
| Intrinsic function coshape | N | Multi-image/Coarray feature |
## Fortran 2008
-All features except those listed in the following table are supported.
+All features except those listed in the following table are supported. The new features available in Fortran 2008 are summarized in the document [The New Features in Fortran 2008](https://wg5-fortran.org/N1851-N1900/N1891.pdf)
| Feature | Status | Comments |
|------------------------------------------------------------|--------|---------------------------------------------------------|
@@ -98,7 +99,7 @@ All features except those listed in the following table are supported.
| Internal procedure as an actual argument or pointer target | Y | Current implementation requires stack to be executable. See [FAQ](FAQ.md#why-do-i-get-a-warning-or-an-error-about-an-executable-stack) and [Proposal](InternalProcedureTrampolines.md) |
## Fortran 2003
-All features except those listed in the following table are supported.
+All features except those listed in the following table are supported. The new features available in Fortran 2003 are summarized in the document [The New Features of Fortran 2003](https://wg5-fortran.org/N1551-N1600/N1579.pdf)
| Feature | Status | Comments |
|------------------------------------------------------------|--------|---------------------------------------------------------|
diff --git a/flang/include/flang/Semantics/scope.h b/flang/include/flang/Semantics/scope.h
index 16553fe692e79..c48cde86cfef2 100644
--- a/flang/include/flang/Semantics/scope.h
+++ b/flang/include/flang/Semantics/scope.h
@@ -260,6 +260,17 @@ class Scope {
void add_importName(const SourceName &);
+ // When a module name appears in a PUBLIC or PRIVATE <access-stmt>, all
+ // entities accessed from that module inherit the accessibility attribute.
+ std::optional<Attr> GetModuleAccessibility(const Symbol &module) const {
+ auto it = useModuleAccessibility_.find(&module);
+ return it != useModuleAccessibility_.end() ? std::optional{it->second}
+ : std::nullopt;
+ }
+ bool SetModuleAccessibility(const Symbol &module, Attr access) {
+ return useModuleAccessibility_.emplace(&module, access).second;
+ }
+
// These members pertain to instantiations of parameterized derived types.
const DerivedTypeSpec *derivedTypeSpec() const { return derivedTypeSpec_; }
DerivedTypeSpec *derivedTypeSpec() { return derivedTypeSpec_; }
@@ -330,6 +341,9 @@ class Scope {
const DeclTypeSpec &MakeLengthlessType(DeclTypeSpec &&);
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Scope &);
+
+ // <access-stmt> Map for explicit PUBLIC/PRIVATE module USE
+ std::map<const Symbol *, Attr> useModuleAccessibility_;
};
// Inline so that it can be called from Evaluate without a link-time dependency.
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 057fa1db239c1..4f491e225e0eb 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -840,6 +840,14 @@ class ModuleVisitor : public virtual ScopeHandler {
std::set<SourceName> intrinsicUses_;
std::set<SourceName> nonIntrinsicUses_;
+ void ApplyModuleAccessibility(Symbol &symbol) {
+ if (useModuleScope_ && useModuleScope_->symbol()) {
+ if (auto accessibility{
+ currScope().GetModuleAccessibility(*useModuleScope_->symbol())}) {
+ symbol.attrs().set(*accessibility);
+ }
+ }
+ }
Symbol &SetAccess(const SourceName &, Attr attr, Symbol * = nullptr);
// A rename in a USE statement: local => use
struct SymbolRename {
@@ -3866,6 +3874,7 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
localSymbol->implicitAttrs() =
localSymbol->attrs() & Attrs{Attr::ASYNCHRONOUS, Attr::VOLATILE};
localSymbol->flags() = useSymbol.flags();
+ ApplyModuleAccessibility(*localSymbol);
return;
}
}
@@ -4108,6 +4117,8 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
useUltimate.attrs() & ~Attrs{Attr::PUBLIC, Attr::PRIVATE},
UseDetails{localName, useUltimate})};
newSymbol.flags() = useSymbol.flags();
+ // Apply module accessibility if specified
+ ApplyModuleAccessibility(newSymbol);
return;
} else {
for (const auto &ref : useGeneric->specificProcs()) {
@@ -4164,6 +4175,8 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
localSymbol->attrs() =
useSymbol.attrs() & ~Attrs{Attr::PUBLIC, Attr::PRIVATE};
localSymbol->flags() = useSymbol.flags();
+ // Apply module accessibility if specified
+ ApplyModuleAccessibility(*localSymbol);
AddGenericUse(*localGeneric, localName, useUltimate);
// Don't duplicate specific procedures.
std::size_t originalLocalSpecifics{localGeneric->specificProcs().size()};
@@ -4206,6 +4219,7 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
useUltimate.attrs() & ~Attrs{Attr::PUBLIC, Attr::PRIVATE},
std::move(generic))};
newSymbol.flags() = useUltimate.flags();
+ ApplyModuleAccessibility(newSymbol);
auto &newUseGeneric{newSymbol.get<GenericDetails>()};
AddGenericUse(newUseGeneric, localName, useUltimate);
newUseGeneric.AddUse(*localSymbol);
@@ -9575,6 +9589,41 @@ bool ModuleVisitor::Pre(const parser::AccessStmt &x) {
for (const auto &accessId : accessIds) {
GenericSpecInfo info{accessId.v.value()};
auto *symbol{FindInScope(info.symbolName())};
+ // An access-spec may have an access-name (module) in order to set
+ // the default accessibility for everything USE-associated from
+ // that module. The module name won't be in the current scope,
+ // so we need to check the global scope.
+ if (info.kind().IsName()) {
+ const Symbol *moduleSymbol{nullptr};
+ Symbol *resolveSymbol{nullptr};
+ if (symbol) {
+ // Check if symbol in current scope is USE-associated from a module
+ const Symbol &ultimate{symbol->GetUltimate()};
+ if (ultimate.has<ModuleDetails>()) {
+ moduleSymbol = &ultimate;
+ resolveSymbol = symbol;
+ }
+ } else {
+ // Look for a module with this name in the global scope
+ if (auto it{context().globalScope().find(info.symbolName())};
+ it != context().globalScope().end()) {
+ Symbol &globalSymbol{*it->second};
+ if (globalSymbol.has<ModuleDetails>()) {
+ moduleSymbol = &globalSymbol;
+ resolveSymbol = &globalSymbol;
+ }
+ }
+ }
+ if (moduleSymbol) {
+ if (!currScope().SetModuleAccessibility(*moduleSymbol, accessAttr)) {
+ Say(info.symbolName(),
+ "The accessibility of entities from module '%s' has already been specified"_err_en_US,
+ moduleSymbol->name());
+ }
+ info.Resolve(resolveSymbol);
+ continue;
+ }
+ }
if (!symbol && !info.kind().IsName()) {
symbol = &MakeSymbol(info.symbolName(), Attrs{}, GenericDetails{});
}
@@ -9904,6 +9953,29 @@ void ResolveNamesVisitor::FinishSpecificationPart(
if (inInterfaceBlock()) {
FinishNamelists(); // NAMELIST is useless in an interface, but allowed
}
+
+ // Apply deferred module accessibility: F2023: Clause 8.6.1
+ // An <access-spec> with an <access-id> of a module name sets the
+ // accessibility for all entities USE-associated from that module.
+ for (auto &pair : currScope()) {
+ auto &symbol{*pair.second};
+ if (const auto *useDetails{symbol.detailsIf<UseDetails>()}) {
+ // If symbol already has explicit accessibility, skip it
+ if (symbol.attrs().HasAny({Attr::PUBLIC, Attr::PRIVATE})) {
+ continue;
+ }
+ // Find the module that this symbol came from
+ const Symbol &useSymbol{useDetails->symbol()};
+ const Scope *moduleScope{FindModuleContaining(useSymbol.owner())};
+ if (moduleScope && moduleScope->symbol()) {
+ if (auto accessibility{
+ currScope().GetModuleAccessibility(*moduleScope->symbol())}) {
+ symbol.attrs().set(*accessibility);
+ }
+ }
+ }
+ }
+
for (auto &pair : currScope()) {
auto &symbol{*pair.second};
if (inInterfaceBlock()) {
diff --git a/flang/test/Semantics/modfile-accessibility01.f90 b/flang/test/Semantics/modfile-accessibility01.f90
new file mode 100644
index 0000000000000..ae7b0fba62701
--- /dev/null
+++ b/flang/test/Semantics/modfile-accessibility01.f90
@@ -0,0 +1,27 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! Test that entities from a module with PRIVATE modulename are not accessible from further USE
+module basemod
+ implicit none
+ type :: base_type
+ integer :: x
+ end type
+ integer :: base_var = 42
+contains
+ subroutine base_sub()
+ end subroutine
+end module
+
+module middlemod
+ use basemod
+ implicit none
+ private basemod ! Make all entities from basemod private
+ integer :: middle_var = 100
+end module
+
+program main
+ ! ERROR: 'base_var' is PRIVATE in 'middlemod'
+ use middlemod, only: base_var
+ ! ERROR: 'base_sub' is PRIVATE in 'middlemod'
+ use middlemod, only: base_sub
+ implicit none
+end
diff --git a/flang/test/Semantics/modfile-accessibility02.f90 b/flang/test/Semantics/modfile-accessibility02.f90
new file mode 100644
index 0000000000000..8361fd9434a29
--- /dev/null
+++ b/flang/test/Semantics/modfile-accessibility02.f90
@@ -0,0 +1,26 @@
+! RUN: %flang_fc1 -fsyntax-only %s
+! Test that entities from a module with PUBLIC modulename remain accessible
+module basemod
+ implicit none
+ private !-- Default is private
+ integer, public :: base_public_var = 42
+ integer :: base_private_var = 99
+end module
+
+module middlemod
+ use basemod
+ implicit none
+ private !-- Default is private
+ public basemod !-- But entities from basemod should remain public
+ integer :: middle_private_var = 100
+end module
+
+program main
+ use middlemod
+ implicit none
+ integer :: x
+ ! base_public_var should be accessible because of "public basemod"
+ x = base_public_var
+ ! This should compile without errors
+ print *, x
+end program
diff --git a/flang/test/Semantics/modfile-accessibility03.f90 b/flang/test/Semantics/modfile-accessibility03.f90
new file mode 100644
index 0000000000000..b35763cadf356
--- /dev/null
+++ b/flang/test/Semantics/modfile-accessibility03.f90
@@ -0,0 +1,14 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! Test error when same module is given conflicting accessibility
+module basemod
+ implicit none
+ integer :: base_var = 42
+end module
+
+module testmod
+ use basemod
+ implicit none
+ private basemod
+ !ERROR: The accessibility of entities from module 'basemod' has already been specified
+ public basemod
+end module
diff --git a/flang/test/Semantics/modfile-accessibility04.f90 b/flang/test/Semantics/modfile-accessibility04.f90
new file mode 100644
index 0000000000000..4fc02156b9a00
--- /dev/null
+++ b/flang/test/Semantics/modfile-accessibility04.f90
@@ -0,0 +1,25 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! Test that explicit PUBLIC/PRIVATE on individual symbols overrides module-level accessibility
+module basemod
+ implicit none
+ integer :: var1 = 1
+ integer :: var2 = 2
+ integer :: var3 = 3
+end module
+
+module middlemod
+ use basemod
+ implicit none
+ private basemod ! Make all entities from basemod private by default
+ public :: var2 ! But explicitly make var2 public
+end module
+
+program main
+ ! var2 should be accessible because of explicit "public :: var2"
+ use middlemod, only: var2
+ ! ERROR: 'var1' is PRIVATE in 'middlemod'
+ use middlemod, only: var1
+ ! ERROR: 'var3' is PRIVATE in 'middlemod'
+ use middlemod, only: var3
+ implicit none
+end
|
|
This passes check-flang and check-flang-rt, but gfortran does not implement this new 2018 feature, so there is a failure in one of llvm-test-suite's gfortran tests (public_private_module.f90). I have a PR ready for disabling this now-invalid test in llvm-test-suite once this is merged. |
| const Symbol *moduleSymbol{nullptr}; | ||
| Symbol *resolveSymbol{nullptr}; | ||
| if (symbol) { | ||
| // Check if symbol in current scope is USE-associated from a module |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this handle the following 3-level module use:
module A
integer :: x = 1
end module A
module B
use A
public :: x ! Re-export x
end module B
module C
use B
implicit none
private ! Everything is private, except what's re-exported from B below
public :: B
end module C
program main
use C
implicit none
print *, x
end program
(This program works with ifx.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I believe that's one of the test cases.
$ cat eugene.f90
module A
integer :: x = 1
end module A
module B
use A
public :: x ! Re-export x
end module B
module C
use B
implicit none
private
public :: B
end module C
program main
use C
implicit none
print *, x
end program
$ flang eugene.f90
$ ./a.out
1
$
This patch enforces PUBLIC and PRIVATE accessibility as defined starting in F2018, and passed initially as paper J3/13-327r3; the brief description below is from "What's New In Fortran 2018" by John Reid:
In the F2023 standard, this is clause 8.6.1.
Other minor nit: The Standard Support markdown doc had the "Whats new" links for
'23 and '18, but not for '08 or '03, so I added them when adding Default Accessibility to
the '18 table.