From owner-sc22wg5@open-std.org  Sun Dec  7 21:35:35 2008
Return-Path: <owner-sc22wg5@open-std.org>
X-Original-To: sc22wg5-dom7
Delivered-To: sc22wg5-dom7@www2.open-std.org
Received: by www2.open-std.org (Postfix, from userid 521)
	id 3B99EC178E0; Sun,  7 Dec 2008 21:35:35 +0100 (CET)
X-Original-To: sc22wg5@open-std.org
Delivered-To: sc22wg5@open-std.org
Received: from mailrelay2.lrz-muenchen.de (mailrelay2.lrz-muenchen.de [129.187.254.102])
	by www2.open-std.org (Postfix) with ESMTP id 96E84C178D6
	for <sc22wg5@open-std.org>; Sun,  7 Dec 2008 21:35:33 +0100 (CET)
Received: from [129.187.48.212] ([129.187.48.212] [129.187.48.212]) by mailout.lrz-muenchen.de with ESMTP for sc22wg5@open-std.org; Sun, 7 Dec 2008 21:35:27 +0100
Message-Id: <493C338F.3000709@lrz.de>
Date: Sun, 07 Dec 2008 21:35:27 +0100
From: Reinhold Bader <Reinhold.Bader@lrz.de>
User-Agent: Thunderbird 2.0.0.18 (Windows/20081105)
MIME-Version: 1.0
To: WG5 <sc22wg5@open-std.org>
Subject: Response on the TR29113 draft N1761
Content-Type: multipart/mixed;
 boundary="------------050501060605050806080203"
Sender: owner-sc22wg5@open-std.org
Precedence: bulk

This is a multi-part message in MIME format.
--------------050501060605050806080203
Content-Type: text/plain; charset=ISO-8859-15; format=flowed
Content-Transfer-Encoding: 7bit

Hello,

 the response is "NO with comments", and I am attaching my thoughts on 
the TR.

Regards,
Reinhold

--------------050501060605050806080203
Content-Type: text/plain;
 name="C_interop_response.txt"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="C_interop_response.txt"

Response on the TR29113 draft N1761

References:

ftp://ftp.nag.co.uk/sc22wg5/N1751-N1800/N1761.txt
http://j3-fortran.org/doc/meeting/186/08-295.txt


My response is "NO, with comments". The comments are provided
below in the form of a list of issues, examples and suggestions.
The minimum required to make me say YES is:
* Issues 1, 2, 4 and 6 must be fixed. Issue 4 may be regarded as a
  showstopper by those from the MPI Forum who require existing
  infrastructure to be preserved, while providing improved Fortran
  interfaces. 



Issue 1 - missing examples for usage of descriptors:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I've tried to supply one which could be added to the draft; it is also used
here to illustrate further TR issues.


Example 1:


C calling a Fortran implementation

module mod_foo
  use, intrinsic :: iso_c_binding
  type, bind(c) :: foo
    integer(c_int) :: i
    real(c_float) :: r(3)
    character(c_int) :: c
  end type
contains
  subroutine construct_foo(this, n, i, r, c) bind(c)
!   bind(c) extended for TR29113 
    type(foo), allocatable, intent(out) :: this(:)
    integer(c_int), intent(in) :: n, i(n)
    real(c_float), intent(in) :: r(3, n)
    character(c_char), intent(in) :: c(n)
    : ! code for constructing this
  end subroutine
end module

C client code:

#include <ISO_Fortran_binding.h>

/* interface */

void construct_foo(void *, int, int *, float *, char *); 

/* matching type definition */

typedef struct {
  int i;
  float r[3];
  char c;
} Foo;

int main() {
  CFI_desc_t *my_desc; 
  type Foo *array_elem; 
  int :: istatus;
  int i[4] = { 1, 2, 3, 4 }; 
  float r[4][3] = { { 1.0, 1.1, 1.2 }, { 2.0, 2.1, 2.2 }, 
                    { 3.0, 3.1, 3.2 }, { 4.0, 4.1, 4.2 } };
  char c[4] = { 'a', 'b', 'c', 'd' };

  my_desc = (CFI_desc_t *) malloc(sizeof(CFI_desc_t));
  my_desc->base_addr = NULL;
  my_desc->elem_len = sizeof(Foo); /* may need Fortran call to C_SIZEOF if unknown */
  my_desc->rank = 1;
  my_desc->type = CFI_type_struct;
  my_desc->attribute = CFI_attribute_allocatable;
  my_desc->state = 0;
  my_desc->fdesc = NULL;

  istatus = CFI_update_fdesc(my_desc); /* make attributes known to processor */
  construct_foo(my_desc->fdesc, 4, i, r, c);
  istatus = CFI_update_cdesc(my_desc); /* update attributes for C program */


/* align 3rd element to C scalar of matching type.
   (for contiguous Fortran arrays one could also align a C array) */
  array_elem = (Foo *)(base_addr + 2 * sizeof(Foo));

  printf("Character component of 3rd element: %s\n",array_elem->c);

  istatus = CFI_deallocate(my_desc);
  free(my_desc);
  
}


Issue 2 - polymorphism of assumed-type entity:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Since no changes to the definition of C_LOC() have been introduced, and 
this function is used to cast an object of TYPE(*) to a usable type, the 
text beginning in line 92 of N1761 should be replaced by

"In the association of actual and dummy arguments, an assumed-type
 dummy argument is type and kind compatible with a non-polymorphic 
 actual data argument of any type."


Issue 3 - interface of CFI_is_contiguous:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I'd suggest 

_Bool CFI_is_contiguous(const CFI_cdesc_t *)

and have it return false if a dynamic object is not allocated. The 
typical usage would then be more concise:

if (mydesc->state && CFI_is_contiguous(mydesc)) ...


Issue 4 - rank matching for generics:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The example provided in line 552-575 of N1761 is not conforming, since 
on of the uses of the interface contravenes p290, para 13 of the F2008 
draft standard 08-007r2, which disallows matching of actual arguments
of arbitrary ranks to the assumed size dummy of rank one for
a generic interface.

Either one must give up using a generic interface, in
which case two separate calls must be provided for the scalar and 
array arguments, or a feature must be added to the language which
allows to pass an argument of arbitrary rank by address / sequence
association.

Suggestion 2 below tries to provide an extension which solves this
problem.


Issue 5 - RANK intrinsic:
~~~~~~~~~~~~~~~~~~~~~~~~~~

Is this really needed considering we already have SIZE(SHAPE(X))?


Issue 6 - referencing or defining assumed rank entities:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We need some rules to deal with this. Unless some checks 
normally performed at compile time are deferred to run time,
introducing a SELECT RANK block construct might be appropriate. 


  real, dimension(::) :: a
  real, pointer :: p(:)

  select rank (a)
    case(0) 
      a = 1.
    case(1)
      a(:) = [ ... ]
    default
      ... ! no references or definitions of a allowed here
  end select

It might also be feasible to allow a pointer of rank 1
to point at such an object:

  p => a
  if (i < size(a)) then
    ... = p(i)
  end if

This would however imply a re-interpretation of scalar actuals 
as rank 1 entities of size 1.


Issue 7 - non-interoperable types:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

While I've heard it said that coverage of non-interoperable types 
was not the purpose of this TR, I wonder whether this is not 
implicitly already contained, at least for non-interoperable
entities with following properties:

* non-polymorphic, non-sequence
* allocatable or pointer type components
* ultimate type components of interoperable intrinsic type
Assumed-shape/assumed-rank/allocatable/pointer, scalars and arrays of 
this type would be acceptable as dummy arguments, based on the 
existing capabilities of the descriptors.
[since BIND is not a characteristic of a dummy argument, 
 normal scalars may be a problem. Since allocatable scalars or
 pointer scalars can be used, it is not really a big problem.]

So, if the type definition from example 1 above were replaced by

  type :: foo
    integer(c_int) :: i
    real(c_float), allocatable :: r(:)
    character(c_int) :: c
  end type

the unpacking process in C would need to use a type definition

typedef struct {
  int i;
  void *fdesc;
  char c;
} Foo;

Once an object of this type is mapped to an array element, either
CFI_update_cdesc() can be used to access the type component, or
(if Suggestion 1 below is adopted), the CFI_create_desc() constructor
with a non-NULL last argument.

[If type fields and dummy arguments use different descriptors, it may
be necessary to introduce another component of CFI_desc_t, say
tf_desc.]


Issue 8 - C variable argument lists:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

with the changes introduced in 08-295 it should now also be possible
to provide support for vararg interfaces by matching to specifics of
a Fortran generic interface. The only obstruction to this would be
dealing with c_char arguments; this however might also be solved by
Suggestion 2 below.


Suggestion 1 (design change interface creation):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This is an attempt to formulate an alternative to the descriptor usage 
(and, to a lesser extent, design) described in N1761. The idea is to
* improve proper separation of concerns
* require the processor to auto-generate code which under the 
  present proposal must be inserted manually, and thereby improve
  usability.
After a first description I'll attempt to illustrate the advantages 
compared to N1761 via example programs.

Since the attributes presently not supported for interoperable procedure
arguments (assumed shape, pointer, allocatable, and perhaps also
non-interoperable types) are characteristics, the processor is in principle 
capable of disambiguating interfaces using these features from those 
previously available for C interoperation. Hence I do not consider it 
necessary to provide a separate attribute with the BIND statement.

(A) C calling a Fortran implementation:

The matching C interface is replaced by an auto-generated mapped C 
interface. For example 1, this would be

     void construct_foo(CFI_desc_t *, int, int *, float *, char *); 

It would be the responsibility of the processor to 
* generate a local name by which the procedure name is known in Fortran
* generate a global name based on the default label of the Fortran entity, unless
  a binding label is specified.
  (These two items could be handled analogously to the treatment of Fortran
  external procedures with no binding label (08-295)).
* automatically generate a wrapper which performs any updates covered by
  CFI_update_fdesc in the present design, unwraps the entity CFI_desc_t
  and hands on the Fortran descriptor field to the Fortran-local procedure
  call. A direct call from Fortran to the global C name is non-conforming
  since no type matching CFI_desc_t is available within Fortran; the processor
  should prevent an interface attempting this from being created.
* after return from the Fortran-local procedure performs any updates which
  are covered by CFI_update_cdesc in the present design.

It is further suggested to provide a constructor (and associated destructor) 
function for the descriptor itself which has as its arguments those entities 
the programmer is responsible for. The call to the constructor would replace
the simple malloc() and the immediately following field settings in the 
example 1 above. The necessary creation of the fdesc field should also be 
performed in this constructor unless a non-NULL value is supplied in the last
argument, replacing this functionality in CFI_update_fdesc(). If a non-NULL
value if provided for fdesc, a consistent C descriptor is created, ignoring
the other arguments. Here the interfaces:

     CFI_desc_t *CFI_create_desc(size_t elem_len, int rank, int type, 
                                 int attribute, void *fdesc);
     void CFI_destroy_desc(CFI_desc_t *cdesc, _Bool destroy_fdesc);

and two constants for readability:

     CFI_destroy_fdesc (_Bool with value true)
     CFI_keep_fdesc    (_Bool with value false)

In contrast to statically determined Fortran code the constructor sets the
attribute, type and rank of an object at run time.  
Do we oblige the processor to update this information if inconsistent with the
defined Fortran interface? 
All other type fields of a CFI_desc_t object should not be explicitly set by
the user, except for other calls to the processor-defined routines.
The API calls CFI_update_fdesc() and CFI_update_cdesc() should be removed.

The net amount of function call overhead would stay the same, but usability 
is improved at least for the real inter-language case. 


The C main() of example 1 above would thus read

int main() {
  CFI_desc_t *my_desc; 
  type Foo *array_elem; 
  int :: istatus;
  int i[4] = { 1, 2, 3, 4 }; 
  float r[4][3] = { { 1.0, 1.1, 1.2 }, { 2.0, 2.1, 2.2 }, 
                    { 3.0, 3.1, 3.2 }, { 4.0, 4.1, 4.2 } };
  char c[4] = { 'a', 'b', 'c', 'd' };

  my_desc = CFI_create_desc(sizeof(Foo), 1, CFI_type_struct,
                            CFI_attribute_allocatable, NULL);

  construct_foo(my_desc, 4, i, r, c);

/* align 3rd element to C scalar of matching type.
   (for contiguous Fortran arrays one could also align a C array) */
  array_elem = (Foo *)(base_addr + 2 * sizeof(Foo));

  printf("Character component of 3rd element: %s\n",array_elem->c);

  istatus = CFI_deallocate(my_desc);
  CFI_destroy_desc(my_desc, CFI_destroy_fdesc);
  
}


(B) Fortran calling a C implementation:

The wrapper generated from the interface
* packs up the Fortran descriptor into a C descriptor upon call to the
  local Fortran name, which is automatically generated for this purpose
* executes the C routine
and once the C routine has finished
* updates the Fortran descriptor, and then deallocates the C descriptor 
  without destroying the Fortran descriptor. Run time checks may be 
  required to assure no incompatibilities with statically defined
  properties have been introduced.

It is not intended that the Fortran local name be dereferenced from C.
A C program calling the C interface (untypical) would need to perform
the construction and allocation, deallocation and destruction of descriptors 
manually before and after the call, respectively.

As an example, suppose we were to implement a modern MPI Interface.

Example 2:

module mpi
  use, intrinsic :: iso_c_binding
  implicit none
  private
! various C interoperable opaque type definitions not shown here
  public :: mpi_send
  interface
    subroutine mpi_send(buf, datatype, elem_size, dest, tag, comm, ierror) bind(c)
      type(*), dimension(..), contiguous, intent(in) :: buf 
      type(mpi_datatype), intent(in) :: datatype
      integer(c_size_t), optional, intent(in) :: elem_size
      integer(c_int), intent(in) :: dest, ierror
      integer(c_size_t), optional, intent(in) :: tag
      type(mpi_comm), intent(in) :: comm
    end subroutine
  end interface
end module

The C implementation could look something like this:

#include <ISO_Fortran_binding.h>

void mpi_send(CFI_desc_t *buf, MPI_Ftype *datatype, size_t *elem_size, 
              int *dest, size_t *tag, MPI_Comm *comm, int *ierror) {
  int local_size = 0; /* bytes */
  int i;
  void *local_buf;
  if (buf->state == 0) {
     *ierror = ...; /* may want to send length 0 buffer? */
     return;
  }
  if (MPI_Ftype->MPI_Datatype == MPI_INT) local_size = 4;
  if (MPI_Ftype->MPI_Datatype == MPI_FLOAT) local_size = ...;
  /* etc. */
  if (elem_size != NULL && MPI_Ftype->MPI_Datatype == MPI_DERIVED) {
    local_size = *elem_size;
  }
  if (local_size == 0) {
     *ierror = ...;
     return;
  }
  for (i=0; i<buf->rank) {
    local_count *= buf->dim[i]->extent;
  }
  *ierror = MPI_Send(buf->base_addr, local_count, MPI_BYTE, *dest, *tag, *comm);
}

this single call would cover 
* scalars and contiguous arrays of arbitrary rank (is "contiguous" unambiguous here?)
* all intrinsic types defined in MPI
* all C interoperable types (with a static type structure, and in a slightly 
  less safe manner than when using the MPI datatype constructors)

A C implementation might for example be of advantage over a Fortran one if 
it is easier to obtain certain type-internal information from C (e.g., the
MPI_Ftype->MPI_Datatype dereferences).

Note that this also illustrates that OPTIONAL arguments can also be handled by
the wrapping process (i.e., non-present arguments will be set NULL on the 
call to the C entity). In particular, the problem with having the combination 
of OPTIONAL and VALUE (not used in this example) attributes vanishes (for
non-c_ptr types a convention may be needed).



Suggestion 2 (add assumed-rank-and-size):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

in analogy to assumed rank (DIMENSION(::)), which one might also call
assumed-rank-and-shape, a dummy variable can be declared with

type(foo), DIMENSION(**) :: dummy

A corresponding actual argument which is either a scalar or an 
arbitrary rank array of type foo would match this dummy argument
and it would be allowed to have exactly one corresponding dummy with this
attribute in a specific procedure of a generic interface when all other dummies
have the same characteristics. 
The only allowed method of dereferencing or defining such an object from
within Fortran would be via one dimensional indexing:

dummy(i) = ...

A BIND(C) interface may specify dummy arguments that are
assumed-rank-and-size. If the dummy argument is assumed-rank-and-size,
the actual argument is passed as its C address.

Otherwise, the restrictions on this kind of argument will be essentially the
same as those for assumed-size entities.


Example 2a: extending the generic interface of example 1 for old-style calls.

module mpi
  use, intrinsic :: iso_c_binding
  implicit none
  private
! various C interoperable opaque type definitions not shown here
  public :: mpi_send
  interface
    subroutine mpif_send(buf, count, datatype, dest, tag, comm, ierror) &
                        bind(c, name='MPIF_Send')
      type(*), dimension(**), intent(in) :: buf 
      integer(c_int) :: dataype, dest, tag, comm, ierror
    end subroutine
    subroutine mpi_send(buf, datatype, elem_size, dest, tag, comm, ierror) bind(c)
      type(*), dimension(..), contiguous, intent(in) :: buf 
      type(mpi_datatype), intent(in) :: datatype
      integer(c_size_t), optional, intent(in) :: elem_size
      integer(c_int), intent(in) :: dest, ierror
      integer(c_size_t), optional, intent(in) :: tag
      type(mpi_comm), intent(in) :: comm
    end subroutine
  end interface
end module

Note however that the ierror argument is still Fortran-style, so a direct 
mapping to the C MPI_Send routine is not possible. With the old-style call,
derived datatypes must be dealt with via the MPI datatype construction
routines; apart from that the same functionality is available as for the
new call, only in a slightly less type-safe manner.


Additional notes:

* processing of C interoperable string entities would be straightforward using
  this, making the special rule introduced for this case in F2003 superfluous.
* the added bonus is that this also supports using character arguments in
  generic interfaces.
 



--------------050501060605050806080203--
