[flang-commits] [flang] 96285e0 - [flang][OpenMP] Add diagnostic for bare DECLARE TARGET in invalid scopes (#198039)
via flang-commits
flang-commits at lists.llvm.org
Sat May 16 19:15:31 PDT 2026
Author: Matt
Date: 2026-05-16T21:15:27-05:00
New Revision: 96285e0deec83c10551ccd09e7eae2a68bcbe7c1
URL: https://github.com/llvm/llvm-project/commit/96285e0deec83c10551ccd09e7eae2a68bcbe7c1
DIFF: https://github.com/llvm/llvm-project/commit/96285e0deec83c10551ccd09e7eae2a68bcbe7c1.diff
LOG: [flang][OpenMP] Add diagnostic for bare DECLARE TARGET in invalid scopes (#198039)
The bare form of `!$omp declare target` (without arguments or clauses)
is only permitted in the specification part of a subroutine, function,
or interface body (OpenMP 5.2, section 7.8.2). Flang previously accepted
it silently in BLOCK DATA, PROGRAM, MODULE, SUBMODULE, and BLOCK
constructs.
This patch:
- Adds a semantic check rejecting the bare form outside Subprogram
scopes.
- Adds MpSubprogramStmt/EndMpSubprogramStmt scope tracking to avoid
false positives in separate module subprograms (MODULE PROCEDURE).
- Fixes pre-existing BlockConstruct scope tracking bugs: the Leave
handler was pushing instead of popping (stack corruption), and the
Enter handler used blockStmt.source which resolves to the parent
scope. Now uses endBlockStmt.source (walked inside the block scope
during name resolution) for correct BlockConstruct scope identity.
Fixes #198038.
Assisted-by: Claude Opus 4.6.
Co-authored-by: Matt P. Dziubinski <matt-p.dziubinski at hpe.com>
Added:
flang/test/Semantics/OpenMP/declare-target09.f90
Modified:
flang/lib/Semantics/check-omp-structure.cpp
flang/lib/Semantics/check-omp-structure.h
Removed:
################################################################################
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 022003a8e1728..a40042068cfe5 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -162,25 +162,25 @@ bool OmpStructureChecker::Enter(const parser::EndFunctionStmt &x) {
return true;
}
+bool OmpStructureChecker::Enter(const parser::MpSubprogramStmt &x) {
+ const Symbol *sym{x.v.symbol};
+ scopeStack_.push_back(sym->scope());
+ return true;
+}
+
+bool OmpStructureChecker::Enter(const parser::EndMpSubprogramStmt &x) {
+ scopeStack_.pop_back();
+ return true;
+}
+
bool OmpStructureChecker::Enter(const parser::BlockConstruct &x) {
- auto &specPart{std::get<parser::BlockSpecificationPart>(x.t)};
- auto &execPart{std::get<parser::Block>(x.t)};
- if (auto &&source{parser::GetSource(specPart)}) {
- scopeStack_.push_back(&context_.FindScope(*source));
- } else if (auto &&source{parser::GetSource(execPart)}) {
- scopeStack_.push_back(&context_.FindScope(*source));
- }
+ auto &endBlockStmt{std::get<parser::Statement<parser::EndBlockStmt>>(x.t)};
+ scopeStack_.push_back(&context_.FindScope(endBlockStmt.source));
return true;
}
void OmpStructureChecker::Leave(const parser::BlockConstruct &x) {
- auto &specPart{std::get<parser::BlockSpecificationPart>(x.t)};
- auto &execPart{std::get<parser::Block>(x.t)};
- if (auto &&source{parser::GetSource(specPart)}) {
- scopeStack_.push_back(&context_.FindScope(*source));
- } else if (auto &&source{parser::GetSource(execPart)}) {
- scopeStack_.push_back(&context_.FindScope(*source));
- }
+ scopeStack_.pop_back();
}
void OmpStructureChecker::Enter(const parser::InternalSubprogram &) {
@@ -2353,6 +2353,17 @@ void OmpStructureChecker::Enter(const parser::OmpDeclareTargetDirective &x) {
}
}
+ // The bare form (no arguments, no clauses) is only permitted in the
+ // specification part of a subroutine, function, or interface body
+ // (OpenMP 5.2 §7.8.2).
+ if (x.v.Arguments().v.empty() && x.v.Clauses().v.empty()) {
+ const Scope &scope{GetScopingUnit(*scopeStack_.back())};
+ if (scope.kind() != Scope::Kind::Subprogram) {
+ context_.Say(dirName.source,
+ "DECLARE TARGET directive without arguments or clauses must appear in a subroutine or function"_err_en_US);
+ }
+ }
+
// Check if there are arguments or clauses, but not both.
if (!x.v.Clauses().v.empty()) {
if (!x.v.Arguments().v.empty()) {
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index f9852bbf77d65..cdac6812dc62d 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -82,6 +82,8 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
bool Enter(const parser::EndSubroutineStmt &);
bool Enter(const parser::FunctionStmt &);
bool Enter(const parser::EndFunctionStmt &);
+ bool Enter(const parser::MpSubprogramStmt &);
+ bool Enter(const parser::EndMpSubprogramStmt &);
bool Enter(const parser::BlockConstruct &);
void Leave(const parser::BlockConstruct &);
void Enter(const parser::InternalSubprogram &);
diff --git a/flang/test/Semantics/OpenMP/declare-target09.f90 b/flang/test/Semantics/OpenMP/declare-target09.f90
new file mode 100644
index 0000000000000..dda3bf02c00a4
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/declare-target09.f90
@@ -0,0 +1,101 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp
+
+! OpenMP 5.2 §7.8.2: The bare form of DECLARE TARGET (without arguments or
+! clauses) is only permitted in the specification part of a subroutine,
+! function, or interface body.
+
+block data foo
+ implicit none
+ integer :: x
+ common /blk/ x
+ !ERROR: DECLARE TARGET directive without arguments or clauses must appear in a subroutine or function
+ !$omp declare target
+ data x /42/
+end block data
+
+program main
+ implicit none
+ integer :: a
+ !ERROR: DECLARE TARGET directive without arguments or clauses must appear in a subroutine or function
+ !$omp declare target
+ a = 1
+end program
+
+! Valid: bare form in a subroutine
+subroutine sub1()
+ implicit none
+ integer :: b
+ !$omp declare target
+ b = 2
+end subroutine
+
+! Valid: bare form in a function
+integer function func1()
+ !$omp declare target
+ func1 = 3
+end function
+
+module mod1
+ implicit none
+ integer :: c
+ !ERROR: DECLARE TARGET directive without arguments or clauses must appear in a subroutine or function
+ !$omp declare target
+end module
+
+module parent_mod
+ implicit none
+ integer :: d
+end module
+
+submodule (parent_mod) child_sub
+ integer :: e
+ !ERROR: DECLARE TARGET directive without arguments or clauses must appear in a subroutine or function
+ !$omp declare target
+end submodule
+
+! Valid: bare form inside a separate module subprogram (MODULE PROCEDURE)
+module mod_with_proc
+ interface
+ module subroutine mod_sub()
+ end subroutine
+ end interface
+end module
+
+submodule (mod_with_proc) mod_with_proc_impl
+contains
+ module procedure mod_sub
+ !$omp declare target
+ end procedure
+end submodule
+
+! Valid: bare form inside an interface body in a module
+module mod_with_interface
+ interface
+ subroutine interface_sub()
+ !$omp declare target
+ end subroutine
+ end interface
+end module
+
+! Invalid: bare form inside a BLOCK construct (even within a subroutine)
+subroutine sub_with_block()
+ implicit none
+ integer :: outer
+ !$omp declare target
+ block
+ integer :: inner
+ !ERROR: DECLARE TARGET directive without arguments or clauses must appear in a subroutine or function
+ !$omp declare target
+ end block
+ outer = 1
+end subroutine
+
+! Valid: empty BLOCK in subroutine does not cause false positive
+subroutine sub_empty_block()
+ implicit none
+ integer :: x
+ !$omp declare target
+ block
+ end block
+ x = 1
+end subroutine
More information about the flang-commits
mailing list