Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28866 Discussions

Fortran calls C++ access violation with option /CA

Wolf_W_
New Contributor I
5,020 Views

Hi, i got an access violation when entering the function c_testfunction. It gives the message: "Attempt to use pointer FOO_C when it is not associated with a target". It is not associated, because it is meant to be allocated in the C++ function. It works as intendet without the /CA option.

Fortran part:

! Compile:  ifort.exe /nologo /CA /stand:f18 /O2 /traceback  /module:obj\W64\ /c main.f90 /object:obj\W64\main.obj
! Link:     ifort.exe /nologo /exe:bin\W64\F_EXE.exe bin\W64\C_DLL.lib obj\W64\main.obj /link

program hello
  use, intrinsic :: iso_c_binding


  interface
    subroutine c_testfunction(foo_c) bind(C, name="c_testfunction")
      import
      integer(c_int), pointer, intent(  out) :: foo_c(:,:)
    end subroutine
  end interface

  integer, allocatable :: bar(:,:)

  call f_testfunction(bar)

  write(*,*) bar

  contains

  subroutine f_testfunction(foo_f)
    integer, allocatable, intent(  out) :: foo_f(:,:)

    integer(c_int), pointer :: foo_c(:,:)

    foo_c => null()

    call c_testfunction(foo_c)

    write(*,*)foo_c
    allocate(foo_f, source = foo_c)

  end subroutine

end program

 

C++ Part (While not important i guess):

// dllmain.h
#pragma once
#include "ISO_Fortran_binding.h"

#ifdef STEPIMPORTDLL_EXPORTS
#define STEPIMPORTDLL_API extern "C" __declspec(dllexport)
#else
#define STEPIMPORTDLL_API extern "C" __declspec(dllimport)
#endif

STEPIMPORTDLL_API int c_testfunction(CFI_cdesc_t* foo_c);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//dllmain.cpp

#include "dllmain.h"
// Compile: /JMC /permissive- /GS /W3 /Zc:wchar_t /ZI /Gm- /Od /sdl /Fd"x64\Debug\vc142.pdb" /Zc:inline /fp:precise /D "_DEBUG" /D "CDLL_EXPORTS" /D "_WINDOWS" /D "_USRDLL" /D "_WINDLL" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /MDd /FC /Fa"x64\Debug\" /EHsc /nologo /Fo"x64\Debug\" /diagnostics:column 
// Link:    /OUT:"..\F_EXE\bin\W64d\C_DLL.dll" /MANIFEST /NXCOMPAT /PDB:"..\F_EXE\bin\W64d\C_DLL.pdb" /DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" "libifcoremdd.lib" /IMPLIB:"..\F_EXE\bin\W64d\C_DLL.lib" /DEBUG /DLL /MACHINE:X64 /INCREMENTAL /PGD:"..\F_EXE\bin\W64d\C_DLL.pgd" /SUBSYSTEM:WINDOWS /MANIFESTUAC:NO /ManifestFile:"x64\Debug\C_DLL.dll.intermediate.manifest" /ERRORREPORT:PROMPT /NOLOGO /TLBID:1 
int c_testfunction(CFI_cdesc_t* foo_c)
{
	int e;
	CFI_index_t dim1 = 2;
	CFI_index_t dim2 = 4;

	CFI_index_t lower_bounds[] = { 1,1 };
	CFI_index_t upper_bounds[] = { dim1, dim2 };

	int is_established = CFI_establish(foo_c, NULL, CFI_attribute_pointer, CFI_type_int, 0, 2, NULL);
	int is_allocated = CFI_allocate(foo_c, lower_bounds, upper_bounds, 0);

	e = (is_established == CFI_SUCCESS) && (is_allocated == CFI_SUCCESS) ? 0 : -1; // If allocation failed then e = -1

	if (is_allocated == CFI_SUCCESS)
	{
		int* array = (int*)foo_c->base_addr;

		for (CFI_index_t i = 0; i < dim2; ++i)
		{
			array[i*2]   = i;
			array[i*2+1] = i*10;
		}
	}
	return e;
}

Optimizing options do not seem to influence this.

Is this an error in my code or is this a compiler bug?

Thanks in advance.

 

This happens with Inter Fortran Compiler 19.1 Update 0 to 2 (PSXE20)

0 Kudos
1 Solution
Steve_Lionel
Honored Contributor III
4,991 Views

No, that's not it - the C++ code does have extern "C" through the declaration.

I think this would be an ifort bug in that the pointer array is INTENT(OUT) and ifort should not be checking to see if it is associated. This is probably an edge case in the F2018 C interoperability stuff that wasn't checked for.

View solution in original post

0 Kudos
17 Replies
FortranFan
Honored Contributor III
5,004 Views

Retry with extern "C" attribute in your C function in your CPP code 

0 Kudos
Steve_Lionel
Honored Contributor III
4,992 Views

No, that's not it - the C++ code does have extern "C" through the declaration.

I think this would be an ifort bug in that the pointer array is INTENT(OUT) and ifort should not be checking to see if it is associated. This is probably an edge case in the F2018 C interoperability stuff that wasn't checked for.

0 Kudos
MWind2
New Contributor III
4,981 Views

I can't build the cdll:

1>------ Rebuild All started: Project: cppdll, Configuration: Debug Win32 ------
1>dllmain.cpp
1>D:\c\vs2019\icom\cppdll\ISO_Fortran_binding.h(156,20): warning C4200: nonstandard extension used: zero-sized array in struct/union
1>D:\c\vs2019\icom\cppdll\ISO_Fortran_binding.h(156,20): message : This member will be ignored by a defaulted constructor or copy/move assignment operator
1> Creating library D:\c\vs2019\icom\Debug\cppdll.lib and object D:\c\vs2019\icom\Debug\cppdll.exp
1>dllmain.obj : error LNK2019: unresolved external symbol _for_CFI_allocate referenced in function _c_testfunction
1>dllmain.obj : error LNK2019: unresolved external symbol _for_CFI_establish referenced in function _c_testfunction
1>D:\c\vs2019\icom\Debug\cppdll.dll : fatal error LNK1120: 2 unresolved externals
1>Done building project "cppdll.vcxproj" -- FAILED.
2>------ Rebuild All started: Project: hello, Configuration: Debug Win32 ------
2>Deleting intermediate files and output files for project 'hello', configuration 'Debug|Win32'.
2>Compiling with Intel(R) Visual Fortran Compiler 19.1.2.254 [IA-32]...
2>hello.f90
2>Linking...
2>Embedding manifest...
2>
2>Build log written to "file://D:\c\vs2019\icom\hello\Debug\BuildLog.htm"
2>hello - 0 error(s), 0 warning(s)
========== Rebuild All: 1 succeeded, 1 failed, 0 skipped ==========

0 Kudos
MWind2
New Contributor III
4,975 Views

I left out libifcoremd.lib, cdll builds.

0 Kudos
MWind2
New Contributor III
4,969 Views

This works in 32 bit mode (in the sense it compiles and does not crash EDI: but not with /CA):

 

#pragma once

#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
#include "ISO_Fortran_binding.h"

#ifdef STEPIMPORTDLL_EXPORTS
#define STEPIMPORTDLL_API extern "C" __declspec(dllexport)
#else
#define STEPIMPORTDLL_API extern "C" __declspec(dllimport)
#endif

extern "C" STEPIMPORTDLL_API int c_testfunction(CFI_cdesc_t* foo_c);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//cpp
// dllmain.cpp : Defines the entry point for the DLL application.
#include "cppdll.h"
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

extern "C" STEPIMPORTDLL_API int c_testfunction(CFI_cdesc_t* foo_c)
{
	int e;
	CFI_index_t dim1 = 2;
	CFI_index_t dim2 = 4;

	CFI_index_t lower_bounds[] = { 1,1 };
	CFI_index_t upper_bounds[] = { dim1, dim2 };

	int is_established = CFI_establish(foo_c, NULL, CFI_attribute_pointer, CFI_type_int, 0, 2, NULL);
	int is_allocated = CFI_allocate(foo_c, lower_bounds, upper_bounds, 0);

	e = (is_established == CFI_SUCCESS) && (is_allocated == CFI_SUCCESS) ? 0 : -1; // If allocation failed then e = -1

	if (is_allocated == CFI_SUCCESS)
	{
		int* array = (int*)foo_c->base_addr;

		for (CFI_index_t i = 0; i < dim2; ++i)
		{
			array[i * 2] = i;
			array[i * 2 + 1] = i * 10;
		}
	}
	return e;
}
!  hello.f90 
!
!  FUNCTIONS:
!  hello - Entry point of console application.
!

!****************************************************************************
!
!  PROGRAM: hello
!
!  PURPOSE:  Entry point for the console application.
!
!****************************************************************************

! Compile:  ifort.exe /nologo /CA /stand:f18 /O2 /traceback  /module:obj\W64\ /c main.f90 /object:obj\W64\main.obj
! Link:     ifort.exe /nologo /exe:bin\W64\F_EXE.exe bin\W64\C_DLL.lib obj\W64\main.obj /link

program hello
  use, intrinsic :: iso_c_binding


  interface
    !subroutine c_testfunction(foo_c) bind(C, name="c_testfunction")
    function c_testfunction(foo_c) bind(C, name="c_testfunction")
      import
      integer(c_int), pointer, intent(  out) :: foo_c(:,:)
    end function !end subroutine
  end interface

  integer, allocatable :: bar(:,:)
  integer :: iret
  call f_testfunction(bar)

  write(*,*) bar
  write(*,*) iret
  contains

  subroutine f_testfunction(foo_f)
    integer, allocatable, intent(  out) :: foo_f(:,:)

    integer(c_int), pointer :: foo_c(:,:)

    foo_c => null()

    !call c_testfunction(foo_c)
    iret =  c_testfunction(foo_c)

    write(*,*)foo_c
    allocate(foo_f, source = foo_c)

  end subroutine

end program

 

 

0 Kudos
MWind2
New Contributor III
4,957 Views

64 bit mode works as well.  I do get warnings

>------ Build started: Project: cppdll, Configuration: Debug x64 ------
1>dllmain.cpp
1>D:\c\vs2019\icom\cppdll\ISO_Fortran_binding.h(156,20): warning C4200: nonstandard extension used: zero-sized array in struct/union
1>D:\c\vs2019\icom\cppdll\ISO_Fortran_binding.h(156,20): message : This member will be ignored by a defaulted constructor or copy/move assignment operator
1>D:\c\vs2019\icom\cppdll\dllmain.cpp(39,19): warning C4244: '=': conversion from 'CFI_index_t' to 'int', possible loss of data
1>D:\c\vs2019\icom\cppdll\dllmain.cpp(40,25): warning C4244: '=': conversion from 'CFI_index_t' to 'int', possible loss of data
1> Creating library D:\c\vs2019\icom\x64\Debug\cppdll.lib and object D:\c\vs2019\icom\x64\Debug\cppdll.exp
1>cppdll.vcxproj -> D:\c\vs2019\icom\x64\Debug\cppdll.dll
1>Done building project "cppdll.vcxproj".
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

0 Kudos
MWind2
New Contributor III
4,951 Views

On a 64 bit build with ?CA I get 

forrtl: severe (408): fort: (7): Attempt to use pointer FOO_C when it is not associated with a target

Image PC Routine Line Source
libifcoremdd.dll 00007FFC7C6A746C Unknown Unknown Unknown
hello.exe 00007FF73C3B1369 HELLO_ip_F_TESTFU 46 hello.f90
hello.exe 00007FF73C3B1068 MAIN__ 32 hello.f90
hello.exe 00007FF73C3B1D2E Unknown Unknown Unknown
hello.exe 00007FF73C3B46E9 Unknown Unknown Unknown
hello.exe 00007FF73C3B460E Unknown Unknown Unknown
hello.exe 00007FF73C3B44CE Unknown Unknown Unknown
hello.exe 00007FF73C3B4759 Unknown Unknown Unknown
KERNEL32.DLL 00007FFCF2E26FD4 Unknown Unknown Unknown
ntdll.dll 00007FFCF367CEC1 Unknown Unknown Unknown

 

which seems the problem as /CA I read as only for 32 bit compiles in an oler intel manual, but I see is in both 32 and 64 bit ifort options. Someone with more knowledge than me needed to explain such.

 

0 Kudos
Wolf_W_
New Contributor I
4,938 Views

Thanks for the confirmation as compiler bug Steve.

I will contact support about it.

@MWind2 good to know it only happens in 64bit

0 Kudos
MWind2
New Contributor III
4,923 Views

It happens in 32-bit as well. I must have made a mistake by not changing 32 bit project options.

0 Kudos
MWind2
New Contributor III
4,896 Views

If the allocation fails in 

e = (is_established == CFI_SUCCESS) && (is_allocated == CFI_SUCCESS) ? 0 : -1; // If allocation failed then e = -1

it would seem foo_ c could be null. Is that what /CA should object about when compiled? It seems to crash when the function is called, not after. Just curious.

Also, it seems in https://software.intel.com/content/www/us/en/develop/documentation/fortran-compiler-developer-guide-and-reference/top/language-reference/a-to-z-reference/c-to-d/cfi-establish.html

int CFI_establish(CFI_cdesc_t *
dv
, void *
base_addr
, CFI_attribute_t
attribute
, CFI_type_t
type
, size_t
elem_len
, CFI_rank_t
rank
, const CFI_index_t
extents
[]);

 

 
Formal Parameters:

 

dv
The address of a data object large enough to hold a C descriptor of the rank specified by 
rank
. It must not have the same value as either a C formal parameter that corresponds to a Fortran actual argument or a C actual argument that corresponds to a Fortran dummy argument. It must not be the address of a C descriptor that describes an allocated allocatable object.
 
that dv should already be allocated?

0 Kudos
Wolf_W_
New Contributor I
4,886 Views

The allocation in the c++ part is not the problem, as it is never reached. The dv pointer, is "magically" created by the intel fortran core library i think. It is present and allocated, when stepping into the c++ routine (without /CA).

The problem can be reproduced without the cpp part:

subroutine c_testfunction(foo_c) bind(C, name="c_testfunction")
  use, intrinsic :: iso_c_binding
  integer(c_int), pointer, intent(  out) :: foo_c(:,:)
  allocate(foo_c(2,4))
  foo_c(1,:) = [1,2,3,4]
  foo_c(2,:) = [10,20,30,40]
end subroutine

program hello
  use, intrinsic :: iso_c_binding
  interface
    subroutine c_testfunction(foo_c) bind(C, name="c_testfunction")
      import
      integer(c_int), pointer, intent(  out) :: foo_c(:,:)
    end subroutine
  end interface

  integer, allocatable :: bar(:,:)
  call f_testfunction(bar)
  write(*,*) bar

contains

  subroutine f_testfunction(foo_f)
    integer, allocatable, intent(  out) :: foo_f(:,:)
    integer(c_int), pointer :: foo_c(:,:)

    foo_c => null()
    call c_testfunction(foo_c)
    write(*,*)foo_c
    allocate(foo_f, source = foo_c)

  end subroutine
end program
0 Kudos
MWind2
New Contributor III
4,874 Views

It would seem to me that the

foo_c => null()

should be the offence as it would be passed as a null pointer. When I allocate enough or more memory to foo_c before it is passed to c_testfunction, /CA does not object. 

0 Kudos
Steve_Lionel
Honored Contributor III
4,866 Views

It should not matter that the pointer is null before calling the C routine. The dummy argument in the interface is a pointer, so there is no access of the data happening on the Fortran side of the call. The check is in error.

0 Kudos
MWind2
New Contributor III
4,859 Views

Thanks for clearing that up. Is it used correctly in

int is_established = CFI_establish(foo_c, NULL, CFI_attribute_pointer, CFI_type_int, 0, 2, NULL); 

where I would think it should not be null. 

0 Kudos
Steve_Lionel
Honored Contributor III
4,853 Views

It would be ok there too. You might want to do a CFI_allocate on it later, for example. A null pointer is a problem only when you're trying to access the data it points to.

0 Kudos
MWind2
New Contributor III
4,848 Views

Is that because a nullified Fortran pointer is not null like c, but with data structures and memory to "hold a C descriptor of the rank specified by rank".

0 Kudos
Steve_Lionel
Honored Contributor III
4,841 Views

Fortran pointers are not like C pointers. In Fortran, they do indeed hold information beyond the data address, and are indirectly accessed when used as ordinary variables. On the C interoperability side, a "C descriptor" is a complex data structure that can describe a scalar, an array, a pointer or an allocatable. The data address is just one aspect of this.

0 Kudos
Reply