Search code examples
fortranlanguage-lawyerintel-fortran

Intel Fortran error #8284 for character type arguments


With Intel's ifort versions 2021.6.0+/ ifx 2022.1.0+ I am receiving error messages during compilation, specifically error #8284 about argument mismatch with an interface, which I suspect is erroneous.

Before I proceed to escalate this with the Intel team, I was wondering; is the following code Standard conforming? I was under the impression that it was indeed legal and that the compiler is incorrect in this case.

MWE

program main
  use iso_c_binding

  call foo("Fortran Arg")

  contains
  function istring_(o) result(v)
    character(len=*), intent(in) :: o
    character(len=:, kind=c_char), allocatable :: v
    v = trim(o)//c_null_char
  end function istring_

  subroutine foo(fileName)
    interface
    subroutine C_API(fileName) bind(C, name="foo")
      use, intrinsic :: iso_c_binding
      character(len=1, kind=c_char), dimension(*), intent(in) :: fileName
    end subroutine C_API
    end interface
    character(len=*), intent(in) :: fileName
    call C_API(fileName=istring_(fileName))
  end subroutine foo
end program main
#include "stdio.h"

void foo(const char* fileName) { printf("%s\n", fileName); }

Output

/app/example.f90(21): error #8284: If the actual argument is scalar, the dummy argument shall be scalar unless the actual argument is of type character or is an element of an array that is not assumed shape, pointer, or polymorphic.   [FILENAME]
    call C_API(fileName=istring_(fileName))
---------^

Fix

Replacing the istring_ call in the interface with a local variable compiles which seems to indicate to me that this is a potential bug/regression.

--- tmp.f90     2023-01-05 10:19:30.778230819 +0000
+++ tmp2.f90    2023-01-05 10:19:36.342418329 +0000
@@ -18,6 +18,8 @@
     end subroutine C_API
     end interface
     character(len=*), intent(in) :: fileName
-    call C_API(fileName=istring_(fileName))
+    character(len=:), allocatable :: fileNameC
+    fileNameC = istring_(fileName)
+    call C_API(fileName=fileNameC)
   end subroutine foo
 end program main

Additional Info

GFortran, Intel's older versions compilers and flang-trunk all can compile the MWE.


Solution

  • Your program may be non-conforming, in which case the compiler is allowed to respond with any gibberish it chooses.

    This doesn't mean that there is no issue to report to the vendor.

    Consider the problematic line:

    v = trim(o)//c_null_char
    

    This violates the Fortran standard in the case that default character (corresponding to o and trim(o)) has different kind type parameter from that of c_null_char (the C character kind if that exists). While unlikely, this is possible. (Even more extreme is the case that c_char has the value -1 in which case the program is even more non-conforming.)

    However, even in this case we have to concern ourselves with quality of implementation issues.

    Let's pretend the compiler has default and C characters of different kind. If our compiler rejects the source code with the message

       v = trim(o)//c_null_char
                  ^
                  |-------------------------------|
    Error: too many chickens crossing the road at |
    

    we'd quite legitimately be a touch annoyed with the compiler vendor. We'd consider this a bug (or a too trendy company we want to avoid).

    The Intel compiler isn't rejecting your program because of this reason (and it's likely the case that C and default characters are the same).

    Let's apply the same QoI assessment to the actual error message.

    If the actual argument is scalar, the dummy argument shall be scalar unless the actual argument is of type character or is an element of an array that is not assumed shape, pointer, or polymorphic.   [FILENAME]
    

    A compiler of high quality would have the error message meaningful when produced. Here, the actual argument is a scalar and the dummy argument is an array, so the error message is not wildly out a place.

    (The standard's requirement is actually slightly more restrictive than the error message notes, but this can be accepted.)

    The actual argument is of type (C) character making the error message unhelpful.

    Additional to the temporary variable as a workaround, there are several others available:

    call C_API(fileName=(istring_(fileName)))
    call C_API(fileName=istring_(fileName)//c_char_'')
    

    These add weight to the belief that the code is conforming, but the point of my answer here is that conformance doesn't actually matter in terms of whether the error message is valuable. Even if something is wrong with the subroutine reference, or program as a whole, a high quality compiler would not report in this way.


    For completeness, let's look at that actual argument in more detail (and convince ourself that the subroutine reference is legitimate).

    The subroutine reference

    call C_API(fileName=istring_(fileName))
    

    is to the subroutine C_API, with the actual argument istring_(fileName) using the keyword fileName.

    We're allowed to use that keyword, and filename is the name of the dummy argument.

    istring_(filename) is a (non-variable) primary expression, because istring_ is the name of the accessible internal function of the same host.1 This primary is evaluated before the subroutine reference and we note that the function result is a scalar C character. Exactly as we and the error message are expecting.


    1 Note that you don't have implicit none in the program. You want to use this (or implicit none (external)) to ensure that an external integer function isn't used by a typographical mistake. In which case the compiler error would be entirely correct.