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

Assigment on polymorphic variables

Javier_Gonzalez-plat
1,753 Views
Hello I'm doing a comparative between fortran compiler and gfortran 8.1. Both compilers have very different behaviour for assignment polymorphic variables and I don't know who have reason or not, if both are applying standard fortran 2008. I'm sending an example where gfortran works but intel give me errors. If I comment the zone of gfortran (into swap_elements_list subroutine) and uncomment the lines for Intel, then ifort works but gfortran give me errors. Someone have an idea about this? Program Check implicit none !> Type definitions Type :: Refl_Type integer,dimension(:), allocatable :: H integer :: Mult =0 End Type Refl_Type Type :: RefList_Type integer :: Nref class(refl_Type), dimension(:), allocatable :: Reflections end Type RefList_Type Type(RefList_Type) :: List Type(Refl_Type), dimension(3) :: Refl_Ini !> Variables integer :: i !> Init Refl_Ini(1)%H=[1, 0, 0]; Refl_Ini(1)%Mult=1 Refl_Ini(2)%H=[0, 2, 0]; Refl_Ini(2)%Mult=2 Refl_Ini(3)%H=[0, 0, 3]; Refl_Ini(3)%Mult=3 List%Nref=3 List%Reflections=Refl_Ini !> Print Step:1 do i=1, List%Nref print '(i3,2x,3i4,2x,i3)', i,List%Reflections(i)%H, List%Reflections(i)%Mult end do print*,' ' print*,' ' !> Swap call Swap_Elements_List(List, 1, 3) !> Print Step:2 do i=1, List%Nref print '(i3,2x,3i4,2x,i3)', i,List%Reflections(i)%H, List%Reflections(i)%Mult end do Contains Subroutine Swap_Elements_List(List, i, j) !---- Argument ----! type (RefList_Type), intent(in out) :: List integer, intent(in) :: i,j !---- Local Variables ----! class(Refl_Type), allocatable :: tmp !> IFort !tmp=List%reflections(i) !List%reflections(i)=List%reflections(j) !List%reflections(j)=tmp !> Gfortran associate(t1 => list%reflections(i), t2 => list%reflections(j), tt => tmp) tt=t1 t1=t2 t2=tt end associate End Subroutine Swap_Elements_List End Program Check
0 Kudos
18 Replies
Steve_Lionel
Honored Contributor III
1,753 Views

If gfortran allows this, it is a bug. As an associate name, tt does not have the ALLOCATABLE attribute. The standard says (F2018 11.1.3.3) "The associating entity does not have the ALLOCATABLE or POINTER attributes; it has the TARGET attribute if and only if the selector is a variable and has either the TARGET or POINTER attribute." And 11.1.3.2 says "If and only if the selector is polymorphic, the associating entity is polymorphic."

For intrinsic assignment, 10.2.1.2 says "if the variable is polymorphic it shall be allocatable and not a coarray,"

Therefore, tt is polymorphic, is not allocatable, and intrinsic assignment is undefined.

0 Kudos
Javier_Gonzalez-plat
1,753 Views

Thanks for your comments.

Fortran2008 or Fortran2018?

So, under your opinion, the procedure that I have done for ifort zone is also correct?

If yes, then gfortran have another bug because give me an error about polymorphic assignation.

 

Thanks 

 

0 Kudos
Steve_Lionel
Honored Contributor III
1,753 Views

I quoted Fortran 2018 but the same holds for Fortran 2008 (just the references will be different.)

I can't spot a problem with your ifort section.

tmp is class(refl_type) and allocatable, and List%reflections(i) is also class(refl_type), so intrinsic assignment is fine. What did gfortran give as an error message? (A request - when writing that a compiler didn't like something, show us the exact and complete text of the error message! Also be clear about which exact compiler version you used. Showing console output, including the compile command, is preferable.)

It may simply be that the version of gfortran you're using doesn't yet support polymorphic assignment. That's a F2008 feature and ifort didn't support it until fairly recently. gfortran isn't close to F2008 yet (I don't think the full F2003 version is released yet.)

 

0 Kudos
Javier_Gonzalez-plat
1,753 Views

Hello,

The error message is:

List%reflections(i)=List%reflections(j)

Error: Nonallocatable variable must not be polymorphic in intrinsic assignment at (1) - check that there is a matching specific subroutine for '=' operator.

I have checked it on gfortran 8.1 in Windows and gfortran 9.1 in MacOS obtaining the same problem.

Thanks for all.

Javier

 

 

0 Kudos
Steve_Lionel
Honored Contributor III
1,753 Views

Hmm - now that I look at it again, gfortran is correct. While List%reflections is allocatable and polymorphic, List%reflections(i) is not. I now recall we've had some discussions on this before and the wording is clear that a subobject of an allocatable isn't itself an allocatable. Otherwise, it would be possible for different elements to be of different type! So this is an ifort bug.

If you enclose this code in a SELECT TYPE construct, it should work - except that I get an internal compiler error when I try it! I will report both of these bugs. (Ticket numbers are 04248892 and 04248904.)

0 Kudos
Javier_Gonzalez-plat
1,753 Views

OK

But in this case, I'm looking for a good alternative.

Could you suggest me an appropriate form to do this?

Thanks in advance.

0 Kudos
FortranFan
Honored Contributor II
1,753 Views

Javier Gonzalez-platas wrote:

OK

But in this case, I'm looking for a good alternative.

Could you suggest me an appropriate form to do this?

Thanks in advance.

You may want to consider the "defined assignment" option in the Fortran standard if you really have a need to work with a polymorphic component of 'Reflections' in your 'RefList_Type' derived type.  However you may take note avoiding polymorphism, if that's an option for you, can make matters simpler for you and also help you with performance.

0 Kudos
Steve_Lionel
Honored Contributor III
1,753 Views

This seems to work:

class(Refl_Type), allocatable :: tmp1,tmp2

!> IFort
select type(ref => List%reflections)
type is (refl_type)
tmp1=ref(i)
tmp2 = ref(j)
ref(j)=tmp1
ref(i)=tmp2
class default
  error stop "Wrong type!"
end select

 

0 Kudos
Javier_Gonzalez-plat
1,753 Views

OK

In order to be more general, as list%reflections have to be the same type I could use class is (refl_type), right?

Javier

 

0 Kudos
Steve_Lionel
Honored Contributor III
1,753 Views

You could use CLASS IS here [edit: no], but it has a subtly different meaning. Here's how the standard describes it:

"A TYPE IS type guard statement matches the selector if the dynamic type and kind type parameter values of the selector are the same as those specified by the statement. A CLASS IS type guard statement matches the selector if the dynamic type of the selector is an extension of the type specified by the statement and the kind type parameter values specified by the statement are the same as the corresponding type parameter values of the dynamic type of the selector."

CLASS IS would probably work here too [edit: nope - see below], and would allow List%reflections to be an extension of Refl_type.

0 Kudos
FortranFan
Honored Contributor II
1,753 Views

Steve Lionel (Ret.) (Blackbelt) wrote:

You could use CLASS IS here ..

..

CLASS IS would probably work here too, and would allow List%reflections to be an extension of Refl_type.

I don't think "CLASS IS" will work.  I think it will place OP back to square one: within "CLASS IS: branch of "select type ( ref => List%reflections )", the standard - if I'm not mistaken - stipulates the associating-entity "ref" be polymorphic similar to ASSOCIATE in OP's original post and will thus disallow intrinsic assignments such as "ref(j)=tmp1"

OP with statements such as "In order to be more general, as list%reflections have to be the same type I could use class is (refl_type)" appears to seek certain coding brevity.  If that's the case and the polymorphism of the type component of "Reflections" is important, my hunch is OP will need to make some compromise somewhere and add more code.

0 Kudos
Steve_Lionel
Honored Contributor III
1,753 Views

FF is correct, CLASS IS would NOT work here as stated.

0 Kudos
Javier_Gonzalez-plat
1,753 Views

Dear all,

I don't understand  why something as simple a swap between 2 polymorphic components as in this case

list%reflections(i)=list%reflections(j)

is giving so much problems.

As resume, if I want to have a general procedure working in different compilers, the only thing that I see is do a swap (or copy) element by element according with the type. For me (I'm not expert) it is inconceivable. 

Thanks for all

0 Kudos
Javier_Gonzalez-plat
1,753 Views

To finish...

Here the code for a simplified example (take the general idea about extends types and so on...). Seems that uses polymorphic variables is more  complicate than I expected. 

Thanks.

 

    Program Check
       implicit none
       
       !> Type definitions
       Type :: Refl_Type
          integer,dimension(:), allocatable :: H            
          integer                           :: Mult  =0     
       End Type Refl_Type
       
       Type, extends (Refl_type) :: sRefl_Type
          integer                           :: S  =0     
       End Type sRefl_Type
       
       Type, extends (sRefl_type) :: tRefl_Type
          integer                           :: T  =0     
       End Type tRefl_Type
       
       Type :: RefList_Type
          integer                                     :: Nref
          class(refl_Type), dimension(:), allocatable :: Reflections
       end Type RefList_Type
       
       Type(RefList_Type)            :: List
       Type(Refl_Type), dimension(3) :: Refl_Ini
     
       !> Variables 
       integer :: i
        
       !> Init
       Refl_Ini(1)%H=[1, 0, 0]; Refl_Ini(1)%Mult=1
       Refl_Ini(2)%H=[0, 2, 0]; Refl_Ini(2)%Mult=2
       Refl_Ini(3)%H=[0, 0, 3]; Refl_Ini(3)%Mult=3
        
       List%Nref=3
       List%Reflections=Refl_Ini
       
       !> Print Step:1
       do i=1, List%Nref
          print '(i3,2x,3i4,2x,i3)', i,List%Reflections(i)%H, List%Reflections(i)%Mult
       end do  
       print*,' '
       print*,' '
       
       !> Swap
       call Swap_Elements_List(List, 1, 3)
       
       !> Print Step:2
       do i=1, List%Nref
          print '(i3,2x,3i4,2x,i3)', i,List%Reflections(i)%H, List%Reflections(i)%Mult
       end do
       
    Contains
     
       Subroutine Swap_Elements_List(List, i, j)
          !---- Argument ----!
          type (RefList_Type), intent(in out) :: List
          integer,             intent(in)     :: i,j
           
          !---- Local Variables ----!
          class(Refl_Type), allocatable :: tmp, tmp1,tmp2
    
          !> IFort 19
          !tmp=List%reflections(i)
          !List%reflections(i)=List%reflections(j)
          !List%reflections(j)=tmp
          
          !> Gfortran
          !associate(t1 => list%reflections(i), t2 => list%reflections(j), tt => tmp)
          !   tt=t1
          !   t1=t2
          !   t2=tt
          !end associate
          
          !> General
          tmp1=List%reflections(i)
          tmp2=List%reflections(j)
          select type (ref => List%Reflections)
             type is (refl_type)
                ref(i)%h=tmp2%h
                ref(i)%Mult=tmp2%Mult
                
                ref(j)%h=tmp1%h
                ref(j)%Mult=tmp1%Mult
                 
             type is (sRefl_type)  
                ref(i)%h=tmp2%h
                ref(i)%Mult=tmp2%Mult
                
                ref(j)%h=tmp1%h
                ref(j)%Mult=tmp1%Mult
                
                select type (tmp2)
                   type is (srefl_type)
                      ref(i)%S=tmp2%S
                end select
                
                select type (tmp1)
                   type is (srefl_type)      
                      ref(j)%S=tmp1%S
                end select    
                
             type is (tRefl_type)    
                ref(i)%h=tmp2%h
                ref(i)%Mult=tmp2%Mult
                
                ref(j)%h=tmp1%h
                ref(j)%Mult=tmp1%Mult
                
                select type (tmp2)
                   type is (srefl_type)
                      ref(i)%S=tmp2%S
                   
                   type is (trefl_type) 
                      ref(i)%S=tmp2%S
                      ref(i)%T=tmp2%T  
                end select
                
                select type (tmp1)
                   type is (srefl_type)      
                      ref(j)%S=tmp1%S
                   
                   type is (trefl_type) 
                      ref(j)%S=tmp1%S
                      ref(j)%T=tmp1%T    
                end select  
               
          end select
       End Subroutine Swap_Elements_List
          
    End Program Check

0 Kudos
Steve_Lionel
Honored Contributor III
1,753 Views

The problem is that when you have a polymorphic variable, it can change types.  That's the whole idea of polymorphism. When you do an assignment to a polymorphic variable, it assumes the dynamic type of what is being assigned to it, and that requires it to be allocatable.

But more of a problem is that you don't want to assign the whole variable, which is easier, you want to assign individual elements, and these are not allocatable. So you need to tell the compiler which type you're assigning with SELECT TYPE.

I have not looked at your newest code yet.

0 Kudos
FortranFan
Honored Contributor II
1,753 Views

Javier Gonzalez-platas wrote:

.. I don't understand  why something as simple a swap between 2 polymorphic components as in this case

list%reflections(i)=list%reflections(j)

is giving so much problems.

As resume, if I want to have a general procedure working in different compilers, the only thing that I see is do a swap (or copy) element by element according with the type. For me (I'm not expert) it is inconceivable. ..

Fortran has considerable limitations when it comes to generics: a 'generic' swap routine in Fortran with the current standard will end up requiring a lot of code by the user in form or other to ensure it covers all anticipated scenarios as OP has discovered with the latest revision in Quote #15.

The issue with generics with intrinsic types as well as derived types, just that with polymorphism there is added complexity and checks can even get deferred to run-time, as opposed to compile-time, and this can lead to more problems.

0 Kudos
Steve_Lionel
Honored Contributor III
1,753 Views

The internal compiler error is fixed in compiler 19.1. The lack of an error message in the erroneous case is not yet fixed.

0 Kudos
Steve_Lionel
Honored Contributor III
1,307 Views

The lack of an error message will be fixed in the 2021.6 compiler release - so Intel tells me.

0 Kudos
Reply