As already mentioned above, in modern code, subblocks of larger matrices can be passed using array notation. The example given above by @certik was f(A(3:5,3:5))
. If the subprogram interface is implicit, then this does trigger copy-in/copy-out argument association when this subblock is not contiguous (it would be contiguous if, for example, the actual array were declared as (3:5,*)
). Even if the interface is explicit, there are still cases where copy-in/copy-out is triggered, but if the dummy argument is assumed shape, then most compilers will pass the original data along with the metadata that accounts for the spacings between elements of rows and columns. In f77 code, this subblock would be passed as something like f(3,3,A(3,3),lda)
. See for example any of the dozens of LAPACK routines that work with matrices that use this convention. The first two arguments indicate to the subroutine that a 3x3 subblock of the matrix is being passed. The A(3,3)
argument is the first element in the subblock. The lda
argument is the leading dimension of the actual matrix argument and is used within the subroutine as the spacing between elements within a row of the dummy argument. This is all based on storage sequence association, which was defined early on within fortran, before even f66 I think, and still applies in modern fortran for explicit shape dummy arguments (m,n)
, assumed size dummy arguments (m,*)
, and for arguments with the contiguous attribute.
I think the original proposal in this discussion was to print warnings whenever this argument passing convention is used by a programmer. Although I do think this would be a good option for the programmer to be able to invoke from time to time during debugging, I do not think it should be the default because 1) it is a common coding convention in legacy code, and 2) it is standard fortran (perhaps with some corner case exceptions). I do agree that the modern conventions have advantages, which is why they were introduced into the language in the first place, but as I stated previously, it is very frustrating to a programmer for a compiler to print false positive warnings for standard features that the programmer intentionally uses.
I don’t think I have written any new code in the last 30 years that uses this convention, but of course I use some half a million to a million lines of legacy code where it is used throughout, and even new code that references one of these legacy routines would still use this convention.
The corner case exceptions include the following situation. Suppose the actual argument array is dimensioned A(6,3)
. That array has 18 contiguous elements in the storage sequence. Suppose the subroutine is invoked as above with actual arguments f(3,3,A,6)
and that it is written as
subroutine f(m,n,D,ldd)
integer :: m, n, ldd, D(ldd,n)
The dummy array will have dimension D(6,3)
, and if written correctly, only the leading 3x3 subblock of elements of that array will be referenced. The actual matrix storage locations of those nine elements are (4,5,6,10,11,12,16,17,18), and they correspond to the dummy matrix storage locations (1,2,3,7,8,9,13,14,15). If only those elements are referenced, then all is fine. But the dummy array actually has 18 elements in its scope. Of those 18 elements, the dummy storage locations (4,5,6,10,11,12) map back to the actual array elements (7,8,9,13,14,15). So even if those elements were referenced (presumably by mistake, because they are outside the intended 3x3 subblock), it would not violate the standard because those dummy elements have a defined association with actual matrix elements that exist. (This shows one advantage of the newer A(3:5,3:5)
actual argument convention, which would allow the compiler to detect these out of bounds errors.)
However, there are three more dummy array elements with dummy storage sequence (16,17,18) that map back to actual storage sequence locations (19,20,21). These elements are beyond the range of the actual array argument. In practice, if those dummy elements are not referenced, then the code works as intended. This has been the case for all versions of fortran, at least back to f66. But it is unclear if this convention actually satisfies the letter of the standard. It is possible that even the dummy declaration itself, D(6,3)
in this example, violates the standard, even if those trailing three elements in the storage sequence are never referenced. Maybe someone who is familiar with the standard wording on this issue can comment on this?
The workaround for this corner case is to declare the dummy argument instead as D(lda,*)
, which is D(6,*)
in the above example. I think this is what LAPACK does, although earlier libraries such as EISPACK and LINPACK could not do that prior to f77. This basically tells the compiler that the programmer is taking all responsibility to stay within the actual argument storage locations, and to not do any bounds checking at all on that last dimension. This has some disadvantages when debugging the code, and even during production runs, because the compiler cannot detect at run time any array bounds violations for that second dimension, and that can cause undetected program errors or even memory access faults during execution of the code.