select type assignment to selector variable

select type assignment to selector variable

Hello,

I am looking for some advice with the following assignment to the selector variable in a SELECT TYPE block.
The Intel compiler 13.1.3 compiles successfully, but version 14.0.1 produces a run-time error (see below):

program p
implicit none
    ! abstract
    type, abstract :: abstract_type
        integer :: nonempty
    end type
 
    ! contains abstract allocatable
    type :: container_type
        integer :: nonempty
        ! fix: uncomment next line or use pointer instead
        class(abstract_type), allocatable :: abstract_field
    end type
 
    type(container_type) :: rhs
    class(container_type), allocatable :: polymorphic_lhs
 
    ! assign into polymorphic
    select type (polymorphic_lhs)
    type is (container_type)
        ! runtime error (ifort 14.0.1)
        ! problem with 'abstract_field' allocatable component
        ! (no matter it's allocation status in lhs and rhs)
        polymorphic_lhs = rhs    
    end select
 
    ! output (ifort 13.1.3): F
    print *, allocated(polymorphic_lhs%abstract_field)
end program

Compilation and output:

$ ifort -g -O0 test.f90
$ ./a.out
forrtl: severe (122): invalid attempt to assign into a pointer that is not associated

My two questions here are:

  • Do I violate some restrictions (read-only variable in the SELECT CASE association or something) here?
  • How else can I achieve the desired assignment in the case where 'polymorphic_lhs' is a PASS'ed derived type dummy argument?

Thanks for any comments!
With regards, Ferdinand

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

Ferdinand,

Have you elided some code here, perhaps? Looking at this, I spot the immediate problem that a) polymorphic_lhs is unallocated upon entry to the select type construct, and therefore has no type and b) If polymorphic_lhs is unallocated, unless you specify -assume realloc_lhs, or perhaps the standard semantics flag with -stand f0[38] ifort does not implement the reallocation on assignment semantics of f03 and f08; you need to explicitly enable them. Finally, I recall there being some issues around polymorphic intrinsic assignment or reallocation on polymorphic assignment. I believe the F08 standard does allow one or both of these, but not the F03 standard, and I am unsure of the current implementation status of this/these F08 features. So I think the answer to your first question is "yes, you have written non standard complying code as its listed above."

As an aside, I am curious what would be the result if you changed 'type is' to 'class is' in your type guard statement. At runtime, if polymorphic_lhs is unallocated there is no way to determine its dynamic type, but, in theory the compiler could know that it was declared as class(container_type). However, I suspect that it is in violation of the standard to pass an unallocated object to the type guard statement.

As to your second question, I think we need to see more of your code. You reference the fact that polymorphic_lhs is a PASSed dummy argument, which implies that there are type bound procedures (TBPs). Are you using the default/implicit passing behavior wherein the object to which the TBP is bound is implicitly passes as the first actual argument corresponding to the first dummy argument? Something like polymorphic_lhs%DoSomething(stuff)? If this is the case, then polymorphic_lhs must be allocated before you can call TBPs.

Any way I hope this is helpful and that I haven't made any errors or led you astray. If you give a more complete example we can likely help more.

Good luck

 

-Zaak

Something is definitely not right here - the SELECT TYPE should not be triggering a segfault. I see the same behavior in 13.1.3, however.

The program is correct as written. Since dynamic_lhs is not allocated, its "dynamic type" is the same as its "declared type", which is "container type". The TYPE IS guard statement would then be executed.  However, it's not even getting to the assignment. I'll send this off to the developers.

Steve - Intel Developer Support

Steve,

Thanks for correcting me regarding the type guard statement, forums like this are quite helpful for learning the new language features. I don't doubt your assertion about something being wrong, but can you comment on whether or not -assume realloc_lhs is needed in the OPs original example and if ifort 14.x supports polymorphic assignment and reallocation on assignment?

-Zaak

ifort 14.0 does not support assignment to a polymorphic variable, which is a F2008 feature. But in the scope of the TYPE IS block, polymorphic_lhs is not polymorphic!

"Within the block following a TYPE IS type guard statement, the associating entity (16.5.5) is not polymorphic (4.3.1.3), has the type named in the type guard statement, and has the type parameter values of the selector." (F2008, page 185, paragraph 5)

As best as I can tell, -assume realloc_lhs would not be required here. My tests show that the assignment (with a type, not class) works without it.

Steve - Intel Developer Support

Steve and Zaak, thank you for your analysis.

@Zaak:

Thanks for pointing out the missing allocation. Indeed I do not use 'realloc_lhs' with my original code, I simply forgot to copy the allocation statement into the example code (or to switch to standard semantics in the compile line). However, the described problem is not related to the allocation status. Even with allocated 'polymorphic_lhs', the runtime-error (invalid attempt to assign into a pointer that is not associated) appears.

To satisfy your curiosoty, I replaced the TYPE IS guard with a CLASS IS which triggers compiler error #8304: "In an intrinsic assignment statement, variable shall not be polymorphic". Makes sense but doesn't really answer your question about how and when the correct block of the SELECT TYPE is choosen in that situation, I guess. (However, I remember that with SELECT TYPE the F2003 standard outlines a short step-by-step algorithm where the general procedure is explained)

The second question is meant exactly as you understood it. Here is an example:

module m
    implicit none
 
    ! abstract
    type, abstract :: abstract_type
        integer :: nonempty
    end type
    type, extends(abstract_type) :: extended_type
    end type
 
    ! contains abstract allocatable
    type :: container_type
        integer :: nonempty
        ! fix: uncomment next line or use pointer instead
        class(abstract_type), allocatable :: abstract_field
    contains
        procedure :: do_something
    end type
 
contains
 
    subroutine do_something(this)
        class(container_type), intent(inout) :: this
        type(container_type) :: some_local_result
 
        ! Type-bound passed dummy is always polymorphic
        ! so we have to type cast it before assignment
        ! (even if the code doesn't use polymorphism at all)
        select type (this)
        type is (container_type)
            ! Intrinsic assignment. In actual code,
            ! the rhs is a function call this = do_stuff(...)
            this = some_local_result
        end select
    end subroutine
end module
 
program p
    use m
    implicit none
    type(container_type) :: my_container
 
    call my_container%do_something()     ! runtime error
end program

In the meantime I found a workaround, which consists in replacing the intrinsic assignment by a call to a (non-type bound) subprogram (copy-constructor) which does the assignment locally. This seems to do the job, but I am not entirely sure about how reliable it is.

Best regards
Ferdinand

Quote:

Steve Lionel (Intel) wrote:

ifort 14.0 does not support assignment to a polymorphic variable, which is a F2008 feature. But in the scope of the TYPE IS block, polymorphic_lhs is not polymorphic!

"Within the block following a TYPE IS type guard statement, the associating entity (16.5.5) is not polymorphic (4.3.1.3), has the type named in the type guard statement, and has the type parameter values of the selector." (F2008, page 185, paragraph 5)

As best as I can tell, -assume realloc_lhs would not be required here. My tests show that the assignment (with a type, not class) works without it.

Ah yes, I knew that about the type is type guard statement, after all that's the whole point of the construct! I guess I'm a bit of a derp today, sorry.

As to the point regarding -assume realloc_lhs, I can't see where polymorphic_lhs was previously allocated in the OP, my reading of the code as listed in the OP is that polymorphic_lhs is still an unallocated scalar inside the type is gaurd statement where it then appears in the lhs of an assignment. I did some testing with gfortran 4.8.2 and ifort 13.x.x and for a simple program listed below, even when turning on -assume realloc_lhs I get segfaults. If I then explicitly try to allocate polymorphic_lhs inside the type is block ifort says: 

error #8306: Associate name defined in ASSOCIATE or SELECT TYPE statements doesn't have ALLOCATABLE or POINTER attribute   [LHS]

	     allocate(myt :: lhs)

	---------------------^

even though lhs was declared as class(myt), allocatable. Gfortran gives a similar complaint: 

Error: Allocate-object at (1) is neither a data pointer nor an allocatable variable

This leads me to think that the selector of a select type construct cannot be (re)allocated within the construct, even if it is declared with the allocatable attribute.

Here is my test program:

program foo

	  implicit none

	  type :: myt

	     integer :: i

	  end type

	  type(myt) :: rhs

	  class(myt) ,allocatable :: lhs
  rhs = myt(5)

	  print*, rhs%i

	!  allocate(myt :: lhs) ! Correct place to allocate polymorphic lhs

	  select type(lhs)

	  type is (myt)

	     allocate(myt :: lhs) ! Doesn't segfault, but gives error

	     lhs = rhs

	  end select

	  

	  print*, rhs%i

	  !select type(lhs)

	  !type is (myt)

	     print*, lhs%i

	  !end select

	end program

compiled with ifort 13.x as: ifort -trace -assume realloc_lhs -warn foo.f90

@ Ferdinand

Can you make any guarantees about the allocation status of the PASSed dummy argument upon entry to the TBP subroutine? It is my understanding that you can only call a TBP if the object it is bound to is allocated, and, further, it seems that one cannot (re)allocate the selector of a select type construct from within it. Finally, the dummy argument does not have the allocatable attribute, so it MUST be allocated upon entry to the subroutine. I suspect that you are trying to do something with an unallocated polymorphic variable which at least the compiler doesn't like, if not the standard.

Edit: your revised code does not appear to suffer specifically from the issue I mentioned above, which is now crossed out, however your original code did. In the revised example, what is the declared and dynamic type of the rhs function result do_stuff(). If it is polymorphic perhaps that is the source of the issue, rather than the intrinsic assignment? FWIW your revised example program does not raise any compile time or runtime errors for me on ifort 13.0.2 on mac, so I think you've either removed the source of the problem or made a mistake in generating a smaller reproducer code.

Edit two: the strike through does not render on google chrome for OS X, I am referring to paragraph above the previous one. Also, as an aside, it is hard to enter code on chrome for os x on this form as extraneous line breaks are added.

-Zaak

ifort will do the reallocation on assignment, with or without -assume realloc_lhs. That is what the current standard says. The case where we won't do this by default is for allocatable arrays as they existed prior to F2003.

Steve - Intel Developer Support

First, @Zaak: thanks for your detailed investigations on that problem. In short:

  • do_stuff is not polymorphic, e.g. of type(container_type)
  • error is present in the program as stated in my second post, compiled with ifort 14.0.1 on unix (ubuntu 12.10)

Second, while trying to figure out some workarounds, I managed to reproduce some segfaults on the following lines:

! DERIVED TYPE (may or maynot be emtpy)
type t; end type
class(t), allocatable :: a
 
! EMPTY SELECT TYPE
select type (a)
type is (t)
end select
 
! OUTPUT: SIGSEGV, segmentation fault occurred
end

Compiled with similar options (or no options at all) on the same machine.

Furthermore, I find Zaak's observations and Steves comment regarding the implicit reallocation in a type guard statement very interesting. What I understand so far:
The associate-name (e.g. 'polymorphic_lhs' within the select type statement) doesn't have the ALLOCATABLE attribute, but the associated data object (e.g. the local variable 'polymorphic_lhs' outside the select type) has it. So you cannot use ALLOCATE. However, as Steve pointed out, you can change the allocation status through reallocation on assignment. As long you don't want to change the dynamic type, you can simply emulate ALLOCATE(x) via x = DERIVED_TYPE_CTOR(), and DEALLOCATE(x) or SOURCEd allocation via x = y, where y is either the source or an unallocated variable of similar dynamic type.

(Is that right? But why does the associate-name not have the ALLOCATABLE-attribute right from the beginning?)

Best regards

Ferdinand

Hmm - I admit I didn't consider the aspect that inside the TYPE IS the variable isn't allocatable. My test regarding the need for the switch or lack thereof didn't involve a TYPE IS. You're right - the selector is not allocatable so it needs to be allocated before the SELECT TYPE.

Steve - Intel Developer Support

I think it is implicit in the various posts above, but 16.5.1.6p2 in F2008 is worth a read.  "If the selector is allocatable it shall be allocated; the associate name is associated with the data object and does not have the allocatable attribute."

The original post and some of the subsequent snippets (#7, #9)  break this. 

This has caught me out before in ASSOCIATE constructs that refer to CHARACTER(:), ALLOCATABLE components.

TYPE some_type

	  CHARACTER(:), ALLOCATABLE :: name

	END TYPE some_type
TYPE(some_type) :: array(10)

	...  ! name component not allocated here.

	ASSOCIATE(name => array(i)%name)

	  name = 'This is bad Fortran.'

	END ASSOCIATE

(I have vague recollections that when I got this wrong the name component was previously allocated to some length, and for the life of me I couldn't understand why the compiler was doing length padding or truncation or whatever.)

I've lost track of the specific details over the years and versions, but my experience is that assignment of derived types containing polymorphic components is broken.  I wonder whether some examples (#6) trigger this.  I am reasonably sure that there are outstanding bug reports around this.  I think my current workaround is to "inline" the assignment to do it component by component, and use ALLOCATE(lhs%poly_comp, SOURCE=rhs%poly_comp) for any polymorphic components.  Consumption of a mix of cabernet sauvignon and merlot while coding the workaround also seems to help.

Quote:

IanH wrote:

I think it is implicit in the various posts above, but 16.5.1.6p2 in F2008 is worth a read.  "If the selector is allocatable it shall be allocated; the associate name is associated with the data object and does not have the allocatable attribute."

[snip]

I've lost track of the specific details over the years and versions, but my experience is that assignment of derived types containing polymorphic components is broken.  I wonder whether some examples (#6) trigger this.  I am reasonably sure that there are outstanding bug reports around this.  I think my current workaround is to "inline" the assignment to do it component by component, and use ALLOCATE(lhs%poly_comp, SOURCE=rhs%poly_comp) for any polymorphic components.  Consumption of a mix of cabernet sauvignon and merlot while coding the workaround also seems to help.

Ian,

Thanks for citing the standard to back up my initial suspicion. (Albeit, my reasoning about WHY passing an unallocated variable as a selector to a select type construct was flawed, as Steve pointed out.)

Looking at this, I spot the immediate problem that a) polymorphic_lhs is unallocated upon entry to the select type construct, and therefore has no type

Ian, when you say that you "inline" the assignment, does that mean that you have public components in your objects and that you assign the subobjects one by one, or do you write defined assignments to achieve this?

-Zaak

Thanks for the chapter 16 catch, Ian. I had looked up "dynamic type" but had not thought to also check construct association!

Therefore, the program is invalid. I would expect -check pointer to give a meaningful error here, but it doesn't. I've asked that it do so.

Steve - Intel Developer Support

While this discussion focusses on the allocation status of 'polymorphic_lhs', from which I actually learned a highly valuable lesson about the implications of the new fortran standard, I just want to clarify that the reported runtime-error is still an issue. As I admitted earlier (#6), I merely forgot to copy the

allocate(polymorphic_lhs)
prior to SELECT TYPE (line 19) into my example code (sorry for that), but the error persists even with correct allocation.

Ok, now I understand and I can reproduce what you're seeing. Thanks.

Steve - Intel Developer Support

Quote:

Izaak Beekman wrote:

Ian, when you say that you "inline" the assignment, does that mean that you have public components in your objects and that you assign the subobjects one by one, or do you write defined assignments to achieve this?

I see I've used both.  I've hit this issue typically dealing with container types (to allow "arrays" with elements of different dynamic type), so normally assignment is limited to one or two subroutines associated with changing the size of container array objects (and those subroutines are in the same module as the type definition).  In other cases I've used defined assignment (where the defined assignment procedure emulates what should happen with intrinsic assignment, but using ALLOCATE(lhs%comp, SOURCE=rhs%comp) for polymorphic components (this also ties in a little with the discussion about ALLOCATE(...SOURCE=xxx) being "harmful"). 

The defined assignment approach seems more robust and easier to unwind once intrinsic assignment behaviour is reliable - not sure why I haven't always done it.  One benefit of not using defined assignment in the context of changing the size of a container array is that you can use MOVE_ALLOC to move the component across - there's no point copying the object out of the original array as the original array is going to go away anyway.

Note that things have evolved over the years with different compiler versions - a workaround appropriate for 12.0 may not be relevant for 14.0.  I'm also guessing here at the nature of the underlying problem, but rather tellingly, if I change Ferdinand's program in #6 to:

module m

	    implicit none

	 

	    ! abstract

	    type, abstract :: abstract_type

	        integer :: nonempty

	    end type

	    type, extends(abstract_type) :: extended_type

	    end type

	 

	    ! contains abstract allocatable

	    type :: container_type

	        integer :: nonempty

	        ! fix: uncomment next line or use pointer instead

	        class(abstract_type), allocatable :: abstract_field

	    contains

	        procedure :: do_something

	    end type

	 

	contains

	 

	    subroutine do_something(this)

	        class(container_type), intent(inout) :: this

	        type(container_type) :: some_local_result
        ! Define the local result, otherwise program is non-conforming.

	        some_local_result%nonempty = 0

	        ! Either leave unallocated (object is still defined), or allocate

	        ! to:

	        !ALLOCATE(extended_type:: some_local_result%abstract_field)

	        

	        ! Type-bound passed dummy is always polymorphic

	        ! so we have to type cast it before assignment

	        ! (even if the code doesn't use polymorphism at all)

	        select type (this)

	        type is (container_type)

	            ! Unrolled imitation of intrinsic assignment.

	            this%nonempty = some_local_result%nonempty

	            IF (ALLOCATED(some_local_result%abstract_field))  &

	                ALLOCATE( this%abstract_field,  &

	                  SOURCE=some_local_result%abstract_field )

	        end select

	    end subroutine

	end module

	 

	program p

	    use m

	    implicit none

	    type(container_type) :: my_container

	 

	    call my_container%do_something()     ! runtime error

	    

	    IF (allocated(my_container%abstract_field)) THEN

	      SELECT TYPE (chicken => my_container%abstract_field)

	      TYPE IS (extended_type)

	        PRINT "('extended_type!')"

	      CLASS DEFAULT

	        PRINT "('rather confused!')"

	      end select

	    ELSE

	      PRINT "('not allocated!')"

	    END IF

	end program

then everything appears to be working fine with 14.0.1.

 

This has been fixed for a release later this year.

Steve - Intel Developer Support

Steve,

To confirm, is it the run-time error alluded to in the original post and the compact reproducer in Quote #9 for a segmentation fault that gets fixed in an upcoming release later this year?

As I read through the above comments and take into account the corrections mentioned (e.g., Quote #14), the following appears to be a complete test case which causes a run-time error in the latest Intel Fortran compiler version.  I assume this code will execute without errors following the fix?  Fyi, the code below works as expected with gfortran GCC 5.0 development trunk.

program p

   implicit none

   integer :: istat

   ! abstract
   type, abstract :: abstract_type
      integer :: nonempty
   end type

   ! contains abstract allocatable
   type :: container_type
      integer :: nonempty
      ! fix: uncomment next line or use pointer instead
      class(abstract_type), allocatable :: abstract_field
   end type

   type, extends(abstract_type) :: extended_type
   end type

   type(container_type) :: rhs
   class(container_type), allocatable :: polymorphic_lhs

   !.. Ensure rhs is allocated and initialized
   allocate( extended_type :: rhs%abstract_field, stat=istat)
   if (istat /= 0) then
      print *, " error allocating rhs%abstract_field."
      stop
   end if
   rhs%nonempty = 2
   select type (tmp => rhs%abstract_field)
      type is (extended_type)
         tmp%nonempty = 1
      class default
   end select

   allocate(polymorphic_lhs, stat=istat)
   if (istat /= 0) then
      print *, " error allocating polymorphic_lhs."
      stop
   end if

   ! assign into polymorphic
   select type (polymorphic_lhs)

      type is (container_type)

         ! runtime error (ifort 14.0.1)
         ! problem with 'abstract_field' allocatable component
         ! (no matter it's allocation status in lhs and rhs)
         polymorphic_lhs = rhs

      class default

   end select

   ! expect output is T
   print *, " lhs%abstract_field allocated? ", allocated(polymorphic_lhs%abstract_field)

   stop

end program p

Upon execution on Windows OS with Intel Fortran compiler 2015, Update 2:

forrtl: severe (122): invalid attempt to assign into a pointer that is not assoc
iated
Image              PC                Routine            Line        Source

p64.exe            000000013FAB45A1  Unknown               Unknown  Unknown
p64.exe            000000013FAB1562  MAIN__                     51  p.f90
p64.exe            000000013FAF79FE  Unknown               Unknown  Unknown
p64.exe            000000013FAF7CD4  Unknown               Unknown  Unknown
kernel32.dll       00000000767759ED  Unknown               Unknown  Unknown
ntdll.dll          0000000076D8BA01  Unknown               Unknown  Unknown
Press any key to continue . . .

 

Yes, this test works in the new version. It won't work in the initial beta but should in any updates after that.

Steve - Intel Developer Support

Thanks, Steve.  If it is of any help, the only relevant compiler option I used was standard-semantics; the rest were for warnings (-stand:f08) or run-time error checks (-trace -check:all) i.e., their equivalent for Windows OS.

Leave a Comment

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