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

move_alloc() on attribute of a Pointer,intent(in) triggers error

Lavergne__Francis
1,056 Views

I posted this issue on Stackoverflow and was advised to report to experts of ifort on intel webite.

https://stackoverflow.com/questions/59967858/ifort-move-alloc-on-attribute-of-a-pointer-intentin-triggers-error/59975882#59975882

I would like to extend an allocatable attribute of a structure and `MOVE_ALLOC()` seems the cleanest way to do it. So I created a routine using a `Pointer, Intent(in)` pointing to the structure as argument and tried to call:
 
 

    Type(STRUCT1), Pointer, Intent(in)  :: str1
    ...
    call MOVE_ALLOC(TO= str1%arrayofint , FROM= temparray)

where `str1` is the pointer to the structure and `arrayofint` is the attribute to be extended. See subroutine `LOC_extendsecond()` of the following sample code. **The compiler ifort returns the following error:**

    Source1.f90(91): error #7999: The FROM or TO arguments of a MOVE_ALLOC reference must not be INTENT(IN).   [MOVE_ALLOC]

As if `str1%arrayofint` were `Intent(in)`. **Keeping in mind that the pointer `str1` is `Intent(in)`,  are the attributes of the pointed structure `str1%arrayofint` to be considered as `Intent(in)`?**

To investigate the issue, I tried not to use `MOVE_AllOC()` and found that deallocating or allocating `str1%arrayofint` in the routine does not trigger any error or warning from ifort. See subroutine `LOC_extendfirst()` of the sample code.

A workaround was suggested by a collegue of mine (thanks Luc!): a local copy of the pointer is created and `MOVE_AllOC()` can be called using this local copy of the pointer without ifort raising an error. See subroutine `LOC_extendthird()` of the sample code.

    Type(STRUCT1), Pointer, Intent(in)  :: str1
    Type(STRUCT1), Pointer  :: str2
    str2=>str1
    ...
    call MOVE_ALLOC(TO= str2%arrayofint , FROM= temparray)

Here goes the sample code:

    Module Source1
    
        Implicit None
        
        Public :: STRUCT1

      Private
    
      Type STRUCT1
          Integer, dimension(:), allocatable    :: arrayofint 
          
      End Type STRUCT1

        Contains

        Subroutine newstruct1(str1,ier,errmsg)
    
            Type(STRUCT1), Pointer, Intent(inout)  :: str1
            Integer, Intent(out) :: ier
            character(len=256), Intent(out) :: errmsg
      
            ier=0
            allocate(str1,stat=ier, errmsg=errmsg)
            if( ier>0) return
      
            allocate(str1%arrayofint(2),stat=ier, errmsg=errmsg)
            if( ier>0) return
      
        End Subroutine newstruct1
        
        Subroutine LOC_extendfirst(str1,targetsize,ier,errmsg)
            Type(STRUCT1), Pointer, Intent(in)  :: str1
            Integer, Intent(out)  :: ier
            character(len=256), Intent(out)  :: errmsg
            Integer, Intent(in)  :: targetsize 
     
            Integer,dimension(1) :: shp
            Integer :: newsize , formersize
            Integer, dimension(:), allocatable :: temparray
     
            ier=0
      
            shp=shape(str1%arrayofint)
            formersize=shp(1)
            if (targetsize .GT. formersize) then
                newsize=MAX(targetsize,2*formersize)
                allocate(temparray(newsize),stat=ier, errmsg=errmsg)
                if( ier>0) then; return ; endif
                temparray(1:formersize)=str1%arrayofint
                allocate(temparray(formersize),stat=ier, errmsg=errmsg)
                if( ier>0) then; return ; endif 
                temparray=str1%arrayofint
                if(allocated(str1%arrayofint)) deallocate(str1%arrayofint)
                allocate(str1%arrayofint(newsize),stat=ier, errmsg=errmsg)
                if( ier>0) then; return ; endif  
                str1%arrayofint(1:formersize)=temparray
                if(allocated(temparray)) deallocate(temparray)
            endif
      
      End Subroutine LOC_extendfirst
     
          Subroutine LOC_extendsecond(str1,targetsize,ier,errmsg)
              Type(STRUCT1), Pointer, Intent(in)  :: str1
              Integer, Intent(out)  :: ier
              character(len=256), Intent(out)  :: errmsg
              Integer, Intent(in)  :: targetsize 
     
              Integer,dimension(1) :: shp
              Integer :: newsize , formersize
              Integer, dimension(:), allocatable :: temparray
     
              ier=0
      
              shp=shape(str1%arrayofint)
              formersize=shp(1)
              if (targetsize .GT. formersize) then 
                   newsize=MAX(targetsize,2*formersize)
                   allocate(temparray(newsize),stat=ier, errmsg=errmsg)
                   if( ier>0) then; return ; endif
                   temparray(1:formersize)=str1%arrayofint
                   ! TODO uncomment the following line to get error from ifort
                   call MOVE_ALLOC(TO= str1%arrayofint , FROM= temparray)
              endif
      
      End Subroutine LOC_extendsecond
      
      Subroutine LOC_extendthird(str1,targetsize,ier,errmsg)
          Type(STRUCT1), Pointer, Intent(in)  :: str1
          Integer, Intent(out)  :: ier
          character(len=256), Intent(out)  :: errmsg
          Integer, Intent(in)  :: targetsize 
     
          Integer,dimension(1) :: shp
          Integer :: newsize , formersize
          Integer, dimension(:), allocatable :: temparray
     
          Type(STRUCT1), Pointer  :: str2
          ier=0
      
          str2=>str1
          shp=shape(str2%arrayofint)
          formersize=shp(1)
          if (targetsize .GT. formersize) then 
              newsize=MAX(targetsize,2*formersize)
              allocate(temparray(newsize),stat=ier, errmsg=errmsg)
              if( ier>0) then; return ; endif
              temparray(1:formersize)=str1%arrayofint
              call MOVE_ALLOC(TO= str2%arrayofint , FROM= temparray)
    
          endif
      
      End Subroutine LOC_extendthird
      
      End Module Source1
     

Calling ifort 19.0.2.190 IA32 on windows, using `ifort /nologo /debug:full /Od /debug-parameters:all /warn:unused /warn:truncated_source /warn:uncalled /warn:interfaces /Qsave  /traceback /check:pointer /check:bounds /check:uninit /libs:static /threads /dbglibs /c /Qm32 "Source1.f90"` produces the error.

**On the contrary, using gfortran from gcc 6.3.0 on a Debian using `gfortran -c  Source1.f90 -Wall` does not result in any error:** it only displays warnings about function not being used.

I am quite a newbie regarding Fortran. I know Fortran pointers wrap much more data than a C pointer, so I wonder whether modifying the attribute `str1%arrayofint` is correct as `str1` is a `Pointer, Intent(in)`, just like modifying str1->arrayofint in a function is correct as pointer str1 is passed by value.

How to resolve the difference of behavior between ifort and gfortran? Is ifort correct as it reports an error in the present situation? Why does ifort considers `str1%arrayofint` to be `Intent(in)`, as if `str1` were a `Type(STRUCT1), Intent(in)` and not a `Type(STRUCT1), Pointer, Intent(in)` ? Is introducing of a local copy of the pointer as shown in `LOC_extendthird()` the most appropriate way to mitigate the issue?

0 Kudos
1 Solution
Steve_Lionel
Honored Contributor III
1,056 Views

Thanks for the detailed post, including a complete test case.

My opinion is that ifort is wrong here. There are two points to consider.

First, INTENT for a POINTER argument restricts changing the pointer, not what it points to. The standard says (8.5.10p2):

The INTENT (IN) attribute for a pointer dummy argument specifies that during the invocation and execution of the procedure its association shall not be changed except that it may become undefined if the target is deallocated other than through the pointer (19.5.2.5).

But it isn't STR1 that is being operated on, it's a subcomponent. There is this rule (8.5.10p6):

If a nonpointer object has an INTENT attribute, then all of its subobjects have the same INTENT attribute.

but STR1 is a pointer, so this does not apply. NOTE 3 adds:

Similarly, the INTENT restrictions on pointer dummy arguments apply only to the association of the dummy argument; they do not restrict the operations allowed on its target.

MOVE_ALLOC does disallow INTENT(IN) for either FROM or TO, but in this case neither does.

Yes, a local pointer copy would (should!) be a workaround. Please report this to Intel using the Online Service Center, as reports here don't always get picked up by the support team. You can point to the discussion here.

View solution in original post

0 Kudos
5 Replies
Steve_Lionel
Honored Contributor III
1,057 Views

Thanks for the detailed post, including a complete test case.

My opinion is that ifort is wrong here. There are two points to consider.

First, INTENT for a POINTER argument restricts changing the pointer, not what it points to. The standard says (8.5.10p2):

The INTENT (IN) attribute for a pointer dummy argument specifies that during the invocation and execution of the procedure its association shall not be changed except that it may become undefined if the target is deallocated other than through the pointer (19.5.2.5).

But it isn't STR1 that is being operated on, it's a subcomponent. There is this rule (8.5.10p6):

If a nonpointer object has an INTENT attribute, then all of its subobjects have the same INTENT attribute.

but STR1 is a pointer, so this does not apply. NOTE 3 adds:

Similarly, the INTENT restrictions on pointer dummy arguments apply only to the association of the dummy argument; they do not restrict the operations allowed on its target.

MOVE_ALLOC does disallow INTENT(IN) for either FROM or TO, but in this case neither does.

Yes, a local pointer copy would (should!) be a workaround. Please report this to Intel using the Online Service Center, as reports here don't always get picked up by the support team. You can point to the discussion here.

0 Kudos
Lavergne__Francis
1,056 Views

Thanks for your detailled answer. Following your advice, I posted a support request quoting the present topic. I'll post the outcome as well.

0 Kudos
FortranFan
Honored Contributor II
1,056 Views

Lavergne, Francis wrote:

.. I am quite a newbie regarding Fortran. I know Fortran pointers wrap much more data than a C pointer ..

@Lavergne, Francis,

Given your comment above, do you really need to use the POINTER attribute with your objects in Fortran?  Have you considered simply the ALLOCATABLE attribute with components of derived types and nonpointer objects of such derived types?  It appears what you're trying to do with the code in the original post can be achieved as follows, just some food for thought to help yourself stay away from issues with pointers such as possible memory leaks, dangling references, aliasing hindering or preventing optimization, etc.

module m

   type :: t
      integer, allocatable :: arr(:)
      integer :: alloc_stat = 0
      character(len=256) :: alloc_msg = ""
   contains
      procedure, pass(this) :: resize
   end type t

contains

   subroutine resize( this, n )
   
      ! Argument list
      class(t), intent(inout)       :: this
      integer, intent(in), optional :: n
      
      ! Local variables
      integer :: narr, i
      integer, allocatable :: arr(:)
      
      narr = 2
      if ( present(n) ) then
         if ( n > 0 ) then
            narr = n
         else
            ! error handling elided
         end if
      end if

      allocate( arr(narr), stat=this%alloc_stat, errmsg=this%alloc_msg )
      if ( allocated(this%arr) ) then
         i = size(this%arr)
         if (i > 0) then
            i = min(i, narr)
            arr(1:i) = this%arr(1:i)
         end if
         arr(i+1:) = 0
      else
         arr = 0
      end if
      
      call move_alloc( from=arr, to=this%arr )

   end subroutine
   
end module
      use m, only : t
      
      type(t) :: foo
      
      call foo%resize()
      foo%arr(1) = 1 ; foo%arr(2) = 2
      print *, "foo%arr = ", foo%arr, "; expected is 1, 2"
      
      call foo%resize(5)
      foo%arr(3:) = [ 3, 4, 5 ]
      print *, "foo%arr = ", foo%arr, "; expected is 1, 2, 3, 4, 5"

      call foo%resize(3)
      print *, "foo%arr = ", foo%arr, "; expected is 1, 2, 3"

      call foo%resize()
      print *, "foo%arr = ", foo%arr, "; expected is 1, 2"

      call foo%resize(4)
      print *, "foo%arr = ", foo%arr, "; expected is 1, 2, 0, 0"
      
end program

Upon execution with Intel Fortran.

 foo%arr =  1 2 ; expected is 1, 2
 foo%arr =  1 2 3 4 5 ; expected is 1, 2, 3, 4, 5
 foo%arr =  1 2 3 ; expected is 1, 2, 3
 foo%arr =  1 2 ; expected is 1, 2
 foo%arr =  1 2 0 0 ; expected is 1, 2, 0, 0

 

0 Kudos
Barbara_P_Intel
Moderator
1,043 Views

This issue is fixed in PSXE Update 2 (Fortran 19.1.2) that was released last week.



0 Kudos
Ron_Green
Moderator
998 Views

this issue was fixed in PSXE 2020 Update 2, compiler 19.1.2


0 Kudos
Reply