j3-fortran / fortran_proposals

Proposals for the Fortran Standard Committee
175 stars 14 forks source link

Relax restrictions on actual argument for allocatable polymorphic dummy argument with intent(in) and intent(out) #242

Open plevold opened 2 years ago

plevold commented 2 years ago

If I'm reading the standard correctly, then section 12.5.2.5 §2 puts restrictions on actual arguments where the dummy argument is allocatable and polymorphic:

The actual argument shall be polymorphic if and only if the associated dummy argument is polymorphic, and either both the actual and dummy arguments shall be unlimited polymorphic, or the declared type of the actual argument shall be the same as the declared type of the dummy argument.

and the following Note 12.27:

The dynamic type of a polymorphic allocatable or pointer dummy argument may change as a result of execution of an ALLOCATE statement or pointer assignment in the subprogram. Because of this the corresponding actual argument needs to be polymorphic and have a declared type that is the same as the declared type of the dummy argument or an extension of that type. However, type compatibility requires that the declared type of the dummy argument be the same as, or an extension of, the type of the actual argument. Therefore, the dummy and actual arguments need to have the same declared type.

I believe this is too restrictive for arguments with intent(in) and intent(out):

When the intent is in then it is sufficient that the actual argument is type compatible with the dummy argument as no allocate or redefinition can take place (though an allocated check can). This will also make the use of a dummy allocatable consistent with a dummy optional.

When the intent is out then it is sufficient that the actual argument is type compatible with the dummy argument because the dummy argument can (should) not be used before it has been allocated inside the subprogram.

Here is a simple example that illustrates how the latter could be useful:

module some_mod
    implicit none

    type, abstract :: base_t
    end type

    type, abstract, extends(base_t) :: sub_t
        integer :: i
    end type

    type, extends(sub_t) :: my_t
        integer :: j
    end type

contains

    subroutine sub1(val)
        class(sub_t), allocatable, intent(out) :: val

        val = my_t(3, 4)
    end subroutine

end module

program main
    use some_mod

    class(base_t), allocatable :: base

    ! This works, but is very verbose
    block
        class(sub_t), allocatable :: sub

        call sub1(sub)
        call move_alloc(sub, base)
    end block

    ! However this porduces a compile error
    call sub1(base)

    select type(base)
        type is (my_t)
            print *, base%i, base%j
    end select
end program