[flang] Local generics must not shadow host-associated generics

It is possible for generic interfaces of equivalent (but not necessarily
identical -- operator(.eq.) is equivalent to operator(==)) names to
be declared in a host scope and a nested scope, and the nested declaration
should function as an extension of the host's.

Differential Revision: https://reviews.llvm.org/D123719
This commit is contained in:
Peter Klausler 2022-04-13 10:27:36 -07:00
parent 8065e48218
commit 95199af4ae
2 changed files with 113 additions and 11 deletions

View File

@ -6888,31 +6888,45 @@ void ResolveNamesVisitor::CreateCommonBlockSymbols(
void ResolveNamesVisitor::CreateGeneric(const parser::GenericSpec &x) {
auto info{GenericSpecInfo{x}};
const SourceName &symbolName{info.symbolName()};
SourceName symbolName{info.symbolName()};
if (IsLogicalConstant(context(), symbolName)) {
Say(symbolName,
"Logical constant '%s' may not be used as a defined operator"_err_en_US);
return;
}
GenericDetails genericDetails;
if (Symbol * existing{FindInScope(symbolName)}) {
if (existing->has<GenericDetails>()) {
info.Resolve(existing);
return; // already have generic, add to it
Symbol *existing{nullptr};
// Check all variants of names, e.g. "operator(.ne.)" for "operator(/=)"
for (const std::string &n : GetAllNames(context(), symbolName)) {
existing = currScope().FindSymbol(n);
if (existing) {
break;
}
}
if (existing) {
Symbol &ultimate{existing->GetUltimate()};
if (auto *ultimateDetails{ultimate.detailsIf<GenericDetails>()}) {
// convert a use-associated generic into a local generic
genericDetails.CopyFrom(*ultimateDetails);
AddGenericUse(genericDetails, existing->name(),
existing->get<UseDetails>().symbol());
} else if (ultimate.has<SubprogramDetails>() ||
if (const auto *existingGeneric{ultimate.detailsIf<GenericDetails>()}) {
if (&ultimate.owner() != &currScope()) {
// Create a local copy of a host or use associated generic so that
// it can be locally extended without corrupting the original.
genericDetails.CopyFrom(*existingGeneric);
if (const auto *use{existing->detailsIf<UseDetails>()}) {
AddGenericUse(genericDetails, existing->name(), use->symbol());
EraseSymbol(*existing);
}
existing = &MakeSymbol(symbolName, Attrs{}, std::move(genericDetails));
}
info.Resolve(existing);
return;
}
if (ultimate.has<SubprogramDetails>() ||
ultimate.has<SubprogramNameDetails>()) {
genericDetails.set_specific(ultimate);
} else if (ultimate.has<DerivedTypeDetails>()) {
genericDetails.set_derivedType(ultimate);
} else {
SayAlreadyDeclared(symbolName, *existing);
return;
}
EraseSymbol(*existing);
}

View File

@ -0,0 +1,88 @@
! RUN: %python %S/test_errors.py %s %flang_fc1
! Exercise ways to define and extend non-type-bound generics
module m1
type :: t1; end type
type :: t2; end type
interface operator(.eq.)
module procedure :: eq1
end interface
generic :: operator(==) => eq2
contains
logical function eq1(x, y)
type(t1), intent(in) :: x
type(t2), intent(in) :: y
eq1 = .true.
end function
logical function eq2(y, x)
type(t2), intent(in) :: y
type(t1), intent(in) :: x
eq2 = .true.
end function
subroutine test1
type(t1) :: a
type(t2) :: b
if (a == b .and. b .eq. a) print *, 'ok'
end subroutine
end module
module m2
use m1
type :: t3; end type
interface operator(==)
module procedure eq3
end interface
generic :: operator(.eq.) => eq4
contains
logical function eq3(x, y)
type(t1), intent(in) :: x
type(t3), intent(in) :: y
eq3 = .true.
end function
logical function eq4(y, x)
type(t3), intent(in) :: y
type(t1), intent(in) :: x
eq4 = .true.
end function
subroutine test2
type(t1) :: a
type(t2) :: b
type(t3) :: c
if (a == b .and. b .eq. a .and. a == c .and. c .eq. a) print *, 'ok'
end subroutine
end module
module m3
use m2
contains
logical function eq5(x, y)
type(t2), intent(in) :: x
type(t3), intent(in) :: y
eq5 = .true.
end function
logical function eq6(y, x)
type(t3), intent(in) :: y
type(t2), intent(in) :: x
eq6 = .true.
end function
subroutine test3
interface operator(==)
module procedure :: eq5
end interface
type(t1) :: a
type(t2) :: b
type(t3) :: c
if (a == b .and. b .eq. a .and. a == c .and. c .eq. a .and. b == c) print *, 'ok'
block
generic :: operator(.eq.) => eq6
if (a == b .and. b .eq. a .and. a == c .and. c .eq. a .and. b == c .and. c .eq. b) print *, 'ok'
end block
contains
subroutine inner
interface operator(.eq.)
module procedure :: eq6
end interface
if (a == b .and. b .eq. a .and. a == c .and. c .eq. a .and. b == c .and. c .eq. b) print *, 'ok'
end subroutine
end subroutine
end module