A first take on Fortran 202X features for LLVM Flang¶
I (Peter Klausler) have been studying the draft PDF of the Fortran 202X standard, which will soon be published as ISO Fortran 2023. I have compiled this summary of its changes relative to the current Fortran 2018 standard from the perspective of a Fortran compiler implementor.
Fortran 202X doesn’t make very many changes to the language
relative to Fortran 2018, which was itself a small increment
over Fortran 2008.
REDUCE clauses that were added to the
DO CONCURRENT construct, there’s little here for Fortran users
to get excited about.
Priority of implementation in LLVM Flang¶
We are working hard to ensure that existing working applications will port successfully to LLVM Flang with minimal effort. I am not particularly concerned with conforming to a new standard as an end in itself.
The only features below that appear to have already been implemented
in other compilers are the
REDUCE clauses and the degree trigonometric
intrinsic functions, so those should have priority as an aid to
We would want to support them earlier even if they were not in a standard.
REDUCE clause also merits early implementation due to
its potential for performance improvements in real codes.
I don’t see any other feature here that would be relevant to
performance (maybe a weak argument could be made for
The bulk of this revision unfortunately comprises changes to Fortran that
are neither performance-related, already available in
some compilers, nor (obviously) in use in existing codes.
I will not prioritize implementing them myself over
other work until they become portability concerns or are
requested by actual users.
Given Fortran’s history of the latency between new standards and the support for their features in real compilers, and then the extra lag before the features are then actually used in codes meant to be portable, I doubt that many of the items below will have to be worked on any time soon due to user demand.
If J3 had chosen to add more features that were material improvements to Fortran – and there’s quite a long list of worthy candidates that were passed over, like read-only pointers – it would have made sense for me to prioritize their implementation in LLVM Flang more urgently.
Specific change descriptions¶
The individual features added to the language are summarized in what I see as their order of significance to Fortran users.
Alert: There’s a breaking change!¶
The Fortran committee used to abhor making breaking changes, apart from fixes, so that conforming codes could be portable across time as well as across compilers. Fortran 202X, however, uncharacteristically perpetrates one such change to existing semantics that will silently cause existing codes to work differently, if that change were to be implemented and enabled by default.
Specifically, automatic reallocation of whole deferred-length character
allocatable scalars is now mandated when they appear for internal output
or as output arguments for some statements and intrinsic procedures
So existing codes that allocate output buffers
for such things will, or would, now observe that their buffers are
silently changing their lengths during execution, rather than being
padded with blanks or being truncated. For example:
character(:), allocatable :: buffer allocate(character(20)::buffer) write(buffer,'F5.3') 3.14159 print *, len(buffer)
prints 20 with Fortran 2018 but would print 5 with Fortran 202X.
There would have no problem with the new standard changing the behavior in the current error case of an unallocated variable; defining new semantics for old errors is a generally safe means for extending a programming language. However, in this case, we’ll need to protect existing conforming codes from the surprising new reallocation semantics, which affect cases that are not errors.
When/if there are requests from real users to implement this breaking
change, and if it is implemented, I’ll have to ensure that users
have the ability to control this change in behavior via an option &/or the
runtime environment, and when it’s enabled, emit a warning at code
sites that are at risk.
This warning should mention a source change they can make to protect
themselves from this change by passing the complete substring (
instead of a whole character allocatable.
This feature reminds me of Fortran 2003’s change to whole
allocatable array assignment, although in that case users were
put at risk only of extra runtime overhead that was needless in
existing codes, not a change in behavior, and users learned to
assign to whole array sections (
A(:)=...) rather than to whole
allocatable arrays where the performance hit mattered.
The features in this section are expensive to implement in terms of engineering time to design, code, refactor, and test (i.e., weeks or months, not days).
DO CONCURRENT REDUCE¶
J3 continues to ignore the
serious semantic problems
DO CONCURRENT, despite the simplicity of the necessary fix and their
admirable willingness to repair the standard to fix problems with
other features (e.g., plugging holes in
PURE procedure requirements)
and their less admirable willingness to make breaking changes (see above).
They did add
REDUCE clauses to
DO CONCURRENT, and those seem to be
immediately useful to HPC codes and worth implementing soon.
SIMPLE procedures constitute a subset of F’95/HPF’s
There are things that one can do in a
but cannot in a
SIMPLE one. But the virtue of being
to be its own reward, not a requirement to access any other
SIMPLE procedures might have been more useful had
DO CONCURRENT been
changed to require callees to be
SIMPLE, not just
The implementation of
SIMPLE will be nontrivial: it involves
some parsing and symbol table work, and some generalization of the
IsPureProcedure(), extending the semantic checking on
PURE procedures to ensure that
only call other
SIMPLE procedures, and modifying the intrinsic
procedure table to note that most intrinsics are now
rather than just
I don’t expect any codes to rush to change their
SIMPLE, since it buys little and reduces portability.
SIMPLE a lower-priority feature.
Conditional expressions and actual arguments¶
Next on the list of “big ticket” items are C-style conditional expressions. These come in two forms, each of which is a distinct feature that would be nontrivial to implement, and I would not be surprised to see some compilers implement one before the other.
The first form is a new parenthesized expression primary that any C programmer would recognize. It has straightforward parsing and semantics, but will require support in folding and all other code that processes expressions. Lowering will be nontrivial due to control flow.
The second form is a conditional actual argument syntax
that allows runtime selection of argument associations, as well
.NIL. syntax for optional arguments to signify an absent actual
argument. This would have been more useful if it had also been
allowed as a pointer assignment statement right-hand side, and
that might be a worthwhile extension. As this form is essentially
a conditional variable reference it may be cleaner to have a
distinct representation from the conditional expression primary
in the parse tree and strongly-typed
Fortran 202X has a new category of type. The new non-interoperable
ENUMERATION TYPE feature is like C++’s
enum class – not, unfortunately,
a powerful sum data type as in Haskell or Rust. Unlike the
ENUM, BIND(C) feature,
ENUMERATION TYPE defines a new
type name and its distinct values.
This feature may well be the item requiring the largest patch to the compiler for its implementation, as it affects parsing, type checking on assignment and argument association, generic resolution, formatted I/O, NAMELIST, debugging symbols, &c. It will indirectly affect every switch statement in the compiler that switches over the six (now seven) type categories. This will be a big project for little useful return to users.
Last on the list of “big ticket” items are the new TYPEOF and CLASSOF
type specifiers, which allow declarations to indirectly use the
types of previously-defined entities. These would have obvious utility
in a language with type polymorphism but aren’t going to be very
useful yet in Fortran 202X (esp.
TYPEOF), although they would be worth
supporting as a utility feature for a parametric module extension.
CLASSOF has implications for semantics and lowering that need to
be thought through as it seems to provide a means of
declaring polymorphic local variables and function results that are
neither allocatables nor pointers.
NOTIFY=specifier on image selector
Arrays with coarray components
“Rank Independent” Features¶
RANK(n) attribute declaration syntax is equivalent to
DIMENSION(:,:,...,:) or an equivalent entity-decl containing
n must be a constant expression, that’s straightforward to implement,
though not terribly useful until the language acquires additional features.
(I can see some utility in being able to declare PDT components with a
RANK that depends on a
KIND type parameter.)
It is now possible to declare the lower and upper bounds of an explicit
shape entity using a constant-length vector specification expression
in a declaration,
ALLOCATE statement, or pointer assignment with
real A([2,3]) is equivalent to
A(@V) “multiple subscript” indexing syntax uses an integer
vector to supply a list of subscripts or of triplet bounds/strides. This one
has tough edge cases for lowering that need to be thought through;
for example, when the lengths of two or more of the vectors in
A(@U,@V,@W) are not known at compilation time, implementing the indexing
would be tricky in generated code and might just end up filling a
The obvious use case for “multiple subscripts” would be as a means to
index into an assumed-rank dummy argument without the bother of a
construct, but that usage is not supported in Fortran 202X.
This feature may well turn out to be Fortran 202X’s analog to Fortran 2003’s
LEN derived type parameters.
So much for the major features of Fortran 202X. The longer list of minor features can be more briefly summarized.
New Edit Descriptors¶
Fortran 202X has some noncontroversial small tweaks to formatted output.
AT edit descriptor automatically trims character output. The
LZ control edit descriptors and
LEADING_ZERO= specifier provide a
means for controlling the output of leading zero digits.
Intrinsic Module Extensions¶
Addressing some issues and omissions in intrinsic modules:
LOGICAL8/16/32/64 and REAL16
IEEE module facilities upgraded to match latest IEEE FP standard
C_F_STRPOINTER, F_C_STRING for NUL-terminated strings
Intrinsic Procedure Extensions¶
SYSTEM_CLOCK intrinsic function got some semantic tweaks.
There are new intrinsic functions for trigonometric functions in units of degrees and half-circles. GNU Fortran already supports the forms that use degree units. These should call into math library implementations that are specialized for those units rather than simply multiplying arguments or results with conversion factors.
SELECTED_LOGICAL_KIND maps a bit size to a kind of
There are two new character utility intrinsic
functions whose implementations have very low priority:
TOKENIZE requires memory allocation to return its results,
and could and should have been implemented once in some Fortran utility
library for those who need a slow tokenization facility rather than
requiring implementations in each vendor’s runtime support library with
all the extra cost and compatibilty risk that entails.
SPLIT is worse – not only could it, like
have been supplied by a Fortran utility library rather than being
added to the standard, it’s redundant;
it provides nothing that cannot be already accomplished by
SCAN intrinsic function with substring indexing:
module m interface split module procedure :: split end interface !instantiate for all possible ck/ik/lk combinations integer, parameter :: ck = kind(''), ik = kind(0), lk = kind(.true.) contains simple elemental subroutine split(string, set, pos, back) character(*, kind=ck), intent(in) :: string, set integer(kind=ik), intent(in out) :: pos logical(kind=lk), intent(in), optional :: back if (present(back)) then if (back) then pos = scan(string(:pos-1), set, .true.) return end if end if npos = scan(string(pos+1:), set) pos = merge(pos + npos, len(string) + 1, npos /= 0) end end
(The code above isn’t a proposed implementation for
SPLIT, just a
demonstration of how programs could use
SCAN to accomplish the same
Fortran 202X raises the maximum number of characters per free form source line and the maximum total number of characters per statement. Both of these have always been unlimited in this compiler (or limited only by available memory, to be more accurate.)
More BOZ usage opportunities¶
BOZ literal constants (binary, octal, and hexadecimal constants, also known as “typeless” values) have more conforming usage in the new standard in contexts where the type is unambiguously known. They may now appear as initializers, as right-hand sides of intrinsic assignments to integer and real variables, in explicitly typed array constructors, and in the definitions of enumerations.
The source base contains hundreds of references to the subclauses, requirements, and constraints of the Fortran 2018 standard, mostly in code comments. These will need to be mapped to their Fortran 202X counterparts once the new standard is published, as the Fortran committee does not provide a means for citing these items by names that are fixed over time like the C++ committee does. If we had access to the LaTeX sources of the standard, we could generate a mapping table and automate this update.