Skip to content

Commit f0cddc6

Browse files
committed
[flang] Fix bogus generic interface error due to hermetic module files
When the same generic interface is processed via USE association from its original module file and from a copy in a hermetic module file, we need to do a better job at detecting and omitting duplicate specific procedures. They won't have the same symbol addresses, but they will have the same name, module name, and characteristics. This will avoid a bogus error about multiple specific procedures matching the actual arguments later when the merged generic interface is referenced.
1 parent 6048c2f commit f0cddc6

File tree

5 files changed

+83
-37
lines changed

5 files changed

+83
-37
lines changed

flang/include/flang/Semantics/tools.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,5 +770,7 @@ std::string GetCommonBlockObjectName(const Symbol &, bool underscoring);
770770
// Check for ambiguous USE associations
771771
bool HadUseError(SemanticsContext &, SourceName at, const Symbol *);
772772

773+
bool AreSameModuleSymbol(const Symbol &, const Symbol &);
774+
773775
} // namespace Fortran::semantics
774776
#endif // FORTRAN_SEMANTICS_TOOLS_H_

flang/lib/Semantics/check-declarations.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2976,14 +2976,6 @@ static std::optional<std::string> DefinesGlobalName(const Symbol &symbol) {
29762976
return std::nullopt;
29772977
}
29782978

2979-
static bool IsSameSymbolFromHermeticModule(
2980-
const Symbol &symbol, const Symbol &other) {
2981-
return symbol.name() == other.name() && symbol.owner().IsModule() &&
2982-
other.owner().IsModule() && symbol.owner() != other.owner() &&
2983-
symbol.owner().GetName() &&
2984-
symbol.owner().GetName() == other.owner().GetName();
2985-
}
2986-
29872979
// 19.2 p2
29882980
void CheckHelper::CheckGlobalName(const Symbol &symbol) {
29892981
if (auto global{DefinesGlobalName(symbol)}) {
@@ -3001,7 +2993,7 @@ void CheckHelper::CheckGlobalName(const Symbol &symbol) {
30012993
(!IsExternalProcedureDefinition(symbol) ||
30022994
!IsExternalProcedureDefinition(other))) {
30032995
// both are procedures/BLOCK DATA, not both definitions
3004-
} else if (IsSameSymbolFromHermeticModule(symbol, other)) {
2996+
} else if (AreSameModuleSymbol(symbol, other)) {
30052997
// Both symbols are the same thing.
30062998
} else if (symbol.has<ModuleDetails>()) {
30072999
Warn(common::LanguageFeature::BenignNameClash, symbol.name(),

flang/lib/Semantics/resolve-names.cpp

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3963,40 +3963,43 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
39633963
}
39643964
}
39653965

3966+
auto AreSameModuleProcOrBothInterfaces{[](const Symbol &p1,
3967+
const Symbol &p2) {
3968+
if (IsProcedure(p1) && !IsPointer(p1) && IsProcedure(p2) &&
3969+
!IsPointer(p2)) {
3970+
auto classification{ClassifyProcedure(p1)};
3971+
if (classification == ClassifyProcedure(p2)) {
3972+
if (classification == ProcedureDefinitionClass::External) {
3973+
const auto *subp1{p1.detailsIf<SubprogramDetails>()};
3974+
const auto *subp2{p2.detailsIf<SubprogramDetails>()};
3975+
return subp1 && subp1->isInterface() && subp2 && subp2->isInterface();
3976+
} else if (classification == ProcedureDefinitionClass::Module) {
3977+
return AreSameModuleSymbol(p1, p2);
3978+
}
3979+
}
3980+
}
3981+
return false;
3982+
}};
3983+
39663984
auto AreSameProcedure{[&](const Symbol &p1, const Symbol &p2) {
3967-
if (&p1 == &p2) {
3985+
if (&p1.GetUltimate() == &p2.GetUltimate()) {
39683986
return true;
39693987
} else if (p1.name() != p2.name()) {
39703988
return false;
39713989
} else if (p1.attrs().test(Attr::INTRINSIC) ||
39723990
p2.attrs().test(Attr::INTRINSIC)) {
39733991
return p1.attrs().test(Attr::INTRINSIC) &&
39743992
p2.attrs().test(Attr::INTRINSIC);
3975-
} else if (!IsProcedure(p1) || !IsProcedure(p2)) {
3976-
return false;
3977-
} else if (IsPointer(p1) || IsPointer(p2)) {
3978-
return false;
3979-
} else if (const auto *subp{p1.detailsIf<SubprogramDetails>()};
3980-
subp && !subp->isInterface()) {
3981-
return false; // defined in module, not an external
3982-
} else if (const auto *subp{p2.detailsIf<SubprogramDetails>()};
3983-
subp && !subp->isInterface()) {
3984-
return false; // defined in module, not an external
3993+
} else if (AreSameModuleProcOrBothInterfaces(p1, p2)) {
3994+
// Both are external interfaces, perhaps to the same procedure,
3995+
// or both are module procedures from modules with the same name.
3996+
auto p1Chars{evaluate::characteristics::Procedure::Characterize(
3997+
p1, GetFoldingContext())};
3998+
auto p2Chars{evaluate::characteristics::Procedure::Characterize(
3999+
p2, GetFoldingContext())};
4000+
return p1Chars && p2Chars && *p1Chars == *p2Chars;
39854001
} else {
3986-
// Both are external interfaces, perhaps to the same procedure
3987-
auto class1{ClassifyProcedure(p1)};
3988-
auto class2{ClassifyProcedure(p2)};
3989-
if (class1 == ProcedureDefinitionClass::External &&
3990-
class2 == ProcedureDefinitionClass::External) {
3991-
auto chars1{evaluate::characteristics::Procedure::Characterize(
3992-
p1, GetFoldingContext())};
3993-
auto chars2{evaluate::characteristics::Procedure::Characterize(
3994-
p2, GetFoldingContext())};
3995-
// same procedure interface defined identically in two modules?
3996-
return chars1 && chars2 && *chars1 == *chars2;
3997-
} else {
3998-
return false;
3999-
}
4002+
return false;
40004003
}
40014004
}};
40024005

@@ -4097,13 +4100,32 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
40974100
localSymbol = &newSymbol;
40984101
}
40994102
if (useGeneric) {
4100-
// Combine two use-associated generics
4103+
// Combine two use-associated generics.
41014104
localSymbol->attrs() =
41024105
useSymbol.attrs() & ~Attrs{Attr::PUBLIC, Attr::PRIVATE};
41034106
localSymbol->flags() = useSymbol.flags();
41044107
AddGenericUse(*localGeneric, localName, useUltimate);
4105-
localGeneric->clear_derivedType();
4106-
localGeneric->CopyFrom(*useGeneric);
4108+
// Don't duplicate specific procedures.
4109+
std::size_t originalLocalSpecifics{localGeneric->specificProcs().size()};
4110+
std::size_t useSpecifics{useGeneric->specificProcs().size()};
4111+
CHECK(originalLocalSpecifics == localGeneric->bindingNames().size());
4112+
CHECK(useSpecifics == useGeneric->bindingNames().size());
4113+
std::size_t j{0};
4114+
for (const Symbol &useSpecific : useGeneric->specificProcs()) {
4115+
SourceName useBindingName{useGeneric->bindingNames()[j++]};
4116+
bool isDuplicate{false};
4117+
std::size_t k{0};
4118+
for (const Symbol &localSpecific : localGeneric->specificProcs()) {
4119+
if (localGeneric->bindingNames()[k++] == useBindingName &&
4120+
AreSameProcedure(localSpecific, useSpecific)) {
4121+
isDuplicate = true;
4122+
break;
4123+
}
4124+
}
4125+
if (!isDuplicate) {
4126+
localGeneric->AddSpecificProc(useSpecific, useBindingName);
4127+
}
4128+
}
41074129
}
41084130
localGeneric->clear_derivedType();
41094131
if (combinedDerivedType) {

flang/lib/Semantics/tools.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1870,4 +1870,9 @@ bool HadUseError(
18701870
}
18711871
}
18721872

1873+
bool AreSameModuleSymbol(const Symbol &symbol, const Symbol &other) {
1874+
return symbol.name() == other.name() && symbol.owner().IsModule() &&
1875+
other.owner().IsModule() && symbol.owner().GetName() &&
1876+
symbol.owner().GetName() == other.owner().GetName();
1877+
}
18731878
} // namespace Fortran::semantics

flang/test/Semantics/modfile80.F90

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
!RUN: %flang_fc1 -DPART1 %s
2+
!RUN: %flang_fc1 -DPART2 -fhermetic-module-files %s
3+
!RUN: %flang_fc1 -DPART3 | FileCheck --allow-empty %s
4+
!CHECK-NOT: error:
5+
6+
#if defined PART1
7+
module modfile80a
8+
interface generic
9+
module procedure specific
10+
end interface
11+
contains
12+
subroutine specific
13+
end
14+
end
15+
#elif defined PART2
16+
module modfile80b
17+
use modfile80a
18+
end
19+
#else
20+
program test
21+
use modfile80a
22+
use modfile80b
23+
call generic
24+
end
25+
#endif

0 commit comments

Comments
 (0)