Hi Mikael, That is an ingenious solution. Given the complexity, I think that the comments are well warranted.
OK for master and, I would suggest, 14-branch after a few weeks. Thanks! Paul On Sun, 12 May 2024 at 14:16, Mikael Morin <mik...@gcc.gnu.org> wrote: > Hello, > > Here is my final patch to fix the ICE of PR99798. > It's maybe overly verbose with comments, but the memory management is > hopefully clarified. > I tested this with a full fortran regression test on x86_64-linux and a > manual check with valgrind on the testcase. > OK for master? > > -- 8< -- > > This prevents a premature release of memory with procedure symbols from > submodules, causing random compiler crashes. > > The problem is a fragile detection of cyclic references, which can match > with procedures host-associated from a module in submodules, in cases > where it > shouldn't. The formal namespace is released, and with it the dummy > arguments > symbols of the procedure. But there is no cyclic reference, so the > procedure > symbol itself is not released and remains, with pointers to its dummy > arguments > now dangling. > > The fix adds a condition to avoid the case, and refactors to a new > predicate > by the way. Part of the original condition is also removed, for lack of a > reason to keep it. > > PR fortran/99798 > > gcc/fortran/ChangeLog: > > * symbol.cc (gfc_release_symbol): Move the condition guarding > the handling cyclic references... > (cyclic_reference_break_needed): ... here as a new predicate. > Remove superfluous parts. Add a condition preventing any premature > release with submodule symbols. > > gcc/testsuite/ChangeLog: > > * gfortran.dg/submodule_33.f08: New test. > --- > gcc/fortran/symbol.cc | 54 +++++++++++++++++++++- > gcc/testsuite/gfortran.dg/submodule_33.f08 | 20 ++++++++ > 2 files changed, 72 insertions(+), 2 deletions(-) > create mode 100644 gcc/testsuite/gfortran.dg/submodule_33.f08 > > diff --git a/gcc/fortran/symbol.cc b/gcc/fortran/symbol.cc > index 8f7deac1d1e..0a1646def67 100644 > --- a/gcc/fortran/symbol.cc > +++ b/gcc/fortran/symbol.cc > @@ -3179,6 +3179,57 @@ gfc_free_symbol (gfc_symbol *&sym) > } > > > +/* Returns true if the symbol SYM has, through its FORMAL_NS field, a > reference > + to itself which should be eliminated for the symbol memory to be > released > + via normal reference counting. > + > + The implementation is crucial as it controls the proper release of > symbols, > + especially (contained) procedure symbols, which can represent a lot of > memory > + through the namespace of their body. > + > + We try to avoid freeing too much memory (causing dangling pointers), > to not > + leak too much (wasting memory), and to avoid expensive walks of the > symbol > + tree (which would be the correct way to check for a cycle). */ > + > +bool > +cyclic_reference_break_needed (gfc_symbol *sym) > +{ > + /* Normal symbols don't reference themselves. */ > + if (sym->formal_ns == nullptr) > + return false; > + > + /* Procedures at the root of the file do have a self reference, but > they don't > + have a reference in a parent namespace preventing the release of the > + procedure namespace, so they can use the normal reference counting. > */ > + if (sym->formal_ns == sym->ns) > + return false; > + > + /* If sym->refs == 1, we can use normal reference counting. If > sym->refs > 2, > + the symbol won't be freed anyway, with or without cyclic reference. > */ > + if (sym->refs != 2) > + return false; > + > + /* Procedure symbols host-associated from a module in submodules are > special, > + because the namespace of the procedure block in the submodule is > different > + from the FORMAL_NS namespace generated by host-association. So > there are > + two different namespaces representing the same procedure namespace. > As > + FORMAL_NS comes from host-association, which only imports symbols > visible > + from the outside (dummy arguments basically), we can assume there is > no > + self reference through FORMAL_NS in that case. */ > + if (sym->attr.host_assoc && sym->attr.used_in_submodule) > + return false; > + > + /* We can assume that contained procedures have cyclic references, > because > + the symbol of the procedure itself is accessible in the procedure > body > + namespace. So we assume that symbols with a formal namespace > different > + from the declaration namespace and two references, one of which is > about > + to be removed, are procedures with just the self reference left. At > this > + point, the symbol SYM matches that pattern, so we return true here to > + permit the release of SYM. */ > + return true; > +} > + > + > /* Decrease the reference counter and free memory when we reach zero. > Returns true if the symbol has been freed, false otherwise. */ > > @@ -3188,8 +3239,7 @@ gfc_release_symbol (gfc_symbol *&sym) > if (sym == NULL) > return false; > > - if (sym->formal_ns != NULL && sym->refs == 2 && sym->formal_ns != > sym->ns > - && (!sym->attr.entry || !sym->module)) > + if (cyclic_reference_break_needed (sym)) > { > /* As formal_ns contains a reference to sym, delete formal_ns just > before the deletion of sym. */ > diff --git a/gcc/testsuite/gfortran.dg/submodule_33.f08 > b/gcc/testsuite/gfortran.dg/submodule_33.f08 > new file mode 100644 > index 00000000000..b61d750def1 > --- /dev/null > +++ b/gcc/testsuite/gfortran.dg/submodule_33.f08 > @@ -0,0 +1,20 @@ > +! { dg-do compile } > +! > +! PR fortran/99798 > +! This example used to trigger an ICE caused by a premature release of > the G > +! symbol (with its argument X) following the rejection of the subroutine > in > +! the submodule. > + > +module m > + interface > + module integer function g(x) > + integer, intent(in) :: x > + end > + end interface > +end > +submodule(m) m2 > +contains > + subroutine g(x) ! { dg-error "FUNCTION attribute conflicts with > SUBROUTINE" } > + integer, intent(in) :: x ! { dg-error "Unexpected data declaration" > } > + end > +end > -- > 2.43.0 > >