(Sorry for the earlier cancelled thread, which I posted prematurely.)
I have found empirically that if source file a.f90 calls a function foo in a module in file b.f90, and I change the internals of function foo, I need to recompile b.f90 (of course) but not a.f90. But if I add an optional argument to function foo, I must also recompile a.f90, even if the calling code in a.f90 is still valid and the program should have the same meaning. Is there a build system that can figure out when it is necessary to recompile a.f90 when it calls a procedure in b.f90? I never write explicit interfaces but always put procedures in modules.
So far I’m not aware of any build system that can offer this kind of module dependency support.
In theory, to support such smart module dependency resolution the build system would have to know about ABI changes (not just API changes) for defined procedures of a module (renaming a function in your module b which is used by a generic interface in a doesn’t change the API but still requires recompilation of module a due to change of the symbol name).
A build system would have to parse, understand and resolve Fortran interfaces for this purpose and good drop-in Fortran parsers are quite rare outside of Fortran compilers.
Although, the necessary information for the build system would be available in the generated module files.
I think that if you do not want to split your module in two files, one for the interface and one for the submodule with the implementation, you should always recompile all the files that depend on the original module. Personally, I work out the dependencies in a makefile at the beginning. If the dependencies are too intricate to figure them out by hand, then my impression is that the architecture is likely to get out of hand. As I see it, module interdependencies should be written down explicitly anyhow.
If you are changing the interface of a module procedure, I think it is reasonable to expect that the linker complains if you have not recompiled the files that use that procedure, even if the parameters in the caller did not change. In fact, I see this as a feature, a way to avoid subtle bugs.
Your particular example of adding an optional argument will always need a recompilation of the calling code because adding an optional argument still changes the .mod file and the underlying binary call (even if your calling code does not need to change).
The legacy Haskell implementation of fpm ('haskell-fpm') is able to determine if a module ABI has changed since it cleverly (and correctly) defines module dependencies based on the .mod module files. The underlying build system Shake uses file hashes to determine if dependencies have changed; therefore if a .mod file is rewritten but unchanged in contents, then the API/ABI can be considered unchanged and hence dependents do not need recompiling. You can still download the Haskell implementation of fpmhere (fpm-haskell-0.1.3-*-x86_64).
For reasons of parsimony the active development version of fpm does not define dependencies in the same manner (since module/submodule filenames are compiler dependent) and will conservatively rebuild all dependents if a module source file changes.