Interoperability of assumed-rank arguments (TS 29113)

To facilitate the interoperability with C functions that accept arguments of arbitrary rank including scalar arguments, assumed-rank arguments are introduced in Fortran.

Rules

If a C descriptor is for a nonallocatable nonpointer assumed-rank argument, the following rules apply:
  • The attribute member of the C descriptor is CFI_attribute_other.
  • The lower_bound member of each element of the dim member of the C descriptor is zero.

The lower bound of every dimension of a nonallocatable nonpointer assumed-rank argument on the Fortran side is always one.

If a dummy argument in an interoperable interface is assumed-rank, the corresponding C formal parameter is interpreted as the address of a C descriptor.

For an interoperable procedure with a Fortran interface that has an assumed-rank dummy argument with the CONTIGUOUS attribute, the associated actual argument can be noncontiguous.
  • If the procedure is invoked from Fortran or the procedure is a Fortran procedure, XL Fortran ensures that the assumed-rank dummy argument is contiguous.
  • If the procedure is invoked from C and the procedure is a C procedure, the C procedure must handle the noncontiguous arguments that might be passed to it.
Restrictions:
  • If a procedure has an assumed-rank dummy argument, the procedure must have an explicit interface.
  • If an object is passed to a Fortran procedure as a nonallocatable nonpointer assumed-rank dummy argument, the lifetime of the object cannot end before the return from the procedure call.
  • In the following situations, the C descriptor cannot be modified:
    • Its address is a formal parameter that corresponds to a Fortran actual argument and the corresponding dummy argument in the Fortran interface is for a nonallocatable nonpointer assumed-rank argument.
    • Its address is a C actual argument that corresponds to a Fortran dummy argument and the corresponding dummy argument in the Fortran interface is for a nonallocatable nonpointer assumed-rank argument.

Examples

This example calls the do_square() function from the Fortran side to calculate the square of every second element of each array, such as scalar objects, one-dimensional, and two-dimensional arrays.
PROGRAM mysquare

  USE, INTRINSIC :: iso_c_binding
  IMPLICIT NONE

  INTERFACE
     SUBROUTINE do_square(x) BIND(C)
       IMPORT
       INTEGER(C_INT), INTENT(INOUT) :: x(..)
     END
  END INTERFACE
         
  INTEGER :: i
  INTEGER(C_INT) :: v_scalar = -1
  INTEGER(C_INT), TARGET :: v_1d(10) = [(i, i=1,10,1)]
  INTEGER(C_INT), TARGET :: v_2d(10,3) = RESHAPE([(i, i=1,30,1)], [10,3])
  INTEGER(C_INT), POINTER :: p_1d(:), p_2d(:,:)

  PRINT *, "before value of scalar:", v_scalar
  PRINT *, "before value of vector:", v_1d
  PRINT *, "before value of 2D:", v_2d
      
  ! Square the scalar value.
  CALL do_square(v_scalar)

  ! Square every second element of each array.
  p_1d => v_1d(::2)
  p_2d => v_2d(::2,::2)
  CALL do_square(p_1d)
  CALL do_square(p_2d)

  PRINT *, "after value of scalar:", v_scalar
  PRINT *, "after value of vector:", v_1d
  PRINT *, "after value of 2D:", v_2d

END
The do_square() function is defined as follows on the C side:
#include <assert.h>
#include "ISO_Fortran_binding.h"

void do_square(CFI_cdesc_t * x)
{
  assert(x->type == CFI_type_int);

  switch (x->rank)
  {
  case 0:
    {
      int v = *(int *)x->base_addr;
      *(int *)x->base_addr = v * v;
    }
    break;

  case 1:
    {
      char * p = (char *)x->base_addr;
      int i;

      char * tp = p;
      for(i = 0; i < x->dim[0].extent; i++)
      {
        int v = *(int *)tp;
        *(int *)tp = v * v;
        tp += x->dim[0].sm; // Advance to the next element.
      }
    }
    break;

  case 2:
    {
      CFI_index_t extents[2];
      CFI_index_t strides[2];
      int i, j;
      char * p = (char *)x->base_addr;

      extents[0] = x->dim[0].extent;
      extents[1] = x->dim[1].extent;
      strides[0] = x->dim[0].sm;
      strides[1] = x->dim[1].sm;

      for(i = 0; i < extents[1]; i++)
      {
        char * tp = p;
        for(j = 0; j < extents[0]; j++)
        {
          int v = *(int *)tp;
          *(int *)tp = v * v;
          tp += strides[0]; // Advance to the next element in dimension 1.
        }
        p += strides[1]; // Advance to the next element in dimension 2.
      }
    }
    break;

  default:
    assert(0);
    break;
  }

  return;
}
The output is as follows:
before value of scalar: -1
before value of vector: 1 2 3 4 5 6 7 8 9 10
before value of 2D: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
after value of scalar: 1
after value of vector: 1 2 9 4 25 6 49 8 81 10
after value of 2D: 1 2 9 4 25 6 49 8 81 10 11 12 13 14 15 16 17 18 19 20 441 22 529 24 625 26 729 28 841 30


Voice your opinion on getting help information Ask IBM compiler experts a technical question in the IBM XL compilers forum Reach out to us