fortranfortran2008

Overload deferred procedure with non-polymorphic procedure in Fortran 2008


Is it possible to overload a deferred procedure with a non-polymorphic procedure?

I'd like to create an abstract class (Parent) with a procedure (foo) which must be overloaded by every class which extends Parent. I run into problems when I want to extend again, such that a class (Grandchild) extends a class (Child) which extends Parent.

Since Child is not abstract, its foo (foo_Child) must be polymorphic. But then Grandchild inherits foo_Child, rather than being forced to define foo_Grandchild. Further, since I don't want foo_Child to be polymorphic I want to be able to use Child-specific non-polymorphic functions within foo_Child.

module test_module

  type, abstract :: Parent
  contains
    procedure(foo_Parent), deferred :: foo
  end type

  abstract interface
    subroutine foo_Parent(this,input)
      import Parent
      class(Parent), intent(out) :: this
      character(*),  intent(in)  :: input
    end subroutine
  end interface

  type, extends(Parent) :: Child
  contains
    procedure :: foo => foo_Child
  end type

  type, extends(Child) :: Grandchild
    ! Is not required to define foo=>foo_Grandchild.
    ! This is not the behaviour I want.
  end type

  interface Child
    module procedure new_Child
  end interface
contains

function new_Child(input) result(this)
  character(*), intent(in) :: input
  type(Child)              :: this
end function

subroutine foo_Child(this,input)
  type(Child),  intent(out) :: this ! Fails: 'this' is not polymorphic.
  character(*), intent(in)  :: input

  this = Child(input) ! Fails if type(Child) is replaced by class(Child).
end subroutine
end module

program test
  use test_module
end program

To summarise:

Is there any way of making foo_Child be non-polymorphic but also overload foo_Parent? Or is there a way of calling non-polymorphic functions (at least Child=Child assignment with non-polymorphic rhs) in a polymorphic procedure? If not, is there a workaround?

(I don't want to define class(Child)=type(Child), but will if it's the only option).


Solution

  • The dummy argument that corresponds to the passed object of a procedure binding must always be polymorphic.

    The rules of the language for intrinsic assignment do not permit assignment to a non-allocatable polymorphic object. This is because typically such an assignment would be an error akin to slicing - you would undefine the bits of the object that are declared in the dynamic type of the rhs but not in the declared type of the lhs.

    A polymorphic object can be downcast to a non-polymorphic object using SELECT TYPE and a type guard that matches the dynamic type of the object. You can also merrily slice to your hearts content via argument association - a polymorphic actual argument can be associated with a non-polymorphic dummy of the same declared type.

    Extensions can be forced to implement a binding by making the parent type abstract and making (or leaving) the binding deferred (as you have already done). In your situation that perhaps requires an additional type in your hierarchy.

    Parent (abstract) --> Child (abstract) +-> RealChild (concrete)
                                           |-> GrandChild (concrete)
    

    Child in the above might simply leave the foo binding deferred, or it may provide a procedure for that binding and then introduce a new deferred binding that RealChild and GrandChild need to implement.