- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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]
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I meant defined assignment, not generic.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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,
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
fan-string2.f90 now works in the version 16 compiler, as long as standard-semantics is enabled.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page