In this minimal example, is it allowed to pass the optional dummy argument y
of test_wrapper
that may not be present
as actual argument for the corresponding optional dummy argument y
of test
?
program main
implicit none
real :: x = 5.0
call test_wrapper(x)
contains
subroutine test_wrapper(x, y)
implicit none
real, intent(in) :: x
real, dimension(:), intent(out), optional :: y
call test(x, y)
end subroutine test_wrapper
subroutine test(x, y)
implicit none
real, intent(in) :: x
real, dimension(:), intent(out), optional :: y
if (present(y)) then
y = x
end if
end subroutine test
end program
UndefinedBehaviourSanitizer raises an error, indicating that it is not: https://godbolt.org/z/nKj1h6G9r
In this Fortran standards document (Section 15.5.2.12, "Argument presence and restrictions on arguments not present" on page 311) it says:
- An optional dummy argument that is not present is subject to the following restrictions.
- If it is a data object, it shall not be referenced or be defined. If it is of a type that has default initialization, the initialization has no effect.
- [...]
- [...]
- [...]
- A designator with it as the base object and with one or more subobject selectors shall not be supplied as an actual argument.
- [...]
- If it is a pointer, it shall not be allocated, deallocated, nullified, pointerassigned, or supplied as an actual argument corresponding to an optional nonpointer dummy argument.
- If it is allocatable, it shall not be allocated, deallocated, or supplied as an actual argument corresponding to an optional nonallocatable dummy argument.
- [...]
- Except as noted in the list above, it may be supplied as an actual argument corresponding to an optional dummy argument, which is then also considered not to be present.
I'm struggling to read the standardese in that list, so perhaps one of the items in it that I don't fully understand prohibits this for assumed-shape arrays? But to my mind, none of the restrictions would apply for this case.
But interestingly, UBSan only seems to raise the error if using dimension(:)
, i.e. if y
is an assumed-shape array. Anything else like dimension(2)
, dimension(n)
with an added size parameter n
, allocatable
, pointer
or nothing do not seem to trigger UBSan.
There is no additional restriction on the use of assumed-shape absent optional dummy arguments. It is allowed to have a not-present assumed-shape array argument as an actual argument for an optional dummy argument in another procedure unless another restriction prevents it. (That subsequent dummy argument will be treated as not present.)
As noted, none of the restrictions listed mentions "assumed shape". In particular, none of those you quote (as Ian Bush comments) applies in this case. Which leaves "except as noted in the list above, it may be supplied..." being permissive.
If you want to check further, the assumed-shape argument y
of each subroutine is an ordinary dummy variable (and subject to the rules of F2018 15.5.2.4).
gfortran 7 does not complain. It may be relevant that this version does not understand -std=f2018
.
After reporting the issue on the GCC bug tracker it has been confirmed that this was indeed a bug in gfortran that UBSan was correct to reveal. A patch that fixes the issue was submitted in the development of GCC 14 and backported for the GCC 13.3 release.
For completeness, let's run through why the restrictions (all, not just the ones quoted in the question) don't apply. I won't quote the restrictions, so the curious will need to look up the text of those which aren't in the question.
y
is referenced or defined when not present (appearing as an actual argument isn't referencing or defining).y
appears in a pointer assignment (and neither is a pointer).y
is a procedure or procedure pointer.y
of test_wrapper
is not used as an actual argument for a non-optional dummy argument; y
of test
is not used as an actual argument.test_wrapper
the actual argument is y
itself, not a subobject of y
; y
in test
is not used as an actual argument.y
is used an actual argument in reference to an elemental procedure.y
is a pointer.y
is allocatable.y
has a length type parameter (and especially not one inquired of).y
is used as a selector.y
is used in a procedure designator.y
is used in a procedure component reference.Versions of gfortran before 13.3 (prior to the patch) show the same issues for the simpler program below:
program main
implicit none
call test_wrapper
contains
subroutine test_wrapper(y)
real, dimension(1), intent(out), optional :: y
call test(y)
end subroutine test_wrapper
subroutine test(y)
real, dimension(:), intent(out), optional :: y
if (present(y)) y=0 ! Used to silence unrelated warning
end subroutine test
end program