Bug 125535 - Wrong-code for implied-do with allocatable-component derived type and transformational intrinsic
Summary: Wrong-code for implied-do with allocatable-component derived type and transfo...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: fortran (show other bugs)
Version: 16.0
: P3 normal
Target Milestone: ---
Assignee: Jerry DeLisle
URL:
Keywords: wrong-code
Depends on:
Blocks: 125515
  Show dependency tree / graph
 
Reported: 2026-05-31 20:22 UTC by Jerry DeLisle
Modified: 2026-06-21 02:52 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2026-05-31 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jerry DeLisle 2026-05-31 20:22:46 UTC
When a nested implied-do array constructor calls a  transformational intrinsic (e.g. RESHAPE) and the element type is a derived type with allocatable components, gfortran produces wrong runtime values or crashes with a heap-use-after-free.

The argument temporaries are freed before the result's allocatable components are deep-copied.  Since RESHAPE does a shallow byte-copy, the result's component pointers alias those of the temporaries, so freeing first yields use-after-free.

  module m
    implicit none
    type :: t
      real, allocatable :: v(:)
    end type
  contains
    pure function make (x) result (r)
      real, intent(in) :: x(:)
      type(t) :: r
      r%v = x
    end function
  end module

  program test
    use m
    implicit none
    integer, parameter :: n = 3, k = 2
    type(t), allocatable :: a(:,:)
    real :: h(k, n)
    integer :: i, j

    h(1,:) = [10., 20., 30.]
    h(2,:) = [11., 21., 31.]

    a = reshape ([([(make (h(:,i)), j=1,1)], i=1,n)], [1,n])

    if (any (a(1,1)%v /= h(:,1))) stop 1
    if (any (a(1,2)%v /= h(:,2))) stop 2
    if (any (a(1,3)%v /= h(:,3))) stop 3
  end program

  $ gfortran implied_do_alloc_comp_1.f90 && ./a.out

  The program stops at STOP 1 or crashes with a
  heap-use-after-free.  It should run and exit cleanly.

This one could be related to 90519 and 96910, not sure
Comment 1 GCC Commits 2026-06-07 19:51:49 UTC
The master branch has been updated by Jerry DeLisle <jvdelisle@gcc.gnu.org>:

https://gcc.gnu.org/g:b5a3b874b8df2ea2ca82bd97fcd5906f7ebe82d8

commit r17-1410-gb5a3b874b8df2ea2ca82bd97fcd5906f7ebe82d8
Author: Jerry DeLisle <jvdelisle@gcc.gnu.org>
Date:   Fri Jun 5 13:20:05 2026 -0700

    fortran: [PR125535] wrong-code for implied-do with allocatable-component
    
    When a nested implied-do array constructor called a transformational
    intrinsic (e.g. RESHAPE) whose result type has allocatable components, the
    argument temporaries were freed before the result's allocatable components
    were deep-copied, causing a heap-use-after-free and wrong runtime values.
    
    Transformational library functions such as RESHAPE do a shallow byte-copy
    of the source array into the result, so the result's component pointers
    alias those of the argument temporaries. Freeing the temporaries first
    and then copying yielded use-after-free.
    
    Move gfc_add_block_to_block (&se->pre, &post) to after the deep-copy
    loop, so that the result's allocatable components are copied while the
    source storage is still live.
    
    Assisted by: Claude Sonnet 4.6
    
            PR fortran/125535
    
    gcc/fortran/ChangeLog:
    
            * trans-expr.cc (gfc_conv_procedure_call): Move post block append
            to after the deep copy of allocatable components for transformational
            intrinsics, so that argument temporaries are not freed before the
            result components are copied.
    
    gcc/testsuite/ChangeLog:
    
            * gfortran.dg/implied_do_alloc_comp_1.f90: New test.
Comment 2 Jerry DeLisle 2026-06-14 00:18:13 UTC
Followup patch submitted for approval to fix a discovered memory leak,

https://gcc.gnu.org/pipermail/fortran/2026-June/064102.html
Comment 3 GCC Commits 2026-06-15 16:30:38 UTC
The releases/gcc-16 branch has been updated by Jerry DeLisle <jvdelisle@gcc.gnu.org>:

https://gcc.gnu.org/g:d1750d8df75f292a0111e94dda82664c988bcc60

commit r16-9101-gd1750d8df75f292a0111e94dda82664c988bcc60
Author: Jerry DeLisle <jvdelisle@gcc.gnu.org>
Date:   Fri Jun 5 13:20:05 2026 -0700

    fortran: wrong-code for implied-do with allocatable-component derived type
    
    When a nested implied-do array constructor called a transformational
    intrinsic (e.g. RESHAPE) whose result type has allocatable components, the
    argument temporaries were freed before the result's allocatable components
    were deep-copied, causing a heap-use-after-free and wrong runtime values.
    
    The root cause: in gfc_conv_procedure_call, the call to
    gfc_add_block_to_block (&se->pre, &post) â which frees the allocatable
    components of argument temporaries â was issued before the deep-copy loop
    for the result's allocatable components.  Transformational library functions
    such as RESHAPE do a shallow byte-copy of the source array into the result,
    so the result's component pointers alias those of the argument temporaries.
    Freeing the temporaries first and then copying yielded use-after-free.
    
    Fix: move gfc_add_block_to_block (&se->pre, &post) to after the deep-copy
    loop, so that the result's allocatable components are copied while the
    source storage is still live.
    
    PR fortran/125535
    
    Assisted-by: Claude Sonnet 4.6
    
    gcc/fortran/ChangeLog:
    
            PR fortran/125535
            * trans-expr.cc (gfc_conv_procedure_call): Move post block append
            to after the deep copy of allocatable components for transformational
            intrinsics, so that argument temporaries are not freed before the
            result components are copied.
    
    gcc/testsuite/ChangeLog:
    
            PR fortran/125535
            * gfortran.dg/implied_do_alloc_comp_1.f90: New test.
    
    (cherry picked from commit a0d7e1c3639a6e19f4413bf53bb1e994494ec77f)
Comment 4 GCC Commits 2026-06-15 16:31:31 UTC
The releases/gcc-16 branch has been updated by Jerry DeLisle <jvdelisle@gcc.gnu.org>:

https://gcc.gnu.org/g:1da2423372115bf33c9885ccd681ca800d848e49

commit r16-9111-g1da2423372115bf33c9885ccd681ca800d848e49
Author: Jerry DeLisle <jvdelisle@gcc.gnu.org>
Date:   Thu Jun 11 18:50:37 2026 -0700

    fortran: [PR125535] Plug remaining leak in implied-do array constructor with allocatable components
    
    Follow-up to the PR fortran/125535 wrong-code fix (commit 1b8421e9d5b,
    already pushed): that fix corrected the *values* produced by an
    implied-do array constructor of derived-type function results with
    allocatable components, but left a separate memory leak in the same
    code path.
    
    An array constructor whose implied-do produces function results of a
    derived type with allocatable components moves each result into the
    constructor temporary, so the temporary owns those components.  The
    per-element finalization in gfc_trans_array_ctor_element only freed
    the single slot referenced by the final loop offset, leaking the
    allocatable components of every other element the loop produced.
    
    A whole-array sweep is the only way to free every slot written by an
    implied-do, but it may be used only when every element is an owned
    function result: a variable element is shallow-copied into the
    temporary and its components are aliased rather than owned, so freeing
    them would double free.  Add gfc_constructor_is_owned_alloc_comp to
    detect the all-owned case and, when it holds, suppress the per-element
    finalization and emit a single gfc_deallocate_alloc_comp_no_caf over
    the whole temporary.
    
    Assisted-by: Claude Opus 4.8
    
            PR fortran/125535
    
    gcc/fortran/ChangeLog:
    
            * trans-array.cc (gfc_constructor_is_owned_alloc_comp): New function.
            (gfc_trans_array_constructor_value): Add OWNED_SWEEP parameter and,
            when set, suppress the per-element finalization.  Pass it through the
            recursive call.
            (trans_array_constructor): Compute OWNED_SWEEP and, when set,
            deallocate the allocatable components of the whole temporary in one
            sweep.
    
    gcc/testsuite/ChangeLog:
    
            * gfortran.dg/asan/implied_do_alloc_comp_leak_1.f90: New test.
    
    (cherry picked from commit b2ec4ebcf914f32f51c35dbd9a2c98dab5841097)
Comment 5 GCC Commits 2026-06-18 17:57:40 UTC
The master branch has been updated by Jerry DeLisle <jvdelisle@gcc.gnu.org>:

https://gcc.gnu.org/g:ccfe2771c6f5272a74561ed5ce02f8569f248607

commit r17-1667-gccfe2771c6f5272a74561ed5ce02f8569f248607
Author: Jerry DeLisle <jvdelisle@gcc.gnu.org>
Date:   Thu Jun 11 18:50:37 2026 -0700

    fortran: [PR125535] Plug remaining leak in implied-do array constructor with allocatable components
    
    Follow-up to the PR fortran/125535 wrong-code fix (commit 1b8421e9d5b,
    already pushed): that fix corrected the *values* produced by an
    implied-do array constructor of derived-type function results with
    allocatable components, but left a separate memory leak in the same
    code path.
    
    An array constructor whose implied-do produces function results of a
    derived type with allocatable components moves each result into the
    constructor temporary, so the temporary owns those components.  The
    per-element finalization in gfc_trans_array_ctor_element only freed
    the single slot referenced by the final loop offset, leaking the
    allocatable components of every other element the loop produced.
    
    A whole-array sweep is the only way to free every slot written by an
    implied-do, but it may be used only when every element is an owned
    function result: a variable element is shallow-copied into the
    temporary and its components are aliased rather than owned, so freeing
    them would double free.  Add gfc_constructor_is_owned_alloc_comp to
    detect the all-owned case and, when it holds, suppress the per-element
    finalization and emit a single gfc_deallocate_alloc_comp_no_caf over
    the whole temporary.
    
            PR fortran/125535
    
    gcc/fortran/ChangeLog:
    
            * trans-array.cc (gfc_constructor_is_owned_alloc_comp): New function.
            (gfc_trans_array_constructor_value): Add OWNED_SWEEP parameter and,
            when set, suppress the per-element finalization.  Pass it through the
            recursive call.
            (trans_array_constructor): Compute OWNED_SWEEP and, when set,
            deallocate the allocatable components of the whole temporary in one
            sweep.
    
    gcc/testsuite/ChangeLog:
    
            * gfortran.dg/asan/implied_do_alloc_comp_leak_1.f90: New test.
Comment 6 Jerry DeLisle 2026-06-21 02:52:56 UTC
Fixed and closing