Reputation: 9817
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
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