Problem with open statement using non-associated pointer argument

Problem with open statement using non-associated pointer argument

Hi,

I'm trying to call the open statement with some specifiers which are non-associated pointer variable.

Since the specifier in the open statement are optional arguments, calling the open statement with a non-associated pointer argument should be the same as if this optional argument were not specified.

However, I'm getting a segmentation fault with the following code.

Program Main
  implicit none
  integer                       ::      Unit
  integer                       ::      ios
  character(*)  ,parameter      ::      FileName = 'formatted.dat'
  character(4)  ,target         ::      Open_Action
  character(4)  ,pointer        ::      Open_Action_Pointer
! *********************************************************************
  Open_Action = 'read'
  write(*,"('Open_Action = ',g0)") Open_Action
  open( NewUnit=Unit, File=FileName, Action=Open_Action, IOStat=ios )         ! -> OK
  write(*,"('open ios = ',g0)") ios
! *********************************************************************
  Open_Action_Pointer => Open_Action
  write(*,"('associated(Open_Action_Pointer) = ',g0)") associated(Open_Action_Pointer)
  if (associated(Open_Action_Pointer)) write(*,"('Open_Action_Pointer = ',g0)") Open_Action_Pointer
  open( NewUnit=Unit, File=FileName, Action=Open_Action_Pointer, IOStat=ios )   ! -> OK
  write(*,"('open ios = ',g0)") ios
! *********************************************************************
  Open_Action_Pointer => null()
  write(*,"('associated(Open_Action_Pointer) = ',g0)") associated(Open_Action_Pointer)
  if (associated(Open_Action_Pointer)) write(*,"('Open_Action_Pointer = ',g0)") Open_Action_Pointer
  open( NewUnit=Unit, File=FileName, Action=Open_Action_Pointer, IOStat=ios )    ! -> DOES NOT WORK
  write(*,"('open ios = ',g0)") ios
! *********************************************************************
End Program

Note that you have to create in the local directory a file called "formatted.dat":

touch formatted.dat

The above code produces the following output

ifort main.f90; ./a.out
Open_Action = read
open ios = 0
associated(Open_Action_Pointer) = T
Open_Action_Pointer = read
open ios = 0
associated(Open_Action_Pointer) = F
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image              PC                Routine            Line        Source             
a.out              00000000004674D9  Unknown               Unknown  Unknown
a.out              0000000000465DAE  Unknown               Unknown  Unknown
a.out              0000000000443D52  Unknown               Unknown  Unknown
a.out              000000000042DCC3  Unknown               Unknown  Unknown
a.out              000000000040329B  Unknown               Unknown  Unknown
libpthread.so.0    00002B99B8665750  Unknown               Unknown  Unknown
a.out              000000000040837F  Unknown               Unknown  Unknown
a.out              0000000000402FC9  Unknown               Unknown  Unknown
a.out              0000000000402C96  Unknown               Unknown  Unknown
libc.so.6          00002B99B8894D65  Unknown               Unknown  Unknown
a.out              0000000000402B89  Unknown               Unknown  Unknown

 

16 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Quote:

flying_hermes wrote:
Since the specifier in the open statement are optional arguments, calling the open statement with a non-associated pointer argument should be the same as if this optional argument were not specified.

Unless you can point to clauses in the Fortran standard to support this conjecture, I am inclined to think that it is not true.

If you have an optional argument present whose INTENT attribute is not OUT, the called routine (user written or intrinsic) would need to access the argument (by dereferencing, if appropriate) in order to decide what to do with it. This is probably the reason for the program to experience an access violation/seg-fault.

Per Jon Reid's "The New Feratures of Fortran 2008 document:

A null pointer or unallocated allocatable that corresponds to an optional non-allocatable
non-pointer dummy argument is interpreted as an absent argument.

I believe such a feature has been available in Intel Fortran since version 14 - perhaps someone (Intel staff?) can verify.  Which version of the compiler are you using?

However I don't know whether a non-associated pointer argument evaluates to a null pointer - seems to make sense, but again, it'll be useful if someone more knowledgeable about the 2008 standard can comment.

 

Note that an OPEN statement doesn't have arguments (it isn't an "open procedure reference") - the things that might look like named arguments are specifiers, so I think discussion of the F2008 feature is moot.

"Null" in that context means a disassociated pointer(pointers either being associated, disassociated or undefined).

So, it there any way I can wrap the open statement in a procedure as in the following example

!  ifort main.f90; ./a.out
Module File_Class
  implicit none
  Type  ::      File_Type
    integer                                     ::      Unit
    character(:)        ,allocatable            ::      FileName
  contains
    procedure   ,public                         ::      Open    =>      Open_File
  End Type
  contains
Subroutine Open_File( This, FileName, Action, Status, IOStat )
  class(File_Type)              ,intent(inout)  ::      This
  character(*)                  ,intent(in)     ::      FileName
  character(*) ,optional ,target,intent(in)     ::      Action
  character(*) ,optional ,target,intent(in)     ::      Status
  integer       ,optional       ,intent(out)    ::      IOStat
  character(:)  ,pointer                        ::      Open_Action_Ptr
  character(:)  ,pointer                        ::      Open_Status_Ptr
  integer                                       ::      Open_IOStat
  Open_Action_Ptr       =>      null()
  Open_Status_Ptr       =>      null()
  if ( present(Action) ) Open_Action_Ptr => Action
  if ( present(Status) ) Open_Status_Ptr => Status
  This%FileName         =       FileName
  open( NewUnit         =       This%Unit,              &
        File            =       This%FileName,          &
        Action          =       Open_Action_Ptr,        &
        Status          =       Open_Status_Ptr,        &
        IOStat          =       Open_IOStat             )
  if ( present(IOStat) ) IOStat = Open_IOStat
End Subroutine
End Module

Program Main
  use File_Class        ,only:  File_Type
  implicit none
  character(*)  ,parameter                      ::      FileName = 'formatted.dat'
  type(File_Type)                               ::      File
  integer                                       ::      Open_IOStat
  character(:)  ,allocatable                    ::      String
! =======================================================================================
! ==>    THIS DOES NOT WORK FINE SINCE SOME OPTIONAL ARGUMENTS ARE NOT SPECIFIED     <===
! =======================================================================================
  String        =       "call File%Open( FileName, IOStat=Open_IOStat )"
  write(*,"(/,a)") String
  call File%Open( FileName, IOStat=Open_IOStat )
  write(*,"('Open_IOStat = ',g0)") Open_IOStat
! =======================================================================================
! ==>          THIS WORKS FINE SINCE ALL OPTIONAL ARGUMENTS ARE SPECIFIED            <===
! =======================================================================================
  write(*,"(/,a)") String
  String        =       "call File%Open( FileName, Action='read', Status='old', IOStat=Open_IOStat )"
  call File%Open( FileName, Action='read', Status='old', IOStat=Open_IOStat )
  write(*,"('Open_IOStat = ',g0)") Open_IOStat
! =======================================================================================
! ===>                          THIS WORKS FINE, AS EXPECTED                         <===
! =======================================================================================
  String        =       "open( NewUnit=File%Unit, File=FileName, Action='read', Status='old', IOStat=Open_IOStat )"
  write(*,"(/,a)") String
  open( NewUnit=File%Unit, File=FileName, Action='read', Status='old', IOStat=Open_IOStat )
  write(*,"('Open_IOStat = ',g0)") Open_IOStat
! =======================================================================================
End Program

Of course, without having a deeply nested 'if' structure for each optional input argument.

Thanks

While the good Doctor is away, I will inquire w/other Developers but it seems to me the program may be in error.

From the Fortran 2008 Standard, Section 9.5.6.2, (page 207, line 2) defines the specifier as “ACTION = scalar-default-char-expr” and on line 34 it says, “A specifier that requires a scalar-default-char-expr may have a limited list of character values. These values are listed for each such specifier. Any trailing blanks are ignored. The value specific is without regard to case.”

Further, in Section 9.5.6.4 (page 208, line 8) it says for the ACTION specifier, the "scalar-default-char-expr shall evaluate to READ, WRITE, or READWRITE."

With that and what IanH notes about "Null", it seems invalid in the context of ACTION specifier.

According to our Fortran UG, the default ACTION is "READWRITE", so in your code, for the case where that optional argument is not specified, you need to assign the pointer to refer to a valid default value instead of null(), which you could make different from the Intel compiler default. The STATUS specifier requires similar handling.

In your #5 code sample you are nullifying the local Open_Action_Ptr, and Open_Status_Ptr

Why not define your own defaults for Action and Status and then point the two pointers at either the defaults or the present() values as desired. In this way the OPEN statement will not have a null pointer.

Jim Dempsey

www.quickthreadprogramming.com

This is what I'm currently doing.

However, this approach quickly becomes very complex if I want to consider all possible specifiers of the open statement, that is: file, status, access, form, position, action, delim, blank, recl, pad and iostat.

In such case, there is no obvious default values for some specifier, since the default value of some specifier depend on the actual value of others. For example, if the "form" specifier is omitted, then then its default value is "form=formatted" for sequential access and "form=unformatted" for direct access, the access behavior being dictated by the "access" specifier.

In addition, some specifier should no be specified if other specifier have a given value. For example, if the specifier "access=direct" is present, then the specifier "position" must be omitted. Another example if that if the "file" specifier must not be present if the "status=scratch" specifier is considered.

Of course, this can be handle by nested "if" construct, but taking into account all possible cases make this approach rather complex.

On the other hand, if the "pointer" approach described above were to work, everything would be much more simple.

One Developer understood both points of view discussed in the thread, and while probably leaning towards the view these are optional keywords to a statement, they believed the run-time should have a better response than SegV for the NULL value so I submitted a request (see internal tracking id below) that the run-time protect against NULL-values provided and return an IOSTAT=0 for the case where the pointer association is False. I will keep you updated on the status of that as I learn it.

(Internal tracking id: DPD200358259)

Great news.

Thanks

Here is some further feedback/clarification from Development:

An OPEN statement is not a procedure call and the specifiers in an OPEN statement like ACTION= are not optional arguments.  If the specifier is in the OPEN statement, the value of the specifier {the expression or variable after the =} is expected to exist if the value is needed to process the OPEN request. So pointers need to be associated and allocatables need to be allocated.

It is true that for procedure invocation, optional arguments can be not present if they are unassociated pointers or unallaocated allocatables.  This is a feature of procedure invocation, not of statements that take keyword specifies like OPEN, CLOSE, and INQUIRE.

Quote:

flying_hermes wrote:

This is what I'm currently doing.

  Open_Action_Ptr       =>      null()
  Open_Status_Ptr       =>      null()
  if ( present(Action) ) Open_Action_Ptr => Action
  if ( present(Status) ) Open_Status_Ptr => Status
  This%FileName         =       FileName
  open( NewUnit         =       This%Unit,              &
        File            =       This%FileName,          &
        Action          =       Open_Action_Ptr,        &
        Status          =       Open_Status_Ptr,        &
        IOStat          =       Open_IOStat             )

In the above, your code, you pass NULL() when arg not present, whereas:

  Open_Action_Ptr       =>     Default_Open_Action ! you define this
  Open_Status_Ptr       =>      Default_Open_Status ! you define this
  if ( present(Action) ) Open_Action_Ptr => Action
  if ( present(Status) ) Open_Status_Ptr => Status
  This%FileName         =       FileName
  open( NewUnit         =       This%Unit,              &
        File            =       This%FileName,          &
        Action          =       Open_Action_Ptr,        &
        Status          =       Open_Status_Ptr,        &
        IOStat          =       Open_IOStat             )

My recommendation does not pass a NULL() when arg not present.

Jim Dempsey

www.quickthreadprogramming.com

Note, nothing precludes you from having different defaults for different Status (or lack thereof).

Jim Dempsey

www.quickthreadprogramming.com

Quote:

Kevin Davis (Intel) wrote:

Here is some further feedback/clarification from Development:

An OPEN statement is not a procedure call and the specifiers in an OPEN statement like ACTION= are not optional arguments.  If the specifier is in the OPEN statement, the value of the specifier {the expression or variable after the =} is expected to exist if the value is needed to process the OPEN request. So pointers need to be associated and allocatables need to be allocated.

It is true that for procedure invocation, optional arguments can be not present if they are unassociated pointers or unallaocated allocatables.  This is a feature of procedure invocation, not of statements that take keyword specifies like OPEN, CLOSE, and INQUIRE.

This is an issue that I ran into a while back as well, but using optional arguments instead of pointers and allocatables. IIRC, the same logic applies to optional arguments.

-Zaak

I believe what you experienced/described Zaak is as one of our Developers describes it:

"If dummy arguments with the OPTIONAL attribute are supplied as inputs to keyword specifiers in the OPEN statement (and the like), they must be present (ie, the PRESENT intrinsic returns .TRUE.) and have a defined value;  otherwise the  program behavior is undefined."

Upon their further consideration/discussion and related to the subsequent clarifications that I posted, Development closed the earlier request DPD200358259 (regarding a better response than SegV) indicating the program behavior is undefined for the original (flying_hermes) case and the OPTIONAL case (Zaak) noted; therefore, no change will be made to the run-time behavior related to using non-associated pointers, non-allocated allocatables, or non-present/non-defined dummy arguments (declared with the OPTIONAL attribute) as inputs to keyword specifiers in OPEN (and the like) statements.

Leave a Comment

Please sign in to add a comment. Not a member? Join today