On 2015-09-04 2:52 PM, Damian Rouson wrote:
> On Thursday, September 3, 2015 at 4:33:25 AM UTC-7, Ian Harvey wrote:
>>
>> My understanding, supported by asking here and elsewhere, is that you
>> can't easily communicate the value of a polymorphic object between
>> images. See C617 in F2008.
>
> That constraint seems to have a lot of qualifiers that narrow its scope. I can
> say that the basic communication a polymorphic object works in GFortran 5.2.
> I just successfully compiled the following example with the OpenCoarrays "caf"
> compiler wrapper and "cafrun" program launcher:
The example below does not communicate the value of a polymorphic
object. The object communicated is non-polymorphic - it is the integer
component. The super-object of that component is polymorphic, but that
is incidental. Similarly incidental is that the communicated thing
happens to be the entirety of the value of the polymorphic object
(because the dynamic type of the object only has that one component),
but that won't be the general case.
> $ cat polymorpic-communication.f90
> program main
> implicit none
> type foo
> integer :: bar
> end type
> class(foo), allocatable :: foobar[:]
> allocate(foo::foobar[*])
> if (num_images()<2) error stop "Not enough images"
> if (this_image()==2) foobar%bar=1
> sync all
> if (this_image()==1) then
> print *,"Before communication: foobar%bar = ",foobar%bar
> foobar%bar = foobar[2] %bar
> print *,"After communication: foobar%bar = ",foobar%bar
> end if
> end program
> The constraint appears to apply to the case when the component (bar) is allocatable. I haven't come across a need for this in my programming, but I imagine it could prove overly restrictive for some applications. I have more experience with writing derived types that have coarray components, but less experience with coarrays that are themselves of derived type.
Considering components, a polymorphic component is either a pointer or
an allocatable. Pointer components, whether polymorphic or not, can
only reference a object on the same image as the pointer (F2008 Note
7.45). Given that reasonable design, it makes no sense to transfer them
between images - hence the general statement that pointers become
undefined when communicated across images (F2008 7.2.2.3p2).
So that leaves polymorphic allocatable components. Given the wording of
that constraint, that knocks polymorphic components out completely.
(I'm not sure why polymorphic pointer components were not also
prohibited by constraint, given communication of them is pointless, but
perhaps the pointer association to something relevant on the local image
can be established by other means post the communication.)
Polymorphic components are what I had in mind with my original point
(refer
https://groups.google.com/d/msg/comp.lang.fortran/52xOEt-HTTc/7QXuFdzkiuQJ),
but to be fair my query does address polymorphic objects in general.
Perhaps I am wrong about that.
So considering polymorphic objects that are not components - i.e.
`foobar` without any further part references in your example above, and
for the sake of example, assume a definition `CLASS(foo), ALLOCATABLE ::
some_local_object`, there is a requirement on assignment statements that
prevents something along the lines of:
foobar[2] = some_local_object
This is reasonable, as it would imply remote allocation, and would break
the requirement that all coarrays have the same dynamic type.
Perhaps the alternative is permitted:
some_local_object = foobar[2]
or using Fortran 2003 sourced allocation, which is more likely to be
supported by current compilers than F2008 polymorphic assignment:
allocate(some_local_object, source=foobar[2])
but is a foobar[2] a subobject of itself? If so - that violates C617.
Or perhaps that concept of self-sub-object is absurd.
I asked one of my compilers (gfortran, current trunk), but, as is to be
reasonably expected with the implementation of new features in compilers
(or perhaps in any program - new features .eqv. new bugs), I didn't get
very far:
gfortran -fcoarray=lib 2015-09-04\ poly-coarray.f90
2015-09-04 poly-coarray.f90:15:0:
allocate(some_local_object, source=foobar[2])
1
internal compiler error: Segmentation fault
ifort 16.0.20150815 (their latest release) accepted it, but
`some_local_object` did not acquire the correct value. Again, new
features, new bugs.
My Cray machine is on the blink, so for now I'll assume this is
conforming. Discussion confirming or contradicting appreciated.
For reference, the complete program was:
program main
implicit none
type foo
integer :: bar = 99
end type
class(foo), allocatable :: foobar[:]
class(foo), allocatable :: some_local_object
allocate(foo::foobar[*])
if (num_images()<2) error stop "Not enough images"
if (this_image()==2) foobar%bar=66
sync all
if (this_image()==1) then
print *,"Before communication: foobar%bar = ",foobar%bar
!foobar%bar = foobar[2] %bar
!print *,"After communication: foobar%bar = ", &
! some_local_object%bar
! some_local_object = foobar[2]
allocate(some_local_object, source=foobar[2])
print *,"After communication: some_local_object%bar = ", &
some_local_object%bar
end if
end program
But let me restate my problem more specifically: I want to be able to
communicate the value of a polymorphic object between images, where the
dynamic type of the object varies from image to image. Perhaps the
dynamic type of the object describes the task that the image needs to
execute, and/or at the other end of the day, the dynamic type of the
object on an image is part of the description of the results of the
calculation done by the image.
I think I can think of ways that sort of hack around this, but they are
very much hacks. I just want to be able to write:
TYPE t
...other polymorphic components in here too...
END TYPE t
TYPE x
...
CLASS(t), ALLOCATABLE :: comp
END TYPE x
CLASS(t), ALLOCATABLE :: local_polymorphic_object
TYPE(x) :: coarray[*]
...
local_polymorphic_object = coarray[idx]%polymorphic_component
The following shows one possible hack, noting that:
- it still precludes the possibility of there being polymorphic
component inside the types that are being communicated (and I find this
seriously limiting);
- I am effectively duplicating the dynamic type information that the
compiler already has in my own variable;
- the in_type, out_type and general use of select type means the main
program has to have intimate knowledge of the extensions being
manipulated, which smashes encapsulation.
- my Cray is still on the blink, so I don't know if this is correct code.
! Module that defines some tasks, represented by extensions of
! `t`, and some results, represented by extensions of `r`.
module m
implicit none
type, abstract :: t
contains
procedure(t_execute), deferred :: execute
end type t
type, abstract :: r
end type r
abstract interface
subroutine t_execute(in, out)
import :: t
import :: r
implicit none
class(t), intent(in) :: in
class(r), intent(out), allocatable :: out
end subroutine t_execute
end interface
type, extends(t) :: ta
integer :: icomp
contains
procedure :: execute => a_execute
end type ta
type, extends(t) :: tb
real :: rcomp
contains
procedure :: execute => b_execute
end type tb
type, extends(t) :: tc
logical :: lcomp
contains
procedure :: execute => c_execute
end type tc
type t_collection
class(t), allocatable :: item
end type t_collection
type, extends(r) :: ra
integer :: icomp
end type ra
type, extends(r) :: rb
real :: rcomp
end type rb
type, extends(r) :: rc
logical :: lcomp
end type rc
type r_collection
class(r), allocatable :: item
end type r_collection
contains
subroutine a_execute(in, out)
class(ta), intent(in) :: in
class(r), intent(out), allocatable :: out
allocate(out, source=rb(real(in%icomp)))
end subroutine a_execute
subroutine b_execute(in, out)
class(tb), intent(in) :: in
class(r), intent(out), allocatable :: out
allocate(out, source=rc(mod(in%rcomp, 2.0) < epsilon(0.0)))
end subroutine b_execute
subroutine c_execute(in, out)
class(tc), intent(in) :: in
class(r), intent(out), allocatable :: out
allocate(out, source=ra(merge(1,-1,in%lcomp)))
end subroutine c_execute
end module m
! Program to execute some tasks on multiple images, and then collect
! results back on image one.
program p3
use m
implicit none
! Type used to broadcast taks data to images. Each possible
! task type needs to be a component.
type :: in_type
type(ta) :: a
type(tb) :: b
type(tc) :: c
end type in_type
! Type used to collect results data from images. Each possible
! result type needs to be a component.
type :: out_type
type(ra) :: a
type(rb) :: b
type(rc) :: c
end type out_type
! The type of task to be executed, then the type of result generated.
character :: instruction[*]
! Used to communicate tasks to images.
type(in_type) :: in[*]
! Used to communicate results from images.
type(out_type) :: out[*]
integer :: image ! Image index
! Assign tasks.
if (this_image() == 1) then
block
type(t_collection) :: tasks(num_images())
do image = 1, num_images()
select case (mod(image, 3))
case (0) ; allocate(tasks(image)%item, source=ta(image))
case (1) ; allocate(tasks(image)%item, source=tb(real(image)))
case (2) ; allocate( tasks(image)%item, &
source=tc(mod(image,2) == 0) )
end select
end do
! At this point the `tasks` variable has all the task
! information. Now we need to communicate that with the
! images.
do image = 1, num_images()
select type (task => tasks(image)%item)
type is (ta)
instruction[image] = 'a'
in[image]%a = task
type is (tb) ;
instruction[image] = 'b'
in[image]%b = task
type is (tc) ;
instruction[image] = 'c'
in[image]%c = task
end select
end do
end block
end if
sync all
! do task
block
class(r), allocatable :: result
select case (instruction)
case ('a') ; call in%a%execute(result)
case ('b') ; call in%b%execute(result)
case ('c') ; call in%c%execute(result)
end select
! Copy result back into coarray
select type (result)
type is (ra)
instruction = 'a'
out%a = result
type is (rb)
instruction = 'b'
out%b = result
type is (rc)
instruction = 'c'
out%c = result
end select
end block
sync all
! Collect results.
if (this_image() == 1) then
block
type (r_collection) :: results(num_images())
do image = 1, num_images()
select case (instruction[image])
case ('a')
allocate(results(image)%item, source=out[image]%a)
case ('b')
allocate(results(image)%item, source=out[image]%b)
case ('c')
allocate(results(image)%item, source=out[image]%c)
end select
end do
! Here results has all our results. Go forth and be merry.
!...
end block
end if
end program p3
In the absence of finding existing reports I will file bug reports for
gfortran and ifort as time permits.