New standards of fortran are more strict about the mismatches between the calls and the procedure definition. However, there are a lot of legacy codes where, for instance, complex array is viewed as a collection of reals. This still can be properly compiled by lowering such errors to warnings. In gfortran it is achieved with the -fallow-argument-mismatch . This provides a temporary working solution, but is unsatisfactory in long-run.
program polymorphic_types_wrk
use Maux_wrk
implicit none
integer, parameter :: dp = kind(1.d0), dim = 10
real(dp) :: wd(2 * dim)
complex(dp) :: wz(dim)
call random_number(wd)
call copy_real_complex_nonconformant(dim, wd, wz)
end program polymorphic_types_wrk
module Maux_wrk
contains
subroutine copy_real_complex_nonconformant(dim, wd, wz)
implicit none
integer, parameter :: dp = kind(1.d0)
integer, intent(in) :: dim
real(dp), intent(in) :: wd(*)
complex(dp), intent(out):: wz(*)
call zcopy(dim, wd, 1, wz, 1)
end subroutine copy_real_complex_nonconformant
end module Maux_wrk
In this example, zcopy is a standard BLAS subroutine. The goal is to copy an array of reals to an array of complexes assuming the data is contiguous.
What would be your recommendation of implementing this code without the use of -fallow-argument-mismatch ? I would like to be more standard conforming, and I would not like to disable arguments check everywhere.
One possible trick is go over c_loc() and c_f_pointer() to create a complex pointer to a real array. The following code should to the trick:
program test
use iso_c_binding, only : c_loc, c_f_pointer
implicit none
integer, parameter :: nn = 5
real, target :: rarray(2 * nn)
complex :: carray(nn)
complex, pointer, contiguous :: cptr(:)
integer :: ii
rarray(:) = [(real(ii), ii = 1, 2 * nn)]
call c_f_pointer(c_loc(rarray), cptr, [nn])
carray(:) = 0.0
write(*, "(a)") "Content of carray before copy:"
write(*, "(2F6.1)") carray
call ccopy(cptr, carray)
write(*, "(a)") "Content of carray after copy:"
write(*, "(2F6.1)") carray
carray(:) = 0.0
write(*, "(a)") "Content of carray before copy (F77):"
write(*, "(2F6.1)") carray
call ccopy_f77(nn, cptr, carray)
write(*, "(a)") "Content of carray after copy (F77):"
write(*, "(2F6.1)") carray
contains
subroutine ccopy(carray1, carray2)
complex, intent(in) :: carray1(:)
complex, intent(out) :: carray2(:)
carray2(:) = carray1
end subroutine ccopy
subroutine ccopy_f77(nn, carray1, carray2)
integer, intent(in) :: nn
complex, intent(in) :: carray1(*)
complex, intent(out) :: carray2(*)
integer :: ii
do ii = 1, nn
carray2(ii) = carray1(ii)
end do
end subroutine ccopy_f77
end program test
I think this should be standard conforming and robust, without the need for degradation of the argument mismatch detection. The target attribute is needed on the real array, and the contiguous attribute of the complex pointer should ensure, that no copy-in/copy-out happens, when passing it to F77-style routines.
Indeed. If it just a copy, transfer() is a much simpler solution. If the problem is more general (passing real arrays to complex routines), the c_loc() solution would help to avoid to create unnecessary copies…
Definitely, but one should check they are contiguous.
By the way it is so common to see a complex array like a real array that I suggest a small language enhancement, basically allowing a real pointer to point to a complex array on the provision that the real pointer has an additional first dimension (that will be of size 2 after the array assignment):
complex, target :: a(N,M,K)
real, pointer :: ap(:,:,:,:)
ap => a
print *,shape(a)
will print (whatever are N, M, K):
2, N, M, K
The converse may not be always possible.
That previous example, with this syntax enhancement, will be:
I don’t need to put the size of the array on the left side or the right side as I know that they are of the correct size. Moreover I used as mold the same array on the left.
That was handy as, this way, I don’t need to specify the size in the transfer function. The output will be a one dimension array able to contain all the meaningful data present in the source.
I had no hand in the proposal. The best support would probably be to read the proposal, provide feedback, give it a like on GitHub and voice your support in a comment.
You could even submit a symbolical letter like this one:
Why is it not standard conforming? (I’ve probably missed those discussions, you can also just direct me to them.) The real type is interoperable with float, so c_loc() gives a C-pointer to the array data. In C, there should be no difference between a float array and a float _Complex array half of its size. So, in C, I could create a float _Complex pointer to this data and access it as if they were complex numbers. But since float _Complex is interoperable with complex in Fortran, I can use c_f_pointer to create a complex pointer to it. I don’t see any problems here.
My “Modern Fortran” contains the following about c_f_pointer (cptr, fptr [, shape]): cptr must not be the C address of a Fortran variable that does not have the ŧarget attribute. But in my example, the real array has the target attribute, so I see no problem here either.
But I might, of course, miss something obvious, so any clarification would be welcome.
@PierU Thanks a lot! In the mean time, I have also found the relevant part in the standard, and I still think,that my example is standard conforming.
Case (ii) does not apply, because the c_loc argument (a real array) is interoperable.
Case (i) only requires, that FPTR shall be of type and type parameter, which are interoperable with the data entity CPTR points to. And since ̇CPTR points to the beginning of a contiguous memory block containing an even number of real numbers, it is IMO interoperable with the complex data type. So the requirement is fulfilled.
And finally the additional requirement for CPTR
The value of CPTR shall not be the C address of a Fortran variable that does not have the TARGET
attribute.
is also fulfilled. Do I miss something? Can you think about any realistic scenario, where this workaround would fail with a standard conforming compiler?
Of course, it would be nice to have better ways to do it (like your proposal), but this way is probably a possible (IMO standard conforming ) workaround so far.
You’re right, I was quoting somewhat sloppy. So the exact wording is
If the value of CPTR is the C address of an interoperable data entity, FPTR
shall be a data pointer with type and type parameter values interoperable with
the type of the entity
I am definitely not a language lawyer: But, if CPTR is the C-address of a contiguous block of an even number or real/float numbers, then it points to a data entity, which is interoperable with complex, doesn’t it?
The only scenario I can imagine, where this would fail (and not being standard conforming), if the representation of the data in a complex number was not equivalent to the representation of the data in a real array with two elements. And if I understood the discussion correctly, it is not likely to ever happen.
The standard has not changed at all in this regard. The first Fortran standard, FORTRAN 66, says:
The actual arguments, which constitute the argument list, must agree in order, number and type with the corresponding dummy arguments in the defining program unit.
The current standard says, “The dummy argument shall be type compatible with the actual argument.” However, COMPLEX is not type compatible with REAL.
Compilers have gotten better at diagnosing argument type mismatches, and some do it by default, which is good.
interoperable has a stricter meaning than just pointing to a right memory area, it ties together a specific Fortran type to a specific C type.
No doubt the c_f_pointer trick will work with virtually all the existing compilers (and as a matter of fact I do use it), because they all follow the implicit convention where a complex number is represented in memory by the sequence real part / imaginary part. Nonetheless, it doesn’t make it standard conforming. It’s the same as passing a complex array actual argument to a dummy real array argument of a routine without explicit interface: it “works” (for the same reason as above), but it’s not standard conforming.
The current standard says, “The dummy argument shall be type compatible with the actual argument.” However, COMPLEX is not type compatible with REAL.
What is the impediment to this? If the standards committee were to make them officially compatible would that break something else in the standard?