Search code examples
pointersfortranpolymorphismfortran2003allocatable-array

Unlimited polymorphic dummy argument with allocatable or pointer attributes?


In the project I'm working on, I find myself frequently needing to resize arrays of objects as new objects are created and old ones destroyed. This happens with numerous different derived types throughout the code, most of which have no relationship to each other. It is tedious to write code to resize these arrays for unique derived type, so I thought I'd try writing a couple helper subroutines using unlimited polymorphic dummy arguments so that any derived type array could use those subroutines.

What I've found is that my unlimited polymorphic routine can be compiled and called with CLASS(*),INTENT(INOUT) :: val. This dummy argument will accept an integer, an allocatable integer, or a pointer to an integer. However, as soon as I try add the ALLOCATABLE or POINTER attributes, the subroutine compiles correctly but I cannot call it without obtaining a compiler error. Since my goal is to be able to resize arrays of derived types, these attributes are necessary for the routine to be able to deallocate/allocate/associate the values.

Here's some test code that doesn't even attempt to actually do anything but fails to compile. This version uses scalar concrete types, but the same errors arise in the original code using arrays of concrete types or arrays of derived types

MODULE Resize_mod
PUBLIC
CONTAINS
  SUBROUTINE resize(val)
    CLASS(*),INTENT(INOUT) :: val
    WRITE(*,*) 'resize'
  ENDSUBROUTINE resize
  SUBROUTINE resize_alloc(val)
    CLASS(*),ALLOCATABLE,INTENT(INOUT) :: val
    WRITE(*,*) 'resize_alloc'
  ENDSUBROUTINE resize_alloc
  SUBROUTINE resize_ptr(val)
    CLASS(*),POINTER,INTENT(INOUT) :: val
    WRITE(*,*) 'resize_ptr'
  ENDSUBROUTINE resize_ptr
ENDMODULE Resize_mod

PROGRAM testResize
  USE Resize_mod
  INTEGER,TARGET :: array0d
  INTEGER,ALLOCATABLE :: alloc0d
  INTEGER,POINTER :: ptr0d

  array0d=1
  CALL resize(array0d)

  ALLOCATE(alloc0d)
  alloc0d=1
  CALL resize(alloc0d)
  !Following line gives: "Error: Actual argument to ‘val’ at (1) must be polymorphic"
  CALL resize_alloc(alloc0d)

  ALLOCATE(ptr0d)
  ptr0d=1
  CALL resize(ptr0d)
  !Following line gives: "Error: Actual argument to ‘val’ at (1) must be polymorphic"
  CALL resize_ptr(ptr0d)

ENDPROGRAM testResize

As shown, the code produces the following errors:

testResize.f90:31:20:

   CALL resize_alloc(alloc0d)
                    1
Error: Actual argument to ‘val’ at (1) must be polymorphic
testResize.f90:37:18:

   CALL resize_ptr(ptr0d)
                  1
Error: Actual argument to ‘val’ at (1) must be polymorphic

If I comment out the 2 lines named in the errors, I get the following correct output:

 resize
 resize
 resize

I have written Fortran code extensively but have never attempted to use unlimited polymorphism before. Please let me know if what I'm trying to do is possible, and if so, what I am doing wrong.

I am using the gfortran compiler 5.4.0, which should fully support unlimited polymorphism as far as I can tell.


Solution

  • The error message is just a restatement of a restriction in the rules of the language - see 15.5.2.5p2 in the Fortran 2018 standard. The restriction is there to stop the called procedure from reallocating an allocatable dummy argument to a different type or kind from the actual argument (or, for pointer dummy arguments, associating the the dummy to a different type or kind). This is not specific to unlimited polymorphic arguments - it applies to any allocatable or pointer polymorphic dummy argument, in which case the restriction in the language prevents the procedure from allocating the dummy argument to a different branch of the type inheritance tree.

    Unlimited polymorphic objects have a role to play in runtime type agnostic storage, but they are not suitable for generic programming in the general case.

    The language provides some syntax support for common array operations in a generic fashion, however these may not be implemented efficiently by current compilers. For example, an element can be appended to an allocatable array with the syntax array = [ array, element ].

    Otherwise you need to provide type specific procedures for your array operations. For the same operation, the token sequence within the body of each procedure can often be the same regardless of the argument type, in which case INCLUDE can be used to reduce the amount of repeated source code.

    Improved support for generic programming is an aspect that is under consideration for the next revision of the language.