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

            Bug ID: 88511
           Summary: passing allocatable character through two levels of
                    procedure calls fails
           Product: gcc
           Version: 7.3.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: fortran
          Assignee: unassigned at gcc dot gnu.org
          Reporter: stephen at soliday dot com
  Target Milestone: ---

Please see the thread that I started in Stack overflow. Several respondents
have reproduced this problem in 8.2.1 and 9.0.0. In those threads there is
reduced version of code that also reproduces the bug.


I am experiencing an allocation failure when using allocatable character
strings as optional arguments. The problem only occurs when I call through two
levels of procedures. In my actual code ***call get_level1()*** (see below)
represents a call to a list data structure and ***call get_level2()***
represents the list calling the same type of accessor function on one of its
records. I have stripped down an example to the bare minimum that adequately
reproduces the problem.

In the code below when I call ***get_level2*** directly the expected character
string is returned through the optional argument. When I call ***get_level1***
which in turn calls ***get_level2*** allocation of the optional dummy argument
fails. Using gdb I find the allocation attempt to create a character*1635...
when it gets back to the actual argument is obviously has an integer overflow
because it thinks the allocation is character*-283635612...

My actual code has many optional arguments not just one. As a simple example I
added an optional integer argument. This time instead of a segmentation fault I
get a null string.

In the second example the integer argument works regardless of using the
character argument. (I would expect this since no  dynamic allocation is being
performed) The integer's presence has no effect on the character. I have also
tried changing the **intent** to (inout). This does not change the behavior,
though I did not expect it to. [I believe that *intent(out)* causes the actual
argument to deallocate first, and *intent(inout)* retains the actual argument's
allocation state]

    call get_level1( NUM=n )              ! works
    call get_level1( NUM=n, TEXT=words )  ! fails
    call get_level1( TEXT=words )         ! fails

my compile cmd is:

    gfortran -Wall -g -std=f2008ts stest1.f08 -o stest

**Environment**

    Linux 4.15.0-42-generic #45-Ubuntu SMP x86_64 GNU/Linux
    GNU Fortran (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0

**Example with one optional argument**

    module stest1
      implicit none

      character(:), allocatable :: data

    contains

      subroutine get_level2( TEXT )
        implicit none
        character(:), optional, allocatable, intent(out) :: TEXT

        if ( PRESENT( TEXT ) ) then
           TEXT = 'Prefix: ' // data // ' :postfix'
        end if

      end subroutine get_level2


      subroutine get_level1( TEXT )
        implicit none
        character(:), optional, allocatable, intent(out) :: TEXT

        call get_level2( TEXT )

      end subroutine get_level1

    end module stest1


    program main
      use stest1
      implicit none

      character(:), allocatable :: words

      data  = 'Hello Doctor'

      call get_level1( words )

      write(*,100) words

    100 format( 'words = [',A,']' )

    end program main

**Example with two optional arguments**

    module stest2
      implicit none

      character(:), allocatable :: data
      integer                   :: count

    contains

      subroutine get_level2( TEXT, NUM )
        implicit none
        character(:), optional, allocatable, intent(out) :: TEXT
        integer,      optional,              intent(out) :: NUM

        if ( PRESENT( TEXT ) ) then
           TEXT = 'Prefix: ' // data // ' :postfix'
        end if

        if ( PRESENT( NUM ) ) then
           NUM = count
        end if

      end subroutine get_level2


      subroutine get_level1( TEXT, NUM )
        implicit none
        character(:), optional, allocatable, intent(out) :: TEXT
        integer,      optional,              intent(out) :: NUM

        call get_level2( NUM=NUM, TEXT=TEXT )

      end subroutine get_level1

    end module stest2


    program main
      use stest2
      implicit none

      character(:), allocatable :: words
      integer                   :: n

      count = 42
      data  = 'Hello Doctor'

      call get_level1( TEXT=words )

      write(*,100) words
      write(*,110) n

    100 format( 'words = [',A,']' )
    110 format( 'N     = [',I0,']' )

    end program main

Reply via email to