Search code examples
compiler-errorsfortrangfortranintel-fortran

Using function result as character length in Fortran


Consider the following minimal working example:

module lib
    type t
    contains
        procedure::f,g
    end type
contains
    integer function f(this)
        class(t),intent(in)::this
        f=10
    end
    function g(this)result(r)
        class(t),intent(in)::this
        character(len=this%f())::r
        r='42'
    end
end

program prog
    use lib
    type(t)::o
    print*,o%g()
end

Compiling this code with the GNU (gfortran 11.1.0) compiler leads to the following message:

f951: internal compiler error: Segmentation fault
0x16463d7 internal_error(char const*, ...)
    ???:0
0x64b47c gfc_find_derived_vtab(gfc_symbol*)
    ???:0
0x672605 gfc_reduce_init_expr(gfc_expr*)
    ???:0
0x6572e3 gfc_match_char_spec(gfc_typespec*)
    ???:0
0x65e227 gfc_match_decl_type_spec(gfc_typespec*, int)
    ???:0
0x65f93c gfc_match_data_decl()
    ???:0
0x6d6236 gfc_parse_file()
    ???:0
Please submit a full bug report

The message says that it is a compiler bug, but I am not so sure. The NAG (nagfor 7.1) compiler also fails to compile the code with Error: line 13: Reference to non-specification function F in specification expression. However, the Intel (ifort 2021.8.0, ifx 2023.0.0) and Nvidia (nvfortran 22.9) compilers compile the code successfully. The problem in the first two compilers (GNU and NAG) is caused by the line character(len=this%f())::r.

Does Fortran standard allow declaration of the character length (in the automatic allocation) with the result of a function (here f)? Is it a bug of the GNU compiler or a feature of the Intel and Nvidia compilers?


Solution

  • Some functions can be used to give the length of a character function result; the particular function used here cannot.

    First: compilers shouldn't crash. What you see with gfortran is an internal compiler error (ICE), a bug. ICEs are bugs whether the code presented is valid Fortran or not.1

    The terminology introduced by the NAG compiler in rejecting your code is key to whether a function is allowed to be used. The length of a character function result must be a specification expression.2 The rules around how a specification expression is made are fairly involved (see Fortran 2018 10.1.11 and other questions and answers here for more detail) but the reason F here is rejected by nagfor is that F is not a pure function.

    For F to be a specification function it must be pure (F2018 10.1.11 Note 1):

    The requirement that they be pure ensures that they cannot have side effects that could affect other objects being declared in the same specification-part.

    At least with the version of gfortran I tested, you can work around the ICE, once you've made F pure, by changing the specification expression to:

    character(len=f(this))::r
    

    Finally, note that one cannot confidently say the Intel/NVIDIA compilers are wrong to accept this code. The code is not valid Fortran so a compiler can chose to do anything, including accept it as an extension. This isn't a case where a compiler is required to be able to diagnose your error. (One could still call it a compiler bug as this particular violation may well be good to diagnose and easy to see.)


    1 This isn't really true. If the code is not valid Fortran then the compiler is allowed to do anything in response (as long as it is capable of reporting those deficiencies it is required to be able to detect and report). Crashing is a thing it's allowed to do, although compiler vendors generally accept that an ICE is a bug even if it's an allowed response under the Fortran standard: it's a poor user experience.

    2 This applies equally to array bounds and to automatic data objects of a procedure more widely.