Issue 1:
Describe the issue:

Let's consider the following subroutine in a test.f90 file that declares an automatic array from two variables(*):

subroutine test (nx, ny, arr)

implicit none

integer, intent(in) :: nx
integer, intent(in) :: ny
integer, dimension(nx*ny) :: arr

return
end subroutine test

It used to work with numpy 1.22.0 or earlier, despite the Warning: cross-dependence between variables "ny" and "nx" and the weird shape checks as shown in the generated test.pyf signature file below:

! -*- f90 -*-
! Note: the context of this file is case sensitive.

python module test ! in
interface ! in :test
subroutine test(nx,ny,arr) ! in :test:src/test.f90
integer, optional,intent(in),check(shape(arr, 0) == nx * ny),depend(ny,arr) :: nx=shape(arr, 0) / nx
integer, optional,intent(in),check(shape(arr, 0) == nx * ny),depend(nx,arr) :: ny=shape(arr, 0) / nx
integer dimension(nx * ny) :: arr
end subroutine test
end interface
end python module test

! This file was auto-generated with f2py (version:1.22.0).
! See:
! https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e

However, with numpy 1.22.1 or later, it generates a test.pyf with "swapped" nx / ny:

! -*- f90 -*-
! Note: the context of this file is case sensitive.

python module test ! in
interface ! in :test
subroutine test(nx,ny,arr) ! in :test:src/test.f90
integer, optional,intent(in),check(shape(arr, 0) == nx * ny),depend(ny,arr) :: nx=shape(arr, 0) / ny
integer, optional,intent(in),check(shape(arr, 0) == nx * ny),depend(arr,nx) :: ny=shape(arr, 0) / nx
integer dimension(nx * ny) :: arr
end subroutine test
end interface
end python module test

! This file was auto-generated with f2py (version:1.22.1).
! See:
! https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e

Which causes the following recursion error when trying to generate the Python extension from the signature file:

Reading fortran codes...
Reading file 'test.pyf' (format:free)
Post-processing...
Block: test
Block: test
Traceback (most recent call last):
File "/Users/me/env/bin/f2py", line 8, in <module>
sys.exit(main())
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/f2py2e.py", line 693, in main
run_main(sys.argv[1:])
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/f2py2e.py", line 430, in run_main
postlist = callcrackfortran(files, options)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/f2py2e.py", line 333, in callcrackfortran
postlist = crackfortran.crackfortran(files)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 3209, in crackfortran
postlist = postcrack(grouplist[0])
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 1926, in postcrack
g = postcrack(g, tab=tab + '\t')
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 1945, in postcrack
block['body'] = analyzebody(block, args, tab=tab)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2107, in analyzebody
b = postcrack(b, as_, tab=tab + '\t')
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 1945, in postcrack
block['body'] = analyzebody(block, args, tab=tab)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2107, in analyzebody
b = postcrack(b, as_, tab=tab + '\t')
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 1941, in postcrack
block['vars'] = analyzevars(block)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2458, in analyzevars
params = get_parameters(vars, get_useparameters(block))
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2329, in get_parameters
for n in get_sorted_names(vars):
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2257, in get_sorted_names
depend_dict = _calc_depend_dict(vars)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2250, in _calc_depend_dict
_get_depend_dict(n, vars, depend_dict)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2236, in _get_depend_dict
or _get_depend_dict(word, vars, deps):
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2236, in _get_depend_dict
or _get_depend_dict(word, vars, deps):
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2236, in _get_depend_dict
or _get_depend_dict(word, vars, deps):
[Previous line repeated 979 more times]
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2228, in _get_depend_dict
if '=' in vars[name] and not isstring(vars[name]):
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/auxfuncs.py", line 77, in isstring
return _isstring(var) and not isarray(var)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/auxfuncs.py", line 72, in _isstring
return 'typespec' in var and var['typespec'] == 'character' and \
RecursionError: maximum recursion depth exceeded in comparison

The difference between the two generated signature files seems to be a result of #20721.

(*) Do you think the issue here could be considered as a regression? Or is the example here wrong and we should not declare dimensions of automatic arrays from two or more variables? Not sure about this as I'm not a Fortran expert and haven't found much info about this practice, although I'd be surprised that compilers let us do it if it wasn't valid.

Reproduce the code example:

see f90 / pyf source above

Error message:

See recursion error above.

NumPy/Python version information:

Numpy 1.22.0 / 1.22.1

Python 3.9

Issue 2:
Describe the issue:

Let's consider the following subroutine in a test.f90 file that declares an automatic array from two variables(*):

subroutine test (nx, ny, arr)

implicit none

integer, intent(in) :: nx
integer, intent(in) :: ny
integer, dimension(nx*ny) :: arr

return
end subroutine test

It used to work with numpy 1.22.0 or earlier, despite the Warning: cross-dependence between variables "ny" and "nx" and the weird shape checks as shown in the generated test.pyf signature file below:

! -*- f90 -*-
! Note: the context of this file is case sensitive.

python module test ! in
interface ! in :test
subroutine test(nx,ny,arr) ! in :test:src/test.f90
integer, optional,intent(in),check(shape(arr, 0) == nx * ny),depend(ny,arr) :: nx=shape(arr, 0) / nx
integer, optional,intent(in),check(shape(arr, 0) == nx * ny),depend(nx,arr) :: ny=shape(arr, 0) / nx
integer dimension(nx * ny) :: arr
end subroutine test
end interface
end python module test

! This file was auto-generated with f2py (version:1.22.0).
! See:
! https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e

However, with numpy 1.22.1 or later, it generates a test.pyf with "swapped" nx / ny:

! -*- f90 -*-
! Note: the context of this file is case sensitive.

python module test ! in
interface ! in :test
subroutine test(nx,ny,arr) ! in :test:src/test.f90
integer, optional,intent(in),check(shape(arr, 0) == nx * ny),depend(ny,arr) :: nx=shape(arr, 0) / ny
integer, optional,intent(in),check(shape(arr, 0) == nx * ny),depend(arr,nx) :: ny=shape(arr, 0) / nx
integer dimension(nx * ny) :: arr
end subroutine test
end interface
end python module test

! This file was auto-generated with f2py (version:1.22.1).
! See:
! https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e

Which causes the following recursion error when trying to generate the Python extension from the signature file:

Reading fortran codes...
Reading file 'test.pyf' (format:free)
Post-processing...
Block: test
Block: test
Traceback (most recent call last):
File "/Users/me/env/bin/f2py", line 8, in <module>
sys.exit(main())
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/f2py2e.py", line 693, in main
run_main(sys.argv[1:])
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/f2py2e.py", line 430, in run_main
postlist = callcrackfortran(files, options)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/f2py2e.py", line 333, in callcrackfortran
postlist = crackfortran.crackfortran(files)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 3209, in crackfortran
postlist = postcrack(grouplist[0])
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 1926, in postcrack
g = postcrack(g, tab=tab + '\t')
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 1945, in postcrack
block['body'] = analyzebody(block, args, tab=tab)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2107, in analyzebody
b = postcrack(b, as_, tab=tab + '\t')
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 1945, in postcrack
block['body'] = analyzebody(block, args, tab=tab)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2107, in analyzebody
b = postcrack(b, as_, tab=tab + '\t')
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 1941, in postcrack
block['vars'] = analyzevars(block)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2458, in analyzevars
params = get_parameters(vars, get_useparameters(block))
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2329, in get_parameters
for n in get_sorted_names(vars):
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2257, in get_sorted_names
depend_dict = _calc_depend_dict(vars)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2250, in _calc_depend_dict
_get_depend_dict(n, vars, depend_dict)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2236, in _get_depend_dict
or _get_depend_dict(word, vars, deps):
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2236, in _get_depend_dict
or _get_depend_dict(word, vars, deps):
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2236, in _get_depend_dict
or _get_depend_dict(word, vars, deps):
[Previous line repeated 979 more times]
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/crackfortran.py", line 2228, in _get_depend_dict
if '=' in vars[name] and not isstring(vars[name]):
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/auxfuncs.py", line 77, in isstring
return _isstring(var) and not isarray(var)
File "/Users/me/env/lib/python3.9/site-packages/numpy/f2py/auxfuncs.py", line 72, in _isstring
return 'typespec' in var and var['typespec'] == 'character' and \
RecursionError: maximum recursion depth exceeded in comparison

The difference between the two generated signature files seems to be a result of #20721.

(*) Do you think the issue here could be considered as a regression? Or is the example here wrong and we should not declare dimensions of automatic arrays from two or more variables? Not sure about this as I'm not a Fortran expert and haven't found much info about this practice, although I'd be surprised that compilers let us do it if it wasn't valid.

Reproduce the code example:

# see f90 / pyf source above

Error message:

No response

NumPy/Python version information:

Numpy 1.22.0 / 1.22.1

Python 3.9