[flang] [llvm] [flang][OpenMP] Semantic checks for TASKGRAPH (PR #160115)

Krzysztof Parzyszek via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 22 07:09:39 PDT 2025


https://github.com/kparzysz created https://github.com/llvm/llvm-project/pull/160115

This verifies the "structural" restrictions on constructs encountered in a TASKGRAPH construct.

There are also restrictions that apply to list items, specifically in the following contexts:
- a list item on a clause on a replayable construct,
- data-sharing attributes for a variable on a replayable construct. These restrictions are not verified, because that would require knowing which clauses (on a potential compound directive) apply to the task- generating construct of interest. This information is not available during semantic checks.

>From 4062bb7972bd574242ffcffabca98b485895857b Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 10 Sep 2025 14:50:01 -0500
Subject: [PATCH] [flang][OpenMP] Semantic checks for TASKGRAPH

This verifies the "structural" restrictions on constructs encountered
in a TASKGRAPH construct.

There are also restrictions that apply to list items, specifically in
the following contexts:
- a list item on a clause on a replayable construct,
- data-sharing attributes for a variable on a replayable construct.
These restrictions are not verified, because that would require knowing
which clauses (on a potential compound directive) apply to the task-
generating construct of interest. This information is not available
during semantic checks.
---
 flang/include/flang/Parser/parse-tree.h      |   4 +-
 flang/include/flang/Semantics/openmp-utils.h |   2 +
 flang/lib/Lower/OpenMP/Clauses.cpp           |   4 +-
 flang/lib/Parser/openmp-parsers.cpp          |   4 +-
 flang/lib/Semantics/check-omp-structure.cpp  | 201 ++++++++++++++++++-
 flang/lib/Semantics/check-omp-structure.h    |   1 +
 flang/lib/Semantics/openmp-utils.cpp         |  41 ++++
 flang/test/Parser/OpenMP/taskgraph.f90       |   4 +-
 flang/test/Semantics/OpenMP/graph-id.f90     |  13 ++
 flang/test/Semantics/OpenMP/graph-reset.f90  |  15 ++
 flang/test/Semantics/OpenMP/taskgraph.f90    | 117 +++++++++++
 llvm/include/llvm/Frontend/OpenMP/ClauseT.h  |  19 +-
 12 files changed, 401 insertions(+), 24 deletions(-)
 create mode 100644 flang/test/Semantics/OpenMP/graph-id.f90
 create mode 100644 flang/test/Semantics/OpenMP/graph-reset.f90
 create mode 100644 flang/test/Semantics/OpenMP/taskgraph.f90

diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index bd0debe297916..78504276726fd 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -4435,7 +4435,7 @@ struct OmpGrainsizeClause {
 // graph_id-clause ->
 //    GRAPH_ID(graph-id-value)                      // since 6.0
 struct OmpGraphIdClause {
-  WRAPPER_CLASS_BOILERPLATE(OmpGraphIdClause, common::Indirection<Expr>);
+  WRAPPER_CLASS_BOILERPLATE(OmpGraphIdClause, ScalarIntExpr);
 };
 
 // Ref: [6.0:438-439]
@@ -4443,7 +4443,7 @@ struct OmpGraphIdClause {
 // graph_reset-clause ->
 //    GRAPH_RESET[(graph-reset-expression)]         // since 6.0
 struct OmpGraphResetClause {
-  WRAPPER_CLASS_BOILERPLATE(OmpGraphResetClause, common::Indirection<Expr>);
+  WRAPPER_CLASS_BOILERPLATE(OmpGraphResetClause, ScalarLogicalExpr);
 };
 
 // Ref: [5.0:234-242], [5.1:266-275], [5.2:299], [6.0:472-473]
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index 68318d6093a1e..e7b0ab11825ec 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -74,6 +74,8 @@ std::optional<SomeExpr> GetEvaluateExpr(const parser::Expr &parserExpr);
 std::optional<evaluate::DynamicType> GetDynamicType(
     const parser::Expr &parserExpr);
 
+std::optional<bool> GetLogicalValue(const SomeExpr &expr);
+
 std::optional<bool> IsContiguous(
     SemanticsContext &semaCtx, const parser::OmpObject &object);
 
diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp
index 42b62413f4a26..48b90ccea2f2a 100644
--- a/flang/lib/Lower/OpenMP/Clauses.cpp
+++ b/flang/lib/Lower/OpenMP/Clauses.cpp
@@ -221,8 +221,6 @@ MAKE_EMPTY_CLASS(Capture, Capture);
 MAKE_EMPTY_CLASS(Compare, Compare);
 MAKE_EMPTY_CLASS(DynamicAllocators, DynamicAllocators);
 MAKE_EMPTY_CLASS(Full, Full);
-MAKE_EMPTY_CLASS(GraphId, GraphId);
-MAKE_EMPTY_CLASS(GraphReset, GraphReset);
 MAKE_EMPTY_CLASS(Inbranch, Inbranch);
 MAKE_EMPTY_CLASS(Mergeable, Mergeable);
 MAKE_EMPTY_CLASS(Nogroup, Nogroup);
@@ -258,6 +256,8 @@ MAKE_EMPTY_CLASS(Groupprivate, Groupprivate);
 
 MAKE_INCOMPLETE_CLASS(AdjustArgs, AdjustArgs);
 MAKE_INCOMPLETE_CLASS(AppendArgs, AppendArgs);
+MAKE_INCOMPLETE_CLASS(GraphId, GraphId);
+MAKE_INCOMPLETE_CLASS(GraphReset, GraphReset);
 MAKE_INCOMPLETE_CLASS(Replayable, Replayable);
 MAKE_INCOMPLETE_CLASS(Transparent, Transparent);
 
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 8ab9905123135..f87bc1757d7f7 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -824,9 +824,9 @@ TYPE_PARSER(construct<OmpFailClause>(
     "RELEASE" >> pure(common::OmpMemoryOrderType::Release) ||
     "SEQ_CST" >> pure(common::OmpMemoryOrderType::Seq_Cst)))
 
-TYPE_PARSER(construct<OmpGraphIdClause>(expr))
+TYPE_PARSER(construct<OmpGraphIdClause>(scalarIntExpr))
 
-TYPE_PARSER(construct<OmpGraphResetClause>(expr))
+TYPE_PARSER(construct<OmpGraphResetClause>(scalarLogicalExpr))
 
 // 2.5 PROC_BIND (MASTER | CLOSE | PRIMARY | SPREAD)
 TYPE_PARSER(construct<OmpProcBindClause>(
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index c39daef6b0ea9..73aea2f714eb3 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -15,6 +15,7 @@
 #include "flang/Common/idioms.h"
 #include "flang/Common/indirection.h"
 #include "flang/Common/visit.h"
+#include "flang/Evaluate/fold.h"
 #include "flang/Evaluate/tools.h"
 #include "flang/Evaluate/type.h"
 #include "flang/Parser/char-block.h"
@@ -990,21 +991,21 @@ void OmpStructureChecker::Enter(const parser::OmpBlockConstruct &x) {
       EnterDirectiveNest(TargetBlockOnlyTeams);
     }
     break;
-  case llvm::omp::OMPD_workshare:
-  case llvm::omp::OMPD_parallel_workshare:
+  case llvm::omp::Directive::OMPD_workshare:
+  case llvm::omp::Directive::OMPD_parallel_workshare:
     CheckWorkshareBlockStmts(block, beginSpec.source);
     HasInvalidWorksharingNesting(
         beginSpec.source, llvm::omp::nestedWorkshareErrSet);
     break;
-  case llvm::omp::OMPD_workdistribute:
+  case llvm::omp::Directive::OMPD_workdistribute:
     if (!CurrentDirectiveIsNested()) {
       context_.Say(beginSpec.source,
           "A WORKDISTRIBUTE region must be nested inside TEAMS region only."_err_en_US);
     }
     CheckWorkdistributeBlockStmts(block, beginSpec.source);
     break;
-  case llvm::omp::OMPD_teams_workdistribute:
-  case llvm::omp::OMPD_target_teams_workdistribute:
+  case llvm::omp::Directive::OMPD_teams_workdistribute:
+  case llvm::omp::Directive::OMPD_target_teams_workdistribute:
     CheckWorkdistributeBlockStmts(block, beginSpec.source);
     break;
   case llvm::omp::Directive::OMPD_scope:
@@ -1057,7 +1058,10 @@ void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAssumes &) {
   dirContext_.pop_back();
 }
 
-void OmpStructureChecker::Leave(const parser::OmpBlockConstruct &) {
+void OmpStructureChecker::Leave(const parser::OmpBlockConstruct &x) {
+  if (GetContext().directive == llvm::omp::Directive::OMPD_taskgraph) {
+    CheckTaskgraph(x);
+  }
   if (GetDirectiveNest(TargetBlockOnlyTeams)) {
     ExitDirectiveNest(TargetBlockOnlyTeams);
   }
@@ -2017,6 +2021,191 @@ void OmpStructureChecker::CheckTargetUpdate() {
   }
 }
 
+namespace {
+struct TaskgraphVisitor {
+  TaskgraphVisitor(SemanticsContext &context) : context_(context) {}
+
+  template <typename T> bool Pre(const T &) { return true; }
+  template <typename T> void Post(const T &) {}
+
+  bool Pre(const parser::OpenMPConstruct &x) {
+    parser::OmpDirectiveName name{GetOmpDirectiveName(x)};
+    llvm::ArrayRef<llvm::omp::Directive> leafs{getLeafConstructsOrSelf(name.v)};
+
+    if (!IsTaskGenerating(leafs)) {
+      context_.Say(name.source,
+          "Only task-generating constructs are allowed inside TASKGRAPH region"_err_en_US);
+      // Only visit top-level constructs.
+      return false;
+    }
+
+    const parser::OmpDirectiveSpecification &dirSpec{GetDirSpec(x)};
+
+    // Most restrictions apply to replayable constructs. All constructs are
+    // replayable unless REPLAYABLE(false) is present.
+    bool isReplayable{IsReplayable(dirSpec)};
+    const parser::OmpClause *nogroup{nullptr};
+
+    for (const parser::OmpClause &clause : dirSpec.Clauses().v) {
+      switch (clause.Id()) {
+      case llvm::omp::Clause::OMPC_transparent:
+        if (isReplayable) {
+          CheckTransparent(clause);
+        }
+        break;
+      case llvm::omp::Clause::OMPC_detach:
+        if (isReplayable) {
+          context_.Say(clause.source,
+              "Detachable replayable tasks are not allowed in a TASKGRAPH region"_err_en_US);
+        }
+        break;
+      case llvm::omp::Clause::OMPC_if:
+        if (isReplayable) {
+          CheckIf(clause, leafs);
+        }
+        break;
+      case llvm::omp::Clause::OMPC_nogroup:
+        nogroup = &clause;
+        break;
+      default:
+        break;
+      }
+    }
+
+    unsigned version{context_.langOptions().OpenMPVersion};
+    bool allowsNogroup{llvm::omp::isAllowedClauseForDirective(
+        leafs[0], llvm::omp::Clause::OMPC_nogroup, version)};
+
+    if (allowsNogroup) {
+      if (!nogroup) {
+        context_.Say(dirSpec.source,
+            "The NOGROUP clause must be specified on every construct in a TASKGRAPH region that could be enclosed in an implicit TASKGROUP"_err_en_US);
+      }
+    }
+
+    // Only visit top-level constructs.
+    return false;
+  }
+
+private:
+  const parser::OmpDirectiveSpecification &GetDirSpec(
+      const parser::OpenMPConstruct &x) const {
+    return common::visit(
+        common::visitors{
+            [&](const parser::OmpBlockConstruct &y) -> decltype(auto) {
+              return y.BeginDir();
+            },
+            [&](const parser::OpenMPLoopConstruct &y) -> decltype(auto) {
+              return y.BeginDir();
+            },
+            [&](const parser::OpenMPStandaloneConstruct &y)
+                -> const parser::OmpDirectiveSpecification & {
+              return std::get<parser::OpenMPSimpleStandaloneConstruct>(y.u).v;
+            },
+            [&](const auto &) -> const parser::OmpDirectiveSpecification & {
+              llvm_unreachable("Invalid construct");
+            },
+        },
+        x.u);
+  }
+
+  bool IsTaskGenerating(llvm::ArrayRef<llvm::omp::Directive> leafs) const {
+    const static llvm::omp::Directive taskGen[] = {
+        llvm::omp::Directive::OMPD_target,
+        llvm::omp::Directive::OMPD_target_data,
+        llvm::omp::Directive::OMPD_target_enter_data,
+        llvm::omp::Directive::OMPD_target_exit_data,
+        llvm::omp::Directive::OMPD_target_update,
+        llvm::omp::Directive::OMPD_task,
+        llvm::omp::Directive::OMPD_taskloop,
+    };
+    return llvm::all_of(leafs,
+        [](llvm::omp::Directive d) { return llvm::is_contained(taskGen, d); });
+  }
+
+  bool IsReplayable(const parser::OmpDirectiveSpecification &dirSpec) const {
+    for (const parser::OmpClause &clause : dirSpec.Clauses().v) {
+      if (clause.Id() != llvm::omp::Clause::OMPC_replayable) {
+        continue;
+      }
+      if (auto &repl{std::get<parser::OmpClause::Replayable>(clause.u).v}) {
+        // Scalar<Logical<Constant<indirection<Expr>>>>
+        const parser::Expr &parserExpr{repl->v.thing.thing.thing.value()};
+        if (auto &&expr{GetEvaluateExpr(parserExpr)}) {
+          return GetLogicalValue(*expr).value_or(true);
+        }
+      }
+      break;
+    }
+    return true;
+  }
+
+  void CheckTransparent(const parser::OmpClause &clause) const {
+    bool isTransparent{true};
+    if (auto &transp{std::get<parser::OmpClause::Transparent>(clause.u).v}) {
+      // Scalar<Integer<indirection<Expr>>>
+      const parser::Expr &parserExpr{transp->v.thing.thing.value()};
+      if (auto &&expr{GetEvaluateExpr(parserExpr)}) {
+        // If the argument is omp_not_impex (defined as 0), then
+        // the task is not transparent, otherwise it is.
+        const int64_t omp_not_impex{0};
+        if (auto &&val{evaluate::ToInt64(*expr)}) {
+          isTransparent = *val != omp_not_impex;
+        }
+      }
+    }
+    if (isTransparent) {
+      context_.Say(clause.source,
+          "Transparent replayable tasks are not allowed in a TASKGRAPH region"_err_en_US);
+    }
+  }
+
+  void CheckIf(const parser::OmpClause &clause,
+      llvm::ArrayRef<llvm::omp::Directive> leafs) const {
+    // The only constructs that can generate undeferred tasks (via IF clause)
+    // are TASK and TASKLOOP.
+    if (leafs[0] != llvm::omp::Directive::OMPD_task &&
+        leafs[0] != llvm::omp::Directive::OMPD_taskloop) {
+      return;
+    }
+
+    auto &&ifc{std::get<parser::OmpClause::If>(clause.u)};
+    // Check if there is a directive-name-modifier first.
+    auto &modifiers{OmpGetModifiers(ifc.v)};
+    if (auto *dnm{OmpGetUniqueModifier<parser::OmpDirectiveNameModifier>(
+            modifiers)}) {
+      llvm::omp::Directive sub{dnm->v};
+      auto subLeafs{llvm::omp::getLeafConstructsOrSelf(sub)};
+      // Only interested in the outermost constructs. The body of the created
+      // task is not a part of the TASKGRAPH region.
+      if (subLeafs[0] != leafs[0]) {
+        return;
+      }
+    }
+    // Scalar<Logical<indirection<Expr>>>
+    auto &parserExpr{
+        std::get<parser::ScalarLogicalExpr>(ifc.v.t).thing.thing.value()};
+    if (auto &&expr{GetEvaluateExpr(parserExpr)}) {
+      // If the value is known to be false, an undeferred task will be
+      // generated.
+      if (!GetLogicalValue(*expr).value_or(true)) {
+        context_.Say(clause.source,
+            "Undeferred replayable tasks are not allowed in a TASKGRAPH region"_err_en_US);
+      }
+    }
+  }
+
+  SemanticsContext &context_;
+};
+} // namespace
+
+void OmpStructureChecker::CheckTaskgraph(const parser::OmpBlockConstruct &x) {
+  const parser::Block &block{std::get<parser::Block>(x.t)};
+
+  TaskgraphVisitor visitor{context_};
+  parser::Walk(block, visitor);
+}
+
 void OmpStructureChecker::CheckTaskDependenceType(
     const parser::OmpTaskDependenceType::Value &x) {
   // Common checks for task-dependence-type (DEPEND and UPDATE clauses).
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index ce074f5f3f86e..a22c80f0a3c3f 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -299,6 +299,7 @@ class OmpStructureChecker
   void CheckSIMDNest(const parser::OpenMPConstruct &x);
   void CheckTargetNest(const parser::OpenMPConstruct &x);
   void CheckTargetUpdate();
+  void CheckTaskgraph(const parser::OmpBlockConstruct &x);
   void CheckDependenceType(const parser::OmpDependenceType::Value &x);
   void CheckTaskDependenceType(const parser::OmpTaskDependenceType::Value &x);
   std::optional<llvm::omp::Directive> GetCancelType(
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 2980f827d3ef3..ef04f0337ba12 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -12,6 +12,7 @@
 
 #include "flang/Semantics/openmp-utils.h"
 
+#include "flang/Common/Fortran-consts.h"
 #include "flang/Common/indirection.h"
 #include "flang/Common/reference.h"
 #include "flang/Common/visit.h"
@@ -187,6 +188,46 @@ std::optional<evaluate::DynamicType> GetDynamicType(
   }
 }
 
+namespace {
+struct LogicalConstantVistor : public evaluate::Traverse<LogicalConstantVistor,
+                                   std::optional<bool>, false> {
+  using Result = std::optional<bool>;
+  using Base = evaluate::Traverse<LogicalConstantVistor, Result, false>;
+  LogicalConstantVistor() : Base(*this) {}
+
+  Result Default() const { return std::nullopt; }
+
+  using Base::operator();
+
+  template <typename T> //
+  Result operator()(const evaluate::Constant<T> &x) const {
+    if constexpr (T::category == common::TypeCategory::Logical) {
+      return llvm::transformOptional(
+          x.GetScalarValue(), [](auto &&v) { return v.IsTrue(); });
+    } else {
+      return std::nullopt;
+    }
+  }
+
+  template <typename... Rs> //
+  Result Combine(Result &&result, Rs &&...results) const {
+    if constexpr (sizeof...(results) == 0) {
+      return result;
+    } else {
+      if (result.has_value()) {
+        return result;
+      } else {
+        return Combine(std::move(results)...);
+      }
+    }
+  }
+};
+} // namespace
+
+std::optional<bool> GetLogicalValue(const SomeExpr &expr) {
+  return LogicalConstantVistor{}(expr);
+}
+
 namespace {
 struct ContiguousHelper {
   ContiguousHelper(SemanticsContext &context)
diff --git a/flang/test/Parser/OpenMP/taskgraph.f90 b/flang/test/Parser/OpenMP/taskgraph.f90
index 7fcbae4227508..fa9994f41345e 100644
--- a/flang/test/Parser/OpenMP/taskgraph.f90
+++ b/flang/test/Parser/OpenMP/taskgraph.f90
@@ -50,9 +50,9 @@ subroutine f01(x, y)
 !PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OmpBlockConstruct
 !PARSE-TREE: | OmpBeginDirective
 !PARSE-TREE: | | OmpDirectiveName -> llvm::omp::Directive = taskgraph
-!PARSE-TREE: | | OmpClauseList -> OmpClause -> GraphId -> OmpGraphIdClause -> Expr = 'x'
+!PARSE-TREE: | | OmpClauseList -> OmpClause -> GraphId -> OmpGraphIdClause -> Scalar -> Integer -> Expr = 'x'
 !PARSE-TREE: | | | Designator -> DataRef -> Name = 'x'
-!PARSE-TREE: | | OmpClause -> GraphReset -> OmpGraphResetClause -> Expr = 'y'
+!PARSE-TREE: | | OmpClause -> GraphReset -> OmpGraphResetClause -> Scalar -> Logical -> Expr = 'y'
 !PARSE-TREE: | | | Designator -> DataRef -> Name = 'y'
 !PARSE-TREE: | | Flags = None
 !PARSE-TREE: | Block
diff --git a/flang/test/Semantics/OpenMP/graph-id.f90 b/flang/test/Semantics/OpenMP/graph-id.f90
new file mode 100644
index 0000000000000..64ce447ce587f
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/graph-id.f90
@@ -0,0 +1,13 @@
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+
+subroutine f00
+  !ERROR: Must have INTEGER type, but is CHARACTER(KIND=1,LEN=8_8)
+  !$omp taskgraph graph_id("my graph")
+  !$omp end taskgraph
+end
+
+subroutine f01
+  !ERROR: At most one GRAPH_ID clause can appear on the TASKGRAPH directive
+  !$omp taskgraph graph_id(1) graph_id(2)
+  !$omp end taskgraph
+end
diff --git a/flang/test/Semantics/OpenMP/graph-reset.f90 b/flang/test/Semantics/OpenMP/graph-reset.f90
new file mode 100644
index 0000000000000..4ff9b3d11902b
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/graph-reset.f90
@@ -0,0 +1,15 @@
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+
+subroutine f00(x)
+  integer :: x(*)
+  !ERROR: Whole assumed-size array 'x' may not appear here without subscripts
+  !ERROR: Must have LOGICAL type, but is INTEGER(4)
+  !$omp taskgraph graph_reset(x)
+  !$omp end taskgraph
+end
+
+subroutine f01
+  !ERROR: At most one GRAPH_RESET clause can appear on the TASKGRAPH directive
+  !$omp taskgraph graph_reset(.true.) graph_reset(.false.)
+  !$omp end taskgraph
+end
diff --git a/flang/test/Semantics/OpenMP/taskgraph.f90 b/flang/test/Semantics/OpenMP/taskgraph.f90
new file mode 100644
index 0000000000000..e45ef46c3bef2
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/taskgraph.f90
@@ -0,0 +1,117 @@
+!RUN: %python %S/../test_errors.py %s %flang %openmp_flags -fopenmp -fopenmp-version=60
+
+module m
+use omp_lib
+
+implicit none
+! Not in omp_lib yet.
+integer, parameter :: omp_not_impex = 0
+integer, parameter :: omp_import = 1
+integer, parameter :: omp_export = 2
+integer, parameter :: omp_impex = 3
+
+contains
+
+subroutine f00
+  !$omp taskgraph
+  !ERROR: Only task-generating constructs are allowed inside TASKGRAPH region
+  !$omp parallel
+  !$omp end parallel
+  !$omp end taskgraph
+end
+
+subroutine f01
+  !$omp taskgraph
+  !$omp task
+  !Non-task-generating constructs are ok if contained in an encountered task.
+  !No diagnostic expected.
+  !$omp parallel
+  !$omp end parallel
+  !$omp end task
+  !$omp end taskgraph
+end
+
+subroutine f02
+  !$omp taskgraph
+  !ERROR: Transparent replayable tasks are not allowed in a TASKGRAPH region
+  !$omp task transparent
+  !$omp end task
+  !$omp end taskgraph
+
+  !$omp taskgraph
+  !Not a transparent task.
+  !No diagnostic expected.
+  !$omp task transparent(omp_not_impex)
+  !$omp end task
+  !$omp end taskgraph
+
+  !$omp taskgraph
+  !Ok: transparent, but not replayable task.
+  !No diagnostic expected.
+  !$omp task replayable(.false.) transparent
+  !$omp end task
+  !$omp end taskgraph
+end
+
+subroutine f03
+  integer(kind=omp_event_handle_kind) :: event
+
+  !$omp taskgraph
+  !ERROR: Detachable replayable tasks are not allowed in a TASKGRAPH region
+  !$omp task detach(event)
+  !$omp end task
+  !$omp end taskgraph
+
+  !$omp taskgraph
+  !Ok: task is detachable, but not replayable.
+  !No diagnostic expected
+  !$omp task detach(event) replayable(.false.)
+  !$omp end task
+  !$omp end taskgraph
+end
+
+subroutine f04
+  !$omp taskgraph
+  !ERROR: Undeferred replayable tasks are not allowed in a TASKGRAPH region
+  !$omp task if(.false.)
+  !$omp end task
+  !$omp end taskgraph
+
+  !$omp taskgraph
+  !Ok: task is undeferred, but not replayable.
+  !No diagnostic expected.
+  !$omp task if(.false.) replayable(.false.)
+  !$omp end task
+  !$omp end taskgraph
+end
+
+subroutine f05
+  integer :: i
+
+  !$omp taskgraph
+  !ERROR: The NOGROUP clause must be specified on every construct in a TASKGRAPH region that could be enclosed in an implicit TASKGROUP
+  !$omp taskloop
+  do i = 1, 10
+  enddo
+  !$omp end taskloop
+  !$omp end taskgraph
+
+  !$omp taskgraph
+  !This also applies to non-replayable constructs
+  !ERROR: The NOGROUP clause must be specified on every construct in a TASKGRAPH region that could be enclosed in an implicit TASKGROUP
+  !$omp taskloop replayable(.false.)
+  do i = 1, 10
+  enddo
+  !$omp end taskloop
+  !$omp end taskgraph
+
+  !$omp taskgraph
+  !No diagnostic expected.
+  !$omp taskloop replayable(.false.) nogroup
+  do i = 1, 10
+  enddo
+  !$omp end taskloop
+  !$omp end taskgraph
+end
+
+end module
diff --git a/llvm/include/llvm/Frontend/OpenMP/ClauseT.h b/llvm/include/llvm/Frontend/OpenMP/ClauseT.h
index 1ed23eed1571d..1ade9ce0c3a7d 100644
--- a/llvm/include/llvm/Frontend/OpenMP/ClauseT.h
+++ b/llvm/include/llvm/Frontend/OpenMP/ClauseT.h
@@ -663,13 +663,13 @@ struct GrainsizeT {
 // [6.0:438] `graph_id` clause
 template <typename T, typename I, typename E> //
 struct GraphIdT {
-  using EmptyTrait = std::true_type;
+  using IncompleteTrait = std::true_type;
 };
 
 // [6.0:438] `graph_reset` clause
 template <typename T, typename I, typename E> //
 struct GraphResetT {
-  using EmptyTrait = std::true_type;
+  using IncompleteTrait = std::true_type;
 };
 
 // V5.2: [5.4.9] `has_device_addr` clause
@@ -1278,11 +1278,10 @@ using ExtensionClausesT =
 template <typename T, typename I, typename E>
 using EmptyClausesT = std::variant<
     AcqRelT<T, I, E>, AcquireT<T, I, E>, CaptureT<T, I, E>, CompareT<T, I, E>,
-    DynamicAllocatorsT<T, I, E>, FullT<T, I, E>, GraphIdT<T, I, E>,
-    GraphResetT<T, I, E>, InbranchT<T, I, E>, MergeableT<T, I, E>,
-    NogroupT<T, I, E>, NoOpenmpRoutinesT<T, I, E>, NoOpenmpT<T, I, E>,
-    NoParallelismT<T, I, E>, NotinbranchT<T, I, E>, NowaitT<T, I, E>,
-    ReadT<T, I, E>, RelaxedT<T, I, E>, ReleaseT<T, I, E>,
+    DynamicAllocatorsT<T, I, E>, FullT<T, I, E>, InbranchT<T, I, E>,
+    MergeableT<T, I, E>, NogroupT<T, I, E>, NoOpenmpRoutinesT<T, I, E>,
+    NoOpenmpT<T, I, E>, NoParallelismT<T, I, E>, NotinbranchT<T, I, E>,
+    NowaitT<T, I, E>, ReadT<T, I, E>, RelaxedT<T, I, E>, ReleaseT<T, I, E>,
     ReverseOffloadT<T, I, E>, SeqCstT<T, I, E>, SimdT<T, I, E>,
     ThreadsT<T, I, E>, UnifiedAddressT<T, I, E>, UnifiedSharedMemoryT<T, I, E>,
     UnknownT<T, I, E>, UntiedT<T, I, E>, UseT<T, I, E>, WeakT<T, I, E>,
@@ -1290,9 +1289,9 @@ using EmptyClausesT = std::variant<
 
 template <typename T, typename I, typename E>
 using IncompleteClausesT =
-    std::variant<AdjustArgsT<T, I, E>, AppendArgsT<T, I, E>, MatchT<T, I, E>,
-                 OtherwiseT<T, I, E>, ReplayableT<T, I, E>,
-                 TransparentT<T, I, E>, WhenT<T, I, E>>;
+    std::variant<AdjustArgsT<T, I, E>, AppendArgsT<T, I, E>, GraphIdT<T, I, E>,
+                 GraphResetT<T, I, E>, MatchT<T, I, E>, OtherwiseT<T, I, E>,
+                 ReplayableT<T, I, E>, TransparentT<T, I, E>, WhenT<T, I, E>>;
 
 template <typename T, typename I, typename E>
 using TupleClausesT =



More information about the llvm-commits mailing list