[flang-commits] [flang] [llvm] [flang][OpenMP] Add parsing and semantic checks for USES_ALLOCATORS (PR #213955)
Sairudra More via flang-commits
flang-commits at lists.llvm.org
Tue Aug 11 07:16:56 PDT 2026
https://github.com/Saieiei updated https://github.com/llvm/llvm-project/pull/213955
>From e4c3da62f875267e027499f4250710cce0ba22d1 Mon Sep 17 00:00:00 2001
From: Sairudra More <sairudra60 at gmail.com>
Date: Tue, 4 Aug 2026 08:43:09 -0500
Subject: [PATCH 1/7] [flang][OpenMP] Add parsing and semantic checks for
USES_ALLOCATORS
Add parse-tree, parser, unparser, and semantic support for the
USES_ALLOCATORS clause, including allocator traits and version-aware
handling of the deprecated comma-separated list syntax.
In OpenMP 6.0 mode, predefined allocator names are made available by
name without an additional local handle-kind check, matching the
specification's predefined-name rule. Diagnose the unsupported
multiple-specification form and keep lowering explicitly
unimplemented.
Add parser, semantics, version, kind, module, and lowering-NYI tests,
and document the supported syntax boundaries.
---
flang/docs/OpenMPSupport.md | 4 +-
flang/include/flang/Parser/dump-parse-tree.h | 5 +
flang/include/flang/Parser/parse-tree.h | 39 +++
.../flang/Semantics/openmp-modifiers.h | 2 +
flang/lib/Lower/OpenMP/Clauses.cpp | 26 +-
flang/lib/Parser/openmp-parsers.cpp | 64 ++++
flang/lib/Parser/unparse.cpp | 31 ++
flang/lib/Semantics/check-omp-loop.cpp | 10 +-
flang/lib/Semantics/check-omp-structure.cpp | 326 ++++++++++++++++++
flang/lib/Semantics/check-omp-structure.h | 6 +
flang/lib/Semantics/openmp-modifiers.cpp | 33 ++
.../OpenMP/Todo/target-uses-allocators.f90 | 42 +++
.../OpenMP/uses-allocators-bad-syntax.f90 | 35 ++
flang/test/Parser/OpenMP/uses-allocators.f90 | 118 +++++++
.../OpenMP/uses-allocators-handle-kind.f90 | 32 ++
.../OpenMP/uses-allocators-no-omp-lib.f90 | 16 +
.../OpenMP/uses-allocators-user-module.f90 | 29 ++
.../OpenMP/uses-allocators-version50.f90 | 56 +++
.../OpenMP/uses-allocators-version51.f90 | 45 +++
.../OpenMP/uses-allocators-version60.f90 | 99 ++++++
.../test/Semantics/OpenMP/uses-allocators.f90 | 314 +++++++++++++++++
llvm/include/llvm/Frontend/OpenMP/OMP.td | 1 +
22 files changed, 1327 insertions(+), 6 deletions(-)
create mode 100644 flang/test/Lower/OpenMP/Todo/target-uses-allocators.f90
create mode 100644 flang/test/Parser/OpenMP/uses-allocators-bad-syntax.f90
create mode 100644 flang/test/Parser/OpenMP/uses-allocators.f90
create mode 100644 flang/test/Semantics/OpenMP/uses-allocators-handle-kind.f90
create mode 100644 flang/test/Semantics/OpenMP/uses-allocators-no-omp-lib.f90
create mode 100644 flang/test/Semantics/OpenMP/uses-allocators-user-module.f90
create mode 100644 flang/test/Semantics/OpenMP/uses-allocators-version50.f90
create mode 100644 flang/test/Semantics/OpenMP/uses-allocators-version51.f90
create mode 100644 flang/test/Semantics/OpenMP/uses-allocators-version60.f90
create mode 100644 flang/test/Semantics/OpenMP/uses-allocators.f90
diff --git a/flang/docs/OpenMPSupport.md b/flang/docs/OpenMPSupport.md
index fd6731fd28a2b..755e6cac7169e 100644
--- a/flang/docs/OpenMPSupport.md
+++ b/flang/docs/OpenMPSupport.md
@@ -124,7 +124,7 @@ Parser/Semantics, MLIR, Lowering, or the OpenMPIRBuilder.
| allocate directive and allocate clause | <span class="part">partial</span> | | Semantics coverage exists in `flang/test/Semantics/OpenMP/allocate-directive.f90` and `flang/test/Semantics/OpenMP/allocate-clause01.f90`; lowering support expanded, with remaining TODO coverage (`flang/test/Lower/OpenMP/Todo/allocate-clause-align.f90`, `flang/test/Lower/OpenMP/Todo/allocate-clause-allocator.f90`). | [llvm/llvm-project#121356](https://github.com/llvm/llvm-project/pull/121356), [llvm/llvm-project#165719](https://github.com/llvm/llvm-project/pull/165719), [llvm/llvm-project#165865](https://github.com/llvm/llvm-project/pull/165865), [llvm/llvm-project#187167](https://github.com/llvm/llvm-project/pull/187167) |
| metadirective | <span class="part">partial</span> | | Semantics coverage exists in `flang/test/Semantics/OpenMP/metadirective-construct.f90`; lowering support exists for several construct-selector paths (`flang/test/Lower/OpenMP/metadirective-construct.f90`, `flang/test/Lower/OpenMP/metadirective-nothing.f90`), but some selector/variant paths remain TODO-tracked in lowering. | [llvm/llvm-project#159945](https://github.com/llvm/llvm-project/pull/159945), [llvm/llvm-project#193664](https://github.com/llvm/llvm-project/pull/193664), [llvm/llvm-project#194402](https://github.com/llvm/llvm-project/pull/194402), [llvm/llvm-project#194424](https://github.com/llvm/llvm-project/pull/194424) |
| support full defaultmap functionality | <span class="part">partial</span> | | Core coverage exists (`flang/test/Lower/OpenMP/defaultmap.f90`, `flang/test/Semantics/OpenMP/defaultmap-clause-v50.f90`), but lowering has known partial paths (for example defaultmap-firstprivate TODO tests). | [llvm/llvm-project#135226](https://github.com/llvm/llvm-project/pull/135226), [llvm/llvm-project#166715](https://github.com/llvm/llvm-project/pull/166715), [llvm/llvm-project#167806](https://github.com/llvm/llvm-project/pull/167806), [llvm/llvm-project#177389](https://github.com/llvm/llvm-project/pull/177389), [llvm/llvm-project#190764](https://github.com/llvm/llvm-project/pull/190764) |
-| clause: uses_allocators | <span class="part">partial</span> | | Parsed/checked in semantics allocator tests, but lowering has explicit TODO handling (`cp.processTODO<...UsesAllocators>` in `flang/lib/Lower/OpenMP/OpenMP.cpp`). | |
+| clause: uses_allocators | <span class="part">partial</span> | | Parsing and semantic checking are supported for the OpenMP 5.2 syntax and for the pre-5.2 comma-separated list syntax, the latter accepted in 5.0/5.1, accepted with a deprecation warning in 5.2, and removed and rejected in 6.0 and later (`flang/test/Parser/OpenMP/uses-allocators.f90`, `flang/test/Semantics/OpenMP/uses-allocators.f90`, `flang/test/Semantics/OpenMP/uses-allocators-version60.f90`); lowering is unimplemented and reports a `not yet implemented` diagnostic (`flang/test/Lower/OpenMP/Todo/target-uses-allocators.f90`). The OpenMP 6.0 form with more than one clause-argument-specification is diagnosed as not yet supported. | |
| clause: in_reduction | <span class="part">partial</span> | | Semantics and lowering coverage exists for several task/taskgroup/taskloop forms (`flang/test/Semantics/OpenMP/in-reduction.f90`, `flang/test/Lower/OpenMP/task-inreduction.f90`, `flang/test/Lower/OpenMP/taskloop-inreduction.f90`, `flang/test/Lower/OpenMP/taskgroup-task_reduction02.f90`); some target-related forms remain TODO (for example `flang/test/Lower/OpenMP/Todo/target-inreduction.f90`). | [llvm/llvm-project#139704](https://github.com/llvm/llvm-project/pull/139704), [llvm/llvm-project#205124](https://github.com/llvm/llvm-project/pull/205124) |
| user-defined mappers | <span class="good">done</span> | | Supported with semantics/lowering/transform coverage. | [llvm/llvm-project#140560](https://github.com/llvm/llvm-project/pull/140560), [llvm/llvm-project#163860](https://github.com/llvm/llvm-project/pull/163860), [llvm/llvm-project#167903](https://github.com/llvm/llvm-project/pull/167903), [llvm/llvm-project#179936](https://github.com/llvm/llvm-project/pull/179936), [llvm/llvm-project#189136](https://github.com/llvm/llvm-project/pull/189136) |
| map array-section with implicit mapper | <span class="part">partial</span> | | Mapper and map coverage exists (`flang/test/Lower/OpenMP/map-mapper.f90`, `flang/test/Lower/OpenMP/target-data-skip-mapper-calls.f90`), with remaining iterator/modifier gaps in lowering (`flang/lib/Lower/OpenMP/ClauseProcessor.cpp` TODOs). | [llvm/llvm-project#175133](https://github.com/llvm/llvm-project/pull/175133), [llvm/llvm-project#177389](https://github.com/llvm/llvm-project/pull/177389) |
@@ -214,7 +214,7 @@ Parser/Semantics, MLIR, Lowering, or the OpenMPIRBuilder.
| minus operator deprecation handling | <span class="part">partial</span> | | Partially handled in semantic diagnostics. | |
| linear clause syntax deprecation | <span class="none">unclaimed</span> | | Add semantic deprecation diagnostics with fix-it guidance and tests for accepted/deprecated spellings. | |
| map clause modifiers without commas (deprecation) | <span class="none">unclaimed</span> | | Add parser/semantics deprecation diagnostics for comma-less map modifiers and tests that verify warning text and accepted replacements. | |
-| uses_allocators list syntax (deprecation) | <span class="none">unclaimed</span> | | Add diagnostics for deprecated uses_allocators list syntax and ensure lowering still handles canonical replacements. | |
+| uses_allocators list syntax (deprecation) | <span class="part">partial</span> | | The comma-separated list syntax is accepted in OpenMP 5.0 and 5.1, accepted with a deprecation/portability warning in 5.2, and removed and rejected in 6.0 and later, where [6.0:B.2] removes all features deprecated in 5.0, 5.1 and 5.2 (`flang/test/Semantics/OpenMP/uses-allocators.f90`, `flang/test/Semantics/OpenMP/uses-allocators-version60.f90`); lowering of the clause is still unimplemented. | |
| default clause on metadirectives (deprecation) | <span class="none">unclaimed</span> | | Add dedicated metadirective deprecation diagnostics and tests for legacy/default-clause usage. | |
| destroy clause syntax on depobj (deprecation) | <span class="part">partial</span> | | Deprecation diagnostics are implemented (for example warning coverage in `flang/test/Semantics/OpenMP/depobj-construct-v52.f90`). | |
| source and sink task-dependence modifiers (deprecation) | <span class="part">partial</span> | | Deprecation diagnostics are implemented (for example warning coverage in `flang/test/Semantics/OpenMP/depobj-construct-v52.f90`). | |
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index b7d5634dafc2c..dc0fd9effe96e 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -690,6 +690,7 @@ class ParseTreeDumper {
NODE(parser, OmpMapTypeModifier)
NODE_ENUM(OmpMapTypeModifier, Value)
NODE(parser, OmpMatchClause)
+ NODE(parser, OmpMemspaceModifier)
NODE(parser, OmpMessageClause)
NODE(parser, OmpMetadirectiveDirective)
NODE(parser, OmpNoOpenMPClause)
@@ -760,6 +761,7 @@ class ParseTreeDumper {
NODE_ENUM(OmpThreadsetClause, ThreadsetPolicy)
NODE(parser, OmpToClause)
NODE(OmpToClause, Modifier)
+ NODE(parser, OmpTraitsArrayModifier)
NODE(parser, OmpTraitProperty)
NODE(parser, OmpTraitPropertyExtension)
NODE(OmpTraitPropertyExtension, Complex)
@@ -779,6 +781,9 @@ class ParseTreeDumper {
NODE(parser, OmpUnifiedSharedMemoryClause)
NODE(parser, OmpUpdateDependObjectsClause)
NODE(parser, OmpUseClause)
+ NODE(parser, OmpUsesAllocatorsClause)
+ NODE(OmpUsesAllocatorsClause, AllocatorSpec)
+ NODE(OmpUsesAllocatorsClause::AllocatorSpec, Modifier)
NODE(parser, OmpUtilityDirective)
NODE(parser, OmpVariableCategory)
NODE_ENUM(OmpVariableCategory, Value)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 6a21d5d8c79d7..a835f92c5e32e 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -4225,6 +4225,14 @@ struct OmpMapTypeModifier {
WRAPPER_CLASS_BOILERPLATE(OmpMapTypeModifier, Value);
};
+// Ref: [5.2:181-182]
+//
+// mem-space-modifier ->
+// MEMSPACE(memspace-handle) // since 5.2
+struct OmpMemspaceModifier {
+ WRAPPER_CLASS_BOILERPLATE(OmpMemspaceModifier, ScalarIntExpr);
+};
+
// Ref: [4.5:56-63], [5.0:101-109], [5.1:126-133], [5.2:252-254]
//
// modifier ->
@@ -4363,6 +4371,14 @@ struct OmpTaskDependenceType {
WRAPPER_CLASS_BOILERPLATE(OmpTaskDependenceType, Value);
};
+// Ref: [5.2:181-182]
+//
+// traits-array-modifier ->
+// TRAITS(traits-array) // since 5.2
+struct OmpTraitsArrayModifier {
+ WRAPPER_CLASS_BOILERPLATE(OmpTraitsArrayModifier, common::Indirection<Expr>);
+};
+
// Ref: [4.5:229-230], [5.0:324-325], [5.1:357-358], [5.2:161-162]
//
// variable-category ->
@@ -5165,6 +5181,29 @@ struct OmpUseClause {
WRAPPER_CLASS_BOILERPLATE(OmpUseClause, OmpObject);
};
+// Ref: [5.0:170-175], [5.1:197-203], [5.2:181-182]
+//
+// uses-allocators-clause ->
+// USES_ALLOCATORS(allocator[(traits-array)]
+// [, allocator[(traits-array)]]...) | // since 5.0, dep. 5.2
+// USES_ALLOCATORS([modifier...:] allocator) // since 5.2
+// modifier ->
+// mem-space-modifier |
+// traits-array-modifier // since 5.2
+struct OmpUsesAllocatorsClause {
+ struct AllocatorSpec {
+ TUPLE_CLASS_BOILERPLATE(AllocatorSpec);
+ MODIFIER_BOILERPLATE(OmpMemspaceModifier, OmpTraitsArrayModifier);
+ CharBlock source;
+ // The traits of the deprecated "allocator(traits-array)" form are stored
+ // as a traits-array modifier. The flag records which of the two surface
+ // syntaxes was written, which both the unparser and the deprecation
+ // diagnostic need.
+ std::tuple<MODIFIERS(), ScalarIntExpr, /*IsLegacySyntax=*/bool> t;
+ };
+ WRAPPER_CLASS_BOILERPLATE(OmpUsesAllocatorsClause, std::list<AllocatorSpec>);
+};
+
// OpenMP Clauses
struct OmpClause {
UNION_CLASS_BOILERPLATE(OmpClause);
diff --git a/flang/include/flang/Semantics/openmp-modifiers.h b/flang/include/flang/Semantics/openmp-modifiers.h
index 41a4a87f60949..79913fda05af1 100644
--- a/flang/include/flang/Semantics/openmp-modifiers.h
+++ b/flang/include/flang/Semantics/openmp-modifiers.h
@@ -96,6 +96,7 @@ DECLARE_DESCRIPTOR(parser::OmpLowerBound);
DECLARE_DESCRIPTOR(parser::OmpMapper);
DECLARE_DESCRIPTOR(parser::OmpMapType);
DECLARE_DESCRIPTOR(parser::OmpMapTypeModifier);
+DECLARE_DESCRIPTOR(parser::OmpMemspaceModifier);
DECLARE_DESCRIPTOR(parser::OmpOrderModifier);
DECLARE_DESCRIPTOR(parser::OmpOrderingModifier);
DECLARE_DESCRIPTOR(parser::OmpPreferType);
@@ -108,6 +109,7 @@ DECLARE_DESCRIPTOR(parser::OmpSelfModifier);
DECLARE_DESCRIPTOR(parser::OmpStepComplexModifier);
DECLARE_DESCRIPTOR(parser::OmpStepSimpleModifier);
DECLARE_DESCRIPTOR(parser::OmpTaskDependenceType);
+DECLARE_DESCRIPTOR(parser::OmpTraitsArrayModifier);
DECLARE_DESCRIPTOR(parser::OmpVariableCategory);
DECLARE_DESCRIPTOR(parser::OmpxHoldModifier);
diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp
index 0d315c218770a..ac68fbe6adaff 100644
--- a/flang/lib/Lower/OpenMP/Clauses.cpp
+++ b/flang/lib/Lower/OpenMP/Clauses.cpp
@@ -1811,8 +1811,30 @@ UseDevicePtr make(const parser::OmpClause::UseDevicePtr &inp,
UsesAllocators make(const parser::OmpClause::UsesAllocators &inp,
semantics::SemanticsContext &semaCtx) {
- // inp -> empty
- llvm_unreachable("Empty: uses_allocators");
+ // inp.v -> parser::OmpUsesAllocatorsClause
+ using AllocatorSpec = parser::OmpUsesAllocatorsClause::AllocatorSpec;
+
+ auto makeSpec = [&](const AllocatorSpec &spec) {
+ auto &mods = semantics::OmpGetModifiers(spec);
+ auto *memSpace =
+ semantics::OmpGetUniqueModifier<parser::OmpMemspaceModifier>(mods);
+ auto *traits =
+ semantics::OmpGetUniqueModifier<parser::OmpTraitsArrayModifier>(mods);
+
+ std::optional<UsesAllocators::TraitsArray> traitsArray;
+ if (traits) {
+ // Semantics has verified that the traits array is a named constant.
+ const parser::Name *name{parser::Unwrap<parser::Name>(traits->v.value())};
+ traitsArray = makeObject(DEREF(name), semaCtx);
+ }
+ return UsesAllocators::AllocatorSpec{
+ {/*MemSpace=*/maybeApplyToV(makeExprFn(semaCtx), memSpace),
+ /*TraitsArray=*/std::move(traitsArray),
+ /*Allocator=*/
+ makeExpr(std::get<parser::ScalarIntExpr>(spec.t), semaCtx)}};
+ };
+
+ return UsesAllocators{/*Allocators=*/makeList(inp.v.v, makeSpec)};
}
// Weak: empty
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 7c4e49a673bb0..be9751ba80ba9 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -989,6 +989,9 @@ struct OmpMapTypeModifierParser {
TYPE_PARSER(OmpMapTypeModifierParser{})
+TYPE_PARSER(construct<OmpMemspaceModifier>( //
+ "MEMSPACE" >> parenthesized(scalarIntExpr)))
+
TYPE_PARSER(construct<OmpOrderModifier>(
"REPRODUCIBLE" >> pure(OmpOrderModifier::Value::Reproducible) ||
"UNCONSTRAINED" >> pure(OmpOrderModifier::Value::Unconstrained)))
@@ -1044,6 +1047,9 @@ TYPE_PARSER(construct<OmpTaskDependenceType>(
"MUTEXINOUTSET" >> pure(OmpTaskDependenceType::Value::Mutexinoutset) ||
"OUT" >> pure(OmpTaskDependenceType::Value::Out)))
+TYPE_PARSER(construct<OmpTraitsArrayModifier>( //
+ "TRAITS" >> parenthesized(indirect(expr))))
+
TYPE_PARSER(construct<OmpVariableCategory>(
"AGGREGATE" >> pure(OmpVariableCategory::Value::Aggregate) ||
"ALL"_id >> pure(OmpVariableCategory::Value::All) ||
@@ -1193,6 +1199,12 @@ TYPE_PARSER(sourced(construct<OmpTaskReductionClause::Modifier>(
TYPE_PARSER(sourced(
construct<OmpThreadLimitClause::Modifier>(Parser<OmpDimsModifier>{})))
+TYPE_PARSER(sourced(construct<OmpUsesAllocatorsClause::AllocatorSpec::Modifier>(
+ sourced(construct<OmpUsesAllocatorsClause::AllocatorSpec::Modifier>(
+ Parser<OmpMemspaceModifier>{}) ||
+ construct<OmpUsesAllocatorsClause::AllocatorSpec::Modifier>(
+ Parser<OmpTraitsArrayModifier>{})))))
+
TYPE_PARSER(sourced(construct<OmpWhenClause::Modifier>( //
Parser<OmpContextSelector>{})))
@@ -1554,6 +1566,55 @@ TYPE_PARSER(construct<OmpSeverityClause>(
TYPE_PARSER(construct<OmpMessageClause>(expr))
TYPE_PARSER(construct<OmpHoldsClause>(indirect(expr)))
+
+// The deprecated "allocator[(traits-array)]" form is normalized into the 5.2
+// representation so that both surface syntaxes share one parse tree shape.
+static OmpUsesAllocatorsClause::AllocatorSpec makeLegacyAllocatorSpec(
+ Name &&name, common::Indirection<Expr> &&traits) {
+ using AllocatorSpec = OmpUsesAllocatorsClause::AllocatorSpec;
+ CharBlock traitsSource{traits.value().source};
+ AllocatorSpec::Modifier mod{OmpTraitsArrayModifier{std::move(traits)}};
+ mod.source = traitsSource;
+ std::list<AllocatorSpec::Modifier> mods;
+ mods.emplace_back(std::move(mod));
+
+ CharBlock allocatorSource{name.source};
+ Expr allocator{Designator{DataRef{std::move(name)}}};
+ allocator.source = allocatorSource;
+ return AllocatorSpec{std::move(mods),
+ ScalarIntExpr{IntExpr{common::Indirection<Expr>{std::move(allocator)}}},
+ /*IsLegacySyntax=*/true};
+}
+
+static OmpUsesAllocatorsClause::AllocatorSpec makeCanonicalAllocatorSpec(
+ std::list<OmpUsesAllocatorsClause::AllocatorSpec::Modifier> &&mods,
+ ScalarIntExpr &&allocator) {
+ return OmpUsesAllocatorsClause::AllocatorSpec{
+ std::move(mods), std::move(allocator), /*IsLegacySyntax=*/false};
+}
+
+// Parse "modifier...: allocator" first: a modifier list is followed by a
+// colon, which neither of the other two forms can contain.
+TYPE_PARSER(sourced( //
+ applyFunction<OmpUsesAllocatorsClause::AllocatorSpec>(
+ makeCanonicalAllocatorSpec,
+ nonemptyList(
+ Parser<OmpUsesAllocatorsClause::AllocatorSpec::Modifier>{}) /
+ ":",
+ scalarIntExpr) ||
+ // Parse the deprecated "allocator(traits-array)" before a bare allocator,
+ // because it is also syntactically an array element reference.
+ applyFunction<OmpUsesAllocatorsClause::AllocatorSpec>(
+ makeLegacyAllocatorSpec, Parser<Name>{},
+ parenthesized(indirect(expr))) ||
+ construct<OmpUsesAllocatorsClause::AllocatorSpec>(
+ pure<std::optional<
+ std::list<OmpUsesAllocatorsClause::AllocatorSpec::Modifier>>>(),
+ scalarIntExpr, pure(false))))
+
+TYPE_PARSER(construct<OmpUsesAllocatorsClause>(
+ nonemptyList(Parser<OmpUsesAllocatorsClause::AllocatorSpec>{})))
+
TYPE_PARSER(construct<OmpAbsentClause>(many(maybe(","_tok) >>
construct<llvm::omp::Directive>(unwrap(OmpDirectiveNameParser{})))))
TYPE_PARSER(construct<OmpContainsClause>(many(maybe(","_tok) >>
@@ -1777,6 +1838,9 @@ TYPE_PARSER( //
"USE_DEVICE_ADDR" >>
construct<OmpClause>(construct<OmpClause::UseDeviceAddr>(
parenthesized(Parser<OmpObjectList>{}))) ||
+ "USES_ALLOCATORS" >>
+ construct<OmpClause>(construct<OmpClause::UsesAllocators>(
+ parenthesized(Parser<OmpUsesAllocatorsClause>{}))) ||
"UNIFIED_ADDRESS" >>
construct<OmpClause>(construct<OmpClause::UnifiedAddress>(
maybe(parenthesized(scalarLogicalConstantExpr)))) ||
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index cde24b91fd246..816b91cf1628f 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2636,6 +2636,37 @@ class UnparseVisitor {
Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": ");
Walk(std::get<OmpObjectList>(x.t));
}
+ void Unparse(const OmpMemspaceModifier &x) {
+ Word("MEMSPACE(");
+ Walk(x.v);
+ Put(")");
+ }
+ void Unparse(const OmpTraitsArrayModifier &x) {
+ Word("TRAITS(");
+ Walk(x.v);
+ Put(")");
+ }
+ void Unparse(const OmpUsesAllocatorsClause &x) { Walk(x.v, ", "); }
+ void Unparse(const OmpUsesAllocatorsClause::AllocatorSpec &x) {
+ using Modifier = OmpUsesAllocatorsClause::AllocatorSpec::Modifier;
+ const auto &modifiers{std::get<std::optional<std::list<Modifier>>>(x.t)};
+ if (std::get<bool>(x.t)) {
+ // Unparse using the deprecated pre-5.2 syntax.
+ Walk(std::get<ScalarIntExpr>(x.t));
+ if (modifiers) {
+ for (const Modifier &m : *modifiers) {
+ if (auto *traits{std::get_if<OmpTraitsArrayModifier>(&m.u)}) {
+ Put("(");
+ Walk(traits->v);
+ Put(")");
+ }
+ }
+ }
+ } else {
+ Walk(modifiers, ": ");
+ Walk(std::get<ScalarIntExpr>(x.t));
+ }
+ }
void Unparse(const OmpTraitPropertyExtension::Complex &x) {
using PropList = std::list<common::Indirection<OmpTraitPropertyExtension>>;
Walk(std::get<OmpTraitPropertyName>(x.t));
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 65d097b5a31f7..e0c221a2ffa32 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -472,8 +472,14 @@ void OmpStructureChecker::CheckIterationVariables(
for (const parser::OmpClause &clause : spec.Clauses().v) {
llvm::omp::Clause clauseId{clause.Id()};
if (llvm::omp::isDataSharingAttributeClause(clauseId, version)) {
- for (const parser::OmpObject &object :
- parser::omp::GetOmpObjectList(clause)->v) {
+ // Not every data-sharing attribute clause takes an object list, e.g.
+ // USES_ALLOCATORS takes allocator specifications instead.
+ const parser::OmpObjectList *objects{
+ parser::omp::GetOmpObjectList(clause)};
+ if (!objects) {
+ continue;
+ }
+ for (const parser::OmpObject &object : objects->v) {
if (const Symbol *symbol{GetObjectSymbol(object, /*ultimate=*/true)}) {
auto maybeSource{parser::omp::GetObjectSource(object)};
assert(maybeSource && "Expecting object source");
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 7bd5f1720fb3c..880f9faf07754 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5388,6 +5388,332 @@ void OmpStructureChecker::Enter(const parser::OmpClause::UseDeviceAddr &x) {
}
}
+static constexpr std::string_view predefinedAllocatorNames[]{
+ "omp_default_mem_alloc", "omp_large_cap_mem_alloc", "omp_const_mem_alloc",
+ "omp_high_bw_mem_alloc", "omp_low_lat_mem_alloc", "omp_cgroup_mem_alloc",
+ "omp_pteam_mem_alloc", "omp_thread_mem_alloc"};
+
+static constexpr std::string_view predefinedMemSpaceNames[]{
+ "omp_default_mem_space", "omp_large_cap_mem_space", "omp_const_mem_space",
+ "omp_high_bw_mem_space", "omp_low_lat_mem_space"};
+
+// omp_null_allocator and omp_null_mem_space are not themselves predefined
+// handles, but [6.0:315-316] gives each its own allowance.
+static constexpr std::string_view nullAllocatorName[]{"omp_null_allocator"};
+static constexpr std::string_view nullMemSpaceName[]{"omp_null_mem_space"};
+
+// Whether the ultimate symbol is an entity of the intrinsic omp_lib module
+// shipped with the compiler, as opposed to a same-named entity of a
+// user-defined module. `IsIntrinsicModules()` on the module scope's parent is
+// the provenance test that mod-file.cpp and expression.cpp already use.
+static bool IsIntrinsicOmpLibEntity(const Symbol &ultimate) {
+ const Scope &scope{ultimate.owner()};
+ if (!scope.IsModule()) {
+ return false;
+ }
+ const Symbol *module{scope.symbol()};
+ return module && module->name() == "omp_lib" &&
+ scope.parent().IsIntrinsicModules();
+}
+
+// Recognition of a predefined allocator or memory space differs by version.
+//
+// [5.2:182] asks whether the allocator *is* a predefined allocator, so it
+// identifies the entity: a use-associated rename of the intrinsic omp_lib
+// entity still denotes it, while an unrelated declaration -- even one with the
+// same spelling, the same value, or in a user module named omp_lib -- does not.
+//
+// [6.0:315] instead asks whether the allocator is an identifier that *matches
+// the name of* a predefined allocator, which is a property of the identifier
+// written in the clause, not of the entity it resolves to. A local declaration
+// of a predefined spelling therefore qualifies, while a rename to some other
+// name does not, even when it denotes the intrinsic entity.
+static bool IsPredefinedHandle(const parser::Name &name,
+ llvm::ArrayRef<std::string_view> names, unsigned version) {
+ if (version >= 60) {
+ return llvm::is_contained(names, name.ToString());
+ }
+ if (const Symbol *symbol{name.symbol}) {
+ const Symbol &ultimate{symbol->GetUltimate()};
+ return IsIntrinsicOmpLibEntity(ultimate) &&
+ llvm::is_contained(names, ultimate.name().ToString());
+ }
+ return false;
+}
+
+// The integer kind of an OpenMP allocator or memory-space handle, which
+// omp_lib declares as c_intptr_t. iso_c_binding is an intrinsic module, so it
+// can be read on demand the way semantics.cpp reads its other builtin modules;
+// the kind is therefore available even when the source uses neither module.
+static std::optional<std::int64_t> GetOmpHandleKind(SemanticsContext &context) {
+ const Scope *scope{context.GetBuiltinModule("iso_c_binding")};
+ if (!scope) {
+ return std::nullopt;
+ }
+ const Symbol *kindSymbol{
+ scope->FindSymbol(SourceName{"c_intptr_t", std::strlen("c_intptr_t")})};
+ if (!kindSymbol) {
+ return std::nullopt;
+ }
+ const auto *object{
+ kindSymbol->GetUltimate().detailsIf<ObjectEntityDetails>()};
+ const auto *init{object ? &object->init() : nullptr};
+ return init && *init ? evaluate::ToInt64(**init) : std::nullopt;
+}
+
+// Whether `symbol` has the integer kind that omp_lib gives its handles. The
+// check is skipped for a non-integer allocator, whose type is diagnosed
+// separately, so that the two do not double up.
+static bool HasOmpHandleKind(
+ const Symbol &symbol, SemanticsContext &context, std::int64_t &expected) {
+ const DeclTypeSpec *type{symbol.GetUltimate().GetType()};
+ if (!type || !type->IsNumeric(TypeCategory::Integer)) {
+ return true;
+ }
+ auto want{GetOmpHandleKind(context)};
+ if (!want) {
+ return true;
+ }
+ expected = *want;
+ auto got{evaluate::ToInt64(type->numericTypeSpec().kind())};
+ return !got || *got == *want;
+}
+
+void OmpStructureChecker::CheckUsesAllocatorsSpec(
+ const parser::OmpUsesAllocatorsClause::AllocatorSpec &spec) {
+ unsigned version{context_.langOptions().OpenMPVersion};
+ bool isLegacySyntax{std::get<bool>(spec.t)};
+
+ // 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.
+ if (!isLegacySyntax) {
+ if (!OmpVerifyModifiers(spec, llvm::omp::OMPC_uses_allocators,
+ GetContext().clauseSource, context_)) {
+ return;
+ }
+ }
+
+ auto &modifiers{OmpGetModifiers(spec)};
+ const auto *memSpace{
+ OmpGetUniqueModifier<parser::OmpMemspaceModifier>(modifiers)};
+ const auto *traits{
+ OmpGetUniqueModifier<parser::OmpTraitsArrayModifier>(modifiers)};
+
+ const parser::Expr &allocatorExpr{
+ std::get<parser::ScalarIntExpr>(spec.t).thing.thing.value()};
+ parser::CharBlock allocatorSource{allocatorExpr.source};
+ const parser::Name *allocatorName{
+ parser::Unwrap<parser::Name>(allocatorExpr)};
+
+ // [5.2:182] The allocator expression must be a base language identifier.
+ if (!allocatorName) {
+ context_.Say(allocatorSource,
+ "The allocator in a USES_ALLOCATORS clause must be a base language identifier"_err_en_US);
+ return;
+ }
+
+ bool isPredefined{
+ IsPredefinedHandle(*allocatorName, predefinedAllocatorNames, version)};
+ // [6.0:315] The clause has no effect for an allocator argument value of
+ // omp_null_allocator, and [6.0:316] exempts it from the variable rule. It
+ // has no such allowance before 6.0.
+ bool isNullAllocator{version >= 60 &&
+ IsPredefinedHandle(*allocatorName, nullAllocatorName, version)};
+
+ // [5.2:182] If allocator is a predefined allocator, no modifiers may be
+ // specified. This also covers the pre-5.2 rule that predefined allocators
+ // cannot have traits specified.
+ if (isPredefined && (memSpace || traits)) {
+ context_.Say(allocatorSource,
+ "A predefined allocator '%s' in a USES_ALLOCATORS clause cannot have modifiers or traits specified"_err_en_US,
+ allocatorName->ToString());
+ }
+
+ if (!isPredefined && !isNullAllocator) {
+ // [5.2:182] If allocator is not a predefined allocator, it must be a
+ // variable. Before 6.0 this also rejects omp_null_allocator, which is a
+ // named constant and is not one of the predefined allocators.
+ const Symbol *symbol{allocatorName->symbol};
+ if (!symbol || !IsVariableName(*symbol)) {
+ context_.Say(allocatorSource,
+ "A non-predefined allocator '%s' in a USES_ALLOCATORS clause must be a variable"_err_en_US,
+ allocatorName->ToString());
+ } else if (std::int64_t kind{0};
+ !HasOmpHandleKind(*symbol, context_, kind)) {
+ // [5.2:181], [6.0:315] The allocator argument is an expression of
+ // allocator_handle type.
+ context_.Say(allocatorSource,
+ "The allocator '%s' in a USES_ALLOCATORS clause must be of type INTEGER(KIND=%jd), i.e. OMP_ALLOCATOR_HANDLE_KIND"_err_en_US,
+ allocatorName->ToString(), static_cast<std::intmax_t>(kind));
+ }
+ // [5.0:175], [5.1:203] Non-predefined allocators appearing in a
+ // uses_allocators clause must have traits specified. The requirement was
+ // removed in 5.2, where omitted traits mean an empty traits array.
+ if (version < 52 && !traits) {
+ context_.Say(allocatorSource,
+ "A non-predefined allocator '%s' in a USES_ALLOCATORS clause must have traits specified in OpenMP v%d.%d"_err_en_US,
+ allocatorName->ToString(), version / 10, version % 10);
+ }
+ }
+
+ // [5.2:182] The allocator argument must not appear in other data-sharing
+ // attribute clauses or data-mapping attribute clauses on the same construct.
+ if (const Symbol *symbol{allocatorName->symbol}) {
+ static const llvm::omp::Clause conflictingClauses[]{
+ llvm::omp::Clause::OMPC_firstprivate,
+ llvm::omp::Clause::OMPC_has_device_addr,
+ llvm::omp::Clause::OMPC_is_device_ptr,
+ llvm::omp::Clause::OMPC_map,
+ llvm::omp::Clause::OMPC_private,
+ };
+ const parser::OmpDirectiveSpecification &dirSpec{*dirStack_.back()};
+ for (llvm::omp::Clause id : conflictingClauses) {
+ const parser::OmpClause *found{parser::omp::FindClause(dirSpec, id)};
+ if (!found) {
+ continue;
+ }
+ if (const parser::OmpObjectList *objects{GetOmpObjectList(*found)}) {
+ for (const parser::OmpObject &object : objects->v) {
+ if (const Symbol *other{GetObjectSymbol(object, /*ultimate=*/true)};
+ other == &symbol->GetUltimate()) {
+ context_.Say(allocatorSource,
+ "An allocator in a USES_ALLOCATORS clause cannot also appear in the %s clause on the same construct"_err_en_US,
+ parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)));
+ }
+ }
+ }
+ }
+ }
+
+ if (memSpace) {
+ // [5.2:182] The memspace-handle argument for the mem-space modifier must
+ // be an identifier that matches one of the predefined memory space names.
+ // [6.0:315] additionally gives omp_null_mem_space the meaning of
+ // omp_default_mem_space, so it is accepted from 6.0 onwards.
+ const parser::Expr &memSpaceExpr{memSpace->v.thing.thing.value()};
+ parser::CharBlock memSpaceSource{OmpGetModifierSource(modifiers, memSpace)};
+ const parser::Name *memSpaceName{
+ parser::Unwrap<parser::Name>(memSpaceExpr)};
+ bool ok{memSpaceName &&
+ (IsPredefinedHandle(*memSpaceName, predefinedMemSpaceNames, version) ||
+ (version >= 60 &&
+ IsPredefinedHandle(*memSpaceName, nullMemSpaceName, version)))};
+ if (!ok) {
+ context_.Say(memSpaceSource,
+ "The MEMSPACE modifier must name a predefined memory space"_err_en_US);
+ }
+ }
+
+ if (traits) {
+ CheckUsesAllocatorsTraits(*traits,
+ isLegacySyntax ? allocatorSource
+ : OmpGetModifierSource(modifiers, traits));
+ }
+}
+
+void OmpStructureChecker::CheckUsesAllocatorsTraits(
+ const parser::OmpTraitsArrayModifier &traits, parser::CharBlock source) {
+ const parser::Expr &expr{traits.v.value()};
+ parser::CharBlock traitsSource{source.empty() ? expr.source : source};
+ const SomeExpr *value{GetExpr(context_, expr)};
+ if (!value) {
+ return;
+ }
+
+ const parser::Name *name{parser::Unwrap<parser::Name>(expr)};
+ const Symbol *symbol{name ? name->symbol : nullptr};
+
+ // [5.2:182] publishes one restriction for both languages: the traits
+ // argument must be a constant array, have constant values, and be defined in
+ // the same scope as the construct. [6.0:317] later splits that per language,
+ // keeping the same-scope requirement for C/C++ and stating the Fortran rule
+ // as a named constant of rank one. Follow the 6.0 clarification, so an
+ // otherwise valid host- or use-associated named constant is accepted.
+ if (!symbol) {
+ context_.Say(traitsSource,
+ "The traits array must be a named constant array"_err_en_US);
+ return;
+ }
+ if (value->Rank() != 1) {
+ context_.Say(traitsSource,
+ "The traits array '%s' must be a rank-one array"_err_en_US,
+ name->ToString());
+ }
+ if (!IsNamedConstant(symbol->GetUltimate()) ||
+ !evaluate::IsConstantExpr(*value)) {
+ context_.Say(traitsSource,
+ "The traits array '%s' must be a constant array with constant values"_err_en_US,
+ name->ToString());
+ }
+ const DeclTypeSpec *type{symbol->GetUltimate().GetType()};
+ const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr};
+ if (!derived || derived->name().ToString() != "omp_alloctrait") {
+ context_.Say(traitsSource,
+ "The traits array '%s' must be of type OMP_ALLOCTRAIT"_err_en_US,
+ name->ToString());
+ }
+}
+
+void OmpStructureChecker::Enter(const parser::OmpClause::UsesAllocators &x) {
+ // If the clause is not allowed, don't diagnose specific problems with it.
+ if (!IsAllowedClause(llvm::omp::Clause::OMPC_uses_allocators)) {
+ return;
+ }
+
+ unsigned version{context_.langOptions().OpenMPVersion};
+ const std::list<parser::OmpUsesAllocatorsClause::AllocatorSpec> &specs{x.v.v};
+
+ // Classify by the syntax each specification was written in, which the parse
+ // tree records, rather than by whether a modifier happens to be present.
+ bool anyLegacyItem{false}, anyModifierItem{false};
+ for (auto &spec : specs) {
+ if (std::get<bool>(spec.t)) {
+ anyLegacyItem = true;
+ } else if (OmpGetModifiers(spec)) {
+ anyModifierItem = true;
+ }
+ }
+ // Only the deprecated grammar separates specifications with commas, so any
+ // parseable list of them is that grammar. A comma list whose items use the
+ // 5.2 modifier syntax is expressible in neither grammar; it is treated as an
+ // approximation of the 6.0 multiple-specification form.
+ bool isLegacyList{anyLegacyItem || (specs.size() > 1 && !anyModifierItem)};
+
+ if (version >= 60 && isLegacyList) {
+ // [6.0:B.2] "All features deprecated in versions 5.0, 5.1 and 5.2 were
+ // removed", which includes the comma-separated list syntax deprecated by
+ // [5.2:181].
+ context_.Say(GetContext().clauseSource,
+ "The comma-separated list syntax for the USES_ALLOCATORS clause was deprecated in OpenMP 5.2 and removed in OpenMP 6.0, use 'USES_ALLOCATORS([TRAITS(traits):] allocator)' instead"_err_en_US);
+ return;
+ }
+
+ if (specs.size() > 1 && version >= 60) {
+ // [6.0:315] permits more than one clause-argument-specification, but only
+ // the single-specification form is implemented so far.
+ context_.Say(GetContext().clauseSource,
+ "Multiple allocator specifications in a USES_ALLOCATORS clause are not yet supported"_err_en_US);
+ return;
+ }
+
+ if (specs.size() > 1 && anyModifierItem) {
+ // [5.2:181] uses_allocators takes a single clause-argument-specification,
+ // and only the deprecated list syntax may repeat it.
+ context_.Say(GetContext().clauseSource,
+ "The USES_ALLOCATORS clause accepts a single allocator specification in OpenMP v%d.%d"_err_en_US,
+ version / 10, version % 10);
+ } else if (version >= 52 && isLegacyList) {
+ // [5.2:181] The comma-separated "allocator[(traits)]" list syntax has been
+ // deprecated.
+ context_.Say(GetContext().clauseSource,
+ "The comma-separated list syntax for the USES_ALLOCATORS clause has been deprecated in OpenMP 5.2, use 'USES_ALLOCATORS([TRAITS(traits):] allocator)' instead"_port_en_US);
+ }
+
+ for (auto &spec : specs) {
+ CheckUsesAllocatorsSpec(spec);
+ }
+}
+
void OmpStructureChecker::Enter(const parser::OmpClause::IsDevicePtr &x) {
SymbolSourceMap currSymbols;
GetSymbolsInObjectList(x.v, currSymbols);
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 4acec2ad05ba1..53d47eaacda24 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -241,6 +241,7 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
void Enter(const parser::OmpClause::UpdateDependObjects &x);
void Enter(const parser::OmpClause::UseDeviceAddr &x);
void Enter(const parser::OmpClause::UseDevicePtr &x);
+ void Enter(const parser::OmpClause::UsesAllocators &x);
void Enter(const parser::OmpClause::When &x);
private:
@@ -424,6 +425,11 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
const parser::OmpAllocateDirective &x, bool isExecutable);
void CheckExecutableAllocateDirective(const parser::OmpAllocateDirective &x);
+ void CheckUsesAllocatorsSpec(
+ const parser::OmpUsesAllocatorsClause::AllocatorSpec &spec);
+ void CheckUsesAllocatorsTraits(
+ const parser::OmpTraitsArrayModifier &traits, parser::CharBlock source);
+
void CheckIteratorRange(const parser::OmpIteratorSpecifier &x);
void CheckIteratorModifier(const parser::OmpIterator &x);
diff --git a/flang/lib/Semantics/openmp-modifiers.cpp b/flang/lib/Semantics/openmp-modifiers.cpp
index 293136d3649ad..d4a2bddc2c3c6 100644
--- a/flang/lib/Semantics/openmp-modifiers.cpp
+++ b/flang/lib/Semantics/openmp-modifiers.cpp
@@ -556,6 +556,22 @@ const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpMapTypeModifier>() {
return desc;
}
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpMemspaceModifier>() {
+ static const OmpModifierDescriptor desc{
+ /*name=*/"mem-space-modifier",
+ /*props=*/
+ {
+ {52, {OmpProperty::Unique}},
+ },
+ /*clauses=*/
+ {
+ {52, {Clause::OMPC_uses_allocators}},
+ },
+ };
+ return desc;
+}
+
template <>
const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpOrderModifier>() {
static const OmpModifierDescriptor desc{
@@ -756,6 +772,23 @@ const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpTaskDependenceType>() {
return desc;
}
+template <>
+const OmpModifierDescriptor &
+OmpGetDescriptor<parser::OmpTraitsArrayModifier>() {
+ static const OmpModifierDescriptor desc{
+ /*name=*/"traits-array-modifier",
+ /*props=*/
+ {
+ {52, {OmpProperty::Unique}},
+ },
+ /*clauses=*/
+ {
+ {52, {Clause::OMPC_uses_allocators}},
+ },
+ };
+ return desc;
+}
+
template <>
const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpVariableCategory>() {
static const OmpModifierDescriptor desc{
diff --git a/flang/test/Lower/OpenMP/Todo/target-uses-allocators.f90 b/flang/test/Lower/OpenMP/Todo/target-uses-allocators.f90
new file mode 100644
index 0000000000000..fbe2e9efe9ac3
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/target-uses-allocators.f90
@@ -0,0 +1,42 @@
+! REQUIRES: openmp_runtime
+! RUN: %not_todo_cmd %flang_fc1 -cpp -DBARE -emit-llvm %openmp_flags -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
+! RUN: %not_todo_cmd %flang_fc1 -cpp -DTRAITS -emit-llvm %openmp_flags -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
+! RUN: %not_todo_cmd %flang_fc1 -cpp -DMEMSPACE -emit-llvm %openmp_flags -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
+! RUN: %not_todo_cmd %flang_fc1 -cpp -DMEMSPACE_TRAITS -emit-llvm %openmp_flags -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
+! RUN: %not_todo_cmd %flang_fc1 -cpp -DTRAITS_MEMSPACE -emit-llvm %openmp_flags -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
+! RUN: %not_todo_cmd %flang_fc1 -cpp -DLEGACY -emit-llvm %openmp_flags -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
+
+! Every shape that semantics accepts must reach the established "not yet
+! implemented" lowering boundary for USES_ALLOCATORS, rather than aborting
+! while the clause is converted for lowering. Each shape needs its own
+! compilation because lowering stops at the first unimplemented clause.
+
+! CHECK: not yet implemented: USES_ALLOCATORS clause is not implemented yet
+program p
+ use omp_lib
+ integer(omp_allocator_handle_kind) :: my_alloc
+ type(omp_alloctrait), parameter :: tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+#ifdef BARE
+ !$omp target uses_allocators(my_alloc)
+#endif
+#ifdef TRAITS
+ !$omp target uses_allocators(traits(tr): my_alloc)
+#endif
+#ifdef MEMSPACE
+ !$omp target uses_allocators(memspace(omp_high_bw_mem_space): my_alloc)
+#endif
+#ifdef MEMSPACE_TRAITS
+ !$omp target uses_allocators(memspace(omp_const_mem_space), traits(tr): my_alloc)
+#endif
+#ifdef TRAITS_MEMSPACE
+ !$omp target uses_allocators(traits(tr), memspace(omp_const_mem_space): my_alloc)
+#endif
+#ifdef LEGACY
+ !$omp target uses_allocators(my_alloc(tr))
+#endif
+ x = 1
+ !$omp end target
+end program p
diff --git a/flang/test/Parser/OpenMP/uses-allocators-bad-syntax.f90 b/flang/test/Parser/OpenMP/uses-allocators-bad-syntax.f90
new file mode 100644
index 0000000000000..71317861883fb
--- /dev/null
+++ b/flang/test/Parser/OpenMP/uses-allocators-bad-syntax.f90
@@ -0,0 +1,35 @@
+! REQUIRES: openmp_runtime
+
+! RUN: %python %S/../../Semantics/test_errors.py %s %flang_fc1 %openmp_flags -fopenmp-version=52
+! RUN: %python %S/../../Semantics/test_errors.py %s %flang_fc1 %openmp_flags -fopenmp-version=60
+
+! Forms that the USES_ALLOCATORS parser does not accept.
+!
+! The first construct is a valid clause carrying no error annotation. It is the
+! reason this test cannot pass vacuously: a compiler without USES_ALLOCATORS
+! parser support does not parse it either, and the resulting unexpected
+! diagnostic makes the harness fail rather than silently agree.
+
+subroutine uses_allocators_bad_syntax
+ use omp_lib
+ integer(omp_allocator_handle_kind) :: a, b
+ integer :: x
+
+ ! Sentinel: the canonical OpenMP 5.2 form parses.
+ !$omp target uses_allocators(a)
+ x = 1
+ !$omp end target
+
+ ! The clause takes at least one allocator specification.
+ !ERROR: expected '='
+ !$omp target uses_allocators()
+ x = 2
+ !$omp end target
+
+ ! The OpenMP 6.0 semicolon-separated form for more than one
+ ! clause-argument-specification is not implemented.
+ !ERROR: expected ':'
+ !$omp target uses_allocators(a; b)
+ x = 3
+ !$omp end target
+end subroutine
diff --git a/flang/test/Parser/OpenMP/uses-allocators.f90 b/flang/test/Parser/OpenMP/uses-allocators.f90
new file mode 100644
index 0000000000000..42ac11512e9bf
--- /dev/null
+++ b/flang/test/Parser/OpenMP/uses-allocators.f90
@@ -0,0 +1,118 @@
+! REQUIRES: openmp_runtime
+
+! RUN: %flang_fc1 %openmp_flags -fopenmp-version=52 -fdebug-unparse-no-sema %s | FileCheck %s --check-prefix=UNPARSE
+! RUN: %flang_fc1 %openmp_flags -fopenmp-version=52 -fdebug-dump-parse-tree %s | FileCheck %s --check-prefix=PARSE-TREE
+
+! Both surface syntaxes of the USES_ALLOCATORS clause must round-trip through
+! the unparser, and the deprecated form must remain distinguishable from the
+! OpenMP 5.2 form in the parse tree.
+
+subroutine uses_allocators_syntax
+ use omp_lib
+ integer(omp_allocator_handle_kind) :: a, b
+ type(omp_alloctrait), parameter :: tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+ !$omp target uses_allocators(omp_default_mem_alloc)
+ x = 1
+ !$omp end target
+
+ !$omp target uses_allocators(traits(tr): a)
+ x = 2
+ !$omp end target
+
+ !$omp target uses_allocators(memspace(omp_default_mem_space): a)
+ x = 3
+ !$omp end target
+
+ !$omp target uses_allocators(memspace(omp_low_lat_mem_space), traits(tr): a)
+ x = 4
+ !$omp end target
+
+ !$omp target uses_allocators(traits(tr), memspace(omp_low_lat_mem_space): a)
+ x = 5
+ !$omp end target
+
+ !$omp target uses_allocators(a(tr))
+ x = 6
+ !$omp end target
+
+ !$omp target uses_allocators(a(tr), b(tr))
+ x = 7
+ !$omp end target
+end subroutine
+
+!UNPARSE: SUBROUTINE uses_allocators_syntax
+!UNPARSE: !$OMP TARGET USES_ALLOCATORS(omp_default_mem_alloc)
+!UNPARSE: !$OMP TARGET USES_ALLOCATORS(TRAITS(tr): a)
+!UNPARSE: !$OMP TARGET USES_ALLOCATORS(MEMSPACE(omp_default_mem_space): a)
+!UNPARSE: !$OMP TARGET USES_ALLOCATORS(MEMSPACE(omp_low_lat_mem_space), TRAITS(tr): a)
+!UNPARSE: !$OMP TARGET USES_ALLOCATORS(TRAITS(tr), MEMSPACE(omp_low_lat_mem_space): a)
+!UNPARSE: !$OMP TARGET USES_ALLOCATORS(a(tr))
+!UNPARSE: !$OMP TARGET USES_ALLOCATORS(a(tr), b(tr))
+
+! A bare allocator is the canonical 5.2 form: no modifier is recorded, and the
+! specification is not flagged as the legacy syntax.
+!PARSE-TREE: OmpClause -> UsesAllocators -> OmpUsesAllocatorsClause -> AllocatorSpec
+!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = '1_8'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'omp_default_mem_alloc'
+!PARSE-TREE-NEXT: bool = 'false'
+
+! TRAITS(tr): a
+!PARSE-TREE: OmpClause -> UsesAllocators -> OmpUsesAllocatorsClause -> AllocatorSpec
+!PARSE-TREE-NEXT: Modifier -> OmpTraitsArrayModifier -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'tr'
+!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'a'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'a'
+!PARSE-TREE-NEXT: bool = 'false'
+
+! MEMSPACE(omp_default_mem_space): a
+!PARSE-TREE: OmpClause -> UsesAllocators -> OmpUsesAllocatorsClause -> AllocatorSpec
+!PARSE-TREE-NEXT: Modifier -> OmpMemspaceModifier -> Scalar -> Integer -> Expr = '99_8'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'omp_default_mem_space'
+!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'a'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'a'
+!PARSE-TREE-NEXT: bool = 'false'
+
+! Both modifiers, in either order.
+!PARSE-TREE: OmpClause -> UsesAllocators -> OmpUsesAllocatorsClause -> AllocatorSpec
+!PARSE-TREE-NEXT: Modifier -> OmpMemspaceModifier -> Scalar -> Integer -> Expr = '4_8'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'omp_low_lat_mem_space'
+!PARSE-TREE-NEXT: Modifier -> OmpTraitsArrayModifier -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'tr'
+!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'a'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'a'
+!PARSE-TREE-NEXT: bool = 'false'
+
+!PARSE-TREE: OmpClause -> UsesAllocators -> OmpUsesAllocatorsClause -> AllocatorSpec
+!PARSE-TREE-NEXT: Modifier -> OmpTraitsArrayModifier -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'tr'
+!PARSE-TREE-NEXT: Modifier -> OmpMemspaceModifier -> Scalar -> Integer -> Expr = '4_8'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'omp_low_lat_mem_space'
+!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'a'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'a'
+!PARSE-TREE-NEXT: bool = 'false'
+
+! The deprecated "allocator(traits)" form is stored as a traits modifier, and
+! is flagged so that it unparses back to the syntax that was written.
+!PARSE-TREE: OmpClause -> UsesAllocators -> OmpUsesAllocatorsClause -> AllocatorSpec
+!PARSE-TREE-NEXT: Modifier -> OmpTraitsArrayModifier -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'tr'
+!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'a'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'a'
+!PARSE-TREE-NEXT: bool = 'true'
+
+! The deprecated syntax can list more than one allocator specification.
+!PARSE-TREE: OmpClause -> UsesAllocators -> OmpUsesAllocatorsClause -> AllocatorSpec
+!PARSE-TREE-NEXT: Modifier -> OmpTraitsArrayModifier -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'tr'
+!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'a'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'a'
+!PARSE-TREE-NEXT: bool = 'true'
+!PARSE-TREE-NEXT: AllocatorSpec
+!PARSE-TREE-NEXT: Modifier -> OmpTraitsArrayModifier -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'tr'
+!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'b'
+!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'b'
+!PARSE-TREE-NEXT: bool = 'true'
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-handle-kind.f90 b/flang/test/Semantics/OpenMP/uses-allocators-handle-kind.f90
new file mode 100644
index 0000000000000..b9c6a764c67f8
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/uses-allocators-handle-kind.f90
@@ -0,0 +1,32 @@
+! RUN: not %flang_fc1 -fopenmp -fopenmp-version=50 -fsyntax-only %s 2>&1 | FileCheck %s
+! RUN: not %flang_fc1 -fopenmp -fopenmp-version=51 -fsyntax-only %s 2>&1 | FileCheck %s
+! RUN: not %flang_fc1 -fopenmp -fopenmp-version=52 -fsyntax-only %s 2>&1 | FileCheck %s
+! RUN: not %flang_fc1 -fopenmp -fopenmp-version=60 -fsyntax-only %s 2>&1 | FileCheck %s
+
+! [5.2:181], [6.0:315] The allocator argument of USES_ALLOCATORS is an
+! expression of allocator_handle type, which omp_lib declares as c_intptr_t.
+! The rule holds whether or not the source uses omp_lib, so none of these
+! subroutines imports it. The expected kind is matched as a number rather than
+! spelled out, because it is the target's C_INTPTR_T kind.
+
+! CHECK: error: The allocator 'wrong_kind_alloc' in a USES_ALLOCATORS clause must be of type INTEGER(KIND={{[0-9]+}}), i.e. OMP_ALLOCATOR_HANDLE_KIND
+subroutine uses_allocators_wrong_handle_kind
+ ! Deliberately a kind that cannot be C_INTPTR_T on any supported target.
+ integer(kind=2) :: wrong_kind_alloc
+ integer :: x
+ !$omp target uses_allocators(wrong_kind_alloc)
+ x = 1
+ !$omp end target
+end subroutine
+
+! A REAL allocator is diagnosed for its type; the kind rule must not pile a
+! second diagnostic onto the same allocator.
+! CHECK: error: Must have INTEGER type, but is REAL(4)
+! CHECK-NOT: 'not_an_integer' in a USES_ALLOCATORS clause must be of type
+subroutine uses_allocators_real_allocator
+ real :: not_an_integer
+ integer :: x
+ !$omp target uses_allocators(not_an_integer)
+ x = 1
+ !$omp end target
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-no-omp-lib.f90 b/flang/test/Semantics/OpenMP/uses-allocators-no-omp-lib.f90
new file mode 100644
index 0000000000000..a420ba83badbd
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/uses-allocators-no-omp-lib.f90
@@ -0,0 +1,16 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=52
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=60
+
+! The allocator_handle kind is the target's C_INTPTR_T, so a correctly typed
+! allocator is accepted without importing omp_lib. This file deliberately does
+! not USE omp_lib and so needs no OpenMP runtime modules.
+
+subroutine uses_allocators_without_omp_lib
+ use iso_c_binding, only: c_intptr_t
+ integer(c_intptr_t) :: my_alloc
+ integer :: x
+
+ !$omp target uses_allocators(my_alloc)
+ x = 1
+ !$omp end target
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-user-module.f90 b/flang/test/Semantics/OpenMP/uses-allocators-user-module.f90
new file mode 100644
index 0000000000000..55dbb130f385f
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/uses-allocators-user-module.f90
@@ -0,0 +1,29 @@
+! RUN: not %flang_fc1 -fopenmp -fopenmp-version=50 -fsyntax-only %s 2>&1 | FileCheck %s
+! RUN: not %flang_fc1 -fopenmp -fopenmp-version=51 -fsyntax-only %s 2>&1 | FileCheck %s
+! RUN: not %flang_fc1 -fopenmp -fopenmp-version=52 -fsyntax-only %s 2>&1 | FileCheck %s
+
+! Before OpenMP 6.0, [5.2:182] asks whether the allocator *is* a predefined
+! allocator, so predefined treatment belongs to the entity of the intrinsic
+! omp_lib module. A user-defined module named omp_lib does not confer it, even
+! when its named constant has the right name and the right handle kind.
+!
+! This module shadows the intrinsic omp_lib for the whole file, so it is kept
+! in a test of its own rather than contaminating tests that need the real
+! intrinsic module.
+
+! CHECK: error: A non-predefined allocator 'omp_const_mem_alloc' in a USES_ALLOCATORS clause must be a variable
+
+module omp_lib
+ use iso_c_binding, only: c_intptr_t
+ integer, parameter :: omp_allocator_handle_kind = c_intptr_t
+ integer(omp_allocator_handle_kind), parameter :: omp_const_mem_alloc = 4242
+end module
+
+subroutine uses_allocators_user_omp_lib
+ use omp_lib
+ integer :: x
+
+ !$omp target uses_allocators(omp_const_mem_alloc)
+ x = 1
+ !$omp end target
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-version50.f90 b/flang/test/Semantics/OpenMP/uses-allocators-version50.f90
new file mode 100644
index 0000000000000..a8491fa764f81
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/uses-allocators-version50.f90
@@ -0,0 +1,56 @@
+! REQUIRES: openmp_runtime
+
+! RUN: %python %S/../test_errors.py %s %flang_fc1 %openmp_flags -fopenmp-version=50
+
+! In OpenMP 5.0 the USES_ALLOCATORS clause only accepts the
+! "allocator[(traits-array)]" list syntax, and [5.0:175] requires a
+! non-predefined allocator to specify traits.
+
+subroutine uses_allocators_50
+ use omp_lib
+ integer(omp_allocator_handle_kind) :: my_alloc, other_alloc
+ type(omp_alloctrait), parameter :: tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+ ! The list syntax is the only syntax in 5.0, so it is not diagnosed.
+ !$omp target uses_allocators(my_alloc(tr), other_alloc(tr))
+ x = 1
+ !$omp end target
+
+ !$omp target uses_allocators(omp_default_mem_alloc)
+ x = 2
+ !$omp end target
+
+ !ERROR: A non-predefined allocator 'my_alloc' in a USES_ALLOCATORS clause must have traits specified in OpenMP v5.0
+ !$omp target uses_allocators(my_alloc)
+ x = 3
+ !$omp end target
+
+ !WARNING: 'traits-array-modifier' modifier is not supported in OpenMP v5.0, try -fopenmp-version=52
+ !$omp target uses_allocators(traits(tr): my_alloc)
+ x = 4
+ !$omp end target
+
+ !WARNING: 'mem-space-modifier' modifier is not supported in OpenMP v5.0, try -fopenmp-version=52
+ !$omp target uses_allocators(memspace(omp_default_mem_space): my_alloc)
+ x = 5
+ !$omp end target
+
+ !ERROR: A non-predefined allocator 'omp_null_allocator' in a USES_ALLOCATORS clause must be a variable
+ !$omp target uses_allocators(omp_null_allocator(tr))
+ x = 6
+ !$omp end target
+end subroutine
+
+subroutine uses_allocators_50_rename
+ use omp_lib, only: renamed_predef => omp_const_mem_alloc
+ integer :: x
+
+ ! Predefined recognition follows the entity before 6.0, so a rename of the
+ ! intrinsic omp_lib allocator is still predefined and needs no traits. The
+ ! same rename is rejected at 6.0; see uses-allocators-version60.f90.
+ !$omp target uses_allocators(renamed_predef)
+ x = 1
+ !$omp end target
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-version51.f90 b/flang/test/Semantics/OpenMP/uses-allocators-version51.f90
new file mode 100644
index 0000000000000..921eb273df6a0
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/uses-allocators-version51.f90
@@ -0,0 +1,45 @@
+! REQUIRES: openmp_runtime
+
+! RUN: %python %S/../test_errors.py %s %flang_fc1 %openmp_flags -fopenmp-version=51
+
+! OpenMP 5.1 keeps the pre-5.2 USES_ALLOCATORS syntax and the [5.1:203]
+! requirement that a non-predefined allocator specifies traits.
+
+subroutine uses_allocators_51
+ use omp_lib
+ integer(omp_allocator_handle_kind) :: my_alloc, other_alloc
+ type(omp_alloctrait), parameter :: tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+ !$omp target uses_allocators(my_alloc(tr), other_alloc(tr))
+ x = 1
+ !$omp end target
+
+ !ERROR: A non-predefined allocator 'my_alloc' in a USES_ALLOCATORS clause must have traits specified in OpenMP v5.1
+ !$omp target uses_allocators(my_alloc)
+ x = 2
+ !$omp end target
+
+ !WARNING: 'traits-array-modifier' modifier is not supported in OpenMP v5.1, try -fopenmp-version=52
+ !$omp target uses_allocators(traits(tr): my_alloc)
+ x = 3
+ !$omp end target
+
+ !ERROR: A predefined allocator 'omp_default_mem_alloc' in a USES_ALLOCATORS clause cannot have modifiers or traits specified
+ !$omp target uses_allocators(omp_default_mem_alloc(tr))
+ x = 4
+ !$omp end target
+end subroutine
+
+subroutine uses_allocators_51_rename
+ use omp_lib, only: renamed_predef => omp_const_mem_alloc
+ integer :: x
+
+ ! Predefined recognition follows the entity before 6.0, so a rename of the
+ ! intrinsic omp_lib allocator is still predefined and needs no traits. The
+ ! same rename is rejected at 6.0; see uses-allocators-version60.f90.
+ !$omp target uses_allocators(renamed_predef)
+ x = 1
+ !$omp end target
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-version60.f90 b/flang/test/Semantics/OpenMP/uses-allocators-version60.f90
new file mode 100644
index 0000000000000..9b64f53be8a8f
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/uses-allocators-version60.f90
@@ -0,0 +1,99 @@
+! REQUIRES: openmp_runtime
+
+! RUN: %python %S/../test_errors.py %s %flang_fc1 %openmp_flags -fopenmp-version=60
+
+! OpenMP Version 6.0, Section 8.8: uses_allocators clause.
+! 6.0 restates the allocator rule in terms of the *name* of a predefined
+! allocator, gives omp_null_allocator and omp_null_mem_space their own
+! allowances, and permits more than one clause-argument-specification.
+! [6.0:B.2] also removes every feature deprecated in 5.0, 5.1 and 5.2, which
+! includes the comma-separated list syntax deprecated by [5.2:181].
+
+subroutine uses_allocators_v60_null
+ use omp_lib
+ integer(omp_allocator_handle_kind) :: my_alloc
+ integer :: x
+
+ ! [6.0:315] The clause has no effect for an allocator argument value of
+ ! omp_null_allocator, and [6.0:316] exempts it from the variable rule.
+ !$omp target uses_allocators(omp_null_allocator)
+ x = 1
+ !$omp end target
+
+ ! [6.0:315] omp_null_mem_space means omp_default_mem_space here, so it is
+ ! accepted even though it is not one of the five predefined memory spaces.
+ !$omp target uses_allocators(memspace(omp_null_mem_space): my_alloc)
+ x = 2
+ !$omp end target
+end subroutine
+
+subroutine uses_allocators_v60_name_matching
+ use omp_lib, only: omp_allocator_handle_kind
+ ! A local named constant that shadows a predefined allocator's spelling.
+ integer(omp_allocator_handle_kind), parameter :: omp_const_mem_alloc = 999
+ integer :: x
+
+ ! [6.0:315] matches the *name* of a predefined allocator, so this shadowing
+ ! declaration is treated as predefined and needs no variable. Before 6.0 the
+ ! same program is rejected; see uses-allocators.f90.
+ !$omp target uses_allocators(omp_const_mem_alloc)
+ x = 1
+ !$omp end target
+end subroutine
+
+subroutine uses_allocators_v60_rename
+ use omp_lib, only: renamed => omp_const_mem_alloc
+ integer :: x
+
+ ! [6.0:315] asks whether the identifier in the clause matches the name of a
+ ! predefined allocator. 'renamed' does not, so it is a non-predefined
+ ! allocator, and being a named constant it violates the variable rule. The
+ ! same rename is accepted at 5.0-5.2, where the entity is what matters; see
+ ! uses-allocators.f90.
+ !ERROR: A non-predefined allocator 'renamed' in a USES_ALLOCATORS clause must be a variable
+ !$omp target uses_allocators(renamed)
+ x = 1
+ !$omp end target
+end subroutine
+
+subroutine uses_allocators_v60_legacy_removed
+ use omp_lib
+ integer(omp_allocator_handle_kind) :: my_alloc, other_alloc
+ type(omp_alloctrait), parameter :: tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+ ! [6.0:B.2] The comma-separated list syntax was deprecated in 5.2 and is
+ ! removed in 6.0.
+ !ERROR: The comma-separated list syntax for the USES_ALLOCATORS clause was deprecated in OpenMP 5.2 and removed in OpenMP 6.0, use 'USES_ALLOCATORS([TRAITS(traits):] allocator)' instead
+ !$omp target uses_allocators(my_alloc, other_alloc)
+ x = 1
+ !$omp end target
+
+ !ERROR: The comma-separated list syntax for the USES_ALLOCATORS clause was deprecated in OpenMP 5.2 and removed in OpenMP 6.0, use 'USES_ALLOCATORS([TRAITS(traits):] allocator)' instead
+ !$omp target uses_allocators(my_alloc(tr))
+ x = 2
+ !$omp end target
+end subroutine
+
+subroutine uses_allocators_v60_multiple
+ use omp_lib
+ integer(omp_allocator_handle_kind) :: my_alloc, other_alloc
+ type(omp_alloctrait), parameter :: tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+ ! [6.0:315] permits more than one clause-argument-specification. The
+ ! semicolon-separated grammar is not parsed at all (see
+ ! flang/test/Parser/OpenMP/uses-allocators-bad-syntax.f90); this shape is
+ ! neither grammar, and reaching it reports the implementation gap.
+ !ERROR: Multiple allocator specifications in a USES_ALLOCATORS clause are not yet supported
+ !$omp target uses_allocators(traits(tr): my_alloc, traits(tr): other_alloc)
+ x = 1
+ !$omp end target
+
+ ! A single specification is still accepted.
+ !$omp target uses_allocators(traits(tr): my_alloc)
+ x = 2
+ !$omp end target
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/uses-allocators.f90 b/flang/test/Semantics/OpenMP/uses-allocators.f90
new file mode 100644
index 0000000000000..1bbb09d16062c
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/uses-allocators.f90
@@ -0,0 +1,314 @@
+! REQUIRES: openmp_runtime
+
+! RUN: %python %S/../test_errors.py %s %flang_fc1 %openmp_flags -fopenmp-version=52
+
+! OpenMP Version 5.2, Section 6.8: uses_allocators clause.
+! Each negative case is paired with the nearest valid case so that the checks
+! cannot reject conforming code.
+
+module uses_allocators_traits_module
+ use omp_lib
+ type(omp_alloctrait), parameter :: module_tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+end module
+
+subroutine uses_allocators_ok
+ use omp_lib
+ integer(omp_allocator_handle_kind) :: my_alloc
+ type(omp_alloctrait), parameter :: tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+ ! A predefined allocator without modifiers.
+ !$omp target uses_allocators(omp_default_mem_alloc)
+ x = 1
+ !$omp end target
+
+ ! Since 5.2, a non-predefined allocator may omit its traits.
+ !$omp target uses_allocators(my_alloc)
+ x = 2
+ !$omp end target
+
+ !$omp target uses_allocators(traits(tr): my_alloc)
+ x = 3
+ !$omp end target
+
+ !$omp target uses_allocators(memspace(omp_high_bw_mem_space): my_alloc)
+ x = 4
+ !$omp end target
+
+ !$omp target uses_allocators(memspace(omp_const_mem_space), traits(tr): my_alloc)
+ x = 5
+ !$omp end target
+
+ ! The clause is repeatable on TARGET.
+ !$omp target uses_allocators(my_alloc) uses_allocators(omp_const_mem_alloc)
+ x = 6
+ !$omp end target
+
+ ! The clause reaches the TARGET leaf of a combined construct.
+ !$omp target teams uses_allocators(traits(tr): my_alloc)
+ x = 7
+ !$omp end target teams
+end subroutine
+
+subroutine uses_allocators_loop_construct
+ use omp_lib
+ integer(omp_allocator_handle_kind) :: my_alloc
+ type(omp_alloctrait), parameter :: tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: i, x(10)
+
+ ! USES_ALLOCATORS is a data-sharing attribute clause that carries allocator
+ ! specifications rather than an object list, so the loop-construct checks
+ ! must not treat it as an object-list clause.
+ !$omp target teams distribute parallel do uses_allocators(traits(tr): my_alloc)
+ do i = 1, 10
+ x(i) = i
+ end do
+
+ !$omp target simd uses_allocators(my_alloc)
+ do i = 1, 10
+ x(i) = i
+ end do
+end subroutine
+
+subroutine uses_allocators_errors
+ use omp_lib
+ integer(omp_allocator_handle_kind) :: my_alloc, other_alloc
+ integer(omp_memspace_handle_kind) :: my_space
+ type(omp_alloctrait), parameter :: tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ type(omp_alloctrait) :: nonconst(1)
+ type(omp_alloctrait), parameter :: tr2(1,1) = &
+ reshape([omp_alloctrait(omp_atk_alignment, 64)], [1,1])
+ real, parameter :: notatrait(1) = [1.0]
+ real :: notaninteger
+ integer :: x
+
+ ! The allocator must be a base language identifier.
+ !ERROR: The allocator in a USES_ALLOCATORS clause must be a base language identifier
+ !$omp target uses_allocators(traits(tr): my_alloc + 1)
+ x = 1
+ !$omp end target
+
+ ! A predefined allocator cannot have modifiers.
+ !ERROR: A predefined allocator 'omp_default_mem_alloc' in a USES_ALLOCATORS clause cannot have modifiers or traits specified
+ !$omp target uses_allocators(traits(tr): omp_default_mem_alloc)
+ x = 2
+ !$omp end target
+
+ !ERROR: A predefined allocator 'omp_const_mem_alloc' in a USES_ALLOCATORS clause cannot have modifiers or traits specified
+ !$omp target uses_allocators(memspace(omp_const_mem_space): omp_const_mem_alloc)
+ x = 3
+ !$omp end target
+
+ ! omp_null_allocator is a named constant and is not a predefined allocator.
+ !ERROR: A non-predefined allocator 'omp_null_allocator' in a USES_ALLOCATORS clause must be a variable
+ !$omp target uses_allocators(omp_null_allocator)
+ x = 4
+ !$omp end target
+
+ ! The allocator must be a scalar integer.
+ !ERROR: Must have INTEGER type, but is REAL(4)
+ !$omp target uses_allocators(notaninteger)
+ x = 5
+ !$omp end target
+
+ ! The memspace-handle must name a predefined memory space.
+ !ERROR: The MEMSPACE modifier must name a predefined memory space
+ !$omp target uses_allocators(memspace(omp_null_mem_space): my_alloc)
+ x = 6
+ !$omp end target
+
+ !ERROR: The MEMSPACE modifier must name a predefined memory space
+ !$omp target uses_allocators(memspace(my_space): my_alloc)
+ x = 7
+ !$omp end target
+
+ ! The traits array must be a constant array of OMP_ALLOCTRAIT.
+ !ERROR: The traits array 'nonconst' must be a constant array with constant values
+ !$omp target uses_allocators(traits(nonconst): my_alloc)
+ x = 8
+ !$omp end target
+
+ !ERROR: The traits array 'tr2' must be a rank-one array
+ !$omp target uses_allocators(traits(tr2): my_alloc)
+ x = 9
+ !$omp end target
+
+ !ERROR: The traits array 'notatrait' must be of type OMP_ALLOCTRAIT
+ !$omp target uses_allocators(traits(notatrait): my_alloc)
+ x = 10
+ !$omp end target
+
+ ! The allocator must not appear in a data-sharing or data-mapping clause.
+ !ERROR: An allocator in a USES_ALLOCATORS clause cannot also appear in the PRIVATE clause on the same construct
+ !ERROR: Variable 'my_alloc' may not appear on both MAP and PRIVATE clauses on a TARGET construct
+ !$omp target uses_allocators(traits(tr): my_alloc) private(my_alloc)
+ x = 11
+ !$omp end target
+
+ !ERROR: An allocator in a USES_ALLOCATORS clause cannot also appear in the MAP clause on the same construct
+ !$omp target map(tofrom: my_alloc) uses_allocators(traits(tr): my_alloc)
+ x = 12
+ !$omp end target
+
+ ! A modifier may appear at most once in one allocator specification.
+ !ERROR: 'traits-array-modifier' modifier cannot occur multiple times
+ !$omp target uses_allocators(traits(tr), traits(tr): my_alloc)
+ x = 13
+ !$omp end target
+
+ !ERROR: 'mem-space-modifier' modifier cannot occur multiple times
+ !$omp target uses_allocators(memspace(omp_const_mem_space), memspace(omp_high_bw_mem_space): my_alloc)
+ x = 131
+ !$omp end target
+
+ ! OpenMP 5.2 allows a single allocator specification.
+ !ERROR: The USES_ALLOCATORS clause accepts a single allocator specification in OpenMP v5.2
+ !$omp target uses_allocators(traits(tr): my_alloc, traits(tr): other_alloc)
+ x = 14
+ !$omp end target
+
+ ! The clause is not allowed on a non-TARGET construct.
+ !ERROR: USES_ALLOCATORS clause is not allowed on PARALLEL directive
+ !$omp parallel uses_allocators(my_alloc)
+ x = 15
+ !$omp end parallel
+end subroutine
+
+subroutine uses_allocators_deprecated
+ use omp_lib
+ integer(omp_allocator_handle_kind) :: my_alloc, other_alloc
+ type(omp_alloctrait), parameter :: tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+ ! [5.2:181] The "allocator[(traits)]" list syntax is deprecated, but it is
+ ! still accepted.
+ !PORTABILITY: The comma-separated list syntax for the USES_ALLOCATORS clause has been deprecated in OpenMP 5.2, use 'USES_ALLOCATORS([TRAITS(traits):] allocator)' instead
+ !$omp target uses_allocators(my_alloc(tr))
+ x = 1
+ !$omp end target
+
+ !PORTABILITY: The comma-separated list syntax for the USES_ALLOCATORS clause has been deprecated in OpenMP 5.2, use 'USES_ALLOCATORS([TRAITS(traits):] allocator)' instead
+ !$omp target uses_allocators(my_alloc(tr), other_alloc(tr))
+ x = 2
+ !$omp end target
+
+ ! A bare comma list is also the deprecated syntax.
+ !PORTABILITY: The comma-separated list syntax for the USES_ALLOCATORS clause has been deprecated in OpenMP 5.2, use 'USES_ALLOCATORS([TRAITS(traits):] allocator)' instead
+ !$omp target uses_allocators(my_alloc, other_alloc)
+ x = 3
+ !$omp end target
+
+ ! A single bare allocator is the canonical 5.2 syntax and is not deprecated.
+ !$omp target uses_allocators(my_alloc)
+ x = 4
+ !$omp end target
+end subroutine
+
+subroutine uses_allocators_traits_association
+ use omp_lib
+ use uses_allocators_traits_module
+ integer(omp_allocator_handle_kind) :: my_alloc
+ integer :: x
+
+ ! [5.2:182] pairs the constant-array requirement with a "same scope as the
+ ! construct" requirement, which [6.0:317] keeps only for C/C++ while stating
+ ! the Fortran rule as a named constant of rank one. A use-associated named
+ ! constant therefore satisfies the Fortran rule.
+ !$omp target uses_allocators(traits(module_tr): my_alloc)
+ x = 1
+ !$omp end target
+
+ ! The same holds for a host-associated named constant.
+ call inner
+contains
+ subroutine inner
+ !$omp target uses_allocators(traits(module_tr): my_alloc)
+ x = 2
+ !$omp end target
+ end subroutine
+end subroutine
+
+subroutine uses_allocators_conflicting_clauses
+ use omp_lib
+ ! Each case uses its own allocator so that the once-per-symbol
+ ! "both MAP and ..." diagnostic of TARGET does not interleave with them.
+ integer(omp_allocator_handle_kind) :: fp_alloc, idp_alloc, hda_alloc
+ type(omp_alloctrait), parameter :: tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+ !ERROR: An allocator in a USES_ALLOCATORS clause cannot also appear in the FIRSTPRIVATE clause on the same construct
+ !$omp target uses_allocators(traits(tr): fp_alloc) firstprivate(fp_alloc)
+ x = 1
+ !$omp end target
+
+ !ERROR: An allocator in a USES_ALLOCATORS clause cannot also appear in the IS_DEVICE_PTR clause on the same construct
+ !ERROR: Variable 'idp_alloc' in IS_DEVICE_PTR clause must be of type C_PTR
+ !$omp target uses_allocators(traits(tr): idp_alloc) is_device_ptr(idp_alloc)
+ x = 2
+ !$omp end target
+
+ !ERROR: An allocator in a USES_ALLOCATORS clause cannot also appear in the HAS_DEVICE_ADDR clause on the same construct
+ !$omp target uses_allocators(traits(tr): hda_alloc) has_device_addr(hda_alloc)
+ x = 3
+ !$omp end target
+end subroutine
+
+subroutine uses_allocators_predefined_identity
+ use omp_lib
+ use omp_lib, only: renamed_predef => omp_default_mem_alloc
+ use omp_lib, only: renamed_space => omp_const_mem_space
+ integer(omp_allocator_handle_kind) :: my_alloc
+ integer(omp_allocator_handle_kind), parameter :: same_value_as_predef = 1
+ type(omp_alloctrait), parameter :: tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+ ! Before 6.0 predefined recognition follows the entity, so a rename of the
+ ! intrinsic omp_lib allocator still denotes it and is accepted bare. At 6.0
+ ! the clause identifier is what matters and the rename is rejected; see
+ ! uses-allocators-version60.f90.
+ !$omp target uses_allocators(renamed_predef)
+ x = 1
+ !$omp end target
+
+ ! ... and, being predefined, it still rejects modifiers.
+ !ERROR: A predefined allocator 'renamed_predef' in a USES_ALLOCATORS clause cannot have modifiers or traits specified
+ !$omp target uses_allocators(traits(tr): renamed_predef)
+ x = 2
+ !$omp end target
+
+ ! A named constant that merely shares a predefined allocator's numeric value
+ ! is neither the predefined entity nor a predefined name, so the variable
+ ! rule applies to it at every version.
+ !ERROR: A non-predefined allocator 'same_value_as_predef' in a USES_ALLOCATORS clause must be a variable
+ !$omp target uses_allocators(same_value_as_predef)
+ x = 3
+ !$omp end target
+
+ ! A renamed memory space likewise still denotes a predefined memory space.
+ !$omp target uses_allocators(memspace(renamed_space): my_alloc)
+ x = 4
+ !$omp end target
+end subroutine
+
+subroutine uses_allocators_predefined_shadow
+ use omp_lib, only: omp_allocator_handle_kind
+ ! A local named constant of the correct handle kind that shadows the spelling
+ ! of a predefined allocator. [5.2:182] asks whether the allocator *is* a
+ ! predefined allocator, which this is not, so the variable rule applies.
+ ! [6.0:315] matches the name instead and accepts it; see
+ ! uses-allocators-version60.f90.
+ integer(omp_allocator_handle_kind), parameter :: omp_const_mem_alloc = 999
+ integer :: x
+
+ !ERROR: A non-predefined allocator 'omp_const_mem_alloc' in a USES_ALLOCATORS clause must be a variable
+ !$omp target uses_allocators(omp_const_mem_alloc)
+ x = 1
+ !$omp end target
+end subroutine
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 037a506c8b175..0048e05d45e76 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -641,6 +641,7 @@ def OMPC_UseDevicePtr : Clause<[Spelling<"use_device_ptr">]> {
}
def OMPC_UsesAllocators : Clause<[Spelling<"uses_allocators">]> {
let clangClass = "OMPUsesAllocatorsClause";
+ let flangClass = "OmpUsesAllocatorsClause";
}
def OMPC_Weak : Clause<[Spelling<"weak">]> {
let clangClass = "OMPWeakClause";
>From 99f1585f999b76081e2c1a62c636859ac25b4966 Mon Sep 17 00:00:00 2001
From: Sairudra More <sairudra60 at gmail.com>
Date: Tue, 4 Aug 2026 12:46:10 -0500
Subject: [PATCH 2/7] [flang][OpenMP] Address review comments on
USES_ALLOCATORS
Rename the modifiers to match the names used in the specification:
OmpMemspaceModifier -> OmpMemSpace and OmpTraitsArrayModifier ->
OmpTraitsArray, with descriptor names "mem-space" and "traits-array".
The MEMSPACE and TRAITS keyword spellings are unchanged.
Widen the conflicting-clause check. It tested a fixed list of five
clauses via FindClause, which returns only the first clause of a given
id, so a conflict in a repeated clause was not diagnosed. It now scans
all clauses using isDataSharingAttributeClause plus map.
Also correct the OpenMPSupport.md description of the OpenMP 6.0
multiple-specification form, add a missing <cstring> include, and trim
comments that restate the code or justify decisions.
---
flang/docs/OpenMPSupport.md | 2 +-
flang/include/flang/Parser/dump-parse-tree.h | 4 +-
flang/include/flang/Parser/parse-tree.h | 21 +++----
.../flang/Semantics/openmp-modifiers.h | 4 +-
flang/lib/Lower/OpenMP/Clauses.cpp | 5 +-
flang/lib/Parser/openmp-parsers.cpp | 10 +--
flang/lib/Parser/unparse.cpp | 6 +-
flang/lib/Semantics/check-omp-structure.cpp | 62 +++++++++----------
flang/lib/Semantics/check-omp-structure.h | 2 +-
flang/lib/Semantics/openmp-modifiers.cpp | 9 ++-
.../OpenMP/Todo/target-uses-allocators.f90 | 3 +-
.../OpenMP/uses-allocators-bad-syntax.f90 | 7 +--
flang/test/Parser/OpenMP/uses-allocators.f90 | 18 +++---
.../OpenMP/uses-allocators-handle-kind.f90 | 4 +-
.../OpenMP/uses-allocators-user-module.f90 | 3 +-
.../OpenMP/uses-allocators-version50.f90 | 4 +-
.../OpenMP/uses-allocators-version51.f90 | 2 +-
.../test/Semantics/OpenMP/uses-allocators.f90 | 38 ++++++++++--
18 files changed, 113 insertions(+), 91 deletions(-)
diff --git a/flang/docs/OpenMPSupport.md b/flang/docs/OpenMPSupport.md
index 755e6cac7169e..1c532e1de7011 100644
--- a/flang/docs/OpenMPSupport.md
+++ b/flang/docs/OpenMPSupport.md
@@ -124,7 +124,7 @@ Parser/Semantics, MLIR, Lowering, or the OpenMPIRBuilder.
| allocate directive and allocate clause | <span class="part">partial</span> | | Semantics coverage exists in `flang/test/Semantics/OpenMP/allocate-directive.f90` and `flang/test/Semantics/OpenMP/allocate-clause01.f90`; lowering support expanded, with remaining TODO coverage (`flang/test/Lower/OpenMP/Todo/allocate-clause-align.f90`, `flang/test/Lower/OpenMP/Todo/allocate-clause-allocator.f90`). | [llvm/llvm-project#121356](https://github.com/llvm/llvm-project/pull/121356), [llvm/llvm-project#165719](https://github.com/llvm/llvm-project/pull/165719), [llvm/llvm-project#165865](https://github.com/llvm/llvm-project/pull/165865), [llvm/llvm-project#187167](https://github.com/llvm/llvm-project/pull/187167) |
| metadirective | <span class="part">partial</span> | | Semantics coverage exists in `flang/test/Semantics/OpenMP/metadirective-construct.f90`; lowering support exists for several construct-selector paths (`flang/test/Lower/OpenMP/metadirective-construct.f90`, `flang/test/Lower/OpenMP/metadirective-nothing.f90`), but some selector/variant paths remain TODO-tracked in lowering. | [llvm/llvm-project#159945](https://github.com/llvm/llvm-project/pull/159945), [llvm/llvm-project#193664](https://github.com/llvm/llvm-project/pull/193664), [llvm/llvm-project#194402](https://github.com/llvm/llvm-project/pull/194402), [llvm/llvm-project#194424](https://github.com/llvm/llvm-project/pull/194424) |
| support full defaultmap functionality | <span class="part">partial</span> | | Core coverage exists (`flang/test/Lower/OpenMP/defaultmap.f90`, `flang/test/Semantics/OpenMP/defaultmap-clause-v50.f90`), but lowering has known partial paths (for example defaultmap-firstprivate TODO tests). | [llvm/llvm-project#135226](https://github.com/llvm/llvm-project/pull/135226), [llvm/llvm-project#166715](https://github.com/llvm/llvm-project/pull/166715), [llvm/llvm-project#167806](https://github.com/llvm/llvm-project/pull/167806), [llvm/llvm-project#177389](https://github.com/llvm/llvm-project/pull/177389), [llvm/llvm-project#190764](https://github.com/llvm/llvm-project/pull/190764) |
-| clause: uses_allocators | <span class="part">partial</span> | | Parsing and semantic checking are supported for the OpenMP 5.2 syntax and for the pre-5.2 comma-separated list syntax, the latter accepted in 5.0/5.1, accepted with a deprecation warning in 5.2, and removed and rejected in 6.0 and later (`flang/test/Parser/OpenMP/uses-allocators.f90`, `flang/test/Semantics/OpenMP/uses-allocators.f90`, `flang/test/Semantics/OpenMP/uses-allocators-version60.f90`); lowering is unimplemented and reports a `not yet implemented` diagnostic (`flang/test/Lower/OpenMP/Todo/target-uses-allocators.f90`). The OpenMP 6.0 form with more than one clause-argument-specification is diagnosed as not yet supported. | |
+| clause: uses_allocators | <span class="part">partial</span> | | Parsing and semantic checking are supported for the OpenMP 5.2 syntax and for the pre-5.2 comma-separated list syntax, the latter accepted in 5.0/5.1, accepted with a deprecation warning in 5.2, and removed and rejected in 6.0 and later (`flang/test/Parser/OpenMP/uses-allocators.f90`, `flang/test/Semantics/OpenMP/uses-allocators.f90`, `flang/test/Semantics/OpenMP/uses-allocators-version60.f90`); lowering is unimplemented and reports a `not yet implemented` diagnostic (`flang/test/Lower/OpenMP/Todo/target-uses-allocators.f90`). Only a single clause-argument-specification is supported; the OpenMP 6.0 form that separates more than one specification with ';' is not parsed. | |
| clause: in_reduction | <span class="part">partial</span> | | Semantics and lowering coverage exists for several task/taskgroup/taskloop forms (`flang/test/Semantics/OpenMP/in-reduction.f90`, `flang/test/Lower/OpenMP/task-inreduction.f90`, `flang/test/Lower/OpenMP/taskloop-inreduction.f90`, `flang/test/Lower/OpenMP/taskgroup-task_reduction02.f90`); some target-related forms remain TODO (for example `flang/test/Lower/OpenMP/Todo/target-inreduction.f90`). | [llvm/llvm-project#139704](https://github.com/llvm/llvm-project/pull/139704), [llvm/llvm-project#205124](https://github.com/llvm/llvm-project/pull/205124) |
| user-defined mappers | <span class="good">done</span> | | Supported with semantics/lowering/transform coverage. | [llvm/llvm-project#140560](https://github.com/llvm/llvm-project/pull/140560), [llvm/llvm-project#163860](https://github.com/llvm/llvm-project/pull/163860), [llvm/llvm-project#167903](https://github.com/llvm/llvm-project/pull/167903), [llvm/llvm-project#179936](https://github.com/llvm/llvm-project/pull/179936), [llvm/llvm-project#189136](https://github.com/llvm/llvm-project/pull/189136) |
| map array-section with implicit mapper | <span class="part">partial</span> | | Mapper and map coverage exists (`flang/test/Lower/OpenMP/map-mapper.f90`, `flang/test/Lower/OpenMP/target-data-skip-mapper-calls.f90`), with remaining iterator/modifier gaps in lowering (`flang/lib/Lower/OpenMP/ClauseProcessor.cpp` TODOs). | [llvm/llvm-project#175133](https://github.com/llvm/llvm-project/pull/175133), [llvm/llvm-project#177389](https://github.com/llvm/llvm-project/pull/177389) |
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index dc0fd9effe96e..9e3ed0ddc4396 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -690,7 +690,7 @@ class ParseTreeDumper {
NODE(parser, OmpMapTypeModifier)
NODE_ENUM(OmpMapTypeModifier, Value)
NODE(parser, OmpMatchClause)
- NODE(parser, OmpMemspaceModifier)
+ NODE(parser, OmpMemSpace)
NODE(parser, OmpMessageClause)
NODE(parser, OmpMetadirectiveDirective)
NODE(parser, OmpNoOpenMPClause)
@@ -761,7 +761,7 @@ class ParseTreeDumper {
NODE_ENUM(OmpThreadsetClause, ThreadsetPolicy)
NODE(parser, OmpToClause)
NODE(OmpToClause, Modifier)
- NODE(parser, OmpTraitsArrayModifier)
+ NODE(parser, OmpTraitsArray)
NODE(parser, OmpTraitProperty)
NODE(parser, OmpTraitPropertyExtension)
NODE(OmpTraitPropertyExtension, Complex)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index a835f92c5e32e..e50c1438a1407 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -4227,10 +4227,10 @@ struct OmpMapTypeModifier {
// Ref: [5.2:181-182]
//
-// mem-space-modifier ->
+// mem-space ->
// MEMSPACE(memspace-handle) // since 5.2
-struct OmpMemspaceModifier {
- WRAPPER_CLASS_BOILERPLATE(OmpMemspaceModifier, ScalarIntExpr);
+struct OmpMemSpace {
+ WRAPPER_CLASS_BOILERPLATE(OmpMemSpace, ScalarIntExpr);
};
// Ref: [4.5:56-63], [5.0:101-109], [5.1:126-133], [5.2:252-254]
@@ -4373,10 +4373,10 @@ struct OmpTaskDependenceType {
// Ref: [5.2:181-182]
//
-// traits-array-modifier ->
+// traits-array ->
// TRAITS(traits-array) // since 5.2
-struct OmpTraitsArrayModifier {
- WRAPPER_CLASS_BOILERPLATE(OmpTraitsArrayModifier, common::Indirection<Expr>);
+struct OmpTraitsArray {
+ WRAPPER_CLASS_BOILERPLATE(OmpTraitsArray, common::Indirection<Expr>);
};
// Ref: [4.5:229-230], [5.0:324-325], [5.1:357-358], [5.2:161-162]
@@ -5188,17 +5188,16 @@ struct OmpUseClause {
// [, allocator[(traits-array)]]...) | // since 5.0, dep. 5.2
// USES_ALLOCATORS([modifier...:] allocator) // since 5.2
// modifier ->
-// mem-space-modifier |
-// traits-array-modifier // since 5.2
+// mem-space |
+// traits-array // since 5.2
struct OmpUsesAllocatorsClause {
struct AllocatorSpec {
TUPLE_CLASS_BOILERPLATE(AllocatorSpec);
- MODIFIER_BOILERPLATE(OmpMemspaceModifier, OmpTraitsArrayModifier);
+ MODIFIER_BOILERPLATE(OmpMemSpace, OmpTraitsArray);
CharBlock source;
// The traits of the deprecated "allocator(traits-array)" form are stored
// as a traits-array modifier. The flag records which of the two surface
- // syntaxes was written, which both the unparser and the deprecation
- // diagnostic need.
+ // syntaxes was written.
std::tuple<MODIFIERS(), ScalarIntExpr, /*IsLegacySyntax=*/bool> t;
};
WRAPPER_CLASS_BOILERPLATE(OmpUsesAllocatorsClause, std::list<AllocatorSpec>);
diff --git a/flang/include/flang/Semantics/openmp-modifiers.h b/flang/include/flang/Semantics/openmp-modifiers.h
index 79913fda05af1..1eae01b717e1e 100644
--- a/flang/include/flang/Semantics/openmp-modifiers.h
+++ b/flang/include/flang/Semantics/openmp-modifiers.h
@@ -96,7 +96,7 @@ DECLARE_DESCRIPTOR(parser::OmpLowerBound);
DECLARE_DESCRIPTOR(parser::OmpMapper);
DECLARE_DESCRIPTOR(parser::OmpMapType);
DECLARE_DESCRIPTOR(parser::OmpMapTypeModifier);
-DECLARE_DESCRIPTOR(parser::OmpMemspaceModifier);
+DECLARE_DESCRIPTOR(parser::OmpMemSpace);
DECLARE_DESCRIPTOR(parser::OmpOrderModifier);
DECLARE_DESCRIPTOR(parser::OmpOrderingModifier);
DECLARE_DESCRIPTOR(parser::OmpPreferType);
@@ -109,7 +109,7 @@ DECLARE_DESCRIPTOR(parser::OmpSelfModifier);
DECLARE_DESCRIPTOR(parser::OmpStepComplexModifier);
DECLARE_DESCRIPTOR(parser::OmpStepSimpleModifier);
DECLARE_DESCRIPTOR(parser::OmpTaskDependenceType);
-DECLARE_DESCRIPTOR(parser::OmpTraitsArrayModifier);
+DECLARE_DESCRIPTOR(parser::OmpTraitsArray);
DECLARE_DESCRIPTOR(parser::OmpVariableCategory);
DECLARE_DESCRIPTOR(parser::OmpxHoldModifier);
diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp
index ac68fbe6adaff..3a77371c99276 100644
--- a/flang/lib/Lower/OpenMP/Clauses.cpp
+++ b/flang/lib/Lower/OpenMP/Clauses.cpp
@@ -1816,10 +1816,9 @@ UsesAllocators make(const parser::OmpClause::UsesAllocators &inp,
auto makeSpec = [&](const AllocatorSpec &spec) {
auto &mods = semantics::OmpGetModifiers(spec);
- auto *memSpace =
- semantics::OmpGetUniqueModifier<parser::OmpMemspaceModifier>(mods);
+ auto *memSpace = semantics::OmpGetUniqueModifier<parser::OmpMemSpace>(mods);
auto *traits =
- semantics::OmpGetUniqueModifier<parser::OmpTraitsArrayModifier>(mods);
+ semantics::OmpGetUniqueModifier<parser::OmpTraitsArray>(mods);
std::optional<UsesAllocators::TraitsArray> traitsArray;
if (traits) {
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index be9751ba80ba9..b4dfe740379ba 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -989,7 +989,7 @@ struct OmpMapTypeModifierParser {
TYPE_PARSER(OmpMapTypeModifierParser{})
-TYPE_PARSER(construct<OmpMemspaceModifier>( //
+TYPE_PARSER(construct<OmpMemSpace>( //
"MEMSPACE" >> parenthesized(scalarIntExpr)))
TYPE_PARSER(construct<OmpOrderModifier>(
@@ -1047,7 +1047,7 @@ TYPE_PARSER(construct<OmpTaskDependenceType>(
"MUTEXINOUTSET" >> pure(OmpTaskDependenceType::Value::Mutexinoutset) ||
"OUT" >> pure(OmpTaskDependenceType::Value::Out)))
-TYPE_PARSER(construct<OmpTraitsArrayModifier>( //
+TYPE_PARSER(construct<OmpTraitsArray>( //
"TRAITS" >> parenthesized(indirect(expr))))
TYPE_PARSER(construct<OmpVariableCategory>(
@@ -1201,9 +1201,9 @@ TYPE_PARSER(sourced(
TYPE_PARSER(sourced(construct<OmpUsesAllocatorsClause::AllocatorSpec::Modifier>(
sourced(construct<OmpUsesAllocatorsClause::AllocatorSpec::Modifier>(
- Parser<OmpMemspaceModifier>{}) ||
+ Parser<OmpMemSpace>{}) ||
construct<OmpUsesAllocatorsClause::AllocatorSpec::Modifier>(
- Parser<OmpTraitsArrayModifier>{})))))
+ Parser<OmpTraitsArray>{})))))
TYPE_PARSER(sourced(construct<OmpWhenClause::Modifier>( //
Parser<OmpContextSelector>{})))
@@ -1573,7 +1573,7 @@ static OmpUsesAllocatorsClause::AllocatorSpec makeLegacyAllocatorSpec(
Name &&name, common::Indirection<Expr> &&traits) {
using AllocatorSpec = OmpUsesAllocatorsClause::AllocatorSpec;
CharBlock traitsSource{traits.value().source};
- AllocatorSpec::Modifier mod{OmpTraitsArrayModifier{std::move(traits)}};
+ AllocatorSpec::Modifier mod{OmpTraitsArray{std::move(traits)}};
mod.source = traitsSource;
std::list<AllocatorSpec::Modifier> mods;
mods.emplace_back(std::move(mod));
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 816b91cf1628f..accb80ddbe8f2 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2636,12 +2636,12 @@ class UnparseVisitor {
Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": ");
Walk(std::get<OmpObjectList>(x.t));
}
- void Unparse(const OmpMemspaceModifier &x) {
+ void Unparse(const OmpMemSpace &x) {
Word("MEMSPACE(");
Walk(x.v);
Put(")");
}
- void Unparse(const OmpTraitsArrayModifier &x) {
+ void Unparse(const OmpTraitsArray &x) {
Word("TRAITS(");
Walk(x.v);
Put(")");
@@ -2655,7 +2655,7 @@ class UnparseVisitor {
Walk(std::get<ScalarIntExpr>(x.t));
if (modifiers) {
for (const Modifier &m : *modifiers) {
- if (auto *traits{std::get_if<OmpTraitsArrayModifier>(&m.u)}) {
+ if (auto *traits{std::get_if<OmpTraitsArray>(&m.u)}) {
Put("(");
Walk(traits->v);
Put(")");
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 880f9faf07754..7f998c2e3ec42 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -46,6 +46,7 @@
#include <algorithm>
#include <cassert>
#include <cstdint>
+#include <cstring>
#include <iterator>
#include <list>
#include <map>
@@ -5404,8 +5405,7 @@ static constexpr std::string_view nullMemSpaceName[]{"omp_null_mem_space"};
// Whether the ultimate symbol is an entity of the intrinsic omp_lib module
// shipped with the compiler, as opposed to a same-named entity of a
-// user-defined module. `IsIntrinsicModules()` on the module scope's parent is
-// the provenance test that mod-file.cpp and expression.cpp already use.
+// user-defined module.
static bool IsIntrinsicOmpLibEntity(const Symbol &ultimate) {
const Scope &scope{ultimate.owner()};
if (!scope.IsModule()) {
@@ -5442,9 +5442,9 @@ static bool IsPredefinedHandle(const parser::Name &name,
}
// The integer kind of an OpenMP allocator or memory-space handle, which
-// omp_lib declares as c_intptr_t. iso_c_binding is an intrinsic module, so it
-// can be read on demand the way semantics.cpp reads its other builtin modules;
-// the kind is therefore available even when the source uses neither module.
+// omp_lib declares as c_intptr_t. iso_c_binding is an intrinsic module and is
+// read on demand, so the kind is available even when the source uses neither
+// module.
static std::optional<std::int64_t> GetOmpHandleKind(SemanticsContext &context) {
const Scope *scope{context.GetBuiltinModule("iso_c_binding")};
if (!scope) {
@@ -5463,7 +5463,7 @@ static std::optional<std::int64_t> GetOmpHandleKind(SemanticsContext &context) {
// Whether `symbol` has the integer kind that omp_lib gives its handles. The
// check is skipped for a non-integer allocator, whose type is diagnosed
-// separately, so that the two do not double up.
+// separately.
static bool HasOmpHandleKind(
const Symbol &symbol, SemanticsContext &context, std::int64_t &expected) {
const DeclTypeSpec *type{symbol.GetUltimate().GetType()};
@@ -5494,10 +5494,8 @@ void OmpStructureChecker::CheckUsesAllocatorsSpec(
}
auto &modifiers{OmpGetModifiers(spec)};
- const auto *memSpace{
- OmpGetUniqueModifier<parser::OmpMemspaceModifier>(modifiers)};
- const auto *traits{
- OmpGetUniqueModifier<parser::OmpTraitsArrayModifier>(modifiers)};
+ const auto *memSpace{OmpGetUniqueModifier<parser::OmpMemSpace>(modifiers)};
+ const auto *traits{OmpGetUniqueModifier<parser::OmpTraitsArray>(modifiers)};
const parser::Expr &allocatorExpr{
std::get<parser::ScalarIntExpr>(spec.t).thing.thing.value()};
@@ -5559,27 +5557,28 @@ void OmpStructureChecker::CheckUsesAllocatorsSpec(
// [5.2:182] The allocator argument must not appear in other data-sharing
// attribute clauses or data-mapping attribute clauses on the same construct.
if (const Symbol *symbol{allocatorName->symbol}) {
- static const llvm::omp::Clause conflictingClauses[]{
- llvm::omp::Clause::OMPC_firstprivate,
- llvm::omp::Clause::OMPC_has_device_addr,
- llvm::omp::Clause::OMPC_is_device_ptr,
- llvm::omp::Clause::OMPC_map,
- llvm::omp::Clause::OMPC_private,
- };
+ const Symbol &ultimate{symbol->GetUltimate()};
const parser::OmpDirectiveSpecification &dirSpec{*dirStack_.back()};
- for (llvm::omp::Clause id : conflictingClauses) {
- const parser::OmpClause *found{parser::omp::FindClause(dirSpec, id)};
- if (!found) {
+ for (const parser::OmpClause &clause : dirSpec.Clauses().v) {
+ llvm::omp::Clause id{clause.Id()};
+ if (id == llvm::omp::Clause::OMPC_uses_allocators) {
continue;
}
- if (const parser::OmpObjectList *objects{GetOmpObjectList(*found)}) {
- for (const parser::OmpObject &object : objects->v) {
- if (const Symbol *other{GetObjectSymbol(object, /*ultimate=*/true)};
- other == &symbol->GetUltimate()) {
- context_.Say(allocatorSource,
- "An allocator in a USES_ALLOCATORS clause cannot also appear in the %s clause on the same construct"_err_en_US,
- parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)));
- }
+ if (!llvm::omp::isDataSharingAttributeClause(id, version) &&
+ id != llvm::omp::Clause::OMPC_map) {
+ continue;
+ }
+ const parser::OmpObjectList *objects{GetOmpObjectList(clause)};
+ if (!objects) {
+ continue;
+ }
+ for (const parser::OmpObject &object : objects->v) {
+ if (const Symbol *other{GetObjectSymbol(object, /*ultimate=*/true)};
+ other == &ultimate) {
+ context_.Say(allocatorSource,
+ "An allocator in a USES_ALLOCATORS clause cannot also appear in the %s clause on the same construct"_err_en_US,
+ parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)));
+ break;
}
}
}
@@ -5612,7 +5611,7 @@ void OmpStructureChecker::CheckUsesAllocatorsSpec(
}
void OmpStructureChecker::CheckUsesAllocatorsTraits(
- const parser::OmpTraitsArrayModifier &traits, parser::CharBlock source) {
+ const parser::OmpTraitsArray &traits, parser::CharBlock source) {
const parser::Expr &expr{traits.v.value()};
parser::CharBlock traitsSource{source.empty() ? expr.source : source};
const SomeExpr *value{GetExpr(context_, expr)};
@@ -5664,7 +5663,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::UsesAllocators &x) {
const std::list<parser::OmpUsesAllocatorsClause::AllocatorSpec> &specs{x.v.v};
// Classify by the syntax each specification was written in, which the parse
- // tree records, rather than by whether a modifier happens to be present.
+ // tree records.
bool anyLegacyItem{false}, anyModifierItem{false};
for (auto &spec : specs) {
if (std::get<bool>(spec.t)) {
@@ -5689,8 +5688,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::UsesAllocators &x) {
}
if (specs.size() > 1 && version >= 60) {
- // [6.0:315] permits more than one clause-argument-specification, but only
- // the single-specification form is implemented so far.
+ // [6.0:315] permits more than one clause-argument-specification.
context_.Say(GetContext().clauseSource,
"Multiple allocator specifications in a USES_ALLOCATORS clause are not yet supported"_err_en_US);
return;
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 53d47eaacda24..ce692d0c99895 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -428,7 +428,7 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
void CheckUsesAllocatorsSpec(
const parser::OmpUsesAllocatorsClause::AllocatorSpec &spec);
void CheckUsesAllocatorsTraits(
- const parser::OmpTraitsArrayModifier &traits, parser::CharBlock source);
+ const parser::OmpTraitsArray &traits, parser::CharBlock source);
void CheckIteratorRange(const parser::OmpIteratorSpecifier &x);
void CheckIteratorModifier(const parser::OmpIterator &x);
diff --git a/flang/lib/Semantics/openmp-modifiers.cpp b/flang/lib/Semantics/openmp-modifiers.cpp
index d4a2bddc2c3c6..2d5487487d0b1 100644
--- a/flang/lib/Semantics/openmp-modifiers.cpp
+++ b/flang/lib/Semantics/openmp-modifiers.cpp
@@ -557,9 +557,9 @@ const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpMapTypeModifier>() {
}
template <>
-const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpMemspaceModifier>() {
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpMemSpace>() {
static const OmpModifierDescriptor desc{
- /*name=*/"mem-space-modifier",
+ /*name=*/"mem-space",
/*props=*/
{
{52, {OmpProperty::Unique}},
@@ -773,10 +773,9 @@ const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpTaskDependenceType>() {
}
template <>
-const OmpModifierDescriptor &
-OmpGetDescriptor<parser::OmpTraitsArrayModifier>() {
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpTraitsArray>() {
static const OmpModifierDescriptor desc{
- /*name=*/"traits-array-modifier",
+ /*name=*/"traits-array",
/*props=*/
{
{52, {OmpProperty::Unique}},
diff --git a/flang/test/Lower/OpenMP/Todo/target-uses-allocators.f90 b/flang/test/Lower/OpenMP/Todo/target-uses-allocators.f90
index fbe2e9efe9ac3..6166f3cd5984f 100644
--- a/flang/test/Lower/OpenMP/Todo/target-uses-allocators.f90
+++ b/flang/test/Lower/OpenMP/Todo/target-uses-allocators.f90
@@ -7,8 +7,7 @@
! RUN: %not_todo_cmd %flang_fc1 -cpp -DLEGACY -emit-llvm %openmp_flags -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
! Every shape that semantics accepts must reach the established "not yet
-! implemented" lowering boundary for USES_ALLOCATORS, rather than aborting
-! while the clause is converted for lowering. Each shape needs its own
+! implemented" lowering boundary for USES_ALLOCATORS. Each shape needs its own
! compilation because lowering stops at the first unimplemented clause.
! CHECK: not yet implemented: USES_ALLOCATORS clause is not implemented yet
diff --git a/flang/test/Parser/OpenMP/uses-allocators-bad-syntax.f90 b/flang/test/Parser/OpenMP/uses-allocators-bad-syntax.f90
index 71317861883fb..81333d6966b58 100644
--- a/flang/test/Parser/OpenMP/uses-allocators-bad-syntax.f90
+++ b/flang/test/Parser/OpenMP/uses-allocators-bad-syntax.f90
@@ -5,10 +5,9 @@
! Forms that the USES_ALLOCATORS parser does not accept.
!
-! The first construct is a valid clause carrying no error annotation. It is the
-! reason this test cannot pass vacuously: a compiler without USES_ALLOCATORS
-! parser support does not parse it either, and the resulting unexpected
-! diagnostic makes the harness fail rather than silently agree.
+! The first construct is a valid clause carrying no error annotation. A
+! compiler without USES_ALLOCATORS parser support does not parse it either, so
+! this test cannot pass vacuously.
subroutine uses_allocators_bad_syntax
use omp_lib
diff --git a/flang/test/Parser/OpenMP/uses-allocators.f90 b/flang/test/Parser/OpenMP/uses-allocators.f90
index 42ac11512e9bf..cc474134c5789 100644
--- a/flang/test/Parser/OpenMP/uses-allocators.f90
+++ b/flang/test/Parser/OpenMP/uses-allocators.f90
@@ -61,7 +61,7 @@ subroutine uses_allocators_syntax
! TRAITS(tr): a
!PARSE-TREE: OmpClause -> UsesAllocators -> OmpUsesAllocatorsClause -> AllocatorSpec
-!PARSE-TREE-NEXT: Modifier -> OmpTraitsArrayModifier -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
+!PARSE-TREE-NEXT: Modifier -> OmpTraitsArray -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'tr'
!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'a'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'a'
@@ -69,7 +69,7 @@ subroutine uses_allocators_syntax
! MEMSPACE(omp_default_mem_space): a
!PARSE-TREE: OmpClause -> UsesAllocators -> OmpUsesAllocatorsClause -> AllocatorSpec
-!PARSE-TREE-NEXT: Modifier -> OmpMemspaceModifier -> Scalar -> Integer -> Expr = '99_8'
+!PARSE-TREE-NEXT: Modifier -> OmpMemSpace -> Scalar -> Integer -> Expr = '99_8'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'omp_default_mem_space'
!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'a'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'a'
@@ -77,18 +77,18 @@ subroutine uses_allocators_syntax
! Both modifiers, in either order.
!PARSE-TREE: OmpClause -> UsesAllocators -> OmpUsesAllocatorsClause -> AllocatorSpec
-!PARSE-TREE-NEXT: Modifier -> OmpMemspaceModifier -> Scalar -> Integer -> Expr = '4_8'
+!PARSE-TREE-NEXT: Modifier -> OmpMemSpace -> Scalar -> Integer -> Expr = '4_8'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'omp_low_lat_mem_space'
-!PARSE-TREE-NEXT: Modifier -> OmpTraitsArrayModifier -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
+!PARSE-TREE-NEXT: Modifier -> OmpTraitsArray -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'tr'
!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'a'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'a'
!PARSE-TREE-NEXT: bool = 'false'
!PARSE-TREE: OmpClause -> UsesAllocators -> OmpUsesAllocatorsClause -> AllocatorSpec
-!PARSE-TREE-NEXT: Modifier -> OmpTraitsArrayModifier -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
+!PARSE-TREE-NEXT: Modifier -> OmpTraitsArray -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'tr'
-!PARSE-TREE-NEXT: Modifier -> OmpMemspaceModifier -> Scalar -> Integer -> Expr = '4_8'
+!PARSE-TREE-NEXT: Modifier -> OmpMemSpace -> Scalar -> Integer -> Expr = '4_8'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'omp_low_lat_mem_space'
!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'a'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'a'
@@ -97,7 +97,7 @@ subroutine uses_allocators_syntax
! The deprecated "allocator(traits)" form is stored as a traits modifier, and
! is flagged so that it unparses back to the syntax that was written.
!PARSE-TREE: OmpClause -> UsesAllocators -> OmpUsesAllocatorsClause -> AllocatorSpec
-!PARSE-TREE-NEXT: Modifier -> OmpTraitsArrayModifier -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
+!PARSE-TREE-NEXT: Modifier -> OmpTraitsArray -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'tr'
!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'a'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'a'
@@ -105,13 +105,13 @@ subroutine uses_allocators_syntax
! The deprecated syntax can list more than one allocator specification.
!PARSE-TREE: OmpClause -> UsesAllocators -> OmpUsesAllocatorsClause -> AllocatorSpec
-!PARSE-TREE-NEXT: Modifier -> OmpTraitsArrayModifier -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
+!PARSE-TREE-NEXT: Modifier -> OmpTraitsArray -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'tr'
!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'a'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'a'
!PARSE-TREE-NEXT: bool = 'true'
!PARSE-TREE-NEXT: AllocatorSpec
-!PARSE-TREE-NEXT: Modifier -> OmpTraitsArrayModifier -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
+!PARSE-TREE-NEXT: Modifier -> OmpTraitsArray -> Expr = '[omp_alloctrait::omp_alloctrait(key=2_4,value=64_8)]'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'tr'
!PARSE-TREE-NEXT: Scalar -> Integer -> Expr = 'b'
!PARSE-TREE-NEXT: Designator -> DataRef -> Name = 'b'
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-handle-kind.f90 b/flang/test/Semantics/OpenMP/uses-allocators-handle-kind.f90
index b9c6a764c67f8..e1d693c69df45 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators-handle-kind.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators-handle-kind.f90
@@ -6,8 +6,8 @@
! [5.2:181], [6.0:315] The allocator argument of USES_ALLOCATORS is an
! expression of allocator_handle type, which omp_lib declares as c_intptr_t.
! The rule holds whether or not the source uses omp_lib, so none of these
-! subroutines imports it. The expected kind is matched as a number rather than
-! spelled out, because it is the target's C_INTPTR_T kind.
+! subroutines imports it. The expected kind is matched as a number because it
+! is the target's C_INTPTR_T kind.
! CHECK: error: The allocator 'wrong_kind_alloc' in a USES_ALLOCATORS clause must be of type INTEGER(KIND={{[0-9]+}}), i.e. OMP_ALLOCATOR_HANDLE_KIND
subroutine uses_allocators_wrong_handle_kind
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-user-module.f90 b/flang/test/Semantics/OpenMP/uses-allocators-user-module.f90
index 55dbb130f385f..4eb441820dce9 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators-user-module.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators-user-module.f90
@@ -8,8 +8,7 @@
! when its named constant has the right name and the right handle kind.
!
! This module shadows the intrinsic omp_lib for the whole file, so it is kept
-! in a test of its own rather than contaminating tests that need the real
-! intrinsic module.
+! in a test of its own.
! CHECK: error: A non-predefined allocator 'omp_const_mem_alloc' in a USES_ALLOCATORS clause must be a variable
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-version50.f90 b/flang/test/Semantics/OpenMP/uses-allocators-version50.f90
index a8491fa764f81..474fea2e55707 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators-version50.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators-version50.f90
@@ -27,12 +27,12 @@ subroutine uses_allocators_50
x = 3
!$omp end target
- !WARNING: 'traits-array-modifier' modifier is not supported in OpenMP v5.0, try -fopenmp-version=52
+ !WARNING: 'traits-array' modifier is not supported in OpenMP v5.0, try -fopenmp-version=52
!$omp target uses_allocators(traits(tr): my_alloc)
x = 4
!$omp end target
- !WARNING: 'mem-space-modifier' modifier is not supported in OpenMP v5.0, try -fopenmp-version=52
+ !WARNING: 'mem-space' modifier is not supported in OpenMP v5.0, try -fopenmp-version=52
!$omp target uses_allocators(memspace(omp_default_mem_space): my_alloc)
x = 5
!$omp end target
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-version51.f90 b/flang/test/Semantics/OpenMP/uses-allocators-version51.f90
index 921eb273df6a0..231774fdd9970 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators-version51.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators-version51.f90
@@ -21,7 +21,7 @@ subroutine uses_allocators_51
x = 2
!$omp end target
- !WARNING: 'traits-array-modifier' modifier is not supported in OpenMP v5.1, try -fopenmp-version=52
+ !WARNING: 'traits-array' modifier is not supported in OpenMP v5.1, try -fopenmp-version=52
!$omp target uses_allocators(traits(tr): my_alloc)
x = 3
!$omp end target
diff --git a/flang/test/Semantics/OpenMP/uses-allocators.f90 b/flang/test/Semantics/OpenMP/uses-allocators.f90
index 1bbb09d16062c..a3f1cdc01b387 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators.f90
@@ -3,8 +3,6 @@
! RUN: %python %S/../test_errors.py %s %flang_fc1 %openmp_flags -fopenmp-version=52
! OpenMP Version 5.2, Section 6.8: uses_allocators clause.
-! Each negative case is paired with the nearest valid case so that the checks
-! cannot reject conforming code.
module uses_allocators_traits_module
use omp_lib
@@ -155,12 +153,12 @@ subroutine uses_allocators_errors
!$omp end target
! A modifier may appear at most once in one allocator specification.
- !ERROR: 'traits-array-modifier' modifier cannot occur multiple times
+ !ERROR: 'traits-array' modifier cannot occur multiple times
!$omp target uses_allocators(traits(tr), traits(tr): my_alloc)
x = 13
!$omp end target
- !ERROR: 'mem-space-modifier' modifier cannot occur multiple times
+ !ERROR: 'mem-space' modifier cannot occur multiple times
!$omp target uses_allocators(memspace(omp_const_mem_space), memspace(omp_high_bw_mem_space): my_alloc)
x = 131
!$omp end target
@@ -312,3 +310,35 @@ subroutine uses_allocators_predefined_shadow
x = 1
!$omp end target
end subroutine
+
+subroutine uses_allocators_repeated_clauses
+ use omp_lib
+ ! As above, each case uses its own allocator.
+ integer(omp_allocator_handle_kind) :: map_alloc, priv_alloc, ir_alloc
+ type(omp_alloctrait), parameter :: tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x, y
+
+ ! The conflict is in the second MAP clause, which is invisible to a scan
+ ! that inspects only the first occurrence of each clause id.
+ !ERROR: An allocator in a USES_ALLOCATORS clause cannot also appear in the MAP clause on the same construct
+ !$omp target map(to: y) map(tofrom: map_alloc) uses_allocators(traits(tr): map_alloc)
+ x = 1
+ !$omp end target
+
+ ! The same, for a repeated PRIVATE clause.
+ !ERROR: An allocator in a USES_ALLOCATORS clause cannot also appear in the PRIVATE clause on the same construct
+ !ERROR: Variable 'y' may not appear on both MAP and PRIVATE clauses on a TARGET construct
+ !$omp target private(y) private(priv_alloc) uses_allocators(traits(tr): priv_alloc)
+ x = 2
+ !$omp end target
+
+ ! IN_REDUCTION is a data-sharing attribute clause allowed on TARGET, but it
+ ! is not one of the privatizing clauses.
+ !$omp taskgroup task_reduction(+: ir_alloc)
+ !ERROR: An allocator in a USES_ALLOCATORS clause cannot also appear in the IN_REDUCTION clause on the same construct
+ !$omp target in_reduction(+: ir_alloc) uses_allocators(traits(tr): ir_alloc)
+ x = 3
+ !$omp end target
+ !$omp end taskgroup
+end subroutine
>From 8344bd4713d5a0068050193898040ea3ed8ee7d5 Mon Sep 17 00:00:00 2001
From: Sairudra More <sairudra60 at gmail.com>
Date: Wed, 5 Aug 2026 03:19:52 -0500
Subject: [PATCH 3/7] [flang][OpenMP] Continue checking USES_ALLOCATORS
extensions
Continue semantic validation when newer modifier syntax is accepted with a
version warning, preventing malformed traits from reaching lowering.
---
flang/lib/Semantics/check-omp-structure.cpp | 10 ++++++----
.../Semantics/OpenMP/uses-allocators-version50.f90 | 13 +++++++++++--
.../Semantics/OpenMP/uses-allocators-version51.f90 | 10 +++++++++-
3 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 7f998c2e3ec42..f593762eb377e 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -53,6 +53,7 @@
#include <optional>
#include <set>
#include <string>
+#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
@@ -5486,11 +5487,12 @@ void OmpStructureChecker::CheckUsesAllocatorsSpec(
// 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.
if (!isLegacySyntax) {
- if (!OmpVerifyModifiers(spec, llvm::omp::OMPC_uses_allocators,
- GetContext().clauseSource, context_)) {
- return;
- }
+ OmpVerifyModifiers(spec, llvm::omp::OMPC_uses_allocators,
+ GetContext().clauseSource, context_);
}
auto &modifiers{OmpGetModifiers(spec)};
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-version50.f90 b/flang/test/Semantics/OpenMP/uses-allocators-version50.f90
index 474fea2e55707..197b8385fdafa 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators-version50.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators-version50.f90
@@ -32,14 +32,23 @@ subroutine uses_allocators_50
x = 4
!$omp end target
+ ! Accepting the newer syntax with a warning does not skip the remaining
+ ! checks on the specification.
+ !WARNING: 'traits-array' modifier is not supported in OpenMP v5.0, try -fopenmp-version=52
+ !ERROR: The traits array must be a named constant array
+ !$omp target uses_allocators(traits(1): my_alloc)
+ x = 5
+ !$omp end target
+
!WARNING: 'mem-space' modifier is not supported in OpenMP v5.0, try -fopenmp-version=52
+ !ERROR: A non-predefined allocator 'my_alloc' in a USES_ALLOCATORS clause must have traits specified in OpenMP v5.0
!$omp target uses_allocators(memspace(omp_default_mem_space): my_alloc)
- x = 5
+ x = 6
!$omp end target
!ERROR: A non-predefined allocator 'omp_null_allocator' in a USES_ALLOCATORS clause must be a variable
!$omp target uses_allocators(omp_null_allocator(tr))
- x = 6
+ x = 7
!$omp end target
end subroutine
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-version51.f90 b/flang/test/Semantics/OpenMP/uses-allocators-version51.f90
index 231774fdd9970..bb262b5ecb123 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators-version51.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators-version51.f90
@@ -26,9 +26,17 @@ subroutine uses_allocators_51
x = 3
!$omp end target
+ ! Accepting the newer syntax with a warning does not skip the remaining
+ ! checks on the specification.
+ !WARNING: 'traits-array' modifier is not supported in OpenMP v5.1, try -fopenmp-version=52
+ !ERROR: The traits array must be a named constant array
+ !$omp target uses_allocators(traits(1): my_alloc)
+ x = 4
+ !$omp end target
+
!ERROR: A predefined allocator 'omp_default_mem_alloc' in a USES_ALLOCATORS clause cannot have modifiers or traits specified
!$omp target uses_allocators(omp_default_mem_alloc(tr))
- x = 4
+ x = 5
!$omp end target
end subroutine
>From ba714767f14afd3d3cfaa9bb180b39df6e7c60b2 Mon Sep 17 00:00:00 2001
From: Sairudra More <sairudra60 at gmail.com>
Date: Sun, 9 Aug 2026 01:44:37 -0500
Subject: [PATCH 4/7] [flang][OpenMP] Check MEMSPACE by written name
Require the MEMSPACE identifier spelling to match a predefined memory space while preserving the OpenMP 6.0 omp_null_mem_space allowance.
---
flang/lib/Semantics/check-omp-structure.cpp | 6 ++++--
flang/test/Semantics/OpenMP/uses-allocators.f90 | 3 ++-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index f593762eb377e..eb31c35a4dc89 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5596,9 +5596,11 @@ void OmpStructureChecker::CheckUsesAllocatorsSpec(
const parser::Name *memSpaceName{
parser::Unwrap<parser::Name>(memSpaceExpr)};
bool ok{memSpaceName &&
- (IsPredefinedHandle(*memSpaceName, predefinedMemSpaceNames, version) ||
+ (llvm::is_contained(
+ predefinedMemSpaceNames, memSpaceName->ToString()) ||
(version >= 60 &&
- IsPredefinedHandle(*memSpaceName, nullMemSpaceName, version)))};
+ llvm::is_contained(
+ nullMemSpaceName, memSpaceName->ToString())))};
if (!ok) {
context_.Say(memSpaceSource,
"The MEMSPACE modifier must name a predefined memory space"_err_en_US);
diff --git a/flang/test/Semantics/OpenMP/uses-allocators.f90 b/flang/test/Semantics/OpenMP/uses-allocators.f90
index a3f1cdc01b387..eabe49b431332 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators.f90
@@ -289,7 +289,8 @@ subroutine uses_allocators_predefined_identity
x = 3
!$omp end target
- ! A renamed memory space likewise still denotes a predefined memory space.
+ ! A memory space must use the written name of a predefined memory space.
+ !ERROR: The MEMSPACE modifier must name a predefined memory space
!$omp target uses_allocators(memspace(renamed_space): my_alloc)
x = 4
!$omp end target
>From 82109f7c8d27daa0aa4247d9daffde0573a5d7bb Mon Sep 17 00:00:00 2001
From: Sairudra More <sairudra60 at gmail.com>
Date: Sun, 9 Aug 2026 03:51:25 -0500
Subject: [PATCH 5/7] [flang][OpenMP] Harden USES_ALLOCATORS semantic checks
Enforce the pre-6.0 traits-array scope rule and intrinsic
OMP_ALLOCTRAIT identity. Apply combined-clause conflicts only when the
clause affects the TARGET constituent.
---
flang/lib/Semantics/check-omp-structure.cpp | 81 +++++++++++++++---
.../OpenMP/uses-allocators-version50.f90 | 29 +++++++
.../OpenMP/uses-allocators-version51.f90 | 29 +++++++
.../OpenMP/uses-allocators-version60.f90 | 27 ++++++
.../test/Semantics/OpenMP/uses-allocators.f90 | 82 +++++++++++++++++--
5 files changed, 232 insertions(+), 16 deletions(-)
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index eb31c35a4dc89..d94f63eff5445 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5417,6 +5417,16 @@ static bool IsIntrinsicOmpLibEntity(const Symbol &ultimate) {
scope.parent().IsIntrinsicModules();
}
+static bool IsIntrinsicOmpAlloctrait(
+ const DerivedTypeSpec &derived, SemanticsContext &context) {
+ const Scope *scope{context.GetBuiltinModule("omp_lib")};
+ const Symbol *symbol{scope ? scope->FindSymbol(SourceName{"omp_alloctrait",
+ std::strlen("omp_alloctrait")})
+ : nullptr};
+ return symbol && IsIntrinsicOmpLibEntity(*symbol) &&
+ &derived.typeSymbol() == &symbol->GetUltimate();
+}
+
// Recognition of a predefined allocator or memory space differs by version.
//
// [5.2:182] asks whether the allocator *is* a predefined allocator, so it
@@ -5480,6 +5490,50 @@ static bool HasOmpHandleKind(
return !got || *got == *want;
}
+static bool ClauseHasTargetEffect(
+ llvm::omp::Directive directive, llvm::omp::Clause clause) {
+ llvm::ArrayRef<llvm::omp::Directive> leafs{
+ llvm::omp::getLeafConstructsOrSelf(directive)};
+ if (!llvm::is_contained(leafs, llvm::omp::Directive::OMPD_target)) {
+ return false;
+ }
+ if (leafs.size() == 1) {
+ return true;
+ }
+
+ // Keep this narrow mirror synchronized with the authoritative distribution
+ // rules in llvm/include/llvm/Frontend/OpenMP/ConstructDecompositionT.h.
+ switch (clause) {
+ case llvm::omp::Clause::OMPC_private:
+ // [5.2:340:1-2] Applies to the innermost permitting leaf.
+ return false;
+ case llvm::omp::Clause::OMPC_shared:
+ // [5.2:340:31-32] TARGET is not a permitting leaf.
+ return false;
+ case llvm::omp::Clause::OMPC_firstprivate:
+ // [5.2:340:3-14] Applies to TARGET.
+ return true;
+ case llvm::omp::Clause::OMPC_map:
+ return true;
+ case llvm::omp::Clause::OMPC_lastprivate:
+ // [5.2:340:21-30] Synthesizes TARGET map(tofrom).
+ return true;
+ case llvm::omp::Clause::OMPC_reduction:
+ // [5.2:341:11-13] Synthesizes TARGET map(tofrom).
+ return true;
+ case llvm::omp::Clause::OMPC_linear:
+ // [5.2:341:15-22] Creates outer FIRSTPRIVATE/LASTPRIVATE state.
+ return true;
+ case llvm::omp::Clause::OMPC_in_reduction:
+ case llvm::omp::Clause::OMPC_is_device_ptr:
+ case llvm::omp::Clause::OMPC_has_device_addr:
+ // [5.2:339-341] These clauses are TARGET-owned.
+ return true;
+ default:
+ return true;
+ }
+}
+
void OmpStructureChecker::CheckUsesAllocatorsSpec(
const parser::OmpUsesAllocatorsClause::AllocatorSpec &spec) {
unsigned version{context_.langOptions().OpenMPVersion};
@@ -5570,6 +5624,9 @@ void OmpStructureChecker::CheckUsesAllocatorsSpec(
id != llvm::omp::Clause::OMPC_map) {
continue;
}
+ if (!ClauseHasTargetEffect(GetContext().directive, id)) {
+ continue;
+ }
const parser::OmpObjectList *objects{GetOmpObjectList(clause)};
if (!objects) {
continue;
@@ -5626,31 +5683,35 @@ void OmpStructureChecker::CheckUsesAllocatorsTraits(
const parser::Name *name{parser::Unwrap<parser::Name>(expr)};
const Symbol *symbol{name ? name->symbol : nullptr};
- // [5.2:182] publishes one restriction for both languages: the traits
- // argument must be a constant array, have constant values, and be defined in
- // the same scope as the construct. [6.0:317] later splits that per language,
- // keeping the same-scope requirement for C/C++ and stating the Fortran rule
- // as a named constant of rank one. Follow the 6.0 clarification, so an
- // otherwise valid host- or use-associated named constant is accepted.
+ // [5.0:175], [5.1:203], and [5.2:182] require the traits array declaration
+ // in the same scoping unit as the construct. [6.0:317] retains same-scope
+ // only for C/C++; Fortran instead requires a rank-one named constant.
if (!symbol) {
context_.Say(traitsSource,
"The traits array must be a named constant array"_err_en_US);
return;
}
+ const Symbol &ultimate{symbol->GetUltimate()};
+ if (context_.langOptions().OpenMPVersion < 60 &&
+ &ultimate.owner() !=
+ &GetScopingUnit(context_.FindScope(GetContext().directiveSource))) {
+ context_.Say(traitsSource,
+ "The traits array '%s' must be defined in the same scope as the construct"_err_en_US,
+ name->ToString());
+ }
if (value->Rank() != 1) {
context_.Say(traitsSource,
"The traits array '%s' must be a rank-one array"_err_en_US,
name->ToString());
}
- if (!IsNamedConstant(symbol->GetUltimate()) ||
- !evaluate::IsConstantExpr(*value)) {
+ if (!IsNamedConstant(ultimate) || !evaluate::IsConstantExpr(*value)) {
context_.Say(traitsSource,
"The traits array '%s' must be a constant array with constant values"_err_en_US,
name->ToString());
}
- const DeclTypeSpec *type{symbol->GetUltimate().GetType()};
+ const DeclTypeSpec *type{ultimate.GetType()};
const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr};
- if (!derived || derived->name().ToString() != "omp_alloctrait") {
+ if (!derived || !IsIntrinsicOmpAlloctrait(*derived, context_)) {
context_.Say(traitsSource,
"The traits array '%s' must be of type OMP_ALLOCTRAIT"_err_en_US,
name->ToString());
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-version50.f90 b/flang/test/Semantics/OpenMP/uses-allocators-version50.f90
index 197b8385fdafa..b899ba676bb7f 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators-version50.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators-version50.f90
@@ -6,6 +6,12 @@
! "allocator[(traits-array)]" list syntax, and [5.0:175] requires a
! non-predefined allocator to specify traits.
+module uses_allocators_50_traits
+ use omp_lib
+ type(omp_alloctrait), parameter :: module_tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+end module
+
subroutine uses_allocators_50
use omp_lib
integer(omp_allocator_handle_kind) :: my_alloc, other_alloc
@@ -52,6 +58,29 @@ subroutine uses_allocators_50
!$omp end target
end subroutine
+subroutine uses_allocators_50_association
+ use omp_lib
+ use uses_allocators_50_traits
+ integer(omp_allocator_handle_kind) :: my_alloc
+ type(omp_alloctrait), parameter :: host_tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+ !ERROR: The traits array 'module_tr' must be defined in the same scope as the construct
+ !$omp target uses_allocators(my_alloc(module_tr))
+ x = 1
+ !$omp end target
+
+ call inner
+contains
+ subroutine inner
+ !ERROR: The traits array 'host_tr' must be defined in the same scope as the construct
+ !$omp target uses_allocators(my_alloc(host_tr))
+ x = 2
+ !$omp end target
+ end subroutine
+end subroutine
+
subroutine uses_allocators_50_rename
use omp_lib, only: renamed_predef => omp_const_mem_alloc
integer :: x
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-version51.f90 b/flang/test/Semantics/OpenMP/uses-allocators-version51.f90
index bb262b5ecb123..0c9ec9a6c4f21 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators-version51.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators-version51.f90
@@ -5,6 +5,12 @@
! OpenMP 5.1 keeps the pre-5.2 USES_ALLOCATORS syntax and the [5.1:203]
! requirement that a non-predefined allocator specifies traits.
+module uses_allocators_51_traits
+ use omp_lib
+ type(omp_alloctrait), parameter :: module_tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+end module
+
subroutine uses_allocators_51
use omp_lib
integer(omp_allocator_handle_kind) :: my_alloc, other_alloc
@@ -40,6 +46,29 @@ subroutine uses_allocators_51
!$omp end target
end subroutine
+subroutine uses_allocators_51_association
+ use omp_lib
+ use uses_allocators_51_traits
+ integer(omp_allocator_handle_kind) :: my_alloc
+ type(omp_alloctrait), parameter :: host_tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+ !ERROR: The traits array 'module_tr' must be defined in the same scope as the construct
+ !$omp target uses_allocators(my_alloc(module_tr))
+ x = 1
+ !$omp end target
+
+ call inner
+contains
+ subroutine inner
+ !ERROR: The traits array 'host_tr' must be defined in the same scope as the construct
+ !$omp target uses_allocators(my_alloc(host_tr))
+ x = 2
+ !$omp end target
+ end subroutine
+end subroutine
+
subroutine uses_allocators_51_rename
use omp_lib, only: renamed_predef => omp_const_mem_alloc
integer :: x
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-version60.f90 b/flang/test/Semantics/OpenMP/uses-allocators-version60.f90
index 9b64f53be8a8f..f787e3982fa44 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators-version60.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators-version60.f90
@@ -9,6 +9,33 @@
! [6.0:B.2] also removes every feature deprecated in 5.0, 5.1 and 5.2, which
! includes the comma-separated list syntax deprecated by [5.2:181].
+module uses_allocators_60_traits
+ use omp_lib
+ type(omp_alloctrait), parameter :: module_tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+end module
+
+subroutine uses_allocators_v60_association
+ use omp_lib
+ use uses_allocators_60_traits
+ integer(omp_allocator_handle_kind) :: my_alloc
+ type(omp_alloctrait), parameter :: host_tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+ !$omp target uses_allocators(traits(module_tr): my_alloc)
+ x = 1
+ !$omp end target
+
+ call inner
+contains
+ subroutine inner
+ !$omp target uses_allocators(traits(host_tr): my_alloc)
+ x = 2
+ !$omp end target
+ end subroutine
+end subroutine
+
subroutine uses_allocators_v60_null
use omp_lib
integer(omp_allocator_handle_kind) :: my_alloc
diff --git a/flang/test/Semantics/OpenMP/uses-allocators.f90 b/flang/test/Semantics/OpenMP/uses-allocators.f90
index eabe49b431332..2974e9d6f2407 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators.f90
@@ -50,6 +50,19 @@ subroutine uses_allocators_ok
!$omp end target teams
end subroutine
+subroutine uses_allocators_renamed_type
+ use omp_lib, only: renamed_alloctrait => omp_alloctrait, &
+ omp_allocator_handle_kind, omp_atk_alignment
+ integer(omp_allocator_handle_kind) :: my_alloc
+ type(renamed_alloctrait), parameter :: tr(1) = &
+ [renamed_alloctrait(omp_atk_alignment, 64)]
+ integer :: x
+
+ !$omp target uses_allocators(traits(tr): my_alloc)
+ x = 1
+ !$omp end target
+end subroutine
+
subroutine uses_allocators_loop_construct
use omp_lib
integer(omp_allocator_handle_kind) :: my_alloc
@@ -211,26 +224,83 @@ subroutine uses_allocators_traits_association
use omp_lib
use uses_allocators_traits_module
integer(omp_allocator_handle_kind) :: my_alloc
+ type(omp_alloctrait), parameter :: host_tr(1) = &
+ [omp_alloctrait(omp_atk_alignment, 64)]
integer :: x
- ! [5.2:182] pairs the constant-array requirement with a "same scope as the
- ! construct" requirement, which [6.0:317] keeps only for C/C++ while stating
- ! the Fortran rule as a named constant of rank one. A use-associated named
- ! constant therefore satisfies the Fortran rule.
+ ! [5.2:182] requires the traits array to be defined in the construct scope.
+ !ERROR: The traits array 'module_tr' must be defined in the same scope as the construct
!$omp target uses_allocators(traits(module_tr): my_alloc)
x = 1
!$omp end target
- ! The same holds for a host-associated named constant.
call inner
contains
subroutine inner
- !$omp target uses_allocators(traits(module_tr): my_alloc)
+ !ERROR: The traits array 'host_tr' must be defined in the same scope as the construct
+ !$omp target uses_allocators(traits(host_tr): my_alloc)
x = 2
!$omp end target
end subroutine
end subroutine
+subroutine uses_allocators_impostor_type
+ use omp_lib, only: omp_allocator_handle_kind
+ type omp_alloctrait
+ integer :: key
+ integer :: value
+ end type
+ integer(omp_allocator_handle_kind) :: my_alloc
+ type(omp_alloctrait), parameter :: tr(1) = [omp_alloctrait(1, 64)]
+ integer :: x
+
+ !ERROR: The traits array 'tr' must be of type OMP_ALLOCTRAIT
+ !$omp target uses_allocators(traits(tr): my_alloc)
+ x = 1
+ !$omp end target
+end subroutine
+
+subroutine uses_allocators_combined_conflicts
+ use omp_lib
+ integer(omp_allocator_handle_kind) :: private_alloc, shared_alloc, fp_alloc
+ integer(omp_allocator_handle_kind) :: map_alloc, last_alloc, reduction_alloc
+ integer :: i, x(10)
+
+ !$omp target teams distribute parallel do uses_allocators(private_alloc) private(private_alloc)
+ do i = 1, 10
+ x(i) = i
+ end do
+
+ !$omp target teams distribute parallel do uses_allocators(shared_alloc) shared(shared_alloc)
+ do i = 1, 10
+ x(i) = i
+ end do
+
+ !ERROR: An allocator in a USES_ALLOCATORS clause cannot also appear in the FIRSTPRIVATE clause on the same construct
+ !$omp target teams distribute parallel do uses_allocators(fp_alloc) firstprivate(fp_alloc)
+ do i = 1, 10
+ x(i) = i
+ end do
+
+ !ERROR: An allocator in a USES_ALLOCATORS clause cannot also appear in the MAP clause on the same construct
+ !$omp target teams distribute parallel do uses_allocators(map_alloc) map(tofrom: map_alloc)
+ do i = 1, 10
+ x(i) = i
+ end do
+
+ !ERROR: An allocator in a USES_ALLOCATORS clause cannot also appear in the LASTPRIVATE clause on the same construct
+ !$omp target teams distribute parallel do uses_allocators(last_alloc) lastprivate(last_alloc)
+ do i = 1, 10
+ x(i) = i
+ end do
+
+ !ERROR: An allocator in a USES_ALLOCATORS clause cannot also appear in the REDUCTION clause on the same construct
+ !$omp target teams distribute parallel do uses_allocators(reduction_alloc) reduction(+: reduction_alloc)
+ do i = 1, 10
+ x(i) = i
+ end do
+end subroutine
+
subroutine uses_allocators_conflicting_clauses
use omp_lib
! Each case uses its own allocator so that the once-per-symbol
>From 1b14f92b0edd688048e345780a33c92e605f75c6 Mon Sep 17 00:00:00 2001
From: Sairudra More <moresair at pe31.hpc.amslabs.hpecorp.net>
Date: Mon, 10 Aug 2026 22:35:12 -0500
Subject: [PATCH 6/7] [flang][OpenMP] Address USES_ALLOCATORS review feedback
Use version-aware memory-space recognition and authoritative TARGET clause metadata. Drop the representation-dependent allocator-handle kind diagnostic.
---
flang/lib/Semantics/check-omp-structure.cpp | 129 ++++++------------
.../OpenMP/uses-allocators-handle-kind.f90 | 32 -----
.../OpenMP/uses-allocators-no-omp-lib.f90 | 8 +-
.../test/Semantics/OpenMP/uses-allocators.f90 | 6 +-
4 files changed, 49 insertions(+), 126 deletions(-)
delete mode 100644 flang/test/Semantics/OpenMP/uses-allocators-handle-kind.f90
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index d94f63eff5445..9244f98de6dfb 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5395,14 +5395,30 @@ static constexpr std::string_view predefinedAllocatorNames[]{
"omp_high_bw_mem_alloc", "omp_low_lat_mem_alloc", "omp_cgroup_mem_alloc",
"omp_pteam_mem_alloc", "omp_thread_mem_alloc"};
-static constexpr std::string_view predefinedMemSpaceNames[]{
- "omp_default_mem_space", "omp_large_cap_mem_space", "omp_const_mem_space",
- "omp_high_bw_mem_space", "omp_low_lat_mem_space"};
-
// omp_null_allocator and omp_null_mem_space are not themselves predefined
// handles, but [6.0:315-316] gives each its own allowance.
static constexpr std::string_view nullAllocatorName[]{"omp_null_allocator"};
-static constexpr std::string_view nullMemSpaceName[]{"omp_null_mem_space"};
+
+struct UsesAllocatorsMemSpaceName {
+ std::string_view name;
+ unsigned since;
+};
+
+static constexpr UsesAllocatorsMemSpaceName usesAllocatorsMemSpaceNames[]{
+ {"omp_default_mem_space", 50},
+ {"omp_large_cap_mem_space", 50},
+ {"omp_const_mem_space", 50},
+ {"omp_high_bw_mem_space", 50},
+ {"omp_low_lat_mem_space", 50},
+ {"omp_null_mem_space", 60},
+};
+
+static bool IsUsesAllocatorsMemSpaceName(
+ const parser::Name &name, unsigned version) {
+ return llvm::any_of(usesAllocatorsMemSpaceNames, [&](const auto &candidate) {
+ return version >= candidate.since && name.ToString() == candidate.name;
+ });
+}
// Whether the ultimate symbol is an entity of the intrinsic omp_lib module
// shipped with the compiler, as opposed to a same-named entity of a
@@ -5427,7 +5443,7 @@ static bool IsIntrinsicOmpAlloctrait(
&derived.typeSymbol() == &symbol->GetUltimate();
}
-// Recognition of a predefined allocator or memory space differs by version.
+// Recognition of a predefined allocator differs by version.
//
// [5.2:182] asks whether the allocator *is* a predefined allocator, so it
// identifies the entity: a use-associated rename of the intrinsic omp_lib
@@ -5452,86 +5468,36 @@ static bool IsPredefinedHandle(const parser::Name &name,
return false;
}
-// The integer kind of an OpenMP allocator or memory-space handle, which
-// omp_lib declares as c_intptr_t. iso_c_binding is an intrinsic module and is
-// read on demand, so the kind is available even when the source uses neither
-// module.
-static std::optional<std::int64_t> GetOmpHandleKind(SemanticsContext &context) {
- const Scope *scope{context.GetBuiltinModule("iso_c_binding")};
- if (!scope) {
- return std::nullopt;
- }
- const Symbol *kindSymbol{
- scope->FindSymbol(SourceName{"c_intptr_t", std::strlen("c_intptr_t")})};
- if (!kindSymbol) {
- return std::nullopt;
- }
- const auto *object{
- kindSymbol->GetUltimate().detailsIf<ObjectEntityDetails>()};
- const auto *init{object ? &object->init() : nullptr};
- return init && *init ? evaluate::ToInt64(**init) : std::nullopt;
-}
-
-// Whether `symbol` has the integer kind that omp_lib gives its handles. The
-// check is skipped for a non-integer allocator, whose type is diagnosed
-// separately.
-static bool HasOmpHandleKind(
- const Symbol &symbol, SemanticsContext &context, std::int64_t &expected) {
- const DeclTypeSpec *type{symbol.GetUltimate().GetType()};
- if (!type || !type->IsNumeric(TypeCategory::Integer)) {
- return true;
- }
- auto want{GetOmpHandleKind(context)};
- if (!want) {
- return true;
- }
- expected = *want;
- auto got{evaluate::ToInt64(type->numericTypeSpec().kind())};
- return !got || *got == *want;
-}
-
-static bool ClauseHasTargetEffect(
- llvm::omp::Directive directive, llvm::omp::Clause clause) {
+static bool ClauseHasTargetEffect(llvm::omp::Directive directive,
+ llvm::omp::Clause clause, unsigned version) {
llvm::ArrayRef<llvm::omp::Directive> leafs{
llvm::omp::getLeafConstructsOrSelf(directive)};
if (!llvm::is_contained(leafs, llvm::omp::Directive::OMPD_target)) {
return false;
}
if (leafs.size() == 1) {
- return true;
+ return llvm::omp::isAllowedClauseForDirective(
+ llvm::omp::Directive::OMPD_target, clause, version);
}
- // Keep this narrow mirror synchronized with the authoritative distribution
- // rules in llvm/include/llvm/Frontend/OpenMP/ConstructDecompositionT.h.
+ // Compound distribution can override direct TARGET clause permission.
switch (clause) {
case llvm::omp::Clause::OMPC_private:
- // [5.2:340:1-2] Applies to the innermost permitting leaf.
- return false;
case llvm::omp::Clause::OMPC_shared:
- // [5.2:340:31-32] TARGET is not a permitting leaf.
return false;
case llvm::omp::Clause::OMPC_firstprivate:
- // [5.2:340:3-14] Applies to TARGET.
- return true;
- case llvm::omp::Clause::OMPC_map:
- return true;
case llvm::omp::Clause::OMPC_lastprivate:
- // [5.2:340:21-30] Synthesizes TARGET map(tofrom).
- return true;
case llvm::omp::Clause::OMPC_reduction:
- // [5.2:341:11-13] Synthesizes TARGET map(tofrom).
- return true;
case llvm::omp::Clause::OMPC_linear:
- // [5.2:341:15-22] Creates outer FIRSTPRIVATE/LASTPRIVATE state.
- return true;
- case llvm::omp::Clause::OMPC_in_reduction:
- case llvm::omp::Clause::OMPC_is_device_ptr:
- case llvm::omp::Clause::OMPC_has_device_addr:
- // [5.2:339-341] These clauses are TARGET-owned.
return true;
default:
- return true;
+ break;
}
+
+ return (llvm::omp::isDataSharingAttributeClause(clause, version) ||
+ clause == llvm::omp::Clause::OMPC_map) &&
+ llvm::omp::isAllowedClauseForDirective(
+ llvm::omp::Directive::OMPD_target, clause, version);
}
void OmpStructureChecker::CheckUsesAllocatorsSpec(
@@ -5592,21 +5558,14 @@ void OmpStructureChecker::CheckUsesAllocatorsSpec(
context_.Say(allocatorSource,
"A non-predefined allocator '%s' in a USES_ALLOCATORS clause must be a variable"_err_en_US,
allocatorName->ToString());
- } else if (std::int64_t kind{0};
- !HasOmpHandleKind(*symbol, context_, kind)) {
- // [5.2:181], [6.0:315] The allocator argument is an expression of
- // allocator_handle type.
- context_.Say(allocatorSource,
- "The allocator '%s' in a USES_ALLOCATORS clause must be of type INTEGER(KIND=%jd), i.e. OMP_ALLOCATOR_HANDLE_KIND"_err_en_US,
- allocatorName->ToString(), static_cast<std::intmax_t>(kind));
}
// [5.0:175], [5.1:203] Non-predefined allocators appearing in a
// uses_allocators clause must have traits specified. The requirement was
// removed in 5.2, where omitted traits mean an empty traits array.
if (version < 52 && !traits) {
context_.Say(allocatorSource,
- "A non-predefined allocator '%s' in a USES_ALLOCATORS clause must have traits specified in OpenMP v%d.%d"_err_en_US,
- allocatorName->ToString(), version / 10, version % 10);
+ "A non-predefined allocator '%s' in a USES_ALLOCATORS clause must have traits specified in %s"_err_en_US,
+ allocatorName->ToString(), ThisVersion(version));
}
}
@@ -5624,7 +5583,7 @@ void OmpStructureChecker::CheckUsesAllocatorsSpec(
id != llvm::omp::Clause::OMPC_map) {
continue;
}
- if (!ClauseHasTargetEffect(GetContext().directive, id)) {
+ if (!ClauseHasTargetEffect(GetContext().directive, id, version)) {
continue;
}
const parser::OmpObjectList *objects{GetOmpObjectList(clause)};
@@ -5652,15 +5611,13 @@ void OmpStructureChecker::CheckUsesAllocatorsSpec(
parser::CharBlock memSpaceSource{OmpGetModifierSource(modifiers, memSpace)};
const parser::Name *memSpaceName{
parser::Unwrap<parser::Name>(memSpaceExpr)};
- bool ok{memSpaceName &&
- (llvm::is_contained(
- predefinedMemSpaceNames, memSpaceName->ToString()) ||
- (version >= 60 &&
- llvm::is_contained(
- nullMemSpaceName, memSpaceName->ToString())))};
+ bool ok{
+ memSpaceName && IsUsesAllocatorsMemSpaceName(*memSpaceName, version)};
if (!ok) {
+ auto name{OmpGetDescriptor<parser::OmpMemSpace>().name};
context_.Say(memSpaceSource,
- "The MEMSPACE modifier must name a predefined memory space"_err_en_US);
+ "The '%s' modifier must name a predefined memory space"_err_en_US,
+ name.str());
}
}
@@ -5763,8 +5720,8 @@ void OmpStructureChecker::Enter(const parser::OmpClause::UsesAllocators &x) {
// [5.2:181] uses_allocators takes a single clause-argument-specification,
// and only the deprecated list syntax may repeat it.
context_.Say(GetContext().clauseSource,
- "The USES_ALLOCATORS clause accepts a single allocator specification in OpenMP v%d.%d"_err_en_US,
- version / 10, version % 10);
+ "The USES_ALLOCATORS clause accepts a single allocator specification in %s"_err_en_US,
+ ThisVersion(version));
} else if (version >= 52 && isLegacyList) {
// [5.2:181] The comma-separated "allocator[(traits)]" list syntax has been
// deprecated.
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-handle-kind.f90 b/flang/test/Semantics/OpenMP/uses-allocators-handle-kind.f90
deleted file mode 100644
index e1d693c69df45..0000000000000
--- a/flang/test/Semantics/OpenMP/uses-allocators-handle-kind.f90
+++ /dev/null
@@ -1,32 +0,0 @@
-! RUN: not %flang_fc1 -fopenmp -fopenmp-version=50 -fsyntax-only %s 2>&1 | FileCheck %s
-! RUN: not %flang_fc1 -fopenmp -fopenmp-version=51 -fsyntax-only %s 2>&1 | FileCheck %s
-! RUN: not %flang_fc1 -fopenmp -fopenmp-version=52 -fsyntax-only %s 2>&1 | FileCheck %s
-! RUN: not %flang_fc1 -fopenmp -fopenmp-version=60 -fsyntax-only %s 2>&1 | FileCheck %s
-
-! [5.2:181], [6.0:315] The allocator argument of USES_ALLOCATORS is an
-! expression of allocator_handle type, which omp_lib declares as c_intptr_t.
-! The rule holds whether or not the source uses omp_lib, so none of these
-! subroutines imports it. The expected kind is matched as a number because it
-! is the target's C_INTPTR_T kind.
-
-! CHECK: error: The allocator 'wrong_kind_alloc' in a USES_ALLOCATORS clause must be of type INTEGER(KIND={{[0-9]+}}), i.e. OMP_ALLOCATOR_HANDLE_KIND
-subroutine uses_allocators_wrong_handle_kind
- ! Deliberately a kind that cannot be C_INTPTR_T on any supported target.
- integer(kind=2) :: wrong_kind_alloc
- integer :: x
- !$omp target uses_allocators(wrong_kind_alloc)
- x = 1
- !$omp end target
-end subroutine
-
-! A REAL allocator is diagnosed for its type; the kind rule must not pile a
-! second diagnostic onto the same allocator.
-! CHECK: error: Must have INTEGER type, but is REAL(4)
-! CHECK-NOT: 'not_an_integer' in a USES_ALLOCATORS clause must be of type
-subroutine uses_allocators_real_allocator
- real :: not_an_integer
- integer :: x
- !$omp target uses_allocators(not_an_integer)
- x = 1
- !$omp end target
-end subroutine
diff --git a/flang/test/Semantics/OpenMP/uses-allocators-no-omp-lib.f90 b/flang/test/Semantics/OpenMP/uses-allocators-no-omp-lib.f90
index a420ba83badbd..73db9aa54446b 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators-no-omp-lib.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators-no-omp-lib.f90
@@ -1,13 +1,11 @@
! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=52
! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=60
-! The allocator_handle kind is the target's C_INTPTR_T, so a correctly typed
-! allocator is accepted without importing omp_lib. This file deliberately does
-! not USE omp_lib and so needs no OpenMP runtime modules.
+! A scalar integer allocator is accepted without importing omp_lib. This file
+! deliberately uses a kind other than the kind in the compiler's omp_lib.
subroutine uses_allocators_without_omp_lib
- use iso_c_binding, only: c_intptr_t
- integer(c_intptr_t) :: my_alloc
+ integer(kind=2) :: my_alloc
integer :: x
!$omp target uses_allocators(my_alloc)
diff --git a/flang/test/Semantics/OpenMP/uses-allocators.f90 b/flang/test/Semantics/OpenMP/uses-allocators.f90
index 2974e9d6f2407..bff2e04104708 100644
--- a/flang/test/Semantics/OpenMP/uses-allocators.f90
+++ b/flang/test/Semantics/OpenMP/uses-allocators.f90
@@ -127,12 +127,12 @@ subroutine uses_allocators_errors
!$omp end target
! The memspace-handle must name a predefined memory space.
- !ERROR: The MEMSPACE modifier must name a predefined memory space
+ !ERROR: The 'mem-space' modifier must name a predefined memory space
!$omp target uses_allocators(memspace(omp_null_mem_space): my_alloc)
x = 6
!$omp end target
- !ERROR: The MEMSPACE modifier must name a predefined memory space
+ !ERROR: The 'mem-space' modifier must name a predefined memory space
!$omp target uses_allocators(memspace(my_space): my_alloc)
x = 7
!$omp end target
@@ -360,7 +360,7 @@ subroutine uses_allocators_predefined_identity
!$omp end target
! A memory space must use the written name of a predefined memory space.
- !ERROR: The MEMSPACE modifier must name a predefined memory space
+ !ERROR: The 'mem-space' modifier must name a predefined memory space
!$omp target uses_allocators(memspace(renamed_space): my_alloc)
x = 4
!$omp end target
>From 042c8841307214bc57272fe2ce7a3b39bc7b94f1 Mon Sep 17 00:00:00 2001
From: Sairudra More <moresair at pe31.hpc.amslabs.hpecorp.net>
Date: Tue, 11 Aug 2026 09:05:42 -0500
Subject: [PATCH 7/7] [flang][OpenMP] Use GetUpperName in USES_ALLOCATORS
diagnostic
---
flang/lib/Semantics/check-omp-structure.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 9244f98de6dfb..cd1e83b7c7dea 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5595,7 +5595,7 @@ void OmpStructureChecker::CheckUsesAllocatorsSpec(
other == &ultimate) {
context_.Say(allocatorSource,
"An allocator in a USES_ALLOCATORS clause cannot also appear in the %s clause on the same construct"_err_en_US,
- parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)));
+ parser::omp::GetUpperName(id, version));
break;
}
}
More information about the flang-commits
mailing list