How to correctly use type extension and polymorphism?

I have now set the gfortran compiler to the following version:

GNU Fortran (Ubuntu 11.1.0-1ubuntu1~18.04.1) 11.1.0

My module looks now like this:

module coords
    use iso_fortran_env
    implicit none
  
    private
    public :: point


    type ::point!(k)
      !integer, kind :: k = kind(0.)
      !real(kind=k) :: x,y,z
      real :: x,y,z
     contains
       procedure,private,pass(this) :: pointplus, pointminus, pointseparation
       procedure,private,pass(this) :: vecotr_dot, vecotr_cros
       procedure,private,pass(this) :: point_multiply_vct_scalar_32, point_multiply_vct_scalar_64
       procedure,private,pass(this) :: point_multiply_scalar_vct_32, point_multiply_scalar_vct_64
       procedure,private,pass(this) :: absvec_real32, absvec_real64
       procedure,private,pass(this) ::  pointdivide_32, pointdivide_64 

       generic,public               :: operator(+)          => pointplus
       generic,public               :: operator(-)          => pointminus
       generic,public               :: operator(*)          => point_multiply_vct_scalar_32, point_multiply_vct_scalar_64
       generic,public               :: operator(*)          => point_multiply_scalar_vct_32, point_multiply_scalar_vct_64
       generic,public               :: operator(/)          => pointdivide_32, pointdivide_64 
       generic,public               :: operator(.distance.) => pointseparation
       generic,public               :: assignment(=)        => absvec_real32, absvec_real64
       generic,public               :: operator(.dot.)      => vecotr_dot
       generic,public               :: operator(.cros.)     => vecotr_cros
    end type point

    type, extends(point), public            :: pointT
      real, PUBLIC            :: T
      contains
      procedure,PUBLIC        :: Tplus,Tminus
    end type pointT

    type, extends(point)            :: point_64
      real(real64),PUBLIC     :: x_64, y_64, z_64     
    end type point_64

    interface absvec
      module procedure absvec_real32, absvec_real64
    end interface absvec
    
  contains
    type(pointT) function Tplus(this)   ! for +
      class(pointT),intent(in)      :: this
      Tplus%T = this%T + Tplus%x
    end function Tplus

    type(pointT) function Tminus(this)   ! for +
      class(pointT),intent(in)      :: this
      Tminus%T = Tminus%T - this%T
    end function Tminus
  
    type(point) function pointplus(this,b)   ! for +
      class(point),intent(in)      :: this, b
      pointplus%x = this%x + b%x
      pointplus%y = this%y + b%y
      pointplus%z = this%z + b%z
    end function pointplus

    type(point) function pointdivide_32(this,scalar)   ! for +
      class(point),intent(in)      :: this
      real(real32), INTENT(IN)     :: scalar
      pointdivide_32%x = this%x / scalar
      pointdivide_32%y = this%y / scalar
      pointdivide_32%z = this%z / scalar
    end function pointdivide_32

    type(point) function pointdivide_64(this,scalar)   ! for +
      class(point),intent(in)      :: this
      real(real64), INTENT(IN)     :: scalar
      pointdivide_64%x = this%x / scalar
      pointdivide_64%y = this%y / scalar
      pointdivide_64%z = this%z / scalar
    end function pointdivide_64

    type(point) function point_multiply_vct_scalar_32(this,scalar)   ! for +
      class(point),intent(in)      :: this
      real(real32), INTENT(IN)     :: scalar
      point_multiply_vct_scalar_32%x = this%x * scalar
      point_multiply_vct_scalar_32%y = this%y * scalar
      point_multiply_vct_scalar_32%z = this%z * scalar
    end function point_multiply_vct_scalar_32

    type(point) function point_multiply_scalar_vct_32(scalar,this)   ! for +
      class(point),intent(in)      :: this
      real(real32), INTENT(IN)     :: scalar
      point_multiply_scalar_vct_32%x = this%x * scalar
      point_multiply_scalar_vct_32%y = this%y * scalar
      point_multiply_scalar_vct_32%z = this%z * scalar
    end function point_multiply_scalar_vct_32

    type(point) function point_multiply_vct_scalar_64(this,scalar)   ! for +
      class(point),intent(in)      :: this
      real(real64), INTENT(IN)     :: scalar

      select type (this)
          class is (point_64)
              point_multiply_vct_scalar_64%x = this%x_64 * scalar
              point_multiply_vct_scalar_64%y = this%y_64 * scalar
              point_multiply_vct_scalar_64%z = this%z_64 * scalar
      end select

    end function point_multiply_vct_scalar_64

    type(point) function point_multiply_scalar_vct_64(scalar,this)   ! for +
      class(point),intent(in)      :: this
      real(real64), INTENT(IN)     :: scalar

      select type (this)
          class is (point_64)
            point_multiply_scalar_vct_64%x = this%x_64 * scalar
            point_multiply_scalar_vct_64%y = this%y_64 * scalar
            point_multiply_scalar_vct_64%z = this%z_64 * scalar
      end select
    end function point_multiply_scalar_vct_64

    type(point) function pointminus(this,b)  !  for -
      class(point),intent(in):: this, b

      pointminus%x = this%x - b%x
      pointminus%y = this%y - b%y
      pointminus%z = this%z - b%z
    end function pointminus
  
    real function pointseparation(this,b) ! for .distance.
      class(point),intent(in)   :: this,b

      pointseparation = sqrt(  &
           (this%x-b%x)**2+(this%y-b%y)**2+(this%z-b%z)**2)
    end function pointseparation
  
    subroutine absvec_real64(a,this)  ! for = (distance
      real(real64),intent(out)  :: a     !  from origin)
      class(point),intent(in)   :: this
      a = sqrt(this%x**2+this%y**2+this%z**2)
    end subroutine absvec_real64

    subroutine absvec_real32(a,this)  ! for = (distance
      real(real32),intent(out)  :: a     !  from origin)
      class(point),intent(in)   :: this
      a = sqrt(this%x**2 + this%y**2 + this%z**2)
    end subroutine absvec_real32

    real function vecotr_dot(this,b) ! for .distance.
      class(point),intent(in)   :: this,b
      vecotr_dot = (this%x*b%x)+(this%y*b%y)+(this%z*b%z)
    end function vecotr_dot

    type(point) function vecotr_cros(this,b)  !  for -
      class(point),intent(in):: this, b

      vecotr_cros%x = this%y * b%z - this%z * b%y
      vecotr_cros%y = this%z * b%x - this%x * b%z
      vecotr_cros%z = this%x * b%y - this%y * b%x
    end function vecotr_cros
    
  end module coords

and my file to run like:

program test
    use coords
    use iso_fortran_env

    implicit none
    real(real32)    ::  r4
    real(real64)    ::  r8
    type(point)     ::  p1, p2, p3
    type(pointT)    ::  pT1, pT2

    

    !test
    r4  = p1
    r8  = p1
    r4  = p1.dot.p2
    p3  = p1.cros.p2
    p3  = p1 * r4
    p3  = p1 * r8
    p3  = r4 * p1
    p3  = r8 * p1
    p3  = p1 / r4
    p3  = p1 / r8
    r4  = pT1%Tplus(pT2)
    r4  = pT1%Tminus(pT2)

end program test

My understanding is that the functions are now the required operations. However, I have a problem with the type extension pointT. I get the following error:

exercise_10_1.f90:24:10:

   24 |     r4  = pT1%Tplus(pT2)
      |          1
Error: More actual than formal arguments in procedure call at (1)
exercise_10_1.f90:25:10:

   25 |     r4  = pT1%Tminus(pT2)
      |          1
Error: More actual than formal arguments in procedure call at (1)
./run: line 4: ./a.out: No such file or directory

How can I interpret this? In my search on the web I only found that I can use Makefiles. But why do I need to do that? I am currently compiling my code as:

gfortran -fbacktrace -g -fcheck=all -fbounds-check -finit-real=snan -ffpe-trap=invalid,zero,overflow -O0 -O -Wall -g  module_cords.f90 exercise_10_1.f90 

Do I really need to use makefiles to solve this error? Or is there something else I did wrong?

A make file just automates commands that could be issued from a Bash or Windows CMD prompt, so it can be convenient, but it is not necessary. Either the code or the compiler options need to be changed.

2 Likes

What made you think the error has to do with Makefiles?

If you look up your own definition of your Tplus and Tminus procedures which are bound to the derived type pointT, you will notice these procedures only take one dummy argument which is the passed-object itself. On the caller side, one actual argument is the thingy to the left of % token. But then you also pass an extra argument, pT2. That is a mismatch in the calling sequence.

You can evaluate what needs to be done here and retry.

1 Like

Ok, many thanks for the clarification. Some posts I found suggested this approach which left me confused :

Thanks a lot for the help. I stumbled across some posts suggesting to solve a similar error using makefiles. However, your explanation makes much more sense and I have since been able to fix the error. Thanks everyone for your help!:smiley:

1 Like