Поведение Undeterministti c в Фортране Объект ориентирован с отложенными и не переопределяемыми процедурами и компилятором g cc - PullRequest
2 голосов
/ 17 апреля 2020

Я строю простой случай, чтобы воспроизвести проблему ниже. Я не мог уменьшить его дальше. Описание проблемы: у меня есть 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

1 Ответ

2 голосов
/ 18 апреля 2020

Это похоже на ошибку в текущем gfortran (9.3). Это требует совершенно определенных c обстоятельств, включая наличие источника для модулей в отдельных файлах.

Если вы хотите, чтобы ошибка была устранена, лучше всего сообщать по обычным g cc каналам сообщения об ошибках (https://gcc.gnu.org/bugzilla/).

...