llvm-project/flang/test/Semantics/resolve102.f90

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

103 lines
2.3 KiB
Fortran
Raw Normal View History

! RUN: %S/test_errors.sh %s %t %flang_fc1
! REQUIRES: shell
! Tests for circularly defined procedures
[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 location of the name of the procedure. But the location of 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 an unordered set that uses the contents of the name of the symbol as the basis for its hash function. This works because the contents of the name of the symbol is preserved by ReplaceName() even though its location changes. I also fixed the error message used when reporting recursively defined dummy procedure arguments by removing extra apostrophes and sorting the list of symbols. I also added tests that will crash the compiler without this change. Note that the "<" operator is used in other contexts, for example, in the map of characterized procedures, maps of items in equivalence sets, maps of structure constructor values, ... All of these situations happen after name resolution has been completed and all calls to ReplaceName() have already happened and thus are not subject to the problem I ran into when ReplaceName() was called when processing procedure entities. Note also that the implementation of the "<" operator uses the relative location in the cooked character stream as the basis of its implementation. This is potentially problematic when symbols from diffent compilation units (for example symbols originating in .mod files) are put into the same map since their names will appear in two different source streams which may not be allocated in the same relative positions in memory. But I was unable to create a test that caused a problem. Using a direct comparison of the content of the name of the symbol in the "<" operator has problems. Symbols in enclosing or parallel scopes can have the same name. Also using the location of the symbol in the cooked character stream has the advantage that it preserves the the order of the symbols in a structure constructor constant, which makes matching the values with the symbols relatively easy. This patch supersedes D97749. Differential Revision: https://reviews.llvm.org/D97774
2021-03-02 23:53:10 +08:00
!ERROR: Procedure 'sub' is recursively defined. Procedures in the cycle: 'sub', 'p2'
subroutine sub(p2)
PROCEDURE(sub) :: p2
call sub()
end subroutine
subroutine circular
!ERROR: Procedure 'p' is recursively defined. Procedures in the cycle: 'p', 'sub', 'p2'
procedure(sub) :: p
call p(sub)
contains
subroutine sub(p2)
procedure(p) :: p2
end subroutine
end subroutine circular
program iface
!ERROR: Procedure 'p' is recursively defined. Procedures in the cycle: 'p', 'sub', 'p2'
procedure(sub) :: p
interface
subroutine sub(p2)
import p
procedure(p) :: p2
end subroutine
end interface
call p(sub)
end program
Program mutual
Procedure(sub1) :: p
Call p(sub)
contains
!ERROR: Procedure 'sub1' is recursively defined. Procedures in the cycle: 'p', 'sub1', 'arg'
Subroutine sub1(arg)
procedure(sub1) :: arg
End Subroutine
Subroutine sub(p2)
Procedure(sub1) :: p2
End Subroutine
End Program
Program mutual1
Procedure(sub1) :: p
Call p(sub)
contains
!ERROR: Procedure 'sub1' is recursively defined. Procedures in the cycle: 'p', 'sub1', 'arg', 'sub', 'p2'
Subroutine sub1(arg)
procedure(sub) :: arg
End Subroutine
Subroutine sub(p2)
Procedure(sub1) :: p2
End Subroutine
End Program
[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 location of the name of the procedure. But the location of 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 an unordered set that uses the contents of the name of the symbol as the basis for its hash function. This works because the contents of the name of the symbol is preserved by ReplaceName() even though its location changes. I also fixed the error message used when reporting recursively defined dummy procedure arguments by removing extra apostrophes and sorting the list of symbols. I also added tests that will crash the compiler without this change. Note that the "<" operator is used in other contexts, for example, in the map of characterized procedures, maps of items in equivalence sets, maps of structure constructor values, ... All of these situations happen after name resolution has been completed and all calls to ReplaceName() have already happened and thus are not subject to the problem I ran into when ReplaceName() was called when processing procedure entities. Note also that the implementation of the "<" operator uses the relative location in the cooked character stream as the basis of its implementation. This is potentially problematic when symbols from diffent compilation units (for example symbols originating in .mod files) are put into the same map since their names will appear in two different source streams which may not be allocated in the same relative positions in memory. But I was unable to create a test that caused a problem. Using a direct comparison of the content of the name of the symbol in the "<" operator has problems. Symbols in enclosing or parallel scopes can have the same name. Also using the location of the symbol in the cooked character stream has the advantage that it preserves the the order of the symbols in a structure constructor constant, which makes matching the values with the symbols relatively easy. This patch supersedes D97749. Differential Revision: https://reviews.llvm.org/D97774
2021-03-02 23:53:10 +08:00
program twoCycle
!ERROR: The interface for procedure 'p1' is recursively defined
!ERROR: The interface for procedure 'p2' is recursively defined
procedure(p1) p2
procedure(p2) p1
call p1
call p2
end program
program threeCycle
!ERROR: The interface for procedure 'p1' is recursively defined
!ERROR: The interface for procedure 'p2' is recursively defined
procedure(p1) p2
!ERROR: The interface for procedure 'p3' is recursively defined
procedure(p2) p3
procedure(p3) p1
call p1
call p2
call p3
end program
module mutualSpecExprs
contains
pure integer function f(n)
integer, intent(in) :: n
real arr(g(n))
f = size(arr)
end function
pure integer function g(n)
integer, intent(in) :: n
!ERROR: Procedure 'f' is referenced before being sufficiently defined in a context where it must be so
real arr(f(n))
g = size(arr)
end function
end