fortranpolymorphismgfortranintel-fortrannag-fortran

Conflict between defined assignment and intrinsic assignment (with nagfor)?


Intrinsic polymorphic assignment is a recent feature of some Fortran compilers (e.g. ifort 18, nagfor 6.2) that is not available in older versions (e.g. ifort 17, gfortran 6.3). A well-known solution that works with these older versions is to use a defined assignment as in the example below (taken and adapted from the book of Chivers and Sleightholme):

module deftypes  
   type, abstract :: shape_t  
      integer :: x = 0, y = 0  
   end type shape_t  

   type, extends(shape_t) :: circle_t  
      integer :: radius = 0  
   end type circle_t  

   interface assignment(=)
      module procedure generic_shape_assign
   end interface   

contains  
   subroutine generic_shape_assign ( lhs, rhs )  
      class(shape_t),              intent(in ) :: rhs  
      class(shape_t), allocatable, intent(out) :: lhs  
      print*,' --> in generic_shape_assign'  
      allocate(lhs, source = rhs)  
   end subroutine generic_shape_assign
end module deftypes  

program check_assign  
   use deftypes  
   implicit none  
   class(shape_t), allocatable :: myshape
   type (circle_t)             :: mycirc1, mycirc2  

   mycirc1 = circle_t ( 1, 2, 3 )    

   print*,'A polymorphic assignment: myshape = mycirc1'  
   myshape = mycirc1  

   print*,'An intrinsic assignment: mycirc2 = mycirc1'   
   mycirc2 = mycirc1
end program check_assign

This example, compiles and works well with ifort 15.0.3 and gfortran 6.3.0. But with nagfor 6.2 I get the following error during the compilation (for the line mycirc2=mycirc1):

Error: check_assign.f90, line 41: Incorrect data type CIRCLE_T (expected SHAPE_T) for argument LHS (no. 1) of GENERIC_SHAPE_ASSIGN  

It's not clear to me why this compiler is trying to use the defined assignment in the instruction mycirc2 = mycirc1 while these two variables are not allocatable polymorphic ones.

Of course, if I delete the defined assignment it works with nagfor but not with the other old compilers. Any idea where this error came from and how to get around it?


Solution

  • I believe that the compiler is correct to reject this program. However, if you have a support contract with NAG I strongly advise asking them over taking my comments as definitive.

    I will show my reasoning.

    It is clear that the reference to the specific procedure generic_shape_assign like

    type(circle_t) mycirc1, mycirc2
    call generic_shape_assign(mycirc2, mycirc1)
    

    is not valid. It fails because the actual argument mycirc2, corresponding to the allocatable polymorphic dummy argument lhs:

    The error message you quote covers rejection of the program for violating this second.

    So, that means that generic_shape_assign is not a valid specific procedure (for this reference) with generic specification assignment(=), right? And thus no defined assignment is chosen and the compiler should fall back to intrinsic assignment?

    This is where things get murky (at least to me).

    I think that the specific subroutine generic_shape_assign is chosen for the defined assignment and the compiler is therefore correct to reject your program because you aren't calling this specific subroutine correctly.

    Let's look further, using Fortran 2008 7.2.1.4 where there's definition of when an assignment statement is a defined assignment statement.

    To decide whether the subroutine generic_shape_assign defines the defined assignment statement mycirc2=mycirc1 we look at the given points:

    1. generic_shape_assign is a subroutine with two dummy arguments (lhs and rhs here);
    2. the interface block gives generic_shape_assign the generic spec assignment(=);
    3. lhs (of type shape_t) is type compatible with mycirc2 (of dynamic type circle_t); rhs similarly;
    4. there are no type parameters for dummy or actual arguments;
    5. the ranks (being scalar) of the dummy and actual arguments match.

    We meet all of the requirements for this being a defined assignment: there is no requirement which states that defined assignment requires the chosen subroutine to be callable!

    In summary:

    It's not clear to me why this compiler is trying to use the defined assignment in the instruction mycirc2 = mycirc1 while these two variables are not allocatable polymorphic ones.

    Because whether defined assignment is used is unrelated to whether the left- and right-hand sides are polymorphic or allocatable.

    Finally, I think the diagnostic message from the compiler could be improved whether my reasoning is correct or incorrect.