- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I posted this issue on Stackoverflow and was advised to report to experts of ifort on intel webite.
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks for your detailled answer. Following your advice, I posted a support request quoting the present topic. I'll post the outcome as well.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
This issue is fixed in PSXE Update 2 (Fortran 19.1.2) that was released last week.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
this issue was fixed in PSXE 2020 Update 2, compiler 19.1.2
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page