[flang-commits] [flang] [flang][OpenMP] Move modifier verification out of header file (PR #218506)
Krzysztof Parzyszek via flang-commits
flang-commits at lists.llvm.org
Tue Aug 25 06:09:52 PDT 2026
https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/218506
>From b8708878ae4864e59b197b22afb6016776ed00cd Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 13 Aug 2026 14:20:19 -0500
Subject: [PATCH] [flang][OpenMP] Move modifier verification out of header file
Since modifiers now have enum ids, verifying syntactical properties
of them no longer has to be type-based. Move the modifier verification
code out of a header file with some changes to use the numeric ids.
Some form of a connection between AST nodes for modifiers and the enum
ids is still required, now it's a static member of the modifier AST node.
---
flang/include/flang/Parser/openmp-utils.h | 41 ++
flang/include/flang/Parser/parse-tree.h | 59 ++-
.../flang/Semantics/openmp-modifiers.h | 368 +-----------------
flang/include/flang/Semantics/openmp-utils.h | 2 +
flang/lib/Parser/openmp-utils.cpp | 42 ++
flang/lib/Semantics/check-omp-loop.cpp | 4 +-
flang/lib/Semantics/check-omp-structure.cpp | 294 +++++++++++---
flang/lib/Semantics/check-omp-structure.h | 13 +
.../Semantics/OpenMP/clause-validity01.f90 | 1 +
.../Semantics/OpenMP/depobj-construct-v52.f90 | 1 +
.../test/Semantics/OpenMP/linear-clause02.f90 | 1 +
flang/test/Semantics/OpenMP/ordered01.f90 | 3 -
12 files changed, 405 insertions(+), 424 deletions(-)
diff --git a/flang/include/flang/Parser/openmp-utils.h b/flang/include/flang/Parser/openmp-utils.h
index fe2d06018c850..b0fae4e5d2094 100644
--- a/flang/include/flang/Parser/openmp-utils.h
+++ b/flang/include/flang/Parser/openmp-utils.h
@@ -15,8 +15,10 @@
#include "flang/Common/indirection.h"
#include "flang/Parser/parse-tree.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Frontend/OpenMP/OMP.h"
+#include "llvm/Frontend/OpenMP/OMPDescriptors.h"
#include <cassert>
#include <iterator>
@@ -201,6 +203,45 @@ struct OmpAllocateInfo {
OmpAllocateInfo SplitOmpAllocate(const OmpAllocateDirective &x);
+namespace detail {
+template <typename ClauseTy, typename VoidTy = void> struct HasModifierImpl {
+ static constexpr bool value{false};
+};
+template <typename ClauseTy>
+struct HasModifierImpl<ClauseTy, std::void_t<typename ClauseTy::Modifier>> {
+ static constexpr bool value{true};
+};
+} // namespace detail
+template <typename ClauseTy>
+static constexpr bool HasModifier = detail::HasModifierImpl<ClauseTy>::value;
+
+struct AppliedModifier {
+ llvm::omp::Modifier modifierId;
+ parser::CharBlock source;
+};
+
+struct AppliedModifierInfo {
+ llvm::SmallVector<AppliedModifier> modifiers;
+};
+
+template <typename UnionTy>
+AppliedModifierInfo GetAppliedModifiers(
+ const std::optional<std::list<UnionTy>> &modifiers) {
+ AppliedModifierInfo info;
+ if (modifiers) {
+ for (auto &m : *modifiers) {
+ common::visit(
+ [&](auto &&s) {
+ info.modifiers.emplace_back(AppliedModifier{s.Id, m.source});
+ },
+ m.u);
+ }
+ }
+ return info;
+}
+
+AppliedModifierInfo GetAppliedModifiers(const parser::OmpClause &clause);
+
template <typename R, typename = void, typename = void> struct is_range {
static constexpr bool value{false};
};
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index aeefb4d0fdf44..75ce04625a584 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -31,6 +31,7 @@
#include "llvm/Frontend/OpenACC/ACC.h.inc"
#include "llvm/Frontend/OpenMP/OMP.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
+#include "llvm/Frontend/OpenMP/OMPDescriptors.h"
#include <cinttypes>
#include <list>
#include <optional>
@@ -3676,6 +3677,7 @@ struct OmpStylizedExpression {
// + | * | .AND. | .OR. | .EQV. | .NEQV. | // since 4.5
// MIN | MAX | IAND | IOR | IEOR // since 4.5
struct OmpReductionIdentifier {
+ static constexpr auto Id = llvm::omp::Modifier::ReductionIdentifier;
UNION_CLASS_BOILERPLATE(OmpReductionIdentifier);
std::variant<DefinedOperator, ProcedureDesignator> u;
};
@@ -3882,6 +3884,7 @@ struct OmpTraitSetSelector {
// context-selector-specification ->
// trait-set-selector, ...
struct OmpContextSelectorSpecification { // Modifier
+ static constexpr auto Id = llvm::omp::Modifier::ContextSelector;
CharBlock source;
WRAPPER_CLASS_BOILERPLATE(
OmpContextSelectorSpecification, std::list<OmpTraitSetSelector>);
@@ -3906,6 +3909,7 @@ inline namespace modifier {
// };
struct OmpAccessGroup {
+ static constexpr auto Id = llvm::omp::Modifier::AccessGroup;
ENUM_CLASS(Value, Cgroup);
WRAPPER_CLASS_BOILERPLATE(OmpAccessGroup, Value);
};
@@ -3916,6 +3920,7 @@ struct OmpAccessGroup {
// NOTHING | // since 5.1
// NEED_DEVICE_PTR // since 5.1
struct OmpAdjustOp {
+ static constexpr auto Id = llvm::omp::Modifier::AdjustOp;
ENUM_CLASS(Value, Nothing, Need_Device_Ptr)
WRAPPER_CLASS_BOILERPLATE(OmpAdjustOp, Value);
};
@@ -3925,6 +3930,7 @@ struct OmpAdjustOp {
// alignment ->
// scalar-integer-expression // since 4.5
struct OmpAlignment {
+ static constexpr auto Id = llvm::omp::Modifier::Alignment;
WRAPPER_CLASS_BOILERPLATE(OmpAlignment, ScalarIntExpr);
};
@@ -3933,6 +3939,7 @@ struct OmpAlignment {
// align-modifier ->
// ALIGN(alignment) // since 5.1
struct OmpAlignModifier {
+ static constexpr auto Id = llvm::omp::Modifier::AlignModifier;
WRAPPER_CLASS_BOILERPLATE(OmpAlignModifier, ScalarIntExpr);
};
@@ -3941,6 +3948,7 @@ struct OmpAlignModifier {
// allocator-simple-modifier ->
// allocator // since 5.0
struct OmpAllocatorSimpleModifier {
+ static constexpr auto Id = llvm::omp::Modifier::AllocatorSimpleModifier;
WRAPPER_CLASS_BOILERPLATE(OmpAllocatorSimpleModifier, ScalarIntExpr);
};
@@ -3949,6 +3957,7 @@ struct OmpAllocatorSimpleModifier {
// allocator-complex-modifier ->
// ALLOCATOR(allocator) // since 5.1
struct OmpAllocatorComplexModifier {
+ static constexpr auto Id = llvm::omp::Modifier::AllocatorComplexModifier;
WRAPPER_CLASS_BOILERPLATE(OmpAllocatorComplexModifier, ScalarIntExpr);
};
@@ -3961,6 +3970,7 @@ struct OmpAllocatorComplexModifier {
// Until 5.2, it was a part of map-type-modifier. Since 6.0 the
// map-type-modifier has been split into individual modifiers.
struct OmpAlwaysModifier {
+ static constexpr auto Id = llvm::omp::Modifier::AlwaysModifier;
ENUM_CLASS(Value, Always)
WRAPPER_CLASS_BOILERPLATE(OmpAlwaysModifier, Value);
};
@@ -3973,6 +3983,7 @@ struct OmpAlwaysModifier {
// attachment-mode ->
// ALWAYS | AUTO | NEVER
struct OmpAttachModifier {
+ static constexpr auto Id = llvm::omp::Modifier::AttachModifier;
ENUM_CLASS(Value, Always, Never, Auto)
WRAPPER_CLASS_BOILERPLATE(OmpAttachModifier, Value);
};
@@ -3983,6 +3994,7 @@ struct OmpAttachModifier {
// automap // since 6.0
//
struct OmpAutomapModifier {
+ static constexpr auto Id = llvm::omp::Modifier::AutomapModifier;
ENUM_CLASS(Value, Automap);
WRAPPER_CLASS_BOILERPLATE(OmpAutomapModifier, Value);
};
@@ -3994,6 +4006,7 @@ struct OmpAutomapModifier {
//
// Prior to 5.2 "chunk-modifier" was a part of "modifier" on SCHEDULE clause.
struct OmpChunkModifier {
+ static constexpr auto Id = llvm::omp::Modifier::ChunkModifier;
ENUM_CLASS(Value, Simd)
WRAPPER_CLASS_BOILERPLATE(OmpChunkModifier, Value);
};
@@ -4007,6 +4020,7 @@ struct OmpChunkModifier {
// Until 5.2, it was a part of map-type-modifier. Since 6.0 the
// map-type-modifier has been split into individual modifiers.
struct OmpCloseModifier {
+ static constexpr auto Id = llvm::omp::Modifier::CloseModifier;
ENUM_CLASS(Value, Close)
WRAPPER_CLASS_BOILERPLATE(OmpCloseModifier, Value);
};
@@ -4019,6 +4033,7 @@ struct OmpCloseModifier {
//
// Until 5.2, it was a part of map-type.
struct OmpDeleteModifier {
+ static constexpr auto Id = llvm::omp::Modifier::DeleteModifier;
ENUM_CLASS(Value, Delete)
WRAPPER_CLASS_BOILERPLATE(OmpDeleteModifier, Value);
};
@@ -4040,6 +4055,7 @@ struct OmpDeleteModifier {
// vector). This would accept the vector "i, j, k" (although interpreted
// incorrectly), while flagging a syntax error for "i+1, j, k".
struct OmpDependenceType {
+ static constexpr auto Id = llvm::omp::Modifier::DependenceType;
ENUM_CLASS(Value, Sink, Source);
WRAPPER_CLASS_BOILERPLATE(OmpDependenceType, Value);
};
@@ -4051,6 +4067,7 @@ struct OmpDependenceType {
// keyword ->
// IN | INOUT | INOUTSET | MUTEXINOUTSET | OUT // since 6.0
struct OmpDepinfoModifier {
+ static constexpr auto Id = llvm::omp::Modifier::DepinfoModifier;
using Value = common::OmpDependenceKind;
TUPLE_CLASS_BOILERPLATE(OmpDepinfoModifier);
std::tuple<Value, OmpObject> t;
@@ -4061,6 +4078,7 @@ struct OmpDepinfoModifier {
// device-modifier ->
// ANCESTOR | DEVICE_NUM // since 5.0
struct OmpDeviceModifier {
+ static constexpr auto Id = llvm::omp::Modifier::DeviceModifier;
ENUM_CLASS(Value, Ancestor, Device_Num)
WRAPPER_CLASS_BOILERPLATE(OmpDeviceModifier, Value);
};
@@ -4070,6 +4088,7 @@ struct OmpDeviceModifier {
// dims-modifier ->
// constant integer expression // since 6.1
struct OmpDimsModifier {
+ static constexpr auto Id = llvm::omp::Modifier::DimsModifier;
WRAPPER_CLASS_BOILERPLATE(OmpDimsModifier, ScalarIntConstantExpr);
};
@@ -4087,21 +4106,19 @@ struct OmpDimsModifier {
// the directive-name-modifier. For the sake of uniformity CANCEL can be
// considered a valid value in 4.5 as well.
struct OmpDirectiveNameModifier : public OmpDirectiveName {
+ static constexpr auto Id = llvm::omp::Modifier::DirectiveNameModifier;
INHERITED_WRAPPER_CLASS_BOILERPLATE(
OmpDirectiveNameModifier, OmpDirectiveName);
};
-// Ref: [5.1:205-209], [5.2:166-168]
+// Ref: [5.2:166-168]
//
-// motion-modifier ->
-// PRESENT | // since 5.0, until 5.0
-// mapper | iterator
// expectation ->
-// PRESENT // since 5.1
-//
+// PRESENT // since 5.2, until 5.2
// The PRESENT value was a part of motion-modifier in 5.1, and became a
// value of expectation in 5.2.
struct OmpExpectation {
+ static constexpr auto Id = llvm::omp::Modifier::Expectation;
ENUM_CLASS(Value, Present);
WRAPPER_CLASS_BOILERPLATE(OmpExpectation, Value);
};
@@ -4113,6 +4130,7 @@ struct OmpExpectation {
// fallback-mode ->
// ABORT | DEFAULT_MEM | NULL // since 6.1
struct OmpFallbackModifier {
+ static constexpr auto Id = llvm::omp::Modifier::FallbackModifier;
ENUM_CLASS(Value, Abort, Default_Mem, Null);
WRAPPER_CLASS_BOILERPLATE(OmpFallbackModifier, Value);
};
@@ -4124,6 +4142,7 @@ struct OmpFallbackModifier {
// TARGETSYNC
// There can be at most only two interop-type.
struct OmpInteropType {
+ static constexpr auto Id = llvm::omp::Modifier::InteropType;
ENUM_CLASS(Value, Target, Targetsync)
WRAPPER_CLASS_BOILERPLATE(OmpInteropType, Value);
};
@@ -4146,6 +4165,7 @@ struct OmpIteratorSpecifier {
// iterator-modifier ->
// ITERATOR(iterator-specifier [, ...]) // since 5.0
struct OmpIterator {
+ static constexpr auto Id = llvm::omp::Modifier::Iterator;
WRAPPER_CLASS_BOILERPLATE(OmpIterator, std::list<OmpIteratorSpecifier>);
};
@@ -4154,6 +4174,7 @@ struct OmpIterator {
// lastprivate-modifier ->
// CONDITIONAL // since 5.0
struct OmpLastprivateModifier {
+ static constexpr auto Id = llvm::omp::Modifier::LastprivateModifier;
ENUM_CLASS(Value, Conditional)
WRAPPER_CLASS_BOILERPLATE(OmpLastprivateModifier, Value);
};
@@ -4163,6 +4184,7 @@ struct OmpLastprivateModifier {
// linear-modifier ->
// REF | UVAL | VAL // since 4.5
struct OmpLinearModifier {
+ static constexpr auto Id = llvm::omp::Modifier::LinearModifier;
ENUM_CLASS(Value, Ref, Uval, Val);
WRAPPER_CLASS_BOILERPLATE(OmpLinearModifier, Value);
};
@@ -4172,6 +4194,7 @@ struct OmpLinearModifier {
// linear-stepr ->
// integer-expresion // since 4.5, until 5.1
struct OmpLinearStep {
+ static constexpr auto Id = llvm::omp::Modifier::LinearStep;
WRAPPER_CLASS_BOILERPLATE(OmpLinearStep, ScalarIntExpr);
};
@@ -4186,6 +4209,7 @@ struct OmpLinearStep {
// UNROLLED
// [( ScalarIntConstantExpr-list )]
struct OmpLoopModifier {
+ static constexpr auto Id = llvm::omp::Modifier::LoopModifier;
TUPLE_CLASS_BOILERPLATE(OmpLoopModifier);
std::tuple<llvm::omp::LoopModifier,
std::optional<std::list<ScalarIntConstantExpr>>>
@@ -4198,6 +4222,7 @@ struct OmpLoopModifier {
// lower-bound ->
// scalar-integer-expression // since 5.1
struct OmpLowerBound {
+ static constexpr auto Id = llvm::omp::Modifier::LowerBound;
WRAPPER_CLASS_BOILERPLATE(OmpLowerBound, ScalarIntExpr);
};
@@ -4206,6 +4231,7 @@ struct OmpLowerBound {
// mapper ->
// identifier // since 4.5
struct OmpMapper {
+ static constexpr auto Id = llvm::omp::Modifier::Mapper;
WRAPPER_CLASS_BOILERPLATE(OmpMapper, Name);
};
@@ -4219,6 +4245,7 @@ struct OmpMapper {
//
// Since 6.0 DELETE is a separate delete-modifier.
struct OmpMapType {
+ static constexpr auto Id = llvm::omp::Modifier::MapType;
ENUM_CLASS(Value, Alloc, Delete, From, Release, Storage, To, Tofrom);
WRAPPER_CLASS_BOILERPLATE(OmpMapType, Value);
};
@@ -4231,6 +4258,7 @@ struct OmpMapType {
// PRESENT // since 5.1, until 5.2
// Since 6.0 the map-type-modifier has been split into individual modifiers.
struct OmpMapTypeModifier {
+ static constexpr auto Id = llvm::omp::Modifier::MapTypeModifier;
ENUM_CLASS(Value, Always, Close, Present)
WRAPPER_CLASS_BOILERPLATE(OmpMapTypeModifier, Value);
};
@@ -4240,6 +4268,7 @@ struct OmpMapTypeModifier {
// mem-space ->
// MEMSPACE(memspace-handle) // since 5.2
struct OmpMemSpace {
+ static constexpr auto Id = llvm::omp::Modifier::MemSpace;
WRAPPER_CLASS_BOILERPLATE(OmpMemSpace, ScalarIntExpr);
};
@@ -4252,6 +4281,7 @@ struct OmpMemSpace {
// so it should be a modifier group rather than a modifier. Both iterator
// and mapper are separate modifiers.
struct OmpMotionModifier {
+ static constexpr auto Id = llvm::omp::Modifier::MotionModifier;
ENUM_CLASS(Value, Present)
WRAPPER_CLASS_BOILERPLATE(OmpMotionModifier, Value);
};
@@ -4267,6 +4297,7 @@ struct OmpMotionModifier {
// Since 5.2 "modifier" was replaced with "ordering-modifier" and "chunk-
// modifier".
struct OmpOrderingModifier {
+ static constexpr auto Id = llvm::omp::Modifier::OrderingModifier;
ENUM_CLASS(Value, Monotonic, Nonmonotonic, Simd)
WRAPPER_CLASS_BOILERPLATE(OmpOrderingModifier, Value);
};
@@ -4276,6 +4307,7 @@ struct OmpOrderingModifier {
// order-modifier ->
// REPRODUCIBLE | UNCONSTRAINED // since 5.1
struct OmpOrderModifier {
+ static constexpr auto Id = llvm::omp::Modifier::OrderModifier;
ENUM_CLASS(Value, Reproducible, Unconstrained)
WRAPPER_CLASS_BOILERPLATE(OmpOrderModifier, Value);
};
@@ -4310,6 +4342,7 @@ struct OmpPreferenceSpecification {
// prefer-type -> // since 5.1
// PREFER_TYPE(preference-specification...)
struct OmpPreferType {
+ static constexpr auto Id = llvm::omp::Modifier::PreferType;
WRAPPER_CLASS_BOILERPLATE(
OmpPreferType, std::list<OmpPreferenceSpecification>);
};
@@ -4319,6 +4352,7 @@ struct OmpPreferType {
// prescriptiveness ->
// STRICT // since 5.1
struct OmpPrescriptiveness {
+ static constexpr auto Id = llvm::omp::Modifier::Prescriptiveness;
ENUM_CLASS(Value, Strict)
WRAPPER_CLASS_BOILERPLATE(OmpPrescriptiveness, Value);
};
@@ -4328,6 +4362,7 @@ struct OmpPrescriptiveness {
// present-modifier ->
// PRESENT // since 6.0
struct OmpPresentModifier {
+ static constexpr auto Id = llvm::omp::Modifier::PresentModifier;
ENUM_CLASS(Value, Present)
WRAPPER_CLASS_BOILERPLATE(OmpPresentModifier, Value);
};
@@ -4337,6 +4372,7 @@ struct OmpPresentModifier {
// reduction-modifier ->
// DEFAULT | INSCAN | TASK // since 5.0
struct OmpReductionModifier {
+ static constexpr auto Id = llvm::omp::Modifier::ReductionModifier;
ENUM_CLASS(Value, Default, Inscan, Task);
WRAPPER_CLASS_BOILERPLATE(OmpReductionModifier, Value);
};
@@ -4347,6 +4383,7 @@ struct OmpReductionModifier {
// REF_PTEE | REF_PTR | REF_PTR_PTEE // since 6.0
//
struct OmpRefModifier {
+ static constexpr auto Id = llvm::omp::Modifier::RefModifier;
ENUM_CLASS(Value, Ref_Ptee, Ref_Ptr, Ref_Ptr_Ptee)
WRAPPER_CLASS_BOILERPLATE(OmpRefModifier, Value);
};
@@ -4357,6 +4394,7 @@ struct OmpRefModifier {
// SELF // since 6.0
//
struct OmpSelfModifier {
+ static constexpr auto Id = llvm::omp::Modifier::SelfModifier;
ENUM_CLASS(Value, Self)
WRAPPER_CLASS_BOILERPLATE(OmpSelfModifier, Value);
};
@@ -4366,6 +4404,7 @@ struct OmpSelfModifier {
// step-complex-modifier ->
// STEP(integer-expression) // since 5.2
struct OmpStepComplexModifier {
+ static constexpr auto Id = llvm::omp::Modifier::StepComplexModifier;
WRAPPER_CLASS_BOILERPLATE(OmpStepComplexModifier, ScalarIntExpr);
};
@@ -4374,6 +4413,7 @@ struct OmpStepComplexModifier {
// step-simple-modifier ->
// integer-expresion // since 5.2
struct OmpStepSimpleModifier {
+ static constexpr auto Id = llvm::omp::Modifier::StepSimpleModifier;
WRAPPER_CLASS_BOILERPLATE(OmpStepSimpleModifier, ScalarIntExpr);
};
@@ -4384,6 +4424,7 @@ struct OmpStepSimpleModifier {
// MUTEXINOUTSET | DEPOBJ | // since 5.0
// INOUTSET // since 5.2
struct OmpTaskDependenceType {
+ static constexpr auto Id = llvm::omp::Modifier::TaskDependenceType;
using Value = common::OmpDependenceKind;
WRAPPER_CLASS_BOILERPLATE(OmpTaskDependenceType, Value);
};
@@ -4393,6 +4434,7 @@ struct OmpTaskDependenceType {
// traits-array ->
// TRAITS(traits-array) // since 5.2
struct OmpTraitsArray {
+ static constexpr auto Id = llvm::omp::Modifier::TraitsArray;
WRAPPER_CLASS_BOILERPLATE(OmpTraitsArray, common::Indirection<Expr>);
};
@@ -4403,6 +4445,7 @@ struct OmpTraitsArray {
// AGGREGATE | ALLOCATABLE | POINTER | // since 5.0
// ALL // since 5.2
struct OmpVariableCategory {
+ static constexpr auto Id = llvm::omp::Modifier::VariableCategory;
ENUM_CLASS(Value, Aggregate, All, Allocatable, Pointer, Scalar)
WRAPPER_CLASS_BOILERPLATE(OmpVariableCategory, Value);
};
@@ -4412,10 +4455,8 @@ struct OmpVariableCategory {
//
// ompx-hold-modifier ->
// OMPX_HOLD // since 4.5
-//
-// Until 5.2, it was a part of map-type-modifier. Since 6.0 the
-// map-type-modifier has been split into individual modifiers.
struct OmpxHoldModifier {
+ static constexpr auto Id = llvm::omp::Modifier::OmpxHoldModifier;
ENUM_CLASS(Value, Ompx_Hold)
WRAPPER_CLASS_BOILERPLATE(OmpxHoldModifier, Value);
};
diff --git a/flang/include/flang/Semantics/openmp-modifiers.h b/flang/include/flang/Semantics/openmp-modifiers.h
index a199e55639ea8..a045a92b951e4 100644
--- a/flang/include/flang/Semantics/openmp-modifiers.h
+++ b/flang/include/flang/Semantics/openmp-modifiers.h
@@ -9,7 +9,6 @@
#ifndef FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_
#define FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_
-#include "flang/Common/enum-set.h"
#include "flang/Parser/characters.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/openmp-utils.h"
@@ -41,94 +40,6 @@ namespace Fortran::semantics {
// Argument defaults: Required, Unique, Compatible, Free
// Modifier defaults: Optional, Unique, Compatible, Free
//
-template <typename SpecificTy> llvm::omp::Modifier OmpGetModifierId();
-template <typename SpecificTy>
-const llvm::omp::descriptor::Modifier &OmpGetDescriptor();
-
-#define DECLARE_DESCRIPTOR(name, id) \
- template <> inline llvm::omp::Modifier OmpGetModifierId<name>() { \
- return id; \
- } \
- template <> \
- inline const llvm::omp::descriptor::Modifier &OmpGetDescriptor<name>() { \
- return llvm::omp::getDescriptor(OmpGetModifierId<name>()); \
- }
-
-DECLARE_DESCRIPTOR(parser::OmpAccessGroup, llvm::omp::Modifier::AccessGroup)
-DECLARE_DESCRIPTOR(parser::OmpAlignment, llvm::omp::Modifier::Alignment)
-DECLARE_DESCRIPTOR(parser::OmpAlignModifier, llvm::omp::Modifier::AlignModifier)
-DECLARE_DESCRIPTOR(parser::OmpAllocatorComplexModifier,
- llvm::omp::Modifier::AllocatorComplexModifier)
-DECLARE_DESCRIPTOR(parser::OmpAllocatorSimpleModifier,
- llvm::omp::Modifier::AllocatorSimpleModifier)
-DECLARE_DESCRIPTOR(
- parser::OmpAlwaysModifier, llvm::omp::Modifier::AlwaysModifier)
-DECLARE_DESCRIPTOR(
- parser::OmpAttachModifier, llvm::omp::Modifier::AttachModifier)
-DECLARE_DESCRIPTOR(
- parser::OmpAutomapModifier, llvm::omp::Modifier::AutomapModifier)
-DECLARE_DESCRIPTOR(parser::OmpChunkModifier, llvm::omp::Modifier::ChunkModifier)
-DECLARE_DESCRIPTOR(parser::OmpCloseModifier, llvm::omp::Modifier::CloseModifier)
-DECLARE_DESCRIPTOR(
- parser::OmpContextSelector, llvm::omp::Modifier::ContextSelector)
-DECLARE_DESCRIPTOR(
- parser::OmpDeleteModifier, llvm::omp::Modifier::DeleteModifier)
-DECLARE_DESCRIPTOR(
- parser::OmpDependenceType, llvm::omp::Modifier::DependenceType)
-DECLARE_DESCRIPTOR(
- parser::OmpDepinfoModifier, llvm::omp::Modifier::DepinfoModifier)
-DECLARE_DESCRIPTOR(
- parser::OmpDeviceModifier, llvm::omp::Modifier::DeviceModifier)
-DECLARE_DESCRIPTOR(parser::OmpDimsModifier, llvm::omp::Modifier::DimsModifier)
-DECLARE_DESCRIPTOR(parser::OmpDirectiveNameModifier,
- llvm::omp::Modifier::DirectiveNameModifier)
-DECLARE_DESCRIPTOR(parser::OmpExpectation, llvm::omp::Modifier::Expectation)
-DECLARE_DESCRIPTOR(
- parser::OmpFallbackModifier, llvm::omp::Modifier::FallbackModifier)
-DECLARE_DESCRIPTOR(parser::OmpInteropType, llvm::omp::Modifier::InteropType)
-DECLARE_DESCRIPTOR(parser::OmpIterator, llvm::omp::Modifier::Iterator)
-DECLARE_DESCRIPTOR(
- parser::OmpLastprivateModifier, llvm::omp::Modifier::LastprivateModifier)
-DECLARE_DESCRIPTOR(
- parser::OmpLinearModifier, llvm::omp::Modifier::LinearModifier)
-DECLARE_DESCRIPTOR(parser::OmpLinearStep, llvm::omp::Modifier::LinearStep)
-DECLARE_DESCRIPTOR(parser::OmpLoopModifier, llvm::omp::Modifier::LoopModifier)
-DECLARE_DESCRIPTOR(parser::OmpLowerBound, llvm::omp::Modifier::LowerBound)
-DECLARE_DESCRIPTOR(parser::OmpMapper, llvm::omp::Modifier::Mapper)
-DECLARE_DESCRIPTOR(parser::OmpMapType, llvm::omp::Modifier::MapType)
-DECLARE_DESCRIPTOR(
- parser::OmpMapTypeModifier, llvm::omp::Modifier::MapTypeModifier)
-DECLARE_DESCRIPTOR(parser::OmpMemSpace, llvm::omp::Modifier::MemSpace)
-DECLARE_DESCRIPTOR(
- parser::OmpMotionModifier, llvm::omp::Modifier::MotionModifier)
-DECLARE_DESCRIPTOR(parser::OmpOrderModifier, llvm::omp::Modifier::OrderModifier)
-DECLARE_DESCRIPTOR(
- parser::OmpOrderingModifier, llvm::omp::Modifier::OrderingModifier)
-DECLARE_DESCRIPTOR(parser::OmpPreferType, llvm::omp::Modifier::PreferType)
-DECLARE_DESCRIPTOR(
- parser::OmpPrescriptiveness, llvm::omp::Modifier::Prescriptiveness)
-DECLARE_DESCRIPTOR(
- parser::OmpPresentModifier, llvm::omp::Modifier::PresentModifier)
-DECLARE_DESCRIPTOR(
- parser::OmpReductionIdentifier, llvm::omp::Modifier::ReductionIdentifier)
-DECLARE_DESCRIPTOR(
- parser::OmpReductionModifier, llvm::omp::Modifier::ReductionModifier)
-DECLARE_DESCRIPTOR(parser::OmpRefModifier, llvm::omp::Modifier::RefModifier)
-DECLARE_DESCRIPTOR(parser::OmpSelfModifier, llvm::omp::Modifier::SelfModifier)
-DECLARE_DESCRIPTOR(
- parser::OmpStepComplexModifier, llvm::omp::Modifier::StepComplexModifier)
-DECLARE_DESCRIPTOR(
- parser::OmpStepSimpleModifier, llvm::omp::Modifier::StepSimpleModifier)
-DECLARE_DESCRIPTOR(
- parser::OmpTaskDependenceType, llvm::omp::Modifier::TaskDependenceType)
-DECLARE_DESCRIPTOR(parser::OmpTraitsArray, llvm::omp::Modifier::TraitsArray)
-DECLARE_DESCRIPTOR(
- parser::OmpVariableCategory, llvm::omp::Modifier::VariableCategory)
-DECLARE_DESCRIPTOR(
- parser::OmpxHoldModifier, llvm::omp::Modifier::OmpxHoldModifier)
-
-#undef DECLARE_DESCRIPTOR
-
// Explanation of terminology:
//
// A typical clause with modifier[s] looks like this (with parts that are
@@ -161,7 +72,7 @@ const llvm::omp::descriptor::Modifier &OmpGetDescriptor(
return common::visit(
[](auto &&m) -> decltype(auto) {
using SpecificTy = llvm::remove_cvref_t<decltype(m)>;
- return OmpGetDescriptor<SpecificTy>();
+ return llvm::omp::getDescriptor(SpecificTy::Id);
},
modifier.u);
}
@@ -282,285 +193,10 @@ Fortran::parser::CharBlock OmpGetModifierSource(
llvm_unreachable("`specific` must be a member of `modifiers`");
}
-namespace detail {
-template <typename T> constexpr const T *make_nullptr() {
- return static_cast<const T *>(nullptr);
-}
-
-/// Verify that all modifiers are allowed in the given OpenMP version.
-template <typename UnionTy>
-bool verifyVersions(const std::optional<std::list<UnionTy>> &modifiers,
- llvm::omp::Clause id, parser::CharBlock clauseSource,
- SemanticsContext &semaCtx) {
- if (!modifiers) {
- return true;
- }
- unsigned version{semaCtx.langOptions().OpenMPVersion};
- bool result{true};
- for (auto &m : *modifiers) {
- const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor(m)};
- if (desc.getClauses(version).test(id)) {
- continue;
- }
- // Find the next higher version that allows this modifier on this clause.
- const auto &versions{desc.getVersions()};
- unsigned since{~0u}, until{0u};
- for (unsigned v : versions) {
- if (desc.getClauses(v).test(id)) {
- if (v < version) {
- until = std::max(until, v);
- } else if (v > version) {
- since = std::min(since, v);
- }
- }
- }
- if (since == ~0u && until == 0u) {
- // This shouldn't really happen, but have it just in case.
- semaCtx.Say(m.source,
- "'%s' modifier is not supported on %s clause"_err_en_US,
- desc.getName().str(),
- parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)));
- } else if (since != ~0u && version < since) {
- semaCtx.Say(m.source,
- "'%s' modifier is not supported in %s on %s clause, %s"_warn_en_US,
- desc.getName().str(), omp::ThisVersion(version),
- parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)),
- omp::TryVersion(since));
- result = false;
- } else if (until != 0u && version > until) {
- semaCtx.Say(m.source,
- "'%s' modifier is no longer supported in %s on %s clause"_warn_en_US,
- desc.getName().str(), omp::ThisVersion(version),
- parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)));
- result = false;
- }
- }
- return result;
-}
-
-/// Helper function for verifying the Required property:
-/// For a specific SpecificTy, if SpecificTy is has the Required property,
-/// check if the list has an item that holds SpecificTy as an alternative.
-/// If SpecificTy does not have the Required property, ignore it.
-template <typename SpecificTy, typename UnionTy>
-bool verifyIfRequired(const SpecificTy *,
- const std::optional<std::list<UnionTy>> &modifiers,
- parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
- unsigned version{semaCtx.langOptions().OpenMPVersion};
- const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor<SpecificTy>()};
- if (!desc.getProperties(version).test(llvm::omp::Property::Required)) {
- // If the modifier is not required, there is nothing to do.
- return true;
- }
- bool present{modifiers.has_value()};
- present = present && llvm::any_of(*modifiers, [](auto &&m) {
- return std::holds_alternative<SpecificTy>(m.u);
- });
- if (!present) {
- semaCtx.Say(clauseSource, "'%s' modifier is required"_err_en_US,
- desc.getName().str());
- }
- return present;
-}
-
-/// Helper function for verifying the Required property:
-/// Visit all specific types in UnionTy, and verify the Required property
-/// for each one of them.
-template <typename UnionTy, size_t... Idxs>
-bool verifyRequiredPack(const std::optional<std::list<UnionTy>> &modifiers,
- parser::CharBlock clauseSource, SemanticsContext &semaCtx,
- std::integer_sequence<size_t, Idxs...>) {
- using VariantTy = typename UnionTy::Variant;
- return (verifyIfRequired(
- make_nullptr<std::variant_alternative_t<Idxs, VariantTy>>(),
- modifiers, clauseSource, semaCtx) &&
- ...);
-}
-
-/// Verify the Required property for the given list. Return true if the
-/// list is valid, or false otherwise.
-template <typename UnionTy>
-bool verifyRequired(const std::optional<std::list<UnionTy>> &modifiers,
- llvm::omp::Clause id, parser::CharBlock clauseSource,
- SemanticsContext &semaCtx) {
- using VariantTy = typename UnionTy::Variant;
- return verifyRequiredPack(modifiers, clauseSource, semaCtx,
- std::make_index_sequence<std::variant_size_v<VariantTy>>{});
-}
-
-/// Helper function to verify the Unique property.
-/// If SpecificTy has the Unique property, and an item is found holding
-/// it as the alternative, verify that none of the elements that follow
-/// hold SpecificTy as the alternative.
-template <typename UnionTy, typename SpecificTy>
-bool verifyIfUnique(const SpecificTy *,
- typename std::list<UnionTy>::const_iterator specific,
- typename std::list<UnionTy>::const_iterator end,
- SemanticsContext &semaCtx) {
- // `specific` is the location of the modifier of type SpecificTy.
- assert(specific != end && "`specific` must be a valid location");
-
- unsigned version{semaCtx.langOptions().OpenMPVersion};
- const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor<SpecificTy>()};
- // Ultimate implies Unique.
- if (!desc.getProperties(version).test(llvm::omp::Property::Unique) &&
- !desc.getProperties(version).test(llvm::omp::Property::Ultimate)) {
- return true;
- }
- if (std::next(specific) != end) {
- auto next{
- detail::findInRange<SpecificTy, UnionTy>(std::next(specific), end)};
- if (next != end) {
- semaCtx.Say(next->source,
- "'%s' modifier cannot occur multiple times"_err_en_US,
- desc.getName().str());
- }
- }
- return true;
-}
-
-/// Verify the Unique property for the given list. Return true if the
-/// list is valid, or false otherwise.
-template <typename UnionTy>
-bool verifyUnique(const std::optional<std::list<UnionTy>> &modifiers,
- llvm::omp::Clause id, parser::CharBlock clauseSource,
- SemanticsContext &semaCtx) {
- if (!modifiers) {
- return true;
- }
- bool result{true};
- for (auto it{modifiers->cbegin()}, end{modifiers->cend()}; it != end; ++it) {
- result = common::visit(
- [&](auto &&m) {
- return verifyIfUnique<UnionTy>(&m, it, end, semaCtx);
- },
- it->u) &&
- result;
- }
- return result;
-}
-
-/// Verify the Ultimate property for the given list. Return true if the
-/// list is valid, or false otherwise.
-template <typename UnionTy>
-bool verifyUltimate(const std::optional<std::list<UnionTy>> &modifiers,
- llvm::omp::Clause id, parser::CharBlock clauseSource,
- SemanticsContext &semaCtx) {
- if (!modifiers || modifiers->size() <= 1) {
- return true;
- }
- unsigned version{semaCtx.langOptions().OpenMPVersion};
- bool result{true};
- auto first{modifiers->cbegin()};
- auto last{std::prev(modifiers->cend())};
-
- // Any item that has the Ultimate property has to be either at the back
- // or at the front of the list (depending on whether it's a pre- or a post-
- // modifier).
- // Walk over the list, and if a given item has the Ultimate property but is
- // not at the right position, mark it as an error.
- for (auto it{first}, end{modifiers->cend()}; it != end; ++it) {
- result = common::visit(
- [&](auto &&m) {
- using SpecificTy = llvm::remove_cvref_t<decltype(m)>;
- const llvm::omp::descriptor::Modifier &desc{
- OmpGetDescriptor<SpecificTy>()};
- const auto &props{desc.getProperties(version)};
-
- if (props.test(llvm::omp::Property::Ultimate)) {
- bool isPre = !llvm::omp::getProperties(id, version)
- .test(llvm::omp::Property::PostModified);
- if (it == (isPre ? last : first)) {
- // Skip, since this is the correct place for this
- // modifier.
- return true;
- }
- llvm::StringRef where{isPre ? "last" : "first"};
- semaCtx.Say(it->source,
- "'%s' should be the %s modifier"_err_en_US,
- desc.getName().str(), where.str());
- return false;
- }
- return true;
- },
- it->u) &&
- result;
- }
- return result;
-}
-
-/// Verify the Exclusive property for the given list. Return true if the
-/// list is valid, or false otherwise.
-template <typename UnionTy>
-bool verifyExclusive(const std::optional<std::list<UnionTy>> &modifiers,
- llvm::omp::Clause id, parser::CharBlock clauseSource,
- SemanticsContext &semaCtx) {
- if (!modifiers || modifiers->size() <= 1) {
- return true;
- }
- unsigned version{semaCtx.langOptions().OpenMPVersion};
- const UnionTy &front{modifiers->front()};
- const llvm::omp::descriptor::Modifier &frontDesc{OmpGetDescriptor(front)};
-
- auto second{std::next(modifiers->cbegin())};
- auto end{modifiers->end()};
-
- auto emitErrorMessage{[&](const UnionTy &excl, const UnionTy &other) {
- const llvm::omp::descriptor::Modifier &descExcl{OmpGetDescriptor(excl)};
- const llvm::omp::descriptor::Modifier &descOther{OmpGetDescriptor(other)};
- parser::MessageFormattedText txt(
- "An exclusive '%s' modifier cannot be specified together with a modifier of a different type"_err_en_US,
- descExcl.getName().str());
- parser::Message message(excl.source, txt);
- message.Attach(
- other.source, "'%s' provided here"_en_US, descOther.getName().str());
- semaCtx.Say(std::move(message));
- }};
-
- if (frontDesc.getProperties(version).test(llvm::omp::Property::Exclusive)) {
- // If the first item has the Exclusive property, then check if there is
- // another item in the rest of the list with a different SpecificTy as
- // the alternative, and mark it as an error. This allows multiple Exclusive
- // items to coexist as long as they hold the same SpecificTy.
- bool result{true};
- size_t frontIndex{front.u.index()};
- for (auto it{second}; it != end; ++it) {
- if (it->u.index() != frontIndex) {
- emitErrorMessage(front, *it);
- result = false;
- break;
- }
- }
- return result;
- } else {
- // If the first item does not have the Exclusive property, then check
- // if there is an item in the rest of the list that is Exclusive, and
- // mark it as an error if so.
- bool result{true};
- for (auto it{second}; it != end; ++it) {
- const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor(*it)};
- if (desc.getProperties(version).test(llvm::omp::Property::Exclusive)) {
- emitErrorMessage(*it, front);
- result = false;
- break;
- }
- }
- return result;
- }
-}
-} // namespace detail
-
template <typename ClauseTy>
bool OmpVerifyModifiers(const ClauseTy &clause, llvm::omp::Clause id,
parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
- auto &modifiers{OmpGetModifiers(clause)};
- bool results[]{//
- detail::verifyVersions(modifiers, id, clauseSource, semaCtx),
- detail::verifyRequired(modifiers, id, clauseSource, semaCtx),
- detail::verifyUnique(modifiers, id, clauseSource, semaCtx),
- detail::verifyUltimate(modifiers, id, clauseSource, semaCtx),
- detail::verifyExclusive(modifiers, id, clauseSource, semaCtx)};
- return llvm::all_of(results, [](bool x) { return x; });
+ return true;
}
} // namespace Fortran::semantics
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index 031926d4a7cc2..f09051bf07f16 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -73,6 +73,8 @@ template <typename T> struct WithSource {
parser::CharBlock source;
};
+template <typename T> WithSource(const T &, parser::CharBlock) -> WithSource<T>;
+
// There is no consistent way to get the source of an ActionStmt, but there
// is "source" in Statement<T>. This structure keeps the ActionStmt with the
// extracted source for further use.
diff --git a/flang/lib/Parser/openmp-utils.cpp b/flang/lib/Parser/openmp-utils.cpp
index 4ea10f8a2e156..351743911b95f 100644
--- a/flang/lib/Parser/openmp-utils.cpp
+++ b/flang/lib/Parser/openmp-utils.cpp
@@ -231,6 +231,48 @@ const OmpInitializerExpression *GetInitializerExpr(const OmpClause &x) {
return nullptr;
}
+static AppliedModifierInfo GetAppliedModifiersFromWrapper(
+ const parser::OmpDependClause &depend) {
+ using TaskDep = parser::OmpDependClause::TaskDep;
+ if (auto *task{std::get_if<TaskDep>(&depend.u)}) {
+ using Modifiers = std::optional<std::list<TaskDep::Modifier>>;
+ return GetAppliedModifiers(std::get<Modifiers>(task->t));
+ } else if (auto *doa{std::get_if<OmpDoacross>(&depend.u)}) {
+ using Modifiers = std::optional<std::list<OmpDoacross::Modifier>>;
+ return GetAppliedModifiers(std::get<Modifiers>(doa->t));
+ }
+ llvm_unreachable("Unexpected alternative in depend");
+}
+
+static AppliedModifierInfo GetAppliedModifiersFromWrapper(
+ const parser::OmpDoacrossClause &doacross) {
+ using Modifiers = std::optional<std::list<OmpDoacross::Modifier>>;
+ return GetAppliedModifiers(std::get<Modifiers>(doacross.v.t));
+}
+
+template <typename T>
+static AppliedModifierInfo GetAppliedModifiersFromWrapper(const T &wrapper) {
+ if constexpr (HasModifier<T>) {
+ using Modifiers = std::optional<std::list<typename T::Modifier>>;
+ return GetAppliedModifiers(std::get<Modifiers>(wrapper.t));
+ } else {
+ return AppliedModifierInfo{};
+ }
+}
+
+AppliedModifierInfo GetAppliedModifiers(const parser::OmpClause &clause) {
+ return common::visit(
+ [&](auto &&s) {
+ using TypeS = llvm::remove_cvref_t<decltype(s)>;
+ if constexpr (WrapperTrait<TypeS>) {
+ return GetAppliedModifiersFromWrapper(s.v);
+ } else {
+ return AppliedModifierInfo{};
+ }
+ },
+ clause.u);
+}
+
static void SplitOmpAllocateHelper(
OmpAllocateInfo &n, const OmpAllocateDirective &x) {
n.dirs.push_back(&x);
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index d1c354a9147b0..7c73ba8f17802 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -773,7 +773,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) {
auto CheckIntegerNoRef{[&](const Symbol *symbol, parser::CharBlock source) {
if (!symbol->GetType()->IsNumeric(TypeCategory::Integer)) {
- auto &desc{OmpGetDescriptor<parser::OmpLinearModifier>()};
+ auto &desc{llvm::omp::getDescriptor(llvm::omp::Modifier::LinearModifier)};
context_.Say(source,
"The list item '%s' specified without the REF '%s' must be of INTEGER type"_err_en_US,
symbol->name(), desc.getName().str());
@@ -784,7 +784,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) {
auto &modifiers{OmpGetModifiers(x.v)};
linearMod = OmpGetUniqueModifier<parser::OmpLinearModifier>(modifiers);
if (linearMod) {
- auto &desc{OmpGetDescriptor<parser::OmpLinearModifier>()};
+ auto &desc{llvm::omp::getDescriptor(llvm::omp::Modifier::LinearModifier)};
parser::CharBlock modSource{OmpGetModifierSource(modifiers, linearMod)};
bool valid{true};
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 1cc5a5d885a0c..deb33079c304f 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -1122,6 +1122,216 @@ void OmpStructureChecker::CheckClauses(parser::OmpDirectiveName dirName,
}
}
+bool OmpStructureChecker::VerifyModifierVersion(
+ WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
+ unsigned version{context_.langOptions().OpenMPVersion};
+ bool valid{true};
+
+ // Verify that the specified modifiers are allowed in this version.
+ for (const AppliedModifier &am : info.modifiers) {
+ auto &mdesc{llvm::omp::getDescriptor(am.modifierId)};
+ if (mdesc.getClauses(version).test(clause.value)) {
+ continue;
+ }
+ // Find the next higher version that allows this modifier on this clause.
+ unsigned since{~0u}, until{0u};
+ for (unsigned v : mdesc.getVersions()) {
+ if (mdesc.getClauses(v).test(clause.value)) {
+ if (v < version) {
+ until = std::max(until, v);
+ } else if (v > version) {
+ since = std::min(since, v);
+ }
+ }
+ }
+ if (since == ~0u && until == 0u) {
+ // This shouldn't really happen, but have it just in case.
+ context_.Say(am.source,
+ "'%s' modifier is not supported on %s clause"_err_en_US,
+ mdesc.getName().str(), GetUpperName(clause.value, version));
+ valid = false;
+ } else if (since != ~0u && version < since) {
+ context_.Say(am.source,
+ "'%s' modifier is not supported in %s on %s clause, %s"_warn_en_US,
+ mdesc.getName().str(), ThisVersion(version),
+ GetUpperName(clause.value, version), TryVersion(since));
+ valid = false;
+ } else if (until != 0u && version > until) {
+ context_.Say(am.source,
+ "'%s' modifier is no longer supported in %s on %s clause"_warn_en_US,
+ mdesc.getName().str(), ThisVersion(version),
+ GetUpperName(clause.value, version));
+ valid = false;
+ }
+ }
+
+ return valid;
+}
+
+bool OmpStructureChecker::VerifyModifierRequired(
+ WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
+ unsigned version{context_.langOptions().OpenMPVersion};
+ auto &cdesc{llvm::omp::getDescriptor(clause.value)};
+ bool valid{true};
+
+ // Verify required modifiers/modifier groups.
+ auto isPresent{[&](llvm::omp::Modifier m) {
+ auto f = llvm::find_if(info.modifiers,
+ [&](const AppliedModifier &am) { return am.modifierId == m; });
+ return f != info.modifiers.end();
+ }};
+
+ for (llvm::omp::Modifier m : cdesc.getModifiers(version)) {
+ auto &mdesc{llvm::omp::getDescriptor(m)};
+ if (mdesc.getProperties(version).test(llvm::omp::Property::Required)) {
+ if (!isPresent(m)) {
+ context_.Say(clause.source, "'%s' modifier is required"_err_en_US,
+ mdesc.getName().str());
+ valid = false;
+ }
+ }
+ }
+
+ return valid;
+}
+
+bool OmpStructureChecker::VerifyModifierUnique(
+ WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
+ unsigned version{context_.langOptions().OpenMPVersion};
+ bool valid{true};
+
+ // Collect unique modifiers for this clause.
+ llvm::omp::Modifiers unique;
+ auto &cdesc{llvm::omp::getDescriptor(clause.value)};
+ for (llvm::omp::Modifier m : cdesc.getModifiers(version)) {
+ auto &mdesc{llvm::omp::getDescriptor(m)};
+ // Exclusive modifiers should have the "unique" property present as well.
+ if (mdesc.getProperties(version).test(llvm::omp::Property::Unique)) {
+ unique.set(m);
+ }
+ }
+
+ llvm::DenseMap<llvm::omp::Modifier, parser::CharBlock> present;
+ for (const AppliedModifier &am : info.modifiers) {
+ auto &mdesc{llvm::omp::getDescriptor(am.modifierId)};
+ if (!mdesc.getClauses(version).test(clause.value)) {
+ continue;
+ }
+ if (unique.test(am.modifierId)) {
+ auto [where, inserted]{present.insert({am.modifierId, am.source})};
+ if (!inserted) {
+ context_
+ .Say(am.source,
+ "'%s' modifier cannot occur multiple times"_err_en_US,
+ mdesc.getName().str())
+ .Attach(
+ where->second, "previous occurrence of this modifier"_en_US);
+ valid = false;
+ }
+ }
+ }
+
+ return valid;
+}
+
+bool OmpStructureChecker::VerifyModifierExclusive(
+ WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
+ if (info.modifiers.empty()) {
+ return true;
+ }
+ unsigned version{context_.langOptions().OpenMPVersion};
+ bool valid{true};
+
+ // Check individual modifiers first.
+ const AppliedModifier &first{info.modifiers.front()};
+ const auto &fdesc{llvm::omp::getDescriptor(first.modifierId)};
+
+ auto emitErrorMessage{[&](const AppliedModifier &excl,
+ const AppliedModifier &other) {
+ auto &descExcl{llvm::omp::getDescriptor(excl.modifierId)};
+ auto &descOther{llvm::omp::getDescriptor(other.modifierId)};
+ parser::MessageFormattedText txt(
+ "An exclusive '%s' modifier cannot be specified together with a modifier of a different type"_err_en_US,
+ descExcl.getName().str());
+ parser::Message message(excl.source, txt);
+ message.Attach(
+ other.source, "'%s' provided here"_en_US, descOther.getName().str());
+ context_.Say(std::move(message));
+ }};
+
+ auto rest{llvm::ArrayRef(info.modifiers).drop_front()};
+ if (fdesc.getProperties(version).test(llvm::omp::Property::Exclusive)) {
+ for (const AppliedModifier &am : rest) {
+ if (am.modifierId == first.modifierId) {
+ continue;
+ }
+ emitErrorMessage(first, am);
+ valid = false;
+ break;
+ }
+ } else {
+ for (const AppliedModifier &am : rest) {
+ auto &mdesc{llvm::omp::getDescriptor(am.modifierId)};
+ if (!mdesc.getProperties(version).test(llvm::omp::Property::Exclusive)) {
+ continue;
+ }
+ // The first modifier was not exclusive, so this must be a different
+ // modifier.
+ emitErrorMessage(am, first);
+ valid = false;
+ break;
+ }
+ }
+
+ return valid;
+}
+
+bool OmpStructureChecker::VerifyModifierUltimate(
+ WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
+ if (info.modifiers.empty()) {
+ return true;
+ }
+ unsigned version{context_.langOptions().OpenMPVersion};
+ bool valid{true};
+
+ // Check if there is an ultimate modifier that is in a wrong position.
+ auto &cdesc{llvm::omp::getDescriptor(clause.value)};
+ llvm::ArrayRef<AppliedModifier> modifiers(info.modifiers);
+ llvm::StringRef where;
+
+ if (cdesc.getProperties(version).test(llvm::omp::Property::PostModified)) {
+ modifiers = modifiers.drop_front(1);
+ where = "first";
+ } else {
+ modifiers = modifiers.drop_back(1);
+ where = "last";
+ }
+ for (const AppliedModifier &am : modifiers) {
+ auto &mdesc{llvm::omp::getDescriptor(am.modifierId)};
+ if (mdesc.getProperties(version).test(llvm::omp::Property::Ultimate)) {
+ context_.Say(am.source, "'%s' should be the %s modifier"_err_en_US,
+ mdesc.getName().str(), where.str());
+ valid = false;
+ }
+ }
+
+ return valid;
+}
+
+bool OmpStructureChecker::VerifyModifiers(
+ WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
+ // Run all checks without short-circuiting, return 'true' if all succeed.
+ bool valid[]{
+ VerifyModifierVersion(clause, info),
+ VerifyModifierRequired(clause, info),
+ VerifyModifierUnique(clause, info),
+ VerifyModifierUltimate(clause, info),
+ VerifyModifierExclusive(clause, info),
+ };
+
+ return llvm::all_of(valid, [](bool x) { return x; });
+}
+
void OmpStructureChecker::CheckMultipleOccurrence(
semantics::UnorderedSymbolSet &listVars,
const std::list<parser::Name> &nameList, const parser::CharBlock &item,
@@ -2156,20 +2366,21 @@ void OmpStructureChecker::CheckInitOnDepobj(
OmpGetUniqueModifier<parser::OmpDepinfoModifier>(modifiers)}) {
auto depKind{std::get<common::OmpDependenceKind>(depInfo->t)};
if (depKind == common::OmpDependenceKind::Depobj) {
- auto &desc{OmpGetDescriptor<parser::OmpDepinfoModifier>()};
+ auto &desc{
+ llvm::omp::getDescriptor(llvm::omp::Modifier::DepinfoModifier)};
context_.Say(OmpGetModifierSource(modifiers, depInfo),
"'%s' is not an allowed value of the '%s' modifier"_err_en_US,
parser::ToUpperCaseLetters(EnumToString(depKind)),
desc.getName().str());
}
} else {
- auto &desc{OmpGetDescriptor<parser::OmpDepinfoModifier>()};
+ auto &desc{llvm::omp::getDescriptor(llvm::omp::Modifier::DepinfoModifier)};
context_.Say(initClause.source,
"The '%s' modifier is required on a DEPOBJ construct"_err_en_US,
desc.getName().str());
}
if (auto *prefType{OmpGetUniqueModifier<parser::OmpPreferType>(modifiers)}) {
- auto &desc{OmpGetDescriptor<parser::OmpPreferType>()};
+ auto &desc{llvm::omp::getDescriptor(llvm::omp::Modifier::PreferType)};
context_.Say(OmpGetModifierSource(modifiers, prefType),
"The '%s' modifier is not allowed on a DEPOBJ construct"_err_en_US,
desc.getName().str());
@@ -2945,48 +3156,15 @@ void OmpStructureChecker::ChecksOnOrderedAsStandalone() {
context_.Say(GetContext().clauseSource,
"THREADS and SIMD clauses are not allowed when ORDERED construct is a standalone construct with no ORDERED region"_err_en_US);
}
-
- int dependSinkCount{0}, dependSourceCount{0};
- bool exclusiveShown{false}, duplicateSourceShown{false};
-
- auto visitDoacross{[&](const parser::OmpDoacross &doa,
- const parser::CharBlock &src) {
- // Modifiers should have been verified by now.
- auto &modifiers{OmpGetModifiers(doa)};
- if (auto *source{
- OmpGetUniqueModifier<parser::OmpDependenceType>(modifiers)}) {
- if (source->v == parser::OmpDependenceType::Value::Source) {
- ++dependSourceCount;
- } else {
- ++dependSinkCount;
- }
- }
- if (!exclusiveShown && dependSinkCount > 0 && dependSourceCount > 0) {
- exclusiveShown = true;
- context_.Say(src,
- "The SINK and SOURCE dependence types are mutually exclusive"_err_en_US);
- }
- if (!duplicateSourceShown && dependSourceCount > 1) {
- duplicateSourceShown = true;
- context_.Say(src,
- "At most one SOURCE dependence type can appear on ORDERED directive"_err_en_US);
- }
- }};
-
// Visit the DEPEND and DOACROSS clauses.
for (auto [_, clause] : FindClauses(llvm::omp::Clause::OMPC_depend)) {
const auto &dependClause{std::get<parser::OmpClause::Depend>(clause->u)};
- if (auto *doAcross{std::get_if<parser::OmpDoacross>(&dependClause.v.u)}) {
- visitDoacross(*doAcross, clause->source);
- } else {
+ using TaskDep = parser::OmpDependClause::TaskDep;
+ if (std::holds_alternative<TaskDep>(dependClause.v.u)) {
context_.Say(clause->source,
"Only SINK or SOURCE dependence types are allowed when ORDERED construct is a standalone construct with no ORDERED region"_err_en_US);
}
}
- for (auto [_, clause] : FindClauses(llvm::omp::Clause::OMPC_doacross)) {
- auto &doaClause{std::get<parser::OmpClause::Doacross>(clause->u)};
- visitDoacross(doaClause.v.v, clause->source);
- }
bool isNestedInDoOrderedWithPara{false};
if (CurrentDirectiveIsNested() &&
@@ -3902,6 +4080,28 @@ void OmpStructureChecker::Leave(const parser::OmpClauseList &x) {
void OmpStructureChecker::Enter(const parser::OmpClause &x) {
SetContextClause(x);
CheckArgumentObjectKind(x);
+
+ llvm::omp::Clause id{x.Id()};
+ auto clauseId{omp::WithSource(id, x.source)};
+ switch (id) {
+ case llvm::omp::Clause::OMPC_ompx_bare:
+ case llvm::omp::Clause::OMPC_cancellation_construct_type:
+ // Those are extensions/synthetic clauses and they don't have descriptors.
+ break;
+ case llvm::omp::Clause::OMPC_uses_allocators: {
+ auto &uac{parser::UnwrapRef<parser::OmpUsesAllocatorsClause>(x)};
+ for (auto &&as : uac.v) {
+ bool legacy{std::get<bool>(as.t)};
+ if (!legacy) {
+ VerifyModifiers(clauseId, GetAppliedModifiers(OmpGetModifiers(as)));
+ }
+ }
+ break;
+ }
+ default:
+ VerifyModifiers(clauseId, GetAppliedModifiers(x));
+ break;
+ }
}
// Restrictions specific to each clause are implemented apart from the
@@ -4666,7 +4866,8 @@ void OmpStructureChecker::Enter(const parser::OmpClause::If &x) {
std::string dirName{parser::omp::GetUpperName(dir, version)};
parser::CharBlock modifierSource{OmpGetModifierSource(modifiers, dnm)};
- auto desc{OmpGetDescriptor<parser::OmpDirectiveNameModifier>()};
+ auto desc{
+ llvm::omp::getDescriptor(llvm::omp::Modifier::DirectiveNameModifier)};
std::string modName{desc.getName().str()};
if (!isConstituent(dir, sub)) {
@@ -4891,7 +5092,8 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
llvm::is_contained(leafs, Directive::OMPD_declare_mapper)};
if (!mapEnteringConstructOrMapper || !IsMapEnteringType(mapType)) {
- const auto &desc{OmpGetDescriptor<parser::OmpAttachModifier>()};
+ const auto &desc{
+ llvm::omp::getDescriptor(llvm::omp::Modifier::AttachModifier)};
context_.Say(OmpGetModifierSource(modifiers, attach),
"The '%s' modifier can only appear on a map-entering construct or on a DECLARE_MAPPER directive"_err_en_US,
desc.getName().str());
@@ -5050,7 +5252,8 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Device &x) {
OmpGetUniqueModifier<parser::OmpDeviceModifier>(modifiers)}) {
using Value = parser::OmpDeviceModifier::Value;
if (dir != llvm::omp::OMPD_target && deviceMod->v == Value::Ancestor) {
- auto name{OmpGetDescriptor<parser::OmpDeviceModifier>().getName()};
+ auto name{llvm::omp::getDescriptor(llvm::omp::Modifier::DeviceModifier)
+ .getName()};
context_.Say(OmpGetModifierSource(modifiers, deviceMod),
"The ANCESTOR %s must not appear on the DEVICE clause on any directive other than the TARGET construct. Found on %s construct."_err_en_US,
name.str(), parser::omp::GetUpperName(dir, version));
@@ -5718,7 +5921,8 @@ void OmpStructureChecker::CheckUsesAllocatorsSpec(
bool ok{
memSpaceName && IsUsesAllocatorsMemSpaceName(*memSpaceName, version)};
if (!ok) {
- auto name{OmpGetDescriptor<parser::OmpMemSpace>().getName()};
+ auto name{
+ llvm::omp::getDescriptor(llvm::omp::Modifier::MemSpace).getName()};
context_.Say(memSpaceSource,
"The '%s' modifier must name a predefined memory space"_err_en_US,
name.str());
@@ -6407,7 +6611,8 @@ void OmpStructureChecker::Enter(const parser::OmpClause::SelfMaps &x) {
void OmpStructureChecker::CheckDimsModifier(parser::CharBlock source,
size_t numValues, const parser::OmpDimsModifier &x) {
- std::string name{OmpGetDescriptor<parser::OmpDimsModifier>().getName().str()};
+ auto &desc{llvm::omp::getDescriptor(llvm::omp::Modifier::DimsModifier)};
+ std::string name{desc.getName().str()};
if (auto dimsVal{GetIntValue(x.v)}) {
if (*dimsVal > 0) {
@@ -6582,7 +6787,8 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
if (auto *depInfo{
OmpGetUniqueModifier<parser::OmpDepinfoModifier>(
modifiers)}) {
- auto &desc{OmpGetDescriptor<parser::OmpDepinfoModifier>()};
+ auto &desc{llvm::omp::getDescriptor(
+ llvm::omp::Modifier::DepinfoModifier)};
context_.Say(OmpGetModifierSource(modifiers, depInfo),
"The '%s' is not allowed on INTEROP construct"_err_en_US,
desc.getName().str());
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 8016c82b8496f..966c32e212dcb 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -18,6 +18,7 @@
#include "flang/Common/enum-set.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/openmp-directive-sets.h"
+#include "flang/Semantics/openmp-utils.h"
#include "flang/Semantics/semantics.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/iterator_range.h"
@@ -357,6 +358,18 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
std::pair<const parser::OmpClause *, const parser::OmpClause *>
FindMutuallyExclusiveClauses(llvm::omp::ClauseSet exclusive,
const std::vector<const parser::OmpClause *> &clauses);
+ bool VerifyModifierVersion(omp::WithSource<llvm::omp::Clause> clause,
+ const parser::omp::AppliedModifierInfo &info);
+ bool VerifyModifierRequired(omp::WithSource<llvm::omp::Clause> clause,
+ const parser::omp::AppliedModifierInfo &info);
+ bool VerifyModifierUnique(omp::WithSource<llvm::omp::Clause> clause,
+ const parser::omp::AppliedModifierInfo &info);
+ bool VerifyModifierExclusive(omp::WithSource<llvm::omp::Clause> clause,
+ const parser::omp::AppliedModifierInfo &info);
+ bool VerifyModifierUltimate(omp::WithSource<llvm::omp::Clause> clause,
+ const parser::omp::AppliedModifierInfo &info);
+ bool VerifyModifiers(omp::WithSource<llvm::omp::Clause> clause,
+ const parser::omp::AppliedModifierInfo &info);
const parser::OpenMPConstruct *GetCurrentConstruct() const;
void CheckSourceLabel(const parser::Label &);
diff --git a/flang/test/Semantics/OpenMP/clause-validity01.f90 b/flang/test/Semantics/OpenMP/clause-validity01.f90
index bc675d72dc9d3..cd4ad04197620 100644
--- a/flang/test/Semantics/OpenMP/clause-validity01.f90
+++ b/flang/test/Semantics/OpenMP/clause-validity01.f90
@@ -504,6 +504,7 @@
!$omp taskyield
!$omp barrier
!$omp taskwait
+ !ERROR: 'task-dependence-type' modifier is required
!ERROR: The SINK and SOURCE dependence types can only be used with the ORDERED directive, used here in the TASKWAIT construct
!WARNING: 'dependence-type' modifier is no longer supported in OpenMP v5.2 on DEPEND clause
!$omp taskwait depend(source)
diff --git a/flang/test/Semantics/OpenMP/depobj-construct-v52.f90 b/flang/test/Semantics/OpenMP/depobj-construct-v52.f90
index 021e07618bb51..6fea74be8f026 100644
--- a/flang/test/Semantics/OpenMP/depobj-construct-v52.f90
+++ b/flang/test/Semantics/OpenMP/depobj-construct-v52.f90
@@ -2,6 +2,7 @@
subroutine f00
integer :: obj
+!ERROR: 'task-dependence-type' modifier is required
!WARNING: 'dependence-type' modifier is no longer supported in OpenMP v5.2 on DEPEND clause
!ERROR: A DEPEND clause on a DEPOBJ construct must not have SINK or SOURCE as dependence type
!$omp depobj(obj) depend(source)
diff --git a/flang/test/Semantics/OpenMP/linear-clause02.f90 b/flang/test/Semantics/OpenMP/linear-clause02.f90
index d76f921f63694..e6eedbcae7c92 100644
--- a/flang/test/Semantics/OpenMP/linear-clause02.f90
+++ b/flang/test/Semantics/OpenMP/linear-clause02.f90
@@ -8,6 +8,7 @@ subroutine f00(x)
subroutine f01(x)
integer :: x
+ !WARNING: The 'modifier(<list>)' syntax is deprecated in OpenMP v5.2, use '<list> : modifier' instead
!ERROR: An exclusive 'step-simple-modifier' modifier cannot be specified together with a modifier of a different type
!$omp declare simd linear(uval(x) : 2)
end
diff --git a/flang/test/Semantics/OpenMP/ordered01.f90 b/flang/test/Semantics/OpenMP/ordered01.f90
index b445b0448ba18..13cd0e3c61151 100644
--- a/flang/test/Semantics/OpenMP/ordered01.f90
+++ b/flang/test/Semantics/OpenMP/ordered01.f90
@@ -38,11 +38,8 @@ program main
!$omp do ordered(1)
do i = 2, N
!ERROR: Only SINK or SOURCE dependence types are allowed when ORDERED construct is a standalone construct with no ORDERED region
- !ERROR: At most one SOURCE dependence type can appear on ORDERED directive
!$omp ordered depend(source) depend(inout: arrayA) depend(source)
arrayA(i) = foo(i)
- !ERROR: The SINK and SOURCE dependence types are mutually exclusive
- !ERROR: At most one SOURCE dependence type can appear on ORDERED directive
!$omp ordered depend(sink: i - 1) depend(source) depend(source)
arrayB(i) = bar(arrayA(i), arrayB(i-1))
!ERROR: Only SINK or SOURCE dependence types are allowed when ORDERED construct is a standalone construct with no ORDERED region
More information about the flang-commits
mailing list