! RUN: %python %S/test_modfile.py %s %flang_fc1
! Resolution of generic names in expressions.
! Test by using generic function in a specification expression that needs
! to be written to a .mod file.

! Resolve based on number of arguments
module m1
  interface f
    pure integer(8) function f1(x)
      real, intent(in) :: x
    end
    pure integer(8) function f2(x, y)
      real, intent(in) :: x, y
    end
    pure integer(8) function f3(x, y, z, w)
      real, intent(in) :: x, y, z, w
      optional :: w
    end
  end interface
contains
  subroutine s1(x, z)
    real :: z(f(x))  ! resolves to f1
  end
  subroutine s2(x, y, z)
    real :: z(f(x, y))  ! resolves to f2
  end
  subroutine s3(x, y, z, w)
    real :: w(f(x, y, z))  ! resolves to f3
  end
  subroutine s4(x, y, z, w, u)
    real :: u(f(x, y, z, w))  ! resolves to f3
  end
end
!Expect: m1.mod
!module m1
! interface f
!  procedure :: f1
!  procedure :: f2
!  procedure :: f3
! end interface
! interface
!  pure function f1(x)
!   real(4), intent(in) :: x
!   integer(8) :: f1
!  end
! end interface
! interface
!  pure function f2(x, y)
!   real(4), intent(in) :: x
!   real(4), intent(in) :: y
!   integer(8) :: f2
!  end
! end interface
! interface
!  pure function f3(x, y, z, w)
!   real(4), intent(in) :: x
!   real(4), intent(in) :: y
!   real(4), intent(in) :: z
!   real(4), intent(in), optional :: w
!   integer(8) :: f3
!  end
! end interface
!contains
! subroutine s1(x, z)
!  real(4) :: x
!  real(4) :: z(1_8:f1(x))
! end
! subroutine s2(x, y, z)
!  real(4) :: x
!  real(4) :: y
!  real(4) :: z(1_8:f2(x, y))
! end
! subroutine s3(x, y, z, w)
!  real(4) :: x
!  real(4) :: y
!  real(4) :: z
!  real(4) :: w(1_8:f3(x, y, z))
! end
! subroutine s4(x, y, z, w, u)
!  real(4) :: x
!  real(4) :: y
!  real(4) :: z
!  real(4) :: w
!  real(4) :: u(1_8:f3(x, y, z, w))
! end
!end

! Resolve based on type or kind
module m2
  interface f
    pure integer(8) function f_real4(x)
      real(4), intent(in) :: x
    end
    pure integer(8) function f_real8(x)
      real(8), intent(in) :: x
    end
    pure integer(8) function f_integer(x)
      integer, intent(in) :: x
    end
  end interface
contains
  subroutine s1(x, y)
    real(4) :: x
    real :: y(f(x))  ! resolves to f_real4
  end
  subroutine s2(x, y)
    real(8) :: x
    real :: y(f(x))  ! resolves to f_real8
  end
  subroutine s3(x, y)
    integer :: x
    real :: y(f(x))  ! resolves to f_integer
  end
end
!Expect: m2.mod
!module m2
! interface f
!  procedure :: f_real4
!  procedure :: f_real8
!  procedure :: f_integer
! end interface
! interface
!  pure function f_real4(x)
!   real(4), intent(in) :: x
!   integer(8) :: f_real4
!  end
! end interface
! interface
!  pure function f_real8(x)
!   real(8), intent(in) :: x
!   integer(8) :: f_real8
!  end
! end interface
! interface
!  pure function f_integer(x)
!   integer(4), intent(in) :: x
!   integer(8) :: f_integer
!  end
! end interface
!contains
! subroutine s1(x, y)
!  real(4) :: x
!  real(4) :: y(1_8:f_real4(x))
! end
! subroutine s2(x, y)
!  real(8) :: x
!  real(4) :: y(1_8:f_real8(x))
! end
! subroutine s3(x, y)
!  integer(4) :: x
!  real(4) :: y(1_8:f_integer(x))
! end
!end

! Resolve based on rank
module m3a
  interface f
    procedure :: f_elem
    procedure :: f_vector
  end interface
contains
  pure integer(8) elemental function f_elem(x) result(result)
    real, intent(in) :: x
    result = 1_8
  end
  pure integer(8) function f_vector(x) result(result)
    real, intent(in) :: x(:)
    result = 2_8
  end
end
!Expect: m3a.mod
!module m3a
! interface f
!  procedure :: f_elem
!  procedure :: f_vector
! end interface
!contains
! elemental pure function f_elem(x) result(result)
!  real(4), intent(in) :: x
!  integer(8) :: result
! end
! pure function f_vector(x) result(result)
!  real(4), intent(in) :: x(:)
!  integer(8) :: result
! end
!end

module m3b
use m3a
contains
  subroutine s1(x, y)
    real :: x
    real :: y(f(x))  ! resolves to f_elem
  end
  subroutine s2(x, y)
    real :: x(10)
    real :: y(f(x))  ! resolves to f_vector (preferred over elemental one)
  end
  subroutine s3(x, y)
    real :: x(10, 10)
    real :: y(ubound(f(x), 1))  ! resolves to f_elem
  end
end
!Expect: m3b.mod
!module m3b
! use m3a, only: f
! use m3a, only: f_elem
! use m3a, only: f_vector
!contains
! subroutine s1(x, y)
!  real(4) :: x
!  real(4) :: y(1_8:f_elem(x))
! end
! subroutine s2(x, y)
!  real(4) :: x(1_8:10_8)
!  real(4) :: y(1_8:f_vector(x))
! end
! subroutine s3(x, y)
!  real(4) :: x(1_8:10_8, 1_8:10_8)
!  real(4) :: y(1_8:10_8)
! end
!end

! Resolve defined unary operator based on type
module m4
  interface operator(.foo.)
    pure integer(8) function f_real(x)
      real, intent(in) :: x
    end
    pure integer(8) function f_integer(x)
      integer, intent(in) :: x
    end
  end interface
contains
  subroutine s1(x, y)
    real :: x
    real :: y(.foo. x)  ! resolves to f_real
  end
  subroutine s2(x, y)
    integer :: x
    real :: y(.foo. x)  ! resolves to f_integer
  end
end
!Expect: m4.mod
!module m4
! interface operator(.foo.)
!  procedure :: f_real
!  procedure :: f_integer
! end interface
! interface
!  pure function f_real(x)
!   real(4), intent(in) :: x
!   integer(8) :: f_real
!  end
! end interface
! interface
!  pure function f_integer(x)
!   integer(4), intent(in) :: x
!   integer(8) :: f_integer
!  end
! end interface
!contains
! subroutine s1(x, y)
!  real(4) :: x
!  real(4) :: y(1_8:f_real(x))
! end
! subroutine s2(x, y)
!  integer(4) :: x
!  real(4) :: y(1_8:f_integer(x))
! end
!end

! Resolve defined binary operator based on type
module m5
  interface operator(.foo.)
    pure integer(8) function f1(x, y)
      real, intent(in) :: x
      real, intent(in) :: y
    end
    pure integer(8) function f2(x, y)
      real, intent(in) :: x
      complex, intent(in) :: y
    end
  end interface
contains
  subroutine s1(x, y)
    complex :: x
    real :: y(1.0 .foo. x)  ! resolves to f2
  end
  subroutine s2(x, y)
    real :: x
    real :: y(1.0 .foo. x)  ! resolves to f1
  end
end
!Expect: m5.mod
!module m5
! interface operator(.foo.)
!  procedure :: f1
!  procedure :: f2
! end interface
! interface
!  pure function f1(x, y)
!   real(4), intent(in) :: x
!   real(4), intent(in) :: y
!   integer(8) :: f1
!  end
! end interface
! interface
!  pure function f2(x, y)
!   real(4), intent(in) :: x
!   complex(4), intent(in) :: y
!   integer(8) :: f2
!  end
! end interface
!contains
! subroutine s1(x, y)
!  complex(4) :: x
!  real(4) :: y(1_8:f2(1._4, x))
! end
! subroutine s2(x, y)
!  real(4) :: x
!  real(4) :: y(1_8:f1(1._4, x))
! end
!end
