[flang-commits] [flang] [Flang][Semantics] Fix incorrect merging of separate module procedure interfaces during USE association (PR #197173)

via flang-commits flang-commits at lists.llvm.org
Thu Jul 23 09:01:59 PDT 2026


https://github.com/ShashwathiNavada updated https://github.com/llvm/llvm-project/pull/197173

>From 8415b3c447406cb8306a91883405320cf5e1789a Mon Sep 17 00:00:00 2001
From: ShashwathiNavada <shashwathinavada at gmail.com>
Date: Tue, 12 May 2026 07:27:05 -0500
Subject: [PATCH 1/5] [Flang][Semantics] Fix incorrect merging of separate
 module procedure interfaces during USE association

---
 flang/lib/Semantics/resolve-names.cpp | 36 ++++++++++++++++++++++---
 flang/test/Semantics/resolve128.f90   | 39 +++++++++++++++++++++++++++
 2 files changed, 71 insertions(+), 4 deletions(-)
 create mode 100644 flang/test/Semantics/resolve128.f90

diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index b6907cc792d76..102978816eab2 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1511,9 +1511,9 @@ void AccVisitor::CopySymbolWithDevice(const parser::Name *name) {
   // attribute.
   if (context_.languageFeatures().IsEnabled(common::LanguageFeature::CUDA) &&
       name && name->symbol) {
-    if (Symbol * copy{currScope().CopySymbol(*name->symbol)}) {
+    if (Symbol * copy{currScope().CopySymbol(name->symbol->GetUltimate())}) {
       name->symbol = copy;
-      if (auto *object{copy->detailsIf<ObjectEntityDetails>()}) {
+      if (auto *object{copy->GetUltimate().detailsIf<ObjectEntityDetails>()}) {
         object->set_cudaDataAttr(common::CUDADataAttr::Device);
       }
     }
@@ -4107,7 +4107,27 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
         if (classification == ProcedureDefinitionClass::External) {
           const auto *subp1{p1.detailsIf<SubprogramDetails>()};
           const auto *subp2{p2.detailsIf<SubprogramDetails>()};
-          return subp1 && subp1->isInterface() && subp2 && subp2->isInterface();
+          if (subp1 && subp1->isInterface() && subp2 && subp2->isInterface()) {
+            // Don't allow merging when either module has a submodule
+            // that provides a body for this procedure.
+            auto hasSubmoduleBody{[](const Symbol &p) {
+              const Scope &owner{p.owner()};
+              if (!owner.IsModule()) {
+                return false;
+              }
+              for (const Scope &child : owner.children()) {
+                if (child.IsSubmodule()) {
+                  auto it{child.find(p.name())};
+                  if (it != child.end() &&
+                      IsProcedure((*it->second).GetUltimate())) {
+                    return true;
+                  }
+                }
+              }
+              return false;
+            }};
+            return !hasSubmoduleBody(p1) && !hasSubmoduleBody(p2);
+          }
         } else if (classification == ProcedureDefinitionClass::Module) {
           return AreSameModuleSymbol(p1, p2);
         }
@@ -4687,6 +4707,10 @@ bool SubprogramVisitor::HandleStmtFunction(const parser::StmtFunctionStmt &x) {
           "Name '%s' from host scope should have a type declaration before its local statement function definition"_port_en_US,
           name.source);
       MakeSymbol(name, Attrs{}, UnknownDetails{});
+      // 'name' may still point to a host-associated SubprogramNameDetails
+      // symbol. Reset it so statement-function processing
+      // re-resolves to the new local SubprogramDetails.
+      name.symbol = nullptr;
     } else if (auto *entity{ultimate.detailsIf<EntityDetails>()};
                entity && !ultimate.has<ProcEntityDetails>()) {
       resultType = entity->type();
@@ -6012,7 +6036,11 @@ bool DeclarationVisitor::Pre(const parser::CUDAAttributesStmt &x) {
       if (!symbol) {
         symbol = &MakeSymbol(name, ObjectEntityDetails{});
       }
-      SetCUDADataAttr(name.source, *symbol, attr);
+      if (attr == common::CUDADataAttr::Value) {
+        SetExplicitAttr(*symbol, Attr::VALUE);
+      } else {
+        SetCUDADataAttr(name.source, *symbol, attr);
+      }
     }
   }
   return false;
diff --git a/flang/test/Semantics/resolve128.f90 b/flang/test/Semantics/resolve128.f90
new file mode 100644
index 0000000000000..8924ce265e29f
--- /dev/null
+++ b/flang/test/Semantics/resolve128.f90
@@ -0,0 +1,39 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+
+module submodules_03_one
+   integer :: one_i
+   interface
+      subroutine inside_one()
+      end subroutine
+   end interface
+ end module
+
+ submodule (submodules_03_one) submodules_03_sub_one
+ contains
+   subroutine inside_one()
+   one_i = 6
+   end subroutine
+ end submodule
+
+ module submodules_03_two
+   integer :: two_i
+   interface
+      subroutine inside_one()
+      end subroutine
+   end interface
+ end module
+
+ submodule (submodules_03_two) sub_one
+   contains
+   subroutine inside_one()
+   two_i = 6
+   end subroutine
+ end submodule
+
+ program p
+ use submodules_03_one
+ use submodules_03_two
+ !ERROR: Reference to 'inside_one' is ambiguous
+ call inside_one()
+ end program
+ 
\ No newline at end of file

>From 695e6ec058b710c5de6ea363e9c55a5f49ae3784 Mon Sep 17 00:00:00 2001
From: ShashwathiNavada <shashwathinavada at gmail.com>
Date: Tue, 12 May 2026 18:46:28 +0530
Subject: [PATCH 2/5] Rename resolve128.f90 to resolve130.f90

---
 flang/test/Semantics/{resolve128.f90 => resolve130.f90} | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
 rename flang/test/Semantics/{resolve128.f90 => resolve130.f90} (99%)

diff --git a/flang/test/Semantics/resolve128.f90 b/flang/test/Semantics/resolve130.f90
similarity index 99%
rename from flang/test/Semantics/resolve128.f90
rename to flang/test/Semantics/resolve130.f90
index 8924ce265e29f..e3d3165748a9b 100644
--- a/flang/test/Semantics/resolve128.f90
+++ b/flang/test/Semantics/resolve130.f90
@@ -36,4 +36,4 @@ program p
  !ERROR: Reference to 'inside_one' is ambiguous
  call inside_one()
  end program
- 
\ No newline at end of file
+ 

>From 436f12113a2df71941342b9b7ce86b5cb237d1d8 Mon Sep 17 00:00:00 2001
From: ShashwathiNavada <shashwathinavada at gmail.com>
Date: Tue, 12 May 2026 19:40:21 +0530
Subject: [PATCH 3/5] Update resolve-names.cpp

---
 flang/lib/Semantics/resolve-names.cpp | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index b4d4dbac63500..850d69c2afc7a 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1616,16 +1616,15 @@ bool AccVisitor::Pre(const parser::OpenACCBlockConstruct &x) {
 }
 
 void AccVisitor::CopySymbolWithDevice(const parser::Name *name) {
-  // When CUDA Fortran is enabled together with OpenACC, new
-  // symbols are created for the one appearing in the use_device
-  // clause. These new symbols have the CUDA Fortran device
-  // attribute.
-  if (context_.languageFeatures().IsEnabled(common::LanguageFeature::CUDA) &&
-      name && name->symbol) {
-    if (Symbol * copy{currScope().CopySymbol(name->symbol->GetUltimate())}) {
-      name->symbol = copy;
-      if (auto *object{copy->GetUltimate().detailsIf<ObjectEntityDetails>()}) {
-        object->set_cudaDataAttr(common::CUDADataAttr::Device);
+  // New symbols are created for those appearing in the use_device clause.
+  // These new symbols get the CUDA UseDevice attribute so that generic
+  // resolution can distinguish them from true DEVICE variables: UseDevice
+  // actuals are compatible with both host and device dummy arguments.
+  if (name && name->symbol) {
+    Symbol *copy{CopyUseDeviceSymbol(*name->symbol)};
+    if (copy) {
+      if (auto *object{copy->detailsIf<ObjectEntityDetails>()}) {
+        object->set_cudaDataAttr(common::CUDADataAttr::UseDevice);
       }
       name->symbol = copy;
     }

>From bd5ecf42f784bbb892eebd220dba050cf4db1e03 Mon Sep 17 00:00:00 2001
From: ShashwathiNavada <shashwathinavada at gmail.com>
Date: Mon, 6 Jul 2026 00:31:39 -0500
Subject: [PATCH 4/5] Suggested changes

---
 flang/lib/Semantics/resolve-names.cpp         | 39 +++++++++----------
 .../{resolve130.f90 => resolve132.f90}        |  5 ++-
 2 files changed, 21 insertions(+), 23 deletions(-)
 rename flang/test/Semantics/{resolve130.f90 => resolve132.f90} (62%)

diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 850d69c2afc7a..4a27f510b4169 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -4252,27 +4252,7 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
         if (classification == ProcedureDefinitionClass::External) {
           const auto *subp1{p1.detailsIf<SubprogramDetails>()};
           const auto *subp2{p2.detailsIf<SubprogramDetails>()};
-          if (subp1 && subp1->isInterface() && subp2 && subp2->isInterface()) {
-            // Don't allow merging when either module has a submodule
-            // that provides a body for this procedure.
-            auto hasSubmoduleBody{[](const Symbol &p) {
-              const Scope &owner{p.owner()};
-              if (!owner.IsModule()) {
-                return false;
-              }
-              for (const Scope &child : owner.children()) {
-                if (child.IsSubmodule()) {
-                  auto it{child.find(p.name())};
-                  if (it != child.end() &&
-                      IsProcedure((*it->second).GetUltimate())) {
-                    return true;
-                  }
-                }
-              }
-              return false;
-            }};
-            return !hasSubmoduleBody(p1) && !hasSubmoduleBody(p2);
-          }
+          return subp1 && subp1->isInterface() && subp2 && subp2->isInterface();
         } else if (classification == ProcedureDefinitionClass::Module) {
           return AreSameModuleSymbol(p1, p2);
         }
@@ -5659,6 +5639,23 @@ 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) {
+    const Scope &parent{currScope().parent()};
+    if (parent.IsModule()) {
+      if (const Symbol *host{parent.FindSymbol(name.source)}) {
+        const Symbol &hostUlt{host->GetUltimate()};
+        const auto *hostSubp{hostUlt.detailsIf<SubprogramDetails>()};
+        if (hostSubp && hostSubp->isInterface() &&
+            hostUlt.attrs().test(Attr::EXTERNAL)) {
+          context().Warn(common::UsageWarning::Portability, name.source,
+              "Subprogram '%s' in this submodule hides an external interface "
+              "from its parent module; did you mean 'MODULE %s'?"_port_en_US,
+              name.source,
+              subpFlag == Symbol::Flag::Subroutine ? "SUBROUTINE" : "FUNCTION");
+        }
+      }
+    }
+  }
   Symbol *symbol{GetSpecificFromGeneric(name)};
   const DeclTypeSpec *previousImplicitType{nullptr};
   SourceName previousName;
diff --git a/flang/test/Semantics/resolve130.f90 b/flang/test/Semantics/resolve132.f90
similarity index 62%
rename from flang/test/Semantics/resolve130.f90
rename to flang/test/Semantics/resolve132.f90
index e3d3165748a9b..985ddbf694da2 100644
--- a/flang/test/Semantics/resolve130.f90
+++ b/flang/test/Semantics/resolve132.f90
@@ -1,4 +1,4 @@
-! RUN: %python %S/test_errors.py %s %flang_fc1
+! RUN: %python %S/test_errors.py %s %flang_fc1 -pedantic -Werror
 
 module submodules_03_one
    integer :: one_i
@@ -10,6 +10,7 @@ subroutine inside_one()
 
  submodule (submodules_03_one) submodules_03_sub_one
  contains
+   !PORTABILITY: Subprogram 'inside_one' in this submodule hides an external interface from its parent module; did you mean 'MODULE SUBROUTINE'? [-Wportability]
    subroutine inside_one()
    one_i = 6
    end subroutine
@@ -25,6 +26,7 @@ subroutine inside_one()
 
  submodule (submodules_03_two) sub_one
    contains
+   !PORTABILITY: Subprogram 'inside_one' in this submodule hides an external interface from its parent module; did you mean 'MODULE SUBROUTINE'? [-Wportability]
    subroutine inside_one()
    two_i = 6
    end subroutine
@@ -33,7 +35,6 @@ subroutine inside_one()
  program p
  use submodules_03_one
  use submodules_03_two
- !ERROR: Reference to 'inside_one' is ambiguous
  call inside_one()
  end program
  

>From 1fdbd99d070f6609c00b0a28cd256e496eeb196a Mon Sep 17 00:00:00 2001
From: ShashwathiNavada <shashwathinavada at gmail.com>
Date: Thu, 23 Jul 2026 10:56:09 -0500
Subject: [PATCH 5/5] Some changes

---
 flang/lib/Semantics/resolve-names.cpp |  9 ++++---
 flang/test/Semantics/resolve132.f90   | 36 +++++++++++----------------
 2 files changed, 19 insertions(+), 26 deletions(-)

diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 6a3995a595489..204ba1fa93469 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -5773,15 +5773,16 @@ Symbol *SubprogramVisitor::PushSubprogramScope(const parser::Name &name,
     bool hasModulePrefix) {
   if (!inInterfaceBlock() && currScope().IsSubmodule() && !hasModulePrefix) {
     const Scope &parent{currScope().parent()};
-    if (parent.IsModule()) {
+    if (parent.IsModule() || parent.IsSubmodule()) {
       if (const Symbol *host{parent.FindSymbol(name.source)}) {
         const Symbol &hostUlt{host->GetUltimate()};
         const auto *hostSubp{hostUlt.detailsIf<SubprogramDetails>()};
         if (hostSubp && hostSubp->isInterface() &&
-            hostUlt.attrs().test(Attr::EXTERNAL)) {
+            hostUlt.attrs().test(Attr::MODULE)) {
           context().Warn(common::UsageWarning::Portability, name.source,
-              "Subprogram '%s' in this submodule hides an external interface "
-              "from its parent module; did you mean 'MODULE %s'?"_port_en_US,
+              "Subprogram '%s' in this submodule is missing the MODULE prefix "
+              "to implement the module procedure interface from its parent; "
+              "did you mean 'MODULE %s'?"_port_en_US,
               name.source,
               subpFlag == Symbol::Flag::Subroutine ? "SUBROUTINE" : "FUNCTION");
         }
diff --git a/flang/test/Semantics/resolve132.f90 b/flang/test/Semantics/resolve132.f90
index 985ddbf694da2..6804be3622756 100644
--- a/flang/test/Semantics/resolve132.f90
+++ b/flang/test/Semantics/resolve132.f90
@@ -1,40 +1,32 @@
 ! RUN: %python %S/test_errors.py %s %flang_fc1 -pedantic -Werror
 
-module submodules_03_one
-   integer :: one_i
+module parent_mod_9
    interface
-      subroutine inside_one()
+      module subroutine inside_one()
       end subroutine
    end interface
  end module
 
- submodule (submodules_03_one) submodules_03_sub_one
+ submodule (parent_mod_9) sub_9
  contains
-   !PORTABILITY: Subprogram 'inside_one' in this submodule hides an external interface from its parent module; did you mean 'MODULE SUBROUTINE'? [-Wportability]
+   !PORTABILITY: Subprogram 'inside_one' in this submodule is missing the MODULE prefix to implement the module procedure interface from its parent; did you mean 'MODULE SUBROUTINE'? [-Wportability]
    subroutine inside_one()
-   one_i = 6
    end subroutine
  end submodule
 
- module submodules_03_two
-   integer :: two_i
+! Same check for a function.
+module parent_mod_9f
    interface
-      subroutine inside_one()
-      end subroutine
+      module integer function inside_func()
+      end function
    end interface
  end module
 
- submodule (submodules_03_two) sub_one
-   contains
-   !PORTABILITY: Subprogram 'inside_one' in this submodule hides an external interface from its parent module; did you mean 'MODULE SUBROUTINE'? [-Wportability]
-   subroutine inside_one()
-   two_i = 6
-   end subroutine
+ submodule (parent_mod_9f) sub_9f
+ contains
+   !PORTABILITY: Subprogram 'inside_func' in this submodule is missing the MODULE prefix to implement the module procedure interface from its parent; did you mean 'MODULE FUNCTION'? [-Wportability]
+   integer function inside_func()
+     inside_func = 0
+   end function
  end submodule
-
- program p
- use submodules_03_one
- use submodules_03_two
- call inside_one()
- end program
  



More information about the flang-commits mailing list