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

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

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.

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”.

“previously” means in an earlier source statement, or to the left in the same statement (subject of an earlier interp). While the standard does not define it as such, it is consistent with English usage.

IMPLICIT NONE is not supposed to have an effect here. That it does in gfortran is a bug.

Thank you all for the great detective work. Here are my takeaways:

  1. My first snippet is invalid Fortran due to 10.1.11.
  2. GFortran is incorrect to compile it.
  3. ifort is correct to not compile it, although with an incorrect or misleading error message (2nd snippet).
  4. GFortran correctly doesn’t compile the module if implicit none is omitted.

Did I get this right?

1 Like

Strictly speaking, I would say that, for 2, it is incorrect for a processor to compile it without marking it an extension.

1 Like

I don’t agree that either of the ifort errors are incorrect, but I agree that they can be confusing. The fundamental issue is that the type and type parameters of string must be established at the point where it is used in the function prefix.

The first ifort message could say that the type is required to be specified, but the average user would point to further down in the source and say it is specified. What compilers tend to do here is assign a type, either by the implicit rules (if implicit none doesn’t appear) or some sort of “invalid type” that then triggers an error later when you try to change the type (ifort’s second error.)

ifort’s error messages often could stand improvement. Those that have been in the compiler a long time tend to be a bit terse or puzzling. Some of the newer messages are better - I lobbied the developers to improve some of the messages regarding generics. Unfortunately, improving established error messages is usually more effort than seems reasonable. Tweaking wording isn’t hard, but adding additional contextual strings requires mechanisms that may not exist.

That should be the error message here. :slight_smile: