5

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:

  1. An optional dummy argument that is not present is subject to the following restrictions.
    1. 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.
    2. [...]
    3. [...]
    4. [...]
    5. A designator with it as the base object and with one or more subobject selectors shall not be supplied as an actual argument.
    6. [...]
    7. 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.
    8. If it is allocatable, it shall not be allocated, deallocated, or supplied as an actual argument corresponding to an optional nonallocatable dummy argument.
    9. [...]
  2. 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.

mjacobse
  • 365
  • 1
  • 12
  • 1
    I don't count myself as fluent in standardese, so I'll leave an answer to those who do, but I would think point 4 fairly clearly says that what you have done is OK - 3.5, 3.7 and 3.8 aren't relevant to this case. – Ian Bush Jun 19 '21 at 11:10

1 Answers1

5

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 arguments y of each subroutine is an ordinary dummy variable (and subject to the rules of 15.5.2.4).

gfortran 7 does not complain. It may be relevant that this version does not understand -std=f2018.

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.

  1. Neither y is referenced or defined when not present (appearing as an actual argument isn't referencing or defining).
  2. Neither y appears in a pointer assignment (and neither is a pointer).
  3. Neither y is a procedure or procedure pointer.
  4. 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.
  5. In test_wrapper the actual argument is y itself, not a subobject of y; y in test is not used as an actual argument.
  6. Although arrays, neither y is used an actual argument in reference to an elemental procedure.
  7. Neither y is a pointer.
  8. Neither y is allocatable.
  9. Neither y has a length type parameter (and especially not one inquired of).
  10. Neither y is used as a selector.
  11. Neither y is used in a procedure designator.
  12. Neither y is used in a procedure component reference.

The simpler program below shows the same issues:

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
francescalus
  • 30,576
  • 16
  • 61
  • 96
  • Thanks for the confirmation and catching that gfortran 7 does not complain. The behavior seems independent of the `-std` flag for all combinations I tried. But indeed curious that the error starts appearing with the same version that introduced `-std=f2018`. I suppose I will file a bug report to gfortran/UBSan. – mjacobse Jun 19 '21 at 11:51
  • 1
    I did have a quick look at reported bugs but didn't find one, so thanks for reporting. Please feel free to add a reference here. There are similar problems in questions here, although I didn't see one looking at this particular case. – francescalus Jun 19 '21 at 11:54
  • Thanks for the simplified version, I used that for the [bug report](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101135). I also noticed that in my example, `-fwrapv` somehow fixes the issue for gfortran versions 10.1 and earlier. For version 10.2 and later it shows again though. If I change `dimension(:)` to `dimension(1)` as in your example, `-fwrapv` does not do anything anymore and the issue is there again for all gcc versions 8.1.0 and later. Very strange. – mjacobse Jun 19 '21 at 13:00
  • 1
    (Working) Compilers are hard things to write. Thanks again for reporting: please do feel free to [edit] in (either now or when there's a suitable response) a reference. If you edit you get credit (reputation, but more importantly in the revision history). gfortran 8 was when there was a change to the array descriptor to support rank-15 arrays, so this may not be a coincidence. Also, `-fwrapv` may change how arithmetic on the dope vector works. (I'm not sure about gfortran internals at all.) – francescalus Jun 19 '21 at 13:34