When a derived type has allocatable components, is it necessary to have a final subroutine check and deallocate each such component, in the general case?
module string_utils
implicit none
private
type string
character(len=:), allocatable :: s
contains
final :: destroy_string
end type string
public :: string
contains
pure elemental subroutine destroy_string(self)
type(string), intent(inout) :: self
if (allocated(self%s)) deallocate(self%s)
end subroutine destroy_string
end module string_utils
No, I don’t think this is necessary. when the variable goes out of scope, any allocatable components are supposed to be deallocated. Finalizers are good for deallocating pointers (to prevent memory leaks) and some other things like closing files, etc.
Ok, I knew that Fortran will deallocate any variables when they go out of scope, but don’t use pointers very often. I must have confused myself with the allocatable components vs pointer components piece then. I know there is some non-intuititve (at least to me) relationship there… pointer variables can be assigned to target or allocatable variables, but allocatable cannot be assigned to target, is that correct?
Do you have any proof that this is the case ?
If a derived type is deallocated or goes out of scope, are the allocatable components deallocated ?
Do you need to provide a finalizer to confirm this ?
To be safe, my use of allocatable components has always been in a derived type that is allocated in a module, so that scope is not an issue.
In section 9.7.3.2 of the f2023 draft, it states, “When a variable of derived type is deallocated, any allocated allocatable subobject is deallocated.” I’m pretty sure this has been the case since f2003, I’ve used this feature many times since then and it has always worked on all the f2003+ compilers I’ve used. Of course, before that, the functionality of allocatables was extremely limited (they were not allowed to be dummy arguments, or components of a derived type, and so on). For example, when you implement a linked list or a tree with allocatables, you don’t really need to write the code that traverses the structure and deallocates the members one by one, you can just deallocate the head, and the compiler does the rest. The same thing happens if that structure is associated with an intent(out) dummy argument – the compiler deletes the whole structure and deallocates everything for you. You cannot have a memory leak when you use alloctables.
In contrast to a pointer, you cannot choose to which object in memory an allocatable “points”. The object is created and pointed at the same time (with allocate or with allocation upon assignment), and it stays like this until the object is destroyed (with deallocate or when it goes out of scope).
The closest C++ concept is std::unique_ptr , I believe.
I usually add a final procedure (or call the destroy function myself,) especially when that derived type was defined in the main program. When I use valgrind to check for issues, I prefer to see this
==29615== All heap blocks were freed -- no leaks are possible
instead of this
==29573== LEAK SUMMARY:
==29573== definitely lost: 0 bytes in 0 blocks
==29573== indirectly lost: 0 bytes in 0 blocks
==29573== possibly lost: 0 bytes in 0 blocks
==29573== still reachable: 1,448 bytes in 30 blocks
==29573== suppressed: 0 bytes in 0 blocks
Non-zero “still reachable” is harmless, though. It’s just my personal preference to get rid of it - whenever that’s possible. And it is, unless you use interoperability with C libraries. In that case you probably won’t avoid it.
A pointer can point to an allocatable entity, but only if it has the target attribute. In recursive structures like trees and linked lists, that target attribute can be inherited, but it still must be there, explicitly, in its declaration. Otherwise, an allocatable is not automatically a target (in the same way that a pointer is automatically a target). There are also some tricky situations where a dummy argument can have a local target attribute, while its actual argument is not a target; in that case, there are restrictions on the programmer regarding pointer assignments to that target, and their scope.
There is currently no way to move a target of a pointer to an allocatable or to otherwise give the target the attributes of an allocatable entity. I think it would be useful if the move_alloc() intrinsic were extended to allow this. It would allow efficient shallow copies from pointer targets to allocatable entities. This could work without much effort on the compiler because allocatable entities are effectively “tame” pointers.