j3-fortran / fortran_proposals

Proposals for the Fortran Standard Committee
178 stars 15 forks source link

Allow an intent(out) argument to adopt the same kind as an input(in) argument #128

Open certik opened 4 years ago

certik commented 4 years ago

Use case: when one wants to write a function that operates on all kinds, the only way currently is:

use kinds, only: sp, dp, qp
...
interface log10
    module subroutine log10_sp
    module subroutine log10_dp
    module subroutine log10_qp
end interface
...
real(sp) function log10_sp(x) result(r)
real(sp), intent(in) :: x
r = log(x) / log(10._sp)
end function

real(dp) function log10_dp(x) result(r)
real(dp), intent(in) :: x
r = log(x) / log(10._dp)
end function

real(qp) function log10_qp(x) result(r)
real(qp), intent(in) :: x
r = log(x) / log(10._qp)
end function

One can generate these in various ways (see https://github.com/fortran-lang/stdlib/issues/35 where we discuss various approaches).

Instead, it would be nice if one could write the above as follows:

real(wp) function log10(x) result(r)
integer, parameter :: wp = kind(x)
real(wp), intent(in) :: x
r = log(x) / log(10._wp)
end function

Where one defines the local variable wp with the "working precision" of x. This would be a generic function (templated on the "kind"), that is instantiated when used to the actual kind of the input argument x at the call site.

This would work for subroutines also, e.g.:

subroutine log10(x, r)
integer, parameter :: wp = kind(x)
real(wp), intent(in) :: x
real(wp), intent(out) :: r
r = log(x) / log(10._wp)
end function

The way compilers would implement that is not by "templates" and "instantiation" but by simply immediately generating different versions of the log10 function for all the precisions that the compiler supports, typically three for reals (sp, dp and qp). So it should be about as fast to compile as the hand written first version above which does this explicitly. And once it is compiled, user code should be as fast to compile as today.

klausler commented 4 years ago

You probably don't want to use both real(wp) and result(r) on the same function statement.

Let me suggest this revision:

function log10(x) result(r)
  real(kind=*), intent(in) :: x ! <- note the "assumed kind" type parameter
  integer, parameter :: wp = kind(x) ! no longer a forward reference to x
  real(kind=wp) :: r
  r = log(x) / log(10._wp)
end
certik commented 4 years ago

@klausler that's better. Thanks!

FortranFan commented 4 years ago

@klausler wrote:

You probably don't want to use both real(wp) and result(r) on the same function statement.

Let me suggest this revision:

function log10(x) result(r)
  real(kind=*), intent(in) :: x ! <- note the "assumed kind" type parameter
  ..

Though I don't have ready references at the moment, indications are the Fortran standard committee has disfavored the "assumed kind" option, that they have insisted the kind type parameter to be either defaulted or be given by a constant expression. One can see evidence of this with parameterized derived type (PDT) facility starting Fortran 2003 that introduced assumed length parameter option with derived types but no assumed kind; and with assumed type ( TYPE(*) ) and assumed rank ( DIMENSION(..) ) options starting Fortran 2018. It's unclear whether such a position against assumed kind can change in the future.

Note the original post here is but one use case for proper GENERICS in Fortran. Toward generics, it appears the committee is open to the notion of certain UTILITIES e.g., TYPEOF/CLASSOF intrinsic inquiry functions that can employed in dummy argument declarations. In the same vein, I wonder if might be feasible to consider another utility, say ALL_OF, that can be allowed in constant expressions.

Considering the standard states in the context of standard intrinsic modules, "The processor shall provide the named constants, derived types, and procedures described in 16.10.2" and among the constants listed include the KINDs of intrinsinc types such as REAL_KINDS, INTEGER_KINDS, CHARACTER_KINDS, etc., a utility such as ALL_OF can make feasible the following which might make it easier for processors to do the needful, meaning put together all the necessary wiring toward the generic interface needed by a coder, as illustrated in the original post.

function log10(x) result(r)
   use, intrinsic :: iso_fortran_env, only : REAL_KINDS
   real(kind=ALL_OF(REAL_KINDS)), intent(in) :: x 
   integer, parameter :: wp = kind(x) ! no longer a forward reference to x
   real(kind=wp) :: r
   r = log(x) / log(10._wp)
end
wclodius2 commented 4 years ago

The TYPE_OF facility plans for 202X would simplify this usage.

FortranFan commented 4 years ago

@wclodius2 wrote June 30, 2020 3;40 PM EDT:

The TYPE_OF facility plans for 202X would simplify this usage.

No, it won't - see my earlier reply.

certik commented 1 year ago

A similar proposal to specify the kind as an argument: https://github.com/j3-fortran/fortran_proposals/issues/91.