Skip to content

Commit 07de084

Browse files
committed
[flang] Detect circularly defined interfaces of procedures
It's possible to define a procedure whose interface depends on a procedure which has an interface that depends on the original procedure. Such a circular definition was causing the compiler to fall into an infinite loop when resolving the name of the second procedure. It's also possible to create circular dependency chains of more than two procedures. I fixed this by adding the function HasCycle() to the class DeclarationVisitor and calling it from DeclareProcEntity() to detect procedures with such circularly defined interfaces. I marked the associated symbols of such procedures by calling SetError() on them. When processing subsequent procedures, I called HasError() before attempting to analyze their interfaces. Unfortunately, this did not work. With help from Tim, we determined that the SymbolSet used to track the erroneous symbols was instantiated using a "<" operator which was defined using the name of the procedure. But the procedure name was being changed by a call to ReplaceName() between the times that the calls to SetError() and HasError() were made. This caused HasError() to incorrectly report that a symbol was not in the set of erroneous symbols. I fixed this by making SymbolSet be an ordered set, which does not use the "<" operator. I also added tests that will crash the compiler without this change. And I fixed the formatting on an error message from a previous update. Differential Revision: https://reviews.llvm.org/D97201
1 parent c9075a1 commit 07de084

File tree

4 files changed

+84
-27
lines changed

4 files changed

+84
-27
lines changed

flang/include/flang/Semantics/symbol.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#include <array>
1818
#include <list>
1919
#include <optional>
20-
#include <set>
20+
#include <unordered_set>
2121
#include <vector>
2222

2323
namespace llvm {
@@ -38,6 +38,12 @@ using SymbolRef = common::Reference<const Symbol>;
3838
using SymbolVector = std::vector<SymbolRef>;
3939
using MutableSymbolRef = common::Reference<Symbol>;
4040
using MutableSymbolVector = std::vector<MutableSymbolRef>;
41+
struct SymbolHash {
42+
std::size_t operator()(SymbolRef symRef) const {
43+
return (std::size_t)(&symRef.get());
44+
}
45+
};
46+
using SymbolSet = std::unordered_set<SymbolRef, SymbolHash>;
4147

4248
// A module or submodule.
4349
class ModuleDetails {
@@ -594,9 +600,10 @@ class Symbol {
594600

595601
bool operator==(const Symbol &that) const { return this == &that; }
596602
bool operator!=(const Symbol &that) const { return !(*this == that); }
603+
// For maps using symbols as keys and sorting symbols. Collate them by their
604+
// position in the cooked character stream
597605
bool operator<(const Symbol &that) const {
598-
// For sets of symbols: collate them by source location
599-
return name_.begin() < that.name_.begin();
606+
return sortName_ < that.sortName_;
600607
}
601608

602609
int Rank() const {
@@ -653,6 +660,7 @@ class Symbol {
653660
private:
654661
const Scope *owner_;
655662
SourceName name_;
663+
const char *sortName_; // used in the "<" operator for sorting symbols
656664
Attrs attrs_;
657665
Flags flags_;
658666
Scope *scope_{nullptr};
@@ -687,6 +695,7 @@ template <std::size_t BLOCK_SIZE> class Symbols {
687695
Symbol &symbol = Get();
688696
symbol.owner_ = &owner;
689697
symbol.name_ = name;
698+
symbol.sortName_ = name.begin();
690699
symbol.attrs_ = attrs;
691700
symbol.details_ = std::move(details);
692701
return symbol;
@@ -765,7 +774,6 @@ inline bool operator<(SymbolRef x, SymbolRef y) { return *x < *y; }
765774
inline bool operator<(MutableSymbolRef x, MutableSymbolRef y) {
766775
return *x < *y;
767776
}
768-
using SymbolSet = std::set<SymbolRef>;
769777

770778
} // namespace Fortran::semantics
771779

flang/lib/Evaluate/characteristics.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ static std::optional<Procedure> CharacterizeProcedure(
369369
std::string procsList{GetSeenProcs(seenProcs)};
370370
context.messages().Say(symbol.name(),
371371
"Procedure '%s' is recursively defined. Procedures in the cycle:"
372-
" '%s'"_err_en_US,
372+
" %s"_err_en_US,
373373
symbol.name(), procsList);
374374
return std::nullopt;
375375
}

flang/lib/Semantics/resolve-names.cpp

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,7 @@ class DeclarationVisitor : public ArraySpecVisitor,
10031003
context().SetError(symbol);
10041004
return symbol;
10051005
}
1006+
bool HasCycle(const Symbol &, const ProcInterface &);
10061007
};
10071008

10081009
// Resolve construct entities and statement entities.
@@ -2132,7 +2133,7 @@ static bool NeedsType(const Symbol &symbol) {
21322133

21332134
void ScopeHandler::ApplyImplicitRules(
21342135
Symbol &symbol, bool allowForwardReference) {
2135-
if (!NeedsType(symbol)) {
2136+
if (context().HasError(symbol) || !NeedsType(symbol)) {
21362137
return;
21372138
}
21382139
if (const DeclTypeSpec * type{GetImplicitType(symbol)}) {
@@ -2156,10 +2157,8 @@ void ScopeHandler::ApplyImplicitRules(
21562157
if (allowForwardReference && ImplicitlyTypeForwardRef(symbol)) {
21572158
return;
21582159
}
2159-
if (!context().HasError(symbol)) {
2160-
Say(symbol.name(), "No explicit type declared for '%s'"_err_en_US);
2161-
context().SetError(symbol);
2162-
}
2160+
Say(symbol.name(), "No explicit type declared for '%s'"_err_en_US);
2161+
context().SetError(symbol);
21632162
}
21642163

21652164
// Extension: Allow forward references to scalar integer dummy arguments
@@ -3641,6 +3640,35 @@ Symbol &DeclarationVisitor::DeclareUnknownEntity(
36413640
}
36423641
}
36433642

3643+
bool DeclarationVisitor::HasCycle(
3644+
const Symbol &procSymbol, const ProcInterface &interface) {
3645+
SymbolSet procsInCycle;
3646+
procsInCycle.insert(procSymbol);
3647+
const ProcInterface *thisInterface{&interface};
3648+
bool haveInterface{true};
3649+
while (haveInterface) {
3650+
haveInterface = false;
3651+
if (const Symbol * interfaceSymbol{thisInterface->symbol()}) {
3652+
if (procsInCycle.count(*interfaceSymbol) > 0) {
3653+
for (const auto procInCycle : procsInCycle) {
3654+
Say(procInCycle->name(),
3655+
"The interface for procedure '%s' is recursively "
3656+
"defined"_err_en_US,
3657+
procInCycle->name());
3658+
context().SetError(*procInCycle);
3659+
}
3660+
return true;
3661+
} else if (const auto *procDetails{
3662+
interfaceSymbol->detailsIf<ProcEntityDetails>()}) {
3663+
haveInterface = true;
3664+
thisInterface = &procDetails->interface();
3665+
procsInCycle.insert(*interfaceSymbol);
3666+
}
3667+
}
3668+
}
3669+
return false;
3670+
}
3671+
36443672
Symbol &DeclarationVisitor::DeclareProcEntity(
36453673
const parser::Name &name, Attrs attrs, const ProcInterface &interface) {
36463674
Symbol &symbol{DeclareEntity<ProcEntityDetails>(name, attrs)};
@@ -3650,20 +3678,20 @@ Symbol &DeclarationVisitor::DeclareProcEntity(
36503678
"The interface for procedure '%s' has already been "
36513679
"declared"_err_en_US);
36523680
context().SetError(symbol);
3653-
} else {
3654-
if (interface.type()) {
3681+
} else if (HasCycle(symbol, interface)) {
3682+
return symbol;
3683+
} else if (interface.type()) {
3684+
symbol.set(Symbol::Flag::Function);
3685+
} else if (interface.symbol()) {
3686+
if (interface.symbol()->test(Symbol::Flag::Function)) {
36553687
symbol.set(Symbol::Flag::Function);
3656-
} else if (interface.symbol()) {
3657-
if (interface.symbol()->test(Symbol::Flag::Function)) {
3658-
symbol.set(Symbol::Flag::Function);
3659-
} else if (interface.symbol()->test(Symbol::Flag::Subroutine)) {
3660-
symbol.set(Symbol::Flag::Subroutine);
3661-
}
3688+
} else if (interface.symbol()->test(Symbol::Flag::Subroutine)) {
3689+
symbol.set(Symbol::Flag::Subroutine);
36623690
}
3663-
details->set_interface(interface);
3664-
SetBindNameOn(symbol);
3665-
SetPassNameOn(symbol);
36663691
}
3692+
details->set_interface(interface);
3693+
SetBindNameOn(symbol);
3694+
SetPassNameOn(symbol);
36673695
}
36683696
return symbol;
36693697
}
@@ -5005,7 +5033,7 @@ Symbol *DeclarationVisitor::NoteInterfaceName(const parser::Name &name) {
50055033

50065034
void DeclarationVisitor::CheckExplicitInterface(const parser::Name &name) {
50075035
if (const Symbol * symbol{name.symbol}) {
5008-
if (!symbol->HasExplicitInterface()) {
5036+
if (!context().HasError(*symbol) && !symbol->HasExplicitInterface()) {
50095037
Say(name,
50105038
"'%s' must be an abstract interface or a procedure with "
50115039
"an explicit interface"_err_en_US,

flang/test/Semantics/resolve102.f90

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
! RUN: %S/test_errors.sh %s %t %f18
22

33
! Tests for circularly defined procedures
4-
!ERROR: Procedure 'sub' is recursively defined. Procedures in the cycle: ''sub', 'p2''
4+
!ERROR: Procedure 'sub' is recursively defined. Procedures in the cycle: 'p2', 'sub'
55
subroutine sub(p2)
66
PROCEDURE(sub) :: p2
77

88
call sub()
99
end subroutine
1010

1111
subroutine circular
12-
!ERROR: Procedure 'p' is recursively defined. Procedures in the cycle: ''p', 'sub', 'p2''
12+
!ERROR: Procedure 'p' is recursively defined. Procedures in the cycle: 'p2', 'p', 'sub'
1313
procedure(sub) :: p
1414

1515
call p(sub)
@@ -21,7 +21,7 @@ subroutine sub(p2)
2121
end subroutine circular
2222

2323
program iface
24-
!ERROR: Procedure 'p' is recursively defined. Procedures in the cycle: ''p', 'sub', 'p2''
24+
!ERROR: Procedure 'p' is recursively defined. Procedures in the cycle: 'p2', 'p', 'sub'
2525
procedure(sub) :: p
2626
interface
2727
subroutine sub(p2)
@@ -38,7 +38,7 @@ Program mutual
3838
Call p(sub)
3939

4040
contains
41-
!ERROR: Procedure 'sub1' is recursively defined. Procedures in the cycle: ''p', 'sub1', 'arg''
41+
!ERROR: Procedure 'sub1' is recursively defined. Procedures in the cycle: 'arg', 'p', 'sub1'
4242
Subroutine sub1(arg)
4343
procedure(sub1) :: arg
4444
End Subroutine
@@ -54,7 +54,7 @@ Program mutual1
5454
Call p(sub)
5555

5656
contains
57-
!ERROR: Procedure 'sub1' is recursively defined. Procedures in the cycle: ''p', 'sub1', 'arg', 'sub', 'p2''
57+
!ERROR: Procedure 'sub1' is recursively defined. Procedures in the cycle: 'p2', 'sub', 'arg', 'p', 'sub1'
5858
Subroutine sub1(arg)
5959
procedure(sub) :: arg
6060
End Subroutine
@@ -63,3 +63,24 @@ Subroutine sub(p2)
6363
Procedure(sub1) :: p2
6464
End Subroutine
6565
End Program
66+
67+
program twoCycle
68+
!ERROR: The interface for procedure 'p1' is recursively defined
69+
!ERROR: The interface for procedure 'p2' is recursively defined
70+
procedure(p1) p2
71+
procedure(p2) p1
72+
call p1
73+
call p2
74+
end program
75+
76+
program threeCycle
77+
!ERROR: The interface for procedure 'p1' is recursively defined
78+
!ERROR: The interface for procedure 'p2' is recursively defined
79+
procedure(p1) p2
80+
!ERROR: The interface for procedure 'p3' is recursively defined
81+
procedure(p2) p3
82+
procedure(p3) p1
83+
call p1
84+
call p2
85+
call p3
86+
end program

0 commit comments

Comments
 (0)