[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:29:00 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/4] [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/4] 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/4] 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
>From 672717703987af70f056b75513452add1927694c Mon Sep 17 00:00:00 2001
From: Vansh <gaurvansh133 at gmail.com>
Date: Wed, 21 Oct 2026 09:58:48 +0530
Subject: [PATCH 4/4] Use unique module names in procinterface07.f90
Flang searches the current directory for module files before the -J
directory, and other tests leave an m.mod there.
---
flang/test/Semantics/procinterface07.f90 | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/flang/test/Semantics/procinterface07.f90 b/flang/test/Semantics/procinterface07.f90
index c42e87b74ed9b..2c8b3f934b21c 100644
--- a/flang/test/Semantics/procinterface07.f90
+++ b/flang/test/Semantics/procinterface07.f90
@@ -7,7 +7,7 @@
! before any use in a descendant, so these uses must be accepted.
!--- m.f90
-module m
+module procinterface07m
abstract interface
subroutine s_iface()
end subroutine
@@ -20,13 +20,13 @@ module subroutine ms()
end module
!--- sm1.f90
-submodule (m) sm1
+submodule (procinterface07m) procinterface07sm1
procedure(q), pointer :: r1
procedure(s_iface), pointer :: q1
end submodule
!--- sm2.f90
-submodule (m:sm1) sm2
+submodule (procinterface07m:procinterface07sm1) procinterface07sm2
procedure(q1), pointer :: r2
contains
module subroutine ms()
More information about the flang-commits
mailing list