Question about the use of common blocks

The example that you have shown involves an external subprogram for which an implicit interface is correct. In such special cases, providing an interface block is not necessary. Providing the redundant interface may have no effect on the generated object code.

Well, an intolerable risk with this is, as I hinted upthread, the interface can be inconsistent with the implementation.

I personally think it’ll be better to have some tooling, perhaps LFortran with some inventive solution by @certik, that motivates library authors to refactor such (“legacy”) implementation(s) with a SUBMODULE wrapper that then ties it with the (“interf”) module.

1 Like

@msz59 Rather than a module, I attempted to create an include file of interfaces for all routines in a library (then the project) I was developing, with the aim of assisting the compiler to check each routine call.

My purpose was to identify any use of a routine in the project that didn’t comply with the latest interface definition in the include file. This was meant to include the actual routine. This approach could assist finding hidden errors while developing the project. This include file was to serve as a project definition of routines being developed.
Unfortunately this is/was not allowed by the Fortran standard, so the approach failed.
I understand ifort provides a similar background functionality, but I don’t think gFortran does, although it could also depend on the order of files being compiled during a rebuild.

Features like this for the compiler and the linker are not well addressed by the Fortran standard.

LFortran can see all the code once you compile the main program. So it should be possible to check that interfaces actually conform to the implementation. There might be some corner cases where this might fail, and perhaps it is not standards conforming, but it might work in most cases.

What do you mean by that? Implicit interface for a subroutine is IMHO a no-op.

I guess this is a feature built into the very concept of interface construct. It is meant to provide info about an external subprogram for the current program unit, thus prone to inconsistency. The only safe method is to put subprogram into a module but then it does not need any additional interface block in the unit which uses it (through the module).

This looks to be a bit of overreach. The only time all the “code” is visible is when linking.
I have hundreds of .f90 files, tens of .bat files for compile then a final .bat file for linking.
What we want is being able to cross reference all the “hundreds of .f90 files” for the compiler to check interfaces, which is basically a static analyser. Modern Fortran compilers are doing more of this.

An alternative I have tried is to list the “hundreds of .f90 files” as a list of includes in a single .f90 file for the compiler to compile all in one .o file and so provide the information to do the cross-check, although I was not confident this achieved the aim. This include list can be readily generated from the link script.

Right, you don’t need to see the whole code to compile. But you can, so it’s possible to write such a tool.

In the absence of module subprograms, it sounds like what you need is an interface block generator and a source tool that replaces every EXTERNAL fsub (you do use EXTERNAL statements?) with a line
USE interfaces_mod, ONLY: fsub

For any other readers interested in this, note the above attempt by @JohnCampbell with INCLUDE file likely involved trying out an interface-to-self that the language does not support directly.

Say a library of “routines” with the “legacy” approach of external subprograms has all of one procedure as follows where the author, for whatever reason, refuses to refactor it to a MODULE subprogram!

subroutine setcalcdata( n, x, irc )
   integer, intent(in) :: n
   real, intent(inout) :: x
   integer, intent(out) :: irc
   if ( n > 1 ) x = 0.0
   irc = 0
end subroutine 

Now the library author wants to write an interface body for the same:

interface
   subroutine setcalcdata( n, x, irc )
      integer, intent(in) :: n
      real, intent(inout) :: x
      integer, intent(out) :: irc
    end subroutine 
end interface

Fortran language standard, as obvious to many, does not support trying to do interface checking of such a body along the following lines:

subroutine setcalcdata( n, x, irc )
   integer, intent(in) :: n
   real, intent(inout) :: x
   integer, intent(out) :: irc
   interface
      subroutine setcalcdata( n, x, irc )  !<-- Not allowed
         integer, intent(in) :: n
         real, intent(inout) :: x
         integer, intent(out) :: irc
       end subroutine 
   end interface
   if ( n > 1 ) x = 0.0
   irc = 0
end subroutine 

So if the interface were to be in a file, say Isetcalcdata.f90, one cannot do the following either:

subroutine setcalcdata( n, x, irc )
   integer, intent(in) :: n
   real, intent(inout) :: x
   integer, intent(out) :: irc
   include "Isetcalcdata.f90" !<-- Not allowed
   if ( n > 1 ) x = 0.0
   irc = 0
end subroutine 

On the other hand, note there is indeed another option with INCLUDE files that can help library authors if they want to make explicit interfaces available to their consumers whilst retaining their code organization if they so desire strongly (some do). There are a few limits to this but it should mostly work with legacy code.

So the library can eschew the above interface and do the following:

module xxx_m
contains
   include "setcalcdata.f90"
   .. ! other such include lines
end module

So among the limitations of doing this is possibly long compilation times and circular dependencies.
The latter can be problematic and involve refactoring of code that library authors are loathe to do.

But having written interface bodies as some library authors have done since Fortran 90 (30 years now), they can try to employ the same and consider using SUBMODULEs to alleviate the issues with compilation times and circular dependencies. It starts with prepending MODULE keyword:

module xxx_m
   interface
      module subroutine setcalcdata( n, x, irc )
         integer, intent(in) :: n
         real, intent(inout) :: x
         integer, intent(out) :: irc
       end subroutine 
   end interface
end module
submodule(xxx_m) setcalcdata_sm
contains
   module &
   include "setcalcdata.f90"
end submodule 

C:\Temp>gfortran -c xxx_m.f90

C:\Temp>

So if anyone has the same thoughts as these quoted comments, note the standard committee refrain will be RTFM, rather than trial-and-error.

Gfortran accepts it but not Ifort:

ifort -c xxx.f90
xxx.f90(13): error #5082: Syntax error, found ‘INCLUDE’ when expecting one of: TYPE MODULE ELEMENTAL IMPURE NON_RECURSIVE PURE RECURSIVE FUNCTION …
include “setcalcdata.f90”
—^
compilation aborted for xxx.f90 (code 1)

Which seems to be conformant to the Standard requirement that

7 When an INCLUDE line is resolved, the first included statement line shall not be a continuation line and the last included statement line shall not be continued.

I have assumed that you used setcalcdata.f90 file containing:

subroutine setcalcdata( n, x, irc )
   integer, intent(in) :: n
   real, intent(inout) :: x
   integer, intent(out) :: irc
   if ( n > 1 ) x = 0.0
   irc = 0
end subroutine

which is the code taken from upthread, with the illegal (there) include line removed and the intent of variable x corrected to inout.

1 Like

Good catch, my bad I didn’t RTFM.

Yes, that’s too bad, as your idea was pretty clever. I wonder if the Standard could relax the requirement (point 7 above) only to forbid literal continuation lines (i.e. starting with &)?

PS. Upon writing this replay, I got a popup box saying: You’ve posted more than 21% of the replies here, is there anyone else you would like to hear from? Should I shut up and start listening? :slight_smile:

I am getting these all the time. It’s very annoying.

No, please keep discussing. 20% is nothing. If you get to 90%, then maybe. :wink:

Honestly that will depend on who puts together the proposal and how; for many a mere mortal such as yours truly making the request, it will be a categorical put-down with all kinds of invented reasons as to no.

Not to mention the decade or longer wait: get the proposal in time for consideration for what will be a very narrow time window for Fortran 202Y; if it survives, the relaxation may be part of publication circa 2030 and another 5+ years for enough compilers to support it.

Those who see the situation as “glass half full” can start here: GitHub - j3-fortran/fortran_proposals: Proposals for the Fortran Standard Committee

Well, gfortran already does it :wink:

Jokes aside, there is another way, also non-standard, but apparently supported both by gfortran and ifort - use C preprocessor directives. It is enough to prepend include with # character and rename the file from *.f90 to *.F90 (unix-like OSes, not sure how that could be in Windows).
This trick (use of cpp) has been in Gnu Fortran (and possibly in many other compilers) for decades, I still use F77 code written in the 1980s containing many *.F source files. Even emacs editor recognizes #include and #define lines in Fortran mode.
Maybe (I know, I am just a mere mortal :slight_smile: ) the use of C preprocessor could be officially made a part of C interoperability?

1 Like

Well, maybe the wording was not precise. I mean there is no requirement that the compiler (or its ‘driver’) does the preprocessing. I now see that this would be probably not possible anyway, so forget my idea.

I strongly agree with @FortranFan that COMMON blocks are error-prone. We have tools to check them and often find problems. In their defence:

i. There are tools to check them. fpt and PlusFort will certainly check them.

ii. They can be useful in organising large data structures to be exported to other languages
(usually c).

iii. We have worked on several simulations where a data structure has been built to record the COMMON address, type and kind of every variable and to get or poke the values under interactive control. To do this with variables in a module requires construction of a dedicated access routine for each variable. The advantage of doing this in this way is that the program is built without debug and runs at full speed.

John

2 Likes

This is very useful indeed. I don’t understand to be honest how a module variable is any different to a variable in a common for this particular use case. Why cannot you do the same with a module variable?

The way the common-variable access works is:

The access routines (peek & poke) use an array which describes the common address, type, kind and shape of each object.

The access routines contain declarations of each common block of the form:
INTEGER*1 :: c1_backbone(0:3017)
COMMON /c1/ c1_backbone
Where 0:3017 is the total size of common /c1/. They do not contain any named references to the variables.

When they peek an object, e.g. a(4), they find which common a is in. They find the address of a and adjust it for the array index (if any) to get the value address. Then they copy the number of bytes at backbone(address) in common c1 into an object of the type and kind of a. Then they print the value of this object.

Poke simply works the other way round.

The whole operation is data-driven. There is one routine for peek, one for poke, and support routines to fetch and compute addresses.

In the applications we have dealt with this is not implemented for derived types or structures, but it could be.

Now try doing that with a module variable. Possible, but you are in danger of having to initialise all the addresses at startup.

This was done to replicate the DATAPOOL handling under MPX Fortran, but has since been used in simulations which (happily) never saw MPX.

John

1 Like