[clang] [flang] [llvm] [flang] Diagnose and optionally repair missing MODULE procedure prefixes (PR #220783)
Eugene Epshteyn via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 2 07:28:11 PDT 2026
================
@@ -5933,6 +5941,18 @@ bool SubprogramVisitor::BeginSubprogram(const parser::Name &name,
EraseSymbol(name);
}
}
+ } else if (isValid && !inInterfaceBlock() && currScope().IsSubmodule() &&
+ context().IsEnabled(common::LanguageFeature::ImplicitModulePrefix) &&
+ (moduleInterface = FindSeparateModuleProcedureInterface(
+ name, /*emitError=*/false))) {
+ // As with the missing-prefix diagnostic below, imported parent scopes
+ // must not suppress a warning for a definition in the current source.
+ context().messages().Warn(/*isInModuleFile=*/InModuleFile(),
+ context().languageFeatures(),
+ common::LanguageFeature::ImplicitModulePrefix, name.source,
+ "Assuming a missing MODULE prefix on '%s' to repair the separate module procedure interface '%s:%s'"_port_en_US,
+ name.source, moduleInterface->owner().GetName().value(),
+ moduleInterface->name());
}
----------------
eugeneepshteyn wrote:
The `InModuleFile()` check here gates only the warning. The binding itself (`moduleInterface` → `set_moduleInterface` → `MODULE`) still runs while re-resolving a `.mod`. So a translation unit compiled with `-fimplicit-module-prefix` reinterprets a submodule `.mod` produced without that option. In that `.mod` the unprefixed procedure is a local procedure (F2023 19.5.1.4 p2), and the reinterpretation silently rebinds it to the interface:
```fortran
! m.f90
module m
interface
module subroutine helper
end subroutine
module subroutine run
end subroutine
end interface
end module
! sm1.f90 -- compiled WITHOUT the flag
submodule(m) sm1
contains
subroutine helper ! no MODULE prefix: a local procedure of sm1
print *, 'sm1 local helper'
end subroutine
end submodule
! sm2.f90
submodule(m) sm2
contains
module subroutine helper
print *, 'm:helper'
end subroutine
end submodule
! sm3.f90
submodule(m:sm1) sm3
contains
module subroutine run
call helper ! sm1's local procedure, by host association
end subroutine
end submodule
! main.f90
program main
use m
call run
end program
```
```console
$ flang -c m.f90 && flang -c sm1.f90 && flang -c sm2.f90 && flang -c main.f90
$ flang -c sm3.f90 && flang m.o sm1.o sm2.o sm3.o main.o && ./a.out
sm1 local helper
$ flang -c -fimplicit-module-prefix -Wimplicit-module-prefix -pedantic sm3.f90 && flang m.o sm1.o sm2.o sm3.o main.o && ./a.out
m:helper
```
The second compile of `sm3.f90` prints nothing, yet the program now calls a different procedure. Without `sm2.o` in the link, it fails instead with an undefined reference to `_QMmPhelper`.
A `.mod` produced *with* the flag already records `module subroutine helper()`, so module-file content never needs the repair, and skipping it there is enough. Moving the owner check into the branch also stops a repair warning from appearing next to the existing "already declared" error when the interface is declared in the same submodule:
```suggestion
} else if (isValid && !inInterfaceBlock() && !InModuleFile() &&
currScope().IsSubmodule() &&
context().IsEnabled(common::LanguageFeature::ImplicitModulePrefix)) {
// Repair only definitions in the current source: a module file already
// records whether its producer treated the subprogram as MODULE.
if (Symbol *iface{FindSeparateModuleProcedureInterface(
name, /*emitError=*/false)};
iface && &iface->owner() != &currScope()) {
moduleInterface = iface;
context().messages().Warn(/*isInModuleFile=*/false,
context().languageFeatures(),
common::LanguageFeature::ImplicitModulePrefix, name.source,
"Assuming a missing MODULE prefix on '%s' to repair the separate module procedure interface '%s:%s'"_port_en_US,
name.source, moduleInterface->owner().GetName().value(),
moduleInterface->name());
}
}
```
With this change the example keeps calling `_QMmSsm1Phelper`, and `check-flang` results are unchanged for me. A split-file test that needs no linking would pin this: compile `sm1.f90` without the flag, then check `-fimplicit-module-prefix -emit-hlfir sm3.f90` for `fir.call @_QMmSsm1Phelper`. The existing IMPORT RUN line in `implicit-module-prefix-modfile.f90` covers only the case where the producer was compiled with the flag.
https://github.com/llvm/llvm-project/pull/220783
More information about the cfe-commits
mailing list