LEN type parameters not working

I don’t think that this program depends on any standard feature more recent that Fortran 2003, yet I can’t find any Fortran compiler implementation that can compile it without failing with internal errors or bogus error messages. (I tried recent versions of gfortran, ifort, nvfortran, nagfor, and xlf). Is this indeed a conforming program?

module m
  interface
    pure recursive function ifact(j)
      integer, intent(in) :: j
    end function
  end interface
  type :: t(len)
    integer, len :: len
    integer :: a(ifact(len))
  end type
 contains
  subroutine subr(n)
    integer, intent(in) :: n
    type(t(n)) :: x
    do j = 1, size(x%a)
      x%a(j) = j
    end do
    print *, x
  end subroutine
end module

pure recursive function ifact(j) result(i)
  integer, intent(in) :: j
  if (j <= 0) then
    i = 1
  else
    i = j * ifact(j - 1)
  end if
end function

use m
call subr(4)
end
1 Like

The way I see it, you are not allowed to declare an explicit shaped array with nonconstant bounds, as in:

integer :: a(ifact(len))

I suppose you could try and use an allocatable array instead, but atleast with gfortran-9, the support is kind of buggy. Here is what I mean precisely:

module m

  implicit none
  private

  public :: t2, init_t2

  type :: t2(len)
    integer, len :: len
    integer, allocatable :: a(:)
  end type

 contains

  subroutine init_t2(this)
    type(t2(*)), intent(inout) :: this
    if (allocated(this%a)) deallocate(this%a)
    allocate(this%a(ifact(this%len)))
  end subroutine

  pure recursive function ifact(j) result(i)
    integer, intent(in) :: j
    integer :: i
    if (j <= 0) then
      i = 1
    else
      i = j * ifact(j - 1)
    end if
  end function

end module

program test_m
use m                      ! works
! use m, only: t2, init_t2 ! does not work
implicit none

integer, parameter :: n = 4
type(t2(:)), allocatable :: obj1
type(t2(3)) :: obj2

allocate(t2(n) :: obj1)

print *, obj1%len, allocated(obj1%a)
print *, obj2%len, allocated(obj2%a)

call init_t2(obj1) 
print *, obj1%len, size(obj1%a)

call init_t2(obj2) 
print *, obj2%len, size(obj2%a)
end program

The output I get is:

$ gfortran-9 fortran_prog.f90
$ ./a.out
           4 F
           3 F
           4          24
           3           6

For whatever it’s worth, I think the program conforms.

My hunch is all the processors are falling over the specification expression in the derived type component definition of integer :: a(ifact(len)), something which I know Intel Fortran supports to some extent but still has gaps; and for which gfortran effectively has no support.

Based on what I see, the program adheres to what is stated in the standard in Section 10.1.11 on Specification Expression: the paragraphs relevant to the example code in the original post (per my understanding) being shown below

39 10.1.11 Specification expression
40 1 A specification expression is an expression with limitations that make it suitable for use in specifications such as
41 length type parameters (C704) and array bounds (R817, R818). A specification-expr shall be a constant expression
42 unless it is in an interface body (15.4.3.2), the specification part of a subprogram or BLOCK construct, a derived
43 type definition, or the declaration-type-spec of a FUNCTION statement (15.6.2.2).
44 R1028 specification-expr is scalar-int-expr
45 C1010 (R1028) The scalar-int-expr shall be a restricted expression.

2 A restricted expression is an expression in which each operation is intrinsic or defined by a specification function
2 and each primary is

4 (2) an object designator with a base object that is a dummy argument that has neither the OPTIONAL
5 nor the INTENT (OUT) attribute,

23 (12) a reference to a specification function where each argument is a restricted expression,
24 (13) a type parameter of the derived type being defined,

38 4 A function is a specification function if it is a pure function, is not a standard intrinsic function, is not an internal
39 function, is not a statement function, and does not have a dummy procedure argument.

Here’s a simplified variant of the code in the original post where a specification expression which reduces to a constant expression is supported in the specification part of a subprogram by some compilers I tried. However even this simple example is processed to varying levels of accuracy by Intel Fortran (looks ok) and gfortran (not so):

module m
   interface
      pure recursive function ifact(j)
         integer, intent(in) :: j
      end function
   end interface
   type :: t(len)
      integer, len :: len
      integer :: a(len)
   end type
contains
   subroutine subr()
      !integer, intent(in) :: n
      type(t(len=ifact(4))) :: x
      do j = 1, size(x%a)
         x%a(j) = j
      end do
      print *, x
   end subroutine
end module

pure recursive function ifact(j) result(i)
   integer, intent(in) :: j
   if (j <= 0) then
      i = 1
   else
      i = j * ifact(j - 1)
   end if
end function

   use m
   call subr()
end

These processors thus struggle with the use of a pure function in a derived type component definition and also when an object designator that is a dummy argument is used.

I agree that the code is conforming. Allowing pure user functions to be referenced in a restricted expression is relatively new, and some compilers (apparently a lot) have not caught up to the change yet. An ICE is always a bug, independent of whether a compiler us up to date with new features.

Interesting. I had not delved this deep. It seems like a very powerful feature (if it worked correctly).

With some (pure) intrinsic functions it appears to work:

program test
implicit none

character(len=5), parameter :: c = "123"
integer :: b(len_trim(c))

type :: t
  integer :: b(len_trim(c))
end type

type(t) :: mt
print *, size(b), size(mt%b)
end program

On the other hand for the case of

type :: t
  character(len=5) :: d
  integer :: b(len(d))
end type

with a references to a type parameter I get error messages.

Now, this code snippet does not conform: other than type parameters and restricted expressions involving them (as per section 10.1.11), the standard does not permit the component of a type being defined to be an object designator in another component of the same type.

Good catch, my bad I missed out on “in which there are no references to specification functions” exception to specification expression in C750.