[flang-commits] [flang] 3783ff1 - [flang][OpenMP] Replace modifier verification with a generic one (#220675)
via flang-commits
flang-commits at lists.llvm.org
Thu Sep 3 09:57:51 PDT 2026
Author: Krzysztof Parzyszek
Date: 2026-09-03T11:57:39-05:00
New Revision: 3783ff1fc631c567badb5b22f6a9d5a36a54cb3b
URL: https://github.com/llvm/llvm-project/commit/3783ff1fc631c567badb5b22f6a9d5a36a54cb3b
DIFF: https://github.com/llvm/llvm-project/commit/3783ff1fc631c567badb5b22f6a9d5a36a54cb3b.diff
LOG: [flang][OpenMP] Replace modifier verification with a generic one (#220675)
Implement verification of syntactic properties (i.e. required, unique,
exclusive, ultimate) in a generic way, agnostic of the kind of element
to which these properties are applied. The goal here is to reuse it
for verifying clause properties in the future.
Refactor the existing modifier verification code to use it. Modify
the previous implementation (OmpVerifyModifiers) to always succeed
to reduce the amount of necessary changes.
Added:
flang/lib/Semantics/check-omp-syntax.cpp
Modified:
flang/include/flang/Parser/openmp-utils.h
flang/include/flang/Parser/parse-tree.h
flang/include/flang/Semantics/openmp-modifiers.h
flang/lib/Semantics/CMakeLists.txt
flang/lib/Semantics/check-omp-loop.cpp
flang/lib/Semantics/check-omp-structure.cpp
flang/lib/Semantics/check-omp-structure.h
flang/test/Parser/OpenMP/dims-modifier.f90
flang/test/Semantics/OpenMP/clause-validity01.f90
flang/test/Semantics/OpenMP/depobj-construct-v52.f90
flang/test/Semantics/OpenMP/linear-clause02.f90
Removed:
################################################################################
diff --git a/flang/include/flang/Parser/openmp-utils.h b/flang/include/flang/Parser/openmp-utils.h
index cd19ed1d72f0d..c36deea672325 100644
--- a/flang/include/flang/Parser/openmp-utils.h
+++ b/flang/include/flang/Parser/openmp-utils.h
@@ -219,6 +219,18 @@ 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;
+
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 d3abfcebf21a0..3e9a035d0bfee 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 970ebd460685d..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;
- }
- llvm::omp::Version version{semaCtx.langOptions().getOpenMPVersion()};
- 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()};
- llvm::omp::Version since(~0u), until(0u);
- for (llvm::omp::Version 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 == ~0 && until == 0) {
- // 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 != ~0 && 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 != 0 && 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) {
- llvm::omp::Version version{semaCtx.langOptions().getOpenMPVersion()};
- 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");
-
- llvm::omp::Version version{semaCtx.langOptions().getOpenMPVersion()};
- 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;
- }
- llvm::omp::Version version{semaCtx.langOptions().getOpenMPVersion()};
- 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;
- }
- llvm::omp::Version version{semaCtx.langOptions().getOpenMPVersion()};
- 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
diff erent 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
diff erent 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/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt
index 649314a023704..6fc45bdb449d9 100644
--- a/flang/lib/Semantics/CMakeLists.txt
+++ b/flang/lib/Semantics/CMakeLists.txt
@@ -24,6 +24,7 @@ add_flang_library(FortranSemantics
check-omp-loop.cpp
check-omp-variant.cpp
check-omp-structure.cpp
+ check-omp-syntax.cpp
check-purity.cpp
check-return.cpp
check-select-rank.cpp
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 393d67ac5f150..052b742d5c667 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 6f9caeea9de2b..9c77960ac12cb 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -2157,20 +2157,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());
@@ -3904,6 +3905,7 @@ void OmpStructureChecker::Leave(const parser::OmpClauseList &x) {
void OmpStructureChecker::Enter(const parser::OmpClause &x) {
SetContextClause(x);
CheckArgumentObjectKind(x);
+ VerifyModifiers(x);
}
// Restrictions specific to each clause are implemented apart from the
@@ -4669,7 +4671,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)) {
@@ -4894,7 +4897,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());
@@ -5053,7 +5057,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));
@@ -5721,7 +5726,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());
@@ -6410,7 +6416,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) {
@@ -6585,7 +6592,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..34606647c5f41 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -15,13 +15,15 @@
#define FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_
#include "check-directive-structure.h"
-#include "flang/Common/enum-set.h"
+
+#include "flang/Parser/openmp-utils.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/openmp-directive-sets.h"
#include "flang/Semantics/semantics.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 <cstddef>
#include <functional>
@@ -52,6 +54,19 @@ namespace omp {
struct LoopSequence;
}
+// Support classes for verifying syntactic properties.
+template <typename ElemTy> struct AppliedElement {
+ parser::omp::WithSource<ElemTy> id;
+};
+
+template <typename ElemTy> struct AppliedElementInfo {
+ using ElementTy = AppliedElement<ElemTy>;
+ llvm::SmallVector<ElementTy> elements;
+};
+
+using AppliedModifierInfo = AppliedElementInfo<llvm::omp::Modifier>;
+using AppliedModifier = AppliedModifierInfo::ElementTy;
+
// Mapping from 'Symbol' to 'Source' to keep track of the variables
// used in multiple clauses
using SymbolSourceMap = std::multimap<const Symbol *, parser::CharBlock>;
@@ -338,6 +353,22 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
void CheckTraitSimd(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
+ // check-omp-syntax.cpp
+ bool VerifyModifierVersion(parser::omp::WithSource<llvm::omp::Clause> clause,
+ const AppliedModifierInfo &info);
+ bool VerifyModifierRequired(parser::omp::WithSource<llvm::omp::Clause> clause,
+ const AppliedModifierInfo &info);
+ bool VerifyModifierUnique(parser::omp::WithSource<llvm::omp::Clause> clause,
+ const AppliedModifierInfo &info);
+ bool VerifyModifierExclusive(
+ parser::omp::WithSource<llvm::omp::Clause> clause,
+ const AppliedModifierInfo &info);
+ bool VerifyModifierUltimate(parser::omp::WithSource<llvm::omp::Clause> clause,
+ const AppliedModifierInfo &info);
+ bool VerifyModifiers(parser::omp::WithSource<llvm::omp::Clause> clause,
+ const AppliedModifierInfo &info);
+ void VerifyModifiers(const parser::OmpClause &x);
+
// check-omp-structure.cpp
using ClauseIterator =
decltype(std::declval<const parser::OmpClauseList>().v.begin());
diff --git a/flang/lib/Semantics/check-omp-syntax.cpp b/flang/lib/Semantics/check-omp-syntax.cpp
new file mode 100644
index 0000000000000..786d7e33262e9
--- /dev/null
+++ b/flang/lib/Semantics/check-omp-syntax.cpp
@@ -0,0 +1,439 @@
+//===-- lib/Semantics/check-omp-syntax.cpp --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "check-omp-structure.h"
+
+#include "flang/Common/visit.h"
+#include "flang/Parser/char-block.h"
+#include "flang/Parser/openmp-utils.h"
+#include "flang/Parser/parse-tree.h"
+#include "flang/Semantics/openmp-modifiers.h"
+#include "flang/Semantics/openmp-utils.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Frontend/Directive/Spelling.h"
+#include "llvm/Frontend/OpenMP/OMP.h"
+#include "llvm/Frontend/OpenMP/OMPDescriptors.h"
+
+#include <algorithm>
+#include <list>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <variant>
+
+namespace Fortran::semantics {
+using namespace Fortran::parser::omp;
+
+template <typename T> struct SetTypeFor {
+ using type = llvm::omp::EnumSet<T,
+ llvm::to_underlying(T::Last_) - llvm::to_underlying(T::First_) + 1>;
+};
+
+static llvm::omp::Modifiers getElements(
+ const llvm::omp::descriptor::Clause &cdesc, llvm::omp::Version version) {
+ return cdesc.getModifiers(version);
+}
+
+template < //
+ typename ElemTy, typename OwnerTy,
+ typename ResultTy = llvm::DenseMap<ElemTy,
+ std::pair<parser::CharBlock, llvm::directive::VersionRange>>>
+static ResultTy VerifyVersions(const AppliedElementInfo<ElemTy> &info,
+ OwnerTy ownerId, llvm::omp::Version version) {
+ using AppliedElementTy = AppliedElement<ElemTy>;
+ ResultTy result;
+
+ auto &odesc{llvm::omp::getDescriptor(ownerId)};
+ auto elements{getElements(odesc, version)};
+
+ for (const AppliedElementTy &elem : info.elements) {
+ if (elements.test(elem.id.value)) {
+ continue;
+ }
+ llvm::omp::Version since{~0u}, until{0u};
+ for (llvm::omp::Version v : odesc.getVersions()) {
+ if (getElements(odesc, v).test(elem.id.value)) {
+ if (v < version) {
+ until = std::max(until, v);
+ } else if (v > version) {
+ since = std::min(since, v);
+ }
+ }
+ }
+ int minVer = static_cast<unsigned>(since);
+ int maxVer = static_cast<unsigned>(until);
+ result.insert({elem.id.value,
+ {elem.id.source, llvm::directive::VersionRange{minVer, maxVer}}});
+ }
+ return result;
+}
+
+template < //
+ typename ElemTy, typename OwnerTy,
+ typename ElemSetTy = typename SetTypeFor<ElemTy>::type,
+ typename ResultTy = ElemSetTy>
+static ResultTy VerifyRequired(const AppliedElementInfo<ElemTy> &info,
+ OwnerTy ownerId, llvm::omp::Version version) {
+ using AppliedElementTy = AppliedElement<ElemTy>;
+ ResultTy required;
+ auto &odesc{llvm::omp::getDescriptor(ownerId)};
+
+ for (auto e : getElements(odesc, version)) {
+ auto &edesc{llvm::omp::getDescriptor(e)};
+ if (edesc.getProperties(version).test(llvm::omp::Property::Required)) {
+ required.set(e);
+ }
+ }
+
+ for (const AppliedElementTy &elem : info.elements) {
+ required.reset(elem.id.value);
+ }
+
+ return required;
+}
+
+template < //
+ typename ElemTy, typename OwnerTy,
+ typename ResultTy =
+ llvm::DenseMap<ElemTy, std::pair<parser::CharBlock, parser::CharBlock>>>
+static ResultTy VerifyUnique(const AppliedElementInfo<ElemTy> &info,
+ OwnerTy ownerId, llvm::omp::Version version) {
+ using AppliedElementTy = AppliedElement<ElemTy>;
+ using ElemSetTy = typename SetTypeFor<ElemTy>::type;
+ ElemSetTy unique;
+
+ auto &odesc{llvm::omp::getDescriptor(ownerId)};
+ auto elements{getElements(odesc, version)};
+
+ for (auto e : elements) {
+ auto &edesc{llvm::omp::getDescriptor(e)};
+ // Exclusive modifiers should have the "unique" property present as well.
+ if (edesc.getProperties(version).test(llvm::omp::Property::Unique)) {
+ unique.set(e);
+ }
+ }
+
+ ResultTy repeated;
+ llvm::DenseMap<ElemTy, parser::CharBlock> present;
+ for (const AppliedElementTy &elem : info.elements) {
+ if (!elements.test(elem.id.value)) {
+ // Skip invalid elements.
+ continue;
+ }
+ if (unique.test(elem.id.value)) {
+ auto [where, inserted]{present.insert({elem.id.value, elem.id.source})};
+ if (!inserted) {
+ repeated.insert({elem.id.value, {where->second, elem.id.source}});
+ }
+ }
+ }
+
+ return repeated;
+}
+
+template < //
+ typename ElemTy, typename OwnerTy,
+ typename ResultTy = llvm::DenseMap<ElemTy,
+ std::tuple<ElemTy, parser::CharBlock, parser::CharBlock>>>
+static ResultTy VerifyExclusive(const AppliedElementInfo<ElemTy> &info,
+ OwnerTy ownerId, llvm::omp::Version version) {
+ using AppliedElementTy = AppliedElement<ElemTy>;
+ ResultTy result;
+
+ auto &odesc{llvm::omp::getDescriptor(ownerId)};
+ auto elements{getElements(odesc, version)};
+
+ llvm::DenseMap<ElemTy, parser::CharBlock> present;
+ for (const AppliedElementTy &elem : info.elements) {
+ if (!elements.test(elem.id.value)) {
+ // Skip invalid elements.
+ continue;
+ }
+ present.insert({elem.id.value, elem.id.source});
+ }
+
+ for (auto [id, source] : present) {
+ auto &edesc{llvm::omp::getDescriptor(id)};
+ if (!edesc.getProperties(version).test(llvm::omp::Property::Exclusive)) {
+ continue;
+ }
+ // Element is exclusive, it cannot coexist with any other element.
+ for (auto [otherId, otherSource] : present) {
+ if (otherId != id) {
+ result.insert({id, {otherId, source, otherSource}});
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+template < //
+ typename ElemTy, typename OwnerTy,
+ typename ResultTy = llvm::DenseMap<ElemTy, parser::CharBlock>>
+static ResultTy VerifyUltimate(const AppliedElementInfo<ElemTy> &info,
+ OwnerTy ownerId, llvm::omp::Version version, bool last = true) {
+ ResultTy result;
+ if (info.elements.empty()) {
+ return result;
+ }
+
+ using AppliedElementTy = AppliedElement<ElemTy>;
+ using ElemSetTy = typename SetTypeFor<ElemTy>::type;
+ ElemSetTy ultimate;
+
+ auto &odesc{llvm::omp::getDescriptor(ownerId)};
+ auto elements{getElements(odesc, version)};
+
+ for (auto e : elements) {
+ auto &edesc{llvm::omp::getDescriptor(e)};
+ if (edesc.getProperties(version).test(llvm::omp::Property::Ultimate)) {
+ ultimate.set(e);
+ }
+ }
+
+ // Check if there is an ultimate modifier that is in a wrong position.
+ auto rest{last
+ ? llvm::ArrayRef<AppliedElementTy>(info.elements).drop_back(1)
+ : llvm::ArrayRef<AppliedElementTy>(info.elements).drop_front(1)};
+
+ for (const AppliedElementTy &elem : rest) {
+ if (!elements.test(elem.id.value)) {
+ // Skip invalid elements.
+ continue;
+ }
+ if (ultimate.test(elem.id.value)) {
+ result.insert({elem.id.value, elem.id.source});
+ }
+ }
+
+ return result;
+}
+
+bool OmpStructureChecker::VerifyModifierVersion(
+ WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
+ // Verify that the specified modifiers are allowed in this version.
+ llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+
+ auto result = VerifyVersions(info, clause.value, version);
+
+ for (auto &[m, svr] : result) {
+ std::string modName{llvm::omp::getDescriptor(m).getName().str()};
+ std::string clauseName{GetUpperName(clause.value, version)};
+ llvm::omp::Version since(svr.second.Min);
+ llvm::omp::Version until(svr.second.Max);
+
+ if (since == ~0u && until == 0u) {
+ // This shouldn't really happen, but have it just in case.
+ context_.Say(svr.first,
+ "'%s' modifier is not supported on %s clause"_err_en_US, modName,
+ clauseName);
+ } else if (since != ~0u && version < since) {
+ context_.Say(svr.first,
+ "'%s' modifier is not supported in %s on %s clause, %s"_warn_en_US,
+ modName, omp::ThisVersion(version), clauseName,
+ omp::TryVersion(since));
+ } else if (until != 0u && version > until) {
+ context_.Say(svr.first,
+ "'%s' modifier is no longer supported in %s on %s clause"_warn_en_US,
+ modName, omp::ThisVersion(version), clauseName);
+ }
+ }
+
+ return result.empty();
+}
+
+bool OmpStructureChecker::VerifyModifierRequired(
+ WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
+ llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+
+ auto result = VerifyRequired(info, clause.value, version);
+
+ for (llvm::omp::Modifier m : result) {
+ auto &mdesc{llvm::omp::getDescriptor(m)};
+ context_.Say(clause.source, "'%s' modifier is required"_err_en_US,
+ mdesc.getName().str());
+ }
+
+ return result.empty();
+}
+
+bool OmpStructureChecker::VerifyModifierUnique(
+ WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
+ llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+
+ auto result = VerifyUnique(info, clause.value, version);
+
+ for (auto [id, where] : result) {
+ auto &mdesc{llvm::omp::getDescriptor(id)};
+ context_
+ .Say(where.first, "'%s' modifier cannot occur multiple times"_err_en_US,
+ mdesc.getName().str())
+ .Attach(where.second, "previous occurrence of this modifier"_en_US);
+ }
+
+ return result.empty();
+}
+
+bool OmpStructureChecker::VerifyModifierExclusive(
+ WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
+ llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+
+ auto result = VerifyExclusive(info, clause.value, version);
+
+ for (auto [id, wrong] : result) {
+ auto [otherId, source, otherSource] = wrong;
+ context_
+ .Say(source,
+ "An exclusive '%s' modifier cannot be specified together with a modifier of a
diff erent type"_err_en_US,
+ llvm::omp::getDescriptor(id).getName().str())
+ .Attach(otherSource, "'%s' provided here"_en_US,
+ llvm::omp::getDescriptor(otherId).getName().str());
+ }
+
+ return result.empty();
+}
+
+bool OmpStructureChecker::VerifyModifierUltimate(
+ WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
+ llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+ auto &cdesc{llvm::omp::getDescriptor(clause.value)};
+ bool last{
+ !cdesc.getProperties(version).test(llvm::omp::Property::PostModified)};
+ std::string expected{last ? "last" : "first"};
+
+ auto result = VerifyUltimate(info, clause.value, version, last);
+
+ for (auto [id, where] : result) {
+ context_.Say(where, "'%s' should be the %s modifier"_err_en_US,
+ llvm::omp::getDescriptor(id).getName().str(), expected);
+ }
+
+ return result.empty();
+}
+
+template <typename UnionTy>
+AppliedModifierInfo GetAppliedModifiers(llvm::omp::Clause clauseId,
+ llvm::omp::Version version,
+ const std::optional<std::list<UnionTy>> &modifiers) {
+ AppliedModifierInfo info;
+ if (modifiers) {
+ auto cdesc{llvm::omp::getDescriptor(clauseId)};
+ for (auto &m : *modifiers) {
+ common::visit(
+ [&](auto &&t) {
+ auto &am{info.elements.emplace_back(AppliedModifier{})};
+ am.id = WithSource{t.Id, m.source};
+ },
+ m.u);
+ }
+ }
+ return info;
+}
+
+static AppliedModifierInfo GetAppliedModifiersFromWrapper(
+ llvm::omp::Clause clauseId, llvm::omp::Version version,
+ 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(
+ llvm::omp::Clause::OMPC_depend, version, std::get<Modifiers>(task->t));
+ } else if (auto *doa{std::get_if<parser::OmpDoacross>(&depend.u)}) {
+ using Modifiers = std::optional<std::list<parser::OmpDoacross::Modifier>>;
+ return GetAppliedModifiers(
+ llvm::omp::Clause::OMPC_depend, version, std::get<Modifiers>(doa->t));
+ }
+ llvm_unreachable("Unexpected alternative in depend");
+}
+
+static AppliedModifierInfo GetAppliedModifiersFromWrapper(
+ llvm::omp::Clause clauseId, llvm::omp::Version version,
+ const parser::OmpDoacrossClause &doacross) {
+ using Modifiers = std::optional<std::list<parser::OmpDoacross::Modifier>>;
+ return GetAppliedModifiers(llvm::omp::Clause::OMPC_doacross, version,
+ std::get<Modifiers>(doacross.v.t));
+}
+
+template <typename T>
+static AppliedModifierInfo GetAppliedModifiersFromWrapper(
+ llvm::omp::Clause clauseId, llvm::omp::Version version, const T &wrapper) {
+ if constexpr (HasModifier<T>) {
+ using Modifiers = std::optional<std::list<typename T::Modifier>>;
+ return GetAppliedModifiers(
+ clauseId, version, std::get<Modifiers>(wrapper.t));
+ } else {
+ return AppliedModifierInfo{};
+ }
+}
+
+AppliedModifierInfo GetAppliedModifiers(
+ const parser::OmpClause &clause, llvm::omp::Version version) {
+ return common::visit(
+ [&](auto &&s) {
+ using TypeS = llvm::remove_cvref_t<decltype(s)>;
+ if constexpr (WrapperTrait<TypeS>) {
+ return GetAppliedModifiersFromWrapper(clause.Id(), version, s.v);
+ } else {
+ return AppliedModifierInfo{};
+ }
+ },
+ clause.u);
+}
+
+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::VerifyModifiers(const parser::OmpClause &x) {
+ llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+ llvm::omp::Clause id{x.Id()};
+ auto clauseId{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: {
+ // The traits of the deprecated syntax are stored as a traits-array
+ // modifier, but they are not the 5.2 modifier, so they must not be
+ // version-checked. A modifier that postdates the OpenMP version in effect
+ // is only warned about, so the specification is accepted as an extension
+ // and must still be checked, otherwise a malformed one would reach lowering
+ // unvalidated.
+ auto &uac{parser::UnwrapRef<parser::OmpUsesAllocatorsClause>(x)};
+ for (auto &&as : uac.v) {
+ bool legacy{std::get<bool>(as.t)};
+ if (!legacy) {
+ VerifyModifiers(
+ clauseId, GetAppliedModifiers(id, version, OmpGetModifiers(as)));
+ }
+ }
+ break;
+ }
+ default:
+ VerifyModifiers(clauseId, GetAppliedModifiers(x, version));
+ break;
+ }
+}
+} // namespace Fortran::semantics
diff --git a/flang/test/Parser/OpenMP/dims-modifier.f90 b/flang/test/Parser/OpenMP/dims-modifier.f90
index 28dc1b5965903..f2ee0483cf7c6 100644
--- a/flang/test/Parser/OpenMP/dims-modifier.f90
+++ b/flang/test/Parser/OpenMP/dims-modifier.f90
@@ -1,5 +1,5 @@
-!RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=61 %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s
-!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=61 %s | FileCheck --check-prefix="PARSE-TREE" %s
+!RUN: %flang_fc1 -fdebug-unparse-no-sema -fopenmp -fopenmp-version=61 %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s
+!RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema -fopenmp -fopenmp-version=61 %s | FileCheck --check-prefix="PARSE-TREE" %s
subroutine f00
!$omp teams num_teams(dims(2): 10, 4)
@@ -7,19 +7,16 @@ subroutine f00
end
!UNPARSE: SUBROUTINE f00
-!UNPARSE: !$OMP TEAMS NUM_TEAMS(DIMS(2_4):10_4, 4_4)
+!UNPARSE: !$OMP TEAMS NUM_TEAMS(DIMS(2):10, 4)
!UNPARSE: !$OMP END TEAMS
!UNPARSE: END SUBROUTINE
!PARSE-TREE: OmpBeginDirective
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = teams
!PARSE-TREE: | OmpClauseList -> OmpClause -> NumTeams -> OmpNumTeamsClause
-!PARSE-TREE: | | Modifier -> OmpDimsModifier -> Scalar -> Integer -> Constant -> Expr = '2_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '2'
-!PARSE-TREE: | | Scalar -> Integer -> Expr = '10_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '10'
-!PARSE-TREE: | | Scalar -> Integer -> Expr = '4_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '4'
+!PARSE-TREE: | | Modifier -> OmpDimsModifier -> Scalar -> Integer -> Constant -> Expr -> LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | | Scalar -> Integer -> Expr -> LiteralConstant -> IntLiteralConstant = '10'
+!PARSE-TREE: | | Scalar -> Integer -> Expr -> LiteralConstant -> IntLiteralConstant = '4'
!PARSE-TREE: | Flags = {}
@@ -29,21 +26,17 @@ subroutine f01
end
!UNPARSE: SUBROUTINE f01
-!UNPARSE: !$OMP TEAMS NUM_TEAMS(DIMS(2_4), 3_4:10_4, 4_4)
+!UNPARSE: !$OMP TEAMS NUM_TEAMS(DIMS(2), 3:10, 4)
!UNPARSE: !$OMP END TEAMS
!UNPARSE: END SUBROUTINE
!PARSE-TREE: OmpBeginDirective
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = teams
!PARSE-TREE: | OmpClauseList -> OmpClause -> NumTeams -> OmpNumTeamsClause
-!PARSE-TREE: | | Modifier -> OmpDimsModifier -> Scalar -> Integer -> Constant -> Expr = '2_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '2'
-!PARSE-TREE: | | Modifier -> OmpLowerBound -> Scalar -> Integer -> Expr = '3_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '3'
-!PARSE-TREE: | | Scalar -> Integer -> Expr = '10_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '10'
-!PARSE-TREE: | | Scalar -> Integer -> Expr = '4_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '4'
+!PARSE-TREE: | | Modifier -> OmpDimsModifier -> Scalar -> Integer -> Constant -> Expr -> LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | | Modifier -> OmpLowerBound -> Scalar -> Integer -> Expr -> LiteralConstant -> IntLiteralConstant = '3'
+!PARSE-TREE: | | Scalar -> Integer -> Expr -> LiteralConstant -> IntLiteralConstant = '10'
+!PARSE-TREE: | | Scalar -> Integer -> Expr -> LiteralConstant -> IntLiteralConstant = '4'
!PARSE-TREE: | Flags = {}
@@ -53,17 +46,15 @@ subroutine f02
end
!UNPARSE: SUBROUTINE f02
-!UNPARSE: !$OMP TEAMS THREAD_LIMIT(DIMS(3_4):16_4)
+!UNPARSE: !$OMP TEAMS THREAD_LIMIT(DIMS(3):16)
!UNPARSE: !$OMP END TEAMS
!UNPARSE: END SUBROUTINE
!PARSE-TREE: OmpBeginDirective
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = teams
!PARSE-TREE: | OmpClauseList -> OmpClause -> ThreadLimit -> OmpThreadLimitClause
-!PARSE-TREE: | | Modifier -> OmpDimsModifier -> Scalar -> Integer -> Constant -> Expr = '3_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '3'
-!PARSE-TREE: | | Scalar -> Integer -> Expr = '16_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '16'
+!PARSE-TREE: | | Modifier -> OmpDimsModifier -> Scalar -> Integer -> Constant -> Expr -> LiteralConstant -> IntLiteralConstant = '3'
+!PARSE-TREE: | | Scalar -> Integer -> Expr -> LiteralConstant -> IntLiteralConstant = '16'
!PARSE-TREE: | Flags = {}
@@ -73,21 +64,16 @@ subroutine f03
end
!UNPARSE: SUBROUTINE f03
-!UNPARSE: !$OMP PARALLEL NUM_THREADS(DIMS(4_4):4_4, 5_4, 6_4, 7_4)
+!UNPARSE: !$OMP PARALLEL NUM_THREADS(DIMS(4):4, 5, 6, 7)
!UNPARSE: !$OMP END PARALLEL
!UNPARSE: END SUBROUTINE
!PARSE-TREE: OmpBeginDirective
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = parallel
!PARSE-TREE: | OmpClauseList -> OmpClause -> NumThreads -> OmpNumThreadsClause
-!PARSE-TREE: | | Modifier -> OmpDimsModifier -> Scalar -> Integer -> Constant -> Expr = '4_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '4'
-!PARSE-TREE: | | Scalar -> Integer -> Expr = '4_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '4'
-!PARSE-TREE: | | Scalar -> Integer -> Expr = '5_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '5'
-!PARSE-TREE: | | Scalar -> Integer -> Expr = '6_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '6'
-!PARSE-TREE: | | Scalar -> Integer -> Expr = '7_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '7'
+!PARSE-TREE: | | Modifier -> OmpDimsModifier -> Scalar -> Integer -> Constant -> Expr -> LiteralConstant -> IntLiteralConstant = '4'
+!PARSE-TREE: | | Scalar -> Integer -> Expr -> LiteralConstant -> IntLiteralConstant = '4'
+!PARSE-TREE: | | Scalar -> Integer -> Expr -> LiteralConstant -> IntLiteralConstant = '5'
+!PARSE-TREE: | | Scalar -> Integer -> Expr -> LiteralConstant -> IntLiteralConstant = '6'
+!PARSE-TREE: | | Scalar -> Integer -> Expr -> LiteralConstant -> IntLiteralConstant = '7'
!PARSE-TREE: | Flags = {}
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
diff erent type
!$omp declare simd linear(uval(x) : 2)
end
More information about the flang-commits
mailing list