aboutsummaryrefslogtreecommitdiff
path: root/flang/lib/Semantics
diff options
context:
space:
mode:
Diffstat (limited to 'flang/lib/Semantics')
-rw-r--r--flang/lib/Semantics/canonicalize-omp.cpp156
-rw-r--r--flang/lib/Semantics/check-omp-structure.cpp703
-rw-r--r--flang/lib/Semantics/check-omp-structure.h37
-rw-r--r--flang/lib/Semantics/openmp-utils.cpp17
-rw-r--r--flang/lib/Semantics/resolve-directives.cpp59
-rw-r--r--flang/lib/Semantics/resolve-names.cpp4
6 files changed, 569 insertions, 407 deletions
diff --git a/flang/lib/Semantics/canonicalize-omp.cpp b/flang/lib/Semantics/canonicalize-omp.cpp
index c884658..a11c525 100644
--- a/flang/lib/Semantics/canonicalize-omp.cpp
+++ b/flang/lib/Semantics/canonicalize-omp.cpp
@@ -51,8 +51,6 @@ public:
} // Block list
}
- void Post(parser::ExecutionPart &body) { RewriteOmpAllocations(body); }
-
// Pre-visit all constructs that have both a specification part and
// an execution part, and store the connection between the two.
bool Pre(parser::BlockConstruct &x) {
@@ -88,6 +86,7 @@ public:
void Post(parser::SpecificationPart &spec) {
CanonicalizeUtilityConstructs(spec);
+ CanonicalizeAllocateDirectives(spec);
}
void Post(parser::OmpMapClause &map) { CanonicalizeMapModifiers(map); }
@@ -239,33 +238,138 @@ private:
}
}
- void RewriteOmpAllocations(parser::ExecutionPart &body) {
- // Rewrite leading declarative allocations so they are nested
- // within their respective executable allocate directive
- //
- // Original:
- // ExecutionPartConstruct -> OpenMPDeclarativeAllocate
- // ExecutionPartConstruct -> OpenMPDeclarativeAllocate
- // ExecutionPartConstruct -> OpenMPExecutableAllocate
- //
- // After rewriting:
- // ExecutionPartConstruct -> OpenMPExecutableAllocate
- // ExecutionPartConstruct -> OpenMPDeclarativeAllocate
- // ExecutionPartConstruct -> OpenMPDeclarativeAllocate
- for (auto it = body.v.rbegin(); it != body.v.rend();) {
- if (auto *exec = GetOmpIf<parser::OpenMPExecutableAllocate>(*(it++))) {
- parser::OpenMPDeclarativeAllocate *decl;
- std::list<parser::OpenMPDeclarativeAllocate> subAllocates;
- while (it != body.v.rend() &&
- (decl = GetOmpIf<parser::OpenMPDeclarativeAllocate>(*it))) {
- subAllocates.push_front(std::move(*decl));
- it = decltype(it)(body.v.erase(std::next(it).base()));
+ // Canonicalization of allocate directives
+ //
+ // In OpenMP 5.0 and 5.1 the allocate directive could either be a declarative
+ // one or an executable one. As usual in such cases, this poses a problem
+ // when the directive appears at the boundary between the specification part
+ // and the execution part.
+ // The executable form can actually consist of several adjacent directives,
+ // whereas the declarative form is always standalone. Additionally, the
+ // executable form must be associated with an allocate statement.
+ //
+ // The parser tries to parse declarative statements first, so in the
+ // following case, the two directives will be declarative, even though
+ // they should be treated as a single executable form:
+ // integer, allocatable :: x, y ! Specification
+ // !$omp allocate(x)
+ // !$omp allocate(y)
+ // allocate(x, y) ! Execution
+ //
+ void CanonicalizeAllocateDirectives(parser::SpecificationPart &spec) {
+ auto found = blockForSpec_.find(&spec);
+ if (found == blockForSpec_.end()) {
+ // There is no corresponding execution part, so there is nothing to do.
+ return;
+ }
+ parser::Block &block = *found->second;
+
+ auto isAllocateStmt = [](const parser::ExecutionPartConstruct &epc) {
+ if (auto *ec = std::get_if<parser::ExecutableConstruct>(&epc.u)) {
+ if (auto *as =
+ std::get_if<parser::Statement<parser::ActionStmt>>(&ec->u)) {
+ return std::holds_alternative<
+ common::Indirection<parser::AllocateStmt>>(as->statement.u);
+ }
+ }
+ return false;
+ };
+
+ if (!block.empty() && isAllocateStmt(block.front())) {
+ // There are two places where an OpenMP declarative construct can
+ // show up in the tuple in specification part:
+ // (1) in std::list<OpenMPDeclarativeConstruct>, or
+ // (2) in std::list<DeclarationConstruct>.
+ // The case (1) is only possible if the list (2) is empty.
+
+ auto &omps =
+ std::get<std::list<parser::OpenMPDeclarativeConstruct>>(spec.t);
+ auto &decls = std::get<std::list<parser::DeclarationConstruct>>(spec.t);
+
+ if (!decls.empty()) {
+ MakeExecutableAllocateFromDecls(decls, block);
+ } else {
+ MakeExecutableAllocateFromOmps(omps, block);
+ }
+ }
+ }
+
+ parser::ExecutionPartConstruct EmbedInExec(
+ parser::OmpAllocateDirective *alo, parser::ExecutionPartConstruct &&epc) {
+ // Nest current epc inside the allocate directive.
+ std::get<parser::Block>(alo->t).push_front(std::move(epc));
+ // Set the new epc to be the ExecutionPartConstruct made from
+ // the allocate directive.
+ parser::OpenMPConstruct opc(std::move(*alo));
+ common::Indirection<parser::OpenMPConstruct> ind(std::move(opc));
+ parser::ExecutableConstruct ec(std::move(ind));
+ return parser::ExecutionPartConstruct(std::move(ec));
+ }
+
+ void MakeExecutableAllocateFromDecls(
+ std::list<parser::DeclarationConstruct> &decls, parser::Block &body) {
+ using OpenMPDeclarativeConstruct =
+ common::Indirection<parser::OpenMPDeclarativeConstruct>;
+
+ auto getAllocate = [](parser::DeclarationConstruct *dc) {
+ if (auto *sc = std::get_if<parser::SpecificationConstruct>(&dc->u)) {
+ if (auto *odc = std::get_if<OpenMPDeclarativeConstruct>(&sc->u)) {
+ if (auto *alo =
+ std::get_if<parser::OmpAllocateDirective>(&odc->value().u)) {
+ return alo;
+ }
+ }
+ }
+ return static_cast<parser::OmpAllocateDirective *>(nullptr);
+ };
+
+ std::list<parser::DeclarationConstruct>::reverse_iterator rlast = [&]() {
+ for (auto rit = decls.rbegin(), rend = decls.rend(); rit != rend; ++rit) {
+ if (getAllocate(&*rit) == nullptr) {
+ return rit;
}
- if (!subAllocates.empty()) {
- std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>(
- exec->t) = {std::move(subAllocates)};
+ }
+ return decls.rend();
+ }();
+
+ if (rlast != decls.rbegin()) {
+ // We have already checked that the first statement in body is
+ // ALLOCATE.
+ parser::ExecutionPartConstruct epc(std::move(body.front()));
+ for (auto rit = decls.rbegin(); rit != rlast; ++rit) {
+ epc = EmbedInExec(getAllocate(&*rit), std::move(epc));
+ }
+
+ body.pop_front();
+ body.push_front(std::move(epc));
+ decls.erase(rlast.base(), decls.end());
+ }
+ }
+
+ void MakeExecutableAllocateFromOmps(
+ std::list<parser::OpenMPDeclarativeConstruct> &omps,
+ parser::Block &body) {
+ using OpenMPDeclarativeConstruct = parser::OpenMPDeclarativeConstruct;
+
+ std::list<OpenMPDeclarativeConstruct>::reverse_iterator rlast = [&]() {
+ for (auto rit = omps.rbegin(), rend = omps.rend(); rit != rend; ++rit) {
+ if (!std::holds_alternative<parser::OmpAllocateDirective>(rit->u)) {
+ return rit;
}
}
+ return omps.rend();
+ }();
+
+ if (rlast != omps.rbegin()) {
+ parser::ExecutionPartConstruct epc(std::move(body.front()));
+ for (auto rit = omps.rbegin(); rit != rlast; ++rit) {
+ epc = EmbedInExec(
+ &std::get<parser::OmpAllocateDirective>(rit->u), std::move(epc));
+ }
+
+ body.pop_front();
+ body.push_front(std::move(epc));
+ omps.erase(rlast.base(), omps.end());
}
}
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index aaaf1ec..d7db15d 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -179,29 +179,21 @@ void OmpStructureChecker::Leave(const parser::BlockConstruct &x) {
}
}
-// Use when clause falls under 'struct OmpClause' in 'parse-tree.h'.
-#define CHECK_SIMPLE_CLAUSE(X, Y) \
- void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \
- CheckAllowedClause(llvm::omp::Clause::Y); \
- }
+void OmpStructureChecker::Enter(const parser::SpecificationPart &) {
+ partStack_.push_back(PartKind::SpecificationPart);
+}
-#define CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(X, Y) \
- void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
- CheckAllowedClause(llvm::omp::Clause::Y); \
- RequiresConstantPositiveParameter(llvm::omp::Clause::Y, c.v); \
- }
+void OmpStructureChecker::Leave(const parser::SpecificationPart &) {
+ partStack_.pop_back();
+}
-#define CHECK_REQ_SCALAR_INT_CLAUSE(X, Y) \
- void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
- CheckAllowedClause(llvm::omp::Clause::Y); \
- RequiresPositiveParameter(llvm::omp::Clause::Y, c.v); \
- }
+void OmpStructureChecker::Enter(const parser::ExecutionPart &) {
+ partStack_.push_back(PartKind::ExecutionPart);
+}
-// Use when clause don't falls under 'struct OmpClause' in 'parse-tree.h'.
-#define CHECK_SIMPLE_PARSER_CLAUSE(X, Y) \
- void OmpStructureChecker::Enter(const parser::X &) { \
- CheckAllowedClause(llvm::omp::Y); \
- }
+void OmpStructureChecker::Leave(const parser::ExecutionPart &) {
+ partStack_.pop_back();
+}
// 'OmpWorkshareBlockChecker' is used to check the validity of the assignment
// statements and the expressions enclosed in an OpenMP Workshare construct
@@ -667,49 +659,6 @@ void OmpStructureChecker::HasInvalidTeamsNesting(
}
}
-void OmpStructureChecker::CheckPredefinedAllocatorRestriction(
- const parser::CharBlock &source, const parser::Name &name) {
- if (const auto *symbol{name.symbol}) {
- const auto *commonBlock{FindCommonBlockContaining(*symbol)};
- const auto &scope{context_.FindScope(symbol->name())};
- const Scope &containingScope{GetProgramUnitContaining(scope)};
- if (!isPredefinedAllocator &&
- (IsSaved(*symbol) || commonBlock ||
- containingScope.kind() == Scope::Kind::Module)) {
- context_.Say(source,
- "If list items within the %s directive have the "
- "SAVE attribute, are a common block name, or are "
- "declared in the scope of a module, then only "
- "predefined memory allocator parameters can be used "
- "in the allocator clause"_err_en_US,
- ContextDirectiveAsFortran());
- }
- }
-}
-
-void OmpStructureChecker::CheckPredefinedAllocatorRestriction(
- const parser::CharBlock &source,
- const parser::OmpObjectList &ompObjectList) {
- for (const auto &ompObject : ompObjectList.v) {
- common::visit(
- common::visitors{
- [&](const parser::Designator &designator) {
- if (const auto *dataRef{
- std::get_if<parser::DataRef>(&designator.u)}) {
- if (const auto *name{std::get_if<parser::Name>(&dataRef->u)}) {
- CheckPredefinedAllocatorRestriction(source, *name);
- }
- }
- },
- [&](const parser::Name &name) {
- CheckPredefinedAllocatorRestriction(source, name);
- },
- [&](const parser::OmpObject::Invalid &invalid) {},
- },
- ompObject.u);
- }
-}
-
void OmpStructureChecker::Enter(const parser::OmpClause::Hint &x) {
CheckAllowedClause(llvm::omp::Clause::OMPC_hint);
auto &dirCtx{GetContext()};
@@ -763,18 +712,10 @@ template <typename Checker> struct DirectiveSpellingVisitor {
return std::get<parser::OmpBeginDirective>(t).DirName();
}
- bool Pre(const parser::OpenMPDeclarativeAllocate &x) {
- checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_allocate);
- return false;
- }
bool Pre(const parser::OpenMPDispatchConstruct &x) {
checker_(GetDirName(x.t).source, Directive::OMPD_dispatch);
return false;
}
- bool Pre(const parser::OpenMPExecutableAllocate &x) {
- checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_allocate);
- return false;
- }
bool Pre(const parser::OpenMPAllocatorsConstruct &x) {
checker_(GetDirName(x.t).source, Directive::OMPD_allocators);
return false;
@@ -1710,12 +1651,39 @@ void OmpStructureChecker::Leave(const parser::OpenMPRequiresConstruct &) {
dirContext_.pop_back();
}
-void OmpStructureChecker::CheckAllocateDirective(parser::CharBlock source,
- const parser::OmpObjectList &objects,
- const parser::OmpClauseList &clauses) {
- const Scope &thisScope{context_.FindScope(source)};
- SymbolSourceMap symbols;
- GetSymbolsInObjectList(objects, symbols);
+static std::pair<const parser::AllocateStmt *, parser::CharBlock>
+getAllocateStmtAndSource(const parser::ExecutionPartConstruct *epc) {
+ if (SourcedActionStmt as{GetActionStmt(epc)}) {
+ using IndirectionAllocateStmt = common::Indirection<parser::AllocateStmt>;
+ if (auto *indirect{std::get_if<IndirectionAllocateStmt>(&as.stmt->u)}) {
+ return {&indirect->value(), as.source};
+ }
+ }
+ return {nullptr, ""};
+}
+
+// Collect symbols that correspond to non-component objects on the
+// ALLOCATE statement.
+static UnorderedSymbolSet GetNonComponentSymbols(
+ const parser::AllocateStmt &stmt) {
+ UnorderedSymbolSet symbols;
+ for (auto &alloc : std::get<std::list<parser::Allocation>>(stmt.t)) {
+ auto &object{std::get<parser::AllocateObject>(alloc.t)};
+ if (auto *name{std::get_if<parser::Name>(&object.u)}) {
+ if (name->symbol) {
+ symbols.insert(name->symbol->GetUltimate());
+ }
+ }
+ }
+ return symbols;
+}
+
+void OmpStructureChecker::CheckIndividualAllocateDirective(
+ const parser::OmpAllocateDirective &x, bool isExecutable) {
+ const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
+ const parser::OmpDirectiveName &dirName{beginSpec.DirName()};
+
+ const Scope &thisScope{context_.FindScope(dirName.source)};
auto maybeHasPredefinedAllocator{[&](const parser::OmpClause *calloc) {
// Return "true" if the ALLOCATOR clause was provided with an argument
@@ -1741,73 +1709,200 @@ void OmpStructureChecker::CheckAllocateDirective(parser::CharBlock source,
return true;
}};
- const auto *allocator{FindClause(llvm::omp::Clause::OMPC_allocator)};
+ const auto *allocator{[&]() {
+ // Can't use FindClause in Enter (because clauses haven't been visited
+ // yet).
+ for (const parser::OmpClause &c : beginSpec.Clauses().v) {
+ if (c.Id() == llvm::omp::Clause::OMPC_allocator) {
+ return &c;
+ }
+ }
+ return static_cast<const parser::OmpClause *>(nullptr);
+ }()};
+
if (InTargetRegion()) {
bool hasDynAllocators{
HasRequires(llvm::omp::Clause::OMPC_dynamic_allocators)};
if (!allocator && !hasDynAllocators) {
- context_.Say(source,
+ context_.Say(dirName.source,
"An ALLOCATE directive in a TARGET region must specify an ALLOCATOR clause or REQUIRES(DYNAMIC_ALLOCATORS) must be specified"_err_en_US);
}
}
auto maybePredefined{maybeHasPredefinedAllocator(allocator)};
- for (auto &[symbol, source] : symbols) {
- if (!inExecutableAllocate_) {
- if (symbol->owner() != thisScope) {
+ unsigned version{context_.langOptions().OpenMPVersion};
+ std::string condStr{version == 50
+ ? "a named common block, has SAVE attribute or is declared in the "
+ "scope of a module"
+ : "a named common block or has SAVE attribute"};
+
+ auto checkSymbol{[&](const Symbol &symbol, parser::CharBlock source) {
+ if (!isExecutable) {
+ // For structure members, the scope is the derived type, which is
+ // never "this" scope. Ignore this check for members, they will be
+ // flagged anyway.
+ if (symbol.owner() != thisScope && !IsStructureComponent(symbol)) {
context_.Say(source,
"A list item on a declarative ALLOCATE must be declared in the same scope in which the directive appears"_err_en_US);
}
- if (IsPointer(*symbol) || IsAllocatable(*symbol)) {
+ if (IsPointer(symbol) || IsAllocatable(symbol)) {
context_.Say(source,
"A list item in a declarative ALLOCATE cannot have the ALLOCATABLE or POINTER attribute"_err_en_US);
}
}
- if (symbol->GetUltimate().has<AssocEntityDetails>()) {
+ if (symbol.GetUltimate().has<AssocEntityDetails>()) {
context_.Say(source,
"A list item in a declarative ALLOCATE cannot be an associate name"_err_en_US);
}
- if (symbol->attrs().test(Attr::SAVE) || IsCommonBlock(*symbol)) {
+ bool inModule{
+ version == 50 && symbol.owner().kind() == Scope::Kind::Module};
+ if (symbol.attrs().test(Attr::SAVE) || IsCommonBlock(symbol) || inModule) {
if (!allocator) {
context_.Say(source,
- "If a list item is a named common block or has SAVE attribute, an ALLOCATOR clause must be present with a predefined allocator"_err_en_US);
+ "If a list item is %s, an ALLOCATOR clause must be present with a predefined allocator"_err_en_US,
+ condStr);
} else if (!maybePredefined) {
context_.Say(source,
- "If a list item is a named common block or has SAVE attribute, only a predefined allocator may be used on the ALLOCATOR clause"_err_en_US);
+ "If a list item is %s, only a predefined allocator may be used on the ALLOCATOR clause"_err_en_US,
+ condStr);
}
}
- if (FindCommonBlockContaining(*symbol)) {
+ if (FindCommonBlockContaining(symbol)) {
context_.Say(source,
"A variable that is part of a common block may not be specified as a list item in an ALLOCATE directive, except implicitly via the named common block"_err_en_US);
}
+ }};
+
+ for (const parser::OmpArgument &arg : beginSpec.Arguments().v) {
+ const parser::OmpObject *object{GetArgumentObject(arg)};
+ if (!object) {
+ context_.Say(arg.source,
+ "An argument to ALLOCATE directive must be a variable list item"_err_en_US);
+ continue;
+ }
+
+ if (const Symbol *symbol{GetObjectSymbol(*object)}) {
+ if (!IsTypeParamInquiry(*symbol)) {
+ checkSymbol(*symbol, arg.source);
+ }
+ CheckVarIsNotPartOfAnotherVar(dirName.source, *object);
+ }
}
- CheckVarIsNotPartOfAnotherVar(source, objects);
}
-void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) {
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
+void OmpStructureChecker::CheckExecutableAllocateDirective(
+ const parser::OmpAllocateDirective &x) {
+ parser::omp::OmpAllocateInfo info{SplitOmpAllocate(x)};
+
+ auto [allocStmt, allocSource]{getAllocateStmtAndSource(info.body)};
+ if (!allocStmt) {
+ // This has been diagnosed already.
+ return;
+ }
+
+ UnorderedSymbolSet allocateSyms{GetNonComponentSymbols(*allocStmt)};
+ SymbolSourceMap directiveSyms;
+ bool hasEmptyList{false};
+
+ for (const parser::OmpAllocateDirective *ompAlloc : info.dirs) {
+ const parser::OmpDirectiveSpecification &spec{DEREF(ompAlloc).BeginDir()};
+ if (spec.Arguments().v.empty()) {
+ if (hasEmptyList && info.dirs.size() > 1) {
+ context_.Say(spec.DirName().source,
+ "If multiple directives are present in an executable ALLOCATE directive, at most one of them may specify no list items"_err_en_US);
+ }
+ hasEmptyList = true;
+ }
+ for (const parser::OmpArgument &arg : spec.Arguments().v) {
+ if (auto *sym{GetArgumentSymbol(arg)}) {
+ // Ignore these checks for structure members. They are not allowed
+ // in the first place, so don't tell the users that they need to
+ // be specified somewhere,
+ if (IsStructureComponent(*sym)) {
+ continue;
+ }
+ if (auto f{directiveSyms.find(sym)}; f != directiveSyms.end()) {
+ parser::MessageFormattedText txt(
+ "A list item on an executable ALLOCATE may only be specified once"_err_en_US);
+ parser::Message message(arg.source, txt);
+ message.Attach(f->second, "The list item was specified here"_en_US);
+ context_.Say(std::move(message));
+ } else {
+ directiveSyms.insert(std::make_pair(sym, arg.source));
+ }
+
+ if (auto f{allocateSyms.find(*sym)}; f == allocateSyms.end()) {
+ context_
+ .Say(arg.source,
+ "A list item on an executable ALLOCATE must be specified on the associated ALLOCATE statement"_err_en_US)
+ .Attach(allocSource, "The ALLOCATE statement"_en_US);
+ }
+ }
+ }
+ }
}
-void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) {
- if (!inExecutableAllocate_) {
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
- const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
+void OmpStructureChecker::Enter(const parser::OmpAllocateDirective &x) {
+ const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
+ const parser::OmpDirectiveName &dirName{beginSpec.DirName()};
+ PushContextAndClauseSets(dirName.source, dirName.v);
+ ++allocateDirectiveLevel;
- isPredefinedAllocator = true;
- CheckAllocateDirective(dir.source, objectList, clauseList);
+ bool isExecutable{partStack_.back() == PartKind::ExecutionPart};
+
+ unsigned version{context_.langOptions().OpenMPVersion};
+ if (isExecutable && allocateDirectiveLevel == 1 && version >= 52) {
+ context_.Warn(common::UsageWarning::OpenMPUsage, dirName.source,
+ "The executable form of the OpenMP ALLOCATE directive has been deprecated, please use ALLOCATORS instead"_warn_en_US);
}
+
+ CheckIndividualAllocateDirective(x, isExecutable);
+
+ if (isExecutable) {
+ auto isOmpAllocate{[](const parser::ExecutionPartConstruct &epc) {
+ if (auto *omp{GetOmp(epc)}) {
+ auto odn{GetOmpDirectiveName(*omp)};
+ return odn.v == llvm::omp::Directive::OMPD_allocate;
+ }
+ return false;
+ }};
+
+ auto &body{std::get<parser::Block>(x.t)};
+ // The parser should put at most one statement in the body.
+ assert(body.size() <= 1 && "Multiple statements in allocate");
+ if (body.empty()) {
+ context_.Say(dirName.source,
+ "An executable ALLOCATE directive must be associated with an ALLOCATE statement"_err_en_US);
+ } else {
+ const parser::ExecutionPartConstruct &first{body.front()};
+ auto [allocStmt, _]{getAllocateStmtAndSource(&body.front())};
+ if (!isOmpAllocate(first) && !allocStmt) {
+ parser::CharBlock source{[&]() {
+ if (auto &&maybeSource{parser::GetSource(first)}) {
+ return *maybeSource;
+ }
+ return dirName.source;
+ }()};
+ context_.Say(source,
+ "The statement associated with executable ALLOCATE directive must be an ALLOCATE statement"_err_en_US);
+ }
+ }
+ }
+}
+
+void OmpStructureChecker::Leave(const parser::OmpAllocateDirective &x) {
+ bool isExecutable{partStack_.back() == PartKind::ExecutionPart};
+ if (isExecutable && allocateDirectiveLevel == 1) {
+ CheckExecutableAllocateDirective(x);
+ }
+
+ --allocateDirectiveLevel;
dirContext_.pop_back();
}
void OmpStructureChecker::Enter(const parser::OmpClause::Allocator &x) {
CheckAllowedClause(llvm::omp::Clause::OMPC_allocator);
- // Note: Predefined allocators are stored in ScalarExpr as numbers
- // whereas custom allocators are stored as strings, so if the ScalarExpr
- // actually has an int value, then it must be a predefined allocator
- isPredefinedAllocator = GetIntValue(x.v).has_value();
RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocator, x.v);
}
@@ -1823,16 +1918,6 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Allocate &x) {
"The alignment value should be a constant positive integer"_err_en_US);
}
}
- // The simple and complex modifiers have the same structure. They only
- // differ in their syntax.
- if (auto *alloc{OmpGetUniqueModifier<parser::OmpAllocatorComplexModifier>(
- modifiers)}) {
- isPredefinedAllocator = GetIntValue(alloc->v).has_value();
- }
- if (auto *alloc{OmpGetUniqueModifier<parser::OmpAllocatorSimpleModifier>(
- modifiers)}) {
- isPredefinedAllocator = GetIntValue(alloc->v).has_value();
- }
}
}
@@ -2115,168 +2200,88 @@ void OmpStructureChecker::Enter(const parser::OmpClause::At &x) {
}
}
-// Goes through the names in an OmpObjectList and checks if each name appears
-// in the given allocate statement
-void OmpStructureChecker::CheckAllNamesInAllocateStmt(
- const parser::CharBlock &source, const parser::OmpObjectList &ompObjectList,
- const parser::AllocateStmt &allocate) {
- for (const auto &obj : ompObjectList.v) {
- if (const auto *d{std::get_if<parser::Designator>(&obj.u)}) {
- if (const auto *ref{std::get_if<parser::DataRef>(&d->u)}) {
- if (const auto *n{std::get_if<parser::Name>(&ref->u)}) {
- CheckNameInAllocateStmt(source, *n, allocate);
- }
- }
- }
- }
-}
-
-void OmpStructureChecker::CheckNameInAllocateStmt(
- const parser::CharBlock &source, const parser::Name &name,
- const parser::AllocateStmt &allocate) {
- for (const auto &allocation :
- std::get<std::list<parser::Allocation>>(allocate.t)) {
- const auto &allocObj = std::get<parser::AllocateObject>(allocation.t);
- if (const auto *n{std::get_if<parser::Name>(&allocObj.u)}) {
- if (n->source == name.source) {
- return;
- }
- }
- }
- unsigned version{context_.langOptions().OpenMPVersion};
- context_.Say(source,
- "Object '%s' in %s directive not "
- "found in corresponding ALLOCATE statement"_err_en_US,
- name.ToString(),
- parser::ToUpperCaseLetters(
- llvm::omp::getOpenMPDirectiveName(GetContext().directive, version)
- .str()));
-}
-
-void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
- inExecutableAllocate_ = true;
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
-
- unsigned version{context_.langOptions().OpenMPVersion};
- if (version >= 52) {
- context_.Warn(common::UsageWarning::OpenMPUsage, x.source,
- "The executable form of the OpenMP ALLOCATE directive has been deprecated, please use ALLOCATORS instead"_warn_en_US);
- }
-
- const auto &allocateStmt =
- std::get<parser::Statement<parser::AllocateStmt>>(x.t).statement;
- if (const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
- CheckAllNamesInAllocateStmt(
- std::get<parser::Verbatim>(x.t).source, *list, allocateStmt);
- }
- if (const auto &subDirs{
- std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>(
- x.t)}) {
- for (const auto &dalloc : *subDirs) {
- CheckAllNamesInAllocateStmt(std::get<parser::Verbatim>(dalloc.t).source,
- std::get<parser::OmpObjectList>(dalloc.t), allocateStmt);
- }
- }
-
- isPredefinedAllocator = true;
-}
+void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) {
+ const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
+ const parser::OmpDirectiveName &dirName{beginSpec.DirName()};
+ PushContextAndClauseSets(
+ dirName.source, llvm::omp::Directive::OMPD_allocators);
-void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &x) {
- parser::OmpObjectList empty{std::list<parser::OmpObject>{}};
- auto &objects{[&]() -> const parser::OmpObjectList & {
- if (auto &objects{std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
- return *objects;
- } else {
- return empty;
+ for (const auto &clause : beginSpec.Clauses().v) {
+ auto *alloc{std::get_if<parser::OmpClause::Allocate>(&clause.u)};
+ if (!alloc) {
+ continue;
}
- }()};
- auto &clauses{std::get<parser::OmpClauseList>(x.t)};
- CheckAllocateDirective(
- std::get<parser::Verbatim>(x.t).source, objects, clauses);
-
- if (const auto &subDirs{
- std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>(
- x.t)}) {
- for (const auto &dalloc : *subDirs) {
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- const auto &clauses{std::get<parser::OmpClauseList>(dalloc.t)};
- const auto &objects{std::get<parser::OmpObjectList>(dalloc.t)};
- CheckAllocateDirective(dir.source, objects, clauses);
+ using OmpAllocatorSimpleModifier = parser::OmpAllocatorSimpleModifier;
+ using OmpAllocatorComplexModifier = parser::OmpAllocatorComplexModifier;
+
+ if (InTargetRegion()) {
+ auto &modifiers{OmpGetModifiers(alloc->v)};
+ bool hasAllocator{
+ OmpGetUniqueModifier<OmpAllocatorSimpleModifier>(modifiers) ||
+ OmpGetUniqueModifier<OmpAllocatorComplexModifier>(modifiers)};
+ bool hasDynAllocators{
+ HasRequires(llvm::omp::Clause::OMPC_dynamic_allocators)};
+ if (!hasAllocator && !hasDynAllocators) {
+ context_.Say(clause.source,
+ "An ALLOCATE clause in a TARGET region must specify an allocator or REQUIRES(DYNAMIC_ALLOCATORS) must be specified"_err_en_US);
+ }
}
}
- dirContext_.pop_back();
- inExecutableAllocate_ = false;
-}
-
-void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) {
- isPredefinedAllocator = true;
-
- const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()};
- auto &block{std::get<parser::Block>(x.t)};
- PushContextAndClauseSets(
- dirSpec.DirName().source, llvm::omp::Directive::OMPD_allocators);
-
- if (block.empty()) {
- context_.Say(dirSpec.source,
- "The ALLOCATORS construct should contain a single ALLOCATE statement"_err_en_US);
+ auto &body{std::get<parser::Block>(x.t)};
+ // The parser should put at most one statement in the body.
+ assert(body.size() <= 1 && "Malformed body in allocators");
+ if (body.empty()) {
+ context_.Say(dirName.source,
+ "The body of an ALLOCATORS construct should be an ALLOCATE statement"_err_en_US);
return;
}
- omp::SourcedActionStmt action{omp::GetActionStmt(block)};
- const auto *allocate{
- action ? parser::Unwrap<parser::AllocateStmt>(action.stmt) : nullptr};
-
- if (allocate) {
- for (const auto &clause : dirSpec.Clauses().v) {
- if (auto *alloc{std::get_if<parser::OmpClause::Allocate>(&clause.u)}) {
- CheckAllNamesInAllocateStmt(
- x.source, std::get<parser::OmpObjectList>(alloc->v.t), *allocate);
-
- using OmpAllocatorSimpleModifier = parser::OmpAllocatorSimpleModifier;
- using OmpAllocatorComplexModifier = parser::OmpAllocatorComplexModifier;
-
- auto &modifiers{OmpGetModifiers(alloc->v)};
- bool hasAllocator{
- OmpGetUniqueModifier<OmpAllocatorSimpleModifier>(modifiers) ||
- OmpGetUniqueModifier<OmpAllocatorComplexModifier>(modifiers)};
-
- // TODO: As with allocate directive, exclude the case when a requires
- // directive with the dynamic_allocators clause is present in
- // the same compilation unit (OMP5.0 2.11.3).
- if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) &&
- !hasAllocator) {
- context_.Say(x.source,
- "ALLOCATORS directives that appear in a TARGET region must specify an allocator"_err_en_US);
- }
+ auto [allocStmt, allocSource]{getAllocateStmtAndSource(&body.front())};
+ if (!allocStmt) {
+ parser::CharBlock source{[&]() {
+ if (auto &&maybeSource{parser::GetSource(body.front())}) {
+ return *maybeSource;
}
- }
- } else {
- const parser::CharBlock &source = action ? action.source : x.source;
+ return dirName.source;
+ }()};
context_.Say(source,
- "The body of the ALLOCATORS construct should be an ALLOCATE statement"_err_en_US);
+ "The body of an ALLOCATORS construct should be an ALLOCATE statement"_err_en_US);
+ return;
}
- for (const auto &clause : dirSpec.Clauses().v) {
- if (const auto *allocClause{
- parser::Unwrap<parser::OmpClause::Allocate>(clause)}) {
- CheckVarIsNotPartOfAnotherVar(
- dirSpec.source, std::get<parser::OmpObjectList>(allocClause->v.t));
+ UnorderedSymbolSet allocateSyms{GetNonComponentSymbols(*allocStmt)};
+ for (const auto &clause : beginSpec.Clauses().v) {
+ auto *alloc{std::get_if<parser::OmpClause::Allocate>(&clause.u)};
+ if (!alloc) {
+ continue;
+ }
+ for (auto &object : DEREF(GetOmpObjectList(clause)).v) {
+ CheckVarIsNotPartOfAnotherVar(dirName.source, object);
+ if (auto *symbol{GetObjectSymbol(object)}) {
+ if (IsStructureComponent(*symbol)) {
+ continue;
+ }
+ parser::CharBlock source{[&]() {
+ if (auto &&objectSource{GetObjectSource(object)}) {
+ return *objectSource;
+ }
+ return clause.source;
+ }()};
+ if (!IsTypeParamInquiry(*symbol)) {
+ if (auto f{allocateSyms.find(*symbol)}; f == allocateSyms.end()) {
+ context_
+ .Say(source,
+ "A list item in an ALLOCATORS construct must be specified on the associated ALLOCATE statement"_err_en_US)
+ .Attach(allocSource, "The ALLOCATE statement"_en_US);
+ }
+ }
+ }
}
}
}
void OmpStructureChecker::Leave(const parser::OpenMPAllocatorsConstruct &x) {
- const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()};
-
- for (const auto &clause : dirSpec.Clauses().v) {
- if (const auto *allocClause{
- std::get_if<parser::OmpClause::Allocate>(&clause.u)}) {
- CheckPredefinedAllocatorRestriction(
- dirSpec.source, std::get<parser::OmpObjectList>(allocClause->v.t));
- }
- }
dirContext_.pop_back();
}
@@ -3362,88 +3367,6 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Sizes &c) {
/*paramName=*/"parameter", /*allowZero=*/false);
}
-// Following clauses do not have a separate node in parse-tree.h.
-CHECK_SIMPLE_CLAUSE(Absent, OMPC_absent)
-CHECK_SIMPLE_CLAUSE(Affinity, OMPC_affinity)
-CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture)
-CHECK_SIMPLE_CLAUSE(Contains, OMPC_contains)
-CHECK_SIMPLE_CLAUSE(Default, OMPC_default)
-CHECK_SIMPLE_CLAUSE(Depobj, OMPC_depobj)
-CHECK_SIMPLE_CLAUSE(DeviceType, OMPC_device_type)
-CHECK_SIMPLE_CLAUSE(DistSchedule, OMPC_dist_schedule)
-CHECK_SIMPLE_CLAUSE(DynGroupprivate, OMPC_dyn_groupprivate)
-CHECK_SIMPLE_CLAUSE(Exclusive, OMPC_exclusive)
-CHECK_SIMPLE_CLAUSE(Final, OMPC_final)
-CHECK_SIMPLE_CLAUSE(Flush, OMPC_flush)
-CHECK_SIMPLE_CLAUSE(Full, OMPC_full)
-CHECK_SIMPLE_CLAUSE(Grainsize, OMPC_grainsize)
-CHECK_SIMPLE_CLAUSE(GraphId, OMPC_graph_id)
-CHECK_SIMPLE_CLAUSE(GraphReset, OMPC_graph_reset)
-CHECK_SIMPLE_CLAUSE(Holds, OMPC_holds)
-CHECK_SIMPLE_CLAUSE(Inclusive, OMPC_inclusive)
-CHECK_SIMPLE_CLAUSE(Initializer, OMPC_initializer)
-CHECK_SIMPLE_CLAUSE(Match, OMPC_match)
-CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal)
-CHECK_SIMPLE_CLAUSE(NumTasks, OMPC_num_tasks)
-CHECK_SIMPLE_CLAUSE(Order, OMPC_order)
-CHECK_SIMPLE_CLAUSE(Read, OMPC_read)
-CHECK_SIMPLE_CLAUSE(Threadprivate, OMPC_threadprivate)
-CHECK_SIMPLE_CLAUSE(Groupprivate, OMPC_groupprivate)
-CHECK_SIMPLE_CLAUSE(Threads, OMPC_threads)
-CHECK_SIMPLE_CLAUSE(Threadset, OMPC_threadset)
-CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch)
-CHECK_SIMPLE_CLAUSE(Link, OMPC_link)
-CHECK_SIMPLE_CLAUSE(Indirect, OMPC_indirect)
-CHECK_SIMPLE_CLAUSE(Mergeable, OMPC_mergeable)
-CHECK_SIMPLE_CLAUSE(NoOpenmp, OMPC_no_openmp)
-CHECK_SIMPLE_CLAUSE(NoOpenmpRoutines, OMPC_no_openmp_routines)
-CHECK_SIMPLE_CLAUSE(NoOpenmpConstructs, OMPC_no_openmp_constructs)
-CHECK_SIMPLE_CLAUSE(NoParallelism, OMPC_no_parallelism)
-CHECK_SIMPLE_CLAUSE(Nogroup, OMPC_nogroup)
-CHECK_SIMPLE_CLAUSE(Notinbranch, OMPC_notinbranch)
-CHECK_SIMPLE_CLAUSE(Partial, OMPC_partial)
-CHECK_SIMPLE_CLAUSE(ProcBind, OMPC_proc_bind)
-CHECK_SIMPLE_CLAUSE(Simd, OMPC_simd)
-CHECK_SIMPLE_CLAUSE(Permutation, OMPC_permutation)
-CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform)
-CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown)
-CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied)
-CHECK_SIMPLE_CLAUSE(UsesAllocators, OMPC_uses_allocators)
-CHECK_SIMPLE_CLAUSE(Write, OMPC_write)
-CHECK_SIMPLE_CLAUSE(Init, OMPC_init)
-CHECK_SIMPLE_CLAUSE(Use, OMPC_use)
-CHECK_SIMPLE_CLAUSE(Novariants, OMPC_novariants)
-CHECK_SIMPLE_CLAUSE(Nocontext, OMPC_nocontext)
-CHECK_SIMPLE_CLAUSE(Severity, OMPC_severity)
-CHECK_SIMPLE_CLAUSE(Message, OMPC_message)
-CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter)
-CHECK_SIMPLE_CLAUSE(Otherwise, OMPC_otherwise)
-CHECK_SIMPLE_CLAUSE(AdjustArgs, OMPC_adjust_args)
-CHECK_SIMPLE_CLAUSE(AppendArgs, OMPC_append_args)
-CHECK_SIMPLE_CLAUSE(MemoryOrder, OMPC_memory_order)
-CHECK_SIMPLE_CLAUSE(Bind, OMPC_bind)
-CHECK_SIMPLE_CLAUSE(Compare, OMPC_compare)
-CHECK_SIMPLE_CLAUSE(OmpxAttribute, OMPC_ompx_attribute)
-CHECK_SIMPLE_CLAUSE(Weak, OMPC_weak)
-CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel)
-CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire)
-CHECK_SIMPLE_CLAUSE(Relaxed, OMPC_relaxed)
-CHECK_SIMPLE_CLAUSE(Release, OMPC_release)
-CHECK_SIMPLE_CLAUSE(Replayable, OMPC_replayable)
-CHECK_SIMPLE_CLAUSE(Transparent, OMPC_transparent)
-CHECK_SIMPLE_CLAUSE(SeqCst, OMPC_seq_cst)
-CHECK_SIMPLE_CLAUSE(Fail, OMPC_fail)
-
-CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams, OMPC_num_teams)
-CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads, OMPC_num_threads)
-CHECK_REQ_SCALAR_INT_CLAUSE(OmpxDynCgroupMem, OMPC_ompx_dyn_cgroup_mem)
-CHECK_REQ_SCALAR_INT_CLAUSE(Priority, OMPC_priority)
-CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit, OMPC_thread_limit)
-
-CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse, OMPC_collapse)
-CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen)
-CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen)
-
void OmpStructureChecker::Enter(const parser::OmpClause::Looprange &x) {
context_.Say(GetContext().clauseSource,
"LOOPRANGE clause is not implemented yet"_err_en_US,
@@ -5516,4 +5439,104 @@ void OmpStructureChecker::CheckAllowedRequiresClause(llvmOmpClause clause) {
}
}
+// Use when clause falls under 'struct OmpClause' in 'parse-tree.h'.
+#define CHECK_SIMPLE_CLAUSE(X, Y) \
+ void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \
+ CheckAllowedClause(llvm::omp::Clause::Y); \
+ }
+
+#define CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(X, Y) \
+ void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
+ CheckAllowedClause(llvm::omp::Clause::Y); \
+ RequiresConstantPositiveParameter(llvm::omp::Clause::Y, c.v); \
+ }
+
+#define CHECK_REQ_SCALAR_INT_CLAUSE(X, Y) \
+ void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
+ CheckAllowedClause(llvm::omp::Clause::Y); \
+ RequiresPositiveParameter(llvm::omp::Clause::Y, c.v); \
+ }
+
+// Following clauses do not have a separate node in parse-tree.h.
+CHECK_SIMPLE_CLAUSE(Absent, OMPC_absent)
+CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel)
+CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire)
+CHECK_SIMPLE_CLAUSE(AdjustArgs, OMPC_adjust_args)
+CHECK_SIMPLE_CLAUSE(Affinity, OMPC_affinity)
+CHECK_SIMPLE_CLAUSE(AppendArgs, OMPC_append_args)
+CHECK_SIMPLE_CLAUSE(Bind, OMPC_bind)
+CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture)
+CHECK_SIMPLE_CLAUSE(Compare, OMPC_compare)
+CHECK_SIMPLE_CLAUSE(Contains, OMPC_contains)
+CHECK_SIMPLE_CLAUSE(Default, OMPC_default)
+CHECK_SIMPLE_CLAUSE(Depobj, OMPC_depobj)
+CHECK_SIMPLE_CLAUSE(DeviceType, OMPC_device_type)
+CHECK_SIMPLE_CLAUSE(DistSchedule, OMPC_dist_schedule)
+CHECK_SIMPLE_CLAUSE(DynGroupprivate, OMPC_dyn_groupprivate)
+CHECK_SIMPLE_CLAUSE(Exclusive, OMPC_exclusive)
+CHECK_SIMPLE_CLAUSE(Fail, OMPC_fail)
+CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter)
+CHECK_SIMPLE_CLAUSE(Final, OMPC_final)
+CHECK_SIMPLE_CLAUSE(Flush, OMPC_flush)
+CHECK_SIMPLE_CLAUSE(Full, OMPC_full)
+CHECK_SIMPLE_CLAUSE(Grainsize, OMPC_grainsize)
+CHECK_SIMPLE_CLAUSE(GraphId, OMPC_graph_id)
+CHECK_SIMPLE_CLAUSE(GraphReset, OMPC_graph_reset)
+CHECK_SIMPLE_CLAUSE(Groupprivate, OMPC_groupprivate)
+CHECK_SIMPLE_CLAUSE(Holds, OMPC_holds)
+CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch)
+CHECK_SIMPLE_CLAUSE(Inclusive, OMPC_inclusive)
+CHECK_SIMPLE_CLAUSE(Indirect, OMPC_indirect)
+CHECK_SIMPLE_CLAUSE(Initializer, OMPC_initializer)
+CHECK_SIMPLE_CLAUSE(Init, OMPC_init)
+CHECK_SIMPLE_CLAUSE(Link, OMPC_link)
+CHECK_SIMPLE_CLAUSE(Match, OMPC_match)
+CHECK_SIMPLE_CLAUSE(MemoryOrder, OMPC_memory_order)
+CHECK_SIMPLE_CLAUSE(Mergeable, OMPC_mergeable)
+CHECK_SIMPLE_CLAUSE(Message, OMPC_message)
+CHECK_SIMPLE_CLAUSE(Nocontext, OMPC_nocontext)
+CHECK_SIMPLE_CLAUSE(Nogroup, OMPC_nogroup)
+CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal)
+CHECK_SIMPLE_CLAUSE(NoOpenmpConstructs, OMPC_no_openmp_constructs)
+CHECK_SIMPLE_CLAUSE(NoOpenmp, OMPC_no_openmp)
+CHECK_SIMPLE_CLAUSE(NoOpenmpRoutines, OMPC_no_openmp_routines)
+CHECK_SIMPLE_CLAUSE(NoParallelism, OMPC_no_parallelism)
+CHECK_SIMPLE_CLAUSE(Notinbranch, OMPC_notinbranch)
+CHECK_SIMPLE_CLAUSE(Novariants, OMPC_novariants)
+CHECK_SIMPLE_CLAUSE(NumTasks, OMPC_num_tasks)
+CHECK_SIMPLE_CLAUSE(OmpxAttribute, OMPC_ompx_attribute)
+CHECK_SIMPLE_CLAUSE(Order, OMPC_order)
+CHECK_SIMPLE_CLAUSE(Otherwise, OMPC_otherwise)
+CHECK_SIMPLE_CLAUSE(Partial, OMPC_partial)
+CHECK_SIMPLE_CLAUSE(Permutation, OMPC_permutation)
+CHECK_SIMPLE_CLAUSE(ProcBind, OMPC_proc_bind)
+CHECK_SIMPLE_CLAUSE(Read, OMPC_read)
+CHECK_SIMPLE_CLAUSE(Relaxed, OMPC_relaxed)
+CHECK_SIMPLE_CLAUSE(Release, OMPC_release)
+CHECK_SIMPLE_CLAUSE(Replayable, OMPC_replayable)
+CHECK_SIMPLE_CLAUSE(SeqCst, OMPC_seq_cst)
+CHECK_SIMPLE_CLAUSE(Severity, OMPC_severity)
+CHECK_SIMPLE_CLAUSE(Simd, OMPC_simd)
+CHECK_SIMPLE_CLAUSE(Threadprivate, OMPC_threadprivate)
+CHECK_SIMPLE_CLAUSE(Threadset, OMPC_threadset)
+CHECK_SIMPLE_CLAUSE(Threads, OMPC_threads)
+CHECK_SIMPLE_CLAUSE(Transparent, OMPC_transparent)
+CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform)
+CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown)
+CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied)
+CHECK_SIMPLE_CLAUSE(Use, OMPC_use)
+CHECK_SIMPLE_CLAUSE(UsesAllocators, OMPC_uses_allocators)
+CHECK_SIMPLE_CLAUSE(Weak, OMPC_weak)
+CHECK_SIMPLE_CLAUSE(Write, OMPC_write)
+
+CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams, OMPC_num_teams)
+CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads, OMPC_num_threads)
+CHECK_REQ_SCALAR_INT_CLAUSE(OmpxDynCgroupMem, OMPC_ompx_dyn_cgroup_mem)
+CHECK_REQ_SCALAR_INT_CLAUSE(Priority, OMPC_priority)
+CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit, OMPC_thread_limit)
+
+CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse, OMPC_collapse)
+CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen)
+CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen)
+
} // namespace Fortran::semantics
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 7426559..1b84bc5 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -82,6 +82,11 @@ public:
bool Enter(const parser::BlockConstruct &);
void Leave(const parser::BlockConstruct &);
+ void Enter(const parser::SpecificationPart &);
+ void Leave(const parser::SpecificationPart &);
+ void Enter(const parser::ExecutionPart &);
+ void Leave(const parser::ExecutionPart &);
+
void Enter(const parser::OpenMPConstruct &);
void Leave(const parser::OpenMPConstruct &);
void Enter(const parser::OpenMPInteropConstruct &);
@@ -113,8 +118,8 @@ public:
void Leave(const parser::OmpDeclareVariantDirective &);
void Enter(const parser::OpenMPDeclareSimdConstruct &);
void Leave(const parser::OpenMPDeclareSimdConstruct &);
- void Enter(const parser::OpenMPDeclarativeAllocate &);
- void Leave(const parser::OpenMPDeclarativeAllocate &);
+ void Enter(const parser::OmpAllocateDirective &);
+ void Leave(const parser::OmpAllocateDirective &);
void Enter(const parser::OpenMPDeclareMapperConstruct &);
void Leave(const parser::OpenMPDeclareMapperConstruct &);
void Enter(const parser::OpenMPDeclareReductionConstruct &);
@@ -129,8 +134,6 @@ public:
void Leave(const parser::OmpErrorDirective &);
void Enter(const parser::OmpNothingDirective &);
void Leave(const parser::OmpNothingDirective &);
- void Enter(const parser::OpenMPExecutableAllocate &);
- void Leave(const parser::OpenMPExecutableAllocate &);
void Enter(const parser::OpenMPAllocatorsConstruct &);
void Leave(const parser::OpenMPAllocatorsConstruct &);
void Enter(const parser::OpenMPRequiresConstruct &);
@@ -263,9 +266,9 @@ private:
bool CheckTargetBlockOnlyTeams(const parser::Block &);
void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);
void CheckWorkdistributeBlockStmts(const parser::Block &, parser::CharBlock);
- void CheckAllocateDirective(parser::CharBlock source,
- const parser::OmpObjectList &objects,
- const parser::OmpClauseList &clauses);
+ void CheckIndividualAllocateDirective(
+ const parser::OmpAllocateDirective &x, bool isExecutable);
+ void CheckExecutableAllocateDirective(const parser::OmpAllocateDirective &x);
void CheckIteratorRange(const parser::OmpIteratorSpecifier &x);
void CheckIteratorModifier(const parser::OmpIterator &x);
@@ -325,11 +328,6 @@ private:
const std::optional<parser::OmpClauseList> &maybeClauses);
void CheckCancellationNest(
const parser::CharBlock &source, llvm::omp::Directive type);
- void CheckAllNamesInAllocateStmt(const parser::CharBlock &source,
- const parser::OmpObjectList &ompObjectList,
- const parser::AllocateStmt &allocate);
- void CheckNameInAllocateStmt(const parser::CharBlock &source,
- const parser::Name &ompObject, const parser::AllocateStmt &allocate);
std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x);
void CheckReductionObjects(
const parser::OmpObjectList &objects, llvm::omp::Clause clauseId);
@@ -353,11 +351,6 @@ private:
const parser::OmpObjectList &ompObjectList);
void CheckIfContiguous(const parser::OmpObject &object);
const parser::Name *GetObjectName(const parser::OmpObject &object);
- void CheckPredefinedAllocatorRestriction(const parser::CharBlock &source,
- const parser::OmpObjectList &ompObjectList);
- void CheckPredefinedAllocatorRestriction(
- const parser::CharBlock &source, const parser::Name &name);
- bool isPredefinedAllocator{false};
void CheckAllowedRequiresClause(llvmOmpClause clause);
bool deviceConstructFound_{false};
@@ -383,7 +376,7 @@ private:
};
int directiveNest_[LastType + 1] = {0};
- bool inExecutableAllocate_{false};
+ int allocateDirectiveLevel{0};
parser::CharBlock visitedAtomicSource_;
SymbolSourceMap deferredNonVariables_;
@@ -392,6 +385,14 @@ private:
std::vector<LoopConstruct> loopStack_;
// Scopes for scoping units.
std::vector<const Scope *> scopeStack_;
+
+ enum class PartKind : int {
+ // There are also other "parts", such as internal-subprogram-part, etc,
+ // but we're keeping track of these two for now.
+ SpecificationPart,
+ ExecutionPart,
+ };
+ std::vector<PartKind> partStack_;
};
/// Find a duplicate entry in the range, and return an iterator to it.
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 6b304b6..4a40d6e 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -186,6 +186,23 @@ bool IsExtendedListItem(const Symbol &sym) {
return IsVariableListItem(sym) || sym.IsSubprogram();
}
+bool IsTypeParamInquiry(const Symbol &sym) {
+ return common::visit( //
+ common::visitors{
+ [&](const MiscDetails &d) {
+ return d.kind() == MiscDetails::Kind::KindParamInquiry ||
+ d.kind() == MiscDetails::Kind::LenParamInquiry;
+ },
+ [&](const TypeParamDetails &s) { return true; },
+ [&](auto &&) { return false; },
+ },
+ sym.details());
+}
+
+bool IsStructureComponent(const Symbol &sym) {
+ return sym.owner().kind() == Scope::Kind::DerivedType;
+}
+
bool IsVarOrFunctionRef(const MaybeExpr &expr) {
if (expr) {
return evaluate::UnwrapProcedureRef(*expr) != nullptr ||
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 628068f..deb57e0 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -415,6 +415,18 @@ public:
return true;
}
+ bool Pre(const parser::SpecificationPart &) {
+ partStack_.push_back(PartKind::SpecificationPart);
+ return true;
+ }
+ void Post(const parser::SpecificationPart &) { partStack_.pop_back(); }
+
+ bool Pre(const parser::ExecutionPart &) {
+ partStack_.push_back(PartKind::ExecutionPart);
+ return true;
+ }
+ void Post(const parser::ExecutionPart &) { partStack_.pop_back(); }
+
bool Pre(const parser::InternalSubprogram &) {
// Clear the labels being tracked in the previous scope
ClearLabels();
@@ -639,8 +651,7 @@ public:
bool Pre(const parser::OpenMPThreadprivate &);
void Post(const parser::OpenMPThreadprivate &) { PopContext(); }
- bool Pre(const parser::OpenMPDeclarativeAllocate &);
- void Post(const parser::OpenMPDeclarativeAllocate &) { PopContext(); }
+ bool Pre(const parser::OmpAllocateDirective &);
bool Pre(const parser::OpenMPAssumeConstruct &);
void Post(const parser::OpenMPAssumeConstruct &) { PopContext(); }
@@ -651,9 +662,6 @@ public:
bool Pre(const parser::OpenMPDispatchConstruct &);
void Post(const parser::OpenMPDispatchConstruct &) { PopContext(); }
- bool Pre(const parser::OpenMPExecutableAllocate &);
- void Post(const parser::OpenMPExecutableAllocate &);
-
bool Pre(const parser::OpenMPAllocatorsConstruct &);
void Post(const parser::OpenMPAllocatorsConstruct &);
@@ -998,6 +1006,14 @@ private:
targetLabels_;
parser::CharBlock currentStatementSource_;
+ enum class PartKind : int {
+ // There are also other "parts", such as internal-subprogram-part, etc,
+ // but we're keeping track of these two for now.
+ SpecificationPart,
+ ExecutionPart,
+ };
+ std::vector<PartKind> partStack_;
+
void AddAllocateName(const parser::Name *&object) {
allocateNames_.push_back(object);
}
@@ -2558,10 +2574,24 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) {
return true;
}
-bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclarativeAllocate &x) {
+bool OmpAttributeVisitor::Pre(const parser::OmpAllocateDirective &x) {
PushContext(x.source, llvm::omp::Directive::OMPD_allocate);
- const auto &list{std::get<parser::OmpObjectList>(x.t)};
- ResolveOmpObjectList(list, Symbol::Flag::OmpDeclarativeAllocateDirective);
+ assert(!partStack_.empty() && "Misplaced directive");
+
+ auto ompFlag{partStack_.back() == PartKind::SpecificationPart
+ ? Symbol::Flag::OmpDeclarativeAllocateDirective
+ : Symbol::Flag::OmpExecutableAllocateDirective};
+
+ parser::omp::OmpAllocateInfo info{parser::omp::SplitOmpAllocate(x)};
+ for (const parser::OmpAllocateDirective *ad : info.dirs) {
+ for (const parser::OmpArgument &arg : ad->BeginDir().Arguments().v) {
+ if (auto *object{omp::GetArgumentObject(arg)}) {
+ ResolveOmpObject(*object, ompFlag);
+ }
+ }
+ }
+
+ PopContext();
return false;
}
@@ -2580,15 +2610,6 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPDispatchConstruct &x) {
return true;
}
-bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate &x) {
- PushContext(x.source, llvm::omp::Directive::OMPD_allocate);
- const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)};
- if (list) {
- ResolveOmpObjectList(*list, Symbol::Flag::OmpExecutableAllocateDirective);
- }
- return true;
-}
-
bool OmpAttributeVisitor::Pre(const parser::OpenMPAllocatorsConstruct &x) {
const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()};
PushContext(x.source, dirSpec.DirId());
@@ -2660,10 +2681,6 @@ bool OmpAttributeVisitor::IsNestedInDirective(llvm::omp::Directive directive) {
return false;
}
-void OmpAttributeVisitor::Post(const parser::OpenMPExecutableAllocate &x) {
- PopContext();
-}
-
void OmpAttributeVisitor::Post(const parser::OpenMPAllocatorsConstruct &x) {
PopContext();
}
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 220f1c9..a2062ef 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1700,12 +1700,12 @@ public:
void Post(const parser::OpenMPDeclareTargetConstruct &) {
SkipImplicitTyping(false);
}
- bool Pre(const parser::OpenMPDeclarativeAllocate &x) {
+ bool Pre(const parser::OmpAllocateDirective &x) {
AddOmpSourceRange(x.source);
SkipImplicitTyping(true);
return true;
}
- void Post(const parser::OpenMPDeclarativeAllocate &) {
+ void Post(const parser::OmpAllocateDirective &) {
SkipImplicitTyping(false);
messageHandler().set_currStmtSource(std::nullopt);
}