[flang-commits] [flang] [flang] Reject type bound procedure overrides with mismatched passed-object dummy names (PR #210876)

via flang-commits flang-commits at lists.llvm.org
Thu Jul 30 21:15:42 PDT 2026


https://github.com/ejose02 updated https://github.com/llvm/llvm-project/pull/210876

>From a9bd9439c86fdbf25e47c336897c5cde563d46de Mon Sep 17 00:00:00 2001
From: ejose <ejose at amd.com>
Date: Tue, 21 Jul 2026 05:05:55 +0000
Subject: [PATCH 1/3] [flang] Reject type bound procedure overrides with
 mismatched passed-object dummy names

Fixes #206913
Issue:
Flang wrongly accepts a type-bound procedure override when the implementing procedure uses a different dummy name than the parent deferred interface.

Root cause:
The PASS override check uses CanOverride, which compares dummy types but not dummy names for the passed-object argument.

Fix:
Check that dummy names match before CanOverride and report a clear error
---
 flang/lib/Semantics/check-declarations.cpp | 20 +++++++++++++++++
 flang/test/Semantics/bindings08.f90        | 25 ++++++++++++++++++++++
 2 files changed, 45 insertions(+)
 create mode 100644 flang/test/Semantics/bindings08.f90

diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp
index 0a62e199d9779..bdd12811c00b8 100644
--- a/flang/lib/Semantics/check-declarations.cpp
+++ b/flang/lib/Semantics/check-declarations.cpp
@@ -2700,6 +2700,19 @@ void CheckHelper::CheckPassArg(
   }
 }
 
+static bool OverrideDummyNamesMatch(
+    const Procedure &binding, const Procedure &overridden) {
+  if (binding.dummyArguments.size() != overridden.dummyArguments.size()) {
+    return false;
+  }
+  for (std::size_t j{0}; j < binding.dummyArguments.size(); ++j) {
+    if (binding.dummyArguments[j].name != overridden.dummyArguments[j].name) {
+      return false;
+    }
+  }
+  return true;
+}
+
 void CheckHelper::CheckProcBinding(
     const Symbol &symbol, const ProcBindingDetails &binding) {
   const Scope &dtScope{symbol.owner()};
@@ -2783,6 +2796,13 @@ void CheckHelper::CheckProcBinding(
               if (*passIndex != *overriddenPassIndex) {
                 SayWithDeclaration(*overridden,
                     "A type-bound procedure and its override must use the same PASS argument"_err_en_US);
+              } else if (!OverrideDummyNamesMatch(
+                             *bindingChars, *overriddenChars)) {
+                SayWithDeclaration(*overridden,
+                    "Passed-object dummy arguments of type-bound procedure "
+                    "'%s' "
+                    "and its override must correspond by name and position"_err_en_US,
+                    symbol.name());
               } else if (!bindingChars->CanOverride(
                              *overriddenChars, passIndex)) {
                 SayWithDeclaration(*overridden,
diff --git a/flang/test/Semantics/bindings08.f90 b/flang/test/Semantics/bindings08.f90
new file mode 100644
index 0000000000000..4542e0091a0c9
--- /dev/null
+++ b/flang/test/Semantics/bindings08.f90
@@ -0,0 +1,25 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+
+module m
+    type, abstract :: parent
+        contains
+        procedure(ip), deferred :: set
+    end type
+    abstract interface
+        subroutine ip(x)
+            import parent
+            class(parent) :: x
+        end subroutine
+    end interface
+    type, public, extends(parent), abstract :: child
+    end type
+    type, extends(child) :: grandchild
+        contains
+        !ERROR: Passed-object dummy arguments of type-bound procedure 'set' and its override must correspond by name and position
+        procedure :: set
+    end type
+    contains
+    subroutine set(y)
+        class(grandchild) :: y
+    end subroutine
+end module

>From ead38ff433cc65ec6b86c58c0e0749d14f7d64f1 Mon Sep 17 00:00:00 2001
From: ejose <ejose at amd.com>
Date: Tue, 28 Jul 2026 05:09:35 +0000
Subject: [PATCH 2/3] Addressed the review concerns: - Return nullopt on
 arg-count mismatch so CanOverride still reports compatible interfaces. -
 Applied name check to NOPASS. - Broadened diagnostic and expanded
 bindings08.f90 with regression cases.

---
 flang/lib/Semantics/check-declarations.cpp |  22 ++--
 flang/test/Semantics/bindings08.f90        | 133 +++++++++++++++++----
 2 files changed, 123 insertions(+), 32 deletions(-)

diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp
index bdd12811c00b8..17e2095c61167 100644
--- a/flang/lib/Semantics/check-declarations.cpp
+++ b/flang/lib/Semantics/check-declarations.cpp
@@ -2700,17 +2700,17 @@ void CheckHelper::CheckPassArg(
   }
 }
 
-static bool OverrideDummyNamesMatch(
+static std::optional<std::size_t> FindOverrideDummyNameMismatch(
     const Procedure &binding, const Procedure &overridden) {
   if (binding.dummyArguments.size() != overridden.dummyArguments.size()) {
-    return false;
+    return std::nullopt;
   }
   for (std::size_t j{0}; j < binding.dummyArguments.size(); ++j) {
     if (binding.dummyArguments[j].name != overridden.dummyArguments[j].name) {
-      return false;
+      return j;
     }
   }
-  return true;
+  return std::nullopt;
 }
 
 void CheckHelper::CheckProcBinding(
@@ -2783,7 +2783,12 @@ void CheckHelper::CheckProcBinding(
         const auto *bindingChars{Characterize(symbol)};
         const auto *overriddenChars{Characterize(*overridden)};
         if (bindingChars && overriddenChars) {
-          if (isNopass) {
+          if (FindOverrideDummyNameMismatch(*bindingChars, *overriddenChars)) {
+            SayWithDeclaration(*overridden,
+                "Dummy arguments of type-bound procedure '%s' and its override "
+                "must correspond by name and position"_err_en_US,
+                symbol.name());
+          } else if (isNopass) {
             if (!bindingChars->CanOverride(*overriddenChars, std::nullopt)) {
               SayWithDeclaration(*overridden,
                   "A NOPASS type-bound procedure and its override must have identical interfaces"_err_en_US);
@@ -2796,13 +2801,6 @@ void CheckHelper::CheckProcBinding(
               if (*passIndex != *overriddenPassIndex) {
                 SayWithDeclaration(*overridden,
                     "A type-bound procedure and its override must use the same PASS argument"_err_en_US);
-              } else if (!OverrideDummyNamesMatch(
-                             *bindingChars, *overriddenChars)) {
-                SayWithDeclaration(*overridden,
-                    "Passed-object dummy arguments of type-bound procedure "
-                    "'%s' "
-                    "and its override must correspond by name and position"_err_en_US,
-                    symbol.name());
               } else if (!bindingChars->CanOverride(
                              *overriddenChars, passIndex)) {
                 SayWithDeclaration(*overridden,
diff --git a/flang/test/Semantics/bindings08.f90 b/flang/test/Semantics/bindings08.f90
index 4542e0091a0c9..36b16bcd17752 100644
--- a/flang/test/Semantics/bindings08.f90
+++ b/flang/test/Semantics/bindings08.f90
@@ -1,25 +1,118 @@
 ! RUN: %python %S/test_errors.py %s %flang_fc1
 
 module m
-    type, abstract :: parent
-        contains
-        procedure(ip), deferred :: set
-    end type
-    abstract interface
-        subroutine ip(x)
-            import parent
-            class(parent) :: x
-        end subroutine
-    end interface
-    type, public, extends(parent), abstract :: child
-    end type
-    type, extends(child) :: grandchild
-        contains
-        !ERROR: Passed-object dummy arguments of type-bound procedure 'set' and its override must correspond by name and position
-        procedure :: set
-    end type
-    contains
-    subroutine set(y)
-        class(grandchild) :: y
+  type, abstract :: parent
+   contains
+    procedure(ip), deferred :: set
+  end type
+  abstract interface
+    subroutine ip(x)
+      import parent
+      class(parent) :: x
     end subroutine
+  end interface
+  type, public, extends(parent), abstract :: child
+  end type
+  type, extends(child) :: grandchild
+   contains
+    !ERROR: Dummy arguments of type-bound procedure 'set' and its override must correspond by name and position
+    procedure :: set
+  end type
+ contains
+  subroutine set(y)
+    class(grandchild) :: y
+  end subroutine
+end module
+
+! Valid override with matching dummy names
+module m_ok
+  type, abstract :: t
+   contains
+    procedure(ip), deferred :: set
+  end type
+  abstract interface
+    subroutine ip(x)
+      import t
+      class(t) :: x
+    end subroutine
+  end interface
+  type, extends(t) :: e
+   contains
+    procedure :: set
+  end type
+ contains
+  subroutine set(x)
+    class(e) :: x
+  end subroutine
+end module
+
+! Extra dummy argument: interface mismatch, not a name error
+module m_extra
+  type, abstract :: t
+   contains
+    procedure(ip), deferred :: set
+  end type
+  abstract interface
+    subroutine ip(x)
+      import t
+      class(t) :: x
+    end subroutine
+  end interface
+  type, extends(t) :: e
+   contains
+    !ERROR: A type-bound procedure and its override must have compatible interfaces
+    procedure :: set
+  end type
+ contains
+  subroutine set(x, n)
+    class(e) :: x
+    integer :: n
+  end subroutine
+end module
+
+! Non-pass dummy renamed
+module m_rename
+  type, abstract :: t
+   contains
+    procedure(ip), deferred :: set
+  end type
+  abstract interface
+    subroutine ip(x, n)
+      import t
+      class(t) :: x
+      integer :: n
+    end subroutine
+  end interface
+  type, extends(t) :: e
+   contains
+    !ERROR: Dummy arguments of type-bound procedure 'set' and its override must correspond by name and position
+    procedure :: set
+  end type
+ contains
+  subroutine set(x, m2)
+    class(e) :: x
+    integer :: m2
+  end subroutine
+end module
+
+! NOPASS deferred override with mismatched dummy name
+module m_nopass
+  type, abstract :: t
+   contains
+    procedure(ip), deferred, nopass :: act
+  end type
+  abstract interface
+    subroutine ip(a)
+      integer :: a
+    end subroutine
+  end interface
+  type, extends(t) :: e
+   contains
+    !ERROR: Dummy arguments of type-bound procedure 'act' and its override must correspond by name and position
+    procedure, nopass :: act => impl
+  end type
+ contains
+  subroutine impl(b)
+    integer :: b
+  end subroutine
 end module

>From 41649b5edcafa545e4c85c217319667837f70269 Mon Sep 17 00:00:00 2001
From: ejose <ejose at amd.com>
Date: Fri, 31 Jul 2026 04:14:27 +0000
Subject: [PATCH 3/3] [flang] Name mismatched TBP override dummies in
 diagnostic

Use the index from FindOverrideDummyNameMismatch to report the override and overridden dummy argument names.
---
 flang/lib/Semantics/check-declarations.cpp | 10 ++++++----
 flang/test/Semantics/bindings08.f90        |  6 +++---
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp
index 17e2095c61167..41ddb61c4340d 100644
--- a/flang/lib/Semantics/check-declarations.cpp
+++ b/flang/lib/Semantics/check-declarations.cpp
@@ -2783,11 +2783,13 @@ void CheckHelper::CheckProcBinding(
         const auto *bindingChars{Characterize(symbol)};
         const auto *overriddenChars{Characterize(*overridden)};
         if (bindingChars && overriddenChars) {
-          if (FindOverrideDummyNameMismatch(*bindingChars, *overriddenChars)) {
+          if (auto mismatch{FindOverrideDummyNameMismatch(
+                  *bindingChars, *overriddenChars)}) {
             SayWithDeclaration(*overridden,
-                "Dummy arguments of type-bound procedure '%s' and its override "
-                "must correspond by name and position"_err_en_US,
-                symbol.name());
+                "Dummy argument '%s' of type-bound procedure '%s' must "
+                "correspond by name to '%s' in the overridden procedure"_err_en_US,
+                bindingChars->dummyArguments[*mismatch].name, symbol.name(),
+                overriddenChars->dummyArguments[*mismatch].name);
           } else if (isNopass) {
             if (!bindingChars->CanOverride(*overriddenChars, std::nullopt)) {
               SayWithDeclaration(*overridden,
diff --git a/flang/test/Semantics/bindings08.f90 b/flang/test/Semantics/bindings08.f90
index 36b16bcd17752..bfc118cd74308 100644
--- a/flang/test/Semantics/bindings08.f90
+++ b/flang/test/Semantics/bindings08.f90
@@ -15,7 +15,7 @@ subroutine ip(x)
   end type
   type, extends(child) :: grandchild
    contains
-    !ERROR: Dummy arguments of type-bound procedure 'set' and its override must correspond by name and position
+    !ERROR: Dummy argument 'y' of type-bound procedure 'set' must correspond by name to 'x' in the overridden procedure
     procedure :: set
   end type
  contains
@@ -85,7 +85,7 @@ subroutine ip(x, n)
   end interface
   type, extends(t) :: e
    contains
-    !ERROR: Dummy arguments of type-bound procedure 'set' and its override must correspond by name and position
+    !ERROR: Dummy argument 'm2' of type-bound procedure 'set' must correspond by name to 'n' in the overridden procedure
     procedure :: set
   end type
  contains
@@ -108,7 +108,7 @@ subroutine ip(a)
   end interface
   type, extends(t) :: e
    contains
-    !ERROR: Dummy arguments of type-bound procedure 'act' and its override must correspond by name and position
+    !ERROR: Dummy argument 'b' of type-bound procedure 'act' must correspond by name to 'a' in the overridden procedure
     procedure, nopass :: act => impl
   end type
  contains



More information about the flang-commits mailing list