diff options
-rw-r--r-- | gcc/fortran/symbol.cc | 54 | ||||
-rw-r--r-- | gcc/testsuite/gfortran.dg/submodule_33.f08 | 20 |
2 files changed, 72 insertions, 2 deletions
diff --git a/gcc/fortran/symbol.cc b/gcc/fortran/symbol.cc index 8f7deac..0a1646d 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 0000000..b61d750d --- /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 |