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 changing SymbolSet to be unordered.
I also added tests that will crash the compiler without this change.
Heap addresses are arbitrary and cannot be expected to have relationships that are portable. This change is going to lead to spurious test failures esp. across platforms. You're going to have to order the symbols via the actual characters in their names instead.