Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28454 Discussions

error #6197: An assignment of different structure types is invalid.

mic_esp96
New Contributor I
782 Views

Hi everyone,

 

I'm trying to deploy some polymorphism in Modern Fortran, but I'm having some troubles removing the error in the title: "error #6197: An assignment of different structure types is invalid."

 

Here is a rerpoducible example:

 

module mz

   implicit none

   type, public, abstract :: mz_t
      character(len = 64) :: name
   !contains
      ! procedure(dumpIntf), pass, deferred :: dump
   end type

   type, public, abstract :: msav_t
      character(len = 64) :: name = ''
   end type

   interface
      module function DumpZone(z) result(sav)
         ! import mz_t, msav_t
         class(mz_t), intent(in)    :: z

         ! gets allocated inside zone specific dump()
         class(msav_t), allocatable :: sav
      end function
   end interface

end module mz


!=========================================================
module mrz

   use mz
   implicit none

   type, public, extends(mz_t) :: mrz_t
      integer :: i
      integer :: j
   contains
      procedure, pass :: dump => dumpRZ
   end type


   type, public, extends(msav_t) :: mrsav_t
      integer :: i
      integer :: j
   end type


   interface
      module function dumpRZ(r) result(rs)
         ! import mrz_t, mrsav_t
         class(mrz_t), intent(in)   :: r
         type(mrsav_t), allocatable :: rs
      end function
   end interface


end module


!=========================================================
submodule(mrz) mrzimpl

implicit none

contains

   module function dumpRZ(r) result(rs)
      class(mrz_t), intent(in)   :: r
      type(mrsav_t), allocatable :: rs

      select type (r)
         class is (mrz_t)
            allocate(rs)
            rs%i = r%i
            rs%j = r%j
      end select
   end function

end submodule


!=========================================================
submodule(mz) mzimpl

implicit none

contains

   module function DumpZone(z) result(sav)
      use mrz, only: mrz_t
      class(mz_t), intent(in)    :: z

      ! gets allocated inside zone specific sump()
      class(msav_t), allocatable :: sav


      select type (z)
         class is (mrz_t)
            sav = z.dump()
         class default
            stop ' Error: unknown zone class..'
      end select

      sav%name = z%name
   end function

end submodule


!=========================================================
!=========================================================


program test

   use mrz
   implicit none

   type(mrz_t)                :: z
   type(mrsav_t), allocatable :: s

   z%name = 'Ciao'
   z%i = 5
   z%j = 10

   ! GENERATES: error #6197: An assignment of different structure types is invalid.
   s = DumpZone(z)

   print *, s%name
   print *, s%i
   print *, s%j

end program

 

I have tested both "ifort" and "gfortran (trunk)", and both complain about the assignment in the program unit

s = DumpZone(z)

 

Clearly, the "DumpZone()" interface expects the result to be "class(BASE), allocatable", while at call, the actual variable is of "type(DERIV1), allocatable".

 

So, the clear solution, was to change the program into:

 

program test

   use mrz
   implicit none

   type(mrz_t)                 :: z
   class(msav_t), allocatable  :: s

   z%name = 'Ciao'
   z%i = 5
   z%j = 10

   s = DumpZone(z)

   select type (s)
      type is (mrsav_t)
         print *, s%name
         print *, s%i
         print *, s%j
   end select

end program

 

using the second `select type` construct before actually referencing the variable "s".

But this looks a bit redundant to me. Digging into the function DumpZone(), to me, the compiler should be able to determine at compile time the actual type returned, since this type is determined by the call to the type-bound procedure "dump()", of the input variable, which type is resolved at compile time using a `select type` statement. But I might be wrong about this statement, and apparently I am.

 

Indeed, this would also require the compiler to implicitly "convert" (cast, or whatever), class(BASE) to type(DERIV1) at function exit, i.e. kind of specialising the function for that particular input (close some sort of templated function). Which I don't have the knowledge to say if it is possible or not.

 

But, yes, I then wonder why it does not actually work as of the first example. I would like to know how the compiler really looks at it. And if this is the real only way in order for it to work.

 

Thanks to everyone.

-Michele

 

Labels (2)
1 Solution
FortranFan
Honored Contributor II
711 Views
Re: "You can include your own defined assignment to override this if that is what you seek.". In this case, I don't think it would solve the problem, since I would not be able to write such an assignment from class(BASE) to type(DERIV1)..

What problem is it you think it is that it won't solve?  Sure, there are complications that are arise with the ALLOCATABLE attribute on objects on the consuming side but that's a separate issue with the Fortran standard.  Re: "an assignment from class(BASE) to type(DERIV1)," it is possible to design code as follows, but again as to whether that is desirable in your actual code will require thought and analysis:

module a_m
   type, abstract :: a_t
   end type
end module
module e1_m
   use a_m, only : a_t
   type, extends(a_t) :: e1_t
   end type
contains
   function construct_e1() result(r)
      class(a_t), allocatable :: r
      allocate( e1_t :: r )
   end function 
end module
module e2_m
   use a_m, only : a_t
   type, extends(a_t) :: e2_t
   end type
   generic :: assignment(=) => assign_a_to_e
contains
   function construct_e2() result(r)
      class(a_t), allocatable :: r
      allocate( e2_t :: r )
   end function 
   subroutine assign_a_to_e( lhs, rhs )
      type(e2_t), intent(inout) :: lhs
      class(a_t), intent(in)    :: rhs
      select type ( rhs )
         type is ( e2_t )
            lhs = rhs
         class default
            error stop "unsupported RHS in assignment." 
      end select
   end subroutine 
end module
   use e1_m
   use e2_m
   type(e1_t) :: e1
   type(e2_t) :: e2
   !e1 = construct_e1()  !<-- A: This is the attempt in the original post; disallowed per standard
   e2 = construct_e2()   !<-- B: via defined assignment; permitted per standard notwithstanding
                         !    separate issues and complications with ALLOCATABLE attribute
end  
C:\temp>ifx /c /standard-semantics /warn:all /Qdiag-disable:7712 /stand p.f90
Intel(R) Fortran Compiler for applications running on Intel(R) 64, Version 2022.2.0 Build 20220730
Copyright (C) 1985-2022 Intel Corporation. All rights reserved.

C:\temp>

So A: is effectively what is attempted in the original post which as you discovered is disallowed.

But a defined assignment makes B: possible as shown in the above example.

View solution in original post

0 Kudos
3 Replies
FortranFan
Honored Contributor II
734 Views

@mic_esp96 ,

Per Fortran standard, an intrinsic assignment where the expression on RHS is based on a certain declared type but where the LHS is an extended type is not allowed.  You can include your own defined assignment to override this if that is what you seek.

Re: "if this is the real only way in order for it to work," please note it all comes down to your first statement, "I'm trying to deploy some polymorphism in Modern Fortran" and what you're trying to achieve with it.  You will note "deploy some polymorphism" really falls into the category of object-oriented (OO) paradigm which generally requires in-depth OO analysis (OOA) followed by well thought-out and thorough OO design phase (OOD) before getting into any actual OO programming (OOP).  There is a strong parallel with architecture of physical structures and the OO paradigm.  Depending on the base needs and requirements, one may uncover many ways to achieve the same but the approach to be followed will depend on the larger vision and mission of the endeavor(s), some approaches may be extremely elegant and inspiring and an artistic marvel to behold and also extensible/welcoming whereas others can be a complete disaster in more ways than one.  Same also applies with your "classes" and especially the DumpZone and other methods operating on the data encapsulated by those classes!

You and your team (which may be you yourself on different days looking at the same problem but with a different perspective!) will need to give it a good deal of thought re: "if this is the real only way in order for it to work."  The standard and the compilers are merely tools that might enforce some basic rules as to what is permissible or not within the framework of the language.

mic_esp96
New Contributor I
715 Views

@FortranFan ,

thanks for your answer.. Always a pleasure to read them!

 

Re: "Per Fortran standard, an intrinsic assignment where the expression on RHS is based on a certain declared type but where the LHS is an extended type is not allowed.". I get it, and also thought about it, reason why I also said that the first thing to do was to change the declaration of the actual variable to match the one of the DumpZone() interface. 

 

Still, I wonder if my reasoning about what I was expecting the compiler to do (which it was not the case obviously), is complete garbage or it does make a bit of sense..

 

Re: "You can include your own defined assignment to override this if that is what you seek.". In this case, I don't think it would solve the problem, since I would not be able to write such an assignment from class(BASE) to type(DERIV1)..

 

Regarding the rest of Your answer. I am NOT an expert of OO, OOP, OOD, etc.. So, it is highly likely my design is bad in structure.. being aware of this, I would like to better understand what you meant by "Same also applies with your "classes" and especially the DumpZone() and other methods operating on the data encapsulated by those classes!". Would you make some architectural changes ? If so, how ?

 

I mean, I ask You because if I am going the wrong way in trying to learn and apply OO, these are some of the occasions where one can get back on track.

 

Finally, You correctly say: " "if this is the real only way in order for it to work." ". Actually, NO. Adn you made me think I have maybe another, cleaner way, to do it.

 

So, in any case, thanks for your guidance.

-Michele

0 Kudos
FortranFan
Honored Contributor II
712 Views
Re: "You can include your own defined assignment to override this if that is what you seek.". In this case, I don't think it would solve the problem, since I would not be able to write such an assignment from class(BASE) to type(DERIV1)..

What problem is it you think it is that it won't solve?  Sure, there are complications that are arise with the ALLOCATABLE attribute on objects on the consuming side but that's a separate issue with the Fortran standard.  Re: "an assignment from class(BASE) to type(DERIV1)," it is possible to design code as follows, but again as to whether that is desirable in your actual code will require thought and analysis:

module a_m
   type, abstract :: a_t
   end type
end module
module e1_m
   use a_m, only : a_t
   type, extends(a_t) :: e1_t
   end type
contains
   function construct_e1() result(r)
      class(a_t), allocatable :: r
      allocate( e1_t :: r )
   end function 
end module
module e2_m
   use a_m, only : a_t
   type, extends(a_t) :: e2_t
   end type
   generic :: assignment(=) => assign_a_to_e
contains
   function construct_e2() result(r)
      class(a_t), allocatable :: r
      allocate( e2_t :: r )
   end function 
   subroutine assign_a_to_e( lhs, rhs )
      type(e2_t), intent(inout) :: lhs
      class(a_t), intent(in)    :: rhs
      select type ( rhs )
         type is ( e2_t )
            lhs = rhs
         class default
            error stop "unsupported RHS in assignment." 
      end select
   end subroutine 
end module
   use e1_m
   use e2_m
   type(e1_t) :: e1
   type(e2_t) :: e2
   !e1 = construct_e1()  !<-- A: This is the attempt in the original post; disallowed per standard
   e2 = construct_e2()   !<-- B: via defined assignment; permitted per standard notwithstanding
                         !    separate issues and complications with ALLOCATABLE attribute
end  
C:\temp>ifx /c /standard-semantics /warn:all /Qdiag-disable:7712 /stand p.f90
Intel(R) Fortran Compiler for applications running on Intel(R) 64, Version 2022.2.0 Build 20220730
Copyright (C) 1985-2022 Intel Corporation. All rights reserved.

C:\temp>

So A: is effectively what is attempted in the original post which as you discovered is disallowed.

But a defined assignment makes B: possible as shown in the above example.

0 Kudos
Reply