Skip to content

Commit 0b8381a

Browse files
authored
[flang] Fix bogus generic interface error due to hermetic module files (#161607)
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 cbe3b72 commit 0b8381a

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
@@ -3048,14 +3048,6 @@ static std::optional<std::string> DefinesGlobalName(const Symbol &symbol) {
30483048
return std::nullopt;
30493049
}
30503050

3051-
static bool IsSameSymbolFromHermeticModule(
3052-
const Symbol &symbol, const Symbol &other) {
3053-
return symbol.name() == other.name() && symbol.owner().IsModule() &&
3054-
other.owner().IsModule() && symbol.owner() != other.owner() &&
3055-
symbol.owner().GetName() &&
3056-
symbol.owner().GetName() == other.owner().GetName();
3057-
}
3058-
30593051
// 19.2 p2
30603052
void CheckHelper::CheckGlobalName(const Symbol &symbol) {
30613053
if (auto global{DefinesGlobalName(symbol)}) {
@@ -3073,7 +3065,7 @@ void CheckHelper::CheckGlobalName(const Symbol &symbol) {
30733065
(!IsExternalProcedureDefinition(symbol) ||
30743066
!IsExternalProcedureDefinition(other))) {
30753067
// both are procedures/BLOCK DATA, not both definitions
3076-
} else if (IsSameSymbolFromHermeticModule(symbol, other)) {
3068+
} else if (AreSameModuleSymbol(symbol, other)) {
30773069
// Both symbols are the same thing.
30783070
} else if (symbol.has<ModuleDetails>()) {
30793071
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
@@ -3962,40 +3962,43 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
39623962
}
39633963
}
39643964

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

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