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

Allocatable Scalar Derived Types and Generic Assignment - having trouble setting them up.

FortranFan
Honored Contributor III
2,062 Views

Is the following code valid per the latest standard?  The compiler does not give any errors, but the program encounters run-time exception when the allocatable scalar derived type is constructed using an assignment at line 180.  Sourced allocation that makes use of the same assignment - see line 161 - seems to work ok.  I'm trying to figure out whether my problem lies in the finalizer or the assignment routine.

Thanks much,

 

[fortran]

   MODULE TestMod

  

      !..

      IMPLICIT NONE

     

      !..

      PRIVATE

     

      !.. Public items

      PUBLIC :: ASSIGNMENT(=)

      PUBLIC :: NewStringClass

 

      !.. Character Type

      TYPE, PUBLIC :: MyStringClass

         PRIVATE

         CHARACTER(LEN=:), ALLOCATABLE :: m_MyString

      CONTAINS

         PRIVATE

         FINAL :: CleanStringClass

         PROCEDURE, PASS(This) :: Clear => ClearAllocatables

         PROCEDURE, PASS(This) :: Init => InitStringClass

         PROCEDURE, PASS(This), PUBLIC :: GetString

         PROCEDURE, PASS(This), PUBLIC :: SetString

      END TYPE MyStringClass

 

      INTERFACE ASSIGNMENT(=)

         MODULE PROCEDURE StringClassAssign

      END INTERFACE

                 

   CONTAINS

 

      !.. Initialize

      PURE ELEMENTAL SUBROUTINE InitStringClass(This)

     

         !.. Argument list

         CLASS(MyStringClass), INTENT(INOUT) :: This

 

         CALL This%Clear()

              

         RETURN

     

      END SUBROUTINE InitStringClass  

 

      !.. Get the value

      PURE ELEMENTAL FUNCTION GetString(This) RESULT(Value)

     

         !.. Argument list

         CLASS(MyStringClass), INTENT(IN) :: This

         !.. FUNCTION RESULT

         CHARACTER(LEN=:), ALLOCATABLE    :: Value

 

         IF (ALLOCATED(This%m_MyString)) THEN

            Value = This%m_MyString

         ELSE

            Value = "ERROR: String not allocated."

         END IF

              

         RETURN

     

      END FUNCTION GetString  

 

      !.. Set the value string class

      PURE ELEMENTAL SUBROUTINE SetString(This, Value)

     

         !.. Argument list

         CLASS(MyStringClass), INTENT(INOUT) :: This

         CHARACTER(LEN=*), INTENT(IN)        :: Value

        

         IF (LEN(Value) > 0) THEN

            This%m_MyString = Value

         END IF

              

         RETURN

     

      END SUBROUTINE SetString  

 

      !.. Finalize Class

      PURE ELEMENTAL SUBROUTINE CleanStringClass(This)

     

         !.. Argument list

         TYPE(MyStringClass), INTENT(INOUT) :: This

 

         CALL This%Clear()

        

         RETURN

            

      END SUBROUTINE CleanStringClass  

 

      !.. Clear Allocatables

      PURE ELEMENTAL SUBROUTINE ClearAllocatables(This)

     

         !.. Argument list

         CLASS(MyStringClass), INTENT(INOUT) :: This

 

         !.. Local variables

         INTEGER :: Istat

 

         IF (ALLOCATED(This%m_MyString)) THEN                 

            DEALLOCATE(This%m_MyString, STAT=IStat)

            IF (IStat /= 0) THEN

               !.. Insert some error handling here

            END IF

         END IF

        

         RETURN

            

      END SUBROUTINE ClearAllocatables  

 

      !.. Assignment

      PURE ELEMENTAL SUBROUTINE StringClassAssign(Lhs, Rhs)

     

         !.. Argument list

         CLASS(MyStringClass), INTENT(OUT) :: Lhs

         CLASS(MyStringClass), INTENT(IN)  :: Rhs

 

         CALL Lhs%Init()

         IF (ALLOCATED(Rhs%m_MyString)) THEN

            Lhs%m_MyString = Rhs%m_MyString

         END IF

              

         RETURN

     

      END SUBROUTINE StringClassAssign

     

      !.. Constructor function

      PURE ELEMENTAL FUNCTION NewStringClass() RESULT(NewObj)

       

         !.. Function result: new class

         TYPE(MyStringClass) :: NewObj

        

         CALL NewObj%Init()    

 

      END FUNCTION NewStringClass   

 

   END MODULE TestMod

 

   PROGRAM TestFor

 

      USE TestMod, ONLY : MyStringClass, NewStringClass, ASSIGNMENT(=)

 

      IMPLICIT NONE

 

      !..

      TYPE(MyStringClass) :: MyClass1

      TYPE(MyStringClass), ALLOCATABLE :: MyClass2

      INTEGER :: Istat

 

      !..

      WRITE(6,*) " --- Test program --- "

     

      !.. Try class 1

      CALL MyClass1%Init()

      CALL MyClass1%SetString( "Test" )

      WRITE(6,*) " Class1 String: ", MyClass1%GetString()

      

      !.. Reset

      MyClass1 = NewStringClass()

      WRITE(6,*) " Class1 String: ", MyClass1%GetString()

     

      !.. Try class 2, method 1

      ALLOCATE(MyClass2, SOURCE=NewStringClass(), STAT=Istat)  ! <-- This seems to work. 

      IF (IStat == 0) THEN

         CALL MyClass2%SetString( "Hello World!" )

         WRITE(6,*) " Class2 String: ", MyClass2%GetString()

      ELSE

         WRITE(6,*) " Class2 Allocation failed.  STAT = ", Istat

         STOP

      END IF

     

      !.. Clean class 2

      IF (ALLOCATED(MyClass2)) THEN

         DEALLOCATE(MyClass2, STAT=Istat)

         IF (Istat /= 0) THEN

            WRITE(6,*) " Class2 Allocation failed.  STAT = ", Istat

            STOP

         END IF

      END IF

     

      !.. Try class 2, method 2

      MyClass2 = NewStringClass()                              ! <-- This does NOT work, why??

      CALL MyClass2%SetString( "Hello World!" )

      WRITE(6,*) " Class2 String: ", MyClass2%GetString()

     

      !..

      STOP

 

   END PROGRAM TestFor

[/fortran]

 

[plain]

  --- Test program ---

  Class1 String: Test

  Class1 String: ERROR: String not allocated.

  Class2 String: Hello World!

forrtl: severe (157): Program Exception - access violation

Image              PC                Routine            Line        Source

 

TestFor64.exe      000000013FBB1395  TESTMOD_mp_CLEARA          90  TestMod.f90

TestFor64.exe      000000013FBB145D  TESTMOD_mp_STRING         116  TestMod.f90

TestFor64.exe      000000013FBB20CC  MAIN__                     44  TestFor.f90

TestFor64.exe      000000013FC06076  Unknown               Unknown  Unknown

TestFor64.exe      000000013FBF03DF  Unknown               Unknown  Unknown

kernel32.dll       00000000774A652D  Unknown               Unknown  Unknown

ntdll.dll          0000000077B9C541  Unknown               Unknown  Unknown

 

[/plain]

0 Kudos
10 Replies
FortranFan
Honored Contributor III
2,062 Views

I meant defined assignment, not generic.

 

 

0 Kudos
IanH
Honored Contributor III
2,062 Views

You have defined assignment for your type.  Defined assignment doesn't result in reallocation on assignment of the top level variable.  Effectively StringClassAssign gets called with an unallocated Lhs.

0 Kudos
FortranFan
Honored Contributor III
2,062 Views

Ian,

I thought as much, so I figured I'll give the following a try.  But it didn't work either.

[fortran]

      !.. Assignment

      PURE ELEMENTAL SUBROUTINE StringClassAssign(Lhs, Rhs)

     

         !.. Argument list

         CLASS(MyStringClass), INTENT(OUT), ALLOCATABLE :: Lhs

         CLASS(MyStringClass), INTENT(IN)  :: Rhs

 

         !.. Local variables

         INTEGER :: Istat

        

         ALLOCATE(Lhs, SOURCE=Rhs, STAT=Istat)

         IF (Istat /= 0) THEN

            !.. Insert some error handling here

         END IF

              

         RETURN

     

      END SUBROUTINE StringClassAssign

     

[/fortran]

0 Kudos
IanH
Honored Contributor III
2,062 Views

Well, that shouldn't compile - it violates F2008 C1289 (thou shall not have allocatable dummy arguments in an elemental procedure).  I have vague recollections of that coming up here before (but then again, all my recollections are vague, and often just plain wrong). 

What do you need the defined assignment for?

(If you are going to have defined assignment, best to make it type bound, like your other bindings.)

0 Kudos
FortranFan
Honored Contributor III
2,062 Views

Oh, I didn't need the elemental attribute - I was only trying things out in a hurry and had that setting from previous code.  However, the compile indeed does not flag any error - if Steve or anyone else at Intel wants to take a look and see if it merits opening an incident.

My eventual goal for this defined assignment is for application in a callable library that will be used by developers who mainly program in other  OO-based languages, not Fortran, and whose first baby steps to instantiate a "class" would have been:

[csharp]

obj = new SomeClass( "{ class constructor parameters}" )

[/csharp]

0 Kudos
IanH
Honored Contributor III
2,062 Views

Your defined assignment is just doing what intrinsic assignment would do, but with the limitation that the defined assignment either needs to be for an allocatable variable (the thing on the left hand side of the =) or a non-allocatable - it cannot do both. 

(This is the same sort of [unfortunate] issue arising as in the IOMSG discussion - you cannot "overload" generic interfaces on the basis of the allocatable attribute alone (in F2008 you can differentiate between allocatable and pointer, but not allocatable and not-allocatable).  I don't see why the language rules, in future, couldn't be changed though.)

Defined assignment here might be useful to do something like take a right hand side character variable and assign it to your MyString type (or vice-versa).  But again, you either support left hand side allocatable or not-allocatable.

Some of your other procedures also simply reproduce what happens automatically if the argument is INTENT(OUT), or deallocated, or whatever.  For example - there's no need for your type, as presented, to have a finalizer.

You might be interested in the possibility of overloading the compiler provided structure constructor.  That lets you do something like:

[fortran]TYPE(MyStringClass) :: s

s = MyStringClass(whatever...)[/fortran]

In the absence of defined assignment for MyStringClass to MyStringClass, that works whether s is allocatable or not.  That should start looking pretty natural for your other OO language friends.

Further examples attached.  If Dr Fortran hasn't fallen asleep while reading this... there's an example of a ICE in here with 14.0.0 and /check:all /warn:all /standard-semantics too (uncomment the #ICE lines).

(Why the "#6931: Fortran 2003 does not allow this assignment statement" warnings if /stand is present?)

0 Kudos
Steven_L_Intel1
Employee
2,062 Views

Three cups of coffee later and I'm awake again. I will look into the issues raised here regarding the ICE and lack of an error when there should be one.

0 Kudos
Steven_L_Intel1
Employee
2,062 Views

Ian, you previously reported the missing error, which is undoubtedly why it sounds familiar - it's being worked on.

Standards warning was also previously reported and is being worked.

ICE escalated as issue DPD200248888.

0 Kudos
FortranFan
Honored Contributor III
2,062 Views

Thanks much for the explanation and sample code, Ian.  So I'll do away with the idea of "defined assignment" for allocatable types for good and stick with structor constructors as you show [even if the future language standards were to provide more flexibility with allocatable types, I don't expect to find a compiler that supports that aspect during my lifetime :-) ].

Some of your other procedures also simply reproduce what happens automatically if the argument is INTENT(OUT), or deallocated, or whatever.  For example - there's no need for your type, as presented, to have a finalizer.

Yes, your comments duly noted.  The above example was a version that I stripped down quickly from another, more complicated derived type and I didn't fully get rid of all the extraneous stuff: for this other derived type, it had made sense for us to have "custom" procedures to override some intrinsic aspects as well as a finalizer.

Regards,

 

0 Kudos
Steven_L_Intel1
Employee
2,062 Views

fan-string2.f90 now works in the version 16 compiler, as long as standard-semantics is enabled.

0 Kudos
Reply