[flang-commits] [flang] [flang] Reject interface name declared by a later PROCEDURE statement (PR #232928)

via flang-commits flang-commits at lists.llvm.org
Tue Oct 20 21:16:04 PDT 2026


https://github.com/Glitch-On updated https://github.com/llvm/llvm-project/pull/232928

>From 9150bb3a436bd72f6a0f4c304963ea9a58aed6fb Mon Sep 17 00:00:00 2001
From: Vansh <gaurvansh133 at gmail.com>
Date: Sat, 17 Oct 2026 01:22:48 +0530
Subject: [PATCH 1/3] [flang] Reject interface name declared by a later
 PROCEDURE statement

C1515 requires that a name used as a procedure interface, when it is
itself declared by a procedure declaration statement, be declared before
that use. Name resolution creates a placeholder symbol for a forward
reference and only checks it after the whole specification part, by which
point the later PROCEDURE statement has given it an explicit interface, so
the program was accepted.

In the deferred check, report an error when the interface name resolves to
a procedure entity whose declaration comes after the use. Forward
references to interface bodies and module procedures are still accepted.

Fixes #229229
---
 flang/lib/Semantics/resolve-names.cpp    | 19 +++++++++++++
 flang/test/Semantics/procinterface06.f90 | 36 ++++++++++++++++++++++++
 2 files changed, 55 insertions(+)
 create mode 100644 flang/test/Semantics/procinterface06.f90

diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index d0964294d10c0..cf315147bdf02 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1187,6 +1187,7 @@ class DeclarationVisitor : public ArraySpecVisitor,
   void LegacyDataInitialization(const parser::Name &,
       const std::list<common::Indirection<parser::DataStmtValue>> &values);
   void CheckExplicitInterface(const parser::Name &);
+  void CheckInterfaceNameDeclaredBeforeUse(const parser::Name &);
   void CheckBindings(const parser::TypeBoundProcedureStmt::WithoutInterface &);
 
   const parser::Name *ResolveDesignator(const parser::Designator &);
@@ -8816,6 +8817,23 @@ void DeclarationVisitor::CheckExplicitInterface(const parser::Name &name) {
   }
 }
 
+// C1515: a name that is declared by a procedure-declaration-stmt may be
+// used as an interface name only after that declaration.
+void DeclarationVisitor::CheckInterfaceNameDeclaredBeforeUse(
+    const parser::Name &name) {
+  if (const Symbol * symbol{name.symbol}) {
+    if (!context().HasError(*symbol) && symbol->has<ProcEntityDetails>() &&
+        symbol->HasExplicitInterface() &&
+        context().allCookedSources().Precedes(name.source, symbol->name())) {
+      Say(name,
+          "'%s' must be declared before it is used as a procedure interface"_err_en_US,
+          symbol->name())
+          .Attach(symbol->name(), "Later declaration of '%s'"_en_US,
+              symbol->name());
+    }
+  }
+}
+
 // Create a symbol for a type parameter, component, or procedure binding in
 // the current derived type scope. Return false on error.
 Symbol *DeclarationVisitor::MakeTypeSymbol(
@@ -11977,6 +11995,7 @@ class DeferredCheckVisitor {
   void Post(const parser::ProcInterface &pi) {
     if (const auto *name{std::get_if<parser::Name>(&pi.u)}) {
       resolver_.CheckExplicitInterface(*name);
+      resolver_.CheckInterfaceNameDeclaredBeforeUse(*name);
     }
   }
   bool Pre(const parser::EntityDecl &decl) {
diff --git a/flang/test/Semantics/procinterface06.f90 b/flang/test/Semantics/procinterface06.f90
new file mode 100644
index 0000000000000..ed2570cdaa0a1
--- /dev/null
+++ b/flang/test/Semantics/procinterface06.f90
@@ -0,0 +1,36 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! C1515: a name declared by a PROCEDURE statement may be used as an
+! interface name only after that declaration.
+module m
+  implicit none
+  abstract interface
+    subroutine s_iface()
+    end subroutine
+  end interface
+  !ERROR: 'q' must be declared before it is used as a procedure interface
+  procedure(q), pointer :: r
+  !ERROR: 'q' must be declared before it is used as a procedure interface
+  procedure(q) :: r2
+  procedure(s_iface), pointer :: q
+  procedure(q), pointer :: t ! ok, q is declared above
+  procedure(later), pointer :: u ! ok, later is declared by an interface body
+  interface
+    subroutine later()
+    end subroutine
+  end interface
+  type dt
+    !ERROR: 'q2' must be declared before it is used as a procedure interface
+    procedure(q2), pointer, nopass :: comp
+  end type
+  procedure(s_iface), pointer :: q2
+end module
+
+subroutine sub(a, b)
+  abstract interface
+    subroutine s_iface()
+    end subroutine
+  end interface
+  !ERROR: 'b' must be declared before it is used as a procedure interface
+  procedure(b), pointer :: a
+  procedure(s_iface) :: b
+end subroutine

>From 9101b514525a96afb704d7ec7a98437a07e41f56 Mon Sep 17 00:00:00 2001
From: Vansh <gaurvansh133 at gmail.com>
Date: Sat, 17 Oct 2026 11:41:53 +0530
Subject: [PATCH 2/3] Fix clang-format issue

---
 flang/lib/Semantics/resolve-names.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index cf315147bdf02..400ccc020df9d 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -8821,7 +8821,7 @@ void DeclarationVisitor::CheckExplicitInterface(const parser::Name &name) {
 // used as an interface name only after that declaration.
 void DeclarationVisitor::CheckInterfaceNameDeclaredBeforeUse(
     const parser::Name &name) {
-  if (const Symbol * symbol{name.symbol}) {
+  if (const Symbol *symbol{name.symbol}) {
     if (!context().HasError(*symbol) && symbol->has<ProcEntityDetails>() &&
         symbol->HasExplicitInterface() &&
         context().allCookedSources().Precedes(name.source, symbol->name())) {

>From 03dbcc6bfb39a72a056557375ca92871b20bc325 Mon Sep 17 00:00:00 2001
From: Vansh <gaurvansh133 at gmail.com>
Date: Wed, 21 Oct 2026 09:45:52 +0530
Subject: [PATCH 3/3] Compare positions only within one source; check deferred
 bindings

Precedes() orders names from different cooked sources by registration
number, so a declaration read from a module file always looked later than
the use and separately compiled submodules were rejected. Report the error
only when the use and the declaration are in the same cooked source.

Also apply the check to the interface name of a deferred type-bound
procedure, and add a separate-compilation test.
---
 flang/lib/Semantics/resolve-names.cpp    | 13 ++++++--
 flang/test/Semantics/procinterface06.f90 | 14 +++++++++
 flang/test/Semantics/procinterface07.f90 | 38 ++++++++++++++++++++++++
 3 files changed, 62 insertions(+), 3 deletions(-)
 create mode 100644 flang/test/Semantics/procinterface07.f90

diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 400ccc020df9d..f2af360820620 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -8822,9 +8822,14 @@ void DeclarationVisitor::CheckExplicitInterface(const parser::Name &name) {
 void DeclarationVisitor::CheckInterfaceNameDeclaredBeforeUse(
     const parser::Name &name) {
   if (const Symbol *symbol{name.symbol}) {
+    // A declaration in a different cooked source, such as a module file,
+    // comes from an earlier compilation and cannot follow the use.
+    const parser::CookedSource *cooked{
+        context().allCookedSources().Find(name.source)};
     if (!context().HasError(*symbol) && symbol->has<ProcEntityDetails>() &&
-        symbol->HasExplicitInterface() &&
-        context().allCookedSources().Precedes(name.source, symbol->name())) {
+        symbol->HasExplicitInterface() && cooked &&
+        cooked->AsCharBlock().Contains(symbol->name()) &&
+        name.source.begin() < symbol->name().begin()) {
       Say(name,
           "'%s' must be declared before it is used as a procedure interface"_err_en_US,
           symbol->name())
@@ -12011,7 +12016,9 @@ class DeferredCheckVisitor {
     return false;
   }
   void Post(const parser::TypeBoundProcedureStmt::WithInterface &tbps) {
-    resolver_.CheckExplicitInterface(std::get<parser::Name>(tbps.t));
+    const auto &name{std::get<parser::Name>(tbps.t)};
+    resolver_.CheckExplicitInterface(name);
+    resolver_.CheckInterfaceNameDeclaredBeforeUse(name);
   }
   void Post(const parser::TypeBoundProcedureStmt::WithoutInterface &tbps) {
     if (outerScope_) {
diff --git a/flang/test/Semantics/procinterface06.f90 b/flang/test/Semantics/procinterface06.f90
index ed2570cdaa0a1..a6141a179caa8 100644
--- a/flang/test/Semantics/procinterface06.f90
+++ b/flang/test/Semantics/procinterface06.f90
@@ -25,6 +25,20 @@ subroutine later()
   procedure(s_iface), pointer :: q2
 end module
 
+module m2
+  implicit none
+  type, abstract :: t
+  contains
+    !ERROR: 'q' must be declared before it is used as a procedure interface
+    procedure(q), deferred, nopass :: b
+  end type
+  abstract interface
+    subroutine s_iface()
+    end subroutine
+  end interface
+  procedure(s_iface), pointer :: q
+end module
+
 subroutine sub(a, b)
   abstract interface
     subroutine s_iface()
diff --git a/flang/test/Semantics/procinterface07.f90 b/flang/test/Semantics/procinterface07.f90
new file mode 100644
index 0000000000000..c42e87b74ed9b
--- /dev/null
+++ b/flang/test/Semantics/procinterface07.f90
@@ -0,0 +1,38 @@
+! RUN: split-file %s %t
+! RUN: %flang_fc1 -fsyntax-only -J%t %t/m.f90
+! RUN: %flang_fc1 -fsyntax-only -J%t %t/sm1.f90
+! RUN: %flang_fc1 -fsyntax-only -J%t %t/sm2.f90
+! C1515: an interface name declared by a procedure-declaration-stmt in an
+! ancestor module or submodule that was compiled separately is declared
+! before any use in a descendant, so these uses must be accepted.
+
+!--- m.f90
+module m
+  abstract interface
+    subroutine s_iface()
+    end subroutine
+  end interface
+  procedure(s_iface), pointer :: q
+  interface
+    module subroutine ms()
+    end subroutine
+  end interface
+end module
+
+!--- sm1.f90
+submodule (m) sm1
+  procedure(q), pointer :: r1
+  procedure(s_iface), pointer :: q1
+end submodule
+
+!--- sm2.f90
+submodule (m:sm1) sm2
+  procedure(q1), pointer :: r2
+contains
+  module subroutine ms()
+    procedure(q), pointer :: r3
+    procedure(q1), pointer :: r4
+    r3 => null()
+    r4 => null()
+  end subroutine
+end submodule



More information about the flang-commits mailing list