francis
francis

Reputation: 9817

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

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?

Upvotes: 4

Views: 242

Answers (1)

francescalus
francescalus

Reputation: 32451

For a pointer dummy argument the intent(in) attribute means that the pointer shall not appear in a so-called pointer association context. Loosely, this means that you aren't allowed to (potentially) change the pointer association of the dummy argument. You are allowed the change the value of the target of this pointer.

For a pointer dummy argument the intent(in) attribute does not "cascade" to the subojects of the argument (such as in this case the component arrayofint): the component arrayofint of the target of str1 does not have the intent(in) attribute.

In a reference like str1%arrayofint with str1 a pointer, this is a reference to the component arrayofint of the target of str1. Even if the intent(in) attribute did apply to subobjects of the pointer dummy argument the object referenced by str1%arrayofint is not a subobject of str1.

ifort is wrong to think that such an object has the intent(in) attribute. You have found a valid way to work around such shortcomings in ifort. You should consider reporting this flaw to Intel.

Finally, there may be better ways to solve your problem of resizing the component without using pointer dummy arguments, but I won't consider those in this answer.

Upvotes: 4

Related Questions