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
