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

Expect compile time error using subsection on MOVE_ALLOC(), instead get run-time ICE or runs

ur
New Contributor II
3,900 Views

I think that calling MOVE_ALLOC() with an array subsection should be caught at compile time.

Similar issues are caught at compile time such as passing  ARRAY(:) to a procedure requiring an allocatable argument; the standard says FROM shall be allocatable; and other compilers I tried catch it at compile time.

 

 

# move_alloc bug

ifort(1) allows subsections on FROM on MOVE_ALLOC() but then sometimes
get ICE, always get entire array with subsection specification ignored
if successful.

Expect error message at compile time indicating FROM is not allocatable
when a subsection is specified.

## 
```fortran
program testit
integer,allocatable :: i(:),j(:)
integer :: stat
character(len=256) :: errmsg
i=[1,2,3,4,5,6,7,8,9,10]
call move_alloc(i(::2),j,stat,errmsg)
call printit()
call move_alloc(i(:),j,stat,errmsg) ! segfault
call printit()

contains

subroutine printit
if(stat.ne.0)then
   write(*,'(a)')'<ERROR>'//trim(errmsg)
   stop
endif
write(*,*)allocated(i),allocated(j)
write(*,*)j
end subroutine printit

end program testit
```
## Results
```text
 F T
           1           2           3           4           5           6
           7           8           9          10
 F F
forrtl: severe (408): fort: (8): Attempt to fetch from allocatable variable J when it is not allocated

Image              PC                Routine            Line        Source             
bug1               0000000000404AA0  testit_IP_printit          17  bug1.f90
bug1               000000000040470D  MAIN__                      9  bug1.f90
bug1               000000000040415D  Unknown               Unknown  Unknown
libc-2.31.so       00007FBEC358C083  __libc_start_main     Unknown  Unknown
bug1               000000000040407E  Unknown               Unknown  Unknown
```

 

1 Solution
Barbara_P_Intel
Employee
1,745 Views

@ur, Thank you for reporting this.

@FortranFan, Thanks for the simplified reproducer.

I filed a bug report, CMPLRLLVM-42905. Both ifx and ifort should be reporting the error.



View solution in original post

0 Kudos
12 Replies
jimdempseyatthecove
Honored Contributor III
3,888 Views

On the line 19 call to move_alloc, the array i was deallocated from the move_alloc on line 17.

Note, while line 17 uses a strided source, the whole array of the array descriptor i was deallocated.

 

Jim Dempsey

 

0 Kudos
ur
New Contributor II
3,881 Views

 

program testit
integer,allocatable :: i(:),j(:)
integer :: stat
character(len=256) :: errmsg
i=[1,2,3,4,5,6,7,8,9,10]
call move_alloc(i(::2),j,stat,errmsg)
call printit()
i=[1,2,3,4,5,6,7,8,9,10]
call move_alloc(i(:),j,stat,errmsg) ! segfault
call printit()
contains
subroutine printit
if(stat.ne.0)then
   write(*,'(a)')'<ERROR>'//trim(errmsg)
   stop
endif
write(*,*)allocated(i),allocated(j)
write(*,*)j
end subroutine printit
end program testit

 

Good point; was combining failures.  The edit mode does not let me remove the inserted block; but correcting it to reallocate it then lets it run instead of segfaulting;  the line is still not legal, as  VAR(:) is not an allocatable argument, although it intuitively seems the same as VAR.

 

 

For example, ifort will not allow this to compile:

 

program testit
integer,allocatable :: i(:)

   i=[1,2,3,4,5,6,7,8,9,10]
   call printit(i)
   call printit(i(:))

contains

subroutine printit(i)
integer,allocatable :: i(:)
   write(*,*)allocated(i)
   write(*,*)i
end subroutine printit

end program testit


!$ ifort testit.f90
!testit.f90(6): error #7851: If dummy arg is allocatable, actual arg must be a !whole array and not a section.   [I]
!   call printit(i(:))
!----------------^
!compilation aborted for testit.f90 (code 1)
!
!$ gfortran testit.f90
!testit.f90:6:16:
!
!    6 |    call printit(i(:))
!      |                1
!Error: Actual argument for 'i' must be ALLOCATABLE at (1)

 

 

If I create a subroutine with allocatable arguments and pass it an allocatable value with the syntax VAR(:) instead of VAR ifort/ifx reports that as trying to pass an unallocatable argument, for example.  The compiler is ignoring that a subsection has been specified, which I argue is incorrect. passing VAR(::2) is not the same as passing VAR.

 

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,876 Views

>>VAR(:) is not an allocatable argument

This is correct, same with var(::2).

I pointed out that i was deallocated, even though the i(::2) was invalid.

This compiler bug should be added to the TO DO list.

 

Jim Dempsey

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,870 Views

FWIW (observation)

There are some subtle issues to resolve.

Consider:

call move_alloc((i(::2)),j,stat,errmsg)

The argument "(i(::2))", assuming heap-arrays enabled, generates a non-strided allocatable temporary copy of the strided data.

Does this satisfy the requirement that the from argument be allocatable?

Note, if the strided descriptor (sans enclosing ()'s) is NOT passed to the underlaying move_alloc procedure, then this syntax will generate a non-strided temporary, and if heap-arrays is enabled, this too will then be an allocatable argument.

 

This should be catchable by the front-end.

 

Jim Dempsey

0 Kudos
ur
New Contributor II
3,864 Views

I had similar questions, but could not find anything that conditionally allowed a subsection with the second example. For example:

$ ifort -heap-arrays bug2.f90
bug2.f90(6): error #7851: If dummy arg is allocatable, actual arg must be a whole array and not a section. [I]
call printit(i(:))
----------------^

 

Note if you did want to conditionally allow it when the temporary is arguably allocatable it appears to currently be ignoring the subsection specification, as all the array is returned, not just every other as specified by the i(::2) syntax.  I was curious if that would still be true with the heap switch on; it still returned the entire array.

 

FROM has to have intent(INOUT) anyway, so it can be deallocated.  That adds another complication. If the temporary is allocatable would the argument still have to be?  Does the original variable still get deallocated?  the heap size would change whether the same line were OK or not;

might be doable but starts to sound pretty complicated.

0 Kudos
ur
New Contributor II
3,866 Views

Yes, I was combining some test cases. Should have paid more attention to the failure becoming a segfault when I combined them

This showed up when someone was testing if they got a performance improvement changing some statements that used array syntax to use MOVE_ALLOC instead, and changed one too many lines and did one with a subsection and got the entire array copied instead of getting an error, which introduced a bug.  I was debating on whether when the whole array was passed but with a subsection (ie. "i(:)") if it should be OK (it should not be, I decided) even though in some ways it is the same values and threw that on in the example too, but incorrectly, as you noted. Thanks for catching that.

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,856 Views

If we are to assume your subroutine printit is to print/file an array and otherwise not allocate/deallocate/move_alloc the array, then the dummy argument should not have the allocatable attribute.

 

>>FROM has to have intent(INOUT) anyway, so it can be deallocated.

In the general usage of move_alloc, the FROM argument is typically a procedure temporary allocated array that is sized and filled with initial data and then who's descriptor is moved from the from to the to (pre-deleting to if necessary, and "nulling out" the from descriptor). This operation does not perform an unnecessary copy and the from descriptor is not actually deleted (as the data now resides at the to discriptor's location), but rather marked as if deleted.

 

Jim Dempsey

0 Kudos
ur
New Contributor II
3,850 Views

printit is just a placeholder for a procedure with an allocatable array. It has no other purpose. It prints to give it something to do.

Ideally the data is not deleted just pointed to for efficiency, but not required by the standard. The allocation status still has to be changed on the FROM variable, which is probably why the standard definition states FROM must have intent(INOUT).  How the system tracks the allocation status is left up to the compiler as far as I know, and could be anything from a state variable that is part of the allocatable object or kept in a table somewhere or anything else that tracks the state in I suppose but the standard does specify it have that intent.

16.9.137 MOVE_ALLOC (FROM, TO [, STAT, ERRMSG])
1 Description. Move an allocation.
2 Class. Subroutine, pure if and only if FROM is not a coarray.
3 Arguments.
FROM may be of any type, rank, and corank. It shall be allocatable and shall not be a coindexed object.
It is an INTENT (INOUT) argument.
TO shall be type compatible (7.3.2.3) with FROM and have the same rank and corank. It shall be
allocatable and shall not be a coindexed object. It shall be polymorphic if FROM is polymorphic.
It is an INTENT (OUT) argument. Each nondeferred parameter of the declared type of TO shall
have the same value as the corresponding parameter of the declared type of FROM.
STAT (optional) shall be a noncoindexed integer scalar with a decimal exponent range of at least four. It is an
INTENT (OUT) argument.
ERRMSG (optional) shall be a noncoindexed default character scalar. It is an INTENT (INOUT) argument.

 

So I am thinking since it specifies FROM must be allocatable, and must have INTENT(INOUT) that even though the idea of allowing a temp to be created for an array subsection that was allocatable that TO could point to it would be a non-standard extension even though an interesting idea.  Since I can already do "i=i(::2)" instead of the MOVE_ALLOC() my guess it would be hard to get that adopted and let MOVE_ALLOC be specifically for reassigning existing storage.  Accept for efficiency I actually like Fortran array syntax. It seems so much more natural. I would have liked something simple like j << i  to have meant "assign values of i to j and deallocate j" or something like that instead of MOVE_ALLOC or games with pointers but we have found several places where nice and neat array syntax was significantly slower than MOVE_ALLOC, which is really the reason MOVE_ALLOC exists I would suppose.

 

PS:  As noted gfortran does not let the MOVE_ALLOC example.  nvfortran does not either:

 

NVFORTRAN-F-0507-The arguments to MOVE_ALLOC must be ALLOCATABLE - (bug3.f90: 7)

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,837 Views

I think you misunderstood me on INTENT(OUT)

The from argument, from the perspective of (scope) within the move_alloc procedure is as if its dummy argument is declared with ALLOCATABLE, INTENT(INOUT). N.B. move_alloc is likely written in C/C++ and the language has no INTENT, nor dummy arguments.

From's caller's perspective (scope), it need minimally be ALLOCATABLE.

Note, for an allocatable array declared within a procedure calling move_alloc, it cannot have an INTENT(...) as INTENT is available only to the dummy arguments of the procedure (if any).

 

Jim Dempsey

0 Kudos
FortranFan
Honored Contributor III
3,823 Views

@ur ,

Re: "I think that calling MOVE_ALLOC() with an array subsection should be caught at compile time," - note the following:

- it looks like a new feature request for Intel Fortran and something many customers of Intel Fortran will appreciate but it will take some convincing, I think, for you to get the Intel software team to implement something.  Because the standard does not have, if I recall correctly (it's possible I'm wrong), a numbered constraint or syntax rules to require the compiler to detect and report this

It may then help you if you can keep it really, really brief with your code example and cut out all the needless verbosity and simply illustrate to Intel team how IFX can do better, say like so:

   integer, allocatable :: a(:), b(:)
   call move_alloc( from=a(1:2), to=b ) !<-- an array section is not a whole object and thus not allocatable
end
  • A processor out there shows it is detectable at compile-time an array section is not allocatable
C:\Temp>gfortran -c p.f90
p.f90:2:25:

    2 |    call move_alloc( from=a(1:2), to=b )
      |                         1
Error: 'from' argument of 'move_alloc' intrinsic at (1) must be ALLOCATABLE

C:\Temp>
  • So that IFX can do better
C:\Temp>ifx /c /standard-semantics p.f90
Intel(R) Fortran Compiler for applications running on Intel(R) 64, Version 2022.1.0 Build 20220316
Copyright (C) 1985-2022 Intel Corporation. All rights reserved.


C:\Temp>

 

 

Barbara_P_Intel
Employee
1,746 Views

@ur, Thank you for reporting this.

@FortranFan, Thanks for the simplified reproducer.

I filed a bug report, CMPLRLLVM-42905. Both ifx and ifort should be reporting the error.



0 Kudos
Barbara_P_Intel
Employee
1,344 Views

There's a lovely error message now for both ifx and ifort in the latest compiler versions available in oneAPI HPC Toolkit 2023.1.

Try it!

+ ifx -what -c section.f90
 Intel(R) Fortran 23.0-1474.2
section.f90(2): error #8195: The argument to the MOVE_ALLOC intrinsic subroutine shall be an allocatable object.   [MOVE_ALLOC]
   call move_alloc( from=a(1:2), to=b ) !<-- an array section is not a whole object and thus not allocatable
-------------------------^
compilation aborted for section.f90 (code 1)

+ ifort -what -c section.f90
 Intel(R) Fortran 2021.9.0-1497
section.f90(2): error #8195: The argument to the MOVE_ALLOC intrinsic subroutine shall be an allocatable object.   [MOVE_ALLOC]
   call move_alloc( from=a(1:2), to=b ) !<-- an array section is not a whole object and thus not allocatable
-------------------------^
compilation aborted for section.f90 (code 1)

 

 

 

0 Kudos
Reply