[flang] Merge use-associated generics

When the same generic is use-associated from two different modules,
they must be merged together into a symbol with GenericDetails.

After that merger, if there is a use association of the same name
with a non-generic we have to report an error. So save the UseDetails
from the original USE in GenericDetails so we can create the
appropriate UseErrorDetails.

Fixes flang-compiler/f18#586.

Original-commit: flang-compiler/f18@5067345f70
Reviewed-on: https://github.com/flang-compiler/f18/pull/591
This commit is contained in:
Tim Keith 2019-07-18 16:58:45 -07:00
parent 7d8a0733c3
commit 64a8b9b3d3
4 changed files with 192 additions and 14 deletions

View file

@ -1931,27 +1931,61 @@ ModuleVisitor::SymbolRename ModuleVisitor::AddUse(
return {&localSymbol, useSymbol};
}
// symbol must be either a Use or a Generic formed by merging two uses.
// Convert it to a UseError with this additional location.
static void ConvertToUseError(
Symbol &symbol, const SourceName &location, const Scope &module) {
const auto *useDetails{symbol.detailsIf<UseDetails>()};
if (!useDetails) {
auto &genericDetails{symbol.get<GenericDetails>()};
useDetails = &genericDetails.useDetails().value();
}
symbol.set_details(
UseErrorDetails{*useDetails}.add_occurrence(location, module));
}
void ModuleVisitor::AddUse(
const SourceName &location, Symbol &localSymbol, const Symbol &useSymbol) {
localSymbol.attrs() = useSymbol.attrs();
localSymbol.attrs() &= ~Attrs{Attr::PUBLIC, Attr::PRIVATE};
localSymbol.flags() = useSymbol.flags();
if (auto *details{localSymbol.detailsIf<UseDetails>()}) {
// check for use-associating the same symbol again:
if (localSymbol.GetUltimate() != useSymbol.GetUltimate()) {
localSymbol.set_details(
UseErrorDetails{*details}.add_occurrence(location, *useModuleScope_));
if (auto *useDetails{localSymbol.detailsIf<UseDetails>()}) {
const Symbol &ultimate{localSymbol.GetUltimate()};
if (ultimate == useSymbol.GetUltimate()) {
// use-associating the same symbol again -- ok
} else if (ultimate.has<GenericDetails>() &&
useSymbol.has<GenericDetails>()) {
// use-associating generics with the same names: merge them into a
// new generic in this scope
auto genericDetails{ultimate.get<GenericDetails>()};
genericDetails.set_useDetails(*useDetails);
genericDetails.AddSpecificProcsFrom(useSymbol);
EraseSymbol(localSymbol);
MakeSymbol(
localSymbol.name(), ultimate.attrs(), std::move(genericDetails));
} else {
ConvertToUseError(localSymbol, location, *useModuleScope_);
}
} else if (auto *details{localSymbol.detailsIf<UseErrorDetails>()}) {
details->add_occurrence(location, *useModuleScope_);
} else if (!localSymbol.has<UnknownDetails>()) {
Say(location,
"Cannot use-associate '%s'; it is already declared in this scope"_err_en_US,
localSymbol.name())
.Attach(localSymbol.name(), "Previous declaration of '%s'"_en_US,
localSymbol.name());
} else {
localSymbol.set_details(UseDetails{location, useSymbol});
auto *genericDetails{localSymbol.detailsIf<GenericDetails>()};
if (genericDetails && genericDetails->useDetails().has_value()) {
// localSymbol came from merging two use-associated generics
if (useSymbol.has<GenericDetails>()) {
genericDetails->AddSpecificProcsFrom(useSymbol);
} else {
ConvertToUseError(localSymbol, location, *useModuleScope_);
}
} else if (auto *details{localSymbol.detailsIf<UseErrorDetails>()}) {
details->add_occurrence(location, *useModuleScope_);
} else if (!localSymbol.has<UnknownDetails>()) {
Say(location,
"Cannot use-associate '%s'; it is already declared in this scope"_err_en_US,
localSymbol.name())
.Attach(localSymbol.name(), "Previous declaration of '%s'"_en_US,
localSymbol.name());
} else {
localSymbol.set_details(UseDetails{location, useSymbol});
}
}
}

View file

@ -180,6 +180,11 @@ Symbol *GenericDetails::CheckSpecific() {
}
}
void GenericDetails::AddSpecificProcsFrom(const Symbol &generic) {
const auto &procs{generic.get<GenericDetails>().specificProcs()};
specificProcs_.insert(specificProcs_.end(), procs.begin(), procs.end());
}
// The name of the kind of details for this symbol.
// This is primarily for debugging.
std::string DetailsToString(const Details &details) {

View file

@ -400,6 +400,7 @@ public:
const SymbolVector &specificProcs() const { return specificProcs_; }
void add_specificProc(const Symbol &proc) { specificProcs_.push_back(&proc); }
void AddSpecificProcsFrom(const Symbol &generic);
Symbol *specific() { return specific_; }
const Symbol *specific() const { return specific_; }
@ -415,6 +416,9 @@ public:
const Symbol *CheckSpecific() const;
Symbol *CheckSpecific();
const std::optional<UseDetails> &useDetails() const { return useDetails_; }
void set_useDetails(const UseDetails &details) { useDetails_ = details; }
private:
GenericKind kind_{GenericKind::Name};
// all of the specific procedures for this generic
@ -423,6 +427,9 @@ private:
Symbol *specific_{nullptr};
// a derived type with the same name as this generic, if any
Symbol *derivedType_{nullptr};
// If two USEs of generics were merged to form this one, this is the
// UseDetails for one of them. Used for reporting USE errors.
std::optional<UseDetails> useDetails_;
};
class UnknownDetails {};

View file

@ -44,3 +44,135 @@ contains
subroutine s2(x)
end subroutine
end module
module m4a
interface g
procedure s_real
end interface
contains
subroutine s_real(x)
end
end
module m4b
interface g
procedure s_int
end interface
contains
subroutine s_int(i)
end
end
! Generic g should merge the two use-associated ones
subroutine s4
use m4a
use m4b
call g(123)
call g(1.2)
end
module m5a
interface g
procedure s_real
end interface
contains
subroutine s_real(x)
end
end
module m5b
interface gg
procedure s_int
end interface
contains
subroutine s_int(i)
end
end
! Generic g should merge the two use-associated ones
subroutine s5
use m5a
use m5b, g => gg
call g(123)
call g(1.2)
end
module m6a
interface gg
procedure sa
end interface
contains
subroutine sa(x)
end
end
module m6b
interface gg
procedure sb
end interface
contains
subroutine sb(y)
end
end
subroutine s6
!ERROR: Generic 'g' may not have specific procedures 'sa' and 'sb' as their interfaces are not distinguishable
use m6a, g => gg
use m6b, g => gg
end
module m7a
interface g
procedure s1
end interface
contains
subroutine s1(x)
end
end
module m7b
interface g
procedure s2
end interface
contains
subroutine s2(x, y)
end
end
module m7c
interface g
procedure s3
end interface
contains
subroutine s3(x, y, z)
end
end
! Merge the three use-associated generics
subroutine s7
use m7a
use m7b
use m7c
call g(1.0)
call g(1.0, 2.0)
call g(1.0, 2.0, 3.0)
end
module m8a
interface g
procedure s1
end interface
contains
subroutine s1(x)
end
end
module m8b
interface g
procedure s2
end interface
contains
subroutine s2(x, y)
end
end
module m8c
integer :: g
end
! If merged generic conflicts with another USE, it is an error (if it is referenced)
subroutine s8
use m8a
use m8b
use m8c
!ERROR: Reference to 'g' is ambiguous
g = 1
end