Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Language version keyword #83

Open
aradi opened this issue Nov 13, 2019 · 72 comments
Open

Language version keyword #83

aradi opened this issue Nov 13, 2019 · 72 comments
Labels
Clause 8 Standard Clause 8: Attribute declarations and specifications

Comments

@aradi
Copy link
Contributor

aradi commented Nov 13, 2019

There are several features in Fortran many of us wish to see changed / deprecated in order to support and enforce modern programming practices (see for example . Unfortunately doing so would break backwards compatibility of the language, which would sacrifice one of Fortrans advantages.

What about adding a keyword to the language, which indicates the language version of the code in a given unit (module). Something like:

module my_modern_module
  language_version 2022
 ...
end module my_modern_module

All module files without explicit language version keyword would be treated as having code complying to the last standard without this keyword (e.g 2018). All module files with the language version keyword would be treated accordingly to the specified version.

Advantages:

  • One could deprecate features much faster without fearing to break backwards compatibility.
  • The user would not have to deal with compiler specific flags to enforce a given practice or language standard (like -assume realloc_lhs or similar). Especially, if you have source files with different language standards in a project (no nice, but can happen), you won't need to fiddle with the compilation options for each file individually.
  • A single line could enforce several requirements without too much verbosity, so instead of having
    implicit none(type, external)
    implicit save off
    short_circuiting on
    ...
    
    one would just write
    language_version 2022
    
    to profit from all the features / requirements a given Fortran standard offers / enforces.
  • Finally, its marketing value would tremendous. Every source file in a modern Fortran project, every slide presented in a Fortran course, would advertise, that this beautiful language, despite being the first high level (modern!) language, is still alive and is able to adapt to the requirements of the present.
@FortranFan
Copy link
Member

FortranFan commented Nov 13, 2019

@aradi , please see this thread at comp,lang.fortran where a similar idea re: a 'version specifier' was posted.

While I am personally not sold yet on the specific keyword idea (I wonder if a simpler semantic alternative might be attainable for the same), I wholeheartedly agree with and support your sentiment you express, particularly in your last bullet, toward Fortran language. The relevance of Fortran in scientific and technical programming is profound and hope it can remain as such, its strength lies in both in its legacy but more importantly with the potential for the future that can allow scientists and technologists to focus almost exclusively on their domain expertise and their computations without having to get their hands "dirty" with the nitty-gritty details and artifacts of computer engineering (pointers, LValue/Rvalue, etc.)

@aradi
Copy link
Contributor Author

aradi commented Nov 13, 2019

@FortranFan Thanks for the link, I was not aware of that discussion. By looking at it, I could not find any serious counter arguments, why this idea can not be or should not be done.

As for the keyword itself: If there is any other superior semantic solution, we should go with that then. For me, it is only important that we can adapt the language to current programming customs faster. While every programming language has its own peculiarities, those peculiarities should have a good justification other than we always did this way. As I mentioned on the other issue here, I am often feeling ashamed, when explaining to students that they must put implicit none in every module (especially if their project should pass the exam 😆 ).

@sblionel
Copy link
Member

I don't have strong feelings one way or another about the general idea, but I will caution that standardizing keywords such as standard version numbers is fraught with risk for compiler developers and users. Consider that the name of Fortran 2018 changed in the year or so before publication. What you would end up with is either vendors having to support multiple, synonymous keywords (some of which would not be conforming in the new standard), or delaying support until the standard is published meaning a delay before programmers could use them.

There is also the issue of incomplete implementations and TSes, assuming a TS changed the meaning of a past standard (unlikely but it could happen.)

See also my 2013 thoughts on a related topic.

@certik
Copy link
Member

certik commented Nov 13, 2019

Available options

In Fortran, we do not break old code (#79), so the only way to introduce a breaking feature is to signal to the compiler that the given source file is using the new feature, and by default no signalling is done, so the old feature is being used. So the only question is how to signal to the compiler. Here are the options that I have seen proposed:

  1. File suffix specifying a version (.f90, .f03, .f08, .f18, .f2x, .f2y)

  2. Compiler option for the version (-std=f95, -std=f18, -std=f2x)

  3. Compiler option for a feature (-frealloc-lhs, -fimplicit-none, ...)

  4. Keyword for the version (this issue Language version keyword #83)

  5. Keyword for a specific feature (implicit none, implicit save off (see Deprecate and remove implicit save behavior #40), ...)

  6. Keyword for modern (modern module X) which would not use obsolete features (features can become obsolete in the future and one would need to update the code)

Is there fundamentally any other way to consider?

Analysis

The option 1. does not seem to be a good way as @sblionel explained here, and I agree.

The option 2. is already used in compilers such as gfortran, but my understanding is that the standard does not enforce new features, so even with the latest -std=f18, the feature implicit none is not enabled unless explicitly told so. In other words, the newest standard does not break old code. So the options 1., 2., and 4. actually cannot impose implicit none, unless F202x or F202y introduces a breaking change of enabling implicit none by default, and historically this is not how we have done things. But unless that is changed, you would still have to do:

language_version 202x
implicit none(type, external)
implicit save off
short_circuiting on
...

That leaves options 3. and 5., both of which one can use today, but the problem with these is that as more and more optional features are added, it will be more and more painful to be always explicitly enabling them at the beginning of each module, such as:

implicit none(type, external)
implicit save off
short_circuiting on
...

The objection from Steve regarding the fact that the name of the standard changes until it is released is easily fixed by simply using the placeholder -std=f2x, until F202X is released and then changing it to -std=f23 (for X=3). That's how C++ does it and f2x just becomes an alias for f23. I think that's not a problem in practice.

So the main issue that I can see in here is that some future standard, say 202y will introduce a breaking change such as enabling implicit none by default. And if you tell the compiler (no matter whether using an option -std=f2y, or by using a keyword language_version 2y), then the new feature will be used by default. That is a fundamentally different way of doing it --- and that's what we should discuss if we want that.

Update: added option 6.

@aradi
Copy link
Contributor Author

aradi commented Nov 13, 2019

@certik That's a nice summary on the possible options, thanks! One note on the topic, whether to realize it via compiler option or language construct: I think, a programming language should be self-contained. A compiler should be able to generate correctly behaving code (and stop on standard violation) just by reading the source code itself without the need of any additional hints in form of command line options. Latter should in my opinion only serve 'fine-tuning' purposes instead of being indispensable for the correctness of the generated code.

@aradi
Copy link
Contributor Author

aradi commented Nov 13, 2019

And yes, I would vote for breaking backwards compatibility in newer standards for the sake of simplicity and code robustness, provided a standardized language construct ensures that old code can be still compiled without the necessity of using extra (vendor dependent!) compiler options.

@certik
Copy link
Member

certik commented Nov 13, 2019

@aradi so let's discuss some of the breaking features that could be considered:

Anything else? Because if it's just these, then there might be a way to achieve what we want without breaking backwards compatibility (for example #40 can be fixed by changing "implicit none" to "explicit all" or something like that --- still just one line to be added to every module), so we do not need the feature discussed in this issue.

@gronki
Copy link

gronki commented Nov 13, 2019

I will be the devils advocate here, since I am usually the hater on the old code, but I will argue that this solution is inherently dangerous.

First, let me prefice that with what I consider the absolutely basic design feature I expect Fortran to have: prevent silent errors. So ugly syntax or not, what I personally get paid for is to get good numeric results.

Consider the very root of such solutions, which probably is the familiar line

implicit none

The implicit typing feature was deemed to be unsafe and thus new, stricter rules for typing can be enabled with this switch. Please notice:

  1. if you omit the implicit none, the functionality of the code will not change
  2. if you use -fimplicit-none (or equivalent) switch, the functionality of the code will not change

Reading the code mid-file, you don't need to check the top of the file to see the implicit none. You just use the implicit typing and whether this mode is enabled or not, you will be good. Now if you start using implicit typing and implicit none was in effect, you will get a compiler error. All good.

Now consider the proposed switch (let's stick to my favorite implicit save). For example:

implicit nosave

If you do that, the behavior of the code will change violently in a completely non-distinguishable way. Whenever you see:

integer :: i = 0

you have no idea whether i will be zero on each call or not. And worst of all, if you make a mistake, you will not be warned.

Now when you work with different files, some of them written in 1980s, other in 2010s, you cannot just write right Fortran: you have to keep in mind which typing/saving/... rules are in place. Which is exactly the reason why implicit typing was deemed unsafe!

As much as I hate and despise this feature, I think we cannot provoke such situations. It must be clear looking at the code whether the variable is save or not. A couple of solutions have been proposed:

  1. integer, nosave :: i = 0 -- IMO pain to type
  2. integer, init :: i = 0 -- nicer to type on QWERTY keyboard
  3. integer :: i := 0 -- or some other operator, such as =>. The good side is that there are no keywords that clutter the code.

Any solution must be nice to type. This keyword/syntax will be used a lot. So I personally would prefer some operator-based syntax because having nosave every 3 lines would cause a lot of clutter, especially in editors with syntax highlighting. But the worst case init would be not that much pain.

Now this ended up being an implicit save post which I didn't intend but I did not know how to split it so maybe I will post exactly the same thing in the other place.

@certik
Copy link
Member

certik commented Nov 13, 2019

@gronki thank you. These are precisely the kinds of arguments that we have to have. I commented at #40 (comment) with my proposal there.

@klausler
Copy link

@aradi so let's discuss some of the breaking features that could be considered:

Anything else? Because if it's just these, then there might be a way to achieve what we want without breaking backwards compatibility (for example #40 can be fixed by changing "implicit none" to "explicit all" or something like that --- still just one line to be added to every module), so we do not need the feature discussed in this issue.

That list could be a lot longer. All "deleted" and most "obsolescent" features could finally be disabled (e.g., EQUIVALENCE, Hollerith, extensions like STRUCTURE) in a program unit that declares itself to be "modern".

Syntactically, this sort of thing might look best as a prefix on the MODULE statement, i.e. MODERN MODULE FOO. And it should be inherited and enforced in submodules.

@certik
Copy link
Member

certik commented Nov 13, 2019

@klausler, that's a great idea. Perhaps the standard can simply declare some features as "obsolescent", and those would work by default, but if you use modern module foo, all "obsolescent" features will get disabled.

One problem that I can see is that when a future standard makes something "obsolescent" and you happen to use that feature in your modern module foo, your code will break. And you cannot just remove modern, because you use some other "modern" features, so your code will simply stop working until you fix it. I think that's a problem.

@aradi
Copy link
Contributor Author

aradi commented Nov 13, 2019

@gronki I think the point you raise is absolutely right. Having a language version keyword would indeed make the behavior of certain constructs "context-dependent". Actually, we have that already in the language, since

integer, allocatable :: a(:), b(:)
...
a = b

triggers LHS reallocation depending whether you stick with Fortran 95 or 2003. And, which one of the both is used, depends on the compiler options you use for compilation. There is no way to tell it by looking at the source code alone. The language keyword would at least let you know it by declaring the standard conformance at the beginning of your scope.

@aradi
Copy link
Contributor Author

aradi commented Nov 13, 2019

@klausler Thanks, indeed the list could be considerably longer. And I agree that it is a good idea to encode the standard compliance already into the module statement. However, I'd suggest to keep it general with possibilities for future adjustments, so using something more specific as modern, e.g.
module, language(f2008) :: my_module
or something similar.

@gronki
Copy link

gronki commented Nov 13, 2019

@aradi I would argue that this is an unfortunate exception from the past which should not be a reason to introduce even more unfortunate exceptions like this. It is not a problem anymore since f2003+ compliant compilers are available for free so there is no reason to use f95 compiler anymore. The time to version Fortran was in 1990: it was great moment to fork the language and make it independent of F77 pains. This was not done and now with F2018 this is far too late.

Please also keep in mind that not everyone using the Fortran language is as passionate as us to remember which year number will enable which behavior. This is also an important factor. It's about making programming simpler, not harder.

@klausler
Copy link

@klausler, that's a great idea. Perhaps the standard can simply declare some features as "obsolescent", and those would work by default, but if you use modern module foo, all "obsolescent" features will get disabled.

See Annex B, especially B.3.

@aradi
Copy link
Contributor Author

aradi commented Nov 14, 2019

@gronki You are right, we definitely should not expect Fortran practitioners to be language history experts.

I think, we have to differentiate between different kind of backwards compatibility breaks:

  • Features becoming deprecated/obsolete/deleted because they are considered harmful and newer constructs allow more robust implementation of the same or similar functionality (e.g. GOTO EQUIVALENCE, common blocks, etc.)

  • Semantic changes, where the same code behaves differently dependent which version of the standard one adheres to (e.g. automatic LHS-reallocation, a possible redefinition of assignment on declaration, etc.)

The acceptable frequency for those two categories is clearly different. For the first one, I would wish that nearly each standard would deprecate old eventually harmful features, when new more robust alternatives had been introduces. Semantic changes on the other hand should be as rare as possible. For me, having 1-2 changes every 20-25 years would be acceptable (max. 2 changes during my entire professional career from writing the first code as student till being retired at the age of 67), but of course your opinion may differ on this.

When we had a language version keyword, it would cover both kind of changes. If semantic changes happen only every 2 or 3 decades, Fortran practitioners would not have to be language history experts, because a given code line would be warranted to keep its exact meaning/behavior for at least that amount of time. But if such a change happens, the explicit language keyword at the top of the module would make sure, that it still compiles (and behaves) as before without the necessity of vendor dependent compiler switches.

Additionally, by specifying the language version at the top of your modules, beginners looking at your modern Fortran projects could immediately realize, that the language constructs used in that code are still considered to be good practice. Having something like module, language(f2018) :: my_module would ensure, no language construct has been used, which the Fortran committee considered harmful or obsolete in the year 2018.

@FortranFan
Copy link
Member

FortranFan commented Nov 14, 2019

@certik wrote:

Available options

In Fortran, we do not break old code (#79), so the only way to introduce a breaking feature is to signal to the compiler that the given source file is using the new feature, and by default no signalling is done, so the old feature is being used. So the only question is how to signal to the compiler. Here are the options that I have seen proposed:

  1. File suffix specifying a version (.f90, .f03, .f08, .f18, .f2x, .f2y)
  2. Compiler option for the version (-std=f95, -std=f18, -std=f2x)
  3. Compiler option for a feature (-frealloc-lhs, -fimplicit-none, ...)
  4. Keyword for the version (this issue Language version keyword #83)
  5. Keyword for a specific feature (implicit none, implicit save off (see Deprecate and remove implicit save behavior #40), ...)

Is there fundamentally any other way to consider?
..

Why not allow the nearly 30 years of usage to be of guidance in some matters at least such as the IMPLICIT problem in Fortran? What I mean by this is take advantage of the built-in "signaling" in the language with free-form source and how this option is being used by the practitioners.

Can anyone point to any code-base using free-form source where "implicit typing" is employed per code design? Code after code that uses free-form source tries to have the "IMPLICIT NONE" line in all the scopes yet end up with embarrassment and grief and anger at the Fortran standard when this line is omitted unintentionally with some disastrous consequences in a scope e.g., INTERFACE block which does not inherit the "IMPLICIT NONE" from the host.

Similarly, can anyone point to any code-base that explicitly relies on implied SAVE in its code design?

What the discussion here and elsewhere show is there are no perfect solutions to this pernicious IMPLICIT problem, both with types and the SAVE attribute. The situation is akin to the legendary Gordian knot. Why not then take an obvious solution from history which is to simply cut the damn thing out?

As is, there are crucial differences in syntax (and by extension, I would argue, in semantics) between free-form and fixed source.

So what breaking impact and damage is expected if both IMPLICIT typing and implied SAVE are deleted altogether from free-form source?

@sblionel
Copy link
Member

sblionel commented Nov 14, 2019

While I hate implicit typing as much as any of you, it is such an integral part of the language I don't see having it off by default being taken seriously. ("God is real, unless declared integer") I would also not be in favor of tying this to source form - the whole idea is that the source forms are equivalent.

Users have learned to account for implicit typing with coding and usage standards (requiring use of compiler options that turn it off, for example). I don't see it worthwhile spending time arguing this after so long.

Implicit save is harder - the posts here have focused on initialization in declarations, but that is a recent (!) addition to the language, supplanting DATA, where a large body of code expects SAVE semantics and breaks if that doesn't occur. Again, this is an integral aspect to the language and I don't see changing it worthwhile. I'd be in favor of adding IMPLICIT (NOSAVE).

@FortranFan
Copy link
Member

.. implicit typing .. it is such an integral part of the language I don't see having it off by default being taken seriously. ("God is real, unless declared integer")

Jokes apart, "implicit typing" only belongs to the past history of this language. It's not "integral" in any way; every scope of a Fortran unit one see out there strives to negate it. There is so much time and energy wasted to inform coders to avoid it (an example here) and there is so much loss of goodwill toward Fortran on account of this 'feature', Fortran is seriously hurting itself by retaining this aspect.

I would also not be in favor of tying this to source form - the whole idea is that the source forms are equivalent.

The two source forms are not equivalent, one only has to look at the syntax and semantics of line continuation which is so important given the restrictions of fixed-form source.

Users have learned to account for implicit typing with coding and usage standards (requiring use of compiler options that turn it off, for example). I don't see it worthwhile spending time arguing this after so long.

No, many a new user does not learn to accept this and that's the point. Trying to get new coders to work on Fortran codes, particularly in industry, is a huge challenge. Few join with any background in Fortran, few schools teach Fortran. Then one loses them at 'hello' itself, meaning at the very beginning stage when one stresses to them the need to type 'implicit none' in every scope.

Implicit save is harder - .. this is an integral aspect to the language and I don't see changing it worthwhile. I'd be in favor of adding IMPLICIT (NOSAVE).

To reiterate, both implicit typing and implied save introduce 'silent errors' (to borrow the term by @gronki), these two aspects are pernicious, they bring tremendous vulnerabilities to Fortran code. To argue they are integral to the language in year 2019 conveys an entirely wrong message about Fortran.

Separately, the added risk that will get introduced with yet another addition to the IMPLICIT statement was mentioned upthread just yesterday by @gronki. That's what made me think further about using the source form itself has a signal to the compiler.

I inquire again of the several questions in #83 (comment).

  1. Can anyone point to any code-base using free-form source where "implicit typing" is employed per code design?
  2. Can anyone point to any code-base that explicitly relies on implied SAVE in its code design?

@klausler
Copy link

I would also not be in favor of tying this to source form - the whole idea is that the source forms are equivalent.

The two source forms are not equivalent, one only has to look at the syntax and semantics of line continuation which is so important given the restrictions of fixed-form source.

Any Fortran program can be represented in both source forms without differing in their semantics as the term is otherwise universally understood. Altering the language so that source form affects semantics is a non-starter.

@certik
Copy link
Member

certik commented Nov 14, 2019

@FortranFan People use implied save unfortunately. For example here:

https://github.com/abinit/abinit/blob/056c00bbb1a2f55c1b9851bf873a329dd8edf3e2/src/95_drive/m_gstate.F90#L2614

I am sure there are tons of other places. Looking at this particular code, I do wonder if it's intended, or if it's a bug.

@FortranFan
Copy link
Member

@certik wrote:

@FortranFan People use implied save unfortunately. For example here:

https://github.com/abinit/abinit/blob/056c00bbb1a2f55c1b9851bf873a329dd8edf3e2/src/95_drive/m_gstate.F90#L2614

I am sure there are tons of other places. Looking at this particular code, I do wonder if it's intended, or if it's a bug.

Thanks much, I've contacted ABINIT to find out whom I can reach out to learn about this particular section of their code. My hunch looking at that code is their intent is different and it's not a significant use of implied SAVE, but I hope to find out more.

@gronki
Copy link

gronki commented Nov 14, 2019

It would be extremely ironic if, contrary to all the fears about backwards compatibility, silently removing "implicit save" would fix more codes than it would break. ;)

@certik
Copy link
Member

certik commented Nov 14, 2019

I must say I am leaning towards @FortranFan's idea of simply removing implied save, exactly as I initially proposed in #40. I posted there a comment how it can be done: #40 (comment). What I like about #40 is that we actually very conservatively discuss the various pros and cons, and breaking backwards compatibility is a huge con that we all agree on, and we try to figure out how to best proceed. But the pros are very compelling and in the end, if done right, might outweigh the con.

(With a backwards compatibility breakage like this, we absolutely need to have a prior compiler implementation and actually test it on real production codes. And we need automatic facility to find implicit save usage in codes and fix it, automatically. That's a given requirement.)

@cmacmackin
Copy link

cmacmackin commented Nov 14, 2019

And we need automatic facility to find implicit save usage in codes and fix it, automatically.

This would be quite easy to write. We wouldn't even need a full parser. All that would be necessary is the ability to:

  • Detect what sort of program unit you are in (program, module, subroutine, function, etc.)
  • Detect assignment within variable declaration
  • Detect attribute lists within variable declaration
  • Detect SAVE :: statements

FORD already does essentially all of this just with regular expressions, although the way it manipulates the source code internally means it would not be suitable for performing the fixes. However, I reckon I could write a single file script along these lines in about a week if I could devote the time to it.

@certik
Copy link
Member

certik commented Nov 14, 2019

@cmacmackin yes, it's absolutely doable. All I am saying we have to do it, test it with real codes, and provide good guidelines how to upgrade, as part of any such change.

@FortranFan
Copy link
Member

FortranFan commented Nov 15, 2019

@FortranFan People use implied save unfortunately. For example here:

https://github.com/abinit/abinit/blob/056c00bbb1a2f55c1b9851bf873a329dd8edf3e2/src/95_drive/m_gstate.F90#L2614

I am sure there are tons of other places. Looking at this particular code, I do wonder if it's intended, or if it's a bug.

@certik, I'm currently inclined to think the authors of ABINIT meant for that variable to be an entity of the module rather than a local in that procedure, so I'm unsure as to whether I can accept ABINIT is a code-base that explicitly relies on implied SAVE in its code design.

module m_gstate
      ...
      integer, target, protected, save :: ndtpawuj = 4
      ..
   contains
      ...
      subroutine subroutine pawuj_drive( ..
      ...

And I'm not convinced the Alexandrian measure of removing implied SAVE from the language will 'break' ABINIT: with such a change in the language and with current ABINIT code remaining as-is. a local variable ndtpawuj will come into existence with a value of 4 and with a TARGET attribute each time the procedure 'pawuj_drive' is invoked and that's all ABINIT might care about.

I will be really surprised to find FOSS-type of code on GitHub, SourceForge, etc. rely on implied SAVE, or if they do, anyone throwing a fit about refactoring to include the explicit SAVE attribute with variable declarations. It's a matter of mindset, a certain openness to adopt FOSS which also enables good sense to be explicit in everything in order to maximize clarity for the widest audience of one's code.

The issue is with unverifiable claims about code held privately. Now I happen to work with a lot of teams in industry who have such 'private/proprietary' code. If there is one thing I know for sure it is that none of the critical 'private/proprietary' code are going to remain in Fortran for long unless this language keeps apace fully with modern trends and truly enhances type safety and reduces code vulnerabilities, particularly as cloud and parallel computing come to the fore. Imagine one line somewhere hidden with an implied SAVE also has the potential to cause data race conditions or lock scenarios with the risk of rendering a Fortran library not thread-safe and thus unfit for consumption in a cloud service.

I too can attest and that is to so many code-bases moving away Fortran permanently. An initiative such as #40 by @certik et al. can be a truly positive intent to help reduce this 'bleeding'.

@certik
Copy link
Member

certik commented Nov 15, 2019

@FortranFan what we absolutely have to prevent is the Python 3 fiasco (e.g., just saw this: https://stackoverflow.blog/2019/11/14/why-is-the-migration-to-python-3-taking-so-long/), when Python 3 was released 11 years ago, and there are still many huge code bases in Python 2 that just cannot upgrade, and the amount of effort it took Facebook, Dropbox, etc. to port is just unbelievable, and all pretty much for nothing.

At the same time, I agree with you, we want to fix some of these things. We just need to figure out how to do it carefully.

@gronki
Copy link

gronki commented Dec 13, 2019 via email

@gronki
Copy link

gronki commented Dec 13, 2019 via email

@klausler
Copy link

So would you also disapprove of an IMPLICIT NOSAVE statement?

@gronki
Copy link

gronki commented Dec 13, 2019 via email

@klausler
Copy link

Could you be placated if the syntax for a non-SAVE (C-like) initialization expression syntax were distinct from the current = expr and the STRICT keyword had the effect of disabling the current syntax?

@FortranFan
Copy link
Member

With all my hate for implicit save, yes. ..

See #40 (comment)

Per the syntax adopted by the standard since 2008 revision, one would think it would be

   implicit none (save)  ! Given the standard has gone with implicit none (type/external)

Short of deleting certain pernicious features such as implicit typing and implied save in one strong Alexandrian cut of this Gordian knot of an issue in Fortran, I still prefer as I suggested earlier:

   explicit all

@sblionel
Copy link
Member

I am with @gronki on "one language".

How about implicit none (*) as meaning everything has to be explicit? I think that would be a better fit for the language and would keep all the related things in one place.

@jacobwilliams
Copy link

I like implicit none (*) which is especially Fortranic since is adds another user of *. 😃

I also like explicit all.

I'm not looking forward to having to type implicit none (type, external) everywhere, or explaining that to newcomers.

@gronki
Copy link

gronki commented Dec 13, 2019 via email

@aradi
Copy link
Contributor Author

aradi commented Dec 13, 2019

I could leave with both, implicit none (*) and explicit all with stronger preferences towards the latter.

Let's assume, the keyword is introduced into the language. What would the processor then do with

subroutine test
  integer :: i = 5
end subroutine test
  • Compilation error? Then we eliminate the possibility of having assignment / initialization on declaration, unless a new special notation is introduced for it. I personally find somewhat confusing using a different operator for assignment on declaration as for assignment in the code, and for beginners just an annoying nuance to remember.
  • Would it be treated as a normal assignment? That would be my preference, but the "one language" rule would be broken again.

@gronki I develop a quantum mechanical simulation package in Fortran and also teach Fortran to students, so I know what you're talking about. Still, I prefer a language, where teaching does not have to start with teaching work-arounds due to historical heritage.

@FortranFan
Copy link
Member

@certik commented on Nov 14, 2019:

@FortranFan People use implied save unfortunately. For example here:

https://github.com/abinit/abinit/blob/056c00bbb1a2f55c1b9851bf873a329dd8edf3e2/src/95_drive/m_gstate.F90#L2614

I am sure there are tons of other places. Looking at this particular code, I do wonder if it's intended, or if it's a bug.

@certik, as I had mentioned in #83 (comment), I'd contacted ABINIT team about this.

Their software engineering support contact has gotten back to me the Fortran developer for their PAW component (you'll know m_gstate.F90 is part of this) is going to look into making that variable a module-level entity with an explicit SAVE attribute to make the intent clear for readers.

@dacarnazzola
Copy link

dacarnazzola commented Jun 9, 2023

In language version keyword is an excellent idea, and one I hope to see added to future Fortran standards. We should not have to rely on vendor specific compiler flags, which also complicates build systems for large + long history code bases.

@PierUgit
Copy link
Contributor

I don't if this topic had some advances, but I really think that something should be planned for F202Y. Among the options listed by @certik, the language version keywords looks the most appealing to me, even if not perfect. And I also agree with @gronki, in that changing the version keyword should not change the behavior of a code that compiles.

One should try listing the possible objections and drawbacks to this system, and how it can be improved. @sblionel has cited two possible issues:

  • the name (year) of the revision is generally known little before the actual publication: it doesn't look too bad, as compilers could adopt a common temporary convention (such as "202Y") until the final name is known.
  • articulation with the Technical Specifications (TS) that are published between 2 revisions.

One question would be the minimal scope of the version keyword: routine/function? Source file? Module? How objects compiled with different versions would works together? For instance would a version="Fortran90" code be allowed to call a version="Fortran2018" routine with assumed rank or value dummy arguments?

(The latter example is purely rhetorical, as I think that the first and only keyword version that should be introduced in 202Y is version="Fortran202Y", and codes without a specified version would conform to F2018, be they F2018 or any other process revision).

@sblionel
Copy link
Member

Some random thoughts.

  • Most compilers don't have options that select behaviors/features among the various standard revisions. They do tend to have options that diagnose use of features not present in a particular revision. There are often options that say "do it the current way/do it the old (previously unspecified) way", affecting semantics, not syntax.
  • This discussion is predicated on adopting the notion that programmers would be required to declare the minimum revision of the standard in order to use particular features, combined with disabling obsolescent or deleted features. I think this would be a very hard sell to the committee and to the larger user base. Personally, I would not support it.
  • I do not agree that compatibility with the past prevents advancement for the future and wish that the community here would direct its efforts into developing useful features rather than punishing existing users.

@PierUgit
Copy link
Contributor

@sblionel I'm a bit surprised, because some time ago in this discussion you were apparently not opposed to such a proposal. And you even proposed something somehow similar in #280 ...

I definitely do not think either that the compability with obsolescent features is the main obstacle to new features. Nonetheless it can happen in some cases, for instance: the idea of turning every control structure into an implicit block is not possible (independently from the fact it is a good or bad idea) because data statements are allowed to appear anywhere in the execution part, which is now obsolete.

And I don't see that as "punishing existing users". There would be absolutely no change to bring to existing codes, even not adding a version keyword, as long as they are not modified to use new features of F202Y. And if they are, 99.9% of the times it would be just adding something like version="F202Y", and there would be changes to bring to the codes only in the case they use delete features.

@sblionel
Copy link
Member

@PierUgit the proposal you link to is different, though I'm unsure I would still support it. Can you tell me of another popular programming language that has this notion of syntax to identify language revision? I don't know of any.

@PierUgit
Copy link
Contributor

PierUgit commented Jan 22, 2024

@sblionel It's different, yet similar. Both aim at disallowing the simultaneous use of obsolescent and new features in a given scope (to be determined). Rust has such a system to identify the langage revision, although not in the source code itself (but I find the source code a better place for that, actually).

I would argue also that among the popular languages I don't know any where there is the same strong emphasis on the backward compatibility as in Fortran (apart waht is claimed in Rust, but it's a very recent language). I like that, and I don't want the backward compatibility to be thrown out, but the language version specification looks to me an interesting approach: no feature has to be deleted, actually.

@certik
Copy link
Member

certik commented Jan 22, 2024

In a way Python does it too: you specify which exact Python version you depend on, the biggest difference of course Python 3 and 2, but there are actually slight backward incompatibilities in corner cases even in versions like from 3.10 to 3.11 and so on. In both Rust and Python you specify which exact version you want in the configuration files, not the source code.

Compilers however already allow to be backwards incompatible via compiler options (for example almost all compilers allow disabling implicit typing), so we can already create tools that specify backwards incompatible features via configure files.

@klausler
Copy link

As a practical matter I don't think that a language extension to disable older features is warranted -- you can just not use those features, and depend on options or tools to ensure that you're not using them. But a language extension to disable newer semantics might be useful when the standard changes in such a way as to silently alter the behavior of conformant code (F'2023) or to add behavior that induced run-time overhead on conformant code (F'2003). I put a warning message into Flang for the F'2023 case, and programmers have learned to work around the older one. so the need for a new language extension to protect compatibility against future standard changes depends on how likely you think they are going to be in the future. (And the odds of getting the committee to approve a language extension like that are extremely low.)

@certik
Copy link
Member

certik commented Jan 22, 2024

Indeed, compiler warnings are extremely effective. We put warnings of the type "style suggestion: Use '>=' instead of '.ge.'" into LFortran and it makes users update their codes. So with proper tooling, one can facilitate modernization and avoiding many pitfalls.

@PierUgit
Copy link
Contributor

PierUgit commented Jan 23, 2024

As a practical matter I don't think that a language extension to disable older features is warranted -- you can just not use those features, and depend on options or tools to ensure that you're not using them.

It's beyond that. I would allow deleting from a revision some obsolete features that are an obstacle to some useful new features, or features that are widely considered harmful (like the implied save). But these features would still be officially part of the standard, using previous revisions. e.g.

integer :: i = 0

would still compile

langage_version F202Y
integer :: i = 0

would fail compiling

langage_version F202Y
integer, save :: i = 0

would compile

Some safeguards would be needed:

  • in contrast to Python, we should not introduce behavior changes. It's acceptable to have an old code that no longer compiles under a specified recent revision, but it's unacceptable if it compiles and produces different results per the standard
  • deletions should remain motivated by strong technical reasons, not just "because we can"

@certik
Copy link
Member

certik commented Jan 23, 2024

@PierUgit for this case, LFortran does the following:

warning: Assuming implicit save attribute for variable declaration
 --> a.f90:6:16
  |
6 |     integer :: i = 0
  |                ^^^^^ help: add explicit save attribute or initialize in a separate statement

Which is enough I think to catch this common mistake for newcomers. In "pedantic" mode we can even turn all such warnings into errors. For legacy codes you use some compiler options to compile them without warnings.

Let me know if you think this completely fixes this particular problem. You can then suggest another issue and we can explore a solution using a compiler or tooling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Clause 8 Standard Clause 8: Attribute declarations and specifications
Projects
None yet
Development

No branches or pull requests