TRANSFER of type with ultimate ALLOCATABLE component
This is intended as a discussion with any language lawyers, rather than a request for help.
I have recently become aware of what I think is a bug, or flaw in the standard. Consider the following code snippet:
type mytype
real, allocatable, dimension(:) :: c
end type mytype
type(mytype) :: a, b
allocate(a%c(8))
b = transfer(a, b)
write(*,*) 'Storage (as integer) of a = ', storage_size(a)/storage_size('a')
write(*,*) 'Storage (as integer) of b = ', storage_size(b)/storage_size('a')
print *, 'Allocation status(a,b)=', allocated(a),allocated(b)
if( allocated(b%c)) deallocate(b%c)
print *, 'Allocation status(a,b)=', allocated(a),allocated(b)
if( allocated(a%c)) deallocate(a%c)
I can find no language in the description of the TRANSFER intrinsic in the standard nor in Modern Fortran Explained which would flag this as non-conforming code. And yet it is clearly wrong. What does it mean to transfer the bits of an allocatable array descriptor to another variable, but not the actual? (Invariably as the storage for âa%câ is dynamically allocated from the heap.) By contrast to the rules for an EQUIVALENCE statement clearly state that a structure with an ultimate allocatable component is not allowed.
In practice some testing with different compilers and valgrind reveals a range of interesting undefined behaviours. In all cases except NAG, ALLOCATED(b%c) becomes TRUE following the transfer and DEALLOCATE(b%c) does not change the allocated status of a%c.
NAG:
raises a compile-time ERROR
GNU:
b appears to become a âshallowâ copy of a, and changing b%c(i) results in a similar change in a%c(i). DEALLOCATE(b%c) followed by DEALLOCATE(a%c) results in a double-free error.
FLANG(new) and CRAYFTN:
a%c becomes deallocated following the transfer - at least the storage is freed, although the ALLOCATED flag remains true. (Presumably an unintended side-effect of the optimisation following deallocation of the TRANSFER function result). DEALLOCATING both a%c and b%c results in a double-free detected" error from the C runtime.
IFX and NVFORTRAN:
These compilers appear to b as a âdeepâ copy of a, despite STORAGE_SIZE(b) being too small to contain the data. The address of the first element of a%c does not appear to point at the same storage as b%c. Valgrind reveals no implicit deallocation or out-of-bounds access. Presumably this arises from treading the assignment to âbâ using the F2003 rules without attempting to optimise along with the deallocation of the âtransferâ result.
By contrast if the type contains a pointer instead of an allocatable, everything compiles and works as expected with all six compilers; following the TRANSFER, b becomes a shallow copy of a, with b%c associated with a%c.
It seems that this must be an oversight in the standard; this issue only arose when allocatable components of a derived type were added in Fortran 2003. Previously there was no such issue. However the issue does not appear to have been resolved, or even discussed for Fortran 2008, 2013, 2018, 2023 and I was unable to find any discussion on the J3 documents website. The only reference to any discussion I was able to find was a 2011 thread on comp.lang.fortran where Richard Maine was of the opinion that this usage might be forbidden by the âcatch-allâ clause that if the standard did not specify the behaviour of a construct then it is non-conforming!
If anyone as a suggestion of how to draw this to the committeeâs attention I would be grateful.