Type specification in function statement

Consider this:

module foo
  implicit none
contains
  character(len(string)) function bar(string)
    character(*), intent(in) :: string
    bar = string
  end function bar
end module foo

and specifically how I declare the function result using the input argument to determine the length.

According to Modern Fortran Explained, this is fine, and they even give an example of this.

gfortran-9.2.0 compiles it fine.

However ifort-2021.1 doesn’t:

$ ifort -c test_type_spec.f90 
test_type_spec.f90(4): error #6362: The data types of the argument(s) are invalid.   [LEN]
  character(len(string)) function bar(string)
----------------^
test_type_spec.f90(5): error #6415: This name cannot be assigned this data type because it conflicts with prior uses of the name.   [STRING]
    character(*), intent(in) :: string
--------------------------------^
compilation aborted for test_type_spec.f90 (code 1)

I think ifort is wrong here. Is it?

Further, if I enable implicit typing by commenting out implicit none, and try again with gfortran, I get this:

test_type_spec.f90:5:38:

    5 |     character(*), intent(in) :: string
      |                                      1
Error: Symbol ‘string’ at (1) already has basic type of REAL
test_type_spec.f90:4:16:

    4 |   character(len(string)) function bar(string)
      |                1
Error: ‘string’ argument of ‘len’ intrinsic at (1) must be CHARACTER
test_type_spec.f90:6:10:

    6 |     bar = string
      |          1
Error: Cannot convert REAL(4) to CHARACTER(1) at (1)

It seems that gfortran now implicitly declared string as real because it appeared in the type specification of the function statement, but I don’t think it should do that. Should it?

1 Like

Fortran 2018 subclause 10.1.11 paragraph 7 has “If a specification expression includes a specification inquiry that depends on a type parameter, array bound, or cobound of an entity specified in the same specification-part, the type parameter, array bound, or cobound shall be specified in a prior specification of the specification-part. The prior specification may be to the left of the specification inquiry in the same statement, but shall not be within the same entity-decl.”

But, strictly speaking, function types are in a “prefix” of a function-stmt, not a specification-part proper, so maybe this sentence does not apply to your situation.

1 Like

I think that gfortran is correct in both case. For the second case, don’t use implicit typing. From 8.7(4), string is first encountered and given a real type. You later try to explicitly type string, but that too late.

Any data entity that is not explicitly declared by a type declaration statement, is not an intrinsic function, is not a component, and is not accessed by use or host association is declared implicitly to be of the type (and type parameters) mapped from the first letter of its name, provided the mapping is not null. The mapping for the first letter of the data entity shall either have been established by a prior IMPLICIT statement or be the default mapping for the letter. An explicit type specification in a FUNCTION statement overrides an IMPLICIT statement for the result of that function.

1 Like

Indeed, I don’t intend to use implicit typing, but was curious about how it works. I stumbled upon this when trying to diagnose ifort’s behavior.

I think the original code is fine, but here is a workaround that Intel Fortran compiles:

module foo
  implicit none
contains
  function bar(string) result(ch)
    character(*), intent(in) :: string
    character (len(string)) :: ch
    ch = string
  end function bar
end module foo
1 Like

Removing IMPLICIT NONE from a correct program should never change its behavior.

I personally never use the prefix type form of a function as it doesn’t look nice IMO, and at one time a wrote a parser to make interfaces automatically to convert several hundred routines to sub-modules. I got to hate the prefex type function as it created a whole load of special cases

Also the prefix type form has some issues like you have found. Just a point of note that I personally would have made the function result allocatable rather than using the string length.

Steve, did you really mean to say that gfortran is correct in the first case? Your (correct) explanation of the rules suggest to me that ifort was correct in that the use of string in the prefix specification expression was invalid, since its type was not “previously specified”. Note that we’re not discussing the type of the function, but a dummy argument used in a specification expression.

@milancurcic I have looked at my copy of Modern Fortran Explained (latest) and have not yet found the example you refer to. I do see a footnote at the bottom of page 179 that would imply that this use is not valid, though it does not specifically reference a function prefix.

FWIW, the Cray (HPE) compiler compiles the original module without errors, and the cross-reference listing looks as expected.

The example is in Section 8.17 on page 177:

character(len=len(char_arg)) function line(char_arg)
   character(len=*)            :: char_arg
   character(len=*), parameter :: char_const = 'page'
   if ( len(char_arg) < len(char_const) ) then
   ...

of which only the first two lines are relevant to my question.

Steve, yes. gfortran compiles the original code and issues an error when implicit none is removed. Without wading through the Fortran standard, when implicit none is present, gfortran knows that all entities in a function statement have a declared type (or its an error!) so it can resolve all entities after all declaration statements have be parsed. When implicit typing is used, gfortran types an entity when it is first seen. In this case, ‘string’ is given a REAL type, which then conflicts with the later declaration statement. I could be wrong, but I believe that this conforms to the Fortran standard.

That’s strange. Which edition is that? The Eighth Edition published 2018 which is what I’m looking at has instead the following:

function line(char_arg)
   character(len=*)             :: char_arg
   character(len=len(char_arg)) :: line
   character(len=*), parameter  :: char_const = 'page'
1 Like

Eight edition published 2018, impression 1.

Here’s what my page looks like:

:sweat_smile:

My edition looks like @milancurcic 's. I need to ask Malcolm how the standard interprets this, as I just don’t see it.

@kargl , I’m skeptical of that interpretation. The standard is quite explicit about “previous specification”. I know compilers that do a look-ahead and allow something like:

integer :: x(r) integer :: r

which is non-standard, and I hope those compilers (not ifort) call this out when standards checking is enabled.

There ARE some cases where the standard requires some amount of look-ahead, but not in specification expressions.

1 Like

Here’s a snip from the e-book version of Modern Fortran Explained, Metcalf et al., Oxford University Press, Fifth Edition, 2018:

For whatever it’s worth, Intel Fortran compiler appears to me to be correct in issuing an error here. Though the diagnostic message issued by Intel Fortran can possibly be worded better for the benefit of users.

I personally think what section 10.1.11 Specification expression in the standard proxy document (18-007r1) which states on page 158, paragraph 6 - see below - likely explains why the authors of the book Modern Fortran Explained (MFE) may have altered the code snippet in the e-book version, as I show above. Moreover, the section that follows in the MFE book - 8.18 - does go over the same in further detail.

1 6 A variable in a specification expression shall have its type and type parameters, if any, specified by a **previous**
2   declaration in the same scoping unit, by the implicit typing rules in effect for the scoping unit, or by host or use
3   association. If a variable in a specification expression is typed by the implicit typing rules, its appearance in any
4   subsequent type declaration statement shall confirm the implied type and type parameters.

(emphasis on “previous” is mine).

But I’m open to be proven wrong here.

Malcolm’s initial reply is:

I don’t understand how that meets the rules either.

Neither does the NAG compiler – it complains that “char_arg” was already implicitly typed when it reaches the declaration for it.

I will consult with my co-authors…

Here is what NAG Fortran gives for Milan’s example with and without implicit none:

NAG Fortran Compiler Release 7.0(Yurakucho) Build 7036
Error: t2.f90, line 4: Implicit type for STRING
detected at (@STRING
Error: t2.f90, line 5: Symbol STRING has already been implicitly typed
detected at STRING@

NAG Fortran Compiler Release 7.0(Yurakucho) Build 7036
Error: t2.f90, line 5: Symbol STRING has already been implicitly typed
detected at STRING@

It will be very useful to know which specific rules the coauthor Malcolm Cohen of MFE, also the editor of the Fortran standard document, had in mind with the above comment!

10.1.11p6-7

This says that the type (and any type parameters the expression depends on) must be “previously declared”.

That’s what I found as well. I don’t think this example conforms with the letter of the law – but there’s not a clear definition of “previously” that applies between the function result type in a prefix and the dummy arguments that’s defined in the text, either… I’m also uncomfortable with the idea that IMPLICIT NONE can affect this rule; adding IMPLICIT NONE should only elicit new errors, not silence existing ones.