https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127881

            Bug ID: 127881
           Summary: Wrong code: address instead of value passed for
                    CHARACTER(c_char),VALUE dummy of BIND(C) interface
                    when actual is a dummy argument or ACHAR() result
           Product: gcc
           Version: 16.2.1
            Status: UNCONFIRMED
          Keywords: wrong-code
          Severity: normal
          Priority: P3
         Component: fortran
          Assignee: unassigned at gcc dot gnu.org
          Reporter: albert at tugraz dot at
  Target Milestone: ---

For a BIND(C) interface with a CHARACTER(kind=c_char), VALUE dummy argument,
gfortran passes an address instead of the character value when the actual
argument is

  (a) a scalar character dummy argument of the calling procedure, or
  (b) an ACHAR() expression.

A local variable, an array element or a literal constant is passed correctly.
The code is standard-conforming (F2018 18.3.6 and Table 18.2: CHARACTER(c_char)
of length 1 with VALUE interoperates with C char).

Testcase (pr.f90):

! gfortran passes an address instead of the value for a CHARACTER(c_char),
! VALUE dummy of a BIND(C) interface when the actual argument is a dummy
! argument of the caller or an ACHAR() expression.
module m
    use, intrinsic :: iso_c_binding, only: c_char, c_int
    implicit none
    interface
        function echo_char(a) bind(C, name="echo_char") result(r)
            import :: c_char, c_int
            character(kind=c_char), value, intent(in) :: a
            integer(c_int) :: r
        end function echo_char
    end interface
contains
    integer function via_dummy(c)
        character(kind=c_char), intent(in) :: c
        via_dummy = echo_char(c)
    end function via_dummy
end module m

program pr
    use m
    implicit none
    integer :: i, nbad_local, nbad_dummy, nbad_achar
    character(kind=c_char) :: c
    nbad_local = 0; nbad_dummy = 0; nbad_achar = 0
    do i = 0, 127
        c = achar(i)
        if (echo_char(c) /= i) nbad_local = nbad_local + 1
        if (via_dummy(c) /= i) nbad_dummy = nbad_dummy + 1
        if (echo_char(achar(i)) /= i) nbad_achar = nbad_achar + 1
    end do
    print '(3(a,i0))', 'local variable: ', nbad_local, &
        '  dummy argument: ', nbad_dummy, '  achar(): ', nbad_achar
    if (nbad_local + nbad_dummy + nbad_achar /= 0) stop 1
end program pr

pr.c:

int echo_char(char a) { return (unsigned char)a; }

$ gfortran -O0 pr.f90 pr.c && ./a.out
local variable: 0  dummy argument: 127  achar(): 128
STOP 1

$ gfortran -O2 pr.f90 pr.c && ./a.out
local variable: 0  dummy argument: 127  achar(): 127
STOP 1

Expected (flang 23.1.1 gives this): local variable: 0  dummy argument: 0 
achar(): 0

-fdump-tree-original shows the problem:

  __result_via_dummy = echo_char (*(character(kind=1) *) &c);   <- c is already
a reference parameter
  if (echo_char (c[1]{lb: 1 sz: 1}) != i)                     <- local
variable: correct
  if (echo_char (&char.1) != i)                                 <- achar(i):
address passed

At -O2 the dummy-argument case passes the low byte of the pointer (movzbl
%sil,%eax).

The same calls without BIND(C) on the interface work. Reproduced with -O0, -O2,
-O3 -march=native, -flto, -fcheck=all and -fopenmp, and with the C file
compiled by gcc or clang.

GNU Fortran (GCC) 16.2.1 20260810, x86_64-pc-linux-gnu (Arch/CachyOS package).
I could not test older releases. The fix for PR 121727 touched character VALUE
argument passing for 16, so this may be related, but that is unverified.

Reply via email to