[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
Sun Jul 5 22:32:04 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/4] [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/4] 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/4] 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/4] 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
  



More information about the flang-commits mailing list