3

I want to allocate an array in a subroutine, and then use this array in the main program and pass it to some other subroutine. In the past (F77?) the passing could be done in a common block, but nowadays the favored procedure seems to be to use a module. When I try this, as in the code example, the compiler tells me

Rank mismatch in argument ‘f’ at (1) (scalar and rank-1)

Apparently, the main program thinks that 'f' is a scalar: but, I read this code to mean that I've declared it as a one-dimensional array, both inside the subroutine and in the main program. What am I missing?

I've tried variations, such as declaring the variables as part of the module, but nothing I could think of made the compilation error-free (and some produced many more errors ;-( ). Any insight is most appreciated.

          module subs
        contains
        subroutine makef(f)
        end subroutine makef
      end module subs
c-----------------------------------------------------------------------
      program work

      use subs
      implicit none
        real, allocatable :: f(:)

      call makef(f)

      write (*,*) f
      stop
      end
c---------------------------------------------------------------------
      subroutine makef(f)
      implicit none

      real, allocatable, intent(out) :: f(:)
      integer :: i
      integer :: is

      is=10
      allocate(f(-is:is))

      do i=-is,is
        f(i)=i
      end do
      return
      end subroutine makef
Nino
  • 45
  • 2

2 Answers2

2

Modules in Fortran are not like header files in other languages which merely provide information about things defined elsewhere. There is the concept of "deferred definition" (submodules) but in this case the module should say everything about the subroutine, not simply attempt to point to its existence.

In the example of the question, we have: the main program; a module subs with module procedure makef; an external subroutine makef.

The main program uses the module subs, and its procedure makef, so reference in the main program to makef is to that module procedure not the external subroutine makef.

The module subroutine makef has the argument f which has no declaration statements, making it an implicitly declared scalar/external function. This is the compiler's message. Use implicit none in modules, just as it's in the main program and external subroutine here.

The entire definition of the subroutine should be placed in the module:

module subs
  implicit none
contains
  subroutine makef(f)
    real, allocatable, intent(out) :: f(:)
    integer :: i
    integer :: is

    is=10
    allocate(f(-is:is))

    do i=-is,is
      f(i)=i
    end do
  end subroutine makef
end module subs

Alternatively, if one does want to refer to the later implementation of an external procedure an interface block can feature in the module without declaring the subroutine itself. In this case it will still be necessary to specify the complete interface:

module subs
  implicit none

! An interface block to give an explicit interface to the external subroutine makef
  interface
     subroutine makef(f)
       implicit none
       real, allocatable, intent(out) :: f(:)
     end subroutine makef
  end interface
end module subs

In this case, don't prefer the interface block.

francescalus
  • 30,576
  • 16
  • 61
  • 96
  • Francescalus' solution works: by trial and error I had found the same approach. FYI, I had misread many earlier posts on related topics I encountered when I tried to understand how modules worked. The most (IMHO) misleading one had subroutine a ..... end subroutine a inside the module, but lacked the ..... in my comment here. I followed that one to the letter, and didn't get anywhere. Let Francescalus' clear response be an example of how to communicate clearly to those who may lack a necessary background, with an explicit example. Thanks. – Nino Aug 17 '20 at 13:41
  • *"Modules in Fortran are not like header files in other languages which merely provide information about things defined elsewhere."* Except with interface modules. With them you get almost exactly the equivalent of a C header. –  Aug 18 '20 at 06:30
  • @Jean-ClaudeArbaut, one can view interface bodies (as in the final example) as tangible things. Now, a module which consists entirely of `external` statements... – francescalus Aug 18 '20 at 08:36
  • @francescalus Yes, I didn't pay attention to the last example. It's what I meant. As in a C header, you declare the functions, which enhances type safety. The benefit over a module containing the implementation is that 1/ you don't need to have access to the source (for instance to interface with C API : Intel provides interface modules to Windows API for instance), and 2/ the Fortran binary objects are still interoperable with software that won't understand modules. I didn't check, but I believe the module interface of the IMSL is also based on this, as the older calls are still available. –  Aug 18 '20 at 10:19
  • @Jean-ClaudeArbaut, I agree entirely that modules with interface blocks/bodies are perfectly valid and useful (either as "adding explicit interfaces for old Fortran code, external procedure" or "separating from the implementation"). – francescalus Aug 18 '20 at 10:30
-1

You only placed the copy of the first and the last line into the module. That can't work. You must move the whole subroutine into the module.