diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -642,6 +642,8 @@ 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 @@ std::optional 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> useRenames_; + // Names that have appeared in an ONLY clause of a USE statement + std::set> useOnly_; Symbol &SetAccess(const SourceName &, Attr attr, Symbol * = nullptr); // A rename in a USE statement: local => use @@ -663,6 +669,22 @@ 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 @@ bool ModuleVisitor::Pre(const parser::Only &x) { std::visit(common::visitors{ [&](const Indirection &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::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 @@ "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 @@ 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()) && !symbol->has() && useNames.count(name) == 0) { @@ -2491,14 +2523,34 @@ 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()}) { + 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()}) { const Symbol &localUltimate{localSymbol.GetUltimate()}; - if (localUltimate == useUltimate) { + if (localUltimate.owner() == useUltimate.owner()) { // use-associating the same symbol again -- ok } else if (localUltimate.has() && useUltimate.has()) { @@ -6064,7 +6116,7 @@ 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 @@ Walk(ompDecls); Walk(compilerDirectives); Walk(useStmts); + ClearUseRenames(); + ClearUseOnly(); Walk(importStmts); Walk(implicitPart); for (const auto &decl : decls) { diff --git a/flang/test/Semantics/modfile41.f90 b/flang/test/Semantics/modfile41.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/modfile41.f90 @@ -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