Lower bound of assumed shape array with CLASS(*) and nvfortran

Take the following example and the latest nvfortran compiler version 24.7:

PROGRAM main
    IMPLICIT NONE

    REAL, ALLOCATABLE :: array(:)

    ALLOCATE(array(1000))
    CALL test_class(array)
    CALL test_real(array)
    CALL test_class(array(100:200))
    CALL test_real(array(100:200))
CONTAINS
    SUBROUTINE test_class(arg)
        ! Subroutine arguments
        CLASS(*), INTENT(in) :: arg(:)

        SELECT TYPE (arg)
        TYPE IS (REAL)
            WRITE(*,*) "Lower bound of CLASS(*) arg: ", LBOUND(arg)
        CLASS DEFAULT
            WRITE(*, *) "Not REAL"
        END SELECT
    END SUBROUTINE test_class

    SUBROUTINE test_real(arg)
        ! Subroutine arguments
        REAL, INTENT(in) :: arg(:)

        WRITE(*,*) "Lower bound of REAL arg:     ", LBOUND(arg)
    END SUBROUTINE test_real
END PROGRAM main

All compilers I tried (gfortran, ifort, ifx, nagfor) besides nvfortran works as expected and give the same result:

 Lower bound of CLASS(*) arg:  1
 Lower bound of REAL arg:      1
 Lower bound of CLASS(*) arg:  1
 Lower bound of REAL arg:      1

With nvfortran there seems to be a bug when a slice is passed in the CLASS(*) argument of test_class:

 Lower bound of CLASS(*) arg:             1
 Lower bound of REAL arg:                 1
 Lower bound of CLASS(*) arg:             0
 Lower bound of REAL arg:                 1

I tried to manually specify the lower bound in the dummy argument specification (CLASS(*), INTENT(in) :: arg(1:)) but that did not work, nothing changed.

Another variant of the same problem can be seen with an adapted example:

PROGRAM main
    IMPLICIT NONE

    REAL, ALLOCATABLE :: array(:)

    ALLOCATE(array(1000))
    CALL test_class(array)
    CALL test_real(array)
    CALL test_class(array(100:200))
    CALL test_real(array(100:200))
CONTAINS
    SUBROUTINE test_class(arg)
        ! Subroutine arguments
        CLASS(*), INTENT(in) :: arg(2:)    ! Differ from original example here

        SELECT TYPE (arg)
        TYPE IS (REAL)
            WRITE(*,*) "Lower bound of CLASS(*) arg: ", LBOUND(arg)
        CLASS DEFAULT
            WRITE(*, *) "Not REAL"
        END SELECT
    END SUBROUTINE test_class

    SUBROUTINE test_real(arg)
        ! Subroutine arguments
        REAL, INTENT(in) :: arg(2:)    ! Differ from original example here

        WRITE(*,*) "Lower bound of REAL arg:     ", LBOUND(arg)
    END SUBROUTINE test_real
END PROGRAM main

This example should give all 2 as lower bound (all working compilers mentioned above does indeed give that) as lower bound, but nvfortran gives:

 Lower bound of CLASS(*) arg:             1
 Lower bound of REAL arg:                 2
 Lower bound of CLASS(*) arg:             0
 Lower bound of REAL arg:                 2

Here both line 1 and 3 is incorrect, they should be 2.

The “practical consequence” of this is that you will be out-of-bounds when accessing array members, which is of course completely spoiling whatever you are trying to compute.

This seems to be a compiler bug in nvfortran. Any comments? Thanks in advance.

Hi Hakostra! I’ll take a look at this along with your other issues and get back to you with an engineering report if I’m able to determine it’s actually a compiler issue (which it appears to be).

Hey Hakostra - I have reported these issues to engineering as TPR#36127. I looked at the upper bound as well, and I found that in the array slice case - essentially you’re getting C type array bounds instead of Fortran style. That is - you get bounds from 0 to N-1 rather than 1 to N. And as you observed - you can’t appear to shift the lower bound manually whatsoever - which also effects the entire array case. Very peculiar and I’m sure engineering will be interested.

As usual - thank you for notifying us about this! Please let us know about any more issues you run into! These go a long way to making a better compiler for everyone.

Thanks for looking into this.

An additional information: I looked into how flang-new (compiled from LLVM main repo today) handled this case - and it gives the correct results as the other compilers I tested - both when the lower bound is not specified arg(:) and when the lower bound is explicitly set arg(2:).

1 Like