[flang-commits] [flang] [flang] Exclude procedure scope variables in isModuleScopeDataUniquedName (PR #192999)

via flang-commits flang-commits at lists.llvm.org
Mon Apr 20 08:28:03 PDT 2026


https://github.com/nvptm created https://github.com/llvm/llvm-project/pull/192999

In particular, for saved local such as `x` in the sample below, isModuleScopeDataUniquedName should return false.
```
module m
contains
    subroutine foo()
      integer, save :: x ! <-- SAVE
    end subroutine
end
```

>From 06eb750e9d6216169c4720abdf67f0fac3cce33b Mon Sep 17 00:00:00 2001
From: nvpm <pmathew at nvidia.com>
Date: Mon, 20 Apr 2026 08:22:09 -0700
Subject: [PATCH] Update isModuleScopeDataUniquedName to exclude module
 procedure scope variables

---
 .../flang/Optimizer/Support/InternalNames.h   |  4 +++-
 flang/lib/Optimizer/Support/InternalNames.cpp |  5 ++++-
 .../unittests/Optimizer/InternalNamesTest.cpp | 21 ++++++++++++++++---
 3 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/flang/include/flang/Optimizer/Support/InternalNames.h b/flang/include/flang/Optimizer/Support/InternalNames.h
index 238d491ac99c1..004fb8633f4df 100644
--- a/flang/include/flang/Optimizer/Support/InternalNames.h
+++ b/flang/include/flang/Optimizer/Support/InternalNames.h
@@ -158,7 +158,9 @@ struct NameUniquer {
 
   /// True if \p uniquedName denotes module-scope data (variable, named
   /// constant, or common block), as opposed to procedures, types, or other
-  /// symbols that may still carry a module prefix in the mangling.
+  /// symbols that may still carry a module prefix in the mangling. This
+  /// excludes symbols nested in a procedure according to the mangled prefix
+  /// (including \c SAVE locals in module procedures).
   static bool isModuleScopeDataUniquedName(llvm::StringRef uniquedName);
 
   /// Given a mangled derived type name, get the name of the related derived
diff --git a/flang/lib/Optimizer/Support/InternalNames.cpp b/flang/lib/Optimizer/Support/InternalNames.cpp
index b02ee054387f4..ba46249d1bba7 100644
--- a/flang/lib/Optimizer/Support/InternalNames.cpp
+++ b/flang/lib/Optimizer/Support/InternalNames.cpp
@@ -351,13 +351,16 @@ bool fir::NameUniquer::belongsToModule(llvm::StringRef uniquedName,
 /// uniqued root produced by \c fir::NameUniquer; \c deconstruct exposes that
 /// as \c parts.modules. A non-empty module path means the symbol was declared
 /// under a module or submodule, not only at program or internal unit scope.
+/// Procedure nesting is encoded as \c F<proc> ancestors in \c parts.procs;
+/// those must be empty so we do not classify locals inside module procedures
+/// (including \c SAVE locals) as module-scope data.
 /// We then require \c VARIABLE, \c CONSTANT, or \c COMMON so we match
 /// module-level data (including common), not procedures or other name kinds
 /// that can also carry a module prefix.
 bool fir::NameUniquer::isModuleScopeDataUniquedName(
     llvm::StringRef uniquedName) {
   auto [kind, parts] = fir::NameUniquer::deconstruct(uniquedName);
-  if (parts.modules.empty())
+  if (parts.modules.empty() || !parts.procs.empty())
     return false;
 
   switch (kind) {
diff --git a/flang/unittests/Optimizer/InternalNamesTest.cpp b/flang/unittests/Optimizer/InternalNamesTest.cpp
index ab0b91622980a..611fd8d571e27 100644
--- a/flang/unittests/Optimizer/InternalNamesTest.cpp
+++ b/flang/unittests/Optimizer/InternalNamesTest.cpp
@@ -154,9 +154,7 @@ TEST(InternalNamesTest, doNamelistGroup) {
 TEST(InternalNamesTest, deconstructTest) {
   std::pair actual = NameUniquer::deconstruct("_QChello");
   auto expectedNameKind = NameUniquer::NameKind::COMMON;
-  struct DeconstructedName expectedComponents {
-    {}, {}, 0, "hello", {}
-  };
+  struct DeconstructedName expectedComponents{{}, {}, 0, "hello", {}};
   validateDeconstructedName(actual, expectedNameKind, expectedComponents);
 }
 
@@ -218,6 +216,23 @@ TEST(InternalNamesTest, needExternalNameMangling) {
   ASSERT_TRUE(NameUniquer::needExternalNameMangling("_QCa"));
 }
 
+TEST(InternalNamesTest, isModuleScopeDataUniquedName) {
+  // True cases: module-scope variable, constant, common block
+  ASSERT_TRUE(NameUniquer::isModuleScopeDataUniquedName("_QMmodEintvar"));
+  ASSERT_TRUE(NameUniquer::isModuleScopeDataUniquedName("_QMmodECpi"));
+  ASSERT_TRUE(NameUniquer::isModuleScopeDataUniquedName("_QMmodSsubEvar"));
+  // False cases (in order): module procedure, derived type, unqualified common
+  // block (no module), saved variable local to a procedure (nested blocks),
+  // external name, empty name, non-module-scope name.
+  ASSERT_FALSE(NameUniquer::isModuleScopeDataUniquedName("_QMmodPsub"));
+  ASSERT_FALSE(NameUniquer::isModuleScopeDataUniquedName("_QMmodTmytype"));
+  ASSERT_FALSE(NameUniquer::isModuleScopeDataUniquedName("_QCblk"));
+  ASSERT_FALSE(NameUniquer::isModuleScopeDataUniquedName("_QFsubB2Ex"));
+  ASSERT_FALSE(NameUniquer::isModuleScopeDataUniquedName("someExternalName"));
+  ASSERT_FALSE(NameUniquer::isModuleScopeDataUniquedName(""));
+  ASSERT_FALSE(NameUniquer::isModuleScopeDataUniquedName("_QMmFfooEx"));
+}
+
 TEST(InternalNamesTest, isExternalFacingUniquedName) {
   std::pair result = NameUniquer::deconstruct("_QMmodSs1modSs2modFsubPfun");
 



More information about the flang-commits mailing list