Ross
Ross

Reputation: 2179

Truncation of deferred-length string when passing as optional

I am passing optional deferred-length strings (character(len=:), allocatable, optional) between subroutines and getting unexpected behavior in GCC.

Here is my minimal example, where I pass an optional string through an interface routine to a routine that sets it:

$ cat main.f90 
module deepest_call_m
   implicit none
contains
subroutine deepest_call(str)
   character(len=:), allocatable, intent(OUT), optional :: str

   if (present(str)) str = '12345'
   write(*,*) 'at bot of deepest_call, str is "'//trim(str)//'"'
end subroutine deepest_call
end module deepest_call_m

module interface_call_m
   implicit none
contains
subroutine interface_call(str)
   use deepest_call_m, only : deepest_call
   character(len=:), allocatable, intent(OUT), optional :: str

   call deepest_call(str=str)
   write(*,*) 'at bot of interface_call, str is "'//trim(str)//'"'
end subroutine interface_call
end module interface_call_m

program main
   use interface_call_m, only : interface_call
   implicit none

   character(len=:), allocatable :: str

   call interface_call(str=str)
   write(*,*) 'at bot of main, str is "'//trim(str)//'"'
end program main

(Note that, for simplicity, I'm not wrapping the write statements in if(present) and if(allocated), although that would be necessary for a real implementation.)

In Intel 16.0 and 2019_U4, and PGI 15.10, this routine gives the expected result: str is set in deepest_call and remains the same through interface_call and in main:

$ ifort --version
ifort (IFORT) 16.0.0 20150815
Copyright (C) 1985-2015 Intel Corporation.  All rights reserved.

$ ifort main.f90 && ./a.out 
 at bot of deepest_call, str is "12345"
 at bot of interface_call, str is "12345"
 at bot of main, str is "12345"

However, with gfortran 4.8.5:

$ gfortran --version
GNU Fortran (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36)
Copyright (C) 2015 Free Software Foundation, Inc.

GNU Fortran comes with NO WARRANTY, to the extent permitted by law.
You may redistribute copies of GNU Fortran
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING

$ gfortran main.f90 && ./a.out 
 at bot of deepest_call, str is "12345"
 at bot of interface_call, str is ""
 at bot of main, str is ""

The string has been truncated when returning from the deepest_call in interface_call. With gfortran 7.3.0 and 8.2.0, the code crashes at runtime when no compile-time options are provided:

[chaud106@epyc-login-1-0 Testing]$ gfortran --version && gfortran main.f90 && ./a.out
GNU Fortran (GCC) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 at bot of deepest_call, str is "12345"

Program received signal SIGSEGV: Segmentation fault - invalid memory reference.

Backtrace for this error:
#0  0x2af39a82733f in ???
#1  0x400c68 in ???
#2  0x400d9c in ???
#3  0x400f0e in ???
#4  0x2af39a813494 in ???
#5  0x400878 in ???
#6  0xffffffffffffffff in ???
Segmentation fault

However, adding compile-time checking recovers the previous truncation behavior:

$ gfortran --version
GNU Fortran (GCC) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gfortran -g -fbacktrace -Wall -Wextra -std=f2008 -fcheck=all -Og main.f90 && ./a.out
 at bot of deepest_call, str is "12345"
 at bot of interface_call, str is ""
 at bot of main, str is ""

This looks a lot like a compiler bug to me, so perhaps I just need to post on the bugzilla. But I would like some feedback here first, specifically:

  1. Is my test case standard-conforming? I'm specifically curious about the str=str argument to deepest_call. I use this structure often to pass an argument as optional if and only if it's optional in the present scope, but I couldn't easily find this in the standard and I'm not sure if it's really valid. Passing just str seemed to give the same behavior, though.

  2. Are there any simple workarounds? Given that this issue affects a wide range of versions (4.8.5 to 8.2.0) simply avoiding them is not feasible.

  3. Is anybody aware of this behavior for other versions of gfortran, or other compilers? I only have easy access to GCC, Intel, and PGI.

Upvotes: 2

Views: 215

Answers (1)

francescalus
francescalus

Reputation: 32366

Your code is standard compliant.

An optional dummy argument may be an actual dummy argument in a subsequent call without the keyword, and whether or not it is present. (Of course, it may only be not present in the case that the following dummy argument is optional.) That is:

call  interface_call(str)

is just as correct as

call  interface_call(str=str)

As to (suboptimal) workarounds (I see this fail with gfortran 9.1.0, but working with gfortran 10.0.0 20190625), you could consider having the arguments as not deferred-length. After all, you're using trim everywhere, so trailing whitespace would be chomped.


Your code is just one small change away from not being compliant: the write statements aren't protected by a presence check for the optional dummy. If the optional dummy is not present in the call chain then the program would be invalid.

Upvotes: 1

Related Questions