- 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