[flang-commits] [clang] [flang] [llvm] [flang] Diagnose and optionally repair missing MODULE procedure prefixes (PR #220783)
Andre Kuhlenschmidt via flang-commits
flang-commits at lists.llvm.org
Wed Oct 7 15:56:24 PDT 2026
https://github.com/akuhlens updated https://github.com/llvm/llvm-project/pull/220783
>From d9d12d683e1cf2f1f5a9967a69cc4673556f3234 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Mon, 31 Aug 2026 13:00:57 -0700
Subject: [PATCH 01/13] [flang] Diagnose missing MODULE procedure prefixes
---
clang/include/clang/Options/FlangOptions.td | 2 +
clang/lib/Driver/ToolChains/Flang.cpp | 2 +
flang/docs/Extensions.md | 7 +++
.../include/flang/Support/Fortran-features.h | 16 +++----
flang/lib/Frontend/CompilerInvocation.cpp | 6 +++
flang/lib/Semantics/resolve-names.cpp | 34 ++++++++++++--
flang/lib/Support/Fortran-features.cpp | 2 +
flang/test/Driver/implicit-module-prefix.f90 | 45 +++++++++++++++++++
.../Lower/implicit-module-prefix-shadow.f90 | 43 ++++++++++++++++++
flang/test/Lower/implicit-module-prefix.f90 | 25 +++++++++++
flang/test/Semantics/separate-mp07.f90 | 19 ++++++++
flang/test/Semantics/separate-mp08.f90 | 14 ++++++
flang/test/Semantics/separate-mp09.f90 | 20 +++++++++
flang/test/Semantics/separate-mp10.f90 | 15 +++++++
flang/test/Semantics/separate-mp11.f90 | 18 ++++++++
flang/test/Semantics/separate-mp12.f90 | 15 +++++++
16 files changed, 272 insertions(+), 11 deletions(-)
create mode 100644 flang/test/Driver/implicit-module-prefix.f90
create mode 100644 flang/test/Lower/implicit-module-prefix-shadow.f90
create mode 100644 flang/test/Lower/implicit-module-prefix.f90
create mode 100644 flang/test/Semantics/separate-mp07.f90
create mode 100644 flang/test/Semantics/separate-mp08.f90
create mode 100644 flang/test/Semantics/separate-mp09.f90
create mode 100644 flang/test/Semantics/separate-mp10.f90
create mode 100644 flang/test/Semantics/separate-mp11.f90
create mode 100644 flang/test/Semantics/separate-mp12.f90
diff --git a/clang/include/clang/Options/FlangOptions.td b/clang/include/clang/Options/FlangOptions.td
index 5a45528a39a6d5..2844ee854a120d 100644
--- a/clang/include/clang/Options/FlangOptions.td
+++ b/clang/include/clang/Options/FlangOptions.td
@@ -183,6 +183,8 @@ defm xor_operator : OptInFC1FFlag<"xor-operator", "Enable .XOR. as a synonym of
defm logical_abbreviations : OptInFC1FFlag<"logical-abbreviations", "Enable logical abbreviations">;
defm implicit_none : OptInFC1FFlag<"implicit-none", "No implicit typing allowed unless overridden by IMPLICIT statements">;
defm implicit_none_ext : OptInFC1FFlag<"implicit-none-ext", "No implicit externals allowed">;
+defm implicit_module_prefix : OptInFC1FFlag<"implicit-module-prefix",
+ "Treat a submodule procedure as if it had a missing MODULE prefix when it matches an ancestor interface">;
defm underscoring : OptInFC1FFlag<"underscoring", "Appends one trailing underscore to external names">;
defm ppc_native_vec_elem_order: BoolOptionWithoutMarshalling<"f", "ppc-native-vector-element-order",
PosFlag<SetTrue, [], [ClangOption], "Specifies PowerPC native vector element order (default)">,
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index 37d0372e7a18ed..a13c69a5b2a50a 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -138,7 +138,9 @@ void Flang::addFortranDialectOptions(const ArgList &Args,
options::OPT_finput_charset_EQ,
options::OPT_fimplicit_none,
options::OPT_fimplicit_none_ext,
+ options::OPT_fimplicit_module_prefix,
options::OPT_fno_implicit_none,
+ options::OPT_fno_implicit_module_prefix,
options::OPT_fbackslash,
options::OPT_fno_backslash,
options::OPT_flogical_abbreviations,
diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index 64f502ee6e484e..ba92f24e232eb2 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -595,6 +595,13 @@ end program
[-fimplicit-none-type-always]
* Ignore occurrences of `IMPLICIT NONE` and `IMPLICIT NONE(TYPE)`
[-fimplicit-none-type-never]
+* Treat a subprogram in a submodule as if it had a missing `MODULE` prefix
+ when its name matches a separate module procedure interface in an ancestor
+ module [-fimplicit-module-prefix]. This extension is disabled by default
+ because the unprefixed subprogram can instead be a conforming local
+ procedure. By default, that likely mistake is diagnosed with
+ `-Wmissing-module-prefix` without changing the program. When the extension
+ is enabled, `-Wimplicit-module-prefix` reports each repaired prefix.
* Old-style `PARAMETER pi=3.14` statement without parentheses
[-falternative-parameter-statement]
* `UNSIGNED` type (-funsigned)
diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index 021d157ae1f8d0..b3506f2aec22e5 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -43,13 +43,13 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
UseGenericIntrinsicWhenSpecificDoesntMatch, DataStmtExtensions,
RedundantContiguous, RedundantAttribute, InitBlankCommon,
EmptyBindCDerivedType, MiscSourceExtensions, AllocateToOtherLength,
- LongNames, IntrinsicAsSpecific, BenignNameClash, BenignRedundancy,
- NullMoldAllocatableComponentValue, NopassScalarBase, MiscUseExtensions,
- ImpliedDoIndexScope, DistinctCommonSizes, OddIndexVariableRestrictions,
- IndistinguishableSpecifics, SubroutineAndFunctionSpecifics,
- EmptySequenceType, NonSequenceCrayPointee, BranchIntoConstruct,
- BadBranchTarget, HollerithPolymorphic, ListDirectedSize,
- NonBindCInteroperability, CudaManaged, CudaUnified,
+ LongNames, IntrinsicAsSpecific, ImplicitModulePrefix, BenignNameClash,
+ BenignRedundancy, NullMoldAllocatableComponentValue, NopassScalarBase,
+ MiscUseExtensions, ImpliedDoIndexScope, DistinctCommonSizes,
+ OddIndexVariableRestrictions, IndistinguishableSpecifics,
+ SubroutineAndFunctionSpecifics, EmptySequenceType, NonSequenceCrayPointee,
+ BranchIntoConstruct, BadBranchTarget, HollerithPolymorphic,
+ ListDirectedSize, NonBindCInteroperability, CudaManaged, CudaUnified,
PolymorphicActualAllocatableOrPointerToMonomorphicDummy, RelaxedPureDummy,
UndefinableAsynchronousOrVolatileActual, AutomaticInMainProgram, PrintCptr,
SavedLocalInSpecExpr, PrintNamelist, AssumedRankPassedToNonAssumedRank,
@@ -91,7 +91,7 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
MisplacedIgnoreTKR, NamelistParameter, ImpureFinalInPure,
IgnoredNoReallocateLHS, ExperimentalOption, IoImpliedDoIndexConflict,
BOZLiteralTruncation, IntentInActualForDefaultIntent, BindCArrayDescriptor,
- OpenMPDeprecated, OpenMPFuture)
+ OpenMPDeprecated, OpenMPFuture, MissingModulePrefix)
using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 13794f5720e741..46ccebe3b030b3 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -906,6 +906,12 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
clang::options::OPT_fno_implicit_none_ext,
false));
+ // -f{no-}implicit-module-prefix
+ opts.features.Enable(
+ Fortran::common::LanguageFeature::ImplicitModulePrefix,
+ args.hasFlag(clang::options::OPT_fimplicit_module_prefix,
+ clang::options::OPT_fno_implicit_module_prefix, false));
+
// -f{no-}backslash
opts.features.Enable(Fortran::common::LanguageFeature::BackslashEscapes,
args.hasFlag(clang::options::OPT_fbackslash,
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 67690d6f47d067..d69e5bba29cb66 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -646,7 +646,8 @@ class ScopeHandler : public ImplicitRulesVisitor {
Symbol *FindInTypeOrParents(const Scope &, const parser::Name &);
Symbol *FindInTypeOrParents(const parser::Name &);
Symbol *FindInScopeOrBlockConstructs(const Scope &, SourceName);
- Symbol *FindSeparateModuleProcedureInterface(const parser::Name &);
+ Symbol *FindSeparateModuleProcedureInterface(
+ const parser::Name &, bool emitError = true);
void EraseSymbol(const parser::Name &);
void EraseSymbol(const Symbol &symbol) { currScope().erase(symbol.name()); }
// Make a new symbol with the name and attrs of an existing one
@@ -5598,6 +5599,11 @@ void SubprogramVisitor::Post(const parser::FunctionStmt &stmt) {
Symbol &SubprogramVisitor::PostSubprogramStmt() {
Symbol &symbol{*currScope().symbol()};
SetExplicitAttrs(symbol, EndAttrs());
+ if (symbol.get<SubprogramDetails>().moduleInterface()) {
+ // An omitted MODULE prefix accepted as an extension still defines the
+ // separate module procedure declared by the interface body.
+ SetExplicitAttr(symbol, Attr::MODULE);
+ }
if (symbol.attrs().test(Attr::MODULE)) {
symbol.attrs().set(Attr::EXTERNAL, false);
symbol.implicitAttrs().set(Attr::EXTERNAL, false);
@@ -5848,7 +5854,7 @@ void SubprogramVisitor::PostEntryStmt(const parser::EntryStmt &stmt) {
}
Symbol *ScopeHandler::FindSeparateModuleProcedureInterface(
- const parser::Name &name) {
+ const parser::Name &name, bool emitError) {
auto *symbol{FindSymbol(name)};
if (symbol && symbol->has<SubprogramNameDetails>()) {
const Scope *parent{nullptr};
@@ -5867,7 +5873,9 @@ Symbol *ScopeHandler::FindSeparateModuleProcedureInterface(
symbol = const_cast<Symbol *>(defnIface);
}
if (!IsSeparateModuleProcedureInterface(symbol)) {
- Say(name, "'%s' was not declared a separate module procedure"_err_en_US);
+ if (emitError) {
+ Say(name, "'%s' was not declared a separate module procedure"_err_en_US);
+ }
symbol = nullptr;
}
return symbol;
@@ -5933,6 +5941,26 @@ bool SubprogramVisitor::BeginSubprogram(const parser::Name &name,
EraseSymbol(name);
}
}
+ } else if (isValid && !inInterfaceBlock() && currScope().IsSubmodule() &&
+ (moduleInterface = FindSeparateModuleProcedureInterface(
+ name, /*emitError=*/false))) {
+ if (context().IsEnabled(common::LanguageFeature::ImplicitModulePrefix)) {
+ context().Warn(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());
+ } else {
+ context().Warn(common::UsageWarning::MissingModulePrefix, name.source,
+ "'%s' is a local procedure that hides the separate module procedure "
+ "interface '%s:%s'; "
+ "a call to that interface will fail to link with this local "
+ "procedure. "
+ "If this procedure is supposed to implement the interface, "
+ "add the MODULE keyword or enable -fimplicit-module-prefix."_warn_en_US,
+ name.source, moduleInterface->owner().GetName().value(),
+ moduleInterface->name());
+ moduleInterface = nullptr;
+ }
}
Symbol *newSymbol{
PushSubprogramScope(name, subpFlag, bindingSpec, hasModulePrefix)};
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index 9cd99008835ed1..61e44f1a670639 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -155,6 +155,7 @@ LanguageFeatureControl::LanguageFeatureControl() {
disable_.set(LanguageFeature::LogicalAbbreviations);
disable_.set(LanguageFeature::XOROperator);
disable_.set(LanguageFeature::OldStyleParameter);
+ disable_.set(LanguageFeature::ImplicitModulePrefix);
// Possibly an accidental "feature" of nvfortran.
disable_.set(LanguageFeature::AssumedRankPassedToNonAssumedRank);
disable_.set(LanguageFeature::Coarray);
@@ -228,6 +229,7 @@ LanguageFeatureControl::LanguageFeatureControl() {
warnUsage_.set(UsageWarning::IgnoredNoReallocateLHS);
warnUsage_.set(UsageWarning::IoImpliedDoIndexConflict);
warnUsage_.set(UsageWarning::BOZLiteralTruncation);
+ warnUsage_.set(UsageWarning::MissingModulePrefix);
warnLanguage_.set(LanguageFeature::PreferIntrinsicModuleUseAssociation);
warnLanguage_.set(LanguageFeature::OpenMPThreadprivateEquivalence);
warnLanguage_.set(LanguageFeature::OpenAccDefaultNoneScalarsStrict);
diff --git a/flang/test/Driver/implicit-module-prefix.f90 b/flang/test/Driver/implicit-module-prefix.f90
new file mode 100644
index 00000000000000..a7af0727394f95
--- /dev/null
+++ b/flang/test/Driver/implicit-module-prefix.f90
@@ -0,0 +1,45 @@
+! REQUIRES: native
+! RUN: %flang -static-libflangrt -fimplicit-module-prefix %s -o %t-enabled
+! RUN: %t-enabled | FileCheck %s --check-prefix=REPAIRED
+! RUN: %flang -static-libflangrt -fno-implicit-module-prefix -fimplicit-module-prefix %s -o %t-reenabled
+! RUN: %t-reenabled | FileCheck %s --check-prefix=REPAIRED
+! RUN: %flang -c %s -o %t-default.o
+! RUN: not %flang %t-default.o -o %t-default
+! RUN: %flang -Wno-missing-module-prefix -c %s -o %t-suppressed.o
+! RUN: not %flang %t-suppressed.o -o %t-suppressed
+! RUN: %flang -Wimplicit-module-prefix -c %s -o %t-warning-only.o
+! RUN: not %flang %t-warning-only.o -o %t-warning-only
+! RUN: %flang -pedantic -c %s -o %t-pedantic.o
+! RUN: not %flang %t-pedantic.o -o %t-pedantic
+! RUN: %flang -fimplicit-module-prefix -fno-implicit-module-prefix -c %s -o %t-disabled.o
+! RUN: not %flang %t-disabled.o -o %t-disabled
+
+module alpha
+ interface
+ module integer function second()
+ end function second
+ module integer function third()
+ end function third
+ end interface
+end module alpha
+
+submodule(alpha) beta
+contains
+ integer function second()
+ second = 2
+ end function second
+end submodule beta
+
+submodule(alpha:beta) gamma
+contains
+ integer function third()
+ third = 3
+ end function third
+end submodule gamma
+
+program main
+ use alpha
+ print *, second(), third()
+end program main
+
+! REPAIRED: 2 3
diff --git a/flang/test/Lower/implicit-module-prefix-shadow.f90 b/flang/test/Lower/implicit-module-prefix-shadow.f90
new file mode 100644
index 00000000000000..d68cd611f0afc7
--- /dev/null
+++ b/flang/test/Lower/implicit-module-prefix-shadow.f90
@@ -0,0 +1,43 @@
+! RUN: %flang_fc1 -Wno-missing-module-prefix -emit-hlfir %s -o - | FileCheck %s
+
+! A local procedure in beta hides alpha's interface only in beta and its
+! descendants. The sibling submodule may still implement alpha's public
+! second procedure.
+module alpha
+ interface
+ module subroutine second
+ end subroutine second
+ module subroutine third
+ end subroutine third
+ end interface
+end module alpha
+
+submodule(alpha) beta
+contains
+ ! CHECK-LABEL: func @_QMalphaSbetaPsecond()
+ subroutine second
+ end subroutine second
+end submodule beta
+
+submodule(alpha:beta) nested_gamma
+contains
+ ! CHECK-LABEL: func @_QMalphaPthird()
+ module subroutine third
+ end subroutine third
+end submodule nested_gamma
+
+submodule(alpha) sibling_gamma
+contains
+ ! CHECK-LABEL: func @_QMalphaPsecond()
+ module subroutine second
+ end subroutine second
+end submodule sibling_gamma
+
+! CHECK-LABEL: func @_QQmain()
+program main
+ use alpha
+ ! CHECK: fir.call @_QMalphaPsecond() {{.*}}
+ call second
+ ! CHECK: fir.call @_QMalphaPthird() {{.*}}
+ call third
+end program main
diff --git a/flang/test/Lower/implicit-module-prefix.f90 b/flang/test/Lower/implicit-module-prefix.f90
new file mode 100644
index 00000000000000..2a57fc73dc093b
--- /dev/null
+++ b/flang/test/Lower/implicit-module-prefix.f90
@@ -0,0 +1,25 @@
+! RUN: %flang_fc1 -fimplicit-module-prefix -emit-hlfir %s -o - | FileCheck %s
+
+module alpha
+ interface
+ module subroutine implementation
+ end subroutine implementation
+ end interface
+end module alpha
+
+submodule(alpha) beta
+end submodule beta
+
+submodule(alpha:beta) gamma
+contains
+ ! CHECK-LABEL: func @_QMalphaPimplementation()
+ subroutine implementation
+ end subroutine implementation
+end submodule gamma
+
+! CHECK-LABEL: func @_QQmain()
+program main
+ use alpha
+ ! CHECK: fir.call @_QMalphaPimplementation() {{.*}}
+ call implementation
+end program main
diff --git a/flang/test/Semantics/separate-mp07.f90 b/flang/test/Semantics/separate-mp07.f90
new file mode 100644
index 00000000000000..896c3f87988cef
--- /dev/null
+++ b/flang/test/Semantics/separate-mp07.f90
@@ -0,0 +1,19 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1 -Werror
+! A local subprogram hides an ancestor interface and leaves calls to the
+! ancestor's separate module procedure undefined at link time.
+module alpha
+ interface
+ module subroutine second
+ end subroutine second
+ end interface
+end module alpha
+
+submodule(alpha) beta
+end submodule beta
+
+submodule(alpha:beta) gamma
+contains
+ !WARNING: 'second' is a local procedure that hides the separate module procedure interface 'alpha:second'; a call to that interface will fail to link with this local procedure. If this procedure is supposed to implement the interface, add the MODULE keyword or enable -fimplicit-module-prefix. [-Wmissing-module-prefix]
+ subroutine second
+ end subroutine second
+end submodule gamma
diff --git a/flang/test/Semantics/separate-mp08.f90 b/flang/test/Semantics/separate-mp08.f90
new file mode 100644
index 00000000000000..70986c352386a3
--- /dev/null
+++ b/flang/test/Semantics/separate-mp08.f90
@@ -0,0 +1,14 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1 -Wno-missing-module-prefix
+! The default diagnostic may be suppressed without enabling the extension.
+module m
+ interface
+ module subroutine implementation
+ end subroutine implementation
+ end interface
+end module m
+
+submodule(m) sm
+contains
+ subroutine implementation
+ end subroutine implementation
+end submodule sm
diff --git a/flang/test/Semantics/separate-mp09.f90 b/flang/test/Semantics/separate-mp09.f90
new file mode 100644
index 00000000000000..ae26635a738e97
--- /dev/null
+++ b/flang/test/Semantics/separate-mp09.f90
@@ -0,0 +1,20 @@
+! RUN: %flang_fc1 -fimplicit-module-prefix -fdebug-dump-symbols %s 2>&1 | FileCheck %s
+! The enabled extension silently repairs a likely omitted MODULE prefix by
+! binding the definition to the separate module procedure interface.
+module m
+ interface
+ module subroutine implementation
+ end subroutine implementation
+ end interface
+end module m
+
+submodule(m) sm
+contains
+ subroutine implementation
+ end subroutine implementation
+end submodule sm
+
+! CHECK: Module scope: m
+! CHECK: implementation, MODULE, PUBLIC (Subroutine): Subprogram isInterface ()
+! CHECK: Module scope: sm
+! CHECK: implementation, MODULE, PUBLIC (Subroutine): Subprogram () moduleInterface: implementation, MODULE, PUBLIC (Subroutine): Subprogram isInterface ()
diff --git a/flang/test/Semantics/separate-mp10.f90 b/flang/test/Semantics/separate-mp10.f90
new file mode 100644
index 00000000000000..677af8f78963d3
--- /dev/null
+++ b/flang/test/Semantics/separate-mp10.f90
@@ -0,0 +1,15 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1 -fimplicit-module-prefix -pedantic -Werror
+! Pedantic mode reports the nonstandard repair.
+module m
+ interface
+ module subroutine implementation
+ end subroutine implementation
+ end interface
+end module m
+
+submodule(m) sm
+contains
+ !PORTABILITY: Assuming a missing MODULE prefix on 'implementation' to repair the separate module procedure interface 'm:implementation' [-Wimplicit-module-prefix]
+ subroutine implementation
+ end subroutine implementation
+end submodule sm
diff --git a/flang/test/Semantics/separate-mp11.f90 b/flang/test/Semantics/separate-mp11.f90
new file mode 100644
index 00000000000000..d66618d8e35334
--- /dev/null
+++ b/flang/test/Semantics/separate-mp11.f90
@@ -0,0 +1,18 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1 -fimplicit-module-prefix -Wimplicit-module-prefix -Werror
+! The extension warning may be requested without enabling all pedantic warnings.
+module alpha
+ interface
+ module subroutine implementation
+ end subroutine implementation
+ end interface
+end module alpha
+
+submodule(alpha) beta
+end submodule beta
+
+submodule(alpha:beta) gamma
+contains
+ !PORTABILITY: Assuming a missing MODULE prefix on 'implementation' to repair the separate module procedure interface 'alpha:implementation' [-Wimplicit-module-prefix]
+ subroutine implementation
+ end subroutine implementation
+end submodule gamma
diff --git a/flang/test/Semantics/separate-mp12.f90 b/flang/test/Semantics/separate-mp12.f90
new file mode 100644
index 00000000000000..deccd90c186154
--- /dev/null
+++ b/flang/test/Semantics/separate-mp12.f90
@@ -0,0 +1,15 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1 -pedantic -Werror
+! Pedantic mode widens diagnostics but does not enable the repair extension.
+module m
+ interface
+ module subroutine implementation
+ end subroutine implementation
+ end interface
+end module m
+
+submodule(m) sm
+contains
+ !WARNING: 'implementation' is a local procedure that hides the separate module procedure interface 'm:implementation'; a call to that interface will fail to link with this local procedure. If this procedure is supposed to implement the interface, add the MODULE keyword or enable -fimplicit-module-prefix. [-Wmissing-module-prefix]
+ subroutine implementation
+ end subroutine implementation
+end submodule sm
>From 4a2cc3da00d53fd4a11f45c14302d0a06ff217bb Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 17 Sep 2026 11:15:05 -0700
Subject: [PATCH 02/13] [flang] Append ImplicitModulePrefix language feature
---
flang/include/flang/Support/Fortran-features.h | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index b3506f2aec22e5..8fc48f3266e9e1 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -43,13 +43,13 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
UseGenericIntrinsicWhenSpecificDoesntMatch, DataStmtExtensions,
RedundantContiguous, RedundantAttribute, InitBlankCommon,
EmptyBindCDerivedType, MiscSourceExtensions, AllocateToOtherLength,
- LongNames, IntrinsicAsSpecific, ImplicitModulePrefix, BenignNameClash,
- BenignRedundancy, NullMoldAllocatableComponentValue, NopassScalarBase,
- MiscUseExtensions, ImpliedDoIndexScope, DistinctCommonSizes,
- OddIndexVariableRestrictions, IndistinguishableSpecifics,
- SubroutineAndFunctionSpecifics, EmptySequenceType, NonSequenceCrayPointee,
- BranchIntoConstruct, BadBranchTarget, HollerithPolymorphic,
- ListDirectedSize, NonBindCInteroperability, CudaManaged, CudaUnified,
+ LongNames, IntrinsicAsSpecific, BenignNameClash, BenignRedundancy,
+ NullMoldAllocatableComponentValue, NopassScalarBase, MiscUseExtensions,
+ ImpliedDoIndexScope, DistinctCommonSizes, OddIndexVariableRestrictions,
+ IndistinguishableSpecifics, SubroutineAndFunctionSpecifics,
+ EmptySequenceType, NonSequenceCrayPointee, BranchIntoConstruct,
+ BadBranchTarget, HollerithPolymorphic, ListDirectedSize,
+ NonBindCInteroperability, CudaManaged, CudaUnified,
PolymorphicActualAllocatableOrPointerToMonomorphicDummy, RelaxedPureDummy,
UndefinableAsynchronousOrVolatileActual, AutomaticInMainProgram, PrintCptr,
SavedLocalInSpecExpr, PrintNamelist, AssumedRankPassedToNonAssumedRank,
@@ -63,7 +63,7 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
CUDAImplicitDataAttrSpelling, OpenAccDefaultNoneScalarsStrict,
OpenACCMultipleNamesInRoutine, EnumerationType, CUDAInit,
PreferIntrinsicModuleUseAssociation, MultipleCommonBlockInit,
- OutOfBoundsSubscripts)
+ OutOfBoundsSubscripts, ImplicitModulePrefix)
// Portability and suspicious usage warnings
ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
>From 7437cccc9f22fad8f624f47f60ae26f8b6862590 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 17 Sep 2026 12:20:58 -0700
Subject: [PATCH 03/13] [flang] Unwrap missing MODULE prefix warning
---
flang/lib/Semantics/resolve-names.cpp | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index d69e5bba29cb66..9ee52ce206d5d6 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -5951,12 +5951,8 @@ bool SubprogramVisitor::BeginSubprogram(const parser::Name &name,
moduleInterface->name());
} else {
context().Warn(common::UsageWarning::MissingModulePrefix, name.source,
- "'%s' is a local procedure that hides the separate module procedure "
- "interface '%s:%s'; "
- "a call to that interface will fail to link with this local "
- "procedure. "
- "If this procedure is supposed to implement the interface, "
- "add the MODULE keyword or enable -fimplicit-module-prefix."_warn_en_US,
+ "'%s' is a local procedure that hides the separate module procedure interface '%s:%s'; a call to that interface will fail to link with this local procedure. "
+ "If this procedure is supposed to implement the interface, add the MODULE keyword or enable -fimplicit-module-prefix."_warn_en_US,
name.source, moduleInterface->owner().GetName().value(),
moduleInterface->name());
moduleInterface = nullptr;
>From e5175e1c0ff612b7bdedd5f68262b14185df9c2c Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 17 Sep 2026 15:22:46 -0700
Subject: [PATCH 04/13] [flang] Format missing MODULE prefix warning
---
flang/lib/Semantics/resolve-names.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 9ee52ce206d5d6..a6826fd8b45179 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -5951,7 +5951,9 @@ bool SubprogramVisitor::BeginSubprogram(const parser::Name &name,
moduleInterface->name());
} else {
context().Warn(common::UsageWarning::MissingModulePrefix, name.source,
- "'%s' is a local procedure that hides the separate module procedure interface '%s:%s'; a call to that interface will fail to link with this local procedure. "
+ "'%s' is a local procedure that hides the separate module procedure "
+ "interface '%s:%s'; a call to that interface will fail to link with "
+ "this local procedure. "
"If this procedure is supposed to implement the interface, add the MODULE keyword or enable -fimplicit-module-prefix."_warn_en_US,
name.source, moduleInterface->owner().GetName().value(),
moduleInterface->name());
>From 786607cff067d22194a9157f96e6e51882340eb6 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 1 Oct 2026 13:42:11 -0700
Subject: [PATCH 05/13] [flang] Reuse upstream diagnostics for implicit MODULE
prefixes
Preserve the upstream portability warning when the extension is disabled. Keep implicit MODULE prefixes opt-in and use module-file-aware diagnostics for repairs. Add coverage for repair warnings with imported module and submodule files.
---
flang/docs/Extensions.md | 7 +--
.../include/flang/Support/Fortran-features.h | 2 +-
flang/lib/Semantics/resolve-names.cpp | 27 +++++-------
flang/lib/Support/Fortran-features.cpp | 1 -
flang/test/Driver/implicit-module-prefix.f90 | 2 +-
.../Lower/implicit-module-prefix-shadow.f90 | 2 +-
.../implicit-module-prefix-modfile.f90 | 43 +++++++++++++++++++
flang/test/Semantics/separate-mp07.f90 | 3 +-
flang/test/Semantics/separate-mp08.f90 | 2 +-
flang/test/Semantics/separate-mp12.f90 | 2 +-
10 files changed, 64 insertions(+), 27 deletions(-)
create mode 100644 flang/test/Semantics/implicit-module-prefix-modfile.f90
diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index ba92f24e232eb2..54121e033867f1 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -599,9 +599,10 @@ end program
when its name matches a separate module procedure interface in an ancestor
module [-fimplicit-module-prefix]. This extension is disabled by default
because the unprefixed subprogram can instead be a conforming local
- procedure. By default, that likely mistake is diagnosed with
- `-Wmissing-module-prefix` without changing the program. When the extension
- is enabled, `-Wimplicit-module-prefix` reports each repaired prefix.
+ procedure. Without this extension, `-pedantic` or `-Wportability` diagnoses
+ a likely missing prefix without changing the program. When the extension
+ is enabled, `-Wimplicit-module-prefix` or `-pedantic` reports each repaired
+ prefix.
* Old-style `PARAMETER pi=3.14` statement without parentheses
[-falternative-parameter-statement]
* `UNSIGNED` type (-funsigned)
diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index 8fc48f3266e9e1..587221ba5dd6d6 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -91,7 +91,7 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
MisplacedIgnoreTKR, NamelistParameter, ImpureFinalInPure,
IgnoredNoReallocateLHS, ExperimentalOption, IoImpliedDoIndexConflict,
BOZLiteralTruncation, IntentInActualForDefaultIntent, BindCArrayDescriptor,
- OpenMPDeprecated, OpenMPFuture, MissingModulePrefix)
+ OpenMPDeprecated, OpenMPFuture)
using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index a6826fd8b45179..ac275acfed4170 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -5942,23 +5942,17 @@ bool SubprogramVisitor::BeginSubprogram(const parser::Name &name,
}
}
} else if (isValid && !inInterfaceBlock() && currScope().IsSubmodule() &&
+ context().IsEnabled(common::LanguageFeature::ImplicitModulePrefix) &&
(moduleInterface = FindSeparateModuleProcedureInterface(
name, /*emitError=*/false))) {
- if (context().IsEnabled(common::LanguageFeature::ImplicitModulePrefix)) {
- context().Warn(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());
- } else {
- context().Warn(common::UsageWarning::MissingModulePrefix, name.source,
- "'%s' is a local procedure that hides the separate module procedure "
- "interface '%s:%s'; a call to that interface will fail to link with "
- "this local procedure. "
- "If this procedure is supposed to implement the interface, add the MODULE keyword or enable -fimplicit-module-prefix."_warn_en_US,
- name.source, moduleInterface->owner().GetName().value(),
- moduleInterface->name());
- moduleInterface = nullptr;
- }
+ // 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());
}
Symbol *newSymbol{
PushSubprogramScope(name, subpFlag, bindingSpec, hasModulePrefix)};
@@ -6086,7 +6080,8 @@ const Symbol *SubprogramVisitor::CheckExtantProc(
Symbol *SubprogramVisitor::PushSubprogramScope(const parser::Name &name,
Symbol::Flag subpFlag, const parser::LanguageBindingSpec *bindingSpec,
bool hasModulePrefix) {
- if (!inInterfaceBlock() && currScope().IsSubmodule() && !hasModulePrefix) {
+ if (!inInterfaceBlock() && currScope().IsSubmodule() && !hasModulePrefix &&
+ !context().IsEnabled(common::LanguageFeature::ImplicitModulePrefix)) {
const Scope &parent{currScope().parent()};
if (parent.IsModule() || parent.IsSubmodule()) {
if (const Symbol *host{parent.FindSymbol(name.source)}) {
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index 61e44f1a670639..9e0069781a117a 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -229,7 +229,6 @@ LanguageFeatureControl::LanguageFeatureControl() {
warnUsage_.set(UsageWarning::IgnoredNoReallocateLHS);
warnUsage_.set(UsageWarning::IoImpliedDoIndexConflict);
warnUsage_.set(UsageWarning::BOZLiteralTruncation);
- warnUsage_.set(UsageWarning::MissingModulePrefix);
warnLanguage_.set(LanguageFeature::PreferIntrinsicModuleUseAssociation);
warnLanguage_.set(LanguageFeature::OpenMPThreadprivateEquivalence);
warnLanguage_.set(LanguageFeature::OpenAccDefaultNoneScalarsStrict);
diff --git a/flang/test/Driver/implicit-module-prefix.f90 b/flang/test/Driver/implicit-module-prefix.f90
index a7af0727394f95..6fcf2072392d2e 100644
--- a/flang/test/Driver/implicit-module-prefix.f90
+++ b/flang/test/Driver/implicit-module-prefix.f90
@@ -5,7 +5,7 @@
! RUN: %t-reenabled | FileCheck %s --check-prefix=REPAIRED
! RUN: %flang -c %s -o %t-default.o
! RUN: not %flang %t-default.o -o %t-default
-! RUN: %flang -Wno-missing-module-prefix -c %s -o %t-suppressed.o
+! RUN: %flang -Wno-portability -c %s -o %t-suppressed.o
! RUN: not %flang %t-suppressed.o -o %t-suppressed
! RUN: %flang -Wimplicit-module-prefix -c %s -o %t-warning-only.o
! RUN: not %flang %t-warning-only.o -o %t-warning-only
diff --git a/flang/test/Lower/implicit-module-prefix-shadow.f90 b/flang/test/Lower/implicit-module-prefix-shadow.f90
index d68cd611f0afc7..ae32fd4a9ec90b 100644
--- a/flang/test/Lower/implicit-module-prefix-shadow.f90
+++ b/flang/test/Lower/implicit-module-prefix-shadow.f90
@@ -1,4 +1,4 @@
-! RUN: %flang_fc1 -Wno-missing-module-prefix -emit-hlfir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -Wno-portability -emit-hlfir %s -o - | FileCheck %s
! A local procedure in beta hides alpha's interface only in beta and its
! descendants. The sibling submodule may still implement alpha's public
diff --git a/flang/test/Semantics/implicit-module-prefix-modfile.f90 b/flang/test/Semantics/implicit-module-prefix-modfile.f90
new file mode 100644
index 00000000000000..1eff4292930924
--- /dev/null
+++ b/flang/test/Semantics/implicit-module-prefix-modfile.f90
@@ -0,0 +1,43 @@
+! RUN: split-file %s %t
+! RUN: %flang_fc1 -fsyntax-only -J%t %t/m.f90
+! RUN: %flang_fc1 -fsyntax-only -fimplicit-module-prefix -J%t %t/s.f90 2>&1 | FileCheck %s --allow-empty --check-prefix=SILENT
+! RUN: %flang_fc1 -fsyntax-only -fimplicit-module-prefix -Wimplicit-module-prefix -J%t %t/s.f90 2>&1 | FileCheck %s --check-prefix=REPAIR
+! RUN: %flang_fc1 -fsyntax-only -fimplicit-module-prefix -pedantic -J%t %t/s.f90 2>&1 | FileCheck %s --check-prefix=REPAIR
+! RUN: %flang_fc1 -fsyntax-only -fimplicit-module-prefix -pedantic -J%t %t/t.f90 2>&1 | FileCheck %s --allow-empty --check-prefix=IMPORT
+
+! A repair in current source must be reported even when the parent comes
+! from a .mod file. Reading the repaired .smod must not repeat the warning.
+
+!--- m.f90
+module implicit_prefix_parent
+ interface
+ module subroutine implementation()
+ end subroutine
+ end interface
+end module
+
+!--- s.f90
+submodule (implicit_prefix_parent) implicit_prefix_child
+ interface
+ module subroutine next_implementation()
+ end subroutine
+ end interface
+contains
+ subroutine implementation()
+ end subroutine
+end submodule
+
+!--- t.f90
+submodule (implicit_prefix_parent:implicit_prefix_child) implicit_prefix_grandchild
+contains
+ module subroutine next_implementation()
+ end subroutine
+end submodule
+
+! SILENT-NOT: warning:
+! SILENT-NOT: portability:
+! REPAIR: portability: Assuming a missing MODULE prefix on 'implementation' to repair the separate module procedure interface 'implicit_prefix_parent:implementation' [-Wimplicit-module-prefix]
+! REPAIR-NOT: missing the MODULE prefix
+! REPAIR-NOT: Assuming a missing MODULE prefix
+! IMPORT-NOT: warning:
+! IMPORT-NOT: portability:
diff --git a/flang/test/Semantics/separate-mp07.f90 b/flang/test/Semantics/separate-mp07.f90
index 896c3f87988cef..d8cae0d5c36bfb 100644
--- a/flang/test/Semantics/separate-mp07.f90
+++ b/flang/test/Semantics/separate-mp07.f90
@@ -1,5 +1,5 @@
! RUN: %python %S/test_errors.py %s %flang_fc1 -Werror
-! A local subprogram hides an ancestor interface and leaves calls to the
+! Without portability warnings, a local subprogram hides an ancestor interface and leaves calls to the
! ancestor's separate module procedure undefined at link time.
module alpha
interface
@@ -13,7 +13,6 @@ end module alpha
submodule(alpha:beta) gamma
contains
- !WARNING: 'second' is a local procedure that hides the separate module procedure interface 'alpha:second'; a call to that interface will fail to link with this local procedure. If this procedure is supposed to implement the interface, add the MODULE keyword or enable -fimplicit-module-prefix. [-Wmissing-module-prefix]
subroutine second
end subroutine second
end submodule gamma
diff --git a/flang/test/Semantics/separate-mp08.f90 b/flang/test/Semantics/separate-mp08.f90
index 70986c352386a3..fd3a7d7e6369d6 100644
--- a/flang/test/Semantics/separate-mp08.f90
+++ b/flang/test/Semantics/separate-mp08.f90
@@ -1,4 +1,4 @@
-! RUN: %python %S/test_errors.py %s %flang_fc1 -Wno-missing-module-prefix
+! RUN: %python %S/test_errors.py %s %flang_fc1 -Wno-portability
! The default diagnostic may be suppressed without enabling the extension.
module m
interface
diff --git a/flang/test/Semantics/separate-mp12.f90 b/flang/test/Semantics/separate-mp12.f90
index deccd90c186154..0b53b317ecd5b8 100644
--- a/flang/test/Semantics/separate-mp12.f90
+++ b/flang/test/Semantics/separate-mp12.f90
@@ -9,7 +9,7 @@ end module m
submodule(m) sm
contains
- !WARNING: 'implementation' is a local procedure that hides the separate module procedure interface 'm:implementation'; a call to that interface will fail to link with this local procedure. If this procedure is supposed to implement the interface, add the MODULE keyword or enable -fimplicit-module-prefix. [-Wmissing-module-prefix]
+ !PORTABILITY: Subprogram 'implementation' in this submodule is missing the MODULE prefix to implement the module procedure interface from its parent; did you mean 'MODULE SUBROUTINE'? [-Wportability]
subroutine implementation
end subroutine implementation
end submodule sm
>From c6d88df8d256a565aad8be6d72083450ddecbbf3 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 1 Oct 2026 15:35:13 -0700
Subject: [PATCH 06/13] [flang] Move implicit MODULE prefix execution test to
Flang-RT
---
.../test/Driver/implicit-module-prefix.f90 | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
rename {flang => flang-rt}/test/Driver/implicit-module-prefix.f90 (51%)
diff --git a/flang/test/Driver/implicit-module-prefix.f90 b/flang-rt/test/Driver/implicit-module-prefix.f90
similarity index 51%
rename from flang/test/Driver/implicit-module-prefix.f90
rename to flang-rt/test/Driver/implicit-module-prefix.f90
index 6fcf2072392d2e..ceab2c0d39c026 100644
--- a/flang/test/Driver/implicit-module-prefix.f90
+++ b/flang-rt/test/Driver/implicit-module-prefix.f90
@@ -1,18 +1,19 @@
! REQUIRES: native
-! RUN: %flang -static-libflangrt -fimplicit-module-prefix %s -o %t-enabled
-! RUN: %t-enabled | FileCheck %s --check-prefix=REPAIRED
-! RUN: %flang -static-libflangrt -fno-implicit-module-prefix -fimplicit-module-prefix %s -o %t-reenabled
-! RUN: %t-reenabled | FileCheck %s --check-prefix=REPAIRED
+! UNSUPPORTED: offload-cuda
+! RUN: %flang %isysroot -L"%libdir" -fimplicit-module-prefix %s -o %t-enabled
+! RUN: env LD_LIBRARY_PATH="$LD_LIBRARY_PATH:%libdir" %t-enabled | FileCheck %s --check-prefix=REPAIRED
+! RUN: %flang %isysroot -L"%libdir" -fno-implicit-module-prefix -fimplicit-module-prefix %s -o %t-reenabled
+! RUN: env LD_LIBRARY_PATH="$LD_LIBRARY_PATH:%libdir" %t-reenabled | FileCheck %s --check-prefix=REPAIRED
! RUN: %flang -c %s -o %t-default.o
-! RUN: not %flang %t-default.o -o %t-default
+! RUN: not %flang %isysroot -L"%libdir" %t-default.o -o %t-default
! RUN: %flang -Wno-portability -c %s -o %t-suppressed.o
-! RUN: not %flang %t-suppressed.o -o %t-suppressed
+! RUN: not %flang %isysroot -L"%libdir" %t-suppressed.o -o %t-suppressed
! RUN: %flang -Wimplicit-module-prefix -c %s -o %t-warning-only.o
-! RUN: not %flang %t-warning-only.o -o %t-warning-only
+! RUN: not %flang %isysroot -L"%libdir" %t-warning-only.o -o %t-warning-only
! RUN: %flang -pedantic -c %s -o %t-pedantic.o
-! RUN: not %flang %t-pedantic.o -o %t-pedantic
+! RUN: not %flang %isysroot -L"%libdir" %t-pedantic.o -o %t-pedantic
! RUN: %flang -fimplicit-module-prefix -fno-implicit-module-prefix -c %s -o %t-disabled.o
-! RUN: not %flang %t-disabled.o -o %t-disabled
+! RUN: not %flang %isysroot -L"%libdir" %t-disabled.o -o %t-disabled
module alpha
interface
>From 1b6cd54cd97034ec731b8b10d658edfe5e6ccb6d Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 2 Oct 2026 11:19:33 -0700
Subject: [PATCH 07/13] [flang] Do not repair procedures imported from module
files
---
flang/lib/Semantics/resolve-names.cpp | 28 ++++++++++--------
.../implicit-module-prefix-modfile.f90 | 29 +++++++++++++++++++
2 files changed, 45 insertions(+), 12 deletions(-)
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index ac275acfed4170..abdd0bcc440f83 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -5941,18 +5941,22 @@ 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());
+ } 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());
+ }
}
Symbol *newSymbol{
PushSubprogramScope(name, subpFlag, bindingSpec, hasModulePrefix)};
diff --git a/flang/test/Semantics/implicit-module-prefix-modfile.f90 b/flang/test/Semantics/implicit-module-prefix-modfile.f90
index 1eff4292930924..5aff4b4d4c5da1 100644
--- a/flang/test/Semantics/implicit-module-prefix-modfile.f90
+++ b/flang/test/Semantics/implicit-module-prefix-modfile.f90
@@ -4,6 +4,9 @@
! RUN: %flang_fc1 -fsyntax-only -fimplicit-module-prefix -Wimplicit-module-prefix -J%t %t/s.f90 2>&1 | FileCheck %s --check-prefix=REPAIR
! RUN: %flang_fc1 -fsyntax-only -fimplicit-module-prefix -pedantic -J%t %t/s.f90 2>&1 | FileCheck %s --check-prefix=REPAIR
! RUN: %flang_fc1 -fsyntax-only -fimplicit-module-prefix -pedantic -J%t %t/t.f90 2>&1 | FileCheck %s --allow-empty --check-prefix=IMPORT
+! RUN: %flang_fc1 -fsyntax-only -J%t %t/local-m.f90
+! RUN: %flang_fc1 -fsyntax-only -I%t -J%t %t/local-sm1.f90
+! RUN: %flang_fc1 -fimplicit-module-prefix -emit-hlfir -I%t -J%t %t/local-sm3.f90 -o - | FileCheck %s --check-prefix=LOCAL
! A repair in current source must be reported even when the parent comes
! from a .mod file. Reading the repaired .smod must not repeat the warning.
@@ -34,6 +37,31 @@ module subroutine next_implementation()
end subroutine
end submodule
+!--- local-m.f90
+module local_parent
+ interface
+ module subroutine helper()
+ end subroutine
+ module subroutine run()
+ end subroutine
+ end interface
+end module
+
+!--- local-sm1.f90
+submodule (local_parent) local_child
+contains
+ subroutine helper()
+ end subroutine
+end submodule
+
+!--- local-sm3.f90
+submodule (local_parent:local_child) local_grandchild
+contains
+ module subroutine run()
+ call helper()
+ end subroutine
+end submodule
+
! SILENT-NOT: warning:
! SILENT-NOT: portability:
! REPAIR: portability: Assuming a missing MODULE prefix on 'implementation' to repair the separate module procedure interface 'implicit_prefix_parent:implementation' [-Wimplicit-module-prefix]
@@ -41,3 +69,4 @@ module subroutine next_implementation()
! REPAIR-NOT: Assuming a missing MODULE prefix
! IMPORT-NOT: warning:
! IMPORT-NOT: portability:
+! LOCAL: fir.call @_QMlocal_parentSlocal_childPhelper()
>From 7b4656771ebc76b9870011f37db7d81b0f9e336c Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 2 Oct 2026 11:21:08 -0700
Subject: [PATCH 08/13] [flang][test] Cover implicit module prefix driver
options
---
.../test/Driver/implicit-module-prefix.f90 | 1 -
flang/test/Driver/implicit-module-prefix.f90 | 22 +++++++++++++++++++
2 files changed, 22 insertions(+), 1 deletion(-)
create mode 100644 flang/test/Driver/implicit-module-prefix.f90
diff --git a/flang-rt/test/Driver/implicit-module-prefix.f90 b/flang-rt/test/Driver/implicit-module-prefix.f90
index ceab2c0d39c026..61de777875dcad 100644
--- a/flang-rt/test/Driver/implicit-module-prefix.f90
+++ b/flang-rt/test/Driver/implicit-module-prefix.f90
@@ -1,4 +1,3 @@
-! REQUIRES: native
! UNSUPPORTED: offload-cuda
! RUN: %flang %isysroot -L"%libdir" -fimplicit-module-prefix %s -o %t-enabled
! RUN: env LD_LIBRARY_PATH="$LD_LIBRARY_PATH:%libdir" %t-enabled | FileCheck %s --check-prefix=REPAIRED
diff --git a/flang/test/Driver/implicit-module-prefix.f90 b/flang/test/Driver/implicit-module-prefix.f90
new file mode 100644
index 00000000000000..5c9f8da45f0e3a
--- /dev/null
+++ b/flang/test/Driver/implicit-module-prefix.f90
@@ -0,0 +1,22 @@
+! RUN: %flang -fsyntax-only -fimplicit-module-prefix -Wimplicit-module-prefix %s 2>&1 | FileCheck %s --check-prefix=ENABLED
+! RUN: %flang -fsyntax-only -fimplicit-module-prefix -fno-implicit-module-prefix -Wimplicit-module-prefix %s 2>&1 | FileCheck %s --allow-empty --check-prefix=DISABLED
+! RUN: %flang -fsyntax-only -fno-implicit-module-prefix -fimplicit-module-prefix -Wimplicit-module-prefix %s 2>&1 | FileCheck %s --check-prefix=ENABLED
+
+! Verify that the driver forwards the extension options to the frontend and
+! that the last option wins.
+
+module m
+ interface
+ module subroutine implementation()
+ end subroutine
+ end interface
+end module
+
+submodule (m) sm
+contains
+ subroutine implementation()
+ end subroutine
+end submodule
+
+! ENABLED-COUNT-1: portability: Assuming a missing MODULE prefix on 'implementation' to repair the separate module procedure interface 'm:implementation' [-Wimplicit-module-prefix]
+! DISABLED-NOT: Assuming a missing MODULE prefix
>From d5e9b22b2deb59e2de2bebe4ed4c21da5de8527c Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 2 Oct 2026 11:21:49 -0700
Subject: [PATCH 09/13] [flang] Document implicit module prefix ambiguity
---
flang/docs/Extensions.md | 5 ++++-
flang/test/Lower/implicit-module-prefix-shadow.f90 | 4 ++++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index 54121e033867f1..3d2f56fa277d9a 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -602,7 +602,10 @@ end program
procedure. Without this extension, `-pedantic` or `-Wportability` diagnoses
a likely missing prefix without changing the program. When the extension
is enabled, `-Wimplicit-module-prefix` or `-pedantic` reports each repaired
- prefix.
+ prefix. Since the extension cannot distinguish a missing prefix from an
+ intentionally local procedure with the same name as an ancestor interface,
+ it can reject a conforming program when that interface is implemented in a
+ different submodule. This behavior is compatible with gfortran.
* Old-style `PARAMETER pi=3.14` statement without parentheses
[-falternative-parameter-statement]
* `UNSIGNED` type (-funsigned)
diff --git a/flang/test/Lower/implicit-module-prefix-shadow.f90 b/flang/test/Lower/implicit-module-prefix-shadow.f90
index ae32fd4a9ec90b..0599d202e7f968 100644
--- a/flang/test/Lower/implicit-module-prefix-shadow.f90
+++ b/flang/test/Lower/implicit-module-prefix-shadow.f90
@@ -1,4 +1,5 @@
! RUN: %flang_fc1 -Wno-portability -emit-hlfir %s -o - | FileCheck %s
+! RUN: not %flang_fc1 -fimplicit-module-prefix -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=IMPLICIT
! A local procedure in beta hides alpha's interface only in beta and its
! descendants. The sibling submodule may still implement alpha's public
@@ -41,3 +42,6 @@ program main
! CHECK: fir.call @_QMalphaPthird() {{.*}}
call third
end program main
+
+! IMPLICIT: error: Module procedure 'second' in 'alpha' has multiple definitions
+! IMPLICIT: Previous definition of 'second'
>From 5ddef03c5a3bea53fa2aa61c15b3a32d03a960de Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 2 Oct 2026 11:22:18 -0700
Subject: [PATCH 10/13] [flang][test] Check silent implicit module prefix
repair
---
flang/test/Semantics/separate-mp08.f90 | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/flang/test/Semantics/separate-mp08.f90 b/flang/test/Semantics/separate-mp08.f90
index fd3a7d7e6369d6..db846cf1eb5175 100644
--- a/flang/test/Semantics/separate-mp08.f90
+++ b/flang/test/Semantics/separate-mp08.f90
@@ -1,5 +1,5 @@
-! RUN: %python %S/test_errors.py %s %flang_fc1 -Wno-portability
-! The default diagnostic may be suppressed without enabling the extension.
+! RUN: %python %S/test_errors.py %s %flang_fc1 -fimplicit-module-prefix -pedantic -Wno-implicit-module-prefix -Werror
+! The repair still applies when its diagnostic is suppressed.
module m
interface
module subroutine implementation
>From 280a510879f25e7fc734941be182e83ba42fdd06 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 2 Oct 2026 13:25:38 -0700
Subject: [PATCH 11/13] [flang] Explain ancestor-only implicit prefix repair
---
flang/lib/Semantics/resolve-names.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index abdd0bcc440f83..57f9f4742d2706 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -5949,6 +5949,9 @@ bool SubprogramVisitor::BeginSubprogram(const parser::Name &name,
if (Symbol *iface{
FindSeparateModuleProcedureInterface(name, /*emitError=*/false)};
iface && &iface->owner() != &currScope()) {
+ // Repairing a same-scope interface would also require replacing its
+ // existing symbol, as the explicit MODULE path above does. That case
+ // seems less likely than a missing prefix on an ancestor interface.
moduleInterface = iface;
context().messages().Warn(/*isInModuleFile=*/false,
context().languageFeatures(),
>From 64739720cfb907d6b85ec780dae8686ec609a908 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 2 Oct 2026 15:53:11 -0700
Subject: [PATCH 12/13] [flang][test] Clarify implicit prefix suppression
coverage
---
flang/test/Semantics/separate-mp08.f90 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/test/Semantics/separate-mp08.f90 b/flang/test/Semantics/separate-mp08.f90
index db846cf1eb5175..c278469928fb7a 100644
--- a/flang/test/Semantics/separate-mp08.f90
+++ b/flang/test/Semantics/separate-mp08.f90
@@ -1,5 +1,5 @@
! RUN: %python %S/test_errors.py %s %flang_fc1 -fimplicit-module-prefix -pedantic -Wno-implicit-module-prefix -Werror
-! The repair still applies when its diagnostic is suppressed.
+! The implicit-prefix diagnostic may be suppressed while the extension is enabled.
module m
interface
module subroutine implementation
>From fe245628e8d96541cf2ba63b6c708cb2959f097a Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Tue, 6 Oct 2026 14:34:22 -0700
Subject: [PATCH 13/13] [flang] Address final implicit module prefix review
---
flang/docs/Extensions.md | 4 +++-
flang/test/Driver/implicit-module-prefix.f90 | 3 ++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index 3d2f56fa277d9a..ea88cdc12c3cb6 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -605,7 +605,9 @@ end program
prefix. Since the extension cannot distinguish a missing prefix from an
intentionally local procedure with the same name as an ancestor interface,
it can reject a conforming program when that interface is implemented in a
- different submodule. This behavior is compatible with gfortran.
+ different submodule. This behavior is compatible with gfortran. Only
+ definitions in the current source are repaired; a module file keeps the
+ interpretation chosen when it was compiled.
* Old-style `PARAMETER pi=3.14` statement without parentheses
[-falternative-parameter-statement]
* `UNSIGNED` type (-funsigned)
diff --git a/flang/test/Driver/implicit-module-prefix.f90 b/flang/test/Driver/implicit-module-prefix.f90
index 5c9f8da45f0e3a..7f9d3c73c37189 100644
--- a/flang/test/Driver/implicit-module-prefix.f90
+++ b/flang/test/Driver/implicit-module-prefix.f90
@@ -18,5 +18,6 @@ subroutine implementation()
end subroutine
end submodule
-! ENABLED-COUNT-1: portability: Assuming a missing MODULE prefix on 'implementation' to repair the separate module procedure interface 'm:implementation' [-Wimplicit-module-prefix]
+! ENABLED: portability: Assuming a missing MODULE prefix on 'implementation' to repair the separate module procedure interface 'm:implementation' [-Wimplicit-module-prefix]
+! ENABLED-NOT: Assuming a missing MODULE prefix
! DISABLED-NOT: Assuming a missing MODULE prefix
More information about the flang-commits
mailing list