pointersfortrangfortranintel-fortranfortran-iso-c-binding

Deallocating arrays defined from c_f_pointer


The following code compiles in both GNU gfortran and Intel ifort. But only the gfortran compiled version will run successfully.

    program fort_tst
        use iso_c_binding

        INTEGER, POINTER :: a(:) 
        TYPE(C_PTR) :: ptr 

        INTEGER, POINTER :: b(:) 

        ALLOCATE(a(5)) 

        ptr = c_loc(a) 

        CALL c_f_pointer(ptr,b,[5]) 

        DEALLOCATE(b) 
    end program fort_tst

The error in the Intel compiled code is :

forrtl: severe (173): A pointer passed to DEALLOCATE points to an object that cannot be deallocated
Image              PC                Routine            Line        Source             
fort_tst           000000000040C5A1  Unknown               Unknown  Unknown
fort_tst           0000000000403A17  Unknown               Unknown  Unknown
fort_tst           0000000000403812  Unknown               Unknown  Unknown
libc-2.17.so       00002AAAAB20F555  __libc_start_main     Unknown  Unknown
fort_tst           0000000000403729  Unknown               Unknown  Unknown

The gfortran code runs to completion. A quick valgrind check does not find any leaks.

Can someone confirm whether the code above is valid/legal code?

I am running

    ifort (IFORT) 2021.2.0 20210228

and

    GNU Fortran (GCC) 9.2.0
    Copyright (C) 2019 Free Software Foundation, Inc.

UPDATE :

What is interesting is that gfortran does the right thing, (i.e. deallocates only allocated memory), even when the user tries to confound it with improper index remapping, or a bogus shape argument. So the internal array descriptor is being properly copied over with gfortran's c_f_pointer.


Solution

  • Posts above inspired the following solution. The idea is to create a type that wraps the actual data array. Then, c_loc/c_f_pointer sequence works fine with a pointer to a scalar object. The data array stored in the type can be safely allocated, along with the array type itself.

    MODULE arraytype_m
        TYPE, PUBLIC :: arraytype
            INTEGER, ALLOCATABLE :: data(:)
        END TYPE arraytype  
    END MODULE arraytype_m
    
    
    PROGRAM fort_tst
        USE iso_c_binding
        USE arraytype_m
    
        TYPE(arraytype), POINTER  :: a, b
        TYPE(C_PTR) :: ptr 
    
        ALLOCATE(a)
        ALLOCATE(a%data(5))
    
        !! Set to C-style pointer, and then copy back to Fortran pointer.
        ptr = c_loc(a) 
        CALL c_f_pointer(ptr,b)
    
        DEALLOCATE(b%data)
        DEALLOCATE(b) 
    END PROGRAM fort_tst
    

    This works with both Intel and gfortan, and is really a better solution than what I was trying to do.

    Special thanks for @Federico for posting the C++/Fortran code that made this solution obvious.

    Update : A complete code, which shows how the ptr above can be stored in C.

    // C code
    typedef void* arraytype;
    
    void allocate_array(arraytype *ptr);
    void deallocate_array(arraytype *ptr);
    void do_something(arraytype *ptr);
    
    int main()
    {
        arraytype ptr;
        allocate_array(&ptr);    
        do_something(&ptr);
        deallocate_array(&ptr);
        return 0;
    }
    

    and the corresponding Fortran :

    !! Fortran code
    MODULE arraytype_mod
        TYPE, PUBLIC :: arraytype
            DOUBLE PRECISION, POINTER :: data(:)
        END TYPE arraytype  
    END MODULE arraytype_mod
    
    SUBROUTINE allocate_array(ptr) BIND(C,name='allocate_array')
        USE iso_c_binding
        USE arraytype_mod
        TYPE(c_ptr) :: ptr
        TYPE(arraytype), POINTER :: a
        ALLOCATE(a)
        ALLOCATE(a%data(5))
        ptr = c_loc(a)
    END
    
    SUBROUTINE deallocate_array(ptr) BIND(C,name='deallocate_array')
        USE iso_c_binding
        USE arraytype_mod
        TYPE(C_PTR) :: ptr
        TYPE(arraytype), pointer :: a
        CALL c_f_pointer(ptr,a)
        DEALLOCATE(a%data)
        DEALLOCATE(a)
    END
    
    SUBROUTINE do_something(ptr) BIND(C,name='do_something')
        USE iso_c_binding
        USE arraytype_mod
        TYPE(c_ptr) :: ptr
        TYPE(arraytype), POINTER :: a
        CALL c_f_pointer(ptr,a)
        a%data = 2.5
        WRITE(6,*) a%data
    END