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

Derived type IO with private DT array component

Thomas_D_1
Beginner
2,323 Views

I've run into a possible bug with derived type IO when I have a derived type with a private component array of derived types. I've included a example code snippet below that should reproduce the problem (I'm using ifort version 14.0.2.114). To summarize the problem, when I try to write my derived type using a formatted write statement it output the public component of the derived type, as expected, and then it outputs the first n-1 values of the component derived type, which is both private and not called in the main formatted write statement.

In the sample code below the write statement should output:

    1.0000

but instead outputs

    1.0000
    2.0000
    2.0000
    2.0000
    2.0000

Sample code:

module DTIO
  implicit none

  ! derived types:
  type T1
    private
    real :: x
  contains
    procedure, private :: T1_write_formatted
    generic :: write(formatted) => T1_write_formatted
  end type T1

  type T2
    private
    real :: x
    type(T1) :: y(5)
  contains
    procedure, private :: T2_write_formatted
    generic :: write(formatted) => T2_write_formatted
  end type T2

  ! interfaces:
  interface T1
    module procedure :: T1_constructor
  end interface T1

  interface T2
    module procedure :: T2_constructor
  end interface T2
contains


  pure function T1_constructor( x ) result( val )
    real, intent(in) :: x
    type(T1) :: val
    val%x = x
  end function T1_constructor

  pure function T2_constructor( x ) result( val )
    real, intent(in) :: x
    type(T2) :: val
    val%x = x
    val%y = T1(2.0*x)
  end function T2_constructor

  subroutine T1_write_formatted( self, unit, iotype, v_list, iostat, iomsg )
    class(T1), intent(in)    :: self
    integer     , intent(in)    :: unit
    character(*), intent(in)    :: iotype
    integer     , intent(in)    :: v_list(:)
    integer     , intent(out)   :: iostat
    character(*), intent(inout) :: iomsg
    ! local variables:
    character(:), allocatable :: fmt

    ! determine format
    if (len_trim(iotype) > 0) then
      if (iotype(1:2) == 'DT') then
        if (len_trim(iotype) > 2) then
          fmt = '('//iotype(3:)//')'
          write(unit, fmt=fmt, iostat=iostat, iomsg=iomsg) self%x
          deallocate(fmt)
        else
          write(unit, *, iostat=iostat, iomsg=iomsg) self%x
        end if
      else if (iotype == 'LISTDIRECTED') then
        write(unit, *, iostat=iostat, iomsg=iomsg) self%x
      else if (iotype == 'NAMELIST') then
        write(unit, *, iostat=iostat, iomsg=iomsg) self%x
      end if
    end if
  end subroutine T1_write_formatted

  subroutine T2_write_formatted( self, unit, iotype, v_list, iostat, iomsg )
    class(T2), intent(in)    :: self
    integer     , intent(in)    :: unit
    character(*), intent(in)    :: iotype
    integer     , intent(in)    :: v_list(:)
    integer     , intent(out)   :: iostat
    character(*), intent(inout) :: iomsg
    ! local variables:
    character(:), allocatable :: fmt

    ! determine format
    if (len_trim(iotype) > 0) then
      if (iotype(1:2) == 'DT') then
        if (len_trim(iotype) > 2) then
          fmt = '('//iotype(3:)//')'
          write(unit, fmt=fmt, iostat=iostat, iomsg=iomsg) self%x
          deallocate(fmt)
        else
          write(unit, *, iostat=iostat, iomsg=iomsg) self%x
        end if
      else if (iotype == 'LISTDIRECTED') then
        write(unit, *, iostat=iostat, iomsg=iomsg) self%x
      else if (iotype == 'NAMELIST') then
        write(unit, *, iostat=iostat, iomsg=iomsg) self%x
      end if
    end if
  end subroutine T2_write_formatted
end module DTIO

program main
  use DTIO
  implicit none
  ! local parameters:
  type(T2) :: var

  var = T2(1.0)
  write(*,'(A)') 'formatting: unspecified'
  write(*,*) var
  write(*,'(A)') ''
  write(*,'(A)') 'formatting: specified'
  write(*,'(DT"F10.4")') var
end program main
0 Kudos
13 Replies
Steven_L_Intel1
Employee
2,323 Views

This source has several syntactic errors. Please clear these up and post the corrected version.

0 Kudos
Thomas_D_1
Beginner
2,323 Views

Fixed source code

0 Kudos
Steven_L_Intel1
Employee
2,323 Views

I think the behavior is correct. Since defined I/O procedures process the derived type and those procedures are in the module defining the type, all components are accessible.

0 Kudos
Ferdinand_T_
New Contributor II
2,323 Views

Isn't the problem here is that these private components (even though accessible) should actually not be printed, according to the I/O procedures in Thomas' example?

And, strangely, they are not accessible for the DTIO in ifort 15.0.1, which refuses to compile the reduced version of Thomas' example code (error #5521: A derived-type object in an input/output list cannot have private components unless a suitable user-defined input/output procedure is available.)

module DTIO
  implicit none

  type T1
    logical, private :: private_component
  end type

  type T2
    type(T1) :: has_private_component
  end type

  interface write(formatted)
    module procedure T2_write_formatted
  end interface

contains

   subroutine T2_write_formatted( self, unit, iotype, v_list, iostat, iomsg )
    class(T2), intent(in) :: self
    integer, intent(in) :: unit
    character(*), intent(in) :: iotype
	integer, dimension(:), intent(in) :: v_list
    integer, intent(out) :: iostat
    character(*), intent(inout) :: iomsg

  end subroutine

end module DTIO

program main
  use DTIO
  implicit none

  type(T2) :: var
  write (*, '(DT)') var

end program main

Generally, I have the feeling that DTIO (esp. formatted) is not fully supported yet on ifort 15.0.1. Is this true? Is there significant improvement with ifort 15.0.2?

0 Kudos
Thomas_D_1
Beginner
2,323 Views

Ferdinand T. wrote:

Isn't the problem here is that these private components (even though accessible) should actually not be printed, according to the I/O procedures in Thomas' example?

And, strangely, they are not accessible for the DTIO in ifort 15.0.1, which refuses to compile the reduced version of Thomas' example code (error #5521: A derived-type object in an input/output list cannot have private components unless a suitable user-defined input/output procedure is available.)

What is especially strange to me is that it only prints four of the five T1 components. If the behavior was intended, i.e., printing the component derived types even though they are private, then why not print all five elements of the array?

0 Kudos
Steven_L_Intel1
Employee
2,323 Views

The accessibility is irrelevant in the case where the list item is processed by a DTIO procedure. I am not aware of any outstanding issues with DTIO in 15.0.

However, that there are only four of the five array elements is not right and I will look into that.

0 Kudos
Ferdinand_T_
New Contributor II
2,323 Views

Steve Lionel wrote:

[...] The accessibility is irrelevant in the case where the list item is processed by a DTIO procedure. [...]

But why does the compiler trigger that error? (Code in my prior post - there is a DTIO procedure defined). I think that is incorrect.

EDIT: Is it intentional that the user-id is displayed per

user-id wrote:
...
instead of the user-name when quoting a post?

0 Kudos
Steven_L_Intel1
Employee
2,323 Views

Ok. The error message is incorrect. I have escalated that as issue DPD200367665. Still working on the other one.

And yeah, the quoting is not right - I will let the forum folks know.

0 Kudos
Thomas_D_1
Beginner
2,323 Views

Steve Lionel wrote:

Ok. The error message is incorrect. I have escalated that as issue DPD200367665. Still working on the other one.

And yeah, the quoting is not right - I will let the forum folks know.

Thanks for looking into this Steve. I looked through one of the working drafts for the Fortran 2008 standard (WD 1539-1) which specifies on page 237:

If a list item of derived type in a formatted input/output statement is not processed by a defined input/output procedure, that list item is treated as if all of the components of the list item were specified in the list in component order; those components shall be accessible in the scoping unit containing the input/output statement and shall not be pointers or allocatable. 

Which seems to imply that this behavior of printing all components of a derived type when a user specified output procedure is present regardless of what that output procedure does is incorrect (unfortunately I couldn't find anything more explicit on the topic). Also it's worth noting that if I change my type(T1) component array to an array of any intrinsic type (integer, real, ...) the output works as I would expect, i.e., it only outputs component x and not component y. 

0 Kudos
Steven_L_Intel1
Employee
2,323 Views

The text relates to what happens if the derived type is NOT processed by a user-defined I/O procedure, which is not the case here. If the type IS processed by a user procedure, then the normal rules of accessibility in that procedure apply.

0 Kudos
Thomas_D_1
Beginner
2,323 Views

Steve Lionel wrote:

The text relates to what happens if the derived type is NOT processed by a user-defined I/O procedure, which is not the case here. If the type IS processed by a user procedure, then the normal rules of accessibility in that procedure apply.

Right. It specifies the behavior we're seeing (minus printing only n-1 array components) explicitly for the case this isn't. Which would imply that for this case a different way of handling the output should be applied, unfortunately I couldn't find a spot where it explicitly says how this case should be handled in my brief search.

0 Kudos
Steven_L_Intel1
Employee
2,323 Views

Ok, I was mistaken in my original claim that the first program executes correctly, and you are absolutely right that the output should be the same in both cases. I think I know what is happening - in the explicit format case, the compiler has to anticipate the possibility that var might not be handled by a DTIO routine, so it generates alternate code to list out all the components. This isn't working properly - it should give a run-time error in the case the DTIO wasn't used since there are private components, but it is somehow falling into the "list all components" case and not doing that right either.  Escalated as issue DPD200367669.

0 Kudos
Steven_L_Intel1
Employee
2,323 Views

The bug that produced the error message for the program in post #5 (DPD200367665) has been fixed - I expect the fix to appear in 16.0.1. The run-time issue (DPD200367669) is still being worked on, but the basic problem is understood.

0 Kudos
Reply