aboutsummaryrefslogtreecommitdiff
path: root/flang/lib/Semantics
diff options
context:
space:
mode:
Diffstat (limited to 'flang/lib/Semantics')
-rw-r--r--flang/lib/Semantics/canonicalize-directives.cpp10
-rw-r--r--flang/lib/Semantics/canonicalize-do.cpp43
-rw-r--r--flang/lib/Semantics/canonicalize-omp.cpp167
-rw-r--r--flang/lib/Semantics/check-call.cpp17
-rw-r--r--flang/lib/Semantics/check-declarations.cpp13
-rw-r--r--flang/lib/Semantics/check-omp-atomic.cpp10
-rw-r--r--flang/lib/Semantics/check-omp-loop.cpp359
-rw-r--r--flang/lib/Semantics/check-omp-structure.cpp164
-rw-r--r--flang/lib/Semantics/check-omp-structure.h12
-rw-r--r--flang/lib/Semantics/dump-expr.cpp1
-rw-r--r--flang/lib/Semantics/expression.cpp38
-rw-r--r--flang/lib/Semantics/mod-file.cpp12
-rw-r--r--flang/lib/Semantics/openmp-modifiers.cpp32
-rw-r--r--flang/lib/Semantics/openmp-utils.cpp28
-rw-r--r--flang/lib/Semantics/resolve-directives.cpp344
-rw-r--r--flang/lib/Semantics/resolve-names.cpp94
-rw-r--r--flang/lib/Semantics/rewrite-parse-tree.cpp46
-rw-r--r--flang/lib/Semantics/symbol.cpp6
-rw-r--r--flang/lib/Semantics/tools.cpp38
-rw-r--r--flang/lib/Semantics/type.cpp7
20 files changed, 870 insertions, 571 deletions
diff --git a/flang/lib/Semantics/canonicalize-directives.cpp b/flang/lib/Semantics/canonicalize-directives.cpp
index a651a87..f32a3d3 100644
--- a/flang/lib/Semantics/canonicalize-directives.cpp
+++ b/flang/lib/Semantics/canonicalize-directives.cpp
@@ -56,6 +56,7 @@ bool CanonicalizeDirectives(
static bool IsExecutionDirective(const parser::CompilerDirective &dir) {
return std::holds_alternative<parser::CompilerDirective::VectorAlways>(
dir.u) ||
+ std::holds_alternative<parser::CompilerDirective::VectorLength>(dir.u) ||
std::holds_alternative<parser::CompilerDirective::Unroll>(dir.u) ||
std::holds_alternative<parser::CompilerDirective::UnrollAndJam>(dir.u) ||
std::holds_alternative<parser::CompilerDirective::NoVector>(dir.u) ||
@@ -64,7 +65,8 @@ static bool IsExecutionDirective(const parser::CompilerDirective &dir) {
dir.u) ||
std::holds_alternative<parser::CompilerDirective::ForceInline>(dir.u) ||
std::holds_alternative<parser::CompilerDirective::Inline>(dir.u) ||
- std::holds_alternative<parser::CompilerDirective::NoInline>(dir.u);
+ std::holds_alternative<parser::CompilerDirective::NoInline>(dir.u) ||
+ std::holds_alternative<parser::CompilerDirective::IVDep>(dir.u);
}
void CanonicalizationOfDirectives::Post(parser::SpecificationPart &spec) {
@@ -120,6 +122,9 @@ void CanonicalizationOfDirectives::Post(parser::Block &block) {
common::visitors{[&](parser::CompilerDirective::VectorAlways &) {
CheckLoopDirective(*dir, block, it);
},
+ [&](parser::CompilerDirective::VectorLength &) {
+ CheckLoopDirective(*dir, block, it);
+ },
[&](parser::CompilerDirective::Unroll &) {
CheckLoopDirective(*dir, block, it);
},
@@ -135,6 +140,9 @@ void CanonicalizationOfDirectives::Post(parser::Block &block) {
[&](parser::CompilerDirective::NoUnrollAndJam &) {
CheckLoopDirective(*dir, block, it);
},
+ [&](parser::CompilerDirective::IVDep &) {
+ CheckLoopDirective(*dir, block, it);
+ },
[&](auto &) {}},
dir->u);
}
diff --git a/flang/lib/Semantics/canonicalize-do.cpp b/flang/lib/Semantics/canonicalize-do.cpp
index ef20cff..8b23f88 100644
--- a/flang/lib/Semantics/canonicalize-do.cpp
+++ b/flang/lib/Semantics/canonicalize-do.cpp
@@ -7,7 +7,9 @@
//===----------------------------------------------------------------------===//
#include "canonicalize-do.h"
+#include "flang/Parser/openmp-utils.h"
#include "flang/Parser/parse-tree-visitor.h"
+#include "flang/Parser/tools.h"
namespace Fortran::parser {
@@ -87,6 +89,16 @@ public:
[&](Statement<ActionStmt> &actionStmt) {
CanonicalizeIfMatch(block, stack, i, actionStmt);
},
+ [&](common::Indirection<OpenMPConstruct> &construct) {
+ // If the body of the OpenMP construct ends with a label,
+ // treat the label as ending the construct itself.
+ OpenMPConstruct &omp{construct.value()};
+ if (CanonicalizeIfMatch(
+ block, stack, i, omp::GetFinalLabel(omp))) {
+ MarkOpenMPConstruct(
+ omp, OmpDirectiveSpecification::Flag::CrossesLabelDo);
+ }
+ },
},
executableConstruct->u);
}
@@ -95,12 +107,16 @@ public:
private:
template <typename T>
- void CanonicalizeIfMatch(Block &originalBlock, std::vector<LabelInfo> &stack,
+ bool CanonicalizeIfMatch(Block &originalBlock, std::vector<LabelInfo> &stack,
Block::iterator &i, Statement<T> &statement) {
- if (!stack.empty() && statement.label &&
- stack.back().label == *statement.label) {
+ return CanonicalizeIfMatch(originalBlock, stack, i, statement.label);
+ }
+
+ bool CanonicalizeIfMatch(Block &originalBlock, std::vector<LabelInfo> &stack,
+ Block::iterator &i, std::optional<Label> label) {
+ if (!stack.empty() && label && stack.back().label == *label) {
auto currentLabel{stack.back().label};
- if constexpr (std::is_same_v<T, common::Indirection<EndDoStmt>>) {
+ if (Unwrap<EndDoStmt>(*i)) {
std::get<ExecutableConstruct>(i->u).u = Statement<ActionStmt>{
std::optional<Label>{currentLabel}, ContinueStmt{}};
}
@@ -129,8 +145,27 @@ private:
stack.pop_back();
} while (!stack.empty() && stack.back().label == currentLabel);
i = --next;
+ return true;
+ } else {
+ return false;
}
}
+
+ void MarkOpenMPConstruct(
+ OpenMPConstruct &omp, OmpDirectiveSpecification::Flag flag) {
+ common::visit(
+ [&](const auto &s) {
+ using S = std::decay_t<decltype(s)>;
+ if constexpr (std::is_base_of_v<OmpBlockConstruct, S> ||
+ std::is_same_v<OpenMPLoopConstruct, S>) {
+ const OmpDirectiveSpecification &beginSpec{s.BeginDir()};
+ auto &flags{
+ std::get<OmpDirectiveSpecification::Flags>(beginSpec.t)};
+ const_cast<OmpDirectiveSpecification::Flags &>(flags).set(flag);
+ }
+ },
+ omp.u);
+ }
};
bool CanonicalizeDo(Program &program) {
diff --git a/flang/lib/Semantics/canonicalize-omp.cpp b/flang/lib/Semantics/canonicalize-omp.cpp
index a11c525..802b2ac 100644
--- a/flang/lib/Semantics/canonicalize-omp.cpp
+++ b/flang/lib/Semantics/canonicalize-omp.cpp
@@ -9,6 +9,7 @@
#include "canonicalize-omp.h"
#include "flang/Parser/parse-tree-visitor.h"
#include "flang/Parser/parse-tree.h"
+#include "flang/Semantics/openmp-directive-sets.h"
#include "flang/Semantics/semantics.h"
// After Loop Canonicalization, rewrite OpenMP parse tree to make OpenMP
@@ -31,26 +32,6 @@ public:
CanonicalizationOfOmp(SemanticsContext &context)
: context_{context}, messages_{context.messages()} {}
- void Post(parser::Block &block) {
- for (auto it{block.begin()}; it != block.end(); ++it) {
- if (auto *ompCons{GetConstructIf<parser::OpenMPConstruct>(*it)}) {
- // OpenMPLoopConstruct
- if (auto *ompLoop{
- std::get_if<parser::OpenMPLoopConstruct>(&ompCons->u)}) {
- RewriteOpenMPLoopConstruct(*ompLoop, block, it);
- }
- } else if (auto *endDir{
- GetConstructIf<parser::OmpEndLoopDirective>(*it)}) {
- // Unmatched OmpEndLoopDirective
- const parser::OmpDirectiveName &endName{endDir->DirName()};
- messages_.Say(endName.source,
- "The %s directive must follow the DO loop associated with the "
- "loop construct"_err_en_US,
- parser::ToUpperCaseLetters(endName.source.ToString()));
- }
- } // Block list
- }
-
// 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) {
@@ -92,152 +73,6 @@ public:
void Post(parser::OmpMapClause &map) { CanonicalizeMapModifiers(map); }
private:
- template <typename T> T *GetConstructIf(parser::ExecutionPartConstruct &x) {
- if (auto *y{std::get_if<parser::ExecutableConstruct>(&x.u)}) {
- if (auto *z{std::get_if<common::Indirection<T>>(&y->u)}) {
- return &z->value();
- }
- }
- return nullptr;
- }
-
- template <typename T> T *GetOmpIf(parser::ExecutionPartConstruct &x) {
- if (auto *construct{GetConstructIf<parser::OpenMPConstruct>(x)}) {
- if (auto *omp{std::get_if<T>(&construct->u)}) {
- return omp;
- }
- }
- return nullptr;
- }
-
- void RewriteOpenMPLoopConstruct(parser::OpenMPLoopConstruct &x,
- parser::Block &block, parser::Block::iterator it) {
- // Check the sequence of DoConstruct and OmpEndLoopDirective
- // in the same iteration
- //
- // Original:
- // ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
- // OmpBeginLoopDirective
- // ExecutableConstruct -> DoConstruct
- // ExecutableConstruct -> OmpEndLoopDirective (if available)
- //
- // After rewriting:
- // ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
- // OmpBeginLoopDirective
- // DoConstruct
- // OmpEndLoopDirective (if available)
- parser::Block::iterator nextIt;
- const parser::OmpDirectiveSpecification &beginDir{x.BeginDir()};
- const parser::OmpDirectiveName &beginName{beginDir.DirName()};
-
- auto missingDoConstruct = [](const parser::OmpDirectiveName &dirName,
- parser::Messages &messages) {
- messages.Say(dirName.source,
- "A DO loop must follow the %s directive"_err_en_US,
- parser::ToUpperCaseLetters(dirName.source.ToString()));
- };
- auto tileUnrollError = [](const parser::OmpDirectiveName &dirName,
- parser::Messages &messages) {
- messages.Say(dirName.source,
- "If a loop construct has been fully unrolled, it cannot then be tiled"_err_en_US,
- parser::ToUpperCaseLetters(dirName.source.ToString()));
- };
-
- nextIt = it;
- while (++nextIt != block.end()) {
- // Ignore compiler directives.
- if (GetConstructIf<parser::CompilerDirective>(*nextIt))
- continue;
-
- if (auto *doCons{GetConstructIf<parser::DoConstruct>(*nextIt)}) {
- if (doCons->GetLoopControl()) {
- // move DoConstruct
- std::get<std::optional<std::variant<parser::DoConstruct,
- common::Indirection<parser::OpenMPLoopConstruct>>>>(x.t) =
- std::move(*doCons);
- nextIt = block.erase(nextIt);
- // try to match OmpEndLoopDirective
- if (nextIt != block.end()) {
- if (auto *endDir{
- GetConstructIf<parser::OmpEndLoopDirective>(*nextIt)}) {
- std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) =
- std::move(*endDir);
- nextIt = block.erase(nextIt);
- }
- }
- } else {
- messages_.Say(beginName.source,
- "DO loop after the %s directive must have loop control"_err_en_US,
- parser::ToUpperCaseLetters(beginName.source.ToString()));
- }
- } else if (auto *ompLoopCons{
- GetOmpIf<parser::OpenMPLoopConstruct>(*nextIt)}) {
- // We should allow UNROLL and TILE constructs to be inserted between an
- // OpenMP Loop Construct and the DO loop itself
- auto &nestedBeginDirective = ompLoopCons->BeginDir();
- auto &nestedBeginName = nestedBeginDirective.DirName();
- if ((nestedBeginName.v == llvm::omp::Directive::OMPD_unroll ||
- nestedBeginName.v == llvm::omp::Directive::OMPD_tile) &&
- !(nestedBeginName.v == llvm::omp::Directive::OMPD_unroll &&
- beginName.v == llvm::omp::Directive::OMPD_tile)) {
- // iterate through the remaining block items to find the end directive
- // for the unroll/tile directive.
- parser::Block::iterator endIt;
- endIt = nextIt;
- while (endIt != block.end()) {
- if (auto *endDir{
- GetConstructIf<parser::OmpEndLoopDirective>(*endIt)}) {
- auto &endDirName = endDir->DirName();
- if (endDirName.v == beginName.v) {
- std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) =
- std::move(*endDir);
- endIt = block.erase(endIt);
- continue;
- }
- }
- ++endIt;
- }
- RewriteOpenMPLoopConstruct(*ompLoopCons, block, nextIt);
- auto &ompLoop = std::get<std::optional<parser::NestedConstruct>>(x.t);
- ompLoop =
- std::optional<parser::NestedConstruct>{parser::NestedConstruct{
- common::Indirection{std::move(*ompLoopCons)}}};
- nextIt = block.erase(nextIt);
- } else if (nestedBeginName.v == llvm::omp::Directive::OMPD_unroll &&
- beginName.v == llvm::omp::Directive::OMPD_tile) {
- // if a loop has been unrolled, the user can not then tile that loop
- // as it has been unrolled
- const parser::OmpClauseList &unrollClauseList{
- nestedBeginDirective.Clauses()};
- if (unrollClauseList.v.empty()) {
- // if the clause list is empty for an unroll construct, we assume
- // the loop is being fully unrolled
- tileUnrollError(beginName, messages_);
- } else {
- // parse the clauses for the unroll directive to find the full
- // clause
- for (auto &clause : unrollClauseList.v) {
- if (clause.Id() == llvm::omp::OMPC_full) {
- tileUnrollError(beginName, messages_);
- }
- }
- }
- } else {
- messages_.Say(nestedBeginName.source,
- "Only Loop Transformation Constructs or Loop Nests can be nested within Loop Constructs"_err_en_US,
- parser::ToUpperCaseLetters(nestedBeginName.source.ToString()));
- }
- } else {
- missingDoConstruct(beginName, messages_);
- }
- // If we get here, we either found a loop, or issued an error message.
- return;
- }
- if (nextIt == block.end()) {
- missingDoConstruct(beginName, messages_);
- }
- }
-
// Canonicalization of allocate directives
//
// In OpenMP 5.0 and 5.1 the allocate directive could either be a declarative
diff --git a/flang/lib/Semantics/check-call.cpp b/flang/lib/Semantics/check-call.cpp
index 995deaa..022b428 100644
--- a/flang/lib/Semantics/check-call.cpp
+++ b/flang/lib/Semantics/check-call.cpp
@@ -548,8 +548,13 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
actualLastSymbol = &ResolveAssociations(*actualLastSymbol);
}
int actualRank{actualType.Rank()};
- if (dummy.type.attrs().test(
- characteristics::TypeAndShape::Attr::AssumedShape)) {
+ if (dummyIsValue && dummyRank == 0 &&
+ dummy.ignoreTKR.test(common::IgnoreTKR::Rank) && actualRank > 0) {
+ messages.Say(
+ "Array actual argument may not be associated with IGNORE_TKR(R) scalar %s with VALUE attribute"_err_en_US,
+ dummyName);
+ } else if (dummy.type.attrs().test(
+ characteristics::TypeAndShape::Attr::AssumedShape)) {
// 15.5.2.4(16)
if (actualIsAssumedRank) {
messages.Say(
@@ -795,7 +800,9 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
bool dummyIsAssumedShape{dummy.type.attrs().test(
characteristics::TypeAndShape::Attr::AssumedShape)};
bool copyOutNeeded{
- evaluate::MayNeedCopy(&arg, &dummyArg, foldingContext, true)};
+ evaluate::ActualArgNeedsCopy(&arg, &dummyArg, foldingContext,
+ /*forCopyOut=*/true)
+ .value_or(false)};
if (copyOutNeeded && !dummyIsValue &&
(dummyIsAsynchronous || dummyIsVolatile)) {
if (actualIsAsynchronous || actualIsVolatile) {
@@ -832,8 +839,8 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
// a unread value in the actual argument.
// Occurences of `volatileOrAsyncNeedsTempDiagnosticIssued = true` indicate a
// more specific error message has already been issued. We might be able to
- // clean this up by switching the coding style of MayNeedCopy to be more like
- // WhyNotDefinable.
+ // clean this up by switching the coding style of ActualArgNeedsCopy to be
+ // more like WhyNotDefinable.
if (copyOutNeeded && !volatileOrAsyncNeedsTempDiagnosticIssued) {
if ((actualIsVolatile || actualIsAsynchronous) &&
(dummyIsVolatile || dummyIsAsynchronous)) {
diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp
index de407d3..9a6b3ff 100644
--- a/flang/lib/Semantics/check-declarations.cpp
+++ b/flang/lib/Semantics/check-declarations.cpp
@@ -855,6 +855,15 @@ void CheckHelper::CheckObjectEntity(
messages_.Say(
"Variable '%s' with EVENT_TYPE or LOCK_TYPE potential component '%s' must be a coarray"_err_en_US,
symbol.name(), component.BuildResultDesignatorName());
+ } else if (IsNotifyType(derived)) { // C1612
+ messages_.Say(
+ "Variable '%s' with NOTIFY_TYPE must be a coarray"_err_en_US,
+ symbol.name());
+ } else if (auto component{FindNotifyPotentialComponent( // C1611
+ *derived, /*ignoreCoarrays=*/true)}) {
+ messages_.Say(
+ "Variable '%s' with NOTIFY_TYPE potential component '%s' must be a coarray"_err_en_US,
+ symbol.name(), component.BuildResultDesignatorName());
}
}
}
@@ -873,6 +882,10 @@ void CheckHelper::CheckObjectEntity(
messages_.Say(
"An INTENT(OUT) dummy argument may not be, or contain, EVENT_TYPE or LOCK_TYPE"_err_en_US);
}
+ if (IsOrContainsNotifyComponent(symbol)) { // C1613
+ messages_.Say(
+ "An INTENT(OUT) dummy argument may not be, or contain, NOTIFY_TYPE"_err_en_US);
+ }
if (IsAssumedSizeArray(symbol)) { // C834
if (type && type->IsPolymorphic()) {
messages_.Say(
diff --git a/flang/lib/Semantics/check-omp-atomic.cpp b/flang/lib/Semantics/check-omp-atomic.cpp
index 2707921..b9e34ca 100644
--- a/flang/lib/Semantics/check-omp-atomic.cpp
+++ b/flang/lib/Semantics/check-omp-atomic.cpp
@@ -19,6 +19,7 @@
#include "flang/Evaluate/rewrite.h"
#include "flang/Evaluate/tools.h"
#include "flang/Parser/char-block.h"
+#include "flang/Parser/openmp-utils.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/openmp-utils.h"
#include "flang/Semantics/symbol.h"
@@ -41,6 +42,7 @@
namespace Fortran::semantics {
+using namespace Fortran::parser::omp;
using namespace Fortran::semantics::omp;
namespace operation = Fortran::evaluate::operation;
@@ -590,9 +592,11 @@ void OmpStructureChecker::CheckAtomicVariable(
CheckAtomicType(syms.back(), source, atom.AsFortran(), checkTypeOnPointer);
- if (IsAllocatable(syms.back()) && !IsArrayElement(atom)) {
- context_.Say(source, "Atomic variable %s cannot be ALLOCATABLE"_err_en_US,
- atom.AsFortran());
+ if (!IsArrayElement(atom) && !ExtractComplexPart(atom)) {
+ if (IsAllocatable(syms.back())) {
+ context_.Say(source, "Atomic variable %s cannot be ALLOCATABLE"_err_en_US,
+ atom.AsFortran());
+ }
}
}
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index aaaa2d6..726dbe8 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -245,6 +245,98 @@ void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) {
}
}
+static bool IsLoopTransforming(llvm::omp::Directive dir) {
+ switch (dir) {
+ // TODO case llvm::omp::Directive::OMPD_flatten:
+ case llvm::omp::Directive::OMPD_fuse:
+ case llvm::omp::Directive::OMPD_interchange:
+ case llvm::omp::Directive::OMPD_nothing:
+ case llvm::omp::Directive::OMPD_reverse:
+ // TODO case llvm::omp::Directive::OMPD_split:
+ case llvm::omp::Directive::OMPD_stripe:
+ case llvm::omp::Directive::OMPD_tile:
+ case llvm::omp::Directive::OMPD_unroll:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void OmpStructureChecker::CheckNestedBlock(const parser::OpenMPLoopConstruct &x,
+ const parser::Block &body, size_t &nestedCount) {
+ for (auto &stmt : body) {
+ if (auto *dir{parser::Unwrap<parser::CompilerDirective>(stmt)}) {
+ context_.Say(dir->source,
+ "Compiler directives are not allowed inside OpenMP loop constructs"_warn_en_US);
+ } else if (parser::Unwrap<parser::DoConstruct>(stmt)) {
+ ++nestedCount;
+ } else if (auto *omp{parser::Unwrap<parser::OpenMPLoopConstruct>(stmt)}) {
+ if (!IsLoopTransforming(omp->BeginDir().DirId())) {
+ context_.Say(omp->source,
+ "Only loop-transforming OpenMP constructs are allowed inside OpenMP loop constructs"_err_en_US);
+ }
+ ++nestedCount;
+ } else if (auto *block{parser::Unwrap<parser::BlockConstruct>(stmt)}) {
+ CheckNestedBlock(x, std::get<parser::Block>(block->t), nestedCount);
+ } else {
+ parser::CharBlock source{parser::GetSource(stmt).value_or(x.source)};
+ context_.Say(source,
+ "OpenMP loop construct can only contain DO loops or loop-nest-generating OpenMP constructs"_err_en_US);
+ }
+ }
+}
+
+void OmpStructureChecker::CheckNestedConstruct(
+ const parser::OpenMPLoopConstruct &x) {
+ size_t nestedCount{0};
+
+ // End-directive is not allowed in such cases:
+ // do 100 i = ...
+ // !$omp do
+ // do 100 j = ...
+ // 100 continue
+ // !$omp end do ! error
+ const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
+ auto &flags{std::get<parser::OmpDirectiveSpecification::Flags>(beginSpec.t)};
+ if (flags.test(parser::OmpDirectiveSpecification::Flag::CrossesLabelDo)) {
+ if (auto &endSpec{x.EndDir()}) {
+ parser::CharBlock beginSource{beginSpec.DirName().source};
+ context_
+ .Say(endSpec->DirName().source,
+ "END %s directive is not allowed when the construct does not contain all loops that share a loop-terminating statement"_err_en_US,
+ parser::ToUpperCaseLetters(beginSource.ToString()))
+ .Attach(beginSource, "The construct starts here"_en_US);
+ }
+ }
+
+ auto &body{std::get<parser::Block>(x.t)};
+ if (body.empty()) {
+ context_.Say(x.source,
+ "OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct"_err_en_US);
+ } else {
+ CheckNestedBlock(x, body, nestedCount);
+ }
+}
+
+void OmpStructureChecker::CheckFullUnroll(
+ const parser::OpenMPLoopConstruct &x) {
+ // If the nested construct is a full unroll, then this construct is invalid
+ // since it won't contain a loop.
+ if (const parser::OpenMPLoopConstruct *nested{x.GetNestedConstruct()}) {
+ auto &nestedSpec{nested->BeginDir()};
+ if (nestedSpec.DirId() == llvm::omp::Directive::OMPD_unroll) {
+ bool isPartial{
+ llvm::any_of(nestedSpec.Clauses().v, [](const parser::OmpClause &c) {
+ return c.Id() == llvm::omp::Clause::OMPC_partial;
+ })};
+ if (!isPartial) {
+ context_.Say(x.source,
+ "OpenMP loop construct cannot apply to a fully unrolled loop"_err_en_US);
+ }
+ }
+ }
+}
+
void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
loopStack_.push_back(&x);
@@ -262,6 +354,15 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
EnterDirectiveNest(SIMDNest);
}
+ if (CurrentDirectiveIsNested() &&
+ llvm::omp::topTeamsSet.test(GetContext().directive) &&
+ GetContextParent().directive == llvm::omp::Directive::OMPD_target &&
+ !GetDirectiveNest(TargetBlockOnlyTeams)) {
+ context_.Say(GetContextParent().directiveSource,
+ "TARGET construct with nested TEAMS region contains statements or "
+ "directives outside of the TEAMS construct"_err_en_US);
+ }
+
// Combined target loop constructs are target device constructs. Keep track of
// whether any such construct has been visited to later check that REQUIRES
// directives for target-related options don't appear after them.
@@ -285,15 +386,15 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
}
SetLoopInfo(x);
- auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
- if (optLoopCons.has_value()) {
- if (const auto &doConstruct{
- std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
+ for (auto &construct : std::get<parser::Block>(x.t)) {
+ if (const auto *doConstruct{parser::omp::GetDoConstruct(construct)}) {
const auto &doBlock{std::get<parser::Block>(doConstruct->t)};
CheckNoBranching(doBlock, beginName.v, beginName.source);
}
}
CheckLoopItrVariableIsInt(x);
+ CheckNestedConstruct(x);
+ CheckFullUnroll(x);
CheckAssociatedLoopConstraints(x);
HasInvalidDistributeNesting(x);
HasInvalidLoopBinding(x);
@@ -305,6 +406,11 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
beginName.v == llvm::omp::Directive::OMPD_distribute_simd) {
CheckDistLinear(x);
}
+ if (beginName.v == llvm::omp::Directive::OMPD_fuse) {
+ CheckLooprangeBounds(x);
+ } else {
+ CheckNestedFuse(x);
+ }
}
const parser::Name OmpStructureChecker::GetLoopIndex(
@@ -314,45 +420,37 @@ const parser::Name OmpStructureChecker::GetLoopIndex(
}
void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) {
- auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
- if (optLoopCons.has_value()) {
- if (const auto &loopConstruct{
- std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
- const parser::DoConstruct *loop{&*loopConstruct};
- if (loop && loop->IsDoNormal()) {
- const parser::Name &itrVal{GetLoopIndex(loop)};
- SetLoopIv(itrVal.symbol);
- }
+ if (const auto *loop{x.GetNestedLoop()}) {
+ if (loop->IsDoNormal()) {
+ const parser::Name &itrVal{GetLoopIndex(loop)};
+ SetLoopIv(itrVal.symbol);
}
}
}
void OmpStructureChecker::CheckLoopItrVariableIsInt(
const parser::OpenMPLoopConstruct &x) {
- auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
- if (optLoopCons.has_value()) {
- if (const auto &loopConstruct{
- std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
-
- for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) {
- if (loop->IsDoNormal()) {
- const parser::Name &itrVal{GetLoopIndex(loop)};
- if (itrVal.symbol) {
- const auto *type{itrVal.symbol->GetType()};
- if (!type->IsNumeric(TypeCategory::Integer)) {
- context_.Say(itrVal.source,
- "The DO loop iteration"
- " variable must be of the type integer."_err_en_US,
- itrVal.ToString());
- }
+ for (auto &construct : std::get<parser::Block>(x.t)) {
+ for (const parser::DoConstruct *loop{
+ parser::omp::GetDoConstruct(construct)};
+ loop;) {
+ if (loop->IsDoNormal()) {
+ const parser::Name &itrVal{GetLoopIndex(loop)};
+ if (itrVal.symbol) {
+ const auto *type{itrVal.symbol->GetType()};
+ if (!type->IsNumeric(TypeCategory::Integer)) {
+ context_.Say(itrVal.source,
+ "The DO loop iteration"
+ " variable must be of the type integer."_err_en_US,
+ itrVal.ToString());
}
}
- // Get the next DoConstruct if block is not empty.
- const auto &block{std::get<parser::Block>(loop->t)};
- const auto it{block.begin()};
- loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
- : nullptr;
}
+ // Get the next DoConstruct if block is not empty.
+ const auto &block{std::get<parser::Block>(loop->t)};
+ const auto it{block.begin()};
+ loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
+ : nullptr;
}
}
}
@@ -401,9 +499,8 @@ void OmpStructureChecker::CheckDistLinear(
// Collect symbols of all the variables from linear clauses
for (auto &clause : clauses.v) {
- if (auto *linearClause{std::get_if<parser::OmpClause::Linear>(&clause.u)}) {
- auto &objects{std::get<parser::OmpObjectList>(linearClause->v.t)};
- GetSymbolsInObjectList(objects, indexVars);
+ if (std::get_if<parser::OmpClause::Linear>(&clause.u)) {
+ GetSymbolsInObjectList(*parser::omp::GetOmpObjectList(clause), indexVars);
}
}
@@ -417,28 +514,27 @@ void OmpStructureChecker::CheckDistLinear(
// Match the loop index variables with the collected symbols from linear
// clauses.
- auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
- if (optLoopCons.has_value()) {
- if (const auto &loopConstruct{
- std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
- for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) {
- if (loop->IsDoNormal()) {
- const parser::Name &itrVal{GetLoopIndex(loop)};
- if (itrVal.symbol) {
- // Remove the symbol from the collected set
- indexVars.erase(&itrVal.symbol->GetUltimate());
- }
- collapseVal--;
- if (collapseVal == 0) {
- break;
- }
+ for (auto &construct : std::get<parser::Block>(x.t)) {
+ std::int64_t curCollapseVal{collapseVal};
+ for (const parser::DoConstruct *loop{
+ parser::omp::GetDoConstruct(construct)};
+ loop;) {
+ if (loop->IsDoNormal()) {
+ const parser::Name &itrVal{GetLoopIndex(loop)};
+ if (itrVal.symbol) {
+ // Remove the symbol from the collected set
+ indexVars.erase(&itrVal.symbol->GetUltimate());
+ }
+ curCollapseVal--;
+ if (curCollapseVal == 0) {
+ break;
}
- // Get the next DoConstruct if block is not empty.
- const auto &block{std::get<parser::Block>(loop->t)};
- const auto it{block.begin()};
- loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
- : nullptr;
}
+ // Get the next DoConstruct if block is not empty.
+ const auto &block{std::get<parser::Block>(loop->t)};
+ const auto it{block.begin()};
+ loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
+ : nullptr;
}
}
@@ -452,6 +548,101 @@ void OmpStructureChecker::CheckDistLinear(
}
}
+void OmpStructureChecker::CheckLooprangeBounds(
+ const parser::OpenMPLoopConstruct &x) {
+ const parser::OmpClauseList &clauseList{x.BeginDir().Clauses()};
+ if (clauseList.v.empty()) {
+ return;
+ }
+ for (auto &clause : clauseList.v) {
+ if (const auto *lrClause{
+ std::get_if<parser::OmpClause::Looprange>(&clause.u)}) {
+ auto first{GetIntValue(std::get<0>((lrClause->v).t))};
+ auto count{GetIntValue(std::get<1>((lrClause->v).t))};
+ if (!first || !count) {
+ return;
+ }
+ auto &loopConsList{std::get<parser::Block>(x.t)};
+ if (*first > 0 && *count > 0 &&
+ loopConsList.size() < (unsigned)(*first + *count - 1)) {
+ context_.Say(clause.source,
+ "The loop range indicated in the %s clause must not be out of the bounds of the Loop Sequence following the construct."_err_en_US,
+ parser::ToUpperCaseLetters(clause.source.ToString()));
+ }
+ return;
+ }
+ }
+}
+
+void OmpStructureChecker::CheckNestedFuse(
+ const parser::OpenMPLoopConstruct &x) {
+ auto &loopConsList{std::get<parser::Block>(x.t)};
+ if (loopConsList.empty()) {
+ return;
+ }
+ const auto *ompConstruct{parser::omp::GetOmpLoop(loopConsList.front())};
+ if (!ompConstruct) {
+ return;
+ }
+ const parser::OmpClauseList &clauseList{ompConstruct->BeginDir().Clauses()};
+ if (clauseList.v.empty()) {
+ return;
+ }
+ for (auto &clause : clauseList.v) {
+ if (const auto *lrClause{
+ std::get_if<parser::OmpClause::Looprange>(&clause.u)}) {
+ auto count{GetIntValue(std::get<1>((lrClause->v).t))};
+ if (!count) {
+ return;
+ }
+ auto &nestedLoopConsList{std::get<parser::Block>(ompConstruct->t)};
+ if (nestedLoopConsList.size() > (unsigned)(*count)) {
+ context_.Say(x.BeginDir().DirName().source,
+ "The loop sequence following the %s construct must be fully fused first."_err_en_US,
+ parser::ToUpperCaseLetters(
+ x.BeginDir().DirName().source.ToString()));
+ }
+ return;
+ }
+ }
+}
+
+void OmpStructureChecker::CheckScanModifier(
+ const parser::OmpClause::Reduction &x) {
+ using ReductionModifier = parser::OmpReductionModifier;
+
+ auto checkReductionSymbolInScan{[&](const parser::Name &name) {
+ if (auto *symbol{name.symbol}) {
+ if (!symbol->test(Symbol::Flag::OmpInclusiveScan) &&
+ !symbol->test(Symbol::Flag::OmpExclusiveScan)) {
+ context_.Say(name.source,
+ "List item %s must appear in EXCLUSIVE or INCLUSIVE clause of an enclosed SCAN directive"_err_en_US,
+ name.ToString());
+ }
+ }
+ }};
+
+ auto &modifiers{OmpGetModifiers(x.v)};
+ auto *maybeModifier{OmpGetUniqueModifier<ReductionModifier>(modifiers)};
+ if (maybeModifier && maybeModifier->v == ReductionModifier::Value::Inscan) {
+ for (const auto &ompObj : parser::omp::GetOmpObjectList(x)->v) {
+ common::visit(
+ common::visitors{
+ [&](const parser::Designator &desg) {
+ if (auto *name{parser::GetDesignatorNameIfDataRef(desg)}) {
+ checkReductionSymbolInScan(*name);
+ }
+ },
+ [&](const parser::Name &name) {
+ checkReductionSymbolInScan(name);
+ },
+ [&](const parser::OmpObject::Invalid &invalid) {},
+ },
+ ompObj.u);
+ }
+ }
+}
+
void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &x) {
const parser::OmpClauseList &clauseList{x.BeginDir().Clauses()};
@@ -459,45 +650,9 @@ void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &x) {
// constructs inside LOOP may add the relevant information. Scan reduction is
// supported only in loop constructs, so same checks are not applicable to
// other directives.
- using ReductionModifier = parser::OmpReductionModifier;
for (const auto &clause : clauseList.v) {
- if (const auto *reductionClause{
- std::get_if<parser::OmpClause::Reduction>(&clause.u)}) {
- auto &modifiers{OmpGetModifiers(reductionClause->v)};
- auto *maybeModifier{OmpGetUniqueModifier<ReductionModifier>(modifiers)};
- if (maybeModifier &&
- maybeModifier->v == ReductionModifier::Value::Inscan) {
- const auto &objectList{
- std::get<parser::OmpObjectList>(reductionClause->v.t)};
- auto checkReductionSymbolInScan = [&](const parser::Name *name) {
- if (auto &symbol = name->symbol) {
- if (!symbol->test(Symbol::Flag::OmpInclusiveScan) &&
- !symbol->test(Symbol::Flag::OmpExclusiveScan)) {
- context_.Say(name->source,
- "List item %s must appear in EXCLUSIVE or "
- "INCLUSIVE clause of an "
- "enclosed SCAN directive"_err_en_US,
- name->ToString());
- }
- }
- };
- for (const auto &ompObj : objectList.v) {
- common::visit(
- common::visitors{
- [&](const parser::Designator &designator) {
- if (const auto *name{
- parser::GetDesignatorNameIfDataRef(designator)}) {
- checkReductionSymbolInScan(name);
- }
- },
- [&](const parser::Name &name) {
- checkReductionSymbolInScan(&name);
- },
- [&](const parser::OmpObject::Invalid &invalid) {},
- },
- ompObj.u);
- }
- }
+ if (auto *reduction{std::get_if<parser::OmpClause::Reduction>(&clause.u)}) {
+ CheckScanModifier(*reduction);
}
}
if (llvm::omp::allSimdSet.test(GetContext().directive)) {
@@ -636,6 +791,20 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) {
}
}
+void OmpStructureChecker::Enter(const parser::OmpClause::Sizes &c) {
+ CheckAllowedClause(llvm::omp::Clause::OMPC_sizes);
+ for (const parser::Cosubscript &v : c.v)
+ RequiresPositiveParameter(llvm::omp::Clause::OMPC_sizes, v,
+ /*paramName=*/"parameter", /*allowZero=*/false);
+}
+
+void OmpStructureChecker::Enter(const parser::OmpClause::Looprange &x) {
+ CheckAllowedClause(llvm::omp::Clause::OMPC_looprange);
+ auto &[first, count]{x.v.t};
+ RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_looprange, count);
+ RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_looprange, first);
+}
+
void OmpStructureChecker::Enter(const parser::DoConstruct &x) {
Base::Enter(x);
loopStack_.push_back(&x);
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index d7db15d..7776f0d 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -624,11 +624,9 @@ void OmpStructureChecker::CheckMultListItems() {
// Linear clause
for (auto [_, clause] : FindClauses(llvm::omp::Clause::OMPC_linear)) {
- auto &linearClause{std::get<parser::OmpClause::Linear>(clause->u)};
std::list<parser::Name> nameList;
SymbolSourceMap symbols;
- GetSymbolsInObjectList(
- std::get<parser::OmpObjectList>(linearClause.v.t), symbols);
+ GetSymbolsInObjectList(*GetOmpObjectList(*clause), symbols);
llvm::transform(symbols, std::back_inserter(nameList), [&](auto &&pair) {
return parser::Name{pair.second, const_cast<Symbol *>(pair.first)};
});
@@ -682,6 +680,13 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Hint &x) {
}
}
+void OmpStructureChecker::Enter(const parser::OmpClause::DynGroupprivate &x) {
+ CheckAllowedClause(llvm::omp::Clause::OMPC_dyn_groupprivate);
+ parser::CharBlock source{GetContext().clauseSource};
+
+ OmpVerifyModifiers(x.v, llvm::omp::OMPC_dyn_groupprivate, source, context_);
+}
+
void OmpStructureChecker::Enter(const parser::OmpDirectiveSpecification &x) {
// OmpDirectiveSpecification exists on its own only in METADIRECTIVE.
// In other cases it's a part of other constructs that handle directive
@@ -2094,29 +2099,29 @@ void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &x) {
}
}
- bool toClauseFound{false}, deviceTypeClauseFound{false},
- enterClauseFound{false};
+ bool toClauseFound{false};
+ bool deviceTypeClauseFound{false};
+ bool enterClauseFound{false};
for (const parser::OmpClause &clause : x.v.Clauses().v) {
common::visit(
common::visitors{
- [&](const parser::OmpClause::To &toClause) {
- toClauseFound = true;
- auto &objList{std::get<parser::OmpObjectList>(toClause.v.t)};
- CheckSymbolNames(dirName.source, objList);
- CheckVarIsNotPartOfAnotherVar(dirName.source, objList);
- CheckThreadprivateOrDeclareTargetVar(objList);
- },
- [&](const parser::OmpClause::Link &linkClause) {
- CheckSymbolNames(dirName.source, linkClause.v);
- CheckVarIsNotPartOfAnotherVar(dirName.source, linkClause.v);
- CheckThreadprivateOrDeclareTargetVar(linkClause.v);
- },
- [&](const parser::OmpClause::Enter &enterClause) {
- enterClauseFound = true;
- auto &objList{std::get<parser::OmpObjectList>(enterClause.v.t)};
- CheckSymbolNames(dirName.source, objList);
- CheckVarIsNotPartOfAnotherVar(dirName.source, objList);
- CheckThreadprivateOrDeclareTargetVar(objList);
+ [&](const auto &c) {
+ using TypeC = llvm::remove_cvref_t<decltype(c)>;
+ if constexpr ( //
+ std::is_same_v<TypeC, parser::OmpClause::Enter> ||
+ std::is_same_v<TypeC, parser::OmpClause::Link> ||
+ std::is_same_v<TypeC, parser::OmpClause::To>) {
+ auto &objList{*GetOmpObjectList(c)};
+ CheckSymbolNames(dirName.source, objList);
+ CheckVarIsNotPartOfAnotherVar(dirName.source, objList);
+ CheckThreadprivateOrDeclareTargetVar(objList);
+ }
+ if constexpr (std::is_same_v<TypeC, parser::OmpClause::Enter>) {
+ enterClauseFound = true;
+ }
+ if constexpr (std::is_same_v<TypeC, parser::OmpClause::To>) {
+ toClauseFound = true;
+ }
},
[&](const parser::OmpClause::DeviceType &deviceTypeClause) {
deviceTypeClauseFound = true;
@@ -2127,7 +2132,6 @@ void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &x) {
deviceConstructFound_ = true;
}
},
- [&](const auto &) {},
},
clause.u);
@@ -2417,12 +2421,8 @@ void OmpStructureChecker::CheckTargetUpdate() {
}
if (toWrapper && fromWrapper) {
SymbolSourceMap toSymbols, fromSymbols;
- auto &fromClause{std::get<parser::OmpClause::From>(fromWrapper->u).v};
- auto &toClause{std::get<parser::OmpClause::To>(toWrapper->u).v};
- GetSymbolsInObjectList(
- std::get<parser::OmpObjectList>(fromClause.t), fromSymbols);
- GetSymbolsInObjectList(
- std::get<parser::OmpObjectList>(toClause.t), toSymbols);
+ GetSymbolsInObjectList(*GetOmpObjectList(*fromWrapper), fromSymbols);
+ GetSymbolsInObjectList(*GetOmpObjectList(*toWrapper), toSymbols);
for (auto &[symbol, source] : toSymbols) {
auto fromSymbol{fromSymbols.find(symbol)};
@@ -2741,8 +2741,8 @@ void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) {
unsigned version{context_.langOptions().OpenMPVersion};
if (version >= 52) {
- using Flags = parser::OmpDirectiveSpecification::Flags;
- if (std::get<Flags>(x.v.t) == Flags::DeprecatedSyntax) {
+ auto &flags{std::get<parser::OmpDirectiveSpecification::Flags>(x.v.t)};
+ if (flags.test(parser::OmpDirectiveSpecification::Flag::DeprecatedSyntax)) {
context_.Say(x.source,
"The syntax \"FLUSH clause (object, ...)\" has been deprecated, use \"FLUSH(object, ...) clause\" instead"_warn_en_US);
}
@@ -2800,7 +2800,7 @@ void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) {
void OmpStructureChecker::Enter(const parser::OpenMPCriticalConstruct &x) {
const parser::OmpBeginDirective &beginSpec{x.BeginDir()};
const std::optional<parser::OmpEndDirective> &endSpec{x.EndDir()};
- PushContextAndClauseSets(beginSpec.DirName().source, beginSpec.DirName().v);
+ PushContextAndClauseSets(beginSpec.DirName().source, beginSpec.DirId());
const auto &block{std::get<parser::Block>(x.t)};
CheckNoBranching(
@@ -3262,7 +3262,7 @@ void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
const auto &irClause{
std::get<parser::OmpClause::InReduction>(dataEnvClause->u)};
checkVarAppearsInDataEnvClause(
- std::get<parser::OmpObjectList>(irClause.v.t), "IN_REDUCTION");
+ *GetOmpObjectList(irClause), "IN_REDUCTION");
}
}
}
@@ -3316,6 +3316,32 @@ void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
}
}
+ // Default access-group for DYN_GROUPPRIVATE is "cgroup". On a given
+ // construct there can be at most one DYN_GROUPPRIVATE with a given
+ // access-group.
+ const parser::OmpClause
+ *accGrpClause[parser::OmpAccessGroup::Value_enumSize] = {nullptr};
+ for (auto [_, clause] :
+ FindClauses(llvm::omp::Clause::OMPC_dyn_groupprivate)) {
+ auto &wrapper{std::get<parser::OmpClause::DynGroupprivate>(clause->u)};
+ auto &modifiers{OmpGetModifiers(wrapper.v)};
+ auto accGrp{parser::OmpAccessGroup::Value::Cgroup};
+ if (auto *ag{OmpGetUniqueModifier<parser::OmpAccessGroup>(modifiers)}) {
+ accGrp = ag->v;
+ }
+ auto &firstClause{accGrpClause[llvm::to_underlying(accGrp)]};
+ if (firstClause) {
+ context_
+ .Say(clause->source,
+ "The access-group modifier can only occur on a single clause in a construct"_err_en_US)
+ .Attach(firstClause->source,
+ "Previous clause with access-group modifier"_en_US);
+ break;
+ } else {
+ firstClause = clause;
+ }
+ }
+
CheckRequireAtLeastOneOf();
}
@@ -3360,19 +3386,6 @@ void OmpStructureChecker::Enter(const parser::OmpClause &x) {
}
}
-void OmpStructureChecker::Enter(const parser::OmpClause::Sizes &c) {
- CheckAllowedClause(llvm::omp::Clause::OMPC_sizes);
- for (const parser::Cosubscript &v : c.v)
- RequiresPositiveParameter(llvm::omp::Clause::OMPC_sizes, v,
- /*paramName=*/"parameter", /*allowZero=*/false);
-}
-
-void OmpStructureChecker::Enter(const parser::OmpClause::Looprange &x) {
- context_.Say(GetContext().clauseSource,
- "LOOPRANGE clause is not implemented yet"_err_en_US,
- ContextDirectiveAsFortran());
-}
-
// Restrictions specific to each clause are implemented apart from the
// generalized restrictions.
@@ -3401,7 +3414,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Destroy &x) {
void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) {
CheckAllowedClause(llvm::omp::Clause::OMPC_reduction);
- auto &objects{std::get<parser::OmpObjectList>(x.v.t)};
+ auto &objects{*GetOmpObjectList(x)};
if (OmpVerifyModifiers(x.v, llvm::omp::OMPC_reduction,
GetContext().clauseSource, context_)) {
@@ -3441,7 +3454,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) {
void OmpStructureChecker::Enter(const parser::OmpClause::InReduction &x) {
CheckAllowedClause(llvm::omp::Clause::OMPC_in_reduction);
- auto &objects{std::get<parser::OmpObjectList>(x.v.t)};
+ auto &objects{*GetOmpObjectList(x)};
if (OmpVerifyModifiers(x.v, llvm::omp::OMPC_in_reduction,
GetContext().clauseSource, context_)) {
@@ -3459,7 +3472,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::InReduction &x) {
void OmpStructureChecker::Enter(const parser::OmpClause::TaskReduction &x) {
CheckAllowedClause(llvm::omp::Clause::OMPC_task_reduction);
- auto &objects{std::get<parser::OmpObjectList>(x.v.t)};
+ auto &objects{*GetOmpObjectList(x)};
if (OmpVerifyModifiers(x.v, llvm::omp::OMPC_task_reduction,
GetContext().clauseSource, context_)) {
@@ -4312,8 +4325,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
}};
evaluate::ExpressionAnalyzer ea{context_};
- const auto &objects{std::get<parser::OmpObjectList>(x.v.t)};
- for (auto &object : objects.v) {
+ for (auto &object : GetOmpObjectList(x)->v) {
if (const parser::Designator *d{GetDesignatorFromObj(object)}) {
if (auto &&expr{ea.Analyze(*d)}) {
if (hasBasePointer(*expr)) {
@@ -4466,7 +4478,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Depend &x) {
}
}
if (taskDep) {
- auto &objList{std::get<parser::OmpObjectList>(taskDep->t)};
+ auto &objList{*GetOmpObjectList(*taskDep)};
if (dir == llvm::omp::OMPD_depobj) {
// [5.0:255:13], [5.1:288:6], [5.2:322:26]
// A depend clause on a depobj construct must only specify one locator.
@@ -4612,7 +4624,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &x) {
void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &x) {
CheckAllowedClause(llvm::omp::Clause::OMPC_lastprivate);
- const auto &objectList{std::get<parser::OmpObjectList>(x.v.t)};
+ const auto &objectList{*GetOmpObjectList(x)};
CheckVarIsNotPartOfAnotherVar(
GetContext().clauseSource, objectList, "LASTPRIVATE");
CheckCrayPointee(objectList, "LASTPRIVATE");
@@ -4673,10 +4685,12 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &x) {
void OmpStructureChecker::CheckStructureComponent(
const parser::OmpObjectList &objects, llvm::omp::Clause clauseId) {
auto CheckComponent{[&](const parser::Designator &designator) {
- if (auto *dataRef{std::get_if<parser::DataRef>(&designator.u)}) {
+ if (const parser::DataRef *dataRef{
+ std::get_if<parser::DataRef>(&designator.u)}) {
if (!IsDataRefTypeParamInquiry(dataRef)) {
- if (auto *comp{parser::Unwrap<parser::StructureComponent>(*dataRef)}) {
- context_.Say(comp->component.source,
+ const auto expr{AnalyzeExpr(context_, designator)};
+ if (expr.has_value() && evaluate::HasStructureComponent(expr.value())) {
+ context_.Say(designator.source,
"A variable that is part of another variable cannot appear on the %s clause"_err_en_US,
parser::ToUpperCaseLetters(getClauseName(clauseId).str()));
}
@@ -4852,9 +4866,8 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Enter &x) {
x.v, llvm::omp::OMPC_enter, GetContext().clauseSource, context_)) {
return;
}
- const parser::OmpObjectList &objList{std::get<parser::OmpObjectList>(x.v.t)};
SymbolSourceMap symbols;
- GetSymbolsInObjectList(objList, symbols);
+ GetSymbolsInObjectList(*GetOmpObjectList(x), symbols);
for (const auto &[symbol, source] : symbols) {
if (!IsExtendedListItem(*symbol)) {
context_.SayWithDecl(*symbol, source,
@@ -4877,7 +4890,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::From &x) {
CheckIteratorModifier(*iter);
}
- const auto &objList{std::get<parser::OmpObjectList>(x.v.t)};
+ const auto &objList{*GetOmpObjectList(x)};
SymbolSourceMap symbols;
GetSymbolsInObjectList(objList, symbols);
CheckVariableListItem(symbols);
@@ -4917,7 +4930,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::To &x) {
CheckIteratorModifier(*iter);
}
- const auto &objList{std::get<parser::OmpObjectList>(x.v.t)};
+ const auto &objList{*GetOmpObjectList(x)};
SymbolSourceMap symbols;
GetSymbolsInObjectList(objList, symbols);
CheckVariableListItem(symbols);
@@ -5180,6 +5193,13 @@ bool OmpStructureChecker::CheckTargetBlockOnlyTeams(
if (dirId == llvm::omp::Directive::OMPD_teams) {
nestedTeams = true;
}
+ } else if (const auto *ompLoopConstruct{
+ std::get_if<parser::OpenMPLoopConstruct>(
+ &ompConstruct->u)}) {
+ llvm::omp::Directive dirId{ompLoopConstruct->BeginDir().DirId()};
+ if (llvm::omp::topTeamsSet.test(dirId)) {
+ nestedTeams = true;
+ }
}
}
@@ -5439,6 +5459,25 @@ void OmpStructureChecker::CheckAllowedRequiresClause(llvmOmpClause clause) {
}
}
+void OmpStructureChecker::Enter(const parser::OpenMPMisplacedEndDirective &x) {
+ context_.Say(x.DirName().source, "Misplaced OpenMP end-directive"_err_en_US);
+ PushContextAndClauseSets(
+ x.DirName().source, llvm::omp::Directive::OMPD_unknown);
+}
+
+void OmpStructureChecker::Leave(const parser::OpenMPMisplacedEndDirective &x) {
+ dirContext_.pop_back();
+}
+
+void OmpStructureChecker::Enter(const parser::OpenMPInvalidDirective &x) {
+ context_.Say(x.source, "Invalid OpenMP directive"_err_en_US);
+ PushContextAndClauseSets(x.source, llvm::omp::Directive::OMPD_unknown);
+}
+
+void OmpStructureChecker::Leave(const parser::OpenMPInvalidDirective &x) {
+ dirContext_.pop_back();
+}
+
// Use when clause falls under 'struct OmpClause' in 'parse-tree.h'.
#define CHECK_SIMPLE_CLAUSE(X, Y) \
void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \
@@ -5466,13 +5505,13 @@ 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(Collector, OMPC_collector)
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)
@@ -5487,6 +5526,7 @@ 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(Inductor, OMPC_inductor)
CHECK_SIMPLE_CLAUSE(Initializer, OMPC_initializer)
CHECK_SIMPLE_CLAUSE(Init, OMPC_init)
CHECK_SIMPLE_CLAUSE(Link, OMPC_link)
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 1b84bc5..5bd5ae0 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -94,6 +94,11 @@ public:
void Enter(const parser::OpenMPDeclarativeConstruct &);
void Leave(const parser::OpenMPDeclarativeConstruct &);
+ void Enter(const parser::OpenMPMisplacedEndDirective &);
+ void Leave(const parser::OpenMPMisplacedEndDirective &);
+ void Enter(const parser::OpenMPInvalidDirective &);
+ void Leave(const parser::OpenMPInvalidDirective &);
+
void Enter(const parser::OpenMPLoopConstruct &);
void Leave(const parser::OpenMPLoopConstruct &);
void Enter(const parser::OmpEndLoopDirective &);
@@ -316,8 +321,15 @@ private:
void CheckAtomicWrite(const parser::OpenMPAtomicConstruct &x);
void CheckAtomicUpdate(const parser::OpenMPAtomicConstruct &x);
+ void CheckScanModifier(const parser::OmpClause::Reduction &x);
+ void CheckLooprangeBounds(const parser::OpenMPLoopConstruct &x);
+ void CheckNestedFuse(const parser::OpenMPLoopConstruct &x);
void CheckDistLinear(const parser::OpenMPLoopConstruct &x);
void CheckSIMDNest(const parser::OpenMPConstruct &x);
+ void CheckNestedBlock(const parser::OpenMPLoopConstruct &x,
+ const parser::Block &body, size_t &nestedCount);
+ void CheckNestedConstruct(const parser::OpenMPLoopConstruct &x);
+ void CheckFullUnroll(const parser::OpenMPLoopConstruct &x);
void CheckTargetNest(const parser::OpenMPConstruct &x);
void CheckTargetUpdate();
void CheckTaskgraph(const parser::OmpBlockConstruct &x);
diff --git a/flang/lib/Semantics/dump-expr.cpp b/flang/lib/Semantics/dump-expr.cpp
index 66cedab..8d354cf 100644
--- a/flang/lib/Semantics/dump-expr.cpp
+++ b/flang/lib/Semantics/dump-expr.cpp
@@ -23,6 +23,7 @@ void DumpEvaluateExpr::Show(const evaluate::CoarrayRef &x) {
Indent("coarray ref");
Show(x.base());
Show(x.cosubscript());
+ Show(x.notify());
Show(x.stat());
Show(x.team());
Outdent();
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index c8167fd..6f5d0bf 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -1579,6 +1579,19 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::CoindexedNamedObject &x) {
std::get<std::list<parser::ImageSelectorSpec>>(x.imageSelector.t)) {
common::visit(
common::visitors{
+ [&](const parser::ImageSelectorSpec::Notify &x) {
+ Analyze(x.v);
+ if (const auto *expr{GetExpr(context_, x.v)}) {
+ if (coarrayRef.notify()) {
+ Say("coindexed reference has multiple NOTIFY= specifiers"_err_en_US);
+ } else if (auto dyType{expr->GetType()};
+ dyType && IsNotifyType(GetDerivedTypeSpec(*dyType))) {
+ coarrayRef.set_notify(Expr<SomeType>{*expr});
+ } else {
+ Say("NOTIFY= specifier must have type NOTIFY_TYPE from ISO_FORTRAN_ENV"_err_en_US);
+ }
+ }
+ },
[&](const parser::ImageSelectorSpec::Stat &x) {
Analyze(x.v);
if (const auto *expr{GetExpr(context_, x.v)}) {
@@ -2090,17 +2103,32 @@ static MaybeExpr ImplicitConvertTo(const Symbol &sym, Expr<SomeType> &&expr,
}
MaybeExpr ExpressionAnalyzer::CheckStructureConstructor(
- parser::CharBlock typeName, const semantics::DerivedTypeSpec &spec,
+ parser::CharBlock typeName, const semantics::DerivedTypeSpec &spec0,
std::list<ComponentSpec> &&componentSpecs) {
+ semantics::Scope &scope{context_.FindScope(typeName)};
+ FoldingContext &foldingContext{GetFoldingContext()};
+ const semantics::DerivedTypeSpec *effectiveSpec{&spec0};
+ if (foldingContext.pdtInstance() && spec0.MightBeParameterized()) {
+ // We're processing a structure constructor in the context of a derived
+ // type instantiation, and the derived type of the structure constructor
+ // is parameterized. Evaluate its parameters in the context of the
+ // instantiation in progress so that the components in constructor's scope
+ // have the correct types.
+ semantics::DerivedTypeSpec newSpec{spec0};
+ newSpec.ReevaluateParameters(context());
+ const semantics::DeclTypeSpec &instantiatedType{
+ semantics::FindOrInstantiateDerivedType(
+ scope, std::move(newSpec), semantics::DeclTypeSpec::TypeDerived)};
+ effectiveSpec = &instantiatedType.derivedTypeSpec();
+ }
+ const semantics::DerivedTypeSpec &spec{*effectiveSpec};
const Symbol &typeSymbol{spec.typeSymbol()};
if (!spec.scope() || !typeSymbol.has<semantics::DerivedTypeDetails>()) {
return std::nullopt; // error recovery
}
- const semantics::Scope &scope{context_.FindScope(typeName)};
const semantics::Scope *pureContext{FindPureProcedureContaining(scope)};
const auto &typeDetails{typeSymbol.get<semantics::DerivedTypeDetails>()};
const Symbol *parentComponent{typeDetails.GetParentComponent(*spec.scope())};
-
if (typeSymbol.attrs().test(semantics::Attr::ABSTRACT)) { // C796
AttachDeclaration(
Say(typeName,
@@ -2140,6 +2168,9 @@ MaybeExpr ExpressionAnalyzer::CheckStructureConstructor(
parser::CharBlock exprSource{componentSpec.exprSource};
auto restorer{messages.SetLocation(source)};
const Symbol *symbol{componentSpec.keywordSymbol};
+ if (symbol) {
+ symbol = spec.scope()->FindComponent(symbol->name());
+ }
MaybeExpr &maybeValue{componentSpec.expr};
if (!maybeValue.has_value()) {
return std::nullopt;
@@ -2315,7 +2346,6 @@ MaybeExpr ExpressionAnalyzer::CheckStructureConstructor(
// convert would cause a segfault. Lowering will deal with
// conditionally converting and preserving the lower bounds in this
// case.
- FoldingContext &foldingContext{GetFoldingContext()};
if (MaybeExpr converted{ImplicitConvertTo(*symbol, std::move(value),
/*keepConvertImplicit=*/IsAllocatable(*symbol),
foldingContext)}) {
diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index b419864..840b98d 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -59,6 +59,7 @@ static void PutBound(llvm::raw_ostream &, const Bound &);
static void PutShapeSpec(llvm::raw_ostream &, const ShapeSpec &);
static void PutShape(
llvm::raw_ostream &, const ArraySpec &, char open, char close);
+static void PutMapper(llvm::raw_ostream &, const Symbol &, SemanticsContext &);
static llvm::raw_ostream &PutAttr(llvm::raw_ostream &, Attr);
static llvm::raw_ostream &PutType(llvm::raw_ostream &, const DeclTypeSpec &);
@@ -938,6 +939,7 @@ void ModFileWriter::PutEntity(llvm::raw_ostream &os, const Symbol &symbol) {
[&](const ProcEntityDetails &) { PutProcEntity(os, symbol); },
[&](const TypeParamDetails &) { PutTypeParam(os, symbol); },
[&](const UserReductionDetails &) { PutUserReduction(os, symbol); },
+ [&](const MapperDetails &) { PutMapper(decls_, symbol, context_); },
[&](const auto &) {
common::die("PutEntity: unexpected details: %s",
DetailsToString(symbol.details()).c_str());
@@ -1101,6 +1103,16 @@ void ModFileWriter::PutUserReduction(
}
}
+static void PutMapper(
+ llvm::raw_ostream &os, const Symbol &symbol, SemanticsContext &context) {
+ const auto &details{symbol.get<MapperDetails>()};
+ // Emit each saved DECLARE MAPPER construct as-is, so that consumers of the
+ // module can reparse it and recreate the mapper symbol and semantics state.
+ for (const auto *decl : details.GetDeclList()) {
+ Unparse(os, *decl, context.langOptions());
+ }
+}
+
void PutInit(llvm::raw_ostream &os, const Symbol &symbol, const MaybeExpr &init,
const parser::Expr *unanalyzed, SemanticsContext &context) {
if (IsNamedConstant(symbol) || symbol.owner().IsDerivedType()) {
diff --git a/flang/lib/Semantics/openmp-modifiers.cpp b/flang/lib/Semantics/openmp-modifiers.cpp
index 717fb03..f191b4d 100644
--- a/flang/lib/Semantics/openmp-modifiers.cpp
+++ b/flang/lib/Semantics/openmp-modifiers.cpp
@@ -75,6 +75,22 @@ unsigned OmpModifierDescriptor::since(llvm::omp::Clause id) const {
// generated in the future.
template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpAccessGroup>() {
+ static const OmpModifierDescriptor desc{
+ /*name=*/"access-group",
+ /*props=*/
+ {
+ {61, {OmpProperty::Unique}},
+ },
+ /*clauses=*/
+ {
+ {61, {Clause::OMPC_dyn_groupprivate}},
+ },
+ };
+ return desc;
+}
+
+template <>
const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpAlignment>() {
static const OmpModifierDescriptor desc{
/*name=*/"alignment",
@@ -322,6 +338,22 @@ const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpExpectation>() {
}
template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpFallbackModifier>() {
+ static const OmpModifierDescriptor desc{
+ /*name=*/"fallback-modifier",
+ /*props=*/
+ {
+ {61, {OmpProperty::Unique}},
+ },
+ /*clauses=*/
+ {
+ {61, {Clause::OMPC_dyn_groupprivate}},
+ },
+ };
+ return desc;
+}
+
+template <>
const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpInteropPreference>() {
static const OmpModifierDescriptor desc{
/*name=*/"interop-preference",
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 4a40d6e..18a37d6 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -496,32 +496,4 @@ bool IsPointerAssignment(const evaluate::Assignment &x) {
return std::holds_alternative<evaluate::Assignment::BoundsSpec>(x.u) ||
std::holds_alternative<evaluate::Assignment::BoundsRemapping>(x.u);
}
-
-/// parser::Block is a list of executable constructs, parser::BlockConstruct
-/// is Fortran's BLOCK/ENDBLOCK construct.
-/// Strip the outermost BlockConstructs, return the reference to the Block
-/// in the executable part of the innermost of the stripped constructs.
-/// Specifically, if the given `block` has a single entry (it's a list), and
-/// the entry is a BlockConstruct, get the Block contained within. Repeat
-/// this step as many times as possible.
-const parser::Block &GetInnermostExecPart(const parser::Block &block) {
- const parser::Block *iter{&block};
- while (iter->size() == 1) {
- const parser::ExecutionPartConstruct &ep{iter->front()};
- if (auto *bc{GetFortranBlockConstruct(ep)}) {
- iter = &std::get<parser::Block>(bc->t);
- } else {
- break;
- }
- }
- return *iter;
-}
-
-bool IsStrictlyStructuredBlock(const parser::Block &block) {
- if (block.size() == 1) {
- return GetFortranBlockConstruct(block.front()) != nullptr;
- } else {
- return false;
- }
-}
} // namespace Fortran::semantics::omp
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index deb57e0..6211643 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -525,10 +525,16 @@ public:
void Post(const parser::OpenMPSimpleStandaloneConstruct &) { PopContext(); }
bool Pre(const parser::OpenMPLoopConstruct &);
- void Post(const parser::OpenMPLoopConstruct &) { PopContext(); }
+ void Post(const parser::OpenMPLoopConstruct &) {
+ ordCollapseLevel++;
+ PopContext();
+ }
void Post(const parser::OmpBeginLoopDirective &) {
GetContext().withinConstruct = true;
}
+ bool Pre(const parser::OpenMPMisplacedEndDirective &x) { return false; }
+ bool Pre(const parser::OpenMPInvalidDirective &x) { return false; }
+
bool Pre(const parser::DoConstruct &);
bool Pre(const parser::OpenMPSectionsConstruct &);
@@ -711,8 +717,8 @@ public:
return false;
}
bool Pre(const parser::OmpAllocateClause &x) {
- const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
- ResolveOmpObjectList(objectList, Symbol::Flag::OmpAllocate);
+ ResolveOmpObjectList(
+ *parser::omp::GetOmpObjectList(x), Symbol::Flag::OmpAllocate);
return false;
}
bool Pre(const parser::OmpClause::Firstprivate &x) {
@@ -720,8 +726,8 @@ public:
return false;
}
bool Pre(const parser::OmpClause::Lastprivate &x) {
- const auto &objList{std::get<parser::OmpObjectList>(x.v.t)};
- ResolveOmpObjectList(objList, Symbol::Flag::OmpLastPrivate);
+ ResolveOmpObjectList(
+ *parser::omp::GetOmpObjectList(x), Symbol::Flag::OmpLastPrivate);
return false;
}
bool Pre(const parser::OmpClause::Copyin &x) {
@@ -733,8 +739,8 @@ public:
return false;
}
bool Pre(const parser::OmpLinearClause &x) {
- auto &objects{std::get<parser::OmpObjectList>(x.t)};
- ResolveOmpObjectList(objects, Symbol::Flag::OmpLinear);
+ ResolveOmpObjectList(
+ *parser::omp::GetOmpObjectList(x), Symbol::Flag::OmpLinear);
return false;
}
@@ -744,13 +750,13 @@ public:
}
bool Pre(const parser::OmpInReductionClause &x) {
- auto &objects{std::get<parser::OmpObjectList>(x.t)};
- ResolveOmpObjectList(objects, Symbol::Flag::OmpInReduction);
+ ResolveOmpObjectList(
+ *parser::omp::GetOmpObjectList(x), Symbol::Flag::OmpInReduction);
return false;
}
bool Pre(const parser::OmpClause::Reduction &x) {
- const auto &objList{std::get<parser::OmpObjectList>(x.v.t)};
+ const auto &objList{*parser::omp::GetOmpObjectList(x)};
ResolveOmpObjectList(objList, Symbol::Flag::OmpReduction);
if (auto &modifiers{OmpGetModifiers(x.v)}) {
@@ -800,8 +806,8 @@ public:
}
bool Pre(const parser::OmpAlignedClause &x) {
- const auto &alignedNameList{std::get<parser::OmpObjectList>(x.t)};
- ResolveOmpObjectList(alignedNameList, Symbol::Flag::OmpAligned);
+ ResolveOmpObjectList(
+ *parser::omp::GetOmpObjectList(x), Symbol::Flag::OmpAligned);
return false;
}
@@ -914,7 +920,7 @@ public:
}
}
- const auto &ompObjList{std::get<parser::OmpObjectList>(x.t)};
+ const auto &ompObjList{*parser::omp::GetOmpObjectList(x)};
for (const auto &ompObj : ompObjList.v) {
common::visit(
common::visitors{
@@ -2028,6 +2034,7 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd:
case llvm::omp::Directive::OMPD_teams_distribute_simd:
case llvm::omp::Directive::OMPD_teams_loop:
+ case llvm::omp::Directive::OMPD_fuse:
case llvm::omp::Directive::OMPD_tile:
case llvm::omp::Directive::OMPD_unroll:
PushContext(beginName.source, beginName.v);
@@ -2038,8 +2045,7 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
if (beginName.v == llvm::omp::OMPD_master_taskloop ||
beginName.v == llvm::omp::OMPD_master_taskloop_simd ||
beginName.v == llvm::omp::OMPD_parallel_master_taskloop ||
- beginName.v == llvm::omp::OMPD_parallel_master_taskloop_simd ||
- beginName.v == llvm::omp::Directive::OMPD_target_loop) {
+ beginName.v == llvm::omp::OMPD_parallel_master_taskloop_simd) {
unsigned version{context_.langOptions().OpenMPVersion};
IssueNonConformanceWarning(beginName.v, beginName.source, version);
}
@@ -2047,13 +2053,9 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
SetContextAssociatedLoopLevel(GetNumAffectedLoopsFromLoopConstruct(x));
if (beginName.v == llvm::omp::Directive::OMPD_do) {
- auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
- if (optLoopCons.has_value()) {
- if (const auto &doConstruct{
- std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
- if (doConstruct->IsDoWhile()) {
- return true;
- }
+ if (const parser::DoConstruct *doConstruct{x.GetNestedLoop()}) {
+ if (doConstruct->IsDoWhile()) {
+ return true;
}
}
}
@@ -2210,18 +2212,11 @@ void OmpAttributeVisitor::CollectNumAffectedLoopsFromInnerLoopContruct(
const parser::OpenMPLoopConstruct &x,
llvm::SmallVector<std::int64_t> &levels,
llvm::SmallVector<const parser::OmpClause *> &clauses) {
-
- const auto &nestedOptional =
- std::get<std::optional<parser::NestedConstruct>>(x.t);
- assert(nestedOptional.has_value() &&
- "Expected a DoConstruct or OpenMPLoopConstruct");
- const auto *innerConstruct =
- std::get_if<common::Indirection<parser::OpenMPLoopConstruct>>(
- &(nestedOptional.value()));
-
- if (innerConstruct) {
- CollectNumAffectedLoopsFromLoopConstruct(
- innerConstruct->value(), levels, clauses);
+ for (auto &construct : std::get<parser::Block>(x.t)) {
+ if (auto *innerConstruct{parser::omp::GetOmpLoop(construct)}) {
+ CollectNumAffectedLoopsFromLoopConstruct(
+ *innerConstruct, levels, clauses);
+ }
}
}
@@ -2286,86 +2281,74 @@ void OmpAttributeVisitor::CheckPerfectNestAndRectangularLoop(
// Find the associated region by skipping nested loop-associated constructs
// such as loop transformations
- const parser::NestedConstruct *innermostAssocRegion{nullptr};
- const parser::OpenMPLoopConstruct *innermostConstruct{&x};
- while (const auto &innerAssocStmt{
- std::get<std::optional<parser::NestedConstruct>>(
- innermostConstruct->t)}) {
- innermostAssocRegion = &(innerAssocStmt.value());
- if (const auto *innerConstruct{
- std::get_if<common::Indirection<parser::OpenMPLoopConstruct>>(
- innermostAssocRegion)}) {
- innermostConstruct = &innerConstruct->value();
- } else {
- break;
- }
- }
-
- if (!innermostAssocRegion)
- return;
- const auto &outer{std::get_if<parser::DoConstruct>(innermostAssocRegion)};
- if (!outer)
- return;
-
- llvm::SmallVector<Symbol *> ivs;
- int curLevel{0};
- const parser::DoConstruct *loop{outer};
- while (true) {
- auto [iv, lb, ub, step] = GetLoopBounds(*loop);
-
- if (lb)
- checkExprHasSymbols(ivs, lb);
- if (ub)
- checkExprHasSymbols(ivs, ub);
- if (step)
- checkExprHasSymbols(ivs, step);
- if (iv) {
- if (auto *symbol{currScope().FindSymbol(iv->source)})
- ivs.push_back(symbol);
- }
+ for (auto &construct : std::get<parser::Block>(x.t)) {
+ if (const auto *innermostConstruct{parser::omp::GetOmpLoop(construct)}) {
+ CheckPerfectNestAndRectangularLoop(*innermostConstruct);
+ } else if (const auto *doConstruct{
+ parser::omp::GetDoConstruct(construct)}) {
+
+ llvm::SmallVector<Symbol *> ivs;
+ int curLevel{0};
+ const auto *loop{doConstruct};
+ while (true) {
+ auto [iv, lb, ub, step] = GetLoopBounds(*loop);
+
+ if (lb)
+ checkExprHasSymbols(ivs, lb);
+ if (ub)
+ checkExprHasSymbols(ivs, ub);
+ if (step)
+ checkExprHasSymbols(ivs, step);
+ if (iv) {
+ if (auto *symbol{currScope().FindSymbol(iv->source)})
+ ivs.push_back(symbol);
+ }
- // Stop after processing all affected loops
- if (curLevel + 1 >= dirDepth)
- break;
+ // Stop after processing all affected loops
+ if (curLevel + 1 >= dirDepth)
+ break;
- // Recurse into nested loop
- const auto &block{std::get<parser::Block>(loop->t)};
- if (block.empty()) {
- // Insufficient number of nested loops already reported by
- // CheckAssocLoopLevel()
- break;
- }
+ // Recurse into nested loop
+ const auto &block{std::get<parser::Block>(loop->t)};
+ if (block.empty()) {
+ // Insufficient number of nested loops already reported by
+ // CheckAssocLoopLevel()
+ break;
+ }
- loop = GetDoConstructIf(block.front());
- if (!loop) {
- // Insufficient number of nested loops already reported by
- // CheckAssocLoopLevel()
- break;
- }
+ loop = GetDoConstructIf(block.front());
+ if (!loop) {
+ // Insufficient number of nested loops already reported by
+ // CheckAssocLoopLevel()
+ break;
+ }
- auto checkPerfectNest = [&, this]() {
- if (block.empty())
- return;
- auto last = block.end();
- --last;
+ auto checkPerfectNest = [&, this]() {
+ if (block.empty())
+ return;
+ auto last = block.end();
+ --last;
- // A trailing CONTINUE is not considered part of the loop body
- if (parser::Unwrap<parser::ContinueStmt>(*last))
- --last;
+ // A trailing CONTINUE is not considered part of the loop body
+ if (parser::Unwrap<parser::ContinueStmt>(*last))
+ --last;
- // In a perfectly nested loop, the nested loop must be the only statement
- if (last == block.begin())
- return;
+ // In a perfectly nested loop, the nested loop must be the only
+ // statement
+ if (last == block.begin())
+ return;
- // Non-perfectly nested loop
- // TODO: Point to non-DO statement, directiveSource as a note
- context_.Say(dirContext.directiveSource,
- "Canonical loop nest must be perfectly nested."_err_en_US);
- };
+ // Non-perfectly nested loop
+ // TODO: Point to non-DO statement, directiveSource as a note
+ context_.Say(dirContext.directiveSource,
+ "Canonical loop nest must be perfectly nested."_err_en_US);
+ };
- checkPerfectNest();
+ checkPerfectNest();
- ++curLevel;
+ ++curLevel;
+ }
+ }
}
}
@@ -2379,7 +2362,6 @@ void OmpAttributeVisitor::CheckPerfectNestAndRectangularLoop(
// construct with multiple associated do-loops are lastprivate.
void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
const parser::OpenMPLoopConstruct &x) {
- unsigned version{context_.langOptions().OpenMPVersion};
std::int64_t level{GetContext().associatedLoopLevel};
if (level <= 0) {
return;
@@ -2398,22 +2380,13 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
const parser::OmpClause *clause{GetAssociatedClause()};
bool hasCollapseClause{
clause ? (clause->Id() == llvm::omp::OMPC_collapse) : false};
- const parser::OpenMPLoopConstruct *innerMostLoop = &x;
- const parser::NestedConstruct *innerMostNest = nullptr;
- while (auto &optLoopCons{
- std::get<std::optional<parser::NestedConstruct>>(innerMostLoop->t)}) {
- innerMostNest = &(optLoopCons.value());
- if (const auto *innerLoop{
- std::get_if<common::Indirection<parser::OpenMPLoopConstruct>>(
- innerMostNest)}) {
- innerMostLoop = &(innerLoop->value());
- } else
- break;
- }
- if (innerMostNest) {
- if (const auto &outer{std::get_if<parser::DoConstruct>(innerMostNest)}) {
- for (const parser::DoConstruct *loop{&*outer}; loop && level > 0;
+ for (auto &construct : std::get<parser::Block>(x.t)) {
+ if (const auto *innermostConstruct{parser::omp::GetOmpLoop(construct)}) {
+ PrivatizeAssociatedLoopIndexAndCheckLoopLevel(*innermostConstruct);
+ } else if (const auto *doConstruct{
+ parser::omp::GetDoConstruct(construct)}) {
+ for (const parser::DoConstruct *loop{&*doConstruct}; loop && level > 0;
--level) {
if (loop->IsDoConcurrent()) {
// DO CONCURRENT is explicitly allowed for the LOOP construct so long
@@ -2446,28 +2419,6 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
}
}
CheckAssocLoopLevel(level, GetAssociatedClause());
- } else if (const auto *loop{std::get_if<
- common::Indirection<parser::OpenMPLoopConstruct>>(
- innerMostNest)}) {
- const parser::OmpDirectiveSpecification &beginSpec{
- loop->value().BeginDir()};
- const parser::OmpDirectiveName &beginName{beginSpec.DirName()};
- if (beginName.v != llvm::omp::Directive::OMPD_unroll &&
- beginName.v != llvm::omp::Directive::OMPD_tile) {
- context_.Say(GetContext().directiveSource,
- "Only UNROLL or TILE constructs are allowed between an OpenMP Loop Construct and a DO construct"_err_en_US,
- parser::ToUpperCaseLetters(llvm::omp::getOpenMPDirectiveName(
- GetContext().directive, version)
- .str()));
- } else {
- PrivatizeAssociatedLoopIndexAndCheckLoopLevel(loop->value());
- }
- } else {
- context_.Say(GetContext().directiveSource,
- "A DO loop must follow the %s directive"_err_en_US,
- parser::ToUpperCaseLetters(
- llvm::omp::getOpenMPDirectiveName(GetContext().directive, version)
- .str()));
}
}
}
@@ -2526,7 +2477,7 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionConstruct &x) {
bool OmpAttributeVisitor::Pre(const parser::OpenMPCriticalConstruct &x) {
const parser::OmpBeginDirective &beginSpec{x.BeginDir()};
- PushContext(beginSpec.DirName().source, beginSpec.DirName().v);
+ PushContext(beginSpec.DirName().source, beginSpec.DirId());
GetContext().withinConstruct = true;
return true;
}
@@ -2615,9 +2566,8 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPAllocatorsConstruct &x) {
PushContext(x.source, dirSpec.DirId());
for (const auto &clause : dirSpec.Clauses().v) {
- if (const auto *allocClause{
- std::get_if<parser::OmpClause::Allocate>(&clause.u)}) {
- ResolveOmpObjectList(std::get<parser::OmpObjectList>(allocClause->v.t),
+ if (std::get_if<parser::OmpClause::Allocate>(&clause.u)) {
+ ResolveOmpObjectList(*parser::omp::GetOmpObjectList(clause),
Symbol::Flag::OmpExecutableAllocateDirective);
}
}
@@ -2965,6 +2915,67 @@ void OmpAttributeVisitor::CreateImplicitSymbols(const Symbol *symbol) {
}
}
+static bool IsOpenMPPointer(const Symbol &symbol) {
+ if (IsPointer(symbol) || IsBuiltinCPtr(symbol))
+ return true;
+ return false;
+}
+
+static bool IsOpenMPAggregate(const Symbol &symbol) {
+ if (IsAllocatable(symbol) || IsOpenMPPointer(symbol))
+ return false;
+
+ const auto *type{symbol.GetType()};
+ // OpenMP categorizes Fortran characters as aggregates.
+ if (type->category() == Fortran::semantics::DeclTypeSpec::Category::Character)
+ return true;
+
+ if (const auto *det{symbol.GetUltimate()
+ .detailsIf<Fortran::semantics::ObjectEntityDetails>()})
+ if (det->IsArray())
+ return true;
+
+ if (type->AsDerived())
+ return true;
+
+ if (IsDeferredShape(symbol) || IsAssumedRank(symbol) ||
+ IsAssumedShape(symbol))
+ return true;
+ return false;
+}
+
+static bool IsOpenMPScalar(const Symbol &symbol) {
+ if (IsOpenMPAggregate(symbol) || IsOpenMPPointer(symbol) ||
+ IsAllocatable(symbol))
+ return false;
+ const auto *type{symbol.GetType()};
+ if ((!symbol.GetShape() || symbol.GetShape()->empty()) &&
+ (type->category() ==
+ Fortran::semantics::DeclTypeSpec::Category::Numeric ||
+ type->category() ==
+ Fortran::semantics::DeclTypeSpec::Category::Logical))
+ return true;
+ return false;
+}
+
+static bool DefaultMapCategoryMatchesSymbol(
+ parser::OmpVariableCategory::Value category, const Symbol &symbol) {
+ using VarCat = parser::OmpVariableCategory::Value;
+ switch (category) {
+ case VarCat::Scalar:
+ return IsOpenMPScalar(symbol);
+ case VarCat::Allocatable:
+ return IsAllocatable(symbol);
+ case VarCat::Aggregate:
+ return IsOpenMPAggregate(symbol);
+ case VarCat::Pointer:
+ return IsOpenMPPointer(symbol);
+ case VarCat::All:
+ return true;
+ }
+ return false;
+}
+
// For OpenMP constructs, check all the data-refs within the constructs
// and adjust the symbol for each Name if necessary
void OmpAttributeVisitor::Post(const parser::Name &name) {
@@ -3000,6 +3011,41 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
}
}
+ // TODO: handle case where default and defaultmap are present on the same
+ // construct and conflict, defaultmap should supersede default if they
+ // conflict.
+ if (!GetContext().defaultMap.empty()) {
+ // Checked before implicit data sharing attributes as this rule ignores
+ // them and expects explicit predetermined/specified attributes to be in
+ // place for the types specified.
+ if (Symbol * found{currScope().FindSymbol(name.source)}) {
+ // If the variable has declare target applied to it (enter or link) it
+ // is exempt from defaultmap(none) restrictions.
+ // We also exempt procedures and named constants from defaultmap(none)
+ // checking.
+ if (!symbol->GetUltimate().test(Symbol::Flag::OmpDeclareTarget) &&
+ !(IsProcedure(*symbol) &&
+ !semantics::IsProcedurePointer(*symbol)) &&
+ !IsNamedConstant(*symbol)) {
+ auto &dMap = GetContext().defaultMap;
+ for (auto defaults : dMap) {
+ if (defaults.second ==
+ parser::OmpDefaultmapClause::ImplicitBehavior::None) {
+ if (DefaultMapCategoryMatchesSymbol(defaults.first, *found)) {
+ if (!IsObjectWithDSA(*symbol)) {
+ context_.Say(name.source,
+ "The DEFAULTMAP(NONE) clause requires that '%s' must be "
+ "listed in a "
+ "data-sharing attribute, data-mapping attribute, or is_device_ptr clause"_err_en_US,
+ symbol->name());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
if (Symbol * found{currScope().FindSymbol(name.source)}) {
if (found->GetUltimate().test(semantics::Symbol::Flag::OmpThreadprivate))
return;
@@ -3578,8 +3624,8 @@ void OmpAttributeVisitor::IssueNonConformanceWarning(llvm::omp::Directive D,
case llvm::omp::OMPD_allocate:
setAlternativeStr("ALLOCATORS");
break;
- case llvm::omp::OMPD_target_loop:
- default:;
+ default:
+ break;
}
context_.Warn(common::UsageWarning::OpenMPUsage, source, "%s"_warn_en_US,
warnStrOS.str());
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index a2062ef..345a0e4 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1852,21 +1852,25 @@ bool OmpVisitor::Pre(const parser::OmpMapClause &x) {
// TODO: Do we need a specific flag or type here, to distinghuish against
// other ConstructName things? Leaving this for the full implementation
// of mapper lowering.
- auto *misc{symbol->detailsIf<MiscDetails>()};
- if (!misc || misc->kind() != MiscDetails::Kind::ConstructName)
+ auto &ultimate{symbol->GetUltimate()};
+ auto *misc{ultimate.detailsIf<MiscDetails>()};
+ auto *md{ultimate.detailsIf<MapperDetails>()};
+ if (!md && (!misc || misc->kind() != MiscDetails::Kind::ConstructName))
context().Say(mapper->v.source,
"Name '%s' should be a mapper name"_err_en_US, mapper->v.source);
else
mapper->v.symbol = symbol;
} else {
- mapper->v.symbol =
- &MakeSymbol(mapper->v, MiscDetails{MiscDetails::Kind::ConstructName});
- // TODO: When completing the implementation, we probably want to error if
- // the symbol is not declared, but right now, testing that the TODO for
- // OmpMapClause happens is obscured by the TODO for declare mapper, so
- // leaving this out. Remove the above line once the declare mapper is
- // implemented. context().Say(mapper->v.source, "'%s' not
- // declared"_err_en_US, mapper->v.source);
+ // Allow the special 'default' mapper identifier without prior
+ // declaration so lowering can recognize and handle it. Emit an
+ // error for any other missing mapper identifier.
+ if (mapper->v.source.ToString() == "default") {
+ mapper->v.symbol = &MakeSymbol(
+ mapper->v, MiscDetails{MiscDetails::Kind::ConstructName});
+ } else {
+ context().Say(
+ mapper->v.source, "'%s' not declared"_err_en_US, mapper->v.source);
+ }
}
}
return true;
@@ -1880,8 +1884,16 @@ void OmpVisitor::ProcessMapperSpecifier(const parser::OmpMapperSpecifier &spec,
// the type has been fully processed.
BeginDeclTypeSpec();
auto &mapperName{std::get<std::string>(spec.t)};
- MakeSymbol(parser::CharBlock(mapperName), Attrs{},
- MiscDetails{MiscDetails::Kind::ConstructName});
+ // Create or update the mapper symbol with MapperDetails and
+ // keep track of the declarative construct for module emission.
+ SourceName mapperSource{context().SaveTempName(std::string{mapperName})};
+ Symbol &mapperSym{MakeSymbol(mapperSource, Attrs{})};
+ if (!mapperSym.detailsIf<MapperDetails>()) {
+ mapperSym.set_details(MapperDetails{});
+ }
+ if (!context().langOptions().OpenMPSimd) {
+ mapperSym.get<MapperDetails>().AddDecl(declaratives_.back());
+ }
PushScope(Scope::Kind::OtherConstruct, nullptr);
Walk(std::get<parser::TypeSpec>(spec.t));
auto &varName{std::get<parser::Name>(spec.t)};
@@ -2141,6 +2153,8 @@ public:
void Post(const parser::AssignedGotoStmt &);
void Post(const parser::CompilerDirective &);
+ bool Pre(const parser::SectionSubscript &);
+
// These nodes should never be reached: they are handled in ProgramUnit
bool Pre(const parser::MainProgram &) {
llvm_unreachable("This node is handled in ProgramUnit");
@@ -3611,10 +3625,20 @@ void ModuleVisitor::Post(const parser::UseStmt &x) {
rename.u);
}
for (const auto &[name, symbol] : *useModuleScope_) {
+ // Default USE imports public names, excluding intrinsic-only and most
+ // miscellaneous details. Allow OpenMP mapper identifiers represented
+ // as MapperDetails, and also legacy MiscDetails::ConstructName.
+ bool isMapper{symbol->has<MapperDetails>()};
+ if (!isMapper) {
+ if (const auto *misc{symbol->detailsIf<MiscDetails>()}) {
+ isMapper = misc->kind() == MiscDetails::Kind::ConstructName;
+ }
+ }
if (symbol->attrs().test(Attr::PUBLIC) && !IsUseRenamed(symbol->name()) &&
(!symbol->implicitAttrs().test(Attr::INTRINSIC) ||
symbol->has<UseDetails>()) &&
- !symbol->has<MiscDetails>() && useNames.count(name) == 0) {
+ (!symbol->has<MiscDetails>() || isMapper) &&
+ useNames.count(name) == 0) {
SourceName location{x.moduleName.source};
if (auto *localSymbol{FindInScope(name)}) {
DoAddUse(location, localSymbol->name(), *localSymbol, *symbol);
@@ -3945,22 +3969,6 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
useProcedure = &useUltimate;
}
- // Creates a UseErrorDetails symbol in the current scope for a
- // current UseDetails symbol, but leaves the UseDetails in the
- // scope's name map.
- auto CreateLocalUseError{[&]() {
- EraseSymbol(*localSymbol);
- CHECK(localSymbol->has<UseDetails>());
- UseErrorDetails details{localSymbol->get<UseDetails>()};
- details.add_occurrence(location, useSymbol);
- Symbol *newSymbol{&MakeSymbol(localName, Attrs{}, std::move(details))};
- // Restore *localSymbol in currScope
- auto iter{currScope().find(localName)};
- CHECK(iter != currScope().end() && &*iter->second == newSymbol);
- iter->second = MutableSymbolRef{*localSymbol};
- return newSymbol;
- }};
-
// When two derived types arrived, try to combine them.
const Symbol *combinedDerivedType{nullptr};
if (!useDerivedType) {
@@ -3986,8 +3994,19 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
combinedDerivedType = localDerivedType;
} else {
// Create a local UseErrorDetails for the ambiguous derived type
- if (localGeneric) {
- combinedDerivedType = CreateLocalUseError();
+ if (localSymbol->has<UseDetails>() && localGeneric) {
+ // Creates a UseErrorDetails symbol in the current scope for a
+ // current UseDetails symbol, but leaves the UseDetails in the
+ // scope's name map.
+ UseErrorDetails details{localSymbol->get<UseDetails>()};
+ EraseSymbol(*localSymbol);
+ details.add_occurrence(location, useSymbol);
+ Symbol *newSymbol{&MakeSymbol(localName, Attrs{}, std::move(details))};
+ // Restore *localSymbol in currScope
+ auto iter{currScope().find(localName)};
+ CHECK(iter != currScope().end() && &*iter->second == newSymbol);
+ iter->second = MutableSymbolRef{*localSymbol};
+ combinedDerivedType = newSymbol;
} else {
ConvertToUseError(*localSymbol, location, useSymbol);
localDerivedType = nullptr;
@@ -10058,6 +10077,7 @@ void ResolveNamesVisitor::Post(const parser::AssignedGotoStmt &x) {
void ResolveNamesVisitor::Post(const parser::CompilerDirective &x) {
if (std::holds_alternative<parser::CompilerDirective::VectorAlways>(x.u) ||
+ std::holds_alternative<parser::CompilerDirective::VectorLength>(x.u) ||
std::holds_alternative<parser::CompilerDirective::Unroll>(x.u) ||
std::holds_alternative<parser::CompilerDirective::UnrollAndJam>(x.u) ||
std::holds_alternative<parser::CompilerDirective::NoVector>(x.u) ||
@@ -10065,7 +10085,9 @@ void ResolveNamesVisitor::Post(const parser::CompilerDirective &x) {
std::holds_alternative<parser::CompilerDirective::NoUnrollAndJam>(x.u) ||
std::holds_alternative<parser::CompilerDirective::ForceInline>(x.u) ||
std::holds_alternative<parser::CompilerDirective::Inline>(x.u) ||
- std::holds_alternative<parser::CompilerDirective::NoInline>(x.u)) {
+ std::holds_alternative<parser::CompilerDirective::Prefetch>(x.u) ||
+ std::holds_alternative<parser::CompilerDirective::NoInline>(x.u) ||
+ std::holds_alternative<parser::CompilerDirective::IVDep>(x.u)) {
return;
}
if (const auto *tkr{
@@ -10198,6 +10220,14 @@ template <typename A> std::set<SourceName> GetUses(const A &x) {
return uses;
}
+bool ResolveNamesVisitor::Pre(const parser::SectionSubscript &x) {
+ // Turn off "in EQUIVALENCE" check for array indexing, because
+ // the indices themselves are not part of the EQUIVALENCE.
+ auto restorer{common::ScopedSet(inEquivalenceStmt_, false)};
+ Walk(x.u);
+ return false;
+}
+
bool ResolveNamesVisitor::Pre(const parser::Program &x) {
if (Scope * hermetic{context().currentHermeticModuleFileScope()}) {
// Processing either the dependent modules or first module of a
diff --git a/flang/lib/Semantics/rewrite-parse-tree.cpp b/flang/lib/Semantics/rewrite-parse-tree.cpp
index 5b7dab3..60e3e6a 100644
--- a/flang/lib/Semantics/rewrite-parse-tree.cpp
+++ b/flang/lib/Semantics/rewrite-parse-tree.cpp
@@ -9,6 +9,7 @@
#include "rewrite-parse-tree.h"
#include "flang/Common/indirection.h"
+#include "flang/Parser/openmp-utils.h"
#include "flang/Parser/parse-tree-visitor.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Parser/tools.h"
@@ -117,7 +118,7 @@ static bool ReturnsDataPointer(const Symbol &symbol) {
}
static bool LoopConstructIsSIMD(parser::OpenMPLoopConstruct *ompLoop) {
- return llvm::omp::allSimdSet.test(ompLoop->BeginDir().DirName().v);
+ return llvm::omp::allSimdSet.test(ompLoop->BeginDir().DirId());
}
// Remove non-SIMD OpenMPConstructs once they are parsed.
@@ -195,20 +196,24 @@ void RewriteMutator::OpenMPSimdOnly(
++it;
continue;
}
- auto &nest =
- std::get<std::optional<parser::NestedConstruct>>(ompLoop->t);
-
- if (auto *doConstruct =
- std::get_if<parser::DoConstruct>(&nest.value())) {
- auto &loopBody = std::get<parser::Block>(doConstruct->t);
- // We can only remove some constructs from a loop when it's _not_ a
- // OpenMP simd loop
- OpenMPSimdOnly(loopBody, /*isNonSimdLoopBody=*/true);
- auto newDoConstruct = std::move(*doConstruct);
- auto newLoop = parser::ExecutionPartConstruct{
- parser::ExecutableConstruct{std::move(newDoConstruct)}};
+ std::list<parser::ExecutionPartConstruct> doList;
+ for (auto &construct : std::get<parser::Block>(ompLoop->t)) {
+ if (auto *doConstruct = const_cast<parser::DoConstruct *>(
+ parser::omp::GetDoConstruct(construct))) {
+ auto &loopBody = std::get<parser::Block>(doConstruct->t);
+ // We can only remove some constructs from a loop when it's _not_
+ // a OpenMP simd loop
+ OpenMPSimdOnly(const_cast<parser::Block &>(loopBody),
+ /*isNonSimdLoopBody=*/true);
+ auto newLoop = parser::ExecutionPartConstruct{
+ parser::ExecutableConstruct{std::move(*doConstruct)}};
+ doList.insert(doList.end(), std::move(newLoop));
+ }
+ }
+ if (!doList.empty()) {
it = block.erase(it);
- block.insert(it, std::move(newLoop));
+ for (auto &newLoop : doList)
+ block.insert(it, std::move(newLoop));
continue;
}
} else if (auto *ompCon{std::get_if<parser::OpenMPSectionsConstruct>(
@@ -386,13 +391,12 @@ bool RewriteMutator::Pre(parser::OpenMPLoopConstruct &ompLoop) {
// If we're looking at a non-simd OpenMP loop, we need to explicitly
// call OpenMPSimdOnly on the nested loop block while indicating where
// the block comes from.
- auto &nest = std::get<std::optional<parser::NestedConstruct>>(ompLoop.t);
- if (!nest.has_value()) {
- return true;
- }
- if (auto *doConstruct = std::get_if<parser::DoConstruct>(&*nest)) {
- auto &innerBlock = std::get<parser::Block>(doConstruct->t);
- OpenMPSimdOnly(innerBlock, /*isNonSimdLoopBody=*/true);
+ for (auto &construct : std::get<parser::Block>(ompLoop.t)) {
+ if (auto *doConstruct = parser::omp::GetDoConstruct(construct)) {
+ auto &innerBlock = std::get<parser::Block>(doConstruct->t);
+ OpenMPSimdOnly(const_cast<parser::Block &>(innerBlock),
+ /*isNonSimdLoopBody=*/true);
+ }
}
}
return true;
diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp
index 0ec44b7..ed0715a 100644
--- a/flang/lib/Semantics/symbol.cpp
+++ b/flang/lib/Semantics/symbol.cpp
@@ -338,7 +338,8 @@ std::string DetailsToString(const Details &details) {
[](const TypeParamDetails &) { return "TypeParam"; },
[](const MiscDetails &) { return "Misc"; },
[](const AssocEntityDetails &) { return "AssocEntity"; },
- [](const UserReductionDetails &) { return "UserReductionDetails"; }},
+ [](const UserReductionDetails &) { return "UserReductionDetails"; },
+ [](const MapperDetails &) { return "MapperDetails"; }},
details);
}
@@ -379,6 +380,7 @@ bool Symbol::CanReplaceDetails(const Details &details) const {
[&](const UserReductionDetails &) {
return has<UserReductionDetails>();
},
+ [&](const MapperDetails &) { return has<MapperDetails>(); },
[](const auto &) { return false; },
},
details);
@@ -685,6 +687,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) {
DumpType(os, type);
}
},
+ // Avoid recursive streaming for MapperDetails; nothing more to dump
+ [&](const MapperDetails &) {},
[&](const auto &x) { os << x; },
},
details);
diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp
index 8eddd03..cf1e5e7 100644
--- a/flang/lib/Semantics/tools.cpp
+++ b/flang/lib/Semantics/tools.cpp
@@ -582,6 +582,18 @@ bool IsOrContainsEventOrLockComponent(const Symbol &original) {
return false;
}
+bool IsOrContainsNotifyComponent(const Symbol &original) {
+ const Symbol &symbol{ResolveAssociations(original, /*stopAtTypeGuard=*/true)};
+ if (evaluate::IsVariable(symbol)) {
+ if (const DeclTypeSpec *type{symbol.GetType()}) {
+ if (const DerivedTypeSpec *derived{type->AsDerived()}) {
+ return IsNotifyType(derived) || FindNotifyPotentialComponent(*derived);
+ }
+ }
+ }
+ return false;
+}
+
// Check this symbol suitable as a type-bound procedure - C769
bool CanBeTypeBoundProc(const Symbol &symbol) {
if (IsDummy(symbol) || IsProcedurePointer(symbol)) {
@@ -1489,6 +1501,32 @@ PotentialComponentIterator::const_iterator FindEventOrLockPotentialComponent(
return iter;
}
+PotentialComponentIterator::const_iterator FindNotifyPotentialComponent(
+ const DerivedTypeSpec &derived, bool ignoreCoarrays) {
+ PotentialComponentIterator potentials{derived};
+ auto iter{potentials.begin()};
+ for (auto end{potentials.end()}; iter != end; ++iter) {
+ const Symbol &component{*iter};
+ if (const auto *object{component.detailsIf<ObjectEntityDetails>()}) {
+ if (const DeclTypeSpec *type{object->type()}) {
+ if (IsNotifyType(type->AsDerived())) {
+ if (!ignoreCoarrays) {
+ break; // found one
+ }
+ auto path{iter.GetComponentPath()};
+ path.pop_back();
+ if (std::find_if(path.begin(), path.end(), [](const Symbol &sym) {
+ return evaluate::IsCoarray(sym);
+ }) == path.end()) {
+ break; // found one not in a coarray
+ }
+ }
+ }
+ }
+ }
+ return iter;
+}
+
UltimateComponentIterator::const_iterator FindAllocatableUltimateComponent(
const DerivedTypeSpec &derived) {
UltimateComponentIterator ultimates{derived};
diff --git a/flang/lib/Semantics/type.cpp b/flang/lib/Semantics/type.cpp
index dba15e6..038a402 100644
--- a/flang/lib/Semantics/type.cpp
+++ b/flang/lib/Semantics/type.cpp
@@ -192,6 +192,13 @@ void DerivedTypeSpec::EvaluateParameters(SemanticsContext &context) {
}
}
+void DerivedTypeSpec::ReevaluateParameters(SemanticsContext &context) {
+ evaluated_ = false;
+ instantiated_ = false;
+ scope_ = nullptr;
+ EvaluateParameters(context);
+}
+
void DerivedTypeSpec::AddParamValue(SourceName name, ParamValue &&value) {
CHECK(cooked_);
auto pair{parameters_.insert(std::make_pair(name, std::move(value)))};