ISO/IEC JTC1/SC22/WG5-N1441 Dynamic arrays and interfacing with C John Reid and Aleksandar Donev Disclaimer: John would like to make it clear that he is writing this paper as an individual and not as Convener. 1. INTRODUCTION We are writing this paper because of our belief that the present proposal for interoperability with C (J3/01-007R2, section 15) has a serious deficiency. It is impossible for a Fortran procedure, while being called from C, to allocate an array and make that array available to the C code. Conversely, it is impossible for a C function, while being called from Fortran, to allocate an array and make that array available to the Fortran code. We have both found a need for this feature within our workplaces. John has colleagues who call Fortran from C extensively and wish to be able to use structures with allocatable components to hold the data pertinent to each particular problem. Aleksandar often interfaces with C graph and sparse-matrix libraries, which return results in the form of dynamic C arrays, that also need to be used in Fortran. It is our belief that if this requirement is not addressed now, there will be a strong request for it next year when the draft is sent out for public comment. We propose a simple way to do this here. It also has the side benefit of providing a dynamic array within Fortran that has far less storage overheads than allocatable or pointer arrays. 2. THE MOVABLE ARRAY Our starting point is the assumed-size array, which is already interoperable with C provided its rank is one. We call our new sort of array 'movable' for the moment. We are not committed to this adjective, but we want to avoid 'pointer' since this is already used extensively in the standard with another meaning. A movable array can be moved from one target to another with the pointer assignment statement: real, movable :: a(*) real, target :: t(100) a => t and allocated and deallocated: allocate (a(30)) deallocate (a) We do not want any array copying to take place with such a pointer assignment, so the right-hand side must be an array that is movable, assumed-size, allocatable, or explicit-size. It must have the same type and must have the target attribute. An element of such an array is allowed, too (as for the actual argument corresponding to an assumed-size dummy array). The ranks are not required to agree (again, as for the actual argument corresponding to an assumed-size dummy array), and in such cases array element sequence association between the target and the movable array is implied. Deallocation will be allowed only if the target was allocated as a movable array. Movable arrays can also be null, tested for null, and tested for being associated with the same target: real, movable :: a(*), b(*)=>null(), c(*) ... a => null() nullify(c) if (associated(b)) then ... if (associated(a,b)) then The two-argument version tests only that a and b share the same first element. We expect the implementation of a movable array to be like that for an assumed-size array. Mostly, its descriptor will consist of an address only. However, a debugging compiler might also hold its size and a flag to indicate whether the target was allocated as a movable array. An explicit interface will not be required. If the dummy argument is a movable array, the actual argument must be a movable array. If the actual argument is a movable array, the dummy argument must be a movable, explicit-shape or assumed-size array; the calling program passes the descriptor and receives it on return. We will also permit movable arrays as components of types: type my_data real, movable :: a(*) => null() end type my_data and we permit multi-rank movable arrays: subroutine mine(lda) integer :: lda real, movable :: a(lda,*) type my_data integer :: lda real, movable :: a(lda,*) end type my_data The bounds must be defined whenever a movable array is referenced and must not be altered while a movable array is associated with a target. If a multi-rank movable array is a component of a type, the bounds of its leading extents must be integer expressions depending only on other components of the type. For the sake of optimization, we propose that the rules that apply to assumed-size arrays with respect to aliasing should apply also to movable arrays. While a movable array is associated with a target, action that affects value of the target must be taken through the movable array. 3. INTEROPERABILITY WITH C A movable array is interoperable with a C pointer provided their types correspond. It may be a component of a type that corresponds to a C struct type with a C pointer of a corresponding type in the corresponding position. Entities of such types may be passed in a procedure call under the existing rules. If a movable array appears directly in an argument list, the VALUE attribute refers to the descriptor (usually just an integer holding the address). If the VALUE attribute is present, the corresponding actual argument must be a C pointer whose referenced type is interoperable with the type of the movable array. If absent, the corresponding actual argument must be a pointer to a pointer whose referenced type is interoperable with the type of the movable array. For example, void AllocateIntegerArray(int ** a, const int n) { *a=(int *) malloc(n*sizeof(int)); } is interoperable with: interface subroutine AllocateIntegerArray(a, n), bind(C) use iso_c_binding integer(c_int), dimension(*), movable :: a integer(c_int), intent(in), value :: n end subroutine AllocateIntegerArray end interface Note that a movable array provides a way to dereference some C pointers (cf NOTE 15.7).