Created attachment 47848 [details] Minimal reproducing code (This issue was first reported on SO here: https://stackoverflow.com/questions/60229805/truncation-of-deferred-length-string-when-passing-as-optional) When passing strings declared as deferred-length and optional through several subroutines, they are truncated. A minimal example is attached, which yields the following (expected) result on Intel 16.0, Intel 2019_U4, or PGI 15.10: $ 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 "" On the newer version of gfortran I have available (7.2 and 8.2.0), this simple example segfaults when compiled with no options, but truncates the output when compiled using checks: $ 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 "" It seems to me that this is a compiler error spanning a range of GCC versions (at least 4.8.5 to 8.2.0). So my first purpose for reporting it here is so people are aware of it. According to francescalus on the StackOverflow post, the error does not occur on the very latest 10.0.0. However, I think it would be good to add this minimal example (or something similar) to the gfortran test suite to prevent the error from occurring again.
Unfortunately, the test case fails with different ways on current trunk: $ gfortran -g a.f90 $ ./a.out at bot of deepest_call, str is "12345" Program received signal SIGSEGV: Segmentation fault - invalid memory reference. Backtrace for this error: #0 0x7f0a66c3059f in ??? at /usr/src/debug/glibc-2.26-lp151.19.11.1.x86_64/signal/../sysdeps/unix/sysv/linux/x86_64/sigaction.c:0 #1 0x400c65 in __interface_call_m_MOD_interface_call at /tmp/a.f90:20 #2 0x400d99 in MAIN__ at /tmp/a.f90:32 #3 0x400f0b in main at /tmp/a.f90:25 Speicherzugriffsfehler (Speicherabzug geschrieben) (gdb) r a.f90 Starting program: /tmp/a.out a.f90 at bot of deepest_call, str is "12345" Program received signal SIGSEGV, Segmentation fault. _gfortran_string_len_trim (s=0x6068d0 "12345", len=<optimized out>) at ../../../gcc/libgfortran/intrinsics/string_intrinsics_inc.c:231 231 if (*((unsigned long*) (s + i + 1)) != blank_longword) (gdb) p s $1 = 0x6068d0 "12345" (gdb) p i $2 = 564082115390472183 Seems like uninitialzed memory for i. Valgrind confirms this: $ valgrind ./a.out ==5621== Memcheck, a memory error detector ==5621== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==5621== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info ==5621== Command: ./a.out ==5621== at bot of deepest_call, str is "12345" ==5621== Conditional jump or move depends on uninitialised value(s) ==5621== at 0x50A29A5: _gfortran_string_len_trim (string_intrinsics_inc.c:188) ==5621== by 0x50A2A87: _gfortran_string_trim (string_intrinsics_inc.c:168) ==5621== by 0x400C65: __interface_call_m_MOD_interface_call (a.f90:20) ==5621== by 0x400D99: MAIN__ (a.f90:32) ==5621== by 0x400F0B: main (a.f90:25) Not sure if this ever worked in a released version.
(In reply to Thomas Koenig from comment #1) > Unfortunately, the test case fails with different ways on > current trunk: > > $ gfortran -g a.f90 > $ ./a.out > at bot of deepest_call, str is "12345" > > Program received signal SIGSEGV: Segmentation fault - invalid memory > reference. > > Backtrace for this error: > #0 0x7f0a66c3059f in ??? > at > /usr/src/debug/glibc-2.26-lp151.19.11.1.x86_64/signal/../sysdeps/unix/sysv/ > linux/x86_64/sigaction.c:0 > #1 0x400c65 in __interface_call_m_MOD_interface_call > at /tmp/a.f90:20 > #2 0x400d99 in MAIN__ > at /tmp/a.f90:32 > #3 0x400f0b in main > at /tmp/a.f90:25 > Speicherzugriffsfehler (Speicherabzug geschrieben) > > (gdb) r a.f90 > Starting program: /tmp/a.out a.f90 > at bot of deepest_call, str is "12345" > > Program received signal SIGSEGV, Segmentation fault. > _gfortran_string_len_trim (s=0x6068d0 "12345", len=<optimized out>) at > ../../../gcc/libgfortran/intrinsics/string_intrinsics_inc.c:231 > 231 if (*((unsigned long*) (s + i + 1)) != blank_longword) > (gdb) p s > $1 = 0x6068d0 "12345" > (gdb) p i > $2 = 564082115390472183 > > Seems like uninitialzed memory for i. > > Valgrind confirms this: > > $ valgrind ./a.out > ==5621== Memcheck, a memory error detector > ==5621== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. > ==5621== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info > ==5621== Command: ./a.out > ==5621== > at bot of deepest_call, str is "12345" > ==5621== Conditional jump or move depends on uninitialised value(s) > ==5621== at 0x50A29A5: _gfortran_string_len_trim > (string_intrinsics_inc.c:188) > ==5621== by 0x50A2A87: _gfortran_string_trim (string_intrinsics_inc.c:168) > ==5621== by 0x400C65: __interface_call_m_MOD_interface_call (a.f90:20) > ==5621== by 0x400D99: MAIN__ (a.f90:32) > ==5621== by 0x400F0B: main (a.f90:25) > > Not sure if this ever worked in a released version. I doubt it ever worked. It seems that the length is not getting set properly for the returning string. Should this be propagated up the call change in the hidden string length argument. Here's a modified testcase where I print out lengths of str. module deepest_call_m implicit none contains subroutine deepest_call(str) character(len=:), allocatable, optional :: str character(len=5) t t = '12345' if (present(str)) then str = t write(*,*) 'at bot of deepest_call, str is "'//trim(str)//'"' end if print *, 'len = ', len(str) print '(A)', 'Returning from deepest_call' 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, optional :: str if (present(str)) then call deepest_call(str) print *, 'len = ', len(str) write(*,*) 'at bot of interface_call, str is "'//trim(str)//'"' end if 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) write(*,*) 'at bot of main, str is "'//trim(str)//'"' end program main I get % gfcx -o z -g a.f90 && ./z at bot of deepest_call, str is "12345" len = 5 Returning from deepest_call len = 134516966 Segmentation fault (core dumped) len = 5 is in deepest_call and the correct value. len = 134516966 seems to be a bit too large.
Here's a better testcasei, which removes IO statement, which makes it easier to read -fdump-tree-original. module deepest_call_m implicit none contains subroutine deepest_call(str) character(len=:), allocatable, intent(out), optional :: str if (present(str)) then str = '12345' if (len(str) /= 5) stop 1 end if 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 if (present(str)) then call deepest_call(str) if (len(str) /= 5) stop 2 end if 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) if (len(str) /= 5) stop 3 end program main Here's the -fdump-tree-original where I have removed inconsequential code and re-ordered to help with thinking. Comments are in-lined. MAIN__ () { integer(kind=4) .str; character(kind=1)[1:.str] * str; str = 0B; interface_call (&str, &.str); /* .str is not set to some value. */ } interface_call (character(kind=1)[1:*_str] * * str, integer(kind=4) * _str) { if (str != 0B) { { /* This is not good. *_str has the value of .str from MAIN, which wasn't set. */ character(kind=1)[1:*_str] * *D.3819; integer(kind=4) D.3820; /* Remove freeing from intent(out) attribute. */ D.3819 = str != 0B ? str : 0B; D.3820 = str != 0B ? *_str : 0; /* Here D.3820 is 0. */ deepest_call (D.3819, &D.3820); /* *_str should be set to D.3820, but isn't. */ } } } deepest_call (character(kind=1)[1:*_str] * * str, integer(kind=4) * _str) { if (str != 0B) { { integer(kind=4) D.3808; integer(kind=4) D.3809; /* Handle intent(out) and/or re-allocation on assign. */ /* Set *_str to 5, which is the desired length. */ *_str = 5; D.3808 = *_str; if (D.3808 > 0) { /* Copy '12345' into str. */ } } } } So, yep! The string length is not propagated up the call chain.
It would be great if somebody possessing the necessary skills could invest the time to fix this. For me this is bug breaks a common usage pattern of including optional stat, errmsg arguments to procedure interfaces.