1

I read that Fortran (unlike C) knows the size of arrays, and can infer it when I pass the array into function (subroutine). But I cannot make it work.

Minimum example:

program.f90

program test_arrayPass
    ! === variables
    integer :: n
    real, dimension (:,:),allocatable :: data
    ! === Body
    n = 3
    allocate(data(n,n))
    write (*,*) "DEBUG test_arrayPass : shape(data) ", shape(data)
    call writeToArray( data )
end

writeToArray.f90

subroutine writeToArray( data )
    real   , dimension(:,:), intent(out) :: data   ! none of these works
    !real   , dimension(:,:), intent(in) :: data   ! none of these works
    !real   , dimension(*,*), intent(in) :: data   ! none of these works
    write (*,*) "DEBUG writeToArray : shape(data) ", shape(data)
end

Makefile

FC      := /usr/bin/gfortran
FCFLAGS = -g -Og -c -fdefault-real-8 -fbacktrace -fno-align-commons -fbounds-check
FLFLAGS =

SRCS = $(patsubst %.f90, %.o, $(wildcard *.f90))

PROGRAM = program

all: $(PROGRAM)

$(PROGRAM): $(SRCS)
    $(FC) $(FLFLAGS) -o $@ $^

%.o: %.f90
    $(FC) $(FCFLAGS) -o $@ $<

clean:
    rm -f *.o *.mod

Result

with dimension(:,:) it does not know corret size inside writeToArray()

 DEBUG test_arrayPass : shape(data)            3           3
 DEBUG writeToArray : shape(data)            1           0

with dimension(*,*) it produces error

    5 |     real   , dimension(*,*), intent(in) :: data
      |                                               1
Error: Non-PARAMETER symbol ‘data’ at (1) cannot be implied-shape
Prokop Hapala
  • 2,424
  • 2
  • 30
  • 59
  • 1
    You need the subroutine to have an [explicit interface](https://stackoverflow.com/questions/55270873/correct-implementation-of-an-explicit-interface-in-fortran). The easiest way to do this is to put the subroutine in a module and `use` the module. – veryreverie Nov 30 '21 at 15:15
  • 2
    You have two (at least) different problems here. One is a syntax error, but the "wrong result" is that you aren't giving the subroutine an _explicit interface_ in the main program. This is a critical programming error. – francescalus Nov 30 '21 at 15:15
  • OK, with interface it does work. But it was absolutely not obvious the error has anything to do with interfaces. The code I'm working on has no interfaces anywhere. – Prokop Hapala Nov 30 '21 at 15:32
  • isn't there some easier way which can be inserted everywhere without either 1) putting everything into modules or 2) writing interfaces everywhere the function is used? – Prokop Hapala Nov 30 '21 at 15:34
  • 2
    Whenever you call a subroutine/function you are dealing with interfaces. By "explicit interface" and "implicit interface" we don't mean interface blocks. "Interface" is what the main program knows about the subroutine. – francescalus Nov 30 '21 at 15:36
  • 2
    You _want_ to put things in modules. Modules are good. They solve many problems you don't know you have yet. – francescalus Nov 30 '21 at 15:37
  • 1
    The main program excepted all Fortran procedures written in the last 30 years should have gone into modules. Not following this simple advice has caused many people much grief they could easily have avoided. – Ian Bush Nov 30 '21 at 16:03
  • As @Jalex mentioned put into a MODULE or a MODULE in a library. – Holmz Nov 30 '21 at 22:56

2 Answers2

4

Fortran can indeed assume (not infer) the shape or size of a dummy argument.

Assuming shape and size are two different things, with different requirements. With the dummy argument (intent doesn't matter)

real   , dimension(:,:), intent(out) :: data

you are assuming its shape (by using : in the declaration). The problem you are seeing with this attempt is that you do not provide an explicit interface in your main program. This is such a common mistake that has been covered many times here that I won't expand more: searching for "explicit interface" will provide lots of ideas.

Instead, I'll talk about your other attempt:

real   , dimension(*,*), intent(in) :: data

This isn't assuming the argument's shape, and neither is it assuming its size. An assumed size dummy argument would look like

real   , dimension(*), intent(in) :: data

or

real   , dimension(3,*), intent(in) :: data

In an assumed size array you can only have "unknown" extent in one of the ranks, and that must be the final one. However, the syntax for an implied-shape array is as you have, but only named constants (and not dummy arguments) may be implied-shape. Your compiler is complaining about this error, not thinking you are trying to talk about an assumed-size dummy array.

Assumed-size array arguments don't require such an explicit interface, but to make this work you'll need to assume just one dimension. (You can make associate a rank-2 array with a rank-1 assumed-size dummy argument if you don't want to guess one of the extents in the subroutine.)

But unless you have a really good reason you want to use an assumed-shape (:,:) array with an explicit interface. (If you have a good reason to use an implicit interface, you need to think very hard about whether you should be doing what you are doing.)

Finally, with an assumed-size array, you can't inquire of the shape of the array (using shape), its size (size) or its upper bounds (ubound). For size and upper bound you can ask about these properties of the initial extents, just not the final. You can use an assumed-size array, but you'll need some other source of information about its shape.

francescalus
  • 30,576
  • 16
  • 61
  • 96
1

Minimal working example:

program test_arrayPass
    ! === variables
    integer :: n
    real, dimension (:,:),allocatable :: data
    ! === Body
    n = 3
    allocate(data(n,n))
    write (*,*) "DEBUG test_arrayPass : shape(data) ", shape(data)
    call writeToArray( data )

contains

subroutine writeToArray( data )
    ! === variables
    real   , dimension(:,:), intent(out) :: data   
    integer :: n, m, k
    ! === Body
    write (*,*) "DEBUG writeToArray : shape(data) ", shape(data)
    n = size(data,1)
    m = size(data,2)

    data = reshape( [(real(k), k=1,n*m)], [n,m] )

end subroutine

end program

The problem with your code is that the subroutine writeToArray is declared outside the scope of the program. You either contain it within the program, or you put in a module.

JAlex
  • 1,486
  • 8
  • 19