[flang] Handle multiple USE statements for the same module

It's possible to have several USE statements for the same module that
have different mixes of rename clauses and ONLY clauses.  The presence
of a rename cause has the effect of hiding a previously associated name,
and the presence of an ONLY clause forces the name to be visible even in
the presence of a rename.

I fixed this by keeping track of the names that appear on rename and ONLY
clauses.  Then, when processing the USE association of a name, I check to see
if it previously appeared in a rename clause and not in a USE clause.  If so, I
remove its USE associated symbol.  Also, when USE associating all of the names
in a module, I do not USE associate names that have appeared in rename clauses.

I also added a test.

Differential Revision: https://reviews.llvm.org/D104130
This commit is contained in:
Peter Steinfeld 2021-06-11 09:28:26 -07:00
parent e0b469ffa1
commit 1b241b9b40
2 changed files with 154 additions and 4 deletions

View File

@ -642,6 +642,8 @@ public:
bool BeginSubmodule(const parser::Name &, const parser::ParentIdentifier &);
void ApplyDefaultAccess();
void AddGenericUse(GenericDetails &, const SourceName &, const Symbol &);
void ClearUseRenames() { useRenames_.clear(); }
void ClearUseOnly() { useOnly_.clear(); }
private:
// The default access spec for this module.
@ -650,6 +652,10 @@ private:
std::optional<SourceName> prevAccessStmt_;
// The scope of the module during a UseStmt
Scope *useModuleScope_{nullptr};
// Names that have appeared in a rename clause of a USE statement
std::set<std::pair<SourceName, Scope *>> useRenames_;
// Names that have appeared in an ONLY clause of a USE statement
std::set<std::pair<SourceName, Scope *>> useOnly_;
Symbol &SetAccess(const SourceName &, Attr attr, Symbol * = nullptr);
// A rename in a USE statement: local => use
@ -663,6 +669,22 @@ private:
void DoAddUse(const SourceName &, const SourceName &, Symbol &localSymbol,
const Symbol &useSymbol);
void AddUse(const GenericSpecInfo &);
// If appropriate, erase a previously USE-associated symbol
void EraseRenamedSymbol(const Symbol &);
// Record a name appearing in a USE rename clause
void AddUseRename(const SourceName &name) {
useRenames_.emplace(std::make_pair(name, useModuleScope_));
}
bool IsUseRenamed(const SourceName &name) const {
return useRenames_.find({name, useModuleScope_}) != useRenames_.end();
}
// Record a name appearing in a USE ONLY clause
void AddUseOnly(const SourceName &name) {
useOnly_.emplace(std::make_pair(name, useModuleScope_));
}
bool IsUseOnly(const SourceName &name) const {
return useOnly_.find({name, useModuleScope_}) != useOnly_.end();
}
Scope *FindModule(const parser::Name &, Scope *ancestor = nullptr);
};
@ -2367,9 +2389,12 @@ void ScopeHandler::MakeExternal(Symbol &symbol) {
bool ModuleVisitor::Pre(const parser::Only &x) {
std::visit(common::visitors{
[&](const Indirection<parser::GenericSpec> &generic) {
AddUse(GenericSpecInfo{generic.value()});
const GenericSpecInfo &genericSpecInfo{generic.value()};
AddUseOnly(genericSpecInfo.symbolName());
AddUse(genericSpecInfo);
},
[&](const parser::Name &name) {
AddUseOnly(name.source);
Resolve(name, AddUse(name.source, name.source).use);
},
[&](const parser::Rename &rename) { Walk(rename); },
@ -2381,7 +2406,11 @@ bool ModuleVisitor::Pre(const parser::Only &x) {
bool ModuleVisitor::Pre(const parser::Rename::Names &x) {
const auto &localName{std::get<0>(x.t)};
const auto &useName{std::get<1>(x.t)};
AddUseRename(useName.source);
SymbolRename rename{AddUse(localName.source, useName.source)};
if (rename.use) {
EraseRenamedSymbol(*rename.use);
}
Resolve(useName, rename.use);
Resolve(localName, rename.local);
return false;
@ -2399,6 +2428,9 @@ bool ModuleVisitor::Pre(const parser::Rename::Operators &x) {
"Logical constant '%s' may not be used as a defined operator"_err_en_US);
} else {
SymbolRename rename{AddUse(localInfo.symbolName(), useInfo.symbolName())};
if (rename.use) {
EraseRenamedSymbol(*rename.use);
}
useInfo.Resolve(rename.use);
localInfo.Resolve(rename.local);
}
@ -2433,7 +2465,7 @@ void ModuleVisitor::Post(const parser::UseStmt &x) {
rename.u);
}
for (const auto &[name, symbol] : *useModuleScope_) {
if (symbol->attrs().test(Attr::PUBLIC) &&
if (symbol->attrs().test(Attr::PUBLIC) && !IsUseRenamed(symbol->name()) &&
(!symbol->attrs().test(Attr::INTRINSIC) ||
symbol->has<UseDetails>()) &&
!symbol->has<MiscDetails>() && useNames.count(name) == 0) {
@ -2491,14 +2523,34 @@ static void ConvertToUseError(
UseErrorDetails{*useDetails}.add_occurrence(location, module));
}
// If a symbol has previously been USE-associated and did not appear in a USE
// ONLY clause, erase it from the current scope. This is needed when a name
// appears in a USE rename clause.
void ModuleVisitor::EraseRenamedSymbol(const Symbol &useSymbol) {
const SourceName &name{useSymbol.name()};
if (const Symbol * symbol{FindInScope(name)}) {
if (auto *useDetails{symbol->detailsIf<UseDetails>()}) {
const Symbol &moduleSymbol{useDetails->symbol()};
if (moduleSymbol.name() == name &&
moduleSymbol.owner() == useSymbol.owner() && IsUseRenamed(name) &&
!IsUseOnly(name)) {
EraseSymbol(*symbol);
}
}
}
}
void ModuleVisitor::DoAddUse(const SourceName &location,
const SourceName &localName, Symbol &localSymbol, const Symbol &useSymbol) {
if (localName != useSymbol.name()) {
EraseRenamedSymbol(useSymbol);
}
localSymbol.attrs() = useSymbol.attrs() & ~Attrs{Attr::PUBLIC, Attr::PRIVATE};
localSymbol.flags() = useSymbol.flags();
const Symbol &useUltimate{useSymbol.GetUltimate()};
if (auto *useDetails{localSymbol.detailsIf<UseDetails>()}) {
const Symbol &localUltimate{localSymbol.GetUltimate()};
if (localUltimate == useUltimate) {
if (localUltimate.owner() == useUltimate.owner()) {
// use-associating the same symbol again -- ok
} else if (localUltimate.has<GenericDetails>() &&
useUltimate.has<GenericDetails>()) {
@ -6064,7 +6116,7 @@ void DeclarationVisitor::NonPointerInitialization(
const parser::Name &name, const parser::ConstantExpr &expr) {
if (name.symbol) {
Symbol &ultimate{name.symbol->GetUltimate()};
if (!context().HasError(ultimate)) {
if (!context().HasError(ultimate) && !context().HasError(name.symbol)) {
if (IsPointer(ultimate)) {
Say(name,
"'%s' is a pointer but is not initialized like one"_err_en_US);
@ -6312,6 +6364,8 @@ bool ResolveNamesVisitor::Pre(const parser::SpecificationPart &x) {
Walk(ompDecls);
Walk(compilerDirectives);
Walk(useStmts);
ClearUseRenames();
ClearUseOnly();
Walk(importStmts);
Walk(implicitPart);
for (const auto &decl : decls) {

View File

@ -0,0 +1,96 @@
! RUN: %S/test_errors.sh %s %t %flang_fc1
! Test USE statements that use same module multiple times mixed with rename
! clauses and ONLY clauses
module m1
integer :: a = 1
integer :: b = 2
end module m1
module m2
integer :: a = 3
end module m2
module m3
integer :: a = 1
type t1
real t1_value
end type
type t2
complex t2_value
end type
end module m3
module m4
use m1
end module m4
module m5
use m1
use m1, z=>a
end module m5
module m6
use m1, only : a
end module m6
program testUse1
use m1
use m1,z=>a ! This prevents the use association of m1's "a" as local "a"
use m2 ! m2's version of "a" gets use associated
!ERROR: 'a' is use-associated from module 'm2' and cannot be re-declared
integer :: a = 2
end
program testUse2
use m1,only : a ! This forces the use association of m1's "a" as local "a"
use m1,z=>a ! This rename doesn't affect the previous forced USE association
!ERROR: 'a' is use-associated from module 'm1' and cannot be re-declared
integer :: a = 2
end
program testUse3
use m1 ! By itself, this would use associate m1's "a" with a local "a"
use m1,z=>a ! This rename of m1'a "a" removes the previous use association
integer :: a = 2
end
program testUse4
use m1,only : a ! Use associate m1's "a" with local "a"
use m1,z=>a ! Also use associate m1's "a" with local "z", also pulls in "b"
!ERROR: 'b' is use-associated from module 'm1' and cannot be re-declared
integer :: b = 2
end
program testUse5
use m1,z=>a ! The rename prevents creation of a local "a"
use m1 ! Does not create a local "a" because of the previous rename
integer :: a = 2
end
program testUse6
use m1, z => a ! Hides m1's "a"
use m1, y => b ! Hides m1's "b"
integer :: a = 4 ! OK
integer :: b = 5 ! OK
end
program testUse7
use m3,t1=>t2,t2=>t1 ! Looks weird but all is good
type(t1) x
type(t2) y
x%t2_value = a
y%t1_value = z
end
program testUse8
use m4 ! This USE associates all of m1
!ERROR: 'a' is use-associated from module 'm4' and cannot be re-declared
integer :: a = 2
end
program testUse9
use m5
integer :: a = 2
end
program testUse10
use m4
use m4, z=>a ! This rename erases the USE assocated "a" from m1
integer :: a = 2
end
program testUse11
use m6
use m6, z=>a ! This rename erases the USE assocated "a" from m1
integer :: a = 2
end
program testUse12
use m4 ! This USE associates "a" from m1
use m1, z=>a ! This renames the "a" from m1, but not the one through m4
!ERROR: 'a' is use-associated from module 'm4' and cannot be re-declared
integer :: a = 2
end