I'm trying to build an allocatable array with polymorphic elements. A minimal example is the following :
program PolyArray
implicit none
type basetype
integer :: ib
end type basetype
type, extends(basetype) :: exttype1
real :: r1
end type exttype1
type, extends(exttype1) :: exttype2
real :: r2
end type exttype2
type arraytype
class(basetype), allocatable :: comp
end type arraytype
type(arraytype), dimension(:), allocatable :: ary
integer :: N, i
N = 5
allocate (ary(N))
do i=1,N; if (mod(i,2)==0) then
allocate(exttype2::ary(i)%comp)
else if ( i==1) then
allocate(basetype::ary(i)%comp)
else
allocate(exttype1::ary(i)%comp)
end if; end do
do i=1,N; select type (this=>ary(i)%comp)
type is (basetype)
write(*,*) i, "is basetype"!, "%ib =", ary(i)%comp%ib
type is (exttype1)
write(*,*) i, "is exttype1"!, "%r1 =", ary(i)%comp%r1
type is (exttype2)
write(*,*) i, "is exttype2"!, "%r2 =", ary(i)%comp%r2
class default
write(*,*) i, "is unknown type !"
end select; end do
end program PolyArray
Now, the code above works fine and prints out (as expected) :
1 is basetype
2 is exttype2
3 is exttype1
4 is exttype2
5 is exttype1
The problem is, however, once I try to access the component of each extended type (e.g. r1
of exttype1
) by uncommenting the commented part of each write(*,*)
line, my compiler (gfortran 7.5.0) gives the following error :
write(*,*) i, "is exttype1", "%r1 =", ary(i)%comp%r1
1
Error: 'r1' at (1) is not a member of the 'basetype' structure
poly.f90:40:60:
write(*,*) i, "is exttype2", "%r2 =", ary(i)%comp%r2
1
Error: 'r2' at (1) is not a member of the 'basetype' structure
I don't understand why these errors are produced since the compiler obviously recognizes the extended types exttype1
and exttype2
.
What's the correct way to access r1
and r2
?
EDIT :
By changing ary(i)%comp
to this
in each write(*,*)
line, the code compiles normally. What difference does this modification make? How are these two NOT equivalent?
In the select type construct where one has
select type (this=>ary(i)%comp)
there are two things: the selector and the associate name. ary(i)%comp
is the selector here and this
is the associate name.
We know that an associate name is required in this case, because ary(i)%comp
is not a name.
However, the associate name isn't just for convenience (like it may be in an associate construct): it has the fundamental required property that you need here. In the blocks governed by the type guards, the variable given by the associate name has declared type of the type guard; the selector retains the declared type as it had outside the construct.
Having the desired declared type allows us to access the components; simply having the dynamic type doesn't.