Recently, I’m seeing a strange problem when compiling my large Fortran project. I am using the switch -fdefault-integer-8 with gfortran 12.2.1 and the first compilation proceeds fine. But when I make a change in one of the module files, I get loads of errors like: Type mismatch in argument ‘lenstring’ at (1); passed INTEGER(4) to INTEGER(8)
for a function call: i = SOMEFUNC(string, lenstring)
Both, the function SOMEFUNC that is called and the lenstring that is passed (which is an INTEGER, PARAMETER) come from two different module files (the latter from mod_parameters.f90. For some reason, it seems that the PARAMETER lenstring is assumed to be an INTEGER(4) despite module also being compiled with -fdefault-integer-8. When I pass a local INTEGER variable i_test (with the lenstring PARAMETER from the module file assigned to it), I don’t get that error.
The mod file in which lenstring is defined shows lenstring to be what I’d interpret as an 8 byte integer:
As the flag says, it applies to the default integer kind, i.e., all declarations that are only integer (no kind whatsoever) become of kind integer(8). So if you have integer(4) explicitly declared, it cannot be passed to an integer variable anymore
No, in all instances I only declare INTEGER with no specified size. The compiled mod file seems to imply that the integer size of the parameter I pass should be 8 bytes but somehow in the module file where I call the function using that constant parameter, that size is assumed to be only 4 bytes.
Well in this case I believe you may have hit a compiler bug. The best thing I do in this cases is to create a minimum viable example programs that’s only a few lines of code, and then test it at the Compiler Explorer with several compiler vendors and versions, and attempt to see what’s going on
This would be my guess too. That file is probably being recompiled, without the -fdefault-integer-8 option, because of some detected dependency. You might begin to debug this by adding a statement like
before one of the offending function references. If this catches the error, then it most likely is not a compiler error but rather an error in your make macros or something like that.
The -fdefault-integer-8 compile flag for that module (actually, in all cases) is certainly there and though I don’t actually know how to interpret the information in the generated mod file, it looks to me like the -fdefault-integer-8 option actually succeeded as it says INTEGER 8. Maybe it is a compiler bug after all.
But then again, why would it succeed the first time then? It must be the build system although I can’t figure out what could go wrong here.
Maybe also the flags -fdump-tree-original and -fdump-parse-tree can be helpful, to analyze the intermediate parse tree and see if the type/kind is correct.
For example on the program,
! dump.f90
implicit none
external somefunc
integer somefunc, i
integer, parameter :: lenstr = 32
character(len=lenstr) :: str
i = somefunc(str,lenstr)
print *, i
end
compiled with gfortran -Wall -fdump-tree-original -c dump.f90 an output file dump.f90.x.original will be created, containing:
Notice how the compiler appended a third argument to somefunc? That is the hidden length argument attached by the (internal) argument passing convention for character dummy arguments. For details see section 6.4.2 (paragraph 7) in the gfortran manual: Argument passing conventions (The GNU Fortran Compiler)
I don’t know why it happens but after I make changes to a module and invoke the second compilation with these changes:
The corresponding mod file gets created in the source tree next to the f90 file but it has the 4 byte integer sizes.
The mod file in the build dir has the correct integer size of 8 but apparently the one in the source tree has precedence over it.
Once I manually remove the mod file created in the source tree, compilation succeeds (no recreation of the mod file in the source tree).
When I make changes in the module file and recompile, the same happens over again.
I just cannot figure out who creates that wrong mod file, why it has the wrong int size, and why it is created in the first place. From the time stamps I saw that the wrong mod file in the source tree gets created some seconds before the correct one in the build tree. The time stamps of the resulting .f90.o, .mod.stamp and .o.provides.build files are created afterwards in very short succession.
If you add the --verbose option to your gfortran command, you should see your GCC_COLLECT_OPTIONS line displayed when you recompile. After changing the module does the output always contain the -fdefault-integer-8 call? It should show up in all cases.
Any chance that this mod file is being generated by the linter in a an IDE (e.g. fortls in VSCode)? If so, it should be possible to configure it to direct the files somewhere they are not picked up by the build system (or to use the same compile options).
One of those include-dir points to the source tree, and has precedence over CMake’s build directory.
So maybe you should set your linter (or whatever tool you use besides CMake) to point to the same build directory and/or have the same -fdefault-integer-8 flag.
You were correct, as it seems, suspecting a build problem.
I analyzed the -I arguments to gfortran (available when I set -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON) and found the order of the directories to list the source directories before the modules output directories.
I now consistently set the CMAKE_Fortran_MODULE_DIRECTORY to modules in all CMakeLists.txt of all project targets and adapted the include paths to list the .mod output paths of the respective dependencies first. Those dependencies are from the same project and I specified them as relative paths along the lines of ${CMAKE_CURRENT_BINARY_DIR}/../<dependency>/modules first, i.e. before other includes which were referencing source directories parallel to the ones containing the CMakeLists.txt. Those include dirs were necessary because they contain required .inc files (which were also used by other .f90 files from different targets).
That seems to have resolved the generation of the faulty .mod files in the source tree which had happened after recompiling with changes made to certain f90 modules. Who had created those .mod files and why is still mysterious to me.
@jwmwalrus I guess the project should have been organized differently in the first place but as it has grown over many, many years, that is quite hard to change now.
It seems likely that you have some module files that are being recompiled multiple times during your build process. That is, at the least, inefficient, even if it is producing a correct executable in the end. Sometimes these build problems are difficult to track down and solve, but it is usually a good thing to do nonetheless in order to avoid problems such as those you just encountered.
With modern fortran, particularly with modules and submodules, the build process is just as important as the source files themselves. Unfortunately, that build process, and particularly the specification of the compilation order, is outside the scope of the fortran standard itself, so the solution to these problems depends on just the local tools you are using at the time. Change build tools, and whole new sets of build problems arise. Get a set of source files from someone, and it can be a difficult challenge to then determine the correct compilation order. I personally think that the specification of the build order should be part of the fortran standard, portable to all compiler and OS combinations and independent of the choice of build tools.
This was not an issue with f77 and earlier, it only became important with f90 and later when include files and modules, and eventually submodules were introduced to the lanuage. With f77, one could compile the files in any order and link them together to create the executable.
I see. But TBH, I’m glad I don’t have to use f77’s fixed format coding :oD
And I actually enjoy being able to use OOP of F03, clunky as it may be compared to other languages. Inheritance and type-bound procedures are quite convenient features for well structured and less redundant programming.