Implicit real - complex conversion in fortran

But x is equivalenced to the “first” part of z, which in turn is defined by the standard to be the real part, z%re. So for default real and complex, I think it is defined by storage sequence association. I agree about nondefault kinds, they are not covered by the storage sequence requirements.

I have never heard of this possibility, and it would not be consistent with the default real and complex storage sequence requirements. With polar representations, addition becomes difficult, while multiplication and division become easy. There are certainly situations where a programmer would want to use this convention, but I don’t think a compiler could do so for its intrinsic real and complex types.

I won’t answer point by point to all the comments above, but I’m a bit puzzled to see how a simple proposal can be complexified in the discussion… The proposal is quite simple:

  • authorizing storage association through pointer association between real and complex arrays of the same kind
  • standardizing the de facto standard used by virtually all compilers, where a complex number is stored in memory with 2 real numbers of the same kind, representing the real and imaginary parts in that order.

In practice it means (1st point) that the following code would be possible:

complex, allocatable, target :: c(:)
real, pointer :: r(:)
r => c   ! or any other more appropriate and dedicated syntax

and that (second point) r(1::2) == c(:)%re and r(2::2) == c(:)%im

That’s it. One can think about a dedicated syntax if one think that r => c is breaking type safety beyond what is acceptable, but to me it’s a detail.

The proposal was also about authorizing argument association between real and complex types, but @everythingfunctional has raised that it would break backward compatibility, so I will drop this part.

I have another possible syntax. We have already %re and %im. Let’s add %reim.
If wz is a complex array wz%reim is a real array with one additional dimension. The same constraints that apply to %re or %im apply to %reim.
It will solve different points.

  1. It doesn’t break type safety.
  2. One can associate a real pointer to a complex target via %reim as it is now possible with %re or %im (even though it is still bugged in ifx and gfortran). And with pointer remapping one can associate a real one dimensional pointer to the complex one.
  3. With the additional dimension it allows “split complex” as suggested by @certik (it would be non contiguous).
  4. Could allow strange packing as the stride sm in the CFI_dim_t structure is in bytes.
  5. Can be passed as definable arguments to a procedure that expects a real argument.

One needs an additional dimension of size 2 otherwise you cannot always have a real view to a complex array.
For example if wz(N) is not contiguous you cannot have a real view like wr(2N) while you can always have a non contiguous real view like wr(2,N).

complex, target :: wz(:)
real, pointer :: wr(:,:)
real, pointer :: wc(:)
wr => wz%reim  ! this will be always permitted
wc(1:size(wz)*2) => wz%reim  ! this is permitted only if wz%reim is contiguous (as it is already)

As I wrote just above, to me the syntax is just a detail at this point. But I find the %reim principle interesting. Note however that it would imply other changes in the standard, as the x%y notation is invalid if neither x nor y are scalar objects.

yes

I’m not sure, as long as the standard doesn’t consider row major storage (which is beyond the current proposal)

I am still disagreeing with this. There’s nothing really useful that can be done easily with the wr(2,N) view, beyond what can already be done easily with the %re and %im views. In the proposal, one of the constraints is that the target arrays are contiguous, such that the real view is directly flattened.

The point is that one must always take into account what happen when wz is not contiguous. If wz is contiguous and if complex arrays are stored as [R1,I1, R2,I2,…] then it can always be pointed by a one dimensional real pointer following your idea. Moreover there is always the sequence association. That is if a procedure is defined like this:

subroutine   proc(wr, N)
integer, intent(in) :: N  
real,intent(inout) :: wr(N)
...
end subroutine

you can call it directly as:

call proc(wz%reim, size(wz)*2)

Keeping in mind that if wz%reim is not contiguous a copying in copying out will take place.
But there is not much you can do in this case.

I don’t understand your point. x can be a section, reim shouldn’t have anything beyond.
You wouldn’t be allowed to write wz(::2)%reim(1), of course, but you can just write wz(::2)%re in this case.

That’s why in the proposal there’s the contiguity constraint on the target array. If the target array is not contiguous, then the association is not allowed, and it can be checked at compile-time.

Well, we agree to disagree :slight_smile: .

I think it is better to have a first step that doesn’t rely on contiguity, and later on further steps that may rely on contiguity, than a much bigger step that necessarily rely on contiguity from the beginning.

The point is that %reim is not a scalar, and you actually have to define what it is.

The purpose of the proposal is not about accessing the real and imaginary parts but about manipulating a real array when it is supposed to contain real elements, not complex elements. With wr(2*N) I can easily write for instance a cumulative sum, or call an existing routine that performs a cumulative sum.

real, pointer :: wr(2*N)
interface 
   subroutine cumsum(a)
   real, intent(inout) :: a(:)
   end subroutine cumsum
end interface 

do i = 2, N
   wr(i) = wr(i) + wr(i-1)
end do 
! or
call cumsum(wr)

With wr(2,N) it’s not that simple

real, pointer :: wr(2,N)

wr(2,1) = wr(2,1) + wr(1,1)
do i = 2, N
   wr(1,i) = wr(1,i) + wr(2,i-1)
   wr(2,i) = wr(2,i) + wr(1,i)
end do 
! or
call cumsum(wr)   ! NO WAY !!!

Well if you have:

subroutine cumsum(wr)
real :: wr(:); integer i
do i=2, size(wr)
   wr(i) = wr(i) + wr(i-1)
enddo
end subroutine

you can do:

complex, target, contiguous :: wz(:)
real, pointer, contiguous :: wr(:)

wr(1:2*size(wz)) => wz%reim

call cumsum(wr)

Compared to yours:

complex, target, contiguous :: wz(:)
real, pointer, contiguous :: wr(:)

wr => wz

call cumsum(wr)

Slightly more verbose, but not much.
Moreover if you got:

subroutine cumsum(wr, n)
integer :: n; real :: wr(n); integer i
do i=2, n
   wr(i) = wr(i) + wr(i-1)
enddo
end subroutine

You can:

complex :: wz(:)

call cumsum(wz%reim, 2*size(wz))

without any problem with type safety.

Again it shows that the intermediate step with the additional dimension is useless in itself, and you have to flatten it before doing anything useful with it.

Which means that you are restricted to routines with an explicit shape or assumed size dummy argument.