# C-Fortran interface type in CFI\_cdesc\_t

**URL:** https://fortran-lang.discourse.group/t/c-fortran-interface-type-in-cfi-cdesc-t/8079
**Category:** Compilers
**Created:** [May 24, 2024, 10:26am UTC](https://fortran-lang.discourse.group/t/c-fortran-interface-type-in-cfi-cdesc-t/8079 "2024-05-24T10:26:26Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![hakostra](https://avatars.discourse-cdn.com/v4/letter/h/a3d4f5/32.png) [@hakostra](https://fortran-lang.discourse.group/u/hakostra)
#### Post date: [May 24, 2024, 10:26am UTC](https://fortran-lang.discourse.group/t/c-fortran-interface-type-in-cfi-cdesc-t/8079/1 "2024-05-24T10:26:26Z")

</div>

I recently started experimenting with the Nvidia `nvfortran` compiler. I have some code that make use of the C-Fortran interop features and the `CFI_cdesc_t` descriptor. I immediately noticed some peculiar behavior, and would like to hear with this community what your opinion are.

I have a C++ routine that print the type of a C-interop array from Fortran:

```auto
#include <ISO_Fortran_binding.h>

#include <iostream>
#include <cassert>

extern "C" {
    void check_array(CFI_cdesc_t* data) {
        std::cout << "data->type: " << static_cast<int>(data->type) << std::endl;

        // Some implementations does noty differ between different identical integer types
        if (data->type == CFI_type_int) {
            std::cout << "Type: int" << std::endl;
        }
        if (data->type == CFI_type_long) {
            std::cout << "Type: long" << std::endl;
        }
        if (data->type == CFI_type_long_long) {
            std::cout << "Type: long long" << std::endl;
        }
        if (data->type == CFI_type_int32_t) {
            std::cout << "Type: int32_t" << std::endl;
        }
        if (data->type == CFI_type_int64_t) {
            std::cout << "Type: int64_t" << std::endl;
        }
        if (data->type == CFI_type_float) {
            std::cout << "Type: float" << std::endl;
        }
        if (data->type == CFI_type_double) {
            std::cout << "Type: double" << std::endl;
        }
    }
}

```

With the accompanying Fortran program:

```auto
PROGRAM main
    USE, INTRINSIC :: ISO_C_BINDING
    USE, INTRINSIC :: ISO_FORTRAN_ENV
    IMPLICIT NONE

    INTERFACE
        SUBROUTINE check_array(res) BIND(C)
            TYPE(*), INTENT(in) :: res(:)
        END SUBROUTINE check_array
    END INTERFACE

    INTEGER :: default_integer(1)
    INTEGER(c_int) :: c_int_integer(1)
    INTEGER(c_long) :: c_long_integer(1)
    INTEGER(c_long_long) :: c_long_long_integer(1)
    INTEGER(c_int32_t) :: c_int32_t_integer(1)
    INTEGER(c_int64_t) :: c_int64_t_integer(1)

    INTEGER(int32) :: int32_integer(1)
    INTEGER(int64) :: int64_integer(1)

    WRITE(*, '("Size of default_integer: ", I0)') C_SIZEOF(default_integer(1))
    CALL check_array(default_integer)
    WRITE(*, '()')

    WRITE(*, '("Size of c_int_integer: ", I0)') C_SIZEOF(c_int_integer(1))
    CALL check_array(c_int_integer)
    WRITE(*, '()')

    WRITE(*, '("Size of c_long_integer: ", I0)') C_SIZEOF(c_long_integer(1))
    CALL check_array(c_long_integer)
    WRITE(*, '()')

    WRITE(*, '("Size of c_long_long_integer: ", I0)') C_SIZEOF(c_long_long_integer(1))
    CALL check_array(c_long_long_integer)
    WRITE(*, '()')

    WRITE(*, '("Size of c_int32_t_integer: ", I0)') C_SIZEOF(c_int32_t_integer(1))
    CALL check_array(c_int32_t_integer)
    WRITE(*, '()')

    WRITE(*, '("Size of c_int64_t: ", I0)') C_SIZEOF(c_int64_t_integer(1))
    CALL check_array(c_int64_t_integer)
    WRITE(*, '()')

    WRITE(*, '("Size of int32_integer: ", I0)') C_SIZEOF(int32_integer(1))
    CALL check_array(int32_integer)
    WRITE(*, '()')

    WRITE(*, '("Size of int64_integer: ", I0)') C_SIZEOF(int64_integer(1))
    CALL check_array(int64_integer)
    WRITE(*, '()')
END PROGRAM main

```

Basically, I tried compilers from GNU, Intel, NAG and Nvidia. They fall in two categories: GNU, Intel and NAG gives output like:

```auto
Size of default_integer: 4
data->type: 1025
Type: int
Type: int32_t

Size of c_int_integer: 4
data->type: 1025
Type: int
Type: int32_t

Size of c_long_integer: 8
data->type: 2049
Type: long
Type: long long
Type: int64_t

Size of c_long_long_integer: 8
data->type: 2049
Type: long
Type: long long
Type: int64_t

Size of c_int32_t_integer: 4
data->type: 1025
Type: int
Type: int32_t

Size of c_int64_t: 8
data->type: 2049
Type: long
Type: long long
Type: int64_t

Size of int32_integer: 4
data->type: 1025
Type: int
Type: int32_t

Size of int64_integer: 8
data->type: 2049
Type: long
Type: long long
Type: int64_t

```

This example is output from Gfortran/G++ version 12. Besides the actual value of the `data->type` which is implementation dependent, both Intel and NAG gives the same output. The key of this is that the `c_int` kind, which should be interoperable with `int` in C is of type `CFI_type_int` **and** `CFI_type_int32_t` at the same time, because `CFI_type_int == CFI_type_int32_t`.

With `nvfortran` and `nvc++` I get a different result:

```auto
Size of default_integer: 4
data->type: 9
Type: int32_t

Size of c_int_integer: 4
data->type: 9
Type: int32_t

Size of c_long_integer: 8
data->type: 10
Type: int64_t

Size of c_long_long_integer: 8
data->type: 10
Type: int64_t

Size of c_int32_t_integer: 4
data->type: 9
Type: int32_t

Size of c_int64_t: 8
data->type: 10
Type: int64_t

Size of int32_integer: 4
data->type: 9
Type: int32_t

Size of int64_integer: 8
data->type: 10
Type: int64_t

```

As you see, the different integers from Fortran all map to either int32\_t or int64\_t. So if I have a check in my program:

```auto
assert (data->type == CFI_type_int);

```

this will always fail.

**I find it peculiar that the type you get when you pass an `c_int` from Fortran to C is not equal to `CFI_type_int`… Any opinions on this?**

An additional observation is that the header file `ISO_Fortran_binding.h` provided by the Nvidia `nvfortran` compiler is very similar to that from the (new) Flang compiler in the LLVM repo. All the symbols and definitions seem to be the same. I do not have access to the new Flang right away, if any of you have it it would be interesting if you could run my example.

---

<div class="post-metadata">

### Author: ![PierU](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pieru/32/1848_2.png) [@PierU](https://fortran-lang.discourse.group/u/PierU)
#### Post date: [May 24, 2024, 12:17pm UTC](https://fortran-lang.discourse.group/t/c-fortran-interface-type-in-cfi-cdesc-t/8079/2 "2024-05-24T12:17:40Z")

</div>

Could you post the content of `ISO_Fortran_binding.h`?

---

<div class="post-metadata">

### Author: ![hakostra](https://avatars.discourse-cdn.com/v4/letter/h/a3d4f5/32.png) [@hakostra](https://fortran-lang.discourse.group/u/hakostra)
#### Post date: [May 24, 2024, 12:40pm UTC](https://fortran-lang.discourse.group/t/c-fortran-interface-type-in-cfi-cdesc-t/8079/3 "2024-05-24T12:40:50Z")

</div>

The ISO\_Fortran\_binding.h is very close to the one in the “new” Flang repo:

> <https://github.com/llvm/llvm-project/blob/main/flang/include/flang/ISO_Fortran_binding.h>

The constants (`CFI_type_int`, `CFI_type_int32_t`) have the same values.

---

<div class="post-metadata">

### Author: ![ivanpribec](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/ivanpribec/32/3290_2.png) [@ivanpribec](https://fortran-lang.discourse.group/u/ivanpribec)
#### Post date: [May 24, 2024, 12:59pm UTC](https://fortran-lang.discourse.group/t/c-fortran-interface-type-in-cfi-cdesc-t/8079/4 "2024-05-24T12:59:48Z")

</div>

But apparently not [when compiled,](https://godbolt.org/z/fh3hK1b8o)

```auto
extern "C" {

    bool are_equal(){ 
        return CFI_type_int == CFI_type_int32_t;
    }
    bool cnst_true() { return true; }
    bool cnst_false() { return false; }
}

```

```txt
define dso_local signext i8 @are_equal() #0 mustprogress !dbg !85 {

        ret i8 0, !dbg !89
}

define dso_local signext i8 @cnst_true() #0 mustprogress !dbg !92 {

        ret i8 1, !dbg !95
}

define dso_local signext i8 @cnst_false() #0 mustprogress !dbg !97 {

        ret i8 0, !dbg !100
}

```

`are_equal` returns the value 0, which matches the constant `false`.

When I preprocess only, using the flag `-E`, I get the output:

```auto
# 2 "/app/example.cpp" 2

extern "C" {

    bool are_equal(){ 
        return 3 == 9;
    }
    bool cnst_true() { return true; }
    bool cnst_false() { return false; }

}

```

In the Flang repo they have different values,

```auto
#define CFI_type_int 3
...
#define CFI_type_int32_t 9

```

---

<div class="post-metadata">

### Author: ![PierU](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pieru/32/1848_2.png) [@PierU](https://fortran-lang.discourse.group/u/PierU)
#### Post date: [May 24, 2024, 1:27pm UTC](https://fortran-lang.discourse.group/t/c-fortran-interface-type-in-cfi-cdesc-t/8079/5 "2024-05-24T13:27:55Z")

</div>

> [@hakostra](#):
>
> The constants (`CFI_type_int`, `CFI_type_int32_t`) have the same values.

They don’t… And the comment is important:

```auto
/* These codes are required to be macros (i.e., #ifdef will work).
 * They are not required to be distinct, but neither are they required
 * to have had their synonyms combined.
 */
#define CFI_type_int 3
...
#define CFI_type_int32_t 9

```

---

<div class="post-metadata">

### Author: ![everythingfunctional](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/everythingfunctional/32/176_2.png) [@everythingfunctional](https://fortran-lang.discourse.group/u/everythingfunctional)
#### Post date: [May 24, 2024, 1:29pm UTC](https://fortran-lang.discourse.group/t/c-fortran-interface-type-in-cfi-cdesc-t/8079/6 "2024-05-24T13:29:17Z")

</div>

> [@hakostra](#):
>
> I find it peculiar that the type you get when you pass an `c_int` from Fortran to C is not equal to `CFI_type_int`… Any opinions on this?

On the Fortran side, there is nothing that says that any integer kind should be the same as any other, even if they have the same representation. That said, if that were the case, I would expect those kind values to be preserved across calls in to C, not all mapped to the same kind.

---

<div class="post-metadata">

### Author: ![FortranFan](https://avatars.discourse-cdn.com/v4/letter/f/96bed5/32.png) [@FortranFan](https://fortran-lang.discourse.group/u/FortranFan)
#### Post date: [May 24, 2024, 4:39pm UTC](https://fortran-lang.discourse.group/t/c-fortran-interface-type-in-cfi-cdesc-t/8079/7 "2024-05-24T16:39:01Z")

</div>

> [@hakostra](#):
>
> … the different integers from Fortran all map to either int32\_t or int64\_t. So if I have a check in my program:
> 
> ```auto
> assert (data->type == CFI_type_int);
> 
> ```
> 
> this will always fail. …

Huh!? “always”!!? You mean only the assertion is not definitive?!

You may help yourself by taking a look at the standard document first and then consulting, say, Modern Fortran Explained, would you have saved you some time. Both touch upon this c.f. the 24-007 proxy document for Fortran 2023:

 ![image](https://global.discourse-cdn.com/free1/uploads/fortran_lang/original/2X/e/e4b3fd9415e6322d7b65ee0fe0850917ba695a19.png)

---

<div class="post-metadata">

### Author: ![hakostra](https://avatars.discourse-cdn.com/v4/letter/h/a3d4f5/32.png) [@hakostra](https://fortran-lang.discourse.group/u/hakostra)
#### Post date: [May 24, 2024, 5:37pm UTC](https://fortran-lang.discourse.group/t/c-fortran-interface-type-in-cfi-cdesc-t/8079/8 "2024-05-24T17:37:41Z")

</div>

> [@FortranFan](#):
>
> Huh!? “always”!!? You mean only the assertion is not definitive?!
> 
> You may help yourself by taking a look at the standard document first and then consulting, say, Modern Fortran Explained, would you have saved you some time. Both touch upon this c.f. the 24-007 proxy document for Fortran 2023:

Section 18.3.1 of the document you refer to has the following:

> A Fortran intrinsic type with particular type parameter values is interoperable with a C type if the type and kind type parameter value are listed in the table on the same row as that C type.

 ![Screenshot 2024-05-24 at 19-24-00 J3_SD-007 - 23-007r1.pdf](https://global.discourse-cdn.com/free1/uploads/fortran_lang/original/2X/f/f118f6a10c5736aa1216c3ba2f2dd1469e525041.png)

From this table 18.2 it is, without any doubt, given an `INTEGER(C_INT)` on the Fortran side is interoperable with an `int` on the C side. Anyone disagree?

And then, in table 18.4 you refer to, it is also no doubt about that an `int` on the C side shall have a type specifier in the `CFI_cdesc_t` structure corresponding to `CFI_type_int` - right?

How can it be, that an `INTEGER(C_INT)` declared in the Fortran side does not result in an `CFI_type_int` on the C side?

---

<div class="post-metadata">

### Author: ![PierU](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pieru/32/1848_2.png) [@PierU](https://fortran-lang.discourse.group/u/PierU)
#### Post date: [May 24, 2024, 7:45pm UTC](https://fortran-lang.discourse.group/t/c-fortran-interface-type-in-cfi-cdesc-t/8079/9 "2024-05-24T19:45:45Z")

</div>

> [@hakostra](#):
>
> How can it be, that an `INTEGER(C_INT)` declared in the Fortran side does not result in an `CFI_type_int` on the C side?

I think that this is basically because you could as well write:

```auto
integer(kind=4) :: arr(1)

```

and still have a perfectly valid Fortran code with an interoperable array `arr`. In such a case what should the compiler put in `data->type`? Why more `CFI_type_int` rather than `CFI_type_int32_t` or `CFI_type_int_least32_t` ? `nvfortran/nvcc` just decides that any `integer(kind=x)` with `x=4` is described with `CFI_type_int32_t` in the descriptor, which is plain correct.

The weird thing is to have different `CFI_type_*` values that map to the same actual type, but they have maybe some good reason for that.

---

<div class="post-metadata">

### Author: ![FortranFan](https://avatars.discourse-cdn.com/v4/letter/f/96bed5/32.png) [@FortranFan](https://fortran-lang.discourse.group/u/FortranFan)
#### Post date: [May 25, 2024, 2:30pm UTC](https://fortran-lang.discourse.group/t/c-fortran-interface-type-in-cfi-cdesc-t/8079/10 "2024-05-25T14:30:28Z")

</div>

> [@hakostra](#):
>
> … How can it be, that an `INTEGER(C_INT)` declared in the Fortran side does not result in an `CFI_type_int` on the C side?

@hakostra ,

Are you choosing to misinterpret the statement in the standard document, “The values for different C types can be the same; for example, CFI\_type\_int and CFI\_type\_int32\_t might have the same value.”

Consider a simple example like so:

- source for the companion C processor

```auto
#include <stdio.h>
#include "ISO_Fortran_binding.h"

void f( CFI_cdesc_t *, CFI_cdesc_t * );

void f( CFI_cdesc_t *a, CFI_cdesc_t *b ) {
  printf("In C function f: a->type = %d\n", (int)a->type);
  printf("and b->type = %d\n", (int)b->type);
  printf("For this processor, CFI_type_int = %d\n", (int)CFI_type_int);
  printf("and CFI_type_int32_t = %d\n", (int)CFI_type_int32_t);
  return;
}

```

- and for the Fortran processor

```fortran
   use, intrinsic :: iso_c_binding, only : c_int, c_int32_t
   interface
      subroutine Cfunc( a, b ) bind(C, name="f")
         import :: c_int, c_int32_t
         integer(c_int), allocatable, intent(inout) :: a
         integer(c_int32_t), allocatable, intent(inout) :: b
      end subroutine
   end interface
   integer(c_int), allocatable :: x
   integer(c_int32_t), allocatable :: y
   call Cfunc( x, y ) 
end 

```

- One processor gives a program response:

```auto
C:\temp>gfortran -c c.c

C:\temp>gfortran -c -ffree-form p.f

C:\temp>gfortran p.o c.o -o p.exe

C:\temp>p.exe
In C function f: a->type = 1025
b->type = 1025
For this processor, CFI_type_int = 1025
and CFI_type_int32_t = 1025

C:\temp>

```

which shows you that your wondering out aloud is effectively **inaccurate** : “How can it be, that an `INTEGER(C_INT)` declared in the Fortran side does not result in an `CFI_type_int` on the C side?,”

To borrow your turns of phrase, it is possible with a Fortran processor and its companion C processor

1. for an “`INTEGER(C_INT32_T)` declared in the Fortran side” to also “result in an `CFI_type_int` on the C side” and
2. an “`INTEGER(C_INT)` declared in the Fortran side” to also “result in an `CFI_type_int32_t` on the C side,”

but that is just how things are, for those on the Fortran standard committee, in their “infinite” wisdom, working on enhanced interoperability with C - as part of [**TS 29113**](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwi2hqjzg6mGAxUrFVkFHR9bCacQFnoECBQQAQ&url=https%3A%2F%2Fwg5-fortran.org%2FN1901-N1950%2FN1942.pdf&usg=AOvVaw08APa8p6V-Vb2HMwO3hmsb&opi=89978449) and Fortran 2018 - thought it to be alright.

Regardless, your assertion, " **when you pass an `c_int` from Fortran to C is not equal to `CFI_type_int`**" is incorrect with conforming processors.

---

<div class="post-metadata">

### Author: ![ivanpribec](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/ivanpribec/32/3290_2.png) [@ivanpribec](https://fortran-lang.discourse.group/u/ivanpribec)
#### Post date: [April 2, 2025, 1:33pm UTC](https://fortran-lang.discourse.group/t/c-fortran-interface-type-in-cfi-cdesc-t/8079/11 "2025-04-02T13:33:20Z")

</div>

I must say this difference is annoying. Imagine writing a helper function to check an array descriptor has a signed integer type. On co-processors for gfortran, Intel, NAG you need to write:

```cpp
bool is_signed_integer(CFI_type_t type) {
    switch (type) {
        case CFI_type_int8_t:
        case CFI_type_int16_t:
        case CFI_type_int32_t:
        case CFI_type_int64_t:
            return true;
        default:
            return false;
    }
}

```

Adding other types like `CFI_type_int` will result in a duplicate case label error.

In flang on the other hand you need to write:

```cpp
bool is_signed_integer(CFI_type_t type) {
    switch (type) {
        case CFI_type_short:
        case CFI_type_int:
        case CFI_type_long:
        case CFI_type_long_long:
        case CFI_type_int8_t:
        case CFI_type_int16_t:
        case CFI_type_int32_t:
        case CFI_type_int64_t:
        case CFI_type_int128_t:
        case CFI_type_int_least8_t:
        case CFI_type_int_least16_t:
        case CFI_type_int_least32_t:
        case CFI_type_int_least64_t:
        case CFI_type_int_least128_t:
        case CFI_type_int_fast8_t:
        case CFI_type_int_fast16_t:
        case CFI_type_int_fast32_t:
        case CFI_type_int_fast64_t:
        case CFI_type_int_fast128_t:
        case CFI_type_intmax_t:
        case CFI_type_intptr_t:
        case CFI_type_ptrdiff_t:
            return true;
        default:
            return false;
    }
}

```

In [this article](https://doi.org/10.1145/2553038.2553040) from 2013, R. Bader wrote:

> The [`ISO_Fortran_binding.h`] header file is implementation specific and can therefore not be used to bind to different processor within one file scope. Portability is limited to the source level semantics described by the TS.

I already accepted the fact there is no ABI compatbility due to differences in layout of the `CFI_cdesc_t` struct, as discussed in [Is ISO\_Fortran\_binding.h compatible for different Fortran ABIs? - #4 by awvwgk](https://fortran-lang.discourse.group/t/is-iso-fortran-binding-h-compatible-for-different-fortran-abis/2652/4)). However this type quirk also compromises source-level portability of the C code
