Why I can access private module variable in this program?

@RonShepard Oh, then it is obvious that I am wrong. I apologize for my unfortunate and misleading comment!

I personally agree with this opinion, but apparently the LFORTRAN authors have decided otherwise. It remains to be seen what are the consequences of this decision. It is possible that it will be only a minor annoyance, or it is possible that it will be a disaster, resulting in people avoiding the compiler because of that choice. Or it could be something in between these extremes.

1 Like

Mega kudos to @certik et al. and the LFortran contributors for transcending the standard on the implicit mapping aspect and making implicit none the processor default. May you always remain forward-thinking like this. Here you have one new community member - @imronuke - who has expressed interest in the same.

As to certain processors requiring certain compiler options to conform to the standard, readers may note that is modus operandi for the Intel compiler which otherwise claims full compliance with the current standard but yet requires explicit specification of -standard-semantics to comply. For the longest period of time, Intel Fortran compiler needed the same option with reallocation upon assignment introduced in Fortran 2003 which led to years of consternation with teams I work with, these teams managed to move away from Fortran successfully by the time Intel made it the default.

Implicit mapping remains a horrible mistake in the standard post-Fortran 90. For leading processors such as LFortran to override the standard on this is a giant step forward for Fortran.

  • RECURSIVE is the default per current standard with which Intel mentions IFORT as conformant but the compiler does not adopt the semantics by default:
C:\temp>ifort /c p.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.8.0 Build 20221119_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.

p.f90(9): error #6437: A subroutine or function is calling itself recursively.   [FIBONACCI_NUMBER]
            num = fibonacci_number(n-1) + fibonacci_number(n-2)
------------------^
p.f90(9): error #6437: A subroutine or function is calling itself recursively.   [FIBONACCI_NUMBER]
            num = fibonacci_number(n-1) + fibonacci_number(n-2)
------------------------------------------^
compilation aborted for p.f90 (code 1)
  • whereas with standard-semantics
C:\temp>ifort /standard-semantics p.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.8.0 Build 20221119_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.

Microsoft (R) Incremental Linker Version 14.34.31937.0
Copyright (C) Microsoft Corporation.  All rights reserved.

-out:p.exe
-subsystem:console
p.obj

C:\temp>p.exe
 Fibonacci number sequence #42:  267914296

C:\temp>
Click for code
   print *, "Fibonacci number sequence #42: ", fibonacci_number( n=42 )
contains
   elemental integer function fibonacci_number( n ) result(num)
      integer, intent(in) :: n
      select case ( n ) 
         case ( 0:1 )
            num = n
         case default
            num = fibonacci_number(n-1) + fibonacci_number(n-2) 
      end select
   end function
end

With most of the other processors, they may take forever to comply with any standard revision, or not even bother with anything past Fortran 95 standard even if they offer a host of other features from later standards. But since they remain effectively nonconformant on account of missing features, the standard compliance becomes meaningless for them and they essentially reduce themselves to pick-and-choose processors. Under the circumstances, them sticking with implicit mapping from FORTRAN I will have as much meaning with posterity as age before beauty!

I commented out the private variable as a temporary workaround until we fix Disallow importing private variables · Issue #1239 · lfortran/lfortran · GitHub.

This is true, but you did not state the reason why. Mature compilers have a legacy, and in cases such as reallocation on assignment and the implicit recursive attribute, compilers may have already implemented their own extensions that were inconsistent with the revised fortran standard, or compliance with the standard was known to result in significant performance penalties compared to their legacy choices. Thus, the compiler writers made choices between their own backward compatibility and the forward compatibility with the new standard. I may or may not agree with those choices, but I understand why they did what they did. To be fair to all these compiler developers, you should have mentioned that in your reply.

However, in the case of LFortran, there are no legacy features which present this dilemma. It is a brand new compiler, and they could just as well have chosen to be 100% consistent with the current fortran standard by default. That would have been welcomed by most fortran programmers, knowing that they do not need long lists of compiler options in order to compile a standard conforming code. But the LFortran authors have chosen to not do that, and to instead adopt nonstandard default behavior; in fact, it is nonstandard behavior that is already known in advance to break legacy codes. As I said before, the consequences of that decision remain to be seen. It could be a disaster for acceptance of the compiler, or it could be seen as a forward-thinking feature. I personally would have liked to have seen a 100% compliant compiler from the beginning, with extensions that allow me to adapt to various legacy behaviors.

With LFortran, it is a far more positive welcome gesture to make implicit none the default rather than perpetuating the implicit mapping mistake from the standard

As to the reasons why a processor such as Intel would do so, it was ostensibly that some customers were more important than other customers. The teams I have worked with wanted standard compliance by default, they even purchased support subscription with Intel support and they requested Intel follow standard behavior by default, however Intel did not want to make certain other customers “unhappy” - so Intel did not oblige.

I think it would be quite easy to create an executable (or a symlink), say lfortran-std (or perhaps even lfortran-f18, lfortran-f23, etc.), that would by default conform to the given standard.

@RonShepard, @kargl would you like to have such an executable?

1 Like

Implicit mapping and implied SAVE are two absolutely horrendously bad semantics in the current ISO IEC standard for Fortran that I will always recommend for processors to not support by default.

My point earlier is none of the compiler implementations are capable or bold enough to support by default the good features of the Fortran standard any way, I showed an example above with RECURSIVE, such a useful aspect to write reentrant and thread-safe library code that forms the basis for concurrent and parallel computing which is what will be paramount, sooner than later.

Then why not turn the thinking around on the bad features and drop them by default in compiler implementations. Such a position will do a ton of good to the actual practitioners of Fortran, most of the practitioners are yet to use Fortran, they will only start their practice for the first time later today, or tomorrow, or next year, maybe a decade, or a generation away from now. These practitioners will not care for I-N starting letter name as integers and as default reals otherwise - one can guarantee that. Compiler implementations should be looking out for these practitioners, not those who can’t and will not change and who mostly write nonconforming code while insisting on “ICARUS is an integer unless declared otherwise, GOD is real unless declared otherwise”!

Those maintaining codebases making use of implicit mapping (which is mostly some legacy codebases that are already broken in more ways than one when it comes to standard conformance) and/or implied save can make use of compiler options to get the standard behavior i.e., the behavior which is bad, bad, bad and which should not be used in anything but dead codebases …

2 Likes

Indeed, this is a temporary situation to cope with significant changes in the standard, before the compiler can eventually fully catch up the standard. Intel developers never meant to have a permanent option needed for reallocation upon assignment (and as a matter of fact it is no longer needed). The same for defaulting recursive: it’s just a matter of time.

Here we go again :stuck_out_tongue:

Someone writes, “Here we go again,” to my comment, but strangely not at the first post five days ago which indeed should received that quip … at least @jacobwilliams remarked rightly, “Implicit typing strikes again”!

Few seem to care here is another Fortran community member in @imronuke whose precious time got wasted because of a decision sometime during the first half of 1950s that remains in the standard and for which a few insist wrongly that it must continue to do its damage for much longer, even forever …

1 Like

Hi All,

Sorry for sparking this never ended debate. I hope my comment is the last comment in this thread. Lets use our times and energies for somethings more beneficial.

Best regards,

2 Likes

We’ll get there, we are making good steady progress. We recently got Minpack working, now we are focusing on fpm. Once we fix all issues you reported, I’ll let you know.

This brings to mind an excerpt from the classic book Software Tools (1976) by Kernighan & Plauger:

Far too many programmers are red pencillers. Some are literal red pencillers who do things by hand that should be done by machine. Others are figurative red pencillers whose use of the machine is so clumsy and awkward that it might as well be manual. One of the purposes of this book is to show how to build tools – programs to help people do things by machine instead of red pencil, and how to do them well instead of badly.
[ … ]
A large part of what programmers do every day is text processing – editing program source, preparing input data, scanning output, writing documentation. These activities are at the heart of programming; as much as possible, they should be mechanized.

With Intel Fortran, the flag to get standard semantics can be added to an ifort.cfg configuration file and installed in the same directory of the compiler executable. Alternatively, it can be placed in a local directory and pointed to by the IFORTCFG environment variable. Naturally, this has to be documented in some place where team members can read it.

Admittedly, the workaround above fails if you have varying option requirements for different projects, e.g. some which expect -standard-semantics and others that don’t. In that case you can still use response files. A response file with the required settings can be checked into the project repository and used when the project is configured. For example with CMake you would use: cmake .. -DCMAKE_Fortran_COMPILER="ifort; @settings", where settings is the response file. You can then keep editing your Fortran source code and re-building the project ad-infinitum (or until you change the configuration).

While I agree it would be more desirable to have standard conformance as the default, as this thread demonstrates views on this topic are different, and different compilers will require different fallbacks. Perhaps more important than the default behavior is having an option to change it easily to suit your needs.

2 Likes

It is also a hassle when different source files within a project require different default behavior, particularly when the options are inconsistent and treat standard conforming code differently (such as changing the default implicit mapping). I will point out again that if the code changes are incorporated directly into the source file, then the problem is solved once and for all – there is no need to hand-pick the required options for the compilation of each source file every time the file is compiled.

Or given the specific context of this thread, you can say those seeking implicit mapping can avoid the vagaries of “different compilers will require different fallbacks” in a conforming manner, possibly like so:

module foo1
   implicit integer(i-n), real(a-h, o-z)
   private
   real :: x = 1
end module

module foo2
   use foo1
   implicit integer(i-n), real(a-h, o-z)
   real :: y = 2
end module

program test
   use foo2
   implicit integer(i-n), real(a-h, o-z)
   x = 5
   write(*,*) x, y
end program
C:\temp>gfortran p.f90 -o p.exe

C:\temp>p.exe
   5.00000000       2.00000000

One of the most common complaints I hear about languages is how incompatible changes caused wasted time or problems, so complaining about backward compatibility being supported in the Fortran standard or via the compilers is bemusing.

As mentioned the Intel compilers gives more options to configure the default behavior than any compiler I can think of. We used the default config file for years to specify preferred default behavior; but most if not all Fortran compilers easily allow you to specify automatic implicit typing, forcing explicit interfaces, and so on.

It is also trivial to make wrapper scripts (we used to use the name “F90” for years) that used the preferred options, including loading a series of libraries so they were available by default, etc.

“F” was an example of a Fortran environment that did not support deprecated features, and was well implemented via g95 at least. Anyone proposing old features not be available should study the history of “F”.

Working with millions of lines of code written by many different authors I have only seen a few bugs due to having implicit typing.

So I cannot believe the amount of time spent in the forum discussing implicit none. It takes five minutes to make your environment have a default of implicit none. Unless Fortran quits adding new features any attempt to not allow use of deprecated features becomes a problem, as it freezes the usage at a particular version or requires immediate code changes each time a change occurs that creates a new preferred approach. The current solutions are a reasonable compromise.

The most important thing is whether the compilers give you the ability to allow a deprecated usage, optionally warn about it, or optionally make it an error. Most do an exemplary job of allowing you to do that.

Make a choice, create a wrapper or config file and use Fortran the way you want. Read the documentation and you will see the solutions you want are available with a few compiler switches.

The community can easily provide a solution by providing such wrappers. I would suggest anyone who knows what the perfect behavior is to make a wrapper or configuration file and provide it for general use so new users can easily be pointed to it to help prevent the errors often encountered by newbies and casual users.

fpm(1) provides a great vehicle for this. Make a pull request that adds a feature to fpm so you can select the preferred behavior and lets see how well it is used.

3 Likes

I was surprised to not see descriptions of ways to simulate namespaces with user-defined types when importing a lot of values. If you want to import a lot of constants, not have to list them all one by one on a “use, … only:” statement, but easily distinguish them from locally scoped values something like this is handy:

module M_constants  
use,intrinsic :: iso_fortran_env, only : dp => real64
private
real(kind=dp),parameter :: &
   gamma               = 0.5772156649015328606065120_dp, &
   e                   = 2.7182818284590452353602874_dp, &
   Golden_Ratio        = 1.6180339887498948482045868_dp, &
   euler               = 0.5772156649015328606065120_dp, &
   pi                  = 3.1415926535897932384626433_dp
type constants;  real(kind=dp) :: pi,gamma,e,golden_ratio,euler; endtype constants
type(constants),parameter,public :: uc=constants(pi=pi,gamma=gamma,e=e,golden_ratio=golden_ratio,euler=euler) 
end module M_constants

program test_universal_constants
use,intrinsic :: iso_fortran_env, only : dp => real64
use M_constants, only : uc ! import a lot of values 
real(kind=dp),parameter :: e=uc%e ! give a local name to a constant

   ! use locally renamed
   print *, 'e=',e

   ! or just use it
   print *, 'gamma=',uc%gamma  

   ! or rename it with ASSOCIATE
   associate (gamma => uc%gamma)
   print *,'gamma=',gamma
   end associate

end program test_universal_constants

and surprised no one discussed pros and cons of values you can only change via a procedure call and/or having values that are private that are changed via procedures and so on when there were discussions of the pros and cons of various ways of using “global” values from modules.

I like importing constants this way in particular, as to me it is much clearer that uc%e is my universal constant “e” rather than just importing the variable “e”.

Even with the current fortran features, one could name the constant something like uc_e in the first place, and then it is clear by its name where it came from, even without only: clauses. If appropriate, its local name can be changed in the use statement or with an associate block.

However, there are still open questions regarding the kind values of the original definitions, and the numbers of digits used to define them. Should they always be specified to the maximum precision of the largest kind? That presents problems involving unintended kind conversion when they are used directly in expressions. Should local parameters of the correct kinds be defined in terms of these long parameters? What about the definitions of physical constants, such as the speed of light, or Boltzmann’s constant, or Avogadro’s number, which change every 10 or 20 years? How should old and new values be differentiated and made accessible? I don’t know the answers to any of these questions.