m.chips
m.chips

Reputation: 173

How to get Fortran to warn me if I pass an allocated array to a subroutine with mismatching dimensions?

I just spent too much time resolving a bug in my program. I call a subroutine (let's call is mysub) defined as:

subroutine mysub(hourval,dayval,some,more,args)
use globals
implicit none

real:: hourval(24,ncol,nrow)
real:: dayval(ncol,nrow)

...
end

where hourval and dayval are the arrays that contain the output of the calculations in the subroutine, and nrow and ncol are integers.

The call to the subroutine looks like

call mysub(daygrid,hourgrid,some,more,args)

where daygrid and hourgrid are arrays that were previously allocated. hourgrid has 3 dimensions (24,ncol,nrow), while daygrid has 2 dimensions (ncol,nrow). All variables mentioned until now (hourgrid, daygrid, nrow, ncol) are declared in the module globals.

I kept getting strange results and finally noticed that I switched the order of hourgrid and daygrid in my subroutine call, and changing this solved my problem (the actual variables in my program have less explicit names, which made the mistake a bit harder to spot). However, the program would compile normally, and I would not get any relevant error messages in the logfile.

I'm wondering, therefore, if there is a way to get a message, either at compile-time or at run-time, pointing to that error. I'm using Intel Fortran 11.1 on Linux with the following compiler flags:

-O3 -C -pg -traceback -g

I was actually counting on the -C flag (equivalent to -check all) to detect this kind of bugs, as it implies the option -check bounds. Is there anything else I can do?

Upvotes: 2

Views: 466

Answers (3)

steabert
steabert

Reputation: 6918

The suggestion to use assume-shape is really good (so a +1 there), as that will detect shape mismatches. However, if your arrays have the same shape then you can run again into trouble. The reason is that there is no way the compiler can know what you intended to do if the arguments match what is in the interface.

So, a better solution to avoid the problem might be to use named arguments (in addition to using assumed shape with explicit interface that is). In the example below, sun and moon are similar arrays that are to be processed. When you switch them around, the subroutine call still works but interchanges the use of the arrays. When using named arguments, no matter what order you put them in, the subroutine will connect it to the correct dummy argument.

program named_arguments
integer, parameter :: n = 2
integer, allocatable :: sun(:,:), moon(:,:)

allocate(sun(n,n))
allocate(moon(n,n))

call mysub(sun,moon)

write(*,*) sun
write(*,*) moon

call mysub(moon,sun)

write(*,*) sun
write(*,*) moon

call mysub(night=moon,day=sun)

write(*,*) sun
write(*,*) moon

contains

subroutine mysub(day,night)
integer :: day(:,:), night(:,:)

day = 1
night = 2
end subroutine

end program

As you probably give meaningful names to the dummy arguments that reflect their usage, connecting them by name to the actual arguments in the call is a good practice to avoid this kind of problem.

In your case:

call mysub(dayval=daygrid,hourval=hourgrid,...)

would do the correct thing as using named arguments doesn't rely on the order of the dummy arguments.

Upvotes: 2

Mali Remorker
Mali Remorker

Reputation: 1276

You may want to use assumed shape dummy arguments to catch errors like this (i.e real, dimension(:,:) :: dayval and real, dimension(:,:,:) :: hourval.)

From Modern Fortran Explained by Metcalf, Cohen and Reid (2011) :

Shape, or character disagreement cannot occur when a dummy argument is assumed-shape.

Also, it does not strike me as the most amazing coding style to pass variables already available through the use statement as the actual arguments to the same subroutine.

Upvotes: 6

francescalus
francescalus

Reputation: 32451

It could be possible to have this noticed by the compiler at compile time if the array sizes are "known" at that time. How you manage depends on your code.

If there is an (explicit or implicit) interface available ifort should do this for you without fail.

Even without an interface you may have some joy with the compile option -warn interface: this essentially has better-than-usual checking where an interface is "obvious" even if not "available". [I hope someone can explain better.]

It's possibly worth noting that, unless there is a failing with the interface, the number of ranks isn't necessarily a problem. Instead it's down to storage extents. In your case the storages are quite different.[1]

In response to your comment, something about interfaces.

You're already using modules, so if mysub is in a(nother) module used by the host of the call then an explicit interface will be available. This is a good thing to do. Many details can be found on SO of interface explanations.

Finally, I'll say that even though hourgrid, daygrid are declared in a module, the dummy arguments by those names are distinct things.

Edit, to be more explicit.

[1] Solving the problem where the dimensions "mismatch" is in general a more difficult problem. The Fortran 2008 standard says in section 12.5.2.11, when it comes to your subroutine declaration (my emphasis)

An actual argument that represents an element sequence and corresponds to a dummy argument that is an array is sequence associated with the dummy argument if the dummy argument is an explicit-shape or assumed-size array. The rank and shape of the actual argument need not agree with the rank and shape of the dummy argument, but the number of elements in the dummy argument shall not exceed the number of elements in the element sequence of the actual argument.

Your dummies are explicit shape. So, in general you can't detect a mismatch, but in this case the fact that your dummy hourval is bigger (by a factor of 24) than the dayval actual argument, is the part allowing your compiler to help.

More generally, one has to be careful when using explicit shape dummies.

Upvotes: 3

Related Questions