The feature that makes an unassociated/unallocated variable interpreted as a non present optional argument looks nice (and I have used it on purpose in the past), but recently I encountered a case where it was misleading. Like this:
real, pointer :: a(:)
call foo(a)
contains
subroutine foo(x)
real, intent(in), optional :: x(:)
if (present(x)) then
! do something
else
! do something else
end if
end subroutine
end
I forgot to associate the pointer a(:) before calling foo(a), and as a result the subroutine was silently doing “something else” instead of the expected “something”… It took me a while to detect the problem, because the code is 100% valid…
Note that replacing the routine with 2 variants (one without the dummy argument and one with the mandatory dummy argument instead of optional) under a generic interface makes the code illegal if a(:) is unassociated/unallocated (and it can be trapped with a runtime check).
Hmm… if the actual argument is not allocated or not associated, then the subroutine cannot really do anything valid with it, can it?
If you explicitly state that the dummy argument is a pointer, that’s a different thing:
real, pointer :: a(:)
call foo(a)
contains
subroutine foo(x)
real, intent(in), pointer, optional :: x(:)
if (present(x)) then
! do something
print*,'pointer is present'
else
! do something else
end if
end subroutine
end
$ ifort -diag-disable=10448 optional_ptr.f90 && ./a.out
pointer is present
Even with CONTAINed procedures, it sometimes helps to think of them as being separate or provided by a third-party —i.e., it’s not the subroutine’s fault that you forgot to associate a(:).
I think the issue is that some unintended behavior that’s beyond the reach of the standard, could really benefit from a language server (or a compiler’s --syntax flag in pedantic mode).
The list could include:
Passed non-associated/unallocated to non-pointer/non-allocatable optional dummy argument.
Reallocated derived type array instance with (now dangling) pointer components.
This is exploring some of the dark corners of the language. In your posted code, I think the behavior is undefined because the actual argument is in an undefined state. Changing the code to
real, pointer :: a(:) => null()
or adding
nullify(a)
will make it conforming. Of course, your actual code might not have this issue, so maybe this has nothing to do with solving your actual problem.
This is yet another dark corner of the language. The combination of intent(in), pointer is allowed for a dummy argument even if the actual argument is not a pointer. It is a shortcut for declaring the dummy argument as target, declaring a local pointer, and assigning that local pointer to the target.
However, I’m unsure what, if anything, the addition of the optional attribute does in this special case. Presumably the programmer can test if present() and pointer assign x(:) to something else? I might even say this is an obscure part of a dark corner.
Yes, I agree with this. It is like implicit save. The semantics of these dark corners of the language is difficult to keep straight. They add only a little functionality to the language (or none at all), but they cause problems when they are used intentionally in an incorrect way or when they are stumbled upon unintentionally. To be honest, I’m not 100% sure that what I said above is correct, and it would take me an hour or so to look through the standard to check all of the cross references to make sure. So let me just say I think what I said above is right, and hopefully someone will correct me if I’m wrong.