https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124161
Bug ID: 124161
Summary: ICE in fold_convert_loc with submodule polymorphic TBP
dispatch
Product: gcc
Version: 15.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: fortran
Assignee: unassigned at gcc dot gnu.org
Reporter: zbeekman at gmail dot com
Target Milestone: ---
Created attachment 63724
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=63724&action=edit
minimal reproducer fortran source file
ICE in fold_convert_loc with submodule polymorphic TBP dispatch (false positive
-Wmaybe-recursive)
SUMMARY
=======
GFortran 15 crashes with an internal compiler error when compiling a submodule
that implements a type-bound procedure (TBP) whose body calls a same-named TBP
via polymorphic dispatch on an allocatable class component. I think that the
code is valid Fortran and compiles cleanly with GFortran 14. A spurious
-Wmaybe-recursive warning is emitted immediately before the crash; the warning
is a false positive because the polymorphic call dispatches through an abstract
type to an unrelated concrete type -- it cannot recurse back to the outer
procedure.
COMPILER VERSIONS
=================
Affected: GNU Fortran (GCC) 15.2.0 -- ICE
Unaffected: GNU Fortran (GCC) 14.3.0 -- clean compile, no warnings
PLATFORMS TESTED
================
16-inch, 2019 MacBook Pro
2.4 GHz 8-Core Intel Core i9
32 GB 2667 MHz DDR4
HOW TO REPRODUCE
================
Attach ice_minimal.f90 (single file, zero external dependencies) and run:
gfortran -c ice_minimal.f90
NOTE: if -Werror is in effect the compiler exits on the spurious warning before
reaching the crash site; omit -Werror to observe the ICE.
ACTUAL RESULT
=============
ice_minimal.f90:67:17:
67 | submodule(outer_m) outer_impl
| 1
Warning: Non-RECURSIVE procedure 'summary' at (1) is possibly calling itself
recursively. Declare it RECURSIVE or use '-frecursive'
ice_minimal.f90:67:17:
67 | submodule(outer_m) outer_impl
| 1
internal compiler error: in fold_convert_loc, at fold-const.cc:2786
Please submit a full bug report, with preprocessed source (by using
-freport-bug).
NOTE: `-freport-bug` doesn't change anything or produce additional output, I
think the ICE kills the process early.
EXPECTED RESULT
===============
Clean compilation with no warnings (as with GFortran 14). The procedure
'summary' in the submodule is not recursive: the call self%component%summary()
dispatches through class(inner_t) to concrete_t%summary, which is a completely
unrelated type from outer_t%summary.
TRIGGER CONDITIONS
==================
I think all five conditions must be present to reproduce the ICE:
1. An abstract type (inner_t) with a deferred TBP whose return type is a
derived type containing an allocatable component.
2. A concrete extension (concrete_t) implementing that deferred TBP.
3. An outer type (outer_t) that holds a class(inner_t) allocatable component
AND also has a TBP with the SAME NAME as the deferred TBP, declared as a
module function with a result() clause (i.e. the result variable has a
different name from the function).
4. The outer module USEs the concrete type (concrete_t) -- this causes the
concrete type's TBP to be encoded in the parent .smod file and visible
when the submodule is compiled.
5. A submodule implements the outer TBP by calling
self%component%summary() via polymorphic dispatch.
Removing any one of these conditions prevents the ICE:
- result() clause removed from outer function: no warning, no ICE
- USE of concrete_t removed from outer module: no warning, no ICE
- Return type changed to INTEGER (no allocatable component): no warning, no
ICE
- Implementation moved from submodule to module contains: no warning, no ICE
FLAG BEHAVIOR
=============
(no extra flags) -Wmaybe-recursive warning fires, then ICE
-w warning text suppressed, ICE still fires
-Wno-maybe-recursive warning NOT suppressed, ICE still fires
-frecursive warning suppressed, ICE still fires
-Werror compiler exits on warning-as-error; ICE masked
REPRODUCER SOURCE (ice_minimal.f90)
====================================
module inner_m
implicit none
type :: dt_t
integer, allocatable :: val
end type
type, abstract :: inner_t
contains
procedure(summary_i), deferred :: summary
end type
abstract interface
function summary_i(self)
import :: inner_t, dt_t
class(inner_t), intent(in) :: self
type(dt_t) :: summary_i
end function
end interface
end module inner_m
module concrete_m
use inner_m, only: inner_t, dt_t
implicit none
type, extends(inner_t) :: concrete_t
contains
procedure :: summary
end type
contains
function summary(self)
class(concrete_t), intent(in) :: self
type(dt_t) :: summary
end function
end module concrete_m
module outer_m
use inner_m, only: inner_t, dt_t
use concrete_m, only: concrete_t
implicit none
type :: outer_t
class(inner_t), allocatable :: component
contains
procedure :: summary
end type
interface
module function summary(self) result(res)
class(outer_t), intent(in) :: self
type(dt_t) :: res
end function
end interface
end module outer_m
submodule(outer_m) outer_impl
contains
module procedure summary
res = self%component%summary()
end procedure
end submodule outer_impl