Я строю простой случай, чтобы воспроизвести проблему ниже. Я не мог уменьшить его дальше. Описание проблемы: у меня есть 3 класса
- A, абстрактный класс в модуле A_Mod (A_Mod.f90) с отложенной процедурой
cleanup
- B, расширение абстрактного класса A в модуле B_Mod (B_Mod.f90). B реализует отложенную процедуру
cleanup
, определяет и реализует процедуры: init, finalize
и определяет следующие отложенные процедуры: setup, free_resources, reset
; - C, расширяющий B, в модуле C_Mod (C_Mod. f90) и реализацию отложенных процедур
setup, free_resources, reset
тестовой программы (test.f90), которая определяет переменную (объект типа C), вызов init
, за которым следуют finalize
процедуры на объекте.
Похоже, что процедура, вызываемая в исходном коде, не является тем, что вызывается (запускается) в исполняемом файле: вызовы подпрограммы путаются во время компиляции. Небольшое изменение (с gfortran 7.5.0), например удаление non_overridable
в процедуре init
класса B
, приводит к неопределенному l oop, вызывающему init
(например, если init и setup указывают на одну и ту же процедуру ). Такое поведение l oop может быть воспроизведено некоторыми другими небольшими изменениями в коде.
Я подозреваю, что проблема связана с deferred
и non_overridable
. Если я не ошибаюсь, это похоже на ошибку в gfortran, которая появилась после 4.8.5.
Ожидаемый результат:
Test: calling C%init
B::init, calling setup
C::setup
B::init done ...............
Test: calling C%finalize
B::finalize, calling free_resources
C::free_resources
B::finalize, calling cleanup
B::cleanup
B::finalize done ...................
Test:done.......................
Вместо этого я получаю:
Test: calling C%init
B::init, calling setup
B::cleanup
B::init done ...............
Test: calling C%finalize
B::finalize, calling free_resources
C::setup
B::finalize, calling cleanup
B::cleanup
B::finalize done ...................
Test:done.......................
Я пытался использовать следующую версию gfortran:
- ifort 19.0.5.281 => ожидаемый результат
- ifort 19.0.4.243 => ожидаемый результат
- ifort 19.0.2.187 => ожидаемый результат
- ifort 18.0.1 => ожидаемый результат
- ifort 17.0.4 => ожидаемый результат
- GNU Fortran (G CC ) 4.8.5 => ожидаемый результат
- GNU Fortran (G CC) 6.3.0 => неверный результат (немного отличается от других, см. Ниже)
- GNU Fortran (G CC) 7.5.0 => неверный результат
- GNU Fortran (G CC) 8.4.0 => неправильный результат
- GNU Fortran (G CC) 9.2.0 => неправильный результат
- GNU Fortran (G CC) 8.2.0 => неправильный результат
- GNU Fortran (G CC) 7.3.0 => неправильный результат
Результат с gfortran 6 (см. Призыв к сбросу)
Test: calling C%init
B::init, calling setup
B::cleanup
B::init done ...............
Test: calling C%finalize
B::finalize, calling free_resources
C::reset
B::finalize, calling cleanup
B::cleanup
B::finalize done ...................
Test:done.......................
Sourc Электронный код:
$ cat A_Mod.f90
!
module A_Mod
implicit none
!
private
!
type, public, abstract :: A
private
logical :: status !< status of the object
contains
!
procedure, non_overridable :: setStatus
procedure :: unsetStatus
!
procedure( cleanup ), deferred :: cleanup
!procedure, nopass :: do_nothing
end type A
!
interface cleanup
!
subroutine cleanup(this)
import A
class(A), intent(in out) :: this
end subroutine cleanup
end interface cleanup
!
contains
!
subroutine setStatus(this)
class(A), intent(in out) :: this
!
this%status = .true.
end subroutine setStatus
!
subroutine unsetStatus(this)
class(A), intent(in out) :: this
!
this%status = .false.
end subroutine unsetStatus
! !
! subroutine do_nothing()
! end subroutine do_nothing
!
end module A_Mod
cat B_Mod.f90
!
module B_Mod
!
use A_Mod
implicit none
!
private
integer, private, parameter :: version = 0
!
type, public, abstract, extends(A) :: B
integer :: action
contains
!
procedure (free_resources), deferred :: free_resources
procedure (reset), deferred :: reset
procedure (setup), deferred :: setup
!
procedure, non_overridable :: init
!
! Procedures from A
procedure, non_overridable :: finalize
procedure, non_overridable :: cleanup
!
end type B
!
interface
!
subroutine free_resources( this )
import B
class(B), intent(in out) :: this
!
end subroutine free_resources
!
subroutine reset( this )
import B
class( B ), intent(in out) :: this
end subroutine reset
!
subroutine setup( this )
import B
class(B), intent(in out) :: this
!
end subroutine setup
!
end interface
!
contains
!
subroutine init( this )
class(B), intent(in out) :: this
!
write(*,"(' B::init, calling setup')")
call this%setup()
write(*,"(' B::init done ...............')")
this%action=1
!
end subroutine init
!
subroutine finalize( this )
class(B), intent(in out) :: this
!
write(*,"(' B::finalize, calling free_resources')")
call this%free_resources( )
write(*,"(' B::finalize, calling cleanup')")
call this%cleanup()
write(*,"(' B::finalize done ...................')")
this%action=0
!
end subroutine finalize
!
subroutine cleanup( this )
class(B), intent(in out) :: this
!
!call this%do_nothing()
write(*,"(' B::cleanup')")
!call this%reset()
this%action=-1
!
end subroutine cleanup
!
end module B_Mod
$ cat C_Mod.f90
!
module C_Mod
!
use B_Mod
!
implicit none
!
private
!
type, public, extends(B) :: C
!integer :: n
contains
! From B
procedure :: free_resources
procedure :: reset
procedure :: setup
!
end type C
!
contains
!
subroutine setup( this )
class(C), intent(in out) :: this
!
!call this%do_nothing()
write(*,"(' C::setup')")
!
end subroutine setup
!
subroutine free_resources( this )
class(C), intent(in out) :: this
!
!call this%do_nothing()
write(*,"(' C::free_resources')")
!
end subroutine free_resources
!
subroutine reset(this)
class(C), intent(in out) :: this
!
!call this%do_nothing()
write(*,"(' C::reset')")
!
end subroutine reset
!
end module C_Mod
$ cat test.f90
!> @file test.f90
!! to test the basic functionalities of the framework
!<
!> @brief test program
!!
!<
program test
use C_Mod
implicit none
!
!
call test_grid1d()
!
contains
!
subroutine test_grid1d()
type(C) :: c1
!
write(*,"('Test: calling C%init')")
call c1%init()
write(*,"('Test: calling C%finalize')")
call c1%finalize()
write(*,"('Test:done.......................')")
!
end subroutine test_grid1d
!
end program test
Скомпилировано и работает как
COMPILE=gfortran -g
LINK=gfortran
${COMPILE} A_Mod.f90 -o A_Mod.o
${COMPILE} B_Mod.f90 -o B_Mod.o
${COMPILE} C_Mod.f90 -o C_Mod.o
${COMPILE} test.f90 -o test.o
${LINK} -o test A_Mod.o B_Mod.o C_Mod.o test.o
./test