ford icon indicating copy to clipboard operation
ford copied to clipboard

ford crashes on module procedure with interface body in same scope

Open rouson opened this issue 1 year ago • 10 comments

 % tree .
.
├── ford.md
└── src
    └── foo_m.f90

1 directory, 2 files
% cat ford.md 
---
project: Foo
summary: yada yada yada
src_dir: src/
% cat src/foo_m.f90 
module foo_m
  implicit none

  interface foo

    module function foo() result(bar)
      implicit none
      logical bar
    end function

  end interface

contains

  module procedure foo
    bar = .true.
  end procedure

end module
% ford ford.md
Reading file src/foo_m.f90
ERROR in file 'foo_m.f90': Unexpected MODULE PROCEDURE in module 'foo_m':
	module procedure foo
ERROR in file 'foo_m.f90': END statement outside of any nesting:
	end module
Traceback (most recent call last):
  File "/usr/local/Cellar/ford/6.1.15/libexec/lib/python3.10/site-packages/ford/fortran_project.py", line 108, in __init__
    ford.sourceform.FortranSourceFile(
  File "/usr/local/Cellar/ford/6.1.15/libexec/lib/python3.10/site-packages/ford/sourceform.py", line 1389, in __init__
    FortranContainer.__init__(self, source, "")
  File "/usr/local/Cellar/ford/6.1.15/libexec/lib/python3.10/site-packages/ford/sourceform.py", line 789, in __init__
    self._cleanup()
  File "/usr/local/Cellar/ford/6.1.15/libexec/lib/python3.10/site-packages/ford/sourceform.py", line 1033, in _cleanup
    raise NotImplementedError()
NotImplementedError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/ford", line 8, in <module>
    sys.exit(run())
  File "/usr/local/Cellar/ford/6.1.15/libexec/lib/python3.10/site-packages/ford/__init__.py", line 645, in run
    main(proj_data, proj_docs, md)
  File "/usr/local/Cellar/ford/6.1.15/libexec/lib/python3.10/site-packages/ford/__init__.py", line 568, in main
    project = ford.fortran_project.Project(proj_data)
  File "/usr/local/Cellar/ford/6.1.15/libexec/lib/python3.10/site-packages/ford/fortran_project.py", line 121, in __init__
    print(f"Warning: Error parsing {relative_path}.\n\t{e.args[0]}")
IndexError: tuple index out of range
% ford --version
ford version 6.1.15

rouson avatar Aug 09 '22 19:08 rouson

Moving the procedure definition to a submodule also generates a compiler error:

 % tree .
.
├── ford.md
└── src
    ├── foo_m.f90
    └── foo_s.f90

1 directory, 3 files
% cat src/foo_m.f90 
module foo_m
  implicit none

  interface foo

    module function foo() result(bar)
      implicit none
      logical bar
    end function

  end interface

end module
 % cat src/foo_s.f90
submodule(foo_m) foo_s
  implicit none

contains

  module procedure foo
    bar = .true.
  end procedure

end submodule

rouson avatar Aug 09 '22 19:08 rouson

Thanks for the bug report @rouson, I really appreciate the clear reproducer! I'm afraid it'll be at least a week before I can look at this properly.

I'm not 100% sure that this is valid Fortran, but gfortran at least accepts it.

ZedThree avatar Aug 10 '22 09:08 ZedThree

@ZedThree although I don't have the relevant citation handy, I'm certain this is standard-conforming code. In case it helps, I'm on the standard committee, I use this feature across more than a dozen projects with multiple compilers,and the non-profit that I lead, Sourcery Institute, paid for this feature to be added to gfortran several years ago.

rouson avatar Aug 10 '22 11:08 rouson

Yes, I happened to have this problem today as well. Replace the procedure in the following example with subroutine, and ford will not report an error, which is indeed valid Fortran syntax.

module mod

    interface
        module subroutine say_hello()
        end subroutine
    end interface

end module mod

submodule(mod) submod
contains

    module procedure say_hello   ! procedure -> subroutine
        print '(a)', "Hello World!"
    end procedure say_hello      ! procedure -> subroutine

end submodule submod

program main

    use mod, only: say_hello

    call say_hello()

end program main

In the Windows-MSYS2 environment, ford produces the following error:

Reading file app\demo.f90

Processing documentation comments...
Correlating information from different parts of your project...
Traceback (most recent call last):
  File "C:\msys64\ucrt64\lib\python3.10\runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\msys64\ucrt64\lib\python3.10\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "C:\msys64\ucrt64\bin\ford.exe\__main__.py", line 7, in <module>
  File "C:\msys64\ucrt64\lib\python3.10\site-packages\ford\__init__.py", line 645, in run
    main(proj_data, proj_docs, md)
  File "C:\msys64\ucrt64\lib\python3.10\site-packages\ford\__init__.py", line 592, in main
    project.correlate()
  File "C:\msys64\ucrt64\lib\python3.10\site-packages\ford\fortran_project.py", line 254, in correlate
    container.correlate(self)
  File "C:\msys64\ucrt64\lib\python3.10\site-packages\ford\sourceform.py", line 1078, in correlate
    if proc.module and proc.name.lower() in self.all_procs:
AttributeError: 'FortranSubmoduleProcedure' object has no attribute 'module'

image

zoziha avatar Aug 10 '22 17:08 zoziha

Thanks,@zoziha! You taught me something. I didn't know about this alternative approach. this issue broke our documentation deployment on several projects and your alternative is less involved than the only workaround I had devised (repeating all of the interface information in the procedure definition).

rouson avatar Aug 12 '22 04:08 rouson

I remember that some older versions of ford can be deployed through the ford document of Fortran-stdlib. Fortran-stdlib has always used the coding paradigm of submodule + module procedure. Now Fortran-stdlib also also fails to deploy. I wonder if it's possible to make the docs deploy pass briefly by identifying an old ford version.

pip install -v ford==6.1.10

zoziha avatar Aug 12 '22 05:08 zoziha

@zoziha please see #447 for one proposed solution. Thanks to @everythingfunctional, I now realize that your workaround requires repeating the interface information in the procedure definition. I hadn't recognized that because your say_hello procedure has no arguments so there was nothing to do other than replace procedure with subroutine. I'm hoping to avoid having the interface content in two places. Hopefully the solution in #447 works for you. In case an example is helpful, we're now using this approach in the Assert repository CI.

rouson avatar Aug 12 '22 18:08 rouson

I think the forced downgrade of markdown is a bit of a red herring. I've also not found a version of ford where module procedure works in a module scope (as opposed to in a submodule, which does work), so I'm not sure what's happening there! The version using a separate submodule works for me with the latest ford

I do have a simple fix that will allow this to work, but it will have some repercussions when a generic (named) interface has the same name as one of its specific routines (@rouson This is the bit I didn't realise was valid Fortran!). Namely, that the specific routine will clobber the generic one and you might not get docs for the interface -- I need to do some more checking to be certain though. That might be a rather bigger fix too.

I've reopened this issue till I've got some proper tests and a fix in.

ZedThree avatar Aug 17 '22 11:08 ZedThree

The version using a separate submodule works for me with the latest ford

@ZedThree I later reached the same conclusion.

I do have a simple fix that will allow this to work, but it will have some repercussions when a generic (named) interface has the same name as one of its specific routines (@rouson This is the bit I didn't realise was valid Fortran!).

That was a mistake on my part. Based on this realization, I agree that what I submitted is is likely not standard-conforming.

I've reopened this issue till I've got some proper tests and a fix in.

I look forward to using the fix. Thanks for investigating this!

rouson avatar Aug 17 '22 11:08 rouson

That was a mistake on my part. Based on this realization, I agree that what I submitted is is likely not standard-conforming.

I actually reached the opposite conclusion, I'm pretty sure what you had is completely valid! Both gfortran and Intel accept it. Fortran is generally a little bit weird about which names clash and which don't. I guess this case is fine because the compiler will always assume a call to foo is the generic name and then resolve which specific name from that.

I'm not sure exactly how to handle this in ford though. Currently, we dump all the procedures found in a generic interface into the parent module's dict of procedures. We then use this dict in various places for matching procedure names to the actual objects, for example for function calls, but also for module procedures. The issue is then that we want the generic name for looking up calls, and the specific name for the module procedures.

Having spent a bit of time looking into this, there are quite a few sharp edges to module procedures in general in Ford that I'll need to work on.

ZedThree avatar Aug 18 '22 14:08 ZedThree

I remember that some older versions of ford can be deployed through the ford document of Fortran-stdlib. Fortran-stdlib has always used the coding paradigm of submodule + module procedure. Now Fortran-stdlib also also fails to deploy. I wonder if it's possible to make the docs deploy pass briefly by identifying an old ford version.

pip install -v ford==6.1.10

I encountered the AttributeError: 'FortranSubmoduleProcedure' building stdlib-0.2.1 documentation using ford-6.1.15 and 6.1.16 while it's successfully built with ford-6.1.13 (Gentoo linux GURU repo package):

Reading file src/common.fypp
Preprocessing /var/tmp/portage/dev-libs/fortran-stdlib-0.2.1/work/stdlib-0.2.1/src/common.fypp
Processing documentation comments...
Correlating information from different parts of your project...
Traceback (most recent call last):
  File "/usr/lib/python-exec/python3.10/ford", line 8, in <module>
    sys.exit(run())
  File "/usr/lib/python3.10/site-packages/ford/__init__.py", line 645, in run
    main(proj_data, proj_docs, md)
  File "/usr/lib/python3.10/site-packages/ford/__init__.py", line 592, in main
    project.correlate()
  File "/usr/lib/python3.10/site-packages/ford/fortran_project.py", line 254, in correlate
    container.correlate(self)
  File "/usr/lib/python3.10/site-packages/ford/sourceform.py", line 1091, in correlate
    if proc.module and proc.name.lower() in self.all_procs:
AttributeError: 'FortranSubmoduleProcedure' object has no attribute 'module'

band-a-prend avatar Nov 19 '22 08:11 band-a-prend

I've got a few projects that fail with the latest version of Ford, except that using an older version of one of its dependencies works. Namely the CI scripts have this command for installing Ford:

sudo pip install ford markdown==3.3.4

everythingfunctional avatar Nov 19 '22 16:11 everythingfunctional

@band-a-prend @zoziha I've checked that #463 works with stdlib, but if you would like to double check links etc are what you expect, that would be lovely

@everythingfunctional Would you mind checking that PR with your projects too?

@rouson Your original error should be fixed in #463 now, but I've not had time to look at how to link the module procedure implementation to its interface when it's inside an interface with the same name. If you keep the docstring on the interface, then this should hopefully be ok for you

ZedThree avatar Nov 21 '22 18:11 ZedThree

@ZedThree thank you for fix. Unfortunately I could check only after 24 November.

band-a-prend avatar Nov 21 '22 19:11 band-a-prend

I'll have to work out how to get it working on my machine, but I'll try to get to it soon.

everythingfunctional avatar Nov 21 '22 19:11 everythingfunctional

@ZedThree I just edited my original comment to add an underscore to the interface name. Matching the interface and procedure names was something that I inadvertently did while trying to develop a short reproducer for the ford bug. In my intended use cases, the names don't match and I'm not certain that the original code with matching names was standard-conforming.

rouson avatar Nov 21 '22 20:11 rouson

@everythingfunctional is the sudo pip install ford markdown==3.3.4 no longer working? I've been using it CI on GitHub. On my local machine, I'm using Homebrew to install ford and this issue is blocking me from producing documentation for the initial release of a new project.

rouson avatar Nov 21 '22 20:11 rouson

@rouson , that still works, but if recent developments have made it unnecessary, that would useful to know.

everythingfunctional avatar Nov 21 '22 21:11 everythingfunctional

@everythingfunctional actually I'm not sure the workaround is working for me anymore. See the Inference-Engine CI results. Or is it possible that the issue is that the PR generated those CI results contains the first version of the deploy-docs.yml file for that repository and the deployment won't work until the PR is merged into the main branch?

rouson avatar Nov 22 '22 02:11 rouson

@rouson I checked out Inference-Engine, and with a small change to your ford.md, I managed to successfully build the docs.

@everythingfunctional I checked out rojff, as it uses submodules, and confirmed that it fails with 6.1.16, and passes with #463

I'm happy this is now working as intended, so I'll go ahead a make a new release now

ZedThree avatar Nov 22 '22 09:11 ZedThree

Fixed in v6.1.17!

ZedThree avatar Nov 22 '22 10:11 ZedThree

@ZedThree if I need your edit to Inference-Engine's ford.md file, could you please submit a pull request on the Inference-Engine repository? If I don't need your edits once I have v6.1.17, then no need to submit the PR. For now, I'm trying brew install ford --HEAD to see if that does the trick.

rouson avatar Nov 22 '22 22:11 rouson

@ZedThree thanks for working on this!

rouson avatar Nov 22 '22 22:11 rouson

@ZedThree I confirm that stdlib-0.2.1 documentation now successfully build with ford-6.1.17.

band-a-prend avatar Nov 24 '22 10:11 band-a-prend