[flang-commits] [flang] [flang] Diagnose BIND(C) procedures in submodules without ancestor interfaces (PR #194571)
Sairudra More via flang-commits
flang-commits at lists.llvm.org
Tue Apr 28 01:25:44 PDT 2026
https://github.com/Saieiei created https://github.com/llvm/llvm-project/pull/194571
This diagnoses `BIND(C)` procedures defined in submodules when their interface is not declared in the ancestor module.
The check is added in `CheckBindC()` and covers plain `BIND(C)`, explicit `NAME=`, empty/all-blank `NAME=`, valid ancestor-module interfaces, and nested submodule cases.
Fixes #194570.
>From 40a4cead8099a0af3a2dcbf356d134de2e973235 Mon Sep 17 00:00:00 2001
From: Sairudra More <moresair at pe31.hpc.amslabs.hpecorp.net>
Date: Tue, 28 Apr 2026 01:08:12 -0500
Subject: [PATCH] [flang] Diagnose invalid BIND(C) procedures in submodules
A procedure defined in a submodule cannot have a binding label unless its
interface is declared in the ancestor module.
Add this check in CheckBindC(), while preserving valid cases such as empty
or all-blank NAME= binding labels and interfaces declared in the ancestor
module.
Fixes #194570
---
flang/lib/Semantics/check-declarations.cpp | 18 ++++
flang/test/Semantics/bind-c19.f90 | 97 ++++++++++++++++++++++
2 files changed, 115 insertions(+)
create mode 100644 flang/test/Semantics/bind-c19.f90
diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp
index 4174cf6d1e340..3521f4935e73c 100644
--- a/flang/lib/Semantics/check-declarations.cpp
+++ b/flang/lib/Semantics/check-declarations.cpp
@@ -3525,6 +3525,24 @@ void CheckHelper::CheckBindC(const Symbol &symbol) {
context_.SetError(symbol);
}
}
+ // C1807 - a procedure defined in a submodule shall not have a binding
+ // label unless its interface is declared in the ancestor module.
+ const std::string *bindName{symbol.GetBindName()};
+ if (symbol.has<SubprogramDetails>() &&
+ !symbol.get<SubprogramDetails>().isInterface() && bindName &&
+ !bindName->empty() && symbol.owner().IsSubmodule()) {
+ const Symbol *iface{FindSeparateModuleSubprogramInterface(&symbol)};
+ bool ok{false};
+ if (iface) {
+ const Scope *ifaceModule{FindModuleOrSubmoduleContaining(iface->owner())};
+ ok = ifaceModule && ifaceModule->IsModule();
+ }
+ if (!ok) {
+ messages_.Say(symbol.name(),
+ "A procedure defined in a submodule shall not have a binding label unless its interface is declared in the ancestor module"_err_en_US);
+ context_.SetError(symbol);
+ }
+ }
if (symbol.has<ObjectEntityDetails>()) {
whyNot = WhyNotInteroperableObject(symbol);
} else if (symbol.has<ProcEntityDetails>() ||
diff --git a/flang/test/Semantics/bind-c19.f90 b/flang/test/Semantics/bind-c19.f90
new file mode 100644
index 0000000000000..a0aa6736c6b5b
--- /dev/null
+++ b/flang/test/Semantics/bind-c19.f90
@@ -0,0 +1,97 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! Check for C1807: A procedure defined in a submodule shall not have a
+! binding label unless its interface is declared in the ancestor module.
+
+module m1
+ implicit none
+end module
+
+! Submodule with BIND(C) procedures that have no interface in the ancestor
+! module - these violate C1807.
+submodule(m1) sm1
+ implicit none
+contains
+ !ERROR: A procedure defined in a submodule shall not have a binding label unless its interface is declared in the ancestor module
+ subroutine sub1() bind(c)
+ end subroutine
+ !ERROR: A procedure defined in a submodule shall not have a binding label unless its interface is declared in the ancestor module
+ subroutine sub2() bind(c, name="my_sub2")
+ end subroutine
+ !ERROR: A procedure defined in a submodule shall not have a binding label unless its interface is declared in the ancestor module
+ function func1() bind(c)
+ use, intrinsic :: iso_c_binding, only: c_int
+ integer(c_int) :: func1
+ func1 = 0
+ end function
+end submodule
+
+! Valid: interfaces declared in ancestor module.
+module m2
+ implicit none
+ interface
+ module subroutine sub3() bind(c, name="sub3")
+ end subroutine
+ module function func2() bind(c) result(res)
+ use, intrinsic :: iso_c_binding, only: c_int
+ integer(c_int) :: res
+ end function
+ end interface
+end module
+
+submodule(m2) sm2
+ implicit none
+contains
+ module subroutine sub3() bind(c, name="sub3")
+ end subroutine
+ module function func2() bind(c) result(res)
+ use, intrinsic :: iso_c_binding, only: c_int
+ integer(c_int) :: res
+ res = 0
+ end function
+end submodule
+
+! Valid: BIND(C,NAME="") gives no binding label.
+module m3
+ implicit none
+end module
+
+submodule(m3) sm3
+ implicit none
+contains
+ subroutine sub4() bind(c, name="")
+ end subroutine
+end submodule
+
+! Valid: BIND(C,NAME=" ") gives no binding label (blanks are discarded).
+module m4
+ implicit none
+end module
+
+submodule(m4) sm4
+ implicit none
+contains
+ subroutine sub5() bind(c, name=" ")
+ end subroutine
+end submodule
+
+! Invalid: interface declared in a parent submodule, not the ancestor module.
+! C1807 requires the interface be in the ancestor module.
+module m5
+ implicit none
+end module
+
+submodule(m5) sm5parent
+ implicit none
+ interface
+ module subroutine sub6() bind(c, name="sub6")
+ end subroutine
+ end interface
+end submodule
+
+submodule(m5:sm5parent) sm5child
+ implicit none
+contains
+ !ERROR: A procedure defined in a submodule shall not have a binding label unless its interface is declared in the ancestor module
+ module subroutine sub6() bind(c, name="sub6")
+ end subroutine
+end submodule
More information about the flang-commits
mailing list