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

--- Comment #7 from Tomáš Trnka <trnka at scm dot com> ---
(In reply to Paul Thomas from comment #5)
> Created attachment 55144 [details]
> Fix for this PR
> 
> Thanks for reporting this. The patch "fingered" in comment #4 is certainly
> responsible for this regression. In particular, it is the first chunk in
> resolve.cc that is the culprit.
> 
> The attached patch feels to be a bit of sticking plaster on top of sticking
> plaster and so I will go back to hunt down the root cause of these
> namespace-less symbols.

Thanks for the patch. It seems to mostly do the trick for our huge proprietary
F2008 codebase, but some files ultimately fail to compile with the following
error (not sure if related or a different bug):

in gfc_format_decoder, at fortran/error.cc:1078
0xb01b5a gfc_format_decoder
        ../../gcc/fortran/error.cc:1078
0x1594c0c pp_format(pretty_printer*, text_info*)
        ../../gcc/pretty-print.cc:1475
0x10f0c5e diagnostic_report_diagnostic(diagnostic_context*, diagnostic_info*)
        ../../gcc/diagnostic.cc:1592
0x1789c5d gfc_report_diagnostic
        ../../gcc/fortran/error.cc:890
0x1789c5d gfc_warning
        ../../gcc/fortran/error.cc:923
0x1789da7 gfc_warning(int, char const*, ...)
        ../../gcc/fortran/error.cc:954
0x1852c41 resolve_procedure_expression
        ../../gcc/fortran/resolve.cc:1957
0x1852c41 resolve_variable
        ../../gcc/fortran/resolve.cc:6030
0x1852c41 gfc_resolve_expr(gfc_expr*)
        ../../gcc/fortran/resolve.cc:7266
0x1806880 gfc_resolve_expr(gfc_expr*)
        ../../gcc/fortran/resolve.cc:7231
0x1806880 resolve_structure_cons
        ../../gcc/fortran/resolve.cc:1341
0x1858969 resolve_values
        ../../gcc/fortran/resolve.cc:12771
0x1869492 do_traverse_symtree
        ../../gcc/fortran/symbol.cc:4190
0x185b02f gfc_traverse_ns(gfc_namespace*, void (*)(gfc_symbol*))
        ../../gcc/fortran/symbol.cc:4215
0x185b02f resolve_types
        ../../gcc/fortran/resolve.cc:17899
0x184cf93 gfc_resolve(gfc_namespace*)
        ../../gcc/fortran/resolve.cc:17996
0x184fb47 resolve_symbol
        ../../gcc/fortran/resolve.cc:16567
0x1869492 do_traverse_symtree
        ../../gcc/fortran/symbol.cc:4190
0x185aee0 gfc_traverse_ns(gfc_namespace*, void (*)(gfc_symbol*))
        ../../gcc/fortran/symbol.cc:4215
0x185aee0 resolve_types
        ../../gcc/fortran/resolve.cc:17880


This seems to be the following assert:

gcc_assert (loc->nextc - loc->lb->line >= 0);

The backtrace I get from gdb is a little different (there's no
resolve_structure_cons in it, for example; I guess that it might be due to
LTO):

#0  gfc_warning (opt=0,
    gmsgid=0x1e55748 "Non-RECURSIVE procedure %qs at %L is possibly calling
itself recursively.  Declare it RECURSIVE or use %<-frecursive%>")
    at ../../gcc/fortran/error.cc:950
#1  0x0000000001852c42 in resolve_procedure_expression (expr=0x2aefc80) at
../../gcc/fortran/resolve.cc:1957
#2  resolve_variable (e=0x2aefc80) at ../../gcc/fortran/resolve.cc:6030
#3  gfc_resolve_expr (e=0x2aefc80) at ../../gcc/fortran/resolve.cc:7266
#4  0x0000000001806881 in gfc_resolve_expr (e=0x2aefc80) at
../../gcc/fortran/resolve.cc:7231
#5  resolve_structure_cons (expr=<optimized out>, init=1) at
../../gcc/fortran/resolve.cc:1341
#6  0x000000000185896a in resolve_values (sym=0x2ad30c0) at
../../gcc/fortran/resolve.cc:12771
#7  0x0000000001869493 in do_traverse_symtree (st=<optimized out>, st_func=0x0,
sym_func=0x1858900 <resolve_values(gfc_symbol*)>)
    at ../../gcc/fortran/symbol.cc:4190
#8  0x000000000185b030 in gfc_traverse_ns (sym_func=0x1858900
<resolve_values(gfc_symbol*)>, ns=0x3ae65e0) at
../../gcc/fortran/symbol.cc:4215
#9  resolve_types (ns=0x3ae65e0) at ../../gcc/fortran/resolve.cc:17899
#10 0x000000000184cf94 in gfc_resolve (ns=0x3ae65e0) at
../../gcc/fortran/resolve.cc:17996
#11 0x000000000184d022 in gfc_resolve (ns=<optimized out>) at
../../gcc/fortran/resolve.cc:17983
#12 0x000000000184fb48 in resolve_symbol (sym=<optimized out>) at
../../gcc/fortran/resolve.cc:16567
#13 0x0000000001869493 in do_traverse_symtree (st=<optimized out>, st_func=0x0,
sym_func=0x184d030 <resolve_symbol(gfc_symbol*)>)
    at ../../gcc/fortran/symbol.cc:4190
#14 0x000000000185aee1 in gfc_traverse_ns (sym_func=0x184d030
<resolve_symbol(gfc_symbol*)>, ns=0x3697bb0) at
../../gcc/fortran/symbol.cc:4215
#15 resolve_types (ns=0x3697bb0) at ../../gcc/fortran/resolve.cc:17880
#16 0x000000000184cf94 in gfc_resolve (ns=0x3697bb0) at
../../gcc/fortran/resolve.cc:17996
#17 0x000000000184d022 in gfc_resolve (ns=<optimized out>) at
../../gcc/fortran/resolve.cc:17983
#18 0x000000000184fb48 in resolve_symbol (sym=<optimized out>) at
../../gcc/fortran/resolve.cc:16567
#19 0x0000000001869493 in do_traverse_symtree (st=<optimized out>, st_func=0x0,
sym_func=0x184d030 <resolve_symbol(gfc_symbol*)>)
    at ../../gcc/fortran/symbol.cc:4190
#20 0x000000000185aee1 in gfc_traverse_ns (sym_func=0x184d030
<resolve_symbol(gfc_symbol*)>, ns=0x3238a50) at
../../gcc/fortran/symbol.cc:4215
#21 resolve_types (ns=0x3238a50) at ../../gcc/fortran/resolve.cc:17880
#22 0x000000000184cf94 in gfc_resolve (ns=0x3238a50) at
../../gcc/fortran/resolve.cc:17996
#23 0x000000000184d022 in gfc_resolve (ns=<optimized out>) at
../../gcc/fortran/resolve.cc:17983
#24 0x000000000184fb48 in resolve_symbol (sym=<optimized out>) at
../../gcc/fortran/resolve.cc:16567
#25 0x0000000001869493 in do_traverse_symtree (st=<optimized out>, st_func=0x0,
sym_func=0x184d030 <resolve_symbol(gfc_symbol*)>)
    at ../../gcc/fortran/symbol.cc:4190
#26 0x000000000185aee1 in gfc_traverse_ns (sym_func=0x184d030
<resolve_symbol(gfc_symbol*)>, ns=0x2503410) at
../../gcc/fortran/symbol.cc:4215
#27 resolve_types (ns=0x2503410) at ../../gcc/fortran/resolve.cc:17880
#28 0x000000000185aff8 in resolve_types (ns=0x24fd1f0) at
../../gcc/fortran/resolve.cc:17892
#29 0x000000000184cf94 in gfc_resolve (ns=0x24fd1f0) at
../../gcc/fortran/resolve.cc:17996
#30 0x0000000001823689 in gfc_resolve (ns=<optimized out>) at
../../gcc/fortran/resolve.cc:17983
#31 gfc_parse_file () at ../../gcc/fortran/parse.cc:6861
#32 0x000000000187b705 in gfc_be_parse_file () at
../../gcc/fortran/f95-lang.cc:229
#33 0x00000000015b4582 in compile_file () at ../../gcc/toplev.cc:447
#34 0x0000000001592fc6 in do_compile (no_backend=false) at
../../gcc/toplev.cc:2128
#35 toplev::main (this=this@entry=0x7fffffffb82e, argc=<optimized out>,
argv=<optimized out>) at ../../gcc/toplev.cc:2282
#36 0x000000000159238f in main (argc=<optimized out>, argv=<optimized out>) at
../../gcc/main.cc:39
(gdb) up
#1  0x0000000001852c42 in resolve_procedure_expression (expr=0x2aefc80) at
../../gcc/fortran/resolve.cc:1957

The affected routine is a defined assignment in the ftlDynArray class, used in
this case to hold ordinary integers: 
https://github.com/SCM-NV/ftl/blob/master/src/ftlDynArray.F90_template

That defined assignment is impure elemental, if that matters.

(gdb) p *sym                                                                   
                                                                    $7 = {name
= 0x7fffea3fd9a0 "assignother", module = 0x7fffea3ec8d0 "ftldynarrayintmodule",
declared_at = {nextc = 0x24d0648, lb = 0x24d0600},         ts = {type =
BT_UNKNOWN, kind = 0, u = {derived = 0x0, cl = 0x0, pad = 0}, interface = 0x0,
is_c_interop = 0, is_iso_c = 0,                           f90_type =
BT_UNKNOWN, deferred = false, interop_kind = 0x0}, attr = {allocatable = 0,
dimension = 0, codimension = 0, external = 0,                intrinsic = 0,
optional = 0, pointer = 0, target = 0, value = 0, volatile_ = 0, temporary = 0,
dummy = 0, result = 0, assign = 0,                   threadprivate = 0,
not_always_present = 0, implied_index = 0, subref_array_pointer = 0,
proc_pointer = 0, asynchronous = 0, contiguous = 0,         fe_temp = 0,
automatic = 0, class_pointer = 0, save = SAVE_NONE, data = 0, is_protected = 0,
use_assoc = 1, used_in_submodule = 0,                  use_only = 0, use_rename
= 0, imported = 0, host_assoc = 0, in_namelist = 0, in_common = 0,
in_equivalence = 0, function = 0, subroutine = 1,       procedure = 0, generic
= 0, generic_copy = 0, implicit_type = 0, untyped = 0, is_bind_c = 0, extension
= 0, is_class = 0, class_ok = 0,             vtab = 0, vtype = 0, is_c_interop
= 0, is_iso_c = 0, sequence = 0, elemental = 1, pure = 0, recursive = 0,
unmaskable = 0, masked = 0,              contained = 0, mod_proc = 0, abstract
= 0, module_procedure = 0, public_used = 0, implicit_pure = 0,
array_outer_dependency = 1, noreturn = 0,      entry = 0, entry_master = 0,
mixed_entry_master = 0, always_explicit = 1, artificial = 0, referenced = 0,
is_main_program = 0,                      access = ACCESS_UNKNOWN, intent =
INTENT_UNKNOWN, flavor = FL_PROCEDURE, if_source = IFSRC_DECL, proc =
PROC_MODULE, cray_pointer = 0,              cray_pointee = 0, alloc_comp = 0,
pointer_comp = 0, proc_pointer_comp = 0, private_comp = 0, zero_comp = 0,
coarray_comp = 0, lock_comp = 0,        event_comp = 0, defined_assign_comp =
0, unlimited_polymorphic = 0, has_dtio_procs = 0, caf_token = 0,
select_type_temporary = 0,                   select_rank_temporary = 0,
associate_var = 0, pdt_kind = 0, pdt_len = 0, pdt_type = 0, pdt_template = 0,
pdt_array = 0, pdt_string = 0,
    omp_udr_artificial_var = 0, omp_declare_target = 0, omp_declare_target_link
= 0, omp_device_type = OMP_DEVICE_TYPE_UNSET,
    oacc_declare_create = 0, oacc_declare_copyin = 0, oacc_declare_deviceptr =
0, oacc_declare_device_resident = 0, oacc_declare_link = 0,
    oacc_routine_lop = OACC_ROUTINE_LOP_NONE, oacc_routine_nohost = 0, ext_attr
= 0, volatile_ns = 0x0, asynchronous_ns = 0x0}, generic = 0x0,
  component_access = ACCESS_UNKNOWN, formal = 0x381f040, formal_ns = 0x3ae65e0,
f2k_derived = 0x0, param_list = 0x0, value = 0x0, as = 0x0,
  result = 0x0, components = 0x0, cp_pointer = 0x0, entry_id = 0, hash_value =
0, common_next = 0x0, common_head = 0x0, dummy_order = 0,
  namelist = 0x0, namelist_tail = 0x0, tlink = 0x0, old_symbol = 0x0, mark = 0,
comp_mark = 0, data_mark = 0, dev_mark = 0, gen_mark = 0,
  reduc_mark = 0, gfc_new = 0, equiv_built = 0, forall_index = 0,
fn_result_spec = 0, resolve_symbol_called = 1, abr_modproc_decl = 0, error = 0,
  maybe_array = 0, pass_as_value = 0, refs = 1, ns = 0x3697bb0, backend_decl =
0x0, from_intmod = INTMOD_NONE, intmod_sym_id = 0,
  binding_label = 0x0, common_block = 0x0, assoc = 0x0, dt_next = 0x0}

expr->where is full of zeros (so is *expr, mostly):

(gdb) p *expr
$9 = {expr_type = EXPR_VARIABLE, ts = {type = BT_PROCEDURE, kind = 0, u =
{derived = 0x0, cl = 0x0, pad = 0}, interface = 0x0, is_c_interop = 0,
    is_iso_c = 0, f90_type = BT_UNKNOWN, deferred = false, interop_kind = 0x0},
rank = 0, shape = 0x0, symtree = 0x3fc73b0, ref = 0x0, where = {
    nextc = 0x0, lb = 0x0}, base_expr = 0x0, is_snan = 0, error = 0,
user_operator = 0, mold = 0, must_finalize = 0, no_bounds_check = 0,
  external_blas = 0, do_not_resolve_again = 0, do_not_warn = 0,
from_constructor = 0, representation = {length = 0, string = 0x0}, boz = {len =
0,
    rdx = 0, str = 0x0}, value = {logical = 0, iokind = M_READ, integer =
{{_mp_alloc = 0, _mp_size = 0, _mp_d = 0x0}}, real = {{_mpfr_prec = 0,
        _mpfr_sign = 0, _mpfr_exp = 0, _mpfr_d = 0x0}}, complex = {{re =
{{_mpfr_prec = 0, _mpfr_sign = 0, _mpfr_exp = 0, _mpfr_d = 0x0}}, im = {{
            _mpfr_prec = 0, _mpfr_sign = 0, _mpfr_exp = 0, _mpfr_d = 0x0}}}},
op = {op = GFC_INTRINSIC_BEGIN, uop = 0x0, op1 = 0x0, op2 = 0x0},
    function = {actual = 0x0, name = 0x0, isym = 0x0, esym = 0x0}, compcall =
{actual = 0x0, name = 0x0, base_object = 0x0, tbp = 0x0,
      ignore_pass = 0, assign = 0}, character = {length = 0, string = 0x0},
constructor = 0x0}, param_list = 0x0}

This all happens when compiling one of our modules with lots of dependencies,
so I can't easily turn it into a standalone testcase. I could possibly just
send you the source together with all the .mod dependencies, but isolating it
further will take me a while (if needed).

Another defined assignment routine triggering the same warning/assert is even
weirder, since I can't imagine how this could end up calling itself
recursively:

   impure elemental subroutine AssignOther(self, other)
      class(InputType), intent(inout) :: self
       type(InputType), intent(in)    :: other

      ! TODO: Implement actual assignment. (Default assignment will not do the
right job, so we disable it for now.)
      call StopIt('Developer error: InputType is not assignable.')
      DontWarnUnused(self)
      DontWarnUnused(other)
   end subroutine

(StopIt is an external subroutine which just does some cleanup and stops the
program. This type does have a finalizer, but it certainly shouldn't trigger
the assignment again.)

   type :: InputType
(snip)
      final              :: Finalizer
      generic  , public  :: assignment(=) => AssignOther
      procedure, private :: AssignOther
   end type

   subroutine DeletePrivate(self)
      class(InputType), intent(inout) :: self

      self%quiet        = .false.
      self%stringLeafs  = .false.
      self%stopOnError  = .true.
      self%hasError     = .true.
      call self%errorMessage%Delete()

      call self%prog%Delete()
      call self%inputDef%Destroy()
      if (associated(self%pRoot)) call self%json%Destroy(self%pRoot)

   end subroutine
   !
   subroutine Finalizer(self)
      type(InputType), intent(inout) :: self
      call self%Delete()
   end subroutine

In the end I ended up having to compile everything -frecursive because this
issue pops up in many different modules and hunting them all down is really
tedious (having to extract the gfortran commandline out from our buildsystem,
get the corresponding f951 command line, run f951 under gdb to break on
gfc_warning, check *sym).

Compiling with this patch also makes PR103931 rear its ugly head in one module,
which does not happen when the offending commit in comment #4 is reverted
instead. But that PR is weird enough as it is, likely some minor changes in the
module file layout are all that's needed to trigger it.

> I fear that you will have to make your procedures impure for the time being
> or, if you don't need the added finalization features, revert to a previous
> version of gfortran.

Well, our codebase uses F2008 features extensively, and defined assignments
plus finalization are by far the two most prominent sources of bugs in both
ifort and gfortran, so we already have layers upon layers of workarounds for
those in place. Any change to that mix is prone to break something else.

Your work on this in gfortran will likely allow us to get rid of quite some of
those workarounds, so it is greatly appreciated!

Indeed, running with gfortran 12 is certainly an option, but 13 with the revert
seems to do the job as well. We want to make our code compile with GCC 13
before that version is picked up by major distros and makes the life of our
developers miserable.

(I'm sure the whole world would be much better off if we could just include our
codebase in the regression test suites of both ifort and gfortran :-))

Reply via email to