[RFC] Support for -std=f2023

While implementing the F2023 changes to SYSTEM_CLOCK, we came across a question about adhering to a default or requested -std=option. The only choice today for an explicit standard version is -std=f2018, but as far as I can tell it doesn’t really do anything.

Should Flang act like Clang/Clang++ and honor the choice of standard, and default to “whatever is current in our implementation” if not specified?

SYSTEM_CLOCK is of particular importance here since F2023 contains additional restrictions; all integer arguments must be a consistent kind type, as well as be at least a default integer in decimal exponent. If there is no way to switch implementations with a -std flang, then I can see where existing timing codes might begin to be rejected for compilation if they are using arguments with different integer kind types.

Here’s what we decided for Flang:

As a general principle, this compiler will accept by default and without complaint many legacy features, extensions to the standard language, and features that have been deleted from the standard, so long as the recognition of those features would not cause a standard-conforming program to be rejected or misinterpreted.

Other non-standard features, which do conflict with the current standard specification of the Fortran programming language, are accepted if enabled by command-line options.

The -pedantic option exists to generate a warning (or -Werror error) for violations of the current standard.

Currently, Flang turns on warnings for non-standard extensions if -std=f2018 is present and does nothing else. Additionally, Flang does not accept -std=f2023 at all. By comparison, with -std=c++XX, Clang++ accepts: c++98, c++11, c++14, c++17, c++20, and c++23 among others. With these options, Clang++ more or less (especially for the newer standards) supports the appropriate standard.

I believe Ted’s question was less about non-standard features, more about places where the Fortran 2018 and Fortran 2023 standards disagree. Currently, if -std=f2018 is present, none of the implemented Fortran 2023 features are disabled.

None of the standard language features are controllable by flags in flang-new. Everything is always available.

Extensions are controllable by flags within the compiler, and warnings can be enabled by the drivers.

The only feature of F’2023 that might be worth controlling with a driver is its regrettable breaking change, which we don’t support anyway, but can emit a portability warning about.

If you want to be able to disable later features by setting --std=1966 or whatever, it would be a very big deal.

Thanks, Steve and Peter.

To be clear, I’m not talking about anything prior to 2018; -std=f66or even -std=03 would indeed be a nightmare. This is strictly a forward-looking question based on Flang supporting f2018 today as a default version.

I’ve seen quite a bit about Flang trying to match clang, clang++, and gfortran in the way the driver and options work - so in theory we should allow SYSTEM_CLOCK(int4,real,int8) by default today but throw an error if the user specifies -std=f2023 - just like the other compilers which support a std switch on semantic checks.

The down side, of course, is the extra testing this will introduce down the road. As we are fairly early in the implementation of 2023 features / interps / corregenda I believe we would want to enable a new -std flag sooner rather than later.

For reference, ifx and gfortran both have usable switches to control the dialect, and crayftn does not.

Why would you want to disallow existing codes with SYSTEM_CLOCK from continuing to work?

In Fortran 2018, the arguments to SYSTEM_CLOCK do not have to match types/kind, and are unrestricted in kind. In Fortran 2023, those same arguments must have the same kind (at least the integer arguments do), and the arguments must be at least the size of the default integer (int4).

In theory someone could be relying on Fortran’s 2018 SYSTEM_CLOCK behavior, as in Ted’s example: SYSTEM_CLOCK(int4,real,int8), or something like SYSTEM_CLOCK(int2, real, int4). These examples are compliant with Fortran 2018, but are not with the Fortran 2023 standard and will cause compile-time errors.

Using SYSTEM_CLOCK like this has been discouraged, basically since Fortran 2018 came out, so it’s unlikely to break anyone’s code, but it is possible. We could have a flag just for the SYSTEM_CLOCK argument type checking in case it does cause someone problems, but using -std=2018 to control this behavior is “cleaner” and matches what at least some other compilers do.

Why would you want to disallow existing codes with SYSTEM_CLOCK from continuing to work?

Isn’t this the answer to your question?

These examples are compliant with Fortran 2018, but are not with the Fortran 2023 standard and will cause compile-time errors.

No, it is my question. Why would we implement breaking changes?

The Fortran 2018 definition SYSTEM.CLOCK seems to be covered by “this compiler will accept by default and without complaint many legacy features”.

I don’t think they’ll be an objection to someone issuing a warning if the -pedantic switch is enabled after there’s general agreement that Flang should consider Fortran 2023 to be the default standard.

The history of -std=2018 may better explain why too much weight should not be given to this option: ⚙ D97119 [flang][driver] Add options for -std=f2018

Because they’re a part of the standard, and our users may expect our compiler to follow it.

I get that this may break some preexisting code, but there may also be people writing new code and expecting it to work as the latest standard dictates.

We have a very long list of items in Extensions.md in the section Intentional violations of the standard and I am happy to add more items to it. Nothing is more important to Fortran than preserving existing investments in applications and libraries. I’m not going to let existing code get broken under default compiler options, especially not before there’s a portability argument to be made from other compilers that have implemented the breaking changes in F’2023.

Some info from Clang side. Hope you’ll find it useful.

  1. Yes, we support 7 language modes (not counting various -f flags). There is also -std=c++03, but it’s fully synonymous to -std=c++98 (might be the other way round, actually).
  2. One of the consequences of that is that our conformance tests are run 7 times, one per language mode. Soon C++26 will get published, and they will be running 8 times instead.
  3. We have to get creative with those tests sometimes, making sure that all the tests that make sense for old language modes are running without too much extension warnings (saying that we’re using a feature from newer standard).
  4. With the way defect reports are understood in the C++ committee and between implementers, even C++98 mode keeps changing.
  5. Even if the Standard is asking us to change, be it in the new revision or via retroactively-applied defect reports, we still evaluate impact on our users, because backwards compatibility is important.
  6. Examples of the above would be the story of C++17 -frelaxed-template-template-args or C++20 -fchar8_t, where implementations found themselves at odds with what Standard is saying.

If you and your users would find it acceptable that you have only one language mode that implements the latest revision of the Standard, I’d go for that, because it would save you a lot of trouble. But given that you have real users, I have my doubts that this is a viable option.

Prior to F’2023 the ISO WG22 / INCITS J3 Fortran committee has been pretty good at avoiding breaking changes. F’2023 did add at least one, maybe two if the two versions of SYSTEM_CLOCK can’t coexist. Unlike C and C++, Fortran compilers haven’t done a good job historically of implementing new features in a portable fashion, or at all. There are no reference implementations or canonical conformance test suites, either. So it’s messy.

Looking only at F2008 (N2122) there were a minimum of 16 of this sort of “breaking changes”. Here’s a couple of them pulled directory from the doc:

  1. In the first paragraph of the subclause, before C1505 add a new constraint: C1504a (R425) A derived type with the BIND attribute shall have at least one component.
  2. Subclause 5.2.1: Add new constraint: C507a An expression that specifies a length type parameter or array bound of a named constant shall be a constant expression.
  3. After constraint C427 insert new constraint: C427a (R426) The same type-param-name shall not appear more than once in a derived-type-stmt.
  4. After constraint C4106, insert the following new constraint: C4106a (R472) The declared type of an ac-value shall not be abstract.

…etc…etc

That first one is a great example - C1504a, is now C1805. And Derived Types with the BIND attribute was further restricted again in F2023 with C1806. So there’s two such “breaking changes” to a single feature in as many releases of the standard. These such things are not rare - they are plentiful.

Because J3 defines a standards-compliant processor as meeting a certain criteria, and the Flang project wants to deliver a viable alternative to other Fortran compilers.

Yes! That is exactly my point, and its why Flang should be accepting of expanding the existing -std= flag to include -std=f2023 as a user option since it is the one and only choice we have for preserving legacy code functionality while also allowing Flang to be a standards-compliant Fortran processor for new development.

NAG, GNU and Intel all have a flag to specify the standard version a user chooses to adhere to.

Here’s what GNU does for this specific issue with the intrinsic in question. This is, to me, a demonstration of the very definition of “preserving existing investments”.

$ cat system_clock.f90 
program p
  integer(8) :: c8, r8, m8
  integer(4) :: c4, r4, m4
  integer(2) :: c2, r2, m2
  call system_clock(c8, r8, m8)
  print '(i5,3i22)', 8, r8, m8, c8

  call system_clock(c4, r8, m8)
  print '(i5,3i22)', 8, r8, m8, c4

  call system_clock(c2, r8, m4)
  print '(i5,3i22)', 8, r4, m4, c2
end

$ gfortran -std=f2018 system_clock.f90 
$ gfortran -std=f2023 system_clock.f90 
system_clock.f90:8:24:

    8 |   call system_clock(c4, r8, m8)
      |                        1
Error: Prohibited in Fortran 2023: integer arguments to SYSTEM_CLOCK at (1) with different kind parameters
system_clock.f90:11:20:

   11 |   call system_clock(c2, r8, m4)
      |                    1
Error: Prohibited in Fortran 2023: COUNT argument to SYSTEM_CLOCK at (1) with kind smaller than default integer

This is more than just a SYSTEM_CLOCK intrinsic issue, as the D97119 PR brings up (thanks for the pointer to that, Steve). This particular RFC is about having a way for developers to being adding features from a new version of the standard (or an interp, or a corrigendum, or finalized F202y features coming 12 months from now) and allowing users to meet some arbitrary standard requirement and to specify it across their entire project (LLVM C++20, anyone?).

2 Likes

Prohibiting the same type parameter name from appearing more than once in a TYPE statement was not a breaking change. It’s an obvious error that compilers weren’t permitting anyway, it just needed to be codified. Most new constraints and restrictions are like that.

A breaking change is one that actually breaks working code – code that compiles and does something reasonable in an implementation. The F’2023 changes to SYSTEM_CLOCK are one kind of breaking change; the F’2023 changes to allocatable character scalars in various I/O contexts are another.

The SYSTEM_CLOCK breaking change takes code that works and proclaims that it should now be an error. Even though it, well, works. That’s unacceptable. We can emit a portability warning about it, of course – and we’re good about doing that for other deleted features, like ASSIGN – but we should have a very good reason to break something that already works.

The I/O change to allocatable character scalars, on the other hand, is a breaking change that takes code that does something well-defined and silently changes what it does. This is even more unacceptable, and must not happen by default.

(Historical note: the change to the semantics of intrinsic assignment to allocatable arrays in F’2003 was a breaking change only for performance – it didn’t change the meaning of any correct code, it only added runtime overhead that users learned to avoid by changing A=… to A(:)=… when it matters.)

Isn’t it an argument in favor of being able to select the effective Fortran standard? If you want your SYSTEM_CLOCK with non-matching kinds to keep doing whatever it’s doing, then you pass -std=f2018. Someone who wants F2023 conformance would say -std=f2023.

Whichever one of these should be the default is a separate conversation.

2 Likes

Enforcing a single Fortran specification by default is against flang’s philosophy. There’s an existing mechanism for enforcing the current standard (-pedantic) and an accepted mechanism to add breaking changes.

None of this argues against or prevents implementing finer-grained conformance with a particular set of standards under options like -std=2003 or similar.

2 Likes

Then it’s time for the philosophy to change, if you ask me.