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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to